diff --git a/src/renderer/src/components/NoteStats/ReplyButton.tsx b/src/renderer/src/components/NoteStats/ReplyButton.tsx index a46c19b0..90f591e9 100644 --- a/src/renderer/src/components/NoteStats/ReplyButton.tsx +++ b/src/renderer/src/components/NoteStats/ReplyButton.tsx @@ -2,7 +2,7 @@ import { useNostr } from '@renderer/providers/NostrProvider' import { useNoteStats } from '@renderer/providers/NoteStatsProvider' import { MessageCircle } from 'lucide-react' import { Event } from 'nostr-tools' -import { useMemo } from 'react' +import { useMemo, useState } from 'react' import PostDialog from '../PostDialog' import { formatCount } from './utils' @@ -10,17 +10,22 @@ export default function ReplyButton({ event }: { event: Event }) { const { noteStatsMap } = useNoteStats() const { pubkey } = useNostr() const { replyCount } = useMemo(() => noteStatsMap.get(event.id) ?? {}, [noteStatsMap, event.id]) + const [open, setOpen] = useState(false) return ( - + <> e.stopPropagation()} + onClick={(e) => { + e.stopPropagation() + setOpen(true) + }} > {formatCount(replyCount)} - + + > ) } diff --git a/src/renderer/src/components/NoteStats/RepostButton.tsx b/src/renderer/src/components/NoteStats/RepostButton.tsx index 0fc94b29..7a0d6867 100644 --- a/src/renderer/src/components/NoteStats/RepostButton.tsx +++ b/src/renderer/src/components/NoteStats/RepostButton.tsx @@ -1,22 +1,19 @@ import { - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, - AlertDialogTrigger -} from '@renderer/components/ui/alert-dialog' + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger +} from '@renderer/components/ui/dropdown-menu' import { createRepostDraftEvent } from '@renderer/lib/draft-event' +import { getSharableEventId } from '@renderer/lib/event' import { cn } from '@renderer/lib/utils' import { useNostr } from '@renderer/providers/NostrProvider' import { useNoteStats } from '@renderer/providers/NoteStatsProvider' import client from '@renderer/services/client.service' -import { Loader, Repeat } from 'lucide-react' +import { Loader, PencilLine, Repeat } from 'lucide-react' import { Event } from 'nostr-tools' import { useEffect, useMemo, useState } from 'react' +import PostDialog from '../PostDialog' import { formatCount } from './utils' export default function RepostButton({ @@ -30,6 +27,7 @@ export default function RepostButton({ const { noteStatsMap, fetchNoteRepostCount, fetchNoteRepostedStatus, markNoteAsReposted } = useNoteStats() const [reposting, setReposting] = useState(false) + const [isPostDialogOpen, setIsPostDialogOpen] = useState(false) const { repostCount, hasReposted } = useMemo( () => noteStatsMap.get(event.id) ?? {}, [noteStatsMap, event.id] @@ -64,7 +62,7 @@ export default function RepostButton({ const targetRelayList = await client.fetchRelayList(event.pubkey) const repost = createRepostDraftEvent(event) - await publish(repost, targetRelayList.read.slice(0, 3)) + await publish(repost, targetRelayList.read.slice(0, 5)) markNoteAsReposted(event.id) } catch (error) { console.error('repost failed', error) @@ -76,33 +74,46 @@ export default function RepostButton({ } return ( - - - e.stopPropagation()} - disabled={!canRepost} - title="repost" + <> + + + e.stopPropagation()} + disabled={!canRepost} + title="repost" + > + {reposting ? : } + {formatCount(repostCount)} + + + { + e.stopPropagation() + e.preventDefault() + }} > - {reposting ? : } - {formatCount(repostCount)} - - - - - Repost Note - - Are you sure you want to repost this note? - - - - e.stopPropagation()}>Cancel - Repost - - - + + Repost + + { + e.stopPropagation() + setIsPostDialogOpen(true) + }} + > + Quote + + + + + > ) } diff --git a/src/renderer/src/components/PostButton/index.tsx b/src/renderer/src/components/PostButton/index.tsx index fdc678d0..b3d31bb7 100644 --- a/src/renderer/src/components/PostButton/index.tsx +++ b/src/renderer/src/components/PostButton/index.tsx @@ -1,14 +1,26 @@ import PostDialog from '@renderer/components/PostDialog' import { Button } from '@renderer/components/ui/button' import { PencilLine } from 'lucide-react' +import { useState } from 'react' export default function PostButton({ variant = 'titlebar' }: { variant?: 'titlebar' | 'sidebar' }) { + const [open, setOpen] = useState(false) + return ( - - + <> + { + e.stopPropagation() + setOpen(true) + }} + > {variant === 'sidebar' && Post} - + + > ) } diff --git a/src/renderer/src/components/PostDialog/index.tsx b/src/renderer/src/components/PostDialog/index.tsx index 9726a66b..92b27002 100644 --- a/src/renderer/src/components/PostDialog/index.tsx +++ b/src/renderer/src/components/PostDialog/index.tsx @@ -4,8 +4,7 @@ import { DialogContent, DialogDescription, DialogHeader, - DialogTitle, - DialogTrigger + DialogTitle } from '@renderer/components/ui/dialog' import { ScrollArea } from '@renderer/components/ui/scroll-area' import { Textarea } from '@renderer/components/ui/textarea' @@ -15,23 +14,26 @@ import { useNostr } from '@renderer/providers/NostrProvider' import client from '@renderer/services/client.service' import { LoaderCircle } from 'lucide-react' import { Event } from 'nostr-tools' -import { useState } from 'react' +import { Dispatch, useState } from 'react' import UserAvatar from '../UserAvatar' import Mentions from './Metions' import Preview from './Preview' import Uploader from './Uploader' export default function PostDialog({ - children, - parentEvent + defaultContent = '', + parentEvent, + open, + setOpen }: { - children: React.ReactNode + defaultContent?: string parentEvent?: Event + open: boolean + setOpen: Dispatch }) { const { toast } = useToast() const { publish, checkLogin } = useNostr() - const [open, setOpen] = useState(false) - const [content, setContent] = useState('') + const [content, setContent] = useState(defaultContent) const [posting, setPosting] = useState(false) const canPost = !!content && !posting @@ -88,7 +90,6 @@ export default function PostDialog({ return ( - {children} @@ -107,6 +108,7 @@ export default function PostDialog({ void highlight?: boolean }) { + const [isPostDialogOpen, setIsPostDialogOpen] = useState(false) + return ( {formatTimestamp(event.created_at)} - - reply - + reply + ) } diff --git a/src/renderer/src/services/client.service.ts b/src/renderer/src/services/client.service.ts index b65e5f1d..09818dec 100644 --- a/src/renderer/src/services/client.service.ts +++ b/src/renderer/src/services/client.service.ts @@ -40,8 +40,8 @@ class ClientService { this.eventBatchLoadFn.bind(this), { cache: false } ) - private profileCache = new LRUCache>({ max: 10000 }) - private profileDataloader = new DataLoader( + private profileCache = new LRUCache>({ max: 10000 }) + private profileDataloader = new DataLoader( (ids) => Promise.all(ids.map((id) => this._fetchProfileByBench32Id(id))), { cacheMap: this.profileCache } ) @@ -237,7 +237,15 @@ class ClientService { let event: NEvent | undefined if (filter.ids) { - event = await this.fetchEventById(relays, filter.ids[0]) + const eventId = filter.ids[0] + if (eventId !== id) { + const cache = this.eventCache.get(eventId) + if (cache) { + this.eventDataLoader.prime(id, cache) + return cache + } + } + event = await this.fetchEventById(relays, eventId) } else { event = await this.tryHarderToFetchEvent(relays, filter) } @@ -271,6 +279,14 @@ class ClientService { throw new Error('Invalid id') } + if (pubkey !== id) { + const cache = this.profileCache.get(pubkey) + if (cache) { + this.profileDataloader.prime(id, cache) + return cache + } + } + const profileFromBigRelays = this.fetchProfileFromBigRelaysDataloader.load(pubkey) if (profileFromBigRelays) { return profileFromBigRelays