Files
genshin-assistant/electron/ipc/persistenceHandlers.ts
T

120 lines
3.9 KiB
TypeScript

import { ipcMain } from "electron";
import type {
ArtifactStoreLoadResult,
ArtifactStoreRemoveResult,
ArtifactStoreRepositoryPort,
ArtifactStoreSaveResult,
ReviewSampleListResult,
ReviewSamplePayload,
ReviewSamplesRepositoryPort,
} from "../repositories/contracts.js";
import type {
GoodDatabase,
LoadScannerLearningRulesResult,
SaveScannerLearningRulesResult,
ScannerLearningRulePayload,
SaveResultWithPath,
GoodImportFileResult,
} from "../../src/types/global.js";
import type { StoredArtifactRecord } from "../../src/types/storage.js";
type ArtifactStoreAccessor = () => ArtifactStoreRepositoryPort;
type ReviewSamplesAccessor = () => ReviewSamplesRepositoryPort;
interface PersistenceDependencies {
getArtifactStoreRepository: ArtifactStoreAccessor;
getReviewSamplesRepository: ReviewSamplesAccessor;
artifactStorePath: () => string;
reviewSamplesPath: () => string;
loadReviewSamples: (limit?: number) => Promise<ReviewSampleListResult>;
loadScannerLearningRules: () => Promise<LoadScannerLearningRulesResult>;
writeScannerLearningRules: (rules: ScannerLearningRulePayload) => Promise<SaveScannerLearningRulesResult>;
exportGood: (payload: GoodDatabase) => Promise<SaveResultWithPath>;
importGoodFile: () => Promise<GoodImportFileResult>;
}
export function registerPersistenceHandlers({
getArtifactStoreRepository,
getReviewSamplesRepository,
artifactStorePath,
reviewSamplesPath,
loadReviewSamples,
loadScannerLearningRules,
writeScannerLearningRules,
exportGood,
importGoodFile,
}: PersistenceDependencies) {
ipcMain.handle("review:saveSample", async (_event, sample: ReviewSamplePayload) => {
try {
return await getReviewSamplesRepository().append(sample);
} catch {
const filePath = reviewSamplesPath();
return { ok: false, path: filePath };
}
});
ipcMain.handle("review:loadSamples", async (_event, limit = 50) => {
return loadReviewSamples(Number(limit) || 50);
});
ipcMain.handle("scanner:loadLearningRules", async () => {
return loadScannerLearningRules();
});
ipcMain.handle("scanner:saveLearningRules", async (_event, rules: ScannerLearningRulePayload) => {
return writeScannerLearningRules(rules);
});
ipcMain.handle("artifacts:load", async () => {
try {
return (await getArtifactStoreRepository().loadAll()) as ArtifactStoreLoadResult;
} catch {
return { ok: false, artifacts: [], total: 0, path: artifactStorePath() };
}
});
ipcMain.handle("artifacts:saveMany", async (_event, records: StoredArtifactRecord[]) => {
try {
const safeRecords = Array.isArray(records) ? records : [];
return (await getArtifactStoreRepository().saveMany(safeRecords)) as ArtifactStoreSaveResult;
} catch {
return { ok: false, added: 0, updated: 0, total: 0, path: artifactStorePath() };
}
});
// Intentionally accepts one exact local Store id only. Native result/crop
// tombstones use their separate scanner workflow, and this handler never
// invokes the input helper or sends any action to Genshin.
ipcMain.handle("artifacts:removeOne", async (_event, id: unknown): Promise<ArtifactStoreRemoveResult> => {
const artifactId = typeof id === "string" ? id.trim() : "";
if (!artifactId) {
return {
ok: false,
removed: 0,
total: 0,
path: artifactStorePath(),
error: "A local artifact Store id is required.",
};
}
try {
return (await getArtifactStoreRepository().removeByIds([artifactId])) as ArtifactStoreRemoveResult;
} catch {
return {
ok: false,
removed: 0,
total: 0,
path: artifactStorePath(),
error: "The local artifact Store record could not be removed.",
};
}
});
ipcMain.handle("good:export", async (_event, payload: GoodDatabase) => {
return exportGood(payload);
});
ipcMain.handle("good:importFile", async () => {
return importGoodFile();
});
}