import { createClient, MatrixClient, IndexedDBStore, IndexedDBCryptoStore } from 'matrix-js-sdk'; import { cryptoCallbacks } from './secretStorageKeys'; import { clearNavToActivePathStore } from '../app/state/navToActivePath'; import { pushSessionToSW } from '../sw-session'; type Session = { baseUrl: string; accessToken: string; userId: string; deviceId: string; }; // Thrown when the local IndexedDB has a higher schema version than this SDK expects. // This happens after a downgrade (e.g. matrix-js-sdk was briefly upgraded and then reverted). export const IDB_VERSION_CONFLICT = 'IDB_VERSION_CONFLICT'; export const initClient = async (session: Session): Promise => { const indexedDBStore = new IndexedDBStore({ indexedDB: globalThis.indexedDB, localStorage: globalThis.localStorage, dbName: 'web-sync-store', }); const legacyCryptoStore = new IndexedDBCryptoStore(globalThis.indexedDB, 'crypto-store'); const mx = createClient({ baseUrl: session.baseUrl, accessToken: session.accessToken, userId: session.userId, store: indexedDBStore, cryptoStore: legacyCryptoStore, deviceId: session.deviceId, timelineSupport: true, cryptoCallbacks: cryptoCallbacks as any, verificationMethods: ['m.sas.v1'], }); try { await indexedDBStore.startup(); } catch (e) { // IDB VersionError = local DB was written by a newer SDK version (schema downgrade). if (e instanceof DOMException && e.name === 'VersionError') { throw new Error(IDB_VERSION_CONFLICT); } throw e; } await mx.initRustCrypto(); mx.setMaxListeners(50); // Each RoomNavItem subscribes to session_started/session_ended; one listener per visible room. mx.matrixRTC.setMaxListeners(100); return mx; }; export const startClient = async (mx: MatrixClient) => { await mx.startClient({ lazyLoadMembers: true, }); }; export const clearCacheAndReload = async (mx: MatrixClient) => { mx.stopClient(); clearNavToActivePathStore(mx.getSafeUserId()); await mx.store.deleteAllData(); window.location.reload(); }; export const logoutClient = async (mx: MatrixClient) => { pushSessionToSW(); mx.stopClient(); try { await mx.logout(); } catch { // ignore if failed to logout } await mx.clearStores(); window.localStorage.clear(); window.location.reload(); }; export const clearLoginData = async () => { const dbs = await window.indexedDB.databases(); dbs.forEach((idbInfo) => { const { name } = idbInfo; if (name) { window.indexedDB.deleteDatabase(name); } }); // Unregister service workers so stale caches don't interfere after a reset if ('serviceWorker' in navigator) { const regs = await navigator.serviceWorker.getRegistrations(); await Promise.all(regs.map((r) => r.unregister())); const cacheNames = await caches.keys(); await Promise.all(cacheNames.map((c) => caches.delete(c))); } window.localStorage.clear(); window.location.reload(); };