chore: cleanup - show spinner on service list when transitioning

config add new list items to end and auto scroll

remove js engine artifacts

fix view button in notification toast
This commit is contained in:
Matt Hill
2022-06-09 13:01:39 -06:00
committed by Lucy C
parent 7916a2352f
commit 8e9d2b5314
15 changed files with 87 additions and 49 deletions

2
.gitignore vendored
View File

@@ -9,3 +9,5 @@
/*_product_key.txt /*_product_key.txt
.vscode/settings.json .vscode/settings.json
deploy_web.sh deploy_web.sh
libs/js_engine/src/artifacts/ARM_JS_SNAPSHOT.bin
libs/js_engine/src/artifacts/JS_SNAPSHOT.bin

View File

@@ -5,7 +5,7 @@ import {
ToastController, ToastController,
ToastOptions, ToastOptions,
} from '@ionic/angular' } from '@ionic/angular'
import { EMPTY, merge, Observable } from 'rxjs' import { EMPTY, merge, Observable, ObservableInput } from 'rxjs'
import { filter, pairwise, switchMap, tap } from 'rxjs/operators' import { filter, pairwise, switchMap, tap } from 'rxjs/operators'
import { ErrorToastService } from '@start9labs/shared' import { ErrorToastService } from '@start9labs/shared'
@@ -13,6 +13,7 @@ import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
import { ConfigService } from 'src/app/services/config.service' import { ConfigService } from 'src/app/services/config.service'
import { ApiService } from 'src/app/services/api/embassy-api.service' import { ApiService } from 'src/app/services/api/embassy-api.service'
import { PatchDataService } from './patch-data.service' import { PatchDataService } from './patch-data.service'
import { DataModel, ServerInfo } from 'src/app/services/patch-db/data-model'
// Watch unread notification count to display toast // Watch unread notification count to display toast
@Injectable() @Injectable()
@@ -20,7 +21,7 @@ export class UnreadToastService extends Observable<unknown> {
private unreadToast: HTMLIonToastElement private unreadToast: HTMLIonToastElement
private readonly stream$ = this.patchData.pipe( private readonly stream$ = this.patchData.pipe(
switchMap(data => { switchMap<DataModel | null, ObservableInput<number>>(data => {
if (data) { if (data) {
return this.patch.watch$('server-info', 'unread-notification-count') return this.patch.watch$('server-info', 'unread-notification-count')
} }
@@ -36,6 +37,29 @@ export class UnreadToastService extends Observable<unknown> {
}), }),
) )
TOAST: ToastOptions = {
header: 'Embassy',
message: `New notifications`,
position: 'bottom',
duration: 4000,
buttons: [
{
side: 'start',
icon: 'close',
handler: () => true,
},
{
side: 'end',
text: 'View',
handler: () => {
this.router.navigate(['/notifications'], {
queryParams: { toast: true },
})
},
},
],
}
constructor( constructor(
private readonly router: Router, private readonly router: Router,
private readonly patchData: PatchDataService, private readonly patchData: PatchDataService,
@@ -50,31 +74,9 @@ export class UnreadToastService extends Observable<unknown> {
private async showToast() { private async showToast() {
await this.unreadToast?.dismiss() await this.unreadToast?.dismiss()
this.unreadToast = await this.toastCtrl.create(TOAST) this.unreadToast = await this.toastCtrl.create(this.TOAST)
this.unreadToast.buttons?.push({ this.unreadToast.buttons?.push()
side: 'end',
text: 'View',
handler: () => {
this.router.navigate(['/notifications'], {
queryParams: { toast: true },
})
},
})
await this.unreadToast.present() await this.unreadToast.present()
} }
} }
const TOAST: ToastOptions = {
header: 'Embassy',
message: `New notifications`,
position: 'bottom',
duration: 4000,
buttons: [
{
side: 'start',
icon: 'close',
handler: () => true,
},
],
}

View File

@@ -7,6 +7,7 @@ const ICONS = [
'alert-circle-outline', 'alert-circle-outline',
'aperture-outline', 'aperture-outline',
'arrow-back', 'arrow-back',
'arrow-forward',
'arrow-up', 'arrow-up',
'briefcase-outline', 'briefcase-outline',
'bookmark-outline', 'bookmark-outline',

View File

@@ -304,6 +304,7 @@
</ng-container> </ng-container>
<!-- string or number --> <!-- string or number -->
<ion-item-group <ion-item-group
[id]="getElementId(entry.key, i)"
*ngIf="spec.subtype === 'string' || spec.subtype === 'number'" *ngIf="spec.subtype === 'string' || spec.subtype === 'number'"
> >
<ion-item color="dark"> <ion-item color="dark">

View File

@@ -141,23 +141,25 @@ export class FormObjectComponent {
if (!newItem) return if (!newItem) return
const index = arr.length
newItem.markAllAsTouched() newItem.markAllAsTouched()
arr.insert(0, newItem) arr.insert(index, newItem)
if (['object', 'union'].includes(listSpec.subtype)) { if (['object', 'union'].includes(listSpec.subtype)) {
const displayAs = (listSpec.spec as ListValueSpecOf<'object'>)[ const displayAs = (listSpec.spec as ListValueSpecOf<'object'>)[
'display-as' 'display-as'
] ]
this.objectListDisplay[key].unshift({ this.objectListDisplay[key].push({
height: '0px', height: '0px',
expanded: true, expanded: true,
displayAs: displayAs ? Mustache.render(displayAs, newItem.value) : '', displayAs: displayAs ? Mustache.render(displayAs, newItem.value) : '',
}) })
pauseFor(200).then(() => {
this.objectListDisplay[key][0].height = this.getDocSize(key, 0)
this.onExpand.emit()
})
} }
pauseFor(400).then(() => {
const element = document.getElementById(this.getElementId(key, index))
element?.parentElement?.scrollIntoView({ behavior: 'smooth' })
})
} }
toggleExpandObject(key: string) { toggleExpandObject(key: string) {

View File

@@ -1,10 +1,18 @@
<ion-icon <ion-icon
*ngIf="pkg.error; else bulb" *ngIf="pkg.error; else noError"
class="warning-icon" class="warning-icon"
name="warning-outline" name="warning-outline"
size="small" size="small"
color="warning" color="warning"
></ion-icon> ></ion-icon>
<ng-template #noError>
<ion-spinner
*ngIf="pkg.transitioning; else bulb"
class="spinner"
size="small"
color="primary"
></ion-spinner>
<ng-template #bulb> <ng-template #bulb>
<div class="bulb" [style.background-color]="color"></div> <div class="bulb" [style.background-color]="color"></div>
</ng-template> </ng-template>
</ng-template>

View File

@@ -18,3 +18,10 @@
background-color: rgba(255, 213, 52, 0.1); background-color: rgba(255, 213, 52, 0.1);
box-shadow: 0 0 4px 4px rgba(255, 213, 52, 0.1); box-shadow: 0 0 4px 4px rgba(255, 213, 52, 0.1);
} }
.spinner {
position: absolute !important;
left: 6px !important;
top: 6px !important;
width: 18px;
}

View File

@@ -1,5 +1,4 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core' import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
import { AlertController } from '@ionic/angular'
import { import {
HealthResult, HealthResult,
PackageDataEntry, PackageDataEntry,
@@ -20,8 +19,6 @@ export class AppShowHealthChecksComponent {
HealthResult = HealthResult HealthResult = HealthResult
constructor(private readonly alertCtrl: AlertController) {}
isLoading(result: HealthResult): boolean { isLoading(result: HealthResult): boolean {
return result === HealthResult.Starting || result === HealthResult.Loading return result === HealthResult.Starting || result === HealthResult.Loading
} }

View File

@@ -12,7 +12,10 @@ import { ValueSpecObject } from 'src/app/pkg-config/config-types'
import { GenericFormPage } from 'src/app/modals/generic-form/generic-form.page' import { GenericFormPage } from 'src/app/modals/generic-form/generic-form.page'
import { PatchDbService } from '../../../services/patch-db/patch-db.service' import { PatchDbService } from '../../../services/patch-db/patch-db.service'
import { v4 } from 'uuid' import { v4 } from 'uuid'
import { UIMarketplaceData } from '../../../services/patch-db/data-model' import {
UIData,
UIMarketplaceData,
} from '../../../services/patch-db/data-model'
import { ConfigService } from '../../../services/config.service' import { ConfigService } from '../../../services/config.service'
import { MarketplaceService } from 'src/app/services/marketplace.service' import { MarketplaceService } from 'src/app/services/marketplace.service'
import { import {
@@ -54,10 +57,10 @@ export class MarketplacesPage {
this.patch this.patch
.watch$('ui') .watch$('ui')
.pipe( .pipe(
map(ui => ui.marketplace), map((ui: UIData) => ui.marketplace),
distinctUntilChanged(), distinctUntilChanged(),
) )
.subscribe(mp => { .subscribe((mp: UIMarketplaceData | undefined) => {
let marketplaces: Marketplaces = [ let marketplaces: Marketplaces = [
{ {
id: undefined, id: undefined,

View File

@@ -12,6 +12,7 @@ import { ApiService } from 'src/app/services/api/embassy-api.service'
import { ConfigService } from 'src/app/services/config.service' import { ConfigService } from 'src/app/services/config.service'
import { import {
ServerInfo, ServerInfo,
UIData,
UIMarketplaceData, UIMarketplaceData,
} from 'src/app/services/patch-db/data-model' } from 'src/app/services/patch-db/data-model'
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service' import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
@@ -34,7 +35,7 @@ export class MarketplaceService extends AbstractMarketplaceService {
private readonly altMarketplaceData$: Observable< private readonly altMarketplaceData$: Observable<
UIMarketplaceData | undefined UIMarketplaceData | undefined
> = this.patch.watch$('ui').pipe( > = this.patch.watch$('ui').pipe(
map(ui => ui.marketplace), map((ui: UIData) => ui.marketplace),
distinctUntilChanged(), distinctUntilChanged(),
shareReplay({ bufferSize: 1, refCount: true }), shareReplay({ bufferSize: 1, refCount: true }),
) )

View File

@@ -16,11 +16,12 @@ export function renderPkgStatus(pkg: PackageDataEntry): PackageStatus {
let primary: PrimaryStatus let primary: PrimaryStatus
let dependency: DependencyStatus | null = null let dependency: DependencyStatus | null = null
let health: HealthStatus | null = null let health: HealthStatus | null = null
const hasHealthChecks = !isEmptyObject(pkg.manifest['health-checks'])
if (pkg.state === PackageState.Installed && pkg.installed) { if (pkg.state === PackageState.Installed && pkg.installed) {
primary = getPrimaryStatus(pkg.installed.status) primary = getPrimaryStatus(pkg.installed.status)
dependency = getDependencyStatus(pkg) dependency = getDependencyStatus(pkg)
health = getHealthStatus(pkg.installed.status) health = getHealthStatus(pkg.installed.status, hasHealthChecks)
} else { } else {
primary = pkg.state as string as PrimaryStatus primary = pkg.state as string as PrimaryStatus
} }
@@ -56,19 +57,24 @@ function getHealthStatus(
} }
const values = Object.values(status.main.health) const values = Object.values(status.main.health)
console.log('HEALTH CHECKS', values)
if (values.some(h => h.result === 'failure')) { if (values.some(h => h.result === 'failure')) {
return HealthStatus.Failure return HealthStatus.Failure
} }
if (values.some(h => h.result === 'starting')) { if (!values.length && hasHealthChecks) {
return HealthStatus.Starting return HealthStatus.Waiting
} }
if (values.some(h => h.result === 'loading')) { if (values.some(h => h.result === 'loading')) {
return HealthStatus.Loading return HealthStatus.Loading
} }
if (values.some(h => !h.result || h.result === 'starting')) {
return HealthStatus.Starting
}
return HealthStatus.Healthy return HealthStatus.Healthy
} }
@@ -101,6 +107,7 @@ export enum DependencyStatus {
export enum HealthStatus { export enum HealthStatus {
Failure = 'failure', Failure = 'failure',
Waiting = 'waiting',
Starting = 'starting', Starting = 'starting',
Loading = 'loading', Loading = 'loading',
Healthy = 'healthy', Healthy = 'healthy',

View File

@@ -12,14 +12,20 @@ import { packageLoadingProgress } from './package-loading-progress'
export function getPackageInfo(entry: PackageDataEntry): PkgInfo { export function getPackageInfo(entry: PackageDataEntry): PkgInfo {
const statuses = renderPkgStatus(entry) const statuses = renderPkgStatus(entry)
const primaryRendering = PrimaryRendering[statuses.primary]
return { return {
entry, entry,
primaryRendering: PrimaryRendering[statuses.primary], primaryRendering,
installProgress: packageLoadingProgress(entry['install-progress']), installProgress: packageLoadingProgress(entry['install-progress']),
error: error:
statuses.health === HealthStatus.Failure || statuses.health === HealthStatus.Failure ||
statuses.dependency === DependencyStatus.Warning, statuses.dependency === DependencyStatus.Warning,
transitioning:
primaryRendering.showDots ||
statuses.health === HealthStatus.Waiting ||
statuses.health === HealthStatus.Loading ||
statuses.health === HealthStatus.Starting,
} }
} }
@@ -28,5 +34,6 @@ export interface PkgInfo {
primaryRendering: StatusRendering primaryRendering: StatusRendering
installProgress: ProgressData | null installProgress: ProgressData | null
error: boolean error: boolean
transitioning: boolean
sub?: Subscription | null sub?: Subscription | null
} }

View File

@@ -2,7 +2,7 @@
"/rpc/v1": { "/rpc/v1": {
"target": "http://<CHANGE_ME>/rpc/v1" "target": "http://<CHANGE_ME>/rpc/v1"
}, },
"/ws/*": { "/ws/db": {
"target": "http://<CHANGE_ME>", "target": "http://<CHANGE_ME>",
"secure": false, "secure": false,
"ws": true "ws": true