diff --git a/src/SIL.XForge.Scripture/ClientApp/src/app/translate/biblical-terms/biblical-terms.component.html b/src/SIL.XForge.Scripture/ClientApp/src/app/translate/biblical-terms/biblical-terms.component.html index f9c8105b3b..d35c4feb0f 100644 --- a/src/SIL.XForge.Scripture/ClientApp/src/app/translate/biblical-terms/biblical-terms.component.html +++ b/src/SIL.XForge.Scripture/ClientApp/src/app/translate/biblical-terms/biblical-terms.component.html @@ -83,8 +83,12 @@ - @if (isLoaded) { + @if (isLoaded && appOnline && biblicalTermsLoaded) { {{ t("not_found") }} + } @else if (appOnline && !biblicalTermsLoaded) { + {{ t("loading") }} + } @else if (isLoaded && !biblicalTermsLoaded) { + {{ t("offline") }} } diff --git a/src/SIL.XForge.Scripture/ClientApp/src/app/translate/biblical-terms/biblical-terms.component.spec.ts b/src/SIL.XForge.Scripture/ClientApp/src/app/translate/biblical-terms/biblical-terms.component.spec.ts index d77036e1e3..e3ac083f77 100644 --- a/src/SIL.XForge.Scripture/ClientApp/src/app/translate/biblical-terms/biblical-terms.component.spec.ts +++ b/src/SIL.XForge.Scripture/ClientApp/src/app/translate/biblical-terms/biblical-terms.component.spec.ts @@ -27,7 +27,10 @@ import { anything, capture, instance, mock, verify, when } from 'ts-mockito'; import { GenericDialogComponent, GenericDialogOptions } from 'xforge-common/generic-dialog/generic-dialog.component'; import { I18nService } from 'xforge-common/i18n.service'; import { Locale } from 'xforge-common/models/i18n-locale'; +import { OnlineStatusService } from 'xforge-common/online-status.service'; import { QueryParameters } from 'xforge-common/query-parameters'; +import { TestOnlineStatusModule } from 'xforge-common/test-online-status.module'; +import { TestOnlineStatusService } from 'xforge-common/test-online-status.service'; import { TestRealtimeModule } from 'xforge-common/test-realtime.module'; import { TestRealtimeService } from 'xforge-common/test-realtime.service'; import { configureTestingModule, TestTranslocoModule } from 'xforge-common/test-utils'; @@ -51,12 +54,19 @@ const mockedUserService = mock(UserService); describe('BiblicalTermsComponent', () => { configureTestingModule(() => ({ - imports: [NoopAnimationsModule, TestTranslocoModule, UICommonModule, TestRealtimeModule.forRoot(SF_TYPE_REGISTRY)], + imports: [ + NoopAnimationsModule, + TestOnlineStatusModule.forRoot(), + TestTranslocoModule, + UICommonModule, + TestRealtimeModule.forRoot(SF_TYPE_REGISTRY) + ], providers: [ { provide: I18nService, useMock: mockedI18nService }, { provide: MatDialog, useMock: mockedMatDialog }, { provide: SFProjectService, useMock: mockedProjectService }, - { provide: UserService, useMock: mockedUserService } + { provide: UserService, useMock: mockedUserService }, + { provide: OnlineStatusService, useClass: TestOnlineStatusService } ] })); @@ -376,6 +386,14 @@ describe('BiblicalTermsComponent', () => { expect(env.notFoundMessage.length).toBe(1); })); + it('should show the offline message if not connected to internet', fakeAsync(() => { + const env = new TestEnvironment('project01', 1, 1, '4'); + env.setupProjectData('en'); + env.onlineStatus = false; + env.wait(); + expect(env.offlineMessage.length).toBe(1); + })); + it('should not show the not found message when loading the component', fakeAsync(() => { const env = new TestEnvironment(undefined, 1, 1, '4'); env.wait(); @@ -391,6 +409,9 @@ class TestEnvironment { readonly mockedDialogRef = mock, GenericDialogOptions>>(MatDialogRef); readonly realtimeService: TestRealtimeService = TestBed.inject(TestRealtimeService); readonly locale$: BehaviorSubject = new BehaviorSubject({} as Locale); + readonly testOnlineStatusService: TestOnlineStatusService = TestBed.inject( + OnlineStatusService + ) as TestOnlineStatusService; private openNoteDialogs: MockNoteDialogRef[] = []; @@ -424,6 +445,7 @@ class TestEnvironment { when(mockedMatDialog.open(GenericDialogComponent, anything())).thenReturn(instance(this.mockedDialogRef)); when(this.mockedDialogRef.afterClosed()).thenReturn(of()); when(mockedMatDialog.openDialogs).thenCall(() => this.openNoteDialogs); + this.testOnlineStatusService.setIsOnline(true); this.fixture = TestBed.createComponent(BiblicalTermsComponent); this.component = this.fixture.componentInstance; @@ -467,6 +489,16 @@ class TestEnvironment { return this.fixture.nativeElement.querySelectorAll('.not-found'); } + get offlineMessage(): NodeList { + return this.fixture.nativeElement.querySelectorAll('.offline-message'); + } + + set onlineStatus(hasConnection: boolean) { + this.testOnlineStatusService.setIsOnline(hasConnection); + tick(); + this.fixture.detectChanges(); + } + getBiblicalTermDoc(projectId: string, dataId: string): BiblicalTermDoc { return this.realtimeService.get(BiblicalTermDoc.COLLECTION, getBiblicalTermDocId(projectId, dataId)); } diff --git a/src/SIL.XForge.Scripture/ClientApp/src/app/translate/biblical-terms/biblical-terms.component.ts b/src/SIL.XForge.Scripture/ClientApp/src/app/translate/biblical-terms/biblical-terms.component.ts index 7319b41549..f820609585 100644 --- a/src/SIL.XForge.Scripture/ClientApp/src/app/translate/biblical-terms/biblical-terms.component.ts +++ b/src/SIL.XForge.Scripture/ClientApp/src/app/translate/biblical-terms/biblical-terms.component.ts @@ -23,6 +23,7 @@ import { DialogService } from 'xforge-common/dialog.service'; import { I18nService } from 'xforge-common/i18n.service'; import { RealtimeQuery } from 'xforge-common/models/realtime-query'; import { NoticeService } from 'xforge-common/notice.service'; +import { OnlineStatusService } from 'xforge-common/online-status.service'; import { UICommonModule } from 'xforge-common/ui-common.module'; import { UserService } from 'xforge-common/user.service'; import { objectId } from 'xforge-common/utils'; @@ -180,6 +181,7 @@ export class BiblicalTermsComponent extends DataLoadingComponent implements OnDe readonly columnsToDisplay = ['term', 'category', 'gloss', 'renderings', 'id']; readonly rangeFilters: RangeFilter[] = ['current_verse', 'current_chapter', 'current_book']; rows: Row[] = []; + biblicalTermsLoaded: boolean = false; private biblicalTermQuery?: RealtimeQuery; private biblicalTermSub?: Subscription; @@ -202,6 +204,7 @@ export class BiblicalTermsComponent extends DataLoadingComponent implements OnDe noticeService: NoticeService, readonly i18n: I18nService, private readonly dialogService: DialogService, + private readonly onlineStatusService: OnlineStatusService, private readonly projectService: SFProjectService, private readonly userService: UserService ) { @@ -240,6 +243,10 @@ export class BiblicalTermsComponent extends DataLoadingComponent implements OnDe this.verse$.next(verse); } + get appOnline(): boolean { + return this.onlineStatusService.isOnline && this.onlineStatusService.isBrowserOnline; + } + get selectedCategory(): string { // To stop visual glitches in the dropdown while the categories are loading, return the category as all if (this.categoriesLoading) { @@ -307,6 +314,7 @@ export class BiblicalTermsComponent extends DataLoadingComponent implements OnDe this.loadingStarted(); this.categoriesLoading = true; const biblicalTermsAndNotesChanges$: Observable = await this.getBiblicalTermsAndNotesChanges(projectId); + this.biblicalTermSub?.unsubscribe(); this.biblicalTermSub = this.subscribe( @@ -322,6 +330,12 @@ export class BiblicalTermsComponent extends DataLoadingComponent implements OnDe this.categoriesLoading = false; } ); + + if (!this.appOnline && biblicalTermsAndNotesChanges$.pipe(filter(val => val == null))) { + this.loadingFinished(); + } else { + this.biblicalTermsLoaded = true; + } }); } @@ -488,6 +502,7 @@ export class BiblicalTermsComponent extends DataLoadingComponent implements OnDe ]); // Return a merged observable to monitor changes + return merge( this.biblicalTermQuery.ready$.pipe(filter(isReady => isReady)), this.biblicalTermQuery.remoteChanges$, 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 a10446ef46..9b4b9235c9 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 @@ -34,8 +34,10 @@ "current_verse": "Current Verse", "edit_renderings": "Edit Renderings", "gloss": "Gloss", + "loading": "Loading...", "not_found": "No Biblical Terms match your criteria.", "notes": "Notes", + "offline": "Currently unavailable offline. Please connect to the internet, once loaded you can continue viewing them offline.", "renderings": "Renderings", "settings": "Settings", "show": "Show",