feat: make sig checks optional
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Kieran 2023-10-13 16:34:31 +01:00
parent 87bb9dafeb
commit 93e8e0bbae
Signed by: Kieran
GPG Key ID: DE71CEB3925BE941
51 changed files with 171 additions and 240 deletions

View File

@ -19,7 +19,7 @@ export interface DMProps {
export default function DM(props: DMProps) { export default function DM(props: DMProps) {
const { publicKey } = useLogin(s => ({ publicKey: s.publicKey })); const { publicKey } = useLogin(s => ({ publicKey: s.publicKey }));
const publisher = useEventPublisher(); const { publisher } = useEventPublisher();
const msg = props.data; const msg = props.data;
const [content, setContent] = useState<string>(); const [content, setContent] = useState<string>();
const { ref, inView } = useInView({ triggerOnce: true }); const { ref, inView } = useInView({ triggerOnce: true });

View File

@ -6,7 +6,6 @@ import { useState } from "react";
import useFileUpload from "Upload"; import useFileUpload from "Upload";
import { openFile } from "SnortUtils"; import { openFile } from "SnortUtils";
import Textarea from "../Textarea"; import Textarea from "../Textarea";
import { System } from "index";
import { Chat } from "chat"; import { Chat } from "chat";
export default function WriteMessage({ chat }: { chat: Chat }) { export default function WriteMessage({ chat }: { chat: Chat }) {
@ -15,7 +14,7 @@ export default function WriteMessage({ chat }: { chat: Chat }) {
const [uploading, setUploading] = useState(false); const [uploading, setUploading] = useState(false);
const [otherEvents, setOtherEvents] = useState<Array<NostrEvent>>([]); const [otherEvents, setOtherEvents] = useState<Array<NostrEvent>>([]);
const [error, setError] = useState(""); const [error, setError] = useState("");
const publisher = useEventPublisher(); const { publisher, system } = useEventPublisher();
const uploader = useFileUpload(); const uploader = useFileUpload();
async function attachFile() { async function attachFile() {
@ -59,7 +58,7 @@ export default function WriteMessage({ chat }: { chat: Chat }) {
if (msg && publisher && chat) { if (msg && publisher && chat) {
setSending(true); setSending(true);
const ev = await chat.createMessage(msg, publisher); const ev = await chat.createMessage(msg, publisher);
await chat.sendMessage(ev, System); await chat.sendMessage(ev, system);
setMsg(""); setMsg("");
setSending(false); setSending(false);
} }

View File

@ -16,7 +16,7 @@ import { WalletInvoiceState } from "Wallet";
export default function PubkeyList({ ev, className }: { ev: NostrEvent; className?: string }) { export default function PubkeyList({ ev, className }: { ev: NostrEvent; className?: string }) {
const wallet = useWallet(); const wallet = useWallet();
const login = useLogin(); const login = useLogin();
const publisher = useEventPublisher(); const { publisher } = useEventPublisher();
const ids = dedupe(ev.tags.filter(a => a[0] === "p").map(a => a[1])); const ids = dedupe(ev.tags.filter(a => a[0] === "p").map(a => a[1]));
async function zapAll() { async function zapAll() {

View File

@ -6,7 +6,6 @@ import { NostrEvent, OkResponse } from "@snort/system";
import AsyncButton from "Element/AsyncButton"; import AsyncButton from "Element/AsyncButton";
import Icon from "Icons/Icon"; import Icon from "Icons/Icon";
import { getRelayName, sanitizeRelayUrl } from "SnortUtils"; import { getRelayName, sanitizeRelayUrl } from "SnortUtils";
import { System } from "index";
import { removeRelay } from "Login"; import { removeRelay } from "Login";
import useLogin from "Hooks/useLogin"; import useLogin from "Hooks/useLogin";
import useEventPublisher from "Hooks/useEventPublisher"; import useEventPublisher from "Hooks/useEventPublisher";
@ -23,7 +22,7 @@ export function NoteBroadcaster({
const [results, setResults] = useState<Array<OkResponse>>([]); const [results, setResults] = useState<Array<OkResponse>>([]);
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();
const login = useLogin(); const login = useLogin();
const publisher = useEventPublisher(); const { publisher, system } = useEventPublisher();
async function sendEventToRelays(ev: NostrEvent) { async function sendEventToRelays(ev: NostrEvent) {
if (customRelays) { if (customRelays) {
@ -31,7 +30,7 @@ export function NoteBroadcaster({
await Promise.all( await Promise.all(
customRelays.map(async r => { customRelays.map(async r => {
try { try {
return await System.WriteOnceToRelay(r, ev); return await system.WriteOnceToRelay(r, ev);
} catch (e) { } catch (e) {
console.error(e); console.error(e);
} }
@ -39,7 +38,7 @@ export function NoteBroadcaster({
), ),
); );
} else { } else {
return await System.BroadcastEvent(ev, r => setResults(x => [...x, r])); return await system.BroadcastEvent(ev, r => setResults(x => [...x, r]));
} }
} }
@ -58,7 +57,7 @@ export function NoteBroadcaster({
if (publisher) { if (publisher) {
removeRelay(login, unwrap(sanitizeRelayUrl(r.relay))); removeRelay(login, unwrap(sanitizeRelayUrl(r.relay)));
const ev = await publisher.contactList(login.follows.item, login.relays.item); const ev = await publisher.contactList(login.follows.item, login.relays.item);
await System.BroadcastEvent(ev); await system.BroadcastEvent(ev);
setResults(s => s.filter(a => a.relay !== r.relay)); setResults(s => s.filter(a => a.relay !== r.relay));
} }
} }
@ -66,7 +65,7 @@ export function NoteBroadcaster({
async function retryPublish(r: OkResponse) { async function retryPublish(r: OkResponse) {
const ev = evs.find(a => a.id === r.id); const ev = evs.find(a => a.id === r.id);
if (ev) { if (ev) {
const rsp = await System.WriteOnceToRelay(unwrap(sanitizeRelayUrl(r.relay)), ev); const rsp = await system.WriteOnceToRelay(unwrap(sanitizeRelayUrl(r.relay)), ev);
setResults(s => setResults(s =>
s.map(x => { s.map(x => {
if (x.relay === r.relay && x.id === r.id) { if (x.relay === r.relay && x.id === r.id) {

View File

@ -1,9 +1,9 @@
import { useState } from "react";
import { FormattedMessage, useIntl } from "react-intl"; import { FormattedMessage, useIntl } from "react-intl";
import { HexKey, Lists, NostrLink, TaggedNostrEvent } from "@snort/system"; import { HexKey, Lists, NostrLink, TaggedNostrEvent } from "@snort/system";
import { Menu, MenuItem } from "@szhsin/react-menu"; import { Menu, MenuItem } from "@szhsin/react-menu";
import { TranslateHost } from "Const"; import { TranslateHost } from "Const";
import { System } from "index";
import Icon from "Icons/Icon"; import Icon from "Icons/Icon";
import { setPinned, setBookmarked } from "Login"; import { setPinned, setBookmarked } from "Login";
import messages from "Element/messages"; import messages from "Element/messages";
@ -11,7 +11,6 @@ import useLogin from "Hooks/useLogin";
import useModeration from "Hooks/useModeration"; import useModeration from "Hooks/useModeration";
import useEventPublisher from "Hooks/useEventPublisher"; import useEventPublisher from "Hooks/useEventPublisher";
import { ReBroadcaster } from "../ReBroadcaster"; import { ReBroadcaster } from "../ReBroadcaster";
import { useState } from "react";
export interface NoteTranslation { export interface NoteTranslation {
text: string; text: string;
@ -30,7 +29,7 @@ export function NoteContextMenu({ ev, ...props }: NosteContextMenuProps) {
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();
const login = useLogin(); const login = useLogin();
const { mute, block } = useModeration(); const { mute, block } = useModeration();
const publisher = useEventPublisher(); const { publisher, system } = useEventPublisher();
const [showBroadcast, setShowBroadcast] = useState(false); const [showBroadcast, setShowBroadcast] = useState(false);
const lang = window.navigator.language; const lang = window.navigator.language;
const langNames = new Intl.DisplayNames([...window.navigator.languages], { const langNames = new Intl.DisplayNames([...window.navigator.languages], {
@ -41,7 +40,7 @@ export function NoteContextMenu({ ev, ...props }: NosteContextMenuProps) {
async function deleteEvent() { async function deleteEvent() {
if (window.confirm(formatMessage(messages.ConfirmDeletion, { id: ev.id.substring(0, 8) })) && publisher) { if (window.confirm(formatMessage(messages.ConfirmDeletion, { id: ev.id.substring(0, 8) })) && publisher) {
const evDelete = await publisher.delete(ev.id); const evDelete = await publisher.delete(ev.id);
System.BroadcastEvent(evDelete); system.BroadcastEvent(evDelete);
} }
} }
@ -90,7 +89,7 @@ export function NoteContextMenu({ ev, ...props }: NosteContextMenuProps) {
if (publisher) { if (publisher) {
const es = [...login.pinned.item, id]; const es = [...login.pinned.item, id];
const ev = await publisher.noteList(es, Lists.Pinned); const ev = await publisher.noteList(es, Lists.Pinned);
System.BroadcastEvent(ev); system.BroadcastEvent(ev);
setPinned(login, es, ev.created_at * 1000); setPinned(login, es, ev.created_at * 1000);
} }
} }
@ -99,7 +98,7 @@ export function NoteContextMenu({ ev, ...props }: NosteContextMenuProps) {
if (publisher) { if (publisher) {
const es = [...login.bookmarked.item, id]; const es = [...login.bookmarked.item, id];
const ev = await publisher.noteList(es, Lists.Bookmarked); const ev = await publisher.noteList(es, Lists.Bookmarked);
System.BroadcastEvent(ev); system.BroadcastEvent(ev);
setBookmarked(login, es, ev.created_at * 1000); setBookmarked(login, es, ev.created_at * 1000);
} }
} }

View File

@ -25,7 +25,8 @@ export function NoteCreator() {
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();
const uploader = useFileUpload(); const uploader = useFileUpload();
const login = useLogin(s => ({ relays: s.relays, publicKey: s.publicKey, pow: s.preferences.pow })); const login = useLogin(s => ({ relays: s.relays, publicKey: s.publicKey, pow: s.preferences.pow }));
const publisher = login.pow ? useEventPublisher()?.pow(login.pow, GetPowWorker()) : useEventPublisher(); const { publisher: pub } = useEventPublisher();
const publisher = login.pow ? pub?.pow(login.pow, GetPowWorker()) : pub;
const note = useNoteCreator(); const note = useNoteCreator();
const relays = login.relays; const relays = login.relays;

View File

@ -1,8 +1,8 @@
import React, { HTMLProps, useContext, useEffect, useState } from "react"; import React, { HTMLProps, useEffect, useState } from "react";
import { FormattedMessage, useIntl } from "react-intl"; import { FormattedMessage, useIntl } from "react-intl";
import { useLongPress } from "use-long-press"; import { useLongPress } from "use-long-press";
import { TaggedNostrEvent, ParsedZap, countLeadingZeros, NostrLink } from "@snort/system"; import { TaggedNostrEvent, ParsedZap, countLeadingZeros, NostrLink } from "@snort/system";
import { SnortContext, useUserProfile } from "@snort/system-react"; import { useUserProfile } from "@snort/system-react";
import { Menu, MenuItem } from "@szhsin/react-menu"; import { Menu, MenuItem } from "@szhsin/react-menu";
import { formatShort } from "Number"; import { formatShort } from "Number";
@ -17,7 +17,6 @@ import { useWallet } from "Wallet";
import useLogin from "Hooks/useLogin"; import useLogin from "Hooks/useLogin";
import { useInteractionCache } from "Hooks/useInteractionCache"; import { useInteractionCache } from "Hooks/useInteractionCache";
import { ZapPoolController } from "ZapPoolController"; import { ZapPoolController } from "ZapPoolController";
import { System } from "index";
import { Zapper, ZapTarget } from "Zapper"; import { Zapper, ZapTarget } from "Zapper";
import { getDisplayName } from "Element/User/DisplayName"; import { getDisplayName } from "Element/User/DisplayName";
import { useNoteCreator } from "State/NoteCreator"; import { useNoteCreator } from "State/NoteCreator";
@ -48,7 +47,6 @@ export interface NoteFooterProps {
export default function NoteFooter(props: NoteFooterProps) { export default function NoteFooter(props: NoteFooterProps) {
const { ev, positive, reposts, zaps } = props; const { ev, positive, reposts, zaps } = props;
const system = useContext(SnortContext);
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();
const { const {
publicKey, publicKey,
@ -57,7 +55,7 @@ export default function NoteFooter(props: NoteFooterProps) {
} = useLogin(s => ({ preferences: s.preferences, publicKey: s.publicKey, readonly: s.readonly })); } = useLogin(s => ({ preferences: s.preferences, publicKey: s.publicKey, readonly: s.readonly }));
const author = useUserProfile(ev.pubkey); const author = useUserProfile(ev.pubkey);
const interactionCache = useInteractionCache(publicKey, ev.id); const interactionCache = useInteractionCache(publicKey, ev.id);
const publisher = useEventPublisher(); const { publisher, system } = useEventPublisher();
const note = useNoteCreator(n => ({ show: n.show, replyTo: n.replyTo, update: n.update, quote: n.quote })); const note = useNoteCreator(n => ({ show: n.show, replyTo: n.replyTo, update: n.update, quote: n.quote }));
const willRenderNoteCreator = note.show && (note.replyTo?.id === ev.id || note.quote); const willRenderNoteCreator = note.show && (note.replyTo?.id === ev.id || note.quote);
const [tip, setTip] = useState(false); const [tip, setTip] = useState(false);
@ -93,7 +91,7 @@ export default function NoteFooter(props: NoteFooterProps) {
async function react(content: string) { async function react(content: string) {
if (!hasReacted(content) && publisher) { if (!hasReacted(content) && publisher) {
const evLike = await publisher.react(ev, content); const evLike = await publisher.react(ev, content);
System.BroadcastEvent(evLike); system.BroadcastEvent(evLike);
await interactionCache.react(); await interactionCache.react();
} }
} }
@ -102,7 +100,7 @@ export default function NoteFooter(props: NoteFooterProps) {
if (!hasReposted() && publisher) { if (!hasReposted() && publisher) {
if (!prefs.confirmReposts || window.confirm(formatMessage(messages.ConfirmRepost, { id: ev.id }))) { if (!prefs.confirmReposts || window.confirm(formatMessage(messages.ConfirmRepost, { id: ev.id }))) {
const evRepost = await publisher.repost(ev); const evRepost = await publisher.repost(ev);
System.BroadcastEvent(evRepost); system.BroadcastEvent(evRepost);
await interactionCache.repost(); await interactionCache.repost();
} }
} }

View File

@ -10,7 +10,6 @@ import useEventPublisher from "Hooks/useEventPublisher";
import { NoteContextMenu, NoteTranslation } from "./NoteContextMenu"; import { NoteContextMenu, NoteTranslation } from "./NoteContextMenu";
import { UserCache } from "../../Cache"; import { UserCache } from "../../Cache";
import messages from "../messages"; import messages from "../messages";
import { System } from "../../index";
import { setBookmarked, setPinned } from "../../Login"; import { setBookmarked, setPinned } from "../../Login";
import Text from "../Text"; import Text from "../Text";
import Reveal from "./Reveal"; import Reveal from "./Reveal";
@ -37,7 +36,7 @@ export function NoteInner(props: NoteProps) {
const { reactions, reposts, deletions, zaps } = useEventReactions(ev, related); const { reactions, reposts, deletions, zaps } = useEventReactions(ev, related);
const login = useLogin(); const login = useLogin();
const { pinned, bookmarked } = login; const { pinned, bookmarked } = login;
const publisher = useEventPublisher(); const { publisher, system } = useEventPublisher();
const [translated, setTranslated] = useState<NoteTranslation>(); const [translated, setTranslated] = useState<NoteTranslation>();
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();
@ -58,7 +57,7 @@ export function NoteInner(props: NoteProps) {
if (window.confirm(formatMessage(messages.ConfirmUnpin))) { if (window.confirm(formatMessage(messages.ConfirmUnpin))) {
const es = pinned.item.filter(e => e !== id); const es = pinned.item.filter(e => e !== id);
const ev = await publisher.noteList(es, Lists.Pinned); const ev = await publisher.noteList(es, Lists.Pinned);
System.BroadcastEvent(ev); system.BroadcastEvent(ev);
setPinned(login, es, ev.created_at * 1000); setPinned(login, es, ev.created_at * 1000);
} }
} }
@ -69,7 +68,7 @@ export function NoteInner(props: NoteProps) {
if (window.confirm(formatMessage(messages.ConfirmUnbookmark))) { if (window.confirm(formatMessage(messages.ConfirmUnbookmark))) {
const es = bookmarked.item.filter(e => e !== id); const es = bookmarked.item.filter(e => e !== id);
const ev = await publisher.noteList(es, Lists.Bookmarked); const ev = await publisher.noteList(es, Lists.Bookmarked);
System.BroadcastEvent(ev); system.BroadcastEvent(ev);
setBookmarked(login, es, ev.created_at * 1000); setBookmarked(login, es, ev.created_at * 1000);
} }
} }

View File

@ -21,7 +21,7 @@ type PollTally = "zaps" | "pubkeys";
export default function Poll(props: PollProps) { export default function Poll(props: PollProps) {
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();
const publisher = useEventPublisher(); const { publisher } = useEventPublisher();
const { wallet } = useWallet(); const { wallet } = useWallet();
const { preferences: prefs, publicKey: myPubKey, relays } = useLogin(); const { preferences: prefs, publicKey: myPubKey, relays } = useLogin();
const pollerProfile = useUserProfile(props.ev.pubkey); const pollerProfile = useUserProfile(props.ev.pubkey);

View File

@ -1,10 +1,10 @@
import { mapEventToProfile } from "@snort/system";
import { useUserProfile } from "@snort/system-react";
import AccountName from "./AccountName"; import AccountName from "./AccountName";
import useLogin from "../../Hooks/useLogin"; import useLogin from "../../Hooks/useLogin";
import { useUserProfile } from "@snort/system-react";
import { System } from "../../index";
import { UserCache } from "../../Cache"; import { UserCache } from "../../Cache";
import useEventPublisher from "../../Hooks/useEventPublisher"; import useEventPublisher from "../../Hooks/useEventPublisher";
import { mapEventToProfile } from "@snort/system";
import FormattedMessage from "Element/FormattedMessage"; import FormattedMessage from "Element/FormattedMessage";
export default function ActiveAccount({ name = "", setAsPrimary = () => {} }) { export default function ActiveAccount({ name = "", setAsPrimary = () => {} }) {
@ -13,7 +13,7 @@ export default function ActiveAccount({ name = "", setAsPrimary = () => {} }) {
readonly: s.readonly, readonly: s.readonly,
})); }));
const profile = useUserProfile(publicKey); const profile = useUserProfile(publicKey);
const publisher = useEventPublisher(); const { publisher, system } = useEventPublisher();
async function saveProfile(nip05: string) { async function saveProfile(nip05: string) {
if (readonly) { if (readonly) {
@ -35,7 +35,7 @@ export default function ActiveAccount({ name = "", setAsPrimary = () => {} }) {
if (publisher) { if (publisher) {
const ev = await publisher.metadata(userCopy); const ev = await publisher.metadata(userCopy);
System.BroadcastEvent(ev); system.BroadcastEvent(ev);
const newProfile = mapEventToProfile(ev); const newProfile = mapEventToProfile(ev);
if (newProfile) { if (newProfile) {

View File

@ -25,7 +25,6 @@ import SnortServiceProvider from "Nip05/SnortServiceProvider";
import { UserCache } from "Cache"; import { UserCache } from "Cache";
import messages from "./messages"; import messages from "./messages";
import { System } from "index";
type Nip05ServiceProps = { type Nip05ServiceProps = {
name: string; name: string;
@ -45,7 +44,7 @@ export default function Nip5Service(props: Nip05ServiceProps) {
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();
const { publicKey } = useLogin(s => ({ publicKey: s.publicKey })); const { publicKey } = useLogin(s => ({ publicKey: s.publicKey }));
const user = useUserProfile(publicKey); const user = useUserProfile(publicKey);
const publisher = useEventPublisher(); const { publisher, system } = useEventPublisher();
const svc = useMemo(() => new ServiceProvider(props.service), [props.service]); const svc = useMemo(() => new ServiceProvider(props.service), [props.service]);
const [serviceConfig, setServiceConfig] = useState<ServiceConfig>(); const [serviceConfig, setServiceConfig] = useState<ServiceConfig>();
const [error, setError] = useState<ServiceError>(); const [error, setError] = useState<ServiceError>();
@ -216,7 +215,7 @@ export default function Nip5Service(props: Nip05ServiceProps) {
nip05, nip05,
} as UserMetadata; } as UserMetadata;
const ev = await publisher.metadata(newProfile); const ev = await publisher.metadata(newProfile);
System.BroadcastEvent(ev); system.BroadcastEvent(ev);
if (props.onSuccess) { if (props.onSuccess) {
props.onSuccess(nip05); props.onSuccess(nip05);
} }

View File

@ -93,7 +93,7 @@ export function PinPrompt({
export function LoginUnlock() { export function LoginUnlock() {
const login = useLogin(); const login = useLogin();
const publisher = useEventPublisher(); const { publisher } = useEventPublisher();
async function encryptMigration(pin: string) { async function encryptMigration(pin: string) {
const k = unwrap(login.privateKey); const k = unwrap(login.privateKey);

View File

@ -1,12 +1,12 @@
import "./Relay.css"; import "./Relay.css";
import { useMemo } from "react"; import { useContext, useMemo } from "react";
import FormattedMessage from "Element/FormattedMessage"; import FormattedMessage from "Element/FormattedMessage";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { RelaySettings } from "@snort/system"; import { RelaySettings } from "@snort/system";
import { unixNowMs } from "@snort/shared"; import { unixNowMs } from "@snort/shared";
import useRelayState from "Feed/RelayState"; import useRelayState from "Feed/RelayState";
import { System } from "index"; import { SnortContext } from "@snort/system-react";
import { getRelayName, unwrap } from "SnortUtils"; import { getRelayName, unwrap } from "SnortUtils";
import useLogin from "Hooks/useLogin"; import useLogin from "Hooks/useLogin";
import { setRelays } from "Login"; import { setRelays } from "Login";
@ -20,9 +20,11 @@ export interface RelayProps {
export default function Relay(props: RelayProps) { export default function Relay(props: RelayProps) {
const navigate = useNavigate(); const navigate = useNavigate();
const system = useContext(SnortContext);
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]);

View File

@ -1,9 +1,8 @@
import "./SendSats.css"; import "./SendSats.css";
import React, { ReactNode, useContext, useEffect, useState } from "react"; import React, { ReactNode, useEffect, useState } from "react";
import { useIntl, FormattedMessage } from "react-intl"; import { useIntl, FormattedMessage } from "react-intl";
import { HexKey } from "@snort/system"; import { HexKey } from "@snort/system";
import { SnortContext } from "@snort/system-react";
import { LNURLSuccessAction } from "@snort/shared"; import { LNURLSuccessAction } from "@snort/shared";
import { formatShort } from "Number"; import { formatShort } from "Number";
@ -48,8 +47,7 @@ export default function SendSats(props: SendSatsProps) {
const [success, setSuccess] = useState<LNURLSuccessAction>(); const [success, setSuccess] = useState<LNURLSuccessAction>();
const [amount, setAmount] = useState<SendSatsInputSelection>(); const [amount, setAmount] = useState<SendSatsInputSelection>();
const system = useContext(SnortContext); const { publisher, system } = useEventPublisher();
const publisher = useEventPublisher();
const walletState = useWallet(); const walletState = useWallet();
const wallet = walletState.wallet; const wallet = walletState.wallet;

View File

@ -1,14 +0,0 @@
.sub-debug {
display: block;
position: fixed;
top: 5px;
left: 5px;
opacity: 0.8;
border: 1px solid;
padding: 5px;
font-family: monospace;
font-size: x-small;
background-color: black;
z-index: 999999;
color: white;
}

View File

@ -1,99 +0,0 @@
import "./SubDebug.css";
import { useState } from "react";
import { ReqFilter } from "@snort/system";
import { useSystemState } from "@snort/system-react";
import useRelayState from "Feed/RelayState";
import Tabs, { Tab } from "Element/Tabs";
import { unwrap } from "SnortUtils";
import { useCopy } from "useCopy";
import { System } from "index";
function RelayInfo({ id }: { id: string }) {
const state = useRelayState(id);
return <div key={id}>{state?.connected ? <>{id}</> : <s>{id}</s>}</div>;
}
function Queries() {
const qs = useSystemState(System);
const { copy } = useCopy();
function countElements(filters: Array<ReqFilter>) {
let total = 0;
for (const f of filters) {
for (const v of Object.values(f)) {
if (Array.isArray(v)) {
total += v.length;
}
}
}
return total;
}
function queryInfo(q: { id: string; filters: Array<ReqFilter>; subFilters: Array<ReqFilter> }) {
return (
<div key={q.id}>
{q.id}
<br />
<span onClick={() => copy(JSON.stringify(q.filters))} className="pointer">
&nbsp; Filters: {q.filters.length} ({countElements(q.filters)} elements)
</span>
<br />
<span onClick={() => copy(JSON.stringify(q.subFilters))} className="pointer">
&nbsp; SubQueries: {q.subFilters.length} ({countElements(q.subFilters)} elements)
</span>
</div>
);
}
return (
<>
<b>Queries</b>
{qs?.queries.map(v => queryInfo(v))}
</>
);
}
const SubDebug = () => {
const [onTab, setTab] = useState(0);
function connections() {
return (
<>
<b>Connections:</b>
{System.Sockets.map(k => (
<RelayInfo id={k.address} />
))}
</>
);
}
const tabs: Tab[] = [
{
text: "Connections",
value: 0,
},
{
text: "Queries",
value: 1,
},
];
return (
<div className="sub-debug">
<Tabs tabs={tabs} setTab={v => setTab(v.value)} tab={unwrap(tabs.find(a => a.value === onTab))} />
{(() => {
switch (onTab) {
case 0:
return connections();
case 1:
return <Queries />;
default:
return null;
}
})()}
</div>
);
};
export default SubDebug;

View File

@ -5,7 +5,6 @@ import useEventPublisher from "Hooks/useEventPublisher";
import { parseId } from "SnortUtils"; import { parseId } from "SnortUtils";
import useLogin from "Hooks/useLogin"; import useLogin from "Hooks/useLogin";
import AsyncButton from "Element/AsyncButton"; import AsyncButton from "Element/AsyncButton";
import { System } from "index";
import messages from "../messages"; import messages from "../messages";
import { FollowsFeed } from "Cache"; import { FollowsFeed } from "Cache";
@ -16,7 +15,7 @@ export interface FollowButtonProps {
} }
export default function FollowButton(props: FollowButtonProps) { export default function FollowButton(props: FollowButtonProps) {
const pubkey = parseId(props.pubkey); const pubkey = parseId(props.pubkey);
const publisher = useEventPublisher(); const { publisher, system } = useEventPublisher();
const { follows, relays, readonly } = useLogin(s => ({ follows: s.follows, relays: s.relays, readonly: s.readonly })); const { follows, relays, readonly } = useLogin(s => ({ follows: s.follows, relays: s.relays, readonly: s.readonly }));
const isFollowing = follows.item.includes(pubkey); const isFollowing = follows.item.includes(pubkey);
const baseClassname = props.className ? `${props.className} ` : ""; const baseClassname = props.className ? `${props.className} ` : "";
@ -24,8 +23,8 @@ export default function FollowButton(props: FollowButtonProps) {
async function follow(pubkey: HexKey) { async function follow(pubkey: HexKey) {
if (publisher) { if (publisher) {
const ev = await publisher.contactList([pubkey, ...follows.item], relays.item); const ev = await publisher.contactList([pubkey, ...follows.item], relays.item);
System.BroadcastEvent(ev); system.BroadcastEvent(ev);
await FollowsFeed.backFill(System, [pubkey]); await FollowsFeed.backFill(system, [pubkey]);
} }
} }
@ -35,7 +34,7 @@ export default function FollowButton(props: FollowButtonProps) {
follows.item.filter(a => a !== pubkey), follows.item.filter(a => a !== pubkey),
relays.item, relays.item,
); );
System.BroadcastEvent(ev); system.BroadcastEvent(ev);
} }
} }

View File

@ -1,17 +1,16 @@
import { ReactNode } from "react"; import { ReactNode } from "react";
import FormattedMessage from "Element/FormattedMessage"; import FormattedMessage from "Element/FormattedMessage";
import { HexKey } from "@snort/system"; import { HexKey } from "@snort/system";
import { dedupe } from "@snort/shared";
import useEventPublisher from "Hooks/useEventPublisher"; import useEventPublisher from "Hooks/useEventPublisher";
import ProfilePreview from "Element/User/ProfilePreview"; import ProfilePreview from "Element/User/ProfilePreview";
import useLogin from "Hooks/useLogin"; import useLogin from "Hooks/useLogin";
import { System } from "index";
import messages from "../messages"; import messages from "../messages";
import { FollowsFeed } from "Cache"; import { FollowsFeed } from "Cache";
import AsyncButton from "../AsyncButton"; import AsyncButton from "../AsyncButton";
import { setFollows } from "Login"; import { setFollows } from "Login";
import { dedupe } from "@snort/shared";
export interface FollowListBaseProps { export interface FollowListBaseProps {
pubkeys: HexKey[]; pubkeys: HexKey[];
@ -32,15 +31,15 @@ export default function FollowListBase({
actions, actions,
profileActions, profileActions,
}: FollowListBaseProps) { }: FollowListBaseProps) {
const publisher = useEventPublisher(); const { publisher, system } = useEventPublisher();
const login = useLogin(); const login = useLogin();
async function followAll() { async function followAll() {
if (publisher) { if (publisher) {
const newFollows = dedupe([...pubkeys, ...login.follows.item]); const newFollows = dedupe([...pubkeys, ...login.follows.item]);
const ev = await publisher.contactList(newFollows, login.relays.item); const ev = await publisher.contactList(newFollows, login.relays.item);
System.BroadcastEvent(ev); system.BroadcastEvent(ev);
await FollowsFeed.backFill(System, pubkeys); await FollowsFeed.backFill(system, pubkeys);
setFollows(login, newFollows, ev.created_at); setFollows(login, newFollows, ev.created_at);
} }
} }

View File

@ -24,7 +24,6 @@ import { SnortPubKey } from "Const";
import { SubscriptionEvent } from "Subscription"; import { SubscriptionEvent } from "Subscription";
import useRelaysFeedFollows from "./RelaysFeedFollows"; import useRelaysFeedFollows from "./RelaysFeedFollows";
import { FollowsFeed, GiftsCache, Notifications, UserRelays } from "Cache"; import { FollowsFeed, GiftsCache, Notifications, UserRelays } from "Cache";
import { System } from "index";
import { Nip28Chats, Nip4Chats } from "chat"; import { Nip28Chats, Nip4Chats } from "chat";
import { useRefreshFeedCache } from "Hooks/useRefreshFeedcache"; import { useRefreshFeedCache } from "Hooks/useRefreshFeedcache";
@ -35,12 +34,16 @@ export default function useLoginFeed() {
const login = useLogin(); const login = useLogin();
const { publicKey: pubKey, readNotifications, follows } = login; const { publicKey: pubKey, readNotifications, follows } = login;
const { isMuted } = useModeration(); const { isMuted } = useModeration();
const publisher = useEventPublisher(); const { publisher, system } = useEventPublisher();
useRefreshFeedCache(Notifications, true); useRefreshFeedCache(Notifications, true);
useRefreshFeedCache(FollowsFeed, true); useRefreshFeedCache(FollowsFeed, true);
useRefreshFeedCache(GiftsCache, true); useRefreshFeedCache(GiftsCache, true);
useEffect(() => {
system.checkSigs = login.preferences.checkSigs;
}, [login]);
const subLogin = useMemo(() => { const subLogin = useMemo(() => {
if (!login || !pubKey) return null; if (!login || !pubKey) return null;
@ -88,7 +91,7 @@ export default function useLoginFeed() {
const pTags = contactList.tags.filter(a => a[0] === "p").map(a => a[1]); const pTags = contactList.tags.filter(a => a[0] === "p").map(a => a[1]);
setFollows(login, pTags, contactList.created_at * 1000); setFollows(login, pTags, contactList.created_at * 1000);
FollowsFeed.backFillIfMissing(System, pTags); FollowsFeed.backFillIfMissing(system, pTags);
} }
Nip4Chats.onEvent(loginFeed.data); Nip4Chats.onEvent(loginFeed.data);
@ -202,7 +205,7 @@ export default function useLoginFeed() {
useEffect(() => { useEffect(() => {
UserRelays.buffer(follows.item).catch(console.error); UserRelays.buffer(follows.item).catch(console.error);
System.ProfileLoader.TrackMetadata(follows.item); // always track follows profiles system.ProfileLoader.TrackMetadata(follows.item); // always track follows profiles
}, [follows.item]); }, [follows.item]);
const fRelays = useRelaysFeedFollows(follows.item); const fRelays = useRelaysFeedFollows(follows.item);

View File

@ -1,6 +1,8 @@
import { System } from "index"; import { useContext } from "react";
import { SnortContext } from "@snort/system-react";
export default function useRelayState(addr: string) { export default function useRelayState(addr: string) {
const c = System.Sockets.find(a => a.address === addr); const system = useContext(SnortContext);
const c = system.Sockets.find(a => a.address === addr);
return c; return c;
} }

View File

@ -89,7 +89,7 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel
const rb = createBuilder(); const rb = createBuilder();
if (rb) { if (rb) {
if (options.method === "LIMIT_UNTIL") { if (options.method === "LIMIT_UNTIL") {
rb.filter.until(until).limit(200); rb.filter.until(until).limit(100);
} else { } else {
rb.filter.since(since).until(until); rb.filter.since(since).until(until);
if (since === undefined) { if (since === undefined) {

View File

@ -1,8 +1,11 @@
import { useContext } from "react";
import { SnortContext } from "@snort/system-react";
import useLogin from "Hooks/useLogin"; import useLogin from "Hooks/useLogin";
import { LoginStore, createPublisher, sessionNeedsPin } from "Login"; import { LoginStore, createPublisher, sessionNeedsPin } from "Login";
export default function useEventPublisher() { export default function useEventPublisher() {
const login = useLogin(); const login = useLogin();
const system = useContext(SnortContext);
let existing = LoginStore.getPublisher(login.id); let existing = LoginStore.getPublisher(login.id);
@ -12,5 +15,8 @@ export default function useEventPublisher() {
LoginStore.setPublisher(login.id, existing); LoginStore.setPublisher(login.id, existing);
} }
} }
return existing; return {
publisher: existing,
system,
};
} }

View File

@ -1,19 +1,20 @@
import { System } from "index";
import { useEffect } from "react"; import { useEffect } from "react";
import useLogin from "./useLogin"; import useLogin from "./useLogin";
import useEventPublisher from "./useEventPublisher";
export function useLoginRelays() { export function useLoginRelays() {
const { relays } = useLogin(); const { relays } = useLogin();
const { system } = useEventPublisher();
useEffect(() => { useEffect(() => {
if (relays) { if (relays) {
(async () => { (async () => {
for (const [k, v] of Object.entries(relays.item)) { for (const [k, v] of Object.entries(relays.item)) {
await System.ConnectToRelay(k, v); await system.ConnectToRelay(k, v);
} }
for (const v of System.Sockets) { for (const v of system.Sockets) {
if (!relays.item[v.address] && !v.ephemeral) { if (!relays.item[v.address] && !v.ephemeral) {
System.DisconnectRelay(v.address); system.DisconnectRelay(v.address);
} }
} }
})(); })();

View File

@ -3,17 +3,16 @@ import useEventPublisher from "Hooks/useEventPublisher";
import useLogin from "Hooks/useLogin"; import useLogin from "Hooks/useLogin";
import { setBlocked, setMuted } from "Login"; import { setBlocked, setMuted } from "Login";
import { appendDedupe } from "SnortUtils"; import { appendDedupe } from "SnortUtils";
import { System } from "index";
export default function useModeration() { export default function useModeration() {
const login = useLogin(); const login = useLogin();
const { muted, blocked, appData } = login; const { muted, blocked, appData } = login;
const publisher = useEventPublisher(); const { publisher, system } = useEventPublisher();
async function setMutedList(pub: HexKey[], priv: HexKey[]) { async function setMutedList(pub: HexKey[], priv: HexKey[]) {
if (publisher) { if (publisher) {
const ev = await publisher.muted(pub, priv); const ev = await publisher.muted(pub, priv);
System.BroadcastEvent(ev); system.BroadcastEvent(ev);
return ev.created_at * 1000; return ev.created_at * 1000;
} }
return 0; return 0;

View File

@ -1,5 +1,4 @@
import { SnortContext } from "@snort/system-react"; import { useEffect, useMemo } from "react";
import { useContext, useEffect, useMemo } from "react";
import { NoopStore, RequestBuilder, TaggedNostrEvent } from "@snort/system"; import { NoopStore, RequestBuilder, TaggedNostrEvent } from "@snort/system";
import { RefreshFeedCache } from "Cache/RefreshFeedCache"; import { RefreshFeedCache } from "Cache/RefreshFeedCache";
@ -8,9 +7,8 @@ import useEventPublisher from "./useEventPublisher";
import { unwrap } from "@snort/shared"; import { unwrap } from "@snort/shared";
export function useRefreshFeedCache<T>(c: RefreshFeedCache<T>, leaveOpen = false) { export function useRefreshFeedCache<T>(c: RefreshFeedCache<T>, leaveOpen = false) {
const system = useContext(SnortContext);
const login = useLogin(); const login = useLogin();
const publisher = useEventPublisher(); const { publisher, system } = useEventPublisher();
const sub = useMemo(() => { const sub = useMemo(() => {
if (login.publicKey) { if (login.publicKey) {

View File

@ -1,4 +1,12 @@
import { RelaySettings, EventPublisher, Nip46Signer, Nip7Signer, PrivateKeySigner, KeyStorage } from "@snort/system"; import {
RelaySettings,
EventPublisher,
Nip46Signer,
Nip7Signer,
PrivateKeySigner,
KeyStorage,
SystemInterface,
} from "@snort/system";
import { unixNowMs } from "@snort/shared"; import { unixNowMs } from "@snort/shared";
import * as secp from "@noble/curves/secp256k1"; import * as secp from "@noble/curves/secp256k1";
import * as utils from "@noble/curves/abstract/utils"; import * as utils from "@noble/curves/abstract/utils";
@ -8,7 +16,6 @@ import { LoginStore, UserPreferences, LoginSession, LoginSessionType, SnortAppDa
import { generateBip39Entropy, entropyToPrivateKey } from "nip6"; import { generateBip39Entropy, entropyToPrivateKey } from "nip6";
import { bech32ToHex, dedupeById, randomSample, sanitizeRelayUrl, unwrap } from "SnortUtils"; import { bech32ToHex, dedupeById, randomSample, sanitizeRelayUrl, unwrap } from "SnortUtils";
import { SubscriptionEvent } from "Subscription"; import { SubscriptionEvent } from "Subscription";
import { System } from "index";
import { Chats, FollowsFeed, GiftsCache, Notifications } from "Cache"; import { Chats, FollowsFeed, GiftsCache, Notifications } from "Cache";
import { Nip7OsSigner } from "./Nip7OsSigner"; import { Nip7OsSigner } from "./Nip7OsSigner";
@ -63,7 +70,7 @@ export function clearEntropy(state: LoginSession) {
/** /**
* Generate a new key and login with this generated key * Generate a new key and login with this generated key
*/ */
export async function generateNewLogin(pin: (key: string) => Promise<KeyStorage>) { export async function generateNewLogin(system: SystemInterface, pin: (key: string) => Promise<KeyStorage>) {
const ent = generateBip39Entropy(); const ent = generateBip39Entropy();
const entropy = utils.bytesToHex(ent); const entropy = utils.bytesToHex(ent);
const privateKey = entropyToPrivateKey(ent); const privateKey = entropyToPrivateKey(ent);
@ -87,7 +94,7 @@ export async function generateNewLogin(pin: (key: string) => Promise<KeyStorage>
const publicKey = utils.bytesToHex(secp.schnorr.getPublicKey(privateKey)); const publicKey = utils.bytesToHex(secp.schnorr.getPublicKey(privateKey));
const publisher = EventPublisher.privateKey(privateKey); const publisher = EventPublisher.privateKey(privateKey);
const ev = await publisher.contactList([bech32ToHex(SnortPubKey), publicKey], newRelays); const ev = await publisher.contactList([bech32ToHex(SnortPubKey), publicKey], newRelays);
System.BroadcastEvent(ev); system.BroadcastEvent(ev);
LoginStore.loginWithPrivateKey(await pin(privateKey), entropy, newRelays); LoginStore.loginWithPrivateKey(await pin(privateKey), entropy, newRelays);
} }

View File

@ -74,11 +74,12 @@ export class MultiAccountStore extends ExternalStore<LoginSession> {
if (!this.#activeAccount) { if (!this.#activeAccount) {
this.#activeAccount = this.#accounts.keys().next().value; this.#activeAccount = this.#accounts.keys().next().value;
} }
// reset readonly on load
for (const [, v] of this.#accounts) { for (const [, v] of this.#accounts) {
// reset readonly on load
if (v.type === LoginSessionType.PrivateKey && v.readonly) { if (v.type === LoginSessionType.PrivateKey && v.readonly) {
v.readonly = false; v.readonly = false;
} }
// fill possibly undefined (migrate up)
v.appData ??= { v.appData ??= {
item: { item: {
mutedWords: [], mutedWords: [],
@ -86,6 +87,7 @@ export class MultiAccountStore extends ExternalStore<LoginSession> {
timestamp: 0, timestamp: 0,
}; };
v.extraChats ??= []; v.extraChats ??= [];
v.preferences.checkSigs ??= true;
if (v.privateKeyData) { if (v.privateKeyData) {
v.privateKeyData = KeyStorage.fromPayload(v.privateKeyData as object); v.privateKeyData = KeyStorage.fromPayload(v.privateKeyData as object);
} }

View File

@ -86,6 +86,11 @@ export interface UserPreferences {
* Show user status messages on profiles * Show user status messages on profiles
*/ */
showStatus?: boolean; showStatus?: boolean;
/**
* Check event signatures
*/
checkSigs: boolean;
} }
export const DefaultPreferences = { export const DefaultPreferences = {
@ -105,4 +110,5 @@ export const DefaultPreferences = {
telemetry: true, telemetry: true,
showBadges: false, showBadges: false,
showStatus: true, showStatus: true,
checkSigs: true,
} as UserPreferences; } as UserPreferences;

View File

@ -1,9 +0,0 @@
import SubDebug from "Element/SubDebug";
export default function DebugPage() {
return (
<>
<SubDebug />
</>
);
}

View File

@ -6,7 +6,6 @@ import Timeline from "Element/Feed/Timeline";
import useEventPublisher from "Hooks/useEventPublisher"; import useEventPublisher from "Hooks/useEventPublisher";
import useLogin from "Hooks/useLogin"; import useLogin from "Hooks/useLogin";
import { setTags } from "Login"; import { setTags } from "Login";
import { System } from "index";
const HashTagsPage = () => { const HashTagsPage = () => {
const params = useParams(); const params = useParams();
@ -15,12 +14,12 @@ const HashTagsPage = () => {
const isFollowing = useMemo(() => { const isFollowing = useMemo(() => {
return login.tags.item.includes(tag); return login.tags.item.includes(tag);
}, [login, tag]); }, [login, tag]);
const publisher = useEventPublisher(); const { publisher, system } = useEventPublisher();
async function followTags(ts: string[]) { async function followTags(ts: string[]) {
if (publisher) { if (publisher) {
const ev = await publisher.tags(ts); const ev = await publisher.tags(ts);
System.BroadcastEvent(ev); system.BroadcastEvent(ev);
setTags(login, ts, ev.created_at * 1000); setTags(login, ts, ev.created_at * 1000);
} }
} }

View File

@ -19,6 +19,7 @@ import QrCode from "Element/QrCode";
import Copy from "Element/Copy"; import Copy from "Element/Copy";
import { delay } from "SnortUtils"; import { delay } from "SnortUtils";
import { PinPrompt } from "Element/PinPrompt"; import { PinPrompt } from "Element/PinPrompt";
import useEventPublisher from "Hooks/useEventPublisher";
declare global { declare global {
interface Window { interface Window {
@ -85,6 +86,7 @@ export default function LoginPage() {
const { proxy } = useImgProxy(); const { proxy } = useImgProxy();
const loginHandler = useLoginHandler(); const loginHandler = useLoginHandler();
const hasNip7 = "nostr" in window; const hasNip7 = "nostr" in window;
const { system } = useEventPublisher();
const hasSubtleCrypto = window.crypto.subtle !== undefined; const hasSubtleCrypto = window.crypto.subtle !== undefined;
const [nostrConnect, setNostrConnect] = useState(""); const [nostrConnect, setNostrConnect] = useState("");
@ -123,7 +125,7 @@ export default function LoginPage() {
async function makeRandomKey(pin?: string) { async function makeRandomKey(pin?: string) {
try { try {
await generateNewLogin(key => makeKeyStore(key, pin)); await generateNewLogin(system, key => makeKeyStore(key, pin));
window.plausible?.("Generate Account"); window.plausible?.("Generate Account");
navigate("/new"); navigate("/new");
} catch (e) { } catch (e) {

View File

@ -3,9 +3,9 @@ import { Link, Outlet, RouteObject, useParams } from "react-router-dom";
import FormattedMessage from "Element/FormattedMessage"; import FormattedMessage from "Element/FormattedMessage";
import { unixNow } from "@snort/shared"; import { unixNow } from "@snort/shared";
import { NostrLink } from "@snort/system"; import { NostrLink } from "@snort/system";
import { SnortContext } from "@snort/system-react";
import Timeline from "Element/Feed/Timeline"; import Timeline from "Element/Feed/Timeline";
import { System } from "index";
import { TimelineSubject } from "Feed/TimelineFeed"; import { TimelineSubject } from "Feed/TimelineFeed";
import { debounce, getRelayName, sha256 } from "SnortUtils"; import { debounce, getRelayName, sha256 } from "SnortUtils";
import useLogin from "Hooks/useLogin"; import useLogin from "Hooks/useLogin";
@ -63,6 +63,7 @@ export const GlobalTab = () => {
const [relay, setRelay] = useState<RelayOption>(); const [relay, setRelay] = useState<RelayOption>();
const [allRelays, setAllRelays] = useState<RelayOption[]>(); const [allRelays, setAllRelays] = useState<RelayOption[]>();
const [now] = useState(unixNow()); const [now] = useState(unixNow());
const system = useContext(SnortContext);
const subject: TimelineSubject = { const subject: TimelineSubject = {
type: "global", type: "global",
@ -111,7 +112,7 @@ export const GlobalTab = () => {
useEffect(() => { useEffect(() => {
return debounce(500, () => { return debounce(500, () => {
const ret: RelayOption[] = []; const ret: RelayOption[] = [];
System.Sockets.forEach(v => { system.Sockets.forEach(v => {
ret.push({ ret.push({
url: v.address, url: v.address,
paid: v.info?.limitation?.payment_required ?? false, paid: v.info?.limitation?.payment_required ?? false,

View File

@ -12,7 +12,7 @@ import { bech32ToHex, getRelayName, unwrap } from "SnortUtils";
import { ZapPoolController, ZapPoolRecipient, ZapPoolRecipientType } from "ZapPoolController"; import { ZapPoolController, ZapPoolRecipient, ZapPoolRecipientType } from "ZapPoolController";
import AsyncButton from "Element/AsyncButton"; import AsyncButton from "Element/AsyncButton";
import { useWallet } from "Wallet"; import { useWallet } from "Wallet";
import { System } from "index"; import useEventPublisher from "Hooks/useEventPublisher";
const DataProviders = [ const DataProviders = [
{ {
@ -71,6 +71,7 @@ function ZapTarget({ target }: { target: ZapPoolRecipient }) {
export default function ZapPoolPage() { export default function ZapPoolPage() {
const login = useLogin(); const login = useLogin();
const { system } = useEventPublisher();
const zapPool = useSyncExternalStore( const zapPool = useSyncExternalStore(
c => ZapPoolController.hook(c), c => ZapPoolController.hook(c),
() => ZapPoolController.snapshot(), () => ZapPoolController.snapshot(),
@ -78,7 +79,7 @@ export default function ZapPoolPage() {
const { wallet } = useWallet(); const { wallet } = useWallet();
const relayConnections = useMemo(() => { const relayConnections = useMemo(() => {
return System.Sockets.map(a => { return system.Sockets.map(a => {
if (a.info?.pubkey && !a.ephemeral) { if (a.info?.pubkey && !a.ephemeral) {
return { return {
address: a.address, address: a.address,

View File

@ -10,7 +10,6 @@ import useLogin from "Hooks/useLogin";
import { UserCache } from "Cache"; import { UserCache } from "Cache";
import AvatarEditor from "Element/User/AvatarEditor"; import AvatarEditor from "Element/User/AvatarEditor";
import { DISCOVER } from "."; import { DISCOVER } from ".";
import { System } from "index";
import messages from "./messages"; import messages from "./messages";
@ -20,7 +19,7 @@ export default function ProfileSetup() {
const [username, setUsername] = useState(""); const [username, setUsername] = useState("");
const [picture, setPicture] = useState(""); const [picture, setPicture] = useState("");
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();
const publisher = useEventPublisher(); const { publisher, system } = useEventPublisher();
const navigate = useNavigate(); const navigate = useNavigate();
useEffect(() => { useEffect(() => {
@ -37,7 +36,7 @@ export default function ProfileSetup() {
name: username, name: username,
picture, picture,
}); });
System.BroadcastEvent(ev); system.BroadcastEvent(ev);
const profile = mapEventToProfile(ev); const profile = mapEventToProfile(ev);
if (profile) { if (profile) {
UserCache.set(profile); UserCache.set(profile);

View File

@ -176,6 +176,23 @@ const PreferencesPage = () => {
</div> </div>
</div> </div>
</div> </div>
<div className="flex f-space w-max">
<div className="flex-column g8">
<h4>
<FormattedMessage defaultMessage="Check Signatures" />
</h4>
<small>
<FormattedMessage defaultMessage="Check all event signatures received from relays" />
</small>
</div>
<div>
<input
type="checkbox"
checked={perf.checkSigs}
onChange={e => updatePreferences(login, { ...perf, checkSigs: e.target.checked })}
/>
</div>
</div>
<div className="flex f-space w-max"> <div className="flex f-space w-max">
<div className="flex-column g8"> <div className="flex-column g8">
<h4> <h4>

View File

@ -5,7 +5,6 @@ import { useNavigate } from "react-router-dom";
import { mapEventToProfile } from "@snort/system"; import { mapEventToProfile } from "@snort/system";
import { useUserProfile } from "@snort/system-react"; import { useUserProfile } from "@snort/system-react";
import { System } from "index";
import useEventPublisher from "Hooks/useEventPublisher"; import useEventPublisher from "Hooks/useEventPublisher";
import { openFile } from "SnortUtils"; import { openFile } from "SnortUtils";
import useFileUpload from "Upload"; import useFileUpload from "Upload";
@ -24,7 +23,7 @@ export default function ProfileSettings(props: ProfileSettingsProps) {
const navigate = useNavigate(); const navigate = useNavigate();
const { publicKey: id, readonly } = useLogin(s => ({ publicKey: s.publicKey, readonly: s.readonly })); const { publicKey: id, readonly } = useLogin(s => ({ publicKey: s.publicKey, readonly: s.readonly }));
const user = useUserProfile(id ?? ""); const user = useUserProfile(id ?? "");
const publisher = useEventPublisher(); const { publisher, system } = useEventPublisher();
const uploader = useFileUpload(); const uploader = useFileUpload();
const [name, setName] = useState<string>(); const [name, setName] = useState<string>();
@ -70,7 +69,7 @@ export default function ProfileSettings(props: ProfileSettingsProps) {
if (publisher) { if (publisher) {
const ev = await publisher.metadata(userCopy); const ev = await publisher.metadata(userCopy);
System.BroadcastEvent(ev); system.BroadcastEvent(ev);
const newProfile = mapEventToProfile(ev); const newProfile = mapEventToProfile(ev);
if (newProfile) { if (newProfile) {

View File

@ -3,18 +3,19 @@ import ProfilePreview from "Element/User/ProfilePreview";
import useRelayState from "Feed/RelayState"; import useRelayState from "Feed/RelayState";
import { useNavigate, useParams } from "react-router-dom"; import { useNavigate, useParams } from "react-router-dom";
import { parseId, unwrap } from "SnortUtils"; import { parseId, unwrap } from "SnortUtils";
import { System } from "index";
import { removeRelay } from "Login"; import { removeRelay } from "Login";
import useLogin from "Hooks/useLogin"; import useLogin from "Hooks/useLogin";
import messages from "./messages"; import messages from "./messages";
import useEventPublisher from "Hooks/useEventPublisher";
const RelayInfo = () => { const RelayInfo = () => {
const params = useParams(); const params = useParams();
const navigate = useNavigate(); const navigate = useNavigate();
const login = useLogin(); const login = useLogin();
const { system } = useEventPublisher();
const conn = System.Sockets.find(a => a.id === params.id); const conn = system.Sockets.find(a => a.id === params.id);
const stats = useRelayState(conn?.address ?? ""); const stats = useRelayState(conn?.address ?? "");
return ( return (

View File

@ -5,7 +5,6 @@ import { unixNowMs } from "@snort/shared";
import { randomSample } from "SnortUtils"; import { randomSample } from "SnortUtils";
import Relay from "Element/Relay/Relay"; import Relay from "Element/Relay/Relay";
import useEventPublisher from "Hooks/useEventPublisher"; import useEventPublisher from "Hooks/useEventPublisher";
import { System } from "index";
import useLogin from "Hooks/useLogin"; import useLogin from "Hooks/useLogin";
import { setRelays } from "Login"; import { setRelays } from "Login";
import AsyncButton from "Element/AsyncButton"; import AsyncButton from "Element/AsyncButton";
@ -13,25 +12,25 @@ import AsyncButton from "Element/AsyncButton";
import messages from "./messages"; import messages from "./messages";
const RelaySettingsPage = () => { const RelaySettingsPage = () => {
const publisher = useEventPublisher(); const { publisher, system } = useEventPublisher();
const login = useLogin(); const login = useLogin();
const relays = login.relays; const relays = login.relays;
const [newRelay, setNewRelay] = useState<string>(); const [newRelay, setNewRelay] = useState<string>();
const otherConnections = useMemo(() => { const otherConnections = useMemo(() => {
return System.Sockets.filter(a => relays.item[a.address] === undefined); return system.Sockets.filter(a => relays.item[a.address] === undefined);
}, [relays]); }, [relays]);
async function saveRelays() { async function saveRelays() {
if (publisher) { if (publisher) {
const ev = await publisher.contactList(login.follows.item, login.relays.item); const ev = await publisher.contactList(login.follows.item, login.relays.item);
System.BroadcastEvent(ev); system.BroadcastEvent(ev);
try { try {
const onlineRelays = await fetch("https://api.nostr.watch/v1/online").then(r => r.json()); const onlineRelays = await fetch("https://api.nostr.watch/v1/online").then(r => r.json());
const relayList = await publisher.relayList(login.relays.item); const relayList = await publisher.relayList(login.relays.item);
const rs = Object.keys(relays.item).concat(randomSample(onlineRelays, 20)); const rs = Object.keys(relays.item).concat(randomSample(onlineRelays, 20));
rs.forEach(r => { rs.forEach(r => {
System.WriteOnceToRelay(r, relayList); system.WriteOnceToRelay(r, relayList);
}); });
} catch (error) { } catch (error) {
console.error(error); console.error(error);

View File

@ -9,7 +9,7 @@ import SnortServiceProvider, { ManageHandle } from "Nip05/SnortServiceProvider";
export default function LNForwardAddress({ handle }: { handle: ManageHandle }) { export default function LNForwardAddress({ handle }: { handle: ManageHandle }) {
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();
const publisher = useEventPublisher(); const { publisher } = useEventPublisher();
const [newAddress, setNewAddress] = useState(handle.lnAddress ?? ""); const [newAddress, setNewAddress] = useState(handle.lnAddress ?? "");
const [error, setError] = useState(""); const [error, setError] = useState("");

View File

@ -8,7 +8,7 @@ import SnortServiceProvider, { ManageHandle } from "Nip05/SnortServiceProvider";
export default function ListHandles() { export default function ListHandles() {
const navigate = useNavigate(); const navigate = useNavigate();
const publisher = useEventPublisher(); const { publisher } = useEventPublisher();
const [handles, setHandles] = useState<Array<ManageHandle>>([]); const [handles, setHandles] = useState<Array<ManageHandle>>([]);
useEffect(() => { useEffect(() => {

View File

@ -1,14 +1,15 @@
import { useState } from "react";
import { FormattedMessage, useIntl } from "react-intl";
import { useNavigate } from "react-router-dom";
import { ApiHost } from "Const"; import { ApiHost } from "Const";
import AsyncButton from "Element/AsyncButton"; import AsyncButton from "Element/AsyncButton";
import useEventPublisher from "Hooks/useEventPublisher"; import useEventPublisher from "Hooks/useEventPublisher";
import { ServiceError } from "Nip05/ServiceProvider"; import { ServiceError } from "Nip05/ServiceProvider";
import SnortServiceProvider, { ManageHandle } from "Nip05/SnortServiceProvider"; import SnortServiceProvider, { ManageHandle } from "Nip05/SnortServiceProvider";
import { useState } from "react";
import { FormattedMessage, useIntl } from "react-intl";
import { useNavigate } from "react-router-dom";
export default function TransferHandle({ handle }: { handle: ManageHandle }) { export default function TransferHandle({ handle }: { handle: ManageHandle }) {
const publisher = useEventPublisher(); const { publisher } = useEventPublisher();
const navigate = useNavigate(); const navigate = useNavigate();
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();

View File

@ -9,7 +9,7 @@ import { mapSubscriptionErrorCode } from ".";
import SubscriptionCard from "./SubscriptionCard"; import SubscriptionCard from "./SubscriptionCard";
export default function ManageSubscriptionPage() { export default function ManageSubscriptionPage() {
const publisher = useEventPublisher(); const { publisher } = useEventPublisher();
const api = new SnortApi(undefined, publisher); const api = new SnortApi(undefined, publisher);
const navigate = useNavigate(); const navigate = useNavigate();

View File

@ -12,7 +12,7 @@ import { SnortNostrAddressService } from "Pages/NostrAddressPage";
import Nip05 from "Element/User/Nip05"; import Nip05 from "Element/User/Nip05";
export default function SubscriptionCard({ sub }: { sub: Subscription }) { export default function SubscriptionCard({ sub }: { sub: Subscription }) {
const publisher = useEventPublisher(); const { publisher } = useEventPublisher();
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();
const created = new Date(sub.created * 1000); const created = new Date(sub.created * 1000);

View File

@ -58,7 +58,7 @@ export function mapSubscriptionErrorCode(c: SubscriptionError) {
} }
export function SubscribePage() { export function SubscribePage() {
const publisher = useEventPublisher(); const { publisher } = useEventPublisher();
const api = new SnortApi(undefined, publisher); const api = new SnortApi(undefined, publisher);
const [invoice, setInvoice] = useState(""); const [invoice, setInvoice] = useState("");
const [error, setError] = useState<SubscriptionError>(); const [error, setError] = useState<SubscriptionError>();

View File

@ -41,7 +41,6 @@ export interface Uploader {
export default function useFileUpload(): Uploader { export default function useFileUpload(): Uploader {
const fileUploader = useLogin().preferences.fileUploader; const fileUploader = useLogin().preferences.fileUploader;
//const publisher = useEventPublisher();
switch (fileUploader) { switch (fileUploader) {
case "nostr.build": { case "nostr.build": {

View File

@ -43,7 +43,6 @@ import NostrLinkHandler from "Pages/NostrLinkHandler";
import { ThreadRoute } from "Element/Event/Thread"; import { ThreadRoute } from "Element/Event/Thread";
import { SubscribeRoutes } from "Pages/subscribe"; import { SubscribeRoutes } from "Pages/subscribe";
import ZapPoolPage from "Pages/ZapPool"; import ZapPoolPage from "Pages/ZapPool";
import DebugPage from "Pages/Debug";
import { db } from "Db"; import { db } from "Db";
import { preload, RelayMetrics, SystemDb, UserCache, UserRelays } from "Cache"; import { preload, RelayMetrics, SystemDb, UserCache, UserRelays } from "Cache";
import { LoginStore } from "Login"; import { LoginStore } from "Login";
@ -80,7 +79,7 @@ export const GetPowWorker = () => (hasWasm ? new WasmPowWorker() : unwrap(Defaul
/** /**
* Singleton nostr system * Singleton nostr system
*/ */
export const System = new NostrSystem({ const System = new NostrSystem({
relayCache: UserRelays, relayCache: UserRelays,
profileCache: UserCache, profileCache: UserCache,
relayMetrics: RelayMetrics, relayMetrics: RelayMetrics,
@ -229,10 +228,6 @@ export const router = createBrowserRouter([
...NewUserRoutes, ...NewUserRoutes,
...WalletRoutes, ...WalletRoutes,
...(CONFIG.features.subscriptions ? SubscribeRoutes : []), ...(CONFIG.features.subscriptions ? SubscribeRoutes : []),
{
path: "/debug",
element: <DebugPage />,
},
{ {
path: "/*", path: "/*",
element: <NostrLinkHandler />, element: <NostrLinkHandler />,

View File

@ -90,6 +90,9 @@
"1nYUGC": { "1nYUGC": {
"defaultMessage": "{n} Following" "defaultMessage": "{n} Following"
}, },
"1o2BgB": {
"defaultMessage": "Check Signatures"
},
"1udzha": { "1udzha": {
"defaultMessage": "Conversations" "defaultMessage": "Conversations"
}, },
@ -832,6 +835,9 @@
"UJTWqI": { "UJTWqI": {
"defaultMessage": "Remove from my relays" "defaultMessage": "Remove from my relays"
}, },
"UNjfWJ": {
"defaultMessage": "Check all event signatures received from relays"
},
"UT7Nkj": { "UT7Nkj": {
"defaultMessage": "New Chat" "defaultMessage": "New Chat"
}, },

View File

@ -29,6 +29,7 @@
"1c4YST": "Connected to: {node} 🎉", "1c4YST": "Connected to: {node} 🎉",
"1iQ8GN": "Toggle Preview", "1iQ8GN": "Toggle Preview",
"1nYUGC": "{n} Following", "1nYUGC": "{n} Following",
"1o2BgB": "Check Signatures",
"1udzha": "Conversations", "1udzha": "Conversations",
"2/2yg+": "Add", "2/2yg+": "Add",
"25V4l1": "Banner", "25V4l1": "Banner",
@ -272,6 +273,7 @@
"Tpy00S": "People", "Tpy00S": "People",
"UDYlxu": "Pending Subscriptions", "UDYlxu": "Pending Subscriptions",
"UJTWqI": "Remove from my relays", "UJTWqI": "Remove from my relays",
"UNjfWJ": "Check all event signatures received from relays",
"UT7Nkj": "New Chat", "UT7Nkj": "New Chat",
"UUPFlt": "Users must accept the content warning to show the content of your note.", "UUPFlt": "Users must accept the content warning to show the content of your note.",
"Up5U7K": "Block", "Up5U7K": "Block",

View File

@ -176,6 +176,6 @@ export abstract class EventExt {
if (type === EventType.ParameterizedReplaceable) { if (type === EventType.ParameterizedReplaceable) {
if (!findTag(ev, "d")) return false; if (!findTag(ev, "d")) return false;
} }
return EventExt.verify(ev); return true;
} }
} }

View File

@ -41,6 +41,11 @@ export * from "./cache/user-metadata";
export * from "./cache/relay-metric"; export * from "./cache/relay-metric";
export interface SystemInterface { export interface SystemInterface {
/**
* Check event signatures (reccomended)
*/
checkSigs: boolean;
/** /**
* Handler function for NIP-42 * Handler function for NIP-42
*/ */

View File

@ -81,6 +81,11 @@ export class NostrSystem extends ExternalStore<SystemSnapshot> implements System
*/ */
#queryOptimizer: QueryOptimizer; #queryOptimizer: QueryOptimizer;
/**
* Check event signatures (reccomended)
*/
checkSigs: boolean;
constructor(props: { constructor(props: {
authHandler?: AuthHandler; authHandler?: AuthHandler;
relayCache?: FeedCache<UsersRelays>; relayCache?: FeedCache<UsersRelays>;
@ -89,6 +94,7 @@ export class NostrSystem extends ExternalStore<SystemSnapshot> implements System
eventsCache?: FeedCache<NostrEvent>; eventsCache?: FeedCache<NostrEvent>;
queryOptimizer?: QueryOptimizer; queryOptimizer?: QueryOptimizer;
db?: SnortSystemDb; db?: SnortSystemDb;
checkSigs?: boolean;
}) { }) {
super(); super();
this.#handleAuth = props.authHandler; this.#handleAuth = props.authHandler;
@ -100,6 +106,7 @@ export class NostrSystem extends ExternalStore<SystemSnapshot> implements System
this.#profileLoader = new ProfileLoaderService(this, this.#profileCache); this.#profileLoader = new ProfileLoaderService(this, this.#profileCache);
this.#relayMetrics = new RelayMetricHandler(this.#relayMetricsCache); this.#relayMetrics = new RelayMetricHandler(this.#relayMetricsCache);
this.checkSigs = props.checkSigs ?? true;
this.#cleanup(); this.#cleanup();
} }
@ -183,6 +190,10 @@ export class NostrSystem extends ExternalStore<SystemSnapshot> implements System
this.#log("Rejecting invalid event %O", ev); this.#log("Rejecting invalid event %O", ev);
return; return;
} }
if (this.checkSigs && !EventExt.verify(ev)) {
this.#log("Invalid sig %O", ev);
return;
}
for (const [, v] of this.Queries) { for (const [, v] of this.Queries) {
v.handleEvent(sub, ev); v.handleEvent(sub, ev);