From 9b8fccb4311de1ab97f1764fec5b94ec30d73d30 Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Wed, 13 Oct 2021 09:11:05 -0600 Subject: [PATCH] many things --- ui/src/app/guards/maintenance.guard.ts | 6 +- ui/src/app/guards/unmaintenance.guard.ts | 7 +- .../modals/app-config/app-config.page.html | 100 ++++++++++-------- .../app/modals/app-config/app-config.page.ts | 11 +- .../app-restore/app-restore.component.html | 25 +++-- .../app-restore/app-restore.component.ts | 28 ++--- .../generic-input/generic-input.component.ts | 26 ++++- ui/src/app/modals/markdown/markdown.page.html | 13 ++- ui/src/app/modals/markdown/markdown.page.ts | 8 +- .../apps-routes/app-list/app-list.page.ts | 8 +- .../apps-routes/app-show/app-show.page.html | 4 +- .../apps-routes/app-show/app-show.page.ts | 6 ++ .../preferences/preferences.page.ts | 22 +--- .../security-routes/ssh-keys/ssh-keys.page.ts | 20 +--- .../server-backup/server-backup.page.html | 11 +- .../server-backup/server-backup.page.ts | 8 +- ui/src/app/services/error-toast.service.ts | 3 +- ui/src/app/services/form.service.ts | 1 - 18 files changed, 162 insertions(+), 145 deletions(-) diff --git a/ui/src/app/guards/maintenance.guard.ts b/ui/src/app/guards/maintenance.guard.ts index 95797a6cf..e115c8d47 100644 --- a/ui/src/app/guards/maintenance.guard.ts +++ b/ui/src/app/guards/maintenance.guard.ts @@ -8,7 +8,6 @@ import { PatchDbService } from '../services/patch-db/patch-db.service' }) export class MaintenanceGuard implements CanActivate, CanActivateChild { serverStatus: ServerStatus - isFullyDownloaded: boolean = false constructor ( private readonly router: Router, @@ -17,9 +16,6 @@ export class MaintenanceGuard implements CanActivate, CanActivateChild { this.patch.watch$('server-info', 'status').subscribe(status => { this.serverStatus = status }) - this.patch.watch$('server-info', 'update-progress').subscribe(progress => { - this.isFullyDownloaded = !!progress && (progress.size === progress.downloaded) - }) } canActivate (): boolean { @@ -31,7 +27,7 @@ export class MaintenanceGuard implements CanActivate, CanActivateChild { } private runServerStatusCheck (): boolean { - if (ServerStatus.BackingUp === this.serverStatus && !this.isFullyDownloaded) { + if (ServerStatus.BackingUp === this.serverStatus) { this.router.navigate(['/maintenance'], { replaceUrl: true }) return false } else { diff --git a/ui/src/app/guards/unmaintenance.guard.ts b/ui/src/app/guards/unmaintenance.guard.ts index 0b3bbd505..7511ef177 100644 --- a/ui/src/app/guards/unmaintenance.guard.ts +++ b/ui/src/app/guards/unmaintenance.guard.ts @@ -8,8 +8,6 @@ import { PatchDbService } from '../services/patch-db/patch-db.service' }) export class UnmaintenanceGuard implements CanActivate { serverStatus: ServerStatus - isFullyDownloaded: boolean = false - constructor ( private readonly router: Router, @@ -18,13 +16,10 @@ export class UnmaintenanceGuard implements CanActivate { this.patch.watch$('server-info', 'status').subscribe(status => { this.serverStatus = status }) - this.patch.watch$('server-info', 'update-progress').subscribe(progress => { - this.isFullyDownloaded = !!progress && (progress.size === progress.downloaded) - }) } canActivate (): boolean { - if (ServerStatus.BackingUp === this.serverStatus || this.isFullyDownloaded) { + if (ServerStatus.BackingUp === this.serverStatus) { return true } else { this.router.navigate([''], { replaceUrl: true }) diff --git a/ui/src/app/modals/app-config/app-config.page.html b/ui/src/app/modals/app-config/app-config.page.html index 4adb5c4c3..32db6cd0b 100644 --- a/ui/src/app/modals/app-config/app-config.page.html +++ b/ui/src/app/modals/app-config/app-config.page.html @@ -6,7 +6,7 @@ Config - + Reset Defaults @@ -23,62 +23,72 @@ - + - To use the default config for {{ pkg.manifest.title }}, click "Save" below. + + {{ loadingError }} + - - + + -

- - - - - {{ rec.dependentTitle }} -

-
-

{{ pkg.manifest.title }} config has been modified to satisfy {{ rec.dependentTitle }}. - To accept the changes, click “Save” above. -

- More Info - -

- hide -
- - - -
+ To use the default config for {{ pkg.manifest.title }}, click "Save" below.
- -
- - - - -

No config options for {{ pkg.manifest.title }} {{ pkg.manifest.version }}.

-
-
- - -
- -
+ + + + +

+ + + + + {{ rec.dependentTitle }} +

+
+

{{ pkg.manifest.title }} config has been modified to satisfy {{ rec.dependentTitle }}. + To accept the changes, click “Save” above. +

+ More Info + +

+ hide +
+ + + +
+
+
+ +
+ + + + +

No config options for {{ pkg.manifest.title }} {{ pkg.manifest.version }}.

+
+
+ + +
+ +
+
- + Save diff --git a/ui/src/app/modals/app-config/app-config.page.ts b/ui/src/app/modals/app-config/app-config.page.ts index 72998e9f2..7e6931b86 100644 --- a/ui/src/app/modals/app-config/app-config.page.ts +++ b/ui/src/app/modals/app-config/app-config.page.ts @@ -1,5 +1,5 @@ import { Component, Input, ViewChild } from '@angular/core' -import { AlertController, ModalController, IonContent, LoadingController } from '@ionic/angular' +import { AlertController, ModalController, IonContent, LoadingController, IonicSafeString } from '@ionic/angular' import { ApiService } from 'src/app/services/api/embassy-api.service' import { isEmptyObject, isObject, Recommendation } from 'src/app/util/misc.util' import { wizardModal } from 'src/app/components/install-wizard/install-wizard.component' @@ -7,7 +7,7 @@ import { WizardBaker } from 'src/app/components/install-wizard/prebaked-wizards' import { ConfigSpec } from 'src/app/pkg-config/config-types' import { PackageDataEntry } from 'src/app/services/patch-db/data-model' import { PatchDbService } from 'src/app/services/patch-db/patch-db.service' -import { ErrorToastService } from 'src/app/services/error-toast.service' +import { ErrorToastService, getErrorMessage } from 'src/app/services/error-toast.service' import { FormGroup } from '@angular/forms' import { convertValuesRecursive, FormService } from 'src/app/services/form.service' @@ -17,6 +17,7 @@ import { convertValuesRecursive, FormService } from 'src/app/services/form.servi styleUrls: ['./app-config.page.scss'], }) export class AppConfigPage { + @ViewChild(IonContent) content: IonContent @Input() pkgId: string @Input() rec: Recommendation | null = null pkg: PackageDataEntry @@ -26,11 +27,9 @@ export class AppConfigPage { current: object hasConfig = false saving = false - showRec = true openRec = false - - @ViewChild(IonContent) content: IonContent + loadingError: string | IonicSafeString constructor ( private readonly wizardBaker: WizardBaker, @@ -59,7 +58,7 @@ export class AppConfigPage { } this.setConfig(spec, config, depConfig) } catch (e) { - this.errToast.present(e) + this.loadingError = getErrorMessage(e) } finally { this.loadingText = undefined } diff --git a/ui/src/app/modals/app-restore/app-restore.component.html b/ui/src/app/modals/app-restore/app-restore.component.html index 7ddf4c280..879824983 100644 --- a/ui/src/app/modals/app-restore/app-restore.component.html +++ b/ui/src/app/modals/app-restore/app-restore.component.html @@ -13,11 +13,7 @@ - - - - - +

Select the drive containing the backup you would like to restore. @@ -47,9 +43,20 @@ - - - + + + + + + + + {{ loadingError }} + + + + + +
{{ disk.logicalname }} - {{ disk.capacity | convertBytes }}

@@ -66,5 +73,5 @@

-
+ diff --git a/ui/src/app/modals/app-restore/app-restore.component.ts b/ui/src/app/modals/app-restore/app-restore.component.ts index 765678583..1d29308d0 100644 --- a/ui/src/app/modals/app-restore/app-restore.component.ts +++ b/ui/src/app/modals/app-restore/app-restore.component.ts @@ -1,10 +1,10 @@ import { Component, Input } from '@angular/core' -import { ModalController } from '@ionic/angular' +import { ModalController, IonicSafeString } from '@ionic/angular' import { ApiService } from 'src/app/services/api/embassy-api.service' import { GenericInputComponent } from 'src/app/modals/generic-input/generic-input.component' import { DiskInfo } from 'src/app/services/api/api.types' import { PatchDbService } from 'src/app/services/patch-db/patch-db.service' -import { ErrorToastService } from 'src/app/services/error-toast.service' +import { getErrorMessage } from 'src/app/services/error-toast.service' @Component({ selector: 'app-restore', @@ -15,14 +15,13 @@ export class AppRestoreComponent { @Input() pkgId: string disks: DiskInfo[] loading = true - submitting = false allPartitionsMounted: boolean modal: HTMLIonModalElement + loadingError: string | IonicSafeString constructor ( private readonly modalCtrl: ModalController, private readonly embassyApi: ApiService, - private readonly errToast: ErrorToastService, public readonly patch: PatchDbService, ) { } @@ -40,7 +39,7 @@ export class AppRestoreComponent { try { this.disks = await this.embassyApi.getDisks({ }) } catch (e) { - this.errToast.present(e) + this.loadingError = getErrorMessage(e) } finally { this.loading = false } @@ -54,7 +53,7 @@ export class AppRestoreComponent { label: 'Password', useMask: true, buttonText: 'Restore', - submitFn: async (value: string) => await this.restore(logicalname, value), + submitFn: (value: string) => this.restore(logicalname, value), }, cssClass: 'alertlike-modal', presentingElement: await this.modalCtrl.getTop(), @@ -73,17 +72,10 @@ export class AppRestoreComponent { } private async restore (logicalname: string, password: string): Promise { - this.submitting = true - try { - await this.embassyApi.restorePackage({ - id: this.pkgId, - logicalname, - password, - }) - } catch (e) { - this.errToast.present(e) - } finally { - this.submitting = false - } + await this.embassyApi.restorePackage({ + id: this.pkgId, + logicalname, + password, + }) } } diff --git a/ui/src/app/modals/generic-input/generic-input.component.ts b/ui/src/app/modals/generic-input/generic-input.component.ts index 6adae6fb1..46958a37d 100644 --- a/ui/src/app/modals/generic-input/generic-input.component.ts +++ b/ui/src/app/modals/generic-input/generic-input.component.ts @@ -1,5 +1,6 @@ import { Component, Input } from '@angular/core' -import { ModalController } from '@ionic/angular' +import { ModalController, IonicSafeString, LoadingController } from '@ionic/angular' +import { getErrorMessage } from 'src/app/services/error-toast.service' @Component({ selector: 'generic-input', @@ -15,12 +16,14 @@ export class GenericInputComponent { @Input() nullable = false @Input() useMask = false @Input() value = '' + @Input() loadingText = '' @Input() submitFn: (value: string) => Promise unmasked = false - error: string + error: string | IonicSafeString constructor ( private readonly modalCtrl: ModalController, + private readonly loadingCtrl: LoadingController, ) { } toggleMask () { @@ -33,7 +36,22 @@ export class GenericInputComponent { async submit () { // @TODO validate input? - await this.submitFn(this.value) - this.modalCtrl.dismiss(undefined, 'success') + + const loader = await this.loadingCtrl.create({ + spinner: 'lines', + cssClass: 'loader', + message: this.loadingText, + }) + await loader.present() + + try { + await this.submitFn(this.value) + this.modalCtrl.dismiss(undefined, 'success') + } catch (e) { + this.error = getErrorMessage(e) + } + finally { + loader.dismiss() + } } } diff --git a/ui/src/app/modals/markdown/markdown.page.html b/ui/src/app/modals/markdown/markdown.page.html index 74970c86e..e300412fc 100644 --- a/ui/src/app/modals/markdown/markdown.page.html +++ b/ui/src/app/modals/markdown/markdown.page.html @@ -13,6 +13,17 @@ -
+ + + + + {{ loadingError }} + + + + + +
+
diff --git a/ui/src/app/modals/markdown/markdown.page.ts b/ui/src/app/modals/markdown/markdown.page.ts index f281bcba6..8da6db351 100644 --- a/ui/src/app/modals/markdown/markdown.page.ts +++ b/ui/src/app/modals/markdown/markdown.page.ts @@ -1,7 +1,7 @@ import { Component, Input } from '@angular/core' -import { ModalController } from '@ionic/angular' +import { ModalController, IonicSafeString } from '@ionic/angular' import { ApiService } from 'src/app/services/api/embassy-api.service' -import { ErrorToastService } from 'src/app/services/error-toast.service' +import { getErrorMessage } from 'src/app/services/error-toast.service' @Component({ selector: 'markdown', @@ -13,10 +13,10 @@ export class MarkdownPage { @Input() title: string content: string loading = true + loadingError: string | IonicSafeString constructor ( private readonly modalCtrl: ModalController, - private readonly errToast: ErrorToastService, private readonly embassyApi: ApiService, ) { } @@ -32,7 +32,7 @@ export class MarkdownPage { } } } catch (e) { - this.errToast.present(e) + this.loadingError = getErrorMessage(e) } finally { this.loading = false } diff --git a/ui/src/app/pages/apps-routes/app-list/app-list.page.ts b/ui/src/app/pages/apps-routes/app-list/app-list.page.ts index 11d46f4b4..6fa4064f1 100644 --- a/ui/src/app/pages/apps-routes/app-list/app-list.page.ts +++ b/ui/src/app/pages/apps-routes/app-list/app-list.page.ts @@ -180,8 +180,8 @@ export class AppListPage { private watchPkgs (): Observable { return this.patch.watch$('package-data') .pipe( - filter(obj => { - return Object.keys(obj).length !== this.pkgs.length + filter(pkgs => { + return Object.keys(pkgs).length !== this.pkgs.length }), tap(pkgs => { const ids = Object.keys(pkgs) @@ -194,6 +194,8 @@ export class AppListPage { } }) + this.empty = !this.pkgs.length + ids.forEach(id => { // if already exists, return const pkg = this.pkgs.find(p => p.entry.manifest.id === id) @@ -223,7 +225,7 @@ export class AppListPage { const statuses = renderPkgStatus(update) const primaryRendering = PrimaryRendering[statuses.primary] pkgInfo.entry = update - pkgInfo.installProgress = !isEmptyObject(pkg['install-progress']) ? this.pkgLoading.transform(pkg['install-progress']) : undefined + pkgInfo.installProgress = !isEmptyObject(update['install-progress']) ? this.pkgLoading.transform(update['install-progress']) : undefined pkgInfo.primaryRendering = primaryRendering pkgInfo.error = statuses.health === HealthStatus.Failure || [DependencyStatus.Issue, DependencyStatus.Critical].includes(statuses.dependency) }) diff --git a/ui/src/app/pages/apps-routes/app-show/app-show.page.html b/ui/src/app/pages/apps-routes/app-show/app-show.page.html index af4505ad2..68ab5cf6a 100644 --- a/ui/src/app/pages/apps-routes/app-show/app-show.page.html +++ b/ui/src/app/pages/apps-routes/app-show/app-show.page.html @@ -118,9 +118,9 @@ - + diff --git a/ui/src/app/pages/apps-routes/app-show/app-show.page.ts b/ui/src/app/pages/apps-routes/app-show/app-show.page.ts index a401fa806..f225b5e77 100644 --- a/ui/src/app/pages/apps-routes/app-show/app-show.page.ts +++ b/ui/src/app/pages/apps-routes/app-show/app-show.page.ts @@ -70,6 +70,12 @@ export class AppShowPage { // 1 this.patch.watch$('package-data', this.pkgId) .subscribe(pkg => { + // if package disappears, navigate to list page + if (!pkg) { + this.navCtrl.navigateRoot('/services') + return + } + this.pkg = pkg this.installProgress = !isEmptyObject(pkg['install-progress']) ? this.packageLoadingService.transform(pkg['install-progress']) : undefined this.statuses = renderPkgStatus(pkg) diff --git a/ui/src/app/pages/server-routes/preferences/preferences.page.ts b/ui/src/app/pages/server-routes/preferences/preferences.page.ts index afb996394..270dad06f 100644 --- a/ui/src/app/pages/server-routes/preferences/preferences.page.ts +++ b/ui/src/app/pages/server-routes/preferences/preferences.page.ts @@ -1,6 +1,6 @@ import { Component, ViewChild } from '@angular/core' import { PatchDbService } from 'src/app/services/patch-db/patch-db.service' -import { IonContent, LoadingController, ModalController } from '@ionic/angular' +import { IonContent, ModalController } from '@ionic/angular' import { GenericInputComponent } from 'src/app/modals/generic-input/generic-input.component' import { ConfigSpec } from 'src/app/pkg-config/config-types' import { ApiService } from 'src/app/services/api/embassy-api.service' @@ -18,8 +18,6 @@ export class PreferencesPage { constructor ( private readonly modalCtrl: ModalController, - private readonly loadingCtrl: LoadingController, - private readonly errToast: ErrorToastService, private readonly api: ApiService, public readonly patch: PatchDbService, ) { } @@ -43,7 +41,8 @@ export class PreferencesPage { nullable: true, value: this.patch.data.ui.name, buttonText: 'Save', - submitFn: async (value: string) => await this.setDbValue('name', value || this.defaultName), + loadingText: 'Saving', + submitFn: (value: string) => this.setDbValue('name', value || this.defaultName), }, cssClass: 'alertlike-modal', presentingElement: await this.modalCtrl.getTop(), @@ -54,20 +53,7 @@ export class PreferencesPage { } private async setDbValue (key: string, value: string): Promise { - const loader = await this.loadingCtrl.create({ - spinner: 'lines', - message: 'Saving...', - cssClass: 'loader', - }) - await loader.present() - - try { - await this.api.setDbValue({ pointer: `/${key}`, value }) - } catch (e) { - this.errToast.present(e) - } finally { - loader.dismiss() - } + await this.api.setDbValue({ pointer: `/${key}`, value }) } } diff --git a/ui/src/app/pages/server-routes/security-routes/ssh-keys/ssh-keys.page.ts b/ui/src/app/pages/server-routes/security-routes/ssh-keys/ssh-keys.page.ts index bc4069ca3..9eddf4967 100644 --- a/ui/src/app/pages/server-routes/security-routes/ssh-keys/ssh-keys.page.ts +++ b/ui/src/app/pages/server-routes/security-routes/ssh-keys/ssh-keys.page.ts @@ -46,7 +46,8 @@ export class SSHKeysPage { title: name, message: description, label: name, - submitFn: async (pk: string) => await this.add(pk), + loadingText: 'Saving', + submitFn: (pk: string) => this.add(pk), }, cssClass: 'alertlike-modal', }) @@ -54,21 +55,8 @@ export class SSHKeysPage { } async add (pubkey: string): Promise { - const loader = await this.loadingCtrl.create({ - spinner: 'lines', - message: 'Saving...', - cssClass: 'loader', - }) - await loader.present() - - try { - const key = await this.embassyApi.addSshKey({ key: pubkey }) - this.sshKeys.push(key) - } catch (e) { - this.errToast.present(e) - } finally { - loader.dismiss() - } + const key = await this.embassyApi.addSshKey({ key: pubkey }) + this.sshKeys.push(key) } async presentAlertDelete (i: number) { diff --git a/ui/src/app/pages/server-routes/server-backup/server-backup.page.html b/ui/src/app/pages/server-routes/server-backup/server-backup.page.html index 68bf331b4..e916d25e4 100644 --- a/ui/src/app/pages/server-routes/server-backup/server-backup.page.html +++ b/ui/src/app/pages/server-routes/server-backup/server-backup.page.html @@ -38,7 +38,16 @@ - + + + + + {{ loadingError }} + + + + +
{{ disk.logicalname }} - {{ disk.capacity | convertBytes }}

diff --git a/ui/src/app/pages/server-routes/server-backup/server-backup.page.ts b/ui/src/app/pages/server-routes/server-backup/server-backup.page.ts index a70a113e4..bbda1965b 100644 --- a/ui/src/app/pages/server-routes/server-backup/server-backup.page.ts +++ b/ui/src/app/pages/server-routes/server-backup/server-backup.page.ts @@ -1,9 +1,9 @@ import { Component } from '@angular/core' -import { ModalController } from '@ionic/angular' +import { ModalController, IonicSafeString } from '@ionic/angular' import { ApiService } from 'src/app/services/api/embassy-api.service' import { GenericInputComponent } from 'src/app/modals/generic-input/generic-input.component' import { DiskInfo } from 'src/app/services/api/api.types' -import { ErrorToastService } from 'src/app/services/error-toast.service' +import { getErrorMessage } from 'src/app/services/error-toast.service' @Component({ selector: 'server-backup', @@ -14,11 +14,11 @@ export class ServerBackupPage { disks: DiskInfo[] loading = true allPartitionsMounted: boolean + loadingError: string | IonicSafeString constructor ( private readonly modalCtrl: ModalController, private readonly embassyApi: ApiService, - private readonly errToast: ErrorToastService, ) { } ngOnInit () { @@ -34,7 +34,7 @@ export class ServerBackupPage { try { this.disks = await this.embassyApi.getDisks({ }) } catch (e) { - this.errToast.present(e) + this.loadingError = getErrorMessage(e) } finally { this.loading = false } diff --git a/ui/src/app/services/error-toast.service.ts b/ui/src/app/services/error-toast.service.ts index d89f01137..2540da782 100644 --- a/ui/src/app/services/error-toast.service.ts +++ b/ui/src/app/services/error-toast.service.ts @@ -47,13 +47,12 @@ export class ErrorToastService { export function getErrorMessage (e: RequestError, link?: string): string | IonicSafeString { let message: string | IonicSafeString - if (e.code) message = String(e.code) if (e.message) message = `${message ? message + ' ' : ''}${e.message}` if (e.details) message = `${message ? message + ': ' : ''}${e.details}` if (!message) { message = 'Unknown Error.' - link = 'https://docs.start9.com' + link = 'https://docs.start9.com/support/FAQ/index.html' } if (link) { diff --git a/ui/src/app/services/form.service.ts b/ui/src/app/services/form.service.ts index 1b8264a7e..c8cc08bc1 100644 --- a/ui/src/app/services/form.service.ts +++ b/ui/src/app/services/form.service.ts @@ -400,7 +400,6 @@ export function convertValuesRecursive (configSpec: ConfigSpec, group: FormGroup } else if (valueSpec.subtype === 'string') { formArr.controls.forEach(control => { if (!control.value) control.setValue(null) - control.setValue(control.value ? Number(control.value) : null) }) } else if (valueSpec.subtype === 'object') { formArr.controls.forEach((formGroup: FormGroup) => {