forked from Kieran/snort
Merge pull request 'Event & profile links' (#653) from mmalmi/snort:main into main
Reviewed-on: Kieran/snort#653
This commit is contained in:
commit
81df18ea4e
@ -13,5 +13,7 @@
|
|||||||
"subscriptions": true,
|
"subscriptions": true,
|
||||||
"deck": true,
|
"deck": true,
|
||||||
"zapPool": true
|
"zapPool": true
|
||||||
}
|
},
|
||||||
|
"eventLinkPrefix": "nevent",
|
||||||
|
"profileLinkPrefix": "nprofile"
|
||||||
}
|
}
|
||||||
|
@ -13,5 +13,7 @@
|
|||||||
"subscriptions": false,
|
"subscriptions": false,
|
||||||
"deck": true,
|
"deck": true,
|
||||||
"zapPool": true
|
"zapPool": true
|
||||||
}
|
},
|
||||||
|
"eventLinkPrefix": "note",
|
||||||
|
"profileLinkPrefix": "npub"
|
||||||
}
|
}
|
||||||
|
2
packages/app/custom.d.ts
vendored
2
packages/app/custom.d.ts
vendored
@ -52,4 +52,6 @@ declare const CONFIG: {
|
|||||||
deck: boolean;
|
deck: boolean;
|
||||||
zapPool: boolean;
|
zapPool: boolean;
|
||||||
};
|
};
|
||||||
|
eventLinkPrefix: NostrPrefix;
|
||||||
|
profileLinkPrefix: NostrPrefix;
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { NostrPrefix, NostrEvent, NostrLink } from "@snort/system";
|
import { NostrEvent, NostrLink } from "@snort/system";
|
||||||
import useEventPublisher from "Hooks/useEventPublisher";
|
import useEventPublisher from "Hooks/useEventPublisher";
|
||||||
import Icon from "Icons/Icon";
|
import Icon from "Icons/Icon";
|
||||||
import Spinner from "Icons/Spinner";
|
import Spinner from "Icons/Spinner";
|
||||||
@ -36,7 +36,7 @@ export default function WriteMessage({ chat }: { chat: Chat }) {
|
|||||||
if (file) {
|
if (file) {
|
||||||
const rx = await uploader.upload(file, file.name);
|
const rx = await uploader.upload(file, file.name);
|
||||||
if (rx.header) {
|
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}`);
|
setMsg(`${msg ? `${msg}\n` : ""}${link}`);
|
||||||
setOtherEvents([...otherEvents, rx.header]);
|
setOtherEvents([...otherEvents, rx.header]);
|
||||||
} else if (rx.url) {
|
} else if (rx.url) {
|
||||||
|
@ -13,7 +13,7 @@ export default function NostrLink({ link, depth }: { link: string; depth?: numbe
|
|||||||
if ((depth ?? 0) > 0) {
|
if ((depth ?? 0) > 0) {
|
||||||
const evLink = nav.encode();
|
const evLink = nav.encode();
|
||||||
return (
|
return (
|
||||||
<Link to={`/e/${evLink}`} onClick={e => e.stopPropagation()} state={{ from: location.pathname }}>
|
<Link to={`/${evLink}`} onClick={e => e.stopPropagation()} state={{ from: location.pathname }}>
|
||||||
#{evLink.substring(0, 12)}
|
#{evLink.substring(0, 12)}
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
|
@ -12,7 +12,7 @@ export default function ZapstrEmbed({ ev }: { ev: NostrEvent }) {
|
|||||||
const subject = ev.tags.find(a => a[0] === "subject");
|
const subject = ev.tags.find(a => a[0] === "subject");
|
||||||
const refPersons = ev.tags.filter(a => a[0] === "p");
|
const refPersons = ev.tags.filter(a => a[0] === "p");
|
||||||
|
|
||||||
const link = NostrLink.fromEvent(ev).encode();
|
const link = NostrLink.fromEvent(ev).encode(CONFIG.eventLinkPrefix);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex zapstr mb10 card">
|
<div className="flex zapstr mb10 card">
|
||||||
|
@ -45,8 +45,8 @@ export function NoteContextMenu({ ev, ...props }: NosteContextMenuProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function share() {
|
async function share() {
|
||||||
const link = NostrLink.fromEvent(ev).encode();
|
const link = NostrLink.fromEvent(ev).encode(CONFIG.eventLinkPrefix);
|
||||||
const url = `${window.location.protocol}//${window.location.host}/e/${link}`;
|
const url = `${window.location.protocol}//${window.location.host}/${link}`;
|
||||||
if ("share" in window.navigator) {
|
if ("share" in window.navigator) {
|
||||||
await window.navigator.share({
|
await window.navigator.share({
|
||||||
title: "Snort",
|
title: "Snort",
|
||||||
@ -81,7 +81,7 @@ export function NoteContextMenu({ ev, ...props }: NosteContextMenuProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function copyId() {
|
async function copyId() {
|
||||||
const link = NostrLink.fromEvent(ev).encode();
|
const link = NostrLink.fromEvent(ev).encode(CONFIG.eventLinkPrefix);
|
||||||
await navigator.clipboard.writeText(link);
|
await navigator.clipboard.writeText(link);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ export function NoteCreator() {
|
|||||||
note.note += "\n";
|
note.note += "\n";
|
||||||
}
|
}
|
||||||
const link = NostrLink.fromEvent(note.quote);
|
const link = NostrLink.fromEvent(note.quote);
|
||||||
note.note += `nostr:${link.encode()}`;
|
note.note += `nostr:${link.encode(CONFIG.eventLinkPrefix)}`;
|
||||||
const quoteTag = link.toEventTag();
|
const quoteTag = link.toEventTag();
|
||||||
if (quoteTag) {
|
if (quoteTag) {
|
||||||
extraTags ??= [];
|
extraTags ??= [];
|
||||||
@ -168,7 +168,7 @@ export function NoteCreator() {
|
|||||||
const rx = await uploader.upload(file, file.name);
|
const rx = await uploader.upload(file, file.name);
|
||||||
note.update(v => {
|
note.update(v => {
|
||||||
if (rx.header) {
|
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.note = `${v.note ? `${v.note}\n` : ""}${link}`;
|
||||||
v.otherEvents = [...(v.otherEvents ?? []), rx.header];
|
v.otherEvents = [...(v.otherEvents ?? []), rx.header];
|
||||||
} else if (rx.url) {
|
} else if (rx.url) {
|
||||||
|
@ -154,9 +154,9 @@ export function NoteInner(props: NoteProps) {
|
|||||||
const link = NostrLink.fromEvent(eTarget);
|
const link = NostrLink.fromEvent(eTarget);
|
||||||
// detect cmd key and open in new tab
|
// detect cmd key and open in new tab
|
||||||
if (e.metaKey) {
|
if (e.metaKey) {
|
||||||
window.open(`/e/${link.encode()}`, "_blank");
|
window.open(`/${link.encode(CONFIG.eventLinkPrefix)}`, "_blank");
|
||||||
} else {
|
} else {
|
||||||
navigate(`/e/${link.encode()}`, {
|
navigate(`/${link.encode(CONFIG.eventLinkPrefix)}`, {
|
||||||
state: eTarget,
|
state: eTarget,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -211,7 +211,7 @@ export function NoteInner(props: NoteProps) {
|
|||||||
{pubMentions} {others}
|
{pubMentions} {others}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
replyLink && <Link to={`/e/${replyLink.encode()}`}>{replyLink.encode().substring(0, 12)}</Link>
|
replyLink && <Link to={`/${replyLink.encode()}`}>{replyLink.encode().substring(0, 12)}</Link>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -238,7 +238,7 @@ export function Thread(props: { onBack?: () => void; disableSpotlight?: boolean
|
|||||||
|
|
||||||
function navigateThread(e: TaggedNostrEvent) {
|
function navigateThread(e: TaggedNostrEvent) {
|
||||||
thread.setCurrent(e.id);
|
thread.setCurrent(e.id);
|
||||||
//router.navigate(`/e/${NostrLink.fromEvent(e).encode()}`, { replace: true })
|
//router.navigate(`/${NostrLink.fromEvent(e).encode()}`, { replace: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
const parent = useMemo(() => {
|
const parent = useMemo(() => {
|
||||||
|
@ -44,7 +44,7 @@ export function LiveEvent({ ev }: { ev: NostrEvent }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function cta() {
|
function cta() {
|
||||||
const link = `https://zap.stream/${NostrLink.fromEvent(ev).encode()}`;
|
const link = `https://zap.stream/${NostrLink.fromEvent(ev).encode(CONFIG.eventLinkPrefix)}`;
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case "live": {
|
case "live": {
|
||||||
return (
|
return (
|
||||||
|
@ -32,7 +32,7 @@ function LiveStreamEvent({ ev }: { ev: NostrEvent }) {
|
|||||||
const image = findTag(ev, "image");
|
const image = findTag(ev, "image");
|
||||||
const status = findTag(ev, "status");
|
const status = findTag(ev, "status");
|
||||||
|
|
||||||
const link = NostrLink.fromEvent(ev).encode();
|
const link = NostrLink.fromEvent(ev).encode(CONFIG.eventLinkPrefix);
|
||||||
const imageProxy = proxy(image ?? "");
|
const imageProxy = proxy(image ?? "");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -36,8 +36,8 @@ export function ProfileLink({
|
|||||||
const [username] = user.nip05.split("@");
|
const [username] = user.nip05.split("@");
|
||||||
return `/${username}`;
|
return `/${username}`;
|
||||||
}
|
}
|
||||||
return `/p/${new NostrLink(
|
return `/${new NostrLink(
|
||||||
NostrPrefix.Profile,
|
CONFIG.profileLinkPrefix,
|
||||||
pubkey,
|
pubkey,
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
@ -45,7 +45,7 @@ export function ProfileLink({
|
|||||||
).encode()}`;
|
).encode()}`;
|
||||||
}
|
}
|
||||||
if (link && (link.type === NostrPrefix.Profile || link.type === NostrPrefix.PublicKey)) {
|
if (link && (link.type === NostrPrefix.Profile || link.type === NostrPrefix.PublicKey)) {
|
||||||
return `/p/${link.encode()}`;
|
return `/${link.encode()}`;
|
||||||
}
|
}
|
||||||
return "#";
|
return "#";
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ export default function useThreadFeed(link: NostrLink) {
|
|||||||
const links = store.data
|
const links = store.data
|
||||||
.map(a => [
|
.map(a => [
|
||||||
NostrLink.fromEvent(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();
|
.flat();
|
||||||
setAllEvents(links);
|
setAllEvents(links);
|
||||||
@ -51,12 +51,15 @@ export default function useThreadFeed(link: NostrLink) {
|
|||||||
const rootOrReplyAsRoot = t?.root ?? t?.replyTo;
|
const rootOrReplyAsRoot = t?.root ?? t?.replyTo;
|
||||||
if (rootOrReplyAsRoot) {
|
if (rootOrReplyAsRoot) {
|
||||||
setRoot(
|
setRoot(
|
||||||
NostrLink.fromTag([
|
NostrLink.fromTag(
|
||||||
rootOrReplyAsRoot.key,
|
[
|
||||||
rootOrReplyAsRoot.value ?? "",
|
rootOrReplyAsRoot.key,
|
||||||
rootOrReplyAsRoot.relay ?? "",
|
rootOrReplyAsRoot.value ?? "",
|
||||||
...(rootOrReplyAsRoot.marker ?? []),
|
rootOrReplyAsRoot.relay ?? "",
|
||||||
]),
|
...(rootOrReplyAsRoot.marker ?? []),
|
||||||
|
],
|
||||||
|
CONFIG.eventLinkPrefix,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (subject.relay) {
|
if (subject.relay) {
|
||||||
f.relay(subject.relay);
|
subject.relay.forEach(r => f.relay(r));
|
||||||
}
|
}
|
||||||
switch (subject.type) {
|
switch (subject.type) {
|
||||||
case "pubkey": {
|
case "pubkey": {
|
||||||
|
@ -3,7 +3,7 @@ import "./MessagesPage.css";
|
|||||||
import React, { useEffect, useMemo, useState } from "react";
|
import React, { useEffect, useMemo, useState } from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
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 { useUserProfile, useUserSearch } from "@snort/system-react";
|
||||||
|
|
||||||
import UnreadCount from "Element/UnreadCount";
|
import UnreadCount from "Element/UnreadCount";
|
||||||
@ -285,7 +285,7 @@ function NewChatWindow() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function Nip28ChatProfile({ id, onClick }: { id: string; onClick: (id: string) => void }) {
|
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) {
|
if (channel?.data) {
|
||||||
const meta = JSON.parse(channel.data.content) as UserMetadata;
|
const meta = JSON.parse(channel.data.content) as UserMetadata;
|
||||||
return (
|
return (
|
||||||
|
@ -36,7 +36,7 @@ function notificationContext(ev: TaggedNostrEvent) {
|
|||||||
}
|
}
|
||||||
const eTag = findTag(ev, "e");
|
const eTag = findTag(ev, "e");
|
||||||
if (eTag) {
|
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];
|
const pTag = ev.tags.filter(a => a[0] === "p").slice(-1)?.[0];
|
||||||
if (pTag) {
|
if (pTag) {
|
||||||
@ -49,7 +49,7 @@ function notificationContext(ev: TaggedNostrEvent) {
|
|||||||
const thread = EventExt.extractThread(ev);
|
const thread = EventExt.extractThread(ev);
|
||||||
const tag = unwrap(thread?.replyTo ?? thread?.root ?? { value: ev.id, key: "e" });
|
const tag = unwrap(thread?.replyTo ?? thread?.root ?? { value: ev.id, key: "e" });
|
||||||
if (tag.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") {
|
} else if (tag.key === "a") {
|
||||||
const [kind, author, d] = unwrap(tag.value).split(":");
|
const [kind, author, d] = unwrap(tag.value).split(":");
|
||||||
return new NostrLink(NostrPrefix.Address, d, Number(kind), author);
|
return new NostrLink(NostrPrefix.Address, d, Number(kind), author);
|
||||||
@ -395,7 +395,7 @@ function NotificationGroup({ evs, onClick }: { evs: Array<TaggedNostrEvent>; onC
|
|||||||
if (onClick) {
|
if (onClick) {
|
||||||
onClick(context);
|
onClick(context);
|
||||||
} else {
|
} else {
|
||||||
navigate(`/e/${context.encode()}`);
|
navigate(`/${context.encode()}`);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -107,7 +107,7 @@ export function eventLink(hex: u256, relays?: Array<string> | string) {
|
|||||||
const encoded = relays
|
const encoded = relays
|
||||||
? encodeTLV(NostrPrefix.Event, hex, Array.isArray(relays) ? relays : [relays])
|
? encodeTLV(NostrPrefix.Event, hex, Array.isArray(relays) ? relays : [relays])
|
||||||
: hexToBech32(NostrPrefix.Note, hex);
|
: hexToBech32(NostrPrefix.Note, hex);
|
||||||
return `/e/${encoded}`;
|
return `/${encoded}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import { bech32ToHex, hexToBech32, unwrap } from "@snort/shared";
|
import { bech32ToHex, hexToBech32, unwrap } from "@snort/shared";
|
||||||
import {
|
import {
|
||||||
NostrPrefix,
|
|
||||||
decodeTLV,
|
decodeTLV,
|
||||||
TLVEntryType,
|
|
||||||
encodeTLV,
|
encodeTLV,
|
||||||
NostrEvent,
|
|
||||||
TaggedNostrEvent,
|
|
||||||
EventExt,
|
EventExt,
|
||||||
Tag,
|
|
||||||
EventKind,
|
EventKind,
|
||||||
|
NostrEvent,
|
||||||
|
NostrPrefix,
|
||||||
|
Tag,
|
||||||
|
TaggedNostrEvent,
|
||||||
|
TLVEntryType,
|
||||||
} from ".";
|
} from ".";
|
||||||
import { findTag } from "./utils";
|
import { findTag } from "./utils";
|
||||||
|
|
||||||
@ -21,11 +21,11 @@ export class NostrLink {
|
|||||||
readonly relays?: Array<string>,
|
readonly relays?: Array<string>,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
encode(): string {
|
encode(type: NostrPrefix = this.type): string {
|
||||||
if (this.type === NostrPrefix.Note || this.type === NostrPrefix.PrivateKey || this.type === NostrPrefix.PublicKey) {
|
if (type === NostrPrefix.Note || type === NostrPrefix.PrivateKey || type === NostrPrefix.PublicKey) {
|
||||||
return hexToBech32(this.type, this.id);
|
return hexToBech32(type, this.id);
|
||||||
} else {
|
} 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;
|
const relay = tag.relay ? [tag.relay] : undefined;
|
||||||
|
|
||||||
switch (tag.key) {
|
switch (tag.key) {
|
||||||
case "e": {
|
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": {
|
case "p": {
|
||||||
return new NostrLink(NostrPrefix.Profile, unwrap(tag.value), undefined, undefined, relay);
|
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}`);
|
throw new Error(`Unknown tag kind ${tag.key}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromTag(tag: Array<string>) {
|
static fromTag(tag: Array<string>, eventLinkPrefix = NostrPrefix.Event) {
|
||||||
const relays = tag.length > 2 ? [tag[2]] : undefined;
|
const relays = tag.length > 2 ? [tag[2]] : undefined;
|
||||||
switch (tag[0]) {
|
switch (tag[0]) {
|
||||||
case "e": {
|
case "e": {
|
||||||
return new NostrLink(NostrPrefix.Event, tag[1], undefined, undefined, relays);
|
return new NostrLink(eventLinkPrefix, tag[1], undefined, undefined, relays);
|
||||||
}
|
}
|
||||||
case "p": {
|
case "p": {
|
||||||
return new NostrLink(NostrPrefix.Profile, tag[1], undefined, undefined, relays);
|
return new NostrLink(NostrPrefix.Profile, tag[1], undefined, undefined, relays);
|
||||||
|
Loading…
Reference in New Issue
Block a user