diff --git a/src/api/Backend.tsx b/src/api/Backend.tsx index bdedb4a3fb3c03e0b03003a4b44669ef388dd601..e0368d0eb4fb75c9cea13c634b6608278db905f8 100644 --- a/src/api/Backend.tsx +++ b/src/api/Backend.tsx @@ -67,6 +67,10 @@ export class BackendImpl { private csrfToken?: string; public isPrivileged = false; + protected fetchImpl(url: string, init?: RequestInit): Promise<Response> { + return fetch(url, init); + } + assetUrl(): string { return "https://video.fsmpi.rwth-aachen.de/files"; } @@ -135,7 +139,7 @@ export class BackendImpl { init.cache = "no-cache"; } init.credentials = "include"; - return fetch(url, init); + return this.fetchImpl(url, init); } setCsrfToken(csrfToken?: string) { @@ -505,3 +509,38 @@ export class BackendImpl { }); } } + +export class SmartCacheBackend extends BackendImpl { + protected fetchImpl(url: string, init?: RequestInit) { + let shouldCache = true; + if (!SmartCacheBackend.isSupported()) shouldCache = false; + if (init && init.cache === "no-cache") shouldCache = false; + if (!url.startsWith(this.baseUrl())) shouldCache = false; + if (!shouldCache) return super.fetchImpl(url, init); + + const cacheName = `api-v1-${this.isPrivileged ? "privileged" : "public"}`; + return caches.open(cacheName).then((cache) => { + return cache.match(url).then((response) => { + if (response) { + console.log(`Cache hit (${cacheName}) on ${url}`); + return response; + } else { + return fetch(url, init).then((response) => { + cache.put(url, response.clone()); + return response; + }); + } + }); + }); + } + + static isSupported() { + return window.isSecureContext && "caches" in window; + } +} + +export class InvalidBackend extends BackendImpl { + protected fetchImpl(url: string, init?: RequestInit): Promise<Response> { + return Promise.reject(new Error("Invalid backend")); + } +} diff --git a/src/components/BackendProvider.tsx b/src/components/BackendProvider.tsx index fb1c5699f438c894cab2cf0c2370dfc507d7acf3..b8dd54064dc8f3f4dd3201213b993cfd7809cf8a 100644 --- a/src/components/BackendProvider.tsx +++ b/src/components/BackendProvider.tsx @@ -1,12 +1,12 @@ -import { BackendImpl } from "@/api/Backend"; +import { Backend, InvalidBackend, SmartCacheBackend } from "@/api/Backend"; import { createContext, useContext, useState } from "react"; import type React from "react"; -const BackendContext = createContext<BackendImpl>(new BackendImpl()); +const BackendContext = createContext<Backend>(new InvalidBackend()); export function RealBackendProvider({ children }: { children: React.ReactNode }) { - let [state, _] = useState(() => new BackendImpl()); + let [state, _] = useState(() => new SmartCacheBackend()); return <BackendContext.Provider value={state}>{children}</BackendContext.Provider>; }