78 lines
2.7 KiB
TypeScript
78 lines
2.7 KiB
TypeScript
import { Injectable } from '@angular/core';
|
|
import { fromEvent, merge, Observable, Subject } from 'rxjs';
|
|
import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http';
|
|
import { LoadingService } from '../loading';
|
|
import { delay, filter, map, mapTo } from 'rxjs/operators';
|
|
import { NavigationStart, Router } from '@angular/router';
|
|
import { shareLast } from '../utils';
|
|
|
|
export class CustomError {
|
|
readonly label: string;
|
|
readonly actionLabel: string;
|
|
readonly actionIcon: string;
|
|
readonly message?: string;
|
|
readonly action?: () => void;
|
|
|
|
constructor(label: string, actionLabel: string, actionIcon: string, message?: string, action?: () => void) {
|
|
this.label = label;
|
|
this.actionLabel = actionLabel;
|
|
this.actionIcon = actionIcon;
|
|
this.message = message;
|
|
this.action = action;
|
|
}
|
|
}
|
|
|
|
export type ErrorType = HttpErrorResponse | CustomError | undefined;
|
|
|
|
const BANDWIDTH_LIMIT_EXCEEDED = 509 as const;
|
|
const OFFLINE_STATUSES = [
|
|
HttpStatusCode.BadGateway,
|
|
HttpStatusCode.ServiceUnavailable,
|
|
HttpStatusCode.GatewayTimeout,
|
|
BANDWIDTH_LIMIT_EXCEEDED,
|
|
] as const;
|
|
|
|
const isOffline = (error?: ErrorType) => error instanceof HttpErrorResponse && OFFLINE_STATUSES.includes(error.status);
|
|
|
|
@Injectable({ providedIn: 'root' })
|
|
export class ErrorService {
|
|
readonly offline$: Observable<Event>;
|
|
readonly online$: Observable<Event>;
|
|
readonly connectionStatus$: Observable<string | undefined>;
|
|
private readonly _error$ = new Subject<ErrorType>();
|
|
readonly error$ = this._error$.pipe(filter(error => !error || !isOffline(error)));
|
|
private readonly _online$ = new Subject();
|
|
|
|
constructor(private readonly _loadingService: LoadingService, private readonly _router: Router) {
|
|
_router.events.pipe(filter(event => event instanceof NavigationStart)).subscribe(() => {
|
|
this.clear();
|
|
});
|
|
this.offline$ = this._offline();
|
|
this.online$ = this._online();
|
|
const removeIndicator$ = this.online$.pipe(delay(3000), mapTo(undefined));
|
|
|
|
this.connectionStatus$ = merge(this.online$, this.offline$, removeIndicator$).pipe(map(event => event?.type));
|
|
}
|
|
|
|
set(error: ErrorType): void {
|
|
this._loadingService.stop();
|
|
this._error$.next(error);
|
|
}
|
|
|
|
setOnline(): void {
|
|
this._online$.next(true);
|
|
}
|
|
|
|
clear(): void {
|
|
this._error$.next(undefined);
|
|
}
|
|
|
|
private _offline() {
|
|
return merge(fromEvent(window, 'offline'), this._error$.pipe(filter(isOffline), mapTo(new Event('offline')), shareLast()));
|
|
}
|
|
|
|
private _online() {
|
|
return merge(fromEvent(window, 'online'), this._online$.pipe(mapTo(new Event('online')))).pipe(shareLast());
|
|
}
|
|
}
|