diff --git a/UI/app.tsx b/UI/app.tsx index b3e8da9..36440a7 100644 --- a/UI/app.tsx +++ b/UI/app.tsx @@ -14,10 +14,9 @@ import { MessagePanel } from "./message-panel.tsx"; import { Setting } from "./setting.tsx"; import { Database_Contextual_View } from "../database.ts"; -import { getAllUsersInformation, ProfilesSyncer, UserInfo } from "./contact-list.ts"; +import { AllUsersInformation, ProfilesSyncer, UserInfo } from "./contact-list.ts"; import { new_DM_EditorModel } from "./editor.tsx"; -import { Channel } from "https://raw.githubusercontent.com/BlowaterNostr/csp/master/csp.ts"; import { initialModel, Model } from "./app_model.ts"; import { AppEventBus, @@ -156,6 +155,7 @@ async function initProfileSyncer( export class App { profileSyncer!: ProfilesSyncer; eventSyncer: EventSyncer; + public readonly allUsersInfo: AllUsersInformation; constructor( public readonly database: Database_Contextual_View, @@ -166,10 +166,11 @@ export class App { public readonly relayPool: ConnectionPool, ) { this.eventSyncer = new EventSyncer(this.relayPool, this.database); + this.allUsersInfo = new AllUsersInformation(myAccountContext); } initApp = async (accountContext: NostrAccountContext) => { - const events = this.database.filterEvents((e) => e.kind == NostrKind.TEXT_NOTE); + // const events = this.database.filterEvents((e) => e.kind == NostrKind.TEXT_NOTE); console.log("App.initApp"); const profilesSyncer = await initProfileSyncer(this.relayPool, accountContext, this.database); @@ -179,15 +180,16 @@ export class App { this.profileSyncer = profilesSyncer; - this.model.allUsersInfo = getAllUsersInformation(this.database, this.myAccountContext); + this.allUsersInfo.addEvents(this.database.events); console.log("App allUsersInfo"); - this.model.social.threads = getSocialPosts(this.database, this.model.allUsersInfo); + this.model.social.threads = getSocialPosts(this.database, this.allUsersInfo.userInfos); /* my profile */ - this.model.myProfile = this.model.allUsersInfo.get(accountContext.publicKey.hex)?.profile?.profile; + this.model.myProfile = this.allUsersInfo.userInfos.get(accountContext.publicKey.hex)?.profile + ?.profile; /* contacts */ - for (const contact of this.model.allUsersInfo.values()) { + for (const contact of this.allUsersInfo.userInfos.values()) { const editor = this.model.editors.get(contact.pubkey.hex); if (editor == null) { const pubkey = PublicKey.FromHex(contact.pubkey.hex); @@ -206,7 +208,7 @@ export class App { } await profilesSyncer.add( - ...Array.from(this.model.allUsersInfo.keys()), + ...Array.from(this.allUsersInfo.userInfos.keys()), ); console.log("user set", profilesSyncer.userSet); @@ -221,6 +223,7 @@ export class App { this.profileSyncer, this.lamport, this.eventBus, + this.allUsersInfo, ) ) { render(, document.body); @@ -261,7 +264,11 @@ export function AppComponent(props: { if (model.navigationModel.activeNav == "Social") { let focusedContentGetter = () => { // console.log("AppComponent:getFocusedContent before", Date.now() - t); - let _ = getFocusedContent(model.social.focusedContent, model.allUsersInfo, model.social.threads); + let _ = getFocusedContent( + model.social.focusedContent, + app.allUsersInfo.userInfos, + model.social.threads, + ); // console.log("AppComponent:getFocusedContent", Date.now() - t); if (_?.type === "MessageThread") { let editor = model.social.replyEditors.get(_.data.root.event.id); @@ -354,7 +361,7 @@ export function AppComponent(props: { myAccountContext: myAccountCtx, db: app.database, pool: app.relayPool, - allUserInfo: model.allUsersInfo, + allUserInfo: app.allUsersInfo.userInfos, profilesSyncer: app.profileSyncer, eventSyncer: app.eventSyncer, })} diff --git a/UI/app_model.ts b/UI/app_model.ts index 6a10b62..5fbaaf8 100644 --- a/UI/app_model.ts +++ b/UI/app_model.ts @@ -24,7 +24,7 @@ export type Model = { key: string; value: string; }; - allUsersInfo: Map; + // allUsersInfo: Map; // social social: { @@ -57,7 +57,7 @@ export function initialModel(): Model { hasNewMessages: new Set(), currentSelectedContact: undefined, }, - allUsersInfo: new Map(), + // allUsersInfo: new Map(), editors: editors, newProfileField: { key: "", diff --git a/UI/app_update.ts b/UI/app_update.ts index 322701f..017e160 100644 --- a/UI/app_update.ts +++ b/UI/app_update.ts @@ -1,7 +1,7 @@ import { getProfileEvent, getProfilesByName, saveProfile } from "../features/profile.ts"; import { App } from "./app.tsx"; -import { getAllUsersInformation, getGroupOf, ProfilesSyncer, UserInfo } from "./contact-list.ts"; +import { AllUsersInformation, getGroupOf, ProfilesSyncer, UserInfo } from "./contact-list.ts"; import * as csp from "https://raw.githubusercontent.com/BlowaterNostr/csp/master/csp.ts"; import { Database_Contextual_View } from "../database.ts"; @@ -27,7 +27,15 @@ import { } 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 { SignInEvent, signInWithExtension, signInWithPrivateKey } from "./signIn.tsx"; -import { computeThreads, getTags, PinContact, UnpinContact } from "../nostr.ts"; +import { + computeThreads, + Decrypted_Nostr_Event, + getTags, + PinContact, + PlainText_Nostr_Event, + Profile_Nostr_Event, + UnpinContact, +} from "../nostr.ts"; import { MessageThread } from "./dm.tsx"; import { DexieDatabase } from "./dexie-db.ts"; import { getSocialPosts } from "../features/social.ts"; @@ -192,7 +200,7 @@ export async function* UI_Interaction_Update( }; const group = getGroupOf( event.pubkey, - model.allUsersInfo, + model.app.allUsersInfo.userInfos, ); model.dm.selectedContactGroup = group; updateConversation(model.app.model, event.pubkey); @@ -440,12 +448,13 @@ export async function* Database_Update( profileSyncer: ProfilesSyncer, lamport: LamportTime, eventEmitter: EventEmitter, + allUserInfo: AllUsersInformation, ) { const changes = database.onChange((_) => true); while (true) { await csp.sleep(333); await changes.ready(); - const changes_events: NostrEvent[] = []; + const changes_events: (PlainText_Nostr_Event | Decrypted_Nostr_Event | Profile_Nostr_Event)[] = []; while (true) { if (!changes.isReadyToPop()) { break; @@ -460,7 +469,7 @@ export async function* Database_Update( let hasKind_1 = false; for (let e of changes_events) { - model.allUsersInfo = getAllUsersInformation(database, ctx); + allUserInfo.addEvents([e]); const t = getTags(e).lamport_timestamp; if (t) { lamport.set(t); @@ -470,7 +479,7 @@ export async function* Database_Update( await profileSyncer.add(key.hex); } if (e.kind == NostrKind.META_DATA || e.kind == NostrKind.DIRECT_MESSAGE) { - for (const contact of model.allUsersInfo.values()) { + for (const contact of allUserInfo.userInfos.values()) { const editor = model.editors.get(contact.pubkey.hex); if (editor == null) { // a stranger sends a message const pubkey = PublicKey.FromHex(contact.pubkey.hex); @@ -547,7 +556,7 @@ export async function* Database_Update( } } if (hasKind_1) { - model.social.threads = getSocialPosts(database, model.allUsersInfo); + model.social.threads = getSocialPosts(database, allUserInfo.userInfos); } yield model; } diff --git a/UI/contact-list.ts b/UI/contact-list.ts index 5ebffe8..a54e241 100644 --- a/UI/contact-list.ts +++ b/UI/contact-list.ts @@ -1,5 +1,5 @@ import { Database_Contextual_View } from "../database.ts"; -import { ProfileFromNostrEvent, profilesStream } from "../features/profile.ts"; +import { profilesStream } from "../features/profile.ts"; import { Channel } from "https://raw.githubusercontent.com/BlowaterNostr/csp/master/csp.ts"; import { ContactGroup } from "./contact-list.tsx"; @@ -13,8 +13,13 @@ import { ConnectionPool, newSubID, } from "https://raw.githubusercontent.com/BlowaterNostr/nostr.ts/main/relay.ts"; -import { CustomAppData, getTags, Profile_Nostr_Event } from "../nostr.ts"; -import { assertEquals } from "https://deno.land/std@0.176.0/testing/asserts.ts"; +import { + CustomAppData, + Decrypted_Nostr_Event, + getTags, + PlainText_Nostr_Event, + Profile_Nostr_Event, +} from "../nostr.ts"; export interface UserInfo { pubkey: PublicKey; @@ -97,18 +102,18 @@ function socialPostsStream(pubkeys: Iterable, pool: ConnectionPool) { return chan; } -export function getAllUsersInformation( - database: Database_Contextual_View, - ctx: NostrAccountContext, -): Map { - const t = Date.now(); - const res = new Map(); - { - for (const event of database.filterEvents((_) => true)) { +export class AllUsersInformation { + readonly userInfos = new Map(); + + constructor(public readonly ctx: NostrAccountContext) {} + + addEvents(events: (Profile_Nostr_Event | PlainText_Nostr_Event | Decrypted_Nostr_Event)[]) { + // const t = Date.now(); + for (const event of events) { switch (event.kind) { case NostrKind.META_DATA: { - const userInfo = res.get(event.pubkey); + const userInfo = this.userInfos.get(event.pubkey); const profileEvent = event; if (userInfo) { if (userInfo.profile) { @@ -126,7 +131,7 @@ export function getAllUsersInformation( newestEventSendByMe: undefined, profile: profileEvent, }; - res.set(event.pubkey, newUserInfo); + this.userInfos.set(event.pubkey, newUserInfo); } } break; @@ -139,19 +144,19 @@ export function getAllUsersInformation( case NostrKind.DIRECT_MESSAGE: { let whoAm_I_TalkingTo = ""; - if (event.pubkey == ctx.publicKey.hex) { + if (event.pubkey == this.ctx.publicKey.hex) { // I am the sender whoAm_I_TalkingTo = getTags(event).p[0]; - } else if (getTags(event).p[0] == ctx.publicKey.hex) { + } else if (getTags(event).p[0] == this.ctx.publicKey.hex) { // I am the receiver whoAm_I_TalkingTo = event.pubkey; } else { // I am neither. Possible because other user has used this device before break; } - const userInfo = res.get(whoAm_I_TalkingTo); + const userInfo = this.userInfos.get(whoAm_I_TalkingTo); if (userInfo) { - if (whoAm_I_TalkingTo == ctx.publicKey.hex) { + if (whoAm_I_TalkingTo == this.ctx.publicKey.hex) { // talking to myself if (userInfo.newestEventSendByMe) { if (event.created_at > userInfo.newestEventSendByMe?.created_at) { @@ -163,7 +168,7 @@ export function getAllUsersInformation( userInfo.newestEventReceivedByMe = event; } } else { - if (ctx.publicKey.hex == event.pubkey) { + if (this.ctx.publicKey.hex == event.pubkey) { // I am the sender if (userInfo.newestEventSendByMe) { if (event.created_at > userInfo.newestEventSendByMe.created_at) { @@ -191,12 +196,12 @@ export function getAllUsersInformation( newestEventSendByMe: undefined, profile: undefined, }; - if (whoAm_I_TalkingTo == ctx.publicKey.hex) { + if (whoAm_I_TalkingTo == this.ctx.publicKey.hex) { // talking to myself newUserInfo.newestEventSendByMe = event; newUserInfo.newestEventReceivedByMe = event; } else { - if (ctx.publicKey.hex == event.pubkey) { + if (this.ctx.publicKey.hex == event.pubkey) { // I am the sender newUserInfo.newestEventSendByMe = event; } else { @@ -204,7 +209,7 @@ export function getAllUsersInformation( newUserInfo.newestEventReceivedByMe = event; } } - res.set(whoAm_I_TalkingTo, newUserInfo); + this.userInfos.set(whoAm_I_TalkingTo, newUserInfo); } } break; @@ -216,7 +221,7 @@ export function getAllUsersInformation( } const obj: CustomAppData = JSON.parse(event.decryptedContent); if (obj.type == "PinContact" || obj.type == "UnpinContact") { - const userInfo = res.get(obj.pubkey); + const userInfo = this.userInfos.get(obj.pubkey); if (userInfo) { if (userInfo.pinEvent) { if (event.created_at > userInfo.pinEvent.created_at) { @@ -232,7 +237,7 @@ export function getAllUsersInformation( }; } } else { - res.set(obj.pubkey, { + this.userInfos.set(obj.pubkey, { pubkey: PublicKey.FromHex(obj.pubkey) as PublicKey, // todo: could throw pinEvent: { content: obj, @@ -247,17 +252,8 @@ export function getAllUsersInformation( } } } + // console.log("AllUsersInformation:addEvents", Date.now() - t); } - // todo: should write a unit test for it instead of runtime assertion - for (const [pubkey, userInfo] of res) { - assertEquals(pubkey, userInfo.pubkey.hex); - if (userInfo.profile) { - assertEquals(pubkey, userInfo.profile.pubkey); - } - } - - console.log("getAllUsersInformation", Date.now() - t); - return res; } export const sortUserInfo = (a: UserInfo, b: UserInfo) => { diff --git a/database.ts b/database.ts index f55d45f..ba8bd81 100644 --- a/database.ts +++ b/database.ts @@ -332,9 +332,11 @@ export class Database_Contextual_View { ////////////////// // On DB Change // ////////////////// - onChange(filter: (e: NostrEvent) => boolean) { + onChange(filter: (e: PlainText_Nostr_Event | Decrypted_Nostr_Event | Profile_Nostr_Event) => boolean) { const c = this.caster.copy(); - const res = csp.chan(buffer_size); + const res = csp.chan( + buffer_size, + ); (async () => { for await (const newE of c) { if (filter(newE)) {