diff --git a/ui/src/app/app.component.html b/ui/src/app/app.component.html index f646bae10..f40fa527b 100644 --- a/ui/src/app/app.component.html +++ b/ui/src/app/app.component.html @@ -1,9 +1,9 @@ - + - {{ name }} + {{ serverName }} @@ -24,7 +24,7 @@ > {{ page.title }} - {{ badge }} + {{ unreadCount }} diff --git a/ui/src/app/app.component.ts b/ui/src/app/app.component.ts index 06b49bb50..482107643 100644 --- a/ui/src/app/app.component.ts +++ b/ui/src/app/app.component.ts @@ -13,6 +13,7 @@ import { PatchDbModel } from './models/patch-db/patch-db-model' import { HttpService } from './services/http.service' import { ServerStatus } from './models/patch-db/data-model' import { ConnectionFailure, ConnectionService } from './services/connection.service' +import { combineLatest, merge } from 'rxjs' @Component({ selector: 'app-root', @@ -23,8 +24,9 @@ export class AppComponent { ServerStatus = ServerStatus showMenu = false selectedIndex = 0 - untilLoaded = true offlineToast: HTMLIonToastElement + serverName: string + unreadCount: number appPages = [ { title: 'Services', @@ -59,8 +61,8 @@ export class AppComponent { private readonly emver: Emver, private readonly connectionService: ConnectionService, private readonly toastCtrl: ToastController, + private readonly patch: PatchDbModel, readonly splitPane: SplitPaneTracker, - readonly patch: PatchDbModel, ) { // set dark theme document.body.classList.toggle('dark', true) @@ -83,8 +85,10 @@ export class AppComponent { this.http.authReqEnabled = true this.showMenu = true this.patch.start() + // watch patch DB to display name and unread count + this.watchPatch() this.connectionService.start() - // watch network + // watch connection to display connectivity issues this.watchConnection(auth) // watch router to highlight selected menu item this.watchRouter(auth) @@ -108,6 +112,17 @@ export class AppComponent { }) } + watchPatch (): void { + combineLatest([ + this.patch.watch$('ui', 'server-name'), + this.patch.watch$('server-info', 'unread-notification-count'), + ]) + .subscribe(([name, unread]) => { + this.serverName = name + this.unreadCount = unread + }) + } + private watchConnection (auth: AuthState): void { this.connectionService.watch$() .pipe( @@ -265,7 +280,7 @@ export class AppComponent { } splitPaneVisible (e: any) { - this.splitPane.menuFixedOpenOnLeft$.next(e.detail.visible) + this.splitPane.sidebarOpen$.next(e.detail.visible) } } diff --git a/ui/src/app/components/badge-menu-button/badge-menu.component.html b/ui/src/app/components/badge-menu-button/badge-menu.component.html index d054d9d99..a07dec233 100644 --- a/ui/src/app/components/badge-menu-button/badge-menu.component.html +++ b/ui/src/app/components/badge-menu-button/badge-menu.component.html @@ -1,4 +1,4 @@
- {{ badge$ | ngrxPush }} + {{ unreadCount }}
diff --git a/ui/src/app/components/badge-menu-button/badge-menu.component.ts b/ui/src/app/components/badge-menu-button/badge-menu.component.ts index 156e00dad..89c57a9b7 100644 --- a/ui/src/app/components/badge-menu-button/badge-menu.component.ts +++ b/ui/src/app/components/badge-menu-button/badge-menu.component.ts @@ -1,7 +1,7 @@ import { Component } from '@angular/core' -import { Observable } from 'rxjs' import { SplitPaneTracker } from 'src/app/services/split-pane.service' import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model' +import { combineLatest, Subscription } from 'rxjs' @Component({ selector: 'badge-menu-button', @@ -10,14 +10,30 @@ import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model' }) export class BadgeMenuComponent { - badge$: Observable - menuFixedOpen$: Observable + unreadCount: number + sidebarOpen: boolean + + subs: Subscription[] = [] constructor ( private readonly splitPane: SplitPaneTracker, private readonly patch: PatchDbModel, - ) { - this.menuFixedOpen$ = this.splitPane.menuFixedOpenOnLeft$.asObservable() - this.badge$ = this.patch.watch$('server-info', 'unread-notification-count') + ) { } + + ngOnInit () { + this.subs = [ + combineLatest([ + this.patch.watch$('server-info', 'unread-notification-count'), + this.splitPane.sidebarOpen$, + ]) + .subscribe(([unread, menu]) => { + this.unreadCount = unread + this.sidebarOpen = menu + }), + ] + } + + ngOnDestroy () { + this.subs.forEach(sub => sub.unsubscribe()) } } 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 8ba597a4b..5065efa5a 100644 --- a/ui/src/app/components/install-wizard/install-wizard.component.html +++ b/ui/src/app/components/install-wizard/install-wizard.component.html @@ -8,7 +8,7 @@
- + @@ -19,7 +19,7 @@ -
+
@@ -35,7 +35,7 @@ - + @@ -58,7 +58,7 @@ - + Dismiss 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 08dba2c15..92a052e06 100644 --- a/ui/src/app/components/install-wizard/install-wizard.component.ts +++ b/ui/src/app/components/install-wizard/install-wizard.component.ts @@ -1,6 +1,5 @@ import { Component, Input, NgZone, QueryList, ViewChild, ViewChildren } from '@angular/core' import { IonContent, IonSlides, ModalController } from '@ionic/angular' -import { BehaviorSubject } from 'rxjs' import { capitalizeFirstLetter, pauseFor } from 'src/app/util/misc.util' import { CompleteComponent } from './complete/complete.component' import { DependentsComponent } from './dependents/dependents.component' @@ -39,8 +38,8 @@ export class InstallWizardComponent { return this.params.slideDefinitions[this.slideIndex].bottomBar } - initializing$ = new BehaviorSubject(true) - error$ = new BehaviorSubject(undefined) + initializing = true + error = '' constructor ( private readonly modalController: ModalController, @@ -54,7 +53,7 @@ export class InstallWizardComponent { } ionViewDidEnter () { - this.initializing$.next(false) + this.initializing = false } // process bottom bar buttons @@ -62,7 +61,7 @@ export class InstallWizardComponent { const i = info as { next?: any, error?: Error, cancelled?: true, final?: true } if (i.cancelled) this.currentSlide.cancel$.next() if (i.final || i.cancelled) return this.modalController.dismiss(i) - if (i.error) return this.error$.next(capitalizeFirstLetter(i.error.message)) + if (i.error) return this.error = capitalizeFirstLetter(i.error.message) this.moveToNextSlide(i.next) } diff --git a/ui/src/app/models/app-model.ts b/ui/src/app/models/app-model.ts deleted file mode 100644 index 85df9ccb2..000000000 --- a/ui/src/app/models/app-model.ts +++ /dev/null @@ -1,153 +0,0 @@ -// import { MapSubject, Delta, Update } from '../util/map-subject.util' -// import { diff, partitionArray } from '../util/misc.util' -// import { Injectable } from '@angular/core' -// import { merge, Observable, of } from 'rxjs' -// import { filter, throttleTime, delay, pairwise, mapTo, take } from 'rxjs/operators' -// import { Storage } from '@ionic/storage' -// import { StorageKeys } from './storage-keys' -// import { AppInstalledFull, AppInstalledPreview } from './app-types' - -// @Injectable({ -// providedIn: 'root', -// }) -// export class AppModel extends MapSubject { -// // hasLoaded tells us if we've successfully queried apps from api or storage, even if there are none. -// hasLoaded = false -// lastUpdatedAt: { [id: string]: Date } = { } -// constructor (private readonly storage: Storage) { -// super() -// // 500ms after first delta, will save to db. Subsequent deltas are ignored for those 500ms. -// // Process continues as long as deltas fire. -// this.watchDelta().pipe(throttleTime(200), delay(200)).subscribe(() => { -// this.commitCache() -// }) -// } - -// update (newValues: Update, timestamp: Date = new Date()): void { -// this.lastUpdatedAt[newValues.id] = this.lastUpdatedAt[newValues.id] || timestamp -// if (this.lastUpdatedAt[newValues.id] > timestamp) { -// return -// } else { -// super.update(newValues) -// this.lastUpdatedAt[newValues.id] = timestamp -// } -// } - -// // client fxns -// watchDelta (filterFor?: Delta['action']): Observable> { -// return filterFor -// ? this.$delta$.pipe(filter(d => d.action === filterFor)) -// : this.$delta$.asObservable() -// } - -// watch (appId: string) : PropertySubject { -// const toReturn = super.watch(appId) -// if (!toReturn) throw new Error(`Expected Service ${appId} but not found.`) -// return toReturn -// } - -// // when an app is installing -// watchForInstallation (appId: string): Observable { -// const toWatch = super.watch(appId) -// if (!toWatch) return of(undefined) - -// return toWatch.status.pipe( -// filter(s => s !== AppStatus.UNREACHABLE && s !== AppStatus.UNKNOWN), -// pairwise(), -// filter( ([old, _]) => old === AppStatus.INSTALLING ), -// take(1), -// mapTo(appId), -// ) -// } - -// // TODO: EJECT-DISKS: we can use this to watch for an app completing its backup process. -// watchForBackup (appId: string): Observable { -// const toWatch = super.watch(appId) -// if (!toWatch) return of(undefined) - -// return toWatch.status.pipe( -// filter(s => s !== AppStatus.UNREACHABLE && s !== AppStatus.UNKNOWN), -// pairwise(), -// filter( ([old, _]) => old === AppStatus.CREATING_BACKUP), -// take(1), -// mapTo(appId), -// ) -// } - -// watchForInstallations (appIds: { id: string }[]): Observable { -// return merge(...appIds.map(({ id }) => this.watchForInstallation(id))).pipe( -// filter(t => !!t), -// ) -// } - -// // cache mgmt -// clear (): void { -// this.ids.forEach(id => { -// complete(this.contents[id] || { } as PropertySubject) -// delete this.contents[id] -// }) -// this.hasLoaded = false -// this.contents = { } -// this.lastUpdatedAt = { } -// } - -// private commitCache (): Promise { -// return this.storage.set(StorageKeys.APPS_CACHE_KEY, this.all || []) -// } - -// async restoreCache (): Promise { -// const stored = await this.storage.get(StorageKeys.APPS_CACHE_KEY) -// console.log(`restored app cache`, stored) -// if (stored) this.hasLoaded = true -// return (stored || []).map(c => this.add({ ...emptyAppInstalledFull(), ...c, status: AppStatus.UNKNOWN })) -// } - -// upsertAppFull (app: AppInstalledFull): void { -// this.update(app) -// } - -// // synchronizers -// upsertApps (apps: AppInstalledPreview[], timestamp: Date): void { -// const [updates, creates] = partitionArray(apps, a => !!this.contents[a.id]) -// updates.map(u => this.update(u, timestamp)) -// creates.map(c => this.add({ ...emptyAppInstalledFull(), ...c })) -// } - -// syncCache (upToDateApps : AppInstalledPreview[], timestamp: Date) { -// this.hasLoaded = true -// this.deleteNonexistentApps(upToDateApps) -// this.upsertApps(upToDateApps, timestamp) -// } - -// private deleteNonexistentApps (apps: AppInstalledPreview[]): void { -// const currentAppIds = apps.map(a => a.id) -// const previousAppIds = Object.keys(this.contents) -// const appsToDelete = diff(previousAppIds, currentAppIds) -// appsToDelete.map(appId => this.delete(appId)) -// } - -// // server state change -// markAppsUnreachable (): void { -// this.updateAllApps({ status: AppStatus.UNREACHABLE }) -// } - -// markAppsUnknown (): void { -// this.updateAllApps({ status: AppStatus.UNKNOWN }) -// } - -// private updateAllApps (uniformUpdate: Partial) { -// this.ids.map(id => { -// this.update(Object.assign(uniformUpdate, { id })) -// }) -// } -// } - -// function emptyAppInstalledFull (): Omit { -// return { -// instructions: null, -// lastBackup: null, -// configuredRequirements: null, -// hasFetchedFull: false, -// actions: [], -// } -// } diff --git a/ui/src/app/models/patch-db/data-model.ts b/ui/src/app/models/patch-db/data-model.ts index b3c814776..0defaddef 100644 --- a/ui/src/app/models/patch-db/data-model.ts +++ b/ui/src/app/models/patch-db/data-model.ts @@ -3,11 +3,7 @@ import { ConfigSpec } from 'src/app/pkg-config/config-types' export interface DataModel { 'server-info': ServerInfo 'package-data': { [id: string]: PackageDataEntry } - ui: { - 'server-name': string - 'welcome-ack': string - 'auto-check-updates': boolean - } + ui: UIData } export interface ServerInfo { @@ -381,3 +377,9 @@ export interface InterfaceInfo { } export type URL = string + +export interface UIData { + 'server-name': string + 'welcome-ack': string + 'auto-check-updates': boolean +} diff --git a/ui/src/app/modules/sharing.module.ts b/ui/src/app/modules/sharing.module.ts index 92097dc4b..a6ac1cf87 100644 --- a/ui/src/app/modules/sharing.module.ts +++ b/ui/src/app/modules/sharing.module.ts @@ -3,7 +3,6 @@ import { EmverComparesPipe, EmverSatisfiesPipe, EmverDisplayPipe } from '../pipe import { IncludesPipe } from '../pipes/includes.pipe' import { TypeofPipe } from '../pipes/typeof.pipe' import { MarkdownPipe } from '../pipes/markdown.pipe' -// import { InstalledLatestComparisonPipe, InstalledViewingComparisonPipe } from '../pipes/installed-latest-comparison.pipe' import { AnnotationStatusPipe } from '../pipes/annotation-status.pipe' import { TruncateCenterPipe, TruncateEndPipe } from '../pipes/truncate.pipe' import { MaskPipe } from '../pipes/mask.pipe' @@ -21,8 +20,6 @@ import { ReactiveComponentModule } from '@ngrx/component' TypeofPipe, IncludesPipe, MarkdownPipe, - // InstalledLatestComparisonPipe, - // InstalledViewingComparisonPipe, AnnotationStatusPipe, TruncateCenterPipe, TruncateEndPipe, @@ -45,8 +42,6 @@ import { ReactiveComponentModule } from '@ngrx/component' TypeofPipe, IncludesPipe, MarkdownPipe, - // InstalledLatestComparisonPipe, - // InstalledViewingComparisonPipe, AnnotationStatusPipe, TruncateEndPipe, TruncateCenterPipe, diff --git a/ui/src/app/pages/apps-routes/app-actions/app-actions.page.html b/ui/src/app/pages/apps-routes/app-actions/app-actions.page.html index f39ebb761..5904df987 100644 --- a/ui/src/app/pages/apps-routes/app-actions/app-actions.page.html +++ b/ui/src/app/pages/apps-routes/app-actions/app-actions.page.html @@ -8,7 +8,7 @@ - + diff --git a/ui/src/app/pages/apps-routes/app-actions/app-actions.page.ts b/ui/src/app/pages/apps-routes/app-actions/app-actions.page.ts index d7b8945e5..e1268b8f9 100644 --- a/ui/src/app/pages/apps-routes/app-actions/app-actions.page.ts +++ b/ui/src/app/pages/apps-routes/app-actions/app-actions.page.ts @@ -1,13 +1,14 @@ -import { Component } from '@angular/core' +import { Component, ViewChild } from '@angular/core' import { ActivatedRoute } from '@angular/router' import { ApiService } from 'src/app/services/api/api.service' -import { AlertController, ModalController, NavController } from '@ionic/angular' +import { AlertController, IonContent, ModalController, NavController } from '@ionic/angular' import { LoaderService } from 'src/app/services/loader.service' import { HttpErrorResponse } from '@angular/common/http' import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model' import { Action, InstalledPackageDataEntry, Manifest, PackageMainStatus } from 'src/app/models/patch-db/data-model' import { wizardModal } from 'src/app/components/install-wizard/install-wizard.component' import { WizardBaker } from 'src/app/components/install-wizard/prebaked-wizards' +import { Subscription } from 'rxjs' @Component({ selector: 'app-actions', @@ -15,7 +16,10 @@ import { WizardBaker } from 'src/app/components/install-wizard/prebaked-wizards' styleUrls: ['./app-actions.page.scss'], }) export class AppActionsPage { - pkgId: string + installed: InstalledPackageDataEntry + + subs: Subscription[] = [] + @ViewChild(IonContent) content: IonContent constructor ( private readonly route: ActivatedRoute, @@ -25,11 +29,26 @@ export class AppActionsPage { private readonly loaderService: LoaderService, private readonly wizardBaker: WizardBaker, private readonly navCtrl: NavController, - public readonly patch: PatchDbModel, + private readonly patch: PatchDbModel, ) { } ngOnInit () { - this.pkgId = this.route.snapshot.paramMap.get('pkgId') + const pkgId = this.route.snapshot.paramMap.get('pkgId') + + this.subs = [ + this.patch.watch$('package-data', pkgId, 'installed') + .subscribe(installed => { + this.installed = installed + }), + ] + } + + ngAfterViewInit () { + this.content.scrollToPoint(undefined, 1) + } + + ngOnDestroy () { + this.subs.forEach(sub => sub.unsubscribe()) } async handleAction (pkg: InstalledPackageDataEntry, action: { key: string, value: Action }) { diff --git a/ui/src/app/pages/apps-routes/app-config/app-config.page.html b/ui/src/app/pages/apps-routes/app-config/app-config.page.html index cef07e8c4..e0635e24a 100644 --- a/ui/src/app/pages/apps-routes/app-config/app-config.page.html +++ b/ui/src/app/pages/apps-routes/app-config/app-config.page.html @@ -12,7 +12,7 @@ - + diff --git a/ui/src/app/pages/apps-routes/app-config/app-config.page.ts b/ui/src/app/pages/apps-routes/app-config/app-config.page.ts index 05f7a68da..c363688b1 100644 --- a/ui/src/app/pages/apps-routes/app-config/app-config.page.ts +++ b/ui/src/app/pages/apps-routes/app-config/app-config.page.ts @@ -1,11 +1,11 @@ -import { Component } from '@angular/core' -import { NavController, AlertController, ModalController, PopoverController } from '@ionic/angular' +import { Component, ViewChild } from '@angular/core' +import { NavController, AlertController, ModalController, PopoverController, IonContent } from '@ionic/angular' import { ActivatedRoute } from '@angular/router' import { ApiService } from 'src/app/services/api/api.service' import { isEmptyObject } from 'src/app/util/misc.util' import { LoaderService } from 'src/app/services/loader.service' import { TrackingModalController } from 'src/app/services/tracking-modal-controller.service' -import { BehaviorSubject, from, fromEvent, of, Subscription } from 'rxjs' +import { from, fromEvent, of, Subscription } from 'rxjs' import { catchError, concatMap, map, take, 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' @@ -26,7 +26,7 @@ export class AppConfigPage { { title: string, description: string, buttonText: string } } - loadingText$ = new BehaviorSubject(undefined) + loadingText: string | undefined pkg: InstalledPackageDataEntry hasConfig = false @@ -45,7 +45,8 @@ export class AppConfigPage { spec: ConfigSpec config: object - subs: Subscription[] + @ViewChild(IonContent) content: IonContent + subs: Subscription[] = [] constructor ( private readonly navCtrl: NavController, @@ -84,47 +85,50 @@ export class AppConfigPage { this.navCtrl.back() } }), - ] - - this.patch.watch$('package-data', pkgId, 'installed') - .pipe( - tap(pkg => this.pkg = pkg), - tap(() => this.loadingText$.next(`Fetching config spec...`)), - concatMap(() => this.apiService.getPackageConfig({ id: pkgId })), - concatMap(({ spec, config }) => { - const rec = history.state && history.state.configRecommendation as Recommendation - if (rec) { - this.loadingText$.next(`Setting properties to accommodate ${rec.dependentTitle}...`) - return from(this.apiService.dryConfigureDependency({ 'dependency-id': pkgId, 'dependent-id': rec.dependentId })) - .pipe( - map(res => ({ - spec, - config, - dependencyConfig: res, - })), - tap(() => this.rec = rec), - catchError(e => { - this.error = { text: `Could not set properties to accommodate ${rec.dependentTitle}: ${e.message}`, moreInfo: { - title: `${rec.dependentTitle} requires the following:`, - description: rec.description, - buttonText: 'Configure Manually', - } } - return of({ spec, config, dependencyConfig: null }) - }), - ) - } else { - return of({ spec, config, dependencyConfig: null }) - } + this.patch.watch$('package-data', pkgId, 'installed') + .pipe( + tap(pkg => this.pkg = pkg), + tap(() => this.loadingText = 'Fetching config spec...'), + concatMap(() => this.apiService.getPackageConfig({ id: pkgId })), + concatMap(({ spec, config }) => { + const rec = history.state && history.state.configRecommendation as Recommendation + if (rec) { + this.loadingText = `Setting properties to accommodate ${rec.dependentTitle}...` + return from(this.apiService.dryConfigureDependency({ 'dependency-id': pkgId, 'dependent-id': rec.dependentId })) + .pipe( + map(res => ({ + spec, + config, + dependencyConfig: res, + })), + tap(() => this.rec = rec), + catchError(e => { + this.error = { text: `Could not set properties to accommodate ${rec.dependentTitle}: ${e.message}`, moreInfo: { + title: `${rec.dependentTitle} requires the following:`, + description: rec.description, + buttonText: 'Configure Manually', + } } + return of({ spec, config, dependencyConfig: null }) + }), + ) + } else { + return of({ spec, config, dependencyConfig: null }) + } + }), + map(({ spec, config, dependencyConfig }) => this.setConfig(spec, config, dependencyConfig)), + tap(() => this.loadingText = undefined), + take(1), + ).subscribe({ + error: e => { + console.error(e.message) + this.error = { text: e.message } + }, }), - map(({ spec, config, dependencyConfig }) => this.setConfig(spec, config, dependencyConfig)), - tap(() => this.loadingText$.next(undefined)), - take(1), - ).subscribe({ - error: e => { - console.error(e.message) - this.error = { text: e.message } - }, - }) + ] + } + + ngAfterViewInit () { + this.content.scrollToPoint(undefined, 1) } ngOnDestroy () { diff --git a/ui/src/app/pages/apps-routes/app-instructions/app-instructions.page.ts b/ui/src/app/pages/apps-routes/app-instructions/app-instructions.page.ts index a6549b2b0..f983f3067 100644 --- a/ui/src/app/pages/apps-routes/app-instructions/app-instructions.page.ts +++ b/ui/src/app/pages/apps-routes/app-instructions/app-instructions.page.ts @@ -1,9 +1,10 @@ -import { Component } from '@angular/core' +import { Component, ViewChild } from '@angular/core' import { ActivatedRoute } from '@angular/router' +import { IonContent } from '@ionic/angular' +import { Subscription } from 'rxjs' import { concatMap, take, tap } from 'rxjs/operators' import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model' import { ApiService } from 'src/app/services/api/api.service' -import { Method } from 'src/app/services/http.service' @Component({ selector: 'app-instructions', @@ -15,6 +16,9 @@ export class AppInstructionsPage { loading = true error = '' + @ViewChild(IonContent) content: IonContent + subs: Subscription[] = [] + constructor ( private readonly route: ActivatedRoute, private readonly apiService: ApiService, @@ -27,7 +31,6 @@ export class AppInstructionsPage { .pipe( concatMap(pkg => this.apiService.getStatic(pkg['static-files'].instructions)), tap(instructions => { - console.log(instructions) this.instructions = instructions }), take(1), @@ -41,4 +44,12 @@ export class AppInstructionsPage { () => console.log('COMPLETE'), ) } + + ngAfterViewInit () { + this.content.scrollToPoint(undefined, 1) + } + + ngOnDestroy () { + this.subs.forEach(sub => sub.unsubscribe()) + } } diff --git a/ui/src/app/pages/apps-routes/app-interfaces/app-interfaces.page.html b/ui/src/app/pages/apps-routes/app-interfaces/app-interfaces.page.html index 295a17cb6..b239172f3 100644 --- a/ui/src/app/pages/apps-routes/app-interfaces/app-interfaces.page.html +++ b/ui/src/app/pages/apps-routes/app-interfaces/app-interfaces.page.html @@ -8,7 +8,7 @@ - + diff --git a/ui/src/app/pages/apps-routes/app-interfaces/app-interfaces.page.ts b/ui/src/app/pages/apps-routes/app-interfaces/app-interfaces.page.ts index dbd48c736..33d8bbd5d 100644 --- a/ui/src/app/pages/apps-routes/app-interfaces/app-interfaces.page.ts +++ b/ui/src/app/pages/apps-routes/app-interfaces/app-interfaces.page.ts @@ -1,7 +1,8 @@ import { Component, ViewChild } from '@angular/core' import { ActivatedRoute } from '@angular/router' import { IonContent, ToastController } from '@ionic/angular' -import { InstalledPackageDataEntry } from 'src/app/models/patch-db/data-model' +import { Subscription } from 'rxjs' +import { InstalledPackageDataEntry, PackageDataEntry } from 'src/app/models/patch-db/data-model' import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model' import { ConfigService } from 'src/app/services/config.service' import { copyToClipboard } from 'src/app/util/web.util' @@ -12,25 +13,33 @@ import { copyToClipboard } from 'src/app/util/web.util' styleUrls: ['./app-Interfaces.page.scss'], }) export class AppInterfacesPage { - pkgId: string + pkg: PackageDataEntry @ViewChild(IonContent) content: IonContent + subs: Subscription[] = [] constructor ( private readonly route: ActivatedRoute, private readonly toastCtrl: ToastController, private readonly config: ConfigService, - public readonly patch: PatchDbModel, + private readonly patch: PatchDbModel, ) { } ngOnInit () { - this.pkgId = this.route.snapshot.paramMap.get('pkgId') + const pkgId = this.route.snapshot.paramMap.get('pkgId') + this.subs = [ + this.patch.watch$('package-data', pkgId).subscribe(pkg => this.pkg = pkg), + ] } - async ngAfterViewInit () { + ngAfterViewInit () { this.content.scrollToPoint(undefined, 1) } + ngOnDestroy () { + this.subs.forEach(sub => sub.unsubscribe()) + } + async copy (address: string): Promise { let message = '' await copyToClipboard(address || '') diff --git a/ui/src/app/pages/apps-routes/app-list/app-list.page.html b/ui/src/app/pages/apps-routes/app-list/app-list.page.html index 6eef95aab..e0d3ba3c7 100644 --- a/ui/src/app/pages/apps-routes/app-list/app-list.page.html +++ b/ui/src/app/pages/apps-routes/app-list/app-list.page.html @@ -39,7 +39,7 @@ - + {{ (pkg.value | manifest).title }} diff --git a/ui/src/app/pages/apps-routes/app-list/app-list.page.scss b/ui/src/app/pages/apps-routes/app-list/app-list.page.scss index 60733179d..2649c9bc1 100644 --- a/ui/src/app/pages/apps-routes/app-list/app-list.page.scss +++ b/ui/src/app/pages/apps-routes/app-list/app-list.page.scss @@ -12,7 +12,7 @@ } ion-card-title { - font-size: calc(8px + .7vw); + font-size: calc(10px + .4vw); color: white; margin: 10px 0; white-space: nowrap; diff --git a/ui/src/app/pages/apps-routes/app-logs/app-logs.page.html b/ui/src/app/pages/apps-routes/app-logs/app-logs.page.html index f38eb4b5e..51e5aadec 100644 --- a/ui/src/app/pages/apps-routes/app-logs/app-logs.page.html +++ b/ui/src/app/pages/apps-routes/app-logs/app-logs.page.html @@ -5,8 +5,8 @@ Logs - - + + diff --git a/ui/src/app/pages/apps-routes/app-manifest/app-manifest.page.ts b/ui/src/app/pages/apps-routes/app-manifest/app-manifest.page.ts index e8079bcf3..5dc757e98 100644 --- a/ui/src/app/pages/apps-routes/app-manifest/app-manifest.page.ts +++ b/ui/src/app/pages/apps-routes/app-manifest/app-manifest.page.ts @@ -1,10 +1,11 @@ -import { Component } from '@angular/core' +import { Component, ViewChild } from '@angular/core' import { ActivatedRoute } from '@angular/router' import { Subscription } from 'rxjs' import { PackageDataEntry } from 'src/app/models/patch-db/data-model' import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model' import { getManifest } from 'src/app/services/config.service' import * as JsonPointer from 'json-pointer' +import { IonContent } from '@ionic/angular' @Component({ selector: 'app-manifest', @@ -12,23 +13,24 @@ import * as JsonPointer from 'json-pointer' styleUrls: ['./app-manifest.page.scss'], }) export class AppManifestPage { - pkgId: string pkg: PackageDataEntry pointer: string node: object - subs: Subscription[] segmentValue: 'formatted' | 'raw' = 'formatted' + @ViewChild(IonContent) content: IonContent + subs: Subscription[] = [] + constructor ( private readonly route: ActivatedRoute, private readonly patch: PatchDbModel, ) { } ngOnInit () { - this.pkgId = this.route.snapshot.paramMap.get('pkgId') + const pkgId = this.route.snapshot.paramMap.get('pkgId') this.subs = [ - this.patch.watch$('package-data', this.pkgId) + this.patch.watch$('package-data', pkgId) .subscribe(pkg => { this.pkg = pkg this.setNode() @@ -38,6 +40,10 @@ export class AppManifestPage { this.setNode() } + ngAfterViewInit () { + this.content.scrollToPoint(undefined, 1) + } + ngOnDestroy () { this.subs.forEach(sub => sub.unsubscribe()) } diff --git a/ui/src/app/pages/apps-routes/app-metrics/app-metrics.page.ts b/ui/src/app/pages/apps-routes/app-metrics/app-metrics.page.ts index 9c1df721a..e83d2cfff 100644 --- a/ui/src/app/pages/apps-routes/app-metrics/app-metrics.page.ts +++ b/ui/src/app/pages/apps-routes/app-metrics/app-metrics.page.ts @@ -1,5 +1,6 @@ -import { Component } from '@angular/core' +import { Component, ViewChild } from '@angular/core' import { ActivatedRoute } from '@angular/router' +import { IonContent } from '@ionic/angular' import { Subscription } from 'rxjs' import { PackageDataEntry } from 'src/app/models/patch-db/data-model' import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model' @@ -10,9 +11,10 @@ import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model' styleUrls: ['./app-metrics.page.scss'], }) export class AppMetricsPage { - pkgId: string pkg: PackageDataEntry - subs: Subscription[] + + @ViewChild(IonContent) content: IonContent + subs: Subscription[] = [] constructor ( private readonly route: ActivatedRoute, @@ -20,16 +22,20 @@ export class AppMetricsPage { ) { } ngOnInit () { - this.pkgId = this.route.snapshot.paramMap.get('pkgId') + const pkgId = this.route.snapshot.paramMap.get('pkgId') this.subs = [ - this.patch.watch$('package-data', this.pkgId) + this.patch.watch$('package-data', pkgId) .subscribe(pkg => { this.pkg = pkg }), ] } + ngAfterViewInit () { + this.content.scrollToPoint(undefined, 1) + } + ngOnDestroy () { this.subs.forEach(sub => sub.unsubscribe()) } diff --git a/ui/src/app/pages/apps-routes/app-properties/app-properties.page.html b/ui/src/app/pages/apps-routes/app-properties/app-properties.page.html index f55fd96f9..f8f279d73 100644 --- a/ui/src/app/pages/apps-routes/app-properties/app-properties.page.html +++ b/ui/src/app/pages/apps-routes/app-properties/app-properties.page.html @@ -4,6 +4,11 @@ Values + + + + + @@ -11,64 +16,58 @@ - - - - - - - {{ error }} - - - - - -

Service not running. Information on this page could be inaccurate.

-
-
+ + {{ error }} + - - - -

No values.

-
-
- - - -
- - - - + + + +

Service not running. Information on this page could be inaccurate.

+
+
+ + + + +

No values.

+
+
+ + + +
+ + + + + + +

{{ prop.key }}

+
+
+ + + + + + +

{{ prop.key }}

+

{{ prop.value.masked && !unmasked[prop.key] ? (prop.value.value | mask ) : (prop.value.value | truncateEnd : 100) }}

+
+
+ + - -

{{ prop.key }}

-
- - - - - + + - -

{{ prop.key }}

-

{{ prop.value.masked && !unmasked[prop.key] ? (prop.value.value | mask ) : (prop.value.value | truncateEnd : 100) }}

-
-
- - - - - - - - - -
-
-
- - + + + +
+
+
+
\ No newline at end of file diff --git a/ui/src/app/pages/apps-routes/app-properties/app-properties.page.ts b/ui/src/app/pages/apps-routes/app-properties/app-properties.page.ts index 4a4f3fc50..be6fee88b 100644 --- a/ui/src/app/pages/apps-routes/app-properties/app-properties.page.ts +++ b/ui/src/app/pages/apps-routes/app-properties/app-properties.page.ts @@ -1,14 +1,15 @@ -import { Component } from '@angular/core' +import { Component, ViewChild } from '@angular/core' import { ActivatedRoute } from '@angular/router' import { ApiService } from 'src/app/services/api/api.service' import { Subscription } from 'rxjs' import { copyToClipboard } from 'src/app/util/web.util' -import { AlertController, NavController, PopoverController, ToastController } from '@ionic/angular' +import { AlertController, IonContent, NavController, PopoverController, ToastController } from '@ionic/angular' import { PackageProperties } from 'src/app/util/properties.util' import { QRComponent } from 'src/app/components/qr/qr.component' import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model' import * as JsonPointer from 'json-pointer' import { FEStatus } from 'src/app/services/pkg-status-rendering.service' +import { PackageMainStatus } from 'src/app/models/patch-db/data-model' @Component({ selector: 'app-properties', @@ -24,8 +25,10 @@ export class AppPropertiesPage { properties: PackageProperties node: PackageProperties unmasked: { [key: string]: boolean } = { } - FeStatus = FEStatus - subs: Subscription[] + running = true + + @ViewChild(IonContent) content: IonContent + subs: Subscription[] = [] constructor ( private readonly route: ActivatedRoute, @@ -48,18 +51,23 @@ export class AppPropertiesPage { this.pointer = queryParams['pointer'] this.node = JsonPointer.get(this.properties, this.pointer || '') }), + this.patch.watch$('package-data', this.pkgId, 'installed', 'status', 'main', 'status') + .subscribe(status => { + this.running = status === PackageMainStatus.Running + }), ] + } - this.loading = false + ngAfterViewInit () { + this.content.scrollToPoint(undefined, 1) } ngOnDestroy () { this.subs.forEach(sub => sub.unsubscribe()) } - async doRefresh (event: any) { - await this.getProperties(), - event.target.complete() + async refresh () { + await this.getProperties() } async presentDescription (property: { key: string, value: PackageProperties[''] }, e: Event) { @@ -114,12 +122,15 @@ export class AppPropertiesPage { } private async getProperties (): Promise { + this.loading = true try { this.properties = await this.apiService.getPackageProperties({ id: this.pkgId }) this.node = JsonPointer.get(this.properties, this.pointer || '') } catch (e) { console.error(e) this.error = e.message + } finally { + this.loading = false } } } diff --git a/ui/src/app/pages/apps-routes/app-restore/app-restore.page.html b/ui/src/app/pages/apps-routes/app-restore/app-restore.page.html index fca076785..874104c32 100644 --- a/ui/src/app/pages/apps-routes/app-restore/app-restore.page.html +++ b/ui/src/app/pages/apps-routes/app-restore/app-restore.page.html @@ -4,15 +4,15 @@ Restore Backup - - - + + + - + @@ -32,7 +32,7 @@

About

- Select a location from which to restore {{ pkg.installed.manifest.title }}. This will overwrite all current data. + Select a location from which to restore {{ title }}. This will overwrite all current data.

diff --git a/ui/src/app/pages/apps-routes/app-restore/app-restore.page.ts b/ui/src/app/pages/apps-routes/app-restore/app-restore.page.ts index 0ac4720c8..d64b36abb 100644 --- a/ui/src/app/pages/apps-routes/app-restore/app-restore.page.ts +++ b/ui/src/app/pages/apps-routes/app-restore/app-restore.page.ts @@ -5,6 +5,7 @@ import { BackupConfirmationComponent } from 'src/app/modals/backup-confirmation/ import { DiskInfo, PartitionInfoEntry } from 'src/app/services/api/api-types' import { ActivatedRoute } from '@angular/router' import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model' +import { Subscription } from 'rxjs' @Component({ selector: 'app-restore', @@ -14,30 +15,44 @@ import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model' export class AppRestorePage { disks: DiskInfo pkgId: string + title: string loading = true error: string allPartitionsMounted: boolean @ViewChild(IonContent) content: IonContent + subs: Subscription[] = [] constructor ( private readonly route: ActivatedRoute, private readonly modalCtrl: ModalController, private readonly apiService: ApiService, private readonly loadingCtrl: LoadingController, - public readonly patch: PatchDbModel, + private readonly patch: PatchDbModel, ) { } ngOnInit () { this.pkgId = this.route.snapshot.paramMap.get('pkgId') + + this.subs = [ + this.patch.watch$('package-data', this.pkgId, 'installed', 'manifest', 'title') + .subscribe(title => { + this.title = title + }), + ] + this.getExternalDisks() } - async ngAfterViewInit () { + ngAfterViewInit () { this.content.scrollToPoint(undefined, 1) } - async doRefresh () { + ngOnDestroy () { + this.subs.forEach(sub => sub.unsubscribe()) + } + + async refresh () { this.loading = true await this.getExternalDisks() } diff --git a/ui/src/app/pages/apps-routes/app-show/app-show.page.html b/ui/src/app/pages/apps-routes/app-show/app-show.page.html index 55da888ed..4678d0540 100644 --- a/ui/src/app/pages/apps-routes/app-show/app-show.page.html +++ b/ui/src/app/pages/apps-routes/app-show/app-show.page.html @@ -80,41 +80,38 @@ - - - Dependencies - - - -
- - -
+ Dependencies + + + + + + -
- -

{{ localDep ? (localDep | manifest).title : pkg.installed.status['dependency-errors'][dep.key]?.title }}

-

{{ manifest.dependencies[dep.key].version | displayEmver }}

-

{{ pkg.installed.status['dependency-errors'][dep.key] ? pkg.installed.status['dependency-errors'][dep.key].type : 'satisfied' }}

+ + +

{{ localDep ? (localDep | manifest).title : pkg.installed.status['dependency-errors'][dep.key]?.title }}

+

{{ manifest.dependencies[dep.key].version | displayEmver }}

+

{{ pkg.installed.status['dependency-errors'][dep.key] ? pkg.installed.status['dependency-errors'][dep.key].type : 'satisfied' }}

- + View - + Install - + Start - + Update - + Configure @@ -122,12 +119,11 @@
- -
+
-
-
-
+ + +
diff --git a/ui/src/app/pages/apps-routes/app-show/app-show.page.scss b/ui/src/app/pages/apps-routes/app-show/app-show.page.scss index d85206dfa..72cecb7d7 100644 --- a/ui/src/app/pages/apps-routes/app-show/app-show.page.scss +++ b/ui/src/app/pages/apps-routes/app-show/app-show.page.scss @@ -40,13 +40,6 @@ border-color: #404040; } -.dep-badge { - position: absolute; width: 2.5vh; - height: 2.5vh; - border-radius: 50px; - left: -1vh; - top: -1vh; -} .dep-issue { background: radial-gradient(var(--ion-color-warning) 40%, transparent) @@ -55,3 +48,9 @@ .dep-sat { background: radial-gradient(var(--ion-color-success) 40%, transparent) } + +ion-item-divider { + text-transform: uppercase; + margin-top: 22px; + font-weight: 400; +} \ No newline at end of file diff --git a/ui/src/app/pages/apps-routes/app-show/app-show.page.ts b/ui/src/app/pages/apps-routes/app-show/app-show.page.ts index a857cd693..4208e21b2 100644 --- a/ui/src/app/pages/apps-routes/app-show/app-show.page.ts +++ b/ui/src/app/pages/apps-routes/app-show/app-show.page.ts @@ -27,14 +27,12 @@ export class AppShowPage { buttons: Button[] = [] manifest: Manifest = { } as Manifest connected: boolean - - subs: Subscription[] = [] - FeStatus = FEStatus PackageState = PackageState DependencyErrorType = DependencyErrorType @ViewChild(IonContent) content: IonContent + subs: Subscription[] = [] constructor ( private readonly alertCtrl: AlertController, @@ -61,7 +59,7 @@ export class AppShowPage { this.setButtons() } - async ngAfterViewInit () { + ngAfterViewInit () { this.content.scrollToPoint(undefined, 1) } diff --git a/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.page.html b/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.page.html index 6eb212d50..07f5d48f1 100644 --- a/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.page.html +++ b/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.page.html @@ -10,7 +10,7 @@ - + @@ -43,23 +43,41 @@
-
+
- - + + - - - - - {{ pkg.title }} - - - {{ pkg.descriptionShort }} - - + + + + + + +

{{ pkg.title }}

+

{{ pkg.descriptionShort }}

+ +

+ Installed + Update Available +

+

+ Installing + +

+

+ Updating + +

+

+ Removing + +

+
+
+
diff --git a/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.page.scss b/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.page.scss index 1e2b65764..4df2d43c9 100644 --- a/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.page.scss +++ b/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.page.scss @@ -1,10 +1,3 @@ -.beneath-title { - font-size: 12px; - font-style: italic; - font-family: 'Open Sans'; - padding: 1px 0px 1.5px 0px; -} - .scrollable { overflow: auto; white-space: nowrap; diff --git a/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.page.ts b/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.page.ts index 3d83481c1..a850bcf6c 100644 --- a/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.page.ts +++ b/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.page.ts @@ -5,7 +5,8 @@ import { wizardModal } from 'src/app/components/install-wizard/install-wizard.co import { ModalController } from '@ionic/angular' import { WizardBaker } from 'src/app/components/install-wizard/prebaked-wizards' import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model' -import { PackageState } from 'src/app/models/patch-db/data-model' +import { PackageDataEntry, PackageState } from 'src/app/models/patch-db/data-model' +import { Subscription } from 'rxjs' @Component({ selector: 'marketplace-list', @@ -23,6 +24,7 @@ export class MarketplaceListPage { data: MarketplaceData eos: MarketplaceEOS pkgs: AvailablePreview[] = [] + installedPkgs: { [id: string]: PackageDataEntry } = { } PackageState = PackageState @@ -30,14 +32,21 @@ export class MarketplaceListPage { needInfinite = false readonly perPage = 20 + subs: Subscription[] = [] + constructor ( private readonly apiService: ApiService, private readonly modalCtrl: ModalController, private readonly wizardBaker: WizardBaker, - public patch: PatchDbModel, + private readonly patch: PatchDbModel, ) { } async ngOnInit () { + this.subs = [ + this.patch.watch$('package-data') + .subscribe(pkgs => this.installedPkgs = pkgs), + ] + try { const [data, eos, pkgs] = await Promise.all([ this.apiService.getMarketplaceData({ }), @@ -56,6 +65,10 @@ export class MarketplaceListPage { } } + ngOnDestroy () { + this.subs.forEach(sub => sub.unsubscribe()) + } + async doInfinite (e: any): Promise { const pkgs = await this.getPkgs() this.pkgs = this.pkgs.concat(pkgs) @@ -65,6 +78,7 @@ export class MarketplaceListPage { async search (e?: any): Promise { this.query = e.target.value || undefined this.page = 1 + this.pkgsLoading = true this.pkgs = await this.getPkgs() } @@ -79,7 +93,6 @@ export class MarketplaceListPage { } private async getPkgs (): Promise { - this.pkgsLoading = true try { const pkgs = await this.apiService.getAvailableList({ category: this.category, @@ -100,6 +113,8 @@ export class MarketplaceListPage { async switchCategory (category: string): Promise { this.category = category + this.pkgsLoading = true + this.page = 1 this.pkgs = await this.getPkgs() } } diff --git a/ui/src/app/pages/marketplace-routes/marketplace-show/marketplace-show.page.html b/ui/src/app/pages/marketplace-routes/marketplace-show/marketplace-show.page.html index 18b6e9159..ea7f2714c 100644 --- a/ui/src/app/pages/marketplace-routes/marketplace-show/marketplace-show.page.html +++ b/ui/src/app/pages/marketplace-routes/marketplace-show/marketplace-show.page.html @@ -19,59 +19,57 @@ {{ error }} - - - - -
- -
-

{{ pkg.manifest.title }}

-

{{ pkg.manifest.version | displayEmver }}

-
- -

- Not Installed + + + +

+ +
+

{{ pkg.manifest.title }}

+

{{ pkg.manifest.version | displayEmver }}

+
+ +

+ Not Installed +

+ + +

+ + {{ installedPkg.state }} +

- - -

- - {{ localPkg.state }} - + + +

+ Installed at {{ installedPkg.installed.manifest.version | displayEmver }}

- - -

- Installed at {{ localPkg.installed.manifest.version | displayEmver }} -

-
-
+
- - - - - Install - - - - - - - Update - - - Downgrade - - - - - - - +
+ + + + + Install + + + + + + + Update + + + Downgrade + + + + + + @@ -121,24 +119,20 @@ Dependencies - - - - - - - -

- {{ pkg['dependency-metadata'][dep.key].title }} - (recommended) -

-

{{ dep.value.version | displayEmver }}

-
-
- - {{ dep.value.description }} - -
+ + + + + + +

+ {{ pkg['dependency-metadata'][dep.key].title }} + (recommended) +

+

{{ dep.value.version | displayEmver }}

+

{{ dep.value.description }}

+
+
diff --git a/ui/src/app/pages/marketplace-routes/marketplace-show/marketplace-show.page.scss b/ui/src/app/pages/marketplace-routes/marketplace-show/marketplace-show.page.scss index 08bc259ad..0b5a89dc0 100644 --- a/ui/src/app/pages/marketplace-routes/marketplace-show/marketplace-show.page.scss +++ b/ui/src/app/pages/marketplace-routes/marketplace-show/marketplace-show.page.scss @@ -2,7 +2,7 @@ font-family: 'Montserrat'; padding: 2%; img { - min-width: 16%; + min-width: 15%; max-width: 18%; } .header-text { @@ -10,12 +10,11 @@ display: inline-block; vertical-align: top; .header-title { - line-height: .65; margin: 0 0 0 -2px; font-size: calc(20px + 3vw) } .header-version { - padding: 12px 0; + padding: 4px 0 12px 0; margin: 0; font-size: calc(10px + 1vw) } diff --git a/ui/src/app/pages/marketplace-routes/marketplace-show/marketplace-show.page.ts b/ui/src/app/pages/marketplace-routes/marketplace-show/marketplace-show.page.ts index da3e9676e..5eb62a794 100644 --- a/ui/src/app/pages/marketplace-routes/marketplace-show/marketplace-show.page.ts +++ b/ui/src/app/pages/marketplace-routes/marketplace-show/marketplace-show.page.ts @@ -8,8 +8,9 @@ import { Emver } from 'src/app/services/emver.service' import { displayEmver } from 'src/app/pipes/emver.pipe' import { pauseFor } from 'src/app/util/misc.util' import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model' -import { PackageState } from 'src/app/models/patch-db/data-model' +import { PackageDataEntry, PackageState } from 'src/app/models/patch-db/data-model' import { MarketplaceService } from '../marketplace.service' +import { Subscription } from 'rxjs' @Component({ selector: 'marketplace-show', @@ -19,12 +20,13 @@ import { MarketplaceService } from '../marketplace.service' export class MarketplaceShowPage { error = '' pkgId: string - + installedPkg: PackageDataEntry PackageState = PackageState - rec: Recommendation | null = null showRec = true + subs: Subscription[] = [] + constructor ( private readonly route: ActivatedRoute, private readonly alertCtrl: AlertController, @@ -32,17 +34,30 @@ export class MarketplaceShowPage { private readonly wizardBaker: WizardBaker, private readonly navCtrl: NavController, private readonly emver: Emver, - public readonly patch: PatchDbModel, + private readonly patch: PatchDbModel, public marketplaceService: MarketplaceService, ) { } async ngOnInit () { + this.pkgId = this.route.snapshot.paramMap.get('pkgId') this.rec = history.state && history.state.installRec as Recommendation + + this.subs = [ + this.patch.watch$('package-data', this.pkgId) + .subscribe(pkg => { + console.log(pkg) + this.installedPkg = pkg + }), + ] + this.getPkg() } + ngOnDestroy () { + this.subs.forEach(sub => sub.unsubscribe()) + } + async getPkg (version?: string): Promise { - this.pkgId = this.route.snapshot.paramMap.get('pkgId') try { await this.marketplaceService.setPkg(this.pkgId, version) } catch (e) { diff --git a/ui/src/app/pages/server-routes/developer-routes/dev-options/dev-options.page.html b/ui/src/app/pages/server-routes/developer-routes/dev-options/dev-options.page.html index 20e470c43..dac499972 100644 --- a/ui/src/app/pages/server-routes/developer-routes/dev-options/dev-options.page.html +++ b/ui/src/app/pages/server-routes/developer-routes/dev-options/dev-options.page.html @@ -9,7 +9,7 @@ - + SSH Keys diff --git a/ui/src/app/pages/server-routes/developer-routes/dev-options/dev-options.page.ts b/ui/src/app/pages/server-routes/developer-routes/dev-options/dev-options.page.ts index 69f9e6299..4c12bf9df 100644 --- a/ui/src/app/pages/server-routes/developer-routes/dev-options/dev-options.page.ts +++ b/ui/src/app/pages/server-routes/developer-routes/dev-options/dev-options.page.ts @@ -1,6 +1,8 @@ import { Component } from '@angular/core' import { ServerConfigService } from 'src/app/services/server-config.service' import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model' +import { ServerInfo } from 'src/app/models/patch-db/data-model' +import { Subscription } from 'rxjs' @Component({ selector: 'dev-options', @@ -8,12 +10,27 @@ import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model' styleUrls: ['./dev-options.page.scss'], }) export class DevOptionsPage { + server: ServerInfo = { } as any + subs: Subscription[] = [] constructor ( private readonly serverConfigService: ServerConfigService, - public readonly patch: PatchDbModel, + private readonly patch: PatchDbModel, ) { } + ngOnInit () { + this.subs = [ + this.patch.watch$('server-info') + .subscribe(server => { + this.server = server + }), + ] + } + + ngOnDestroy () { + this.subs.forEach(sub => sub.unsubscribe()) + } + async presentModalValueEdit (key: string, current?: any): Promise { await this.serverConfigService.presentModalValueEdit(key, current) } diff --git a/ui/src/app/pages/server-routes/developer-routes/dev-ssh-keys/dev-ssh-keys.page.html b/ui/src/app/pages/server-routes/developer-routes/dev-ssh-keys/dev-ssh-keys.page.html index 98a44c7b7..51765defa 100644 --- a/ui/src/app/pages/server-routes/developer-routes/dev-ssh-keys/dev-ssh-keys.page.html +++ b/ui/src/app/pages/server-routes/developer-routes/dev-ssh-keys/dev-ssh-keys.page.html @@ -4,6 +4,11 @@ SSH Keys + + + + + @@ -16,7 +21,7 @@ Saved Keys - + {{ ssh.value.alg }} {{ ssh.key }} {{ ssh.value.hostname }} @@ -26,10 +31,4 @@ - - - - - - \ No newline at end of file diff --git a/ui/src/app/pages/server-routes/developer-routes/dev-ssh-keys/dev-ssh-keys.page.ts b/ui/src/app/pages/server-routes/developer-routes/dev-ssh-keys/dev-ssh-keys.page.ts index ac8a29f54..91e7b8ffc 100644 --- a/ui/src/app/pages/server-routes/developer-routes/dev-ssh-keys/dev-ssh-keys.page.ts +++ b/ui/src/app/pages/server-routes/developer-routes/dev-ssh-keys/dev-ssh-keys.page.ts @@ -3,6 +3,8 @@ import { ServerConfigService } from 'src/app/services/server-config.service' import { AlertController } from '@ionic/angular' import { LoaderService } from 'src/app/services/loader.service' import { SSHService } from './ssh.service' +import { Subscription } from 'rxjs' +import { SSHKeys } from 'src/app/services/api/api-types' @Component({ selector: 'dev-ssh-keys', @@ -12,18 +14,31 @@ import { SSHService } from './ssh.service' export class DevSSHKeysPage { error = '' loading = true + sshKeys: SSHKeys + subs: Subscription[] = [] constructor ( private readonly loader: LoaderService, private readonly serverConfigService: ServerConfigService, private readonly alertCtrl: AlertController, - public readonly sshService: SSHService, + private readonly sshService: SSHService, ) { } - ngOnInit () { - this.sshService.getKeys().then(() => { - this.loading = false - }) + async ngOnInit () { + await this.sshService.getKeys() + + this.subs = [ + this.sshService.watch$() + .subscribe(keys => { + this.sshKeys = keys + }), + ] + + this.loading = false + } + + ngOnDestroy () { + this.subs.forEach(sub => sub.unsubscribe()) } async presentModalAdd () { diff --git a/ui/src/app/pages/server-routes/lan/lan.page.html b/ui/src/app/pages/server-routes/lan/lan.page.html index 01582a3e8..cd9adcefe 100644 --- a/ui/src/app/pages/server-routes/lan/lan.page.html +++ b/ui/src/app/pages/server-routes/lan/lan.page.html @@ -17,8 +17,9 @@

You can connect to your Embassy over your Local Area Network (LAN). This can be useful for achieving a faster experience, as well as a fallback in case the Tor network is experiencing issues.

- - View Instructions + + + View Instructions @@ -39,11 +40,9 @@

If you are having issues connecting to your Embassy over LAN, try refreshing the network by clicking the button below.

- - - - Refresh Network - + + + Refresh Network @@ -63,7 +62,7 @@

LAN Address

-

https://{{ patch.watch$('server-info', 'lan-address') | ngrxPush }}

+

{{ lanAddress }}

diff --git a/ui/src/app/pages/server-routes/lan/lan.page.ts b/ui/src/app/pages/server-routes/lan/lan.page.ts index 3f5c23b75..96ec0009a 100644 --- a/ui/src/app/pages/server-routes/lan/lan.page.ts +++ b/ui/src/app/pages/server-routes/lan/lan.page.ts @@ -5,6 +5,7 @@ import { ConfigService } from 'src/app/services/config.service' import { LoaderService } from 'src/app/services/loader.service' import { ApiService } from 'src/app/services/api/api.service' import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model' +import { Subscription } from 'rxjs' @Component({ selector: 'lan', @@ -19,13 +20,14 @@ export class LANPage { NotTor: `For security reasons, you must setup LAN over a Tor connection. Please navigate to your Embassy Tor Address and try again.`, } readonly docsUrl = 'https://docs.start9.com/user-manual/general/lan-setup' + subs: Subscription[] = [] constructor ( private readonly toastCtrl: ToastController, private readonly config: ConfigService, private readonly loader: LoaderService, private readonly apiService: ApiService, - public readonly patch: PatchDbModel, + private readonly patch: PatchDbModel, ) { } ngOnInit () { @@ -34,6 +36,16 @@ export class LANPage { } else if (!this.config.isTor()) { this.lanDisabled = LanSetupIssue.NOT_TOR } + this.subs = [ + this.patch.watch$('server-info', 'lan-address') + .subscribe(addr => { + this.lanAddress = `https://${addr}` + }), + ] + } + + ngOnDestroy () { + this.subs.forEach(sub => sub.unsubscribe()) } async refreshLAN (): Promise { diff --git a/ui/src/app/pages/server-routes/preferences/preferences.page.html b/ui/src/app/pages/server-routes/preferences/preferences.page.html index f1eeb8ee8..bdc35663f 100644 --- a/ui/src/app/pages/server-routes/preferences/preferences.page.html +++ b/ui/src/app/pages/server-routes/preferences/preferences.page.html @@ -9,7 +9,7 @@ - + Embassy Name {{ ui['server-name'] }} diff --git a/ui/src/app/pages/server-routes/preferences/preferences.page.ts b/ui/src/app/pages/server-routes/preferences/preferences.page.ts index 68fea05f4..46eddd2f9 100644 --- a/ui/src/app/pages/server-routes/preferences/preferences.page.ts +++ b/ui/src/app/pages/server-routes/preferences/preferences.page.ts @@ -1,6 +1,8 @@ import { Component } from '@angular/core' import { ServerConfigService } from 'src/app/services/server-config.service' import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model' +import { Subscription } from 'rxjs' +import { UIData } from 'src/app/models/patch-db/data-model' @Component({ selector: 'preferences', @@ -8,11 +10,27 @@ import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model' styleUrls: ['./preferences.page.scss'], }) export class PreferencesPage { + subs: Subscription[] = [] + ui: UIData = { } as any + constructor ( private readonly serverConfigService: ServerConfigService, - public readonly patch: PatchDbModel, + private readonly patch: PatchDbModel, ) { } + ngOnInit () { + this.subs = [ + this.patch.watch$('ui') + .subscribe(ui => { + this.ui = ui + }), + ] + } + + ngOnDestroy () { + this.subs.forEach(sub => sub.unsubscribe()) + } + async presentModalValueEdit (key: string, current?: string): Promise { await this.serverConfigService.presentModalValueEdit(key, current) } diff --git a/ui/src/app/pages/server-routes/server-backup/server-backup.page.html b/ui/src/app/pages/server-routes/server-backup/server-backup.page.html index ca1522764..fd976b01f 100644 --- a/ui/src/app/pages/server-routes/server-backup/server-backup.page.html +++ b/ui/src/app/pages/server-routes/server-backup/server-backup.page.html @@ -4,9 +4,9 @@ Create Backup - - - + + + diff --git a/ui/src/app/pages/server-routes/server-logs/server-logs.page.html b/ui/src/app/pages/server-routes/server-logs/server-logs.page.html index 96698d57c..f2cef6767 100644 --- a/ui/src/app/pages/server-routes/server-logs/server-logs.page.html +++ b/ui/src/app/pages/server-routes/server-logs/server-logs.page.html @@ -5,8 +5,8 @@ Logs - - + + diff --git a/ui/src/app/pages/server-routes/server-show/server-show.page.html b/ui/src/app/pages/server-routes/server-show/server-show.page.html index 81a063600..1f29117ae 100644 --- a/ui/src/app/pages/server-routes/server-show/server-show.page.html +++ b/ui/src/app/pages/server-routes/server-show/server-show.page.html @@ -1,6 +1,6 @@ - {{ patch.watch$('ui', 'server-name') | ngrxPush }} + {{ patch.watch$('ui', 'server-name') | async }} diff --git a/ui/src/app/pages/server-routes/server-specs/server-specs.page.html b/ui/src/app/pages/server-routes/server-specs/server-specs.page.html index 70e877a1b..2566c20ff 100644 --- a/ui/src/app/pages/server-routes/server-specs/server-specs.page.html +++ b/ui/src/app/pages/server-routes/server-specs/server-specs.page.html @@ -11,7 +11,7 @@ Basic - +

Version

diff --git a/ui/src/app/pages/server-routes/server-specs/server-specs.page.ts b/ui/src/app/pages/server-routes/server-specs/server-specs.page.ts index d254eff1c..87208181d 100644 --- a/ui/src/app/pages/server-routes/server-specs/server-specs.page.ts +++ b/ui/src/app/pages/server-routes/server-specs/server-specs.page.ts @@ -2,6 +2,8 @@ import { Component } from '@angular/core' import { ToastController } from '@ionic/angular' import { copyToClipboard } from 'src/app/util/web.util' import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model' +import { ServerInfo } from 'src/app/models/patch-db/data-model' +import { Subscription } from 'rxjs' @Component({ selector: 'server-specs', @@ -9,12 +11,27 @@ import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model' styleUrls: ['./server-specs.page.scss'], }) export class ServerSpecsPage { + server: ServerInfo + subs: Subscription[] = [] constructor ( private readonly toastCtrl: ToastController, - public readonly patch: PatchDbModel, + private readonly patch: PatchDbModel, ) { } + ngOnInit () { + this.subs = [ + this.patch.watch$('server-info') + .subscribe(server => { + this.server = server + }), + ] + } + + ngOnDestroy () { + this.subs.forEach(sub => sub.unsubscribe()) + } + async copy (address: string) { let message = '' await copyToClipboard(address || '') diff --git a/ui/src/app/pages/server-routes/wifi/wifi.page.html b/ui/src/app/pages/server-routes/wifi/wifi.page.html index 15b3766e9..800d8caf1 100644 --- a/ui/src/app/pages/server-routes/wifi/wifi.page.html +++ b/ui/src/app/pages/server-routes/wifi/wifi.page.html @@ -4,6 +4,11 @@ WiFi Settings + + + + +
@@ -17,13 +22,11 @@

About

Embassy will automatically connect to available networks, allowing you to remove the Ethernet cable.

-
-

Connecting, disconnecting, or changing WiFi networks can cause your Embassy and its services to become unreachable for up to an hour. Please be patient.

Saved Networks - + {{ ssid }} @@ -31,10 +34,4 @@
- - - - - -
\ No newline at end of file diff --git a/ui/src/app/pages/server-routes/wifi/wifi.page.ts b/ui/src/app/pages/server-routes/wifi/wifi.page.ts index f37ecc0aa..82cb1cf25 100644 --- a/ui/src/app/pages/server-routes/wifi/wifi.page.ts +++ b/ui/src/app/pages/server-routes/wifi/wifi.page.ts @@ -6,6 +6,7 @@ import { WifiService } from './wifi.service' import { LoaderService } from 'src/app/services/loader.service' import { WiFiInfo } from 'src/app/models/patch-db/data-model' import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model' +import { Subscription } from 'rxjs' @Component({ selector: 'wifi', @@ -14,15 +15,30 @@ import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model' }) export class WifiListPage { error = '' + wifi: WiFiInfo + subs: Subscription[] = [] constructor ( private readonly apiService: ApiService, private readonly loader: LoaderService, private readonly actionCtrl: ActionSheetController, private readonly wifiService: WifiService, - public readonly patch: PatchDbModel, + private readonly patch: PatchDbModel, ) { } + ngOnInit () { + this.subs = [ + this.patch.watch$('server-info', 'wifi') + .subscribe(wifi => { + this.wifi = wifi + }), + ] + } + + ngOnDestroy () { + this.subs.forEach(sub => sub.unsubscribe()) + } + async presentAction (ssid: string, wifi: WiFiInfo) { const buttons: ActionSheetButton[] = [ { diff --git a/ui/src/app/pages/server-routes/wifi/wifi.service.ts b/ui/src/app/pages/server-routes/wifi/wifi.service.ts index a908b03bc..ccbd1cfb8 100644 --- a/ui/src/app/pages/server-routes/wifi/wifi.service.ts +++ b/ui/src/app/pages/server-routes/wifi/wifi.service.ts @@ -57,7 +57,7 @@ export class WifiService { }, }, ], - cssClass: 'notification-toast-error', + cssClass: 'notification-toast', }) await toast.present() diff --git a/ui/src/app/pipes/installed-latest-comparison.pipe.ts b/ui/src/app/pipes/installed-latest-comparison.pipe.ts deleted file mode 100644 index 9eeb77078..000000000 --- a/ui/src/app/pipes/installed-latest-comparison.pipe.ts +++ /dev/null @@ -1,45 +0,0 @@ -// import { Pipe, PipeTransform } from '@angular/core' -// import { combineLatest, Observable } from 'rxjs' -// import { map } from 'rxjs/operators' -// import { Emver } from '../services/emver.service' - - -// @Pipe({ -// name: 'compareInstalledAndLatest', -// }) -// export class InstalledLatestComparisonPipe implements PipeTransform { -// constructor (private readonly emver: Emver) { } - -// transform (app: PropertySubject): Observable<'not-installed' | 'installed-below' | 'installed-above' | 'installed-equal'> { -// return combineLatest([app.versionInstalled, app.versionLatest]).pipe( -// map(([i, l]) => { -// if (!i) return 'not-installed' -// switch (this.emver.compare(i, l)){ -// case 0: return 'installed-equal' -// case 1: return 'installed-above' -// case -1: return 'installed-below' -// } -// }), -// ) -// } -// } - -// @Pipe({ -// name: 'compareInstalledAndViewing', -// }) -// export class InstalledViewingComparisonPipe implements PipeTransform { -// constructor (private readonly emver: Emver) { } - -// transform (app: PropertySubject): Observable<'not-installed' | 'installed-below' | 'installed-above' | 'installed-equal'> { -// return combineLatest([app.versionInstalled, app.versionViewing]).pipe( -// map(([i, l]) => { -// if (!i) return 'not-installed' -// switch (this.emver.compare(i, l)){ -// case 0: return 'installed-equal' -// case 1: return 'installed-above' -// case -1: return 'installed-below' -// } -// }), -// ) -// } -// } diff --git a/ui/src/app/services/split-pane.service.ts b/ui/src/app/services/split-pane.service.ts index 0a7082c44..51b1325cd 100644 --- a/ui/src/app/services/split-pane.service.ts +++ b/ui/src/app/services/split-pane.service.ts @@ -5,5 +5,5 @@ import { Injectable } from '@angular/core' providedIn: 'root', }) export class SplitPaneTracker { - menuFixedOpenOnLeft$: BehaviorSubject = new BehaviorSubject(false) + sidebarOpen$: BehaviorSubject = new BehaviorSubject(false) } \ No newline at end of file diff --git a/ui/src/global.scss b/ui/src/global.scss index bf26f5d38..2f22ee4a3 100644 --- a/ui/src/global.scss +++ b/ui/src/global.scss @@ -58,14 +58,6 @@ border-style: none; } -.fab-button { - margin: 20px; - --background: transparent; - --border-color: var(--ion-color-primary); - --border-style: solid; - --border-width: 2px; -} - .help-button { margin: 0 8px 0 0; } @@ -74,12 +66,6 @@ --highlight-background: transparent !important; } -.alert-config-value { - .alert-message.sc-ion-alert-md { - color: var(--ion-color-danger) !important; - } -} - .center { display: block; margin: auto; @@ -95,15 +81,6 @@ --color: white; } -.notification-toast-error { - --background: var(--ion-color-light); - --button-color: var(--ion-color-dark); - --border-color: var(--ion-color-danger); - --border-style: solid; - --border-width: 1px; - --color: white; -} - .sublist-spinner { text-align: center; margin-top: 40px; @@ -315,8 +292,3 @@ ion-item-divider { width: 16px !important; height: 16px !important; } - -.dependency-item { - --padding-start: 20px; - --padding-end: 2px; -}