8a5a5d6c4d
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Has been skipped
CI / integration (pull_request) Has been skipped
CI / ui (pull_request) Successful in 56s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m20s
Theme, reduce-motion and board labels lived only in local IndexedDB/localStorage, so they did not follow the user across devices and could be lost when the Telegram WebView cleared storage. Mirror these three device-independent prefs to Telegram CloudStorage (Bot API 6.9) from the single local-persist point (persistPrefs), and reconcile them from CloudStorage in the background on launch (reconcileCloudPrefs) so a change made on another device follows the user here. The local store stays the instant-render cache; the interface language is intentionally excluded (it syncs via the durable account). Pure encode/decode extracted to cloudprefs.ts with unit tests; the CloudStorage transport wrappers are added to telegram.ts. No-op outside Telegram or on a client predating CloudStorage.
50 lines
2.3 KiB
TypeScript
50 lines
2.3 KiB
TypeScript
// Telegram CloudStorage sync for the device-independent client display preferences — theme,
|
|
// reduce-motion and board labels — so they follow the user across their Telegram devices. The
|
|
// interface language is intentionally excluded: it has its own server-side sync
|
|
// (Profile.preferredLanguage) plus an on-launch reconciler, and mixing it in here would fight that.
|
|
// The pure encode/decode is kept free of the SDK and the DOM so it unit-tests in the node
|
|
// environment; the CloudStorage transport wrappers live in telegram.ts and the wiring (mirror on
|
|
// save, reconcile on launch) in app.svelte.ts.
|
|
|
|
import type { ThemePref } from './theme';
|
|
import type { BoardLabelMode } from './boardlabels';
|
|
|
|
/** ClientPrefs is the subset of preferences synced across devices via Telegram CloudStorage. */
|
|
export interface ClientPrefs {
|
|
theme: ThemePref;
|
|
reduceMotion: boolean;
|
|
boardLabels: BoardLabelMode;
|
|
}
|
|
|
|
/** CLOUD_PREFS_KEY is the Telegram CloudStorage key holding the JSON-encoded ClientPrefs. */
|
|
export const CLOUD_PREFS_KEY = 'prefs';
|
|
|
|
/** encodeClientPrefs serialises the synced client prefs (and only those — never the locale). */
|
|
export function encodeClientPrefs(p: ClientPrefs): string {
|
|
return JSON.stringify({ theme: p.theme, reduceMotion: p.reduceMotion, boardLabels: p.boardLabels });
|
|
}
|
|
|
|
/**
|
|
* decodeClientPrefs parses a CloudStorage payload into a partial ClientPrefs, keeping only valid
|
|
* fields and dropping anything unknown, mistyped or malformed — so a value written by a newer or
|
|
* older build, or a corrupt entry, never throws and never applies a bad setting. A missing field
|
|
* stays absent, so the caller leaves the corresponding local value untouched.
|
|
*/
|
|
export function decodeClientPrefs(raw: string | null | undefined): Partial<ClientPrefs> {
|
|
if (!raw) return {};
|
|
let o: Record<string, unknown>;
|
|
try {
|
|
o = JSON.parse(raw) as Record<string, unknown>;
|
|
} catch {
|
|
return {};
|
|
}
|
|
if (!o || typeof o !== 'object') return {};
|
|
const out: Partial<ClientPrefs> = {};
|
|
if (o.theme === 'auto' || o.theme === 'light' || o.theme === 'dark') out.theme = o.theme;
|
|
if (typeof o.reduceMotion === 'boolean') out.reduceMotion = o.reduceMotion;
|
|
if (o.boardLabels === 'beginner' || o.boardLabels === 'classic' || o.boardLabels === 'none') {
|
|
out.boardLabels = o.boardLabels;
|
|
}
|
|
return out;
|
|
}
|