diff --git a/packages/app/config/default.json b/packages/app/config/default.json index a9a0b496..841ad304 100644 --- a/packages/app/config/default.json +++ b/packages/app/config/default.json @@ -13,5 +13,7 @@ "subscriptions": true, "deck": true, "zapPool": true - } + }, + "eventLinkPrefix": "nevent", + "profileLinkPrefix": "nprofile" } diff --git a/packages/app/config/iris.json b/packages/app/config/iris.json index cc5f1e0b..d063c3f0 100644 --- a/packages/app/config/iris.json +++ b/packages/app/config/iris.json @@ -13,5 +13,7 @@ "subscriptions": false, "deck": true, "zapPool": true - } + }, + "eventLinkPrefix": "note", + "profileLinkPrefix": "npub" } diff --git a/packages/app/custom.d.ts b/packages/app/custom.d.ts index d2df8418..c0b207a4 100644 --- a/packages/app/custom.d.ts +++ b/packages/app/custom.d.ts @@ -52,4 +52,6 @@ declare const CONFIG: { deck: boolean; zapPool: boolean; }; + eventLinkPrefix: NostrPrefix; + profileLinkPrefix: NostrPrefix; }; diff --git a/packages/app/src/Element/Chat/WriteMessage.tsx b/packages/app/src/Element/Chat/WriteMessage.tsx index 1e1c1bbb..3a03812b 100644 --- a/packages/app/src/Element/Chat/WriteMessage.tsx +++ b/packages/app/src/Element/Chat/WriteMessage.tsx @@ -1,4 +1,4 @@ -import { NostrPrefix, NostrEvent, NostrLink } from "@snort/system"; +import { NostrEvent, NostrLink } from "@snort/system"; import useEventPublisher from "Hooks/useEventPublisher"; import Icon from "Icons/Icon"; import Spinner from "Icons/Spinner"; @@ -36,7 +36,7 @@ export default function WriteMessage({ chat }: { chat: Chat }) { if (file) { const rx = await uploader.upload(file, file.name); if (rx.header) { - const link = `nostr:${new NostrLink(NostrPrefix.Event, rx.header.id, rx.header.kind).encode()}`; + const link = `nostr:${new NostrLink(CONFIG.eventLinkPrefix, rx.header.id, rx.header.kind).encode()}`; setMsg(`${msg ? `${msg}\n` : ""}${link}`); setOtherEvents([...otherEvents, rx.header]); } else if (rx.url) { diff --git a/packages/app/src/Element/Embed/NostrLink.tsx b/packages/app/src/Element/Embed/NostrLink.tsx index e99306c7..994a7748 100644 --- a/packages/app/src/Element/Embed/NostrLink.tsx +++ b/packages/app/src/Element/Embed/NostrLink.tsx @@ -13,7 +13,7 @@ export default function NostrLink({ link, depth }: { link: string; depth?: numbe if ((depth ?? 0) > 0) { const evLink = nav.encode(); return ( - e.stopPropagation()} state={{ from: location.pathname }}> + e.stopPropagation()} state={{ from: location.pathname }}> #{evLink.substring(0, 12)} ); diff --git a/packages/app/src/Element/Embed/ZapstrEmbed.tsx b/packages/app/src/Element/Embed/ZapstrEmbed.tsx index 0ca0f7cc..04ccc4ee 100644 --- a/packages/app/src/Element/Embed/ZapstrEmbed.tsx +++ b/packages/app/src/Element/Embed/ZapstrEmbed.tsx @@ -12,7 +12,7 @@ export default function ZapstrEmbed({ ev }: { ev: NostrEvent }) { const subject = ev.tags.find(a => a[0] === "subject"); const refPersons = ev.tags.filter(a => a[0] === "p"); - const link = NostrLink.fromEvent(ev).encode(); + const link = NostrLink.fromEvent(ev).encode(CONFIG.eventLinkPrefix); return ( <>
diff --git a/packages/app/src/Element/Event/NoteContextMenu.tsx b/packages/app/src/Element/Event/NoteContextMenu.tsx index b35cfb44..645690f8 100644 --- a/packages/app/src/Element/Event/NoteContextMenu.tsx +++ b/packages/app/src/Element/Event/NoteContextMenu.tsx @@ -45,8 +45,8 @@ export function NoteContextMenu({ ev, ...props }: NosteContextMenuProps) { } async function share() { - const link = NostrLink.fromEvent(ev).encode(); - const url = `${window.location.protocol}//${window.location.host}/e/${link}`; + const link = NostrLink.fromEvent(ev).encode(CONFIG.eventLinkPrefix); + const url = `${window.location.protocol}//${window.location.host}/${link}`; if ("share" in window.navigator) { await window.navigator.share({ title: "Snort", @@ -81,7 +81,7 @@ export function NoteContextMenu({ ev, ...props }: NosteContextMenuProps) { } async function copyId() { - const link = NostrLink.fromEvent(ev).encode(); + const link = NostrLink.fromEvent(ev).encode(CONFIG.eventLinkPrefix); await navigator.clipboard.writeText(link); } diff --git a/packages/app/src/Element/Event/NoteCreator.tsx b/packages/app/src/Element/Event/NoteCreator.tsx index baade7d6..9218caa7 100644 --- a/packages/app/src/Element/Event/NoteCreator.tsx +++ b/packages/app/src/Element/Event/NoteCreator.tsx @@ -104,7 +104,7 @@ export function NoteCreator() { note.note += "\n"; } const link = NostrLink.fromEvent(note.quote); - note.note += `nostr:${link.encode()}`; + note.note += `nostr:${link.encode(CONFIG.eventLinkPrefix)}`; const quoteTag = link.toEventTag(); if (quoteTag) { extraTags ??= []; @@ -168,7 +168,7 @@ export function NoteCreator() { const rx = await uploader.upload(file, file.name); note.update(v => { if (rx.header) { - const link = `nostr:${new NostrLink(NostrPrefix.Event, rx.header.id, rx.header.kind).encode()}`; + const link = `nostr:${new NostrLink(CONFIG.eventLinkPrefix, rx.header.id, rx.header.kind).encode()}`; v.note = `${v.note ? `${v.note}\n` : ""}${link}`; v.otherEvents = [...(v.otherEvents ?? []), rx.header]; } else if (rx.url) { diff --git a/packages/app/src/Element/Event/NoteInner.tsx b/packages/app/src/Element/Event/NoteInner.tsx index 2fb2d165..c60faeb5 100644 --- a/packages/app/src/Element/Event/NoteInner.tsx +++ b/packages/app/src/Element/Event/NoteInner.tsx @@ -154,9 +154,9 @@ export function NoteInner(props: NoteProps) { const link = NostrLink.fromEvent(eTarget); // detect cmd key and open in new tab if (e.metaKey) { - window.open(`/e/${link.encode()}`, "_blank"); + window.open(`/${link.encode(CONFIG.eventLinkPrefix)}`, "_blank"); } else { - navigate(`/e/${link.encode()}`, { + navigate(`/${link.encode(CONFIG.eventLinkPrefix)}`, { state: eTarget, }); } @@ -211,7 +211,7 @@ export function NoteInner(props: NoteProps) { {pubMentions} {others} ) : ( - replyLink && {replyLink.encode().substring(0, 12)} + replyLink && {replyLink.encode().substring(0, 12)} )}
); diff --git a/packages/app/src/Element/Event/Thread.tsx b/packages/app/src/Element/Event/Thread.tsx index c82c27c6..96392ec6 100644 --- a/packages/app/src/Element/Event/Thread.tsx +++ b/packages/app/src/Element/Event/Thread.tsx @@ -238,7 +238,7 @@ export function Thread(props: { onBack?: () => void; disableSpotlight?: boolean function navigateThread(e: TaggedNostrEvent) { thread.setCurrent(e.id); - //router.navigate(`/e/${NostrLink.fromEvent(e).encode()}`, { replace: true }) + //router.navigate(`/${NostrLink.fromEvent(e).encode()}`, { replace: true }) } const parent = useMemo(() => { diff --git a/packages/app/src/Element/LiveEvent.tsx b/packages/app/src/Element/LiveEvent.tsx index 0ae46f89..838444aa 100644 --- a/packages/app/src/Element/LiveEvent.tsx +++ b/packages/app/src/Element/LiveEvent.tsx @@ -44,7 +44,7 @@ export function LiveEvent({ ev }: { ev: NostrEvent }) { } function cta() { - const link = `https://zap.stream/${NostrLink.fromEvent(ev).encode()}`; + const link = `https://zap.stream/${NostrLink.fromEvent(ev).encode(CONFIG.eventLinkPrefix)}`; switch (status) { case "live": { return ( diff --git a/packages/app/src/Element/LiveStreams.tsx b/packages/app/src/Element/LiveStreams.tsx index 02f83c26..74cb7657 100644 --- a/packages/app/src/Element/LiveStreams.tsx +++ b/packages/app/src/Element/LiveStreams.tsx @@ -32,7 +32,7 @@ function LiveStreamEvent({ ev }: { ev: NostrEvent }) { const image = findTag(ev, "image"); const status = findTag(ev, "status"); - const link = NostrLink.fromEvent(ev).encode(); + const link = NostrLink.fromEvent(ev).encode(CONFIG.eventLinkPrefix); const imageProxy = proxy(image ?? ""); return ( diff --git a/packages/app/src/Element/User/ProfileLink.tsx b/packages/app/src/Element/User/ProfileLink.tsx index 67531293..c081a017 100644 --- a/packages/app/src/Element/User/ProfileLink.tsx +++ b/packages/app/src/Element/User/ProfileLink.tsx @@ -36,8 +36,8 @@ export function ProfileLink({ const [username] = user.nip05.split("@"); return `/${username}`; } - return `/p/${new NostrLink( - NostrPrefix.Profile, + return `/${new NostrLink( + CONFIG.profileLinkPrefix, pubkey, undefined, undefined, @@ -45,7 +45,7 @@ export function ProfileLink({ ).encode()}`; } if (link && (link.type === NostrPrefix.Profile || link.type === NostrPrefix.PublicKey)) { - return `/p/${link.encode()}`; + return `/${link.encode()}`; } return "#"; } diff --git a/packages/app/src/Feed/ThreadFeed.ts b/packages/app/src/Feed/ThreadFeed.ts index 6eef9946..4921cca7 100644 --- a/packages/app/src/Feed/ThreadFeed.ts +++ b/packages/app/src/Feed/ThreadFeed.ts @@ -39,7 +39,7 @@ export default function useThreadFeed(link: NostrLink) { const links = store.data .map(a => [ NostrLink.fromEvent(a), - ...a.tags.filter(a => a[0] === "e" || a[0] === "a").map(v => NostrLink.fromTag(v)), + ...a.tags.filter(a => a[0] === "e" || a[0] === "a").map(v => NostrLink.fromTag(v, CONFIG.eventLinkPrefix)), ]) .flat(); setAllEvents(links); @@ -51,12 +51,15 @@ export default function useThreadFeed(link: NostrLink) { const rootOrReplyAsRoot = t?.root ?? t?.replyTo; if (rootOrReplyAsRoot) { setRoot( - NostrLink.fromTag([ - rootOrReplyAsRoot.key, - rootOrReplyAsRoot.value ?? "", - rootOrReplyAsRoot.relay ?? "", - ...(rootOrReplyAsRoot.marker ?? []), - ]), + NostrLink.fromTag( + [ + rootOrReplyAsRoot.key, + rootOrReplyAsRoot.value ?? "", + rootOrReplyAsRoot.relay ?? "", + ...(rootOrReplyAsRoot.marker ?? []), + ], + CONFIG.eventLinkPrefix, + ), ); } } diff --git a/packages/app/src/Feed/TimelineFeed.ts b/packages/app/src/Feed/TimelineFeed.ts index f00d9de1..797d4094 100644 --- a/packages/app/src/Feed/TimelineFeed.ts +++ b/packages/app/src/Feed/TimelineFeed.ts @@ -46,7 +46,7 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel ); if (subject.relay) { - f.relay(subject.relay); + subject.relay.forEach(r => f.relay(r)); } switch (subject.type) { case "pubkey": { diff --git a/packages/app/src/Pages/MessagesPage.tsx b/packages/app/src/Pages/MessagesPage.tsx index 0675bfe0..7f6bcdf9 100644 --- a/packages/app/src/Pages/MessagesPage.tsx +++ b/packages/app/src/Pages/MessagesPage.tsx @@ -3,7 +3,7 @@ import "./MessagesPage.css"; import React, { useEffect, useMemo, useState } from "react"; import { FormattedMessage, useIntl } from "react-intl"; import { useNavigate, useParams } from "react-router-dom"; -import { NostrLink, NostrPrefix, TLVEntryType, UserMetadata, decodeTLV } from "@snort/system"; +import { NostrLink, TLVEntryType, UserMetadata, decodeTLV } from "@snort/system"; import { useUserProfile, useUserSearch } from "@snort/system-react"; import UnreadCount from "Element/UnreadCount"; @@ -285,7 +285,7 @@ function NewChatWindow() { } export function Nip28ChatProfile({ id, onClick }: { id: string; onClick: (id: string) => void }) { - const channel = useEventFeed(new NostrLink(NostrPrefix.Event, id, 40)); + const channel = useEventFeed(new NostrLink(CONFIG.eventLinkPrefix, id, 40)); if (channel?.data) { const meta = JSON.parse(channel.data.content) as UserMetadata; return ( diff --git a/packages/app/src/Pages/Notifications.tsx b/packages/app/src/Pages/Notifications.tsx index 87e35509..43ccff78 100644 --- a/packages/app/src/Pages/Notifications.tsx +++ b/packages/app/src/Pages/Notifications.tsx @@ -36,7 +36,7 @@ function notificationContext(ev: TaggedNostrEvent) { } const eTag = findTag(ev, "e"); if (eTag) { - return new NostrLink(NostrPrefix.Event, eTag); + return new NostrLink(CONFIG.eventLinkPrefix, eTag); } const pTag = ev.tags.filter(a => a[0] === "p").slice(-1)?.[0]; if (pTag) { @@ -49,7 +49,7 @@ function notificationContext(ev: TaggedNostrEvent) { const thread = EventExt.extractThread(ev); const tag = unwrap(thread?.replyTo ?? thread?.root ?? { value: ev.id, key: "e" }); if (tag.key === "e") { - return new NostrLink(NostrPrefix.Event, unwrap(tag.value)); + return new NostrLink(CONFIG.eventLinkPrefix, unwrap(tag.value)); } else if (tag.key === "a") { const [kind, author, d] = unwrap(tag.value).split(":"); return new NostrLink(NostrPrefix.Address, d, Number(kind), author); @@ -395,7 +395,7 @@ function NotificationGroup({ evs, onClick }: { evs: Array; onC if (onClick) { onClick(context); } else { - navigate(`/e/${context.encode()}`); + navigate(`/${context.encode()}`); } }} /> diff --git a/packages/app/src/SnortUtils/index.ts b/packages/app/src/SnortUtils/index.ts index 811b5c08..48b6c679 100644 --- a/packages/app/src/SnortUtils/index.ts +++ b/packages/app/src/SnortUtils/index.ts @@ -107,7 +107,7 @@ export function eventLink(hex: u256, relays?: Array | string) { const encoded = relays ? encodeTLV(NostrPrefix.Event, hex, Array.isArray(relays) ? relays : [relays]) : hexToBech32(NostrPrefix.Note, hex); - return `/e/${encoded}`; + return `/${encoded}`; } /** diff --git a/packages/system/src/nostr-link.ts b/packages/system/src/nostr-link.ts index 4e88d696..f97856a2 100644 --- a/packages/system/src/nostr-link.ts +++ b/packages/system/src/nostr-link.ts @@ -1,14 +1,14 @@ import { bech32ToHex, hexToBech32, unwrap } from "@snort/shared"; import { - NostrPrefix, decodeTLV, - TLVEntryType, encodeTLV, - NostrEvent, - TaggedNostrEvent, EventExt, - Tag, EventKind, + NostrEvent, + NostrPrefix, + Tag, + TaggedNostrEvent, + TLVEntryType, } from "."; import { findTag } from "./utils"; @@ -21,11 +21,11 @@ export class NostrLink { readonly relays?: Array, ) {} - encode(): string { - if (this.type === NostrPrefix.Note || this.type === NostrPrefix.PrivateKey || this.type === NostrPrefix.PublicKey) { - return hexToBech32(this.type, this.id); + encode(type: NostrPrefix = this.type): string { + if (type === NostrPrefix.Note || type === NostrPrefix.PrivateKey || type === NostrPrefix.PublicKey) { + return hexToBech32(type, this.id); } else { - return encodeTLV(this.type, this.id, this.relays, this.kind, this.author); + return encodeTLV(type, this.id, this.relays, this.kind, this.author); } } @@ -128,12 +128,12 @@ export class NostrLink { } } - static fromThreadTag(tag: Tag) { + static fromThreadTag(tag: Tag, eventLinkPrefix = NostrPrefix.Event) { const relay = tag.relay ? [tag.relay] : undefined; switch (tag.key) { case "e": { - return new NostrLink(NostrPrefix.Event, unwrap(tag.value), undefined, undefined, relay); + return new NostrLink(eventLinkPrefix, unwrap(tag.value), undefined, undefined, relay); } case "p": { return new NostrLink(NostrPrefix.Profile, unwrap(tag.value), undefined, undefined, relay); @@ -146,11 +146,11 @@ export class NostrLink { throw new Error(`Unknown tag kind ${tag.key}`); } - static fromTag(tag: Array) { + static fromTag(tag: Array, eventLinkPrefix = NostrPrefix.Event) { const relays = tag.length > 2 ? [tag[2]] : undefined; switch (tag[0]) { case "e": { - return new NostrLink(NostrPrefix.Event, tag[1], undefined, undefined, relays); + return new NostrLink(eventLinkPrefix, tag[1], undefined, undefined, relays); } case "p": { return new NostrLink(NostrPrefix.Profile, tag[1], undefined, undefined, relays);