fix: multiple bug fixes
isolated users, db backup, weird disconnect issues for long voice sessions,
This commit is contained in:
@@ -9,9 +9,11 @@ import {
|
||||
BanEntry
|
||||
} from '../../shared-kernel';
|
||||
import type { ChatAttachmentMeta } from '../../shared-kernel';
|
||||
import { getStoredCurrentUserId } from '../../core/storage/current-user-storage';
|
||||
|
||||
/** IndexedDB database name for the MetoYou application. */
|
||||
const DATABASE_NAME = 'metoyou';
|
||||
const ANONYMOUS_DATABASE_SCOPE = 'anonymous';
|
||||
/** IndexedDB schema version - bump when adding/changing object stores. */
|
||||
const DATABASE_VERSION = 2;
|
||||
/** Names of every object store used by the application. */
|
||||
@@ -44,13 +46,18 @@ const ALL_STORE_NAMES: string[] = [
|
||||
export class BrowserDatabaseService {
|
||||
/** Handle to the opened IndexedDB database, or `null` before {@link initialize}. */
|
||||
private database: IDBDatabase | null = null;
|
||||
private activeDatabaseName: string | null = null;
|
||||
|
||||
/** Open (or create) the IndexedDB database. Safe to call multiple times. */
|
||||
async initialize(): Promise<void> {
|
||||
if (this.database)
|
||||
const databaseName = await this.resolveDatabaseName();
|
||||
|
||||
if (this.database && this.activeDatabaseName === databaseName)
|
||||
return;
|
||||
|
||||
this.database = await this.openDatabase();
|
||||
this.closeDatabase();
|
||||
this.database = await this.openDatabase(databaseName);
|
||||
this.activeDatabaseName = databaseName;
|
||||
}
|
||||
|
||||
/** Persist a single message. */
|
||||
@@ -180,6 +187,15 @@ export class BrowserDatabaseService {
|
||||
return this.getUser(meta.value);
|
||||
}
|
||||
|
||||
/** Retrieve the persisted current user ID without loading the full user. */
|
||||
async getCurrentUserId(): Promise<string | null> {
|
||||
const meta = await this.get<{ id: string; value: string }>(
|
||||
STORE_META, 'currentUserId'
|
||||
);
|
||||
|
||||
return meta?.value?.trim() || null;
|
||||
}
|
||||
|
||||
/** Store which user ID is considered "current" (logged-in). */
|
||||
async setCurrentUserId(userId: string): Promise<void> {
|
||||
await this.put(STORE_META, { id: 'currentUserId',
|
||||
@@ -313,9 +329,66 @@ export class BrowserDatabaseService {
|
||||
await this.awaitTransaction(transaction);
|
||||
}
|
||||
|
||||
private openDatabase(): Promise<IDBDatabase> {
|
||||
private async resolveDatabaseName(): Promise<string> {
|
||||
const currentUserId = getStoredCurrentUserId();
|
||||
const scopedDatabaseName = this.createScopedDatabaseName(currentUserId);
|
||||
|
||||
if (!currentUserId) {
|
||||
return scopedDatabaseName;
|
||||
}
|
||||
|
||||
if (await this.databaseExists(scopedDatabaseName)) {
|
||||
return scopedDatabaseName;
|
||||
}
|
||||
|
||||
const legacyCurrentUserId = await this.readCurrentUserIdFromDatabase(DATABASE_NAME);
|
||||
|
||||
return legacyCurrentUserId === currentUserId
|
||||
? DATABASE_NAME
|
||||
: scopedDatabaseName;
|
||||
}
|
||||
|
||||
private createScopedDatabaseName(userId: string | null): string {
|
||||
return `${DATABASE_NAME}::${encodeURIComponent(userId || ANONYMOUS_DATABASE_SCOPE)}`;
|
||||
}
|
||||
|
||||
private async databaseExists(name: string): Promise<boolean> {
|
||||
const hasDatabasesApi = typeof indexedDB.databases === 'function';
|
||||
|
||||
if (!hasDatabasesApi) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const databases = await indexedDB.databases();
|
||||
|
||||
return databases.some((database) => database.name === name);
|
||||
}
|
||||
|
||||
private async readCurrentUserIdFromDatabase(databaseName: string): Promise<string | null> {
|
||||
if (!await this.databaseExists(databaseName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const database = await this.openDatabase(databaseName);
|
||||
|
||||
try {
|
||||
const transaction = database.transaction(STORE_META, 'readonly');
|
||||
const request = transaction.objectStore(STORE_META).get('currentUserId');
|
||||
|
||||
return await new Promise<string | null>((resolve, reject) => {
|
||||
request.onsuccess = () => resolve((request.result as { value?: string } | undefined)?.value?.trim() || null);
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
} catch {
|
||||
return null;
|
||||
} finally {
|
||||
database.close();
|
||||
}
|
||||
}
|
||||
|
||||
private openDatabase(databaseName: string): Promise<IDBDatabase> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = indexedDB.open(DATABASE_NAME, DATABASE_VERSION);
|
||||
const request = indexedDB.open(databaseName, DATABASE_VERSION);
|
||||
|
||||
request.onerror = () => reject(request.error);
|
||||
request.onupgradeneeded = () => this.setupSchema(request.result);
|
||||
@@ -323,6 +396,12 @@ export class BrowserDatabaseService {
|
||||
});
|
||||
}
|
||||
|
||||
private closeDatabase(): void {
|
||||
this.database?.close();
|
||||
this.database = null;
|
||||
this.activeDatabaseName = null;
|
||||
}
|
||||
|
||||
private setupSchema(database: IDBDatabase): void {
|
||||
const messagesStore = this.ensureStore(database, STORE_MESSAGES, { keyPath: 'id' });
|
||||
|
||||
|
||||
Reference in New Issue
Block a user