diff --git a/frontend/index.html b/frontend/index.html index 91f2579..b924493 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -3,12 +3,180 @@ + Cluedo Sheet - - - + + + + + + + + + + + + +
diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 84b88dd..8d4e48e 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -342,6 +342,14 @@ export default function App() { setThemeKey(key); applyTheme(key); + // ✅ sofort für nächsten Start merken (verhindert Flash) + try { + localStorage.setItem(`hpTheme:${(me?.email || "guest").toLowerCase()}`, key); + localStorage.setItem("hpTheme:guest", key); // fallback, falls noch nicht eingeloggt + } catch { + // ignore + } + try { await api("/auth/theme", { method: "PATCH", @@ -351,6 +359,7 @@ export default function App() { // theme locally already applied; ignore backend error } }; + // ===== Stats (always fresh on open) ===== const openStatsModal = async () => { diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx index 19def02..c0a0bc2 100644 --- a/frontend/src/main.jsx +++ b/frontend/src/main.jsx @@ -1,14 +1,51 @@ import React from "react"; -import { createRoot } from "react-dom/client"; +import ReactDOM from "react-dom/client"; import App from "./App.jsx"; +import { applyTheme, DEFAULT_THEME_KEY } from "./styles/themes"; import { registerSW } from "virtual:pwa-register"; -createRoot(document.getElementById("root")).render(); -registerSW({ immediate: true }); -const updateSW = registerSW({ +async function bootstrap() { + // Theme sofort setzen + try { + const key = localStorage.getItem("hpTheme:guest") || DEFAULT_THEME_KEY; + applyTheme(key); + } catch { + applyTheme(DEFAULT_THEME_KEY); + } + + // Fonts abwarten (verhindert Layout-Sprung) + try { + if (document.fonts?.ready) { + await document.fonts.ready; + } + } catch {} + + // App rendern + ReactDOM.createRoot(document.getElementById("root")).render(); + + // Splash mind. 3 Sekunden anzeigen (3000ms) + const MIN_SPLASH_MS = 3000; + const tStart = performance.now(); + + const hideSplash = () => { + const splash = document.getElementById("app-splash"); + if (!splash) return; + splash.classList.add("hide"); + setTimeout(() => splash.remove(), 320); + }; + + const elapsed = performance.now() - tStart; + const remaining = Math.max(0, MIN_SPLASH_MS - elapsed); + setTimeout(hideSplash, remaining); + + // Service Worker ohne Auto-Reload-Flash + registerSW({ immediate: true, onNeedRefresh() { - updateSW(true); // sofort neue Version aktivieren - window.location.reload(); + console.info("Neue Version verfügbar"); + // optional: später Toast "Update verfügbar" }, }); +} + +bootstrap(); diff --git a/frontend/src/styles/themes.js b/frontend/src/styles/themes.js index ff01d9c..cb182c6 100644 --- a/frontend/src/styles/themes.js +++ b/frontend/src/styles/themes.js @@ -195,6 +195,32 @@ export const THEMES = { export const DEFAULT_THEME_KEY = "default"; +export function setThemeColorMeta(color) { + try { + const safe = typeof color === "string" ? color.trim() : ""; + if (!safe) return; + + // only allow solid colors (hex, rgb, hsl); ignore urls/gradients/rgba overlays + const looksSolid = + safe.startsWith("#") || + safe.startsWith("rgb(") || + safe.startsWith("hsl(") || + safe.startsWith("oklch("); + + if (!looksSolid) return; + + let meta = document.querySelector('meta[name="theme-color"]'); + if (!meta) { + meta = document.createElement("meta"); + meta.setAttribute("name", "theme-color"); + document.head.appendChild(meta); + } + meta.setAttribute("content", safe); + } catch { + // ignore + } +} + export function applyTheme(themeKey) { const t = THEMES[themeKey] || THEMES[DEFAULT_THEME_KEY]; const root = document.documentElement; @@ -202,6 +228,10 @@ export function applyTheme(themeKey) { for (const [k, v] of Object.entries(t.tokens)) { root.style.setProperty(`--hp-${k}`, v); } + + // ✅ PWA/Android Statusbar dynamisch an Theme anpassen + // Nimmt (falls vorhanden) statusBarColor, sonst pageBg + setThemeColorMeta(t.tokens.statusBarColor || t.tokens.pageBg || "#000000"); } export function themeStorageKey(email) { diff --git a/frontend/vite.config.js b/frontend/vite.config.js index d23be78..2219097 100644 --- a/frontend/vite.config.js +++ b/frontend/vite.config.js @@ -20,7 +20,7 @@ export default defineConfig({ scope: "/", display: "standalone", background_color: "#1c140d", - theme_color: "#caa45a", + theme_color: "#000000", icons: [ { src: "/icons/icon-512.png", sizes: "512x512", type: "image/png" } ]