import { HexKey, RelaySettings } from "@snort/nostr"; import * as secp from "@noble/secp256k1"; import { DefaultRelays, SnortPubKey } from "Const"; import { LoginStore, UserPreferences, LoginSession } from "Login"; import { generateBip39Entropy, entropyToPrivateKey } from "nip6"; import { bech32ToHex, dedupeById, randomSample, sanitizeRelayUrl, unixNowMs, unwrap } from "Util"; import { SubscriptionEvent } from "Subscription"; import { EventPublisher } from "System/EventPublisher"; export function setRelays(state: LoginSession, relays: Record, createdAt: number) { if (state.relays.timestamp >= createdAt) { return; } // filter out non-websocket urls const filtered = new Map(); for (const [k, v] of Object.entries(relays)) { if (k.startsWith("wss://") || k.startsWith("ws://")) { const url = sanitizeRelayUrl(k); if (url) { filtered.set(url, v as RelaySettings); } } } state.relays.item = Object.fromEntries(filtered.entries()); state.relays.timestamp = createdAt; LoginStore.updateSession(state); } export function removeRelay(state: LoginSession, addr: string) { delete state.relays.item[addr]; LoginStore.updateSession(state); } export function updatePreferences(state: LoginSession, p: UserPreferences) { state.preferences = p; LoginStore.updateSession(state); } export function logout(k: HexKey) { LoginStore.removeSession(k); } export function markNotificationsRead(state: LoginSession) { state.readNotifications = unixNowMs(); LoginStore.updateSession(state); } export function clearEntropy(state: LoginSession) { state.generatedEntropy = undefined; LoginStore.updateSession(state); } /** * Generate a new key and login with this generated key */ export async function generateNewLogin() { const ent = generateBip39Entropy(); const entropy = secp.utils.bytesToHex(ent); const privateKey = entropyToPrivateKey(ent); let newRelays: Record = {}; try { const rsp = await fetch("https://api.nostr.watch/v1/online"); if (rsp.ok) { const online: string[] = await rsp.json(); const pickRandom = randomSample(online, 4); const relayObjects = pickRandom.map(a => [unwrap(sanitizeRelayUrl(a)), { read: true, write: true }]); newRelays = { ...Object.fromEntries(relayObjects), ...Object.fromEntries(DefaultRelays.entries()), }; } } catch (e) { console.warn(e); } const publicKey = secp.utils.bytesToHex(secp.schnorr.getPublicKey(privateKey)); const publisher = new EventPublisher(publicKey, privateKey); const ev = await publisher.contactList([bech32ToHex(SnortPubKey), publicKey], newRelays); publisher.broadcast(ev); LoginStore.loginWithPrivateKey(privateKey, entropy, newRelays); } export function generateRandomKey() { const privateKey = secp.utils.bytesToHex(secp.utils.randomPrivateKey()); const publicKey = secp.utils.bytesToHex(secp.schnorr.getPublicKey(privateKey)); return { privateKey, publicKey, }; } export function setTags(state: LoginSession, tags: Array, ts: number) { if (state.tags.timestamp >= ts) { return; } state.tags.item = tags; state.tags.timestamp = ts; LoginStore.updateSession(state); } export function setMuted(state: LoginSession, muted: Array, ts: number) { if (state.muted.timestamp >= ts) { return; } state.muted.item = muted; state.muted.timestamp = ts; LoginStore.updateSession(state); } export function setBlocked(state: LoginSession, blocked: Array, ts: number) { if (state.blocked.timestamp >= ts) { return; } state.blocked.item = blocked; state.blocked.timestamp = ts; LoginStore.updateSession(state); } export function setFollows(state: LoginSession, follows: Array, ts: number) { if (state.follows.timestamp >= ts) { return; } state.follows.item = follows; state.follows.timestamp = ts; LoginStore.updateSession(state); } export function setPinned(state: LoginSession, pinned: Array, ts: number) { if (state.pinned.timestamp >= ts) { return; } state.pinned.item = pinned; state.pinned.timestamp = ts; LoginStore.updateSession(state); } export function setBookmarked(state: LoginSession, bookmarked: Array, ts: number) { if (state.bookmarked.timestamp >= ts) { return; } state.bookmarked.item = bookmarked; state.bookmarked.timestamp = ts; LoginStore.updateSession(state); } export function addSubscription(state: LoginSession, ...subs: SubscriptionEvent[]) { const newSubs = dedupeById([...(state.subscriptions || []), ...subs]); if (newSubs.length !== state.subscriptions.length) { state.subscriptions = newSubs; LoginStore.updateSession(state); } }