[Fix] websocket connecting and patchDB connection monitoring (#1738)

* refactor how we handle rpc responses and patchdb connection monitoring

* websockets only

* remove unused global error handlers

* chore: clear storage inside auth service

* feat: convert all global toasts to declarative approach (#1754)

* no more reference to serverID

Co-authored-by: Aiden McClelland <me@drbonez.dev>
Co-authored-by: waterplea <alexander@inkin.ru>
This commit is contained in:
Matt Hill
2022-08-22 10:53:52 -06:00
committed by GitHub
parent 70baed88f4
commit 3ddeb5fa94
101 changed files with 1177 additions and 1298 deletions

View File

@@ -1,4 +1,4 @@
import { Component, Input } from '@angular/core'
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
import { ActivatedRoute } from '@angular/router'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import {
@@ -22,6 +22,7 @@ import { hasCurrentDeps } from 'src/app/util/has-deps'
selector: 'app-actions',
templateUrl: './app-actions.page.html',
styleUrls: ['./app-actions.page.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AppActionsPage {
readonly pkgId = getPkgId(this.route)
@@ -103,7 +104,7 @@ export class AppActionsPage {
} else if (last) {
statusesStr = `${last}`
} else {
error = `There is state for which this action may be run. This is a bug. Please file an issue with the service maintainer.`
error = `There is no status for which this action may be run. This is a bug. Please file an issue with the service maintainer.`
}
const alert = await this.alertCtrl.create({
header: 'Forbidden',
@@ -158,10 +159,12 @@ export class AppActionsPage {
try {
await this.embassyApi.uninstallPackage({ id: this.pkgId })
this.embassyApi.setDbValue({
pointer: `/ack-instructions/${this.pkgId}`,
value: false,
})
this.embassyApi
.setDbValue({
pointer: `/ack-instructions/${this.pkgId}`,
value: false,
})
.catch(e => console.error('Failed to mark instructions as unseen', e))
this.navCtrl.navigateRoot('/services')
} catch (e: any) {
this.errToast.present(e)
@@ -185,7 +188,7 @@ export class AppActionsPage {
'action-id': actionId,
input,
})
this.modalCtrl.dismiss()
const successModal = await this.modalCtrl.create({
component: ActionSuccessPage,
componentProps: {
@@ -193,8 +196,8 @@ export class AppActionsPage {
},
})
setTimeout(() => successModal.present(), 400)
return true
setTimeout(() => successModal.present(), 500)
return false
} catch (e: any) {
this.errToast.present(e)
return false
@@ -218,6 +221,7 @@ interface LocalAction {
selector: 'app-actions-item',
templateUrl: './app-actions-item.component.html',
styleUrls: ['./app-actions.page.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AppActionsItemComponent {
@Input() action!: LocalAction

View File

@@ -1,9 +1,4 @@
<div
*ngIf="disconnected$ | async; else connected"
class="bulb"
[style.background-color]="'var(--ion-color-dark)'"
></div>
<ng-template #connected>
<ng-container *ngIf="connected$ | async; else disconnected">
<ion-icon
*ngIf="pkg.error; else noError"
class="warning-icon"
@@ -28,4 +23,8 @@
></div>
</ng-template>
</ng-template>
</ng-container>
<ng-template #disconnected>
<div class="bulb" [style.background-color]="'var(--ion-color-dark)'"></div>
</ng-template>

View File

@@ -12,7 +12,7 @@ export class AppListIconComponent {
@Input()
pkg!: PkgInfo
disconnected$ = this.connectionService.watchDisconnected$()
readonly connected$ = this.connectionService.connected$
constructor(private readonly connectionService: ConnectionService) {}
}

View File

@@ -11,7 +11,7 @@ import { ErrorToastService } from '@start9labs/shared'
import { AbstractMarketplaceService } from '@start9labs/marketplace'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import { from, merge, OperatorFunction, pipe, Subject } from 'rxjs'
import { catchError, mapTo, startWith, switchMap, tap } from 'rxjs/operators'
import { catchError, map, startWith, switchMap, tap } from 'rxjs/operators'
import { RecoveredInfo } from 'src/app/util/parse-data-model'
import { MarketplaceService } from 'src/app/services/marketplace.service'
@@ -103,7 +103,7 @@ function loading(
// Show notification on error
catchError(e => from(errToast.present(e))),
// Map any result to false to stop loading indicator
mapTo(false),
map(() => false),
// Start operation with true
startWith(true),
)

View File

@@ -9,12 +9,12 @@
<ion-content class="ion-padding">
<!-- loading -->
<ng-template #loading>
<ng-container *ngIf="loading else loaded">
<text-spinner text="Connecting to Embassy"></text-spinner>
</ng-template>
</ng-container>
<!-- not loading -->
<ng-container *ngIf="connected$ | async else loading">
<!-- loaded -->
<ng-template #loaded>
<app-list-empty
*ngIf="empty; else list"
class="ion-text-center ion-padding"
@@ -39,5 +39,5 @@
</ion-item-group>
</ng-container>
</ng-template>
</ng-container>
</ng-template>
</ion-content>

View File

@@ -14,12 +14,11 @@ import { parseDataModel, RecoveredInfo } from 'src/app/util/parse-data-model'
providers: [DestroyService],
})
export class AppListPage {
loading = true
pkgs: readonly PackageDataEntry[] = []
recoveredPkgs: readonly RecoveredInfo[] = []
reordering = false
readonly connected$ = this.patch.connected$
constructor(
private readonly api: ApiService,
private readonly destroy$: DestroyService,
@@ -38,6 +37,7 @@ export class AppListPage {
take(1),
map(parseDataModel),
tap(({ pkgs, recoveredPkgs }) => {
this.loading = false
this.pkgs = pkgs
this.recoveredPkgs = recoveredPkgs
}),

View File

@@ -56,8 +56,8 @@ export class AppShowPage {
readonly currentMarketplace$: Observable<Marketplace> =
this.marketplaceService.getMarketplace()
readonly altMarketplaceData$: Observable<UIMarketplaceData | undefined> =
this.marketplaceService.getAltMarketplace()
readonly altMarketplaceData$: Observable<UIMarketplaceData> =
this.marketplaceService.getAltMarketplaceData()
constructor(
private readonly route: ActivatedRoute,

View File

@@ -3,19 +3,10 @@
>
<ng-container *ngIf="checks.length">
<ion-item-divider>Health Checks</ion-item-divider>
<ng-container *ngIf="disconnected$ | async; else connected">
<ion-item *ngFor="let health of checks">
<ion-avatar slot="start">
<ion-skeleton-text class="avatar"></ion-skeleton-text>
</ion-avatar>
<ion-label>
<ion-skeleton-text class="label"></ion-skeleton-text>
<ion-skeleton-text class="description"></ion-skeleton-text>
</ion-label>
</ion-item>
</ng-container>
<ng-template #connected>
<!-- connected -->
<ng-container *ngIf="connected$ | async; else disconnected">
<ion-item *ngFor="let health of checks">
<!-- result -->
<ng-container *ngIf="health.value?.result as result; else noResult">
<ion-spinner
*ngIf="isLoading(result)"
@@ -69,6 +60,7 @@
</ion-text>
</ion-label>
</ng-container>
<!-- no result -->
<ng-template #noResult>
<ion-spinner
class="icon-spinner"
@@ -83,6 +75,18 @@
</ion-label>
</ng-template>
</ion-item>
</ng-container>
<!-- disconnected -->
<ng-template #disconnected>
<ion-item *ngFor="let health of checks">
<ion-avatar slot="start">
<ion-skeleton-text class="avatar"></ion-skeleton-text>
</ion-avatar>
<ion-label>
<ion-skeleton-text class="label"></ion-skeleton-text>
<ion-skeleton-text class="description"></ion-skeleton-text>
</ion-label>
</ion-item>
</ng-template>
</ng-container>
</ng-container>

View File

@@ -17,7 +17,7 @@ export class AppShowHealthChecksComponent {
HealthResult = HealthResult
readonly disconnected$ = this.connectionService.watchDisconnected$()
readonly connected$ = this.connectionService.connected$
constructor(private readonly connectionService: ConnectionService) {}

View File

@@ -11,7 +11,7 @@
</ion-label>
</ion-item>
<ng-container *ngIf="isInstalled && !(disconnected$ | async)">
<ng-container *ngIf="isInstalled && (connected$ | async)">
<ion-grid>
<ion-row style="padding-left: 12px">
<ion-col>

View File

@@ -37,7 +37,7 @@ export class AppShowStatusComponent {
PR = PrimaryRendering
disconnected$ = this.connectionService.watchDisconnected$()
readonly connected$ = this.connectionService.connected$
constructor(
private readonly alertCtrl: AlertController,

View File

@@ -117,10 +117,12 @@ export class ToButtonsPipe implements PipeTransform {
}
private async presentModalInstructions(pkg: PackageDataEntry) {
this.apiService.setDbValue({
pointer: `/ack-instructions/${pkg.manifest.id}`,
value: true,
})
this.apiService
.setDbValue({
pointer: `/ack-instructions/${pkg.manifest.id}`,
value: true,
})
.catch(e => console.error('Failed to mark instructions as seen', e))
const modal = await this.modalCtrl.create({
componentProps: {

View File

@@ -8,7 +8,6 @@ import {
DependencyErrorType,
PackageDataEntry,
} from 'src/app/services/patch-db/data-model'
import { exists } from '@start9labs/shared'
import { DependentInfo } from 'src/app/types/dependent-info'
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
import { ModalService } from 'src/app/services/modal.service'
@@ -49,7 +48,7 @@ export class ToDependenciesPipe implements PipeTransform {
'dependency-errors',
),
]).pipe(
filter(deps => deps.every(exists) && !!pkg.installed),
filter(deps => deps.every(Boolean) && !!pkg.installed),
map(([currentDeps, depErrors]) =>
Object.keys(currentDeps)
.filter(id => !!pkg.manifest.dependencies[id])

View File

@@ -1,7 +1,7 @@
import { Component } from '@angular/core'
import { ActivatedRoute } from '@angular/router'
import { ModalController } from '@ionic/angular'
import { debounce, exists, ErrorToastService } from '@start9labs/shared'
import { debounce, ErrorToastService } from '@start9labs/shared'
import * as yaml from 'js-yaml'
import { filter, take } from 'rxjs/operators'
import { ApiService } from 'src/app/services/api/embassy-api.service'
@@ -31,7 +31,7 @@ export class DevConfigPage {
ngOnInit() {
this.patchDb
.watch$('ui', 'dev', this.projectId, 'config')
.pipe(filter(exists), take(1))
.pipe(filter(Boolean), take(1))
.subscribe(config => {
this.code = config
})

View File

@@ -5,7 +5,6 @@ import { filter, take } from 'rxjs/operators'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import {
debounce,
exists,
ErrorToastService,
MarkdownComponent,
} from '@start9labs/shared'
@@ -20,8 +19,8 @@ import { getProjectId } from 'src/app/util/get-project-id'
export class DevInstructionsPage {
readonly projectId = getProjectId(this.route)
editorOptions = { theme: 'vs-dark', language: 'markdown' }
code: string = ''
saving: boolean = false
code = ''
saving = false
constructor(
private readonly route: ActivatedRoute,
@@ -34,7 +33,7 @@ export class DevInstructionsPage {
ngOnInit() {
this.patchDb
.watch$('ui', 'dev', this.projectId, 'instructions')
.pipe(filter(exists), take(1))
.pipe(filter(Boolean), take(1))
.subscribe(config => {
this.code = config
})

View File

@@ -5,7 +5,6 @@ import {
AlertController,
LoadingController,
ModalController,
NavController,
} from '@ionic/angular'
import {
GenericInputComponent,
@@ -17,7 +16,6 @@ import { ConfigSpec } from 'src/app/pkg-config/config-types'
import * as yaml from 'js-yaml'
import { v4 } from 'uuid'
import { DevData } from 'src/app/services/patch-db/data-model'
import { ActivatedRoute } from '@angular/router'
import { DestroyService, ErrorToastService } from '@start9labs/shared'
import { takeUntil } from 'rxjs/operators'
@@ -36,8 +34,6 @@ export class DeveloperListPage {
private readonly loadingCtrl: LoadingController,
private readonly errToast: ErrorToastService,
private readonly alertCtrl: AlertController,
private readonly navCtrl: NavController,
private readonly route: ActivatedRoute,
private readonly destroy$: DestroyService,
private readonly patch: PatchDbService,
private readonly actionCtrl: ActionSheetController,

View File

@@ -1,4 +1,4 @@
import { Component } from '@angular/core'
import { ChangeDetectionStrategy, Component } from '@angular/core'
import { ActivatedRoute } from '@angular/router'
import { LoadingController, ModalController } from '@ionic/angular'
import { GenericFormPage } from 'src/app/modals/generic-form/generic-form.page'
@@ -13,6 +13,7 @@ import { DevProjectData } from 'src/app/services/patch-db/data-model'
selector: 'developer-menu',
templateUrl: 'developer-menu.page.html',
styleUrls: ['developer-menu.page.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DeveloperMenuPage {
readonly projectId = getProjectId(this.route)

View File

@@ -3,11 +3,7 @@ import { CommonModule } from '@angular/common'
import { FormsModule } from '@angular/forms'
import { Routes, RouterModule } from '@angular/router'
import { IonicModule } from '@ionic/angular'
import {
SharedPipesModule,
EmverPipesModule,
TextSpinnerComponentModule,
} from '@start9labs/shared'
import { SharedPipesModule, EmverPipesModule } from '@start9labs/shared'
import {
FilterPackagesPipeModule,
CategoriesModule,
@@ -16,7 +12,6 @@ import {
SkeletonModule,
} from '@start9labs/marketplace'
import { BadgeMenuComponentModule } from 'src/app/components/badge-menu-button/badge-menu.component.module'
import { MarketplaceStatusModule } from '../marketplace-status/marketplace-status.module'
import { MarketplaceListPage } from './marketplace-list.page'
import { MarketplaceListContentComponent } from './marketplace-list-content/marketplace-list-content.component'
@@ -34,7 +29,6 @@ const routes: Routes = [
IonicModule,
FormsModule,
RouterModule.forChild(routes),
TextSpinnerComponentModule,
SharedPipesModule,
EmverPipesModule,
FilterPackagesPipeModule,

View File

@@ -8,14 +8,9 @@
<ion-content class="ion-padding">
<marketplace-list-content
*ngIf="connected$ | async else loading"
[localPkgs]="(localPkgs$ | async) || {}"
[pkgs]="pkgs$ | async"
[categories]="categories$ | async"
[name]="(name$ | async) || ''"
></marketplace-list-content>
<ng-template #loading>
<text-spinner text="Connecting to Embassy"></text-spinner>
</ng-template>
</ion-content>

View File

@@ -1,45 +1,30 @@
import { Component } from '@angular/core'
import { Observable } from 'rxjs'
import { filter, first, map, startWith, switchMapTo } from 'rxjs/operators'
import { exists, isEmptyObject } from '@start9labs/shared'
import {
AbstractMarketplaceService,
MarketplacePkg,
} from '@start9labs/marketplace'
import { ChangeDetectionStrategy, Component } from '@angular/core'
import { map } from 'rxjs/operators'
import { AbstractMarketplaceService } from '@start9labs/marketplace'
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
import { ConnectionService } from 'src/app/services/connection.service'
@Component({
selector: 'marketplace-list',
templateUrl: './marketplace-list.page.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class MarketplaceListPage {
readonly connected$ = this.patch.connected$
readonly connected$ = this.connectionService.connected$
readonly localPkgs$: Observable<Record<string, PackageDataEntry>> = this.patch
.watch$('package-data')
.pipe(
filter(data => exists(data) && !isEmptyObject(data)),
startWith({}),
)
readonly localPkgs$ = this.patch.watch$('package-data')
readonly categories$ = this.marketplaceService.getCategories()
readonly pkgs$: Observable<MarketplacePkg[]> = this.patch
.watch$('server-info')
.pipe(
filter(data => exists(data) && !isEmptyObject(data)),
first(),
switchMapTo(this.marketplaceService.getPackages()),
)
readonly pkgs$ = this.marketplaceService.getPackages()
readonly name$: Observable<string> = this.marketplaceService
readonly name$ = this.marketplaceService
.getMarketplace()
.pipe(map(({ name }) => name))
constructor(
private readonly patch: PatchDbService,
private readonly marketplaceService: AbstractMarketplaceService,
private readonly connectionService: ConnectionService,
) {}
}

View File

@@ -1,4 +1,4 @@
import { Component } from '@angular/core'
import { ChangeDetectionStrategy, Component } from '@angular/core'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import {
ServerNotifications,

View File

@@ -1,4 +1,4 @@
import { Component } from '@angular/core'
import { ChangeDetectionStrategy, Component } from '@angular/core'
import { ConfigService } from 'src/app/services/config.service'
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
@@ -6,6 +6,7 @@ import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
selector: 'lan',
templateUrl: './lan.page.html',
styleUrls: ['./lan.page.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class LANPage {
readonly downloadIsDisabled = !this.config.isTor()

View File

@@ -11,12 +11,9 @@
<ng-container *ngIf="ui$ | async as ui">
<ion-item-group *ngIf="server$ | async as server">
<ion-item-divider>General</ion-item-divider>
<ion-item
button
(click)="presentModalName('Embassy-' + server.id, ui.name)"
>
<ion-item button (click)="presentModalName('My Embassy', ui.name)">
<ion-label>Device Name</ion-label>
<ion-note slot="end">{{ ui.name || 'Embassy-' + server.id }}</ion-note>
<ion-note slot="end">{{ ui.name || 'My Embassy' }}</ion-note>
</ion-item>
<ion-item-divider>Marketplace</ion-item-divider>

View File

@@ -1,4 +1,4 @@
import { Component } from '@angular/core'
import { ChangeDetectionStrategy, Component } from '@angular/core'
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
import {
LoadingController,
@@ -17,6 +17,7 @@ import { LocalStorageService } from '../../../services/local-storage.service'
selector: 'preferences',
templateUrl: './preferences.page.html',
styleUrls: ['./preferences.page.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PreferencesPage {
clicks = 0

View File

@@ -5,7 +5,7 @@ import {
PipeTransform,
} from '@angular/core'
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
import { take } from 'rxjs/operators'
import { filter, take } from 'rxjs/operators'
import { PackageMainStatus } from 'src/app/services/patch-db/data-model'
import { Observable } from 'rxjs'
@@ -15,7 +15,9 @@ import { Observable } from 'rxjs'
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class BackingUpComponent {
readonly pkgs$ = this.patch.watch$('package-data').pipe(take(1))
readonly pkgs$ = this.patch
.watch$('package-data')
.pipe(filter(Boolean), take(1))
readonly backupProgress$ = this.patch.watch$(
'server-info',
'status-info',

View File

@@ -1,10 +1,12 @@
<ion-header>
<ion-toolbar>
<ion-title *ngIf="connected$ | async else loading">
{{ (ui$ | async)?.name || "Embassy-" + (server$ | async)?.id }}
<ion-title *ngIf="ui$ | async as ui; else loadingTitle">
{{ ui.name || "My Embassy" }}
</ion-title>
<ng-template #loading>
<ion-title>Loading<span class="loading-dots"></span></ion-title>
<ng-template #loadingTitle>
<ion-title>
<ion-title>Loading<span class="loading-dots"></span></ion-title>
</ion-title>
</ng-template>
<ion-buttons slot="end">
<badge-menu-button></badge-menu-button>
@@ -14,86 +16,80 @@
<ion-content class="ion-padding">
<!-- loading -->
<ng-template #spinner>
<ng-template #loading>
<text-spinner text="Connecting to Embassy"></text-spinner>
</ng-template>
<!-- not loading -->
<ng-container *ngIf="connected$ | async else spinner">
<ion-item-group *ngIf="server$ | async as server">
<div *ngFor="let cat of settings | keyvalue : asIsOrder">
<ion-item-divider>
<ion-text color="dark" *ngIf="cat.key !== 'Power'">
{{ cat.key }}
</ion-text>
<ion-text
color="dark"
*ngIf="cat.key === 'Power'"
(click)="addClick()"
>
{{ cat.key }}
</ion-text>
</ion-item-divider>
<ng-container *ngFor="let button of cat.value">
<ion-item
button
[style.display]="(button.title === 'Repair Disk' && !(showDiskRepair$ | async)) ? 'none' : 'block'"
[detail]="button.detail"
[disabled]="button.disabled | async"
(click)="button.action()"
>
<ion-icon slot="start" [name]="button.icon"></ion-icon>
<ion-label>
<h2>{{ button.title }}</h2>
<p *ngIf="button.description">{{ button.description }}</p>
<!-- loaded -->
<ion-item-group *ngIf="server$ | async as server; else loading">
<div *ngFor="let cat of settings | keyvalue : asIsOrder">
<ion-item-divider>
<ion-text color="dark" *ngIf="cat.key !== 'Power'">
{{ cat.key }}
</ion-text>
<ion-text color="dark" *ngIf="cat.key === 'Power'" (click)="addClick()">
{{ cat.key }}
</ion-text>
</ion-item-divider>
<ng-container *ngFor="let button of cat.value">
<ion-item
button
[style.display]="(button.title === 'Repair Disk' && !(showDiskRepair$ | async)) ? 'none' : 'block'"
[detail]="button.detail"
[disabled]="button.disabled$ | async"
(click)="button.action()"
>
<ion-icon slot="start" [name]="button.icon"></ion-icon>
<ion-label>
<h2>{{ button.title }}</h2>
<p *ngIf="button.description">{{ button.description }}</p>
<!-- "Create Backup" button only -->
<p *ngIf="button.title === 'Create Backup'">
<ng-container *ngIf="server['status-info'] as statusInfo">
<ion-text
color="warning"
*ngIf="!statusInfo['backup-progress'] && !statusInfo['update-progress']"
>
Last Backup: {{ server['last-backup'] ?
(server['last-backup'] | date: 'medium') : 'never' }}
</ion-text>
<span *ngIf="!!statusInfo['backup-progress']" class="inline">
<ion-spinner
color="success"
style="height: 12px; width: 12px; margin-right: 6px"
></ion-spinner>
<ion-text color="success">Backing up</ion-text>
</span>
</ng-container>
</p>
<!-- "Software Update" button only -->
<p *ngIf="button.title === 'Software Update'">
<!-- "Create Backup" button only -->
<p *ngIf="button.title === 'Create Backup'">
<ng-container *ngIf="server['status-info'] as statusInfo">
<ion-text
*ngIf="server['status-info'].updated; else notUpdated"
class="inline"
color="warning"
*ngIf="!statusInfo['backup-progress'] && !statusInfo['update-progress']"
>
Update Complete. Restart to apply changes
Last Backup: {{ server['last-backup'] ? (server['last-backup']
| date: 'medium') : 'never' }}
</ion-text>
<ng-template #notUpdated>
<ng-container *ngIf="showUpdate$ | async; else check">
<ion-text class="inline" color="success">
<ion-icon name="rocket-outline"></ion-icon>
Update Available
</ion-text>
</ng-container>
<ng-template #check>
<ion-text class="inline" color="dark">
<ion-icon name="refresh"></ion-icon>
Check for updates
</ion-text>
</ng-template>
<span *ngIf="!!statusInfo['backup-progress']" class="inline">
<ion-spinner
color="success"
style="height: 12px; width: 12px; margin-right: 6px"
></ion-spinner>
<ion-text color="success">Backing up</ion-text>
</span>
</ng-container>
</p>
<!-- "Software Update" button only -->
<p *ngIf="button.title === 'Software Update'">
<ion-text
*ngIf="server['status-info'].updated; else notUpdated"
class="inline"
color="warning"
>
Update Complete. Restart to apply changes
</ion-text>
<ng-template #notUpdated>
<ng-container *ngIf="showUpdate$ | async; else check">
<ion-text class="inline" color="success">
<ion-icon name="rocket-outline"></ion-icon>
Update Available
</ion-text>
</ng-container>
<ng-template #check>
<ion-text class="inline" color="dark">
<ion-icon name="refresh"></ion-icon>
Check for updates
</ion-text>
</ng-template>
</p>
</ion-label>
</ion-item>
</ng-container>
</div>
</ion-item-group>
</ng-container>
</ng-template>
</p>
</ion-label>
</ion-item>
</ng-container>
</div>
</ion-item-group>
</ion-content>

View File

@@ -9,11 +9,10 @@ import { ApiService } from 'src/app/services/api/embassy-api.service'
import { ActivatedRoute } from '@angular/router'
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
import { Observable, of } from 'rxjs'
import { filter, take } from 'rxjs/operators'
import { exists, isEmptyObject, ErrorToastService } from '@start9labs/shared'
import { filter, take, tap } from 'rxjs/operators'
import { isEmptyObject, ErrorToastService } from '@start9labs/shared'
import { EOSService } from 'src/app/services/eos.service'
import { LocalStorageService } from 'src/app/services/local-storage.service'
import { RecoveredPackageDataEntry } from 'src/app/services/patch-db/data-model'
import { OSUpdatePage } from 'src/app/modals/os-update/os-update.page'
import { getAllPackages } from '../../../util/get-package-data'
@@ -28,7 +27,6 @@ export class ServerShowPage {
readonly server$ = this.patch.watch$('server-info')
readonly ui$ = this.patch.watch$('ui')
readonly connected$ = this.patch.connected$
readonly showUpdate$ = this.eosService.showUpdate$
readonly showDiskRepair$ = this.localStorageService.showDiskRepair$
@@ -48,10 +46,12 @@ export class ServerShowPage {
ngOnInit() {
this.patch
.watch$('recovered-packages')
.pipe(filter(exists), take(1))
.subscribe((rps: { [id: string]: RecoveredPackageDataEntry }) => {
this.hasRecoveredPackage = !isEmptyObject(rps)
})
.pipe(
filter(Boolean),
take(1),
tap(data => (this.hasRecoveredPackage = !isEmptyObject(data))),
)
.subscribe()
}
async updateEos(): Promise<void> {
@@ -290,7 +290,7 @@ export class ServerShowPage {
action: () =>
this.navCtrl.navigateForward(['backup'], { relativeTo: this.route }),
detail: true,
disabled: of(false),
disabled$: of(false),
},
{
title: 'Restore From Backup',
@@ -299,7 +299,7 @@ export class ServerShowPage {
action: () =>
this.navCtrl.navigateForward(['restore'], { relativeTo: this.route }),
detail: true,
disabled: this.eosService.updatingOrBackingUp$,
disabled$: this.eosService.updatingOrBackingUp$,
},
],
Settings: [
@@ -312,7 +312,7 @@ export class ServerShowPage {
? this.updateEos()
: this.checkForEosUpdate(),
detail: false,
disabled: this.eosService.updatingOrBackingUp$,
disabled$: this.eosService.updatingOrBackingUp$,
},
{
title: 'Preferences',
@@ -323,7 +323,7 @@ export class ServerShowPage {
relativeTo: this.route,
}),
detail: true,
disabled: of(false),
disabled$: of(false),
},
{
title: 'LAN',
@@ -332,7 +332,7 @@ export class ServerShowPage {
action: () =>
this.navCtrl.navigateForward(['lan'], { relativeTo: this.route }),
detail: true,
disabled: of(false),
disabled$: of(false),
},
{
title: 'SSH',
@@ -341,7 +341,7 @@ export class ServerShowPage {
action: () =>
this.navCtrl.navigateForward(['ssh'], { relativeTo: this.route }),
detail: true,
disabled: of(false),
disabled$: of(false),
},
{
title: 'WiFi',
@@ -350,7 +350,7 @@ export class ServerShowPage {
action: () =>
this.navCtrl.navigateForward(['wifi'], { relativeTo: this.route }),
detail: true,
disabled: of(false),
disabled$: of(false),
},
{
title: 'Sideload Service',
@@ -361,7 +361,7 @@ export class ServerShowPage {
relativeTo: this.route,
}),
detail: true,
disabled: of(false),
disabled$: of(false),
},
{
title: 'Marketplace Settings',
@@ -372,7 +372,7 @@ export class ServerShowPage {
relativeTo: this.route,
}),
detail: true,
disabled: of(false),
disabled$: of(false),
},
],
Insights: [
@@ -383,7 +383,7 @@ export class ServerShowPage {
action: () =>
this.navCtrl.navigateForward(['specs'], { relativeTo: this.route }),
detail: true,
disabled: of(false),
disabled$: of(false),
},
{
title: 'Monitor',
@@ -392,7 +392,7 @@ export class ServerShowPage {
action: () =>
this.navCtrl.navigateForward(['metrics'], { relativeTo: this.route }),
detail: true,
disabled: of(false),
disabled$: of(false),
},
{
title: 'Active Sessions',
@@ -403,7 +403,7 @@ export class ServerShowPage {
relativeTo: this.route,
}),
detail: true,
disabled: of(false),
disabled$: of(false),
},
{
title: 'OS Logs',
@@ -412,7 +412,7 @@ export class ServerShowPage {
action: () =>
this.navCtrl.navigateForward(['logs'], { relativeTo: this.route }),
detail: true,
disabled: of(false),
disabled$: of(false),
},
{
title: 'Kernel Logs',
@@ -424,7 +424,7 @@ export class ServerShowPage {
relativeTo: this.route,
}),
detail: true,
disabled: of(false),
disabled$: of(false),
},
],
Support: [
@@ -439,7 +439,7 @@ export class ServerShowPage {
'noreferrer',
),
detail: true,
disabled: of(false),
disabled$: of(false),
},
{
title: 'Contact Support',
@@ -452,7 +452,7 @@ export class ServerShowPage {
'noreferrer',
),
detail: true,
disabled: of(false),
disabled$: of(false),
},
],
Power: [
@@ -462,7 +462,7 @@ export class ServerShowPage {
icon: 'reload',
action: () => this.presentAlertRestart(),
detail: false,
disabled: of(false),
disabled$: of(false),
},
{
title: 'Shutdown',
@@ -470,7 +470,7 @@ export class ServerShowPage {
icon: 'power',
action: () => this.presentAlertShutdown(),
detail: false,
disabled: of(false),
disabled$: of(false),
},
{
title: 'System Rebuild',
@@ -478,7 +478,7 @@ export class ServerShowPage {
icon: 'construct-outline',
action: () => this.presentAlertSystemRebuild(),
detail: false,
disabled: of(false),
disabled$: of(false),
},
{
title: 'Repair Disk',
@@ -486,7 +486,7 @@ export class ServerShowPage {
icon: 'medkit-outline',
action: () => this.presentAlertRepairDisk(),
detail: false,
disabled: of(false),
disabled$: of(false),
},
],
}
@@ -517,5 +517,5 @@ interface SettingBtn {
icon: string
action: Function
detail: boolean
disabled: Observable<boolean>
disabled$: Observable<boolean>
}

View File

@@ -1,4 +1,4 @@
import { Component } from '@angular/core'
import { ChangeDetectionStrategy, Component } from '@angular/core'
import { ToastController } from '@ionic/angular'
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
import { ConfigService } from 'src/app/services/config.service'
@@ -8,6 +8,7 @@ import { copyToClipboard } from '@start9labs/shared'
selector: 'server-specs',
templateUrl: './server-specs.page.html',
styleUrls: ['./server-specs.page.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ServerSpecsPage {
readonly server$ = this.patch.watch$('server-info')