From 76c6d3e48c0838d71445d7acb566abd67117a9ef Mon Sep 17 00:00:00 2001 From: Silberengel Date: Thu, 4 Dec 2025 06:17:03 +0100 Subject: [PATCH] fully-implement citations --- src/components/PostEditor/PostContent.tsx | 465 +++++++++++++++++++++- 1 file changed, 454 insertions(+), 11 deletions(-) diff --git a/src/components/PostEditor/PostContent.tsx b/src/components/PostEditor/PostContent.tsx index 57917f5..ed6aade 100644 --- a/src/components/PostEditor/PostContent.tsx +++ b/src/components/PostEditor/PostContent.tsx @@ -123,6 +123,34 @@ export default function PostContent({ const [isCitationExternal, setIsCitationExternal] = useState(false) const [isCitationHardcopy, setIsCitationHardcopy] = useState(false) const [isCitationPrompt, setIsCitationPrompt] = useState(false) + + // Citation metadata fields + // Internal Citation (30) + const [citationInternalCTag, setCitationInternalCTag] = useState('') + const [citationInternalRelayHint, setCitationInternalRelayHint] = useState('') + // External Citation (31) + const [citationExternalUrl, setCitationExternalUrl] = useState('') + const [citationExternalOpenTimestamp, setCitationExternalOpenTimestamp] = useState('') + // Hardcopy Citation (32) + const [citationHardcopyPageRange, setCitationHardcopyPageRange] = useState('') + const [citationHardcopyChapterTitle, setCitationHardcopyChapterTitle] = useState('') + const [citationHardcopyEditor, setCitationHardcopyEditor] = useState('') + const [citationHardcopyPublishedIn, setCitationHardcopyPublishedIn] = useState('') + const [citationHardcopyVolume, setCitationHardcopyVolume] = useState('') + const [citationHardcopyDoi, setCitationHardcopyDoi] = useState('') + // Prompt Citation (33) + const [citationPromptLlm, setCitationPromptLlm] = useState('') + // Shared citation fields + const [citationTitle, setCitationTitle] = useState('') + const [citationAuthor, setCitationAuthor] = useState('') + const [citationPublishedOn, setCitationPublishedOn] = useState('') + const [citationPublishedBy, setCitationPublishedBy] = useState('') + const [citationAccessedOn, setCitationAccessedOn] = useState('') + const [citationLocation, setCitationLocation] = useState('') + const [citationGeohash, setCitationGeohash] = useState('') + const [citationVersion, setCitationVersion] = useState('') + const [citationSummary, setCitationSummary] = useState('') + const [hasPrivateRelaysAvailable, setHasPrivateRelaysAvailable] = useState(false) const [showMediaKindDialog, setShowMediaKindDialog] = useState(false) const [pendingMediaUpload, setPendingMediaUpload] = useState<{ url: string; tags: string[][]; file: File } | null>(null) @@ -141,7 +169,12 @@ export default function PostContent({ (!isProtectedEvent || additionalRelayUrls.length > 0) && (!isHighlight || highlightData.sourceValue.trim() !== '') && // For articles, dTag is mandatory - (!isArticle || !!articleDTag.trim()) + (!isArticle || !!articleDTag.trim()) && + // For citations, required fields must be filled + (!isCitationInternal || !!citationInternalCTag.trim()) && + (!isCitationExternal || (!!citationExternalUrl.trim() && !!citationAccessedOn.trim())) && + (!isCitationHardcopy || !!citationAccessedOn.trim()) && + (!isCitationPrompt || (!!citationPromptLlm.trim() && !!citationAccessedOn.trim())) ) return result @@ -165,7 +198,15 @@ export default function PostContent({ isWikiArticle, isWikiArticleMarkdown, isPublicationContent, - articleDTag + articleDTag, + isCitationInternal, + citationInternalCTag, + isCitationExternal, + citationExternalUrl, + citationAccessedOn, + isCitationHardcopy, + isCitationPrompt, + citationPromptLlm ]) // Clear highlight data when initialHighlightData changes or is removed @@ -514,24 +555,55 @@ export default function PostContent({ // Citations if (isCitationInternal) { return createCitationInternalDraftEvent(cleanedText, { - cTag: '', - title: cleanedText.substring(0, 100) + cTag: citationInternalCTag.trim(), + relayHint: citationInternalRelayHint.trim() || undefined, + title: citationTitle.trim() || undefined, + author: citationAuthor.trim() || undefined, + publishedOn: citationPublishedOn.trim() || undefined, + accessedOn: citationAccessedOn.trim() || undefined, + location: citationLocation.trim() || undefined, + geohash: citationGeohash.trim() || undefined, + summary: citationSummary.trim() || undefined }) } else if (isCitationExternal) { return createCitationExternalDraftEvent(cleanedText, { - url: '', - accessedOn: new Date().toISOString(), - title: cleanedText.substring(0, 100) + url: citationExternalUrl.trim(), + accessedOn: citationAccessedOn.trim() || new Date().toISOString(), + title: citationTitle.trim() || undefined, + author: citationAuthor.trim() || undefined, + publishedOn: citationPublishedOn.trim() || undefined, + publishedBy: citationPublishedBy.trim() || undefined, + version: citationVersion.trim() || undefined, + location: citationLocation.trim() || undefined, + geohash: citationGeohash.trim() || undefined, + openTimestamp: citationExternalOpenTimestamp.trim() || undefined, + summary: citationSummary.trim() || undefined }) } else if (isCitationHardcopy) { return createCitationHardcopyDraftEvent(cleanedText, { - accessedOn: new Date().toISOString(), - title: cleanedText.substring(0, 100) + accessedOn: citationAccessedOn.trim() || new Date().toISOString(), + title: citationTitle.trim() || undefined, + author: citationAuthor.trim() || undefined, + pageRange: citationHardcopyPageRange.trim() || undefined, + chapterTitle: citationHardcopyChapterTitle.trim() || undefined, + editor: citationHardcopyEditor.trim() || undefined, + publishedOn: citationPublishedOn.trim() || undefined, + publishedBy: citationPublishedBy.trim() || undefined, + publishedIn: citationHardcopyPublishedIn.trim() || undefined, + volume: citationHardcopyVolume.trim() || undefined, + doi: citationHardcopyDoi.trim() || undefined, + version: citationVersion.trim() || undefined, + location: citationLocation.trim() || undefined, + geohash: citationGeohash.trim() || undefined, + summary: citationSummary.trim() || undefined }) } else if (isCitationPrompt) { return createCitationPromptDraftEvent(cleanedText, { - llm: '', - accessedOn: new Date().toISOString() + llm: citationPromptLlm.trim(), + accessedOn: citationAccessedOn.trim() || new Date().toISOString(), + version: citationVersion.trim() || undefined, + summary: citationSummary.trim() || undefined, + url: citationExternalUrl.trim() || undefined }) } @@ -1363,6 +1435,11 @@ export default function PostContent({ setIsWikiArticle(false) setIsWikiArticleMarkdown(false) setIsPublicationContent(false) + + // Set default accessedOn if not already set + if (!citationAccessedOn && (type === 'external' || type === 'hardcopy' || type === 'prompt')) { + setCitationAccessedOn(new Date().toISOString().split('T')[0]) // ISO date format YYYY-MM-DD + } } const handleClear = () => { @@ -1390,6 +1467,27 @@ export default function PostContent({ setIsCitationExternal(false) setIsCitationHardcopy(false) setIsCitationPrompt(false) + // Clear citation fields + setCitationInternalCTag('') + setCitationInternalRelayHint('') + setCitationExternalUrl('') + setCitationExternalOpenTimestamp('') + setCitationHardcopyPageRange('') + setCitationHardcopyChapterTitle('') + setCitationHardcopyEditor('') + setCitationHardcopyPublishedIn('') + setCitationHardcopyVolume('') + setCitationHardcopyDoi('') + setCitationTitle('') + setCitationAuthor('') + setCitationPublishedOn('') + setCitationPublishedBy('') + setCitationAccessedOn('') + setCitationLocation('') + setCitationGeohash('') + setCitationVersion('') + setCitationSummary('') + setCitationPromptLlm('') setPollCreateData({ isMultipleChoice: false, options: ['', ''], @@ -1541,6 +1639,351 @@ export default function PostContent({ )} + {/* Citation metadata fields */} + {(isCitationInternal || isCitationExternal || isCitationHardcopy || isCitationPrompt) && ( +
+
+ {isCitationInternal && t('Internal Citation Settings')} + {isCitationExternal && t('External Citation Settings')} + {isCitationHardcopy && t('Hardcopy Citation Settings')} + {isCitationPrompt && t('Prompt Citation Settings')} +
+ + {/* Prompt Citation specific fields - shown first if prompt */} + {isCitationPrompt && ( + <> +
+ + setCitationPromptLlm(e.target.value)} + placeholder={t('e.g., GPT-4, Claude, etc. (required)')} + className={!citationPromptLlm.trim() ? 'border-destructive' : ''} + /> +

+ {t('Name of the language model used')} +

+
+ +
+ + setCitationExternalUrl(e.target.value)} + placeholder={t('Website where LLM was accessed (optional)')} + /> +
+ +
+ + setCitationVersion(e.target.value)} + placeholder={t('Version number (optional)')} + /> +
+ + )} + + {/* Shared fields - not shown for prompt citations */} + {!isCitationPrompt && ( + <> +
+ + setCitationTitle(e.target.value)} + placeholder={t('Citation title (optional)')} + /> +
+ +
+ + setCitationAuthor(e.target.value)} + placeholder={t('Author name (optional)')} + /> +
+ + )} + + {/* Internal Citation specific fields */} + {isCitationInternal && ( + <> +
+ + setCitationInternalCTag(e.target.value)} + placeholder={t('kind:pubkey:hex format (required)')} + className={!citationInternalCTag.trim() ? 'border-destructive' : ''} + /> +

+ {t('Reference to the cited Nostr event in kind:pubkey:hex format')} +

+
+ +
+ + setCitationInternalRelayHint(e.target.value)} + placeholder={t('Relay URL (optional)')} + /> +
+ + )} + + {/* External Citation specific fields */} + {isCitationExternal && ( + <> +
+ + setCitationExternalUrl(e.target.value)} + placeholder={t('https://example.com (required)')} + className={!citationExternalUrl.trim() ? 'border-destructive' : ''} + /> +
+ +
+ + setCitationExternalOpenTimestamp(e.target.value)} + placeholder={t('e tag of kind 1040 event (optional)')} + /> +
+ +
+ + setCitationPublishedBy(e.target.value)} + placeholder={t('Publisher name (optional)')} + /> +
+ +
+ + setCitationVersion(e.target.value)} + placeholder={t('Version number (optional)')} + /> +
+ + )} + + {/* Hardcopy Citation specific fields */} + {isCitationHardcopy && ( + <> +
+ + setCitationHardcopyPageRange(e.target.value)} + placeholder={t('e.g., 123-145 (optional)')} + /> +
+ +
+ + setCitationHardcopyChapterTitle(e.target.value)} + placeholder={t('Chapter title (optional)')} + /> +
+ +
+ + setCitationHardcopyEditor(e.target.value)} + placeholder={t('Editor name (optional)')} + /> +
+ +
+ + setCitationHardcopyPublishedIn(e.target.value)} + placeholder={t('Journal/Publication name (optional)')} + /> +
+ +
+ + setCitationHardcopyVolume(e.target.value)} + placeholder={t('Volume number (optional)')} + /> +
+ +
+ + setCitationHardcopyDoi(e.target.value)} + placeholder={t('Digital Object Identifier (optional)')} + /> +
+ +
+ + setCitationPublishedBy(e.target.value)} + placeholder={t('Publisher name (optional)')} + /> +
+ +
+ + setCitationVersion(e.target.value)} + placeholder={t('Version number (optional)')} + /> +
+ + )} + + {/* Shared date fields - not shown for prompt citations */} + {!isCitationPrompt && ( +
+ + setCitationPublishedOn(e.target.value)} + /> +
+ )} + +
+ + setCitationAccessedOn(e.target.value)} + className={(isCitationExternal || isCitationHardcopy || isCitationPrompt) && !citationAccessedOn.trim() ? 'border-destructive' : ''} + /> +
+ + {/* Summary field - different label for prompt citations */} +
+ +