feat: render note quotes

This commit is contained in:
Kieran 2023-04-18 22:20:13 +01:00
parent a6eefb1027
commit 13461cca80
Signed by: Kieran
GPG Key ID: DE71CEB3925BE941
11 changed files with 61 additions and 25 deletions

View File

@ -26,7 +26,7 @@ import NostrLink from "Element/NostrLink";
import RevealMedia from "Element/RevealMedia"; import RevealMedia from "Element/RevealMedia";
import MagnetLink from "Element/MagnetLink"; import MagnetLink from "Element/MagnetLink";
export default function HyperText({ link, creator }: { link: string; creator: string }) { export default function HyperText({ link, creator, depth }: { link: string; creator: string; depth?: number }) {
const a = link; const a = link;
try { try {
const url = new URL(a); const url = new URL(a);
@ -85,7 +85,7 @@ export default function HyperText({ link, creator }: { link: string; creator: st
} else if (isWavlakeLink) { } else if (isWavlakeLink) {
return <WavlakeEmbed link={a} />; return <WavlakeEmbed link={a} />;
} else if (url.protocol === "nostr:" || url.protocol === "web+nostr:") { } else if (url.protocol === "nostr:" || url.protocol === "web+nostr:") {
return <NostrLink link={a} />; return <NostrLink link={a} depth={depth} />;
} else if (url.protocol === "magnet:") { } else if (url.protocol === "magnet:") {
const parsed = magnetURIDecode(a); const parsed = magnetURIDecode(a);
if (parsed) { if (parsed) {

View File

@ -2,23 +2,23 @@ import useEventFeed from "Feed/EventFeed";
import { NostrLink } from "Util"; import { NostrLink } from "Util";
import HyperText from "Element/HyperText"; import HyperText from "Element/HyperText";
import { FormattedMessage } from "react-intl"; import { FormattedMessage } from "react-intl";
import Spinner from "Icons/Spinner"; import PageSpinner from "Element/PageSpinner";
export default function NostrFileHeader({ link }: { link: NostrLink }) { export default function NostrFileHeader({ link }: { link: NostrLink }) {
const ev = useEventFeed(link); const ev = useEventFeed(link);
if (!ev.data?.length) return <Spinner />; if (!ev.data) return <PageSpinner />;
// assume image or embed which can be rendered by the hypertext kind // assume image or embed which can be rendered by the hypertext kind
// todo: make use of hash // todo: make use of hash
// todo: use magnet or other links if present // todo: use magnet or other links if present
const u = ev.data?.[0]?.tags.find(a => a[0] === "u")?.[1] ?? ""; const u = ev.data?.tags.find(a => a[0] === "u")?.[1] ?? "";
if (u) { if (u) {
return <HyperText link={u} creator={ev.data?.[0]?.pubkey ?? ""} />; return <HyperText link={u} creator={ev.data?.pubkey ?? ""} />;
} else { } else {
return ( return (
<b className="error"> <b className="error">
<FormattedMessage defaultMessage="Unknown file header: {name}" values={{ name: ev.data?.[0]?.content }} /> <FormattedMessage defaultMessage="Unknown file header: {name}" values={{ name: ev.data?.content }} />
</b> </b>
); );
} }

View File

@ -1,11 +1,12 @@
import { EventKind, NostrPrefix } from "@snort/nostr";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { EventKind, NostrPrefix } from "@snort/nostr";
import Mention from "Element/Mention"; import Mention from "Element/Mention";
import NostrFileHeader from "Element/NostrFileHeader"; import NostrFileHeader from "Element/NostrFileHeader";
import { parseNostrLink } from "Util"; import { parseNostrLink } from "Util";
import NoteQuote from "Element/NoteQuote";
export default function NostrLink({ link }: { link: string }) { export default function NostrLink({ link, depth }: { link: string; depth?: number }) {
const nav = parseNostrLink(link); const nav = parseNostrLink(link);
if (nav?.type === NostrPrefix.PublicKey || nav?.type === NostrPrefix.Profile) { if (nav?.type === NostrPrefix.PublicKey || nav?.type === NostrPrefix.Profile) {
@ -14,12 +15,16 @@ export default function NostrLink({ link }: { link: string }) {
if (nav.kind === EventKind.FileHeader) { if (nav.kind === EventKind.FileHeader) {
return <NostrFileHeader link={nav} />; return <NostrFileHeader link={nav} />;
} }
const evLink = nav.encode(); if ((depth ?? 0) > 0) {
return ( const evLink = nav.encode();
<Link to={`/e/${evLink}`} onClick={e => e.stopPropagation()} state={{ from: location.pathname }}> return (
#{evLink.substring(0, 12)} <Link to={`/e/${evLink}`} onClick={e => e.stopPropagation()} state={{ from: location.pathname }}>
</Link> #{evLink.substring(0, 12)}
); </Link>
);
} else {
return <NoteQuote link={nav} depth={depth} />;
}
} else { } else {
return ( return (
<a href={link} onClick={e => e.stopPropagation()} target="_blank" rel="noreferrer" className="ext"> <a href={link} onClick={e => e.stopPropagation()} target="_blank" rel="noreferrer" className="ext">

View File

@ -57,6 +57,14 @@
margin-right: 8px; margin-right: 8px;
} }
.note-quote {
border: 1px solid var(--gray);
}
.note-quote.note > .body {
padding-left: 0;
}
.note > .body { .note > .body {
margin-top: 4px; margin-top: 4px;
margin-bottom: 24px; margin-bottom: 24px;
@ -98,9 +106,6 @@
border-radius: 16px; border-radius: 16px;
} }
.light .note > .footer .ctx-menu {
}
.note > .footer .ctx-menu li { .note > .footer .ctx-menu li {
background: #1e1e1e; background: #1e1e1e;
padding-top: 8px; padding-top: 8px;

View File

@ -39,6 +39,7 @@ export interface NoteProps {
highlight?: boolean; highlight?: boolean;
ignoreModeration?: boolean; ignoreModeration?: boolean;
onClick?: (e: TaggedRawEvent) => void; onClick?: (e: TaggedRawEvent) => void;
depth?: number;
options?: { options?: {
showHeader?: boolean; showHeader?: boolean;
showTime?: boolean; showTime?: boolean;
@ -187,7 +188,7 @@ export default function Note(props: NoteProps) {
</Reveal> </Reveal>
); );
} }
return <Text content={body} tags={ev.tags} creator={ev.pubkey} />; return <Text content={body} tags={ev.tags} creator={ev.pubkey} depth={props.depth} />;
}; };
useLayoutEffect(() => { useLayoutEffect(() => {

View File

@ -0,0 +1,20 @@
import useEventFeed from "Feed/EventFeed";
import { NostrLink } from "Util";
import Note from "Element/Note";
import PageSpinner from "Element/PageSpinner";
export default function NoteQuote({ link, depth }: { link: NostrLink; depth?: number }) {
const ev = useEventFeed(link);
if (!ev.data) return <PageSpinner />;
return (
<Note
data={ev.data}
related={[]}
className="note-quote"
depth={(depth ?? 0) + 1}
options={{
showFooter: false,
}}
/>
);
}

View File

@ -5,7 +5,7 @@
word-break: break-word; word-break: break-word;
} }
.text a { .text > a {
color: var(--highlight); color: var(--highlight);
text-decoration: none; text-decoration: none;
} }

View File

@ -25,9 +25,10 @@ export interface TextProps {
creator: HexKey; creator: HexKey;
tags: Array<Array<string>>; tags: Array<Array<string>>;
disableMedia?: boolean; disableMedia?: boolean;
depth?: number;
} }
export default function Text({ content, tags, creator, disableMedia }: TextProps) { export default function Text({ content, tags, creator, disableMedia, depth }: TextProps) {
const location = useLocation(); const location = useLocation();
function extractLinks(fragments: Fragment[]) { function extractLinks(fragments: Fragment[]) {
@ -43,7 +44,7 @@ export default function Text({ content, tags, creator, disableMedia }: TextProps
</a> </a>
); );
} }
return <HyperText link={a} creator={creator} />; return <HyperText link={a} creator={creator} depth={depth} />;
} }
return a; return a;
}); });

View File

@ -88,7 +88,9 @@ const Timeline = (props: TimelineProps) => {
if (eRef) { if (eRef) {
return <NoteReaction data={e} key={e.id} root={findRelated(eRef)} />; return <NoteReaction data={e} key={e.id} root={findRelated(eRef)} />;
} }
return <Note key={e.id} data={e} related={relatedFeed(e.id)} ignoreModeration={props.ignoreModeration} />; return (
<Note key={e.id} data={e} related={relatedFeed(e.id)} ignoreModeration={props.ignoreModeration} depth={0} />
);
} }
case EventKind.ZapReceipt: { case EventKind.ZapReceipt: {
const zap = parseZap(e); const zap = parseZap(e);

View File

@ -1,7 +1,7 @@
import { useMemo } from "react"; import { useMemo } from "react";
import useRequestBuilder from "Hooks/useRequestBuilder"; import useRequestBuilder from "Hooks/useRequestBuilder";
import { FlatNoteStore, RequestBuilder } from "System"; import { RequestBuilder, ReplaceableNoteStore } from "System";
import { NostrLink } from "Util"; import { NostrLink } from "Util";
export default function useEventFeed(link: NostrLink) { export default function useEventFeed(link: NostrLink) {
@ -11,5 +11,5 @@ export default function useEventFeed(link: NostrLink) {
return b; return b;
}, [link]); }, [link]);
return useRequestBuilder<FlatNoteStore>(FlatNoteStore, sub); return useRequestBuilder<ReplaceableNoteStore>(ReplaceableNoteStore, sub);
} }

View File

@ -8,6 +8,7 @@ import {
NoteStore, NoteStore,
PubkeyReplaceableNoteStore, PubkeyReplaceableNoteStore,
ParameterizedReplaceableNoteStore, ParameterizedReplaceableNoteStore,
ReplaceableNoteStore,
} from "./NoteCollection"; } from "./NoteCollection";
import { diffFilters } from "./RequestSplitter"; import { diffFilters } from "./RequestSplitter";
import { Query } from "./Query"; import { Query } from "./Query";
@ -18,6 +19,7 @@ export {
FlatNoteStore, FlatNoteStore,
PubkeyReplaceableNoteStore, PubkeyReplaceableNoteStore,
ParameterizedReplaceableNoteStore, ParameterizedReplaceableNoteStore,
ReplaceableNoteStore,
Query, Query,
EventBuilder, EventBuilder,
}; };