Files
bilin/src/main/ipc/handlers/pack.handler.ts
T
bing a4412793f4 feat(w2): add PackService for .novel and .novel-all export/import
Introduce pack-bootstrap chunk, ZipArchive-based PackService with progress IPC,
book registry import helpers, and IT-PACK-01 / IT-ALL-02 integration tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-08 15:36:47 +08:00

82 lines
2.7 KiB
TypeScript

import { dialog, ipcMain } from 'electron'
import { IPC } from '../../../shared/ipc-channels'
import type { PackImportStrategy } from '../../../shared/novel-pack'
import { PackService } from '../../services/pack.service'
import type { BookRegistryService } from '../../services/book-registry'
import type { GlobalSettingsService } from '../../services/global-settings'
import { wrap } from '../result'
let packService: PackService | null = null
function svc(registry: BookRegistryService, settings: GlobalSettingsService): PackService {
if (!packService) {
packService = new PackService(registry, settings, registry.getUserDataDir())
}
return packService
}
export function registerPackHandlers(
registry: BookRegistryService,
settings: GlobalSettingsService
): void {
const service = () => svc(registry, settings)
ipcMain.handle(
IPC.PACK_EXPORT_BOOK,
(_e, { bookId, destPath }: { bookId: string; destPath: string }) =>
wrap(() => service().exportBook(bookId, destPath))
)
ipcMain.handle(IPC.PACK_IMPORT_BOOK, (_e, { filePath }: { filePath: string }) =>
wrap(() => service().importBook(filePath))
)
ipcMain.handle(IPC.PACK_EXPORT_ALL, (_e, { destPath }: { destPath: string }) =>
wrap(() => service().exportAll(destPath))
)
ipcMain.handle(
IPC.PACK_IMPORT_ALL,
(_e, { filePath, strategy }: { filePath: string; strategy: PackImportStrategy }) =>
wrap(() => service().importAll(filePath, strategy))
)
ipcMain.handle(IPC.PACK_ESTIMATE_EXPORT_ALL, () =>
wrap(() => ({
bytes: service().estimateExportAllBytes(),
needsConfirm: service().needsExportAllConfirm()
}))
)
ipcMain.handle(IPC.PACK_NEEDS_CONFIRM, () =>
wrap(() => service().needsExportAllConfirm())
)
ipcMain.handle(IPC.PACK_CANCEL, () =>
wrap(() => {
service().cancel()
})
)
ipcMain.handle(IPC.PACK_PICK_FILE, (_e, { mode }: { mode: 'novel' | 'novel-all' | 'save-novel' | 'save-all' }) =>
wrap(async () => {
if (mode === 'save-novel' || mode === 'save-all') {
const ext = mode === 'save-all' ? 'novel-all' : 'novel'
const { canceled, filePath } = await dialog.showSaveDialog({
filters: [{ name: 'Bilin Pack', extensions: [ext] }],
defaultPath: mode === 'save-all' ? 'bilin-backup.novel-all' : 'book.novel'
})
if (canceled || !filePath) return null
return filePath
}
const extensions = mode === 'novel-all' ? ['novel-all'] : ['novel', 'novel-all']
const { canceled, filePaths } = await dialog.showOpenDialog({
properties: ['openFile'],
filters: [{ name: 'Bilin Pack', extensions }]
})
if (canceled || filePaths.length === 0) return null
return filePaths[0]
})
)
}