diff --git a/package-lock.json b/package-lock.json index ede0779..cbec342 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,8 +10,8 @@ "dependencies": { "@getalby/lightning-tools": "5.0.3", "@mdxeditor/editor": "^3.20.0", - "@nostr-dev-kit/ndk": "2.11.0", - "@nostr-dev-kit/ndk-cache-dexie": "2.5.9", + "@nostr-dev-kit/ndk": "2.11.2", + "@nostr-dev-kit/ndk-cache-dexie": "2.5.11", "@reduxjs/toolkit": "2.2.6", "@types/react-helmet": "^6.1.11", "axios": "^1.7.9", @@ -2134,9 +2134,9 @@ } }, "node_modules/@nostr-dev-kit/ndk": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/@nostr-dev-kit/ndk/-/ndk-2.11.0.tgz", - "integrity": "sha512-FKIMtcVsVcquzrC+yir9lOXHCIHmQ3IKEVCMohqEB7N96HjP2qrI9s5utbjI3lkavFNF5tXg1Gp9ODEo7XCfLA==", + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@nostr-dev-kit/ndk/-/ndk-2.11.2.tgz", + "integrity": "sha512-DNrodIBC0j2MqEUQ5Mqaa671iZiRiKluu0c/wLkX7PCva07KSPyvcuyGp5fhk+/EZBurwZccMaML0syH0Qu8kQ==", "license": "MIT", "dependencies": { "@noble/curves": "^1.6.0", @@ -2156,12 +2156,12 @@ } }, "node_modules/@nostr-dev-kit/ndk-cache-dexie": { - "version": "2.5.9", - "resolved": "https://registry.npmjs.org/@nostr-dev-kit/ndk-cache-dexie/-/ndk-cache-dexie-2.5.9.tgz", - "integrity": "sha512-SZ5FjON0QPekiC7oW9Hy3JQxG0Oxxtud9LBa1q/A49JV/Qppv1x37nFHxi0XLxEbDgFTNYbaN27Zjfp2NPem2g==", + "version": "2.5.11", + "resolved": "https://registry.npmjs.org/@nostr-dev-kit/ndk-cache-dexie/-/ndk-cache-dexie-2.5.11.tgz", + "integrity": "sha512-lhoKcjwxlNB2rrnZ2zDAGJeh5k7x1f51oAwUnlDAuPvNEe4q/2XynxnI3uTe7rBg9+pq085esOQK7pg75E+BgQ==", "license": "MIT", "dependencies": { - "@nostr-dev-kit/ndk": "2.11.0", + "@nostr-dev-kit/ndk": "2.11.2", "debug": "^4.3.7", "dexie": "^4.0.8", "nostr-tools": "^2.4.0", diff --git a/package.json b/package.json index 8901e62..64b90f8 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,8 @@ "dependencies": { "@getalby/lightning-tools": "5.0.3", "@mdxeditor/editor": "^3.20.0", - "@nostr-dev-kit/ndk": "2.11.0", - "@nostr-dev-kit/ndk-cache-dexie": "2.5.9", + "@nostr-dev-kit/ndk": "2.11.2", + "@nostr-dev-kit/ndk-cache-dexie": "2.5.11", "@reduxjs/toolkit": "2.2.6", "@types/react-helmet": "^6.1.11", "axios": "^1.7.9", diff --git a/src/components/Notes/Note.tsx b/src/components/Notes/Note.tsx index 8f37947..9e0652c 100644 --- a/src/components/Notes/Note.tsx +++ b/src/components/Notes/Note.tsx @@ -18,7 +18,7 @@ import { import { useComments } from 'hooks/useComments' import { nip19 } from 'nostr-tools' import { useState } from 'react' -import { Link, useSubmit } from 'react-router-dom' +import { Link, useNavigation, useSubmit } from 'react-router-dom' import { appRoutes, getProfilePageRoute } from 'routes' import { FeedPostsFilter, NSFWFilter, UserProfile } from 'types' import { DEFAULT_FILTER_OPTIONS, hexToNpub, log, LogType } from 'utils' @@ -33,6 +33,7 @@ interface NoteProps { export const Note = ({ ndkEvent }: NoteProps) => { const { ndk } = useNDKContext() const submit = useSubmit() + const navigation = useNavigation() const userState = useAppSelector((state) => state.user) const userPubkey = userState.user?.pubkey as string | undefined const [eventProfile, setEventProfile] = useState() @@ -167,6 +168,8 @@ export const Note = ({ ndkEvent }: NoteProps) => { ) : null const handleRepost = async (confirm: boolean) => { + if (navigation.state !== 'idle') return + setShowRepostPopup(false) // Cancel if not confirmed diff --git a/src/components/Notes/NoteSubmit.tsx b/src/components/Notes/NoteSubmit.tsx index dd0a028..ef086bf 100644 --- a/src/components/Notes/NoteSubmit.tsx +++ b/src/components/Notes/NoteSubmit.tsx @@ -68,7 +68,8 @@ export const NoteSubmit = ({ submit(JSON.stringify(formSubmit), { method: 'post', - encType: 'application/json' + encType: 'application/json', + action: appRoutes.feed }) typeof handleClose === 'function' && handleClose() @@ -152,7 +153,7 @@ export const NoteSubmit = ({ style={{ padding: '5px 20px', borderRadius: '8px' }} disabled={navigation.state !== 'idle'} > - {navigation.state === 'idle' ? 'Post' : 'Posting...'} + {navigation.state === 'submitting' ? 'Posting...' : 'Post'} diff --git a/src/components/comment/CommentsPopup.tsx b/src/components/comment/CommentsPopup.tsx index 8c16919..56698f0 100644 --- a/src/components/comment/CommentsPopup.tsx +++ b/src/components/comment/CommentsPopup.tsx @@ -1,5 +1,11 @@ import { formatDate } from 'date-fns' -import { useBodyScrollDisable, useNDKContext, useReplies } from 'hooks' +import { + useAppSelector, + useBodyScrollDisable, + useDidMount, + useNDKContext, + useReplies +} from 'hooks' import { nip19 } from 'nostr-tools' import { ChangeEvent, useCallback, useEffect, useMemo, useState } from 'react' import { @@ -7,7 +13,9 @@ import { useLoaderData, useLocation, useNavigate, - useParams + useNavigation, + useParams, + useSubmit } from 'react-router-dom' import { appRoutes, @@ -24,6 +32,14 @@ import { Comment } from './Comment' import { useComments } from 'hooks/useComments' import { CommentContent } from './CommentContent' import { Dots } from 'components/Spinner' +import { + NDKEvent, + NDKFilter, + NDKKind, + NDKSubscriptionCacheUsage +} from '@nostr-dev-kit/ndk' +import { NoteQuoteRepostPopup } from 'components/Notes/NoteQuoteRepostPopup' +import { NoteRepostPopup } from 'components/Notes/NoteRepostPopup' interface CommentsPopupProps { title: string @@ -116,6 +132,80 @@ export const CommentsPopup = ({ title }: CommentsPopupProps) => { setIsSubmitting(false) } + const submit = useSubmit() + const navigation = useNavigation() + const [repostEvents, setRepostEvents] = useState([]) + const [quoteRepostEvents, setQuoteRepostEvents] = useState([]) + const [hasReposted, setHasReposted] = useState(false) + const [hasQuoted, setHasQuoted] = useState(false) + const [showRepostPopup, setShowRepostPopup] = useState(false) + const [showQuoteRepostPopup, setShowQuoteRepostPopup] = useState(false) + const userState = useAppSelector((state) => state.user) + const userPubkey = userState.user?.pubkey as string | undefined + useDidMount(() => { + const repostFilter: NDKFilter = { + kinds: [NDKKind.Repost], + '#e': [event.id] + } + const quoteFilter: NDKFilter = { + kinds: [NDKKind.Text], + '#q': [event.id] + } + ndk + .fetchEvents([repostFilter, quoteFilter], { + closeOnEose: true, + cacheUsage: NDKSubscriptionCacheUsage.PARALLEL + }) + .then((ndkEventSet) => { + const ndkEvents = Array.from(ndkEventSet) + + if (ndkEventSet.size) { + const quoteRepostEvents = ndkEvents.filter( + (n) => n.kind === NDKKind.Text + ) + userPubkey && + setHasQuoted( + quoteRepostEvents.some((qr) => qr.pubkey === userPubkey) + ) + setQuoteRepostEvents(quoteRepostEvents) + + const repostEvents = ndkEvents.filter( + (n) => n.kind === NDKKind.Repost + ) + userPubkey && + setHasReposted(repostEvents.some((qr) => qr.pubkey === userPubkey)) + setRepostEvents(repostEvents) + } + }) + .finally(() => { + setIsLoading(false) + }) + }) + + const handleRepost = async (confirm: boolean) => { + if (navigation.state !== 'idle') return + + setShowRepostPopup(false) + + // Cancel if not confirmed + if (!confirm) return + + const repostNdkEvent = await event.repost(false) + const rawEvent = repostNdkEvent.rawEvent() + submit( + JSON.stringify({ + intent: 'repost', + note1: event.encode(), + data: rawEvent + }), + { + method: 'post', + encType: 'application/json', + action: appRoutes.feed + } + ) + } + return (
@@ -231,25 +321,80 @@ export const CommentsPopup = ({ title }: CommentsPopupProps) => {
- {/*
- - - -

0

-
-
-
-
*/} + {event.kind === NDKKind.Text && ( + <> + {/* Quote Repost, Kind 1 */} +
setShowQuoteRepostPopup(true) + } + > + + + +

+ {isLoading ? : quoteRepostEvents.length} +

+
+
+
+
+ {showQuoteRepostPopup && ( + setShowQuoteRepostPopup(false)} + /> + )} + + {/* Repost, Kind 6 */} +
setShowRepostPopup(true) + } + > + + + +

+ {isLoading ? : repostEvents.length} +

+
+
+
+
+ {showRepostPopup && ( + setShowRepostPopup(false)} + /> + )} + + )} {typeof profile?.lud16 !== 'undefined' && profile.lud16 !== '' && }