From 4129cac5115d48c40d144d0f23e1d4fc3a0c30fb Mon Sep 17 00:00:00 2001 From: Shibo Lyu Date: Mon, 17 Mar 2025 01:54:56 +0800 Subject: [PATCH] refactor: continue to migrate components --- package.json | 2 +- pnpm-lock.yaml | 10 +- src/lib/accounts/accountStore.ts | 16 +-- src/lib/accounts/identityFileDB.ts | 81 ++++++++++++++ src/lib/blah/connection/chatServer.ts | 4 +- src/lib/blah/structures/keybox.ts | 2 +- src/lib/blah/structures/message.ts | 2 +- src/lib/blah/structures/roomInfo.ts | 2 +- .../GroupedList/GroupedListItem.svelte | 19 +--- src/lib/components/RichTextRenderer.svelte | 3 +- .../RichTextRenderer/RichTextSpan.svelte | 7 +- src/lib/identityFiles/identityFileDB.ts | 81 -------------- src/lib/mock/messages.ts | 2 +- src/lib/richText.ts | 103 ------------------ src/lib/types/message.ts | 2 +- src/routes/(app)/ChatListHeader.svelte | 7 +- src/routes/(app)/ChatListItem.svelte | 33 +++--- src/routes/(app)/CurrentAccountPicture.svelte | 4 +- .../(app)/SearchChatResultSection.svelte | 4 +- src/routes/(app)/SearchPanel.svelte | 3 +- .../chats/[server]/[chatId]/ChatInput.svelte | 2 +- .../chats/[server]/[chatId]/ChatPage.svelte | 2 +- .../settings/SettingsAccountSections.svelte | 12 +- src/routes/(internal)/_rich-text/+page.svelte | 6 +- 24 files changed, 147 insertions(+), 262 deletions(-) create mode 100644 src/lib/accounts/identityFileDB.ts delete mode 100644 src/lib/identityFiles/identityFileDB.ts delete mode 100644 src/lib/richText.ts diff --git a/package.json b/package.json index aa176fa..e620937 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "vitest": "^3.0.8" }, "dependencies": { - "@blah-im/core": "^0.4.2", + "@blah-im/core": "^0.4.3", "@melt-ui/svelte": "^0.86.4", "@zeabur/svelte-adapter": "^1.0.0", "bits-ui": "^1.3.12", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0cf7d63..bd451f7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,8 +9,8 @@ importers: .: dependencies: '@blah-im/core': - specifier: ^0.4.2 - version: 0.4.2 + specifier: ^0.4.3 + version: 0.4.3 '@melt-ui/svelte': specifier: ^0.86.4 version: 0.86.4(svelte@5.23.0) @@ -122,8 +122,8 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@blah-im/core@0.4.2': - resolution: {integrity: sha512-/3OsqGzDjAS5qTcizBGCR6QZmlCYSj7BYdS+ySK0GltRr3wOftmeoo7W6dWataZPFhIIcIvyZAQ3pMbL2707FQ==} + '@blah-im/core@0.4.3': + resolution: {integrity: sha512-qrVybHBFctonxAo/TYmyQ6X+7JPzu8jDMCxcfQvlI+SnZBWcYjqEVb3+A9Oqws0nivNlRvue7D0QdXxaEO8AyA==} '@esbuild/aix-ppc64@0.19.12': resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==} @@ -2075,7 +2075,7 @@ snapshots: '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.25 - '@blah-im/core@0.4.2': + '@blah-im/core@0.4.3': dependencies: zod: 3.24.2 diff --git a/src/lib/accounts/accountStore.ts b/src/lib/accounts/accountStore.ts index c387b27..1ec27ce 100644 --- a/src/lib/accounts/accountStore.ts +++ b/src/lib/accounts/accountStore.ts @@ -5,7 +5,7 @@ import { type BlahIdentityDescription, type BlahProfile } from '@blah-im/core/identity'; -import { type IdentityFileDB, openIdentityFileDB } from '$lib/identityFiles/identityFileDB'; +import { type IdentityDB, openIdentityDB } from './identityFileDB'; import { BlahKeyPair } from '@blah-im/core/crypto'; import { persisted } from 'svelte-persisted-store'; @@ -16,18 +16,18 @@ export type Account = BlahIdentityDescription & { class AccountStore implements Readable { private keyDB: AccountKeyDB; - private identityFileDB: IdentityFileDB; + private identityDB: IdentityDB; private internalStore = writable([]); subscribe = this.internalStore.subscribe; - private constructor(keyDB: AccountKeyDB, identityFileDB: IdentityFileDB) { + private constructor(keyDB: AccountKeyDB, identityDB: IdentityDB) { this.keyDB = keyDB; - this.identityFileDB = identityFileDB; + this.identityDB = identityDB; } static async open(): Promise { const keyDB = await openAccountKeyDB(); - const identityFileDB = await openIdentityFileDB(); + const identityFileDB = await openIdentityDB(); const store = new AccountStore(keyDB, identityFileDB); await store.loadAccounts(); return store; @@ -35,7 +35,7 @@ class AccountStore implements Readable { async loadAccounts() { const accountCreds = await this.keyDB.fetchAllAccounts(); - const identityFileMap = await this.identityFileDB.fetchIdentityFiles( + const identityFileMap = await this.identityDB.fetchIdentities( accountCreds.map((x) => x.idKeyId) ); @@ -61,7 +61,7 @@ class AccountStore implements Readable { const idKeyId = typeof accountOrIdKeyId === 'string' ? accountOrIdKeyId : accountOrIdKeyId.id_key; - const identityFile = await this.identityFileDB.fetchIdentityFile(idKeyId); + const identityFile = await this.identityDB.fetchIdentity(idKeyId); if (!identityFile) throw new Error('Identity file not found'); const accountCreds = await this.keyDB.fetchAccount(idKeyId); @@ -76,7 +76,7 @@ class AccountStore implements Readable { async saveIdentityDescription(identity: BlahIdentity) { const identityDesc = identity.generateIdentityDescription(); - await this.identityFileDB.updateIdentityFile(identityDesc); + await this.identityDB.updateIdentity(identityDesc); } async createAccount(profile: BlahProfile, password: string): Promise { diff --git a/src/lib/accounts/identityFileDB.ts b/src/lib/accounts/identityFileDB.ts new file mode 100644 index 0000000..09c3954 --- /dev/null +++ b/src/lib/accounts/identityFileDB.ts @@ -0,0 +1,81 @@ +import type { BlahIdentityDescription } from '@blah-im/core/identity'; +import { openDB, type DBSchema, type IDBPDatabase } from 'idb'; + +const IDB_NAME = 'weblah-identities'; +const IDB_OBJECT_STORE_NAME = 'identities'; + +const IDENTITY_FILE_MAX_AGE = 1000 * 60 * 60 * 24 * 30; // 30 days + +interface IdentityDBSchema extends DBSchema { + [IDB_OBJECT_STORE_NAME]: { + key: string; + value: BlahIdentityDescription & { lastUpdatedAt: Date }; + indexes: { id_urls: string }; + }; +} + +class IdentityDB { + private db: IDBPDatabase; + + private constructor(db: IDBPDatabase) { + this.db = db; + } + + static async open(): Promise { + const db = await openDB(IDB_NAME, 1, { + upgrade(db) { + if (!db.objectStoreNames.contains(IDB_OBJECT_STORE_NAME)) { + const store = db.createObjectStore(IDB_OBJECT_STORE_NAME, { keyPath: 'id_key' }); + store.createIndex('id_urls', 'profile.signee.payload.id_urls', { + multiEntry: true, + unique: true + }); + } + } + }); + + const store = new IdentityDB(db); + await store.removeExpiredIdentities(); + return store; + } + + async updateIdentity(identityDescription: BlahIdentityDescription): Promise { + await this.db.put(IDB_OBJECT_STORE_NAME, { ...identityDescription, lastUpdatedAt: new Date() }); + } + + async fetchIdentity(idKeyId: string): Promise { + return await this.db.get(IDB_OBJECT_STORE_NAME, idKeyId); + } + + async fetchIdentities(idKeyIds: string[]): Promise> { + return new Map( + ( + await Promise.all( + idKeyIds.map(async (idKeyId): Promise<[string, BlahIdentityDescription] | null> => { + const profile = await this.fetchIdentity(idKeyId); + return profile ? [idKeyId, profile] : null; + }) + ) + ).filter((x): x is [string, BlahIdentityDescription] => !!x) + ); + } + + async getIdentityByIdUrl(idUrl: string): Promise { + return await this.db.getFromIndex(IDB_OBJECT_STORE_NAME, 'id_urls', idUrl); + } + + async removeExpiredIdentities(): Promise { + const now = new Date(); + const cutoff = new Date(now.getTime() - IDENTITY_FILE_MAX_AGE); + await this.db.delete(IDB_OBJECT_STORE_NAME, IDBKeyRange.upperBound(cutoff)); + } +} + +let identityFileDB: IdentityDB | null = null; +export async function openIdentityDB(): Promise { + if (!identityFileDB) { + identityFileDB = await IdentityDB.open(); + } + return identityFileDB; +} +export type { IdentityDB }; diff --git a/src/lib/blah/connection/chatServer.ts b/src/lib/blah/connection/chatServer.ts index 3e329f8..fda47a3 100644 --- a/src/lib/blah/connection/chatServer.ts +++ b/src/lib/blah/connection/chatServer.ts @@ -1,6 +1,6 @@ import { version } from '$app/environment'; -import type { BlahRichText } from '$lib/richText'; -import type { BlahKeyPair, BlahSignedPayload } from '../crypto'; +import type { BlahRichText } from '@blah-im/core/richText'; +import type { BlahKeyPair, BlahSignedPayload } from '@blah-im/core/crypto'; import type { BlahAuth, BlahMessage, BlahRoomInfo, BlahUserJoinMessage } from '../structures'; import { BlahError } from './error'; diff --git a/src/lib/blah/structures/keybox.ts b/src/lib/blah/structures/keybox.ts index 9b8e127..051eaa8 100644 --- a/src/lib/blah/structures/keybox.ts +++ b/src/lib/blah/structures/keybox.ts @@ -1,4 +1,4 @@ -import type { BlahSignedPayload } from '../crypto'; +import type { BlahSignedPayload } from '@blah-im/core/crypto'; export type BlahActKeyEntry = { exp: number; diff --git a/src/lib/blah/structures/message.ts b/src/lib/blah/structures/message.ts index 0c879de..0e7109f 100644 --- a/src/lib/blah/structures/message.ts +++ b/src/lib/blah/structures/message.ts @@ -1,4 +1,4 @@ -import type { BlahRichText } from '$lib/richText'; +import type { BlahRichText } from '@blah-im/core/richText'; export type BlahMessage = { rich_text: BlahRichText; diff --git a/src/lib/blah/structures/roomInfo.ts b/src/lib/blah/structures/roomInfo.ts index 573ac1c..c792582 100644 --- a/src/lib/blah/structures/roomInfo.ts +++ b/src/lib/blah/structures/roomInfo.ts @@ -1,4 +1,4 @@ -import type { BlahSignedPayload } from '../crypto'; +import type { BlahSignedPayload } from '@blah-im/core/crypto'; import type { BlahMessage } from './message'; export type BlahRoomInfo = { diff --git a/src/lib/components/GroupedList/GroupedListItem.svelte b/src/lib/components/GroupedList/GroupedListItem.svelte index b36b53c..6827b82 100644 --- a/src/lib/components/GroupedList/GroupedListItem.svelte +++ b/src/lib/components/GroupedList/GroupedListItem.svelte @@ -1,7 +1,4 @@ {#if icon} {/if} diff --git a/src/lib/components/RichTextRenderer.svelte b/src/lib/components/RichTextRenderer.svelte index 9bd799e..63eb6e7 100644 --- a/src/lib/components/RichTextRenderer.svelte +++ b/src/lib/components/RichTextRenderer.svelte @@ -1,5 +1,5 @@
diff --git a/src/lib/components/RichTextRenderer/RichTextSpan.svelte b/src/lib/components/RichTextRenderer/RichTextSpan.svelte index 78b5ce7..ab12d11 100644 --- a/src/lib/components/RichTextRenderer/RichTextSpan.svelte +++ b/src/lib/components/RichTextRenderer/RichTextSpan.svelte @@ -1,12 +1,13 @@ -
+
{ isSearchFocused = true; }} diff --git a/src/routes/(app)/ChatListItem.svelte b/src/routes/(app)/ChatListItem.svelte index 7510c29..3118c0a 100644 --- a/src/routes/(app)/ChatListItem.svelte +++ b/src/routes/(app)/ChatListItem.svelte @@ -1,13 +1,11 @@
  • {chat.name} {#if chat.lastMessage} {/if}
  • -

    +

    {#if chat.lastMessage} {#if chat.id !== chat.lastMessage.sender.id} @@ -62,12 +61,12 @@ : chat.lastMessage.sender.name}: {/if} - {blahRichTextToPlainText(chat.lastMessage.content)} + {toPlainText(chat.lastMessage.content)} {/if}

    {#if chat.unreadCount} {formatUnreadCount(chat.unreadCount)} diff --git a/src/routes/(app)/CurrentAccountPicture.svelte b/src/routes/(app)/CurrentAccountPicture.svelte index 93ef0c9..c5d78aa 100644 --- a/src/routes/(app)/CurrentAccountPicture.svelte +++ b/src/routes/(app)/CurrentAccountPicture.svelte @@ -13,7 +13,7 @@ let { size = 32 }: Props = $props(); - let accountStore: AccountStore = $state(); + let accountStore: AccountStore | undefined = $state(); onMount(() => { openAccountStore().then((store) => { @@ -22,7 +22,7 @@ }); -{#if accountStore} +{#if accountStore && $accountStore} {@const currentAccount = $accountStore.find((account) => account.id_key === $currentAccountStore)} {:else} diff --git a/src/routes/(app)/SearchChatResultSection.svelte b/src/routes/(app)/SearchChatResultSection.svelte index d24be03..607ebaa 100644 --- a/src/routes/(app)/SearchChatResultSection.svelte +++ b/src/routes/(app)/SearchChatResultSection.svelte @@ -13,12 +13,12 @@
  • {name}

      - {#each results as chat} + {#each results as chat (chat.id)} {/each}
    diff --git a/src/routes/(app)/SearchPanel.svelte b/src/routes/(app)/SearchPanel.svelte index 5faf0d1..12b2085 100644 --- a/src/routes/(app)/SearchPanel.svelte +++ b/src/routes/(app)/SearchPanel.svelte @@ -1,7 +1,6 @@ -{#if accountStore} +{#if accountStore && $accountStore} {@const currentAccount = $accountStore.find((acc) => acc.id_key === $currentAccountStore)} {@const remainingAccounts = $accountStore .filter((acc) => acc.id_key !== $currentAccountStore) @@ -38,12 +38,12 @@
  • - + {currentAccount.profile.signee.payload.name}

    - + {currentAccount.profile.signee.id_key.slice(0, 6) + '...' + currentAccount.profile.signee.id_key.slice(-6)} @@ -57,7 +57,7 @@ {#each remainingAccounts as account (account.id_key)}

    - switchToAccount(account)}> + switchToAccount(account)}>
    {account.profile.signee.payload.name}
    diff --git a/src/routes/(internal)/_rich-text/+page.svelte b/src/routes/(internal)/_rich-text/+page.svelte index 041de85..beadc87 100644 --- a/src/routes/(internal)/_rich-text/+page.svelte +++ b/src/routes/(internal)/_rich-text/+page.svelte @@ -1,6 +1,6 @@