From dd74aec884c180204b6b7b8e358f563e2f70136b Mon Sep 17 00:00:00 2001 From: altair823 Date: Fri, 1 May 2026 21:32:22 +0900 Subject: [PATCH] feat(trash): IPC 5 channels + native dialog confirm + InboxApi extension (#4 v0.2.3) --- src/main/ipc/inboxApi.ts | 56 +++++++++++++++++++++++++++++++++++++++- src/preload/index.ts | 6 +++++ src/shared/types.ts | 6 +++++ 3 files changed, 67 insertions(+), 1 deletion(-) diff --git a/src/main/ipc/inboxApi.ts b/src/main/ipc/inboxApi.ts index af5aa92..85e9c4c 100644 --- a/src/main/ipc/inboxApi.ts +++ b/src/main/ipc/inboxApi.ts @@ -1,6 +1,6 @@ import electron from 'electron'; import type { BrowserWindow } from 'electron'; -const { ipcMain } = electron; +const { ipcMain, dialog } = electron; import type { NoteRepository } from '../repository/NoteRepository.js'; import type { ContinuityService } from '../services/ContinuityService.js'; import type { CaptureService } from '../services/CaptureService.js'; @@ -52,6 +52,60 @@ export function registerInboxApi(deps: InboxIpcDeps): void { ipcMain.handle('inbox:pendingCount', () => deps.repo.getPendingCount()); ipcMain.handle('inbox:ollamaStatus', () => deps.health.lastStatus()); ipcMain.handle('inbox:todayCount', () => deps.repo.countToday()); + + ipcMain.handle('inbox:restore', async (_e, noteId: string) => { + await deps.capture.restoreNote(noteId); + }); + + ipcMain.handle('inbox:permanentDelete', async (_e, noteId: string) => { + const win = deps.getInboxWindow(); + const opts: Electron.MessageBoxOptions = { + type: 'question', + buttons: ['영구 삭제', '취소'], + defaultId: 1, + cancelId: 1, + title: 'Inkling', + message: '이 노트를 영구 삭제합니다', + detail: '이 작업은 되돌릴 수 없습니다. 첨부된 이미지도 함께 삭제됩니다.' + }; + const r = win + ? await dialog.showMessageBox(win, opts) + : await dialog.showMessageBox(opts); + if (r.response !== 0) return { confirmed: false }; + await deps.capture.permanentDeleteNote(noteId); + return { confirmed: true }; + }); + + ipcMain.handle('inbox:emptyTrash', async () => { + const trashed = deps.repo.listTrashed({ limit: 1 }); + if (trashed.length === 0) return { confirmed: true, count: 0 }; + // 정확한 카운트 위해 limit 200 으로 다시 조회 + const fullCount = deps.repo.listTrashed({ limit: 200 }).length; + const win = deps.getInboxWindow(); + const opts: Electron.MessageBoxOptions = { + type: 'question', + buttons: ['휴지통 비우기', '취소'], + defaultId: 1, + cancelId: 1, + title: 'Inkling', + message: `휴지통의 노트 ${fullCount}개를 영구 삭제합니다`, + detail: '이 작업은 되돌릴 수 없습니다. 첨부된 이미지도 함께 삭제됩니다.' + }; + const r = win + ? await dialog.showMessageBox(win, opts) + : await dialog.showMessageBox(opts); + if (r.response !== 0) return { confirmed: false, count: 0 }; + const result = await deps.capture.emptyTrash(); + return { confirmed: true, count: result.count }; + }); + + ipcMain.handle('inbox:listTrash', (_e, opts: { limit: number }) => + deps.repo.listTrashed(opts) + ); + + ipcMain.handle('inbox:trashCount', () => + deps.repo.listTrashed({ limit: 200 }).length + ); } export function pushNoteUpdated(getWin: () => BrowserWindow | null, note: Note): void { diff --git a/src/preload/index.ts b/src/preload/index.ts index 7d80085..7111851 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -19,6 +19,12 @@ const api: InklingApi = { getPendingCount: () => ipcRenderer.invoke('inbox:pendingCount'), getOllamaStatus: () => ipcRenderer.invoke('inbox:ollamaStatus'), getTodayCount: () => ipcRenderer.invoke('inbox:todayCount'), + // 신규 v0.2.3 #4: + restoreNote: (noteId) => ipcRenderer.invoke('inbox:restore', noteId), + permanentDeleteNote: (noteId) => ipcRenderer.invoke('inbox:permanentDelete', noteId), + emptyTrash: () => ipcRenderer.invoke('inbox:emptyTrash'), + listTrash: (opts) => ipcRenderer.invoke('inbox:listTrash', opts), + getTrashCount: () => ipcRenderer.invoke('inbox:trashCount'), onNoteUpdated: (cb) => { const listener = (_e: unknown, note: Note) => cb(note); ipcRenderer.on('note:updated', listener); diff --git a/src/shared/types.ts b/src/shared/types.ts index b968901..e0b0179 100644 --- a/src/shared/types.ts +++ b/src/shared/types.ts @@ -71,6 +71,12 @@ export interface InboxApi { getPendingCount(): Promise; getOllamaStatus(): Promise<{ ok: boolean; reason?: string }>; getTodayCount(): Promise; + // 신규 v0.2.3 #4: + restoreNote(noteId: string): Promise; + permanentDeleteNote(noteId: string): Promise<{ confirmed: boolean }>; + emptyTrash(): Promise<{ confirmed: boolean; count: number }>; + listTrash(opts: { limit: number }): Promise; + getTrashCount(): Promise; onNoteUpdated(cb: (note: Note) => void): () => void; }