ui: add embassy os release notes

This commit is contained in:
Aaron Greenspan
2021-01-28 17:22:05 -07:00
committed by Aiden McClelland
parent 68faa17ab6
commit 79604182c8
18 changed files with 148 additions and 115 deletions

View File

@@ -11,7 +11,7 @@
<ion-slides *ngIf="!($error$ | async)" id="slide-show" style="--bullet-background: white" pager="false">
<ion-slide *ngFor="let slide of params.slideDefinitions">
<dependencies #components *ngIf="slide.selector === 'dependencies'" [params]="slide.params"></dependencies>
<developer-notes #components *ngIf="slide.selector === 'developer-notes'" [params]="slide.params"></developer-notes>
<notes #components *ngIf="slide.selector === 'notes'" [params]="slide.params"></notes>
<dependents #components *ngIf="slide.selector === 'dependents'" [params]="slide.params" [finished]="finished"></dependents>
<complete #components *ngIf="slide.selector === 'complete'" [params]="slide.params" [finished]="finished"></complete>
</ion-slide>

View File

@@ -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: [

View File

@@ -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;
}
}

View File

@@ -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<string> = 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']
}
)

View File

@@ -2,11 +2,11 @@
<div style="margin-top: 25px;">
<div style="margin: 15px; display: flex; justify-content: center; align-items: center;">
<ion-label [color]="$color$ | async" style="font-size: xx-large; font-weight: bold;">
Warning
{{params.title}}
</ion-label>
</div>
<div class="long-message">
{{params.developerNotes}}
{{params.notes}}
</div>
</div>
</div>

View File

@@ -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'

View File

@@ -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<void>()
load () { }
constructor () { }
ngOnInit () {
console.log('Developer Notes', this.params)
}
ngOnInit () { this.$color$.next(this.params.titleColor) }
}

View File

@@ -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 ),

View File

@@ -1,5 +1,5 @@
<ion-item button lines="none" *ngIf="updateAvailable$ | async as version" (click)="confirmUpdate(version)">
<ion-item button lines="none" *ngIf="updateAvailable$ | async as res" (click)="confirmUpdate(res)">
<ion-label>
New EmbassyOS Version {{version | displayEmver}} Available!
New EmbassyOS Version {{res.versionLatest | displayEmver}} Available!
</ion-label>
</ion-item>

View File

@@ -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<undefined | string>
updateAvailable$: Observable<undefined | ReqRes.GetVersionLatestRes>
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,
}),
)
}
}

View File

@@ -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

View File

@@ -81,6 +81,13 @@
</ion-item>
</ng-container>
<ion-item-divider style="color: var(--ion-color-primary); font-weight: bold;">Release Notes</ion-item-divider>
<ion-item lines="none" button details="true" [disabled]="" (click)="presentModalReleaseNotes()" [disabled]="$newVersionLoading$ | async">
<ion-icon slot="start" name="newspaper-outline" color="medium"></ion-icon>
<ion-label *ngIf="!($newVersionLoading$ | async)"><ion-text style="font-weight: bold;" color="medium">New in {{ vars.versionViewing | displayEmver }}</ion-text></ion-label>
<ion-spinner style="display: block; margin: auto;" name="lines" color="dark" *ngIf="$newVersionLoading$ | async"></ion-spinner>
</ion-item>
<ion-item-divider class="divider">Description</ion-item-divider>
<ion-item lines="none">
<ion-label class="ion-text-wrap">
@@ -90,13 +97,6 @@
</ion-label>
</ion-item>
<ion-item-divider>Release Notes</ion-item-divider>
<ion-item lines="none" button details="true" [disabled]="" (click)="presentModalReleaseNotes()" [disabled]="$newVersionLoading$ | async">
<ion-icon slot="start" name="newspaper-outline" color="medium"></ion-icon>
<ion-label *ngIf="!($newVersionLoading$ | async)"><ion-text color="medium">New in {{ vars.versionViewing | displayEmver }}</ion-text></ion-label>
<ion-spinner style="display: block; margin: auto;" name="lines" color="dark" *ngIf="$newVersionLoading$ | async"></ion-spinner>
</ion-item>
<ng-container *ngIf="(vars.serviceRequirements)?.length">
<ion-item-divider class="divider">Service Dependencies
<ion-button style="position: relative; right: 10px;" size="small" fill="clear" color="medium" (click)="presentPopover(serviceDependencyDefintion, $event)">

View File

@@ -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'

View File

@@ -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 () {

View File

@@ -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

View File

@@ -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 = {

View File

@@ -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<string>(undefined)
private readonly $updateAvailable$ = new BehaviorSubject<UpdateAvailable>(undefined)
watchForUpdateAvailable$ (): Observable<undefined | string> {
watchForUpdateAvailable$ (): Observable<undefined | UpdateAvailable> {
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<undefined | string> {
checkWhenNotAvailable$ (): Observable<undefined | UpdateAvailable> {
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<undefined | string> {
checkForUpdates$ (): Observable<undefined | UpdateAvailable> {
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 {

View File

@@ -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<string | undefined> = {
osUpdate: Check<ReqRes.GetVersionLatestRes | undefined> = {
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<S9Server>): Promise<string | undefined> {
const { versionLatest } = await this.apiService.getVersionLatest()
return this.osUpdateService.updateIsAvailable(s.versionInstalled, versionLatest) ? versionLatest : undefined
private async osUpdateCheck (s: Readonly<S9Server>): Promise<ReqRes.GetVersionLatestRes | undefined> {
const res = await this.apiService.getVersionLatest()
return this.osUpdateService.updateIsAvailable(s.versionInstalled, res) ? res : undefined
}
private async appsCheck (): Promise<boolean> {
@@ -113,12 +114,17 @@ export class StartupAlertsNotifier {
})
}
private async displayOsUpdateCheck (versionLatest: string | undefined): Promise<boolean> {
const { update } = await this.presentAlertNewOS(versionLatest)
private async displayOsUpdateCheck (res: ReqRes.GetVersionLatestRes): Promise<boolean> {
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 {
<div>New service updates are available in the Marketplace.</div>
<div style="font-size:x-small">You can disable these checks in your Embassy Config</div>
</div>
`
`,
),
buttons: [
{
@@ -165,7 +171,7 @@ export class StartupAlertsNotifier {
<div>Update EmbassyOS to version ${displayEmver(versionLatest)}?</div>
<div style="font-size:x-small">You can disable these checks in your Embassy Config</div>
</div>
`
`,
),
buttons: [
{