From 65ae5a72aebcdb50622c4879a8ccd99dce2cb4ab Mon Sep 17 00:00:00 2001 From: Silberengel Date: Mon, 25 May 2026 18:49:56 +0200 Subject: [PATCH] bug-fixes --- src/components/BookmarkButton/index.tsx | 2 +- src/components/Note/index.tsx | 12 +-- src/components/NoteCard/MainNoteCard.tsx | 1 + src/components/NoteOptions/DesktopMenu.tsx | 18 ++-- src/components/NoteOptions/MobileMenu.tsx | 33 ++++--- .../NoteOptions/NoteOptionsMetaHeader.tsx | 85 +++++++++++++++++++ src/components/NoteOptions/index.tsx | 21 ++++- src/components/NoteStats/LikeButton.tsx | 6 +- src/components/NoteStats/ReplyButton.tsx | 2 +- src/components/NoteStats/RepostButton.tsx | 6 +- src/components/NoteStats/SeenOnButton.tsx | 46 ++-------- src/components/NoteStats/ZapButton.tsx | 6 +- src/components/NoteStats/index.tsx | 10 +-- src/components/ReplyNote/index.tsx | 18 +--- src/hooks/useSeenOnRelays.ts | 46 ++++++++++ 15 files changed, 208 insertions(+), 104 deletions(-) create mode 100644 src/components/NoteOptions/NoteOptionsMetaHeader.tsx create mode 100644 src/hooks/useSeenOnRelays.ts diff --git a/src/components/BookmarkButton/index.tsx b/src/components/BookmarkButton/index.tsx index 8e0a66e1..439626dc 100644 --- a/src/components/BookmarkButton/index.tsx +++ b/src/components/BookmarkButton/index.tsx @@ -68,7 +68,7 @@ export default function BookmarkButton({ event }: { event: Event }) { - ) - }) + <> + {header} + {menuActions.map((action, index) => { + const Icon = action.icon + return ( + + ) + })} + ) : ( <> + + ) + }) + + return ( +
+

+ {t('Note kind label line', { kind: event.kind, description })} +

+ {relays.length > 0 ? ( +
+

+ {t('Seen on')} +

+ {inDropdown ? ( +
{relayRows}
+ ) : ( +
    {relayRows}
+ )} +
+ ) : null} +
+ ) +} diff --git a/src/components/NoteOptions/index.tsx b/src/components/NoteOptions/index.tsx index 7bfdced3..b969d835 100644 --- a/src/components/NoteOptions/index.tsx +++ b/src/components/NoteOptions/index.tsx @@ -10,6 +10,7 @@ import { useNostr } from '@/providers/NostrProvider' import { DesktopMenu } from './DesktopMenu' import EditOrCloneEventDialog, { type TEditOrCloneMode } from './EditOrCloneEventDialog' import { MobileMenu } from './MobileMenu' +import NoteOptionsMetaHeader from './NoteOptionsMetaHeader' import RawEventDialog from './RawEventDialog' import ReportDialog from './ReportDialog' import { SubMenuAction, useMenuActions, type ShowSubMenuOptions } from './useMenuActions' @@ -27,12 +28,15 @@ export default function NoteOptions({ initialPublicMessageTo, onOpenCallInvite, initialDefaultContent, - pinned = false + pinned = false, + seenOnAllowlist }: { event: Event className?: string /** Note is shown in a pinned section (profile pins, etc.). */ pinned?: boolean + /** When set (home favorites feed), relay list in the menu matches the feed allowlist. */ + seenOnAllowlist?: readonly string[] initialHighlightData?: HighlightData highlightDefaultContent?: string isPostEditorOpen?: boolean @@ -124,12 +128,25 @@ export default function NoteOptions({ [] ) + const menuHeader = useMemo( + () => ( + + ), + [event, seenOnAllowlist, isSmallScreen] + ) + return (
e.stopPropagation()}> {isSmallScreen ? ( ) : ( - + )} -
+
{(likeCount ?? 0) >= 100 ? '99+' : String(likeCount ?? 0)}
) : ( - + ) // Discussions (kind 11) and kind 1111 under a discussion: only +/- vote reactions diff --git a/src/components/NoteStats/ReplyButton.tsx b/src/components/NoteStats/ReplyButton.tsx index a01cf117..2c75f271 100644 --- a/src/components/NoteStats/ReplyButton.tsx +++ b/src/components/NoteStats/ReplyButton.tsx @@ -40,7 +40,7 @@ export function ReplyButtonWithStats({ event, hideCount = false, noteStats }: Re <> ) @@ -103,7 +69,7 @@ export default function SeenOnButton({ setIsDrawerOpen(false) setTimeout(() => { push(toRelay(relay)) - }, 50) // Timeout to allow the drawer to close before navigating + }, 50) }} > {simplifyUrl(relay)} diff --git a/src/components/NoteStats/ZapButton.tsx b/src/components/NoteStats/ZapButton.tsx index 86c74835..0cda25cb 100644 --- a/src/components/NoteStats/ZapButton.tsx +++ b/src/components/NoteStats/ZapButton.tsx @@ -225,7 +225,7 @@ export function ZapButtonWithStats({ event, hideCount = false, noteStats }: ZapB
-
{interactionButtons}
-
- {utilityButtons} - -
+ {interactionButtons} + {utilityButtons}
) diff --git a/src/components/ReplyNote/index.tsx b/src/components/ReplyNote/index.tsx index 480744a5..ab435e00 100644 --- a/src/components/ReplyNote/index.tsx +++ b/src/components/ReplyNote/index.tsx @@ -38,7 +38,6 @@ import ParentNotePreview from '../ParentNotePreview' import WebPreview from '../WebPreview' import UserAvatar from '../UserAvatar' import Username from '../Username' -import NoteKindLabel from '../Note/NoteKindLabel' import Superchat from '../Note/Superchat' import Zap from '../Note/Zap' import MoneroTip from '../Note/MoneroTip' @@ -156,22 +155,7 @@ export default function ReplyNote({ -
- - -
+ {webReactionParentUrl ? (
diff --git a/src/hooks/useSeenOnRelays.ts b/src/hooks/useSeenOnRelays.ts new file mode 100644 index 00000000..a11691e4 --- /dev/null +++ b/src/hooks/useSeenOnRelays.ts @@ -0,0 +1,46 @@ +import { filterRelaysToUserAllowlist } from '@/lib/relay-allowlist' +import { normalizeAnyRelayUrl } from '@/lib/url' +import client from '@/services/client.service' +import { useEffect, useRef, useState } from 'react' + +export function useSeenOnRelays( + eventId: string, + allowedRelays?: readonly string[] +): string[] { + const [relays, setRelays] = useState([]) + const allowedRelaysRef = useRef(allowedRelays) + allowedRelaysRef.current = allowedRelays + const allowedRelaysKey = allowedRelays?.length + ? [...allowedRelays] + .map((u) => normalizeAnyRelayUrl(u) || u.trim()) + .filter(Boolean) + .sort() + .join('|') + : '' + + useEffect(() => { + let cancelled = false + let attempts = 0 + const maxAttempts = 20 + const apply = () => { + const seenOn = client.getSeenEventRelayUrls(eventId) + const allowlist = allowedRelaysRef.current + const visible = + allowlist?.length ? filterRelaysToUserAllowlist(seenOn, allowlist) : seenOn + if (!cancelled) setRelays(visible) + return visible.length > 0 + } + if (apply()) return + const id = setInterval(() => { + if (cancelled) return + attempts++ + if (apply() || attempts >= maxAttempts) clearInterval(id) + }, 500) + return () => { + cancelled = true + clearInterval(id) + } + }, [eventId, allowedRelaysKey]) + + return relays +}