Skip to content

Commit f38ae20

Browse files
authored
SF-3588 Fix translator dialog lynx master switch reactivity to project changes (#3486)
1 parent 3f79932 commit f38ae20

File tree

3 files changed

+85
-86
lines changed

3 files changed

+85
-86
lines changed

src/SIL.XForge.Scripture/ClientApp/src/app/translate/editor/translator-settings-dialog.component.html

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ <h1 mat-dialog-title>{{ t("translator_settings") }}</h1>
2626
<mat-select
2727
id="num-suggestions-select"
2828
[disabled]="translationSuggestionsDisabled"
29-
[(ngModel)]="numSuggestions"
29+
[ngModel]="numSuggestions"
30+
(ngModelChange)="setNumSuggestions($event)"
3031
>
3132
@for (value of ["1", "2", "3", "4", "5"]; track value) {
3233
<mat-option [value]="value">{{ value }}</mat-option>
@@ -39,7 +40,7 @@ <h1 mat-dialog-title>{{ t("translator_settings") }}</h1>
3940
}}</span>
4041
<div class="slider-labels">
4142
<span>{{ t("more") }}</span>
42-
<span>{{ confidenceThreshold }}%</span>
43+
<span>{{ confidenceThreshold$ | async }}%</span>
4344
<span>{{ t("better") }}</span>
4445
</div>
4546
<mat-slider
@@ -50,8 +51,9 @@ <h1 mat-dialog-title>{{ t("translator_settings") }}</h1>
5051
>
5152
<input
5253
matSliderThumb
53-
[(ngModel)]="confidenceThreshold"
54-
(dragEnd)="confidenceThreshold = $event.value"
54+
[ngModel]="confidenceThreshold$ | async"
55+
(ngModelChange)="setConfidenceThreshold($event)"
56+
(dragEnd)="setConfidenceThreshold($event.value)"
5557
/>
5658
</mat-slider>
5759
</div>

src/SIL.XForge.Scripture/ClientApp/src/app/translate/editor/translator-settings-dialog.component.spec.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { HarnessLoader } from '@angular/cdk/testing';
22
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
3+
import { CommonModule } from '@angular/common';
34
import { DebugElement, NgModule } from '@angular/core';
45
import { ComponentFixture, fakeAsync, flush, TestBed, tick } from '@angular/core/testing';
56
import { MatDialog, MatDialogConfig } from '@angular/material/dialog';
@@ -53,10 +54,10 @@ describe('TranslatorSettingsDialogComponent', () => {
5354
it('update confidence threshold', fakeAsync(() => {
5455
const env = new TestEnvironment();
5556
env.openDialog();
56-
expect(env.component!.confidenceThreshold).toEqual(50);
57+
expect(env.component!.confidenceThreshold$.value).toEqual(50);
5758

5859
env.updateConfidenceThresholdSlider(60);
59-
expect(env.component!.confidenceThreshold).toEqual(60);
60+
expect(env.component!.confidenceThreshold$.value).toEqual(60);
6061
const userConfigDoc = env.getProjectUserConfigDoc();
6162
expect(userConfigDoc.data!.confidenceThreshold).toEqual(0.6);
6263
env.closeDialog();
@@ -65,14 +66,14 @@ describe('TranslatorSettingsDialogComponent', () => {
6566
it('update suggestions enabled', fakeAsync(async () => {
6667
const env = new TestEnvironment();
6768
env.openDialog();
68-
expect(env.component!.translationSuggestionsUserEnabled).toBe(true);
69+
expect(env.component!['translationSuggestionsUserEnabled']).toBe(true);
6970

7071
const suggestionsToggle = await env.getSuggestionsEnabledToggle();
7172
expect(suggestionsToggle).not.toBeNull();
7273
expect(await env.isToggleChecked(suggestionsToggle!)).toBe(true);
7374

7475
await env.toggleSlideToggle(suggestionsToggle!);
75-
expect(env.component!.translationSuggestionsUserEnabled).toBe(false);
76+
expect(env.component!['translationSuggestionsUserEnabled']).toBe(false);
7677
expect(await env.isToggleChecked(suggestionsToggle!)).toBe(false);
7778

7879
const userConfigDoc = env.getProjectUserConfigDoc();
@@ -95,7 +96,7 @@ describe('TranslatorSettingsDialogComponent', () => {
9596
it('shows correct confidence threshold even when suggestions disabled', fakeAsync(() => {
9697
const env = new TestEnvironment({ translationSuggestionsEnabled: false });
9798
env.openDialog();
98-
expect(env.component?.confidenceThreshold).toEqual(50);
99+
expect(env.component?.confidenceThreshold$.value).toEqual(50);
99100
env.closeDialog();
100101
}));
101102

@@ -312,7 +313,7 @@ describe('TranslatorSettingsDialogComponent', () => {
312313
});
313314

314315
@NgModule({
315-
imports: [UICommonModule, TestTranslocoModule, NoticeComponent],
316+
imports: [CommonModule, UICommonModule, TestTranslocoModule, NoticeComponent],
316317
declarations: [TranslatorSettingsDialogComponent]
317318
})
318319
class DialogTestModule {}
@@ -426,7 +427,7 @@ class TestEnvironment {
426427
}
427428

428429
updateConfidenceThresholdSlider(value: number): void {
429-
this.component!.confidenceThreshold = value;
430+
this.component!.confidenceThreshold$.next(value);
430431
tick(CONFIDENCE_THRESHOLD_TIMEOUT);
431432
this.fixture.detectChanges();
432433
}

src/SIL.XForge.Scripture/ClientApp/src/app/translate/editor/translator-settings-dialog.component.ts

Lines changed: 71 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Component, DestroyRef, Inject, OnInit } from '@angular/core';
22
import { FormControl } from '@angular/forms';
33
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
4-
import { BehaviorSubject, combineLatest } from 'rxjs';
4+
import { BehaviorSubject } from 'rxjs';
55
import { debounceTime, map, skip, startWith } from 'rxjs/operators';
66
import { OnlineStatusService } from 'xforge-common/online-status.service';
77
import { quietTakeUntilDestroyed } from 'xforge-common/util/rxjs-util';
@@ -21,44 +21,46 @@ export interface TranslatorSettingsDialogData {
2121
standalone: false
2222
})
2323
export class TranslatorSettingsDialogComponent implements OnInit {
24-
suggestionsEnabledSwitch = new FormControl<boolean>({ value: false, disabled: !this.onlineStatusService.isOnline });
25-
lynxMasterSwitch = new FormControl<boolean>(false);
26-
lynxAssessmentsEnabled = new FormControl<boolean>(false);
27-
lynxAutoCorrectEnabled = new FormControl<boolean>(false);
24+
readonly suggestionsEnabledSwitch = new FormControl<boolean>({
25+
value: false,
26+
disabled: !this.onlineStatusService.isOnline
27+
});
28+
readonly lynxMasterSwitch = new FormControl<boolean>(false);
29+
readonly lynxAssessmentsEnabled = new FormControl<boolean>(false);
30+
readonly lynxAutoCorrectEnabled = new FormControl<boolean>(false);
31+
32+
showSuggestionsSettings = false;
33+
showLynxSettings = false;
34+
translationSuggestionsDisabled = false;
35+
lynxAssessmentsProjectEnabled = false;
36+
lynxAutoCorrectProjectEnabled = false;
37+
numSuggestions = '1';
38+
39+
readonly confidenceThreshold$ = new BehaviorSubject<number>(20);
2840

2941
private readonly projectDoc: SFProjectProfileDoc = this.data.projectDoc;
3042
private readonly projectUserConfigDoc: SFProjectUserConfigDoc = this.data.projectUserConfigDoc;
31-
private confidenceThreshold$ = new BehaviorSubject<number>(20);
3243

3344
constructor(
3445
@Inject(MAT_DIALOG_DATA) private readonly data: TranslatorSettingsDialogData,
3546
readonly onlineStatusService: OnlineStatusService,
36-
private destroyRef: DestroyRef
47+
private readonly destroyRef: DestroyRef
3748
) {}
3849

3950
ngOnInit(): void {
40-
if (this.projectUserConfigDoc.data != null) {
41-
const percent = Math.round(this.projectUserConfigDoc.data.confidenceThreshold * 100);
42-
this.confidenceThreshold$.next(percent);
43-
}
51+
this.updateComponentState();
4452

45-
this.suggestionsEnabledSwitch.setValue(this.translationSuggestionsUserEnabled);
46-
this.lynxAssessmentsEnabled.setValue(this.lynxAssessmentsUserEnabled);
47-
this.lynxAutoCorrectEnabled.setValue(this.lynxAutoCorrectUserEnabled);
48-
this.lynxMasterSwitch.setValue(this.lynxMasterEnabled);
53+
this.onlineStatusService.onlineStatus$
54+
.pipe(quietTakeUntilDestroyed(this.destroyRef))
55+
.subscribe(() => this.onOnlineStatusChange());
56+
57+
this.projectDoc.changes$
58+
.pipe(startWith(null), quietTakeUntilDestroyed(this.destroyRef))
59+
.subscribe(() => this.updateComponentState());
4960

50-
combineLatest([this.onlineStatusService.onlineStatus$, this.projectDoc.changes$.pipe(startWith(null))])
61+
this.projectUserConfigDoc.changes$
5162
.pipe(quietTakeUntilDestroyed(this.destroyRef))
52-
.subscribe(() => {
53-
this.updateTranslationSuggestionsSwitch();
54-
});
55-
56-
this.projectUserConfigDoc.changes$.pipe(quietTakeUntilDestroyed(this.destroyRef)).subscribe(() => {
57-
this.updateTranslationSuggestionsSwitch();
58-
this.lynxAssessmentsEnabled.setValue(this.lynxAssessmentsUserEnabled, { emitEvent: false });
59-
this.lynxAutoCorrectEnabled.setValue(this.lynxAutoCorrectUserEnabled, { emitEvent: false });
60-
this.lynxMasterSwitch.setValue(this.lynxMasterEnabled, { emitEvent: false });
61-
});
63+
.subscribe(() => this.updateComponentState());
6264

6365
this.confidenceThreshold$
6466
.pipe(
@@ -73,88 +75,82 @@ export class TranslatorSettingsDialogComponent implements OnInit {
7375
);
7476
}
7577

76-
get translationSuggestionsDisabled(): boolean {
77-
return !this.translationSuggestionsUserEnabled || !this.onlineStatusService.isOnline;
78+
setConfidenceThreshold(value: number): void {
79+
this.confidenceThreshold$.next(value);
7880
}
7981

80-
get translationSuggestionsUserEnabled(): boolean {
81-
return this.projectUserConfigDoc.data == null ? true : this.projectUserConfigDoc.data.translationSuggestionsEnabled;
82+
setNumSuggestions(value: string): void {
83+
this.numSuggestions = value;
84+
void this.projectUserConfigDoc.submitJson0Op(op => op.set(puc => puc.numSuggestions, parseInt(value, 10)));
8285
}
8386

84-
get numSuggestions(): string {
85-
return this.projectUserConfigDoc.data == null ? '1' : this.projectUserConfigDoc.data.numSuggestions.toString();
87+
setTranslationSettingsEnabled(value: boolean): void {
88+
void this.projectUserConfigDoc.submitJson0Op(op =>
89+
op.set<boolean>(puc => puc.translationSuggestionsEnabled, value)
90+
);
8691
}
8792

88-
set numSuggestions(value: string) {
89-
void this.projectUserConfigDoc.submitJson0Op(op => op.set(puc => puc.numSuggestions, parseInt(value, 10)));
93+
setLynxAssessmentsEnabled(value: boolean): void {
94+
this.updateLynxInsightState({ assessmentsEnabled: value });
9095
}
9196

92-
get confidenceThreshold(): number {
93-
return this.confidenceThreshold$.value;
97+
setLynxAutoCorrectEnabled(value: boolean): void {
98+
this.updateLynxInsightState({ autoCorrectionsEnabled: value });
9499
}
95100

96-
set confidenceThreshold(value: number) {
97-
this.confidenceThreshold$.next(value);
101+
setLynxMasterEnabled(value: boolean): void {
102+
this.updateLynxInsightState({
103+
assessmentsEnabled: value,
104+
autoCorrectionsEnabled: value
105+
});
106+
}
107+
private get translationSuggestionsUserEnabled(): boolean {
108+
return this.projectUserConfigDoc.data?.translationSuggestionsEnabled ?? true;
98109
}
99110

100-
get lynxAssessmentsUserEnabled(): boolean {
111+
private get lynxAssessmentsUserEnabled(): boolean {
101112
return this.projectUserConfigDoc.data?.lynxInsightState?.assessmentsEnabled ?? true;
102113
}
103114

104-
get lynxAutoCorrectUserEnabled(): boolean {
115+
private get lynxAutoCorrectUserEnabled(): boolean {
105116
return this.projectUserConfigDoc.data?.lynxInsightState?.autoCorrectionsEnabled ?? true;
106117
}
107118

108-
get lynxAssessmentsProjectEnabled(): boolean {
109-
return !!this.projectDoc.data?.lynxConfig?.assessmentsEnabled;
110-
}
111-
112-
get lynxAutoCorrectProjectEnabled(): boolean {
113-
return !!this.projectDoc.data?.lynxConfig?.autoCorrectionsEnabled;
114-
}
115-
116-
get lynxMasterEnabled(): boolean {
119+
private get lynxMasterEnabled(): boolean {
117120
return (
118121
(this.lynxAssessmentsProjectEnabled && this.lynxAssessmentsUserEnabled) ||
119122
(this.lynxAutoCorrectProjectEnabled && this.lynxAutoCorrectUserEnabled)
120123
);
121124
}
122125

123-
get showSuggestionsSettings(): boolean {
124-
return !!this.projectDoc.data?.translateConfig.translationSuggestionsEnabled;
125-
}
126-
127-
get showLynxSettings(): boolean {
128-
return this.lynxAssessmentsProjectEnabled || this.lynxAutoCorrectProjectEnabled;
129-
}
130-
131-
setTranslationSettingsEnabled(value: boolean): void {
132-
void this.projectUserConfigDoc.submitJson0Op(op =>
133-
op.set<boolean>(puc => puc.translationSuggestionsEnabled, value)
134-
);
135-
}
136-
137-
updateTranslationSuggestionsSwitch(): void {
126+
private onOnlineStatusChange(): void {
138127
if (this.onlineStatusService.isOnline) {
139128
this.suggestionsEnabledSwitch.enable();
129+
this.translationSuggestionsDisabled = !this.translationSuggestionsUserEnabled;
140130
} else {
141131
this.suggestionsEnabledSwitch.disable();
132+
this.translationSuggestionsDisabled = true;
142133
}
143134
}
144135

145-
setLynxAssessmentsEnabled(value: boolean): void {
146-
this.updateLynxInsightState({ assessmentsEnabled: value });
147-
}
136+
private updateComponentState(): void {
137+
this.showSuggestionsSettings = !!this.projectDoc.data?.translateConfig.translationSuggestionsEnabled;
138+
this.lynxAssessmentsProjectEnabled = !!this.projectDoc.data?.lynxConfig?.assessmentsEnabled;
139+
this.lynxAutoCorrectProjectEnabled = !!this.projectDoc.data?.lynxConfig?.autoCorrectionsEnabled;
140+
this.showLynxSettings = this.lynxAssessmentsProjectEnabled || this.lynxAutoCorrectProjectEnabled;
141+
this.translationSuggestionsDisabled = !this.translationSuggestionsUserEnabled || !this.onlineStatusService.isOnline;
148142

149-
setLynxAutoCorrectEnabled(value: boolean): void {
150-
this.updateLynxInsightState({ autoCorrectionsEnabled: value });
151-
}
143+
if (this.projectUserConfigDoc.data != null) {
144+
const percent = Math.round(this.projectUserConfigDoc.data.confidenceThreshold * 100);
145+
this.numSuggestions = this.projectUserConfigDoc.data.numSuggestions.toString();
146+
this.confidenceThreshold$.next(percent);
147+
}
152148

153-
setLynxMasterEnabled(value: boolean): void {
154-
this.updateLynxInsightState({
155-
assessmentsEnabled: value,
156-
autoCorrectionsEnabled: value
157-
});
149+
// Update form control state
150+
this.suggestionsEnabledSwitch.setValue(this.translationSuggestionsUserEnabled, { emitEvent: false });
151+
this.lynxAssessmentsEnabled.setValue(this.lynxAssessmentsUserEnabled, { emitEvent: false });
152+
this.lynxAutoCorrectEnabled.setValue(this.lynxAutoCorrectUserEnabled, { emitEvent: false });
153+
this.lynxMasterSwitch.setValue(this.lynxMasterEnabled, { emitEvent: false });
158154
}
159155

160156
private updateLynxInsightState(updates: { assessmentsEnabled?: boolean; autoCorrectionsEnabled?: boolean }): void {

0 commit comments

Comments
 (0)