mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 12:11:56 +00:00
[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:
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -12,7 +12,7 @@ export class AppListIconComponent {
|
||||
@Input()
|
||||
pkg!: PkgInfo
|
||||
|
||||
disconnected$ = this.connectionService.watchDisconnected$()
|
||||
readonly connected$ = this.connectionService.connected$
|
||||
|
||||
constructor(private readonly connectionService: ConnectionService) {}
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
}),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -17,7 +17,7 @@ export class AppShowHealthChecksComponent {
|
||||
|
||||
HealthResult = HealthResult
|
||||
|
||||
readonly disconnected$ = this.connectionService.watchDisconnected$()
|
||||
readonly connected$ = this.connectionService.connected$
|
||||
|
||||
constructor(private readonly connectionService: ConnectionService) {}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -37,7 +37,7 @@ export class AppShowStatusComponent {
|
||||
|
||||
PR = PrimaryRendering
|
||||
|
||||
disconnected$ = this.connectionService.watchDisconnected$()
|
||||
readonly connected$ = this.connectionService.connected$
|
||||
|
||||
constructor(
|
||||
private readonly alertCtrl: AlertController,
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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
|
||||
})
|
||||
|
||||
@@ -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
|
||||
})
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
) {}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
}
|
||||
|
||||
@@ -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')
|
||||
|
||||
Reference in New Issue
Block a user