diff --git a/client/.env b/client/.env index ff153ae..711b86f 100644 --- a/client/.env +++ b/client/.env @@ -8,7 +8,7 @@ DATABASE_URL="file:./dev.db" SCHOOL=knust COOKIE_SECRET=secret1,secret2 SECRET_KEY=secret1 -RESEND_API_KEY= +RESEND_API_KEY=123 AWS_UPLOAD_ENDPOINT="eu-central-1.linodeobjects.com" AWS_REGION="eu-central-1" AWS_ACCESS_KEY_ID= diff --git a/client/app/entry.client.tsx b/client/app/entry.client.tsx index 98533cd..709c72c 100644 --- a/client/app/entry.client.tsx +++ b/client/app/entry.client.tsx @@ -8,12 +8,12 @@ import { RemixBrowser } from "@remix-run/react"; import { startTransition, StrictMode } from "react"; import { hydrateRoot } from "react-dom/client"; import posthog from "posthog-js"; -import AudioRecorder from 'audio-recorder-polyfill' -import mpegEncoder from 'audio-recorder-polyfill/mpeg-encoder' +import AudioRecorder from "audio-recorder-polyfill"; +import mpegEncoder from "audio-recorder-polyfill/mpeg-encoder"; -AudioRecorder.encoder = mpegEncoder -AudioRecorder.prototype.mimeType = 'audio/mpeg' -window.MediaRecorder = AudioRecorder +AudioRecorder.encoder = mpegEncoder; +AudioRecorder.prototype.mimeType = "audio/mpeg"; +window.MediaRecorder = AudioRecorder; if (process.env.NODE_ENV === "production") { posthog.init("phc_qmxF7NTz6XUnYUDoMpkTign6mujS8F8VqR75wb0Bsl7", { @@ -21,6 +21,15 @@ if (process.env.NODE_ENV === "production") { }); } +if ("serviceWorker" in navigator) { + try { + navigator.serviceWorker.register("/service-worker.js", { + scope: "/", + }); + } catch (error) { + console.error(`Registration failed with ${error}`); + } +} startTransition(() => { hydrateRoot( diff --git a/client/app/root.tsx b/client/app/root.tsx index 989939e..d858410 100644 --- a/client/app/root.tsx +++ b/client/app/root.tsx @@ -10,8 +10,11 @@ import { Outlet, Scripts, ScrollRestoration, + UIMatch, json, useLoaderData, + useLocation, + useMatches, } from "@remix-run/react"; import { BottomNav, Navbar } from "./components/navbar"; import { Footer } from "./components/footer"; @@ -21,6 +24,8 @@ import { prisma } from "./lib/prisma.server"; import { GlobalCtx } from "./lib/global-ctx"; import { User } from "@prisma/client"; import { CommonHead } from "./components/common-head"; +import { useRef } from "react"; +import React from "react"; export const loader = async ({ request }: LoaderFunctionArgs) => { let user: User | undefined | null; @@ -43,6 +48,89 @@ export { ErrorBoundary } from "./components/error-boundary"; export default function App() { const { user } = useLoaderData(); + const location = useLocation(); + const isMount = useRef(true); + const matches = useMatches(); + + function isPromise(p: any): boolean { + if (p && typeof p === "object" && typeof p.then === "function") { + return true; + } + return false; + } + + function isFunction(p: any): boolean { + if (typeof p === "function") { + return true; + } + return false; + } + + React.useEffect(() => { + const mounted = isMount; + isMount.current = false; + + if ("serviceWorker" in navigator) { + if (navigator.serviceWorker.controller) { + navigator.serviceWorker.controller?.postMessage({ + action: "clearCache", + }); + navigator.serviceWorker.controller?.postMessage({ + type: "REMIX_NAVIGATION", + isMount: mounted, + location, + manifest: window.__remixManifest, + matches: matches.filter(filteredMatches).map(sanitizeHandleObject), + }); + } else { + const listener = async () => { + await navigator.serviceWorker.ready; + navigator.serviceWorker.controller?.postMessage({ + action: "clearCache", + }); + navigator.serviceWorker.controller?.postMessage({ + type: "REMIX_NAVIGATION", + isMount: mounted, + location, + manifest: window.__remixManifest, + matches: matches.filter(filteredMatches).map(sanitizeHandleObject), + }); + }; + navigator.serviceWorker.addEventListener("controllerchange", listener); + return () => { + navigator.serviceWorker.removeEventListener( + "controllerchange", + listener, + ); + }; + } + } + + function filteredMatches(route: UIMatch) { + if (route.data) { + return ( + Object.values(route.data).filter((elem) => { + return isPromise(elem); + }).length === 0 + ); + } + return true; + } + + function sanitizeHandleObject(route: UIMatch) { + let handle = route.handle; + + if (handle) { + const filterInvalidTypes = ([, value]: any) => + !isPromise(value) && !isFunction(value); + handle = Object.fromEntries( + Object.entries(route.handle!).filter(filterInvalidTypes), + ); + } + return { ...route, handle }; + } + }, [location, matches]); + return ( @@ -60,7 +148,7 @@ export default function App() { - +