mirror of
https://github.com/luminous-devs/lume.git
synced 2024-09-18 11:13:30 +00:00
polish
This commit is contained in:
parent
ce864c8990
commit
6b030f2902
@ -114,6 +114,9 @@ input::-ms-clear {
|
||||
inset: 20px 20px auto auto;
|
||||
cursor: zoom-out;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
[data-rmiz-content="found"] img,
|
||||
@ -153,7 +156,8 @@ input::-ms-clear {
|
||||
}
|
||||
|
||||
[data-rmiz-modal-overlay="visible"] {
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
[data-rmiz-modal-content] {
|
||||
|
@ -1,5 +1,3 @@
|
||||
import { Image } from '@shared/image';
|
||||
|
||||
import { useProfile } from '@utils/hooks/useProfile';
|
||||
import { displayNpub } from '@utils/shortenKey';
|
||||
|
||||
@ -20,7 +18,7 @@ export function MentionPopupItem({ pubkey, embed }: { pubkey: string; embed?: st
|
||||
|
||||
return (
|
||||
<div className="flex h-11 items-center justify-start gap-2.5 px-2 hover:bg-neutral-200 dark:bg-neutral-800">
|
||||
<Image
|
||||
<img
|
||||
src={user.picture || user.image}
|
||||
alt={pubkey}
|
||||
className="shirnk-0 h-8 w-8 rounded-md object-cover"
|
||||
|
@ -1,32 +0,0 @@
|
||||
import { minidenticon } from 'minidenticons';
|
||||
import { ImgHTMLAttributes, memo, useState } from 'react';
|
||||
|
||||
export const Image = memo(function Image({
|
||||
src,
|
||||
...props
|
||||
}: ImgHTMLAttributes<HTMLImageElement>) {
|
||||
const [isError, setIsError] = useState(false);
|
||||
|
||||
if (isError || !src) {
|
||||
const svgURI =
|
||||
'data:image/svg+xml;utf8,' + encodeURIComponent(minidenticon(props.alt, 90, 50));
|
||||
return (
|
||||
<img src={svgURI} alt={props.alt} {...props} style={{ backgroundColor: '#000' }} />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<img
|
||||
{...props}
|
||||
src={src}
|
||||
onError={({ currentTarget }) => {
|
||||
currentTarget.onerror = null;
|
||||
setIsError(true);
|
||||
}}
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
alt="lume default img"
|
||||
style={{ contentVisibility: 'auto' }}
|
||||
/>
|
||||
);
|
||||
});
|
@ -35,11 +35,11 @@ export function ArticleNote({ event }: { event: NDKEvent }) {
|
||||
<div className="mb-3 h-min w-full px-3">
|
||||
<div className="relative flex flex-col gap-2 overflow-hidden rounded-xl bg-neutral-50 pt-3 dark:bg-neutral-950">
|
||||
<User pubkey={event.pubkey} time={event.created_at} eventId={event.id} />
|
||||
<div>
|
||||
<div className="px-3">
|
||||
<Link
|
||||
to={`/notes/article/${event.id}`}
|
||||
preventScrollReset={true}
|
||||
className="flex w-full flex-col rounded-lg border border-neutral-200 bg-neutral-100 dark:border-neutral-800 dark:bg-neutral-900"
|
||||
className="flex w-full flex-col rounded-lg bg-neutral-100 dark:bg-neutral-900"
|
||||
>
|
||||
{metadata.image && (
|
||||
<img
|
||||
@ -48,7 +48,7 @@ export function ArticleNote({ event }: { event: NDKEvent }) {
|
||||
className="h-56 w-full rounded-t-lg object-cover"
|
||||
/>
|
||||
)}
|
||||
<div className="flex flex-col gap-1 rounded-b-lg bg-neutral-200 px-3 py-3 dark:bg-neutral-800">
|
||||
<div className="flex flex-col gap-1 rounded-b-lg rounded-t-lg bg-neutral-100 px-3 py-3 dark:bg-neutral-900">
|
||||
<h5 className="break-all font-semibold text-neutral-900 dark:text-neutral-100">
|
||||
{metadata.title}
|
||||
</h5>
|
||||
|
@ -6,7 +6,6 @@ export * from './child';
|
||||
export * from './notify';
|
||||
export * from './unknown';
|
||||
export * from './skeleton';
|
||||
export * from './stats';
|
||||
export * from './actions';
|
||||
export * from './actions/reaction';
|
||||
export * from './actions/reply';
|
||||
|
@ -3,7 +3,7 @@ import { download } from '@tauri-apps/plugin-upload';
|
||||
import { SyntheticEvent } from 'react';
|
||||
import Zoom from 'react-medium-image-zoom';
|
||||
|
||||
import { DownloadIcon } from '@shared/icons';
|
||||
import { CancelIcon, DownloadIcon } from '@shared/icons';
|
||||
|
||||
export function ImagePreview({ url }: { url: string }) {
|
||||
const downloadImage = async (url: string) => {
|
||||
@ -17,7 +17,7 @@ export function ImagePreview({ url }: { url: string }) {
|
||||
};
|
||||
|
||||
return (
|
||||
<Zoom key={url} zoomMargin={50}>
|
||||
<Zoom key={url} zoomMargin={50} IconUnzoom={() => <CancelIcon className="h-4 w-4" />}>
|
||||
<div className="group relative mt-2">
|
||||
<img
|
||||
src={url}
|
||||
|
@ -49,7 +49,7 @@ export function LinkPreview({ url }: { url: string }) {
|
||||
<img
|
||||
src={data.image}
|
||||
alt={url}
|
||||
className="h-44 w-full rounded-t-lg bg-white object-cover"
|
||||
className="h-48 w-full rounded-t-lg bg-white object-cover"
|
||||
/>
|
||||
) : null}
|
||||
<div className="flex flex-col items-start px-3 py-3">
|
||||
|
@ -12,25 +12,15 @@ export function Reply({ event, root }: { event: NDKEventWithReplies; root?: stri
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<User pubkey={event.pubkey} time={event.created_at} eventId={event.id} />
|
||||
<MemoizedTextKind content={event.content} />
|
||||
<div className="-ml-1">
|
||||
<NoteActions
|
||||
id={event.id}
|
||||
pubkey={event.pubkey}
|
||||
root={root}
|
||||
extraButtons={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="pl-4">
|
||||
<Collapsible.Root open={open} onOpenChange={setOpen}>
|
||||
{event.replies?.length > 0 ? (
|
||||
<div>
|
||||
<Collapsible.Root open={open} onOpenChange={setOpen}>
|
||||
<div className="rounded-xl bg-neutral-50 dark:bg-neutral-950">
|
||||
<div className="flex flex-col gap-2 pt-3">
|
||||
<User pubkey={event.pubkey} time={event.created_at} eventId={event.id} />
|
||||
<MemoizedTextKind content={event.content} />
|
||||
<div className="-ml-1 flex items-center justify-between">
|
||||
{event.replies?.length > 0 ? (
|
||||
<Collapsible.Trigger asChild>
|
||||
<div className="inline-flex h-10 items-center gap-1 font-semibold text-blue-500">
|
||||
<div className="ml-4 inline-flex h-14 items-center gap-1 font-semibold text-blue-500">
|
||||
<NavArrowDownIcon
|
||||
className={twMerge('h-3 w-3', open ? 'rotate-180 transform' : '')}
|
||||
/>
|
||||
@ -39,13 +29,23 @@ export function Reply({ event, root }: { event: NDKEventWithReplies; root?: stri
|
||||
(event.replies?.length === 1 ? 'reply' : 'replies')}
|
||||
</div>
|
||||
</Collapsible.Trigger>
|
||||
<Collapsible.Content>
|
||||
{event.replies?.map((sub) => <SubReply key={sub.id} event={sub} />)}
|
||||
</Collapsible.Content>
|
||||
</div>
|
||||
) : null}
|
||||
<NoteActions
|
||||
id={event.id}
|
||||
pubkey={event.pubkey}
|
||||
root={root}
|
||||
extraButtons={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={twMerge('px-3', open ? 'pb-3' : '')}>
|
||||
{event.replies?.length > 0 ? (
|
||||
<Collapsible.Content>
|
||||
{event.replies?.map((sub) => <SubReply key={sub.id} event={sub} />)}
|
||||
</Collapsible.Content>
|
||||
) : null}
|
||||
</Collapsible.Root>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Collapsible.Root>
|
||||
);
|
||||
}
|
||||
|
@ -48,6 +48,7 @@ export function ReplyList({ eventId }: { eventId: string }) {
|
||||
|
||||
return (
|
||||
<div className="mt-3 flex flex-col gap-5">
|
||||
<h3 className="font-semibold">Replies</h3>
|
||||
{data?.length === 0 ? (
|
||||
<div className="mt-2 flex w-full items-center justify-center">
|
||||
<div className="flex flex-col items-center justify-center gap-2 py-6">
|
||||
|
@ -5,7 +5,7 @@ import { User } from '@shared/user';
|
||||
|
||||
export function SubReply({ event }: { event: NDKEvent }) {
|
||||
return (
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex flex-col gap-2 rounded-lg bg-neutral-100 pt-3 dark:bg-neutral-900">
|
||||
<User pubkey={event.pubkey} time={event.created_at} eventId={event.id} />
|
||||
<MemoizedTextKind content={event.content} />
|
||||
<div className="-ml-1">
|
||||
|
@ -1,85 +0,0 @@
|
||||
import { NDKEvent, NDKFilter } from '@nostr-dev-kit/ndk';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { decode } from 'light-bolt11-decoder';
|
||||
|
||||
import { useNDK } from '@libs/ndk/provider';
|
||||
|
||||
import { LoaderIcon } from '@shared/icons';
|
||||
|
||||
import { compactNumber } from '@utils/number';
|
||||
|
||||
export function NoteStats({ id }: { id: string }) {
|
||||
const { ndk } = useNDK();
|
||||
const { status, data } = useQuery({
|
||||
queryKey: ['note-stats', id],
|
||||
queryFn: async () => {
|
||||
let reactions = 0;
|
||||
let reposts = 0;
|
||||
let zaps = 0;
|
||||
|
||||
const filter: NDKFilter = {
|
||||
'#e': [id],
|
||||
kinds: [6, 7, 9735],
|
||||
};
|
||||
|
||||
const events = await ndk.fetchEvents(filter);
|
||||
events.forEach((event: NDKEvent) => {
|
||||
switch (event.kind) {
|
||||
case 6:
|
||||
reposts += 1;
|
||||
break;
|
||||
case 7:
|
||||
reactions += 1;
|
||||
break;
|
||||
case 9735: {
|
||||
const bolt11 = event.tags.find((tag) => tag[0] === 'bolt11')[1];
|
||||
if (bolt11) {
|
||||
const decoded = decode(bolt11);
|
||||
const amount = decoded.sections.find((item) => item.name === 'amount');
|
||||
const sats = amount.value / 1000;
|
||||
zaps += sats;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
return { reposts, reactions, zaps };
|
||||
},
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnReconnect: false,
|
||||
});
|
||||
|
||||
if (status === 'pending') {
|
||||
return (
|
||||
<div className="flex h-11 items-center">
|
||||
<LoaderIcon className="h-4 w-4 animate-spin text-white" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mt-3 flex w-full flex-wrap gap-2">
|
||||
<div className="flex flex-1 flex-col rounded-lg bg-neutral-100 px-3 py-2 dark:bg-neutral-900">
|
||||
<div className="text-lg font-semibold text-neutral-900 dark:text-neutral-100">
|
||||
{compactNumber.format(data.reactions)}
|
||||
</div>
|
||||
<div className="text-sm text-neutral-500 dark:text-neutral-300">Reactions</div>
|
||||
</div>
|
||||
<div className="flex flex-1 flex-col rounded-lg bg-neutral-100 px-3 py-2 dark:bg-neutral-900">
|
||||
<div className="text-lg font-semibold text-neutral-900 dark:text-neutral-100">
|
||||
{compactNumber.format(data.reposts)}
|
||||
</div>
|
||||
<div className="text-sm text-neutral-500 dark:text-neutral-300">Reposts</div>
|
||||
</div>
|
||||
<div className="flex flex-1 flex-col rounded-lg bg-neutral-100 px-3 py-2 dark:bg-neutral-900">
|
||||
<div className="text-lg font-semibold text-neutral-900 dark:text-neutral-100">
|
||||
{compactNumber.format(data.zaps)}
|
||||
</div>
|
||||
<div className="text-sm text-neutral-500 dark:text-neutral-300">Zaps</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -298,7 +298,7 @@ export const User = memo(function User({
|
||||
alt={pubkey}
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
className="h-10 w-10 rounded-lg"
|
||||
className="h-10 w-10 rounded-lg object-cover"
|
||||
/>
|
||||
<Avatar.Fallback delayMs={300}>
|
||||
<img
|
||||
|
@ -3,8 +3,6 @@ import { useEffect, useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
import { UserStats } from '@app/users/components/stats';
|
||||
|
||||
import { useNDK } from '@libs/ndk/provider';
|
||||
import { useStorage } from '@libs/storage/provider';
|
||||
|
||||
@ -71,10 +69,9 @@ export function UserProfile({ pubkey }: { pubkey: string }) {
|
||||
<img
|
||||
src={user?.picture || user?.image}
|
||||
alt={pubkey}
|
||||
className="h-12 w-12 shrink-0 rounded-lg"
|
||||
className="h-12 w-12 shrink-0 rounded-lg object-cover"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
style={{ contentVisibility: 'auto' }}
|
||||
/>
|
||||
<div className="inline-flex items-center gap-2">
|
||||
{followed ? (
|
||||
@ -102,7 +99,7 @@ export function UserProfile({ pubkey }: { pubkey: string }) {
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-2 flex flex-1 flex-col">
|
||||
<div className="mt-2 flex flex-1 flex-col gap-1.5">
|
||||
<div className="flex flex-col">
|
||||
<h5 className="text-lg font-semibold">
|
||||
{user?.name || user?.display_name || user?.displayName || 'Anon'}
|
||||
@ -119,11 +116,8 @@ export function UserProfile({ pubkey }: { pubkey: string }) {
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col gap-3">
|
||||
<p className="mb-3 mt-2 max-w-[500px] select-text break-words text-neutral-900 dark:text-neutral-100">
|
||||
{user?.about}
|
||||
</p>
|
||||
<UserStats pubkey={pubkey} />
|
||||
<div className="max-w-[500px] select-text break-words text-neutral-900 dark:text-neutral-100">
|
||||
{user?.about}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -17,6 +17,8 @@ import { TitleBar } from '@shared/titleBar';
|
||||
import { WidgetWrapper } from '@shared/widgets';
|
||||
import { LiveUpdater } from '@shared/widgets';
|
||||
|
||||
import { FETCH_LIMIT } from '@stores/constants';
|
||||
|
||||
export function NewsfeedWidget() {
|
||||
const { db } = useStorage();
|
||||
const { relayUrls, ndk, fetcher } = useNDK();
|
||||
@ -40,7 +42,7 @@ export function NewsfeedWidget() {
|
||||
kinds: [NDKKind.Text, NDKKind.Repost],
|
||||
authors: db.account.circles,
|
||||
},
|
||||
20,
|
||||
FETCH_LIMIT,
|
||||
{ asOf: pageParam === 0 ? undefined : pageParam, abortSignal: signal }
|
||||
);
|
||||
|
||||
|
@ -7,7 +7,6 @@ import { useNDK } from '@libs/ndk/provider';
|
||||
import { useStorage } from '@libs/storage/provider';
|
||||
|
||||
import { FollowIcon, UnfollowIcon } from '@shared/icons';
|
||||
import { Image } from '@shared/image';
|
||||
|
||||
import { compactNumber } from '@utils/number';
|
||||
import { shortenKey } from '@utils/shortenKey';
|
||||
@ -95,8 +94,9 @@ export function NostrBandUserProfile({ data }: { data: Profile }) {
|
||||
<div className="rounded-xl bg-neutral-100 px-5 py-5 dark:bg-neutral-900">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="inline-flex items-center gap-2">
|
||||
<Image
|
||||
<img
|
||||
src={profile.picture}
|
||||
alt={data.pubkey}
|
||||
className="h-11 w-11 shrink-0 rounded-lg object-cover"
|
||||
/>
|
||||
<div className="inline-flex flex-col">
|
||||
@ -128,12 +128,10 @@ export function NostrBandUserProfile({ data }: { data: Profile }) {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-2">
|
||||
<p className="whitespace-pre-line break-words text-neutral-900 dark:text-neutral-100">
|
||||
{profile.about || profile.bio}
|
||||
</p>
|
||||
<div className="mt-2 whitespace-pre-line break-words text-neutral-900 dark:text-neutral-100">
|
||||
{profile.about || profile.bio}
|
||||
</div>
|
||||
<div className="mt-8">
|
||||
<div className="mt-5">
|
||||
{status === 'pending' ? (
|
||||
<p>Loading...</p>
|
||||
) : (
|
||||
|
@ -11,6 +11,8 @@ import { MemoizedNotifyNote, NoteSkeleton } from '@shared/notes';
|
||||
import { TitleBar } from '@shared/titleBar';
|
||||
import { WidgetWrapper } from '@shared/widgets';
|
||||
|
||||
import { FETCH_LIMIT } from '@stores/constants';
|
||||
|
||||
import { useNostr } from '@utils/hooks/useNostr';
|
||||
import { sendNativeNotification } from '@utils/notification';
|
||||
|
||||
@ -37,7 +39,7 @@ export function NotificationWidget() {
|
||||
kinds: [NDKKind.Text, NDKKind.Repost, NDKKind.Reaction, NDKKind.Zap],
|
||||
'#p': [db.account.pubkey],
|
||||
},
|
||||
20,
|
||||
FETCH_LIMIT,
|
||||
{ asOf: pageParam === 0 ? undefined : pageParam, abortSignal: signal }
|
||||
);
|
||||
|
||||
|
@ -8,7 +8,7 @@ export const FULL_RELAYS = [
|
||||
'wss://nostr.mutinywallet.com',
|
||||
];
|
||||
|
||||
export const FETCH_LIMIT = 50;
|
||||
export const FETCH_LIMIT = 20;
|
||||
|
||||
export const WidgetKinds = {
|
||||
local: {
|
||||
|
@ -1,153 +0,0 @@
|
||||
import { nip19 } from 'nostr-tools';
|
||||
import {
|
||||
AddressPointer,
|
||||
EventPointer,
|
||||
ProfilePointer,
|
||||
} from 'nostr-tools/lib/types/nip19';
|
||||
|
||||
import { RichContent } from '@utils/types';
|
||||
|
||||
function isURL(string: string) {
|
||||
try {
|
||||
const url = new URL(string);
|
||||
if (url.protocol.length > 0) {
|
||||
if (url.protocol === 'https:' || url.protocol === 'http:') {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function parser(content: string) {
|
||||
const richContent: RichContent = {
|
||||
parsed: null,
|
||||
images: [],
|
||||
videos: [],
|
||||
links: [],
|
||||
notes: [],
|
||||
};
|
||||
|
||||
const parsed = content
|
||||
.trim()
|
||||
.split(/(\s+)/)
|
||||
.map((word) => {
|
||||
// url
|
||||
if (isURL(word)) {
|
||||
const url = new URL(word);
|
||||
url.search = '';
|
||||
|
||||
if (url.pathname.match(/\.(jpg|jpeg|gif|png|webp|avif|tiff)$/)) {
|
||||
// image url
|
||||
richContent.images.push(word);
|
||||
// remove url from original content
|
||||
return word.replace(word, '');
|
||||
}
|
||||
|
||||
if (url.pathname.match(/\.(mp4|mov|webm|wmv|flv|mts|avi|ogv|mkv|mp3|m3u8)$/)) {
|
||||
// video url
|
||||
richContent.videos.push(word);
|
||||
// remove url from original content
|
||||
return word.replace(word, '');
|
||||
}
|
||||
|
||||
// normal url
|
||||
if (richContent.links.length < 1) {
|
||||
richContent.links.push(url.toString());
|
||||
}
|
||||
}
|
||||
|
||||
// hashtag
|
||||
if (word.startsWith('#') && word.length > 1) {
|
||||
return word.replace(word, `<Hashtag tag='${word}' />`);
|
||||
}
|
||||
|
||||
// boost
|
||||
if (word.startsWith('$prism') && word.length > 1) {
|
||||
return word.replace(word, `<Boost boost='${word}' />`);
|
||||
}
|
||||
|
||||
// nostr account references (depreciated)
|
||||
if (word.startsWith('@npub1')) {
|
||||
const npub = word.replace('@', '').replace(/[^a-zA-Z0-9 ]/g, '');
|
||||
try {
|
||||
const pubkey = nip19.decode(npub).data as string;
|
||||
return word.replace(word, `<MentionUser pubkey='${pubkey}' />`);
|
||||
} catch {
|
||||
return word;
|
||||
}
|
||||
}
|
||||
|
||||
// nostr account references
|
||||
if (word.startsWith('nostr:npub1') || word.startsWith('npub1')) {
|
||||
const npub = word.replace('nostr:', '').replace(/[^a-zA-Z0-9 ]/g, '');
|
||||
try {
|
||||
const pubkey = nip19.decode(npub).data as string;
|
||||
return word.replace(word, `<MentionUser pubkey='${pubkey}' />`);
|
||||
} catch {
|
||||
return word;
|
||||
}
|
||||
}
|
||||
|
||||
// nostr profile references
|
||||
if (word.startsWith('nostr:nprofile1') || word.startsWith('nprofile1')) {
|
||||
const nprofile = word.replace('nostr:', '').replace(/[^a-zA-Z0-9 ]/g, '');
|
||||
try {
|
||||
const decoded = nip19.decode(nprofile).data as ProfilePointer;
|
||||
return word.replace(word, `<MentionUser pubkey='${decoded.pubkey}' />`);
|
||||
} catch {
|
||||
return word;
|
||||
}
|
||||
}
|
||||
|
||||
// nostr address references
|
||||
if (word.startsWith('nostr:naddr1') || word.startsWith('naddr1')) {
|
||||
const naddr = word.replace('nostr:', '').replace(/[^a-zA-Z0-9 ]/g, '');
|
||||
try {
|
||||
const decoded = nip19.decode(naddr).data as AddressPointer;
|
||||
return word.replace(word, `<MentionUser pubkey='${decoded.pubkey}' />`);
|
||||
} catch {
|
||||
return word;
|
||||
}
|
||||
}
|
||||
|
||||
// lightning invoice
|
||||
if (word.startsWith('lnbc') && word.length > 60) {
|
||||
return word.replace(word, `<Invoice invoice='${word}' />`);
|
||||
}
|
||||
|
||||
// nostr note references
|
||||
if (word.startsWith('nostr:note1') || word.startsWith('note1')) {
|
||||
const note = word.replace('nostr:', '').replace(/[^a-zA-Z0-9 ]/g, '');
|
||||
try {
|
||||
const eventId = nip19.decode(note).data as string;
|
||||
richContent.notes.push(eventId);
|
||||
return word.replace(word, '');
|
||||
} catch {
|
||||
return word;
|
||||
}
|
||||
}
|
||||
|
||||
// nostr event references
|
||||
if (word.startsWith('nostr:nevent1') || word.startsWith('nevent1')) {
|
||||
const nevent = word.replace('nostr:', '').replace(/[^a-zA-Z0-9 ]/g, '');
|
||||
try {
|
||||
const decoded = nip19.decode(nevent).data as EventPointer;
|
||||
richContent.notes.push(decoded.id);
|
||||
return word.replace(word, '');
|
||||
} catch {
|
||||
return word;
|
||||
}
|
||||
}
|
||||
|
||||
return word;
|
||||
});
|
||||
|
||||
// update content with parsed version
|
||||
richContent.parsed = parsed.join(' ').trim();
|
||||
return richContent;
|
||||
}
|
Loading…
Reference in New Issue
Block a user