vojo/src/client/initMatrix.ts

143 lines
4.2 KiB
TypeScript

import { createClient, MatrixClient, IndexedDBStore, IndexedDBCryptoStore } from 'matrix-js-sdk';
import { cryptoCallbacks } from './secretStorageKeys';
import { clearNavToActivePathStore } from '../app/state/navToActivePath';
import { pushSessionToSW } from '../sw-session';
import { clearPusherIds, loadPusherIds, setPushEnabled, unregisterPusher } from '../app/utils/push';
import { isNativePlatform } from '../app/utils/capacitor';
import { clearSessionBridge } from '../app/utils/sessionBridge';
type Session = {
baseUrl: string;
accessToken: string;
userId: string;
deviceId: string;
};
export const initClient = async (session: Session): Promise<MatrixClient> => {
const indexedDBStore = new IndexedDBStore({
indexedDB: global.indexedDB,
localStorage: global.localStorage,
dbName: 'web-sync-store',
});
const legacyCryptoStore = new IndexedDBCryptoStore(global.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'],
});
await indexedDBStore.startup();
await mx.initRustCrypto();
mx.setMaxListeners(50);
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) => {
// 1. Deactivate pusher on the homeserver while we still have a valid token.
// Run before pushSessionToSW() so that if a push arrives mid-logout, the
// SW still has a working session to resolve event details with.
const pusherIds = loadPusherIds();
if (pusherIds) {
try {
await unregisterPusher(mx, pusherIds.pushkey, pusherIds.appId);
} catch {
// ignore — logout must proceed
}
}
pushSessionToSW();
// 2. Unsubscribe locally so the browser/FCM stops delivering to this device.
// Critical: if step 1 failed (bad network, server 5xx), this is the only
// thing that actually stops pushes from arriving at this installation.
try {
if (isNativePlatform()) {
const { PushNotifications } = await import('@capacitor/push-notifications');
await PushNotifications.unregister();
} else if ('serviceWorker' in navigator) {
const reg = await navigator.serviceWorker.ready;
const sub = await reg.pushManager.getSubscription();
if (sub) await sub.unsubscribe();
}
} catch {
// ignore
}
clearPusherIds();
setPushEnabled(false);
// Wipe the native session bridge so a re-login with a different user
// can't resurrect the old access_token via CallDeclineReceiver.
await clearSessionBridge();
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);
}
});
window.localStorage.clear();
window.location.reload();
};
// Boot-error logout: full LOCAL cleanup with no network calls — useful when the
// network is the reason we're stuck (sync error, init/start failure). Wipes
// the native session bridge so CallDeclineReceiver can't resurrect a dead
// token, clears push state so a re-login doesn't double-register, and nukes
// IndexedDB + localStorage. Server-side logout is skipped — the homeserver
// will time out the session naturally.
export const clearLocalSessionAndReload = async () => {
await clearSessionBridge();
clearPusherIds();
setPushEnabled(false);
const dbs = await window.indexedDB.databases();
dbs.forEach((idbInfo) => {
const { name } = idbInfo;
if (name) {
window.indexedDB.deleteDatabase(name);
}
});
window.localStorage.clear();
window.location.reload();
};