hide disconnected indicator when request passed
This commit is contained in:
parent
1febd714cd
commit
f353bd9b50
@ -1,9 +1,10 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { Subject } from 'rxjs';
|
||||
import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http';
|
||||
import { LoadingService } from '../loading';
|
||||
import { filter, mapTo } from 'rxjs/operators';
|
||||
import { filter, map, skip } from 'rxjs/operators';
|
||||
import { NavigationStart, Router } from '@angular/router';
|
||||
import { shareLast } from '../utils';
|
||||
|
||||
const BANDWIDTH_LIMIT_EXCEEDED = 509 as const;
|
||||
const OFFLINE_STATUSES = [
|
||||
@ -17,9 +18,14 @@ const isOffline = (error?: HttpErrorResponse) => !!error && OFFLINE_STATUSES.inc
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class ErrorService {
|
||||
readonly error$ = new BehaviorSubject<HttpErrorResponse | undefined>(undefined);
|
||||
readonly offlineError$ = this.error$.pipe(filter(isOffline), mapTo(new Event('offline')));
|
||||
readonly serverError$ = this.error$.pipe(filter(error => !isOffline(error)));
|
||||
readonly error$ = new Subject<HttpErrorResponse | undefined>();
|
||||
readonly offlineError$ = this.error$.pipe(
|
||||
skip(1),
|
||||
filter(error => !error || isOffline(error)),
|
||||
map(error => new Event(error ? 'offline' : 'online')),
|
||||
shareLast(),
|
||||
);
|
||||
readonly serverError$ = this.error$.pipe(filter(error => !error || !isOffline(error)));
|
||||
|
||||
constructor(private readonly _loadingService: LoadingService, private readonly _router: Router) {
|
||||
_router.events.pipe(filter(event => event instanceof NavigationStart)).subscribe(() => {
|
||||
@ -33,6 +39,6 @@ export class ErrorService {
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.error$.next(undefined);
|
||||
this.error$.next();
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ import { IconButtonTypes } from '../../buttons';
|
||||
import { ErrorService } from '../error.service';
|
||||
import { animate, state, style, transition, trigger } from '@angular/animations';
|
||||
import { fromEvent, merge, Observable } from 'rxjs';
|
||||
import { delay, map, mapTo } from 'rxjs/operators';
|
||||
import { delay, filter, map, mapTo } from 'rxjs/operators';
|
||||
import { shareLast } from '../../utils';
|
||||
import { connectionStatusTranslations } from '../../translations/connection-status-translations';
|
||||
|
||||
@ -29,7 +29,9 @@ export class FullPageErrorComponent {
|
||||
constructor(readonly errorService: ErrorService) {
|
||||
const offline$ = merge(fromEvent(window, 'offline'), this.errorService.offlineError$);
|
||||
const online$ = fromEvent(window, 'online').pipe(shareLast());
|
||||
const removeIndicator$ = online$.pipe(delay(3000), mapTo(undefined));
|
||||
|
||||
const errorGone$ = this.errorService.offlineError$.pipe(filter(error => !error));
|
||||
const removeIndicator$ = merge(online$, errorGone$).pipe(delay(3000), mapTo(undefined));
|
||||
|
||||
this.connectionStatus$ = merge(online$, offline$, removeIndicator$).pipe(map(event => event?.type));
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpStatusCode } from '@angular/common/http';
|
||||
import { Inject, Injectable, Optional } from '@angular/core';
|
||||
import { MonoTypeOperatorFunction, Observable, throwError, timer } from 'rxjs';
|
||||
import { MonoTypeOperatorFunction, Observable, pipe, throwError, timer } from 'rxjs';
|
||||
import { catchError, mergeMap, retryWhen, tap } from 'rxjs/operators';
|
||||
import { MAX_RETRIES_ON_SERVER_ERROR } from './max-retries.token';
|
||||
import { ErrorService } from './error.service';
|
||||
@ -16,24 +16,25 @@ function updateSeconds(seconds: number) {
|
||||
|
||||
function backoffOnServerError(maxRetries = 3): MonoTypeOperatorFunction<HttpEvent<unknown>> {
|
||||
let seconds = 0;
|
||||
return retryWhen(attempts =>
|
||||
attempts.pipe(
|
||||
tap(() => (seconds = updateSeconds(seconds))),
|
||||
mergeMap((error: HttpErrorResponse, index) => {
|
||||
if ((error.status < HttpStatusCode.InternalServerError && error.status !== 0) || index === maxRetries) {
|
||||
return throwError(error);
|
||||
}
|
||||
const timerExpiration = pipe(
|
||||
tap<HttpErrorResponse>(() => (seconds = updateSeconds(seconds))),
|
||||
mergeMap((error: HttpErrorResponse, index) => {
|
||||
if ((error.status < HttpStatusCode.InternalServerError && error.status !== 0) || index === maxRetries) {
|
||||
return throwError(error);
|
||||
}
|
||||
|
||||
console.error('An error occurred: ', error);
|
||||
console.error(`Retrying in ${seconds} seconds...`);
|
||||
return timer(seconds * 1000);
|
||||
}),
|
||||
),
|
||||
console.error('An error occurred: ', error);
|
||||
console.error(`Retrying in ${seconds} seconds...`);
|
||||
return timer(seconds * 1000);
|
||||
}),
|
||||
);
|
||||
return retryWhen(errors => errors.pipe(timerExpiration));
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ServerErrorInterceptor implements HttpInterceptor {
|
||||
private readonly _urlsWithError = new Set();
|
||||
|
||||
constructor(
|
||||
private readonly _errorService: ErrorService,
|
||||
private readonly _keycloakService: KeycloakService,
|
||||
@ -47,13 +48,22 @@ export class ServerErrorInterceptor implements HttpInterceptor {
|
||||
if (error.status === HttpStatusCode.Unauthorized) {
|
||||
this._keycloakService.logout();
|
||||
}
|
||||
|
||||
// server error
|
||||
if (error.status >= HttpStatusCode.InternalServerError) {
|
||||
this._errorService.set(error);
|
||||
this._urlsWithError.add(req.url);
|
||||
}
|
||||
|
||||
return throwError(error);
|
||||
}),
|
||||
backoffOnServerError(this._maxRetries || 3),
|
||||
tap(() => {
|
||||
if (this._urlsWithError.has(req.url)) {
|
||||
this._errorService.clear();
|
||||
this._urlsWithError.delete(req.url);
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user