feat: realtime sync, settings UX and chat list improvements
Some checks failed
CI / test (push) Failing after 21s
Some checks failed
CI / test (push) Failing after 21s
- add chat_updated realtime event and dynamic chat subscriptions - auto-join invite links in web app - implement Telegram-like settings panel (general/notifications/privacy) - add browser notification preferences and keyboard send mode - improve chat list with last message preview/time and online badge - rework chat members UI with context actions and role crowns
This commit is contained in:
87
web/src/utils/preferences.ts
Normal file
87
web/src/utils/preferences.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
export type ThemeMode = "light" | "dark" | "system";
|
||||
export type SendMode = "enter" | "ctrl_enter";
|
||||
|
||||
export interface AppPreferences {
|
||||
theme: ThemeMode;
|
||||
messageFontSize: number;
|
||||
sendMode: SendMode;
|
||||
webNotifications: boolean;
|
||||
privateNotifications: boolean;
|
||||
groupNotifications: boolean;
|
||||
channelNotifications: boolean;
|
||||
messagePreview: boolean;
|
||||
}
|
||||
|
||||
const STORAGE_KEY = "bm_preferences_v1";
|
||||
|
||||
const DEFAULTS: AppPreferences = {
|
||||
theme: "system",
|
||||
messageFontSize: 16,
|
||||
sendMode: "enter",
|
||||
webNotifications: true,
|
||||
privateNotifications: true,
|
||||
groupNotifications: true,
|
||||
channelNotifications: true,
|
||||
messagePreview: true,
|
||||
};
|
||||
|
||||
export function getAppPreferences(): AppPreferences {
|
||||
if (typeof window === "undefined") {
|
||||
return DEFAULTS;
|
||||
}
|
||||
try {
|
||||
const raw = window.localStorage.getItem(STORAGE_KEY);
|
||||
if (!raw) {
|
||||
return DEFAULTS;
|
||||
}
|
||||
const parsed = JSON.parse(raw) as Partial<AppPreferences>;
|
||||
return {
|
||||
theme: parsed.theme === "light" || parsed.theme === "dark" || parsed.theme === "system" ? parsed.theme : DEFAULTS.theme,
|
||||
messageFontSize: normalizeFontSize(parsed.messageFontSize),
|
||||
sendMode: parsed.sendMode === "ctrl_enter" ? "ctrl_enter" : "enter",
|
||||
webNotifications: typeof parsed.webNotifications === "boolean" ? parsed.webNotifications : DEFAULTS.webNotifications,
|
||||
privateNotifications: typeof parsed.privateNotifications === "boolean" ? parsed.privateNotifications : DEFAULTS.privateNotifications,
|
||||
groupNotifications: typeof parsed.groupNotifications === "boolean" ? parsed.groupNotifications : DEFAULTS.groupNotifications,
|
||||
channelNotifications: typeof parsed.channelNotifications === "boolean" ? parsed.channelNotifications : DEFAULTS.channelNotifications,
|
||||
messagePreview: typeof parsed.messagePreview === "boolean" ? parsed.messagePreview : DEFAULTS.messagePreview,
|
||||
};
|
||||
} catch {
|
||||
return DEFAULTS;
|
||||
}
|
||||
}
|
||||
|
||||
export function saveAppPreferences(next: AppPreferences): void {
|
||||
if (typeof window === "undefined") {
|
||||
return;
|
||||
}
|
||||
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(next));
|
||||
}
|
||||
|
||||
export function updateAppPreferences(patch: Partial<AppPreferences>): AppPreferences {
|
||||
const current = getAppPreferences();
|
||||
const next: AppPreferences = {
|
||||
...current,
|
||||
...patch,
|
||||
messageFontSize: normalizeFontSize((patch.messageFontSize ?? current.messageFontSize)),
|
||||
};
|
||||
saveAppPreferences(next);
|
||||
applyAppearancePreferences(next);
|
||||
return next;
|
||||
}
|
||||
|
||||
export function applyAppearancePreferences(prefs: AppPreferences): void {
|
||||
if (typeof document === "undefined") {
|
||||
return;
|
||||
}
|
||||
document.documentElement.style.setProperty("--bm-font-size", `${prefs.messageFontSize}px`);
|
||||
document.documentElement.setAttribute("data-theme", prefs.theme);
|
||||
}
|
||||
|
||||
function normalizeFontSize(value: number | undefined): number {
|
||||
const input = Number(value);
|
||||
if (!Number.isFinite(input)) {
|
||||
return DEFAULTS.messageFontSize;
|
||||
}
|
||||
return Math.max(12, Math.min(24, Math.round(input)));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user