mirror of
https://github.com/multipleof4/ntfy.git
synced 2026-10-15 07:57:54 +00:00
COndense comments, better prefcache use for theme
This commit is contained in:
+2
-22
@@ -6,12 +6,7 @@ export const THEME = {
|
||||
SYSTEM: "system",
|
||||
};
|
||||
|
||||
// Key under which the theme preference is mirrored to localStorage. The inline script in
|
||||
// index.html reads it synchronously to pick the splash background before first paint.
|
||||
export const THEME_LOCALSTORAGE_KEY = "theme";
|
||||
|
||||
// Default values returned by the getters below when a preference hasn't been set. Single source of
|
||||
// truth, also used by the PrefCache (see PrefCache.jsx) for its loading-state placeholder.
|
||||
// Default values the getters return when a pref is unset; also used by PrefCache (PrefCache.jsx).
|
||||
export const PREF_DEFAULTS = {
|
||||
sound: "ding",
|
||||
minPriority: 1,
|
||||
@@ -20,15 +15,6 @@ export const PREF_DEFAULTS = {
|
||||
webPushEnabled: false,
|
||||
};
|
||||
|
||||
const mirrorThemeToLocalStorage = (value) => {
|
||||
try {
|
||||
localStorage.setItem(THEME_LOCALSTORAGE_KEY, value);
|
||||
} catch (e) {
|
||||
// localStorage may be unavailable (private mode, disabled cookies); the splash just falls
|
||||
// back to the system color scheme in that case.
|
||||
}
|
||||
};
|
||||
|
||||
class Prefs {
|
||||
constructor(dbImpl) {
|
||||
this.db = dbImpl;
|
||||
@@ -72,17 +58,11 @@ class Prefs {
|
||||
|
||||
async theme() {
|
||||
const theme = await this.db.prefs.get("theme");
|
||||
const value = theme?.value ?? PREF_DEFAULTS.theme;
|
||||
// Mirror to localStorage so the inline script in index.html can pick the splash background
|
||||
// synchronously before first paint. Self-heals for users who set their theme before the
|
||||
// mirror existed.
|
||||
mirrorThemeToLocalStorage(value);
|
||||
return value;
|
||||
return theme?.value ?? PREF_DEFAULTS.theme;
|
||||
}
|
||||
|
||||
async setTheme(mode) {
|
||||
await this.db.prefs.put({ key: "theme", value: mode });
|
||||
mirrorThemeToLocalStorage(mode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,9 +44,8 @@ class Session {
|
||||
}
|
||||
|
||||
async resetAndRedirect(url, { fade = false } = {}) {
|
||||
// For user-initiated exits (logout, account deletion) fade the app out first -- while the data
|
||||
// is still intact -- so the wipe + reload below doesn't flash a broken/empty UI. Error-driven
|
||||
// redirects (e.g. an expired session) pass no options and cut straight to the target page.
|
||||
// User-initiated exits (logout, account deletion) fade out first, while data's intact, so the
|
||||
// wipe + reload doesn't flash a broken UI. Error redirects pass no options and cut straight through.
|
||||
if (fade) {
|
||||
await fadeOut();
|
||||
}
|
||||
|
||||
+11
-17
@@ -1,15 +1,11 @@
|
||||
// Fades out and removes the static splash screen baked into index.html (see web/index.html).
|
||||
// The splash paints before the JS bundle loads to avoid the white flash + spinner flicker on
|
||||
// first load; the app calls this once it has mounted and the initial data is ready. Idempotent --
|
||||
// safe to call from multiple routes/effects.
|
||||
// Fades out and removes the static splash (see web/index.html), called once the app has mounted and
|
||||
// its data is ready. Idempotent.
|
||||
|
||||
// Keep the splash up for at least this long so it doesn't flash-and-vanish on fast (warm-cache)
|
||||
// loads -- the logo gets a beat to be seen (and to pulse) before fading out.
|
||||
// Minimum time the splash stays up, so it doesn't flash-and-vanish on warm-cache loads.
|
||||
const MIN_VISIBLE_MS = 1000;
|
||||
|
||||
// Hide in two phases: first fade the (pulsing) logo all the way out, then fade the background away
|
||||
// to reveal -- "fade in" -- the app underneath. APP_FADE_MS must match the #splash opacity
|
||||
// transition in index.html.
|
||||
// Hide in two phases: fade the logo out, then fade the background away to reveal the app.
|
||||
// APP_FADE_MS must match the #splash opacity transition in index.html.
|
||||
const LOGO_FADE_MS = 300;
|
||||
const APP_FADE_MS = 100;
|
||||
|
||||
@@ -21,24 +17,23 @@ const fadeOutAndRemove = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
// Phase 1: fade the logo out completely. Freeze the pulse at its current opacity first, then
|
||||
// transition to 0 -- otherwise stopping the animation would snap the logo to full opacity.
|
||||
// Phase 1: freeze the pulse at its current opacity (else stopping the animation snaps to full),
|
||||
// then fade the logo to 0.
|
||||
const img = splash.querySelector("img");
|
||||
if (img) {
|
||||
const current = getComputedStyle(img).opacity;
|
||||
img.style.opacity = current;
|
||||
img.style.animation = "none";
|
||||
img.getBoundingClientRect(); // force reflow so the fade starts from `current`, not the snapped value
|
||||
img.getBoundingClientRect(); // force reflow so the fade starts from `current`
|
||||
img.style.transition = `opacity ${LOGO_FADE_MS}ms ease-out`;
|
||||
img.style.opacity = "0";
|
||||
}
|
||||
|
||||
// Phase 2: once the logo is gone, lift the background to fade the app in, then remove the node.
|
||||
// Phase 2: lift the background to fade the app in, then remove the node.
|
||||
setTimeout(() => {
|
||||
splash.classList.add("splash-hidden");
|
||||
const remove = () => splash.remove();
|
||||
// Only react to the background's own opacity transition -- the logo's transitionend bubbles up
|
||||
// here too, and would otherwise remove the splash before the app has finished fading in.
|
||||
// Ignore the logo's bubbling transitionend; only the background's own fade should remove it.
|
||||
const onEnd = (event) => {
|
||||
if (event.target === splash) {
|
||||
splash.removeEventListener("transitionend", onEnd);
|
||||
@@ -55,8 +50,7 @@ const hideSplash = () => {
|
||||
return;
|
||||
}
|
||||
removed = true;
|
||||
// performance.now() is the time since the page started loading, i.e. roughly how long the splash
|
||||
// has been visible. Hold it until MIN_VISIBLE_MS has elapsed before fading out.
|
||||
// performance.now() ~= how long the splash has been visible; hold until MIN_VISIBLE_MS elapses.
|
||||
const remaining = Math.max(0, MIN_VISIBLE_MS - performance.now());
|
||||
setTimeout(fadeOutAndRemove, remaining);
|
||||
};
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
// Fade transitions for navigating between the main app and the auth pages (login/signup/reset).
|
||||
// We fade the whole app (#root) out, then either navigate client-side and fade back in, or do a
|
||||
// full reload (where the splash screen in index.html fades the next page in).
|
||||
// Fade transitions between the app and the auth pages (login/signup/reset): fade #root out, then
|
||||
// navigate client-side (fading back in) or full-reload (the splash fades the next page in).
|
||||
|
||||
const FADE_MS = 150;
|
||||
|
||||
// Fade #root out over FADE_MS, resolving once the fade has finished (immediately if #root isn't in
|
||||
// the document). Exported so callers that need to run their own teardown between the fade-out and
|
||||
// the reload -- e.g. session.resetAndRedirect() wiping IndexedDB -- can await it first.
|
||||
// Fade #root out, resolving when done. Exported so callers can await it before their own
|
||||
// teardown + reload (e.g. resetAndRedirect wiping IndexedDB).
|
||||
export const fadeOut = () =>
|
||||
new Promise((resolve) => {
|
||||
const node = document.getElementById("root");
|
||||
@@ -19,10 +17,8 @@ export const fadeOut = () =>
|
||||
setTimeout(resolve, FADE_MS);
|
||||
});
|
||||
|
||||
// Fade #root back in, then remove the inline styles we added so nothing is left behind on the
|
||||
// element -- otherwise the lingering `transition` would silently animate any future opacity change
|
||||
// to #root. Uses setTimeout (not requestAnimationFrame) so a backgrounded tab can't strand #root
|
||||
// at opacity 0; rAF can be paused entirely in background tabs, while timers still fire.
|
||||
// Fade #root back in, then strip the inline styles (a lingering `transition` would animate future
|
||||
// opacity changes). setTimeout, not rAF, so a backgrounded tab can't strand #root at opacity 0.
|
||||
const fadeInRoot = () => {
|
||||
const node = document.getElementById("root");
|
||||
if (!node) {
|
||||
@@ -35,8 +31,7 @@ const fadeInRoot = () => {
|
||||
}, FADE_MS);
|
||||
};
|
||||
|
||||
// Fade the app out, run a client-side navigation, then fade the new page back in. Used for
|
||||
// app -> login/signup, which stay within the same document (no reload).
|
||||
// Fade out, navigate client-side, fade the new page in (app -> login/signup, no reload).
|
||||
export const fadeNavigate = (navigate, to) => {
|
||||
fadeOut().then(() => {
|
||||
navigate(to);
|
||||
@@ -44,8 +39,8 @@ export const fadeNavigate = (navigate, to) => {
|
||||
});
|
||||
};
|
||||
|
||||
// Fade the app out, then do a full page reload to `url`. Used for login/signup -> app, which must
|
||||
// reload (the per-user IndexedDB changes). The splash screen fades the reloaded app back in.
|
||||
// Fade out, then full-reload to `url` (login/signup -> app needs a reload for the per-user DB).
|
||||
// The splash fades it back in.
|
||||
export const fadeReload = (url) => {
|
||||
fadeOut().then(() => {
|
||||
window.location.href = url;
|
||||
|
||||
Reference in New Issue
Block a user