mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-09-11 17:16:18 -07:00
fix: handle notification changes and timing review text safely
- Dismiss stale overlay progress when notification mode changes - Validate and forward combined timing review text - Keep macOS timing preview socket paths within system limits
This commit is contained in:
@@ -1262,6 +1262,41 @@ test('AnkiIntegration dismisses persistent overlay update progress when no termi
|
||||
assert.deepEqual(dismissedIds, ['anki-update-progress']);
|
||||
});
|
||||
|
||||
test('AnkiIntegration dismisses overlay update progress after notifications switch to OSD', () => {
|
||||
const behavior: NonNullable<AnkiConnectConfig['behavior']> = {
|
||||
notificationType: 'overlay',
|
||||
};
|
||||
const dismissedIds: string[] = [];
|
||||
const integration = new AnkiIntegration(
|
||||
{ behavior },
|
||||
{} as never,
|
||||
{} as never,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
{},
|
||||
undefined,
|
||||
() => {},
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
(id) => {
|
||||
dismissedIds.push(id);
|
||||
},
|
||||
);
|
||||
const updateNotifications = integration as unknown as {
|
||||
beginUpdateProgress: (message: string) => void;
|
||||
endUpdateProgress: () => void;
|
||||
};
|
||||
|
||||
updateNotifications.beginUpdateProgress('Updating card');
|
||||
behavior.notificationType = 'osd';
|
||||
updateNotifications.endUpdateProgress();
|
||||
|
||||
assert.deepEqual(dismissedIds, ['anki-update-progress']);
|
||||
});
|
||||
|
||||
test('AnkiIntegration keeps overlay notification image when temp icon write fails', async () => {
|
||||
const desktopNotifications: Array<{ title: string; body?: string; icon?: string }> = [];
|
||||
const overlayNotifications: TestOverlayNotificationPayload[] = [];
|
||||
|
||||
@@ -1237,11 +1237,11 @@ export class AnkiIntegration {
|
||||
}
|
||||
|
||||
private endUpdateProgress(): void {
|
||||
if (this.overlayUpdateProgressActive) {
|
||||
this.overlayUpdateProgressActive = false;
|
||||
this.overlayNotificationDismissCallback?.('anki-update-progress');
|
||||
}
|
||||
if (!this.shouldUseOsdNotifications()) {
|
||||
if (this.overlayUpdateProgressActive) {
|
||||
this.overlayUpdateProgressActive = false;
|
||||
this.overlayNotificationDismissCallback?.('anki-update-progress');
|
||||
}
|
||||
return;
|
||||
}
|
||||
endUpdateProgress(this.uiFeedbackState, (timer) => {
|
||||
|
||||
@@ -670,6 +670,61 @@ test('registerIpcHandlers accepts the keep-without-media timing decision', async
|
||||
assert.deepEqual(requests, [{ reviewId: 'review-1', decision: { action: 'skip-media' } }]);
|
||||
});
|
||||
|
||||
test('registerIpcHandlers validates and forwards combined timing review text', async () => {
|
||||
const { registrar, handlers } = createFakeIpcRegistrar();
|
||||
const requests: unknown[] = [];
|
||||
registerIpcHandlers(
|
||||
createRegisterIpcDeps({
|
||||
resolveMediaTimingReview: async (request) => {
|
||||
requests.push(request);
|
||||
return { ok: true };
|
||||
},
|
||||
}),
|
||||
registrar,
|
||||
);
|
||||
|
||||
const handler = handlers.handle.get(IPC_CHANNELS.request.mediaTimingReviewResolve);
|
||||
assert.ok(handler);
|
||||
assert.deepEqual(
|
||||
await handler!(
|
||||
{},
|
||||
{
|
||||
reviewId: 'review-1',
|
||||
decision: {
|
||||
action: 'confirm',
|
||||
startTime: 10,
|
||||
endTime: 12,
|
||||
text: '前の行 対象の行',
|
||||
},
|
||||
},
|
||||
),
|
||||
{ ok: true },
|
||||
);
|
||||
assert.deepEqual(requests, [
|
||||
{
|
||||
reviewId: 'review-1',
|
||||
decision: {
|
||||
action: 'confirm',
|
||||
startTime: 10,
|
||||
endTime: 12,
|
||||
text: '前の行 対象の行',
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
assert.deepEqual(
|
||||
await handler!(
|
||||
{},
|
||||
{
|
||||
reviewId: 'review-1',
|
||||
decision: { action: 'confirm', startTime: 10, endTime: 12, text: ' ' },
|
||||
},
|
||||
),
|
||||
{ ok: false, message: 'Timing review is unavailable.' },
|
||||
);
|
||||
assert.equal(requests.length, 1);
|
||||
});
|
||||
|
||||
test('registerIpcHandlers forwards yomitan lookup tracking commands to immersion tracker', () => {
|
||||
const { registrar, handlers } = createFakeIpcRegistrar();
|
||||
const calls: string[] = [];
|
||||
|
||||
@@ -288,7 +288,9 @@ function parseMediaTimingReviewResolveRequest(
|
||||
typeof decisionRecord.startTime === 'number' &&
|
||||
Number.isFinite(decisionRecord.startTime) &&
|
||||
typeof decisionRecord.endTime === 'number' &&
|
||||
Number.isFinite(decisionRecord.endTime)
|
||||
Number.isFinite(decisionRecord.endTime) &&
|
||||
(decisionRecord.text === undefined ||
|
||||
(typeof decisionRecord.text === 'string' && decisionRecord.text.trim().length > 0))
|
||||
) {
|
||||
return {
|
||||
reviewId: record.reviewId,
|
||||
@@ -296,6 +298,7 @@ function parseMediaTimingReviewResolveRequest(
|
||||
action: 'confirm',
|
||||
startTime: decisionRecord.startTime,
|
||||
endTime: decisionRecord.endTime,
|
||||
...(decisionRecord.text === undefined ? {} : { text: decisionRecord.text }),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -59,7 +59,11 @@ function createDefaultSocketPath(): string {
|
||||
const suffix = `${process.pid}-${randomUUID()}`;
|
||||
return process.platform === 'win32'
|
||||
? `\\\\.\\pipe\\subminer-timing-preview-${suffix}`
|
||||
: path.join(os.tmpdir(), `subminer-timing-preview-${suffix}.sock`);
|
||||
: path.join(
|
||||
// macOS limits Unix socket paths to 104 bytes, while its temp directory can be long.
|
||||
process.platform === 'darwin' ? '/tmp' : os.tmpdir(),
|
||||
`subminer-timing-preview-${suffix}.sock`,
|
||||
);
|
||||
}
|
||||
|
||||
function removePosixSocketFile(socketPath: string): void {
|
||||
|
||||
Reference in New Issue
Block a user