195 lines
4.8 KiB
TypeScript
195 lines
4.8 KiB
TypeScript
import { STORAGE_KEY_GENERAL_SETTINGS, STORAGE_KEY_LAST_VIEWED_CHAT } from '../../core/constants';
|
|
import { getUserScopedStorageKey } from '../../core/storage/current-user-storage';
|
|
import { runWhenIdle } from '../../shared/rxjs';
|
|
|
|
export interface GeneralSettings {
|
|
reopenLastViewedChat: boolean;
|
|
}
|
|
|
|
export const DEFAULT_GENERAL_SETTINGS: GeneralSettings = {
|
|
reopenLastViewedChat: true
|
|
};
|
|
|
|
export interface LastViewedChatSnapshot {
|
|
userId: string;
|
|
roomId: string;
|
|
channelId: string | null;
|
|
}
|
|
|
|
const pendingWrites = new Map<string, string | null>();
|
|
|
|
let flushScheduled = false;
|
|
let cancelScheduledFlush: (() => void) | null = null;
|
|
|
|
function scheduleStorageFlush(): void {
|
|
if (flushScheduled) {
|
|
return;
|
|
}
|
|
|
|
flushScheduled = true;
|
|
cancelScheduledFlush = runWhenIdle(() => {
|
|
flushScheduled = false;
|
|
cancelScheduledFlush = null;
|
|
|
|
const snapshot = Array.from(pendingWrites.entries());
|
|
|
|
pendingWrites.clear();
|
|
|
|
for (const [key, value] of snapshot) {
|
|
try {
|
|
if (value === null) {
|
|
localStorage.removeItem(key);
|
|
} else {
|
|
localStorage.setItem(key, value);
|
|
}
|
|
} catch {
|
|
// storage not available
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function scheduleStorageWrite(key: string, serialised: string): void {
|
|
pendingWrites.set(key, serialised);
|
|
scheduleStorageFlush();
|
|
}
|
|
|
|
function scheduleStorageRemove(key: string): void {
|
|
pendingWrites.set(key, null);
|
|
scheduleStorageFlush();
|
|
}
|
|
|
|
function readMaybePending(key: string): string | null {
|
|
if (pendingWrites.has(key)) {
|
|
return pendingWrites.get(key) ?? null;
|
|
}
|
|
|
|
try {
|
|
return localStorage.getItem(key);
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function loadGeneralSettingsFromStorage(): GeneralSettings {
|
|
try {
|
|
const raw = readMaybePending(getUserScopedStorageKey(STORAGE_KEY_GENERAL_SETTINGS))
|
|
?? readMaybePending(STORAGE_KEY_GENERAL_SETTINGS);
|
|
|
|
if (!raw) {
|
|
return { ...DEFAULT_GENERAL_SETTINGS };
|
|
}
|
|
|
|
return normaliseGeneralSettings(JSON.parse(raw) as Partial<GeneralSettings>);
|
|
} catch {
|
|
return { ...DEFAULT_GENERAL_SETTINGS };
|
|
}
|
|
}
|
|
|
|
export function saveGeneralSettingsToStorage(patch: Partial<GeneralSettings>): GeneralSettings {
|
|
const nextSettings = normaliseGeneralSettings({
|
|
...loadGeneralSettingsFromStorage(),
|
|
...patch
|
|
});
|
|
|
|
scheduleStorageWrite(getUserScopedStorageKey(STORAGE_KEY_GENERAL_SETTINGS), JSON.stringify(nextSettings));
|
|
|
|
return nextSettings;
|
|
}
|
|
|
|
export function loadLastViewedChatFromStorage(userId?: string | null): LastViewedChatSnapshot | null {
|
|
try {
|
|
const raw = readMaybePending(getUserScopedStorageKey(STORAGE_KEY_LAST_VIEWED_CHAT, userId))
|
|
?? readMaybePending(STORAGE_KEY_LAST_VIEWED_CHAT);
|
|
|
|
if (!raw) {
|
|
return null;
|
|
}
|
|
|
|
const snapshot = normaliseLastViewedChatSnapshot(JSON.parse(raw) as Partial<LastViewedChatSnapshot>);
|
|
|
|
if (!snapshot) {
|
|
return null;
|
|
}
|
|
|
|
if (userId && snapshot.userId !== userId) {
|
|
return null;
|
|
}
|
|
|
|
return snapshot;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function saveLastViewedChatToStorage(snapshot: LastViewedChatSnapshot): void {
|
|
const normalised = normaliseLastViewedChatSnapshot(snapshot);
|
|
|
|
if (!normalised) {
|
|
return;
|
|
}
|
|
|
|
scheduleStorageWrite(
|
|
getUserScopedStorageKey(STORAGE_KEY_LAST_VIEWED_CHAT, normalised.userId),
|
|
JSON.stringify(normalised)
|
|
);
|
|
}
|
|
|
|
export function clearLastViewedChatFromStorage(userId?: string | null): void {
|
|
scheduleStorageRemove(getUserScopedStorageKey(STORAGE_KEY_LAST_VIEWED_CHAT, userId));
|
|
scheduleStorageRemove(STORAGE_KEY_LAST_VIEWED_CHAT);
|
|
}
|
|
|
|
/** Force-flush any pending app-resume writes (e.g. before unload). */
|
|
export function flushAppResumeStorage(): void {
|
|
if (cancelScheduledFlush) {
|
|
cancelScheduledFlush();
|
|
cancelScheduledFlush = null;
|
|
}
|
|
|
|
flushScheduled = false;
|
|
|
|
const snapshot = Array.from(pendingWrites.entries());
|
|
|
|
pendingWrites.clear();
|
|
|
|
for (const [key, value] of snapshot) {
|
|
try {
|
|
if (value === null) {
|
|
localStorage.removeItem(key);
|
|
} else {
|
|
localStorage.setItem(key, value);
|
|
}
|
|
} catch {
|
|
// storage not available
|
|
}
|
|
}
|
|
}
|
|
|
|
function normaliseGeneralSettings(raw: Partial<GeneralSettings>): GeneralSettings {
|
|
return {
|
|
reopenLastViewedChat:
|
|
typeof raw.reopenLastViewedChat === 'boolean'
|
|
? raw.reopenLastViewedChat
|
|
: DEFAULT_GENERAL_SETTINGS.reopenLastViewedChat
|
|
};
|
|
}
|
|
|
|
function normaliseLastViewedChatSnapshot(raw: Partial<LastViewedChatSnapshot>): LastViewedChatSnapshot | null {
|
|
const userId = typeof raw.userId === 'string' ? raw.userId.trim() : '';
|
|
const roomId = typeof raw.roomId === 'string' ? raw.roomId.trim() : '';
|
|
const channelId = typeof raw.channelId === 'string'
|
|
? raw.channelId.trim() || null
|
|
: null;
|
|
|
|
if (!userId || !roomId) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
userId,
|
|
roomId,
|
|
channelId
|
|
};
|
|
}
|