maintain all user information in a stateful class

This commit is contained in:
BlowaterNostr 2023-07-15 16:40:21 +00:00
parent 13eba08177
commit 934818e7a9
5 changed files with 68 additions and 54 deletions

View File

@ -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(<AppComponent model={this.model} eventBus={this.eventBus} />, 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,
})}

View File

@ -24,7 +24,7 @@ export type Model = {
key: string;
value: string;
};
allUsersInfo: Map<string, UserInfo>;
// allUsersInfo: Map<string, UserInfo>;
// 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: "",

View File

@ -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<SelectProfile>,
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;
}

View File

@ -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<string>, pool: ConnectionPool) {
return chan;
}
export function getAllUsersInformation(
database: Database_Contextual_View,
ctx: NostrAccountContext,
): Map<string, UserInfo> {
const t = Date.now();
const res = new Map<string, UserInfo>();
{
for (const event of database.filterEvents((_) => true)) {
export class AllUsersInformation {
readonly userInfos = new Map<string, UserInfo>();
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) => {

View File

@ -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<NostrEvent>(buffer_size);
const res = csp.chan<PlainText_Nostr_Event | Decrypted_Nostr_Event | Profile_Nostr_Event>(
buffer_size,
);
(async () => {
for await (const newE of c) {
if (filter(newE)) {