From accf3319e7fc48fe522d0be11e384a959bcd9048 Mon Sep 17 00:00:00 2001 From: Cody Tseng Date: Fri, 7 Mar 2025 23:39:46 +0800 Subject: [PATCH] style: adjust the style of NoteStats (#222) --- src/components/Nip22ReplyNoteList/index.tsx | 18 ++- src/components/Note/index.tsx | 38 +++-- .../NoteOptions/RawEventDialog.tsx | 0 src/components/NoteOptions/index.tsx | 152 ++++++++++++++++++ src/components/NoteStats/LikeButton.tsx | 6 +- .../NoteStats/NoteOptions/index.tsx | 68 -------- src/components/NoteStats/ReplyButton.tsx | 4 +- src/components/NoteStats/RepostButton.tsx | 89 +++++++--- src/components/NoteStats/SeenOnButton.tsx | 63 ++++++-- src/components/NoteStats/ZapButton.tsx | 21 ++- src/components/NoteStats/index.tsx | 35 +++- src/components/PictureNote/index.tsx | 34 ++-- src/components/ReplyNote/index.tsx | 29 ++-- src/components/ReplyNoteList/index.tsx | 22 +-- src/components/ui/drawer.tsx | 6 +- src/pages/secondary/NotePage/index.tsx | 18 +-- 16 files changed, 417 insertions(+), 186 deletions(-) rename src/components/{NoteStats => }/NoteOptions/RawEventDialog.tsx (100%) create mode 100644 src/components/NoteOptions/index.tsx delete mode 100644 src/components/NoteStats/NoteOptions/index.tsx diff --git a/src/components/Nip22ReplyNoteList/index.tsx b/src/components/Nip22ReplyNoteList/index.tsx index 9dbf1db2..7fc3600c 100644 --- a/src/components/Nip22ReplyNoteList/index.tsx +++ b/src/components/Nip22ReplyNoteList/index.tsx @@ -167,14 +167,16 @@ export default function Nip22ReplyNoteList({ return ( <> -
- {loading ? t('loading...') : until ? t('load more older replies') : null} -
- {replies.length > 0 && (loading || until) && } -
+ {(loading || until) && ( +
+ {loading ? t('loading...') : t('load more older replies')} +
+ )} + {replies.length > 0 && (loading || until) && } +
{replies.map((reply) => { const info = replyMap[reply.id] return ( diff --git a/src/components/Note/index.tsx b/src/components/Note/index.tsx index 0ec0c34a..01de7cee 100644 --- a/src/components/Note/index.tsx +++ b/src/components/Note/index.tsx @@ -6,6 +6,7 @@ import { Event } from 'nostr-tools' import { useMemo } from 'react' import Content from '../Content' import { FormattedTimestamp } from '../FormattedTimestamp' +import NoteOptions from '../NoteOptions' import NoteStats from '../NoteStats' import ParentNotePreview from '../ParentNotePreview' import UserAvatar from '../UserAvatar' @@ -36,25 +37,28 @@ export default function Note({ return (
-
- -
-
- - {usingClient && size === 'normal' && ( -
using {usingClient}
- )} -
-
- +
+
+ +
+
+ + {usingClient && size === 'normal' && ( +
using {usingClient}
+ )} +
+
+ +
+ {size === 'normal' && }
{parentEventId && ( mutePubkeys.includes(event.pubkey), [mutePubkeys, event]) + + const trigger = ( + + ) + + const rawEventDialog = ( + setIsRawEventDialogOpen(false)} + /> + ) + + if (isSmallScreen) { + return ( +
e.stopPropagation()}> + {trigger} + + setIsDrawerOpen(false)} /> + +
+ + + + {pubkey && ( + + )} +
+
+
+ {rawEventDialog} +
+ ) + } + + return ( +
e.stopPropagation()}> + + {trigger} + + navigator.clipboard.writeText(getSharableEventId(event))} + > + + {t('Copy event ID')} + + navigator.clipboard.writeText(pubkeyToNpub(event.pubkey) ?? '')} + > + + {t('Copy user ID')} + + + setIsRawEventDialogOpen(true)}> + + {t('View raw event')} + + {pubkey && ( + <> + + (isMuted ? unmutePubkey(event.pubkey) : mutePubkey(event.pubkey))} + className="text-destructive focus:text-destructive" + > + {isMuted ? : } + {isMuted ? t('Unmute user') : t('Mute user')} + + + )} + + + {rawEventDialog} +
+ ) +} diff --git a/src/components/NoteStats/LikeButton.tsx b/src/components/NoteStats/LikeButton.tsx index c57144b6..e2fc0c40 100644 --- a/src/components/NoteStats/LikeButton.tsx +++ b/src/components/NoteStats/LikeButton.tsx @@ -55,7 +55,7 @@ export default function LikeButton({ event }: { event: Event }) { return ( diff --git a/src/components/NoteStats/NoteOptions/index.tsx b/src/components/NoteStats/NoteOptions/index.tsx deleted file mode 100644 index fbe13d62..00000000 --- a/src/components/NoteStats/NoteOptions/index.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger -} from '@/components/ui/dropdown-menu' -import { getSharableEventId } from '@/lib/event' -import { pubkeyToNpub } from '@/lib/pubkey' -import { useMuteList } from '@/providers/MuteListProvider' -import { useNostr } from '@/providers/NostrProvider' -import { Bell, BellOff, Code, Copy, Ellipsis } from 'lucide-react' -import { Event } from 'nostr-tools' -import { useMemo, useState } from 'react' -import { useTranslation } from 'react-i18next' -import RawEventDialog from './RawEventDialog' - -export default function NoteOptions({ event }: { event: Event }) { - const { t } = useTranslation() - const { pubkey } = useNostr() - const [isRawEventDialogOpen, setIsRawEventDialogOpen] = useState(false) - const { mutePubkey, unmutePubkey, mutePubkeys } = useMuteList() - const isMuted = useMemo(() => mutePubkeys.includes(event.pubkey), [mutePubkeys, event]) - - return ( -
e.stopPropagation()}> - - - - - - navigator.clipboard.writeText(getSharableEventId(event))} - > - - {t('Copy event ID')} - - navigator.clipboard.writeText(pubkeyToNpub(event.pubkey) ?? '')} - > - - {t('Copy user ID')} - - setIsRawEventDialogOpen(true)}> - - {t('View raw event')} - - {pubkey && ( - (isMuted ? unmutePubkey(event.pubkey) : mutePubkey(event.pubkey))} - className="text-destructive focus:text-destructive" - > - {isMuted ? : } - {isMuted ? t('Unmute user') : t('Mute user')} - - )} - - - setIsRawEventDialogOpen(false)} - /> -
- ) -} diff --git a/src/components/NoteStats/ReplyButton.tsx b/src/components/NoteStats/ReplyButton.tsx index 1d0a426e..c7393969 100644 --- a/src/components/NoteStats/ReplyButton.tsx +++ b/src/components/NoteStats/ReplyButton.tsx @@ -26,7 +26,7 @@ export default function ReplyButton({ return ( <> + ) + + const postEditor = ( + + ) + + if (isSmallScreen) { + return ( + <> + {trigger} + + setIsDrawerOpen(false)} /> + +
+ + +
+
+
+ {postEditor} + + ) + } + return ( <> - - - - + {trigger} + {t('Repost')} @@ -93,11 +150,7 @@ export default function RepostButton({ event }: { event: Event }) { - + {postEditor} ) } diff --git a/src/components/NoteStats/SeenOnButton.tsx b/src/components/NoteStats/SeenOnButton.tsx index eaab4dcb..7cb2ebd4 100644 --- a/src/components/NoteStats/SeenOnButton.tsx +++ b/src/components/NoteStats/SeenOnButton.tsx @@ -1,4 +1,6 @@ import { useSecondaryPage } from '@/PageManager' +import { Button } from '@/components/ui/button' +import { Drawer, DrawerContent, DrawerOverlay } from '@/components/ui/drawer' import { DropdownMenu, DropdownMenuContent, @@ -9,39 +11,78 @@ import { } from '@/components/ui/dropdown-menu' import { toRelay } from '@/lib/link' import { simplifyUrl } from '@/lib/url' +import { useScreenSize } from '@/providers/ScreenSizeProvider' import client from '@/services/client.service' import { Server } from 'lucide-react' import { Event } from 'nostr-tools' import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' +import RelayIcon from '../RelayIcon' export default function SeenOnButton({ event }: { event: Event }) { const { t } = useTranslation() + const { isSmallScreen } = useScreenSize() const { push } = useSecondaryPage() const [relays, setRelays] = useState([]) + const [isDrawerOpen, setIsDrawerOpen] = useState(false) useEffect(() => { const seenOn = client.getSeenEventRelayUrls(event.id) setRelays(seenOn) }, []) + const trigger = ( + + ) + + if (isSmallScreen) { + return ( + <> + {trigger} + + setIsDrawerOpen(false)} /> + +
+ {relays.map((relay) => ( + + ))} +
+
+
+ + ) + } return ( - - - + {trigger} {t('Seen on')} {relays.map((relay) => ( - push(toRelay(relay))}> + push(toRelay(relay))} className="min-w-52"> + {simplifyUrl(relay)} ))} diff --git a/src/components/NoteStats/ZapButton.tsx b/src/components/NoteStats/ZapButton.tsx index 5d08cb99..253629d5 100644 --- a/src/components/NoteStats/ZapButton.tsx +++ b/src/components/NoteStats/ZapButton.tsx @@ -18,6 +18,7 @@ export default function ZapButton({ event }: { event: Event }) { const { checkLogin, pubkey } = useNostr() const { noteStatsMap, addZap } = useNoteStats() const { defaultZapSats, defaultZapComment, quickZap } = useZap() + const [touchStart, setTouchStart] = useState<{ x: number; y: number } | null>(null) const [openZapDialog, setOpenZapDialog] = useState(false) const [zapping, setZapping] = useState(false) const { zapAmount, hasZapped } = useMemo(() => { @@ -71,6 +72,11 @@ export default function ZapButton({ event }: { event: Event }) { e.preventDefault() isLongPressRef.current = false + if ('touches' in e) { + const touch = e.touches[0] + setTouchStart({ x: touch.clientX, y: touch.clientY }) + } + if (quickZap) { timerRef.current = setTimeout(() => { isLongPressRef.current = true @@ -89,6 +95,15 @@ export default function ZapButton({ event }: { event: Event }) { clearTimeout(timerRef.current) } + if ('touches' in e) { + setTouchStart(null) + if (!touchStart) return + const touch = e.changedTouches[0] + const diffX = Math.abs(touch.clientX - touchStart.x) + const diffY = Math.abs(touch.clientY - touchStart.y) + if (diffX > 10 || diffY > 10) return + } + if (!quickZap) { checkLogin(() => { setOpenZapDialog(true) @@ -110,7 +125,7 @@ export default function ZapButton({ event }: { event: Event }) { <> diff --git a/src/components/NoteStats/index.tsx b/src/components/NoteStats/index.tsx index 16c449b2..aa16ffb5 100644 --- a/src/components/NoteStats/index.tsx +++ b/src/components/NoteStats/index.tsx @@ -1,9 +1,9 @@ import { cn } from '@/lib/utils' import { useNoteStats } from '@/providers/NoteStatsProvider' +import { useScreenSize } from '@/providers/ScreenSizeProvider' import { Event } from 'nostr-tools' import { useEffect } from 'react' import LikeButton from './LikeButton' -import NoteOptions from './NoteOptions' import ReplyButton from './ReplyButton' import RepostButton from './RepostButton' import SeenOnButton from './SeenOnButton' @@ -13,14 +13,19 @@ import ZapButton from './ZapButton' export default function NoteStats({ event, className, + classNames, fetchIfNotExisting = false, variant = 'note' }: { event: Event className?: string + classNames?: { + buttonBar?: string + } fetchIfNotExisting?: boolean variant?: 'note' | 'reply' }) { + const { isSmallScreen } = useScreenSize() const { fetchNoteStats } = useNoteStats() useEffect(() => { @@ -28,19 +33,39 @@ export default function NoteStats({ fetchNoteStats(event) }, [event, fetchIfNotExisting]) + if (isSmallScreen) { + return ( +
+ +
e.stopPropagation()} + > + + + + + +
+
+ ) + } + return (
-
-
e.stopPropagation()}> +
+
e.stopPropagation()}>
-
e.stopPropagation()}> +
e.stopPropagation()}> -
diff --git a/src/components/PictureNote/index.tsx b/src/components/PictureNote/index.tsx index adf0c83b..f3881f16 100644 --- a/src/components/PictureNote/index.tsx +++ b/src/components/PictureNote/index.tsx @@ -6,6 +6,7 @@ import NoteStats from '../NoteStats' import UserAvatar from '../UserAvatar' import Username from '../Username' import PictureContent from '../PictureContent' +import NoteOptions from '../NoteOptions' export default function PictureNote({ event, @@ -22,23 +23,26 @@ export default function PictureNote({ return (
-
- -
-
- - {usingClient && ( -
using {usingClient}
- )} -
-
- +
+
+ +
+
+ + {usingClient && ( +
using {usingClient}
+ )} +
+
+ +
+
{!hideStats && ( diff --git a/src/components/ReplyNote/index.tsx b/src/components/ReplyNote/index.tsx index 30fcb712..104d7d39 100644 --- a/src/components/ReplyNote/index.tsx +++ b/src/components/ReplyNote/index.tsx @@ -5,6 +5,7 @@ import { useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import Content from '../Content' import { FormattedTimestamp } from '../FormattedTimestamp' +import NoteOptions from '../NoteOptions' import NoteStats from '../NoteStats' import ParentNotePreview from '../ParentNotePreview' import UserAvatar from '../UserAvatar' @@ -31,19 +32,22 @@ export default function ReplyNote({ return (
-
- -
- +
+
+ +
+ +
+
{parentEvent && ( - + ) : (