mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 20:14:49 +00:00
Rework PackageDataEntry for new strategy (#2573)
* rework PackageDataEntry for new strategy * fix type error * fix issues with manifest fetching * mock installs working
This commit is contained in:
@@ -8,30 +8,34 @@
|
||||
</ion-header>
|
||||
|
||||
<ion-content class="ion-padding-top with-widgets">
|
||||
<ion-item-group *ngIf="pkg$ | async as pkg">
|
||||
<!-- ** standard actions ** -->
|
||||
<ion-item-divider>Standard Actions</ion-item-divider>
|
||||
<app-actions-item
|
||||
[action]="{
|
||||
name: 'Uninstall',
|
||||
description: 'This will uninstall the service from StartOS and delete all data permanently.',
|
||||
icon: 'trash-outline'
|
||||
}"
|
||||
(click)="tryUninstall(pkg)"
|
||||
></app-actions-item>
|
||||
<ng-container *ngIf="pkg$ | async as pkg">
|
||||
<ion-item-group
|
||||
*ngIf="pkg['state-info'].state === 'installed' && pkg['state-info'].manifest as manifest"
|
||||
>
|
||||
<!-- ** standard actions ** -->
|
||||
<ion-item-divider>Standard Actions</ion-item-divider>
|
||||
<app-actions-item
|
||||
[action]="{
|
||||
name: 'Uninstall',
|
||||
description: 'This will uninstall the service from StartOS and delete all data permanently.',
|
||||
icon: 'trash-outline'
|
||||
}"
|
||||
(click)="tryUninstall(pkg)"
|
||||
></app-actions-item>
|
||||
|
||||
<!-- ** specific actions ** -->
|
||||
<ion-item-divider *ngIf="!(pkg.manifest.actions | empty)">
|
||||
Actions for {{ pkg.manifest.title }}
|
||||
</ion-item-divider>
|
||||
<app-actions-item
|
||||
*ngFor="let action of pkg.manifest.actions | keyvalue: asIsOrder"
|
||||
[action]="{
|
||||
name: action.value.name,
|
||||
description: action.value.description,
|
||||
icon: 'play-circle-outline'
|
||||
}"
|
||||
(click)="handleAction(pkg, action)"
|
||||
></app-actions-item>
|
||||
</ion-item-group>
|
||||
<!-- ** specific actions ** -->
|
||||
<ion-item-divider *ngIf="!(manifest.actions | empty)">
|
||||
Actions for {{ manifest.title }}
|
||||
</ion-item-divider>
|
||||
<app-actions-item
|
||||
*ngFor="let action of manifest.actions | keyvalue: asIsOrder"
|
||||
[action]="{
|
||||
name: action.value.name,
|
||||
description: action.value.description,
|
||||
icon: 'play-circle-outline'
|
||||
}"
|
||||
(click)="handleAction(pkg.status, action)"
|
||||
></app-actions-item>
|
||||
</ion-item-group>
|
||||
</ng-container>
|
||||
</ion-content>
|
||||
|
||||
@@ -11,13 +11,17 @@ import { PatchDB } from 'patch-db-client'
|
||||
import {
|
||||
Action,
|
||||
DataModel,
|
||||
InstalledState,
|
||||
PackageDataEntry,
|
||||
PackageMainStatus,
|
||||
StateInfo,
|
||||
Status,
|
||||
} from 'src/app/services/patch-db/data-model'
|
||||
import { GenericFormPage } from 'src/app/modals/generic-form/generic-form.page'
|
||||
import { isEmptyObject, ErrorToastService, getPkgId } from '@start9labs/shared'
|
||||
import { ActionSuccessPage } from 'src/app/modals/action-success/action-success.page'
|
||||
import { hasCurrentDeps } from 'src/app/util/has-deps'
|
||||
import { getManifest } from 'src/app/util/get-package-data'
|
||||
|
||||
@Component({
|
||||
selector: 'app-actions',
|
||||
@@ -40,11 +44,7 @@ export class AppActionsPage {
|
||||
private readonly patch: PatchDB<DataModel>,
|
||||
) {}
|
||||
|
||||
async handleAction(
|
||||
pkg: PackageDataEntry,
|
||||
action: { key: string; value: Action },
|
||||
) {
|
||||
const status = pkg.installed?.status
|
||||
async handleAction(status: Status, action: { key: string; value: Action }) {
|
||||
if (
|
||||
status &&
|
||||
(action.value['allowed-statuses'] as PackageMainStatus[]).includes(
|
||||
@@ -120,7 +120,7 @@ export class AppActionsPage {
|
||||
}
|
||||
|
||||
async tryUninstall(pkg: PackageDataEntry): Promise<void> {
|
||||
const { title, alerts } = pkg.manifest
|
||||
const { title, alerts } = getManifest(pkg)
|
||||
|
||||
let message =
|
||||
alerts.uninstall ||
|
||||
|
||||
@@ -29,7 +29,7 @@ export class AppInterfacesPage {
|
||||
readonly pkgId = getPkgId(this.route)
|
||||
|
||||
readonly serviceInterfaces$ = this.patch
|
||||
.watch$('package-data', this.pkgId, 'installed', 'service-interfaces')
|
||||
.watch$('package-data', this.pkgId, 'service-interfaces')
|
||||
.pipe(
|
||||
map(interfaces => {
|
||||
const sorted = Object.values(interfaces)
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
<ion-item
|
||||
button
|
||||
*ngIf="pkg.entry.manifest as manifest"
|
||||
*ngIf="pkg.entry | toManifest as manifest"
|
||||
detail="false"
|
||||
class="service-card"
|
||||
[routerLink]="['/services', manifest.id]"
|
||||
>
|
||||
<app-list-icon slot="start" [pkg]="pkg"></app-list-icon>
|
||||
<ion-thumbnail slot="start">
|
||||
<img alt="" [src]="pkg.entry['static-files'].icon" />
|
||||
<img alt="" [src]="pkg.entry.icon" />
|
||||
</ion-thumbnail>
|
||||
<ion-label>
|
||||
<h2 ticker>{{ manifest.title }}</h2>
|
||||
<p>{{ manifest.version | displayEmver }}</p>
|
||||
<status
|
||||
[rendering]="pkg.primaryRendering"
|
||||
[installProgress]="pkg.entry['install-progress']"
|
||||
[installingInfo]="$any(pkg.entry['state-info'])['installing-info']"
|
||||
weight="bold"
|
||||
size="small"
|
||||
[sigtermTimeout]="sigtermTimeout"
|
||||
></status>
|
||||
</ion-label>
|
||||
<ion-button
|
||||
*ngIf="
|
||||
pkg.entry.installed && (pkg.entry.installed['service-interfaces'] | hasUi)
|
||||
"
|
||||
*ngIf="pkg.entry['service-interfaces'] | hasUi"
|
||||
slot="end"
|
||||
fill="clear"
|
||||
color="primary"
|
||||
(click)="launchUi($event, pkg.entry.installed['service-interfaces'])"
|
||||
[disabled]="!(pkg.entry.state | isLaunchable: pkgMainStatus.status)"
|
||||
(click)="launchUi($event, pkg.entry['service-interfaces'])"
|
||||
[disabled]="
|
||||
!(pkg.entry['state-info'].state | isLaunchable: pkgMainStatus.status)
|
||||
"
|
||||
>
|
||||
<ion-icon slot="icon-only" name="open-outline"></ion-icon>
|
||||
</ion-button>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
|
||||
import {
|
||||
InstalledPackageDataEntry,
|
||||
MainStatus,
|
||||
PackageDataEntry,
|
||||
PackageMainStatus,
|
||||
} from 'src/app/services/patch-db/data-model'
|
||||
import { PkgInfo } from 'src/app/util/get-package-info'
|
||||
@@ -20,7 +20,7 @@ export class AppListPkgComponent {
|
||||
|
||||
get pkgMainStatus(): MainStatus {
|
||||
return (
|
||||
this.pkg.entry.installed?.status.main || {
|
||||
this.pkg.entry.status.main || {
|
||||
status: PackageMainStatus.Stopped,
|
||||
}
|
||||
)
|
||||
@@ -32,10 +32,7 @@ export class AppListPkgComponent {
|
||||
: null
|
||||
}
|
||||
|
||||
launchUi(
|
||||
e: Event,
|
||||
interfaces: InstalledPackageDataEntry['service-interfaces'],
|
||||
): void {
|
||||
launchUi(e: Event, interfaces: PackageDataEntry['service-interfaces']): void {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
this.launcherService.launch(interfaces)
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
sizeMd="6"
|
||||
>
|
||||
<app-list-pkg
|
||||
*ngIf="pkg.manifest.id | packageInfo | async as info"
|
||||
*ngIf="(pkg | toManifest).id | packageInfo | async as info"
|
||||
[pkg]="info"
|
||||
></app-list-pkg>
|
||||
</ion-col>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||
import { filter, map, pairwise, startWith } from 'rxjs/operators'
|
||||
import { getManifest } from 'src/app/util/get-package-data'
|
||||
|
||||
@Component({
|
||||
selector: 'app-list',
|
||||
@@ -20,7 +21,7 @@ export class AppListPage {
|
||||
}),
|
||||
map(([_, pkgs]) =>
|
||||
pkgs.sort((a, b) =>
|
||||
b.manifest.title.toLowerCase() > a.manifest.title.toLowerCase()
|
||||
getManifest(b).title.toLowerCase() > getManifest(a).title.toLowerCase()
|
||||
? -1
|
||||
: 1,
|
||||
),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core'
|
||||
import { Observable, combineLatest, firstValueFrom } from 'rxjs'
|
||||
import { Observable, combineLatest } from 'rxjs'
|
||||
import { filter, map } from 'rxjs/operators'
|
||||
import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||
import { getPackageInfo, PkgInfo } from '../../../util/get-package-info'
|
||||
|
||||
@@ -41,7 +41,7 @@ export class AppPropertiesPage {
|
||||
unmasked: { [key: string]: boolean } = {}
|
||||
|
||||
stopped$ = this.patch
|
||||
.watch$('package-data', this.pkgId, 'installed', 'status', 'main', 'status')
|
||||
.watch$('package-data', this.pkgId, 'status', 'main', 'status')
|
||||
.pipe(map(status => status === PackageMainStatus.Stopped))
|
||||
|
||||
@ViewChild(IonBackButtonDelegate, { static: false })
|
||||
|
||||
@@ -18,7 +18,7 @@ import { AppShowAdditionalComponent } from './components/app-show-additional/app
|
||||
import { HealthColorPipe } from './pipes/health-color.pipe'
|
||||
import { ToHealthChecksPipe } from './pipes/to-health-checks.pipe'
|
||||
import { ToButtonsPipe } from './pipes/to-buttons.pipe'
|
||||
import { ProgressDataPipe } from './pipes/progress-data.pipe'
|
||||
import { InstallingProgressPipeModule } from 'src/app/pipes/install-progress/install-progress.module'
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
@@ -31,7 +31,6 @@ const routes: Routes = [
|
||||
declarations: [
|
||||
AppShowPage,
|
||||
HealthColorPipe,
|
||||
ProgressDataPipe,
|
||||
ToHealthChecksPipe,
|
||||
ToButtonsPipe,
|
||||
AppShowHeaderComponent,
|
||||
@@ -44,7 +43,7 @@ const routes: Routes = [
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
StatusComponentModule,
|
||||
InstallingProgressPipeModule,
|
||||
IonicModule,
|
||||
RouterModule.forChild(routes),
|
||||
AppConfigPageModule,
|
||||
@@ -52,6 +51,7 @@ const routes: Routes = [
|
||||
LaunchablePipeModule,
|
||||
UiPipeModule,
|
||||
ResponsiveColModule,
|
||||
StatusComponentModule,
|
||||
],
|
||||
})
|
||||
export class AppShowPageModule {}
|
||||
|
||||
@@ -7,9 +7,8 @@
|
||||
<!-- ** installing, updating, restoring ** -->
|
||||
<ng-container *ngIf="showProgress(pkg); else installed">
|
||||
<app-show-progress
|
||||
*ngIf="pkg | progressData as progressData"
|
||||
[pkg]="pkg"
|
||||
[progressData]="progressData"
|
||||
*ngIf="pkg['state-info']['installing-info'] as installingInfo"
|
||||
[phases]="installingInfo.progress.phases"
|
||||
></app-show-progress>
|
||||
</ng-container>
|
||||
|
||||
@@ -19,11 +18,13 @@
|
||||
<!-- ** status ** -->
|
||||
<app-show-status [pkg]="pkg" [status]="status"></app-show-status>
|
||||
<!-- ** installed && !backing-up ** -->
|
||||
<ng-container *ngIf="isInstalled(pkg) && !isBackingUp(status)">
|
||||
<ng-container
|
||||
*ngIf="isInstalled(pkg) && status.primary !== 'backing-up'"
|
||||
>
|
||||
<!-- ** health checks ** -->
|
||||
<app-show-health-checks
|
||||
*ngIf="isRunning(status)"
|
||||
[pkg]="pkg"
|
||||
*ngIf="status.primary === 'running'"
|
||||
[manifest]="pkg['state-info'].manifest"
|
||||
></app-show-health-checks>
|
||||
<!-- ** dependencies ** -->
|
||||
<app-show-dependencies
|
||||
@@ -33,7 +34,9 @@
|
||||
<!-- ** menu ** -->
|
||||
<app-show-menu [buttons]="pkg | toButtons"></app-show-menu>
|
||||
<!-- ** additional ** -->
|
||||
<app-show-additional [pkg]="pkg"></app-show-additional>
|
||||
<app-show-additional
|
||||
[manifest]="pkg['state-info'].manifest"
|
||||
></app-show-additional>
|
||||
</ng-container>
|
||||
</ion-item-group>
|
||||
</ng-template>
|
||||
|
||||
@@ -3,16 +3,12 @@ import { NavController } from '@ionic/angular'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import {
|
||||
DataModel,
|
||||
InstalledPackageDataEntry,
|
||||
InstallingState,
|
||||
Manifest,
|
||||
PackageDataEntry,
|
||||
PackageState,
|
||||
UpdatingState,
|
||||
} from 'src/app/services/patch-db/data-model'
|
||||
import {
|
||||
PackageStatus,
|
||||
PrimaryStatus,
|
||||
renderPkgStatus,
|
||||
} from 'src/app/services/pkg-status-rendering.service'
|
||||
import { renderPkgStatus } from 'src/app/services/pkg-status-rendering.service'
|
||||
import { map, tap } from 'rxjs/operators'
|
||||
import { ActivatedRoute, NavigationExtras } from '@angular/router'
|
||||
import { getPkgId } from '@start9labs/shared'
|
||||
@@ -24,6 +20,13 @@ import {
|
||||
PkgDependencyErrors,
|
||||
} from 'src/app/services/dep-error.service'
|
||||
import { combineLatest } from 'rxjs'
|
||||
import {
|
||||
getManifest,
|
||||
isInstalled,
|
||||
isInstalling,
|
||||
isRestoring,
|
||||
isUpdating,
|
||||
} from 'src/app/util/get-package-data'
|
||||
|
||||
export interface DependencyInfo {
|
||||
id: string
|
||||
@@ -35,12 +38,6 @@ export interface DependencyInfo {
|
||||
action: () => any
|
||||
}
|
||||
|
||||
const STATES = [
|
||||
PackageState.Installing,
|
||||
PackageState.Updating,
|
||||
PackageState.Restoring,
|
||||
]
|
||||
|
||||
@Component({
|
||||
selector: 'app-show',
|
||||
templateUrl: './app-show.page.html',
|
||||
@@ -66,6 +63,8 @@ export class AppShowPage {
|
||||
}),
|
||||
)
|
||||
|
||||
isInstalled = isInstalled
|
||||
|
||||
constructor(
|
||||
private readonly route: ActivatedRoute,
|
||||
private readonly navCtrl: NavController,
|
||||
@@ -74,55 +73,44 @@ export class AppShowPage {
|
||||
private readonly depErrorService: DepErrorService,
|
||||
) {}
|
||||
|
||||
isInstalled({ state }: PackageDataEntry): boolean {
|
||||
return state === PackageState.Installed
|
||||
}
|
||||
|
||||
isRunning({ primary }: PackageStatus): boolean {
|
||||
return primary === PrimaryStatus.Running
|
||||
}
|
||||
|
||||
isBackingUp({ primary }: PackageStatus): boolean {
|
||||
return primary === PrimaryStatus.BackingUp
|
||||
}
|
||||
|
||||
showProgress({ state }: PackageDataEntry): boolean {
|
||||
return STATES.includes(state)
|
||||
showProgress(
|
||||
pkg: PackageDataEntry,
|
||||
): pkg is PackageDataEntry<InstallingState | UpdatingState> {
|
||||
return isInstalling(pkg) || isUpdating(pkg) || isRestoring(pkg)
|
||||
}
|
||||
|
||||
private getDepInfo(
|
||||
pkg: PackageDataEntry,
|
||||
depErrors: PkgDependencyErrors,
|
||||
): DependencyInfo[] {
|
||||
const pkgInstalled = pkg.installed
|
||||
const manifest = getManifest(pkg)
|
||||
|
||||
if (!pkgInstalled) return []
|
||||
|
||||
return Object.keys(pkgInstalled['current-dependencies'])
|
||||
.filter(id => !!pkgInstalled.manifest.dependencies[id])
|
||||
.map(id => this.getDepValues(pkgInstalled, id, depErrors))
|
||||
return Object.keys(pkg['current-dependencies'])
|
||||
.filter(id => !!manifest.dependencies[id])
|
||||
.map(id => this.getDepValues(pkg, manifest, id, depErrors))
|
||||
}
|
||||
|
||||
private getDepValues(
|
||||
pkgInstalled: InstalledPackageDataEntry,
|
||||
pkg: PackageDataEntry,
|
||||
manifest: Manifest,
|
||||
depId: string,
|
||||
depErrors: PkgDependencyErrors,
|
||||
): DependencyInfo {
|
||||
const { errorText, fixText, fixAction } = this.getDepErrors(
|
||||
pkgInstalled,
|
||||
manifest,
|
||||
depId,
|
||||
depErrors,
|
||||
)
|
||||
|
||||
const depInfo = pkgInstalled['dependency-info'][depId]
|
||||
const depInfo = pkg['dependency-info'][depId]
|
||||
|
||||
return {
|
||||
id: depId,
|
||||
version: pkgInstalled.manifest.dependencies[depId].version, // do we want this version range?
|
||||
version: manifest.dependencies[depId].version, // do we want this version range?
|
||||
title: depInfo?.title || depId,
|
||||
icon: depInfo?.icon || '',
|
||||
errorText: errorText
|
||||
? `${errorText}. ${pkgInstalled.manifest.title} will not work as expected.`
|
||||
? `${errorText}. ${manifest.title} will not work as expected.`
|
||||
: '',
|
||||
actionText: fixText || 'View',
|
||||
action:
|
||||
@@ -131,11 +119,10 @@ export class AppShowPage {
|
||||
}
|
||||
|
||||
private getDepErrors(
|
||||
pkgInstalled: InstalledPackageDataEntry,
|
||||
manifest: Manifest,
|
||||
depId: string,
|
||||
depErrors: PkgDependencyErrors,
|
||||
) {
|
||||
const pkgManifest = pkgInstalled.manifest
|
||||
const depError = depErrors[depId]
|
||||
|
||||
let errorText: string | null = null
|
||||
@@ -146,15 +133,15 @@ export class AppShowPage {
|
||||
if (depError.type === DependencyErrorType.NotInstalled) {
|
||||
errorText = 'Not installed'
|
||||
fixText = 'Install'
|
||||
fixAction = () => this.fixDep(pkgManifest, 'install', depId)
|
||||
fixAction = () => this.fixDep(manifest, 'install', depId)
|
||||
} else if (depError.type === DependencyErrorType.IncorrectVersion) {
|
||||
errorText = 'Incorrect version'
|
||||
fixText = 'Update'
|
||||
fixAction = () => this.fixDep(pkgManifest, 'update', depId)
|
||||
fixAction = () => this.fixDep(manifest, 'update', depId)
|
||||
} else if (depError.type === DependencyErrorType.ConfigUnsatisfied) {
|
||||
errorText = 'Config not satisfied'
|
||||
fixText = 'Auto config'
|
||||
fixAction = () => this.fixDep(pkgManifest, 'configure', depId)
|
||||
fixAction = () => this.fixDep(manifest, 'configure', depId)
|
||||
} else if (depError.type === DependencyErrorType.NotRunning) {
|
||||
errorText = 'Not running'
|
||||
fixText = 'Start'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<ion-item-divider>Additional Info</ion-item-divider>
|
||||
<ion-grid *ngIf="pkg.manifest as manifest">
|
||||
<ion-grid>
|
||||
<ion-row>
|
||||
<ion-col responsiveCol sizeXs="12" sizeMd="6">
|
||||
<ion-item-group>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { ModalController, ToastController } from '@ionic/angular'
|
||||
import { copyToClipboard, MarkdownComponent } from '@start9labs/shared'
|
||||
import { from } from 'rxjs'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
||||
import { Manifest } from 'src/app/services/patch-db/data-model'
|
||||
|
||||
@Component({
|
||||
selector: 'app-show-additional',
|
||||
@@ -12,7 +12,7 @@ import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
||||
})
|
||||
export class AppShowAdditionalComponent {
|
||||
@Input()
|
||||
pkg!: PackageDataEntry
|
||||
manifest!: Manifest
|
||||
|
||||
constructor(
|
||||
private readonly modalCtrl: ModalController,
|
||||
@@ -35,10 +35,16 @@ export class AppShowAdditionalComponent {
|
||||
}
|
||||
|
||||
async presentModalLicense() {
|
||||
const { id, version } = this.manifest
|
||||
|
||||
const modal = await this.modalCtrl.create({
|
||||
componentProps: {
|
||||
title: 'License',
|
||||
content: from(this.api.getStatic(this.pkg['static-files']['license'])),
|
||||
content: from(
|
||||
this.api.getStatic(
|
||||
`/public/package-data/${id}/${version}/LICENSE.md`,
|
||||
),
|
||||
),
|
||||
},
|
||||
component: MarkdownComponent,
|
||||
})
|
||||
|
||||
@@ -4,15 +4,12 @@
|
||||
<ion-back-button defaultHref="services"></ion-back-button>
|
||||
</ion-buttons>
|
||||
<div class="header">
|
||||
<img class="logo" [src]="pkg['static-files'].icon" alt="" />
|
||||
<ion-label>
|
||||
<h1
|
||||
class="montserrat"
|
||||
[class.less-large]="pkg.manifest.title.length > 20"
|
||||
>
|
||||
{{ pkg.manifest.title }}
|
||||
<img class="logo" [src]="pkg.icon" alt="" />
|
||||
<ion-label *ngIf="pkg | toManifest as manifest">
|
||||
<h1 class="montserrat" [class.less-large]="manifest.title.length > 20">
|
||||
{{ manifest.title }}
|
||||
</h1>
|
||||
<h2>{{ pkg.manifest.version | displayEmver }}</h2>
|
||||
<h2>{{ manifest.version | displayEmver }}</h2>
|
||||
</ion-label>
|
||||
</div>
|
||||
</ion-toolbar>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<ng-container
|
||||
*ngIf="pkg | toHealthChecks | async | keyvalue: asIsOrder as checks"
|
||||
*ngIf="manifest | toHealthChecks | async | keyvalue: asIsOrder as checks"
|
||||
>
|
||||
<ng-container *ngIf="checks.length">
|
||||
<ion-item-divider>Health Checks</ion-item-divider>
|
||||
@@ -34,7 +34,7 @@
|
||||
></ion-icon>
|
||||
<ion-label>
|
||||
<h2 class="bold">
|
||||
{{ pkg.manifest['health-checks'][health.key].name }}
|
||||
{{ manifest['health-checks'][health.key].name }}
|
||||
</h2>
|
||||
<ion-text [color]="result | healthColor">
|
||||
<p>
|
||||
@@ -49,13 +49,11 @@
|
||||
<span
|
||||
*ngIf="
|
||||
result === HealthResult.Success &&
|
||||
pkg.manifest['health-checks'][health.key]['success-message']
|
||||
manifest['health-checks'][health.key]['success-message']
|
||||
"
|
||||
>
|
||||
:
|
||||
{{
|
||||
pkg.manifest['health-checks'][health.key]['success-message']
|
||||
}}
|
||||
{{ manifest['health-checks'][health.key]['success-message'] }}
|
||||
</span>
|
||||
</p>
|
||||
</ion-text>
|
||||
@@ -70,7 +68,7 @@
|
||||
></ion-spinner>
|
||||
<ion-label>
|
||||
<h2 class="bold">
|
||||
{{ pkg.manifest['health-checks'][health.key].name }}
|
||||
{{ manifest['health-checks'][health.key].name }}
|
||||
</h2>
|
||||
<p class="primary">Awaiting result...</p>
|
||||
</ion-label>
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
|
||||
import { ConnectionService } from 'src/app/services/connection.service'
|
||||
import {
|
||||
HealthResult,
|
||||
PackageDataEntry,
|
||||
} from 'src/app/services/patch-db/data-model'
|
||||
import { HealthResult, Manifest } from 'src/app/services/patch-db/data-model'
|
||||
|
||||
@Component({
|
||||
selector: 'app-show-health-checks',
|
||||
@@ -13,7 +10,7 @@ import {
|
||||
})
|
||||
export class AppShowHealthChecksComponent {
|
||||
@Input()
|
||||
pkg!: PackageDataEntry
|
||||
manifest!: Manifest
|
||||
|
||||
HealthResult = HealthResult
|
||||
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
<p>Downloading: {{ progressData.downloadProgress }}%</p>
|
||||
<ion-progress-bar
|
||||
[color]="getColor('download-complete')"
|
||||
[value]="progressData.downloadProgress / 100"
|
||||
[buffer]="!progressData.downloadProgress ? 0 : 1"
|
||||
></ion-progress-bar>
|
||||
|
||||
<p>Validating: {{ progressData.validateProgress }}%</p>
|
||||
<ion-progress-bar
|
||||
[color]="getColor('validation-complete')"
|
||||
[value]="progressData.validateProgress / 100"
|
||||
[buffer]="validationBuffer"
|
||||
></ion-progress-bar>
|
||||
|
||||
<p>Unpacking: {{ progressData.unpackProgress }}%</p>
|
||||
<ion-progress-bar
|
||||
[color]="getColor('unpack-complete')"
|
||||
[value]="progressData.unpackProgress / 100"
|
||||
[buffer]="unpackingBuffer"
|
||||
></ion-progress-bar>
|
||||
<ng-container *ngFor="let phase of phases">
|
||||
<p>
|
||||
{{ phase.name }}
|
||||
<span *ngIf="phase.progress | installingProgress as progress">
|
||||
: {{ progress * 100 }}%
|
||||
</span>
|
||||
</p>
|
||||
<ion-progress-bar
|
||||
[type]="
|
||||
phase.progress === true ||
|
||||
(phase.progress !== false && phase.progress.total)
|
||||
? 'determinate'
|
||||
: 'indeterminate'
|
||||
"
|
||||
[color]="phase.progress === true ? 'success' : 'secondary'"
|
||||
[value]="phase.progress | installingProgress"
|
||||
></ion-progress-bar>
|
||||
</ng-container>
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
|
||||
import {
|
||||
InstallProgress,
|
||||
PackageDataEntry,
|
||||
} from 'src/app/services/patch-db/data-model'
|
||||
import { ProgressData } from 'src/app/types/progress-data'
|
||||
import { FullProgress } from 'src/app/services/patch-db/data-model'
|
||||
|
||||
@Component({
|
||||
selector: 'app-show-progress',
|
||||
@@ -13,26 +9,5 @@ import { ProgressData } from 'src/app/types/progress-data'
|
||||
})
|
||||
export class AppShowProgressComponent {
|
||||
@Input()
|
||||
pkg!: PackageDataEntry
|
||||
|
||||
@Input()
|
||||
progressData!: ProgressData
|
||||
|
||||
get unpackingBuffer(): number {
|
||||
return this.progressData.validateProgress === 100 &&
|
||||
!this.progressData.unpackProgress
|
||||
? 0
|
||||
: 1
|
||||
}
|
||||
|
||||
get validationBuffer(): number {
|
||||
return this.progressData.downloadProgress === 100 &&
|
||||
!this.progressData.validateProgress
|
||||
? 0
|
||||
: 1
|
||||
}
|
||||
|
||||
getColor(action: keyof InstallProgress): string {
|
||||
return this.pkg['install-progress']?.[action] ? 'success' : 'secondary'
|
||||
}
|
||||
phases!: FullProgress['phases']
|
||||
}
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
<status
|
||||
size="x-large"
|
||||
weight="600"
|
||||
[installProgress]="pkg['install-progress']"
|
||||
[installingInfo]="$any(pkg['state-info'])['installing-info']"
|
||||
[rendering]="PR[status.primary]"
|
||||
[sigtermTimeout]="sigtermTimeout"
|
||||
></status>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<ng-container *ngIf="isInstalled && (connected$ | async)">
|
||||
<ng-container *ngIf="isInstalled(pkg) && (connected$ | async)">
|
||||
<ion-grid>
|
||||
<ion-row style="padding-left: 12px">
|
||||
<ion-col>
|
||||
@@ -59,7 +59,9 @@
|
||||
*ngIf="pkgStatus && interfaces && (interfaces | hasUi)"
|
||||
class="action-button"
|
||||
color="primary"
|
||||
[disabled]="!(pkg.state | isLaunchable: pkgStatus.main.status)"
|
||||
[disabled]="
|
||||
!(pkg['state-info'].state | isLaunchable: pkgStatus.main.status)
|
||||
"
|
||||
(click)="launchUi(interfaces)"
|
||||
>
|
||||
<ion-icon slot="start" name="open-outline"></ion-icon>
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
PrimaryStatus,
|
||||
} from 'src/app/services/pkg-status-rendering.service'
|
||||
import {
|
||||
InstalledPackageDataEntry,
|
||||
Manifest,
|
||||
PackageDataEntry,
|
||||
PackageMainStatus,
|
||||
PackageState,
|
||||
@@ -18,6 +18,7 @@ import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { ModalService } from 'src/app/services/modal.service'
|
||||
import { hasCurrentDeps } from 'src/app/util/has-deps'
|
||||
import { ConnectionService } from 'src/app/services/connection.service'
|
||||
import { isInstalled, getManifest } from 'src/app/util/get-package-data'
|
||||
|
||||
@Component({
|
||||
selector: 'app-show-status',
|
||||
@@ -34,6 +35,8 @@ export class AppShowStatusComponent {
|
||||
|
||||
PR = PrimaryRendering
|
||||
|
||||
isInstalled = isInstalled
|
||||
|
||||
readonly connected$ = this.connectionService.connected$
|
||||
|
||||
constructor(
|
||||
@@ -46,18 +49,16 @@ export class AppShowStatusComponent {
|
||||
private readonly connectionService: ConnectionService,
|
||||
) {}
|
||||
|
||||
get interfaces():
|
||||
| InstalledPackageDataEntry['service-interfaces']
|
||||
| undefined {
|
||||
return this.pkg.installed?.['service-interfaces']
|
||||
get interfaces(): PackageDataEntry['service-interfaces'] {
|
||||
return this.pkg['service-interfaces']
|
||||
}
|
||||
|
||||
get pkgStatus(): Status | null {
|
||||
return this.pkg.installed?.status || null
|
||||
get pkgStatus(): Status {
|
||||
return this.pkg.status
|
||||
}
|
||||
|
||||
get isInstalled(): boolean {
|
||||
return this.pkg.state === PackageState.Installed
|
||||
get manifest(): Manifest {
|
||||
return getManifest(this.pkg)
|
||||
}
|
||||
|
||||
get isRunning(): boolean {
|
||||
@@ -82,25 +83,25 @@ export class AppShowStatusComponent {
|
||||
: null
|
||||
}
|
||||
|
||||
launchUi(interfaces: InstalledPackageDataEntry['service-interfaces']): void {
|
||||
launchUi(interfaces: PackageDataEntry['service-interfaces']): void {
|
||||
this.launcherService.launch(interfaces)
|
||||
}
|
||||
|
||||
async presentModalConfig(): Promise<void> {
|
||||
return this.modalService.presentModalConfig({
|
||||
pkgId: this.id,
|
||||
pkgId: this.manifest.id,
|
||||
})
|
||||
}
|
||||
|
||||
async tryStart(): Promise<void> {
|
||||
if (this.status.dependency === 'warning') {
|
||||
const depErrMsg = `${this.pkg.manifest.title} has unmet dependencies. It will not work as expected.`
|
||||
const depErrMsg = `${this.manifest.title} has unmet dependencies. It will not work as expected.`
|
||||
const proceed = await this.presentAlertStart(depErrMsg)
|
||||
|
||||
if (!proceed) return
|
||||
}
|
||||
|
||||
const alertMsg = this.pkg.manifest.alerts.start
|
||||
const alertMsg = this.manifest.alerts.start
|
||||
|
||||
if (alertMsg) {
|
||||
const proceed = await this.presentAlertStart(alertMsg)
|
||||
@@ -112,7 +113,7 @@ export class AppShowStatusComponent {
|
||||
}
|
||||
|
||||
async tryStop(): Promise<void> {
|
||||
const { title, alerts } = this.pkg.manifest
|
||||
const { title, alerts } = this.manifest
|
||||
|
||||
let message = alerts.stop || ''
|
||||
if (hasCurrentDeps(this.pkg)) {
|
||||
@@ -150,7 +151,7 @@ export class AppShowStatusComponent {
|
||||
if (hasCurrentDeps(this.pkg)) {
|
||||
const alert = await this.alertCtrl.create({
|
||||
header: 'Warning',
|
||||
message: `Services that depend on ${this.pkg.manifest.title} may temporarily experiences issues`,
|
||||
message: `Services that depend on ${this.manifest.title} may temporarily experiences issues`,
|
||||
buttons: [
|
||||
{
|
||||
text: 'Cancel',
|
||||
@@ -173,10 +174,6 @@ export class AppShowStatusComponent {
|
||||
}
|
||||
}
|
||||
|
||||
private get id(): string {
|
||||
return this.pkg.manifest.id
|
||||
}
|
||||
|
||||
private async start(): Promise<void> {
|
||||
const loader = await this.loadingCtrl.create({
|
||||
message: `Starting...`,
|
||||
@@ -184,7 +181,7 @@ export class AppShowStatusComponent {
|
||||
await loader.present()
|
||||
|
||||
try {
|
||||
await this.embassyApi.startPackage({ id: this.id })
|
||||
await this.embassyApi.startPackage({ id: this.manifest.id })
|
||||
} catch (e: any) {
|
||||
this.errToast.present(e)
|
||||
} finally {
|
||||
@@ -199,7 +196,7 @@ export class AppShowStatusComponent {
|
||||
await loader.present()
|
||||
|
||||
try {
|
||||
await this.embassyApi.stopPackage({ id: this.id })
|
||||
await this.embassyApi.stopPackage({ id: this.manifest.id })
|
||||
} catch (e: any) {
|
||||
this.errToast.present(e)
|
||||
} finally {
|
||||
@@ -214,7 +211,7 @@ export class AppShowStatusComponent {
|
||||
await loader.present()
|
||||
|
||||
try {
|
||||
await this.embassyApi.restartPackage({ id: this.id })
|
||||
await this.embassyApi.restartPackage({ id: this.manifest.id })
|
||||
} catch (e: any) {
|
||||
this.errToast.present(e)
|
||||
} finally {
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core'
|
||||
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
||||
import { ProgressData } from 'src/app/types/progress-data'
|
||||
import { packageLoadingProgress } from 'src/app/util/package-loading-progress'
|
||||
|
||||
@Pipe({
|
||||
name: 'progressData',
|
||||
})
|
||||
export class ProgressDataPipe implements PipeTransform {
|
||||
transform(pkg: PackageDataEntry): ProgressData | null {
|
||||
return packageLoadingProgress(pkg['install-progress'])
|
||||
}
|
||||
}
|
||||
@@ -4,12 +4,15 @@ import { ModalController, NavController } from '@ionic/angular'
|
||||
import { MarkdownComponent } from '@start9labs/shared'
|
||||
import {
|
||||
DataModel,
|
||||
InstalledState,
|
||||
Manifest,
|
||||
PackageDataEntry,
|
||||
} from 'src/app/services/patch-db/data-model'
|
||||
import { ModalService } from 'src/app/services/modal.service'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { from, map, Observable } from 'rxjs'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import { getManifest } from 'src/app/util/get-package-data'
|
||||
|
||||
export interface Button {
|
||||
title: string
|
||||
@@ -33,26 +36,26 @@ export class ToButtonsPipe implements PipeTransform {
|
||||
private readonly patch: PatchDB<DataModel>,
|
||||
) {}
|
||||
|
||||
transform(pkg: PackageDataEntry): Button[] {
|
||||
const pkgTitle = pkg.manifest.title
|
||||
transform(pkg: PackageDataEntry<InstalledState>): Button[] {
|
||||
const manifest = pkg['state-info'].manifest
|
||||
|
||||
return [
|
||||
// instructions
|
||||
{
|
||||
action: () => this.presentModalInstructions(pkg),
|
||||
action: () => this.presentModalInstructions(manifest),
|
||||
title: 'Instructions',
|
||||
description: `Understand how to use ${pkgTitle}`,
|
||||
description: `Understand how to use ${manifest.title}`,
|
||||
icon: 'list-outline',
|
||||
highlighted$: this.patch
|
||||
.watch$('ui', 'ack-instructions', pkg.manifest.id)
|
||||
.watch$('ui', 'ack-instructions', manifest.id)
|
||||
.pipe(map(seen => !seen)),
|
||||
},
|
||||
// config
|
||||
{
|
||||
action: async () =>
|
||||
this.modalService.presentModalConfig({ pkgId: pkg.manifest.id }),
|
||||
this.modalService.presentModalConfig({ pkgId: manifest.id }),
|
||||
title: 'Config',
|
||||
description: `Customize ${pkgTitle}`,
|
||||
description: `Customize ${manifest.title}`,
|
||||
icon: 'options-outline',
|
||||
},
|
||||
// properties
|
||||
@@ -71,7 +74,7 @@ export class ToButtonsPipe implements PipeTransform {
|
||||
action: () =>
|
||||
this.navCtrl.navigateForward(['actions'], { relativeTo: this.route }),
|
||||
title: 'Actions',
|
||||
description: `Uninstall and other commands specific to ${pkgTitle}`,
|
||||
description: `Uninstall and other commands specific to ${manifest.title}`,
|
||||
icon: 'flash-outline',
|
||||
},
|
||||
// interfaces
|
||||
@@ -97,16 +100,18 @@ export class ToButtonsPipe implements PipeTransform {
|
||||
]
|
||||
}
|
||||
|
||||
private async presentModalInstructions(pkg: PackageDataEntry) {
|
||||
private async presentModalInstructions(manifest: Manifest) {
|
||||
this.apiService
|
||||
.setDbValue<boolean>(['ack-instructions', pkg.manifest.id], true)
|
||||
.setDbValue<boolean>(['ack-instructions', manifest.id], true)
|
||||
.catch(e => console.error('Failed to mark instructions as seen', e))
|
||||
|
||||
const modal = await this.modalCtrl.create({
|
||||
componentProps: {
|
||||
title: 'Instructions',
|
||||
content: from(
|
||||
this.apiService.getStatic(pkg['static-files']['instructions']),
|
||||
this.apiService.getStatic(
|
||||
`/public/package-data/${manifest.id}/${manifest.version}/INSTRUCTIONS.md`,
|
||||
),
|
||||
),
|
||||
},
|
||||
component: MarkdownComponent,
|
||||
@@ -115,17 +120,22 @@ export class ToButtonsPipe implements PipeTransform {
|
||||
await modal.present()
|
||||
}
|
||||
|
||||
private viewInMarketplaceButton(pkg: PackageDataEntry): Button {
|
||||
const url = pkg.installed?.['marketplace-url']
|
||||
private viewInMarketplaceButton(
|
||||
pkg: PackageDataEntry<InstalledState>,
|
||||
): Button {
|
||||
const url = pkg['marketplace-url']
|
||||
const queryParams = url ? { url } : {}
|
||||
|
||||
let button: Button = {
|
||||
title: 'Marketplace Listing',
|
||||
icon: 'storefront-outline',
|
||||
action: () =>
|
||||
this.navCtrl.navigateForward([`marketplace/${pkg.manifest.id}`], {
|
||||
queryParams,
|
||||
}),
|
||||
this.navCtrl.navigateForward(
|
||||
[`marketplace/${pkg['state-info'].manifest.id}`],
|
||||
{
|
||||
queryParams,
|
||||
},
|
||||
),
|
||||
disabled: false,
|
||||
description: 'View service in the marketplace',
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Pipe, PipeTransform } from '@angular/core'
|
||||
import {
|
||||
DataModel,
|
||||
HealthCheckResult,
|
||||
PackageDataEntry,
|
||||
Manifest,
|
||||
PackageMainStatus,
|
||||
} from 'src/app/services/patch-db/data-model'
|
||||
import { isEmptyObject } from '@start9labs/shared'
|
||||
@@ -17,15 +17,15 @@ export class ToHealthChecksPipe implements PipeTransform {
|
||||
constructor(private readonly patch: PatchDB<DataModel>) {}
|
||||
|
||||
transform(
|
||||
pkg: PackageDataEntry,
|
||||
manifest: Manifest,
|
||||
): Observable<Record<string, HealthCheckResult | null>> | null {
|
||||
const healthChecks = Object.keys(pkg.manifest['health-checks']).reduce(
|
||||
const healthChecks = Object.keys(manifest['health-checks']).reduce(
|
||||
(obj, key) => ({ ...obj, [key]: null }),
|
||||
{},
|
||||
)
|
||||
|
||||
const healthChecks$ = this.patch
|
||||
.watch$('package-data', pkg.manifest.id, 'installed', 'status', 'main')
|
||||
.watch$('package-data', manifest.id, 'status', 'main')
|
||||
.pipe(
|
||||
map(main => {
|
||||
// Question: is this ok or do we have to use Object.keys
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
View Installed
|
||||
</ion-button>
|
||||
<ng-container *ngIf="localPkg; else install">
|
||||
<ng-container *ngIf="localPkg.state === PackageState.Installed">
|
||||
<ng-container *ngIf="localPkg['state-info'].state === 'installed'">
|
||||
<ion-button
|
||||
*ngIf="(localVersion | compareEmver: pkg.manifest.version) === -1"
|
||||
expand="block"
|
||||
|
||||
@@ -24,7 +24,7 @@ import { ClientStorageService } from 'src/app/services/client-storage.service'
|
||||
import { MarketplaceService } from 'src/app/services/marketplace.service'
|
||||
import { hasCurrentDeps } from 'src/app/util/has-deps'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import { getAllPackages } from 'src/app/util/get-package-data'
|
||||
import { getAllPackages, getManifest } from 'src/app/util/get-package-data'
|
||||
import { firstValueFrom } from 'rxjs'
|
||||
import { dryUpdate } from 'src/app/util/dry-update'
|
||||
|
||||
@@ -46,8 +46,6 @@ export class MarketplaceShowControlsComponent {
|
||||
|
||||
readonly showDevTools$ = this.ClientStorageService.showDevTools$
|
||||
|
||||
readonly PackageState = PackageState
|
||||
|
||||
constructor(
|
||||
private readonly alertCtrl: AlertController,
|
||||
private readonly ClientStorageService: ClientStorageService,
|
||||
@@ -60,7 +58,7 @@ export class MarketplaceShowControlsComponent {
|
||||
) {}
|
||||
|
||||
get localVersion(): string {
|
||||
return this.localPkg?.manifest.version || ''
|
||||
return this.localPkg ? getManifest(this.localPkg).version : ''
|
||||
}
|
||||
|
||||
async tryInstall() {
|
||||
@@ -72,7 +70,7 @@ export class MarketplaceShowControlsComponent {
|
||||
if (!this.localPkg) {
|
||||
this.alertInstall(url)
|
||||
} else {
|
||||
const originalUrl = this.localPkg.installed?.['marketplace-url']
|
||||
const originalUrl = this.localPkg['marketplace-url']
|
||||
|
||||
if (!sameUrl(url, originalUrl)) {
|
||||
const proceed = await this.presentAlertDifferentMarketplace(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<ng-container *ngIf="localPkg" [ngSwitch]="localPkg.state">
|
||||
<div *ngSwitchCase="PackageState.Installed">
|
||||
<ng-container *ngIf="localPkg">
|
||||
<div *ngIf="isInstalled(localPkg)">
|
||||
<ion-text
|
||||
*ngIf="(version | compareEmver: localVersion) !== 1"
|
||||
color="primary"
|
||||
@@ -13,15 +13,24 @@
|
||||
Update Available
|
||||
</ion-text>
|
||||
</div>
|
||||
<div *ngSwitchCase="PackageState.Removing">
|
||||
|
||||
<div *ngIf="isRemoving(localPkg)">
|
||||
<ion-text color="danger">
|
||||
Removing
|
||||
<span class="loading-dots"></span>
|
||||
</ion-text>
|
||||
</div>
|
||||
<div *ngSwitchDefault>
|
||||
|
||||
<div
|
||||
*ngIf="
|
||||
isInstalling(localPkg) || isUpdating(localPkg) || isRestoring(localPkg)
|
||||
"
|
||||
>
|
||||
<ion-text
|
||||
*ngIf="localPkg['install-progress'] | installProgressDisplay as progress"
|
||||
*ngIf="
|
||||
localPkg['state-info']['installing-info']!.progress.overall
|
||||
| installingProgressString as progress
|
||||
"
|
||||
color="primary"
|
||||
>
|
||||
Installing
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
import { Component, Input } from '@angular/core'
|
||||
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
||||
import {
|
||||
PackageDataEntry,
|
||||
PackageState,
|
||||
} from 'src/app/services/patch-db/data-model'
|
||||
isInstalled,
|
||||
isInstalling,
|
||||
isUpdating,
|
||||
isRemoving,
|
||||
isRestoring,
|
||||
getManifest,
|
||||
} from 'src/app/util/get-package-data'
|
||||
|
||||
@Component({
|
||||
selector: 'marketplace-status',
|
||||
@@ -14,9 +19,13 @@ export class MarketplaceStatusComponent {
|
||||
|
||||
@Input() localPkg?: PackageDataEntry
|
||||
|
||||
PackageState = PackageState
|
||||
isInstalled = isInstalled
|
||||
isInstalling = isInstalling
|
||||
isUpdating = isUpdating
|
||||
isRemoving = isRemoving
|
||||
isRestoring = isRestoring
|
||||
|
||||
get localVersion(): string {
|
||||
return this.localPkg?.manifest.version || ''
|
||||
return this.localPkg ? getManifest(this.localPkg).version : ''
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,7 @@ import { CommonModule } from '@angular/common'
|
||||
import { NgModule } from '@angular/core'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { EmverPipesModule } from '@start9labs/shared'
|
||||
|
||||
import { InstallProgressPipeModule } from '../../../pipes/install-progress/install-progress.module'
|
||||
import { InstallingProgressPipeModule } from '../../../pipes/install-progress/install-progress.module'
|
||||
import { MarketplaceStatusComponent } from './marketplace-status.component'
|
||||
|
||||
@NgModule({
|
||||
@@ -11,7 +10,7 @@ import { MarketplaceStatusComponent } from './marketplace-status.component'
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
EmverPipesModule,
|
||||
InstallProgressPipeModule,
|
||||
InstallingProgressPipeModule,
|
||||
],
|
||||
declarations: [MarketplaceStatusComponent],
|
||||
exports: [MarketplaceStatusComponent],
|
||||
|
||||
@@ -6,6 +6,7 @@ import { NotificationsPage } from './notifications.page'
|
||||
import { BadgeMenuComponentModule } from 'src/app/components/badge-menu-button/badge-menu.component.module'
|
||||
import { SharedPipesModule } from '@start9labs/shared'
|
||||
import { BackupReportPageModule } from 'src/app/modals/backup-report/backup-report.module'
|
||||
import { UiPipeModule } from 'src/app/pipes/ui/ui.module'
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
@@ -22,6 +23,7 @@ const routes: Routes = [
|
||||
BadgeMenuComponentModule,
|
||||
SharedPipesModule,
|
||||
BackupReportPageModule,
|
||||
UiPipeModule,
|
||||
],
|
||||
declarations: [NotificationsPage],
|
||||
})
|
||||
|
||||
@@ -87,8 +87,8 @@
|
||||
<h2>
|
||||
<b>
|
||||
<span *ngIf="not['package-id'] as pkgId">
|
||||
<!-- @TODO remove $any when Angular gets smart enough -->
|
||||
{{ $any(packageData[pkgId])?.manifest.title || pkgId }} -
|
||||
{{ packageData[pkgId] ? (packageData[pkgId] |
|
||||
toManifest).title : pkgId }} -
|
||||
</span>
|
||||
<ion-text [color]="getColor(not)">{{ not.title }}</ion-text>
|
||||
</b>
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
<ng-container *ngFor="let pkg of pkgs | keyvalue">
|
||||
<ion-item *ngIf="backupProgress[pkg.key] as pkgProgress">
|
||||
<ion-avatar slot="start">
|
||||
<img [src]="pkg.value['static-files'].icon" />
|
||||
<img [src]="pkg.value.icon" />
|
||||
</ion-avatar>
|
||||
<ion-label>{{ pkg.value.manifest.title }}</ion-label>
|
||||
<ion-label>{{ (pkg.value | toManifest).title }}</ion-label>
|
||||
<!-- complete -->
|
||||
<ion-note
|
||||
*ngIf="pkgProgress.complete; else incomplete"
|
||||
@@ -35,10 +35,7 @@
|
||||
>
|
||||
<!-- active -->
|
||||
<ion-note
|
||||
*ngIf="
|
||||
pkgStatus === PackageMainStatus.BackingUp;
|
||||
else queued
|
||||
"
|
||||
*ngIf="pkgStatus === 'backing-up'; else queued"
|
||||
class="inline"
|
||||
slot="end"
|
||||
>
|
||||
|
||||
@@ -25,8 +25,6 @@ export class BackingUpComponent {
|
||||
'backup-progress',
|
||||
)
|
||||
|
||||
PackageMainStatus = PackageMainStatus
|
||||
|
||||
constructor(private readonly patch: PatchDB<DataModel>) {}
|
||||
}
|
||||
|
||||
@@ -35,14 +33,7 @@ export class BackingUpComponent {
|
||||
})
|
||||
export class PkgMainStatusPipe implements PipeTransform {
|
||||
transform(pkgId: string): Observable<PackageMainStatus> {
|
||||
return this.patch.watch$(
|
||||
'package-data',
|
||||
pkgId,
|
||||
'installed',
|
||||
'status',
|
||||
'main',
|
||||
'status',
|
||||
)
|
||||
return this.patch.watch$('package-data', pkgId, 'status', 'main', 'status')
|
||||
}
|
||||
|
||||
constructor(private readonly patch: PatchDB<DataModel>) {}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { BackupDrivesComponentModule } from 'src/app/components/backup-drives/ba
|
||||
import { SharedPipesModule } from '@start9labs/shared'
|
||||
import { BackupSelectPageModule } from 'src/app/modals/backup-select/backup-select.module'
|
||||
import { PkgMainStatusPipe } from './backing-up/backing-up.component'
|
||||
import { UiPipeModule } from 'src/app/pipes/ui/ui.module'
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
@@ -24,6 +25,7 @@ const routes: Routes = [
|
||||
SharedPipesModule,
|
||||
BackupDrivesComponentModule,
|
||||
BackupSelectPageModule,
|
||||
UiPipeModule,
|
||||
],
|
||||
declarations: [ServerBackupPage, BackingUpComponent, PkgMainStatusPipe],
|
||||
})
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
} from '@start9labs/shared'
|
||||
import { SkeletonListComponentModule } from 'src/app/components/skeleton-list/skeleton-list.component.module'
|
||||
import { RoundProgressModule } from 'angular-svg-round-progressbar'
|
||||
import { InstallProgressPipeModule } from 'src/app/pipes/install-progress/install-progress.module'
|
||||
import { InstallingProgressPipeModule } from 'src/app/pipes/install-progress/install-progress.module'
|
||||
import { StoreIconComponentModule } from 'src/app/components/store-icon/store-icon.component.module'
|
||||
import { MimeTypePipeModule } from '@start9labs/marketplace'
|
||||
|
||||
@@ -34,7 +34,7 @@ const routes: Routes = [
|
||||
SkeletonListComponentModule,
|
||||
MarkdownPipeModule,
|
||||
RoundProgressModule,
|
||||
InstallProgressPipeModule,
|
||||
InstallingProgressPipeModule,
|
||||
StoreIconComponentModule,
|
||||
EmverPipesModule,
|
||||
MimeTypePipeModule,
|
||||
|
||||
@@ -39,8 +39,8 @@
|
||||
<h1 style="line-height: 1.3">{{ pkg.manifest.title }}</h1>
|
||||
<h2 class="inline">
|
||||
<span>
|
||||
{{ local.installed?.manifest?.version || '' |
|
||||
displayEmver }}
|
||||
{{ local['state-info'].manifest.version | displayEmver
|
||||
}}
|
||||
</span>
|
||||
|
||||
<ion-icon name="arrow-forward"></ion-icon>
|
||||
@@ -57,8 +57,8 @@
|
||||
</ion-label>
|
||||
<div slot="end" style="margin-left: 4px">
|
||||
<round-progress
|
||||
*ngIf="local.state === 'updating' else notUpdating"
|
||||
[current]="local['install-progress'] | installProgress"
|
||||
*ngIf="local['state-info'].state === 'updating' else notUpdating"
|
||||
[current]="(local['state-info']['installing-info'].progress.overall | installingProgress) || 0"
|
||||
[max]="100"
|
||||
[radius]="13"
|
||||
[stroke]="3"
|
||||
|
||||
@@ -2,7 +2,9 @@ import { Component, Inject } from '@angular/core'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import {
|
||||
DataModel,
|
||||
InstalledState,
|
||||
PackageDataEntry,
|
||||
UpdatingState,
|
||||
} from 'src/app/services/patch-db/data-model'
|
||||
import { MarketplaceService } from 'src/app/services/marketplace.service'
|
||||
import {
|
||||
@@ -14,16 +16,20 @@ import {
|
||||
} from '@start9labs/marketplace'
|
||||
import { Emver, isEmptyObject } from '@start9labs/shared'
|
||||
import { Pipe, PipeTransform } from '@angular/core'
|
||||
import { combineLatest, Observable } from 'rxjs'
|
||||
import { combineLatest, map, Observable } from 'rxjs'
|
||||
import { AlertController, NavController } from '@ionic/angular'
|
||||
import { hasCurrentDeps } from 'src/app/util/has-deps'
|
||||
import { getAllPackages } from 'src/app/util/get-package-data'
|
||||
import {
|
||||
getAllPackages,
|
||||
isInstalled,
|
||||
isUpdating,
|
||||
} from 'src/app/util/get-package-data'
|
||||
import { dryUpdate } from 'src/app/util/dry-update'
|
||||
|
||||
interface UpdatesData {
|
||||
hosts: StoreIdentity[]
|
||||
marketplace: Marketplace
|
||||
localPkgs: Record<string, PackageDataEntry>
|
||||
localPkgs: Record<string, PackageDataEntry<InstalledState | UpdatingState>>
|
||||
errors: string[]
|
||||
}
|
||||
|
||||
@@ -36,7 +42,14 @@ export class UpdatesPage {
|
||||
readonly data$: Observable<UpdatesData> = combineLatest({
|
||||
hosts: this.marketplaceService.getKnownHosts$(true),
|
||||
marketplace: this.marketplaceService.getMarketplace$(),
|
||||
localPkgs: this.patch.watch$('package-data'),
|
||||
localPkgs: this.patch.watch$('package-data').pipe(
|
||||
map(pkgs =>
|
||||
Object.values(pkgs).reduce((acc, curr) => {
|
||||
if (isInstalled(curr) || isUpdating(curr)) return { ...acc, curr }
|
||||
return acc
|
||||
}, {} as Record<string, PackageDataEntry<InstalledState | UpdatingState>>),
|
||||
),
|
||||
),
|
||||
errors: this.marketplaceService.getRequestErrors$(),
|
||||
})
|
||||
|
||||
@@ -154,14 +167,17 @@ export class FilterUpdatesPipe implements PipeTransform {
|
||||
|
||||
transform(
|
||||
pkgs: MarketplacePkg[],
|
||||
local: Record<string, PackageDataEntry | undefined>,
|
||||
local: Record<string, PackageDataEntry<InstalledState | UpdatingState>>,
|
||||
): MarketplacePkg[] {
|
||||
return pkgs.filter(
|
||||
({ manifest }) =>
|
||||
return pkgs.filter(({ manifest }) => {
|
||||
const localPkg = local[manifest.id]
|
||||
return (
|
||||
localPkg &&
|
||||
this.emver.compare(
|
||||
manifest.version,
|
||||
local[manifest.id]?.installed?.manifest.version || '',
|
||||
) === 1,
|
||||
)
|
||||
localPkg['state-info'].manifest.version,
|
||||
) === 1
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import { PrimaryStatus } from 'src/app/services/pkg-status-rendering.service'
|
||||
import { getPackageInfo, PkgInfo } from '../../../../util/get-package-info'
|
||||
import { combineLatest } from 'rxjs'
|
||||
import { DepErrorService } from 'src/app/services/dep-error.service'
|
||||
import { getManifest } from 'src/app/util/get-package-data'
|
||||
|
||||
@Component({
|
||||
selector: 'widget-health',
|
||||
@@ -31,7 +32,7 @@ export class HealthComponent {
|
||||
]).pipe(
|
||||
map(([data, depErrors]) => {
|
||||
const pkgs = Object.values<PackageDataEntry>(data).map(pkg =>
|
||||
getPackageInfo(pkg, depErrors[pkg.manifest.id]),
|
||||
getPackageInfo(pkg, depErrors[getManifest(pkg).id]),
|
||||
)
|
||||
const result = this.labels.reduce<Record<string, number>>(
|
||||
(acc, label) => ({
|
||||
|
||||
Reference in New Issue
Block a user