Skip to content

Commit a96e7d0

Browse files
authored
SF-3587 Fix lingering lynx action prompt when changing book/chapter (#3472)
1 parent d0ff8ed commit a96e7d0

File tree

2 files changed

+78
-0
lines changed

2 files changed

+78
-0
lines changed

src/SIL.XForge.Scripture/ClientApp/src/app/translate/editor/lynx/insights/lynx-insight-editor-objects/lynx-insight-editor-objects.component.spec.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { ComponentFixture, fakeAsync, flush, TestBed, tick } from '@angular/core
33
import { Delta } from 'quill';
44
import { BehaviorSubject } from 'rxjs';
55
import { anything, instance, mock, verify, when } from 'ts-mockito';
6+
import { ActivatedBookChapterService, RouteBookChapter } from 'xforge-common/activated-book-chapter.service';
67
import { configureTestingModule } from 'xforge-common/test-utils';
78
import { TextDocId } from '../../../../../core/models/text-doc';
89
import { EditorReadyService } from '../base-services/editor-ready.service';
@@ -21,6 +22,7 @@ const mockInsightStateService = mock(LynxInsightStateService);
2122
const mockEditorReadyService = mock(QuillEditorReadyService);
2223
const mockOverlayService = mock(LynxInsightOverlayService);
2324
const mockLynxWorkspaceService = mock(LynxWorkspaceService);
25+
const mockActivatedBookChapterService = mock(ActivatedBookChapterService);
2426
const mockDestroyRef = mock(DestroyRef);
2527
const mockTextModelConverter = mock<LynxTextModelConverter>();
2628

@@ -33,6 +35,7 @@ describe('LynxInsightEditorObjectsComponent', () => {
3335
{ provide: EditorReadyService, useMock: mockEditorReadyService },
3436
{ provide: LynxInsightOverlayService, useMock: mockOverlayService },
3537
{ provide: LynxWorkspaceService, useMock: mockLynxWorkspaceService },
38+
{ provide: ActivatedBookChapterService, useMock: mockActivatedBookChapterService },
3639
{ provide: DestroyRef, useMock: mockDestroyRef }
3740
],
3841
schemas: [NO_ERRORS_SCHEMA]
@@ -274,6 +277,60 @@ describe('LynxInsightEditorObjectsComponent', () => {
274277
}));
275278
});
276279

280+
describe('book/chapter navigation', () => {
281+
it('should clear display state when changing chapters', fakeAsync(() => {
282+
const env = new TestEnvironment();
283+
const testInsight = env.createTestInsight();
284+
285+
env.setEditorReady(true);
286+
env.setFilteredInsights([testInsight]);
287+
tick();
288+
flush();
289+
290+
// Set some display state (simulate active insights)
291+
env.setDisplayState({
292+
activeInsightIds: [testInsight.id],
293+
actionOverlayActive: true,
294+
promptActive: true,
295+
cursorActiveInsightIds: [testInsight.id]
296+
});
297+
tick();
298+
299+
// Trigger chapter change
300+
env.changeBookChapter('MAT', 2);
301+
tick();
302+
303+
// Verify clearDisplayState was called on chapter change
304+
verify(mockInsightStateService.clearDisplayState()).atLeast(1);
305+
}));
306+
307+
it('should clear display state when changing books', fakeAsync(() => {
308+
const env = new TestEnvironment();
309+
const testInsight = env.createTestInsight();
310+
311+
env.setEditorReady(true);
312+
env.setFilteredInsights([testInsight]);
313+
tick();
314+
flush();
315+
316+
// Set some display state (simulate active insights)
317+
env.setDisplayState({
318+
activeInsightIds: [testInsight.id],
319+
actionOverlayActive: true,
320+
promptActive: true,
321+
cursorActiveInsightIds: [testInsight.id]
322+
});
323+
tick();
324+
325+
// Trigger book change
326+
env.changeBookChapter('MRK', 1);
327+
tick();
328+
329+
// Verify clearDisplayState was called on book change
330+
verify(mockInsightStateService.clearDisplayState()).atLeast(1);
331+
}));
332+
});
333+
277334
describe('cleanup', () => {
278335
it('should remove all insight formatting on destroy', fakeAsync(() => {
279336
const env = new TestEnvironment();
@@ -320,6 +377,7 @@ class TestEnvironment {
320377
private editorReadySubject: BehaviorSubject<boolean>;
321378
private filteredInsightsSubject: BehaviorSubject<LynxInsight[]>;
322379
private displayStateSubject: BehaviorSubject<LynxInsightDisplayState>;
380+
private activatedBookChapterSubject: BehaviorSubject<RouteBookChapter | undefined>;
323381

324382
constructor(args: TestEnvArgs = {}) {
325383
const textModelConverter = instance(mockTextModelConverter);
@@ -335,6 +393,11 @@ class TestEnvironment {
335393
cursorActiveInsightIds: []
336394
});
337395

396+
this.activatedBookChapterSubject = new BehaviorSubject<RouteBookChapter | undefined>({
397+
bookId: 'MAT',
398+
chapter: 1
399+
});
400+
338401
// Create mock editor
339402
const mockRoot = document.createElement('div');
340403
const actualEditor = {
@@ -362,6 +425,7 @@ class TestEnvironment {
362425
when(mockEditorReadyService.listenEditorReadyState(anything())).thenReturn(this.editorReadySubject);
363426
when(mockInsightStateService.filteredChapterInsights$).thenReturn(this.filteredInsightsSubject);
364427
when(mockInsightStateService.displayState$).thenReturn(this.displayStateSubject);
428+
when(mockActivatedBookChapterService.activatedBookChapter$).thenReturn(this.activatedBookChapterSubject);
365429
when(mockInsightStateService.updateDisplayState(anything())).thenReturn();
366430
when(mockInsightStateService.clearDisplayState()).thenReturn();
367431
when(mockInsightRenderService.render(anything(), anything())).thenResolve();
@@ -412,6 +476,10 @@ class TestEnvironment {
412476
this.displayStateSubject.next(fullState);
413477
}
414478

479+
changeBookChapter(bookId: string, chapter: number): void {
480+
this.activatedBookChapterSubject.next({ bookId, chapter });
481+
}
482+
415483
triggerSelectionChange(selection: LynxInsightRange | undefined): void {
416484
const handlers = this.eventHandlers.get('selection-change');
417485
if (handlers) {

src/SIL.XForge.Scripture/ClientApp/src/app/translate/editor/lynx/insights/lynx-insight-editor-objects/lynx-insight-editor-objects.component.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
tap
1616
} from 'rxjs';
1717
import { distinctUntilChanged, map, observeOn, scan } from 'rxjs/operators';
18+
import { ActivatedBookChapterService } from 'xforge-common/activated-book-chapter.service';
1819
import { quietTakeUntilDestroyed } from 'xforge-common/util/rxjs-util';
1920
import { EditorReadyService } from '../base-services/editor-ready.service';
2021
import { InsightRenderService } from '../base-services/insight-render.service';
@@ -50,6 +51,7 @@ export class LynxInsightEditorObjectsComponent implements OnChanges, OnInit, OnD
5051
private readonly editorReadyService: EditorReadyService,
5152
private readonly overlayService: LynxInsightOverlayService,
5253
private readonly lynxWorkspaceService: LynxWorkspaceService,
54+
private readonly activatedBookChapterService: ActivatedBookChapterService,
5355
@Inject(DOCUMENT) private readonly document: Document
5456
) {}
5557

@@ -110,6 +112,14 @@ export class LynxInsightEditorObjectsComponent implements OnChanges, OnInit, OnD
110112
}
111113
});
112114

115+
this.activatedBookChapterService.activatedBookChapter$
116+
.pipe(quietTakeUntilDestroyed(this.destroyRef))
117+
.subscribe(() => {
118+
// Clear display state when changing chapters.
119+
// This happens before active insights are assigned from the problems panel.
120+
this.insightState.clearDisplayState();
121+
});
122+
113123
this.editorReadyService
114124
.listenEditorReadyState(this.editor)
115125
.pipe(

0 commit comments

Comments
 (0)