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
new file mode 100644
index 000000000..0f8cbc42f
--- /dev/null
+++ b/ui/src/app/components/update-os-banner/update-os-banner.component.html
@@ -0,0 +1,5 @@
+
+
+ New Embassy OS Version {{version}} Available!
+
+
\ No newline at end of file
diff --git a/ui/src/app/components/update-os-banner/update-os-banner.component.module.ts b/ui/src/app/components/update-os-banner/update-os-banner.component.module.ts
new file mode 100644
index 000000000..e06ba5fa8
--- /dev/null
+++ b/ui/src/app/components/update-os-banner/update-os-banner.component.module.ts
@@ -0,0 +1,16 @@
+import { NgModule } from '@angular/core'
+import { CommonModule } from '@angular/common'
+import { UpdateOsBannerComponent } from './update-os-banner.component'
+import { IonicModule } from '@ionic/angular'
+
+@NgModule({
+ declarations: [
+ UpdateOsBannerComponent,
+ ],
+ imports: [
+ CommonModule,
+ IonicModule,
+ ],
+ exports: [UpdateOsBannerComponent],
+})
+export class UpdateOsBannerComponentModule { }
diff --git a/ui/src/app/components/update-os-banner/update-os-banner.component.scss b/ui/src/app/components/update-os-banner/update-os-banner.component.scss
new file mode 100644
index 000000000..43db077a7
--- /dev/null
+++ b/ui/src/app/components/update-os-banner/update-os-banner.component.scss
@@ -0,0 +1,11 @@
+ion-item {
+
+ --background: linear-gradient(90deg, var(--ion-color-light), var(--ion-color-primary));
+ --min-height: 0px;
+ ion-label {
+ font-family: 'Open Sans';
+ font-size: small;
+ text-align: center;
+ font-weight: bold;
+ }
+}
\ No newline at end of file
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
new file mode 100644
index 000000000..4b407d951
--- /dev/null
+++ b/ui/src/app/components/update-os-banner/update-os-banner.component.ts
@@ -0,0 +1,47 @@
+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'
+
+@Component({
+ selector: 'update-os-banner',
+ templateUrl: './update-os-banner.component.html',
+ styleUrls: ['./update-os-banner.component.scss'],
+})
+export class UpdateOsBannerComponent {
+ updateAvailable$: Observable
+ constructor (
+ private readonly osUpdateService: OsUpdateService,
+ private readonly alertCtrl: AlertController,
+ private readonly loader: LoaderService,
+ ) {
+ this.updateAvailable$ = this.osUpdateService.watchForUpdateAvailable()
+ }
+
+ ngOnInit () { }
+
+ async confirmUpdate (versionLatest: string) {
+ const alert = await this.alertCtrl.create({
+ header: `Update Embassy OS`,
+ message: `Are you sure you want to update your Embassy OS to version ${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),
+ )
+ }
+}
diff --git a/ui/src/app/pages/apps-routes/app-available-list/app-available-list.module.ts b/ui/src/app/pages/apps-routes/app-available-list/app-available-list.module.ts
index abe1fa401..434130c4c 100644
--- a/ui/src/app/pages/apps-routes/app-available-list/app-available-list.module.ts
+++ b/ui/src/app/pages/apps-routes/app-available-list/app-available-list.module.ts
@@ -7,6 +7,7 @@ import { SharingModule } from '../../../modules/sharing.module'
import { PwaBackComponentModule } from 'src/app/components/pwa-back-button/pwa-back.component.module'
import { BadgeMenuComponentModule } from 'src/app/components/badge-menu-button/badge-menu.component.module'
import { StatusComponentModule } from 'src/app/components/status/status.component.module'
+import { UpdateOsBannerComponentModule } from 'src/app/components/update-os-banner/update-os-banner.component.module'
const routes: Routes = [
@@ -25,6 +26,7 @@ const routes: Routes = [
SharingModule,
PwaBackComponentModule,
BadgeMenuComponentModule,
+ UpdateOsBannerComponentModule,
],
declarations: [AppAvailableListPage],
})
diff --git a/ui/src/app/pages/apps-routes/app-available-list/app-available-list.page.html b/ui/src/app/pages/apps-routes/app-available-list/app-available-list.page.html
index ddc829024..7765a6793 100644
--- a/ui/src/app/pages/apps-routes/app-available-list/app-available-list.page.html
+++ b/ui/src/app/pages/apps-routes/app-available-list/app-available-list.page.html
@@ -5,10 +5,10 @@
+
-
diff --git a/ui/src/app/pages/apps-routes/app-available-list/app-available-list.page.ts b/ui/src/app/pages/apps-routes/app-available-list/app-available-list.page.ts
index 03f74a3ce..912931566 100644
--- a/ui/src/app/pages/apps-routes/app-available-list/app-available-list.page.ts
+++ b/ui/src/app/pages/apps-routes/app-available-list/app-available-list.page.ts
@@ -7,6 +7,7 @@ import { PropertySubjectId, initPropertySubject } from 'src/app/util/property-su
import { Subscription, BehaviorSubject, combineLatest } from 'rxjs'
import { take } from 'rxjs/operators'
import { markAsLoadingDuringP } from 'src/app/services/loader.service'
+import { OsUpdateService } from 'src/app/services/os-update.service'
@Component({
selector: 'app-available-list',
@@ -24,6 +25,7 @@ export class AppAvailableListPage {
private readonly apiService: ApiService,
private readonly appModel: AppModel,
private readonly zone: NgZone,
+ private readonly osUpdateService: OsUpdateService,
) { }
async ngOnInit () {
@@ -33,6 +35,7 @@ export class AppAvailableListPage {
markAsLoadingDuringP(this.$loading$, Promise.all([
this.getApps(),
+ this.osUpdateService.checkForUpdates(), // checks for an os update, banner component renders conditionally
pauseFor(600),
]))
}
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 978f6002f..1ec311e8b 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
@@ -153,7 +153,7 @@ export class ServerShowPage {
const alert = await this.alertCtrl.create({
backdropDismiss: false,
header: 'Confirm',
- message: `Are you sure you shut down your Embassy? To turn it back on, you will need to physically unplug the device and plug it back in.`,
+ message: `Are you sure you want to shut down your Embassy? To turn it back on, you will need to physically unplug the device and plug it back in.`,
buttons: [
{
text: 'Cancel',
diff --git a/ui/src/app/services/api/mock-api.service.ts b/ui/src/app/services/api/mock-api.service.ts
index acadf6e1a..ff109010c 100644
--- a/ui/src/app/services/api/mock-api.service.ts
+++ b/ui/src/app/services/api/mock-api.service.ts
@@ -420,7 +420,7 @@ const mockApiServer: () => ReqRes.GetServerRes = () => ({
})
const mockVersionLatest: ReqRes.GetVersionLatestRes = {
- versionLatest: '0.2.8',
+ versionLatest: '15.2.8',
canUpdate: true,
}
diff --git a/ui/src/app/services/os-update.service.ts b/ui/src/app/services/os-update.service.ts
new file mode 100644
index 000000000..1dbe3d26b
--- /dev/null
+++ b/ui/src/app/services/os-update.service.ts
@@ -0,0 +1,48 @@
+import { Injectable } from '@angular/core'
+import { NavController } from '@ionic/angular'
+import { BehaviorSubject, forkJoin, Observable, of } from 'rxjs'
+import { catchError, map, take, tap } from 'rxjs/operators'
+import { ServerModel, ServerStatus } from '../models/server-model'
+import { ApiService } from './api/api.service'
+import { Emver } from './emver.service'
+
+
+// call checkForUpdates in marketplace pages, can subscribe globally however
+@Injectable({ providedIn: 'root' })
+export class OsUpdateService {
+ private readonly $updateAvailable$ = new BehaviorSubject(undefined)
+
+ constructor (
+ private readonly emver: Emver,
+ private readonly serverModel: ServerModel,
+ private readonly apiService: ApiService,
+ private readonly navCtrl: NavController,
+ ) { }
+
+ watchForUpdateAvailable (): Observable {
+ return this.$updateAvailable$.asObservable()
+ }
+
+ // undefined when no update available, string for the versionLatest if there is
+ checkForUpdates (): Promise {
+ return forkJoin([
+ this.apiService.getVersionLatest(),
+ this.serverModel.watch().versionInstalled.pipe(take(1)),
+ ]).pipe(
+ map(([vl, vi]) => this.emver.compare(vi, vl.versionLatest) === -1 ? vl.versionLatest : undefined),
+ catchError(e => {
+ console.error(`OsUpdateService Error: ${e}`)
+ return of(undefined)
+ }),
+ // cache the result for components to learn update available without having to have called this method
+ tap(updateAvailable => this.$updateAvailable$.next(updateAvailable)),
+ ).toPromise()
+ }
+
+ async updateEmbassyOS (versionLatest: string): Promise {
+ await this.apiService.updateAgent(versionLatest)
+ this.serverModel.update({ status: ServerStatus.UPDATING })
+ this.$updateAvailable$.next(undefined)
+ await this.navCtrl.navigateRoot('/embassy')
+ }
+}
\ No newline at end of file
diff --git a/ui/use-mocks.json b/ui/use-mocks.json
index 2caee7215..3425efc8a 100644
--- a/ui/use-mocks.json
+++ b/ui/use-mocks.json
@@ -1,3 +1,3 @@
{
- "useMocks": true
+ "useMocks": false
}