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) {
const { publicKey } = useLogin(s => ({ publicKey: s.publicKey }));
const publisher = useEventPublisher();
const { publisher } = useEventPublisher();
const msg = props.data;
const [content, setContent] = useState<string>();
const { ref, inView } = useInView({ triggerOnce: true });

View File

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

View File

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

View File

@ -6,7 +6,6 @@ import { NostrEvent, OkResponse } from "@snort/system";
import AsyncButton from "Element/AsyncButton";
import Icon from "Icons/Icon";
import { getRelayName, sanitizeRelayUrl } from "SnortUtils";
import { System } from "index";
import { removeRelay } from "Login";
import useLogin from "Hooks/useLogin";
import useEventPublisher from "Hooks/useEventPublisher";
@ -23,7 +22,7 @@ export function NoteBroadcaster({
const [results, setResults] = useState<Array<OkResponse>>([]);
const { formatMessage } = useIntl();
const login = useLogin();
const publisher = useEventPublisher();
const { publisher, system } = useEventPublisher();
async function sendEventToRelays(ev: NostrEvent) {
if (customRelays) {
@ -31,7 +30,7 @@ export function NoteBroadcaster({
await Promise.all(
customRelays.map(async r => {
try {
return await System.WriteOnceToRelay(r, ev);
return await system.WriteOnceToRelay(r, ev);
} catch (e) {
console.error(e);
}
@ -39,7 +38,7 @@ export function NoteBroadcaster({
),
);
} 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) {
removeRelay(login, unwrap(sanitizeRelayUrl(r.relay)));
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));
}
}
@ -66,7 +65,7 @@ export function NoteBroadcaster({
async function retryPublish(r: OkResponse) {
const ev = evs.find(a => a.id === r.id);
if (ev) {
const rsp = await System.WriteOnceToRelay(unwrap(sanitizeRelayUrl(r.relay)), ev);
const rsp = await system.WriteOnceToRelay(unwrap(sanitizeRelayUrl(r.relay)), ev);
setResults(s =>
s.map(x => {
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 { HexKey, Lists, NostrLink, TaggedNostrEvent } from "@snort/system";
import { Menu, MenuItem } from "@szhsin/react-menu";
import { TranslateHost } from "Const";
import { System } from "index";
import Icon from "Icons/Icon";
import { setPinned, setBookmarked } from "Login";
import messages from "Element/messages";
@ -11,7 +11,6 @@ import useLogin from "Hooks/useLogin";
import useModeration from "Hooks/useModeration";
import useEventPublisher from "Hooks/useEventPublisher";
import { ReBroadcaster } from "../ReBroadcaster";
import { useState } from "react";
export interface NoteTranslation {
text: string;
@ -30,7 +29,7 @@ export function NoteContextMenu({ ev, ...props }: NosteContextMenuProps) {
const { formatMessage } = useIntl();
const login = useLogin();
const { mute, block } = useModeration();
const publisher = useEventPublisher();
const { publisher, system } = useEventPublisher();
const [showBroadcast, setShowBroadcast] = useState(false);
const lang = window.navigator.language;
const langNames = new Intl.DisplayNames([...window.navigator.languages], {
@ -41,7 +40,7 @@ export function NoteContextMenu({ ev, ...props }: NosteContextMenuProps) {
async function deleteEvent() {
if (window.confirm(formatMessage(messages.ConfirmDeletion, { id: ev.id.substring(0, 8) })) && publisher) {
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) {
const es = [...login.pinned.item, id];
const ev = await publisher.noteList(es, Lists.Pinned);
System.BroadcastEvent(ev);
system.BroadcastEvent(ev);
setPinned(login, es, ev.created_at * 1000);
}
}
@ -99,7 +98,7 @@ export function NoteContextMenu({ ev, ...props }: NosteContextMenuProps) {
if (publisher) {
const es = [...login.bookmarked.item, id];
const ev = await publisher.noteList(es, Lists.Bookmarked);
System.BroadcastEvent(ev);
system.BroadcastEvent(ev);
setBookmarked(login, es, ev.created_at * 1000);
}
}

View File

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

View File

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

View File

@ -21,7 +21,7 @@ type PollTally = "zaps" | "pubkeys";
export default function Poll(props: PollProps) {
const { formatMessage } = useIntl();
const publisher = useEventPublisher();
const { publisher } = useEventPublisher();
const { wallet } = useWallet();
const { preferences: prefs, publicKey: myPubKey, relays } = useLogin();
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 useLogin from "../../Hooks/useLogin";
import { useUserProfile } from "@snort/system-react";
import { System } from "../../index";
import { UserCache } from "../../Cache";
import useEventPublisher from "../../Hooks/useEventPublisher";
import { mapEventToProfile } from "@snort/system";
import FormattedMessage from "Element/FormattedMessage";
export default function ActiveAccount({ name = "", setAsPrimary = () => {} }) {
@ -13,7 +13,7 @@ export default function ActiveAccount({ name = "", setAsPrimary = () => {} }) {
readonly: s.readonly,
}));
const profile = useUserProfile(publicKey);
const publisher = useEventPublisher();
const { publisher, system } = useEventPublisher();
async function saveProfile(nip05: string) {
if (readonly) {
@ -35,7 +35,7 @@ export default function ActiveAccount({ name = "", setAsPrimary = () => {} }) {
if (publisher) {
const ev = await publisher.metadata(userCopy);
System.BroadcastEvent(ev);
system.BroadcastEvent(ev);
const newProfile = mapEventToProfile(ev);
if (newProfile) {

View File

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

View File

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

View File

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

View File

@ -1,9 +1,8 @@
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 { HexKey } from "@snort/system";
import { SnortContext } from "@snort/system-react";
import { LNURLSuccessAction } from "@snort/shared";
import { formatShort } from "Number";
@ -48,8 +47,7 @@ export default function SendSats(props: SendSatsProps) {
const [success, setSuccess] = useState<LNURLSuccessAction>();
const [amount, setAmount] = useState<SendSatsInputSelection>();
const system = useContext(SnortContext);
const publisher = useEventPublisher();
const { publisher, system } = useEventPublisher();
const walletState = useWallet();
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 useLogin from "Hooks/useLogin";
import AsyncButton from "Element/AsyncButton";
import { System } from "index";
import messages from "../messages";
import { FollowsFeed } from "Cache";
@ -16,7 +15,7 @@ export interface FollowButtonProps {
}
export default function FollowButton(props: FollowButtonProps) {
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 isFollowing = follows.item.includes(pubkey);
const baseClassname = props.className ? `${props.className} ` : "";
@ -24,8 +23,8 @@ export default function FollowButton(props: FollowButtonProps) {
async function follow(pubkey: HexKey) {
if (publisher) {
const ev = await publisher.contactList([pubkey, ...follows.item], relays.item);
System.BroadcastEvent(ev);
await FollowsFeed.backFill(System, [pubkey]);
system.BroadcastEvent(ev);
await FollowsFeed.backFill(system, [pubkey]);
}
}
@ -35,7 +34,7 @@ export default function FollowButton(props: FollowButtonProps) {
follows.item.filter(a => a !== pubkey),
relays.item,
);
System.BroadcastEvent(ev);
system.BroadcastEvent(ev);
}
}

View File

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

View File

@ -24,7 +24,6 @@ import { SnortPubKey } from "Const";
import { SubscriptionEvent } from "Subscription";
import useRelaysFeedFollows from "./RelaysFeedFollows";
import { FollowsFeed, GiftsCache, Notifications, UserRelays } from "Cache";
import { System } from "index";
import { Nip28Chats, Nip4Chats } from "chat";
import { useRefreshFeedCache } from "Hooks/useRefreshFeedcache";
@ -35,12 +34,16 @@ export default function useLoginFeed() {
const login = useLogin();
const { publicKey: pubKey, readNotifications, follows } = login;
const { isMuted } = useModeration();
const publisher = useEventPublisher();
const { publisher, system } = useEventPublisher();
useRefreshFeedCache(Notifications, true);
useRefreshFeedCache(FollowsFeed, true);
useRefreshFeedCache(GiftsCache, true);
useEffect(() => {
system.checkSigs = login.preferences.checkSigs;
}, [login]);
const subLogin = useMemo(() => {
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]);
setFollows(login, pTags, contactList.created_at * 1000);
FollowsFeed.backFillIfMissing(System, pTags);
FollowsFeed.backFillIfMissing(system, pTags);
}
Nip4Chats.onEvent(loginFeed.data);
@ -202,7 +205,7 @@ export default function useLoginFeed() {
useEffect(() => {
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]);
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) {
const c = System.Sockets.find(a => a.address === addr);
const system = useContext(SnortContext);
const c = system.Sockets.find(a => a.address === addr);
return c;
}

View File

@ -89,7 +89,7 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel
const rb = createBuilder();
if (rb) {
if (options.method === "LIMIT_UNTIL") {
rb.filter.until(until).limit(200);
rb.filter.until(until).limit(100);
} else {
rb.filter.since(since).until(until);
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 { LoginStore, createPublisher, sessionNeedsPin } from "Login";
export default function useEventPublisher() {
const login = useLogin();
const system = useContext(SnortContext);
let existing = LoginStore.getPublisher(login.id);
@ -12,5 +15,8 @@ export default function useEventPublisher() {
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 useLogin from "./useLogin";
import useEventPublisher from "./useEventPublisher";
export function useLoginRelays() {
const { relays } = useLogin();
const { system } = useEventPublisher();
useEffect(() => {
if (relays) {
(async () => {
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) {
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 { setBlocked, setMuted } from "Login";
import { appendDedupe } from "SnortUtils";
import { System } from "index";
export default function useModeration() {
const login = useLogin();
const { muted, blocked, appData } = login;
const publisher = useEventPublisher();
const { publisher, system } = useEventPublisher();
async function setMutedList(pub: HexKey[], priv: HexKey[]) {
if (publisher) {
const ev = await publisher.muted(pub, priv);
System.BroadcastEvent(ev);
system.BroadcastEvent(ev);
return ev.created_at * 1000;
}
return 0;

View File

@ -1,5 +1,4 @@
import { SnortContext } from "@snort/system-react";
import { useContext, useEffect, useMemo } from "react";
import { useEffect, useMemo } from "react";
import { NoopStore, RequestBuilder, TaggedNostrEvent } from "@snort/system";
import { RefreshFeedCache } from "Cache/RefreshFeedCache";
@ -8,9 +7,8 @@ import useEventPublisher from "./useEventPublisher";
import { unwrap } from "@snort/shared";
export function useRefreshFeedCache<T>(c: RefreshFeedCache<T>, leaveOpen = false) {
const system = useContext(SnortContext);
const login = useLogin();
const publisher = useEventPublisher();
const { publisher, system } = useEventPublisher();
const sub = useMemo(() => {
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 * as secp from "@noble/curves/secp256k1";
import * as utils from "@noble/curves/abstract/utils";
@ -8,7 +16,6 @@ import { LoginStore, UserPreferences, LoginSession, LoginSessionType, SnortAppDa
import { generateBip39Entropy, entropyToPrivateKey } from "nip6";
import { bech32ToHex, dedupeById, randomSample, sanitizeRelayUrl, unwrap } from "SnortUtils";
import { SubscriptionEvent } from "Subscription";
import { System } from "index";
import { Chats, FollowsFeed, GiftsCache, Notifications } from "Cache";
import { Nip7OsSigner } from "./Nip7OsSigner";
@ -63,7 +70,7 @@ export function clearEntropy(state: LoginSession) {
/**
* 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 entropy = utils.bytesToHex(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 publisher = EventPublisher.privateKey(privateKey);
const ev = await publisher.contactList([bech32ToHex(SnortPubKey), publicKey], newRelays);
System.BroadcastEvent(ev);
system.BroadcastEvent(ev);
LoginStore.loginWithPrivateKey(await pin(privateKey), entropy, newRelays);
}

View File

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

View File

@ -86,6 +86,11 @@ export interface UserPreferences {
* Show user status messages on profiles
*/
showStatus?: boolean;
/**
* Check event signatures
*/
checkSigs: boolean;
}
export const DefaultPreferences = {
@ -105,4 +110,5 @@ export const DefaultPreferences = {
telemetry: true,
showBadges: false,
showStatus: true,
checkSigs: true,
} 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 useLogin from "Hooks/useLogin";
import { setTags } from "Login";
import { System } from "index";
const HashTagsPage = () => {
const params = useParams();
@ -15,12 +14,12 @@ const HashTagsPage = () => {
const isFollowing = useMemo(() => {
return login.tags.item.includes(tag);
}, [login, tag]);
const publisher = useEventPublisher();
const { publisher, system } = useEventPublisher();
async function followTags(ts: string[]) {
if (publisher) {
const ev = await publisher.tags(ts);
System.BroadcastEvent(ev);
system.BroadcastEvent(ev);
setTags(login, ts, ev.created_at * 1000);
}
}

View File

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

View File

@ -3,9 +3,9 @@ import { Link, Outlet, RouteObject, useParams } from "react-router-dom";
import FormattedMessage from "Element/FormattedMessage";
import { unixNow } from "@snort/shared";
import { NostrLink } from "@snort/system";
import { SnortContext } from "@snort/system-react";
import Timeline from "Element/Feed/Timeline";
import { System } from "index";
import { TimelineSubject } from "Feed/TimelineFeed";
import { debounce, getRelayName, sha256 } from "SnortUtils";
import useLogin from "Hooks/useLogin";
@ -63,6 +63,7 @@ export const GlobalTab = () => {
const [relay, setRelay] = useState<RelayOption>();
const [allRelays, setAllRelays] = useState<RelayOption[]>();
const [now] = useState(unixNow());
const system = useContext(SnortContext);
const subject: TimelineSubject = {
type: "global",
@ -111,7 +112,7 @@ export const GlobalTab = () => {
useEffect(() => {
return debounce(500, () => {
const ret: RelayOption[] = [];
System.Sockets.forEach(v => {
system.Sockets.forEach(v => {
ret.push({
url: v.address,
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 AsyncButton from "Element/AsyncButton";
import { useWallet } from "Wallet";
import { System } from "index";
import useEventPublisher from "Hooks/useEventPublisher";
const DataProviders = [
{
@ -71,6 +71,7 @@ function ZapTarget({ target }: { target: ZapPoolRecipient }) {
export default function ZapPoolPage() {
const login = useLogin();
const { system } = useEventPublisher();
const zapPool = useSyncExternalStore(
c => ZapPoolController.hook(c),
() => ZapPoolController.snapshot(),
@ -78,7 +79,7 @@ export default function ZapPoolPage() {
const { wallet } = useWallet();
const relayConnections = useMemo(() => {
return System.Sockets.map(a => {
return system.Sockets.map(a => {
if (a.info?.pubkey && !a.ephemeral) {
return {
address: a.address,

View File

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

View File

@ -176,6 +176,23 @@ const PreferencesPage = () => {
</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-column g8">
<h4>

View File

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

View File

@ -3,18 +3,19 @@ import ProfilePreview from "Element/User/ProfilePreview";
import useRelayState from "Feed/RelayState";
import { useNavigate, useParams } from "react-router-dom";
import { parseId, unwrap } from "SnortUtils";
import { System } from "index";
import { removeRelay } from "Login";
import useLogin from "Hooks/useLogin";
import messages from "./messages";
import useEventPublisher from "Hooks/useEventPublisher";
const RelayInfo = () => {
const params = useParams();
const navigate = useNavigate();
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 ?? "");
return (

View File

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

View File

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

View File

@ -8,7 +8,7 @@ import SnortServiceProvider, { ManageHandle } from "Nip05/SnortServiceProvider";
export default function ListHandles() {
const navigate = useNavigate();
const publisher = useEventPublisher();
const { publisher } = useEventPublisher();
const [handles, setHandles] = useState<Array<ManageHandle>>([]);
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 AsyncButton from "Element/AsyncButton";
import useEventPublisher from "Hooks/useEventPublisher";
import { ServiceError } from "Nip05/ServiceProvider";
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 }) {
const publisher = useEventPublisher();
const { publisher } = useEventPublisher();
const navigate = useNavigate();
const { formatMessage } = useIntl();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -176,6 +176,6 @@ export abstract class EventExt {
if (type === EventType.ParameterizedReplaceable) {
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 interface SystemInterface {
/**
* Check event signatures (reccomended)
*/
checkSigs: boolean;
/**
* Handler function for NIP-42
*/

View File

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