mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-03-21 00:11:27 -07:00
feat: add mark-as-watched keybinding and Yomitan lookup tracking
Add configurable keybinding to mark the current video as watched with IPC plumbing between renderer and main process. Add event listener infrastructure for tracking Yomitan dictionary lookups per session.
This commit is contained in:
@@ -98,6 +98,7 @@ function createRegisterIpcDeps(overrides: Partial<IpcServiceDeps> = {}): IpcServ
|
||||
getKeybindings: () => [],
|
||||
getConfiguredShortcuts: () => ({}),
|
||||
getStatsToggleKey: () => 'Backquote',
|
||||
getMarkWatchedKey: () => 'KeyW',
|
||||
getControllerConfig: () => createControllerConfigFixture(),
|
||||
saveControllerConfig: async () => {},
|
||||
saveControllerPreference: async () => {},
|
||||
@@ -121,6 +122,39 @@ function createRegisterIpcDeps(overrides: Partial<IpcServiceDeps> = {}): IpcServ
|
||||
};
|
||||
}
|
||||
|
||||
function createFakeImmersionTracker(
|
||||
overrides: Partial<NonNullable<IpcServiceDeps['immersionTracker']>> = {},
|
||||
): NonNullable<IpcServiceDeps['immersionTracker']> {
|
||||
return {
|
||||
recordYomitanLookup: () => {},
|
||||
getSessionSummaries: async () => [],
|
||||
getDailyRollups: async () => [],
|
||||
getMonthlyRollups: async () => [],
|
||||
getQueryHints: async () => ({
|
||||
totalSessions: 0,
|
||||
activeSessions: 0,
|
||||
episodesToday: 0,
|
||||
activeAnimeCount: 0,
|
||||
totalActiveMin: 0,
|
||||
totalCards: 0,
|
||||
activeDays: 0,
|
||||
totalEpisodesWatched: 0,
|
||||
totalAnimeCompleted: 0,
|
||||
}),
|
||||
getSessionTimeline: async () => [],
|
||||
getSessionEvents: async () => [],
|
||||
getVocabularyStats: async () => [],
|
||||
getKanjiStats: async () => [],
|
||||
getMediaLibrary: async () => [],
|
||||
getMediaDetail: async () => null,
|
||||
getMediaSessions: async () => [],
|
||||
getMediaDailyRollups: async () => [],
|
||||
getCoverArt: async () => null,
|
||||
markActiveVideoWatched: async () => false,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
test('createIpcDepsRuntime wires AniList handlers', async () => {
|
||||
const calls: string[] = [];
|
||||
const deps = createIpcDepsRuntime({
|
||||
@@ -142,6 +176,7 @@ test('createIpcDepsRuntime wires AniList handlers', async () => {
|
||||
getKeybindings: () => [],
|
||||
getConfiguredShortcuts: () => ({}),
|
||||
getStatsToggleKey: () => 'Backquote',
|
||||
getMarkWatchedKey: () => 'KeyW',
|
||||
getControllerConfig: () => createControllerConfigFixture(),
|
||||
saveControllerConfig: () => {},
|
||||
saveControllerPreference: () => {},
|
||||
@@ -210,6 +245,7 @@ test('registerIpcHandlers rejects malformed runtime-option payloads', async () =
|
||||
getKeybindings: () => [],
|
||||
getConfiguredShortcuts: () => ({}),
|
||||
getStatsToggleKey: () => 'Backquote',
|
||||
getMarkWatchedKey: () => 'KeyW',
|
||||
getControllerConfig: () => createControllerConfigFixture(),
|
||||
saveControllerConfig: () => {},
|
||||
saveControllerPreference: () => {},
|
||||
@@ -278,6 +314,28 @@ test('registerIpcHandlers rejects malformed runtime-option payloads', async () =
|
||||
);
|
||||
});
|
||||
|
||||
test('registerIpcHandlers forwards yomitan lookup tracking commands to immersion tracker', () => {
|
||||
const { registrar, handlers } = createFakeIpcRegistrar();
|
||||
const calls: string[] = [];
|
||||
registerIpcHandlers(
|
||||
createRegisterIpcDeps({
|
||||
immersionTracker: createFakeImmersionTracker({
|
||||
recordYomitanLookup: () => {
|
||||
calls.push('lookup');
|
||||
},
|
||||
}),
|
||||
}),
|
||||
registrar,
|
||||
);
|
||||
|
||||
const handler = handlers.on.get(IPC_CHANNELS.command.recordYomitanLookup);
|
||||
assert.equal(typeof handler, 'function');
|
||||
|
||||
handler?.({}, null);
|
||||
|
||||
assert.deepEqual(calls, ['lookup']);
|
||||
});
|
||||
|
||||
test('registerIpcHandlers returns empty stats overview shape without a tracker', async () => {
|
||||
const { registrar, handlers } = createFakeIpcRegistrar();
|
||||
registerIpcHandlers(createRegisterIpcDeps(), registrar);
|
||||
@@ -308,6 +366,7 @@ test('registerIpcHandlers validates and clamps stats request limits', async () =
|
||||
registerIpcHandlers(
|
||||
createRegisterIpcDeps({
|
||||
immersionTracker: {
|
||||
recordYomitanLookup: () => {},
|
||||
getSessionSummaries: async (limit = 0) => {
|
||||
calls.push(['sessions', limit]);
|
||||
return [];
|
||||
@@ -352,6 +411,7 @@ test('registerIpcHandlers validates and clamps stats request limits', async () =
|
||||
getMediaSessions: async () => [],
|
||||
getMediaDailyRollups: async () => [],
|
||||
getCoverArt: async () => null,
|
||||
markActiveVideoWatched: async () => false,
|
||||
},
|
||||
}),
|
||||
registrar,
|
||||
@@ -413,6 +473,7 @@ test('registerIpcHandlers ignores malformed fire-and-forget payloads', () => {
|
||||
getKeybindings: () => [],
|
||||
getConfiguredShortcuts: () => ({}),
|
||||
getStatsToggleKey: () => 'Backquote',
|
||||
getMarkWatchedKey: () => 'KeyW',
|
||||
getControllerConfig: () => createControllerConfigFixture(),
|
||||
saveControllerConfig: () => {},
|
||||
saveControllerPreference: (update) => {
|
||||
@@ -476,6 +537,7 @@ test('registerIpcHandlers awaits saveControllerPreference through request-respon
|
||||
getKeybindings: () => [],
|
||||
getConfiguredShortcuts: () => ({}),
|
||||
getStatsToggleKey: () => 'Backquote',
|
||||
getMarkWatchedKey: () => 'KeyW',
|
||||
getControllerConfig: () => createControllerConfigFixture(),
|
||||
saveControllerConfig: async () => {},
|
||||
saveControllerPreference: async (update) => {
|
||||
@@ -546,6 +608,7 @@ test('registerIpcHandlers rejects malformed controller preference payloads', asy
|
||||
getKeybindings: () => [],
|
||||
getConfiguredShortcuts: () => ({}),
|
||||
getStatsToggleKey: () => 'Backquote',
|
||||
getMarkWatchedKey: () => 'KeyW',
|
||||
getControllerConfig: () => createControllerConfigFixture(),
|
||||
saveControllerConfig: async () => {},
|
||||
saveControllerPreference: async () => {},
|
||||
|
||||
@@ -51,6 +51,7 @@ export interface IpcServiceDeps {
|
||||
getKeybindings: () => unknown;
|
||||
getConfiguredShortcuts: () => unknown;
|
||||
getStatsToggleKey: () => string;
|
||||
getMarkWatchedKey: () => string;
|
||||
getControllerConfig: () => ResolvedControllerConfig;
|
||||
saveControllerConfig: (update: ControllerConfigUpdate) => void | Promise<void>;
|
||||
saveControllerPreference: (update: ControllerPreferenceUpdate) => void | Promise<void>;
|
||||
@@ -70,6 +71,7 @@ export interface IpcServiceDeps {
|
||||
retryAnilistQueueNow: () => Promise<{ ok: boolean; message: string }>;
|
||||
appendClipboardVideoToQueue: () => { ok: boolean; message: string };
|
||||
immersionTracker?: {
|
||||
recordYomitanLookup: () => void;
|
||||
getSessionSummaries: (limit?: number) => Promise<unknown>;
|
||||
getDailyRollups: (limit?: number) => Promise<unknown>;
|
||||
getMonthlyRollups: (limit?: number) => Promise<unknown>;
|
||||
@@ -93,6 +95,7 @@ export interface IpcServiceDeps {
|
||||
getMediaSessions: (videoId: number, limit?: number) => Promise<unknown>;
|
||||
getMediaDailyRollups: (videoId: number, limit?: number) => Promise<unknown>;
|
||||
getCoverArt: (videoId: number) => Promise<unknown>;
|
||||
markActiveVideoWatched: () => Promise<boolean>;
|
||||
} | null;
|
||||
}
|
||||
|
||||
@@ -143,6 +146,7 @@ export interface IpcDepsRuntimeOptions {
|
||||
getKeybindings: () => unknown;
|
||||
getConfiguredShortcuts: () => unknown;
|
||||
getStatsToggleKey: () => string;
|
||||
getMarkWatchedKey: () => string;
|
||||
getControllerConfig: () => ResolvedControllerConfig;
|
||||
saveControllerConfig: (update: ControllerConfigUpdate) => void | Promise<void>;
|
||||
saveControllerPreference: (update: ControllerPreferenceUpdate) => void | Promise<void>;
|
||||
@@ -199,6 +203,7 @@ export function createIpcDepsRuntime(options: IpcDepsRuntimeOptions): IpcService
|
||||
getKeybindings: options.getKeybindings,
|
||||
getConfiguredShortcuts: options.getConfiguredShortcuts,
|
||||
getStatsToggleKey: options.getStatsToggleKey,
|
||||
getMarkWatchedKey: options.getMarkWatchedKey,
|
||||
getControllerConfig: options.getControllerConfig,
|
||||
saveControllerConfig: options.saveControllerConfig,
|
||||
saveControllerPreference: options.saveControllerPreference,
|
||||
@@ -274,6 +279,14 @@ export function registerIpcHandlers(deps: IpcServiceDeps, ipc: IpcMainRegistrar
|
||||
deps.openYomitanSettings();
|
||||
});
|
||||
|
||||
ipc.on(IPC_CHANNELS.command.recordYomitanLookup, () => {
|
||||
deps.immersionTracker?.recordYomitanLookup();
|
||||
});
|
||||
|
||||
ipc.handle(IPC_CHANNELS.command.markActiveVideoWatched, async () => {
|
||||
return (await deps.immersionTracker?.markActiveVideoWatched()) ?? false;
|
||||
});
|
||||
|
||||
ipc.on(IPC_CHANNELS.command.quitApp, () => {
|
||||
deps.quitApp();
|
||||
});
|
||||
@@ -366,6 +379,10 @@ export function registerIpcHandlers(deps: IpcServiceDeps, ipc: IpcMainRegistrar
|
||||
return deps.getStatsToggleKey();
|
||||
});
|
||||
|
||||
ipc.handle(IPC_CHANNELS.request.getMarkWatchedKey, () => {
|
||||
return deps.getMarkWatchedKey();
|
||||
});
|
||||
|
||||
ipc.handle(IPC_CHANNELS.request.getControllerConfig, () => {
|
||||
return deps.getControllerConfig();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user