Error handling:
- catch QuotaExceededError - add generic error message to the spinner
This commit is contained in:
parent
a450a1dff5
commit
619a698272
@ -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]
|
||||
})
|
||||
|
@ -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;
|
||||
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 {
|
||||
|
36
client/src/app/core/core-services/error.service.ts
Normal file
36
client/src/app/core/core-services/error.service.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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' });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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>
|
||||
|
@ -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,
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user