commit 41320e5f18cfac09a37dbcb553eefab57262d573 truex <truex@equwal.com> 2026-09-26 19:22:52 -0700 Say a player problem once in the strip On the phone, play without notification access gave the same sentence two times: in the player part and in the event part. A PlayerError for the problem of the last player state now shows only in the player part while a book is open (shownInPlayerPart). Before a book is open the player part is hidden, so the event part still says it.
src/main.ts | 8 +++++++- src/messages.ts | 9 +++++++++ test/messages.test.ts | 18 ++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-)
diff --git a/src/main.ts b/src/main.ts index f3ab66a..f5090d3 100644 --- a/src/main.ts +++ b/src/main.ts @@ -36,6 +36,7 @@ import { playerMessage, readingEndMessage, readingText, + shownInPlayerPart, SUBREAD_NOT_INSTALLED, subreadErrorText, subreadQuestion, @@ -281,8 +282,13 @@ function setForceOcr(on: boolean): void { showReadingPart(); } -/** Shows a command that failed in the strip. */ +/** + * Shows a command that failed in the strip. The player part shows the + * problem of the player while a book is open, so the event part does not + * say it again. + */ function sayError(err: unknown): void { + if (state.pdf && shownInPlayerPart(err, state.clock?.error)) return; say(errorMessage(err, isAndroid, overlayInstalled())); } diff --git a/src/messages.ts b/src/messages.ts index b745df4..71010ab 100644 --- a/src/messages.ts +++ b/src/messages.ts @@ -103,6 +103,15 @@ export function errorMessage(err: unknown, android: boolean, overlayInstalled = return { text: String(err) }; } +/** + * True when `err` is a PlayerError for the problem of the last player state + * (`playerError`). The player part of the strip shows that problem, so the + * event part does not say it again. + */ +export function shownInPlayerPart(err: unknown, playerError: string | null | undefined): boolean { + return err instanceof PlayerError && err.reason === playerError; +} + /** The player part of the strip: the time and the play state, or the problem. */ export function playerMessage( s: ClockState | null, diff --git a/test/messages.test.ts b/test/messages.test.ts index 98d160b..0a9e440 100644 --- a/test/messages.test.ts +++ b/test/messages.test.ts @@ -22,6 +22,7 @@ import { playerProblem, readingEndMessage, readingText, + shownInPlayerPart, subreadErrorText, subreadQuestion, subreadResultText, @@ -80,6 +81,23 @@ describe('playerProblem', () => { }); }); +describe('shownInPlayerPart', () => { + it('does not repeat the problem that the player part shows', () => { + // The phone finding: after play, the strip said the same sentence twice. + expect( + shownInPlayerPart(new PlayerError('no_notification_access'), 'no_notification_access'), + ).toBe(true); + expect(shownInPlayerPart(new PlayerError('no_player'), 'no_player')).toBe(true); + }); + + it('tells another error, or a problem that the player part does not show', () => { + expect(shownInPlayerPart(new PlayerError('no_player'), null)).toBe(false); + expect(shownInPlayerPart(new PlayerError('no_player'), undefined)).toBe(false); + expect(shownInPlayerPart(new PlayerError('no_overlay'), 'no_player')).toBe(false); + expect(shownInPlayerPart(new Error('no_player'), 'no_player')).toBe(false); + }); +}); + describe('playerCommandsOff', () => { it('turns off play and "audio to page" without notification access', () => { // The phone finding: with no notification access, play stayed on.