feat(sync): wire SyncService — tray '지금 동기화' + on-quit drain
- src/main/index.ts: SyncService instantiate with paths.profileDir + exportSvc - 트레이 6번째 콜백 — 토스트로 not_configured / done / unchanged / failed 안내 - before-quit 훅에 sync drain 추가 (backup 완료 후, syncSvc.isConfigured() 인 경우만) - src/main/tray.ts: 6번째 callback runSync, '지금 동기화' 메뉴 (내보내기/복원 다음) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -27,6 +27,7 @@ import { MediaGc } from './services/MediaGc.js';
|
||||
import { BackupService } from './services/BackupService.js';
|
||||
import { ExportService } from './services/ExportService.js';
|
||||
import { ImportService } from './services/ImportService.js';
|
||||
import { SyncService } from './services/SyncService.js';
|
||||
|
||||
const HIDDEN_ARG = '--hidden';
|
||||
const startedHidden = process.argv.includes(HIDDEN_ARG);
|
||||
@@ -106,6 +107,7 @@ app.whenReady().then(async () => {
|
||||
|
||||
const exportSvc = new ExportService(repo, store);
|
||||
const importSvc = new ImportService(repo, store);
|
||||
const syncSvc = new SyncService(paths.profileDir, exportSvc);
|
||||
|
||||
const backup = new BackupService(db, join(paths.profileDir, 'backups'));
|
||||
void backup.runDaily()
|
||||
@@ -119,6 +121,17 @@ app.whenReady().then(async () => {
|
||||
backup.runDaily()
|
||||
.then((r) => logger.info('backup.beforeQuit', { ...r } as Record<string, unknown>))
|
||||
.catch((e2) => logger.warn('backup.beforeQuit.failed', { reason: String(e2) }))
|
||||
.then(() => syncSvc.isConfigured().then((cfg) => {
|
||||
if (!cfg) return;
|
||||
return syncSvc.sync()
|
||||
.then((r) => logger.info('sync.beforeQuit', {
|
||||
ok: r.ok,
|
||||
changed: r.changed ?? false,
|
||||
pushed: r.pushed ?? false,
|
||||
reason: r.reason
|
||||
} as Record<string, unknown>))
|
||||
.catch((e3) => logger.warn('sync.beforeQuit.failed', { reason: String(e3) }));
|
||||
}))
|
||||
.finally(() => {
|
||||
backupOnQuitDone = true;
|
||||
app.isQuitting = true;
|
||||
@@ -243,6 +256,29 @@ app.whenReady().then(async () => {
|
||||
silent: true
|
||||
}).show();
|
||||
}
|
||||
},
|
||||
async () => {
|
||||
// runSync — 트레이 "지금 동기화"
|
||||
try {
|
||||
const r = await syncSvc.sync();
|
||||
if (!r.ok) {
|
||||
logger.warn('sync.failed', { reason: r.reason });
|
||||
const body = r.reason === 'not_configured'
|
||||
? `${syncSvc.getSyncDir()} 에서 git init + remote 설정이 필요합니다.`
|
||||
: '동기화를 완료하지 못했습니다.';
|
||||
new Notification({ title: 'Inkling', body, silent: true }).show();
|
||||
return;
|
||||
}
|
||||
if (r.changed) {
|
||||
logger.info('sync.done', { sha: r.sha, pushed: r.pushed });
|
||||
new Notification({ title: 'Inkling', body: '동기화 완료', silent: true }).show();
|
||||
} else {
|
||||
new Notification({ title: 'Inkling', body: '변경 사항 없음', silent: true }).show();
|
||||
}
|
||||
} catch (e) {
|
||||
logger.warn('sync.exception', { reason: String(e) });
|
||||
new Notification({ title: 'Inkling', body: '동기화를 완료하지 못했습니다.', silent: true }).show();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -9,7 +9,8 @@ function buildMenu(
|
||||
showCapture: () => void,
|
||||
runBackup: () => void,
|
||||
runExport: () => void,
|
||||
runImport: () => void
|
||||
runImport: () => void,
|
||||
runSync: () => void
|
||||
) {
|
||||
const items: MenuItemConstructorOptions[] = [
|
||||
{ label: '보관한 메모 보기', click: showInbox },
|
||||
@@ -17,7 +18,8 @@ function buildMenu(
|
||||
{ type: 'separator' },
|
||||
{ label: '지금 백업', click: runBackup },
|
||||
{ label: '내보내기...', click: runExport },
|
||||
{ label: '백업에서 복원...', click: runImport }
|
||||
{ label: '백업에서 복원...', click: runImport },
|
||||
{ label: '지금 동기화', click: runSync }
|
||||
];
|
||||
if (app.isPackaged) {
|
||||
const { openAtLogin } = app.getLoginItemSettings();
|
||||
@@ -45,12 +47,13 @@ export function createTray(
|
||||
showCapture: () => void,
|
||||
runBackup: () => void,
|
||||
runExport: () => void,
|
||||
runImport: () => void
|
||||
runImport: () => void,
|
||||
runSync: () => void
|
||||
): TrayType {
|
||||
const icon = nativeImage.createEmpty();
|
||||
tray = new Tray(icon);
|
||||
tray.setToolTip('Inkling');
|
||||
tray.setContextMenu(buildMenu(showInbox, showCapture, runBackup, runExport, runImport));
|
||||
tray.setContextMenu(buildMenu(showInbox, showCapture, runBackup, runExport, runImport, runSync));
|
||||
tray.on('click', showInbox);
|
||||
return tray;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user