diff --git a/ui/src/app/components/install-wizard/install-wizard.component.html b/ui/src/app/components/install-wizard/install-wizard.component.html index 80af87d79..d1df0a92c 100644 --- a/ui/src/app/components/install-wizard/install-wizard.component.html +++ b/ui/src/app/components/install-wizard/install-wizard.component.html @@ -11,7 +11,7 @@ - + diff --git a/ui/src/app/components/install-wizard/install-wizard.component.module.ts b/ui/src/app/components/install-wizard/install-wizard.component.module.ts index 0586ba594..a45248ddc 100644 --- a/ui/src/app/components/install-wizard/install-wizard.component.module.ts +++ b/ui/src/app/components/install-wizard/install-wizard.component.module.ts @@ -7,7 +7,7 @@ import { SharingModule } from 'src/app/modules/sharing.module' import { DependenciesComponentModule } from './dependencies/dependencies.component.module' import { DependentsComponentModule } from './dependents/dependents.component.module' import { CompleteComponentModule } from './complete/complete.component.module' -import { DeveloperNotesComponentModule } from './developer-notes/developer-notes.component.module' +import { DeveloperNotesComponentModule } from './notes/notes.component.module' @NgModule({ declarations: [ diff --git a/ui/src/app/components/install-wizard/install-wizard.component.scss b/ui/src/app/components/install-wizard/install-wizard.component.scss index 03a59a661..7066352d1 100644 --- a/ui/src/app/components/install-wizard/install-wizard.component.scss +++ b/ui/src/app/components/install-wizard/install-wizard.component.scss @@ -47,6 +47,7 @@ font-size: small; border-width: 0px 0px 1px 0px; border-color: #393b40; + text-align: left; } @media (min-width:500px) { @@ -57,6 +58,7 @@ font-size: medium; border-width: 0px 0px 1px 0px; border-color: #393b40; + text-align: left; } } diff --git a/ui/src/app/components/install-wizard/install-wizard.component.ts b/ui/src/app/components/install-wizard/install-wizard.component.ts index ffa46834c..63aa8497e 100644 --- a/ui/src/app/components/install-wizard/install-wizard.component.ts +++ b/ui/src/app/components/install-wizard/install-wizard.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit, QueryList, ViewChild, ViewChildren } from '@angular/core' +import { Component, Input, NgZone, OnInit, QueryList, ViewChild, ViewChildren } from '@angular/core' import { IonContent, IonSlides, ModalController } from '@ionic/angular' import { BehaviorSubject, combineLatest, Subscription } from 'rxjs' import { map } from 'rxjs/operators' @@ -7,7 +7,7 @@ import { capitalizeFirstLetter } from 'src/app/util/misc.util' import { CompleteComponent } from './complete/complete.component' import { DependenciesComponent } from './dependencies/dependencies.component' import { DependentsComponent } from './dependents/dependents.component' -import { DeveloperNotesComponent } from './developer-notes/developer-notes.component' +import { DeveloperNotesComponent } from './notes/notes.component' import { Colorable, Loadable } from './loadable' import { WizardAction } from './wizard-types' @@ -50,7 +50,7 @@ export class InstallWizardComponent extends Cleanup implements OnInit { $currentColor$: BehaviorSubject = new BehaviorSubject('medium') $error$ = new BehaviorSubject(undefined) - constructor (private readonly modalController: ModalController) { super() } + constructor (private readonly modalController: ModalController, private readonly zone: NgZone) { super() } ngOnInit () { } ngAfterViewInit () { @@ -80,15 +80,17 @@ export class InstallWizardComponent extends Cleanup implements OnInit { private async slide () { if (this.slideComponents[this.slideIndex + 1] === undefined) { return this.finished({ final: true }) } - this.slideIndex += 1 - this.currentSlide.load() - await this.slideContainer.lockSwipes(false) - await Promise.all([ - this.contentContainer.scrollToTop(), - this.slideContainer.slideNext(500), - ]) - await this.slideContainer.lockSwipes(true) - this.slideContainer.update() + this.zone.run(async () => { + this.slideIndex += 1 + this.currentSlide.load() + await this.slideContainer.lockSwipes(false) + await Promise.all([ + this.contentContainer.scrollToTop(), + this.slideContainer.slideNext(500), + ]) + await this.slideContainer.lockSwipes(true) + // this.slideContainer.update() + }) } } @@ -114,7 +116,7 @@ export type SlideDefinition = SlideCommon & ( selector: 'complete', params: CompleteComponent['params'] } | { - selector: 'developer-notes', + selector: 'notes', params: DeveloperNotesComponent['params'] } ) diff --git a/ui/src/app/components/install-wizard/developer-notes/developer-notes.component.html b/ui/src/app/components/install-wizard/notes/notes.component.html similarity index 87% rename from ui/src/app/components/install-wizard/developer-notes/developer-notes.component.html rename to ui/src/app/components/install-wizard/notes/notes.component.html index 9b5ca1b81..6dfed7f2d 100644 --- a/ui/src/app/components/install-wizard/developer-notes/developer-notes.component.html +++ b/ui/src/app/components/install-wizard/notes/notes.component.html @@ -2,11 +2,11 @@
- Warning + {{params.title}}
- {{params.developerNotes}} + {{params.notes}}
diff --git a/ui/src/app/components/install-wizard/developer-notes/developer-notes.component.module.ts b/ui/src/app/components/install-wizard/notes/notes.component.module.ts similarity index 87% rename from ui/src/app/components/install-wizard/developer-notes/developer-notes.component.module.ts rename to ui/src/app/components/install-wizard/notes/notes.component.module.ts index 993e369ba..6c80d04cd 100644 --- a/ui/src/app/components/install-wizard/developer-notes/developer-notes.component.module.ts +++ b/ui/src/app/components/install-wizard/notes/notes.component.module.ts @@ -1,6 +1,6 @@ import { NgModule } from '@angular/core' import { CommonModule } from '@angular/common' -import { DeveloperNotesComponent } from './developer-notes.component' +import { DeveloperNotesComponent } from './notes.component' import { IonicModule } from '@ionic/angular' import { RouterModule } from '@angular/router' import { SharingModule } from 'src/app/modules/sharing.module' diff --git a/ui/src/app/components/install-wizard/developer-notes/developer-notes.component.ts b/ui/src/app/components/install-wizard/notes/notes.component.ts similarity index 69% rename from ui/src/app/components/install-wizard/developer-notes/developer-notes.component.ts rename to ui/src/app/components/install-wizard/notes/notes.component.ts index 22a68203b..4badbd982 100644 --- a/ui/src/app/components/install-wizard/developer-notes/developer-notes.component.ts +++ b/ui/src/app/components/install-wizard/notes/notes.component.ts @@ -4,24 +4,24 @@ import { Colorable, Loadable } from '../loadable' import { WizardAction } from '../wizard-types' @Component({ - selector: 'developer-notes', - templateUrl: './developer-notes.component.html', + selector: 'notes', + templateUrl: './notes.component.html', styleUrls: ['../install-wizard.component.scss'], }) export class DeveloperNotesComponent implements OnInit, Loadable, Colorable { @Input() params: { action: WizardAction - developerNotes: string + notes: string + title: string + titleColor: string } $loading$ = new BehaviorSubject(false) - $color$ = new BehaviorSubject('warning') + $color$ = new BehaviorSubject('light') $cancel$ = new Subject() load () { } constructor () { } - ngOnInit () { - console.log('Developer Notes', this.params) - } + ngOnInit () { this.$color$.next(this.params.titleColor) } } diff --git a/ui/src/app/components/install-wizard/prebaked-wizards.ts b/ui/src/app/components/install-wizard/prebaked-wizards.ts index 521df79d4..3ebfb36c3 100644 --- a/ui/src/app/components/install-wizard/prebaked-wizards.ts +++ b/ui/src/app/components/install-wizard/prebaked-wizards.ts @@ -1,5 +1,6 @@ import { Injectable } from '@angular/core' import { AppModel, AppStatus } from 'src/app/models/app-model' +import { OsUpdateService } from 'src/app/services/os-update.service' import { exists } from 'src/app/util/misc.util' import { AppDependency, DependentBreakage, AppInstalledPreview } from '../../models/app-types' import { ApiService } from '../../services/api/api.service' @@ -7,7 +8,11 @@ import { InstallWizardComponent, SlideDefinition, TopbarParams } from './install @Injectable({ providedIn: 'root' }) export class WizardBaker { - constructor (private readonly apiService: ApiService, private readonly appModel: AppModel) { } + constructor ( + private readonly apiService: ApiService, + private readonly updateService: OsUpdateService, + private readonly appModel: AppModel + ) { } install (values: { id: string, title: string, version: string, serviceRequirements: AppDependency[], installAlert?: string @@ -23,8 +28,8 @@ export class WizardBaker { const toolbar: TopbarParams = { action, title, version } const slideDefinitions: SlideDefinition[] = [ - installAlert ? { selector: 'developer-notes', cancelButton: { afterLoading: { text: 'Cancel' } }, nextButton: 'Next', params: { - action, developerNotes: installAlert, + installAlert ? { selector: 'notes', cancelButton: { afterLoading: { text: 'Cancel' } }, nextButton: 'Next', params: { + action, notes: installAlert, title: 'Warning', titleColor: 'warning', }} : undefined, { selector: 'dependencies', cancelButton: { afterLoading: { text: 'Cancel' } }, nextButton: 'Install', params: { action, title, version, serviceRequirements, @@ -52,8 +57,8 @@ export class WizardBaker { const toolbar: TopbarParams = { action, title, version } const slideDefinitions: SlideDefinition[] = [ - installAlert ? { selector: 'developer-notes', cancelButton: { afterLoading: { text: 'Cancel' } }, nextButton: 'Next', params: { - action, developerNotes: installAlert, + installAlert ? { selector: 'notes', cancelButton: { afterLoading: { text: 'Cancel' } }, nextButton: 'Next', params: { + action, notes: installAlert, title: 'Warning', titleColor: 'warning', }} : undefined, { selector: 'dependencies', cancelButton: { afterLoading: { text: 'Cancel' } }, nextButton: 'Update', params: { action, title, version, serviceRequirements, @@ -70,6 +75,29 @@ export class WizardBaker { return { toolbar, slideDefinitions: slideDefinitions.filter(exists) } } + updateOS (values: { + version: string, releaseNotes: string + }): InstallWizardComponent['params'] { + const { version, releaseNotes } = values + + validate(version, exists, 'missing version') + validate(releaseNotes, exists, 'missing updateMessage') + + const action = 'update' + const title = 'EmbassyOS' + const toolbar: TopbarParams = { action, title, version } + + const slideDefinitions: SlideDefinition[] = [ + { selector: 'notes', cancelButton: { afterLoading: { text: 'Cancel' } }, nextButton: 'Update OS', params: { + action, notes: releaseNotes, title: 'Release Notes', titleColor: 'dark', + }}, + { selector: 'complete', finishButton: 'Dismiss', cancelButton: { whileLoading: { } }, params: { + action, verb: 'beginning update for', title, executeAction: () => this.updateService.updateEmbassyOS(version), + }}, + ] + return { toolbar, slideDefinitions: slideDefinitions.filter(exists) } + } + downgrade (values: { id: string, title: string, version: string, serviceRequirements: AppDependency[], installAlert?: string }): InstallWizardComponent['params'] { @@ -84,8 +112,8 @@ export class WizardBaker { const toolbar: TopbarParams = { action, title, version } const slideDefinitions: SlideDefinition[] = [ - installAlert ? { selector: 'developer-notes', cancelButton: { afterLoading: { text: 'Cancel' } }, nextButton: 'Next', params: { - action, developerNotes: installAlert, + installAlert ? { selector: 'notes', cancelButton: { afterLoading: { text: 'Cancel' } }, nextButton: 'Next', params: { + action, notes: installAlert, title: 'Warning', titleColor: 'warning', }} : undefined, { selector: 'dependencies', cancelButton: { afterLoading: { text: 'Cancel' } }, nextButton: 'Downgrade', params: { action, title, version, serviceRequirements, @@ -115,8 +143,8 @@ export class WizardBaker { const toolbar: TopbarParams = { action, title, version } const slideDefinitions: SlideDefinition[] = [ - { selector: 'developer-notes', cancelButton: { afterLoading: { text: 'Cancel' } }, nextButton: 'Continue', params: { - action, developerNotes: uninstallAlert || defaultUninstallationWarning(title) }, + { selector: 'notes', cancelButton: { afterLoading: { text: 'Cancel' } }, nextButton: 'Continue', params: { + action, notes: uninstallAlert || defaultUninstallationWarning(title), title: 'Warning', titleColor: 'warning' }, }, { selector: 'dependents', cancelButton: { whileLoading: { }, afterLoading: { text: 'Cancel' } }, nextButton: 'Uninstall', params: { action, verb: 'uninstalling', title, fetchBreakages: () => this.apiService.uninstallApp(id, true).then( ({ breakages }) => breakages ), diff --git a/ui/src/app/components/update-os-banner/update-os-banner.component.html b/ui/src/app/components/update-os-banner/update-os-banner.component.html index e48adb668..f94816b6d 100644 --- a/ui/src/app/components/update-os-banner/update-os-banner.component.html +++ b/ui/src/app/components/update-os-banner/update-os-banner.component.html @@ -1,5 +1,5 @@ - + - New EmbassyOS Version {{version | displayEmver}} Available! + New EmbassyOS Version {{res.versionLatest | displayEmver}} Available! diff --git a/ui/src/app/components/update-os-banner/update-os-banner.component.ts b/ui/src/app/components/update-os-banner/update-os-banner.component.ts index 8671118eb..36ae531aa 100644 --- a/ui/src/app/components/update-os-banner/update-os-banner.component.ts +++ b/ui/src/app/components/update-os-banner/update-os-banner.component.ts @@ -1,9 +1,10 @@ import { Component } from '@angular/core' import { OsUpdateService } from 'src/app/services/os-update.service' import { Observable } from 'rxjs' -import { AlertController } from '@ionic/angular' -import { LoaderService } from 'src/app/services/loader.service' -import { displayEmver } from 'src/app/pipes/emver.pipe' +import { ModalController } from '@ionic/angular' +import { WizardBaker } from '../install-wizard/prebaked-wizards' +import { wizardModal } from '../install-wizard/install-wizard.component' +import { ReqRes } from 'src/app/services/api/api.service' @Component({ selector: 'update-os-banner', @@ -11,38 +12,24 @@ import { displayEmver } from 'src/app/pipes/emver.pipe' styleUrls: ['./update-os-banner.component.scss'], }) export class UpdateOsBannerComponent { - updateAvailable$: Observable + updateAvailable$: Observable constructor ( private readonly osUpdateService: OsUpdateService, - private readonly alertCtrl: AlertController, - private readonly loader: LoaderService, + private readonly modalCtrl: ModalController, + private readonly wizardBaker: WizardBaker, ) { this.updateAvailable$ = this.osUpdateService.watchForUpdateAvailable$() } ngOnInit () { } - async confirmUpdate (versionLatest: string) { - const alert = await this.alertCtrl.create({ - header: `Update EmbassyOS`, - message: `Update EmbassyOS to version ${displayEmver(versionLatest)}?`, - buttons: [ - { - text: 'Cancel', - role: 'cancel', - }, - { - text: 'Update', - handler: () => this.update(versionLatest), - }, - ], - }) - await alert.present() - } - - private async update (versionLatest: string) { - return this.loader.displayDuringP( - this.osUpdateService.updateEmbassyOS(versionLatest), + async confirmUpdate (res: ReqRes.GetVersionLatestRes) { + await wizardModal( + this.modalCtrl, + this.wizardBaker.updateOS({ + version: res.versionLatest, + releaseNotes: res.releaseNotes, + }), ) } } diff --git a/ui/src/app/models/server-model.ts b/ui/src/app/models/server-model.ts index 54b15700a..aeaffccac 100644 --- a/ui/src/app/models/server-model.ts +++ b/ui/src/app/models/server-model.ts @@ -115,7 +115,7 @@ export interface S9Server { name: string origin: string versionInstalled: string - versionLatest: string | undefined + versionLatest: string | undefined // not on the api as of 0.2.8 status: ServerStatus badge: number alternativeRegistryUrl: string | null diff --git a/ui/src/app/pages/apps-routes/app-available-show/app-available-show.page.html b/ui/src/app/pages/apps-routes/app-available-show/app-available-show.page.html index 355f5c828..5614030ae 100644 --- a/ui/src/app/pages/apps-routes/app-available-show/app-available-show.page.html +++ b/ui/src/app/pages/apps-routes/app-available-show/app-available-show.page.html @@ -81,6 +81,13 @@ + Release Notes + + + New in {{ vars.versionViewing | displayEmver }} + + + Description @@ -90,13 +97,6 @@ - Release Notes - - - New in {{ vars.versionViewing | displayEmver }} - - - Service Dependencies diff --git a/ui/src/app/pages/apps-routes/app-available-show/app-available-show.page.ts b/ui/src/app/pages/apps-routes/app-available-show/app-available-show.page.ts index 20d62da5d..74c3ca7f5 100644 --- a/ui/src/app/pages/apps-routes/app-available-show/app-available-show.page.ts +++ b/ui/src/app/pages/apps-routes/app-available-show/app-available-show.page.ts @@ -1,10 +1,10 @@ -import { Component, HostListener, NgZone } from '@angular/core' +import { Component, NgZone } from '@angular/core' import { ActivatedRoute } from '@angular/router' -import { AppAvailableFull, AppAvailableVersionSpecificInfo, AppDependency } from 'src/app/models/app-types' +import { AppAvailableFull, AppAvailableVersionSpecificInfo } from 'src/app/models/app-types' import { ApiService } from 'src/app/services/api/api.service' import { AlertController, ModalController, NavController, PopoverController } from '@ionic/angular' import { markAsLoadingDuring$ } from 'src/app/services/loader.service' -import { BehaviorSubject, from, merge, Observable, of, Subscription } from 'rxjs' +import { BehaviorSubject, from, Observable, of } from 'rxjs' import { catchError, concatMap, filter, switchMap, tap } from 'rxjs/operators' import { Recommendation } from 'src/app/components/recommendation-button/recommendation-button.component' import { wizardModal } from 'src/app/components/install-wizard/install-wizard.component' @@ -13,7 +13,6 @@ import { AppModel } from 'src/app/models/app-model' import { initPropertySubject, peekProperties, PropertySubject } from 'src/app/util/property-subject.util' import { Cleanup } from 'src/app/util/cleanup' import { InformationPopoverComponent } from 'src/app/components/information-popover/information-popover.component' -import { pauseFor } from 'src/app/util/misc.util' import { AppReleaseNotesPage } from 'src/app/modals/app-release-notes/app-release-notes.page' import { Emver } from 'src/app/services/emver.service' import { displayEmver } from 'src/app/pipes/emver.pipe' diff --git a/ui/src/app/pages/server-routes/server-show/server-show.page.ts b/ui/src/app/pages/server-routes/server-show/server-show.page.ts index e9704b070..7b34140f6 100644 --- a/ui/src/app/pages/server-routes/server-show/server-show.page.ts +++ b/ui/src/app/pages/server-routes/server-show/server-show.page.ts @@ -1,7 +1,7 @@ import { Component } from '@angular/core' import { LoadingOptions } from '@ionic/core' import { ServerModel, ServerStatus } from 'src/app/models/server-model' -import { AlertController } from '@ionic/angular' +import { AlertController, ModalController } from '@ionic/angular' import { S9Server } from 'src/app/models/server-model' import { ApiService } from 'src/app/services/api/api.service' import { SyncDaemon } from 'src/app/services/sync.service' @@ -10,6 +10,8 @@ import { PropertySubject, toObservable } from 'src/app/util/property-subject.uti import { doForAtLeast } from 'src/app/util/misc.util' import { LoaderService } from 'src/app/services/loader.service' import { Emver } from 'src/app/services/emver.service' +import { wizardModal } from 'src/app/components/install-wizard/install-wizard.component' +import { WizardBaker } from 'src/app/components/install-wizard/prebaked-wizards' @Component({ selector: 'server-show', @@ -35,6 +37,8 @@ export class ServerShowPage { private readonly apiService: ApiService, private readonly syncDaemon: SyncDaemon, private readonly emver: Emver, + private readonly modalCtrl: ModalController, + private readonly wizardBaker: WizardBaker, ) { } async ngOnInit () { @@ -83,9 +87,9 @@ export class ServerShowPage { await loader.present() try { - const { versionLatest } = await this.apiService.getVersionLatest() + const { versionLatest, releaseNotes } = await this.apiService.getVersionLatest() if (this.emver.compare(this.server.versionInstalled.getValue(), versionLatest) === -1) { - this.presentAlertUpdate(versionLatest) + this.presentAlertUpdate(versionLatest, releaseNotes) } else { this.presentAlertUpToDate() } @@ -106,7 +110,7 @@ export class ServerShowPage { await alert.present() } - async presentAlertUpdate (versionLatest: string) { + async presentAlertUpdate (versionLatest: string, releaseNotes: string) { const alert = await this.alertCtrl.create({ backdropDismiss: false, header: 'Confirm', @@ -119,7 +123,7 @@ export class ServerShowPage { { text: 'Update', handler: () => { - this.updateEmbassyOS(versionLatest) + this.updateEmbassyOS(versionLatest, releaseNotes) }, }, ], @@ -171,17 +175,18 @@ export class ServerShowPage { await alert.present() } - private async updateEmbassyOS (versionLatest: string) { - this.loader - .displayDuringAsync(async () => { - await this.apiService.updateAgent(versionLatest) - this.serverModel.update({ status: ServerStatus.UPDATING }) - // hides the "Update Embassy to..." button for this intance of the component - this.updatingFreeze = true - this.updating = true - setTimeout(() => this.updatingFreeze = false, 8000) - }) - .catch(e => this.setError(e)) + private async updateEmbassyOS (versionLatest: string, releaseNotes: string) { + const { cancelled } = await wizardModal( + this.modalCtrl, + this.wizardBaker.updateOS({ + version: versionLatest, + releaseNotes: releaseNotes, + }), + ) + if (cancelled) return + this.updatingFreeze = true + this.updating = true + setTimeout(() => this.updatingFreeze = false, 8000) } private async restart () { diff --git a/ui/src/app/services/api/api.service.ts b/ui/src/app/services/api/api.service.ts index 18cc0f6bb..f920f5fc4 100644 --- a/ui/src/app/services/api/api.service.ts +++ b/ui/src/app/services/api/api.service.ts @@ -67,7 +67,7 @@ export module ReqRes { export type PostLoginRes = Unit export type GetCheckAuthRes = { } export type GetServerRes = ApiServer - export type GetVersionLatestRes = { versionLatest: string, canUpdate: boolean } // canUpdate not supported at least at 0.2.8 + export type GetVersionLatestRes = { versionLatest: string, releaseNotes: string } export type GetServerMetricsRes = ServerMetrics export type GetAppAvailableRes = ApiAppAvailableFull export type GetAppAvailableVersionInfoRes = AppAvailableVersionSpecificInfo diff --git a/ui/src/app/services/api/mock-api.service.ts b/ui/src/app/services/api/mock-api.service.ts index 3c0129427..525764c79 100644 --- a/ui/src/app/services/api/mock-api.service.ts +++ b/ui/src/app/services/api/mock-api.service.ts @@ -410,7 +410,7 @@ const mockApiServer: () => ReqRes.GetServerRes = () => ({ status: ServerStatus.RUNNING, alternativeRegistryUrl: 'beta-registry.start9labs.com', welcomeAck: true, - autoCheckUpdates: false, + autoCheckUpdates: true, specs: { 'Tor Address': 'nfsnjkcnaskjnlkasnfahj7dh23fdnieqwjdnhjewbfijendiueqwbd.onion', 'CPU': 'Broadcom BCM2711, Quad core Cortex-A72 (ARM v8) 64-bit SoC @ 1.5GHz', @@ -440,7 +440,10 @@ const mockApiServer: () => ReqRes.GetServerRes = () => ({ const mockVersionLatest: ReqRes.GetVersionLatestRes = { versionLatest: '15.2.8.6', - canUpdate: true, + releaseNotes: `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent aliquet, sapien sit amet pretium lacinia, neque tortor consectetur nunc, non volutpat lectus neque in leo. Curabitur a odio eleifend, placerat purus non, aliquet nulla. Aliquam eget lacinia lectus. Aliquam gravida elit eu magna pretium, non interdum tortor vulputate. Ut ac tortor vel tellus blandit malesuada ac ac tortor. Integer tincidunt est quam, non convallis sapien vehicula sed. Donec ullamcorper convallis massa, nec euismod enim tempus vitae. In condimentum semper pulvinar. Sed viverra est id lectus tincidunt, et malesuada eros malesuada. + Curabitur scelerisque eu mauris eget dapibus. In egestas est sit amet nisi cursus iaculis. Mauris consequat pharetra ex, vitae sollicitudin tortor viverra id. Suspendisse lacinia justo id tincidunt feugiat. Nunc risus elit, viverra vel vestibulum ac, varius vel eros. Nam at tellus tempor, semper metus et, tristique elit. Vivamus a dui sit amet orci tincidunt tincidunt. Cras ut velit pretium, euismod dolor non, pulvinar lorem. Praesent dignissim eros quis tortor bibendum, nec convallis libero viverra. Aenean sit amet massa maximus eros congue pellentesque ac nec massa. Nam feugiat felis mi, a aliquet enim porta eget. + Phasellus pellentesque magna vel elit malesuada elementum. Curabitur maximus scelerisque vulputate. Duis facilisis et nisi sed facilisis. Ut consectetur odio tortor, vitae elementum velit scelerisque eget. Maecenas bibendum, massa eu bibendum rhoncus, turpis ex condimentum elit, vel pulvinar ex mi sed urna. Etiam ac erat lectus. Suspendisse dignissim massa tortor. Donec ac dolor in tortor faucibus scelerisque. Nullam et lacus eros. Cras eget sapien nec felis condimentum tincidunt. Praesent ac ante dui. Nam euismod nunc neque, et scelerisque erat efficitur nec. Aenean efficitur tincidunt nulla, ac tempor leo blandit sed. Duis sed tellus quis ante consequat ornare nec vitae eros. Praesent ultrices nunc ut lacus tincidunt finibus. Praesent at eros non est commodo ultricies. + Curabitur eu felis convallis, lobortis nulla laoreet, commodo lacus. Vestibulum at sapien sed metus tincidunt vulputate. Donec cursus augue non sapien imperdiet cursus. Aliquam pellentesque ligula id magna blandit rutrum. Aliquam mattis ipsum leo, nec pharetra lectus tristique eu. Duis egestas mollis aliquam. Duis aliquet dictum risus, quis dictum mauris finibus id.`, } const mockApiServerMetrics: ReqRes.GetServerMetricsRes = { diff --git a/ui/src/app/services/os-update.service.ts b/ui/src/app/services/os-update.service.ts index ca179adc1..5aa27defd 100644 --- a/ui/src/app/services/os-update.service.ts +++ b/ui/src/app/services/os-update.service.ts @@ -8,12 +8,13 @@ import { Emver } from './emver.service' // call checkForUpdates in marketplace pages, can subscribe globally however +type UpdateAvailable = { versionLatest: string, releaseNotes: string} @Injectable({ providedIn: 'root' }) export class OsUpdateService { // holds version latest if update available, undefined if not. - private readonly $updateAvailable$ = new BehaviorSubject(undefined) + private readonly $updateAvailable$ = new BehaviorSubject(undefined) - watchForUpdateAvailable$ (): Observable { + watchForUpdateAvailable$ (): Observable { return this.$updateAvailable$.asObservable().pipe(distinctUntilChanged()) } @@ -25,7 +26,7 @@ export class OsUpdateService { ) { } // emits the latest version or re-checks to see if there's a new latest version - checkWhenNotAvailable$ (): Observable { + checkWhenNotAvailable$ (): Observable { return this.$updateAvailable$.pipe( take(1), concatMap(vl => vl ? of(vl) : this.checkForUpdates$()), @@ -33,12 +34,12 @@ export class OsUpdateService { } // can sub to this imperatively and take the return value as gospel, or watch the $updateAvailable$ subject for the same info. - checkForUpdates$ (): Observable { + checkForUpdates$ (): Observable { return forkJoin([ this.serverModel.watch().versionInstalled.pipe(take(1)), this.apiService.getVersionLatest(), ]).pipe( - map(([vi, vl]) => this.updateIsAvailable(vi, vl.versionLatest) ? vl.versionLatest : undefined), + map(([vi, vl]) => this.updateIsAvailable(vi, vl) ? vl : undefined), catchError(e => { console.error(`OsUpdateService Error: ${e}`) return of(undefined) @@ -48,9 +49,9 @@ export class OsUpdateService { ) } - updateIsAvailable (vi: string, vl: string): boolean { + updateIsAvailable (vi: string, vl: UpdateAvailable): boolean { if (!vi || !vl) return false - if (this.emver.compare(vi, vl) === -1) { + if (this.emver.compare(vi, vl.versionLatest) === -1) { this.$updateAvailable$.next(vl) return true } else { diff --git a/ui/src/app/services/startup-alerts.notifier.ts b/ui/src/app/services/startup-alerts.notifier.ts index 6d2f2cc8c..7bd6c7a9f 100644 --- a/ui/src/app/services/startup-alerts.notifier.ts +++ b/ui/src/app/services/startup-alerts.notifier.ts @@ -1,12 +1,13 @@ import { Injectable } from '@angular/core' import { AlertController, IonicSafeString, ModalController, NavController } from '@ionic/angular' +import { wizardModal } from '../components/install-wizard/install-wizard.component' +import { WizardBaker } from '../components/install-wizard/prebaked-wizards' import { OSWelcomePage } from '../modals/os-welcome/os-welcome.page' import { S9Server } from '../models/server-model' import { displayEmver } from '../pipes/emver.pipe' -import { ApiService } from './api/api.service' +import { ApiService, ReqRes } from './api/api.service' import { ConfigService } from './config.service' import { Emver } from './emver.service' -import { LoaderService } from './loader.service' import { OsUpdateService } from './os-update.service' @Injectable({ providedIn: 'root' }) @@ -14,12 +15,12 @@ export class StartupAlertsNotifier { constructor ( private readonly alertCtrl: AlertController, private readonly navCtrl: NavController, - private readonly loader: LoaderService, private readonly config: ConfigService, private readonly modalCtrl: ModalController, private readonly apiService: ApiService, private readonly emver: Emver, private readonly osUpdateService: OsUpdateService, + private readonly wizardBaker: WizardBaker, ) { } // This takes our three checks and filters down to those that should run. @@ -54,7 +55,7 @@ export class StartupAlertsNotifier { display: s => this.displayOsWelcome(s), hasRun: false, } - osUpdate: Check = { + osUpdate: Check = { name: 'osUpdate', shouldRun: s => this.shouldRunOsUpdateCheck(s), check: s => this.osUpdateCheck(s), @@ -83,9 +84,9 @@ export class StartupAlertsNotifier { return server.autoCheckUpdates } - private async osUpdateCheck (s: Readonly): Promise { - const { versionLatest } = await this.apiService.getVersionLatest() - return this.osUpdateService.updateIsAvailable(s.versionInstalled, versionLatest) ? versionLatest : undefined + private async osUpdateCheck (s: Readonly): Promise { + const res = await this.apiService.getVersionLatest() + return this.osUpdateService.updateIsAvailable(s.versionInstalled, res) ? res : undefined } private async appsCheck (): Promise { @@ -113,12 +114,17 @@ export class StartupAlertsNotifier { }) } - private async displayOsUpdateCheck (versionLatest: string | undefined): Promise { - const { update } = await this.presentAlertNewOS(versionLatest) + private async displayOsUpdateCheck (res: ReqRes.GetVersionLatestRes): Promise { + const { update } = await this.presentAlertNewOS(res.versionLatest) if (update) { - await this.loader.displayDuringP( - this.osUpdateService.updateEmbassyOS(versionLatest), - ).catch(e => alert(e)) + const { cancelled } = await wizardModal( + this.modalCtrl, + this.wizardBaker.updateOS({ + version: res.versionLatest, + releaseNotes: res.releaseNotes, + }), + ) + if (cancelled) return true return false } return true @@ -134,7 +140,7 @@ export class StartupAlertsNotifier {
New service updates are available in the Marketplace.
You can disable these checks in your Embassy Config
- ` + `, ), buttons: [ { @@ -165,7 +171,7 @@ export class StartupAlertsNotifier {
Update EmbassyOS to version ${displayEmver(versionLatest)}?
You can disable these checks in your Embassy Config
- ` + `, ), buttons: [ {