From 4fced3a56a546c8313e255189e92aa4b37465a62 Mon Sep 17 00:00:00 2001 From: BlowaterNostr <127284497+BlowaterNostr@users.noreply.github.com> Date: Fri, 14 Jul 2023 22:13:15 +0800 Subject: [PATCH] pre-parse profile data (#79) --- UI/contact-list.ts | 14 +---- database.ts | 132 ++++++++++++++++++++++++++++++-------------- features/profile.ts | 51 +++++++---------- nostr.ts | 2 +- 4 files changed, 115 insertions(+), 84 deletions(-) diff --git a/UI/contact-list.ts b/UI/contact-list.ts index cceac34..5ebffe8 100644 --- a/UI/contact-list.ts +++ b/UI/contact-list.ts @@ -109,16 +109,7 @@ export function getAllUsersInformation( case NostrKind.META_DATA: { const userInfo = res.get(event.pubkey); - const profileEvent = ProfileFromNostrEvent({ - content: event.content, - created_at: event.created_at, - id: event.id, - kind: event.kind, - parsedTags: event.parsedTags, - pubkey: event.pubkey, - sig: event.sig, - tags: event.tags, - }); + const profileEvent = event; if (userInfo) { if (userInfo.profile) { if (profileEvent.created_at > userInfo.profile?.created_at) { @@ -133,9 +124,8 @@ export function getAllUsersInformation( pubkey: PublicKey.FromHex(event.pubkey) as PublicKey, newestEventReceivedByMe: undefined, newestEventSendByMe: undefined, - profile: undefined, + profile: profileEvent, }; - newUserInfo.profile = profileEvent; res.set(event.pubkey, newUserInfo); } } diff --git a/database.ts b/database.ts index ffe7526..aa8389a 100644 --- a/database.ts +++ b/database.ts @@ -13,10 +13,12 @@ import { getTags, ParsedTag_Nostr_Event, PlainText_Nostr_Event, + Profile_Nostr_Event, Tag, } from "./nostr.ts"; import * as csp from "https://raw.githubusercontent.com/BlowaterNostr/csp/master/csp.ts"; import { DexieDatabase } from "./UI/dexie-db.ts"; +import { parseProfileData, ProfileFromNostrEvent } from "./features/profile.ts"; export const NotFound = Symbol("Not Found"); const buffer_size = 1000; @@ -29,30 +31,66 @@ export interface Indices { } export class Database_Contextual_View { - private readonly sourceOfChange = csp.chan(buffer_size); - private readonly caster = csp.multi(this.sourceOfChange); + private readonly sourceOfChange = csp.chan< + PlainText_Nostr_Event | Decrypted_Nostr_Event | Profile_Nostr_Event + >(buffer_size); + private readonly caster = csp.multi( + this.sourceOfChange, + ); static async New(database: DexieDatabase, ctx: NostrAccountContext) { const t = Date.now(); + const onload: (NostrEvent)[] = await database.events.filter( (e: NostrEvent) => { return e.kind != NostrKind.CustomAppData; }, ).toArray(); - const cache: (PlainText_Nostr_Event | Decrypted_Nostr_Event)[] = onload.map((event) => { - const e: PlainText_Nostr_Event = { - content: event.content, - created_at: event.created_at, - id: event.id, - // @ts-ignore - kind: event.kind, - pubkey: event.pubkey, - sig: event.sig, - tags: event.tags, - parsedTags: getTags(event), - }; - return e; - }); + const cache: (PlainText_Nostr_Event | Decrypted_Nostr_Event | Profile_Nostr_Event)[] = []; + for (const event of onload) { + switch (event.kind) { + case NostrKind.META_DATA: + { + const profileData = parseProfileData(event.content); + if (profileData instanceof Error) { + console.error(profileData); + console.log("Database:delete", event.id); + database.events.delete(event.id); + continue; + } + const e: Profile_Nostr_Event = { + ...event, + kind: event.kind, + parsedTags: getTags(event), + profile: profileData, + }; + cache.push(e); + } + break; + case NostrKind.TEXT_NOTE: + case NostrKind.RECOMMED_SERVER: + case NostrKind.CONTACTS: + case NostrKind.DIRECT_MESSAGE: + case NostrKind.DELETE: + { + const e: PlainText_Nostr_Event = { + content: event.content, + created_at: event.created_at, + id: event.id, + kind: event.kind, + pubkey: event.pubkey, + sig: event.sig, + tags: event.tags, + parsedTags: getTags(event), + }; + cache.push(e); + } + break; + case NostrKind.CustomAppData: + // ignore + break; + } + } const db = new Database_Contextual_View( database, cache, @@ -111,7 +149,7 @@ export class Database_Contextual_View { constructor( private readonly database: DexieDatabase, - public readonly events: (PlainText_Nostr_Event | Decrypted_Nostr_Event)[], + public readonly events: (PlainText_Nostr_Event | Decrypted_Nostr_Event | Profile_Nostr_Event)[], private readonly ctx: NostrAccountContext, ) {} @@ -123,15 +161,15 @@ export class Database_Contextual_View { return this.events.filter(filter); }; - async addEvent(event: NostrEvent) { + async addEvent(event: NostrEvent): Promise { const storedEvent = await this.getEvent({ id: event.id }); if (storedEvent) { // event exist - return; + return false; } console.log("Database.addEvent", event.id); - await this.database.events.put(event); + let e: PlainText_Nostr_Event | Decrypted_Nostr_Event | Profile_Nostr_Event; if (event.kind == NostrKind.CustomAppData) { - const e = await transformEvent({ + const _e = await transformEvent({ content: event.content, created_at: event.created_at, id: event.id, @@ -140,30 +178,44 @@ export class Database_Contextual_View { sig: event.sig, tags: event.tags, }, this.ctx); - if (e == undefined) { - return; + if (_e == undefined) { + return false; } - if (e instanceof Error) { + if (_e instanceof Error) { console.log("Database:delete", event.id); - this.database.events.delete(event.id); - return; + this.database.events.delete(event.id); // todo: remove + return false; } - this.events.push(e); - await this.sourceOfChange.put(e); + e = _e; } else { - const e: PlainText_Nostr_Event = { - content: event.content, - created_at: event.created_at, - id: event.id, - kind: event.kind, - pubkey: event.pubkey, - sig: event.sig, - tags: event.tags, - parsedTags: getTags(event), - }; - this.events.push(e); - await this.sourceOfChange.put(e); + if (event.kind == NostrKind.META_DATA) { + const profileData = parseProfileData(event.content); + if (profileData instanceof Error) { + return false; + } + e = { + ...event, + kind: event.kind, + profile: profileData, + parsedTags: getTags(event), + }; + } else { + e = { + content: event.content, + created_at: event.created_at, + id: event.id, + kind: event.kind, + pubkey: event.pubkey, + sig: event.sig, + tags: event.tags, + parsedTags: getTags(event), + }; + } } + await this.database.events.put(event); + this.events.push(e); + /* not await */ this.sourceOfChange.put(e); + return true; } syncEvents( diff --git a/features/profile.ts b/features/profile.ts index 1fcb8ed..3a63058 100644 --- a/features/profile.ts +++ b/features/profile.ts @@ -75,19 +75,10 @@ export function getProfileEvent( db: Database_Contextual_View, pubkey: PublicKey, ): Profile_Nostr_Event | undefined { - const events: ParsedTag_Nostr_Event[] = []; + const events: Profile_Nostr_Event[] = []; for (const e of db.events) { if (e.kind === NostrKind.META_DATA && e.pubkey === pubkey.hex) { - events.push({ - content: e.content, - kind: e.kind, - created_at: e.created_at, - id: e.id, - parsedTags: e.parsedTags, - pubkey: e.pubkey, - sig: e.sig, - tags: e.tags, - }); + events.push(e); } } if (events.length == 0) { @@ -95,23 +86,14 @@ export function getProfileEvent( } events.sort((e1, e2) => e2.created_at - e1.created_at); const newest = events[0]; - return ProfileFromNostrEvent(newest); + return newest; } export function getProfilesByName(db: Database_Contextual_View, name: string): Profile_Nostr_Event[] { - const events: ParsedTag_Nostr_Event[] = []; + const events: Profile_Nostr_Event[] = []; for (const e of db.events) { if (e.kind === NostrKind.META_DATA) { - events.push({ - content: e.content, - kind: e.kind, - created_at: e.created_at, - id: e.id, - parsedTags: e.parsedTags, - pubkey: e.pubkey, - sig: e.sig, - tags: e.tags, - }); + events.push(e); } } if (events.length == 0) { @@ -122,7 +104,7 @@ export function getProfilesByName(db: Database_Contextual_View, name: string): P const result = []; for (const events of profilesPerUser.values()) { events.sort((e1, e2) => e2.created_at - e1.created_at); - const p = ProfileFromNostrEvent(events[0]); + const p = events[0]; if (p.profile.name && p.profile.name?.toLocaleLowerCase().indexOf(name.toLowerCase()) != -1) { result.push(p); } @@ -154,14 +136,12 @@ export interface ProfileData { export function ProfileFromNostrEvent( event: ParsedTag_Nostr_Event, -): Profile_Nostr_Event { - let profileData: ProfileData = {}; - try { - profileData = JSON.parse(event.content); - } catch (e) { - console.error(event.id, event.content, "is not valid JSON"); +) { + const profileData = parseProfileData(event.content); + if (profileData instanceof Error) { + return profileData; } - return { + const e: Profile_Nostr_Event = { kind: event.kind, id: event.id, sig: event.sig, @@ -172,4 +152,13 @@ export function ProfileFromNostrEvent( parsedTags: event.parsedTags, profile: profileData, }; + return e; +} + +export function parseProfileData(content: string) { + try { + return JSON.parse(content) as ProfileData; + } catch (e) { + return e as Error; + } } diff --git a/nostr.ts b/nostr.ts index 7889306..599db03 100644 --- a/nostr.ts +++ b/nostr.ts @@ -284,7 +284,7 @@ export type Decrypted_Nostr_Event = ParsedTag_Nostr_Event; export type PlainText_Nostr_Event = ParsedTag_Nostr_Event< - Exclude // todo: exclude DM as well + Exclude // todo: exclude DM as well >; export type Profile_Nostr_Event = ParsedTag_Nostr_Event & { profile: ProfileData;