diff --git a/src/components/Note/LongFormArticlePreview.tsx b/src/components/Note/LongFormArticlePreview.tsx
index c957af9..6bc997b 100644
--- a/src/components/Note/LongFormArticlePreview.tsx
+++ b/src/components/Note/LongFormArticlePreview.tsx
@@ -6,7 +6,6 @@ import { useScreenSize } from '@/providers/ScreenSizeProvider'
import { Event, kinds } from 'nostr-tools'
import { useMemo } from 'react'
import Image from '../Image'
-import ArticleExportMenu from '../ArticleExportMenu/ArticleExportMenu'
export default function LongFormArticlePreview({
event,
@@ -66,9 +65,6 @@ export default function LongFormArticlePreview({
{titleComponent}
{summaryComponent}
{tagsComponent}
-
@@ -93,9 +89,6 @@ export default function LongFormArticlePreview({
{titleComponent}
{summaryComponent}
{tagsComponent}
-
diff --git a/src/components/Note/PublicationCard.tsx b/src/components/Note/PublicationCard.tsx
index 2b95e88..522d1f2 100644
--- a/src/components/Note/PublicationCard.tsx
+++ b/src/components/Note/PublicationCard.tsx
@@ -4,11 +4,8 @@ import { useSecondaryPage } from '@/PageManager'
import { useContentPolicy } from '@/providers/ContentPolicyProvider'
import { useScreenSize } from '@/providers/ScreenSizeProvider'
import { Event, kinds } from 'nostr-tools'
-import { nip19 } from 'nostr-tools'
import { useMemo } from 'react'
-import { BookOpen } from 'lucide-react'
import Image from '../Image'
-import ArticleExportMenu from '../ArticleExportMenu/ArticleExportMenu'
export default function PublicationCard({
event,
@@ -22,39 +19,11 @@ export default function PublicationCard({
const { autoLoadMedia } = useContentPolicy()
const metadata = useMemo(() => getLongFormArticleMetadataFromEvent(event), [event])
- // Generate naddr for Alexandria URL
- const naddr = useMemo(() => {
- try {
- const dTag = event.tags.find(tag => tag[0] === 'd')?.[1] || ''
- const relays = event.tags
- .filter(tag => tag[0] === 'relay')
- .map(tag => tag[1])
- .filter(Boolean)
-
- return nip19.naddrEncode({
- kind: event.kind,
- pubkey: event.pubkey,
- identifier: dTag,
- relays: relays.length > 0 ? relays : undefined
- })
- } catch (error) {
- console.error('Error generating naddr:', error)
- return ''
- }
- }, [event])
-
const handleCardClick = (e: React.MouseEvent) => {
e.stopPropagation()
push(toNote(event.id))
}
- const handleAlexandriaClick = (e: React.MouseEvent) => {
- e.stopPropagation()
- if (naddr) {
- window.open(`https://next-alexandria.gitcitadel.eu/publication/naddr/${naddr}`, '_blank', 'noopener,noreferrer')
- }
- }
-
const titleComponent = {metadata.title}
const tagsComponent = metadata.tags.length > 0 && (
@@ -78,16 +47,6 @@ export default function PublicationCard({
{metadata.summary}
)
- const alexandriaButton = naddr && (
-
- )
-
if (isSmallScreen) {
return (
@@ -106,10 +65,6 @@ export default function PublicationCard({
{titleComponent}
{summaryComponent}
{tagsComponent}
-
@@ -134,10 +89,6 @@ export default function PublicationCard({
{titleComponent}
{summaryComponent}
{tagsComponent}
-
diff --git a/src/components/Note/WikiCard.tsx b/src/components/Note/WikiCard.tsx
index 2c17c26..1f6fd96 100644
--- a/src/components/Note/WikiCard.tsx
+++ b/src/components/Note/WikiCard.tsx
@@ -4,11 +4,8 @@ import { useSecondaryPage } from '@/PageManager'
import { useContentPolicy } from '@/providers/ContentPolicyProvider'
import { useScreenSize } from '@/providers/ScreenSizeProvider'
import { Event, kinds } from 'nostr-tools'
-import { nip19 } from 'nostr-tools'
import { useMemo } from 'react'
-import { BookOpen, Globe } from 'lucide-react'
import Image from '../Image'
-import ArticleExportMenu from '../ArticleExportMenu/ArticleExportMenu'
export default function WikiCard({
event,
@@ -22,50 +19,11 @@ export default function WikiCard({
const { autoLoadMedia } = useContentPolicy()
const metadata = useMemo(() => getLongFormArticleMetadataFromEvent(event), [event])
- // Extract d-tag for Wikistr URL
- const dTag = useMemo(() => {
- return event.tags.find(tag => tag[0] === 'd')?.[1] || ''
- }, [event])
-
- // Generate naddr for Alexandria URL
- const naddr = useMemo(() => {
- try {
- const relays = event.tags
- .filter(tag => tag[0] === 'relay')
- .map(tag => tag[1])
- .filter(Boolean)
-
- return nip19.naddrEncode({
- kind: event.kind,
- pubkey: event.pubkey,
- identifier: dTag,
- relays: relays.length > 0 ? relays : undefined
- })
- } catch (error) {
- console.error('Error generating naddr:', error)
- return ''
- }
- }, [event, dTag])
-
const handleCardClick = (e: React.MouseEvent) => {
e.stopPropagation()
push(toNote(event.id))
}
- const handleWikistrClick = (e: React.MouseEvent) => {
- e.stopPropagation()
- if (dTag) {
- window.open(`https://wikistr.imwald.eu/${dTag}*${event.pubkey}`, '_blank', 'noopener,noreferrer')
- }
- }
-
- const handleAlexandriaClick = (e: React.MouseEvent) => {
- e.stopPropagation()
- if (naddr) {
- window.open(`https://next-alexandria.gitcitadel.eu/publication/naddr/${naddr}`, '_blank', 'noopener,noreferrer')
- }
- }
-
const titleComponent = {metadata.title}
const tagsComponent = metadata.tags.length > 0 && (
@@ -88,30 +46,6 @@ export default function WikiCard({
const summaryComponent = metadata.summary && (
{metadata.summary}
)
-
- const buttons = (
-
-
- {dTag && (
-
- )}
- {naddr && (
-
- )}
-
- )
if (isSmallScreen) {
return (
@@ -131,9 +65,6 @@ export default function WikiCard({
{titleComponent}
{summaryComponent}
{tagsComponent}
-
- {buttons}
-
@@ -158,9 +89,6 @@ export default function WikiCard({
{titleComponent}
{summaryComponent}
{tagsComponent}
-
- {buttons}
-
diff --git a/src/components/NoteOptions/useMenuActions.tsx b/src/components/NoteOptions/useMenuActions.tsx
index 436080d..c45f7e3 100644
--- a/src/components/NoteOptions/useMenuActions.tsx
+++ b/src/components/NoteOptions/useMenuActions.tsx
@@ -1,5 +1,6 @@
import { ExtendedKind } from '@/constants'
import { getNoteBech32Id, isProtectedEvent, getRootEventHexId } from '@/lib/event'
+import { getLongFormArticleMetadataFromEvent } from '@/lib/event-metadata'
import { toNjump } from '@/lib/link'
import logger from '@/lib/logger'
import { pubkeyToNpub } from '@/lib/pubkey'
@@ -10,8 +11,9 @@ import { useMuteList } from '@/providers/MuteListProvider'
import { useNostr } from '@/providers/NostrProvider'
import { BIG_RELAY_URLS, FAST_READ_RELAY_URLS, FAST_WRITE_RELAY_URLS } from '@/constants'
import client from '@/services/client.service'
-import { Bell, BellOff, Code, Copy, Link, SatelliteDish, Trash2, TriangleAlert, Pin } from 'lucide-react'
-import { Event } from 'nostr-tools'
+import { Bell, BellOff, Code, Copy, Link, SatelliteDish, Trash2, TriangleAlert, Pin, FileDown, Globe, BookOpen } from 'lucide-react'
+import { Event, kinds } from 'nostr-tools'
+import { nip19 } from 'nostr-tools'
import { useMemo, useState, useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import { toast } from 'sonner'
@@ -303,7 +305,120 @@ export function useMenuActions({
return items
}, [pubkey, relayUrls, relaySets])
+ // Check if this is an article-type event
+ const isArticleType = useMemo(() => {
+ return event.kind === kinds.LongFormArticle ||
+ event.kind === ExtendedKind.WIKI_ARTICLE ||
+ event.kind === ExtendedKind.WIKI_ARTICLE_MARKDOWN ||
+ event.kind === ExtendedKind.PUBLICATION ||
+ event.kind === ExtendedKind.PUBLICATION_CONTENT
+ }, [event.kind])
+
+ // Get article metadata for export
+ const articleMetadata = useMemo(() => {
+ if (!isArticleType) return null
+ return getLongFormArticleMetadataFromEvent(event)
+ }, [isArticleType, event])
+
+ // Extract d-tag for Wikistr URL
+ const dTag = useMemo(() => {
+ if (!isArticleType) return ''
+ return event.tags.find(tag => tag[0] === 'd')?.[1] || ''
+ }, [isArticleType, event])
+
+ // Generate naddr for Alexandria URL
+ const naddr = useMemo(() => {
+ if (!isArticleType || !dTag) return ''
+ try {
+ const relays = event.tags
+ .filter(tag => tag[0] === 'relay')
+ .map(tag => tag[1])
+ .filter(Boolean)
+
+ return nip19.naddrEncode({
+ kind: event.kind,
+ pubkey: event.pubkey,
+ identifier: dTag,
+ relays: relays.length > 0 ? relays : undefined
+ })
+ } catch (error) {
+ console.error('Error generating naddr:', error)
+ return ''
+ }
+ }, [isArticleType, event, dTag])
+
const menuActions: MenuAction[] = useMemo(() => {
+ // Export functions for articles
+ const exportAsMarkdown = () => {
+ if (!isArticleType) return
+
+ try {
+ const title = articleMetadata?.title || 'Article'
+ const content = event.content
+ const filename = `${title}.md`
+
+ const blob = new Blob([content], { type: 'text/markdown' })
+ const url = URL.createObjectURL(blob)
+ const a = document.createElement('a')
+ a.href = url
+ a.download = filename
+ document.body.appendChild(a)
+ a.click()
+ document.body.removeChild(a)
+ URL.revokeObjectURL(url)
+
+ logger.info('[NoteOptions] Exported article as Markdown')
+ toast.success(t('Article exported as Markdown'))
+ } catch (error) {
+ logger.error('[NoteOptions] Error exporting article:', error)
+ toast.error(t('Failed to export article'))
+ }
+ }
+
+ const exportAsAsciidoc = () => {
+ if (!isArticleType) return
+
+ try {
+ const title = articleMetadata?.title || 'Article'
+ const content = event.content
+ const filename = `${title}.adoc`
+
+ const blob = new Blob([content], { type: 'text/plain' })
+ const url = URL.createObjectURL(blob)
+ const a = document.createElement('a')
+ a.href = url
+ a.download = filename
+ document.body.appendChild(a)
+ a.click()
+ document.body.removeChild(a)
+ URL.revokeObjectURL(url)
+
+ logger.info('[NoteOptions] Exported article as AsciiDoc')
+ toast.success(t('Article exported as AsciiDoc'))
+ } catch (error) {
+ logger.error('[NoteOptions] Error exporting article:', error)
+ toast.error(t('Failed to export article'))
+ }
+ }
+
+ // View on external sites functions
+ const handleViewOnWikistr = () => {
+ if (!dTag) return
+ closeDrawer()
+ window.open(`https://wikistr.imwald.eu/${dTag}*${event.pubkey}`, '_blank', 'noopener,noreferrer')
+ }
+
+ const handleViewOnAlexandria = () => {
+ if (!naddr) return
+ closeDrawer()
+ window.open(`https://next-alexandria.gitcitadel.eu/publication/naddr/${naddr}`, '_blank', 'noopener,noreferrer')
+ }
+
+ const handleViewOnDecentNewsroom = () => {
+ if (!dTag) return
+ closeDrawer()
+ window.open(`https://decentnewsroom.com/article/d/${dTag}`, '_blank', 'noopener,noreferrer')
+ }
const actions: MenuAction[] = [
{
icon: Copy,
@@ -340,6 +455,76 @@ export function useMenuActions({
}
]
+ // Add export options for article-type events
+ if (isArticleType) {
+ const isMarkdownFormat = event.kind === kinds.LongFormArticle || event.kind === ExtendedKind.WIKI_ARTICLE_MARKDOWN
+ const isAsciidocFormat = event.kind === ExtendedKind.WIKI_ARTICLE || event.kind === ExtendedKind.PUBLICATION || event.kind === ExtendedKind.PUBLICATION_CONTENT
+
+ if (isMarkdownFormat) {
+ actions.push({
+ icon: FileDown,
+ label: t('Export as Markdown'),
+ onClick: () => {
+ closeDrawer()
+ exportAsMarkdown()
+ },
+ separator: true
+ })
+ }
+
+ if (isAsciidocFormat) {
+ actions.push({
+ icon: FileDown,
+ label: t('Export as AsciiDoc'),
+ onClick: () => {
+ closeDrawer()
+ exportAsAsciidoc()
+ },
+ separator: true
+ })
+ }
+
+ // Add view options based on event kind
+ if (event.kind === kinds.LongFormArticle) {
+ // For LongFormArticle (30023): Alexandria and DecentNewsroom
+ if (naddr) {
+ actions.push({
+ icon: BookOpen,
+ label: t('View on Alexandria'),
+ onClick: handleViewOnAlexandria
+ })
+ }
+ if (dTag) {
+ actions.push({
+ icon: Globe,
+ label: t('View on DecentNewsroom'),
+ onClick: handleViewOnDecentNewsroom
+ })
+ }
+ } else if (
+ event.kind === ExtendedKind.PUBLICATION_CONTENT ||
+ event.kind === ExtendedKind.PUBLICATION ||
+ event.kind === ExtendedKind.WIKI_ARTICLE ||
+ event.kind === ExtendedKind.WIKI_ARTICLE_MARKDOWN
+ ) {
+ // For 30041, 30040, 30818, 30817: Alexandria and Wikistr
+ if (naddr) {
+ actions.push({
+ icon: BookOpen,
+ label: t('View on Alexandria'),
+ onClick: handleViewOnAlexandria
+ })
+ }
+ if (dTag) {
+ actions.push({
+ icon: Globe,
+ label: t('View on Wikistr'),
+ onClick: handleViewOnWikistr
+ })
+ }
+ }
+ }
+
const isProtected = isProtectedEvent(event)
const isDiscussion = event.kind === ExtendedKind.DISCUSSION
if ((!isProtected || event.pubkey === pubkey) && !isDiscussion && !isReplyToDiscussion) {
@@ -446,7 +631,11 @@ export function useMenuActions({
unmutePubkey,
attemptDelete,
isPinned,
- handlePinNote
+ handlePinNote,
+ isArticleType,
+ articleMetadata,
+ dTag,
+ naddr
])
return menuActions
diff --git a/src/components/ZapDialog/index.tsx b/src/components/ZapDialog/index.tsx
index c57887e..624d0b9 100644
--- a/src/components/ZapDialog/index.tsx
+++ b/src/components/ZapDialog/index.tsx
@@ -93,7 +93,7 @@ export default function ZapDialog({
-
+ {t('Send a Lightning payment to this user')}
+ {t('Send a Lightning payment to this user')}