mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 10:21:52 +00:00
many things
This commit is contained in:
committed by
Aiden McClelland
parent
7adb66cf4c
commit
9b8fccb431
@@ -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 {
|
||||
|
||||
@@ -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 })
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</ion-button>
|
||||
</ion-buttons>
|
||||
<ion-title>Config</ion-title>
|
||||
<ion-buttons *ngIf="!loadingText && hasConfig" slot="end" class="ion-padding-end">
|
||||
<ion-buttons *ngIf="!loadingText && !loadingError && hasConfig" slot="end" class="ion-padding-end">
|
||||
<ion-button fill="clear" (click)="resetDefaults()">
|
||||
<ion-icon slot="start" name="refresh"></ion-icon>
|
||||
Reset Defaults
|
||||
@@ -23,62 +23,72 @@
|
||||
<!-- not loading -->
|
||||
<ng-template #loaded>
|
||||
|
||||
<ion-item *ngIf="hasConfig && !pkg.installed?.status.configured && !configForm.dirty">
|
||||
<ion-item *ngIf="loadingError; else noError">
|
||||
<ion-label>
|
||||
<ion-text color="success">To use the default config for {{ pkg.manifest.title }}, click "Save" below.</ion-text>
|
||||
<ion-text color="danger">
|
||||
{{ loadingError }}
|
||||
</ion-text>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<ng-container *ngIf="rec && showRec">
|
||||
<ion-item class="rec-item">
|
||||
<ng-template #noError>
|
||||
<ion-item *ngIf="hasConfig && !pkg.installed.status.configured && !configForm.dirty">
|
||||
<ion-label>
|
||||
<h2 style="display: flex; align-items: center;">
|
||||
<ion-icon size="small" style="margin: 4px" slot="start" color="primary" slot="start" name="ellipse"></ion-icon>
|
||||
<ion-thumbnail style="width: 3vh; height: 3vh; margin: 0px 2px 0px 5px;" slot="start">
|
||||
<img [src]="rec.dependentIcon" [alt]="rec.dependentTitle"/>
|
||||
</ion-thumbnail>
|
||||
<ion-text style="margin: 5px; font-family: 'Montserrat'; font-size: smaller;">{{ rec.dependentTitle }}</ion-text>
|
||||
</h2>
|
||||
<div style="margin: 7px 5px;">
|
||||
<p style="font-size: small; color: var(--ion-color-medium)"> {{ pkg.manifest.title }} config has been modified to satisfy {{ rec.dependentTitle }}.
|
||||
<ion-text color="dark">To accept the changes, click “Save” above.</ion-text>
|
||||
</p>
|
||||
<a style="font-size: small" *ngIf="!openRec" (click)="openRec = true">More Info</a>
|
||||
<ng-container *ngIf="openRec">
|
||||
<p style="margin-top: 10px; color: var(--ion-color-medium); font-size: small" [innerHTML]="rec.description"></p>
|
||||
<a style="font-size: x-small; font-style: italic;" (click)="openRec = false">hide</a>
|
||||
</ng-container>
|
||||
<ion-button style="position: absolute; right: 0; top: 0" color="primary" fill="clear" (click)="dismissRec()">
|
||||
<ion-icon name="close"></ion-icon>
|
||||
</ion-button>
|
||||
</div>
|
||||
<ion-text color="success">To use the default config for {{ pkg.manifest.title }}, click "Save" below.</ion-text>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item-divider></ion-item-divider>
|
||||
</ng-container>
|
||||
|
||||
<!-- no config -->
|
||||
<ion-item *ngIf="!hasConfig">
|
||||
<ion-label>
|
||||
<p>No config options for {{ pkg.manifest.title }} {{ pkg.manifest.version }}.</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<!-- has config -->
|
||||
<form *ngIf="hasConfig" [formGroup]="configForm" novalidate>
|
||||
<form-object
|
||||
[objectSpec]="configSpec"
|
||||
[formGroup]="configForm"
|
||||
[current]="current"
|
||||
[showEdited]="true"
|
||||
></form-object>
|
||||
</form>
|
||||
|
||||
<ng-container *ngIf="rec && showRec">
|
||||
<ion-item class="rec-item">
|
||||
<ion-label>
|
||||
<h2 style="display: flex; align-items: center;">
|
||||
<ion-icon size="small" style="margin: 4px" slot="start" color="primary" slot="start" name="ellipse"></ion-icon>
|
||||
<ion-thumbnail style="width: 3vh; height: 3vh; margin: 0px 2px 0px 5px;" slot="start">
|
||||
<img [src]="rec.dependentIcon" [alt]="rec.dependentTitle"/>
|
||||
</ion-thumbnail>
|
||||
<ion-text style="margin: 5px; font-family: 'Montserrat'; font-size: smaller;">{{ rec.dependentTitle }}</ion-text>
|
||||
</h2>
|
||||
<div style="margin: 7px 5px;">
|
||||
<p style="font-size: small; color: var(--ion-color-medium)"> {{ pkg.manifest.title }} config has been modified to satisfy {{ rec.dependentTitle }}.
|
||||
<ion-text color="dark">To accept the changes, click “Save” above.</ion-text>
|
||||
</p>
|
||||
<a style="font-size: small" *ngIf="!openRec" (click)="openRec = true">More Info</a>
|
||||
<ng-container *ngIf="openRec">
|
||||
<p style="margin-top: 10px; color: var(--ion-color-medium); font-size: small" [innerHTML]="rec.description"></p>
|
||||
<a style="font-size: x-small; font-style: italic;" (click)="openRec = false">hide</a>
|
||||
</ng-container>
|
||||
<ion-button style="position: absolute; right: 0; top: 0" color="primary" fill="clear" (click)="dismissRec()">
|
||||
<ion-icon name="close"></ion-icon>
|
||||
</ion-button>
|
||||
</div>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item-divider></ion-item-divider>
|
||||
</ng-container>
|
||||
|
||||
<!-- no config -->
|
||||
<ion-item *ngIf="!hasConfig">
|
||||
<ion-label>
|
||||
<p>No config options for {{ pkg.manifest.title }} {{ pkg.manifest.version }}.</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<!-- has config -->
|
||||
<form *ngIf="hasConfig" [formGroup]="configForm" novalidate>
|
||||
<form-object
|
||||
[objectSpec]="configSpec"
|
||||
[formGroup]="configForm"
|
||||
[current]="current"
|
||||
[showEdited]="true"
|
||||
></form-object>
|
||||
</form>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
</ion-content>
|
||||
|
||||
<ion-footer>
|
||||
<ion-toolbar>
|
||||
<ion-buttons *ngIf="!loadingText" slot="end" class="ion-padding-end">
|
||||
<ion-buttons *ngIf="!loadingText && !loadingError" slot="end" class="ion-padding-end">
|
||||
<ion-button *ngIf="hasConfig" fill="outline" [disabled]="saving" (click)="save()" class="enter-click" [class.no-click]="saving">
|
||||
Save
|
||||
</ion-button>
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -13,11 +13,7 @@
|
||||
|
||||
<ion-content class="ion-padding-top">
|
||||
|
||||
<!-- submitting -->
|
||||
<text-spinner *ngIf="submitting" text="Initiating Backup"></text-spinner>
|
||||
|
||||
<!-- not submitting -->
|
||||
<ion-item *ngIf="!submitting" class="ion-margin-bottom">
|
||||
<ion-item class="ion-margin-bottom">
|
||||
<ion-label>
|
||||
<h2>
|
||||
Select the drive containing the backup you would like to restore.
|
||||
@@ -47,9 +43,20 @@
|
||||
</ion-item>
|
||||
</ng-container>
|
||||
|
||||
<!-- not loading and not submitting -->
|
||||
<ion-content *ngIf="!loading && !submitting">
|
||||
<ion-item-group>
|
||||
<!-- not loading -->
|
||||
<ng-container *ngIf="!loading">
|
||||
|
||||
<!-- error -->
|
||||
<ion-item *ngIf="loadingError">
|
||||
<ion-label>
|
||||
<ion-text color="danger">
|
||||
{{ loadingError }}
|
||||
</ion-text>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<!-- no error -->
|
||||
<ion-item-group *ngIf="!loadingError">
|
||||
<div *ngFor="let disk of disks">
|
||||
<ion-item-divider>{{ disk.logicalname }} - {{ disk.capacity | convertBytes }}</ion-item-divider>
|
||||
<p class="item-subdivider" *ngIf="disk.vendor || disk.model">
|
||||
@@ -66,5 +73,5 @@
|
||||
</ion-item>
|
||||
</div>
|
||||
</ion-item-group>
|
||||
</ion-content>
|
||||
</ng-container>
|
||||
</ion-content>
|
||||
|
||||
@@ -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<void> {
|
||||
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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<any>
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,17 @@
|
||||
<text-spinner *ngIf="loading; else loaded" [text]="'Loading ' + title | titlecase"></text-spinner>
|
||||
|
||||
<ng-template #loaded>
|
||||
<div *ngIf="content" class="content-padding" [innerHTML]="content | markdown"></div>
|
||||
|
||||
<ion-item *ngIf="loadingError; else noError">
|
||||
<ion-label>
|
||||
<ion-text color="danger">
|
||||
{{ loadingError }}
|
||||
</ion-text>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<ng-template #noError>
|
||||
<div class="content-padding" [innerHTML]="content | markdown"></div>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
</ion-content>
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -180,8 +180,8 @@ export class AppListPage {
|
||||
private watchPkgs (): Observable<DataModel['package-data']> {
|
||||
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)
|
||||
})
|
||||
|
||||
@@ -118,9 +118,9 @@
|
||||
</ion-item>
|
||||
</ng-container>
|
||||
<!-- @TODO better maintenance messaging -->
|
||||
<ng-template #maintenance>
|
||||
<!-- <ng-template #maintenance>
|
||||
Service is undergoing maintenance.
|
||||
</ng-template>
|
||||
</ng-template> -->
|
||||
</ng-container>
|
||||
</ion-item-group>
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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<void> {
|
||||
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 })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<void> {
|
||||
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) {
|
||||
|
||||
@@ -38,7 +38,16 @@
|
||||
<!-- loaded -->
|
||||
<ng-container *ngIf="!loading">
|
||||
|
||||
<ion-item-group>
|
||||
<!-- error -->
|
||||
<ion-item *ngIf="loadingError">
|
||||
<ion-label>
|
||||
<ion-text color="danger">
|
||||
{{ loadingError }}
|
||||
</ion-text>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<ion-item-group *ngIf="!loadingError">
|
||||
<div *ngFor="let disk of disks">
|
||||
<ion-item-divider>{{ disk.logicalname }} - {{ disk.capacity | convertBytes }}</ion-item-divider>
|
||||
<p class="item-subdivider" *ngIf="disk.vendor || disk.model">
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
Reference in New Issue
Block a user