feat: render embed for zapstr replies
continuous-integration/drone/push Build is failing Details

This commit is contained in:
Kieran 2023-05-15 13:52:29 +01:00
parent 323d4e761e
commit d8fc92fafc
Signed by: Kieran
GPG Key ID: DE71CEB3925BE941
10 changed files with 33 additions and 39 deletions

View File

@ -2,10 +2,8 @@ import { Link } from "react-router-dom";
import { EventKind, NostrPrefix } from "@snort/nostr";
import Mention from "Element/Mention";
import NostrFileHeader from "Element/NostrFileHeader";
import { parseNostrLink } from "Util";
import NoteQuote from "Element/NoteQuote";
import ZapstrEmbed from "Element/ZapstrEmbed";
export default function NostrLink({ link, depth }: { link: string; depth?: number }) {
const nav = parseNostrLink(link);
@ -13,13 +11,6 @@ export default function NostrLink({ link, depth }: { link: string; depth?: numbe
if (nav?.type === NostrPrefix.PublicKey || nav?.type === NostrPrefix.Profile) {
return <Mention pubkey={nav.id} relays={nav.relays} />;
} else if (nav?.type === NostrPrefix.Note || nav?.type === NostrPrefix.Event || nav?.type === NostrPrefix.Address) {
if (nav.kind === EventKind.FileHeader) {
return <NostrFileHeader link={nav} />;
}
if (nav.kind === 31337) {
return <ZapstrEmbed link={nav} />;
}
if ((depth ?? 0) > 0) {
const evLink = nav.encode();
return (

View File

@ -30,6 +30,7 @@ import { EventExt } from "System/EventExt";
import useLogin from "Hooks/useLogin";
import { setBookmarked, setPinned } from "Login";
import { NostrFileElement } from "Element/NostrFileHeader";
import ZapstrEmbed from "Element/ZapstrEmbed";
import messages from "./messages";
@ -78,7 +79,9 @@ export default function Note(props: NoteProps) {
if (ev.kind === EventKind.FileHeader) {
return <NostrFileElement ev={ev} />;
}
if (ev.kind === 31337) {
return <ZapstrEmbed ev={ev} />;
}
const navigate = useNavigate();
const [showReactions, setShowReactions] = useState(false);
const deletions = useMemo(() => getReactions(related, ev.id, EventKind.Deletion), [related]);

View File

@ -142,7 +142,7 @@ export function NoteCreator() {
if (file) {
const rx = await uploader.upload(file, file.name);
if (rx.header) {
const link = `nostr:${encodeTLV(rx.header.id, NostrPrefix.Event, undefined, rx.header.kind)}`;
const link = `nostr:${encodeTLV(NostrPrefix.Event, rx.header.id, undefined, rx.header.kind)}`;
dispatch(setNote(`${note ? `${note}\n` : ""}${link}`));
dispatch(setOtherEvents([...otherEvents, rx.header]));
} else if (rx.url) {

View File

@ -264,7 +264,7 @@ export default function NoteFooter(props: NoteFooterProps) {
}
async function share() {
const link = encodeTLV(ev.id, NostrPrefix.Event, ev.relays);
const link = encodeTLV(NostrPrefix.Event, ev.id, ev.relays);
const url = `${window.location.protocol}//${window.location.host}/e/${link}`;
if ("share" in window.navigator) {
await window.navigator.share({
@ -300,7 +300,7 @@ export default function NoteFooter(props: NoteFooterProps) {
}
async function copyId() {
const link = encodeTLV(ev.id, NostrPrefix.Event, ev.relays);
const link = encodeTLV(NostrPrefix.Event, ev.id, ev.relays);
await navigator.clipboard.writeText(link);
}

View File

@ -35,7 +35,7 @@ export default function WriteDm({ chatPubKey }: { chatPubKey: string }) {
if (file) {
const rx = await uploader.upload(file, file.name);
if (rx.header) {
const link = `nostr:${encodeTLV(rx.header.id, NostrPrefix.Event, undefined, rx.header.kind)}`;
const link = `nostr:${encodeTLV(NostrPrefix.Event, rx.header.id, undefined, rx.header.kind)}`;
setMsg(`${msg ? `${msg}\n` : ""}${link}`);
setOtherEvents([...otherEvents, rx.header]);
} else if (rx.url) {

View File

@ -1,25 +1,27 @@
import "./ZapstrEmbed.css";
import { Link } from "react-router-dom";
import { encodeTLV, NostrPrefix, RawEvent } from "@snort/nostr";
import useEventFeed from "Feed/EventFeed";
import Spinner from "Icons/Spinner";
import { NostrLink } from "Util";
import { ProxyImg } from "Element/ProxyImg";
import ProfileImage from "Element/ProfileImage";
import { FormattedMessage } from "react-intl";
export default function ZapstrEmbed({ link }: { link: NostrLink }) {
const ev = useEventFeed(link);
export default function ZapstrEmbed({ ev }: { ev: RawEvent }) {
const media = ev.tags.find(a => a[0] === "media");
const cover = ev.tags.find(a => a[0] === "cover");
const subject = ev.tags.find(a => a[0] === "subject");
const refPersons = ev.tags.filter(a => a[0] === "p");
if (!ev.data) return <Spinner />;
const media = ev.data.tags.find(a => a[0] === "media");
const cover = ev.data.tags.find(a => a[0] === "cover");
const subject = ev.data.tags.find(a => a[0] === "subject");
const refPersons = ev.data.tags.filter(a => a[0] === "p");
const link = encodeTLV(
NostrPrefix.Address,
ev.tags.find(a => a[0] === "d")?.[1] ?? "",
undefined,
ev.kind,
ev.pubkey
);
return (
<>
<div className="flex zapstr mb10">
<div className="flex zapstr mb10 card">
<ProxyImg src={cover?.[1] ?? ""} size={100} />
<div className="flex f-col">
<div>
@ -33,7 +35,7 @@ export default function ZapstrEmbed({ link }: { link: NostrLink }) {
</div>
</div>
</div>
<Link to={`https://zapstr.live/?track=${link.encode()}`} target="_blank">
<Link to={`https://zapstr.live/?track=${link}`} target="_blank">
<button>
<FormattedMessage defaultMessage="Open on Zapstr" />
</button>

View File

@ -296,7 +296,7 @@ export default function ProfilePage() {
function renderIcons() {
if (!id) return;
const link = encodeTLV(id, NostrPrefix.Profile);
const link = encodeTLV(NostrPrefix.Profile, id);
return (
<div className="icon-actions">
<IconButton onClick={() => setShowProfileQr(true)}>

View File

@ -15,7 +15,7 @@ export default function ExportKeys() {
<FormattedMessage defaultMessage="Public Key" />
</h3>
<Copy text={hexToBech32("npub", publicKey ?? "")} maxSize={48} className="mb10" />
<Copy text={encodeTLV(publicKey ?? "", NostrPrefix.Profile)} maxSize={48} />
<Copy text={encodeTLV(NostrPrefix.Profile, publicKey ?? "")} maxSize={48} />
{privateKey && (
<>
<h3>

View File

@ -91,7 +91,7 @@ export function bech32ToText(str: string) {
*/
export function eventLink(hex: u256, relays?: Array<string> | string) {
const encoded = relays
? encodeTLV(hex, NostrPrefix.Event, Array.isArray(relays) ? relays : [relays])
? encodeTLV(NostrPrefix.Event, hex, Array.isArray(relays) ? relays : [relays])
: hexToBech32(NostrPrefix.Note, hex);
return `/e/${encoded}`;
}
@ -101,7 +101,7 @@ export function eventLink(hex: u256, relays?: Array<string> | string) {
*/
export function profileLink(hex: HexKey, relays?: Array<string> | string) {
const encoded = relays
? encodeTLV(hex, NostrPrefix.Profile, Array.isArray(relays) ? relays : [relays])
? encodeTLV(NostrPrefix.Event, hex, Array.isArray(relays) ? relays : [relays])
: hexToBech32(NostrPrefix.PublicKey, hex);
return `/p/${encoded}`;
}
@ -119,7 +119,7 @@ export function hexToBech32(hrp: string, hex?: string) {
const buf = secp.utils.hexToBytes(hex);
return bech32.encode(hrp, bech32.toWords(buf));
} else {
return encodeTLV(hex, hrp as NostrPrefix);
return encodeTLV(hrp as NostrPrefix, hex);
}
} catch (e) {
console.warn("Invalid hex", hex, e);

View File

@ -27,13 +27,9 @@ export interface TLVEntry {
value: string | HexKey | number;
}
export function encodeTLV(hex: string, prefix: NostrPrefix, relays?: string[], kind?: number) {
if (typeof hex !== "string" || hex.length === 0 || hex.length % 2 !== 0) {
return "";
}
export function encodeTLV(prefix: NostrPrefix, id: string, relays?: string[], kind?: number, author?: string) {
const enc = new TextEncoder();
const buf = secp.utils.hexToBytes(hex);
const buf = prefix === NostrPrefix.Address ? enc.encode(id) : secp.utils.hexToBytes(id);
const tl0 = [0, buf.length, ...buf];
const tl1 =
@ -43,9 +39,11 @@ export function encodeTLV(hex: string, prefix: NostrPrefix, relays?: string[], k
return [1, data.length, ...data];
})
.flat() ?? [];
const tl2 = author ? [2, 32, ...secp.utils.hexToBytes(author)] : [];
const tl3 = kind ? [3, 4, ...new Uint8Array(new Uint32Array([kind]).buffer).reverse()] : []
return bech32.encode(prefix, bech32.toWords([...tl0, ...tl1, ...tl3]), 1_000);
return bech32.encode(prefix, bech32.toWords([...tl0, ...tl1, ...tl2, ...tl3]), 1_000);
}
export function decodeTLV(str: string) {