mirror of
https://github.com/multipleof4/ntfy.git
synced 2026-10-16 16:37:55 +00:00
Centralize fade out
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import Dexie from "dexie";
|
import Dexie from "dexie";
|
||||||
|
import { fadeOut } from "./transition";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages the logged-in user's session and access token.
|
* Manages the logged-in user's session and access token.
|
||||||
@@ -42,7 +43,13 @@ class Session {
|
|||||||
localStorage.setItem("token", token);
|
localStorage.setItem("token", token);
|
||||||
}
|
}
|
||||||
|
|
||||||
async resetAndRedirect(url) {
|
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.
|
||||||
|
if (fade) {
|
||||||
|
await fadeOut();
|
||||||
|
}
|
||||||
await this.db.delete();
|
await this.db.delete();
|
||||||
localStorage.removeItem("user");
|
localStorage.removeItem("user");
|
||||||
localStorage.removeItem("token");
|
localStorage.removeItem("token");
|
||||||
|
|||||||
+16
-19
@@ -4,15 +4,20 @@
|
|||||||
|
|
||||||
const FADE_MS = 150;
|
const FADE_MS = 150;
|
||||||
|
|
||||||
// Fade #root out and return it (or null if not found / no document).
|
// Fade #root out over FADE_MS, resolving once the fade has finished (immediately if #root isn't in
|
||||||
const fadeOutRoot = () => {
|
// the document). Exported so callers that need to run their own teardown between the fade-out and
|
||||||
const node = document.getElementById("root");
|
// the reload -- e.g. session.resetAndRedirect() wiping IndexedDB -- can await it first.
|
||||||
if (node) {
|
export const fadeOut = () =>
|
||||||
|
new Promise((resolve) => {
|
||||||
|
const node = document.getElementById("root");
|
||||||
|
if (!node) {
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
node.style.transition = `opacity ${FADE_MS}ms ease-out`;
|
node.style.transition = `opacity ${FADE_MS}ms ease-out`;
|
||||||
node.style.opacity = "0";
|
node.style.opacity = "0";
|
||||||
}
|
setTimeout(resolve, FADE_MS);
|
||||||
return node;
|
});
|
||||||
};
|
|
||||||
|
|
||||||
// Fade #root back in, then remove the inline styles we added so nothing is left behind on the
|
// 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
|
// element -- otherwise the lingering `transition` would silently animate any future opacity change
|
||||||
@@ -33,24 +38,16 @@ const fadeInRoot = () => {
|
|||||||
// Fade the app out, run a client-side navigation, then fade the new page back in. Used for
|
// 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).
|
// app -> login/signup, which stay within the same document (no reload).
|
||||||
export const fadeNavigate = (navigate, to) => {
|
export const fadeNavigate = (navigate, to) => {
|
||||||
if (!fadeOutRoot()) {
|
fadeOut().then(() => {
|
||||||
navigate(to);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setTimeout(() => {
|
|
||||||
navigate(to);
|
navigate(to);
|
||||||
fadeInRoot();
|
fadeInRoot();
|
||||||
}, FADE_MS);
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Fade the app out, then do a full page reload to `url`. Used for login/signup -> app, which must
|
// 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.
|
// reload (the per-user IndexedDB changes). The splash screen fades the reloaded app back in.
|
||||||
export const fadeReload = (url) => {
|
export const fadeReload = (url) => {
|
||||||
if (!fadeOutRoot()) {
|
fadeOut().then(() => {
|
||||||
window.location.href = url;
|
window.location.href = url;
|
||||||
return;
|
});
|
||||||
}
|
|
||||||
setTimeout(() => {
|
|
||||||
window.location.href = url;
|
|
||||||
}, FADE_MS);
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1396,7 +1396,7 @@ const DeleteAccountDialog = (props) => {
|
|||||||
await accountApi.delete(password);
|
await accountApi.delete(password);
|
||||||
await db().delete();
|
await db().delete();
|
||||||
console.debug(`[Account] Account deleted`);
|
console.debug(`[Account] Account deleted`);
|
||||||
await session.resetAndRedirect(routes.app);
|
await session.resetAndRedirect(routes.app, { fade: true });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(`[Account] Error deleting account`, e);
|
console.log(`[Account] Error deleting account`, e);
|
||||||
if (e instanceof IncorrectPasswordError) {
|
if (e instanceof IncorrectPasswordError) {
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ const ProfileIcon = () => {
|
|||||||
await accountApi.logout();
|
await accountApi.logout();
|
||||||
await db().delete();
|
await db().delete();
|
||||||
} finally {
|
} finally {
|
||||||
await session.resetAndRedirect(routes.app);
|
await session.resetAndRedirect(routes.app, { fade: true });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ import accountApi, { Permission, Role } from "../app/AccountApi";
|
|||||||
import { Pref, PrefGroup } from "./Pref";
|
import { Pref, PrefGroup } from "./Pref";
|
||||||
import AccountContext from "./AccountContext";
|
import AccountContext from "./AccountContext";
|
||||||
import { Paragraph } from "./styles";
|
import { Paragraph } from "./styles";
|
||||||
import prefs, { THEME } from "../app/Prefs";
|
import prefs, { THEME, THEME_LOCALSTORAGE_KEY } from "../app/Prefs";
|
||||||
import { PermissionDenyAll, PermissionRead, PermissionReadWrite, PermissionWrite } from "./ReserveIcons";
|
import { PermissionDenyAll, PermissionRead, PermissionReadWrite, PermissionWrite } from "./ReserveIcons";
|
||||||
import { ReserveAddDialog, ReserveDeleteDialog, ReserveEditDialog } from "./ReserveDialogs";
|
import { ReserveAddDialog, ReserveDeleteDialog, ReserveEditDialog } from "./ReserveDialogs";
|
||||||
import { UnauthorizedError } from "../app/errors";
|
import { UnauthorizedError } from "../app/errors";
|
||||||
@@ -230,7 +230,10 @@ const DeleteAfter = () => {
|
|||||||
const Theme = () => {
|
const Theme = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const labelId = "prefTheme";
|
const labelId = "prefTheme";
|
||||||
const theme = useCachedPref(() => prefs.theme(), "pref.theme", THEME.SYSTEM);
|
// Reuse the same localStorage key Prefs already mirrors for the splash (THEME_LOCALSTORAGE_KEY),
|
||||||
|
// rather than a second cache key, so the value isn't stored twice. The cached value renders the
|
||||||
|
// dropdown instantly; the live query then confirms it from IndexedDB.
|
||||||
|
const theme = useLiveQuery(() => prefs.theme()) ?? localStorage.getItem(THEME_LOCALSTORAGE_KEY) ?? THEME.SYSTEM;
|
||||||
const handleChange = async (ev) => {
|
const handleChange = async (ev) => {
|
||||||
await prefs.setTheme(ev.target.value);
|
await prefs.setTheme(ev.target.value);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user