fix unsubscribes on logout, better connection monitoring, and better message for lost connection

This commit is contained in:
Matt Hill
2021-08-23 15:06:24 -06:00
committed by Aiden McClelland
parent 056452faca
commit 6e82ef48ca
10 changed files with 115 additions and 97 deletions

View File

@@ -3,7 +3,7 @@ import { Storage } from '@ionic/storage-angular'
import { AuthService, AuthState } from './services/auth.service' import { AuthService, AuthState } from './services/auth.service'
import { ApiService } from './services/api/embassy-api.service' import { ApiService } from './services/api/embassy-api.service'
import { Router, RoutesRecognized } from '@angular/router' import { Router, RoutesRecognized } from '@angular/router'
import { debounceTime, distinctUntilChanged, filter, take, takeWhile } from 'rxjs/operators' import { debounceTime, distinctUntilChanged, filter, take } from 'rxjs/operators'
import { AlertController, IonicSafeString, LoadingController, ToastController } from '@ionic/angular' import { AlertController, IonicSafeString, LoadingController, ToastController } from '@ionic/angular'
import { Emver } from './services/emver.service' import { Emver } from './services/emver.service'
import { SplitPaneTracker } from './services/split-pane.service' import { SplitPaneTracker } from './services/split-pane.service'
@@ -16,6 +16,7 @@ import { StartupAlertsService } from './services/startup-alerts.service'
import { ConfigService } from './services/config.service' import { ConfigService } from './services/config.service'
import { isEmptyObject } from './util/misc.util' import { isEmptyObject } from './util/misc.util'
import { ErrorToastService } from './services/error-toast.service' import { ErrorToastService } from './services/error-toast.service'
import { Subscription } from 'rxjs'
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
@@ -29,6 +30,7 @@ export class AppComponent {
offlineToast: HTMLIonToastElement offlineToast: HTMLIonToastElement
serverName: string serverName: string
unreadCount: number unreadCount: number
subscriptions: Subscription[] = []
appPages = [ appPages = [
{ {
title: 'Services', title: 'Services',
@@ -97,23 +99,28 @@ export class AppComponent {
if (this.router.url.startsWith('/login')) { if (this.router.url.startsWith('/login')) {
this.router.navigate([''], { replaceUrl: true }) this.router.navigate([''], { replaceUrl: true })
} }
// start the connection monitor
this.connectionService.start(auth) this.subscriptions = [
// watch connection to display connectivity issues // start the connection monitor
this.watchConnection(auth) ...this.connectionService.start(),
// // watch router to highlight selected menu item // watch connection to display connectivity issues
this.watchRouter(auth) this.watchConnection(),
// // watch status to display/hide maintenance page // // watch router to highlight selected menu item
this.watchStatus(auth) this.watchRouter(),
// // watch version to refresh browser window // // watch status to display/hide maintenance page
this.watchVersion(auth) this.watchStatus(),
// // watch unread notification count to display toast // // watch version to refresh browser window
this.watchNotifications(auth) this.watchVersion(),
// // run startup alerts // // watch unread notification count to display toast
this.startupAlertsService.runChecks() this.watchNotifications(),
// // run startup alerts
this.startupAlertsService.runChecks(),
]
}) })
// UNVERIFIED // UNVERIFIED
} else if (auth === AuthState.UNVERIFIED) { } else if (auth === AuthState.UNVERIFIED) {
this.subscriptions.forEach(sub => sub.unsubscribe())
this.subscriptions = []
this.showMenu = false this.showMenu = false
this.patch.stop() this.patch.stop()
this.storage.clear() this.storage.clear()
@@ -136,12 +143,11 @@ export class AppComponent {
window.open(url, '_blank') window.open(url, '_blank')
} }
private watchConnection (auth: AuthState): void { private watchConnection (): Subscription {
this.connectionService.watchFailure$() return this.connectionService.watchFailure$()
.pipe( .pipe(
distinctUntilChanged(), distinctUntilChanged(),
debounceTime(500), debounceTime(500),
takeWhile(() => auth === AuthState.VERIFIED),
) )
.subscribe(async connectionFailure => { .subscribe(async connectionFailure => {
if (connectionFailure === ConnectionFailure.None) { if (connectionFailure === ConnectionFailure.None) {
@@ -157,7 +163,7 @@ export class AppComponent {
message = 'Phone or computer has no network connection.' message = 'Phone or computer has no network connection.'
break break
case ConnectionFailure.Diagnosing: case ConnectionFailure.Diagnosing:
message = new IonicSafeString('Running network diagnostics <ion-spinner name="dots"></ion-spinner>') message = new IonicSafeString('Running network diagnostics <ion-spinner style="padding: 0; margin: 0" name="dots"></ion-spinner>')
break break
case ConnectionFailure.Embassy: case ConnectionFailure.Embassy:
message = 'Embassy appears to be offline.' message = 'Embassy appears to be offline.'
@@ -180,11 +186,10 @@ export class AppComponent {
}) })
} }
private watchRouter (auth: AuthState): void { private watchRouter (): Subscription {
this.router.events return this.router.events
.pipe( .pipe(
filter((e: RoutesRecognized) => !!e.urlAfterRedirects), filter((e: RoutesRecognized) => !!e.urlAfterRedirects),
takeWhile(() => auth === AuthState.VERIFIED),
) )
.subscribe(e => { .subscribe(e => {
const appPageIndex = this.appPages.findIndex( const appPageIndex = this.appPages.findIndex(
@@ -194,11 +199,8 @@ export class AppComponent {
}) })
} }
private watchStatus (auth: AuthState): void { private watchStatus (): Subscription {
this.patch.watch$('server-info', 'status') return this.patch.watch$('server-info', 'status')
.pipe(
takeWhile(() => auth === AuthState.VERIFIED),
)
.subscribe(status => { .subscribe(status => {
const maintenance = '/maintenance' const maintenance = '/maintenance'
const route = this.router.url const route = this.router.url
@@ -213,11 +215,8 @@ export class AppComponent {
}) })
} }
private watchVersion (auth: AuthState): void { private watchVersion (): Subscription {
this.patch.watch$('server-info', 'version') return this.patch.watch$('server-info', 'version')
.pipe(
takeWhile(() => auth === AuthState.VERIFIED),
)
.subscribe(version => { .subscribe(version => {
if (this.emver.compare(this.config.version, version) !== 0) { if (this.emver.compare(this.config.version, version) !== 0) {
this.presentAlertRefreshNeeded() this.presentAlertRefreshNeeded()
@@ -225,12 +224,9 @@ export class AppComponent {
}) })
} }
private watchNotifications (auth: AuthState): void { private watchNotifications (): Subscription {
let previous: number let previous: number
this.patch.watch$('server-info', 'unread-notification-count') return this.patch.watch$('server-info', 'unread-notification-count')
.pipe(
takeWhile(() => auth === AuthState.VERIFIED),
)
.subscribe(count => { .subscribe(count => {
this.unreadCount = count this.unreadCount = count
if (previous !== undefined && count > previous) this.presentToastNotifications() if (previous !== undefined && count > previous) this.presentToastNotifications()

View File

@@ -1,9 +1,9 @@
<p <p
[style.color]="'var(--ion-color-' + rendering.color + ')'" [style.color]="disconnected ? 'gray' : 'var(--ion-color-' + rendering.color + ')'"
[style.font-size]="size" [style.font-size]="size"
[style.font-style]="style" [style.font-style]="style"
[style.font-weight]="weight" [style.font-weight]="weight"
> >
{{ rendering.display }} {{ disconnected ? 'Unknown' : rendering.display }}
<ion-spinner *ngIf="rendering.showDots" class="dots dots-small" name="dots" [color]="color"></ion-spinner> <ion-spinner *ngIf="rendering.showDots" class="dots dots-small" name="dots" [color]="color"></ion-spinner>
</p> </p>

View File

@@ -11,5 +11,6 @@ export class StatusComponent {
@Input() size?: 'small' | 'medium' | 'large' | 'x-large' = 'large' @Input() size?: 'small' | 'medium' | 'large' | 'x-large' = 'large'
@Input() style?: string = 'regular' @Input() style?: string = 'regular'
@Input() weight?: string = 'normal' @Input() weight?: string = 'normal'
@Input() disconnected?: boolean = false
} }

View File

@@ -36,8 +36,8 @@
<img *ngIf="!connectionFailure" [class]="pkg.value.bulb.class" [src]="pkg.value.bulb.img" /> <img *ngIf="!connectionFailure" [class]="pkg.value.bulb.class" [src]="pkg.value.bulb.img" />
<ion-card-header> <ion-card-header>
<status *ngIf="[PackageState.Installed, PackageState.Removing] | includes : pkg.value.entry.state" [rendering]="pkg.value.statusRendering" size="calc(8px + .4vw)" weight="bold"></status> <status *ngIf="[PackageState.Installed, PackageState.Removing] | includes : pkg.value.entry.state" [disconnected]="connectionFailure" [rendering]="pkg.value.statusRendering" size="calc(8px + .4vw)" weight="bold"></status>
<p *ngIf="[PackageState.Installing, PackageState.Updating] | includes : pkg.value.entry.state" class="installing-status"><ion-text color="primary">{{ pkg.value.entry.state | titlecase }}...{{ (pkg.value.entry['install-progress'] | installState).totalProgress }}%</ion-text></p> <p *ngIf="[PackageState.Installing, PackageState.Updating] | includes : pkg.value.entry.state" class="main-status"><ion-text color="primary">{{ pkg.value.entry.state | titlecase }}...{{ (pkg.value.entry['install-progress'] | installState).totalProgress }}%</ion-text></p>
<ion-card-title>{{ pkg.value.entry.manifest.title }}</ion-card-title> <ion-card-title>{{ pkg.value.entry.manifest.title }}</ion-card-title>
</ion-card-header> </ion-card-header>
</ion-card> </ion-card>

View File

@@ -78,7 +78,7 @@
right: 0px; right: 0px;
} }
.installing-status { .main-status {
font-size: calc(8px + .4vw); font-size: calc(8px + .4vw);
font-weight: bold; font-weight: bold;
margin: 0; margin: 0;

View File

@@ -23,54 +23,69 @@
<ion-item-divider>Status</ion-item-divider> <ion-item-divider>Status</ion-item-divider>
<ion-item> <ion-item>
<ion-label style="overflow: visible;"> <ion-label style="overflow: visible;">
<status size="x-large" weight="500" [rendering]="rendering"></status> <status [disconnected]="connectionFailure" size="x-large" weight="500" [rendering]="rendering"></status>
</ion-label> </ion-label>
<ion-button slot="end" class="action-button" *ngIf="pkg.state === PackageState.Installed && (pkg.manifest.interfaces | hasUi)" [disabled]="!(pkg.state | isLaunchable : pkg.installed.status.main.status : pkg.manifest.interfaces)" (click)="launchUiTab()"> <ion-button slot="end" class="action-button" *ngIf="pkg.state === PackageState.Installed && (pkg.manifest.interfaces | hasUi)" [disabled]="!(pkg.state | isLaunchable : pkg.installed.status.main.status : pkg.manifest.interfaces)" (click)="launchUiTab()">
<ion-icon slot="start" name="open-outline"></ion-icon> <ion-icon slot="start" name="open-outline"></ion-icon>
Open UI Open UI
</ion-button> </ion-button>
<ion-button slot="end" class="action-button" *ngIf="rendering.feStatus === FeStatus.NeedsConfig" [routerLink]="['config']"> <ng-container *ngIf="!connectionFailure">
Configure <ion-button slot="end" class="action-button" *ngIf="rendering.feStatus === FeStatus.NeedsConfig" [routerLink]="['config']">
</ion-button> Configure
<ion-button slot="end" class="action-button" *ngIf="[FeStatus.Running, FeStatus.StartingUp, FeStatus.NeedsAttention] | includes : rendering.feStatus" color="danger" (click)="stop()"> </ion-button>
Stop <ion-button slot="end" class="action-button" *ngIf="[FeStatus.Running, FeStatus.StartingUp, FeStatus.NeedsAttention] | includes : rendering.feStatus" color="danger" (click)="stop()">
</ion-button> Stop
<ion-button slot="end" class="action-button" *ngIf="rendering.feStatus === FeStatus.DependencyIssue" (click)="scrollToRequirements()"> </ion-button>
Fix <ion-button slot="end" class="action-button" *ngIf="rendering.feStatus === FeStatus.DependencyIssue" (click)="scrollToRequirements()">
</ion-button> Fix
<ion-button slot="end" class="action-button" *ngIf="rendering.feStatus === FeStatus.Stopped" color="success" (click)="tryStart()"> </ion-button>
Start <ion-button slot="end" class="action-button" *ngIf="rendering.feStatus === FeStatus.Stopped" color="success" (click)="tryStart()">
</ion-button> Start
</ion-button>
</ng-container>
</ion-item> </ion-item>
<!-- ** iff && !restoring/backing-up ** --> <!-- ** installed ** -->
<ng-container *ngIf="pkg.state === PackageState.Installed"> <ng-container *ngIf="pkg.state === PackageState.Installed">
<!-- ** iff !restoring/backing-up ** --> <!-- ** !restoring/backing-up ** -->
<ng-container *ngIf="!([PackageMainStatus.BackingUp, PackageMainStatus.Restoring] | includes : mainStatus.status); else maintenance"> <ng-container *ngIf="!([PackageMainStatus.BackingUp, PackageMainStatus.Restoring] | includes : mainStatus.status); else maintenance">
<!-- ** iff health checks ** --> <!-- ** health checks ** -->
<ng-container *ngIf="!(mainStatus.health | empty)"> <ng-container *ngIf="!(mainStatus.health | empty)">
<ion-item-divider>Health Checks</ion-item-divider> <ion-item-divider>Health Checks</ion-item-divider>
<ion-item *ngFor="let health of mainStatus.health | keyvalue : asIsOrder"> <ng-container *ngIf="connectionFailure">
<ion-spinner class="icon-spinner" color="warning" slot="start" *ngIf="['starting', 'loading'] | includes : health.value.result"></ion-spinner> <ion-item *ngFor="let health of mainStatus.health | keyvalue">
<ion-icon slot="start" *ngIf="health.value.result === 'success'" name="checkmark-outline" color="success"></ion-icon> <ion-avatar slot="start">
<ion-icon slot="start" *ngIf="health.value.result === 'failure'" name="close" color="danger"></ion-icon> <ion-skeleton-text style="width: 20px; height: 20px; border-radius: 0;"></ion-skeleton-text>
<ion-icon slot="start" *ngIf="health.value.result === 'disabled'" name="remove-outline" color="dark"></ion-icon> </ion-avatar>
<ion-label> <ion-label>
<p>{{ health.key }}</p> <ion-skeleton-text style="width: 100px; margin-bottom: 10px;"></ion-skeleton-text>
<h2>{{ health.value.result }}</h2> <ion-skeleton-text style="width: 150px; margin-bottom: 10px;"></ion-skeleton-text>
<p *ngIf="health.value.result === 'failure'"><ion-text color="danger">{{ health.value.error }}</ion-text></p> </ion-label>
</ion-label> </ion-item>
</ion-item> </ng-container>
<ng-container *ngIf="!connectionFailure">
<ion-item *ngFor="let health of mainStatus.health | keyvalue : asIsOrder">
<ion-spinner class="icon-spinner" color="warning" slot="start" *ngIf="['starting', 'loading'] | includes : health.value.result"></ion-spinner>
<ion-icon slot="start" *ngIf="health.value.result === 'success'" name="checkmark-outline" color="success"></ion-icon>
<ion-icon slot="start" *ngIf="health.value.result === 'failure'" name="close" color="danger"></ion-icon>
<ion-icon slot="start" *ngIf="health.value.result === 'disabled'" name="remove-outline" color="dark"></ion-icon>
<ion-label>
<p>{{ health.key }}</p>
<h2>{{ health.value.result }}</h2>
<p *ngIf="health.value.result === 'failure'"><ion-text color="danger">{{ health.value.error }}</ion-text></p>
</ion-label>
</ion-item>
</ng-container>
</ng-container> </ng-container>
<!-- ** always ** --> <!-- ** menu ** -->
<ion-item-divider>Menu</ion-item-divider> <ion-item-divider>Menu</ion-item-divider>
<ion-item button detail *ngFor="let button of buttons" (click)="button.action()"> <ion-item button detail *ngFor="let button of buttons" (click)="button.action()">
<ion-icon slot="start" [name]="button.icon"></ion-icon> <ion-icon slot="start" [name]="button.icon"></ion-icon>
<ion-label>{{ button.title }}</ion-label> <ion-label>{{ button.title }}</ion-label>
</ion-item> </ion-item>
<!-- ** iff dependencies ** --> <!-- ** dependencies ** -->
<ng-container *ngIf="!(pkg.installed['current-dependencies'] | empty)"> <ng-container *ngIf="!(pkg.installed['current-dependencies'] | empty)">
<ion-item-divider id="dependencies">Dependencies</ion-item-divider> <ion-item-divider id="dependencies">Dependencies</ion-item-divider>
<!-- A current-dependency is a subset of the pkg.manifest.dependencies that is currently required as determined by the service config. --> <!-- A current-dependency is a subset of the pkg.manifest.dependencies that is currently required as determined by the service config. -->

View File

@@ -10,7 +10,7 @@ import { ConfigService } from 'src/app/services/config.service'
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service' import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
import { DependencyErrorConfigUnsatisfied, DependencyErrorNotInstalled, DependencyErrorType, MainStatus, PackageDataEntry, PackageMainStatus, PackageState } from 'src/app/services/patch-db/data-model' import { DependencyErrorConfigUnsatisfied, DependencyErrorNotInstalled, DependencyErrorType, MainStatus, PackageDataEntry, PackageMainStatus, PackageState } from 'src/app/services/patch-db/data-model'
import { FEStatus, PkgStatusRendering, renderPkgStatus } from 'src/app/services/pkg-status-rendering.service' import { FEStatus, PkgStatusRendering, renderPkgStatus } from 'src/app/services/pkg-status-rendering.service'
import { ConnectionService } from 'src/app/services/connection.service' import { ConnectionFailure, ConnectionService } from 'src/app/services/connection.service'
import { ErrorToastService } from 'src/app/services/error-toast.service' import { ErrorToastService } from 'src/app/services/error-toast.service'
import { AppConfigPage } from 'src/app/modals/app-config/app-config.page' import { AppConfigPage } from 'src/app/modals/app-config/app-config.page'
@@ -24,7 +24,6 @@ export class AppShowPage {
pkg: PackageDataEntry pkg: PackageDataEntry
hideLAN: boolean hideLAN: boolean
buttons: Button[] = [] buttons: Button[] = []
connected: boolean
FeStatus = FEStatus FeStatus = FEStatus
PackageState = PackageState PackageState = PackageState
DependencyErrorType = DependencyErrorType DependencyErrorType = DependencyErrorType
@@ -32,6 +31,7 @@ export class AppShowPage {
Math = Math Math = Math
mainStatus: MainStatus mainStatus: MainStatus
PackageMainStatus = PackageMainStatus PackageMainStatus = PackageMainStatus
connectionFailure: boolean
@ViewChild(IonContent) content: IonContent @ViewChild(IonContent) content: IonContent
subs: Subscription[] = [] subs: Subscription[] = []
@@ -53,18 +53,17 @@ export class AppShowPage {
async ngOnInit () { async ngOnInit () {
this.pkgId = this.route.snapshot.paramMap.get('pkgId') this.pkgId = this.route.snapshot.paramMap.get('pkgId')
this.subs = [ this.subs = [
combineLatest([ // 1
this.patch.connected$(), this.patch.watch$('package-data', this.pkgId)
this.patch.watch$('package-data', this.pkgId), .subscribe(pkg => {
])
.subscribe(([connected, pkg]) => {
this.pkg = pkg this.pkg = pkg
this.connected = connected
this.rendering = renderPkgStatus(pkg.state, pkg.installed.status) this.rendering = renderPkgStatus(pkg.state, pkg.installed.status)
this.mainStatus = pkg.installed.status.main
}), }),
this.patch.watch$('package-data', this.pkgId, 'installed', 'status', 'main') // 2
.subscribe(main => { this.connectionService.watchFailure$()
this.mainStatus = main .subscribe(connectionFailure => {
this.connectionFailure = connectionFailure !== ConnectionFailure.None
}), }),
] ]
this.setButtons() this.setButtons()

View File

@@ -2,9 +2,9 @@ import { Injectable } from '@angular/core'
import { BehaviorSubject, combineLatest, fromEvent, merge, Subscription } from 'rxjs' import { BehaviorSubject, combineLatest, fromEvent, merge, Subscription } from 'rxjs'
import { PatchConnection, PatchDbService } from './patch-db/patch-db.service' import { PatchConnection, PatchDbService } from './patch-db/patch-db.service'
import { HttpService, Method } from './http.service' import { HttpService, Method } from './http.service'
import { distinctUntilChanged, takeWhile } from 'rxjs/operators' import { distinctUntilChanged } from 'rxjs/operators'
import { ConfigService } from './config.service' import { ConfigService } from './config.service'
import { AuthState } from './auth.service' import { pauseFor } from '../util/misc.util'
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
@@ -23,16 +23,13 @@ export class ConnectionService {
return this.connectionFailure$.asObservable() return this.connectionFailure$.asObservable()
} }
start (auth: AuthState) { start (): Subscription[] {
merge(fromEvent(window, 'online'), fromEvent(window, 'offline')) const sub1 = merge(fromEvent(window, 'online'), fromEvent(window, 'offline'))
.pipe(
takeWhile(() => auth === AuthState.VERIFIED),
)
.subscribe(event => { .subscribe(event => {
this.networkState$.next(event.type === 'online') this.networkState$.next(event.type === 'online')
}), })
combineLatest([ const sub2 = combineLatest([
// 1 // 1
this.networkState$ this.networkState$
.pipe( .pipe(
@@ -46,7 +43,6 @@ export class ConnectionService {
// 3 // 3
this.patch.watch$('server-info', 'connection-addresses') this.patch.watch$('server-info', 'connection-addresses')
.pipe( .pipe(
takeWhile(() => auth === AuthState.VERIFIED),
distinctUntilChanged(), distinctUntilChanged(),
), ),
]) ])
@@ -76,6 +72,7 @@ export class ConnectionService {
} }
} }
}) })
return [sub1, sub2]
} }
private async testAddrs (addrs: string[]): Promise<boolean> { private async testAddrs (addrs: string[]): Promise<boolean> {

View File

@@ -2,6 +2,7 @@ import { Inject, Injectable, InjectionToken } from '@angular/core'
import { Bootstrapper, PatchDB, Source, Store } from 'patch-db-client' import { Bootstrapper, PatchDB, Source, Store } from 'patch-db-client'
import { BehaviorSubject, Observable, of, Subscription } from 'rxjs' import { BehaviorSubject, Observable, of, Subscription } from 'rxjs'
import { catchError, debounceTime, finalize, map, tap } from 'rxjs/operators' import { catchError, debounceTime, finalize, map, tap } from 'rxjs/operators'
import { pauseFor } from 'src/app/util/misc.util'
import { ApiService } from '../api/embassy-api.service' import { ApiService } from '../api/embassy-api.service'
import { DataModel } from './data-model' import { DataModel } from './data-model'
@@ -40,7 +41,11 @@ export class PatchDbService {
start (): void { start (): void {
// make sure everything is stopped before initializing // make sure everything is stopped before initializing
this.stop() if (this.patchSub) {
this.patchSub.unsubscribe()
this.patchSub = undefined
}
console.log('Retrying')
try { try {
this.patchSub = this.patchDb.sync$() this.patchSub = this.patchDb.sync$()
.pipe(debounceTime(500)) .pipe(debounceTime(500))
@@ -49,10 +54,12 @@ export class PatchDbService {
this.patchConnection$.next(PatchConnection.Connected) this.patchConnection$.next(PatchConnection.Connected)
this.bootstrapper.update(cache) this.bootstrapper.update(cache)
}, },
error: e => { error: async e => {
console.error('patch-db-sync sub ERROR', e) console.error('patch-db-sync sub ERROR', e)
this.patchConnection$.next(PatchConnection.Disconnected) this.patchConnection$.next(PatchConnection.Disconnected)
// this.start() console.log('Erroring out')
await pauseFor(4000)
this.start()
}, },
complete: () => { complete: () => {
console.warn('patch-db-sync sub COMPLETE') console.warn('patch-db-sync sub COMPLETE')
@@ -64,7 +71,9 @@ export class PatchDbService {
} }
stop (): void { stop (): void {
console.log('STOPPING PATCH DB')
this.patchConnection$.next(PatchConnection.Initializing) this.patchConnection$.next(PatchConnection.Initializing)
this.patchDb.store.reset()
if (this.patchSub) { if (this.patchSub) {
this.patchSub.unsubscribe() this.patchSub.unsubscribe()
this.patchSub = undefined this.patchSub = undefined

View File

@@ -13,6 +13,7 @@ import { PatchDbService } from './patch-db/patch-db.service'
import { filter, take } from 'rxjs/operators' import { filter, take } from 'rxjs/operators'
import { isEmptyObject } from '../util/misc.util' import { isEmptyObject } from '../util/misc.util'
import { ApiService } from './api/embassy-api.service' import { ApiService } from './api/embassy-api.service'
import { Subscription } from 'rxjs'
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
@@ -60,8 +61,8 @@ export class StartupAlertsService {
// Then, the reduce fires, quickly iterating through yielding a promise (previousDisplay) to the next element // Then, the reduce fires, quickly iterating through yielding a promise (previousDisplay) to the next element
// Each promise fires more or less concurrently, so each c.check(server) is run concurrently // Each promise fires more or less concurrently, so each c.check(server) is run concurrently
// Then, since we await previousDisplay before c.display(res), each promise executing gets hung awaiting the display of the previous run // Then, since we await previousDisplay before c.display(res), each promise executing gets hung awaiting the display of the previous run
async runChecks (): Promise<void> { runChecks (): Subscription {
this.patch.watch$() return this.patch.watch$()
.pipe( .pipe(
filter(data => !isEmptyObject(data)), filter(data => !isEmptyObject(data)),
take(1), take(1),