发布候选:整合桌面端界面与发布稳定化里程碑
Client Quality Gates / Shared client and Web (push) Has been cancelled
Client Quality Gates / macOS Desktop (push) Has been cancelled
Storage Persistence Guard / storage-persistence-audit (push) Has been cancelled
Client Quality Gates / Shared client and Web (pull_request) Has been cancelled
Client Quality Gates / macOS Desktop (pull_request) Has been cancelled
Storage Persistence Guard / storage-persistence-audit (pull_request) Has been cancelled
Client Quality Gates / Shared client and Web (push) Has been cancelled
Client Quality Gates / macOS Desktop (push) Has been cancelled
Storage Persistence Guard / storage-persistence-audit (push) Has been cancelled
Client Quality Gates / Shared client and Web (pull_request) Has been cancelled
Client Quality Gates / macOS Desktop (pull_request) Has been cancelled
Storage Persistence Guard / storage-persistence-audit (pull_request) Has been cancelled
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
export const DESKTOP_RECENT_ROUTES_KEY = "ctms_desktop_recent_routes";
|
||||
export const DESKTOP_FAVORITE_ROUTES_KEY = "ctms_desktop_favorite_routes";
|
||||
export const DESKTOP_THEME_KEY = "ctms_desktop_theme";
|
||||
export const DESKTOP_THEME_CHANGED_EVENT = "ctms:desktop-theme-changed";
|
||||
|
||||
export type DesktopThemePreference = "light" | "dark";
|
||||
|
||||
export interface DesktopRoutePreference {
|
||||
path: string;
|
||||
title: string;
|
||||
group?: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
const MAX_RECENT_ROUTES = 8;
|
||||
const MAX_FAVORITE_ROUTES = 12;
|
||||
const DEFAULT_DESKTOP_THEME: DesktopThemePreference = "light";
|
||||
const DESKTOP_THEME_ATTRIBUTE = "data-ctms-theme";
|
||||
|
||||
const isStorageAvailable = () => typeof window !== "undefined" && typeof window.localStorage !== "undefined";
|
||||
|
||||
const sanitizeRoutePreference = (value: Partial<DesktopRoutePreference> | null | undefined): DesktopRoutePreference | null => {
|
||||
const path = typeof value?.path === "string" ? value.path.trim() : "";
|
||||
const title = typeof value?.title === "string" ? value.title.trim() : "";
|
||||
if (!path.startsWith("/") || !title) return null;
|
||||
return {
|
||||
path,
|
||||
title: title.slice(0, 80),
|
||||
group: typeof value?.group === "string" ? value.group.slice(0, 40) : undefined,
|
||||
updatedAt: typeof value?.updatedAt === "string" ? value.updatedAt : new Date().toISOString(),
|
||||
};
|
||||
};
|
||||
|
||||
const readRoutePreferences = (key: string): DesktopRoutePreference[] => {
|
||||
if (!isStorageAvailable()) return [];
|
||||
try {
|
||||
const raw = window.localStorage.getItem(key);
|
||||
if (!raw) return [];
|
||||
const parsed = JSON.parse(raw);
|
||||
if (!Array.isArray(parsed)) return [];
|
||||
return parsed
|
||||
.map((item) => sanitizeRoutePreference(item))
|
||||
.filter(Boolean) as DesktopRoutePreference[];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
const writeRoutePreferences = (key: string, routes: DesktopRoutePreference[]) => {
|
||||
if (!isStorageAvailable()) return;
|
||||
window.localStorage.setItem(key, JSON.stringify(routes));
|
||||
};
|
||||
|
||||
const sanitizeDesktopThemePreference = (value: unknown): DesktopThemePreference =>
|
||||
value === "dark" ? "dark" : DEFAULT_DESKTOP_THEME;
|
||||
|
||||
export const readDesktopThemePreference = (): DesktopThemePreference => {
|
||||
if (!isStorageAvailable()) return DEFAULT_DESKTOP_THEME;
|
||||
return sanitizeDesktopThemePreference(window.localStorage.getItem(DESKTOP_THEME_KEY));
|
||||
};
|
||||
|
||||
export const applyDesktopThemePreference = (theme: DesktopThemePreference = readDesktopThemePreference()): DesktopThemePreference => {
|
||||
const nextTheme = sanitizeDesktopThemePreference(theme);
|
||||
if (typeof document !== "undefined") {
|
||||
document.documentElement.setAttribute(DESKTOP_THEME_ATTRIBUTE, nextTheme);
|
||||
document.documentElement.style.colorScheme = nextTheme;
|
||||
}
|
||||
return nextTheme;
|
||||
};
|
||||
|
||||
export const setDesktopThemePreference = (theme: DesktopThemePreference): DesktopThemePreference => {
|
||||
const nextTheme = sanitizeDesktopThemePreference(theme);
|
||||
if (isStorageAvailable()) {
|
||||
window.localStorage.setItem(DESKTOP_THEME_KEY, nextTheme);
|
||||
}
|
||||
applyDesktopThemePreference(nextTheme);
|
||||
if (typeof window !== "undefined") {
|
||||
window.dispatchEvent(new CustomEvent(DESKTOP_THEME_CHANGED_EVENT, { detail: nextTheme }));
|
||||
}
|
||||
return nextTheme;
|
||||
};
|
||||
|
||||
export const readDesktopRecentRoutes = (): DesktopRoutePreference[] => readRoutePreferences(DESKTOP_RECENT_ROUTES_KEY);
|
||||
|
||||
export const readDesktopFavoriteRoutes = (): DesktopRoutePreference[] => readRoutePreferences(DESKTOP_FAVORITE_ROUTES_KEY);
|
||||
|
||||
export const recordDesktopRecentRoute = (route: Pick<DesktopRoutePreference, "path" | "title" | "group">): DesktopRoutePreference[] => {
|
||||
const item = sanitizeRoutePreference({ ...route, updatedAt: new Date().toISOString() });
|
||||
if (!item) return readDesktopRecentRoutes();
|
||||
const next = [
|
||||
item,
|
||||
...readDesktopRecentRoutes().filter((existing) => existing.path !== item.path),
|
||||
].slice(0, MAX_RECENT_ROUTES);
|
||||
writeRoutePreferences(DESKTOP_RECENT_ROUTES_KEY, next);
|
||||
return next;
|
||||
};
|
||||
|
||||
export const isDesktopFavoriteRoute = (path: string): boolean =>
|
||||
readDesktopFavoriteRoutes().some((item) => item.path === path);
|
||||
|
||||
export const toggleDesktopFavoriteRoute = (
|
||||
route: Pick<DesktopRoutePreference, "path" | "title" | "group">,
|
||||
): DesktopRoutePreference[] => {
|
||||
const item = sanitizeRoutePreference({ ...route, updatedAt: new Date().toISOString() });
|
||||
if (!item) return readDesktopFavoriteRoutes();
|
||||
const current = readDesktopFavoriteRoutes();
|
||||
const exists = current.some((existing) => existing.path === item.path);
|
||||
const next = exists
|
||||
? current.filter((existing) => existing.path !== item.path)
|
||||
: [item, ...current].slice(0, MAX_FAVORITE_ROUTES);
|
||||
writeRoutePreferences(DESKTOP_FAVORITE_ROUTES_KEY, next);
|
||||
return next;
|
||||
};
|
||||
Reference in New Issue
Block a user