From 455756302804c56e94f5c77104bbd3644a469ea7 Mon Sep 17 00:00:00 2001 From: earino-assistant Date: Thu, 10 Sep 2026 12:11:53 +0000 Subject: [PATCH] fix(sequence): restore Spatial mode on touchcancel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sequence scrubber's changing-position restore only listens for documentMouseUp and touchEnd with zero remaining touches. An Android touchcancel (gesture takeover, notification shade, second finger) delivers neither, so _changingPosition latches true and the graph mode stays Sequence for the life of the viewer — killing spatial navigation arrows and running a full-sequence prefetch on every image change. Merge touchCancel$ into the restore subscription so a touchcancel is treated like a terminal release and restores Spatial mode. --- src/component/sequence/SequenceDOMRenderer.ts | 3 ++- .../sequence/SequenceComponent.test.ts | 21 +++++++++++++++++++ test/helper/TouchServiceMockCreator.ts | 1 + 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/component/sequence/SequenceDOMRenderer.ts b/src/component/sequence/SequenceDOMRenderer.ts index 26508fddf..e004a0782 100644 --- a/src/component/sequence/SequenceDOMRenderer.ts +++ b/src/component/sequence/SequenceDOMRenderer.ts @@ -105,7 +105,8 @@ export class SequenceDOMRenderer { filter( (touchEvent: TouchEvent): boolean => { return touchEvent.touches.length === 0; - }))) + })), + this._container.touchService.touchCancel$) .subscribe( (): void => { if (this._changingSpeed) { diff --git a/test/component/sequence/SequenceComponent.test.ts b/test/component/sequence/SequenceComponent.test.ts index dbcaa590b..92a290849 100644 --- a/test/component/sequence/SequenceComponent.test.ts +++ b/test/component/sequence/SequenceComponent.test.ts @@ -99,6 +99,27 @@ describe("SequenceComponent.activate", () => { expect(setGraphModeSpy.calls.argsFor(0)[0]).toBe(GraphMode.Spatial); }); + it("should reset changing position to spatial on touchcancel (Android gesture takeover)", () => { + const setGraphModeSpy: jasmine.Spy = navigatorMock.graphService.setGraphMode; + + const touchCancelSubject$: Subject = + >containerMock.touchService.touchCancel$; + + const component: SequenceComponent = createComponent(); + component.activate(); + + // Simulate a scrubber drag that latches the renderer's changing-position + // flag (the state the touchcancel handler checks). + (renderer)._setChangingPosition(true); + expect(setGraphModeSpy.calls.mostRecent().args[0]).toBe(GraphMode.Sequence); + + // A touchcancel (gesture takeover / notification shade / second finger) + // must be treated like a terminal release and restore Spatial mode. + touchCancelSubject$.next({ touches: [] } as unknown as TouchEvent); + + expect(setGraphModeSpy.calls.mostRecent().args[0]).toBe(GraphMode.Spatial); + }); + it("should stop play when changing position", () => { const stopSpy: jasmine.Spy = navigatorMock.playService.stop; diff --git a/test/helper/TouchServiceMockCreator.ts b/test/helper/TouchServiceMockCreator.ts index 626b18720..ceb3a988c 100644 --- a/test/helper/TouchServiceMockCreator.ts +++ b/test/helper/TouchServiceMockCreator.ts @@ -18,6 +18,7 @@ export class TouchServiceMockCreator extends MockCreatorBase { this._mockProperty(mock, "singleTouchDrag$", new Subject()); this._mockProperty(mock, "singleTouchDragEnd$", new Subject()); this._mockProperty(mock, "touchEnd$", new Subject()); + this._mockProperty(mock, "touchCancel$", new Subject()); this._mockProperty(mock, "touchMove$", new Subject()); this._mockProperty(mock, "touchStart$", new Subject());