Skip to content

Commit fcec536

Browse files
authored
Merge pull request #2907 from Brain-up/fix-audio-idle-during-listen
Fix idle pause during LISTEN audio gaps (followup to #2893)
2 parents 6322d5a + 22936d4 commit fcec536

3 files changed

Lines changed: 58 additions & 2 deletions

File tree

‎frontend/app/services/audio.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import type { Signal as SignalModel } from 'brn/schemas/signal';
3434
import Intl from 'ember-intl/services/intl';
3535
import { PolySynth, Synth, SynthOptions } from 'tone';
3636
import UserDataService from './user-data';
37+
import StudyingTimerService from './studying-timer';
3738
import type { Exercise } from 'brn/schemas/exercise';
3839

3940
type ISourceCollection = (ISource | IToneSource | null)[];
@@ -52,6 +53,7 @@ export default class AudioService extends Service {
5253
@service('stats') declare stats: StatsService;
5354
@service('intl') declare intl: Intl;
5455
@service('user-data') declare userData: UserDataService;
56+
@service('studying-timer') declare studyingTimer: StudyingTimerService;
5557
context!: AudioContext;
5658

5759
willDestroy(): void {
@@ -218,6 +220,7 @@ export default class AudioService extends Service {
218220

219221
@action
220222
async playAudio() {
223+
this.studyingTimer.resetIdle();
221224
try {
222225
if (!isTesting()) {
223226
await this.playTask.perform();

‎frontend/app/services/studying-timer.ts‎

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,28 @@ export default class StudyingTimerService extends Service {
6666
}
6767
@action
6868
maybeIdlePause() {
69-
// Pause cascades into audio.stop() via task-player.onPauseStateChanged,
70-
// which would interrupt exercises whenever the user stops moving the mouse.
69+
// resetIdle() (invoked on every playAudio) is the primary mechanism that
70+
// keeps the watcher from firing mid-sequence. This guard is a defensive
71+
// backstop in case a single clip ever outruns the idle window: pause
72+
// cascades into audio.stop() via task-player.onPauseStateChanged, which
73+
// would interrupt exercises whenever the user stops moving the mouse.
7174
if (this.audio.isPlaying) {
7275
return;
7376
}
7477
this.pause();
7578
}
7679
@action
80+
resetIdle() {
81+
if (this.idleWatcher) {
82+
try {
83+
this.idleWatcher.stop();
84+
this.idleWatcher.start();
85+
} catch (_e) {
86+
// idle-js may not support stop/start cycle in some edge cases
87+
}
88+
}
89+
}
90+
@action
7791
async startIdleWatcher() {
7892
if (isTesting()) {
7993
return;

‎frontend/tests/unit/services/studying-timer-test.js‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,43 @@ module('Unit | Service | studying-timer', function (hooks) {
3636
assert.true(timer.isPaused);
3737
});
3838
});
39+
40+
module('resetIdle', function () {
41+
test('rearms the idle watcher via stop/start', function (assert) {
42+
const timer = this.owner.lookup('service:studying-timer');
43+
let stopCount = 0;
44+
let startCount = 0;
45+
timer.idleWatcher = {
46+
stop() {
47+
stopCount++;
48+
},
49+
start() {
50+
startCount++;
51+
},
52+
};
53+
timer.resetIdle();
54+
assert.strictEqual(stopCount, 1, 'idleWatcher.stop called once');
55+
assert.strictEqual(startCount, 1, 'idleWatcher.start called once');
56+
});
57+
58+
test('does not toggle isPaused', function (assert) {
59+
// Regression guard: resetIdle must not clear a user-initiated pause.
60+
const timer = this.owner.lookup('service:studying-timer');
61+
timer.idleWatcher = { stop() {}, start() {} };
62+
timer.pause();
63+
timer.resetIdle();
64+
assert.true(timer.isPaused, 'user pause remains sticky after resetIdle');
65+
});
66+
67+
test('is a no-op when idleWatcher is null', function (assert) {
68+
const timer = this.owner.lookup('service:studying-timer');
69+
timer.idleWatcher = null;
70+
try {
71+
timer.resetIdle();
72+
assert.ok(true, 'did not throw');
73+
} catch (_e) {
74+
assert.ok(false, 'resetIdle threw with null idleWatcher');
75+
}
76+
});
77+
});
3978
});

0 commit comments

Comments
 (0)