diff --git a/UI/app.tsx b/UI/app.tsx index 0a921c1..2d59a29 100644 --- a/UI/app.tsx +++ b/UI/app.tsx @@ -29,8 +29,6 @@ import { getSocialPosts } from "../features/social.ts"; import * as time from "../time.ts"; import { PublicKey } from "https://raw.githubusercontent.com/BlowaterNostr/nostr.ts/main/key.ts"; import { - DecryptionFailure, - decryptNostrEvent, NostrAccountContext, NostrEvent, NostrKind, @@ -41,13 +39,11 @@ import { } from "https://raw.githubusercontent.com/BlowaterNostr/nostr.ts/main/relay.ts"; import { getCurrentSignInCtx, setSignInState, SignIn } from "./signIn.tsx"; import { AppList } from "./app-list.tsx"; -import { LinkColor, PrimaryTextColor, SecondaryBackgroundColor } from "./style/colors.ts"; +import { SecondaryBackgroundColor } from "./style/colors.ts"; import { EventSyncer } from "./event_syncer.ts"; -import { getRelayURLs } from "./setting.ts"; +import { RelayConfig } from "./setting.ts"; import { DexieDatabase } from "./dexie-db.ts"; -import { DividerClass } from "./components/tw.ts"; import { About } from "./about.tsx"; -import { computeThreads } from "../nostr.ts"; export async function Start(database: DexieDatabase) { const model = initialModel(); @@ -143,12 +139,6 @@ async function initProfileSyncer( } })(); - /////////////////////////////////// - // Add relays to Connection Pool // - /////////////////////////////////// - const relayURLs = getRelayURLs(database); - pool.addRelayURLs(relayURLs); - return profilesSyncer; } @@ -156,6 +146,7 @@ export class App { profileSyncer!: ProfilesSyncer; eventSyncer: EventSyncer; public readonly allUsersInfo: AllUsersInformation; + public readonly relayConfig: RelayConfig; constructor( public readonly database: Database_Contextual_View, @@ -163,24 +154,37 @@ export class App { public readonly model: Model, public readonly myAccountContext: NostrAccountContext, public readonly eventBus: EventBus, - public readonly relayPool: ConnectionPool, + relayPool: ConnectionPool, ) { - this.eventSyncer = new EventSyncer(this.relayPool, this.database); + this.eventSyncer = new EventSyncer(relayPool, this.database); this.allUsersInfo = new AllUsersInformation(myAccountContext); + this.relayConfig = new RelayConfig(relayPool, this.myAccountContext); } initApp = async (accountContext: NostrAccountContext) => { - // const events = this.database.filterEvents((e) => e.kind == NostrKind.TEXT_NOTE); - console.log("App.initApp"); - const profilesSyncer = await initProfileSyncer(this.relayPool, accountContext, this.database); + this.allUsersInfo.addEvents(this.database.events); + { + /////////////////////////////////// + // Add relays to Connection Pool // + /////////////////////////////////// + const events = []; + for (const e of this.database.events) { + if (e.kind == NostrKind.CustomAppData) { + events.push(e); + } + } + await this.relayConfig.addEvents(events); + } + console.log("relay urls::", this.relayConfig.getRelayURLs()); + + const profilesSyncer = await initProfileSyncer(this.relayConfig.pool, accountContext, this.database); if (profilesSyncer instanceof Error) { return profilesSyncer; } this.profileSyncer = profilesSyncer; - this.allUsersInfo.addEvents(this.database.events); console.log("App allUsersInfo"); this.model.social.threads = getSocialPosts(this.database, this.allUsersInfo.userInfos); @@ -224,6 +228,7 @@ export class App { this.lamport, this.eventBus, this.allUsersInfo, + this.relayConfig, ) ) { render(, document.body); @@ -322,7 +327,7 @@ export function AppComponent(props: { > {Setting({ logout: app.logout, - pool: app.relayPool, + relayConfig: app.relayConfig, eventBus: app.eventBus, AddRelayButtonClickedError: model.AddRelayButtonClickedError, AddRelayInput: model.AddRelayInput, @@ -361,7 +366,7 @@ export function AppComponent(props: { eventEmitter: app.eventBus, myAccountContext: myAccountCtx, db: app.database, - pool: app.relayPool, + pool: app.relayConfig.pool, allUserInfo: app.allUsersInfo.userInfos, profilesSyncer: app.profileSyncer, eventSyncer: app.eventSyncer, @@ -386,7 +391,7 @@ export function AppComponent(props: { profilePicURL: model.myProfile?.picture, publicKey: myAccountCtx.publicKey, database: app.database, - pool: app.relayPool, + pool: app.relayConfig.pool, eventEmitter: app.eventBus, AddRelayButtonClickedError: model.AddRelayButtonClickedError, AddRelayInput: model.AddRelayInput, @@ -421,7 +426,7 @@ export function AppComponent(props: { profilePicURL={model.myProfile?.picture} publicKey={myAccountCtx.publicKey} database={app.database} - pool={app.relayPool} + pool={app.relayConfig.pool} eventEmitter={app.eventBus} AddRelayButtonClickedError={model.AddRelayButtonClickedError} AddRelayInput={model.AddRelayInput} diff --git a/UI/app_update.ts b/UI/app_update.ts index 9ab55d7..8a0cc01 100644 --- a/UI/app_update.ts +++ b/UI/app_update.ts @@ -38,6 +38,7 @@ import { import { MessageThread } from "./dm.tsx"; import { DexieDatabase } from "./dexie-db.ts"; import { getSocialPosts } from "../features/social.ts"; +import { RelayConfig } from "./setting.ts"; export type UI_Interaction_Event = | RemoveRelayButtonClicked @@ -144,7 +145,7 @@ export async function* UI_Interaction_Update( // if (event.type == "AddRelayButtonClicked") { // todo: need to think about concurrent/async UI update - model.app.relayPool.addRelayURL(event.url).then((err) => { + model.app.relayConfig.addRelayURL(event.url).then((err) => { if (err instanceof Error) { model.AddRelayButtonClickedError = err.message; } else { @@ -155,7 +156,7 @@ export async function* UI_Interaction_Update( } else if (event.type == "AddRelayInputChange") { model.AddRelayInput = event.url; } else if (event.type == "RemoveRelayButtonClicked") { - await model.app.relayPool.removeRelay(event.url); + await model.app.relayConfig.removeRelay(event.url); } // // // Search @@ -220,7 +221,7 @@ export async function* UI_Interaction_Update( console.error(nostrEvent); continue; } - const err = await model.app.relayPool.sendEvent(nostrEvent); + const err = await model.app.relayConfig.pool.sendEvent(nostrEvent); if (err instanceof Error) { console.error(err); } @@ -241,7 +242,7 @@ export async function* UI_Interaction_Update( files: event.files, kind: event.target.kind, lamport_timestamp: model.app.lamport.now(), - pool: model.app.relayPool, + pool: model.app.relayConfig.pool, waitAll: false, tags: event.tags, }); @@ -259,7 +260,7 @@ export async function* UI_Interaction_Update( sender: model.app.myAccountContext, message: event.text, lamport_timestamp: model.app.lamport.now(), - pool: model.app.relayPool, + pool: model.app.relayConfig.pool, tags: event.tags, }); if (event.id == "social") { @@ -328,7 +329,7 @@ export async function* UI_Interaction_Update( await saveProfile( event.profile, model.app.myAccountContext, - model.app.relayPool, + model.app.relayConfig.pool, ); } else if (event.type == "EditNewProfileFieldKey") { model.newProfileField.key = event.key; @@ -450,6 +451,7 @@ export async function* Database_Update( lamport: LamportTime, eventEmitter: EventEmitter, allUserInfo: AllUsersInformation, + relayConfig: RelayConfig, ) { const changes = database.onChange((_) => true); while (true) { @@ -469,6 +471,15 @@ export async function* Database_Update( } let hasKind_1 = false; + { + const events = []; + for (const e of changes_events) { + if (e.kind == NostrKind.CustomAppData) { + events.push(e); + } + } + await relayConfig.addEvents(events); + } for (let e of changes_events) { allUserInfo.addEvents([e]); const t = getTags(e).lamport_timestamp; diff --git a/UI/contact-list.ts b/UI/contact-list.ts index 80afd77..b81ecef 100644 --- a/UI/contact-list.ts +++ b/UI/contact-list.ts @@ -220,9 +220,6 @@ export class AllUsersInformation { case NostrKind.DELETE: break; case NostrKind.CustomAppData: { - if (event.kind == NostrKind.CustomAppData) { - event; - } const obj: CustomAppData = JSON.parse(event.decryptedContent); if (obj.type == "PinContact" || obj.type == "UnpinContact") { const userInfo = this.userInfos.get(obj.pubkey); diff --git a/UI/deno.lock b/UI/deno.lock index 32641c6..a956acf 100644 --- a/UI/deno.lock +++ b/UI/deno.lock @@ -2,13 +2,9 @@ "version": "2", "remote": { "https://deno.land/std@0.176.0/encoding/hex.ts": "50f8c95b52eae24395d3dfcb5ec1ced37c5fe7610ef6fffdcc8b0fdc38e3b32f", - "https://deno.land/std@0.176.0/fmt/colors.ts": "938c5d44d889fb82eff6c358bea8baa7e85950a16c9f6dae3ec3a7a729164471", - "https://deno.land/std@0.176.0/testing/_diff.ts": "1a3c044aedf77647d6cac86b798c6417603361b66b54c53331b312caeb447aea", - "https://deno.land/std@0.176.0/testing/_format.ts": "a69126e8a469009adf4cf2a50af889aca364c349797e63174884a52ff75cf4c7", - "https://deno.land/std@0.176.0/testing/asserts.ts": "984ab0bfb3faeed92ffaa3a6b06536c66811185328c5dd146257c702c41b01ab", "https://esm.sh/preact@10.11.3": "ad3c24796c4132c84b4b392f812228d53a0fd600fa64648f27aebd05ec09b24e", "https://esm.sh/stable/preact@10.11.3/denonext/preact.mjs": "c828d9020ea26f07ba07b2f0a3ef97fd7ceb1c2a772c380ca05e2647ac8d3923", - "https://esm.sh/twind@0.16.16": "6c8934ebceb225d94d9e11f42338e09a94cf9e7bce84d9817a1123a0cea44cd2", + "https://esm.sh/twind@0.16.16": "e6e76f9434facce39a98842576a98921cc62e52ca5e8fbe5ed285d09472a491e", "https://esm.sh/v128/csstype@3.1.2/index.d.ts": "4c68749a564a6facdf675416d75789ee5a557afda8960e0803cf6711fa569288", "https://esm.sh/v128/preact@10.11.3/src/index.d.ts": "76842a9d103548261ee001f2aee5fa5cacc8b1b4b4032af537ed84603c8f4a31", "https://esm.sh/v128/preact@10.11.3/src/jsx.d.ts": "77ce5bd7324455c9f6dc85bfc3c94b733acb92cc98fdf82e4b1059c8bca7866d", @@ -19,8 +15,8 @@ "https://raw.githubusercontent.com/BlowaterNostr/nostr.ts/main/ende.ts": "00807d3602a65d5a6881c8fbb01d6886bf7a6640a2cc5c7c1bf00dd00b320ae7", "https://raw.githubusercontent.com/BlowaterNostr/nostr.ts/main/key.ts": "1830bb6eded80c71aeae6a94ce921dbf09fcdf4820a3b846f8e8d9e23ce17119", "https://raw.githubusercontent.com/BlowaterNostr/nostr.ts/main/nip19.ts": "880aa40c85bb770703b5e85d72f29fed96f21777dc17906017c385873aedfd44", - "https://raw.githubusercontent.com/BlowaterNostr/nostr.ts/main/nostr.ts": "aeac23272327a1a0c576aaa66a040497474ec0d325fa27bbb112b2b36110abb3", - "https://raw.githubusercontent.com/BlowaterNostr/nostr.ts/main/relay.ts": "93c5c6fe196dc9cae7c0e82ee8057a5350f77e69ae7d395fdb141a25b2664739", + "https://raw.githubusercontent.com/BlowaterNostr/nostr.ts/main/nostr.ts": "8cee22b9a14ec735cbea9d00b06a634417d54cf5b847eef3d93c7ca4906742d8", + "https://raw.githubusercontent.com/BlowaterNostr/nostr.ts/main/relay.ts": "c58ac282ec1c980ee5eac6f3d1fc323f24d25dbea9f07af6381b9e6d3f1d5109", "https://raw.githubusercontent.com/BlowaterNostr/nostr.ts/main/scure.js": "fbc4be16918272bd167fff1184a7f5bbd1a676bad2a73130bb530d78df893a99", "https://raw.githubusercontent.com/BlowaterNostr/nostr.ts/main/vendor/esm.sh/v106/@noble/secp256k1@1.7.1/es2022/secp256k1.js": "69e32f6c686cc651ff2e6d4d22ed6e9b6f86b38311b405b47b2abdf3cb98eb4d", "https://raw.githubusercontent.com/BlowaterNostr/nostr.ts/main/websocket.ts": "d817c40d32fe45e30bfb30255287e76a977930f6709c9585772dc54e077a31ed", diff --git a/UI/setting.ts b/UI/setting.ts index f350502..f1283e7 100644 --- a/UI/setting.ts +++ b/UI/setting.ts @@ -1,4 +1,9 @@ -import { Database_Contextual_View } from "../database.ts"; +import { + NostrAccountContext, + prepareCustomAppDataEvent, +} from "https://raw.githubusercontent.com/BlowaterNostr/nostr.ts/main/nostr.ts"; +import { ConnectionPool } from "https://raw.githubusercontent.com/BlowaterNostr/nostr.ts/main/relay.ts"; +import { CustomAppData, Decrypted_Nostr_Event } from "../nostr.ts"; const damus = "wss://relay.damus.io"; const nos = "wss://nos.lol"; @@ -9,6 +14,74 @@ export const defaultRelays = [ "wss://relay.nostr.wirednet.jp", ]; -export function getRelayURLs(db: Database_Contextual_View): string[] { - return defaultRelays; +export class RelayConfig { + private readonly relaySet = new Set(); + + constructor( + public readonly pool: ConnectionPool, + public readonly ctx: NostrAccountContext, + ) {} + + getRelayURLs(): string[] { + const urls = this.pool.getRelays().map((r) => r.url); + if (urls.length == 0) { + return defaultRelays; + } + return urls; + } + + async addEvents(events: Decrypted_Nostr_Event[]) { + for (const event of events) { + const obj: CustomAppData = JSON.parse(event.decryptedContent); + if (obj.type == "AddRelay") { + this.relaySet.add(obj.url); + } else if (obj.type == "RemoveRelay") { + this.relaySet.delete(obj.url); + } + } + const s = new Set(this.pool.getRelays().map((r) => r.url)); + // add + for (const url of this.relaySet) { + if (!s.has(url)) { + const err = await this.pool.addRelayURL(url); + if (err instanceof Error) { + console.error(err); + continue; + } + } + } + // remove + for (const url of s) { + if (!this.relaySet.has(url)) { + this.pool.removeRelay(url); + } + } + } + + async addRelayURL(url: string) { + const err = await this.pool.addRelayURL(url); + if (err instanceof Error) { + return err; + } + const event = await prepareCustomAppDataEvent(this.ctx, { + type: "AddRelay", + url: url, + }); + if (event instanceof Error) { + return event; + } + return this.pool.sendEvent(event); + } + + async removeRelay(url: string) { + await this.pool.removeRelay(url); + const event = await prepareCustomAppDataEvent(this.ctx, { + type: "RemoveRelay", + url: url, + }); + if (event instanceof Error) { + return event; + } + return this.pool.sendEvent(event); + } } diff --git a/UI/setting.tsx b/UI/setting.tsx index 8b54a66..fc73b62 100644 --- a/UI/setting.tsx +++ b/UI/setting.tsx @@ -1,5 +1,5 @@ /** @jsx h */ -import { Fragment, FunctionComponent, h } from "https://esm.sh/preact@10.11.3"; +import { Fragment, h } from "https://esm.sh/preact@10.11.3"; import { tw } from "https://esm.sh/twind@0.16.16"; import { EventBus } from "../event-bus.ts"; @@ -31,10 +31,11 @@ import { } from "./style/colors.ts"; import { RelayIcon } from "./icons2/relay-icon.tsx"; import { DeleteIcon } from "./icons2/delete-icon.tsx"; +import { RelayConfig } from "./setting.ts"; export interface SettingProps { logout: () => void; - pool: ConnectionPool; + relayConfig: RelayConfig; eventBus: EventBus; AddRelayButtonClickedError: string; AddRelayInput: string; @@ -48,8 +49,8 @@ const colors = { "Closed": ErrorColor, }; -export const Setting: FunctionComponent = (props: SettingProps) => { - const relays = props.pool.getRelays().map( +export const Setting = (props: SettingProps) => { + const relays = props.relayConfig.pool.getRelays().map( (r) => ({ url: r.url, status: r.ws.status(), diff --git a/deno.lock b/deno.lock index 32641c6..424dc37 100644 --- a/deno.lock +++ b/deno.lock @@ -8,7 +8,7 @@ "https://deno.land/std@0.176.0/testing/asserts.ts": "984ab0bfb3faeed92ffaa3a6b06536c66811185328c5dd146257c702c41b01ab", "https://esm.sh/preact@10.11.3": "ad3c24796c4132c84b4b392f812228d53a0fd600fa64648f27aebd05ec09b24e", "https://esm.sh/stable/preact@10.11.3/denonext/preact.mjs": "c828d9020ea26f07ba07b2f0a3ef97fd7ceb1c2a772c380ca05e2647ac8d3923", - "https://esm.sh/twind@0.16.16": "6c8934ebceb225d94d9e11f42338e09a94cf9e7bce84d9817a1123a0cea44cd2", + "https://esm.sh/twind@0.16.16": "e6e76f9434facce39a98842576a98921cc62e52ca5e8fbe5ed285d09472a491e", "https://esm.sh/v128/csstype@3.1.2/index.d.ts": "4c68749a564a6facdf675416d75789ee5a557afda8960e0803cf6711fa569288", "https://esm.sh/v128/preact@10.11.3/src/index.d.ts": "76842a9d103548261ee001f2aee5fa5cacc8b1b4b4032af537ed84603c8f4a31", "https://esm.sh/v128/preact@10.11.3/src/jsx.d.ts": "77ce5bd7324455c9f6dc85bfc3c94b733acb92cc98fdf82e4b1059c8bca7866d", @@ -19,8 +19,8 @@ "https://raw.githubusercontent.com/BlowaterNostr/nostr.ts/main/ende.ts": "00807d3602a65d5a6881c8fbb01d6886bf7a6640a2cc5c7c1bf00dd00b320ae7", "https://raw.githubusercontent.com/BlowaterNostr/nostr.ts/main/key.ts": "1830bb6eded80c71aeae6a94ce921dbf09fcdf4820a3b846f8e8d9e23ce17119", "https://raw.githubusercontent.com/BlowaterNostr/nostr.ts/main/nip19.ts": "880aa40c85bb770703b5e85d72f29fed96f21777dc17906017c385873aedfd44", - "https://raw.githubusercontent.com/BlowaterNostr/nostr.ts/main/nostr.ts": "aeac23272327a1a0c576aaa66a040497474ec0d325fa27bbb112b2b36110abb3", - "https://raw.githubusercontent.com/BlowaterNostr/nostr.ts/main/relay.ts": "93c5c6fe196dc9cae7c0e82ee8057a5350f77e69ae7d395fdb141a25b2664739", + "https://raw.githubusercontent.com/BlowaterNostr/nostr.ts/main/nostr.ts": "8cee22b9a14ec735cbea9d00b06a634417d54cf5b847eef3d93c7ca4906742d8", + "https://raw.githubusercontent.com/BlowaterNostr/nostr.ts/main/relay.ts": "c58ac282ec1c980ee5eac6f3d1fc323f24d25dbea9f07af6381b9e6d3f1d5109", "https://raw.githubusercontent.com/BlowaterNostr/nostr.ts/main/scure.js": "fbc4be16918272bd167fff1184a7f5bbd1a676bad2a73130bb530d78df893a99", "https://raw.githubusercontent.com/BlowaterNostr/nostr.ts/main/vendor/esm.sh/v106/@noble/secp256k1@1.7.1/es2022/secp256k1.js": "69e32f6c686cc651ff2e6d4d22ed6e9b6f86b38311b405b47b2abdf3cb98eb4d", "https://raw.githubusercontent.com/BlowaterNostr/nostr.ts/main/websocket.ts": "d817c40d32fe45e30bfb30255287e76a977930f6709c9585772dc54e077a31ed", diff --git a/nostr.ts b/nostr.ts index 3c6e6f5..f3bebd8 100644 --- a/nostr.ts +++ b/nostr.ts @@ -299,7 +299,7 @@ export type PlainText_Nostr_Event = export type Profile_Nostr_Event = Parsed_Event & { profile: ProfileData; }; -export type CustomAppData = PinContact | UnpinContact | UserLogin; +export type CustomAppData = PinContact | UnpinContact | UserLogin | AddRelay | RemoveRelay; export type PinContact = { type: "PinContact"; @@ -314,3 +314,13 @@ export type UnpinContact = { export type UserLogin = { type: "UserLogin"; }; + +export type AddRelay = { + type: "AddRelay"; + url: string; +}; + +export type RemoveRelay = { + type: "RemoveRelay"; + url: string; +};