fix(settings): sidebar_visible/width 영속화 — IPC + store hydration 추가
final code review 의 Important issue 대응. SettingsService 의 setSidebarVisible/ setSidebarWidth getter/setter 는 이미 있었지만 IPC handler + store hydration missing 으로 매 launch 시 사이드바 닫힌 상태로 시작하던 회귀. - settings:set-sidebar-visible / set-sidebar-width IPC 핸들러 추가 - InboxApi.getSettings 응답에 sidebar_visible/sidebar_width 포함 - preload 의 setSidebarVisible/setSidebarWidth invoke 노출 - store.loadInitial 가 settings.sidebar_visible/sidebar_width 로 hydrate - store.toggleSidebar 가 IPC 호출하여 영속화 - test mock 에 setSidebarVisible/setSidebarWidth 추가 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -112,6 +112,17 @@ export function registerSettingsApi(deps?: SettingsIpcDeps): void {
|
||||
return { ok: true as const };
|
||||
});
|
||||
|
||||
// v0.4 — Sidebar UI state 영속화
|
||||
ipcMain.handle('settings:set-sidebar-visible', async (_e, visible: boolean) => {
|
||||
await settings.setSidebarVisible(visible);
|
||||
return { ok: true as const };
|
||||
});
|
||||
|
||||
ipcMain.handle('settings:set-sidebar-width', async (_e, width: number) => {
|
||||
await settings.setSidebarWidth(width);
|
||||
return { ok: true as const };
|
||||
});
|
||||
|
||||
ipcMain.handle('settings:set-sync-auto-enabled', async (_e, value: boolean) => {
|
||||
await deps.settings.setAutoSyncEnabled(value);
|
||||
await deps.syncTimer?.reconfigure();
|
||||
|
||||
@@ -81,6 +81,9 @@ const api: InklingApi = {
|
||||
getSettings: () => ipcRenderer.invoke('settings:get'),
|
||||
setAiEnabled: (enabled: boolean) => ipcRenderer.invoke('settings:set-ai-enabled', enabled),
|
||||
setOnboardingCompleted: (completed: boolean) => ipcRenderer.invoke('settings:set-onboarding-completed', completed),
|
||||
// v0.4 — Sidebar UI state 영속화
|
||||
setSidebarVisible: (visible: boolean) => ipcRenderer.invoke('settings:set-sidebar-visible', visible),
|
||||
setSidebarWidth: (width: number) => ipcRenderer.invoke('settings:set-sidebar-width', width),
|
||||
// v0.2.9 Cut B Task 16 — disabled 메모 재투입 + count.
|
||||
enqueueDisabled: () => ipcRenderer.invoke('inbox:enqueue-disabled'),
|
||||
getDisabledCount: () => ipcRenderer.invoke('inbox:get-disabled-count'),
|
||||
|
||||
@@ -152,7 +152,14 @@ export const useInbox = create<InboxState>((set, get) => ({
|
||||
inboxApi.countsByStatus({ notebookId }),
|
||||
inboxApi.getSettings()
|
||||
]);
|
||||
set({ notes, continuity, pendingCount, ollamaStatus, todayCount, trashCount, expiredCandidates, failedCount, recallCandidate, counts, ai_enabled: settings.ai_enabled ?? true, loading: false });
|
||||
set({
|
||||
notes, continuity, pendingCount, ollamaStatus, todayCount, trashCount, expiredCandidates,
|
||||
failedCount, recallCandidate, counts,
|
||||
ai_enabled: settings.ai_enabled ?? true,
|
||||
sidebarVisible: settings.sidebar_visible ?? false,
|
||||
sidebarWidth: settings.sidebar_width ?? 240,
|
||||
loading: false
|
||||
});
|
||||
} catch (e) {
|
||||
// 첫 launch 의 IPC 실패 (DB migration 실패 / main process 비정상) 시 무한 loading 회피.
|
||||
// 빈 데이터로 진입하면 사용자가 캡처 시도 → 실제 fail 이 표면화 → 재시도 가능.
|
||||
@@ -480,7 +487,9 @@ export const useInbox = create<InboxState>((set, get) => ({
|
||||
await get().refreshMeta();
|
||||
},
|
||||
toggleSidebar() {
|
||||
set({ sidebarVisible: !get().sidebarVisible });
|
||||
const next = !get().sidebarVisible;
|
||||
set({ sidebarVisible: next });
|
||||
void inboxApi.setSidebarVisible(next);
|
||||
},
|
||||
// v0.4 Task 11 — promotion candidate actions.
|
||||
async loadPromotionCandidates() {
|
||||
|
||||
@@ -228,8 +228,13 @@ export interface InboxApi {
|
||||
vision_model?: string | null;
|
||||
vision_capable_cache?: string[];
|
||||
vision_cache_at?: string;
|
||||
// v0.4 — Sidebar UI state 영속화
|
||||
sidebar_visible?: boolean;
|
||||
sidebar_width?: number;
|
||||
}>;
|
||||
setAiEnabled(enabled: boolean): Promise<{ ok: true }>;
|
||||
setSidebarVisible(visible: boolean): Promise<{ ok: true }>;
|
||||
setSidebarWidth(width: number): Promise<{ ok: true }>;
|
||||
setOnboardingCompleted(completed: boolean): Promise<{ ok: true }>;
|
||||
// v0.2.9 Cut B Task 16 — ai_status='disabled' 메모 재투입 (사용자가 ai_enabled OFF→ON 전환 시).
|
||||
enqueueDisabled(): Promise<{ count: number }>;
|
||||
|
||||
@@ -56,6 +56,8 @@ vi.mock('../../src/renderer/inbox/api.js', () => ({
|
||||
// v0.2.9 Cut B Task 12 — onboarding wizard 분기. default 는 onboarding_completed=true 라 wizard 미표시.
|
||||
getSettings: vi.fn(async () => ({ onboarding_completed: true })),
|
||||
setAiEnabled: vi.fn(async () => ({ ok: true as const })),
|
||||
setSidebarVisible: vi.fn(async () => ({ ok: true as const })),
|
||||
setSidebarWidth: vi.fn(async () => ({ ok: true as const })),
|
||||
setOnboardingCompleted: vi.fn(async () => ({ ok: true as const })),
|
||||
// v0.2.9 Cut B Task 16 — AiProviderSection 가 SettingsPage 렌더 시 호출.
|
||||
getDisabledCount: vi.fn(async () => 0),
|
||||
@@ -174,11 +176,12 @@ describe('App header — 3 tabs (v0.4)', () => {
|
||||
});
|
||||
|
||||
it('Sidebar 컴포넌트가 렌더 트리에 포함됨 (sidebarVisible=true)', async () => {
|
||||
useInbox.setState({ sidebarVisible: true, notebooks: [] });
|
||||
// loadInitial 의 getSettings 가 sidebar_visible=true 반환 (Strict Mode 중복 호출 대비 mockResolvedValue).
|
||||
vi.mocked(inboxApi.getSettings).mockResolvedValue({ onboarding_completed: true, sidebar_visible: true });
|
||||
render(<App />);
|
||||
await screen.findByRole('tab', { name: /Inbox/ });
|
||||
// Sidebar renders an <aside> element when visible
|
||||
expect(document.querySelector('aside')).not.toBeNull();
|
||||
// loadInitial 비동기 hydrate 가 완료될 때까지 기다림
|
||||
await waitFor(() => expect(document.querySelector('aside')).not.toBeNull());
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -19,7 +19,9 @@ vi.mock('../../src/renderer/inbox/api.js', () => ({
|
||||
listRecallCandidate: vi.fn(async () => null),
|
||||
countsByStatus: mockCountsByStatus,
|
||||
getSettings: vi.fn(async () => ({ ai_enabled: true })),
|
||||
listByStatus: mockListByStatus
|
||||
listByStatus: mockListByStatus,
|
||||
setSidebarVisible: vi.fn(async () => ({ ok: true as const })),
|
||||
setSidebarWidth: vi.fn(async () => ({ ok: true as const }))
|
||||
},
|
||||
notebookApi: {
|
||||
list: vi.fn(async () => [
|
||||
|
||||
Reference in New Issue
Block a user