diff --git a/frontend/projects/diagnostic-ui/src/app/pages/home/home.page.ts b/frontend/projects/diagnostic-ui/src/app/pages/home/home.page.ts index c2c0c95ba..1537acfd0 100644 --- a/frontend/projects/diagnostic-ui/src/app/pages/home/home.page.ts +++ b/frontend/projects/diagnostic-ui/src/app/pages/home/home.page.ts @@ -91,7 +91,6 @@ export class HomePage { async restart(): Promise { const loader = await this.loadingCtrl.create({ - spinner: 'lines', cssClass: 'loader', }) await loader.present() @@ -108,7 +107,6 @@ export class HomePage { async forgetDrive(): Promise { const loader = await this.loadingCtrl.create({ - spinner: 'lines', cssClass: 'loader', }) await loader.present() @@ -126,7 +124,6 @@ export class HomePage { async repairDrive(): Promise { const loader = await this.loadingCtrl.create({ - spinner: 'lines', cssClass: 'loader', }) await loader.present() @@ -144,10 +141,9 @@ export class HomePage { async presentAlertRepairDisk() { const alert = await this.alertCtrl.create({ - header: 'RepairDisk', - message: new IonicSafeString( - `Warning: This action will attempt to preform a disk repair operation and system reboot. No data will be deleted. This action should only be executed if directed by a Start9 support specialist. We recommend backing up your device before preforming this action. If anything happens to the device during the reboot (between the bep and chime), such as loosing power, a power surge, unplugging the drive, or unplugging the Embassy, the filesystem *will* be in an unrecoverable state. Please proceed with caution.`, - ), + header: 'Warning', + message: + 'This action will attempt to preform a disk repair operation and system reboot. No data will be deleted. This action should only be executed if directed by a Start9 support specialist. We recommend backing up your device before preforming this action. If anything happens to the device during the reboot (between the bep and chime), such as loosing power, a power surge, unplugging the drive, or unplugging the Embassy, the filesystem *will* be in an unrecoverable state. Please proceed with caution.', buttons: [ { text: 'Cancel', @@ -167,6 +163,7 @@ export class HomePage { cssClass: 'enter-click', }, ], + cssClass: 'alert-warning-message', }) await alert.present() } diff --git a/frontend/projects/marketplace/src/services/marketplace.service.ts b/frontend/projects/marketplace/src/services/marketplace.service.ts index 3db2baabc..7521e5832 100644 --- a/frontend/projects/marketplace/src/services/marketplace.service.ts +++ b/frontend/projects/marketplace/src/services/marketplace.service.ts @@ -3,8 +3,6 @@ import { MarketplacePkg } from '../types/marketplace-pkg' import { Marketplace } from '../types/marketplace' export abstract class AbstractMarketplaceService { - abstract install(id: string, version?: string): Observable - abstract getMarketplace(): Observable abstract getReleaseNotes(id: string): Observable> diff --git a/frontend/projects/marketplace/src/types/marketplace-manifest.ts b/frontend/projects/marketplace/src/types/marketplace-manifest.ts index e180ae3ea..05ca26cca 100644 --- a/frontend/projects/marketplace/src/types/marketplace-manifest.ts +++ b/frontend/projects/marketplace/src/types/marketplace-manifest.ts @@ -1,5 +1,4 @@ import { Url } from '@start9labs/shared' - import { Dependency } from './dependency' export interface MarketplaceManifest { @@ -22,6 +21,7 @@ export interface MarketplaceManifest { uninstall: string | null restore: string | null start: string | null + stop: string | null } dependencies: Record> } diff --git a/frontend/projects/setup-wizard/src/app/modals/cifs-modal/cifs-modal.page.ts b/frontend/projects/setup-wizard/src/app/modals/cifs-modal/cifs-modal.page.ts index 713cd93fb..a671897c5 100644 --- a/frontend/projects/setup-wizard/src/app/modals/cifs-modal/cifs-modal.page.ts +++ b/frontend/projects/setup-wizard/src/app/modals/cifs-modal/cifs-modal.page.ts @@ -38,7 +38,6 @@ export class CifsModal { async submit(): Promise { const loader = await this.loadingCtrl.create({ - spinner: 'lines', message: 'Connecting to shared folder...', cssClass: 'loader', }) diff --git a/frontend/projects/setup-wizard/src/app/pages/embassy/embassy.page.ts b/frontend/projects/setup-wizard/src/app/pages/embassy/embassy.page.ts index f7a13496c..cf6b451f2 100644 --- a/frontend/projects/setup-wizard/src/app/pages/embassy/embassy.page.ts +++ b/frontend/projects/setup-wizard/src/app/pages/embassy/embassy.page.ts @@ -100,6 +100,7 @@ export class EmbassyPage { this.presentModalPassword(drive) } }, + cssClass: 'enter-click', }, ], }) diff --git a/frontend/projects/ui/src/app/app/global/services/patch-data.service.ts b/frontend/projects/ui/src/app/app/global/services/patch-data.service.ts index 9bf624479..7e6bf0caf 100644 --- a/frontend/projects/ui/src/app/app/global/services/patch-data.service.ts +++ b/frontend/projects/ui/src/app/app/global/services/patch-data.service.ts @@ -1,12 +1,10 @@ import { Injectable } from '@angular/core' import { ModalController } from '@ionic/angular' -import { Storage } from '@ionic/storage-angular' import { Observable, of } from 'rxjs' import { filter, share, switchMap, take, tap } from 'rxjs/operators' import { isEmptyObject } from '@start9labs/shared' import { PatchDbService } from 'src/app/services/patch-db/patch-db.service' -import { AuthService } from 'src/app/services/auth.service' import { DataModel, UIData } from 'src/app/services/patch-db/data-model' import { EOSService } from 'src/app/services/eos.service' import { OSWelcomePage } from 'src/app/modals/os-welcome/os-welcome.page' @@ -39,9 +37,7 @@ export class PatchDataService extends Observable { constructor( private readonly patchMonitor: PatchMonitorService, - private readonly authService: AuthService, private readonly patch: PatchDbService, - private readonly storage: Storage, private readonly eosService: EOSService, private readonly config: ConfigService, private readonly modalCtrl: ModalController, diff --git a/frontend/projects/ui/src/app/app/global/services/update-toast.service.ts b/frontend/projects/ui/src/app/app/global/services/update-toast.service.ts index 5b06ce76f..0d161664b 100644 --- a/frontend/projects/ui/src/app/app/global/services/update-toast.service.ts +++ b/frontend/projects/ui/src/app/app/global/services/update-toast.service.ts @@ -46,7 +46,6 @@ export class UpdateToastService extends Observable { } LOADER: LoadingOptions = { - spinner: 'lines', message: 'Restarting...', } diff --git a/frontend/projects/ui/src/app/app/menu/menu.component.html b/frontend/projects/ui/src/app/app/menu/menu.component.html index c3579153d..8bcde01c8 100644 --- a/frontend/projects/ui/src/app/app/menu/menu.component.html +++ b/frontend/projects/ui/src/app/app/menu/menu.component.html @@ -26,7 +26,7 @@ {{ page.title }} this.logout(), + cssClass: 'enter-click', }, ], }) diff --git a/frontend/projects/ui/src/app/components/app-wizard/app-wizard.component.html b/frontend/projects/ui/src/app/components/app-wizard/app-wizard.component.html index 9618ff366..97266271c 100644 --- a/frontend/projects/ui/src/app/components/app-wizard/app-wizard.component.html +++ b/frontend/projects/ui/src/app/components/app-wizard/app-wizard.component.html @@ -33,11 +33,6 @@ *ngIf="slide.selector === 'alert'" [params]="slide.params" > - -

Release Notes

-
-
-

- - {{ v.version }} - -

-
-
-
-
- diff --git a/frontend/projects/ui/src/app/components/app-wizard/notes/notes.component.module.ts b/frontend/projects/ui/src/app/components/app-wizard/notes/notes.component.module.ts deleted file mode 100644 index 5257edbdc..000000000 --- a/frontend/projects/ui/src/app/components/app-wizard/notes/notes.component.module.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { NgModule } from '@angular/core' -import { CommonModule } from '@angular/common' -import { NotesComponent } from './notes.component' -import { IonicModule } from '@ionic/angular' -import { RouterModule } from '@angular/router' -import { MarkdownPipeModule } from '@start9labs/shared' - -@NgModule({ - declarations: [NotesComponent], - imports: [ - CommonModule, - IonicModule, - RouterModule.forChild([]), - MarkdownPipeModule, - ], - exports: [NotesComponent], -}) -export class NotesComponentModule {} diff --git a/frontend/projects/ui/src/app/components/app-wizard/notes/notes.component.ts b/frontend/projects/ui/src/app/components/app-wizard/notes/notes.component.ts deleted file mode 100644 index e53049e8e..000000000 --- a/frontend/projects/ui/src/app/components/app-wizard/notes/notes.component.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Component, Input } from '@angular/core' -import { BaseSlide } from '../wizard-types' - -@Component({ - selector: 'notes', - templateUrl: './notes.component.html', - styleUrls: ['../app-wizard.component.scss'], -}) -export class NotesComponent implements BaseSlide { - @Input() params: { - versions: { version: string; notes: string }[] - headline: string - } - - loading = false - - async load() {} - - asIsOrder() { - return 0 - } -} diff --git a/frontend/projects/ui/src/app/components/app-wizard/wizard-defs.ts b/frontend/projects/ui/src/app/components/app-wizard/wizard-defs.ts index 001ee5115..0fae49742 100644 --- a/frontend/projects/ui/src/app/components/app-wizard/wizard-defs.ts +++ b/frontend/projects/ui/src/app/components/app-wizard/wizard-defs.ts @@ -34,14 +34,6 @@ export class WizardDefs { }, } : undefined, - { - selector: 'dependents', - params: { - verb: 'updating', - title, - Fn: () => this.embassyApi.dryUpdatePackage({ id, version }), - }, - }, { selector: 'complete', params: { @@ -67,54 +59,6 @@ export class WizardDefs { } } - updateOS(values: { - version: string - releaseNotes: { [version: string]: string } - headline: string - }): AppWizardComponent['params'] { - const { version, releaseNotes, headline } = values - - const versions = Object.keys(releaseNotes) - .sort() - .reverse() - .map(version => { - return { - version, - notes: releaseNotes[version], - } - }) - - const title = 'EmbassyOS' - - const slides: SlideDefinition[] = [ - { - selector: 'notes', - params: { - versions, - headline, - }, - }, - { - selector: 'complete', - params: { - verb: 'beginning update for', - title, - Fn: () => - this.embassyApi.updateServer({ - 'marketplace-url': this.config.marketplace.url, - }), - }, - }, - ] - return { - action: 'update', - title, - version, - slides: slides.filter(exists), - submitBtn: 'Begin Update', - } - } - downgrade(values: { id: string title: string @@ -132,14 +76,6 @@ export class WizardDefs { }, } : undefined, - { - selector: 'dependents', - params: { - verb: 'downgrading', - title, - Fn: () => this.embassyApi.dryUpdatePackage({ id, version }), - }, - }, { selector: 'complete', params: { @@ -180,14 +116,6 @@ export class WizardDefs { message: uninstallAlert || defaultUninstallWarning(title), }, }, - { - selector: 'dependents', - params: { - verb: 'uninstalling', - title, - Fn: () => this.embassyApi.dryUninstallPackage({ id }), - }, - }, { selector: 'complete', params: { @@ -210,14 +138,6 @@ export class WizardDefs { const { title, id } = values const slides: SlideDefinition[] = [ - { - selector: 'dependents', - params: { - verb: 'stopping', - title, - Fn: () => this.embassyApi.dryStopPackage({ id }), - }, - }, { selector: 'complete', params: { diff --git a/frontend/projects/ui/src/app/components/backup-drives/backup-drives.component.ts b/frontend/projects/ui/src/app/components/backup-drives/backup-drives.component.ts index 4cf6275f8..926abbba6 100644 --- a/frontend/projects/ui/src/app/components/backup-drives/backup-drives.component.ts +++ b/frontend/projects/ui/src/app/components/backup-drives/backup-drives.component.ts @@ -142,7 +142,6 @@ export class BackupDrivesComponent { private async addCifs(value: RR.AddBackupTargetReq): Promise { const loader = await this.loadingCtrl.create({ - spinner: 'lines', message: 'Testing connectivity to shared folder...', }) await loader.present() @@ -200,7 +199,6 @@ export class BackupDrivesComponent { index: number, ): Promise { const loader = await this.loadingCtrl.create({ - spinner: 'lines', message: 'Testing connectivity to shared folder...', }) await loader.present() @@ -217,7 +215,6 @@ export class BackupDrivesComponent { private async deleteCifs(id: string, index: number): Promise { const loader = await this.loadingCtrl.create({ - spinner: 'lines', message: 'Removing...', }) await loader.present() diff --git a/frontend/projects/ui/src/app/modals/app-config/app-config.page.html b/frontend/projects/ui/src/app/modals/app-config/app-config.page.html index 28d72273d..83e75ba02 100644 --- a/frontend/projects/ui/src/app/modals/app-config/app-config.page.html +++ b/frontend/projects/ui/src/app/modals/app-config/app-config.page.html @@ -110,7 +110,7 @@ fill="solid" color="primary" [disabled]="saving" - (click)="save()" + (click)="tryConfigure()" class="enter-click btn-128" [class.no-click]="saving" > diff --git a/frontend/projects/ui/src/app/modals/app-config/app-config.page.ts b/frontend/projects/ui/src/app/modals/app-config/app-config.page.ts index 16593a491..6aa0de6c7 100644 --- a/frontend/projects/ui/src/app/modals/app-config/app-config.page.ts +++ b/frontend/projects/ui/src/app/modals/app-config/app-config.page.ts @@ -10,10 +10,10 @@ import { ApiService } from 'src/app/services/api/embassy-api.service' import { ErrorToastService, getErrorMessage, + isEmptyObject, isObject, } from '@start9labs/shared' import { DependentInfo } from 'src/app/types/dependent-info' -import { wizardModal } from 'src/app/components/app-wizard/app-wizard.component' import { WizardDefs } from 'src/app/components/app-wizard/wizard-defs' import { ConfigSpec } from 'src/app/pkg-config/config-types' import { PackageDataEntry } from 'src/app/services/patch-db/data-model' @@ -24,6 +24,8 @@ import { FormService, } from 'src/app/services/form.service' import { compare, Operation, getValueByPointer } from 'fast-json-patch' +import { hasCurrentDeps } from 'src/app/util/has-deps' +import { Breakages } from 'src/app/services/api/api.types' @Component({ selector: 'app-config', @@ -35,7 +37,7 @@ export class AppConfigPage { @Input() pkgId: string @Input() dependentInfo?: DependentInfo diff: string[] // only if dependent info - pkg?: PackageDataEntry + pkg: PackageDataEntry loadingText: string | undefined configSpec: ConfigSpec configForm: FormGroup @@ -45,7 +47,6 @@ export class AppConfigPage { loadingError: string | IonicSafeString constructor( - private readonly wizards: WizardDefs, private readonly embassyApi: ApiService, private readonly errToast: ErrorToastService, private readonly loadingCtrl: LoadingController, @@ -127,7 +128,7 @@ export class AppConfigPage { } } - async save() { + async tryConfigure() { convertValuesRecursive(this.configSpec, this.configForm) if (this.configForm.invalid) { @@ -137,48 +138,104 @@ export class AppConfigPage { return } - const hasDependents = !!Object.keys( - this.pkg?.installed?.['current-dependents'] || {}, - ).filter(depId => depId !== this.pkgId).length + this.saving = true - const config = this.configForm.value - - if (!hasDependents) { - const loader = await this.loadingCtrl.create({ - spinner: 'lines', - message: `Saving config. This could take a while...`, - }) - await loader.present() - - this.saving = true - - try { - await this.embassyApi.setPackageConfig({ - id: this.pkgId, - config, - }) - this.modalCtrl.dismiss() - } catch (e: any) { - this.errToast.present(e) - } finally { - this.saving = false - loader.dismiss() - } + if (hasCurrentDeps(this.pkg)) { + this.dryConfigure() } else { - const success = await wizardModal( - this.modalCtrl, - this.wizards.configure({ - manifest: this.pkg!.manifest, - config, - }), - ) - - if (success) { - this.modalCtrl.dismiss() - } + this.configure() } } + private async dryConfigure() { + const loader = await this.loadingCtrl.create({ + message: 'Checking dependent services...', + }) + await loader.present() + + try { + const breakages = await this.embassyApi.drySetPackageConfig({ + id: this.pkgId, + config: this.configForm.value, + }) + + if (isEmptyObject(breakages)) { + this.configure(loader) + } else { + await loader.dismiss() + const proceed = await this.presentAlertBreakages(breakages) + if (proceed) { + this.configure() + } else { + this.saving = false + } + } + } catch (e: any) { + this.errToast.present(e) + this.saving = false + } + } + + private async configure(loader?: HTMLIonLoadingElement) { + const message = 'Saving...' + if (loader) { + loader.message = message + } else { + loader = await this.loadingCtrl.create({ message }) + await loader.present() + } + + try { + await this.embassyApi.setPackageConfig({ + id: this.pkgId, + config: this.configForm.value, + }) + this.modalCtrl.dismiss() + } catch (e: any) { + this.errToast.present(e) + } finally { + this.saving = false + loader.dismiss() + } + } + + private async presentAlertBreakages(breakages: Breakages): Promise { + let message: string | IonicSafeString = + 'As a result of this change, the following services will no longer work properly and may crash:
    ' + const localPkgs = this.patch.getData()['package-data'] + const bullets = Object.keys(breakages).map(id => { + const title = localPkgs[id].manifest.title + return `
  • ${title}
  • ` + }) + message = new IonicSafeString(`${message}${bullets}
`) + + return new Promise(async resolve => { + const alert = await this.alertCtrl.create({ + header: 'Warning', + message, + buttons: [ + { + text: 'Cancel', + role: 'cancel', + handler: () => { + resolve(false) + }, + }, + { + text: 'Continue', + handler: () => { + resolve(true) + }, + cssClass: 'enter-click', + }, + ], + cssClass: 'alert-warning-message', + }) + + await alert.present() + }) + } + private getDiff(patch: Operation[]): string[] { return patch.map(op => { let message: string diff --git a/frontend/projects/ui/src/app/modals/app-recover-select/app-recover-select.page.ts b/frontend/projects/ui/src/app/modals/app-recover-select/app-recover-select.page.ts index 9f478200e..710afa864 100644 --- a/frontend/projects/ui/src/app/modals/app-recover-select/app-recover-select.page.ts +++ b/frontend/projects/ui/src/app/modals/app-recover-select/app-recover-select.page.ts @@ -68,7 +68,6 @@ export class AppRecoverSelectPage { .map(option => option.id) const loader = await this.loadingCtrl.create({ - spinner: 'lines', message: 'Initializing...', }) await loader.present() diff --git a/frontend/projects/ui/src/app/modals/os-update/os-update.page.html b/frontend/projects/ui/src/app/modals/os-update/os-update.page.html new file mode 100644 index 000000000..480a3b9aa --- /dev/null +++ b/frontend/projects/ui/src/app/modals/os-update/os-update.page.html @@ -0,0 +1,43 @@ + + +
+ EmbassyOS {{ versions[0].version }} +
+ + Release Notes + +
+ + + + + +
+
+ + +
+ +

{{ v.version }}

+
+
+
+
+
+ + + + + + Begin Update + + + + diff --git a/frontend/projects/ui/src/app/modals/os-update/os-update.page.module.ts b/frontend/projects/ui/src/app/modals/os-update/os-update.page.module.ts new file mode 100644 index 000000000..2d3e0176a --- /dev/null +++ b/frontend/projects/ui/src/app/modals/os-update/os-update.page.module.ts @@ -0,0 +1,12 @@ +import { NgModule } from '@angular/core' +import { CommonModule } from '@angular/common' +import { IonicModule } from '@ionic/angular' +import { OSUpdatePage } from './os-update.page' +import { MarkdownPipeModule } from '@start9labs/shared' + +@NgModule({ + declarations: [OSUpdatePage], + imports: [CommonModule, IonicModule, MarkdownPipeModule], + exports: [OSUpdatePage], +}) +export class OSUpdatePageModule {} diff --git a/frontend/projects/ui/src/app/modals/os-update/os-update.page.scss b/frontend/projects/ui/src/app/modals/os-update/os-update.page.scss new file mode 100644 index 000000000..586a54126 --- /dev/null +++ b/frontend/projects/ui/src/app/modals/os-update/os-update.page.scss @@ -0,0 +1,6 @@ +.underline { + margin: 6px 0 8px 16px; + border-style: solid; + border-width: 0px 0px 1px 0px; + border-color: #404040; + } \ No newline at end of file diff --git a/frontend/projects/ui/src/app/modals/os-update/os-update.page.ts b/frontend/projects/ui/src/app/modals/os-update/os-update.page.ts new file mode 100644 index 000000000..d0041a845 --- /dev/null +++ b/frontend/projects/ui/src/app/modals/os-update/os-update.page.ts @@ -0,0 +1,62 @@ +import { Component, Input } from '@angular/core' +import { LoadingController, ModalController } from '@ionic/angular' +import { ConfigService } from '../../services/config.service' +import { ApiService } from '../../services/api/embassy-api.service' +import { ErrorToastService } from '../../../../../shared/src/services/error-toast.service' + +@Component({ + selector: 'os-update', + templateUrl: './os-update.page.html', + styleUrls: ['./os-update.page.scss'], +}) +export class OSUpdatePage { + @Input() releaseNotes: { [version: string]: string } + + versions: { version: string; notes: string }[] = [] + + constructor( + private readonly modalCtrl: ModalController, + private readonly loadingCtrl: LoadingController, + private readonly config: ConfigService, + private readonly errToast: ErrorToastService, + private readonly embassyApi: ApiService, + ) {} + + ngOnInit() { + this.versions = Object.keys(this.releaseNotes) + .sort() + .reverse() + .map(version => { + return { + version, + notes: this.releaseNotes[version], + } + }) + } + + dismiss() { + this.modalCtrl.dismiss() + } + + async updateEOS() { + const loader = await this.loadingCtrl.create({ + message: 'Beginning update...', + }) + await loader.present() + + try { + await this.embassyApi.updateServer({ + 'marketplace-url': this.config.marketplace.url, + }) + this.dismiss() + } catch (e: any) { + this.errToast.present(e) + } finally { + loader.dismiss() + } + } + + asIsOrder() { + return 0 + } +} diff --git a/frontend/projects/ui/src/app/pages/apps-routes/app-actions/app-actions.page.html b/frontend/projects/ui/src/app/pages/apps-routes/app-actions/app-actions.page.html index ce36ecdf5..610d92e58 100644 --- a/frontend/projects/ui/src/app/pages/apps-routes/app-actions/app-actions.page.html +++ b/frontend/projects/ui/src/app/pages/apps-routes/app-actions/app-actions.page.html @@ -9,7 +9,6 @@ - Standard Actions + (click)="tryUninstall()" + > - Actions for {{ pkg.manifest.title }} + Actions for {{ pkg.manifest.title }} + (click)="handleAction(action)" + > - \ No newline at end of file + diff --git a/frontend/projects/ui/src/app/pages/apps-routes/app-actions/app-actions.page.ts b/frontend/projects/ui/src/app/pages/apps-routes/app-actions/app-actions.page.ts index 33332fcfb..ee8da6856 100644 --- a/frontend/projects/ui/src/app/pages/apps-routes/app-actions/app-actions.page.ts +++ b/frontend/projects/ui/src/app/pages/apps-routes/app-actions/app-actions.page.ts @@ -4,6 +4,7 @@ import { ApiService } from 'src/app/services/api/embassy-api.service' import { AlertController, IonContent, + IonicSafeString, LoadingController, ModalController, NavController, @@ -14,12 +15,11 @@ import { PackageDataEntry, PackageMainStatus, } from 'src/app/services/patch-db/data-model' -import { wizardModal } from 'src/app/components/app-wizard/app-wizard.component' -import { WizardDefs } from 'src/app/components/app-wizard/wizard-defs' import { Subscription } from 'rxjs' import { GenericFormPage } from 'src/app/modals/generic-form/generic-form.page' import { isEmptyObject, ErrorToastService, getPkgId } from '@start9labs/shared' import { ActionSuccessPage } from 'src/app/modals/action-success/action-success.page' +import { hasCurrentDeps } from 'src/app/util/has-deps' @Component({ selector: 'app-actions', @@ -39,7 +39,6 @@ export class AppActionsPage { private readonly alertCtrl: AlertController, private readonly errToast: ErrorToastService, private readonly loadingCtrl: LoadingController, - private readonly wizards: WizardDefs, private readonly navCtrl: NavController, private readonly patch: PatchDbService, ) {} @@ -136,19 +135,52 @@ export class AppActionsPage { } } - async uninstall() { - const { id, title, alerts } = this.pkg.manifest - const success = await wizardModal( - this.modalCtrl, - this.wizards.uninstall({ - id, - title, - uninstallAlert: alerts.uninstall || undefined, - }), - ) + async tryUninstall(): Promise { + const { title, alerts } = this.pkg.manifest - if (success) { - return this.navCtrl.navigateRoot('/services') + let message = + alerts.uninstall || + `Uninstalling ${title} will permanently delete its data` + + if (hasCurrentDeps(this.pkg)) { + message = `${message}. Services that depend on ${title} will no longer work properly and may crash` + } + + const alert = await this.alertCtrl.create({ + header: 'Warning', + message, + buttons: [ + { + text: 'Cancel', + role: 'cancel', + }, + { + text: 'Uninstall', + handler: () => { + this.uninstall() + }, + cssClass: 'enter-click', + }, + ], + cssClass: 'alert-warning-message', + }) + + await alert.present() + } + + private async uninstall() { + const loader = await this.loadingCtrl.create({ + message: `Beginning uninstall...`, + }) + await loader.present() + + try { + await this.embassyApi.uninstallPackage({ id: this.pkgId }) + this.navCtrl.navigateRoot('/services') + } catch (e: any) { + this.errToast.present(e) + } finally { + loader.dismiss() } } @@ -157,7 +189,6 @@ export class AppActionsPage { input?: object, ): Promise { const loader = await this.loadingCtrl.create({ - spinner: 'lines', message: 'Executing action...', }) await loader.present() diff --git a/frontend/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-status/app-show-status.component.html b/frontend/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-status/app-show-status.component.html index a3f896766..5e28b0cba 100644 --- a/frontend/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-status/app-show-status.component.html +++ b/frontend/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-status/app-show-status.component.html @@ -13,50 +13,49 @@ - - - - Stop - + + + + + + + Stop + + - - - Start - + + + Start + - - - Configure - + + + Configure + - - - Launch UI - - - + + + Launch UI + + + + + \ No newline at end of file diff --git a/frontend/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-status/app-show-status.component.ts b/frontend/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-status/app-show-status.component.ts index 9ad438499..9bf3c1e57 100644 --- a/frontend/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-status/app-show-status.component.ts +++ b/frontend/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-status/app-show-status.component.ts @@ -12,16 +12,15 @@ import { Status, } from 'src/app/services/patch-db/data-model' import { ErrorToastService } from '@start9labs/shared' -import { wizardModal } from 'src/app/components/app-wizard/app-wizard.component' -import { WizardDefs } from 'src/app/components/app-wizard/wizard-defs' import { AlertController, + IonicSafeString, LoadingController, - ModalController, } from '@ionic/angular' import { ApiService } from 'src/app/services/api/embassy-api.service' import { ModalService } from 'src/app/services/modal.service' import { DependencyInfo } from '../../pipes/to-dependencies.pipe' +import { hasCurrentDeps } from 'src/app/util/has-deps' @Component({ selector: 'app-show-status', @@ -48,9 +47,7 @@ export class AppShowStatusComponent { private readonly alertCtrl: AlertController, private readonly errToast: ErrorToastService, private readonly loadingCtrl: LoadingController, - private readonly modalCtrl: ModalController, private readonly embassyApi: ApiService, - private readonly wizards: WizardDefs, private readonly launcherService: UiLauncherService, private readonly modalService: ModalService, ) {} @@ -105,41 +102,73 @@ export class AppShowStatusComponent { this.start() } - async stop(): Promise { - const { id, title } = this.pkg.manifest - const hasDependents = !!Object.keys( - this.pkg.installed?.['current-dependents'] || {}, - ).filter(depId => depId !== id).length + async tryStop(): Promise { + const { title, alerts } = this.pkg.manifest - if (!hasDependents) { - const loader = await this.loadingCtrl.create({ - message: `Stopping...`, - spinner: 'lines', + let message = alerts.stop || '' + if (hasCurrentDeps(this.pkg)) { + const depMessage = `Services that depend on ${title} will no longer work properly and may crash` + message = message ? `${message}.\n\n${depMessage}` : depMessage + } + + if (message) { + const alert = await this.alertCtrl.create({ + header: 'Warning', + message, + buttons: [ + { + text: 'Cancel', + role: 'cancel', + }, + { + text: 'Stop', + handler: () => { + this.stop() + }, + cssClass: 'enter-click', + }, + ], + cssClass: 'alert-warning-message', }) - await loader.present() - try { - await this.embassyApi.stopPackage({ id }) - } catch (e: any) { - this.errToast.present(e) - } finally { - loader.dismiss() - } + await alert.present() } else { - wizardModal( - this.modalCtrl, - this.wizards.stop({ - id, - title, - }), - ) + this.stop() } } +<<<<<<< HEAD +======= + async tryRestart(): Promise { + if (hasCurrentDeps(this.pkg)) { + const alert = await this.alertCtrl.create({ + header: 'Warning', + message: `Services that depend on ${this.pkg.manifest.title} may temporarily experiences issues`, + buttons: [ + { + text: 'Cancel', + role: 'cancel', + }, + { + text: 'Restart', + handler: () => { + this.restart() + }, + cssClass: 'enter-click', + }, + ], + }) + + await alert.present() + } else { + this.restart() + } + } + +>>>>>>> 918a1907... Remove app wiz and dry calls (#1541) private async start(): Promise { const loader = await this.loadingCtrl.create({ message: `Starting...`, - spinner: 'lines', }) await loader.present() @@ -152,10 +181,43 @@ export class AppShowStatusComponent { } } +<<<<<<< HEAD +======= + private async stop(): Promise { + const loader = await this.loadingCtrl.create({ + message: 'Stopping...', + }) + await loader.present() + + try { + await this.embassyApi.stopPackage({ id: this.pkg.manifest.id }) + } catch (e: any) { + this.errToast.present(e) + } finally { + loader.dismiss() + } + } + + private async restart(): Promise { + const loader = await this.loadingCtrl.create({ + message: `Restarting...`, + }) + await loader.present() + + try { + await this.embassyApi.restartPackage({ id: this.pkg.manifest.id }) + } catch (e: any) { + this.errToast.present(e) + } finally { + loader.dismiss() + } + } + +>>>>>>> 918a1907... Remove app wiz and dry calls (#1541) private async presentAlertStart(message: string): Promise { return new Promise(async resolve => { const alert = await this.alertCtrl.create({ - header: 'Warning', + header: 'Alert', message, buttons: [ { diff --git a/frontend/projects/ui/src/app/pages/developer-routes/developer-list/developer-list.page.ts b/frontend/projects/ui/src/app/pages/developer-routes/developer-list/developer-list.page.ts index 092b84b75..5ced59694 100644 --- a/frontend/projects/ui/src/app/pages/developer-routes/developer-list/developer-list.page.ts +++ b/frontend/projects/ui/src/app/pages/developer-routes/developer-list/developer-list.page.ts @@ -141,7 +141,6 @@ export class DeveloperListPage { return const loader = await this.loadingCtrl.create({ - spinner: 'lines', message: 'Creating Project...', }) await loader.present() @@ -188,7 +187,6 @@ export class DeveloperListPage { async editName(id: string, newName: string) { const loader = await this.loadingCtrl.create({ - spinner: 'lines', message: 'Saving...', }) await loader.present() @@ -204,7 +202,6 @@ export class DeveloperListPage { async delete(id: string) { const loader = await this.loadingCtrl.create({ - spinner: 'lines', message: 'Removing Project...', }) await loader.present() diff --git a/frontend/projects/ui/src/app/pages/developer-routes/developer-menu/developer-menu.page.ts b/frontend/projects/ui/src/app/pages/developer-routes/developer-menu/developer-menu.page.ts index 780526d81..e50b230f5 100644 --- a/frontend/projects/ui/src/app/pages/developer-routes/developer-menu/developer-menu.page.ts +++ b/frontend/projects/ui/src/app/pages/developer-routes/developer-menu/developer-menu.page.ts @@ -72,7 +72,6 @@ export class DeveloperMenuPage { async saveBasicInfo(basicInfo: BasicInfo) { const loader = await this.loadingCtrl.create({ - spinner: 'lines', message: 'Saving...', }) await loader.present() diff --git a/frontend/projects/ui/src/app/pages/login/login.page.ts b/frontend/projects/ui/src/app/pages/login/login.page.ts index ff87713a0..cd7bf4bbf 100644 --- a/frontend/projects/ui/src/app/pages/login/login.page.ts +++ b/frontend/projects/ui/src/app/pages/login/login.page.ts @@ -34,8 +34,7 @@ export class LoginPage { this.error = '' this.loader = await this.loadingCtrl.create({ - message: 'Logging in', - spinner: 'lines', + message: 'Logging in...', }) await this.loader.present() diff --git a/frontend/projects/ui/src/app/pages/marketplace-routes/marketplace-show/marketplace-show-controls/marketplace-show-controls.component.html b/frontend/projects/ui/src/app/pages/marketplace-routes/marketplace-show/marketplace-show-controls/marketplace-show-controls.component.html index ef14808db..20ae4efae 100644 --- a/frontend/projects/ui/src/app/pages/marketplace-routes/marketplace-show/marketplace-show-controls/marketplace-show-controls.component.html +++ b/frontend/projects/ui/src/app/pages/marketplace-routes/marketplace-show/marketplace-show-controls/marketplace-show-controls.component.html @@ -10,24 +10,24 @@ Update Downgrade { + this.install() + }, + cssClass: 'enter-click', + }, + ], + }) + await alert.present() + } + + private async install(loader?: HTMLIonLoadingElement) { + const message = 'Beginning Install...' + if (loader) { + loader.message = message + } else { + loader = await this.loadingCtrl.create({ message }) + await loader.present() + } + + const { id, version } = this.pkg.manifest + + try { + await this.marketplaceService + .installPackage({ + id, + 'version-spec': `=${version}`, + }) + .pipe(first()) + .toPromise() + } catch (e: any) { + this.errToast.present(e) + } finally { + loader.dismiss() + } + } + + private async presentAlertBreakages(breakages: Breakages): Promise { + let message: string | IonicSafeString = + 'As a result of this update, the following services will no longer work properly and may crash:
    ' + const localPkgs = this.patch.getData()['package-data'] + const bullets = Object.keys(breakages).map(id => { + const title = localPkgs[id].manifest.title + return `
  • ${title}
  • ` + }) + message = new IonicSafeString(`${message}${bullets}
`) + + return new Promise(async resolve => { const alert = await this.alertCtrl.create({ - header: title, - subHeader: version, - message: alerts.install, + header: 'Warning', + message, buttons: [ { text: 'Cancel', role: 'cancel', + handler: () => { + resolve(false) + }, }, { - text: 'Install', - handler: () => - this.marketplaceService.install(id, version).subscribe(), + text: 'Continue', + handler: () => { + resolve(true) + }, + cssClass: 'enter-click', }, ], + cssClass: 'alert-warning-message', }) + await alert.present() - } - } - - async presentModal(action: 'update' | 'downgrade') { - // TODO: Fix type - const { id, title, version, dependencies, alerts } = this.pkg - .manifest as Manifest - const value = { - id, - title, - version, - serviceRequirements: dependencies, - installAlert: alerts.install || undefined, - } - - wizardModal( - this.modalCtrl, - action === 'update' - ? this.wizards.update(value) - : this.wizards.downgrade(value), - ) + }) } } diff --git a/frontend/projects/ui/src/app/pages/notifications/notifications.page.ts b/frontend/projects/ui/src/app/pages/notifications/notifications.page.ts index 620a5479c..bf098ec71 100644 --- a/frontend/projects/ui/src/app/pages/notifications/notifications.page.ts +++ b/frontend/projects/ui/src/app/pages/notifications/notifications.page.ts @@ -72,7 +72,6 @@ export class NotificationsPage { async delete(id: number, index: number): Promise { const loader = await this.loadingCtrl.create({ - spinner: 'lines', message: 'Deleting...', }) await loader.present() @@ -100,10 +99,10 @@ export class NotificationsPage { }, { text: 'Delete', - cssClass: 'enter-click', handler: () => { this.deleteAll() }, + cssClass: 'enter-click', }, ], }) @@ -160,7 +159,6 @@ export class NotificationsPage { private async deleteAll(): Promise { const loader = await this.loadingCtrl.create({ - spinner: 'lines', message: 'Deleting...', }) await loader.present() diff --git a/frontend/projects/ui/src/app/pages/server-routes/marketplaces/marketplaces.page.ts b/frontend/projects/ui/src/app/pages/server-routes/marketplaces/marketplaces.page.ts index 35c43704a..b08d56900 100644 --- a/frontend/projects/ui/src/app/pages/server-routes/marketplaces/marketplaces.page.ts +++ b/frontend/projects/ui/src/app/pages/server-routes/marketplaces/marketplaces.page.ts @@ -145,7 +145,6 @@ export class MarketplacesPage { : this.config.marketplace.url const loader = await this.loadingCtrl.create({ - spinner: 'lines', message: 'Validating Marketplace...', }) await loader.present() @@ -189,7 +188,6 @@ export class MarketplacesPage { ) const loader = await this.loadingCtrl.create({ - spinner: 'lines', message: 'Deleting...', }) await loader.present() @@ -219,7 +217,6 @@ export class MarketplacesPage { if (currentUrls.includes(new URL(url).hostname)) return const loader = await this.loadingCtrl.create({ - spinner: 'lines', message: 'Validating Marketplace...', }) @@ -264,7 +261,6 @@ export class MarketplacesPage { if (currentUrls.includes(new URL(url).hostname)) return const loader = await this.loadingCtrl.create({ - spinner: 'lines', message: 'Validating Marketplace...', }) await loader.present() diff --git a/frontend/projects/ui/src/app/pages/server-routes/preferences/preferences.page.ts b/frontend/projects/ui/src/app/pages/server-routes/preferences/preferences.page.ts index eea8a0813..89fb5392f 100644 --- a/frontend/projects/ui/src/app/pages/server-routes/preferences/preferences.page.ts +++ b/frontend/projects/ui/src/app/pages/server-routes/preferences/preferences.page.ts @@ -66,7 +66,6 @@ export class PreferencesPage { private async setDbValue(key: string, value: string): Promise { const loader = await this.loadingCtrl.create({ - spinner: 'lines', message: 'Saving...', }) await loader.present() diff --git a/frontend/projects/ui/src/app/pages/server-routes/restore/restore.component.ts b/frontend/projects/ui/src/app/pages/server-routes/restore/restore.component.ts index 5279a9477..24adff639 100644 --- a/frontend/projects/ui/src/app/pages/server-routes/restore/restore.component.ts +++ b/frontend/projects/ui/src/app/pages/server-routes/restore/restore.component.ts @@ -65,7 +65,6 @@ export class RestorePage { oldPassword?: string, ): Promise { const loader = await this.loadingCtrl.create({ - spinner: 'lines', message: 'Decrypting drive...', }) await loader.present() diff --git a/frontend/projects/ui/src/app/pages/server-routes/server-backup/server-backup.page.ts b/frontend/projects/ui/src/app/pages/server-routes/server-backup/server-backup.page.ts index 6e0c4cffe..559e931b7 100644 --- a/frontend/projects/ui/src/app/pages/server-routes/server-backup/server-backup.page.ts +++ b/frontend/projects/ui/src/app/pages/server-routes/server-backup/server-backup.page.ts @@ -158,7 +158,6 @@ export class ServerBackupPage { oldPassword?: string, ): Promise { const loader = await this.loadingCtrl.create({ - spinner: 'lines', message: 'Beginning backup...', }) await loader.present() diff --git a/frontend/projects/ui/src/app/pages/server-routes/server-show/server-show.module.ts b/frontend/projects/ui/src/app/pages/server-routes/server-show/server-show.module.ts index 2efdac950..8f8086bc6 100644 --- a/frontend/projects/ui/src/app/pages/server-routes/server-show/server-show.module.ts +++ b/frontend/projects/ui/src/app/pages/server-routes/server-show/server-show.module.ts @@ -6,6 +6,7 @@ import { ServerShowPage } from './server-show.page' import { FormsModule } from '@angular/forms' import { TextSpinnerComponentModule } from '@start9labs/shared' import { BadgeMenuComponentModule } from 'src/app/components/badge-menu-button/badge-menu.component.module' +import { OSUpdatePageModule } from 'src/app/modals/os-update/os-update.page.module' const routes: Routes = [ { @@ -22,6 +23,7 @@ const routes: Routes = [ RouterModule.forChild(routes), TextSpinnerComponentModule, BadgeMenuComponentModule, + OSUpdatePageModule, ], declarations: [ServerShowPage], }) diff --git a/frontend/projects/ui/src/app/pages/server-routes/server-show/server-show.page.html b/frontend/projects/ui/src/app/pages/server-routes/server-show/server-show.page.html index 7ea2d6767..dadfe79ff 100644 --- a/frontend/projects/ui/src/app/pages/server-routes/server-show/server-show.page.html +++ b/frontend/projects/ui/src/app/pages/server-routes/server-show/server-show.page.html @@ -69,18 +69,16 @@

- - - Update Complete, Restart to apply changes - - - + + Update Complete. Restart to apply changes + + diff --git a/frontend/projects/ui/src/app/pages/server-routes/server-show/server-show.page.ts b/frontend/projects/ui/src/app/pages/server-routes/server-show/server-show.page.ts index 2b6cdf075..89384de6c 100644 --- a/frontend/projects/ui/src/app/pages/server-routes/server-show/server-show.page.ts +++ b/frontend/projects/ui/src/app/pages/server-routes/server-show/server-show.page.ts @@ -11,12 +11,11 @@ import { ActivatedRoute } from '@angular/router' import { PatchDbService } from 'src/app/services/patch-db/patch-db.service' import { Observable, of } from 'rxjs' import { filter, map, take } from 'rxjs/operators' -import { wizardModal } from 'src/app/components/app-wizard/app-wizard.component' -import { WizardDefs } from 'src/app/components/app-wizard/wizard-defs' import { exists, 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' @Component({ selector: 'server-show', @@ -33,7 +32,6 @@ export class ServerShowPage { constructor( private readonly alertCtrl: AlertController, private readonly modalCtrl: ModalController, - private readonly wizards: WizardDefs, private readonly loadingCtrl: LoadingController, private readonly errToast: ErrorToastService, private readonly embassyApi: ApiService, @@ -63,26 +61,19 @@ export class ServerShowPage { }) await alert.present() } else { - const { - version, - headline, - 'release-notes': releaseNotes, - } = this.eosService.eos - - await wizardModal( - this.modalCtrl, - this.wizards.updateOS({ - version, - headline, - releaseNotes, - }), - ) + const modal = await this.modalCtrl.create({ + componentProps: { + releaseNotes: this.eosService.eos['release-notes'], + }, + component: OSUpdatePage, + }) + modal.present() } } async presentAlertRestart() { const alert = await this.alertCtrl.create({ - header: 'Confirm', + header: 'Restart', message: 'Are you sure you want to restart your Embassy? It can take several minutes to come back online.', buttons: [ @@ -106,7 +97,7 @@ export class ServerShowPage { const alert = await this.alertCtrl.create({ header: 'Warning', message: - 'Are you sure you want to power down your Embassy? This can take several minutes, and your Embassy will not come back online automatically. To power on again, You will need to physically unplug your Embassy and plug it back in.', + 'Are you sure you want to power down your Embassy? This can take several minutes, and your Embassy will not come back online automatically. To power on again, You will need to physically unplug your Embassy and plug it back in', buttons: [ { text: 'Cancel', @@ -120,6 +111,7 @@ export class ServerShowPage { cssClass: 'enter-click', }, ], + cssClass: 'alert-warning-message', }) await alert.present() } @@ -127,9 +119,9 @@ export class ServerShowPage { async presentAlertSystemRebuild() { const minutes = Object.keys(this.patch.getData()['package-data']).length * 2 const alert = await this.alertCtrl.create({ - header: 'System Rebuild', + header: 'Warning', message: new IonicSafeString( - `Warning: This action will tear down all service containers and rebuild them from scratch. No data will be deleted. This action is useful if your system gets into a bad state, and it should only be performed if you are experiencing general performance or reliability issues. It may take up to ${minutes} minutes to complete. During this time, you will lose all connectivity to your Embassy.`, + `This action will tear down all service containers and rebuild them from scratch. No data will be deleted. This action is useful if your system gets into a bad state, and it should only be performed if you are experiencing general performance or reliability issues. It may take up to ${minutes} minutes to complete. During this time, you will lose all connectivity to your Embassy.`, ), buttons: [ { @@ -144,15 +136,16 @@ export class ServerShowPage { cssClass: 'enter-click', }, ], + cssClass: 'alert-warning-message', }) await alert.present() } async presentAlertRepairDisk() { const alert = await this.alertCtrl.create({ - header: 'Repair Disk', + header: 'Warning', message: new IonicSafeString( - `Warning:

This action will attempt to preform a disk repair operation and system reboot. No data will be deleted. This action should only be executed if directed by a Start9 support specialist. We recommend backing up your device before preforming this action.

If anything happens to the device during the reboot (between the bep and chime), such as loosing power, a power surge, unplugging the drive, or unplugging the Embassy, the filesystem *will* be in an unrecoverable state. Please proceed with caution.

`, + `

This action will attempt to preform a disk repair operation and system reboot. No data will be deleted. This action should only be executed if directed by a Start9 support specialist. We recommend backing up your device before preforming this action.

If anything happens to the device during the reboot (between the bep and chime), such as loosing power, a power surge, unplugging the drive, or unplugging the Embassy, the filesystem will be in an unrecoverable state. Please proceed with caution.

`, ), buttons: [ { @@ -173,13 +166,13 @@ export class ServerShowPage { cssClass: 'enter-click', }, ], + cssClass: 'alert-warning-message', }) await alert.present() } private async restart() { const loader = await this.loadingCtrl.create({ - spinner: 'lines', message: 'Restarting...', }) await loader.present() @@ -195,7 +188,6 @@ export class ServerShowPage { private async shutdown() { const loader = await this.loadingCtrl.create({ - spinner: 'lines', message: 'Shutting down...', }) await loader.present() @@ -211,7 +203,6 @@ export class ServerShowPage { private async systemRebuild() { const loader = await this.loadingCtrl.create({ - spinner: 'lines', message: 'Hard Restarting...', }) await loader.present() @@ -227,7 +218,6 @@ export class ServerShowPage { private async checkForEosUpdate(): Promise { const loader = await this.loadingCtrl.create({ - spinner: 'lines', message: 'Checking for updates', }) await loader.present() @@ -282,14 +272,7 @@ export class ServerShowPage { action: () => this.navCtrl.navigateForward(['restore'], { relativeTo: this.route }), detail: true, - disabled: this.patch - .watch$('server-info', 'status-info') - .pipe( - map( - status => - status && (status['backing-up'] || !!status['update-progress']), - ), - ), + disabled: this.eosService.updatingOrBackingUp$, }, ], Settings: [ @@ -302,17 +285,7 @@ export class ServerShowPage { ? this.updateEos() : this.checkForEosUpdate(), detail: false, - disabled: this.patch - .watch$('server-info', 'status-info') - .pipe( - map( - status => - status && - (status['backing-up'] || - !!status['update-progress'] || - status.updated), - ), - ), + disabled: this.eosService.updatingOrBackingUp$, }, { title: 'Preferences', diff --git a/frontend/projects/ui/src/app/pages/server-routes/sessions/sessions.page.ts b/frontend/projects/ui/src/app/pages/server-routes/sessions/sessions.page.ts index 83993655a..f59e3ed51 100644 --- a/frontend/projects/ui/src/app/pages/server-routes/sessions/sessions.page.ts +++ b/frontend/projects/ui/src/app/pages/server-routes/sessions/sessions.page.ts @@ -53,7 +53,6 @@ export class SessionsPage { async kill(id: string): Promise { const loader = await this.loadingCtrl.create({ - spinner: 'lines', message: 'Killing session...', }) await loader.present() diff --git a/frontend/projects/ui/src/app/pages/server-routes/sideload/sideload.page.ts b/frontend/projects/ui/src/app/pages/server-routes/sideload/sideload.page.ts index a0d38ec49..7a904a419 100644 --- a/frontend/projects/ui/src/app/pages/server-routes/sideload/sideload.page.ts +++ b/frontend/projects/ui/src/app/pages/server-routes/sideload/sideload.page.ts @@ -50,8 +50,7 @@ export class SideloadPage { } async setFile(files?: File[]) { const loader = await this.loadingCtrl.create({ - spinner: 'lines', - message: 'Verifying Package', + message: 'Verifying package', cssClass: 'loader', }) await loader.present() @@ -84,8 +83,7 @@ export class SideloadPage { async handleUpload() { const loader = await this.loadingCtrl.create({ - spinner: 'lines', - message: 'Uploading Package', + message: 'Uploading package', cssClass: 'loader', }) await loader.present() diff --git a/frontend/projects/ui/src/app/pages/server-routes/ssh-keys/ssh-keys.page.ts b/frontend/projects/ui/src/app/pages/server-routes/ssh-keys/ssh-keys.page.ts index 93d5a3184..5ac59787a 100644 --- a/frontend/projects/ui/src/app/pages/server-routes/ssh-keys/ssh-keys.page.ts +++ b/frontend/projects/ui/src/app/pages/server-routes/ssh-keys/ssh-keys.page.ts @@ -64,7 +64,6 @@ export class SSHKeysPage { async add(pubkey: string): Promise { const loader = await this.loadingCtrl.create({ - spinner: 'lines', message: 'Saving...', }) await loader.present() @@ -100,7 +99,6 @@ export class SSHKeysPage { async delete(i: number): Promise { const loader = await this.loadingCtrl.create({ - spinner: 'lines', message: 'Deleting...', }) await loader.present() diff --git a/frontend/projects/ui/src/app/pages/server-routes/wifi/wifi.page.ts b/frontend/projects/ui/src/app/pages/server-routes/wifi/wifi.page.ts index 784f2e9b8..b779b9068 100644 --- a/frontend/projects/ui/src/app/pages/server-routes/wifi/wifi.page.ts +++ b/frontend/projects/ui/src/app/pages/server-routes/wifi/wifi.page.ts @@ -170,7 +170,7 @@ export class WifiPage { private async setCountry(country: string): Promise { const loader = await this.loadingCtrl.create({ - spinner: 'lines', + message: 'Setting country...', }) await loader.present() @@ -261,7 +261,6 @@ export class WifiPage { private async connect(ssid: string): Promise { const loader = await this.loadingCtrl.create({ - spinner: 'lines', message: 'Connecting. This could take a while...', }) await loader.present() @@ -278,7 +277,6 @@ export class WifiPage { private async delete(ssid: string): Promise { const loader = await this.loadingCtrl.create({ - spinner: 'lines', message: 'Deleting...', }) await loader.present() @@ -296,7 +294,6 @@ export class WifiPage { private async save(ssid: string, password: string): Promise { const loader = await this.loadingCtrl.create({ - spinner: 'lines', message: 'Saving...', }) await loader.present() @@ -318,7 +315,6 @@ export class WifiPage { private async saveAndConnect(ssid: string, password: string): Promise { const loader = await this.loadingCtrl.create({ - spinner: 'lines', message: 'Connecting. This could take a while...', }) await loader.present() diff --git a/frontend/projects/ui/src/app/services/api/api.fixures.ts b/frontend/projects/ui/src/app/services/api/api.fixures.ts index 3b5ca1972..b5d927c64 100644 --- a/frontend/projects/ui/src/app/services/api/api.fixures.ts +++ b/frontend/projects/ui/src/app/services/api/api.fixures.ts @@ -62,6 +62,7 @@ export module Mock { 'Chain state will be lost, as will any funds stored on your Bitcoin Core waller that have not been backed up.', restore: null, start: 'Starting Bitcoin is good for your health.', + stop: null, }, main: { type: 'docker', @@ -359,6 +360,7 @@ export module Mock { restore: 'If this is a duplicate instance of the same LND node, you may loose your funds.', start: 'Starting LND is good for your health.', + stop: null, }, main: { type: 'docker', @@ -499,10 +501,11 @@ export module Mock { 'marketing-site': '', 'donation-url': 'https://start9.com', alerts: { - install: null, + install: 'Testing install alert', uninstall: null, restore: null, start: null, + stop: null, }, main: { type: 'docker', diff --git a/frontend/projects/ui/src/app/services/api/api.types.ts b/frontend/projects/ui/src/app/services/api/api.types.ts index ca09f5846..e3b036a44 100644 --- a/frontend/projects/ui/src/app/services/api/api.types.ts +++ b/frontend/projects/ui/src/app/services/api/api.types.ts @@ -215,15 +215,9 @@ export module RR { export type StartPackageReq = WithExpire<{ id: string }> // package.start export type StartPackageRes = WithRevision - export type DryStopPackageReq = StopPackageReq // package.stop.dry - export type DryStopPackageRes = Breakages - export type StopPackageReq = WithExpire<{ id: string }> // package.stop export type StopPackageRes = WithRevision - export type DryUninstallPackageReq = UninstallPackageReq // package.uninstall.dry - export type DryUninstallPackageRes = Breakages - export type UninstallPackageReq = WithExpire<{ id: string }> // package.uninstall export type UninstallPackageRes = WithRevision diff --git a/frontend/projects/ui/src/app/services/api/embassy-api.service.ts b/frontend/projects/ui/src/app/services/api/embassy-api.service.ts index dca58e3e6..495e80f9b 100644 --- a/frontend/projects/ui/src/app/services/api/embassy-api.service.ts +++ b/frontend/projects/ui/src/app/services/api/embassy-api.service.ts @@ -233,20 +233,12 @@ export abstract class ApiService implements Source, Http { startPackage = (params: RR.StartPackageReq) => this.syncResponse(() => this.startPackageRaw(params))() - abstract dryStopPackage( - params: RR.DryStopPackageReq, - ): Promise - protected abstract stopPackageRaw( params: RR.StopPackageReq, ): Promise stopPackage = (params: RR.StopPackageReq) => this.syncResponse(() => this.stopPackageRaw(params))() - abstract dryUninstallPackage( - params: RR.DryUninstallPackageReq, - ): Promise - protected abstract uninstallPackageRaw( params: RR.UninstallPackageReq, ): Promise @@ -273,7 +265,7 @@ export abstract class ApiService implements Source, Http { private syncResponse< T, F extends (...args: any[]) => Promise<{ response: T; revision?: Revision }>, - >(f: F, temp?: Operation): (...args: Parameters) => Promise { + >(f: F, temp?: Operation): (...args: Parameters) => Promise { return (...a) => { // let expireId = undefined // if (temp) { diff --git a/frontend/projects/ui/src/app/services/api/embassy-live-api.service.ts b/frontend/projects/ui/src/app/services/api/embassy-live-api.service.ts index c487c0960..2ee89804d 100644 --- a/frontend/projects/ui/src/app/services/api/embassy-live-api.service.ts +++ b/frontend/projects/ui/src/app/services/api/embassy-live-api.service.ts @@ -12,7 +12,7 @@ export class LiveApiService extends ApiService { private readonly config: ConfigService, ) { super() - ;(window as any).rpcClient = this + ; (window as any).rpcClient = this } async getStatic(url: string): Promise { @@ -306,22 +306,10 @@ export class LiveApiService extends ApiService { return this.http.rpcRequest({ method: 'package.start', params }) } - async dryStopPackage( - params: RR.DryStopPackageReq, - ): Promise { - return this.http.rpcRequest({ method: 'package.stop.dry', params }) - } - async stopPackageRaw(params: RR.StopPackageReq): Promise { return this.http.rpcRequest({ method: 'package.stop', params }) } - async dryUninstallPackage( - params: RR.DryUninstallPackageReq, - ): Promise { - return this.http.rpcRequest({ method: 'package.uninstall.dry', params }) - } - async deleteRecoveredPackageRaw( params: RR.DeleteRecoveredPackageReq, ): Promise { diff --git a/frontend/projects/ui/src/app/services/api/embassy-mock-api.service.ts b/frontend/projects/ui/src/app/services/api/embassy-mock-api.service.ts index 5711f4ca7..dad557746 100644 --- a/frontend/projects/ui/src/app/services/api/embassy-mock-api.service.ts +++ b/frontend/projects/ui/src/app/services/api/embassy-mock-api.service.ts @@ -501,7 +501,16 @@ export class MockApiService extends ApiService { params: RR.DryUpdatePackageReq, ): Promise { await pauseFor(2000) - return {} + return { + lnd: { + dependency: 'bitcoind', + error: { + type: DependencyErrorType.IncorrectVersion, + expected: '>0.23.0', + received: params.version, + }, + }, + } } async getPackageConfig( @@ -645,20 +654,6 @@ export class MockApiService extends ApiService { return this.withRevision(originalPatch) } - async dryStopPackage( - params: RR.DryStopPackageReq, - ): Promise { - await pauseFor(2000) - return { - lnd: { - dependency: 'bitcoind', - error: { - type: DependencyErrorType.NotRunning, - }, - }, - } - } - async stopPackageRaw(params: RR.StopPackageReq): Promise { await pauseFor(2000) const path = `/package-data/${params.id}/installed/status/main` @@ -690,20 +685,6 @@ export class MockApiService extends ApiService { return this.withRevision(patch) } - async dryUninstallPackage( - params: RR.DryUninstallPackageReq, - ): Promise { - await pauseFor(2000) - return { - lnd: { - dependency: 'bitcoind', - error: { - type: DependencyErrorType.NotRunning, - }, - }, - } - } - async uninstallPackageRaw( params: RR.UninstallPackageReq, ): Promise { diff --git a/frontend/projects/ui/src/app/services/api/mock-patch.ts b/frontend/projects/ui/src/app/services/api/mock-patch.ts index bc700f6df..48d18b084 100644 --- a/frontend/projects/ui/src/app/services/api/mock-patch.ts +++ b/frontend/projects/ui/src/app/services/api/mock-patch.ts @@ -68,6 +68,7 @@ export const mockPatchData: DataModel = { 'Chain state will be lost, as will any funds stored on your Bitcoin Core waller that have not been backed up.', restore: null, start: 'Starting Bitcoin is good for your health.', + stop: null, }, main: { type: 'docker', @@ -448,6 +449,7 @@ export const mockPatchData: DataModel = { restore: 'If this is a duplicate instance of the same LND node, you may loose your funds.', start: 'Starting LND is good for your health.', + stop: null, }, main: { type: 'docker', diff --git a/frontend/projects/ui/src/app/services/eos.service.ts b/frontend/projects/ui/src/app/services/eos.service.ts index 3446c1d59..2369512c3 100644 --- a/frontend/projects/ui/src/app/services/eos.service.ts +++ b/frontend/projects/ui/src/app/services/eos.service.ts @@ -1,10 +1,10 @@ import { Injectable } from '@angular/core' -import { BehaviorSubject } from 'rxjs' +import { BehaviorSubject, combineLatest } from 'rxjs' import { MarketplaceEOS } from 'src/app/services/api/api.types' import { ApiService } from 'src/app/services/api/embassy-api.service' import { Emver } from '@start9labs/shared' import { PatchDbService } from 'src/app/services/patch-db/patch-db.service' -import { switchMap, take } from 'rxjs/operators' +import { map } from 'rxjs/operators' @Injectable({ providedIn: 'root', @@ -13,6 +13,36 @@ export class EOSService { eos: MarketplaceEOS updateAvailable$ = new BehaviorSubject(false) + readonly updating$ = this.patch.watch$('server-info', 'status-info').pipe( + map(status => { + return status && (!!status['update-progress'] || status.updated) + }), + ) + + readonly backingUp$ = this.patch.watch$( + 'server-info', + 'status-info', + 'backing-up', + ) + + readonly updatingOrBackingUp$ = combineLatest([ + this.updating$, + this.backingUp$, + ]).pipe( + map(([updating, backingUp]) => { + return updating || backingUp + }), + ) + + readonly showUpdate$ = combineLatest([ + this.updateAvailable$, + this.updating$, + ]).pipe( + map(([available, updating]) => { + return available && !updating + }), + ) + constructor( private readonly api: ApiService, private readonly emver: Emver, diff --git a/frontend/projects/ui/src/app/services/marketplace.service.ts b/frontend/projects/ui/src/app/services/marketplace.service.ts index 52f4d00d3..8088a26e2 100644 --- a/frontend/projects/ui/src/app/services/marketplace.service.ts +++ b/frontend/projects/ui/src/app/services/marketplace.service.ts @@ -145,28 +145,6 @@ export class MarketplaceService extends AbstractMarketplaceService { ) } - install(id: string, version?: string): Observable { - return defer(() => - from( - this.loadingCtrl.create({ - spinner: 'lines', - message: 'Beginning Installation', - }), - ), - ).pipe( - tap(loader => loader.present()), - switchMap(loader => - this.installPackage({ - id, - 'version-spec': version ? `=${version}` : undefined, - }).pipe( - catchError(e => from(this.errToast.present(e))), - tap(() => loader.dismiss()), - ), - ), - ) - } - installPackage( req: Omit, ): Observable { diff --git a/frontend/projects/ui/src/app/services/server-config.service.ts b/frontend/projects/ui/src/app/services/server-config.service.ts index 8d1fc4f5f..cba6b2d22 100644 --- a/frontend/projects/ui/src/app/services/server-config.service.ts +++ b/frontend/projects/ui/src/app/services/server-config.service.ts @@ -32,7 +32,6 @@ export class ServerConfigService { text: 'Save', handler: async (data: any) => { const loader = await this.loadingCtrl.create({ - spinner: 'lines', message: 'Saving...', }) loader.present() diff --git a/frontend/projects/ui/src/app/util/has-deps.ts b/frontend/projects/ui/src/app/util/has-deps.ts new file mode 100644 index 000000000..115a09292 --- /dev/null +++ b/frontend/projects/ui/src/app/util/has-deps.ts @@ -0,0 +1,7 @@ +import { PackageDataEntry } from '../services/patch-db/data-model' + +export function hasCurrentDeps(pkg: PackageDataEntry): boolean { + return !!Object.keys(pkg.installed?.['current-dependents'] || {}) + // @TODO fix Manifest type + .filter(depId => depId !== (pkg.manifest as any).id).length +} diff --git a/frontend/projects/ui/src/app/util/rxjs.util.ts b/frontend/projects/ui/src/app/util/rxjs.util.ts index 449e62d87..5554c77c6 100644 --- a/frontend/projects/ui/src/app/util/rxjs.util.ts +++ b/frontend/projects/ui/src/app/util/rxjs.util.ts @@ -5,7 +5,6 @@ import { race, OperatorFunction, Observer, - combineLatest, } from 'rxjs' import { take, map } from 'rxjs/operators' diff --git a/frontend/projects/ui/src/styles.scss b/frontend/projects/ui/src/styles.scss index 70250ed65..2a2f262d0 100644 --- a/frontend/projects/ui/src/styles.scss +++ b/frontend/projects/ui/src/styles.scss @@ -205,6 +205,12 @@ ion-button { } } +.alert-warning-message { + .alert-title { + color: var(--ion-color-warning); + } +} + .alert-success-message { .alert-title { color: var(--ion-color-success);