Files
cinny/src/app/state/settings.ts
litruv 30edd62d96 Make room switches instant and cut redundant client work.
Keep-alive room cache with CSS-only warm switches, nav loading feedback, and per-context members drawer prefs. Share hierarchy/call/sub-room subscriptions, demux state events, and lazy lobby power-level loading to avoid duplicate scans and listener storms.
2026-07-22 18:00:54 +10:00

160 lines
3.9 KiB
TypeScript

import { atom } from 'jotai';
const STORAGE_KEY = 'settings';
export type DateFormat = 'D MMM YYYY' | 'DD/MM/YYYY' | 'MM/DD/YYYY' | 'YYYY/MM/DD' | '';
export type MessageSpacing = '0' | '100' | '200' | '300' | '400' | '500';
export enum MessageLayout {
Modern = 0,
Compact = 1,
Bubble = 2,
}
export enum EmojiStyle {
System = 'system',
Apple = 'apple',
Twemoji = 'twemoji',
}
export enum ScrollToLatestBehavior {
Always = 'always',
Never = 'never',
OnNewMessage = 'onNewMessage',
}
export type CallPanelDockPosition = 'right' | 'sidebar';
export interface Settings {
themeId?: string;
useSystemTheme: boolean;
lightThemeId?: string;
darkThemeId?: string;
monochromeMode?: boolean;
isMarkdown: boolean;
editorToolbar: boolean;
emojiStyle: EmojiStyle;
autoConvertEmoticons: boolean;
pageZoom: number;
hideActivity: boolean;
/** Last open/closed state of the members panel in non-DM rooms. */
peopleDrawerInRooms: boolean;
/** Last open/closed state of the members panel in DMs. */
peopleDrawerInDirects: boolean;
isCallPanelDocked: boolean;
callPanelDockPosition: CallPanelDockPosition;
memberSortFilterIndex: number;
enterForNewline: boolean;
messageLayout: MessageLayout;
messageSpacing: MessageSpacing;
hideMembershipEvents: boolean;
hideNickAvatarEvents: boolean;
mediaAutoLoad: boolean;
urlPreview: boolean;
encUrlPreview: boolean;
showHiddenEvents: boolean;
legacyUsernameColor: boolean;
showNotifications: boolean;
isNotificationSounds: boolean;
showRemoteCursor: boolean;
hour24Clock: boolean;
dateFormatString: string;
developerTools: boolean;
autoJoinSpaceRooms: boolean;
scrollToLatestBehavior: ScrollToLatestBehavior;
}
const defaultSettings: Settings = {
themeId: undefined,
useSystemTheme: true,
lightThemeId: undefined,
darkThemeId: undefined,
monochromeMode: false,
isMarkdown: true,
editorToolbar: false,
emojiStyle: EmojiStyle.Apple,
autoConvertEmoticons: true,
pageZoom: 100,
hideActivity: false,
peopleDrawerInRooms: true,
peopleDrawerInDirects: false,
isCallPanelDocked: false,
callPanelDockPosition: 'right',
memberSortFilterIndex: 0,
enterForNewline: false,
messageLayout: 0,
messageSpacing: '400',
hideMembershipEvents: false,
hideNickAvatarEvents: true,
mediaAutoLoad: true,
urlPreview: true,
encUrlPreview: false,
showHiddenEvents: false,
legacyUsernameColor: false,
showNotifications: true,
isNotificationSounds: true,
showRemoteCursor: true,
hour24Clock: false,
dateFormatString: 'D MMM YYYY',
developerTools: false,
autoJoinSpaceRooms: true,
scrollToLatestBehavior: ScrollToLatestBehavior.Always,
};
export const getSettings = () => {
const settings = localStorage.getItem(STORAGE_KEY);
if (settings === null) return defaultSettings;
const parsed = JSON.parse(settings) as Settings & {
twitterEmoji?: boolean;
isPeopleDrawer?: boolean;
};
// Migrate old twitterEmoji boolean to new emojiStyle enum
if ('twitterEmoji' in parsed && parsed.emojiStyle === undefined) {
parsed.emojiStyle = parsed.twitterEmoji ? EmojiStyle.Twemoji : EmojiStyle.Apple;
delete parsed.twitterEmoji;
}
// Migrate single people-drawer flag into room/DM prefs.
if (
typeof parsed.isPeopleDrawer === 'boolean' &&
parsed.peopleDrawerInRooms === undefined &&
parsed.peopleDrawerInDirects === undefined
) {
parsed.peopleDrawerInRooms = parsed.isPeopleDrawer;
parsed.peopleDrawerInDirects = parsed.isPeopleDrawer;
}
delete parsed.isPeopleDrawer;
return {
...defaultSettings,
...parsed,
};
};
export const setSettings = (settings: Settings) => {
localStorage.setItem(STORAGE_KEY, JSON.stringify(settings));
};
const baseSettings = atom<Settings>(getSettings());
export const settingsAtom = atom<Settings, [Settings], undefined>(
(get) => get(baseSettings),
(get, set, update) => {
set(baseSettings, update);
setSettings(update);
}
);