Error handling:

- catch QuotaExceededError
- add generic error message to the spinner
This commit is contained in:
Finn Stutzenstein 2021-03-01 13:17:07 +01:00 committed by Emanuel Schütze
parent a450a1dff5
commit 619a698272
9 changed files with 96 additions and 16 deletions

View File

@ -1,5 +1,5 @@
import { HttpClientModule, HttpClientXsrfModule } from '@angular/common/http';
import { APP_INITIALIZER, NgModule } from '@angular/core';
import { APP_INITIALIZER, ErrorHandler, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ServiceWorkerModule } from '@angular/service-worker';
@ -11,6 +11,7 @@ import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { CoreModule } from './core/core.module';
import { environment } from '../environments/environment';
import { ErrorService } from './core/core-services/error.service';
import { httpInterceptorProviders } from './core/core-services/http-interceptors';
import { LoginModule } from './site/login/login.module';
import { OpenSlidesTranslateModule } from './core/translate/openslides-translate-module';
@ -47,7 +48,8 @@ export function AppLoaderFactory(appLoadService: AppLoadService): () => Promise<
],
providers: [
{ provide: APP_INITIALIZER, useFactory: AppLoaderFactory, deps: [AppLoadService], multi: true },
httpInterceptorProviders
httpInterceptorProviders,
{ provide: ErrorHandler, useClass: ErrorService }
],
bootstrap: [AppComponent]
})

View File

@ -5,6 +5,7 @@ import { Observable, Subject } from 'rxjs';
import { BaseModel, ModelConstructor } from '../../shared/models/base/base-model';
import { CollectionStringMapperService } from './collection-string-mapper.service';
import { Deferred } from '../promises/deferred';
import { OpenSlidesStatusService } from './openslides-status.service';
import { RelationCacheService } from './relation-cache.service';
import { StorageService } from './storage.service';
@ -338,7 +339,8 @@ export class DataStoreService {
public constructor(
private storageService: StorageService,
private modelMapper: CollectionStringMapperService,
private DSUpdateManager: DataStoreUpdateManagerService
private DSUpdateManager: DataStoreUpdateManagerService,
private statusService: OpenSlidesStatusService
) {}
/**
@ -662,8 +664,16 @@ export class DataStoreService {
*/
public async flushToStorage(changeId: number): Promise<void> {
this._maxChangeId = changeId;
await this.storageService.set(DataStoreService.cachePrefix + 'DS', this.jsonStore);
await this.storageService.set(DataStoreService.cachePrefix + 'maxChangeId', changeId);
try {
await this.storageService.set(DataStoreService.cachePrefix + 'DS', this.jsonStore);
await this.storageService.set(DataStoreService.cachePrefix + 'maxChangeId', changeId);
} catch (e) {
if (e?.name === 'QuotaExceededError') {
this.statusService.setTooLessLocalStorage();
} else {
throw e;
}
}
}
public print(): void {

View File

@ -0,0 +1,36 @@
import { ErrorHandler, Injectable } from '@angular/core';
import { OpenSlidesStatusService } from './openslides-status.service';
@Injectable({
providedIn: 'root'
})
export class ErrorService extends ErrorHandler {
// TODO: This service cannot be injected into other services since it is constructed twice.
public constructor(private statusService: OpenSlidesStatusService) {
super();
}
public handleError(error: any): void {
const errorInformation = {
error,
name: this.guessName(error)
};
this.statusService.currentError.next(errorInformation);
super.handleError(error);
}
private guessName(error: any): string | null {
if (!error) {
return;
}
if (error.rejection?.name) {
return error.rejection.name;
}
if (error.name) {
return error.name;
}
}
}

View File

@ -6,6 +6,11 @@ import { History } from 'app/shared/models/core/history';
import { BannerDefinition, BannerService } from '../ui-services/banner.service';
import { Deferred } from '../promises/deferred';
export interface ErrorInformation {
error: any;
name?: string;
}
/**
* Holds information about OpenSlides. This is not included into other services to
* avoid circular dependencies.
@ -18,10 +23,12 @@ export class OpenSlidesStatusService {
* in History mode, saves the history point.
*/
private history: History = null;
private bannerDefinition: BannerDefinition = {
private historyBanner: BannerDefinition = {
type: 'history'
};
private tooLessLocalStorage = false;
/**
* Returns, if OpenSlides is in the history mode.
*/
@ -35,6 +42,8 @@ export class OpenSlidesStatusService {
public isPrioritizedClient = false;
public readonly currentError = new BehaviorSubject<ErrorInformation | null>(null);
private _stable = new Deferred();
private _bootedSubject = new BehaviorSubject<boolean>(false);
@ -63,7 +72,7 @@ export class OpenSlidesStatusService {
*/
public enterHistoryMode(history: History): void {
this.history = history;
this.banner.addBanner(this.bannerDefinition);
this.banner.addBanner(this.historyBanner);
}
/**
@ -71,6 +80,13 @@ export class OpenSlidesStatusService {
*/
public leaveHistoryMode(): void {
this.history = null;
this.banner.removeBanner(this.bannerDefinition);
this.banner.removeBanner(this.historyBanner);
}
public setTooLessLocalStorage(): void {
if (!this.tooLessLocalStorage) {
this.tooLessLocalStorage = true;
this.banner.addBanner({ type: 'tooLessLocalStorage' });
}
}
}

View File

@ -30,10 +30,7 @@ export class BannerService {
public activeBanners: BehaviorSubject<BannerDefinition[]> = new BehaviorSubject<BannerDefinition[]>([]);
public constructor(/*translate: TranslateService, */ offlineBroadcastService: OfflineBroadcastService) {
/*translate.onLangChange.subscribe(() => {
this.offlineBannerDefinition.text = translate.instant(this.offlineBannerDefinition.text);
});*/
public constructor(offlineBroadcastService: OfflineBroadcastService) {
offlineBroadcastService.isOfflineObservable.subscribe(offline => {
if (offline) {
this.addBanner(this.offlineBannerDefinition);

View File

@ -3,6 +3,7 @@
class="banner"
[ngClass]="[
banner.type === 'history' ? 'history-mode-indicator' : '',
banner.type === 'tooLessLocalStorage' ? 'too-less-local-storage-indicator' : '',
banner.class ? banner.class : '',
banner.largerOnMobileView ? 'larger-on-mobile' : ''
]"
@ -13,6 +14,10 @@
<span>({{ getHistoryTimestamp() }})</span>
<a (click)="timeTravel.resumeTime()">{{ 'Exit' | translate }}</a>
</ng-container>
<ng-container *ngSwitchCase="'tooLessLocalStorage'">
<span>{{ "QuotaExceededError: The local storage's quota is too low" | translate }}</span>
<a href="https://support.openslides.com/help/de-de/7/57" target="_blank">{{ 'Click here for more information' | translate }}</a>
</ng-container>
<ng-container *ngSwitchDefault>
<a class="banner-link" [routerLink]="banner.link" [style.cursor]="banner.link ? 'pointer' : 'default'">
<mat-icon>{{ banner.icon }}</mat-icon>

View File

@ -40,7 +40,8 @@
}
}
.history-mode-indicator {
.history-mode-indicator,
.too-less-local-storage-indicator {
background: repeating-linear-gradient(45deg, #ffee00, #ffee00 10px, #070600 10px, #000000 20px);
span,

View File

@ -2,7 +2,11 @@
<div class="spinner-container">
<div>
<div class="spinner-component"></div>
<div class="spinner-text">{{ text }}</div>
<div *ngIf="!error" class="spinner-text">{{ text }}</div>
<div *ngIf="error" class="spinner-text">
{{ 'An error happened' | translate}}{{ error.name ? ': ' + error.name : ''}}.<br>
{{ 'Please contact your system administrator.' | translate }}
</div>
</div>
</div>
</os-overlay>

View File

@ -4,6 +4,7 @@ import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { Subscription } from 'rxjs';
import { ErrorInformation, OpenSlidesStatusService } from 'app/core/core-services/openslides-status.service';
import { OverlayService } from 'app/core/ui-services/overlay.service';
/**
@ -35,6 +36,8 @@ export class GlobalSpinnerComponent implements OnInit, OnDestroy {
*/
private LOADING = this.translate.instant('Loading data. Please wait ...');
public error: ErrorInformation | null = null;
/**
* Constructor
*
@ -45,8 +48,14 @@ export class GlobalSpinnerComponent implements OnInit, OnDestroy {
public constructor(
private overlayService: OverlayService,
protected translate: TranslateService,
private cd: ChangeDetectorRef
) {}
private cd: ChangeDetectorRef,
private statusService: OpenSlidesStatusService
) {
this.statusService.currentError.subscribe(error => {
this.error = error;
this.cd.markForCheck();
});
}
/**
* Init method