Prettier
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing

This commit is contained in:
Kieran 2023-09-12 15:02:16 +01:00
parent f42096fa64
commit 9e2453444d
Signed by: Kieran
GPG Key ID: DE71CEB3925BE941
74 changed files with 776 additions and 728 deletions

View File

@ -1,4 +1,4 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />

View File

@ -1,4 +1,4 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />

View File

@ -38,7 +38,7 @@ export class GiftWrapCache extends RefreshFeedCache<UnwrappedGift> {
} catch (e) { } catch (e) {
console.debug(e, v); console.debug(e, v);
} }
}) }),
) )
) )
.filter(a => a !== undefined) .filter(a => a !== undefined)

View File

@ -26,7 +26,7 @@ export default function CashuNuts({ token }: { token: string }) {
e.stopPropagation(); e.stopPropagation();
const lnurl = profile?.lud16 ?? ""; const lnurl = profile?.lud16 ?? "";
const url = `https://redeem.cashu.me?token=${encodeURIComponent(token)}&lightning=${encodeURIComponent( const url = `https://redeem.cashu.me?token=${encodeURIComponent(token)}&lightning=${encodeURIComponent(
lnurl lnurl,
)}&autopay=yes`; )}&autopay=yes`;
window.open(url, "_blank"); window.open(url, "_blank");
} }

View File

@ -34,7 +34,7 @@ export default function FollowButton(props: FollowButtonProps) {
if (publisher) { if (publisher) {
const ev = await publisher.contactList( const ev = await publisher.contactList(
follows.item.filter(a => a !== pubkey), follows.item.filter(a => a !== pubkey),
relays.item relays.item,
); );
System.BroadcastEvent(ev); System.BroadcastEvent(ev);
} }

View File

@ -261,9 +261,7 @@ export default function Nip5Service(props: Nip05ServiceProps) {
/> />
&nbsp;@&nbsp; &nbsp;@&nbsp;
<select value={domain} onChange={onDomainChange}> <select value={domain} onChange={onDomainChange}>
{serviceConfig?.domains.map(a => ( {serviceConfig?.domains.map(a => <option key={a.name}>{a.name}</option>)}
<option key={a.name}>{a.name}</option>
))}
</select> </select>
</div> </div>
)} )}

View File

@ -135,7 +135,7 @@ export function NoteInner(props: NoteProps) {
{ {
[Reaction.Positive]: [] as TaggedNostrEvent[], [Reaction.Positive]: [] as TaggedNostrEvent[],
[Reaction.Negative]: [] as TaggedNostrEvent[], [Reaction.Negative]: [] as TaggedNostrEvent[],
} },
); );
return { return {
[Reaction.Positive]: dedupeByPubkey(result[Reaction.Positive]), [Reaction.Positive]: dedupeByPubkey(result[Reaction.Positive]),
@ -150,7 +150,7 @@ export function NoteInner(props: NoteProps) {
...getReactions(related, ev.id, EventKind.TextNote).filter(e => e.tags.some(tagFilterOfTextRepost(e, ev.id))), ...getReactions(related, ev.id, EventKind.TextNote).filter(e => e.tags.some(tagFilterOfTextRepost(e, ev.id))),
...getReactions(related, ev.id, EventKind.Repost), ...getReactions(related, ev.id, EventKind.Repost),
]), ]),
[related, ev] [related, ev],
); );
const zaps = useMemo(() => { const zaps = useMemo(() => {
const sortedZaps = getReactions(related, ev.id, EventKind.ZapReceipt) const sortedZaps = getReactions(related, ev.id, EventKind.ZapReceipt)
@ -241,7 +241,7 @@ export function NoteInner(props: NoteProps) {
function goToEvent( function goToEvent(
e: React.MouseEvent, e: React.MouseEvent,
eTarget: TaggedNostrEvent, eTarget: TaggedNostrEvent,
isTargetAllowed: boolean = e.target === e.currentTarget isTargetAllowed: boolean = e.target === e.currentTarget,
) { ) {
if (!isTargetAllowed || opt?.canClick === false) { if (!isTargetAllowed || opt?.canClick === false) {
return; return;

View File

@ -2,7 +2,8 @@
border: 1px solid transparent; border: 1px solid transparent;
border-radius: 12px; border-radius: 12px;
box-shadow: 0px 0px 6px 1px rgba(182, 108, 156, 0.3); box-shadow: 0px 0px 6px 1px rgba(182, 108, 156, 0.3);
background: linear-gradient(var(--gray-superdark), var(--gray-superdark)) padding-box, background:
linear-gradient(var(--gray-superdark), var(--gray-superdark)) padding-box,
linear-gradient(90deg, #ef9644, #fd7c49, #ff5e58, #ff3b70, #ff088e, #eb00b1, #c31ed5, #7b41f6) border-box; linear-gradient(90deg, #ef9644, #fd7c49, #ff5e58, #ff3b70, #ff088e, #eb00b1, #c31ed5, #7b41f6) border-box;
} }

View File

@ -76,8 +76,8 @@ export function NoteCreator() {
setError( setError(
formatMessage({ formatMessage({
defaultMessage: "Invalid LNURL", defaultMessage: "Invalid LNURL",
}) }),
) ),
); );
return; return;
} }
@ -256,9 +256,9 @@ export function NoteCreator() {
? false ? false
: // otherwise return selectedCustomRelays with target relay added / removed : // otherwise return selectedCustomRelays with target relay added / removed
a.filter(el => a.filter(el =>
el === r ? e.target.checked : !selectedCustomRelays || selectedCustomRelays.includes(el) el === r ? e.target.checked : !selectedCustomRelays || selectedCustomRelays.includes(el),
) ),
) ),
) )
} }
/> />

View File

@ -71,7 +71,7 @@ export default function NoteFooter(props: NoteFooterProps) {
}, },
{ {
captureEvent: true, captureEvent: true,
} },
); );
function hasReacted(emoji: string) { function hasReacted(emoji: string) {

View File

@ -66,7 +66,7 @@ export default function NoteReaction(props: NoteReactionProps) {
const root = useMemo(() => extractRoot(), [ev, props.root, inView]); const root = useMemo(() => extractRoot(), [ev, props.root, inView]);
if (!inView) { if (!inView) {
return (<div className="card reaction" ref={ref}></div>) return <div className="card reaction" ref={ref}></div>;
} }
const isOpMuted = root && isMuted(root.pubkey); const isOpMuted = root && isMuted(root.pubkey);
const shouldNotBeRendered = isOpMuted || root?.kind !== EventKind.TextNote; const shouldNotBeRendered = isOpMuted || root?.kind !== EventKind.TextNote;

View File

@ -47,15 +47,15 @@ export default function Poll(props: PollProps) {
}, },
{ {
amount, amount,
} },
) ),
); );
} }
setVoting(opt); setVoting(opt);
const r = Object.keys(relays.item); const r = Object.keys(relays.item);
const zap = await publisher.zap(amount * 1000, props.ev.pubkey, r, props.ev.id, undefined, eb => const zap = await publisher.zap(amount * 1000, props.ev.pubkey, r, props.ev.id, undefined, eb =>
eb.tag(["poll_option", opt.toString()]) eb.tag(["poll_option", opt.toString()]),
); );
const lnurl = props.ev.tags.find(a => a[0] === "zap")?.[1] || pollerProfile?.lud16 || pollerProfile?.lud06; const lnurl = props.ev.tags.find(a => a[0] === "zap")?.[1] || pollerProfile?.lud16 || pollerProfile?.lud06;
@ -68,7 +68,7 @@ export default function Poll(props: PollProps) {
throw new Error( throw new Error(
formatMessage({ formatMessage({
defaultMessage: "Can't vote because LNURL service does not support zaps", defaultMessage: "Can't vote because LNURL service does not support zaps",
}) }),
); );
} }
@ -85,7 +85,7 @@ export default function Poll(props: PollProps) {
setError( setError(
formatMessage({ formatMessage({
defaultMessage: "Failed to send vote", defaultMessage: "Failed to send vote",
}) }),
); );
} }
} finally { } finally {

View File

@ -34,7 +34,7 @@ export default function PubkeyList({ ev, className }: { ev: NostrEvent; classNam
pk, pk,
Object.keys(login.relays.item), Object.keys(login.relays.item),
undefined, undefined,
`Zap from ${hexToBech32("note", ev.id)}` `Zap from ${hexToBech32("note", ev.id)}`,
); );
const invoice = await svc.getInvoice(amtSend, undefined, zap); const invoice = await svc.getInvoice(amtSend, undefined, zap);
if (invoice.pr) { if (invoice.pr) {

View File

@ -55,9 +55,9 @@ export function ReBroadcaster() {
? false ? false
: // otherwise return selectedCustomRelays with target relay added / removed : // otherwise return selectedCustomRelays with target relay added / removed
a.filter(el => a.filter(el =>
el === r ? e.target.checked : !selectedCustomRelays || selectedCustomRelays.includes(el) el === r ? e.target.checked : !selectedCustomRelays || selectedCustomRelays.includes(el),
) ),
) ),
) )
} }
/> />

View File

@ -60,7 +60,7 @@ const Reactions = ({ show, setShow, positive, negative, reposts, zaps }: Reactio
value: 3, value: 3,
}, },
] ]
: [] : [],
); );
const [tab, setTab] = useState(tabs[0]); const [tab, setTab] = useState(tabs[0]);

View File

@ -22,7 +22,7 @@ export default function Relay(props: RelayProps) {
const navigate = useNavigate(); const navigate = useNavigate();
const login = useLogin(); const login = useLogin();
const relaySettings = unwrap( const relaySettings = unwrap(
login.relays.item[props.addr] ?? System.Sockets.find(a => a.address === props.addr)?.settings ?? {} login.relays.item[props.addr] ?? System.Sockets.find(a => a.address === props.addr)?.settings ?? {},
); );
const state = useRelayState(props.addr); const state = useRelayState(props.addr);
const name = useMemo(() => getRelayName(props.addr), [props.addr]); const name = useMemo(() => getRelayName(props.addr), [props.addr]);
@ -34,7 +34,7 @@ export default function Relay(props: RelayProps) {
...login.relays.item, ...login.relays.item,
[props.addr]: o, [props.addr]: o,
}, },
unixNowMs() unixNowMs(),
); );
} }

View File

@ -285,7 +285,7 @@ export default function SendSats(props: SendSatsProps) {
{makeTab(ZapType.AnonZap, <FormattedMessage defaultMessage="Anon" description="Anonymous Zap" />)} {makeTab(ZapType.AnonZap, <FormattedMessage defaultMessage="Anon" description="Anonymous Zap" />)}
{makeTab( {makeTab(
ZapType.NonZap, ZapType.NonZap,
<FormattedMessage defaultMessage="Non-Zap" description="Non-Zap, Regular LN payment" /> <FormattedMessage defaultMessage="Non-Zap" description="Non-Zap, Regular LN payment" />,
)} )}
</div> </div>
</> </>

View File

@ -243,7 +243,7 @@ export default function Thread() {
if (t?.root?.key === "a" && t?.root?.value) { if (t?.root?.key === "a" && t?.root?.value) {
const parsed = t.root.value.split(":"); const parsed = t.root.value.split(":");
replyTo = thread.data?.find( replyTo = thread.data?.find(
a => a.kind === Number(parsed[0]) && a.pubkey === parsed[1] && findTag(a, "d") === parsed[2] a => a.kind === Number(parsed[0]) && a.pubkey === parsed[1] && findTag(a, "d") === parsed[2],
)?.id; )?.id;
} }
if (replyTo) { if (replyTo) {
@ -264,7 +264,7 @@ export default function Thread() {
thread.data?.find( thread.data?.find(
ne => ne =>
ne.id === currentId || ne.id === currentId ||
(link.type === NostrPrefix.Address && findTag(ne, "d") === currentId && ne.pubkey === link.author) (link.type === NostrPrefix.Address && findTag(ne, "d") === currentId && ne.pubkey === link.author),
) ?? (location.state && "sig" in location.state ? (location.state as TaggedNostrEvent) : undefined); ) ?? (location.state && "sig" in location.state ? (location.state as TaggedNostrEvent) : undefined);
if (currentNote) { if (currentNote) {
const currentThread = EventExt.extractThread(currentNote); const currentThread = EventExt.extractThread(currentNote);
@ -280,7 +280,7 @@ export default function Thread() {
if (replyTo.key === "a" && replyTo.value) { if (replyTo.key === "a" && replyTo.value) {
const parsed = replyTo.value.split(":"); const parsed = replyTo.value.split(":");
return thread.data?.find( return thread.data?.find(
a => a.kind === Number(parsed[0]) && a.pubkey === parsed[1] && findTag(a, "d") === parsed[2] a => a.kind === Number(parsed[0]) && a.pubkey === parsed[1] && findTag(a, "d") === parsed[2],
); );
} }
if (replyTo.value) { if (replyTo.value) {
@ -348,7 +348,7 @@ export default function Thread() {
notes={replies} notes={replies}
related={getAllReactions( related={getAllReactions(
thread.data, thread.data,
replies.map(a => a.id) replies.map(a => a.id),
)} )}
chains={chains} chains={chains}
onNavigate={navigateThread} onNavigate={navigateThread}

View File

@ -47,7 +47,7 @@ const Timeline = (props: TimelineProps) => {
?.filter(a => (props.postsOnly ? !a.tags.some(b => b[0] === "e") : true)) ?.filter(a => (props.postsOnly ? !a.tags.some(b => b[0] === "e") : true))
.filter(a => props.ignoreModeration || !isMuted(a.pubkey)); .filter(a => props.ignoreModeration || !isMuted(a.pubkey));
}, },
[props.postsOnly, muted, props.ignoreModeration] [props.postsOnly, muted, props.ignoreModeration],
); );
const mainFeed = useMemo(() => { const mainFeed = useMemo(() => {
@ -60,7 +60,7 @@ const Timeline = (props: TimelineProps) => {
(id: u256) => { (id: u256) => {
return (feed.related ?? []).filter(a => findTag(a, "e") === id); return (feed.related ?? []).filter(a => findTag(a, "e") === id);
}, },
[feed.related] [feed.related],
); );
const liveStreams = useMemo(() => { const liveStreams = useMemo(() => {
return (feed.main ?? []).filter(a => a.kind === EventKind.LiveEvent && findTag(a, "status") === "live"); return (feed.main ?? []).filter(a => a.kind === EventKind.LiveEvent && findTag(a, "status") === "live");

View File

@ -28,11 +28,11 @@ const TimelineFollows = (props: TimelineFollowsProps) => {
const [latest, setLatest] = useState(unixNow()); const [latest, setLatest] = useState(unixNow());
const feed = useSyncExternalStore( const feed = useSyncExternalStore(
cb => FollowsFeed.hook(cb, "*"), cb => FollowsFeed.hook(cb, "*"),
() => FollowsFeed.snapshot() () => FollowsFeed.snapshot(),
); );
const reactions = useReactions( const reactions = useReactions(
"follows-feed-reactions", "follows-feed-reactions",
feed.map(a => a.id) feed.map(a => a.id),
); );
const system = useContext(SnortContext); const system = useContext(SnortContext);
const login = useLogin(); const login = useLogin();
@ -48,7 +48,7 @@ const TimelineFollows = (props: TimelineFollowsProps) => {
?.filter(a => (props.postsOnly ? !a.tags.some(b => b[0] === "e") : true)) ?.filter(a => (props.postsOnly ? !a.tags.some(b => b[0] === "e") : true))
.filter(a => !isMuted(a.pubkey) && login.follows.item.includes(a.pubkey)); .filter(a => !isMuted(a.pubkey) && login.follows.item.includes(a.pubkey));
}, },
[props.postsOnly, muted, login.follows.timestamp] [props.postsOnly, muted, login.follows.timestamp],
); );
const mainFeed = useMemo(() => { const mainFeed = useMemo(() => {
@ -63,7 +63,7 @@ const TimelineFollows = (props: TimelineFollowsProps) => {
(id: u256) => { (id: u256) => {
return (reactions?.data ?? []).filter(a => findTag(a, "e") === id); return (reactions?.data ?? []).filter(a => findTag(a, "e") === id);
}, },
[reactions] [reactions],
); );
const liveStreams = useMemo(() => { const liveStreams = useMemo(() => {

View File

@ -17,7 +17,7 @@ export default function ZapstrEmbed({ ev }: { ev: NostrEvent }) {
ev.tags.find(a => a[0] === "d")?.[1] ?? "", ev.tags.find(a => a[0] === "d")?.[1] ?? "",
undefined, undefined,
ev.kind, ev.kind,
ev.pubkey ev.pubkey,
); );
return ( return (
<> <>

View File

@ -23,7 +23,7 @@ export default function useProfileBadges(pubkey?: HexKey) {
if (profileBadges.data) { if (profileBadges.data) {
return chunks( return chunks(
profileBadges.data.tags.filter(t => t[0] === "a" || t[0] === "e"), profileBadges.data.tags.filter(t => t[0] === "a" || t[0] === "e"),
2 2,
).reduce((acc, [a, e]) => { ).reduce((acc, [a, e]) => {
return { return {
...acc, ...acc,
@ -44,7 +44,7 @@ export default function useProfileBadges(pubkey?: HexKey) {
} }
return acc; return acc;
}, },
{ pubkeys: [], ds: [] } as BadgeAwards { pubkeys: [], ds: [] } as BadgeAwards,
) as BadgeAwards; ) as BadgeAwards;
}, [profile]); }, [profile]);
@ -77,7 +77,7 @@ export default function useProfileBadges(pubkey?: HexKey) {
}) })
.filter( .filter(
({ award, badge }) => ({ award, badge }) =>
badge && award.pubkey === badge.pubkey && award.tags.find(t => t[0] === "p" && t[1] === pubkey) badge && award.pubkey === badge.pubkey && award.tags.find(t => t[0] === "p" && t[1] === pubkey),
) )
.map(({ badge }) => unwrap(badge)); .map(({ badge }) => unwrap(badge));
} }

View File

@ -13,7 +13,7 @@ export function useReactions(subId: string, ids: Array<string>, others?: (rb: Re
.kinds( .kinds(
pref.enableReactions pref.enableReactions
? [EventKind.Reaction, EventKind.Repost, EventKind.ZapReceipt] ? [EventKind.Reaction, EventKind.Repost, EventKind.ZapReceipt]
: [EventKind.ZapReceipt, EventKind.Repost] : [EventKind.ZapReceipt, EventKind.Repost],
) )
.tag("e", ids); .tag("e", ids);
} }

View File

@ -14,7 +14,7 @@ export default function useFollowersFeed(pubkey?: HexKey) {
const followers = useMemo(() => { const followers = useMemo(() => {
const contactLists = followersFeed.data?.filter( const contactLists = followersFeed.data?.filter(
a => a.kind === EventKind.ContactList && a.tags.some(b => b[0] === "p" && b[1] === pubkey) a => a.kind === EventKind.ContactList && a.tags.some(b => b[0] === "p" && b[1] === pubkey),
); );
return [...new Set(contactLists?.map(a => a.pubkey))]; return [...new Set(contactLists?.map(a => a.pubkey))];
}, [followersFeed, pubkey]); }, [followersFeed, pubkey]);

View File

@ -85,7 +85,7 @@ export default function useLoginFeed() {
Nip4Chats.onEvent(loginFeed.data); Nip4Chats.onEvent(loginFeed.data);
const subs = loginFeed.data.filter( const subs = loginFeed.data.filter(
a => a.kind === EventKind.SnortSubscriptions && a.pubkey === bech32ToHex(SnortPubKey) a => a.kind === EventKind.SnortSubscriptions && a.pubkey === bech32ToHex(SnortPubKey),
); );
Promise.all( Promise.all(
subs.map(async a => { subs.map(async a => {
@ -97,7 +97,7 @@ export default function useLoginFeed() {
...ex, ...ex,
} as SubscriptionEvent; } as SubscriptionEvent;
} }
}) }),
).then(a => addSubscription(login, ...a.filter(a => a !== undefined).map(unwrap))); ).then(a => addSubscription(login, ...a.filter(a => a !== undefined).map(unwrap)));
} }
}, [loginFeed, publisher]); }, [loginFeed, publisher]);
@ -106,7 +106,7 @@ export default function useLoginFeed() {
useEffect(() => { useEffect(() => {
if (loginFeed.data) { if (loginFeed.data) {
const replies = loginFeed.data.filter( const replies = loginFeed.data.filter(
a => a.kind === EventKind.TextNote && !isMuted(a.pubkey) && a.created_at > readNotifications a => a.kind === EventKind.TextNote && !isMuted(a.pubkey) && a.created_at > readNotifications,
); );
replies.forEach(async nx => { replies.forEach(async nx => {
const n = await makeNotification(nx); const n = await makeNotification(nx);

View File

@ -35,11 +35,11 @@ export default function useThreadFeed(link: NostrLink) {
.kinds( .kinds(
pref.enableReactions pref.enableReactions
? [EventKind.Reaction, EventKind.TextNote, EventKind.Repost, EventKind.ZapReceipt] ? [EventKind.Reaction, EventKind.TextNote, EventKind.Repost, EventKind.ZapReceipt]
: [EventKind.TextNote, EventKind.ZapReceipt, EventKind.Repost] : [EventKind.TextNote, EventKind.ZapReceipt, EventKind.Repost],
) )
.tag( .tag(
"e", "e",
allEvents.map(a => a.id) allEvents.map(a => a.id),
); );
} }
if (trackingATags.length > 0) { if (trackingATags.length > 0) {
@ -50,7 +50,7 @@ export default function useThreadFeed(link: NostrLink) {
.authors(parsed.map(a => a[1])) .authors(parsed.map(a => a[1]))
.tag( .tag(
"d", "d",
parsed.map(a => a[2]) parsed.map(a => a[2]),
); );
sub.withFilter().tag("a", trackingATags); sub.withFilter().tag("a", trackingATags);
} }
@ -85,7 +85,7 @@ export default function useThreadFeed(link: NostrLink) {
id: b[1], id: b[1],
relay: b[2], relay: b[2],
}; };
}) }),
) )
.flat(); .flat();
const eTagsMissing = eTags.filter(a => !mainNotes.some(b => b.id === a.id)); const eTagsMissing = eTags.filter(a => !mainNotes.some(b => b.id === a.id));

View File

@ -43,7 +43,7 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel
.kinds( .kinds(
subject.type === "profile_keyword" subject.type === "profile_keyword"
? [EventKind.SetMetadata] ? [EventKind.SetMetadata]
: [EventKind.TextNote, EventKind.Repost, EventKind.Polls] : [EventKind.TextNote, EventKind.Repost, EventKind.Polls],
); );
if (subject.relay) { if (subject.relay) {
@ -149,7 +149,7 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel
.map(a => unwrap(a)[1]); .map(a => unwrap(a)[1]);
const repostsByKind1 = main.data const repostsByKind1 = main.data
.filter( .filter(
a => (a.kind === EventKind.Repost || a.kind === EventKind.TextNote) && a.tags.some(tagFilterOfTextRepost(a)) a => (a.kind === EventKind.Repost || a.kind === EventKind.TextNote) && a.tags.some(tagFilterOfTextRepost(a)),
) )
.map(a => a.tags.find(tagFilterOfTextRepost(a))) .map(a => a.tags.find(tagFilterOfTextRepost(a)))
.filter(a => a) .filter(a => a)

View File

@ -21,7 +21,7 @@ export default function useImgProxy() {
const result = hmacSha256( const result = hmacSha256(
utils.hexToBytes(unwrap(settings).key), utils.hexToBytes(unwrap(settings).key),
utils.hexToBytes(unwrap(settings).salt), utils.hexToBytes(unwrap(settings).salt),
te.encode(u) te.encode(u),
); );
return urlSafe(base64.encode(result)); return urlSafe(base64.encode(result));
} }

View File

@ -15,7 +15,7 @@ export function useInteractionCache(pubkey?: HexKey, event?: u256) {
const data = const data =
useSyncExternalStore( useSyncExternalStore(
c => InteractionCache.hook(c, id), c => InteractionCache.hook(c, id),
() => InteractionCache.snapshot().find(a => a.id === id) () => InteractionCache.snapshot().find(a => a.id === id),
) || EmptyInteraction; ) || EmptyInteraction;
return { return {
data: data, data: data,

View File

@ -4,6 +4,6 @@ import { useSyncExternalStore } from "react";
export default function useLogin() { export default function useLogin() {
return useSyncExternalStore( return useSyncExternalStore(
s => LoginStore.hook(s), s => LoginStore.hook(s),
() => LoginStore.snapshot() () => LoginStore.snapshot(),
); );
} }

View File

@ -96,7 +96,7 @@ export class MultiAccountStore extends ExternalStore<LoginSession> {
type: LoginSessionType, type: LoginSessionType,
relays?: Record<string, RelaySettings>, relays?: Record<string, RelaySettings>,
remoteSignerRelays?: Array<string>, remoteSignerRelays?: Array<string>,
privateKey?: string privateKey?: string,
) { ) {
if (this.#accounts.has(key)) { if (this.#accounts.has(key)) {
throw new Error("Already logged in with this pubkey"); throw new Error("Already logged in with this pubkey");

View File

@ -97,7 +97,7 @@ export class ServiceProvider {
path: string, path: string,
method?: "GET" | string, method?: "GET" | string,
body?: unknown, body?: unknown,
headers?: { [key: string]: string } headers?: { [key: string]: string },
): Promise<T | ServiceError> { ): Promise<T | ServiceError> {
try { try {
const rsp = await fetch(`${this.url}${path}`, { const rsp = await fetch(`${this.url}${path}`, {

View File

@ -54,7 +54,7 @@ export default class SnortServiceProvider extends ServiceProvider {
path: string, path: string,
method?: "GET" | string, method?: "GET" | string,
body?: unknown, body?: unknown,
headers?: { [key: string]: string } headers?: { [key: string]: string },
): Promise<T | ServiceError> { ): Promise<T | ServiceError> {
const auth = await this.#publisher.generic(eb => { const auth = await this.#publisher.generic(eb => {
eb.kind(EventKind.HttpAuthentication); eb.kind(EventKind.HttpAuthentication);

View File

@ -25,7 +25,7 @@ const ErrorPage = () => {
{JSON.stringify( {JSON.stringify(
error instanceof Error ? { name: error.name, message: error.message, stack: error.stack } : error, error instanceof Error ? { name: error.name, message: error.message, stack: error.stack } : error,
undefined, undefined,
" " " ",
)} )}
</pre> </pre>
} }

View File

@ -89,7 +89,7 @@ export default function Layout() {
useEffect(() => { useEffect(() => {
const osTheme = window.matchMedia("(prefers-color-scheme: light)"); const osTheme = window.matchMedia("(prefers-color-scheme: light)");
setTheme( setTheme(
preferences.theme === "system" && osTheme.matches ? "light" : preferences.theme === "light" ? "light" : "dark" preferences.theme === "system" && osTheme.matches ? "light" : preferences.theme === "light" ? "light" : "dark",
); );
osTheme.onchange = e => { osTheme.onchange = e => {
@ -174,7 +174,7 @@ const AccountHeader = () => {
const hasNotifications = useMemo( const hasNotifications = useMemo(
() => latestNotification > readNotifications, () => latestNotification > readNotifications,
[latestNotification, readNotifications] [latestNotification, readNotifications],
); );
const unreadDms = useMemo(() => (publicKey ? 0 : 0), [publicKey]); const unreadDms = useMemo(() => (publicKey ? 0 : 0), [publicKey]);

View File

@ -55,7 +55,7 @@ const Artwork: Array<ArtworkEntry> = [
export async function getNip05PubKey(addr: string): Promise<string> { export async function getNip05PubKey(addr: string): Promise<string> {
const [username, domain] = addr.split("@"); const [username, domain] = addr.split("@");
const rsp = await fetch( const rsp = await fetch(
`https://${domain}/.well-known/nostr.json?name=${encodeURIComponent(username.toLocaleLowerCase())}` `https://${domain}/.well-known/nostr.json?name=${encodeURIComponent(username.toLocaleLowerCase())}`,
); );
if (rsp.ok) { if (rsp.ok) {
const data = await rsp.json(); const data = await rsp.json();
@ -103,7 +103,7 @@ export default function LoginPage() {
setError( setError(
formatMessage({ formatMessage({
defaultMessage: "Unknown login error", defaultMessage: "Unknown login error",
}) }),
); );
} }
console.error(e); console.error(e);

View File

@ -75,7 +75,7 @@ export default function NotificationsPage() {
const notifications = useSyncExternalStore( const notifications = useSyncExternalStore(
c => Notifications.hook(c, "*"), c => Notifications.hook(c, "*"),
() => Notifications.snapshot() () => Notifications.snapshot(),
); );
const timeKey = (ev: NostrEvent) => { const timeKey = (ev: NostrEvent) => {
@ -119,7 +119,7 @@ function NotificationGroup({ evs }: { evs: Array<TaggedNostrEvent> }) {
return zap.anonZap ? "anon" : zap.sender ?? a.pubkey; return zap.anonZap ? "anon" : zap.sender ?? a.pubkey;
} }
return a.pubkey; return a.pubkey;
}) }),
); );
const firstPubkey = pubkeys[0]; const firstPubkey = pubkeys[0];
const firstPubkeyProfile = useUserProfile(inView ? (firstPubkey === "anon" ? "" : firstPubkey) : ""); const firstPubkeyProfile = useUserProfile(inView ? (firstPubkey === "anon" ? "" : firstPubkey) : "");
@ -213,7 +213,7 @@ function NotificationGroup({ evs }: { evs: Array<TaggedNostrEvent> }) {
pubkeys.length - 1, pubkeys.length - 1,
firstPubkey === "anon" firstPubkey === "anon"
? formatMessage({ defaultMessage: "Anon" }) ? formatMessage({ defaultMessage: "Anon" })
: getDisplayName(firstPubkeyProfile, firstPubkey) : getDisplayName(firstPubkeyProfile, firstPubkey),
)} )}
</div> </div>
)} )}

View File

@ -221,7 +221,7 @@ export default function ProfilePage() {
} as { [key: string]: Tab }; } as { [key: string]: Tab };
const [tab, setTab] = useState<Tab>(ProfileTab.Notes); const [tab, setTab] = useState<Tab>(ProfileTab.Notes);
const optionalTabs = [ProfileTab.Zaps, ProfileTab.Relays, ProfileTab.Bookmarks, ProfileTab.Muted].filter(a => const optionalTabs = [ProfileTab.Zaps, ProfileTab.Relays, ProfileTab.Bookmarks, ProfileTab.Muted].filter(a =>
unwrap(a) unwrap(a),
) as Tab[]; ) as Tab[];
const horizontalScroll = useHorizontalScroll(); const horizontalScroll = useHorizontalScroll();
@ -433,7 +433,7 @@ export default function ProfilePage() {
type: TLVEntryType.Author, type: TLVEntryType.Author,
length: 64, length: 64,
value: id, value: id,
})}` })}`,
) )
}> }>
<Icon name="envelope" size={16} /> <Icon name="envelope" size={16} />

View File

@ -73,7 +73,7 @@ export default function ZapPoolPage() {
const login = useLogin(); const login = useLogin();
const zapPool = useSyncExternalStore( const zapPool = useSyncExternalStore(
c => ZapPoolController.hook(c), c => ZapPoolController.hook(c),
() => ZapPoolController.snapshot() () => ZapPoolController.snapshot(),
); );
const { wallet } = useWallet(); const { wallet } = useWallet();

View File

@ -27,7 +27,7 @@ export default function AccountsPage() {
setError( setError(
formatMessage({ formatMessage({
defaultMessage: "Unknown login error", defaultMessage: "Unknown login error",
}) }),
); );
} }
console.error(e); console.error(e);

View File

@ -29,7 +29,7 @@ export default function LNForwardAddress({ handle }: { handle: ManageHandle }) {
setError( setError(
formatMessage({ formatMessage({
defaultMessage: "Invalid LNURL", defaultMessage: "Invalid LNURL",
}) }),
); );
return; return;
} }

View File

@ -39,7 +39,7 @@ const ConnectCashu = () => {
setError( setError(
formatMessage({ formatMessage({
defaultMessage: "Unknown error", defaultMessage: "Unknown error",
}) }),
); );
} }
} }

View File

@ -32,7 +32,7 @@ const ConnectLNC = () => {
setError( setError(
formatMessage({ formatMessage({
defaultMessage: "Unknown error", defaultMessage: "Unknown error",
}) }),
); );
} }
} }

View File

@ -37,7 +37,7 @@ const ConnectLNDHub = () => {
setError( setError(
formatMessage({ formatMessage({
defaultMessage: "Unknown error", defaultMessage: "Unknown error",
}) }),
); );
} }
} }

View File

@ -37,7 +37,7 @@ const ConnectNostrWallet = () => {
setError( setError(
formatMessage({ formatMessage({
defaultMessage: "Unknown error", defaultMessage: "Unknown error",
}) }),
); );
} }
} }

View File

@ -88,7 +88,7 @@ export default class SnortApi {
path: string, path: string,
method?: "GET" | string, method?: "GET" | string,
body?: { [key: string]: string }, body?: { [key: string]: string },
headers?: { [key: string]: string } headers?: { [key: string]: string },
): Promise<T> { ): Promise<T> {
if (!this.#publisher) { if (!this.#publisher) {
throw new Error("Publisher not set"); throw new Error("Publisher not set");
@ -110,7 +110,7 @@ export default class SnortApi {
path: string, path: string,
method?: "GET" | string, method?: "GET" | string,
body?: { [key: string]: string }, body?: { [key: string]: string },
headers?: { [key: string]: string } headers?: { [key: string]: string },
): Promise<T> { ): Promise<T> {
const rsp = await fetch(`${this.#url}${path}`, { const rsp = await fetch(`${this.#url}${path}`, {
method: method, method: method,

View File

@ -50,7 +50,7 @@ export async function openFile(): Promise<File | undefined> {
} }
}, 300); }, 300);
}, },
{ once: true } { once: true },
); );
}); });
} }
@ -209,7 +209,7 @@ export function dedupeByPubkey(events: TaggedNostrEvent[]) {
list: [...list, ev], list: [...list, ev],
}; };
}, },
{ list: [], seen: new Set([]) } { list: [], seen: new Set([]) },
); );
return deduped.list as TaggedNostrEvent[]; return deduped.list as TaggedNostrEvent[];
} }
@ -226,7 +226,7 @@ export function dedupeById<T extends { id: string }>(events: Array<T>) {
list: [...list, ev], list: [...list, ev],
}; };
}, },
{ list: [], seen: new Set([]) } { list: [], seen: new Set([]) },
); );
return deduped.list as Array<T>; return deduped.list as Array<T>;
} }
@ -487,7 +487,7 @@ export function kvToObject<T>(o: string, sep?: string) {
return [match[1], match[2]]; return [match[1], match[2]];
} }
return []; return [];
}) }),
) as T; ) as T;
} }

View File

@ -39,7 +39,7 @@ export const Toastore = new ToasterSlots();
export default function Toaster() { export default function Toaster() {
const toast = useSyncExternalStore( const toast = useSyncExternalStore(
c => Toastore.hook(c), c => Toastore.hook(c),
() => Toastore.snapshot() () => Toastore.snapshot(),
); );
return ( return (

View File

@ -12,7 +12,7 @@ import { magnetURIDecode } from "SnortUtils";
export default async function VoidCatUpload( export default async function VoidCatUpload(
file: File | Blob, file: File | Blob,
filename: string, filename: string,
publisher?: EventPublisher publisher?: EventPublisher,
): Promise<UploadResult> { ): Promise<UploadResult> {
const api = new VoidApi(VoidCatHost); const api = new VoidApi(VoidCatHost);
const uploader = api.getUploader(file); const uploader = api.getUploader(file);

View File

@ -57,6 +57,6 @@ export interface NutStashBackup {
mints: [ mints: [
{ {
mintURL: string; mintURL: string;
} },
]; ];
} }

View File

@ -126,7 +126,7 @@ export class LNCWallet implements LNWallet {
err => { err => {
this.#log(err); this.#log(err);
reject(err); reject(err);
} },
); );
}); });
} }

View File

@ -182,7 +182,7 @@ export class NostrConnectWallet implements LNWallet {
], ],
() => { () => {
// ignored // ignored
} },
); );
await this.#conn.SendAsync(evCommand); await this.#conn.SendAsync(evCommand);
return await new Promise<T>((resolve, reject) => { return await new Promise<T>((resolve, reject) => {

View File

@ -84,7 +84,7 @@ export class WebLNWallet implements LNWallet {
await window.webln?.makeInvoice({ await window.webln?.makeInvoice({
amount: req.amount, amount: req.amount,
defaultMemo: req.memo, defaultMemo: req.memo,
}) }),
); );
if (rsp) { if (rsp) {
const invoice = prToWalletInvoice(rsp.paymentRequest); const invoice = prToWalletInvoice(rsp.paymentRequest);

View File

@ -248,7 +248,7 @@ window.document.addEventListener("close", () => {
export function useWallet() { export function useWallet() {
const wallet = useSyncExternalStore<WalletStoreSnapshot>( const wallet = useSyncExternalStore<WalletStoreSnapshot>(
h => Wallets.hook(h), h => Wallets.hook(h),
() => Wallets.snapshot() () => Wallets.snapshot(),
); );
useEffect(() => { useEffect(() => {
if (wallet.wallet?.isReady() === false && wallet.wallet.canAutoLogin()) { if (wallet.wallet?.isReady() === false && wallet.wallet.canAutoLogin()) {

View File

@ -54,7 +54,7 @@ class ZapPool extends ExternalStore<Array<ZapPoolRecipient>> {
Toastore.push({ Toastore.push({
element: `Sent ${amtSend.toLocaleString()} sats to ${getDisplayName( element: `Sent ${amtSend.toLocaleString()} sats to ${getDisplayName(
profile, profile,
x.pubkey x.pubkey,
)} from your zap pool`, )} from your zap pool`,
expire: unixNow() + 10, expire: unixNow() + 10,
icon: "zap", icon: "zap",

View File

@ -2,7 +2,7 @@ import { bytesToHex } from "@noble/hashes/utils";
import { DefaultQueryOptimizer, FlatReqFilter, QueryOptimizer, ReqFilter } from "@snort/system"; import { DefaultQueryOptimizer, FlatReqFilter, QueryOptimizer, ReqFilter } from "@snort/system";
import { compress, expand_filter, flat_merge, get_diff, default as wasmInit } from "@snort/system-query"; import { compress, expand_filter, flat_merge, get_diff, default as wasmInit } from "@snort/system-query";
import WasmPath from "@snort/system-query/pkg/system_query_bg.wasm"; import WasmPath from "@snort/system-query/pkg/system_query_bg.wasm";
import { Bench } from 'tinybench'; import { Bench } from "tinybench";
const WasmQueryOptimizer = { const WasmQueryOptimizer = {
expandFilter: (f: ReqFilter) => { expandFilter: (f: ReqFilter) => {
@ -16,13 +16,13 @@ const WasmQueryOptimizer = {
}, },
compress: (all: Array<ReqFilter>) => { compress: (all: Array<ReqFilter>) => {
return compress(all) as Array<ReqFilter>; return compress(all) as Array<ReqFilter>;
} },
} as QueryOptimizer; } as QueryOptimizer;
const makeOnePubkey = () => { const makeOnePubkey = () => {
const rnd = globalThis.crypto.getRandomValues(new Uint8Array(32)); const rnd = globalThis.crypto.getRandomValues(new Uint8Array(32));
return bytesToHex(rnd); return bytesToHex(rnd);
} };
const randomPubkeys = (() => { const randomPubkeys = (() => {
const ret = []; const ret = [];
@ -35,48 +35,57 @@ const WasmQueryOptimizer = {
const testExpand = (q: QueryOptimizer) => { const testExpand = (q: QueryOptimizer) => {
q.expandFilter({ q.expandFilter({
kinds: [1, 2, 3], kinds: [1, 2, 3],
authors: randomPubkeys authors: randomPubkeys,
}); });
} };
const testGetDiff = (q: QueryOptimizer) => { const testGetDiff = (q: QueryOptimizer) => {
q.getDiff([{ q.getDiff(
[
{
kinds: [1, 2, 3], kinds: [1, 2, 3],
authors: randomPubkeys authors: randomPubkeys,
}], [{ },
],
[
{
kinds: [1, 2, 3, 4, 5], kinds: [1, 2, 3, 4, 5],
authors: randomPubkeys authors: randomPubkeys,
}]); },
} ],
);
};
const testFlatMerge = (q: QueryOptimizer) => { const testFlatMerge = (q: QueryOptimizer) => {
q.flatMerge(q.expandFilter({ q.flatMerge(
q.expandFilter({
kinds: [1, 6, 7, 6969], kinds: [1, 6, 7, 6969],
authors: randomPubkeys authors: randomPubkeys,
})); }),
} );
};
const testCompress = (q: QueryOptimizer) => { const testCompress = (q: QueryOptimizer) => {
q.compress([ q.compress([
{ {
kinds: [1, 6, 7, 6969], kinds: [1, 6, 7, 6969],
authors: randomPubkeys authors: randomPubkeys,
}, },
{ {
kinds: [1, 6, 7, 6969], kinds: [1, 6, 7, 6969],
authors: randomPubkeys authors: randomPubkeys,
}, },
{ {
kinds: [1, 6, 7, 6969], kinds: [1, 6, 7, 6969],
authors: randomPubkeys authors: randomPubkeys,
}, },
{ {
kinds: [1, 6, 7, 6969], kinds: [1, 6, 7, 6969],
authors: randomPubkeys authors: randomPubkeys,
}, },
{ {
kinds: [1, 6, 7, 6969], kinds: [1, 6, 7, 6969],
authors: randomPubkeys authors: randomPubkeys,
} },
]) ]);
} };
const wasmSuite = new Bench({ time: 1_000 }); const wasmSuite = new Bench({ time: 1_000 });
const suite = new Bench({ time: 1_000 }); const suite = new Bench({ time: 1_000 });
@ -86,7 +95,7 @@ const testCompress = (q: QueryOptimizer) => {
s.add("get_diff", () => testGetDiff(q)); s.add("get_diff", () => testGetDiff(q));
s.add("flat_merge", () => testFlatMerge(q)); s.add("flat_merge", () => testFlatMerge(q));
s.add("compress", () => testCompress(q)); s.add("compress", () => testCompress(q));
} };
addTests(suite, DefaultQueryOptimizer); addTests(suite, DefaultQueryOptimizer);
addTests(wasmSuite, WasmQueryOptimizer); addTests(wasmSuite, WasmQueryOptimizer);

View File

@ -116,7 +116,7 @@ export function createChatLink(type: ChatType, ...params: Array<string>) {
type: TLVEntryType.Author, type: TLVEntryType.Author,
length: params[0].length, length: params[0].length,
value: params[0], value: params[0],
} as TLVEntry } as TLVEntry,
)}`; )}`;
} }
case ChatType.PrivateDirectMessage: { case ChatType.PrivateDirectMessage: {
@ -127,7 +127,7 @@ export function createChatLink(type: ChatType, ...params: Array<string>) {
type: TLVEntryType.Author, type: TLVEntryType.Author,
length: params[0].length, length: params[0].length,
value: params[0], value: params[0],
} as TLVEntry } as TLVEntry,
)}`; )}`;
} }
case ChatType.PrivateGroupChat: { case ChatType.PrivateGroupChat: {
@ -139,8 +139,8 @@ export function createChatLink(type: ChatType, ...params: Array<string>) {
type: TLVEntryType.Author, type: TLVEntryType.Author,
length: a.length, length: a.length,
value: a, value: a,
} as TLVEntry) }) as TLVEntry,
) ),
)}`; )}`;
} }
} }
@ -161,14 +161,14 @@ export function useNip4Chat() {
const { publicKey } = useLogin(); const { publicKey } = useLogin();
return useSyncExternalStore( return useSyncExternalStore(
c => Nip4Chats.hook(c), c => Nip4Chats.hook(c),
() => Nip4Chats.snapshot(publicKey) () => Nip4Chats.snapshot(publicKey),
); );
} }
export function useNip29Chat() { export function useNip29Chat() {
return useSyncExternalStore( return useSyncExternalStore(
c => Nip29Chats.hook(c), c => Nip29Chats.hook(c),
() => Nip29Chats.snapshot() () => Nip29Chats.snapshot(),
); );
} }
@ -176,7 +176,7 @@ export function useNip24Chat() {
const { publicKey } = useLogin(); const { publicKey } = useLogin();
return useSyncExternalStore( return useSyncExternalStore(
c => Nip24Chats.hook(c), c => Nip24Chats.hook(c),
() => Nip24Chats.snapshot(publicKey) () => Nip24Chats.snapshot(publicKey),
); );
} }

View File

@ -39,8 +39,8 @@ export class Nip24ChatSystem extends ExternalStore<Array<Chat>> implements ChatS
value: v, value: v,
type: TLVEntryType.Author, type: TLVEntryType.Author,
length: v.length, length: v.length,
} as TLVEntry) }) as TLVEntry,
) ),
); );
}; };
return dedupe(messages.map(a => chatId(a))).map(a => { return dedupe(messages.map(a => chatId(a))).map(a => {
@ -72,7 +72,7 @@ export class Nip24ChatSystem extends ExternalStore<Array<Chat>> implements ChatS
} as { } as {
t: number; t: number;
title: string | undefined; title: string | undefined;
} },
); );
return { return {
type: ChatType.PrivateDirectMessage, type: ChatType.PrivateDirectMessage,

View File

@ -46,12 +46,12 @@ export class Nip29ChatSystem extends ExternalStore<Array<Chat>> implements ChatS
.map(a => a.tags.find(b => b[0] === "g")) .map(a => a.tags.find(b => b[0] === "g"))
.filter(a => a !== undefined) .filter(a => a !== undefined)
.map(a => unwrap(a)) .map(a => unwrap(a))
.map(a => `${a[2]}${a[1]}`) .map(a => `${a[2]}${a[1]}`),
); );
return groups.map(g => { return groups.map(g => {
const [relay, channel] = g.split("/", 2); const [relay, channel] = g.split("/", 2);
const messages = allMessages.filter( const messages = allMessages.filter(
a => `${a.tags.find(b => b[0] === "g")?.[2]}${a.tags.find(b => b[0] === "g")?.[1]}` === g a => `${a.tags.find(b => b[0] === "g")?.[2]}${a.tags.find(b => b[0] === "g")?.[1]}` === g,
); );
const lastRead = lastReadInChat(g); const lastRead = lastReadInChat(g);
return { return {

View File

@ -34,7 +34,7 @@ export class Nip4ChatSystem extends ExternalStore<Array<Chat>> implements ChatSy
const dms = this.#cache.snapshot(); const dms = this.#cache.snapshot();
const dmSince = dms.reduce( const dmSince = dms.reduce(
(acc, v) => (v.created_at > acc && v.kind === EventKind.DirectMessage ? (acc = v.created_at) : acc), (acc, v) => (v.created_at > acc && v.kind === EventKind.DirectMessage ? (acc = v.created_at) : acc),
0 0,
); );
this.#log("Loading DMS since %s", new Date(dmSince * 1000)); this.#log("Loading DMS since %s", new Date(dmSince * 1000));
@ -49,12 +49,15 @@ export class Nip4ChatSystem extends ExternalStore<Array<Chat>> implements ChatSy
listChats(pk: string): Chat[] { listChats(pk: string): Chat[] {
const myDms = this.#nip4Events(); const myDms = this.#nip4Events();
const chats = myDms.reduce((acc, v) => { const chats = myDms.reduce(
(acc, v) => {
const chatId = inChatWith(v, pk); const chatId = inChatWith(v, pk);
acc[chatId] ??= []; acc[chatId] ??= [];
acc[chatId].push(v); acc[chatId].push(v);
return acc; return acc;
}, {} as Record<string, Array<NostrEvent>>); },
{} as Record<string, Array<NostrEvent>>,
);
return [...Object.entries(chats)].map(([k, v]) => Nip4ChatSystem.createChatObj(k, v)); return [...Object.entries(chats)].map(([k, v]) => Nip4ChatSystem.createChatObj(k, v));
} }

View File

@ -9,7 +9,16 @@ import { StrictMode } from "react";
import * as ReactDOM from "react-dom/client"; import * as ReactDOM from "react-dom/client";
import { Provider } from "react-redux"; import { Provider } from "react-redux";
import { createBrowserRouter, RouterProvider } from "react-router-dom"; import { createBrowserRouter, RouterProvider } from "react-router-dom";
import { EventPublisher, NostrSystem, ProfileLoaderService, Nip7Signer, PowWorker, QueryOptimizer, FlatReqFilter, ReqFilter } from "@snort/system"; import {
EventPublisher,
NostrSystem,
ProfileLoaderService,
Nip7Signer,
PowWorker,
QueryOptimizer,
FlatReqFilter,
ReqFilter,
} from "@snort/system";
import { SnortContext } from "@snort/system-react"; import { SnortContext } from "@snort/system-react";
import * as serviceWorkerRegistration from "serviceWorkerRegistration"; import * as serviceWorkerRegistration from "serviceWorkerRegistration";
@ -51,7 +60,7 @@ const WasmQueryOptimizer = {
}, },
compress: (all: Array<ReqFilter>) => { compress: (all: Array<ReqFilter>) => {
return compress(all) as Array<ReqFilter>; return compress(all) as Array<ReqFilter>;
} },
} as QueryOptimizer; } as QueryOptimizer;
/** /**
@ -193,5 +202,5 @@ root.render(
</SnortContext.Provider> </SnortContext.Provider>
</IntlProvider> </IntlProvider>
</Provider> </Provider>
</StrictMode> </StrictMode>,
); );

View File

@ -19,7 +19,7 @@ const config = {
import: require.resolve("@snort/system/dist/pow-worker.js"), import: require.resolve("@snort/system/dist/pow-worker.js"),
filename: "pow.js", filename: "pow.js",
}, },
bench: "./src/benchmarks.ts" bench: "./src/benchmarks.ts",
}, },
target: "browserslist", target: "browserslist",
mode: isProduction ? "production" : "development", mode: isProduction ? "production" : "development",
@ -156,7 +156,7 @@ const config = {
extensions: ["...", ".tsx", ".ts", ".jsx", ".js"], extensions: ["...", ".tsx", ".ts", ".jsx", ".js"],
modules: ["...", __dirname, path.resolve(__dirname, "src")], modules: ["...", __dirname, path.resolve(__dirname, "src")],
fallback: { crypto: false }, fallback: { crypto: false },
} },
}; };
module.exports = () => config; module.exports = () => config;

View File

@ -4,7 +4,9 @@ const heap = new Array(128).fill(undefined);
heap.push(undefined, null, true, false); heap.push(undefined, null, true, false);
function getObject(idx) { return heap[idx]; } function getObject(idx) {
return heap[idx];
}
let heap_next = heap.length; let heap_next = heap.length;
@ -31,9 +33,17 @@ function getUint8Memory0() {
return cachedUint8Memory0; return cachedUint8Memory0;
} }
const cachedTextEncoder = (typeof TextEncoder !== 'undefined' ? new TextEncoder('utf-8') : { encode: () => { throw Error('TextEncoder not available') } } ); const cachedTextEncoder =
typeof TextEncoder !== "undefined"
? new TextEncoder("utf-8")
: {
encode: () => {
throw Error("TextEncoder not available");
},
};
const encodeString = (typeof cachedTextEncoder.encodeInto === 'function' const encodeString =
typeof cachedTextEncoder.encodeInto === "function"
? function (arg, view) { ? function (arg, view) {
return cachedTextEncoder.encodeInto(arg, view); return cachedTextEncoder.encodeInto(arg, view);
} }
@ -42,16 +52,17 @@ const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
view.set(buf); view.set(buf);
return { return {
read: arg.length, read: arg.length,
written: buf.length written: buf.length,
};
}; };
});
function passStringToWasm0(arg, malloc, realloc) { function passStringToWasm0(arg, malloc, realloc) {
if (realloc === undefined) { if (realloc === undefined) {
const buf = cachedTextEncoder.encode(arg); const buf = cachedTextEncoder.encode(arg);
const ptr = malloc(buf.length, 1) >>> 0; const ptr = malloc(buf.length, 1) >>> 0;
getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf); getUint8Memory0()
.subarray(ptr, ptr + buf.length)
.set(buf);
WASM_VECTOR_LEN = buf.length; WASM_VECTOR_LEN = buf.length;
return ptr; return ptr;
} }
@ -65,7 +76,7 @@ function passStringToWasm0(arg, malloc, realloc) {
for (; offset < len; offset++) { for (; offset < len; offset++) {
const code = arg.charCodeAt(offset); const code = arg.charCodeAt(offset);
if (code > 0x7F) break; if (code > 0x7f) break;
mem[ptr + offset] = code; mem[ptr + offset] = code;
} }
@ -73,7 +84,7 @@ function passStringToWasm0(arg, malloc, realloc) {
if (offset !== 0) { if (offset !== 0) {
arg = arg.slice(offset); arg = arg.slice(offset);
} }
ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0; ptr = realloc(ptr, len, (len = offset + arg.length * 3), 1) >>> 0;
const view = getUint8Memory0().subarray(ptr + offset, ptr + len); const view = getUint8Memory0().subarray(ptr + offset, ptr + len);
const ret = encodeString(arg, view); const ret = encodeString(arg, view);
@ -97,9 +108,18 @@ function getInt32Memory0() {
return cachedInt32Memory0; return cachedInt32Memory0;
} }
const cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } ); const cachedTextDecoder =
typeof TextDecoder !== "undefined"
? new TextDecoder("utf-8", { ignoreBOM: true, fatal: true })
: {
decode: () => {
throw Error("TextDecoder not available");
},
};
if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); }; if (typeof TextDecoder !== "undefined") {
cachedTextDecoder.decode();
}
function getStringFromWasm0(ptr, len) { function getStringFromWasm0(ptr, len) {
ptr = ptr >>> 0; ptr = ptr >>> 0;
@ -127,39 +147,39 @@ function getFloat64Memory0() {
function debugString(val) { function debugString(val) {
// primitive types // primitive types
const type = typeof val; const type = typeof val;
if (type == 'number' || type == 'boolean' || val == null) { if (type == "number" || type == "boolean" || val == null) {
return `${val}`; return `${val}`;
} }
if (type == 'string') { if (type == "string") {
return `"${val}"`; return `"${val}"`;
} }
if (type == 'symbol') { if (type == "symbol") {
const description = val.description; const description = val.description;
if (description == null) { if (description == null) {
return 'Symbol'; return "Symbol";
} else { } else {
return `Symbol(${description})`; return `Symbol(${description})`;
} }
} }
if (type == 'function') { if (type == "function") {
const name = val.name; const name = val.name;
if (typeof name == 'string' && name.length > 0) { if (typeof name == "string" && name.length > 0) {
return `Function(${name})`; return `Function(${name})`;
} else { } else {
return 'Function'; return "Function";
} }
} }
// objects // objects
if (Array.isArray(val)) { if (Array.isArray(val)) {
const length = val.length; const length = val.length;
let debug = '['; let debug = "[";
if (length > 0) { if (length > 0) {
debug += debugString(val[0]); debug += debugString(val[0]);
} }
for (let i = 1; i < length; i++) { for (let i = 1; i < length; i++) {
debug += ', ' + debugString(val[i]); debug += ", " + debugString(val[i]);
} }
debug += ']'; debug += "]";
return debug; return debug;
} }
// Test for built-in // Test for built-in
@ -171,14 +191,14 @@ function debugString(val) {
// Failed to match the standard '[object ClassName]' // Failed to match the standard '[object ClassName]'
return toString.call(val); return toString.call(val);
} }
if (className == 'Object') { if (className == "Object") {
// we're a user defined class or Object // we're a user defined class or Object
// JSON.stringify avoids problems with cycles, and is generally much // JSON.stringify avoids problems with cycles, and is generally much
// easier than looping through ownProperties of `val`. // easier than looping through ownProperties of `val`.
try { try {
return 'Object(' + JSON.stringify(val) + ')'; return "Object(" + JSON.stringify(val) + ")";
} catch (_) { } catch (_) {
return 'Object'; return "Object";
} }
} }
// errors // errors
@ -299,15 +319,16 @@ function handleError(f, args) {
} }
async function __wbg_load(module, imports) { async function __wbg_load(module, imports) {
if (typeof Response === 'function' && module instanceof Response) { if (typeof Response === "function" && module instanceof Response) {
if (typeof WebAssembly.instantiateStreaming === 'function') { if (typeof WebAssembly.instantiateStreaming === "function") {
try { try {
return await WebAssembly.instantiateStreaming(module, imports); return await WebAssembly.instantiateStreaming(module, imports);
} catch (e) { } catch (e) {
if (module.headers.get('Content-Type') != 'application/wasm') { if (module.headers.get("Content-Type") != "application/wasm") {
console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e); console.warn(
"`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n",
e,
);
} else { } else {
throw e; throw e;
} }
@ -316,13 +337,11 @@ async function __wbg_load(module, imports) {
const bytes = await module.arrayBuffer(); const bytes = await module.arrayBuffer();
return await WebAssembly.instantiate(bytes, imports); return await WebAssembly.instantiate(bytes, imports);
} else { } else {
const instance = await WebAssembly.instantiate(module, imports); const instance = await WebAssembly.instantiate(module, imports);
if (instance instanceof WebAssembly.Instance) { if (instance instanceof WebAssembly.Instance) {
return { instance, module }; return { instance, module };
} else { } else {
return instance; return instance;
} }
@ -337,7 +356,7 @@ function __wbg_get_imports() {
}; };
imports.wbg.__wbindgen_string_get = function (arg0, arg1) { imports.wbg.__wbindgen_string_get = function (arg0, arg1) {
const obj = getObject(arg1); const obj = getObject(arg1);
const ret = typeof(obj) === 'string' ? obj : undefined; const ret = typeof obj === "string" ? obj : undefined;
var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
var len1 = WASM_VECTOR_LEN; var len1 = WASM_VECTOR_LEN;
getInt32Memory0()[arg0 / 4 + 1] = len1; getInt32Memory0()[arg0 / 4 + 1] = len1;
@ -345,7 +364,7 @@ function __wbg_get_imports() {
}; };
imports.wbg.__wbindgen_is_object = function (arg0) { imports.wbg.__wbindgen_is_object = function (arg0) {
const val = getObject(arg0); const val = getObject(arg0);
const ret = typeof(val) === 'object' && val !== null; const ret = typeof val === "object" && val !== null;
return ret; return ret;
}; };
imports.wbg.__wbindgen_is_undefined = function (arg0) { imports.wbg.__wbindgen_is_undefined = function (arg0) {
@ -370,12 +389,12 @@ function __wbg_get_imports() {
}; };
imports.wbg.__wbindgen_boolean_get = function (arg0) { imports.wbg.__wbindgen_boolean_get = function (arg0) {
const v = getObject(arg0); const v = getObject(arg0);
const ret = typeof(v) === 'boolean' ? (v ? 1 : 0) : 2; const ret = typeof v === "boolean" ? (v ? 1 : 0) : 2;
return ret; return ret;
}; };
imports.wbg.__wbindgen_number_get = function (arg0, arg1) { imports.wbg.__wbindgen_number_get = function (arg0, arg1) {
const obj = getObject(arg1); const obj = getObject(arg1);
const ret = typeof(obj) === 'number' ? obj : undefined; const ret = typeof obj === "number" ? obj : undefined;
getFloat64Memory0()[arg0 / 8 + 1] = isLikeNone(ret) ? 0 : ret; getFloat64Memory0()[arg0 / 8 + 1] = isLikeNone(ret) ? 0 : ret;
getInt32Memory0()[arg0 / 4 + 0] = !isLikeNone(ret); getInt32Memory0()[arg0 / 4 + 0] = !isLikeNone(ret);
}; };
@ -407,17 +426,19 @@ function __wbg_get_imports() {
return addHeapObject(ret); return addHeapObject(ret);
}; };
imports.wbg.__wbindgen_is_function = function (arg0) { imports.wbg.__wbindgen_is_function = function (arg0) {
const ret = typeof(getObject(arg0)) === 'function'; const ret = typeof getObject(arg0) === "function";
return ret; return ret;
}; };
imports.wbg.__wbg_next_526fc47e980da008 = function (arg0) { imports.wbg.__wbg_next_526fc47e980da008 = function (arg0) {
const ret = getObject(arg0).next; const ret = getObject(arg0).next;
return addHeapObject(ret); return addHeapObject(ret);
}; };
imports.wbg.__wbg_next_ddb3312ca1c4e32a = function() { return handleError(function (arg0) { imports.wbg.__wbg_next_ddb3312ca1c4e32a = function () {
return handleError(function (arg0) {
const ret = getObject(arg0).next(); const ret = getObject(arg0).next();
return addHeapObject(ret); return addHeapObject(ret);
}, arguments) }; }, arguments);
};
imports.wbg.__wbg_done_5c1f01fb660d73b5 = function (arg0) { imports.wbg.__wbg_done_5c1f01fb660d73b5 = function (arg0) {
const ret = getObject(arg0).done; const ret = getObject(arg0).done;
return ret; return ret;
@ -430,14 +451,18 @@ function __wbg_get_imports() {
const ret = Symbol.iterator; const ret = Symbol.iterator;
return addHeapObject(ret); return addHeapObject(ret);
}; };
imports.wbg.__wbg_get_97b561fb56f034b5 = function() { return handleError(function (arg0, arg1) { imports.wbg.__wbg_get_97b561fb56f034b5 = function () {
return handleError(function (arg0, arg1) {
const ret = Reflect.get(getObject(arg0), getObject(arg1)); const ret = Reflect.get(getObject(arg0), getObject(arg1));
return addHeapObject(ret); return addHeapObject(ret);
}, arguments) }; }, arguments);
imports.wbg.__wbg_call_cb65541d95d71282 = function() { return handleError(function (arg0, arg1) { };
imports.wbg.__wbg_call_cb65541d95d71282 = function () {
return handleError(function (arg0, arg1) {
const ret = getObject(arg0).call(getObject(arg1)); const ret = getObject(arg0).call(getObject(arg1));
return addHeapObject(ret); return addHeapObject(ret);
}, arguments) }; }, arguments);
};
imports.wbg.__wbg_new_b51585de1b234aff = function () { imports.wbg.__wbg_new_b51585de1b234aff = function () {
const ret = new Object(); const ret = new Object();
return addHeapObject(ret); return addHeapObject(ret);
@ -506,9 +531,7 @@ function __wbg_get_imports() {
return imports; return imports;
} }
function __wbg_init_memory(imports, maybe_memory) { function __wbg_init_memory(imports, maybe_memory) {}
}
function __wbg_finalize_init(instance, module) { function __wbg_finalize_init(instance, module) {
wasm = instance.exports; wasm = instance.exports;
@ -517,7 +540,6 @@ function __wbg_finalize_init(instance, module) {
cachedInt32Memory0 = null; cachedInt32Memory0 = null;
cachedUint8Memory0 = null; cachedUint8Memory0 = null;
return wasm; return wasm;
} }
@ -540,12 +562,16 @@ function initSync(module) {
async function __wbg_init(input) { async function __wbg_init(input) {
if (wasm !== undefined) return wasm; if (wasm !== undefined) return wasm;
if (typeof input === 'undefined') { if (typeof input === "undefined") {
input = new URL('system_query_bg.wasm', import.meta.url); input = new URL("system_query_bg.wasm", import.meta.url);
} }
const imports = __wbg_get_imports(); const imports = __wbg_get_imports();
if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) { if (
typeof input === "string" ||
(typeof Request === "function" && input instanceof Request) ||
(typeof URL === "function" && input instanceof URL)
) {
input = fetch(input); input = fetch(input);
} }
@ -556,5 +582,5 @@ async function __wbg_init(input) {
return __wbg_finalize_init(instance, module); return __wbg_finalize_init(instance, module);
} }
export { initSync } export { initSync };
export default __wbg_init; export default __wbg_init;

View File

@ -254,9 +254,7 @@ export class NostrSystem extends ExternalStore<SystemSnapshot> implements System
if (existing.fromInstance === req.instance) { if (existing.fromInstance === req.instance) {
return existing; return existing;
} }
const filters = !req.options?.skipDiff const filters = !req.options?.skipDiff ? req.buildDiff(this, existing.filters) : req.build(this);
? req.buildDiff(this, existing.filters)
: req.build(this);
if (filters.length === 0 && !!req.options?.skipDiff) { if (filters.length === 0 && !!req.options?.skipDiff) {
return existing; return existing;
} else { } else {

View File

@ -1,7 +1,7 @@
import { ReqFilter } from "../nostr" import { ReqFilter } from "../nostr";
import { expandFilter } from "./request-expander" import { expandFilter } from "./request-expander";
import { flatMerge, mergeSimilar } from "./request-merger" import { flatMerge, mergeSimilar } from "./request-merger";
import { diffFilters } from "./request-splitter" import { diffFilters } from "./request-splitter";
export interface FlatReqFilter { export interface FlatReqFilter {
keys: number; keys: number;
@ -20,10 +20,10 @@ export interface FlatReqFilter {
} }
export interface QueryOptimizer { export interface QueryOptimizer {
expandFilter(f: ReqFilter): Array<FlatReqFilter> expandFilter(f: ReqFilter): Array<FlatReqFilter>;
getDiff(prev: Array<ReqFilter>, next: Array<ReqFilter>): Array<FlatReqFilter> getDiff(prev: Array<ReqFilter>, next: Array<ReqFilter>): Array<FlatReqFilter>;
flatMerge(all: Array<FlatReqFilter>): Array<ReqFilter> flatMerge(all: Array<FlatReqFilter>): Array<ReqFilter>;
compress(all: Array<ReqFilter>): Array<ReqFilter> compress(all: Array<ReqFilter>): Array<ReqFilter>;
} }
export const DefaultQueryOptimizer = { export const DefaultQueryOptimizer = {
@ -31,7 +31,10 @@ export const DefaultQueryOptimizer = {
return expandFilter(f); return expandFilter(f);
}, },
getDiff: (prev: Array<ReqFilter>, next: Array<ReqFilter>) => { getDiff: (prev: Array<ReqFilter>, next: Array<ReqFilter>) => {
const diff = diffFilters(prev.flatMap(a => expandFilter(a)), next.flatMap(a => expandFilter(a))); const diff = diffFilters(
prev.flatMap(a => expandFilter(a)),
next.flatMap(a => expandFilter(a)),
);
return diff.added; return diff.added;
}, },
flatMerge: (all: Array<FlatReqFilter>) => { flatMerge: (all: Array<FlatReqFilter>) => {
@ -39,5 +42,5 @@ export const DefaultQueryOptimizer = {
}, },
compress: (all: Array<ReqFilter>) => { compress: (all: Array<ReqFilter>) => {
return mergeSimilar(all); return mergeSimilar(all);
} },
} as QueryOptimizer; } as QueryOptimizer;

View File

@ -2852,6 +2852,7 @@ __metadata:
"@peculiar/webcrypto": ^1.4.3 "@peculiar/webcrypto": ^1.4.3
"@scure/base": ^1.1.2 "@scure/base": ^1.1.2
"@snort/shared": ^1.0.5 "@snort/shared": ^1.0.5
"@snort/system-query": "workspace:*"
"@stablelib/xchacha20": ^1.0.1 "@stablelib/xchacha20": ^1.0.1
"@types/debug": ^4.1.8 "@types/debug": ^4.1.8
"@types/jest": ^29.5.1 "@types/jest": ^29.5.1