diff --git a/bun.lock b/bun.lock index a5f208e..f655f59 100644 --- a/bun.lock +++ b/bun.lock @@ -31,8 +31,8 @@ "facehash": "^0.0.7", "framer-motion": "^12.23.12", "pocketbase": "^0.26.2", - "react": "^19.2.7", - "react-dom": "^19.2.7", + "react": "^19.3.0", + "react-dom": "^19.3.0", "react-imask": "^7.6.1", "redaxios": "^0.5.1", "sonner": "^2.0.7", @@ -49,8 +49,8 @@ "@lingui/cli": "^6.5.0", "@lingui/vite-plugin": "^6.5.0", "@types/node": "^22.5.4", - "@types/react": "^19.2.17", - "@types/react-dom": "^19.2.3", + "@types/react": "^19.3.0", + "@types/react-dom": "^19.3.0", "@types/web-push": "^3.6.4", "@vitejs/plugin-react": "^5.0.0", "autoprefixer": "^10.4.20", @@ -655,9 +655,9 @@ "@types/node": ["@types/node@22.19.10", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-tF5VOugLS/EuDlTBijk0MqABfP8UxgYazTLo3uIn3b4yJgg26QRbVYJYsDtHrjdDUIRfP70+VfhTTc+CE1yskw=="], - "@types/react": ["@types/react@19.2.17", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw=="], + "@types/react": ["@types/react@19.3.0", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg=="], - "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], + "@types/react-dom": ["@types/react-dom@19.3.0", "", { "peerDependencies": { "@types/react": "^19.3.0" } }, "sha512-ZI7bU42mZXXKHn/qNLEw2IrbiINU7X5+vfgdixBHkCNpYWXjKgfQ/P+uyGb5CjOLB9UcnTeg3rylQtV2hym44Q=="], "@types/resolve": ["@types/resolve@1.20.2", "", {}, "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="], @@ -1329,9 +1329,9 @@ "raf-schd": ["raf-schd@4.0.3", "", {}, "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ=="], - "react": ["react@19.2.7", "", {}, "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ=="], + "react": ["react@19.3.0", "", {}, "sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog=="], - "react-dom": ["react-dom@19.2.7", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.7" } }, "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ=="], + "react-dom": ["react-dom@19.3.0", "", { "dependencies": { "scheduler": "^0.28.0" }, "peerDependencies": { "react": "^19.3.0" } }, "sha512-JDk8dgif51OjFoDE70+OT9ICyYr+69HlmihNwp1+Nsfbna3t5sIiCa9ZJktDmQ4/1b/rn26hIAR2uYXDMr5r0Q=="], "react-imask": ["react-imask@7.6.1", "", { "dependencies": { "imask": "^7.6.1", "prop-types": "^15.8.1" }, "peerDependencies": { "react": ">=0.14.0" } }, "sha512-vLNfzcCz62Yzx/GRGh5tiCph9Gbh2cZu+Tz8OiO5it2eNuuhpA0DWhhSlOtVtSJ80+Bx+vFK5De8eQ9AmbkXzA=="], @@ -1397,7 +1397,7 @@ "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], - "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], + "scheduler": ["scheduler@0.28.0", "", {}, "sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw=="], "scmp": ["scmp@2.1.0", "", {}, "sha512-o/mRQGk9Rcer/jEEw/yw4mwo3EU/NvYvp577/Btqrym9Qy5/MdWGBqipbALgd2lrdWTJ5/gqDusxfnQBxOxT2Q=="], diff --git a/package.json b/package.json index c7cb483..aa9e2f1 100644 --- a/package.json +++ b/package.json @@ -43,8 +43,8 @@ "facehash": "^0.0.7", "framer-motion": "^12.23.12", "pocketbase": "^0.26.2", - "react": "^19.2.7", - "react-dom": "^19.2.7", + "react": "^19.3.0", + "react-dom": "^19.3.0", "react-imask": "^7.6.1", "redaxios": "^0.5.1", "sonner": "^2.0.7", @@ -61,8 +61,8 @@ "@lingui/cli": "^6.5.0", "@lingui/vite-plugin": "^6.5.0", "@types/node": "^22.5.4", - "@types/react": "^19.2.17", - "@types/react-dom": "^19.2.3", + "@types/react": "^19.3.0", + "@types/react-dom": "^19.3.0", "@types/web-push": "^3.6.4", "@vitejs/plugin-react": "^5.0.0", "autoprefixer": "^10.4.20", diff --git a/scripts/generate-sw.mjs b/scripts/generate-sw.mjs index a0dad01..943a142 100644 --- a/scripts/generate-sw.mjs +++ b/scripts/generate-sw.mjs @@ -13,7 +13,6 @@ const { count, size, warnings } = await generateSW({ 'icon-192x192.png', 'icon-512x512.png', 'site.webmanifest', - 'styles.css', 'push-handlers.js', ], // Hand-written Web Push handlers (public/push-handlers.js) are pulled into diff --git a/src/app/router.tsx b/src/app/router.tsx index 655ea71..5eaced4 100644 --- a/src/app/router.tsx +++ b/src/app/router.tsx @@ -3,6 +3,7 @@ import { createRouter as createTanStackRouter } from "@tanstack/react-router"; import { setupRouterSsrQueryIntegration } from "@tanstack/react-router-ssr-query"; import { routeTree } from "./routeTree.gen"; import { DefaultCatchBoundary } from "../components/DefaultCatchBoundary"; +import RoutePending from "../components/route-pending"; import { defaultHeaderConfig } from "@/features/core/hooks/use-router-config"; export function getRouter() { @@ -30,6 +31,9 @@ export function getRouter() { }, defaultPreload: "intent", defaultPreloadStaleTime: 60_000, + defaultPendingMs: 100, + defaultPendingMinMs: 0, + defaultPendingComponent: RoutePending, defaultErrorComponent: DefaultCatchBoundary, }); diff --git a/src/app/routes/__root.tsx b/src/app/routes/__root.tsx index a81eb22..3ebd006 100644 --- a/src/app/routes/__root.tsx +++ b/src/app/routes/__root.tsx @@ -31,6 +31,7 @@ import { getRootMeta } from "@/lib/i18n/meta"; import FullScreenLoader from "@/components/full-screen-loader"; import { CHROME_COLORS } from "@/lib/mantine/theme-colors"; import mantineCssUrl from '@mantine/core/styles.css?url' +import appStylesUrl from '../styles.css?url' import mantineDatesCssUrl from '@mantine/dates/styles.css?url' import mantineCarouselCssUrl from '@mantine/carousel/styles.css?url' import mantineTiptapCssUrl from '@mantine/tiptap/styles.css?url' @@ -101,6 +102,7 @@ export const Route = createRootRouteWithContext<{ }, { rel: "manifest", href: "/site.webmanifest" }, { rel: "icon", href: "/favicon.ico" }, + { rel: 'stylesheet', href: appStylesUrl }, { rel: 'stylesheet', href: mantineCssUrl }, { rel: 'stylesheet', href: mantineCarouselCssUrl }, { rel: 'stylesheet', href: mantineDatesCssUrl }, @@ -183,7 +185,19 @@ function RootComponent() { ensureSuperTokensFrontend(); initTelemetry(router); if (import.meta.env.PROD && 'serviceWorker' in navigator) { - navigator.serviceWorker.register('/sw.js').catch(() => track('sw_register_failed')); + // first install claims the page too; only reload on real updates + const hadController = !!navigator.serviceWorker.controller; + let reloading = false; + navigator.serviceWorker.addEventListener('controllerchange', () => { + if (!hadController || reloading) return; + reloading = true; + window.location.reload(); + }); + navigator.serviceWorker.register('/sw.js').then((registration) => { + document.addEventListener('visibilitychange', () => { + if (document.visibilityState === 'visible') registration.update().catch(() => {}); + }); + }).catch(() => track('sw_register_failed')); } }, [router]); @@ -231,7 +245,6 @@ function RootDocument({ children }: { children: React.ReactNode }) { 'try{var d=document.documentElement,t=localStorage.getItem("flxn-theme");if(t&&t!=="default"){d.setAttribute("data-theme",t);}var s=localStorage.getItem("flxn-theme-scheme");if(s){d.setAttribute("data-mantine-color-scheme",s);}var f=JSON.parse(localStorage.getItem("flxn-theme-fonts")||"[]");for(var i=0;i -
{children}
+