위젯 기능 추가

This commit is contained in:
Macbook
2026-06-14 01:03:51 +09:00
parent 95595f7e9e
commit 6936792ad4
57 changed files with 6728 additions and 540 deletions
+39
View File
@@ -3,6 +3,7 @@
*/
import {
getAppSettingsCache,
isAppSettingsCacheLoaded,
patchAppSettingsCache,
subscribeAppSettings,
} from '../hooks/useAppSettings';
@@ -12,6 +13,7 @@ import {
UI_PREFERENCES_VERSION,
type UiPreferences,
} from '../types/uiPreferences';
import { normalizeWidgetLayout } from '../types/widgetDashboard';
const SAVE_DEBOUNCE_MS = 400;
let saveTimer: ReturnType<typeof setTimeout> | null = null;
@@ -51,6 +53,9 @@ export function resolveUiPreferences(raw: unknown): UiPreferences {
panels: { ...EMPTY_UI_PREFERENCES.panels, ...o.panels },
virtual: { ...EMPTY_UI_PREFERENCES.virtual, ...o.virtual },
tradeNotifications: { ...EMPTY_UI_PREFERENCES.tradeNotifications, ...o.tradeNotifications },
widgetDashboard: o.widgetDashboard
? normalizeWidgetLayout(o.widgetDashboard)
: undefined,
};
}
@@ -61,6 +66,13 @@ export function getUiPreferences(): UiPreferences {
/** UI 설정 부분 갱신 — 낙관적 캐시 + 디바운스 DB 저장 */
export function patchUiPreferences(patch: Partial<UiPreferences>, immediate = false): void {
if (!isAppSettingsCacheLoaded()) {
pendingPatch = pendingPatch
? (deepMerge(pendingPatch as Record<string, unknown>, patch as Record<string, unknown>) as Partial<UiPreferences>)
: patch;
return;
}
const current = getUiPreferences();
const next = deepMerge(current as Record<string, unknown>, patch as Record<string, unknown>) as UiPreferences;
next.v = UI_PREFERENCES_VERSION;
@@ -73,6 +85,7 @@ export function patchUiPreferences(patch: Partial<UiPreferences>, immediate = fa
const flush = () => {
saveTimer = null;
if (!isAppSettingsCacheLoaded()) return;
const toSave = pendingPatch
? resolveUiPreferences(deepMerge(getUiPreferences() as Record<string, unknown>, pendingPatch as Record<string, unknown>))
: next;
@@ -95,10 +108,36 @@ export function patchUiPreferences(patch: Partial<UiPreferences>, immediate = fa
saveTimer = setTimeout(flush, SAVE_DEBOUNCE_MS);
}
/** 디바운스 대기 중인 UI 설정 즉시 DB 저장 (페이지 이탈 시) */
export function flushPendingUiPreferences(): void {
if (saveTimer != null) {
clearTimeout(saveTimer);
saveTimer = null;
}
if (!isAppSettingsCacheLoaded() || !pendingPatch) return;
const toSave = resolveUiPreferences(
deepMerge(getUiPreferences() as Record<string, unknown>, pendingPatch as Record<string, unknown>),
);
pendingPatch = null;
void saveAppSettings({ uiPreferences: toSave }).catch(err => {
console.warn('[uiPreferences] DB flush 저장 실패:', err);
});
}
export function subscribeUiPreferences(listener: () => void): () => void {
return subscribeAppSettings(listener);
}
/** 앱 설정 DB 로드 직후 — 로드 전에 큐잉된 patch 병합 */
export function reconcilePendingUiPreferencesOnLoad(): void {
if (!isAppSettingsCacheLoaded() || !pendingPatch) return;
const merged = resolveUiPreferences(
deepMerge(getUiPreferences() as Record<string, unknown>, pendingPatch as Record<string, unknown>),
);
patchAppSettingsCache({ uiPreferences: merged });
pendingPatch = null;
}
/** 중첩 경로 헬퍼 */
export function getUiPref<K extends keyof UiPreferences>(key: K): UiPreferences[K] {
return getUiPreferences()[key];