Skip to content

Commit b8edfe0

Browse files
authored
SF-3505 Add Lynx options in settings and remove feature flag (#3392)
1 parent 96f354d commit b8edfe0

27 files changed

+1096
-231
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export interface LynxConfig {
2+
autoCorrectionsEnabled: boolean;
3+
assessmentsEnabled: boolean;
4+
punctuationCheckerEnabled: boolean;
5+
allowedCharacterCheckerEnabled: boolean;
6+
}

src/RealtimeServer/scriptureforge/models/sf-project-test-data.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ function testProjectProfile(ordinal: number): SFProjectProfile {
4444
biblicalTermsEnabled: false,
4545
hasRenderings: false
4646
},
47+
lynxConfig: {
48+
autoCorrectionsEnabled: false,
49+
assessmentsEnabled: false,
50+
punctuationCheckerEnabled: false,
51+
allowedCharacterCheckerEnabled: false
52+
},
4753
editable: true,
4854
defaultFontSize: 12,
4955
defaultFont: 'Charis SIL',

src/RealtimeServer/scriptureforge/models/sf-project.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { WritingSystem } from '../../common/models/writing-system';
44
import { obj } from '../../common/utils/obj-path';
55
import { BiblicalTermsConfig } from './biblical-terms-config';
66
import { CheckingConfig } from './checking-config';
7+
import { LynxConfig } from './lynx-config';
78
import { NoteTag } from './note-tag';
89
import { ParatextUserProfile } from './paratext-user-profile';
910
import { Sync } from './sync';
@@ -38,6 +39,7 @@ export interface SFProjectProfile extends Project {
3839
translateConfig: TranslateConfig;
3940
checkingConfig: CheckingConfig;
4041
resourceConfig?: ResourceConfig;
42+
lynxConfig: LynxConfig;
4143
texts: TextInfo[];
4244
noteTags?: NoteTag[];
4345
sync: Sync;

src/RealtimeServer/scriptureforge/services/sf-project-migrations.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,30 @@ class SFProjectMigration24 extends DocMigration {
453453
}
454454
}
455455

456+
class SFProjectMigration25 extends DocMigration {
457+
static readonly VERSION = 25;
458+
459+
async migrateDoc(doc: Doc): Promise<void> {
460+
const ops: Op[] = [];
461+
if (doc.data.lynxConfig == null) {
462+
ops.push({ p: ['lynxConfig'], oi: {} });
463+
}
464+
if (doc.data.lynxConfig?.autoCorrectionsEnabled == null) {
465+
ops.push({ p: ['lynxConfig', 'autoCorrectionsEnabled'], oi: false });
466+
}
467+
if (doc.data.lynxConfig?.assessmentsEnabled == null) {
468+
ops.push({ p: ['lynxConfig', 'assessmentsEnabled'], oi: false });
469+
}
470+
if (doc.data.lynxConfig?.punctuationCheckerEnabled == null) {
471+
ops.push({ p: ['lynxConfig', 'punctuationCheckerEnabled'], oi: false });
472+
}
473+
if (doc.data.lynxConfig?.allowedCharacterCheckerEnabled == null) {
474+
ops.push({ p: ['lynxConfig', 'allowedCharacterCheckerEnabled'], oi: false });
475+
}
476+
await submitMigrationOp(SFProjectMigration25.VERSION, doc, ops);
477+
}
478+
}
479+
456480
export const SF_PROJECT_MIGRATIONS: MigrationConstructor[] = monotonicallyIncreasingMigrationList([
457481
SFProjectMigration1,
458482
SFProjectMigration2,
@@ -477,5 +501,6 @@ export const SF_PROJECT_MIGRATIONS: MigrationConstructor[] = monotonicallyIncrea
477501
SFProjectMigration21,
478502
SFProjectMigration22,
479503
SFProjectMigration23,
480-
SFProjectMigration24
504+
SFProjectMigration24,
505+
SFProjectMigration25
481506
]);

src/RealtimeServer/scriptureforge/services/sf-project-service.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ const SF_PROJECT_PROFILE_FIELDS: ShareDB.ProjectionFields = {
3131
defaultFont: true,
3232
translateConfig: true,
3333
checkingConfig: true,
34+
lynxConfig: true,
3435
texts: true,
3536
syncDisabled: true,
3637
sync: true,
@@ -391,6 +392,24 @@ export class SFProjectService extends ProjectService<SFProject> {
391392
},
392393
additionalProperties: false
393394
},
395+
lynxConfig: {
396+
bsonType: 'object',
397+
properties: {
398+
autoCorrectionsEnabled: {
399+
bsonType: 'bool'
400+
},
401+
assessmentsEnabled: {
402+
bsonType: 'bool'
403+
},
404+
punctuationCheckerEnabled: {
405+
bsonType: 'bool'
406+
},
407+
allowedCharacterCheckerEnabled: {
408+
bsonType: 'bool'
409+
}
410+
},
411+
additionalProperties: false
412+
},
394413
resourceConfig: {
395414
bsonType: 'object',
396415
properties: {
@@ -586,6 +605,7 @@ export class SFProjectService extends ProjectService<SFProject> {
586605
this.pathTemplate(p => p.texts),
587606
this.pathTemplate(p => p.translateConfig),
588607
this.pathTemplate(p => p.checkingConfig),
608+
this.pathTemplate(p => p.lynxConfig),
589609
this.pathTemplate(p => p.shortName),
590610
this.pathTemplate(p => p.writingSystem),
591611
this.pathTemplate(p => p.copyrightBanner),

src/SIL.XForge.Scripture/ClientApp/src/app/core/models/sf-project-settings.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,9 @@ export interface SFProjectSettings {
2626
communityCheckersShareEnabled?: boolean | null;
2727
commentersShareEnabled?: boolean | null;
2828
viewersShareEnabled?: boolean | null;
29+
30+
lynxAutoCorrectionsEnabled?: boolean | null;
31+
lynxAssessmentsEnabled?: boolean | null;
32+
lynxPunctuationCheckerEnabled?: boolean | null;
33+
lynxAllowedCharacterCheckerEnabled?: boolean | null;
2934
}

src/SIL.XForge.Scripture/ClientApp/src/app/settings/settings.component.html

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,74 @@ <h4>{{ t("sharing_scripture_forge_guests") }}</h4>
316316
</div>
317317
</mat-card-content>
318318
</mat-card>
319+
320+
<!-- Lynx Settings Card -->
321+
<mat-card>
322+
<mat-card-content>
323+
<mat-card-title class="lynx-settings-title"
324+
>{{ t("lynx_settings_header") }}<span class="beta-tag">{{ t("beta_tag") }}</span></mat-card-title
325+
>
326+
<p class="helper-text">{{ t("lynx_settings_description") }}</p>
327+
<app-notice type="info" icon="info" class="requirements">{{ t("lynx_latin_script_notice") }}</app-notice>
328+
<div class="tool-setting">
329+
<div class="tool-setting-field checkbox-field">
330+
<mat-checkbox formControlName="lynxAutoCorrectionsEnabled" id="checkbox-lynx-auto-corrections">
331+
{{ t("lynx_auto_corrections") }}
332+
</mat-checkbox>
333+
<app-info [text]="t('lynx_auto_corrections_help')"></app-info>
334+
<app-write-status
335+
[state]="getControlState('lynxAutoCorrectionsEnabled')"
336+
[formGroup]="form"
337+
id="lynx-auto-corrections-status"
338+
></app-write-status>
339+
</div>
340+
</div>
341+
<div class="tool-setting">
342+
<div class="tool-setting-field checkbox-field">
343+
<mat-checkbox formControlName="lynxAssessmentsEnabled" id="checkbox-lynx-assessments">
344+
{{ t("lynx_assessments") }}
345+
</mat-checkbox>
346+
<app-info [text]="t('lynx_assessments_help')"></app-info>
347+
<app-write-status
348+
[state]="getControlState('lynxAssessmentsEnabled')"
349+
[formGroup]="form"
350+
id="lynx-assessments-status"
351+
></app-write-status>
352+
</div>
353+
</div>
354+
@if (isLynxAssessmentsEnabled) {
355+
<div class="tool-setting">
356+
<div class="tool-setting-field checkbox-field child-setting">
357+
<mat-checkbox formControlName="lynxPunctuationCheckerEnabled" id="checkbox-lynx-punctuation-checker">
358+
{{ t("lynx_punctuation_checker") }}
359+
</mat-checkbox>
360+
<app-info [text]="t('lynx_punctuation_checker_help')"></app-info>
361+
<app-write-status
362+
[state]="getControlState('lynxPunctuationCheckerEnabled')"
363+
[formGroup]="form"
364+
id="lynx-punctuation-checker-status"
365+
></app-write-status>
366+
</div>
367+
</div>
368+
<div class="tool-setting">
369+
<div class="tool-setting-field checkbox-field child-setting">
370+
<mat-checkbox
371+
formControlName="lynxAllowedCharacterCheckerEnabled"
372+
id="checkbox-lynx-allowed-character-checker"
373+
>
374+
{{ t("lynx_allowed_character_checker") }}
375+
</mat-checkbox>
376+
<app-info [text]="t('lynx_allowed_character_checker_help')"></app-info>
377+
<app-write-status
378+
[state]="getControlState('lynxAllowedCharacterCheckerEnabled')"
379+
[formGroup]="form"
380+
id="lynx-allowed-character-checker-status"
381+
></app-write-status>
382+
</div>
383+
</div>
384+
}
385+
</mat-card-content>
386+
</mat-card>
319387
</form>
320388
</div>
321389

src/SIL.XForge.Scripture/ClientApp/src/app/settings/settings.component.scss

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ mat-icon {
8686
align-items: center;
8787
}
8888

89+
.child-setting {
90+
margin-inline-start: 24px;
91+
}
92+
8993
#btn-log-in-settings {
9094
background-color: variables.$pt-button-green;
9195
color: white;
@@ -184,3 +188,18 @@ form {
184188
flex-direction: column;
185189
row-gap: 16px;
186190
}
191+
192+
.lynx-settings-title {
193+
display: flex;
194+
align-items: center;
195+
}
196+
197+
.beta-tag {
198+
font-size: 0.55em;
199+
color: var(--sys-on-primary);
200+
background-color: var(--sys-on-primary-container);
201+
padding: 0.2em 0.6em;
202+
border-radius: 1em;
203+
margin-inline-start: 1.5em;
204+
line-height: 1.2em;
205+
}

src/SIL.XForge.Scripture/ClientApp/src/app/settings/settings.component.ts

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import { SFProjectSettings } from '../core/models/sf-project-settings';
2929
import { ParatextService, SelectableProject } from '../core/paratext.service';
3030
import { SFProjectService } from '../core/sf-project.service';
3131
import { DeleteProjectDialogComponent } from './delete-project-dialog/delete-project-dialog.component';
32+
3233
/** Allows user to configure high-level settings of how SF will use their Paratext project. */
3334
@Component({
3435
selector: 'app-settings',
@@ -48,6 +49,10 @@ export class SettingsComponent extends DataLoadingComponent implements OnInit {
4849
communityCheckersShareEnabled = new FormControl(false);
4950
commentersShareEnabled = new FormControl(false);
5051
viewersShareEnabled = new FormControl(false);
52+
lynxAutoCorrectionsEnabled = new FormControl(false);
53+
lynxAssessmentsEnabled = new FormControl(false);
54+
lynxPunctuationCheckerEnabled = new FormControl(false);
55+
lynxAllowedCharacterCheckerEnabled = new FormControl(false);
5156

5257
// Expose enums to the template
5358
CheckingAnswerExport = CheckingAnswerExport;
@@ -65,7 +70,11 @@ export class SettingsComponent extends DataLoadingComponent implements OnInit {
6570
translatorsShareEnabled: this.translatorsShareEnabled,
6671
communityCheckersShareEnabled: this.communityCheckersShareEnabled,
6772
commentersShareEnabled: this.commentersShareEnabled,
68-
viewersShareEnabled: this.viewersShareEnabled
73+
viewersShareEnabled: this.viewersShareEnabled,
74+
lynxAutoCorrectionsEnabled: this.lynxAutoCorrectionsEnabled,
75+
lynxAssessmentsEnabled: this.lynxAssessmentsEnabled,
76+
lynxPunctuationCheckerEnabled: this.lynxPunctuationCheckerEnabled,
77+
lynxAllowedCharacterCheckerEnabled: this.lynxAllowedCharacterCheckerEnabled
6978
});
7079

7180
isActiveSourceProject: boolean = false;
@@ -130,6 +139,10 @@ export class SettingsComponent extends DataLoadingComponent implements OnInit {
130139
return this.checkingEnabled.value ?? false;
131140
}
132141

142+
get isLynxAssessmentsEnabled(): boolean {
143+
return this.lynxAssessmentsEnabled.value ?? false;
144+
}
145+
133146
get showPreTranslationSettings(): boolean {
134147
const translateConfig = this.projectDoc?.data?.translateConfig;
135148
if (translateConfig == null) {
@@ -358,6 +371,9 @@ export class SettingsComponent extends DataLoadingComponent implements OnInit {
358371
this.updateSharingSetting(newValue, 'communityCheckersShareEnabled', SFProjectRole.CommunityChecker);
359372
this.updateSharingSetting(newValue, 'commentersShareEnabled', SFProjectRole.Commenter);
360373
this.updateSharingSetting(newValue, 'viewersShareEnabled', SFProjectRole.Viewer);
374+
375+
// Update Lynx settings
376+
this.updateLynxSettings(newValue);
361377
}
362378

363379
private settingChanged(
@@ -420,6 +436,48 @@ export class SettingsComponent extends DataLoadingComponent implements OnInit {
420436
}
421437
}
422438

439+
private updateLynxSettings(newValue: SFProjectSettings): void {
440+
if (this.settingChanged(newValue, 'lynxAutoCorrectionsEnabled')) {
441+
this.updateSetting(newValue, 'lynxAutoCorrectionsEnabled');
442+
}
443+
444+
if (this.settingChanged(newValue, 'lynxAssessmentsEnabled')) {
445+
this.updateSetting(newValue, 'lynxAssessmentsEnabled');
446+
447+
// When 'assessments' is checked, auto-enable punctuation checker
448+
if (newValue.lynxAssessmentsEnabled) {
449+
this.lynxPunctuationCheckerEnabled.setValue(true, { emitEvent: false });
450+
this.updateSetting({ ...newValue, lynxPunctuationCheckerEnabled: true }, 'lynxPunctuationCheckerEnabled');
451+
} else {
452+
// When 'assessments' is unchecked, auto-disable children
453+
this.lynxPunctuationCheckerEnabled.setValue(false, { emitEvent: false });
454+
this.updateSetting({ ...newValue, lynxPunctuationCheckerEnabled: false }, 'lynxPunctuationCheckerEnabled');
455+
this.lynxAllowedCharacterCheckerEnabled.setValue(false, { emitEvent: false });
456+
this.updateSetting(
457+
{ ...newValue, lynxAllowedCharacterCheckerEnabled: false },
458+
'lynxAllowedCharacterCheckerEnabled'
459+
);
460+
}
461+
} else {
462+
// If both child settings are disabled, auto-disable assessments (parent setting)
463+
if (
464+
!newValue.lynxAllowedCharacterCheckerEnabled &&
465+
!newValue.lynxPunctuationCheckerEnabled &&
466+
newValue.lynxAssessmentsEnabled
467+
) {
468+
this.lynxAssessmentsEnabled.setValue(false, { emitEvent: false });
469+
this.updateSetting({ ...newValue, lynxAssessmentsEnabled: false }, 'lynxAssessmentsEnabled');
470+
}
471+
472+
if (this.settingChanged(newValue, 'lynxPunctuationCheckerEnabled')) {
473+
this.updateSetting(newValue, 'lynxPunctuationCheckerEnabled');
474+
}
475+
if (this.settingChanged(newValue, 'lynxAllowedCharacterCheckerEnabled')) {
476+
this.updateSetting(newValue, 'lynxAllowedCharacterCheckerEnabled');
477+
}
478+
}
479+
}
480+
423481
private checkUpdateStatus(setting: keyof SFProjectSettings, updatePromise: Promise<void>): void {
424482
this.controlStates.set(setting, ElementState.Submitting);
425483
updatePromise
@@ -455,7 +513,11 @@ export class SettingsComponent extends DataLoadingComponent implements OnInit {
455513
this.projectDoc.data.rolePermissions[SFProjectRole.Viewer]?.includes(
456514
SF_PROJECT_RIGHTS.joinRight(SFProjectDomain.UserInvites, Operation.Create)
457515
) === true,
458-
checkingAnswerExport: this.projectDoc.data.checkingConfig.answerExportMethod ?? CheckingAnswerExport.All
516+
checkingAnswerExport: this.projectDoc.data.checkingConfig.answerExportMethod ?? CheckingAnswerExport.All,
517+
lynxAutoCorrectionsEnabled: this.projectDoc.data.lynxConfig.autoCorrectionsEnabled,
518+
lynxAssessmentsEnabled: this.projectDoc.data.lynxConfig.assessmentsEnabled,
519+
lynxPunctuationCheckerEnabled: this.projectDoc.data.lynxConfig.punctuationCheckerEnabled,
520+
lynxAllowedCharacterCheckerEnabled: this.projectDoc.data.lynxConfig.allowedCharacterCheckerEnabled
459521
};
460522
this.form.reset(this.previousFormValues);
461523
this.setIndividualControlDisabledStates();
@@ -489,6 +551,10 @@ export class SettingsComponent extends DataLoadingComponent implements OnInit {
489551
this.controlStates.set('communityCheckersShareEnabled', ElementState.InSync);
490552
this.controlStates.set('commentersShareEnabled', ElementState.InSync);
491553
this.controlStates.set('viewersShareEnabled', ElementState.InSync);
554+
this.controlStates.set('lynxAutoCorrectionsEnabled', ElementState.InSync);
555+
this.controlStates.set('lynxAssessmentsEnabled', ElementState.InSync);
556+
this.controlStates.set('lynxPunctuationCheckerEnabled', ElementState.InSync);
557+
this.controlStates.set('lynxAllowedCharacterCheckerEnabled', ElementState.InSync);
492558
}
493559

494560
private updateNonSelectableProjects(): void {

src/SIL.XForge.Scripture/ClientApp/src/app/shared/text/text.component.html

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1-
@if (showInsights && editor != null) {
2-
<app-lynx-insight-editor-objects [editor]="editor" [lynxTextModelConverter]="viewModel" />
1+
@if ((lynxInsightsEnabled || lynxAutoCorrectionsEnabled) && editor != null) {
2+
<app-lynx-insight-editor-objects
3+
[editor]="editor"
4+
[lynxTextModelConverter]="viewModel"
5+
[insightsEnabled]="lynxInsightsEnabled"
6+
[autoCorrectionsEnabled]="lynxAutoCorrectionsEnabled"
7+
/>
38
}
49

510
<!-- translate="no" is to prevent tools like Google translate from editing the text -->
@@ -21,7 +26,7 @@
2126
rtl: isRtl,
2227
'mark-invalid': markInvalid,
2328
'selectable-verses': selectableVerses,
24-
'custom-local-cursor': showInsights && !isCursorMoveKeyDown
29+
'custom-local-cursor': lynxInsightsEnabled && !isCursorMoveKeyDown
2530
}"
2631
[dir]="$any(textDirection)"
2732
[lang]="lang"

0 commit comments

Comments
 (0)