finsih status refactor

This commit is contained in:
Matt Hill
2021-09-22 13:35:29 -06:00
committed by Aiden McClelland
parent 9a9403e51b
commit 5b3e445b53
5 changed files with 148 additions and 103 deletions

View File

@@ -42,7 +42,17 @@
<img *ngIf="!connectionFailure" [class]="pkg.value.bulb.class" [src]="pkg.value.bulb.img" />
<ion-card-header>
<status *ngIf="[PackageState.Installed, PackageState.Removing] | includes : pkg.value.entry.state" [disconnected]="connectionFailure" [rendering]="pkg.value.statusRendering" size="calc(8px + .4vw)" weight="bold"></status>
<ion-grid class="status-grid">
<ion-row class="ion-align-items-center">
<ion-col></ion-col>
<ion-col>
<status *ngIf="[PackageState.Installed, PackageState.Removing] | includes : pkg.value.entry.state" [disconnected]="connectionFailure" [rendering]="pkg.value.primaryRendering" size="calc(8px + .4vw)" weight="bold"></status>
</ion-col>
<ion-col>
<ion-icon *ngIf="pkg.value.error" name="warning-outline" color="warning"></ion-icon>
</ion-col>
</ion-row>
</ion-grid>
<p *ngIf="!!pkg.value.installProgress" class="main-status"><ion-text color="primary">{{ pkg.value.entry.state | titlecase }}...{{ pkg.value.installProgress.totalProgress }}%</ion-text></p>
<ion-card-title>{{ pkg.value.entry.manifest.title }}</ion-card-title>
</ion-card-header>

View File

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

View File

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

View File

@@ -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<void> {
@@ -398,6 +411,7 @@ interface DependencyInfo {
spinnerColor: string
actionText: string
action: () => any
sub: Subscription
}
interface Button {

View File

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