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 { 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 { BrowserModule } from '@angular/platform-browser';
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { ServiceWorkerModule } from '@angular/service-worker';
|
import { ServiceWorkerModule } from '@angular/service-worker';
|
||||||
@ -11,6 +11,7 @@ import { AppRoutingModule } from './app-routing.module';
|
|||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
import { CoreModule } from './core/core.module';
|
import { CoreModule } from './core/core.module';
|
||||||
import { environment } from '../environments/environment';
|
import { environment } from '../environments/environment';
|
||||||
|
import { ErrorService } from './core/core-services/error.service';
|
||||||
import { httpInterceptorProviders } from './core/core-services/http-interceptors';
|
import { httpInterceptorProviders } from './core/core-services/http-interceptors';
|
||||||
import { LoginModule } from './site/login/login.module';
|
import { LoginModule } from './site/login/login.module';
|
||||||
import { OpenSlidesTranslateModule } from './core/translate/openslides-translate-module';
|
import { OpenSlidesTranslateModule } from './core/translate/openslides-translate-module';
|
||||||
@ -47,7 +48,8 @@ export function AppLoaderFactory(appLoadService: AppLoadService): () => Promise<
|
|||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: APP_INITIALIZER, useFactory: AppLoaderFactory, deps: [AppLoadService], multi: true },
|
{ provide: APP_INITIALIZER, useFactory: AppLoaderFactory, deps: [AppLoadService], multi: true },
|
||||||
httpInterceptorProviders
|
httpInterceptorProviders,
|
||||||
|
{ provide: ErrorHandler, useClass: ErrorService }
|
||||||
],
|
],
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent]
|
||||||
})
|
})
|
||||||
|
@ -5,6 +5,7 @@ import { Observable, Subject } from 'rxjs';
|
|||||||
import { BaseModel, ModelConstructor } from '../../shared/models/base/base-model';
|
import { BaseModel, ModelConstructor } from '../../shared/models/base/base-model';
|
||||||
import { CollectionStringMapperService } from './collection-string-mapper.service';
|
import { CollectionStringMapperService } from './collection-string-mapper.service';
|
||||||
import { Deferred } from '../promises/deferred';
|
import { Deferred } from '../promises/deferred';
|
||||||
|
import { OpenSlidesStatusService } from './openslides-status.service';
|
||||||
import { RelationCacheService } from './relation-cache.service';
|
import { RelationCacheService } from './relation-cache.service';
|
||||||
import { StorageService } from './storage.service';
|
import { StorageService } from './storage.service';
|
||||||
|
|
||||||
@ -338,7 +339,8 @@ export class DataStoreService {
|
|||||||
public constructor(
|
public constructor(
|
||||||
private storageService: StorageService,
|
private storageService: StorageService,
|
||||||
private modelMapper: CollectionStringMapperService,
|
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> {
|
public async flushToStorage(changeId: number): Promise<void> {
|
||||||
this._maxChangeId = changeId;
|
this._maxChangeId = changeId;
|
||||||
await this.storageService.set(DataStoreService.cachePrefix + 'DS', this.jsonStore);
|
try {
|
||||||
await this.storageService.set(DataStoreService.cachePrefix + 'maxChangeId', changeId);
|
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 {
|
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 { BannerDefinition, BannerService } from '../ui-services/banner.service';
|
||||||
import { Deferred } from '../promises/deferred';
|
import { Deferred } from '../promises/deferred';
|
||||||
|
|
||||||
|
export interface ErrorInformation {
|
||||||
|
error: any;
|
||||||
|
name?: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds information about OpenSlides. This is not included into other services to
|
* Holds information about OpenSlides. This is not included into other services to
|
||||||
* avoid circular dependencies.
|
* avoid circular dependencies.
|
||||||
@ -18,10 +23,12 @@ export class OpenSlidesStatusService {
|
|||||||
* in History mode, saves the history point.
|
* in History mode, saves the history point.
|
||||||
*/
|
*/
|
||||||
private history: History = null;
|
private history: History = null;
|
||||||
private bannerDefinition: BannerDefinition = {
|
private historyBanner: BannerDefinition = {
|
||||||
type: 'history'
|
type: 'history'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private tooLessLocalStorage = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns, if OpenSlides is in the history mode.
|
* Returns, if OpenSlides is in the history mode.
|
||||||
*/
|
*/
|
||||||
@ -35,6 +42,8 @@ export class OpenSlidesStatusService {
|
|||||||
|
|
||||||
public isPrioritizedClient = false;
|
public isPrioritizedClient = false;
|
||||||
|
|
||||||
|
public readonly currentError = new BehaviorSubject<ErrorInformation | null>(null);
|
||||||
|
|
||||||
private _stable = new Deferred();
|
private _stable = new Deferred();
|
||||||
private _bootedSubject = new BehaviorSubject<boolean>(false);
|
private _bootedSubject = new BehaviorSubject<boolean>(false);
|
||||||
|
|
||||||
@ -63,7 +72,7 @@ export class OpenSlidesStatusService {
|
|||||||
*/
|
*/
|
||||||
public enterHistoryMode(history: History): void {
|
public enterHistoryMode(history: History): void {
|
||||||
this.history = history;
|
this.history = history;
|
||||||
this.banner.addBanner(this.bannerDefinition);
|
this.banner.addBanner(this.historyBanner);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -71,6 +80,13 @@ export class OpenSlidesStatusService {
|
|||||||
*/
|
*/
|
||||||
public leaveHistoryMode(): void {
|
public leaveHistoryMode(): void {
|
||||||
this.history = null;
|
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 activeBanners: BehaviorSubject<BannerDefinition[]> = new BehaviorSubject<BannerDefinition[]>([]);
|
||||||
|
|
||||||
public constructor(/*translate: TranslateService, */ offlineBroadcastService: OfflineBroadcastService) {
|
public constructor(offlineBroadcastService: OfflineBroadcastService) {
|
||||||
/*translate.onLangChange.subscribe(() => {
|
|
||||||
this.offlineBannerDefinition.text = translate.instant(this.offlineBannerDefinition.text);
|
|
||||||
});*/
|
|
||||||
offlineBroadcastService.isOfflineObservable.subscribe(offline => {
|
offlineBroadcastService.isOfflineObservable.subscribe(offline => {
|
||||||
if (offline) {
|
if (offline) {
|
||||||
this.addBanner(this.offlineBannerDefinition);
|
this.addBanner(this.offlineBannerDefinition);
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
class="banner"
|
class="banner"
|
||||||
[ngClass]="[
|
[ngClass]="[
|
||||||
banner.type === 'history' ? 'history-mode-indicator' : '',
|
banner.type === 'history' ? 'history-mode-indicator' : '',
|
||||||
|
banner.type === 'tooLessLocalStorage' ? 'too-less-local-storage-indicator' : '',
|
||||||
banner.class ? banner.class : '',
|
banner.class ? banner.class : '',
|
||||||
banner.largerOnMobileView ? 'larger-on-mobile' : ''
|
banner.largerOnMobileView ? 'larger-on-mobile' : ''
|
||||||
]"
|
]"
|
||||||
@ -13,6 +14,10 @@
|
|||||||
<span>({{ getHistoryTimestamp() }})</span>
|
<span>({{ getHistoryTimestamp() }})</span>
|
||||||
<a (click)="timeTravel.resumeTime()">{{ 'Exit' | translate }}</a>
|
<a (click)="timeTravel.resumeTime()">{{ 'Exit' | translate }}</a>
|
||||||
</ng-container>
|
</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>
|
<ng-container *ngSwitchDefault>
|
||||||
<a class="banner-link" [routerLink]="banner.link" [style.cursor]="banner.link ? 'pointer' : 'default'">
|
<a class="banner-link" [routerLink]="banner.link" [style.cursor]="banner.link ? 'pointer' : 'default'">
|
||||||
<mat-icon>{{ banner.icon }}</mat-icon>
|
<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);
|
background: repeating-linear-gradient(45deg, #ffee00, #ffee00 10px, #070600 10px, #000000 20px);
|
||||||
|
|
||||||
span,
|
span,
|
||||||
|
@ -2,7 +2,11 @@
|
|||||||
<div class="spinner-container">
|
<div class="spinner-container">
|
||||||
<div>
|
<div>
|
||||||
<div class="spinner-component"></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>
|
||||||
</div>
|
</div>
|
||||||
</os-overlay>
|
</os-overlay>
|
||||||
|
@ -4,6 +4,7 @@ import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
|
|||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
|
|
||||||
|
import { ErrorInformation, OpenSlidesStatusService } from 'app/core/core-services/openslides-status.service';
|
||||||
import { OverlayService } from 'app/core/ui-services/overlay.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 ...');
|
private LOADING = this.translate.instant('Loading data. Please wait ...');
|
||||||
|
|
||||||
|
public error: ErrorInformation | null = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*
|
*
|
||||||
@ -45,8 +48,14 @@ export class GlobalSpinnerComponent implements OnInit, OnDestroy {
|
|||||||
public constructor(
|
public constructor(
|
||||||
private overlayService: OverlayService,
|
private overlayService: OverlayService,
|
||||||
protected translate: TranslateService,
|
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
|
* Init method
|
||||||
|
Loading…
Reference in New Issue
Block a user