feat: render nip94 from regular note reference
This commit is contained in:
parent
a31aa35490
commit
82956f3c69
@ -1,43 +1,31 @@
|
||||
import { FileExtensionRegex } from "Const";
|
||||
import { ProxyImg } from "Element/ProxyImg";
|
||||
|
||||
export default function MediaLink({ link }: { link: string }) {
|
||||
const url = new URL(link);
|
||||
const extension = FileExtensionRegex.test(url.pathname.toLowerCase()) && RegExp.$1;
|
||||
switch (extension) {
|
||||
case "gif":
|
||||
case "jpg":
|
||||
case "jpeg":
|
||||
case "jfif":
|
||||
case "png":
|
||||
case "bmp":
|
||||
case "webp": {
|
||||
return <ProxyImg key={url.toString()} src={url.toString()} />;
|
||||
}
|
||||
case "wav":
|
||||
case "mp3":
|
||||
case "ogg": {
|
||||
return <audio key={url.toString()} src={url.toString()} controls />;
|
||||
}
|
||||
case "mp4":
|
||||
case "mov":
|
||||
case "mkv":
|
||||
case "avi":
|
||||
case "m4v":
|
||||
case "webm": {
|
||||
return <video key={url.toString()} src={url.toString()} controls />;
|
||||
}
|
||||
default:
|
||||
return (
|
||||
<a
|
||||
key={url.toString()}
|
||||
href={url.toString()}
|
||||
onClick={e => e.stopPropagation()}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="ext">
|
||||
{url.toString()}
|
||||
</a>
|
||||
);
|
||||
interface MediaElementProps {
|
||||
mime: string;
|
||||
url: string;
|
||||
magnet?: string;
|
||||
sha256?: string;
|
||||
blurHash?: string;
|
||||
}
|
||||
|
||||
export function MediaElement(props: MediaElementProps) {
|
||||
if (props.mime.startsWith("image/")) {
|
||||
return <ProxyImg key={props.url} src={props.url} />;
|
||||
} else if (props.mime.startsWith("audio/")) {
|
||||
return <audio key={props.url} src={props.url} controls />;
|
||||
} else if (props.mime.startsWith("video/")) {
|
||||
return <video key={props.url} src={props.url} controls />;
|
||||
} else {
|
||||
return (
|
||||
<a
|
||||
key={props.url}
|
||||
href={props.url}
|
||||
onClick={e => e.stopPropagation()}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="ext">
|
||||
{props.url}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,24 +1,39 @@
|
||||
import useEventFeed from "Feed/EventFeed";
|
||||
import { NostrLink } from "Util";
|
||||
import HyperText from "Element/HyperText";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import { RawEvent } from "@snort/nostr";
|
||||
|
||||
import { findTag, NostrLink } from "Util";
|
||||
import useEventFeed from "Feed/EventFeed";
|
||||
import PageSpinner from "Element/PageSpinner";
|
||||
import Reveal from "Element/Reveal";
|
||||
import { MediaElement } from "Element/MediaLink";
|
||||
|
||||
export default function NostrFileHeader({ link }: { link: NostrLink }) {
|
||||
const ev = useEventFeed(link);
|
||||
|
||||
if (!ev.data) return <PageSpinner />;
|
||||
return <NostrFileElement ev={ev.data} />;
|
||||
}
|
||||
|
||||
export function NostrFileElement({ ev }: { ev: RawEvent }) {
|
||||
// assume image or embed which can be rendered by the hypertext kind
|
||||
// todo: make use of hash
|
||||
// todo: use magnet or other links if present
|
||||
const u = ev.data?.tags.find(a => a[0] === "u")?.[1] ?? "";
|
||||
if (u) {
|
||||
return <HyperText link={u} creator={ev.data?.pubkey ?? ""} />;
|
||||
const u = findTag(ev, "url");
|
||||
const x = findTag(ev, "x");
|
||||
const m = findTag(ev, "m");
|
||||
const blurHash = findTag(ev, "blurhash");
|
||||
const magnet = findTag(ev, "magnet");
|
||||
|
||||
if (u && m) {
|
||||
return (
|
||||
<Reveal message={<FormattedMessage defaultMessage="Click to load content from {link}" values={{ link: u }} />}>
|
||||
<MediaElement mime={m} url={u} sha256={x} magnet={magnet} blurHash={blurHash} />
|
||||
</Reveal>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<b className="error">
|
||||
<FormattedMessage defaultMessage="Unknown file header: {name}" values={{ name: ev.data?.content }} />
|
||||
<FormattedMessage defaultMessage="Unknown file header: {name}" values={{ name: ev.content }} />
|
||||
</b>
|
||||
);
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ import Poll from "Element/Poll";
|
||||
import { EventExt } from "System/EventExt";
|
||||
import useLogin from "Hooks/useLogin";
|
||||
import { setBookmarked, setPinned } from "Login";
|
||||
import { NostrFileElement } from "Element/NostrFileHeader";
|
||||
|
||||
import messages from "./messages";
|
||||
|
||||
@ -72,8 +73,13 @@ const HiddenNote = ({ children }: { children: React.ReactNode }) => {
|
||||
};
|
||||
|
||||
export default function Note(props: NoteProps) {
|
||||
const navigate = useNavigate();
|
||||
const { data: ev, related, highlight, options: opt, ignoreModeration = false } = props;
|
||||
|
||||
if (ev.kind === EventKind.FileHeader) {
|
||||
return <NostrFileElement ev={ev} />;
|
||||
}
|
||||
|
||||
const navigate = useNavigate();
|
||||
const [showReactions, setShowReactions] = useState(false);
|
||||
const deletions = useMemo(() => getReactions(related, ev.id, EventKind.Deletion), [related]);
|
||||
const { isMuted } = useModeration();
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
import MediaLink from "Element/MediaLink";
|
||||
import { FileExtensionRegex } from "Const";
|
||||
import Reveal from "Element/Reveal";
|
||||
import useLogin from "Hooks/useLogin";
|
||||
import { MediaElement } from "Element/MediaLink";
|
||||
|
||||
interface RevealMediaProps {
|
||||
creator: string;
|
||||
@ -18,14 +19,42 @@ export default function RevealMedia(props: RevealMediaProps) {
|
||||
const hideMedia = pref.autoLoadMedia === "none" || (!isMine && hideNonFollows);
|
||||
const hostname = new URL(props.link).hostname;
|
||||
|
||||
const url = new URL(props.link);
|
||||
const extension = FileExtensionRegex.test(url.pathname.toLowerCase()) && RegExp.$1;
|
||||
const type = (() => {
|
||||
switch (extension) {
|
||||
case "gif":
|
||||
case "jpg":
|
||||
case "jpeg":
|
||||
case "jfif":
|
||||
case "png":
|
||||
case "bmp":
|
||||
case "webp":
|
||||
return "image";
|
||||
case "wav":
|
||||
case "mp3":
|
||||
case "ogg":
|
||||
return "audio";
|
||||
case "mp4":
|
||||
case "mov":
|
||||
case "mkv":
|
||||
case "avi":
|
||||
case "m4v":
|
||||
case "webm":
|
||||
return "video";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
})();
|
||||
|
||||
if (hideMedia) {
|
||||
return (
|
||||
<Reveal
|
||||
message={<FormattedMessage defaultMessage="Click to load content from {link}" values={{ link: hostname }} />}>
|
||||
<MediaLink link={props.link} />
|
||||
<MediaElement mime={`${type}/${extension}`} url={url.toString()} />
|
||||
</Reveal>
|
||||
);
|
||||
} else {
|
||||
return <MediaLink link={props.link} />;
|
||||
return <MediaElement mime={`${type}/${extension}`} url={url.toString()} />;
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,17 @@ import { bytesToHex } from "@noble/hashes/utils";
|
||||
import { decode as invoiceDecode } from "light-bolt11-decoder";
|
||||
import { bech32 } from "bech32";
|
||||
import base32Decode from "base32-decode";
|
||||
import { HexKey, TaggedRawEvent, u256, EventKind, encodeTLV, NostrPrefix, decodeTLV, TLVEntryType } from "@snort/nostr";
|
||||
import {
|
||||
HexKey,
|
||||
TaggedRawEvent,
|
||||
u256,
|
||||
EventKind,
|
||||
encodeTLV,
|
||||
NostrPrefix,
|
||||
decodeTLV,
|
||||
TLVEntryType,
|
||||
RawEvent,
|
||||
} from "@snort/nostr";
|
||||
import { MetadataCache } from "Cache";
|
||||
|
||||
export const sha256 = (str: string | Uint8Array): u256 => {
|
||||
@ -466,7 +476,7 @@ export function chunks<T>(arr: T[], length: number) {
|
||||
return result;
|
||||
}
|
||||
|
||||
export function findTag(e: TaggedRawEvent, tag: string) {
|
||||
export function findTag(e: RawEvent, tag: string) {
|
||||
const maybeTag = e.tags.find(evTag => {
|
||||
return evTag[0] === tag;
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user