mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
ui: add embassy os release notes
This commit is contained in:
committed by
Aiden McClelland
parent
68faa17ab6
commit
79604182c8
@@ -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>
|
||||
|
||||
@@ -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: [
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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']
|
||||
}
|
||||
)
|
||||
|
||||
@@ -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>
|
||||
@@ -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'
|
||||
@@ -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) }
|
||||
}
|
||||
@@ -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 ),
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)">
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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: [
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user