/* eslint-disable import/first */ import React from 'react'; import { createRoot } from 'react-dom/client'; import { enableMapSet } from 'immer'; import '@fontsource-variable/inter/index.css'; import 'folds/dist/style.css'; import { configClass, varsClass } from 'folds'; enableMapSet(); import './index.css'; import { trimTrailingSlash } from './app/utils/common'; import App from './app/pages/App'; // import i18n (needs to be bundled ;)) import './app/i18n'; import { pushSessionToSW } from './sw-session'; import { getFallbackSession } from './app/state/sessions'; document.body.classList.add(configClass, varsClass); // Register Service Worker if ('serviceWorker' in navigator) { const swUrl = import.meta.env.MODE === 'production' ? `${trimTrailingSlash(import.meta.env.BASE_URL)}/sw.js` : `/dev-sw.js?dev-sw`; const sendSessionToSW = () => { const session = getFallbackSession(); pushSessionToSW(session?.baseUrl, session?.accessToken); }; navigator.serviceWorker.register(swUrl).then(sendSessionToSW); navigator.serviceWorker.ready.then(sendSessionToSW); navigator.serviceWorker.addEventListener('message', (ev) => { const { type } = ev.data ?? {}; if (type === 'requestSession') { sendSessionToSW(); } }); } // Request persistent storage so the browser can't evict the IndexedDB // rust-crypto store under storage pressure. Eviction (while the localStorage // session/device-id survives) resurrects the device with a blank crypto store, // which then re-uploads OTKs the server already holds → the "one time key // already exists" upload storm and E2EE breakage. Only ask for sessions worth // protecting (skip anonymous/landing visitors to avoid a needless Firefox // prompt); check persisted() first so we don't re-prompt. Best-effort. if (navigator.storage?.persist && getFallbackSession()) { navigator.storage .persisted() .then((already) => (already ? undefined : navigator.storage.persist())) .catch(() => undefined); } // Reload once if a lazy-loaded chunk is missing (stale deployment) window.addEventListener('vite:preloadError', () => { if (!sessionStorage.getItem('chunk-reload-attempted')) { sessionStorage.setItem('chunk-reload-attempted', '1'); window.location.reload(); } }); // Clear the reload flag after a successful load so future deploys can still trigger a reload window.addEventListener('load', () => sessionStorage.removeItem('chunk-reload-attempted')); // Filter out known-benign, high-volume matrix-js-sdk console warnings that we // can't fix client-side and that would otherwise flood the console: // - "Adding default global …": the SDK patches MSC3786/MSC3914 push rules on // every login (one warn each) until Synapse ships them as server defaults. // - "EventTimelineSet…": the SDK loudly warns whenever a decrypted event // references a thread/room the current timeline set doesn't hold, then // discards it harmlessly. This fires constantly in E2EE rooms with threads. // - "Decrypted event … is not in room …": same family — a late decryption for // an event the room's timeline no longer tracks; ignored by the SDK. // These are informational SDK bookkeeping, not errors; real warnings still log. { const suppressedPrefixes = ['Adding default global ', 'EventTimelineSet']; const _warn = console.warn.bind(console); console.warn = (...args: unknown[]) => { const first = args[0]; if (typeof first === 'string') { if (suppressedPrefixes.some((p) => first.startsWith(p))) return; if (first.startsWith('Decrypted event ') && first.includes('is not in room')) return; } _warn(...args); }; } const mountApp = () => { const rootContainer = document.getElementById('root'); if (rootContainer === null) { console.error('Root container element not found!'); return; } const root = createRoot(rootContainer); root.render(); }; mountApp();