From 5b3e445b531e854152b7a97e2207ef8e8322ea06 Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Wed, 22 Sep 2021 13:35:29 -0600 Subject: [PATCH] finsih status refactor --- .../apps-routes/app-list/app-list.page.html | 12 +- .../apps-routes/app-list/app-list.page.scss | 17 +- .../apps-routes/app-list/app-list.page.ts | 41 +++-- .../apps-routes/app-show/app-show.page.ts | 174 ++++++++++-------- .../services/pkg-status-rendering.service.ts | 7 +- 5 files changed, 148 insertions(+), 103 deletions(-) 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 0f1c52ce8..cc6ace3e9 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 @@ -42,7 +42,17 @@ - + + + + + + + + + + +

{{ pkg.value.entry.state | titlecase }}...{{ pkg.value.installProgress.totalProgress }}%

{{ pkg.value.entry.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 fca704000..a33eca105 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 @@ -1,5 +1,6 @@ .installed-card { - margin: 2px; + margin: 4px; + padding: 4px; background: linear-gradient(37deg, #333333, #131313); border-radius: 10px; text-align: center; @@ -83,3 +84,17 @@ font-weight: bold; margin: 0; } + +.status-grid { + --ion-grid-padding: 0; + ion-row { + min-height: 22px; + } + ion-col { + --ion-grid-column-padding: 0 + } + ion-icon { + font-size: calc(12px + .4vw); + padding-top: 4px; + } +} diff --git a/ui/src/app/pages/apps-routes/app-list/app-list.page.ts b/ui/src/app/pages/apps-routes/app-list/app-list.page.ts index 868b05d01..c06eb2f98 100644 --- a/ui/src/app/pages/apps-routes/app-list/app-list.page.ts +++ b/ui/src/app/pages/apps-routes/app-list/app-list.page.ts @@ -4,7 +4,7 @@ import { ConnectionFailure, ConnectionService } from 'src/app/services/connectio import { PatchDbService } from 'src/app/services/patch-db/patch-db.service' import { PackageDataEntry, PackageState } from 'src/app/services/patch-db/data-model' import { Subscription } from 'rxjs' -import { PrimaryRendering, renderPkgStatus, StatusRendering } from 'src/app/services/pkg-status-rendering.service' +import { DependencyStatus, HealthStatus, PrimaryRendering, renderPkgStatus, StatusRendering } from 'src/app/services/pkg-status-rendering.service' import { filter } from 'rxjs/operators' import { isEmptyObject } from 'src/app/util/misc.util' import { PackageLoadingService, ProgressData } from 'src/app/services/package-loading.service' @@ -15,19 +15,11 @@ import { PackageLoadingService, ProgressData } from 'src/app/services/package-lo styleUrls: ['./app-list.page.scss'], }) export class AppListPage { + PackageState = PackageState + subs: Subscription[] = [] connectionFailure: boolean - pkgs: { [id: string]: { - entry: PackageDataEntry - bulb: { - class: string - img: string - } - statusRendering: StatusRendering | null - sub: Subscription | null - installProgress: ProgressData - }} = { } - PackageState = PackageState + pkgs: { [id: string]: PkgInfo } = { } loading = true constructor ( @@ -70,17 +62,19 @@ export class AppListPage { class: 'bulb-off', img: 'assets/img/off-bulb.png', }, - statusRendering: PrimaryRendering[renderPkgStatus(pkgs[id]).primary], - sub: null, + primaryRendering: PrimaryRendering[renderPkgStatus(pkgs[id]).primary], installProgress: !isEmptyObject(pkgs[id]['install-progress']) ? this.installPackageService.transform(pkgs[id]['install-progress']) : undefined, + error: false, + sub: null, } // subscribe to pkg this.pkgs[id].sub = this.patch.watch$('package-data', id).subscribe(pkg => { if (!pkg) return let bulbClass = 'bulb-on' let img = '' - const statusRendering = PrimaryRendering[renderPkgStatus(pkg).primary] - switch (statusRendering.color) { + const statuses = renderPkgStatus(pkg) + const primaryRendering = PrimaryRendering[statuses.primary] + switch (primaryRendering.color) { case 'danger': img = 'assets/img/danger-bulb.png' break @@ -101,7 +95,8 @@ export class AppListPage { class: bulbClass, img, } - this.pkgs[id].statusRendering = statusRendering + this.pkgs[id].primaryRendering = primaryRendering + this.pkgs[id].error = [HealthStatus.NeedsConfig, HealthStatus.Failure].includes(statuses.health) || [DependencyStatus.Issue, DependencyStatus.Critical].includes(statuses.dependency) }) }) }), @@ -128,3 +123,15 @@ export class AppListPage { return 0 } } + +interface PkgInfo { + entry: PackageDataEntry + bulb: { + class: string + img: string + } + primaryRendering: StatusRendering + installProgress: ProgressData + error: boolean + sub: Subscription | null +} 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 6ceafe5ea..bdbef3273 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 @@ -65,13 +65,47 @@ export class AppShowPage { async ngOnInit () { this.pkgId = this.route.snapshot.paramMap.get('pkgId') - this.setValues(this.patch.data['package-data']) + // this.setServiceValues(this.patch.data['package-data']) this.subs = [ // 1 - this.patch.watch$('package-data') - .subscribe(pkgs => { - this.setValues(pkgs) + this.patch.watch$('package-data', this.pkgId) + .subscribe(pkg => { + this.pkg = pkg + this.installProgress = !isEmptyObject(pkg['install-progress']) ? this.packageLoadingService.transform(pkg['install-progress']) : undefined + this.statuses = renderPkgStatus(pkg) + + // health + if (this.pkg.installed?.status.main.status === PackageMainStatus.Running) { + this.healthChecks = { ...this.pkg.installed.status.main.health } + } else { + this.healthChecks = { } + } + + // dependencies + if (!pkg.installed) { + this.dependencies = { } + } else { + const currentDeps = pkg.installed['current-dependencies'] + Object.keys(currentDeps).forEach(key => { + const manifestDep = pkg.manifest.dependencies[key] + if (!this.dependencies[key] && manifestDep) { + this.dependencies[key] = { } as any + this.dependencies[key].sub = this.patch.watch$('package-data', key) + .subscribe(localDep => { + this.setDepValues(key, manifestDep.version, localDep) + }) + } + }) + + // unsub to deleted + Object.keys(this.dependencies).forEach(key => { + if (!currentDeps[key]) { + this.dependencies[key].sub.unsubscribe() + delete this.dependencies[key] + } + }) + } }), // 2 this.connectionService.watchFailure$() @@ -88,6 +122,9 @@ export class AppShowPage { ngOnDestroy () { this.subs.forEach(sub => sub.unsubscribe()) + Object.values(this.dependencies).forEach(dep => { + dep.sub.unsubscribe() + }) } launchUi (): void { @@ -166,87 +203,63 @@ export class AppShowPage { await modal.present() } - private setValues (pkgs: { [id: string]: PackageDataEntry }): void { - this.pkg = pkgs[this.pkgId] - this.installProgress = !isEmptyObject(this.pkg['install-progress']) ? this.packageLoadingService.transform(this.pkg['install-progress']) : undefined - this.statuses = renderPkgStatus(this.pkg) + private setDepValues (id: string, version: string, localDep: PackageDataEntry | undefined): void { + let errorText = '' + let spinnerColor = '' + let actionText = 'View' + let action: () => any = () => this.navCtrl.navigateForward(`/services/${id}`) - if (!this.pkg.installed) { - this.dependencies = { } - this.healthChecks = { } - } else { - // ** dependencies - Object.keys(this.pkg.installed['current-dependencies'] || { }) - .forEach(id => { - // we can safely ignore any current dependencies that are not defined in the service manifest - const manifestDep = this.pkg.manifest.dependencies[id] - if (manifestDep) { - let errorText = '' - let spinnerColor = '' - let actionText = 'View' - let action: () => any = () => this.navCtrl.navigateForward(`/services/${id}`) + const error = this.pkg.installed.status['dependency-errors'][id] || null - const error = this.pkg.installed.status['dependency-errors'][id] || null + if (error) { + // health checks failed + if ([DependencyErrorType.InterfaceHealthChecksFailed, DependencyErrorType.HealthChecksFailed].includes(error.type)) { + errorText = 'Health Check Failed' + // not fully installed (same as !localDep?.installed) + } else if (error.type === DependencyErrorType.NotInstalled) { + if (localDep) { + errorText = localDep.state // 'Installing' | 'Removing' + } else { + errorText = 'Not Installed' + actionText = 'Install' + action = () => this.fixDep('install', id) + } + // incorrect version + } else if (error.type === DependencyErrorType.IncorrectVersion) { + if (localDep) { + errorText = localDep.state // 'Updating' | 'Removing' + } else { + errorText = 'Incorrect Version' + actionText = 'Update' + action = () => this.fixDep('update', id) + } + // not running + } else if (error.type === DependencyErrorType.NotRunning) { + errorText = 'Not Running' + actionText = 'Start' + // config unsatisfied + } else if (error.type === DependencyErrorType.ConfigUnsatisfied) { + errorText = 'Config Not Satisfied' + actionText = 'Auto Config' + action = () => this.fixDep('configure', id) + } - if (error) { - const localDep = pkgs[id] - // health checks failed - if ([DependencyErrorType.InterfaceHealthChecksFailed, DependencyErrorType.HealthChecksFailed].includes(error.type)) { - errorText = 'Health Check Failed' - // not fully installed (same as !localDep?.installed) - } else if (error.type === DependencyErrorType.NotInstalled) { - if (localDep) { - errorText = localDep.state // 'Installing' | 'Removing' - } else { - errorText = 'Not Installed' - actionText = 'Install' - action = () => this.fixDep('install', id) - } - // incorrect version - } else if (error.type === DependencyErrorType.IncorrectVersion) { - if (localDep) { - errorText = localDep.state // 'Updating' | 'Removing' - } else { - errorText = 'Incorrect Version' - actionText = 'Update' - action = () => this.fixDep('update', id) - } - // not running - } else if (error.type === DependencyErrorType.NotRunning) { - errorText = 'Not Running' - actionText = 'Start' - // config unsatisfied - } else if (error.type === DependencyErrorType.ConfigUnsatisfied) { - errorText = 'Config Not Satisfied' - actionText = 'Auto Config' - action = () => this.fixDep('configure', id) - } - - if (localDep && localDep.state !== PackageState.Installed) { - spinnerColor = localDep.state === PackageState.Removing ? 'danger' : 'primary' - } - } - - if (!this.dependencies[id]) this.dependencies[id] = { } as any - - const depInfo = this.pkg.installed['dependency-info'][id] - - this.dependencies[id].title = depInfo.manifest.title - this.dependencies[id].icon = depInfo.icon - this.dependencies[id].version = manifestDep.version - this.dependencies[id].errorText = errorText - this.dependencies[id].actionText = actionText - this.dependencies[id].spinnerColor = spinnerColor - this.dependencies[id].action = action - } - }) - // ** health - if (this.pkg.installed.status.main.status === PackageMainStatus.Running) { - this.healthChecks = { ...this.pkg.installed.status.main.health } - } else { - this.healthChecks = { } + if (localDep && localDep.state !== PackageState.Installed) { + spinnerColor = localDep.state === PackageState.Removing ? 'danger' : 'primary' } } + + if (!this.dependencies[id]) this.dependencies[id] = { } as any + + const depInfo = this.pkg.installed['dependency-info'][id] + + this.dependencies[id].title = depInfo.manifest.title + this.dependencies[id].icon = depInfo.icon + this.dependencies[id].version = version + this.dependencies[id].errorText = errorText + this.dependencies[id].actionText = actionText + this.dependencies[id].spinnerColor = spinnerColor + this.dependencies[id].action = action } private async installDep (depId: string): Promise { @@ -398,6 +411,7 @@ interface DependencyInfo { spinnerColor: string actionText: string action: () => any + sub: Subscription } interface Button { diff --git a/ui/src/app/services/pkg-status-rendering.service.ts b/ui/src/app/services/pkg-status-rendering.service.ts index 4238dab7d..2be75cd36 100644 --- a/ui/src/app/services/pkg-status-rendering.service.ts +++ b/ui/src/app/services/pkg-status-rendering.service.ts @@ -1,12 +1,11 @@ import { isEmptyObject } from '../util/misc.util' -import { PackageDataEntry, InstalledPackageDataEntry, PackageMainStatus, PackageState, Status } from './patch-db/data-model' +import { PackageDataEntry, PackageMainStatus, PackageState, Status } from './patch-db/data-model' export function renderPkgStatus (pkg: PackageDataEntry): { primary: PrimaryStatus, dependency: DependencyStatus | null, health: HealthStatus | null } { - console.log('PKGPKG', pkg) let primary: PrimaryStatus let dependency: DependencyStatus | null = null let health: HealthStatus | null = null @@ -92,10 +91,10 @@ export enum HealthStatus { export const PrimaryRendering: { [key: string]: StatusRendering } = { [PrimaryStatus.Installing]: { display: 'Installing', color: 'primary', showDots: true }, [PrimaryStatus.Updating]: { display: 'Updating', color: 'primary', showDots: true }, - [PrimaryStatus.Removing]: { display: 'Removing', color: 'warning', showDots: true }, + [PrimaryStatus.Removing]: { display: 'Removing', color: 'danger', showDots: true }, [PrimaryStatus.Stopping]: { display: 'Stopping', color: 'dark-shade', showDots: true }, [PrimaryStatus.Stopped]: { display: 'Stopped', color: 'dark-shade', showDots: false }, - [PrimaryStatus.BackingUp]: { display: 'Backing Up', color: 'warning', showDots: true }, + [PrimaryStatus.BackingUp]: { display: 'Backing Up', color: 'primary', showDots: true }, [PrimaryStatus.Restoring]: { display: 'Restoring', color: 'primary', showDots: true }, [PrimaryStatus.Running]: { display: 'Running', color: 'success', showDots: false }, }