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.
160 lines
3.9 KiB
TypeScript
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);
|
|
}
|
|
);
|