mirror of
https://github.com/BlowaterNostr/blowater.git
synced 2024-10-18 07:33:22 +00:00
maintain all user information in a stateful class
This commit is contained in:
parent
13eba08177
commit
934818e7a9
27
UI/app.tsx
27
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(<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,
|
||||
})}
|
||||
|
@ -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: "",
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) => {
|
||||
|
@ -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)) {
|
||||
|
Loading…
Reference in New Issue
Block a user