diff --git a/src/SIL.XForge.Scripture/ClientApp/src/app/checking/checking/checking-answers/checking-question/checking-question.component.ts b/src/SIL.XForge.Scripture/ClientApp/src/app/checking/checking/checking-answers/checking-question/checking-question.component.ts index d4146f480c..08ca38bbb1 100644 --- a/src/SIL.XForge.Scripture/ClientApp/src/app/checking/checking/checking-answers/checking-question/checking-question.component.ts +++ b/src/SIL.XForge.Scripture/ClientApp/src/app/checking/checking/checking-answers/checking-question/checking-question.component.ts @@ -60,6 +60,9 @@ export class CheckingQuestionComponent extends SubscriptionDisposable implements if (doc?.data == null) { return; } + if (doc.id !== this._questionDoc?.id) { + this.stopAudio(); + } this._questionDoc = doc; } diff --git a/src/SIL.XForge.Scripture/ClientApp/src/app/checking/checking/checking.component.html b/src/SIL.XForge.Scripture/ClientApp/src/app/checking/checking/checking.component.html index b2fbd57c92..66720af771 100644 --- a/src/SIL.XForge.Scripture/ClientApp/src/app/checking/checking/checking.component.html +++ b/src/SIL.XForge.Scripture/ClientApp/src/app/checking/checking/checking.component.html @@ -112,7 +112,7 @@

{{ t("filter_questions") }}

[matTooltipDisabled]="isAudioPlaying()" (click)="toggleAudio()" > - {{ isAudioPlaying() ? "pause" : "play_circle_outline" }} + {{ isAudioPlaying() ? "stop" : "play_circle_outline" }} @@ -128,12 +128,14 @@

{{ t("filter_questions") }}

- +
{{ t("filter_questions") }} [projectDoc]="projectDoc" >
-
- -
- +
{{ t("filter_questions") }} >
+ +
+ +
+
diff --git a/src/SIL.XForge.Scripture/ClientApp/src/app/checking/checking/checking.component.spec.ts b/src/SIL.XForge.Scripture/ClientApp/src/app/checking/checking/checking.component.spec.ts index 02ae82f013..f5b777cb23 100644 --- a/src/SIL.XForge.Scripture/ClientApp/src/app/checking/checking/checking.component.spec.ts +++ b/src/SIL.XForge.Scripture/ClientApp/src/app/checking/checking/checking.component.spec.ts @@ -212,15 +212,15 @@ describe('CheckingComponent', () => { discardPeriodicTasks(); })); - it('should re-calculate scripture slide position on resize', fakeAsync(() => { + it('should re-calculate scripture slide position on drag end', fakeAsync(() => { const testProject: SFProject = TestEnvironment.generateTestProject(); - testProject.checkingConfig.hideCommunityCheckingText = true; const env = new TestEnvironment({ user: CHECKER_USER, testProject }); env.waitForSliderUpdate(); - (env.component as any)._scriptureAreaMaxSize = 1; - expect(env.component.scriptureAreaMaxSize).toEqual(1); - window.dispatchEvent(new Event('resize')); - expect(env.component.scriptureAreaMaxSize).toBeGreaterThan(1); + env.component.splitComponent?.setVisibleAreaSizes(['*', 1]); + expect(env.component.splitComponent?.getVisibleAreaSizes()[1]).toEqual(1); + env.component.checkSliderPosition({ sizes: ['*', 20] }); + env.waitForSliderUpdate(); + expect(env.component.splitComponent?.getVisibleAreaSizes()[1]).toBeGreaterThan(1); flush(); discardPeriodicTasks(); })); @@ -1500,7 +1500,7 @@ describe('CheckingComponent', () => { env.simulateNewRemoteAnswer(); expect(env.showUnreadAnswersButton).toBeNull(); expect(env.answers.length).withContext('broken unrelated functionality').toEqual(0); - // Incoming remote answer should have been absorbed into the set of i + // Incoming remote answer should have been absorbed into the set of // answers pending to show, since user was looking at the Add Answer button expect(env.component.answersPanel!.answers.length).toEqual(2); // We don't show the total answer count in the heading until the user adds her answer. @@ -2108,27 +2108,17 @@ describe('CheckingComponent', () => { discardPeriodicTasks(); })); - it('audio continues when changing question on the same chapter', fakeAsync(() => { + it('audio stops when changing question on the same chapter', fakeAsync(() => { const env = new TestEnvironment({ user: ADMIN_USER, scriptureAudio: true }); const audio = env.mockScriptureAudioAndPlay(); env.selectQuestion(4); - verify(audio.stop()).never(); + verify(audio.stop()).once(); expect(env.component).toBeDefined(); flush(); })); - it('audio continues when adding an answer on a question', fakeAsync(() => { - const env = new TestEnvironment({ user: ADMIN_USER, scriptureAudio: true }); - const audio = env.mockScriptureAudioAndPlay(); - - env.answerQuestion('Answer while audio is playing'); - env.waitForQuestionTimersToComplete(); - verify(audio.stop()).never(); - expect(env.component).toBeDefined(); - })); - it('pauses chapter audio when adding a question', fakeAsync(() => { const env = new TestEnvironment({ user: ADMIN_USER, scriptureAudio: true }); const audio = env.mockScriptureAudioAndPlay(); diff --git a/src/SIL.XForge.Scripture/ClientApp/src/app/checking/checking/checking.component.ts b/src/SIL.XForge.Scripture/ClientApp/src/app/checking/checking/checking.component.ts index 8e5c5e2b7c..ecc8f62f8f 100644 --- a/src/SIL.XForge.Scripture/ClientApp/src/app/checking/checking/checking.component.ts +++ b/src/SIL.XForge.Scripture/ClientApp/src/app/checking/checking/checking.component.ts @@ -16,7 +16,7 @@ import { SFProjectRole } from 'realtime-server/lib/esm/scriptureforge/models/sf- import { getTextAudioId } from 'realtime-server/lib/esm/scriptureforge/models/text-audio'; import { TextInfo } from 'realtime-server/lib/esm/scriptureforge/models/text-info'; import { toVerseRef, VerseRefData } from 'realtime-server/lib/esm/scriptureforge/models/verse-ref-data'; -import { asyncScheduler, combineLatest, fromEvent, merge, Subscription } from 'rxjs'; +import { asyncScheduler, combineLatest, merge, Subscription } from 'rxjs'; import { distinctUntilChanged, filter, map, startWith, throttleTime } from 'rxjs/operators'; import { DataLoadingComponent } from 'xforge-common/data-loading-component'; import { FeatureFlagService } from 'xforge-common/feature-flags/feature-flag.service'; @@ -107,7 +107,7 @@ export class CheckingComponent extends DataLoadingComponent implements OnInit, A projectUserConfigDoc?: SFProjectUserConfigDoc; textDocId?: TextDocId; totalVisibleQuestionsString: string = '0'; - visibleQuestions?: QuestionDoc[]; + visibleQuestions: Readonly; showScriptureAudioPlayer: boolean = false; hasQuestionWithoutAudio: boolean = false; isCreatingNewQuestion: boolean = false; @@ -157,7 +157,6 @@ export class CheckingComponent extends DataLoadingComponent implements OnInit, A private text?: TextInfo; private isProjectAdmin: boolean = false; private _scriptureAudioPlayer?: CheckingScriptureAudioPlayerComponent; - private _scriptureAreaMaxSize: number | null = null; constructor( private readonly activatedRoute: ActivatedRoute, @@ -190,10 +189,6 @@ export class CheckingComponent extends DataLoadingComponent implements OnInit, A return this.questionFilters.get(this.activeQuestionFilter); } - get bookName(): string { - return this.text == null ? '' : this.i18n.localizeBook(this.text.bookNum); - } - get chapter(): number | undefined { return this._chapter; } @@ -214,10 +209,6 @@ export class CheckingComponent extends DataLoadingComponent implements OnInit, A } } - get chapterStrings(): string[] { - return this.chapters.map(c => c.toString()); - } - get isQuestionListPermanent(): boolean { return this._isDrawerPermanent; } @@ -320,7 +311,7 @@ export class CheckingComponent extends DataLoadingComponent implements OnInit, A return this._book; } - private set book(book: number | undefined) { + set book(book: number | undefined) { if (book === this.book) { return; } @@ -440,42 +431,17 @@ export class CheckingComponent extends DataLoadingComponent implements OnInit, A ); } - private get minAnswerPanelPercent(): number { - return Math.ceil((this.answerPanelElementMinimumHeight / this.splitContainerElementHeight) * 100); - } - private get fullyExpandedAnswerPanelPercent(): number { - return Math.ceil((this.fullyExpandedAnswerPanelHeight / this.splitContainerElementHeight) * 100); - } - private get splitContainerElementHeight(): number { return this.splitContainerElement && this.splitComponent ? this.splitContainerElement.nativeElement.offsetHeight - this.splitComponent.gutterSize! : 0; } - private get contentPanelHeight(): number { - return this.scripturePanelContainerElement?.nativeElement.offsetHeight; - } - private get scriptureAudioPlayerAreaHeight(): number { const scriptureAudioPlayerArea: Element | null = document.querySelector('.scripture-audio-player-wrapper'); return scriptureAudioPlayerArea == null ? 0 : scriptureAudioPlayerArea.getBoundingClientRect().height; } - /** Percentage of the vertical space of the as-splitter, needed by just the Scripture audio player. */ - private get scriptureAudioPlayerHeightPercent(): number { - return (this.scriptureAudioPlayerAreaHeight / this.splitContainerElementHeight) * 100; - } - - /** maxSize for as-split-area for the Scripture+audio area. */ - public get scriptureAreaMaxSize(): number | null { - return this._scriptureAreaMaxSize; - } - - private set scriptureAreaMaxSize(value: number | null) { - this._scriptureAreaMaxSize = value; - } - ngOnInit(): void { this.subscribe( combineLatest([this.activatedRoute.params, this.activatedRoute.queryParams]), @@ -760,15 +726,6 @@ export class CheckingComponent extends DataLoadingComponent implements OnInit, A this.isScreenSmall = state.matches; } ); - - this.subscribe(fromEvent(window, 'resize'), () => { - if (this.hideChapterText) { - this.scriptureAreaMaxSize = this.scriptureAudioPlayerHeightPercent; - if (this.contentPanelHeight > this.scriptureAudioPlayerAreaHeight) { - this.calculateScriptureSliderPosition(); - } - } - }); } ngOnDestroy(): void { @@ -926,7 +883,7 @@ export class CheckingComponent extends DataLoadingComponent implements OnInit, A checkSliderPosition(event: any): void { if (event.hasOwnProperty('sizes')) { - if (event.sizes[1] < this.minAnswerPanelPercent) { + if (event.sizes[1] < this.answerPanelElementMinimumHeight) { this.calculateScriptureSliderPosition(); } } @@ -965,6 +922,9 @@ export class CheckingComponent extends DataLoadingComponent implements OnInit, A if (this.onlineStatusService.isOnline) { questionDoc.updateAnswerFileCache(); } + if (!this.hideChapterText && !(actionSource?.isQuestionListChange ?? false)) { + this.toggleAudio(true); + } // Ensure navigation is set to book/chapter of selected question if (this.navigateQuestionChapter(questionDoc)) { @@ -1083,9 +1043,17 @@ export class CheckingComponent extends DataLoadingComponent implements OnInit, A this.showScriptureAudioPlayer = this.hideChapterText; } - toggleAudio(): void { - this.showScriptureAudioPlayer = true; - this._scriptureAudioPlayer?.isPlaying ? this._scriptureAudioPlayer?.pause() : this._scriptureAudioPlayer?.play(); + toggleAudio(forceStopAndHide: boolean = false): void { + this._scriptureAudioPlayer?.isPlaying || forceStopAndHide + ? this._scriptureAudioPlayer?.stop() + : this._scriptureAudioPlayer?.play(); + + this.showScriptureAudioPlayer = + this.hideChapterText || this._scriptureAudioPlayer?.isPlaying + ? true + : forceStopAndHide + ? false + : !this.showScriptureAudioPlayer; } /** @@ -1397,27 +1365,19 @@ export class CheckingComponent extends DataLoadingComponent implements OnInit, A // 100 ms is a speculative value for waiting for elements to be loaded and updated in the DOM. const changeUpdateDelayMs: number = 100; setTimeout(async () => { - if (this.splitComponent == null) { + if (this.splitComponent == null || this.hideChapterText) { return; } - if (this.hideChapterText) { - const answerPanelHeight = 100 - this.scriptureAudioPlayerHeightPercent; - this.splitComponent?.setVisibleAreaSizes([this.scriptureAudioPlayerHeightPercent, answerPanelHeight]); - this.scriptureAreaMaxSize = this.scriptureAudioPlayerHeightPercent; + let answerPanelHeight: number; + if (maximizeAnswerPanel) { + answerPanelHeight = Math.min(this.splitContainerElementHeight * 0.75, this.fullyExpandedAnswerPanelHeight); } else { - let answerPanelHeight: number; - if (maximizeAnswerPanel) { - answerPanelHeight = this.fullyExpandedAnswerPanelPercent; - } else { - answerPanelHeight = this.minAnswerPanelPercent; - } - - answerPanelHeight = Math.min(75, answerPanelHeight); - const scripturePanelHeight = 100 - answerPanelHeight; - - this.splitComponent.setVisibleAreaSizes([scripturePanelHeight, answerPanelHeight]); - this.scriptureAreaMaxSize = null; + answerPanelHeight = this.answerPanelElementMinimumHeight; } + this.splitComponent.setVisibleAreaSizes([ + '*', + this.showScriptureAudioPlayer ? this.scriptureAudioPlayerAreaHeight : answerPanelHeight + ]); }, changeUpdateDelayMs); }