From 096d09c6460310b056c881f9d918e5c8e06cf627 Mon Sep 17 00:00:00 2001 From: minottic Date: Wed, 28 Feb 2024 16:40:22 +0100 Subject: [PATCH] Add spinner when api call is not complete --- .../logbook/core/scroll-base.service.spec.ts | 15 ++++++++++++ .../app/logbook/core/scroll-base.service.ts | 24 ++++++++++++++++++- .../search-window/search-window.component.css | 10 +++++++- .../search-window.component.html | 1 + .../search-window.component.spec.ts | 11 ++++++++- .../search-window/search-window.component.ts | 16 +++++++++---- ...ogbook-icon-scroll-service.service.spec.ts | 7 ++++++ .../src/app/overview/overview.component.css | 8 +++++++ .../src/app/overview/overview.component.html | 1 + .../app/overview/overview.component.spec.ts | 3 ++- scilog/src/app/overview/overview.component.ts | 12 ++++++++-- 11 files changed, 98 insertions(+), 10 deletions(-) diff --git a/scilog/src/app/logbook/core/scroll-base.service.spec.ts b/scilog/src/app/logbook/core/scroll-base.service.spec.ts index bebcb6bd..d6ffd705 100644 --- a/scilog/src/app/logbook/core/scroll-base.service.spec.ts +++ b/scilog/src/app/logbook/core/scroll-base.service.spec.ts @@ -1,6 +1,7 @@ import { TestBed } from '@angular/core/testing'; import { ScrollBaseService } from './scroll-base.service'; +import { IDatasource } from 'ngx-ui-scroll'; describe('ScrollBaseService', () => { let service: ScrollBaseService; @@ -15,4 +16,18 @@ describe('ScrollBaseService', () => { it('should be created', () => { expect(service).toBeTruthy(); }); + + it('should decorate with isLoadedDecorator', async () => { + const toDecorate = async (index, count, config) => index + count + config; + const decorated = await service.isLoadedDecorator(toDecorate)(1, 2, 3); + expect(decorated).toEqual(6); + }); + + it('should reset isLoaded flag after reset', async () => { + service.datasource = { adapter: { reset: async () => ({}) } } as IDatasource; + service.isLoaded = true; + service.reset(); + expect(service.isLoaded).toEqual(false); + }); + }); diff --git a/scilog/src/app/logbook/core/scroll-base.service.ts b/scilog/src/app/logbook/core/scroll-base.service.ts index cdef7eb5..b8a8822f 100644 --- a/scilog/src/app/logbook/core/scroll-base.service.ts +++ b/scilog/src/app/logbook/core/scroll-base.service.ts @@ -8,8 +8,11 @@ export class ScrollBaseService { datasource: IDatasource = null; startIndex = 0; config: WidgetItemConfig = null; + isLoaded = false; - constructor() { } + constructor() { + this.getDataBuffer = this.isLoadedDecorator(this.getDataBuffer.bind(this)); + } public async initialize(config: WidgetItemConfig) { // console.log("Config: ", this.config); @@ -64,6 +67,24 @@ export class ScrollBaseService { throw new Error("Abstract method needs to be implemented in derived class.") } + isLoadedDecorator(func: Function) { + const decorated = async (index: number, count: number, config: any) => { + this.isLoaded = false; + let data; + try { + data = await func(index, count, config); + } + catch { + console.log('scroller get data returned an error'); + } + finally { + this.isLoaded = true; + return data; + } + } + return decorated; + } + async updateViewportEstimate() { await this.datasource.adapter.relax(); this.datasource.adapter.check(); @@ -75,6 +96,7 @@ export class ScrollBaseService { reset() { if (this.datasource != null) { + this.isLoaded = false; this.datasource.adapter.reset(); } } diff --git a/scilog/src/app/logbook/core/search-window/search-window.component.css b/scilog/src/app/logbook/core/search-window/search-window.component.css index 7d166bf2..4edbc05d 100644 --- a/scilog/src/app/logbook/core/search-window/search-window.component.css +++ b/scilog/src/app/logbook/core/search-window/search-window.component.css @@ -97,4 +97,12 @@ border: unset; padding: 4px; margin-left: 4px; -} \ No newline at end of file +} + +.spinner { + position: fixed; + z-index: 1031; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} diff --git a/scilog/src/app/logbook/core/search-window/search-window.component.html b/scilog/src/app/logbook/core/search-window/search-window.component.html index 79257645..81b16714 100644 --- a/scilog/src/app/logbook/core/search-window/search-window.component.html +++ b/scilog/src/app/logbook/core/search-window/search-window.component.html @@ -61,6 +61,7 @@
Logbook view: +
{ it('should create', () => { expect(component).toBeTruthy(); }); + + it('should call _prepareConfig on searchString emission', fakeAsync(() => { + const prepareConfigSpy = spyOn(component, '_prepareConfig'); + component.searchString = ''; + component.searchString = 'someSearch'; + tick(501); + expect(prepareConfigSpy).toHaveBeenCalledTimes(1); + })); + }); diff --git a/scilog/src/app/logbook/core/search-window/search-window.component.ts b/scilog/src/app/logbook/core/search-window/search-window.component.ts index 4bee9aba..c0da7095 100644 --- a/scilog/src/app/logbook/core/search-window/search-window.component.ts +++ b/scilog/src/app/logbook/core/search-window/search-window.component.ts @@ -9,6 +9,7 @@ import { LogbookInfoService } from '@shared/logbook-info.service'; import { TagService } from '@shared/tag.service'; import { ScrollToElementService } from '@shared/scroll-to-element.service'; import { Hotkeys } from '@shared/hotkeys.service'; +import { animate, style, transition, trigger } from '@angular/animations'; interface SearchResult { location: string[], @@ -23,7 +24,15 @@ interface SearchResult { selector: 'search-window', templateUrl: './search-window.component.html', styleUrls: ['./search-window.component.css'], - providers: [SearchScrollService] + providers: [SearchScrollService], + animations: [ + trigger('spinner', [ + transition(':enter', [ + style({opacity: 0}), + animate('1ms 0.2s ease-out', style({opacity: 1})) + ]) + ]), + ] }) export class SearchWindowComponent implements OnInit { @@ -65,13 +74,12 @@ export class SearchWindowComponent implements OnInit { this.subscriptions.push(this.searchStringSubject.pipe(debounceTime(500)).subscribe(() => { if (this._searchString.length > 0) { this.showResults = !this.showHelp; + this.searchScrollService.config = this._prepareConfig(); + this.searchScrollService.reset(); } else { this.showHelp = true; this.showResults = false; } - this.searchScrollService.config = this._prepareConfig(); - this.searchScrollService.reset(); - })); this._initialize_help(); diff --git a/scilog/src/app/overview/logbook-icon-scroll-service.service.spec.ts b/scilog/src/app/overview/logbook-icon-scroll-service.service.spec.ts index eada2641..1447b5ea 100644 --- a/scilog/src/app/overview/logbook-icon-scroll-service.service.spec.ts +++ b/scilog/src/app/overview/logbook-icon-scroll-service.service.spec.ts @@ -28,4 +28,11 @@ describe('LogbookIconScrollServiceService', () => { expect(await service.getData(0, 10, {})).toEqual([[1, 2, 3], [4, 5, 6], [7]]); }); + it('should test getDataBuffer after decoration', async () => { + expect(service['isLoaded']).toEqual(false); + spyOn(service, 'getData').and.resolveTo([]); + await service.getDataBuffer(1, 2, 3); + expect(service['isLoaded']).toEqual(true); + }); + }); diff --git a/scilog/src/app/overview/overview.component.css b/scilog/src/app/overview/overview.component.css index 222b0d11..1f186ac7 100644 --- a/scilog/src/app/overview/overview.component.css +++ b/scilog/src/app/overview/overview.component.css @@ -38,3 +38,11 @@ mat-button-toggle-group { transform: scale(75%) translate(18px, -50%); margin-right: 0; } + +.spinner { + position: fixed; + z-index: 1031; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} diff --git a/scilog/src/app/overview/overview.component.html b/scilog/src/app/overview/overview.component.html index b05ca477..4781cf5d 100644 --- a/scilog/src/app/overview/overview.component.html +++ b/scilog/src/app/overview/overview.component.html @@ -17,6 +17,7 @@

Logbooks

view_headline
+
diff --git a/scilog/src/app/overview/overview.component.spec.ts b/scilog/src/app/overview/overview.component.spec.ts index 7b962301..4c99d529 100644 --- a/scilog/src/app/overview/overview.component.spec.ts +++ b/scilog/src/app/overview/overview.component.spec.ts @@ -11,6 +11,7 @@ import { RouterTestingModule } from '@angular/router/testing'; import {Pipe, PipeTransform} from '@angular/core'; import { Logbooks } from '@model/logbooks'; import { ResizedEvent } from '@shared/directives/resized.directive'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; @Pipe({name: 'logbookSearch'}) class LogbookSearchMockPipe implements PipeTransform { @@ -47,7 +48,7 @@ describe('OverviewComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ declarations: [ OverviewComponent, LogbookSearchMockPipe], - imports: [MatDialogModule, RouterTestingModule], + imports: [MatDialogModule, RouterTestingModule, BrowserAnimationsModule], providers: [ { provide: MAT_DIALOG_DATA, useValue: {} }, {provide: LogbookInfoService, useValue: logbookInfoSpy}, diff --git a/scilog/src/app/overview/overview.component.ts b/scilog/src/app/overview/overview.component.ts index 33f260d5..42d339c9 100644 --- a/scilog/src/app/overview/overview.component.ts +++ b/scilog/src/app/overview/overview.component.ts @@ -12,6 +12,7 @@ import { LogbookDataService } from '@shared/remote-data.service'; import { LogbookIconScrollService } from './logbook-icon-scroll-service.service'; import { debounceTime } from 'rxjs/operators'; import { ResizedEvent } from '@shared/directives/resized.directive'; +import { animate, style, transition, trigger } from '@angular/animations'; enum ContentType { COLLECTION = 'collection', @@ -24,8 +25,15 @@ export type MatCardType = 'logbook-module' | 'logbook-headline'; selector: 'app-overview', templateUrl: './overview.component.html', styleUrls: ['./overview.component.css'], - providers: [LogbookIconScrollService] - + providers: [LogbookIconScrollService], + animations: [ + trigger('spinner', [ + transition(':enter', [ + style({opacity: 0}), + animate('1ms 0.2s ease-out', style({opacity: 1})) + ]) + ]), + ] }) export class OverviewComponent implements OnInit {