diff --git a/src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-generation.component.html b/src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-generation.component.html index 2bf640f292..d15c4617a0 100644 --- a/src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-generation.component.html +++ b/src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-generation.component.html @@ -275,13 +275,18 @@

@if (draftJob != null) { @if (isDraftQueued(draftJob)) {
-

{{ t("draft_queued_header") }}

- @if (!hasDraftQueueDepth(draftJob)) { -

{{ t("draft_queued_detail") }}

+ @if (isSyncing()) { +

{{ t("draft_syncing") }}

+

{{ t("draft_syncing_detail") }}

} @else { -

- {{ t("draft_queued_detail_multiple", { count: draftJob.queueDepth }) }} -

+

{{ t("draft_queued_header") }}

+ @if (!hasDraftQueueDepth(draftJob)) { +

{{ t("draft_queued_detail") }}

+ } @else { +

+ {{ t("draft_queued_detail_multiple", { count: draftJob.queueDepth }) }} +

+ } }
diff --git a/src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-generation.component.spec.ts b/src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-generation.component.spec.ts index c5b1e1efc4..5b91acf2e7 100644 --- a/src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-generation.component.spec.ts +++ b/src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-generation.component.spec.ts @@ -1,5 +1,5 @@ import { HttpErrorResponse } from '@angular/common/http'; -import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; +import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; import { MatDialogRef, MatDialogState } from '@angular/material/dialog'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { RouterModule } from '@angular/router'; @@ -10,12 +10,12 @@ import { SFProjectRole } from 'realtime-server/lib/esm/scriptureforge/models/sf- import { createTestProjectProfile } from 'realtime-server/lib/esm/scriptureforge/models/sf-project-test-data'; import { TextInfoPermission } from 'realtime-server/lib/esm/scriptureforge/models/text-info-permission'; import { ProjectType } from 'realtime-server/lib/esm/scriptureforge/models/translate-config'; -import { BehaviorSubject, EMPTY, Subject, of, throwError } from 'rxjs'; +import { BehaviorSubject, EMPTY, of, Subject, throwError } from 'rxjs'; import { instance, mock, verify, when } from 'ts-mockito'; import { ActivatedProjectService } from 'xforge-common/activated-project.service'; import { AuthService } from 'xforge-common/auth.service'; import { DialogService } from 'xforge-common/dialog.service'; -import { FeatureFlagService, createTestFeatureFlag } from 'xforge-common/feature-flags/feature-flag.service'; +import { createTestFeatureFlag, FeatureFlagService } from 'xforge-common/feature-flags/feature-flag.service'; import { I18nService } from 'xforge-common/i18n.service'; import { Locale } from 'xforge-common/models/i18n-locale'; import { UserDoc } from 'xforge-common/models/user-doc'; @@ -2114,6 +2114,49 @@ describe('DraftGenerationComponent', () => { verify(mockDialogRef.close()).once(); }); + it('should track whether the current project is not syncing', fakeAsync(() => { + let env = new TestEnvironment(); + env.fixture.detectChanges(); + tick(); + + expect(env.component.isSyncing()).toBe(false); + })); + + it('should track whether the current project is syncing', fakeAsync(() => { + const projectDoc: SFProjectProfileDoc = { + data: createTestProjectProfile({ + writingSystem: { + tag: 'xyz' + }, + translateConfig: { + projectType: ProjectType.BackTranslation, + source: { + projectRef: 'testSourceProjectId', + writingSystem: { + tag: 'en' + } + } + }, + sync: { + queuedCount: 1 + } + }) + } as SFProjectProfileDoc; + let env = new TestEnvironment(() => { + mockActivatedProjectService = jasmine.createSpyObj('ActivatedProjectService', [''], { + projectId: projectId, + projectId$: of(projectId), + projectDoc: projectDoc, + projectDoc$: of(projectDoc), + changes$: of(projectDoc) + }); + }); + env.fixture.detectChanges(); + tick(); + + expect(env.component.isSyncing()).toBe(true); + })); + it('should display the Paratext credentials update prompt when startBuild throws a forbidden error', fakeAsync(() => { let env = new TestEnvironment(() => { mockDraftGenerationService.startBuildOrGetActiveBuild.and.returnValue( diff --git a/src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-generation.component.ts b/src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-generation.component.ts index ac43d299db..1e2b9a4990 100644 --- a/src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-generation.component.ts +++ b/src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-generation.component.ts @@ -10,7 +10,7 @@ import { RouterLink } from 'ngx-transloco-markup-router-link'; import { SystemRole } from 'realtime-server/lib/esm/common/models/system-role'; import { SFProjectRole } from 'realtime-server/lib/esm/scriptureforge/models/sf-project-role'; import { ProjectType } from 'realtime-server/lib/esm/scriptureforge/models/translate-config'; -import { Subscription, combineLatest, of } from 'rxjs'; +import { combineLatest, of, Subscription } from 'rxjs'; import { catchError, filter, switchMap, tap } from 'rxjs/operators'; import { ActivatedProjectService } from 'xforge-common/activated-project.service'; import { AuthService } from 'xforge-common/auth.service'; @@ -33,7 +33,7 @@ import { ServalProjectComponent } from '../../serval-administration/serval-proje import { SharedModule } from '../../shared/shared.module'; import { WorkingAnimatedIndicatorComponent } from '../../shared/working-animated-indicator/working-animated-indicator.component'; import { NllbLanguageService } from '../nllb-language.service'; -import { BuildConfig, DraftZipProgress, activeBuildStates } from './draft-generation'; +import { activeBuildStates, BuildConfig, DraftZipProgress } from './draft-generation'; import { DraftGenerationStepsComponent, DraftGenerationStepsResult @@ -480,6 +480,10 @@ export class DraftGenerationComponent extends DataLoadingComponent implements On return activeBuildStates.includes(job?.state as BuildStates); } + isSyncing(): boolean { + return this.activatedProject.projectDoc.data.sync.queuedCount > 0; + } + isDraftQueued(job?: BuildDto): boolean { return [BuildStates.Queued, BuildStates.Pending].includes(job?.state as BuildStates); } diff --git a/src/SIL.XForge.Scripture/ClientApp/src/assets/i18n/non_checking_en.json b/src/SIL.XForge.Scripture/ClientApp/src/assets/i18n/non_checking_en.json index a802f9a6d4..d0f0dcecbe 100644 --- a/src/SIL.XForge.Scripture/ClientApp/src/assets/i18n/non_checking_en.json +++ b/src/SIL.XForge.Scripture/ClientApp/src/assets/i18n/non_checking_en.json @@ -187,6 +187,8 @@ "draft_active_detail": "Generating draft; this usually takes at least {{ count }} hours.", "draft_active_header": "Draft in progress", "draft_is_ready": "Your draft is ready", + "draft_syncing": "Draft initializing", + "draft_syncing_detail": "Your project is being synced before queuing the draft.", "draft_queued_detail_multiple": "The translation draft generation is number {{ count }} in the queue, and will begin once the server is finished with other projects. Please check back later.", "draft_queued_detail": "Generation of the translation draft has been queued, and will begin once the server is finished with another project. Please check back later.", "draft_queued_header": "Draft queued",