mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-28 09:33:46 +00:00
Telegram CloudStorage token recovery (resilience against WebView localStorage wipes): - mirror the refresh token to per-user CloudStorage on login, remove it on logout - restoreRefreshTokenFromCloud() recovers the refresh token in initialize() when localStorage is empty, then the normal refresh flow re-establishes the session - move the initialize() bootstrap call from auth.ts module-load into main.tsx after the Telegram SDK init(), since launch params + CloudStorage are unavailable before init - best-effort: no-ops outside Telegram and on any error -> falls back to prior behavior Accessibility: - role=alertdialog + aria-modal + aria-labelledby + focus management (useFocusTrap) on Maintenance, Blacklisted, ChannelSubscription and AccountDeleted blocking screens - aria-label on ColorPicker Hue/Saturation/Lightness range inputs
133 lines
4.1 KiB
TypeScript
133 lines
4.1 KiB
TypeScript
import React from 'react';
|
|
import ReactDOM from 'react-dom/client';
|
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
import {
|
|
init,
|
|
restoreInitData,
|
|
retrieveRawInitData,
|
|
mountMiniApp,
|
|
miniAppReady,
|
|
mountViewport,
|
|
expandViewport,
|
|
mountSwipeBehavior,
|
|
disableVerticalSwipes,
|
|
mountClosingBehavior,
|
|
disableClosingConfirmation,
|
|
mountBackButton,
|
|
bindThemeParamsCssVars,
|
|
bindViewportCssVars,
|
|
requestFullscreen,
|
|
isFullscreen,
|
|
} from '@telegram-apps/sdk-react';
|
|
import { clearStaleSessionIfNeeded } from './utils/token';
|
|
import { useAuthStore } from './store/auth';
|
|
import { AppWithNavigator } from './AppWithNavigator';
|
|
import { ErrorBoundary } from './components/ErrorBoundary';
|
|
import { initLogoPreload } from './api/branding';
|
|
import { getCachedFullscreenEnabled, isTelegramMobile } from './hooks/useTelegramSDK';
|
|
import { applyTelegramLanguage } from './i18n';
|
|
import './styles/globals.css';
|
|
|
|
// Polyfill Object.hasOwn for older iOS/Android WebViews (Safari < 15.4, old Chrome).
|
|
// @telegram-apps/sdk v3 depends on valibot which uses Object.hasOwn internally.
|
|
// Without this, init() throws LaunchParamsRetrieveError on affected devices.
|
|
// See: https://github.com/Telegram-Mini-Apps/tma.js/issues/683
|
|
if (typeof (Object as { hasOwn?: unknown }).hasOwn !== 'function') {
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
(Object as any).hasOwn = (obj: object, prop: PropertyKey): boolean =>
|
|
Object.prototype.hasOwnProperty.call(obj, prop);
|
|
}
|
|
|
|
// Only initialize Telegram SDK when running inside Telegram
|
|
const isTelegramEnv =
|
|
!!(window as unknown as Record<string, unknown>).TelegramWebviewProxy ||
|
|
location.hash.includes('tgWebApp') ||
|
|
location.search.includes('tgWebApp');
|
|
|
|
const HMR_KEY = '__tg_sdk_initialized';
|
|
const alreadyInitialized = (window as unknown as Record<string, unknown>)[HMR_KEY] === true;
|
|
|
|
if (isTelegramEnv && !alreadyInitialized) {
|
|
(window as unknown as Record<string, unknown>)[HMR_KEY] = true;
|
|
|
|
try {
|
|
init();
|
|
restoreInitData();
|
|
|
|
clearStaleSessionIfNeeded(retrieveRawInitData() || null);
|
|
|
|
// Adopt the user's Telegram client language on first run (no explicit choice yet).
|
|
applyTelegramLanguage();
|
|
|
|
// Each mount in its own try/catch so one failure doesn't block others.
|
|
// mountMiniApp() internally mounts themeParams in SDK v3,
|
|
// so we don't call mountThemeParams() separately to avoid ConcurrentCallError.
|
|
try {
|
|
mountMiniApp();
|
|
} catch {}
|
|
try {
|
|
bindThemeParamsCssVars();
|
|
} catch {}
|
|
try {
|
|
mountSwipeBehavior();
|
|
disableVerticalSwipes();
|
|
} catch {}
|
|
try {
|
|
mountClosingBehavior();
|
|
disableClosingConfirmation();
|
|
} catch {}
|
|
try {
|
|
mountBackButton();
|
|
} catch {}
|
|
// Viewport must be mounted before requesting fullscreen
|
|
mountViewport()
|
|
.then(() => {
|
|
bindViewportCssVars();
|
|
expandViewport();
|
|
|
|
// Auto-enter fullscreen if enabled in settings (mobile only)
|
|
if (getCachedFullscreenEnabled() && isTelegramMobile()) {
|
|
if (!isFullscreen()) {
|
|
requestFullscreen();
|
|
}
|
|
}
|
|
})
|
|
.catch(() => {});
|
|
|
|
miniAppReady();
|
|
} catch {}
|
|
} else if (!isTelegramEnv) {
|
|
// Outside Telegram — still clear stale session tokens if any
|
|
clearStaleSessionIfNeeded(null);
|
|
}
|
|
|
|
// Bootstrap auth after the Telegram SDK is initialised so CloudStorage-backed
|
|
// refresh-token recovery can run inside initialize() (launch params + CloudStorage
|
|
// are only available post-init()).
|
|
void useAuthStore.getState().initialize();
|
|
|
|
if ('requestIdleCallback' in window) {
|
|
requestIdleCallback(() => initLogoPreload());
|
|
} else {
|
|
setTimeout(initLogoPreload, 100);
|
|
}
|
|
|
|
const queryClient = new QueryClient({
|
|
defaultOptions: {
|
|
queries: {
|
|
retry: 1,
|
|
refetchOnWindowFocus: false,
|
|
},
|
|
},
|
|
});
|
|
|
|
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
<React.StrictMode>
|
|
<ErrorBoundary level="app">
|
|
<QueryClientProvider client={queryClient}>
|
|
<AppWithNavigator />
|
|
</QueryClientProvider>
|
|
</ErrorBoundary>
|
|
</React.StrictMode>,
|
|
);
|