Add long-form article reader (Phase 1 #1, NIP-23)

- fetchArticle(naddr): fetches kind 30023 by d-tag + author pubkey
- fetchAuthorArticles(pubkey): for future profile articles tab
- ArticleView: cover image, title, italic summary, author row (avatar +
  name + date), tag pills, full markdown body (DOMPurify sanitized),
  zap author button in header and footer, copy nostr: link, njump.me
  fallback on fetch error
- prose-article CSS: reader-optimised typography (15px base, 1.8 line
  height, h2 border, styled blockquote/code/pre/links/images)
- NoteContent: naddr1 clicks for kind 30023 now open ArticleView
  instead of falling through to njump.me
- openArticle(naddr) added to UI store with previousView tracking

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Jure
2026-03-10 20:42:13 +01:00
parent f0cb9a97bb
commit ab7af96c45
9 changed files with 318 additions and 6 deletions

View File

@@ -134,7 +134,7 @@ function tryHandleUrlInternally(url: string): boolean {
function tryOpenNostrEntity(raw: string): boolean {
try {
const decoded = nip19.decode(raw);
const { openProfile } = useUIStore.getState();
const { openProfile, openArticle } = useUIStore.getState();
if (decoded.type === "npub") {
openProfile(decoded.data as string);
return true;
@@ -143,7 +143,14 @@ function tryOpenNostrEntity(raw: string): boolean {
openProfile((decoded.data as { pubkey: string }).pubkey);
return true;
}
// note / nevent / naddr — no internal reader yet, fall through to njump.me
if (decoded.type === "naddr") {
const { kind } = decoded.data as { kind: number; pubkey: string; identifier: string };
if (kind === 30023) {
openArticle(raw);
return true;
}
}
// note / nevent / other naddr kinds — fall through to njump.me
} catch { /* invalid entity */ }
return false;
}