RED-6802, fixed the reconnect notification changing rapidly.

This commit is contained in:
George 2023-06-09 17:10:15 +03:00
parent 8ebd5f760d
commit 58d4ece28d
2 changed files with 60 additions and 27 deletions

View File

@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component } from '@angular/core'; import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import { connectionStatusTranslations } from '../../translations'; import { connectionStatusTranslations } from '../../translations';
import { animate, state, style, transition, trigger } from '@angular/animations'; import { animate, state, style, transition, trigger } from '@angular/animations';
import { ErrorService } from '../error.service'; import { ErrorService } from '../error.service';
@ -19,6 +19,5 @@ import { ErrorService } from '../error.service';
}) })
export class ConnectionStatusComponent { export class ConnectionStatusComponent {
connectionStatusTranslations = connectionStatusTranslations; connectionStatusTranslations = connectionStatusTranslations;
protected readonly errorService = inject(ErrorService);
constructor(readonly errorService: ErrorService) {}
} }

View File

@ -1,10 +1,11 @@
import { Injectable } from '@angular/core'; import { inject, Injectable } from '@angular/core';
import { fromEvent, merge, Observable, Subject } from 'rxjs'; import { fromEvent, merge, Observable, Subject } from 'rxjs';
import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http'; import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http';
import { LoadingService } from '../loading'; import { LoadingService } from '../loading';
import { delay, filter, map } from 'rxjs/operators'; import { filter, map, tap } from 'rxjs/operators';
import { NavigationStart, Router } from '@angular/router'; import { NavigationStart, Router } from '@angular/router';
import { shareLast } from '../utils'; import { shareLast } from '../utils';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
export class CustomError { export class CustomError {
readonly label: string; readonly label: string;
@ -39,41 +40,72 @@ export class ErrorService {
readonly offline$: Observable<Event>; readonly offline$: Observable<Event>;
readonly online$: Observable<Event>; readonly online$: Observable<Event>;
readonly connectionStatus$: Observable<string | undefined>; readonly connectionStatus$: Observable<string | undefined>;
private readonly _error$ = new Subject<ErrorType>(); readonly #error$ = new Subject<ErrorType>();
readonly error$ = this._error$.pipe(filter(error => !error || !isOffline(error))); readonly error$ = this.#error$.pipe(filter(error => !error || !isOffline(error)));
private readonly _online$ = new Subject(); readonly #online$ = new Subject();
readonly #loadingService = inject(LoadingService);
readonly #router = inject(Router);
readonly #clearNotification$ = new Subject<undefined>();
// eslint-disable-next-line no-undef
#timeout: NodeJS.Timeout | undefined;
constructor(private readonly _loadingService: LoadingService, private readonly _router: Router) { constructor() {
_router.events.pipe(filter(event => event instanceof NavigationStart)).subscribe(() => { this.#router.events
this.clear(); .pipe(
}); filter(event => event instanceof NavigationStart),
this.offline$ = this._offline(); tap(() => this.clear()),
this.online$ = this._online(); takeUntilDestroyed(),
const removeIndicator$ = this.online$.pipe( )
delay(3000), // eslint-disable-next-line rxjs/no-ignored-subscription
map(() => undefined), .subscribe();
this.offline$ = this.#offline();
this.online$ = this.#online();
this.connectionStatus$ = merge(this.online$, this.offline$, this.#clearNotification$.asObservable()).pipe(
map(event => event?.type),
filter(type => {
if (type === 'online') {
this.#setOnlineTimeout();
return false;
}
this.#clearOnlineTimeout();
return true;
}),
// used display-online when I manually generate a new event to bypass the above filter
map(type => (type === 'display-online' ? 'online' : type)),
); );
this.connectionStatus$ = merge(this.online$, this.offline$, removeIndicator$).pipe(map(event => event?.type));
} }
set(error: ErrorType): void { set(error: ErrorType): void {
this._loadingService.stop(); this.#loadingService.stop();
this._error$.next(error); this.#error$.next(error);
} }
setOnline(): void { setOnline(): void {
this._online$.next(true); this.#online$.next(true);
} }
clear(): void { clear(): void {
this._error$.next(undefined); this.#error$.next(undefined);
} }
private _offline() { #setOnlineTimeout() {
this.#timeout ??= setTimeout(() => {
this.#online$.next(new Event('display-online'));
setTimeout(() => this.#clearNotification$.next(undefined), 3000);
}, 3000);
}
#clearOnlineTimeout() {
clearTimeout(this.#timeout);
this.#timeout = undefined;
}
#offline() {
return merge( return merge(
fromEvent(window, 'offline'), fromEvent(window, 'offline'),
this._error$.pipe( this.#error$.pipe(
filter(isOffline), filter(isOffline),
map(() => new Event('offline')), map(() => new Event('offline')),
shareLast(), shareLast(),
@ -81,7 +113,9 @@ export class ErrorService {
); );
} }
private _online() { #online() {
return merge(fromEvent(window, 'online'), this._online$.pipe(map(() => new Event('online')))).pipe(shareLast()); return merge(fromEvent(window, 'online'), this.#online$.pipe(map(v => (v instanceof Event ? v : new Event('online'))))).pipe(
shareLast(),
);
} }
} }