All files / app/services/app-update version-check.service.ts

95.12% Statements 39/41
90% Branches 9/10
100% Functions 9/9
94.87% Lines 37/39

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 754x 4x 4x 4x 4x 4x   4x   4x     4x 9x 9x 9x 9x 9x   9x     9x 9x     8x 1x     7x       7x   7x     7x 6x 6x       7x     8x 7x 7x     6x 3x       3x 3x           14x 14x   2x 2x          
import { environment } from '../../../environments/environment';
import { DOCUMENT } from '@angular/common';
import { HttpClient } from '@angular/common/http';
import { DestroyRef, inject, Injectable } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { LoggerService, WINDOW } from '@drevo-web/core';
import { VersionInfo } from '@drevo-web/shared';
import { catchError, filter, of, Observable, Subject, switchMap, timer } from 'rxjs';
 
const VERSION_URL = '/version.json';
 
@Injectable({ providedIn: 'root' })
export class VersionCheckService {
    private readonly http = inject(HttpClient);
    private readonly document = inject(DOCUMENT);
    private readonly window = inject(WINDOW);
    private readonly logger = inject(LoggerService).withContext('VersionCheckService');
    private readonly destroyRef = inject(DestroyRef);
 
    private started = false;
    private currentVersion: string | undefined;
 
    private readonly _newVersionAvailable$ = new Subject<VersionInfo>();
    readonly newVersionAvailable$ = this._newVersionAvailable$.asObservable();
 
    startPolling(): void {
        if (!this.window) {
            return;
        }
 
        Iif (this.started) {
            this.logger.warn('startPolling() called more than once — ignoring');
            return;
        }
        this.started = true;
 
        this.fetchVersion()
            .pipe(takeUntilDestroyed(this.destroyRef))
            .subscribe(info => {
                if (info) {
                    this.currentVersion = info.version;
                    this.logger.info('Initial version loaded', { version: info.version });
                }
            });
 
        timer(environment.versionCheckIntervalMs, environment.versionCheckIntervalMs)
            .pipe(
                takeUntilDestroyed(this.destroyRef),
                filter(() => this.document.visibilityState === 'visible'),
                switchMap(() => this.fetchVersion()),
                filter((info): info is VersionInfo => info !== undefined),
            )
            .subscribe(info => {
                if (this.currentVersion && info.version !== this.currentVersion) {
                    this.logger.info('New version detected', {
                        oldVersion: this.currentVersion,
                        newVersion: info.version,
                    });
                    this._newVersionAvailable$.next(info);
                    this.currentVersion = info.version;
                }
            });
    }
 
    private fetchVersion(): Observable<VersionInfo | undefined> {
        const url = `${VERSION_URL}?_=${Date.now()}`;
        return this.http.get<VersionInfo>(url).pipe(
            catchError(err => {
                this.logger.warn('Failed to fetch version.json', { error: err });
                return of(undefined);
            }),
        );
    }
}