Files
edgeever/apps/web/src/main.tsx
T
tianma-if 0681e70c97 fix(mobile): fill the note column for images on narrow screens
A stored desktop width such as 35% stays in the note. Phone-width
layouts show the image at the text column width.
2026-09-26 16:30:04 +08:00

163 lines
5.1 KiB
TypeScript

import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import React, { useEffect } from "react";
import { createRoot } from "react-dom/client";
import { BrowserRouter, HashRouter } from "react-router";
import { registerSW } from "virtual:pwa-register";
import { App } from "./app/App";
import { bootstrapI18n } from "./i18n";
import { emitPwaUpdateNotice } from "./lib/pwa-update-notice";
import { withEnvironmentTitlePrefix } from "./lib/environment-title";
import { initializeTheme, ThemeProvider } from "./components/ThemeProvider";
import { installPhoneImageFillStyle } from "@edgeever/shared/image-display";
import { applyEditorBodyFontPreference } from "./lib/editor-body-font";
import { installEditorBodyFontFaces } from "./lib/editor-body-font-faces";
import { applyUiFontPreference } from "./lib/ui-font";
import { DesktopRendererErrorBoundary } from "./components/DesktopRendererErrorBoundary";
import { reportDesktopRendererReadyAfterPaint } from "./lib/desktop-renderer-ready";
import "./styles/globals.css";
import "./styles/editor-body-fonts.css";
installPhoneImageFillStyle();
const PWA_UPDATE_CHECK_INTERVAL_MS = 10 * 60 * 1_000;
const DEVELOPMENT_PWA_RELOAD_KEY = "edgeever.dev-pwa-reset";
const isDesktopRenderer = __EDGEEVER_DESKTOP_BUILD__ || window.edgeeverDesktop?.isAvailable === true;
const DesktopBootstrapReady = () => {
useEffect(() => reportDesktopRendererReadyAfterPaint(), []);
return null;
};
if (import.meta.env.DEV) {
if (__EDGEEVER_DEVELOPMENT_PROFILE__) {
document.documentElement.dataset.edgeeverEnvironment = __EDGEEVER_DEVELOPMENT_PROFILE__;
}
document.title = withEnvironmentTitlePrefix(document.title, {
development: true,
profile: __EDGEEVER_DEVELOPMENT_PROFILE__,
});
}
const clearDevelopmentPwaState = async () => {
if (!("serviceWorker" in navigator)) {
return false;
}
const hadController = Boolean(navigator.serviceWorker.controller);
try {
const registrations = await navigator.serviceWorker.getRegistrations();
await Promise.all(registrations.map((registration) => registration.unregister()));
if ("caches" in window) {
const cacheNames = await window.caches.keys();
await Promise.all(cacheNames.map((cacheName) => window.caches.delete(cacheName)));
}
} catch (error) {
console.warn("Failed to clear development service worker state", error);
}
// A still-controlling worker can keep serving a broken HMR shell after a
// runtime crash. Force one clean reload once the worker is unregistered.
if (hadController && window.sessionStorage.getItem(DEVELOPMENT_PWA_RELOAD_KEY) !== "1") {
window.sessionStorage.setItem(DEVELOPMENT_PWA_RELOAD_KEY, "1");
window.location.reload();
return true;
}
window.sessionStorage.removeItem(DEVELOPMENT_PWA_RELOAD_KEY);
return false;
};
const registerProductionServiceWorker = () => {
let updateServiceWorker: ReturnType<typeof registerSW>;
updateServiceWorker = registerSW({
immediate: true,
onNeedRefresh() {
emitPwaUpdateNotice({ kind: "checking" });
void updateServiceWorker(true);
},
onNeedReload() {
window.location.reload();
},
onRegisteredSW(_swScriptUrl, registration) {
if (!registration) {
return;
}
const checkForUpdate = () => {
if (document.visibilityState === "visible") {
void registration.update().catch(() => undefined);
}
};
const updateInterval = window.setInterval(checkForUpdate, PWA_UPDATE_CHECK_INTERVAL_MS);
window.addEventListener("beforeunload", () => window.clearInterval(updateInterval), { once: true });
document.addEventListener("visibilitychange", checkForUpdate);
},
onRegisterError(error) {
console.warn("PWA service worker registration failed", error);
},
});
};
const mountApp = () => {
const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
retry: 1,
staleTime: 15_000,
},
},
});
const root = document.getElementById("root");
if (!root) {
throw new Error("Root element not found");
}
initializeTheme();
installEditorBodyFontFaces();
applyEditorBodyFontPreference();
applyUiFontPreference();
const Router = isDesktopRenderer ? HashRouter : BrowserRouter;
createRoot(root, {
onUncaughtError(error, errorInfo) {
console.error("Uncaught React error", error, errorInfo.componentStack);
},
}).render(
<React.StrictMode>
<DesktopRendererErrorBoundary>
<DesktopBootstrapReady />
<QueryClientProvider client={queryClient}>
<ThemeProvider>
<Router>
<App />
</Router>
</ThemeProvider>
</QueryClientProvider>
</DesktopRendererErrorBoundary>
</React.StrictMode>
);
};
const bootstrap = async () => {
await bootstrapI18n();
if (import.meta.env.DEV) {
const reloading = await clearDevelopmentPwaState();
if (reloading) {
return;
}
} else if (!isDesktopRenderer) {
registerProductionServiceWorker();
}
mountApp();
};
void bootstrap();