mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
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:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -9,3 +9,5 @@
|
||||
/*_product_key.txt
|
||||
.vscode/settings.json
|
||||
deploy_web.sh
|
||||
libs/js_engine/src/artifacts/ARM_JS_SNAPSHOT.bin
|
||||
libs/js_engine/src/artifacts/JS_SNAPSHOT.bin
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
ToastController,
|
||||
ToastOptions,
|
||||
} 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 { 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 { ApiService } from 'src/app/services/api/embassy-api.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
|
||||
@Injectable()
|
||||
@@ -20,7 +21,7 @@ export class UnreadToastService extends Observable<unknown> {
|
||||
private unreadToast: HTMLIonToastElement
|
||||
|
||||
private readonly stream$ = this.patchData.pipe(
|
||||
switchMap(data => {
|
||||
switchMap<DataModel | null, ObservableInput<number>>(data => {
|
||||
if (data) {
|
||||
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(
|
||||
private readonly router: Router,
|
||||
private readonly patchData: PatchDataService,
|
||||
@@ -50,31 +74,9 @@ export class UnreadToastService extends Observable<unknown> {
|
||||
private async showToast() {
|
||||
await this.unreadToast?.dismiss()
|
||||
|
||||
this.unreadToast = await this.toastCtrl.create(TOAST)
|
||||
this.unreadToast.buttons?.push({
|
||||
side: 'end',
|
||||
text: 'View',
|
||||
handler: () => {
|
||||
this.router.navigate(['/notifications'], {
|
||||
queryParams: { toast: true },
|
||||
})
|
||||
},
|
||||
})
|
||||
this.unreadToast = await this.toastCtrl.create(this.TOAST)
|
||||
this.unreadToast.buttons?.push()
|
||||
|
||||
await this.unreadToast.present()
|
||||
}
|
||||
}
|
||||
|
||||
const TOAST: ToastOptions = {
|
||||
header: 'Embassy',
|
||||
message: `New notifications`,
|
||||
position: 'bottom',
|
||||
duration: 4000,
|
||||
buttons: [
|
||||
{
|
||||
side: 'start',
|
||||
icon: 'close',
|
||||
handler: () => true,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ const ICONS = [
|
||||
'alert-circle-outline',
|
||||
'aperture-outline',
|
||||
'arrow-back',
|
||||
'arrow-forward',
|
||||
'arrow-up',
|
||||
'briefcase-outline',
|
||||
'bookmark-outline',
|
||||
|
||||
@@ -304,6 +304,7 @@
|
||||
</ng-container>
|
||||
<!-- string or number -->
|
||||
<ion-item-group
|
||||
[id]="getElementId(entry.key, i)"
|
||||
*ngIf="spec.subtype === 'string' || spec.subtype === 'number'"
|
||||
>
|
||||
<ion-item color="dark">
|
||||
|
||||
@@ -141,23 +141,25 @@ export class FormObjectComponent {
|
||||
|
||||
if (!newItem) return
|
||||
|
||||
const index = arr.length
|
||||
|
||||
newItem.markAllAsTouched()
|
||||
arr.insert(0, newItem)
|
||||
arr.insert(index, newItem)
|
||||
if (['object', 'union'].includes(listSpec.subtype)) {
|
||||
const displayAs = (listSpec.spec as ListValueSpecOf<'object'>)[
|
||||
'display-as'
|
||||
]
|
||||
this.objectListDisplay[key].unshift({
|
||||
this.objectListDisplay[key].push({
|
||||
height: '0px',
|
||||
expanded: true,
|
||||
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) {
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
<ion-icon
|
||||
*ngIf="pkg.error; else bulb"
|
||||
*ngIf="pkg.error; else noError"
|
||||
class="warning-icon"
|
||||
name="warning-outline"
|
||||
size="small"
|
||||
color="warning"
|
||||
></ion-icon>
|
||||
<ng-template #noError>
|
||||
<ion-spinner
|
||||
*ngIf="pkg.transitioning; else bulb"
|
||||
class="spinner"
|
||||
size="small"
|
||||
color="primary"
|
||||
></ion-spinner>
|
||||
<ng-template #bulb>
|
||||
<div class="bulb" [style.background-color]="color"></div>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
|
||||
@@ -18,3 +18,10 @@
|
||||
background-color: 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;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
|
||||
import { AlertController } from '@ionic/angular'
|
||||
import {
|
||||
HealthResult,
|
||||
PackageDataEntry,
|
||||
@@ -20,8 +19,6 @@ export class AppShowHealthChecksComponent {
|
||||
|
||||
HealthResult = HealthResult
|
||||
|
||||
constructor(private readonly alertCtrl: AlertController) {}
|
||||
|
||||
isLoading(result: HealthResult): boolean {
|
||||
return result === HealthResult.Starting || result === HealthResult.Loading
|
||||
}
|
||||
|
||||
@@ -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 { PatchDbService } from '../../../services/patch-db/patch-db.service'
|
||||
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 { MarketplaceService } from 'src/app/services/marketplace.service'
|
||||
import {
|
||||
@@ -54,10 +57,10 @@ export class MarketplacesPage {
|
||||
this.patch
|
||||
.watch$('ui')
|
||||
.pipe(
|
||||
map(ui => ui.marketplace),
|
||||
map((ui: UIData) => ui.marketplace),
|
||||
distinctUntilChanged(),
|
||||
)
|
||||
.subscribe(mp => {
|
||||
.subscribe((mp: UIMarketplaceData | undefined) => {
|
||||
let marketplaces: Marketplaces = [
|
||||
{
|
||||
id: undefined,
|
||||
|
||||
@@ -12,6 +12,7 @@ import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { ConfigService } from 'src/app/services/config.service'
|
||||
import {
|
||||
ServerInfo,
|
||||
UIData,
|
||||
UIMarketplaceData,
|
||||
} from 'src/app/services/patch-db/data-model'
|
||||
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
|
||||
@@ -34,7 +35,7 @@ export class MarketplaceService extends AbstractMarketplaceService {
|
||||
private readonly altMarketplaceData$: Observable<
|
||||
UIMarketplaceData | undefined
|
||||
> = this.patch.watch$('ui').pipe(
|
||||
map(ui => ui.marketplace),
|
||||
map((ui: UIData) => ui.marketplace),
|
||||
distinctUntilChanged(),
|
||||
shareReplay({ bufferSize: 1, refCount: true }),
|
||||
)
|
||||
|
||||
@@ -16,11 +16,12 @@ export function renderPkgStatus(pkg: PackageDataEntry): PackageStatus {
|
||||
let primary: PrimaryStatus
|
||||
let dependency: DependencyStatus | null = null
|
||||
let health: HealthStatus | null = null
|
||||
const hasHealthChecks = !isEmptyObject(pkg.manifest['health-checks'])
|
||||
|
||||
if (pkg.state === PackageState.Installed && pkg.installed) {
|
||||
primary = getPrimaryStatus(pkg.installed.status)
|
||||
dependency = getDependencyStatus(pkg)
|
||||
health = getHealthStatus(pkg.installed.status)
|
||||
health = getHealthStatus(pkg.installed.status, hasHealthChecks)
|
||||
} else {
|
||||
primary = pkg.state as string as PrimaryStatus
|
||||
}
|
||||
@@ -56,19 +57,24 @@ function getHealthStatus(
|
||||
}
|
||||
|
||||
const values = Object.values(status.main.health)
|
||||
console.log('HEALTH CHECKS', values)
|
||||
|
||||
if (values.some(h => h.result === 'failure')) {
|
||||
return HealthStatus.Failure
|
||||
}
|
||||
|
||||
if (values.some(h => h.result === 'starting')) {
|
||||
return HealthStatus.Starting
|
||||
if (!values.length && hasHealthChecks) {
|
||||
return HealthStatus.Waiting
|
||||
}
|
||||
|
||||
if (values.some(h => h.result === 'loading')) {
|
||||
return HealthStatus.Loading
|
||||
}
|
||||
|
||||
if (values.some(h => !h.result || h.result === 'starting')) {
|
||||
return HealthStatus.Starting
|
||||
}
|
||||
|
||||
return HealthStatus.Healthy
|
||||
}
|
||||
|
||||
@@ -101,6 +107,7 @@ export enum DependencyStatus {
|
||||
|
||||
export enum HealthStatus {
|
||||
Failure = 'failure',
|
||||
Waiting = 'waiting',
|
||||
Starting = 'starting',
|
||||
Loading = 'loading',
|
||||
Healthy = 'healthy',
|
||||
|
||||
@@ -12,14 +12,20 @@ import { packageLoadingProgress } from './package-loading-progress'
|
||||
|
||||
export function getPackageInfo(entry: PackageDataEntry): PkgInfo {
|
||||
const statuses = renderPkgStatus(entry)
|
||||
const primaryRendering = PrimaryRendering[statuses.primary]
|
||||
|
||||
return {
|
||||
entry,
|
||||
primaryRendering: PrimaryRendering[statuses.primary],
|
||||
primaryRendering,
|
||||
installProgress: packageLoadingProgress(entry['install-progress']),
|
||||
error:
|
||||
statuses.health === HealthStatus.Failure ||
|
||||
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
|
||||
installProgress: ProgressData | null
|
||||
error: boolean
|
||||
transitioning: boolean
|
||||
sub?: Subscription | null
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"/rpc/v1": {
|
||||
"target": "http://<CHANGE_ME>/rpc/v1"
|
||||
},
|
||||
"/ws/*": {
|
||||
"/ws/db": {
|
||||
"target": "http://<CHANGE_ME>",
|
||||
"secure": false,
|
||||
"ws": true
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user