From f0c14df08699d95e7fda70c4e7321bdda2374fd8 Mon Sep 17 00:00:00 2001 From: Milad Raeisi Date: Sat, 28 Sep 2024 21:44:04 +0400 Subject: [PATCH] Refactor update service --- .../common/update/update.component.html | 7 +- .../layout/common/update/update.component.ts | 16 ++++- src/app/services/update.service.ts | 72 +++++++++++-------- 3 files changed, 60 insertions(+), 35 deletions(-) diff --git a/src/app/layout/common/update/update.component.html b/src/app/layout/common/update/update.component.html index ec145cf..235f9ef 100644 --- a/src/app/layout/common/update/update.component.html +++ b/src/app/layout/common/update/update.component.html @@ -1,6 +1,7 @@ -@if (updateService.isNewVersionAvailable) { - -} diff --git a/src/app/layout/common/update/update.component.ts b/src/app/layout/common/update/update.component.ts index 0fa2620..0f76ca2 100644 --- a/src/app/layout/common/update/update.component.ts +++ b/src/app/layout/common/update/update.component.ts @@ -1,6 +1,7 @@ import { CommonModule, DatePipe, NgClass, NgTemplateOutlet } from '@angular/common'; import { ChangeDetectionStrategy, + ChangeDetectorRef, Component, inject, Input, @@ -30,13 +31,22 @@ import { NewVersionCheckerService } from 'app/services/update.service'; CommonModule ], }) -export class UpdateComponent { +export class UpdateComponent { @Input() tooltip: string; - constructor(public updateService:NewVersionCheckerService) { + constructor( + public updateService: NewVersionCheckerService, + private cdr: ChangeDetectorRef // Injecting ChangeDetectorRef + ) { + // Listen to the update available event and trigger change detection + this.updateService.isNewVersionAvailable$.subscribe((isAvailable) => { + if (isAvailable) { + this.cdr.detectChanges(); // Trigger change detection + } + }); } - applyUpdate(): void { + applyUpdate(): void { this.updateService.applyUpdate(); } } diff --git a/src/app/services/update.service.ts b/src/app/services/update.service.ts index 12c2982..c05f390 100644 --- a/src/app/services/update.service.ts +++ b/src/app/services/update.service.ts @@ -1,78 +1,92 @@ import { Injectable, NgZone } from '@angular/core'; -import { SwUpdate } from '@angular/service-worker'; -import { Subscription, interval } from 'rxjs'; +import { SwUpdate, VersionEvent } from '@angular/service-worker'; +import { BehaviorSubject, Subscription, interval } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class NewVersionCheckerService { - isNewVersionAvailable: boolean = false; - newVersionSubscription?: Subscription; - intervalSource = interval(15 * 60 * 1000); // every 15 mins - intervalSubscription?: Subscription; + private newVersionAvailableSubject = new BehaviorSubject(false); + isNewVersionAvailable$ = this.newVersionAvailableSubject.asObservable(); + private newVersionSubscription?: Subscription; + private intervalSource = interval(15 * 60 * 1000); + private intervalSubscription?: Subscription; constructor( private swUpdate: SwUpdate, private zone: NgZone, ) { - this.checkForUpdateOnInterval(); this.checkForUpdateOnLoad(); + this.checkForUpdateOnInterval(); } applyUpdate(): void { - // Reload the page to update to the latest version after the new version is activated this.swUpdate .activateUpdate() .then(() => document.location.reload()) .catch((error) => console.error('Failed to apply updates:', error)); } - checkForUpdateOnInterval(): void { - this.intervalSubscription?.unsubscribe(); + private checkForUpdateOnInterval(): void { + this.unsubscribeInterval(); + if (!this.swUpdate.isEnabled) { + console.log('Service worker updates are disabled.'); return; } this.zone.runOutsideAngular(() => { this.intervalSubscription = this.intervalSource.subscribe(async () => { - if (!this.isNewVersionAvailable) { - try { - this.isNewVersionAvailable = await this.swUpdate.checkForUpdate(); - console.log(this.isNewVersionAvailable ? 'A new version is available.' : 'Already on the latest version.'); - } catch (error) { - console.error('Failed to check for updates:', error); + try { + const updateAvailable = await this.swUpdate.checkForUpdate(); + console.log(updateAvailable ? 'A new version is available.' : 'Already on the latest version.'); + if (updateAvailable) { + this.newVersionAvailableSubject.next(true); } - } else { - // Check for updates at interval, which will keep the - // browser updating to latest version as long as it's being kept open. - await this.swUpdate.checkForUpdate(); + } catch (error) { + console.error('Failed to check for updates:', error); } }); }); } - checkForUpdateOnLoad(): void { - this.newVersionSubscription?.unsubscribe(); + private checkForUpdateOnLoad(): void { + this.unsubscribeNewVersion(); + if (!this.swUpdate.isEnabled) { console.log('Service worker updates are disabled for this app.'); return; } - this.newVersionSubscription = this.swUpdate.versionUpdates.subscribe((evt) => { - console.log('New version update event:'); - console.log(evt); + + this.newVersionSubscription = this.swUpdate.versionUpdates.subscribe((evt: VersionEvent) => { + console.log('New version update event:', evt); switch (evt.type) { case 'VERSION_DETECTED': console.log(`Downloading new app version: ${evt.version.hash}`); break; case 'VERSION_READY': - console.log(`Current app version: ${evt.currentVersion.hash}`); - console.log(`New app version ready for use: ${evt.latestVersion.hash}`); - this.isNewVersionAvailable = true; + console.log(`New app version is ready for use: ${evt.latestVersion.hash}`); + this.newVersionAvailableSubject.next(true); break; case 'VERSION_INSTALLATION_FAILED': - console.log(`Failed to install app version '${evt.version.hash}': ${evt.error}`); + console.error(`Failed to install app version '${evt.version.hash}': ${evt.error}`); break; + default: + console.log('Unknown version event type:', evt.type); } }); console.log('Subscribed to new version updates.'); } + + + private unsubscribeInterval(): void { + if (this.intervalSubscription) { + this.intervalSubscription.unsubscribe(); + } + } + + private unsubscribeNewVersion(): void { + if (this.newVersionSubscription) { + this.newVersionSubscription.unsubscribe(); + } + } }