toast errors and some styling

This commit is contained in:
Matt Hill
2021-07-13 23:43:39 -06:00
committed by Aiden McClelland
parent 529f7c1ef6
commit fbcaf3070e
43 changed files with 463 additions and 403 deletions

View File

@@ -49,6 +49,7 @@ const routes: Routes = [
@NgModule({ @NgModule({
imports: [ imports: [
RouterModule.forRoot(routes, { RouterModule.forRoot(routes, {
scrollPositionRestoration: 'enabled',
preloadingStrategy: PreloadAllModules, preloadingStrategy: PreloadAllModules,
initialNavigation: 'disabled', initialNavigation: 'disabled',
useHash: true, useHash: true,

View File

@@ -5,7 +5,10 @@
.menu-style { .menu-style {
border-style: solid; border-style: solid;
border-width: 0px 1px 0px 0px; border-width: 0px 1px 0px 0px;
border-color: #ff4960; border-color: var(--ion-color-danger);
ion-item::part(native) {
height: 56px;
}
} }
.selected-badge { .selected-badge {

View File

@@ -11,11 +11,8 @@
<ion-content> <ion-content>
<text-spinner *ngIf="loading; else loaded" [text]="'Loading ' + title | titlecase"></text-spinner> <text-spinner *ngIf="loading; else loaded" [text]="'Loading ' + title | titlecase"></text-spinner>
<ng-template #loaded>
<ion-item *ngIf="error" style="margin-bottom: 16px;">
<ion-text class="ion-text-wrap" color="danger">{{ error }}</ion-text>
</ion-item>
<ng-template #loaded>
<div *ngIf="content" class="content-padding" [innerHTML]="content | markdown"></div> <div *ngIf="content" class="content-padding" [innerHTML]="content | markdown"></div>
</ng-template> </ng-template>
</ion-content> </ion-content>

View File

@@ -1,6 +1,7 @@
import { Component, Input } from '@angular/core' import { Component, Input } from '@angular/core'
import { ModalController } from '@ionic/angular' import { ModalController } from '@ionic/angular'
import { ApiService } from 'src/app/services/api/api.service' import { ApiService } from 'src/app/services/api/api.service'
import { ErrorToastService } from 'src/app/services/error-toast.service'
@Component({ @Component({
selector: 'markdown', selector: 'markdown',
@@ -12,10 +13,10 @@ export class MarkdownPage {
@Input() title: string @Input() title: string
content: string content: string
loading = true loading = true
error = ''
constructor ( constructor (
private readonly modalCtrl: ModalController, private readonly modalCtrl: ModalController,
private readonly errToast: ErrorToastService,
private readonly apiService: ApiService, private readonly apiService: ApiService,
) { } ) { }
@@ -23,7 +24,8 @@ export class MarkdownPage {
try { try {
this.content = await this.apiService.getStatic(this.contentUrl) this.content = await this.apiService.getStatic(this.contentUrl)
} catch (e) { } catch (e) {
this.error = e.message console.error(e.message)
this.errToast.present(e.message)
} finally { } finally {
this.loading = false this.loading = false
} }

View File

@@ -18,7 +18,8 @@ export class OSWelcomePage {
) { } ) { }
async dismiss () { async dismiss () {
this.apiService.setDbValue({ pointer: '/welcome-ack', value: this.config.version }).catch(console.error) this.apiService.setDbValue({ pointer: '/welcome-ack', value: this.config.version })
.catch(console.error)
// return false to skip subsequent alert modals (e.g. check for updates modals) // return false to skip subsequent alert modals (e.g. check for updates modals)
// return true to show subsequent alert modals // return true to show subsequent alert modals

View File

@@ -11,10 +11,6 @@
<text-spinner *ngIf="loading; else loaded" text="Loading Instructions"></text-spinner> <text-spinner *ngIf="loading; else loaded" text="Loading Instructions"></text-spinner>
<ng-template #loaded> <ng-template #loaded>
<ion-item *ngIf="error" style="margin-bottom: 16px;">
<ion-text class="ion-text-wrap" color="danger">{{ error }}</ion-text>
</ion-item>
<div *ngIf="instructions" class="instuctions-padding" [innerHTML]="instructions | markdown"></div> <div *ngIf="instructions" class="instuctions-padding" [innerHTML]="instructions | markdown"></div>
</ng-template> </ng-template>
</ion-content> </ion-content>

View File

@@ -1,10 +1,9 @@
import { Component, ViewChild } from '@angular/core' import { Component, ViewChild } from '@angular/core'
import { ActivatedRoute } from '@angular/router' import { ActivatedRoute } from '@angular/router'
import { IonContent } from '@ionic/angular' import { IonContent } from '@ionic/angular'
import { Subscription } from 'rxjs'
import { concatMap, take, tap } from 'rxjs/operators'
import { PatchDbModel } from 'src/app/services/patch-db/patch-db.service' import { PatchDbModel } from 'src/app/services/patch-db/patch-db.service'
import { ApiService } from 'src/app/services/api/api.service' import { ApiService } from 'src/app/services/api/api.service'
import { ErrorToastService } from 'src/app/services/error-toast.service'
@Component({ @Component({
selector: 'app-instructions', selector: 'app-instructions',
@@ -14,42 +13,30 @@ import { ApiService } from 'src/app/services/api/api.service'
export class AppInstructionsPage { export class AppInstructionsPage {
instructions: string instructions: string
loading = true loading = true
error = ''
@ViewChild(IonContent) content: IonContent @ViewChild(IonContent) content: IonContent
subs: Subscription[] = []
constructor ( constructor (
private readonly route: ActivatedRoute, private readonly route: ActivatedRoute,
private readonly errToast: ErrorToastService,
private readonly apiService: ApiService, private readonly apiService: ApiService,
private readonly patch: PatchDbModel, private readonly patch: PatchDbModel,
) { } ) { }
async ngOnInit () { async ngOnInit () {
const pkgId = this.route.snapshot.paramMap.get('pkgId') const pkgId = this.route.snapshot.paramMap.get('pkgId')
this.patch.watch$('package-data', pkgId) const url = this.patch.data['package-data'][pkgId]['static-files'].instructions
.pipe( try {
concatMap(pkg => this.apiService.getStatic(pkg['static-files'].instructions)), this.instructions = await this.apiService.getStatic(url)
tap(instructions => { } catch (e) {
this.instructions = instructions console.error(e)
}), this.errToast.present(e.message)
take(1), } finally {
) this.loading = false
.subscribe( }
() => { this.loading = false },
e => {
this.error = e.message
this.loading = false
},
() => console.log('COMPLETE'),
)
} }
ngAfterViewInit () { ngAfterViewInit () {
this.content.scrollToPoint(undefined, 1) this.content.scrollToPoint(undefined, 1)
} }
ngOnDestroy () {
this.subs.forEach(sub => sub.unsubscribe())
}
} }

View File

@@ -14,10 +14,6 @@
<ion-content class="ion-padding" color="light"> <ion-content class="ion-padding" color="light">
<ion-item *ngIf="error" style="margin-bottom: 16px;">
<ion-text class="ion-text-wrap" color="danger">{{ error }}</ion-text>
</ion-item>
<text-spinner *ngIf="!logs" text="Loading Logs"></text-spinner> <text-spinner *ngIf="!logs" text="Loading Logs"></text-spinner>
<div style="white-space: pre-line;">{{ logs }}</div> <div style="white-space: pre-line;">{{ logs }}</div>

View File

@@ -2,6 +2,7 @@ import { Component, ViewChild } from '@angular/core'
import { ActivatedRoute } from '@angular/router' import { ActivatedRoute } from '@angular/router'
import { ApiService } from 'src/app/services/api/api.service' import { ApiService } from 'src/app/services/api/api.service'
import { IonContent } from '@ionic/angular' import { IonContent } from '@ionic/angular'
import { ErrorToastService } from 'src/app/services/error-toast.service'
@Component({ @Component({
selector: 'app-logs', selector: 'app-logs',
@@ -12,10 +13,10 @@ export class AppLogsPage {
@ViewChild(IonContent, { static: false }) private content: IonContent @ViewChild(IonContent, { static: false }) private content: IonContent
pkgId: string pkgId: string
logs = '' logs = ''
error = ''
constructor ( constructor (
private readonly route: ActivatedRoute, private readonly route: ActivatedRoute,
private readonly errToast: ErrorToastService,
private readonly apiService: ApiService, private readonly apiService: ApiService,
) { } ) { }
@@ -32,7 +33,8 @@ export class AppLogsPage {
this.logs = logs.map(l => `${l.timestamp} ${l.log}`).join('\n\n') this.logs = logs.map(l => `${l.timestamp} ${l.log}`).join('\n\n')
setTimeout(async () => await this.content.scrollToBottom(100), 200) setTimeout(async () => await this.content.scrollToBottom(100), 200)
} catch (e) { } catch (e) {
this.error = e.message console.error(e)
this.errToast.present(e.message)
} }
} }
} }

View File

@@ -7,10 +7,8 @@
</ion-toolbar> </ion-toolbar>
</ion-header> </ion-header>
<ion-content *ngIf="pkg"> <ion-content>
<ion-item *ngIf="error" style="margin-bottom: 16px;">
<ion-text class="ion-text-wrap" color="danger">{{ error }}</ion-text>
</ion-item>
<ion-card> <ion-card>
<ion-card-header> <ion-card-header>
<ion-card-title> <ion-card-title>
@@ -19,41 +17,61 @@
</ion-card-title> </ion-card-title>
</ion-card-header> </ion-card-header>
<ion-card-content> <ion-card-content>
<ion-item *ngIf="!(pkg.installed?.status.main.health | empty); else noHealth" color="light" style="margin: 10px;"> <ion-item *ngIf="pkg.installed?.status.main.health | empty; else health">
<ion-label> <ion-label>
<div *ngFor="let health of pkg.installed.status.main.health | keyvalue : asIsOrder" class="align" style="margin-left: 12px;"> No health checks
<ion-icon *ngIf="health.value.result === 'success'" name="checkmark-outline" color="success"></ion-icon>
<ion-icon *ngIf="health.value.result === 'starting'" name="timer-outline" color="warning"></ion-icon>
<ion-icon *ngIf="health.value.result === 'loading'" name="sync-circle-outline" color="warning"></ion-icon>
<ion-icon *ngIf="health.value.result === 'failure'" name="close-outline" color="danger"></ion-icon>
<ion-icon *ngIf="health.value.result === 'disabled'" name="remove-outline" color="medium"></ion-icon>
<h2>{{ health.key }}</h2>
<p style="margin-left: 24px;" *ngIf="health.value.result === 'failure'"><ion-text color="danger">{{ health.value.error }}</ion-text></p>
</div>
</ion-label> </ion-label>
</ion-item> </ion-item>
<ng-template #noHealth> <ng-template #health>
No health checks <ion-item *ngIf="!(pkg.installed?.status.main.health | empty); else noHealth" color="light" style="margin: 10px;">
<ion-label>
<div *ngFor="let health of pkg.installed.status.main.health | keyvalue : asIsOrder" class="align" style="margin-left: 12px;">
<ion-icon *ngIf="health.value.result === 'success'" name="checkmark-outline" color="success"></ion-icon>
<ion-icon *ngIf="health.value.result === 'starting'" name="timer-outline" color="warning"></ion-icon>
<ion-icon *ngIf="health.value.result === 'loading'" name="sync-circle-outline" color="warning"></ion-icon>
<ion-icon *ngIf="health.value.result === 'failure'" name="close-outline" color="danger"></ion-icon>
<ion-icon *ngIf="health.value.result === 'disabled'" name="remove-outline" color="medium"></ion-icon>
<h2>{{ health.key }}</h2>
<p style="margin-left: 24px;" *ngIf="health.value.result === 'failure'"><ion-text color="danger">{{ health.value.error }}</ion-text></p>
</div>
</ion-label>
</ion-item>
</ng-template> </ng-template>
</ion-card-content> </ion-card-content>
</ion-card> </ion-card>
<ion-card> <ion-card>
<ion-card-header> <ion-card-header>
<ion-card-title> <ion-card-title>
<ion-icon style="vertical-align: middle; padding-right: 12px;" name="analytics-outline"></ion-icon> <ion-icon style="vertical-align: middle; padding-right: 12px;" name="pulse-outline"></ion-icon>
<span style="vertical-align: middle;">Service Metrics</span> <span style="vertical-align: middle;">Metrics</span>
</ion-card-title> </ion-card-title>
</ion-card-header> </ion-card-header>
<ion-card-content> <ion-card-content>
<ion-item *ngFor="let metric of metrics | keyvalue : asIsOrder"> <ng-container *ngIf="loading">
<ion-label> <div class="post">
<ion-text color="medium">{{ metric.key }}</ion-text> <div class="label"></div>
</ion-label> <div class="note"></div>
<ion-note *ngIf="metric.value" slot="end" class="metric-note"> </div>
<ion-text style="color: white;">{{ metric.value.value }} {{ metric.value.unit }}</ion-text> <div class="post">
</ion-note> <div class="label"></div>
</ion-item> <div class="note"></div>
</div>
<div class="post">
<div class="label"></div>
<div class="note"></div>
</div>
</ng-container>
<ion-item-group *ngIf="!loading">
<ion-item *ngFor="let metric of metrics | keyvalue : asIsOrder">
<ion-label>
<ion-text color="medium">{{ metric.key }}</ion-text>
</ion-label>
<ion-note *ngIf="metric.value" slot="end" class="metric-note">
<ion-text style="color: white;">{{ metric.value.value }} {{ metric.value.unit }}</ion-text>
</ion-note>
</ion-item>
</ion-item-group>
</ion-card-content> </ion-card-content>
</ion-card> </ion-card>
</ion-content> </ion-content>

View File

@@ -1,3 +1,44 @@
.metric-note { .metric-note {
font-size: 16px; font-size: 16px;
} }
$base-color: #7b7b7b;
$shine-color: #8a8a8a;
$animation-duration: 1.2s;
@mixin background-gradient {
background-image: linear-gradient(90deg, $base-color 0px, $shine-color 40px, $base-color 80px);
background-size: 100px;
}
.post {
height: 52px;
position: relative;
left: 12px;
.label {
float: left;
left: 30px;
width: 55%;
height: 24px;
margin: 14px 0;
border-radius: 7px;
@include background-gradient;
animation: shine-lines $animation-duration infinite linear
}
.note {
float: right;
position: relative;
right: 12px;
width: 30%;
height: 24px;
margin: 14px 0;
border-radius: 7px;
@include background-gradient;
animation: shine-lines $animation-duration infinite linear
}
}
@keyframes shine-lines {
0% { background-position: -100px; }
40%, 100% { background-position: 140px; }
}

View File

@@ -3,6 +3,7 @@ import { ActivatedRoute } from '@angular/router'
import { IonContent } from '@ionic/angular' import { IonContent } from '@ionic/angular'
import { Metric } from 'src/app/services/api/api-types' import { Metric } from 'src/app/services/api/api-types'
import { ApiService } from 'src/app/services/api/api.service' import { ApiService } from 'src/app/services/api/api.service'
import { ErrorToastService } from 'src/app/services/error-toast.service'
import { PackageDataEntry } from 'src/app/services/patch-db/data-model' import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
import { PatchDbModel } from 'src/app/services/patch-db/patch-db.service' import { PatchDbModel } from 'src/app/services/patch-db/patch-db.service'
import { pauseFor } from 'src/app/util/misc.util' import { pauseFor } from 'src/app/util/misc.util'
@@ -13,6 +14,7 @@ import { pauseFor } from 'src/app/util/misc.util'
styleUrls: ['./app-metrics.page.scss'], styleUrls: ['./app-metrics.page.scss'],
}) })
export class AppMetricsPage { export class AppMetricsPage {
loading = true
pkgId: string pkgId: string
pkg: PackageDataEntry pkg: PackageDataEntry
going = false going = false
@@ -22,6 +24,7 @@ export class AppMetricsPage {
constructor ( constructor (
private readonly route: ActivatedRoute, private readonly route: ActivatedRoute,
private readonly errToast: ErrorToastService,
private readonly patch: PatchDbModel, private readonly patch: PatchDbModel,
private readonly apiService: ApiService, private readonly apiService: ApiService,
) { } ) { }
@@ -33,6 +36,10 @@ export class AppMetricsPage {
this.startDaemon() this.startDaemon()
} }
ngAfterViewInit () {
this.content.scrollToPoint(undefined, 1)
}
ngOnDestroy () { ngOnDestroy () {
this.stopDaemon() this.stopDaemon()
} }
@@ -53,16 +60,14 @@ export class AppMetricsPage {
try { try {
this.metrics = await this.apiService.getPkgMetrics({ id: this.pkgId}) this.metrics = await this.apiService.getPkgMetrics({ id: this.pkgId})
} catch (e) { } catch (e) {
console.error(e)
this.errToast.present(e.message)
this.stopDaemon() this.stopDaemon()
await pauseFor(1000) } finally {
this.startDaemon() this.loading = false
} }
} }
ngAfterViewInit () {
this.content.scrollToPoint(undefined, 1)
}
asIsOrder (a: any, b: any) { asIsOrder (a: any, b: any) {
return 0 return 0
} }

View File

@@ -16,12 +16,8 @@
<text-spinner *ngIf="loading; else loaded" text="Loading Properties"></text-spinner> <text-spinner *ngIf="loading; else loaded" text="Loading Properties"></text-spinner>
<ng-template #loaded> <ng-template #loaded>
<ion-item *ngIf="error" style="margin-bottom: 16px;">
<ion-text class="ion-text-wrap" color="danger">{{ error }}</ion-text>
</ion-item>
<!-- not running --> <!-- not running -->
<ion-item *ngIf="notRunning" class="ion-margin-bottom"> <ion-item *ngIf="!running" class="ion-margin-bottom">
<ion-label class="ion-text-wrap"> <ion-label class="ion-text-wrap">
<p><ion-text color="warning">Service not running. Information on this page could be inaccurate.</ion-text></p> <p><ion-text color="warning">Service not running. Information on this page could be inaccurate.</ion-text></p>
</ion-label> </ion-label>

View File

@@ -7,9 +7,9 @@ import { AlertController, IonContent, NavController, PopoverController, ToastCon
import { PackageProperties } from 'src/app/util/properties.util' import { PackageProperties } from 'src/app/util/properties.util'
import { QRComponent } from 'src/app/components/qr/qr.component' import { QRComponent } from 'src/app/components/qr/qr.component'
import { PatchDbModel } from 'src/app/services/patch-db/patch-db.service' import { PatchDbModel } from 'src/app/services/patch-db/patch-db.service'
import * as JsonPointer from 'json-pointer'
import { FEStatus } from 'src/app/services/pkg-status-rendering.service'
import { PackageMainStatus } from 'src/app/services/patch-db/data-model' import { PackageMainStatus } from 'src/app/services/patch-db/data-model'
import { ErrorToastService } from 'src/app/services/error-toast.service'
import * as JsonPointer from 'json-pointer'
@Component({ @Component({
selector: 'app-properties', selector: 'app-properties',
@@ -17,11 +17,9 @@ import { PackageMainStatus } from 'src/app/services/patch-db/data-model'
styleUrls: ['./app-properties.page.scss'], styleUrls: ['./app-properties.page.scss'],
}) })
export class AppPropertiesPage { export class AppPropertiesPage {
error = ''
loading = true loading = true
pkgId: string pkgId: string
pointer: string pointer: string
qrCode: string
properties: PackageProperties properties: PackageProperties
node: PackageProperties node: PackageProperties
unmasked: { [key: string]: boolean } = { } unmasked: { [key: string]: boolean } = { }
@@ -33,15 +31,17 @@ export class AppPropertiesPage {
constructor ( constructor (
private readonly route: ActivatedRoute, private readonly route: ActivatedRoute,
private readonly apiService: ApiService, private readonly apiService: ApiService,
private readonly errToast: ErrorToastService,
private readonly alertCtrl: AlertController, private readonly alertCtrl: AlertController,
private readonly toastCtrl: ToastController, private readonly toastCtrl: ToastController,
private readonly popoverCtrl: PopoverController, private readonly popoverCtrl: PopoverController,
private readonly navCtrl: NavController, private readonly navCtrl: NavController,
public readonly patch: PatchDbModel, private readonly patch: PatchDbModel,
) { } ) { }
async ngOnInit () { async ngOnInit () {
this.pkgId = this.route.snapshot.paramMap.get('pkgId') this.pkgId = this.route.snapshot.paramMap.get('pkgId')
this.running = this.patch.data['package-data'][this.pkgId].installed?.status.main.status === PackageMainStatus.Running
await this.getProperties() await this.getProperties()
@@ -51,10 +51,6 @@ export class AppPropertiesPage {
this.pointer = queryParams['pointer'] this.pointer = queryParams['pointer']
this.node = JsonPointer.get(this.properties, this.pointer || '') this.node = JsonPointer.get(this.properties, this.pointer || '')
}), }),
this.patch.watch$('package-data', this.pkgId, 'installed', 'status', 'main', 'status')
.subscribe(status => {
this.running = status === PackageMainStatus.Running
}),
] ]
} }
@@ -116,10 +112,6 @@ export class AppPropertiesPage {
this.unmasked[key] = !this.unmasked[key] this.unmasked[key] = !this.unmasked[key]
} }
asIsOrder (a: any, b: any) {
return 0
}
private async getProperties (): Promise<void> { private async getProperties (): Promise<void> {
this.loading = true this.loading = true
try { try {
@@ -127,9 +119,13 @@ export class AppPropertiesPage {
this.node = JsonPointer.get(this.properties, this.pointer || '') this.node = JsonPointer.get(this.properties, this.pointer || '')
} catch (e) { } catch (e) {
console.error(e) console.error(e)
this.error = e.message this.errToast.present(e.message)
} finally { } finally {
this.loading = false this.loading = false
} }
} }
asIsOrder (a: any, b: any) {
return 0
}
} }

View File

@@ -6,6 +6,7 @@ import { RouterModule, Routes } from '@angular/router'
import { PwaBackComponentModule } from 'src/app/components/pwa-back-button/pwa-back.component.module' import { PwaBackComponentModule } from 'src/app/components/pwa-back-button/pwa-back.component.module'
import { BackupConfirmationComponentModule } from 'src/app/modals/backup-confirmation/backup-confirmation.component.module' import { BackupConfirmationComponentModule } from 'src/app/modals/backup-confirmation/backup-confirmation.component.module'
import { SharingModule } from 'src/app/modules/sharing.module' import { SharingModule } from 'src/app/modules/sharing.module'
import { TextSpinnerComponentModule } from 'src/app/components/text-spinner/text-spinner.component.module'
const routes: Routes = [ const routes: Routes = [
{ {
@@ -22,6 +23,7 @@ const routes: Routes = [
RouterModule.forChild(routes), RouterModule.forChild(routes),
BackupConfirmationComponentModule, BackupConfirmationComponentModule,
PwaBackComponentModule, PwaBackComponentModule,
TextSpinnerComponentModule,
], ],
declarations: [ declarations: [
AppRestorePage, AppRestorePage,

View File

@@ -14,20 +14,9 @@
<ion-content class="ion-padding-top"> <ion-content class="ion-padding-top">
<ion-grid *ngIf="loading; else loaded" style="height: 100%;"> <text-spinner *ngIf="loading; else loaded" text="Loading Drives"></text-spinner>
<ion-row class="ion-align-items-center ion-text-center" style="height: 100%;">
<ion-col>
<ion-spinner name="lines" color="warning"></ion-spinner>
<p>Loading Drives</p>
</ion-col>
</ion-row>
</ion-grid>
<ng-template #loaded> <ng-template #loaded>
<ion-item *ngIf="error" style="margin-bottom: 16px;">
<ion-text class="ion-text-wrap" color="danger">{{ error }}</ion-text>
</ion-item>
<ion-item class="ion-margin-bottom"> <ion-item class="ion-margin-bottom">
<ion-label class="ion-text-wrap"> <ion-label class="ion-text-wrap">
<p class="ion-padding-bottom"><ion-text color="warning">Warning</ion-text></p> <p class="ion-padding-bottom"><ion-text color="warning">Warning</ion-text></p>

View File

@@ -11,10 +11,6 @@
</ion-header> </ion-header>
<ion-content class="ion-padding-top"> <ion-content class="ion-padding-top">
<ion-item *ngIf="error" style="margin-bottom: 16px;">
<ion-text class="ion-text-wrap" color="danger">{{ error }}</ion-text>
</ion-item>
<ng-container *ngIf="pkg"> <ng-container *ngIf="pkg">
<!-- top plate --> <!-- top plate -->
<div class="top-plate"> <div class="top-plate">

View File

@@ -12,6 +12,7 @@ import { PatchDbModel } from 'src/app/services/patch-db/patch-db.service'
import { DependencyErrorConfigUnsatisfied, DependencyErrorNotInstalled, DependencyErrorType, PackageDataEntry, PackageState } from 'src/app/services/patch-db/data-model' import { DependencyErrorConfigUnsatisfied, DependencyErrorNotInstalled, DependencyErrorType, PackageDataEntry, PackageState } from 'src/app/services/patch-db/data-model'
import { FEStatus, PkgStatusRendering, renderPkgStatus } from 'src/app/services/pkg-status-rendering.service' import { FEStatus, PkgStatusRendering, renderPkgStatus } from 'src/app/services/pkg-status-rendering.service'
import { ConnectionService } from 'src/app/services/connection.service' import { ConnectionService } from 'src/app/services/connection.service'
import { ErrorToastService } from 'src/app/services/error-toast.service'
@Component({ @Component({
selector: 'app-show', selector: 'app-show',
@@ -19,7 +20,6 @@ import { ConnectionService } from 'src/app/services/connection.service'
styleUrls: ['./app-show.page.scss'], styleUrls: ['./app-show.page.scss'],
}) })
export class AppShowPage { export class AppShowPage {
error: string
pkgId: string pkgId: string
pkg: PackageDataEntry pkg: PackageDataEntry
hideLAN: boolean hideLAN: boolean
@@ -37,6 +37,7 @@ export class AppShowPage {
private readonly alertCtrl: AlertController, private readonly alertCtrl: AlertController,
private readonly route: ActivatedRoute, private readonly route: ActivatedRoute,
private readonly navCtrl: NavController, private readonly navCtrl: NavController,
private readonly errToast: ErrorToastService,
private readonly loader: LoaderService, private readonly loader: LoaderService,
private readonly modalCtrl: ModalController, private readonly modalCtrl: ModalController,
private readonly apiService: ApiService, private readonly apiService: ApiService,
@@ -62,9 +63,9 @@ export class AppShowPage {
this.setButtons() this.setButtons()
} }
ngAfterViewInit () { // ngAfterViewInit () {
this.content.scrollToPoint(undefined, 1) // this.content.scrollToPoint(undefined, 1)
} // }
ngOnDestroy () { ngOnDestroy () {
this.subs.forEach(sub => sub.unsubscribe()) this.subs.forEach(sub => sub.unsubscribe())
@@ -215,23 +216,24 @@ export class AppShowPage {
} }
private setError (e: Error): Observable<void> { private setError (e: Error): Observable<void> {
this.error = e.message console.error(e)
this.errToast.present(e.message)
return of() return of()
} }
setButtons (): void { setButtons (): void {
this.buttons = [ this.buttons = [
{ {
action: () => this.navCtrl.navigateForward(['metrics'], { relativeTo: this.route }), action: () => this.navCtrl.navigateForward(['instructions'], { relativeTo: this.route }),
title: 'Health', title: 'Instructions',
icon: 'medkit-outline', icon: 'list-outline',
color: 'danger', color: 'danger',
disabled: [], disabled: [],
}, },
{ {
action: () => this.navCtrl.navigateForward(['instructions'], { relativeTo: this.route }), action: () => this.navCtrl.navigateForward(['metrics'], { relativeTo: this.route }),
title: 'Instructions', title: 'Monitor',
icon: 'list-outline', icon: 'medkit-outline',
color: 'danger', color: 'danger',
disabled: [], disabled: [],
}, },

View File

@@ -14,11 +14,6 @@
<text-spinner *ngIf="pageLoading; else pageLoaded" [text]="'loading marketplace'"></text-spinner> <text-spinner *ngIf="pageLoading; else pageLoaded" [text]="'loading marketplace'"></text-spinner>
<ng-template #pageLoaded> <ng-template #pageLoaded>
<ion-item *ngIf="error" style="margin-bottom: 16px;">
<ion-text class="ion-text-wrap" color="danger">{{ error }}</ion-text>
</ion-item>
<ion-card *ngIf="eos" class="eos-card" (click)="updateEos()"> <ion-card *ngIf="eos" class="eos-card" (click)="updateEos()">
<ion-card-header> <ion-card-header>
<ion-card-subtitle>Now Available...</ion-card-subtitle> <ion-card-subtitle>Now Available...</ion-card-subtitle>
@@ -50,7 +45,7 @@
<ng-template #loaded> <ng-template #loaded>
<ion-grid> <ion-grid>
<ion-row> <ion-row>
<ion-col *ngFor="let pkg of pkgs" sizeSm="12" sizeMd="6"> <ion-col *ngFor="let pkg of pkgs" sizeXs="12" sizeSm="12" sizeMd="6">
<ion-item [routerLink]="['/marketplace', pkg.id]"> <ion-item [routerLink]="['/marketplace', pkg.id]">
<ion-thumbnail slot="start"> <ion-thumbnail slot="start">
<img [src]="pkg.icon" /> <img [src]="pkg.icon" />

View File

@@ -23,4 +23,5 @@
.cat-selected { .cat-selected {
border-width: 0 0 1px 0; border-width: 0 0 1px 0;
border-style: solid; border-style: solid;
border-color: var(--ion-color-primary);
} }

View File

@@ -5,8 +5,9 @@ import { wizardModal } from 'src/app/components/install-wizard/install-wizard.co
import { IonContent, ModalController } from '@ionic/angular' import { IonContent, ModalController } from '@ionic/angular'
import { WizardBaker } from 'src/app/components/install-wizard/prebaked-wizards' import { WizardBaker } from 'src/app/components/install-wizard/prebaked-wizards'
import { PatchDbModel } from 'src/app/services/patch-db/patch-db.service' import { PatchDbModel } from 'src/app/services/patch-db/patch-db.service'
import { PackageDataEntry, PackageState } from 'src/app/services/patch-db/data-model' import { PackageState } from 'src/app/services/patch-db/data-model'
import { Subscription } from 'rxjs' import { Subscription } from 'rxjs'
import { ErrorToastService } from 'src/app/services/error-toast.service'
@Component({ @Component({
selector: 'marketplace-list', selector: 'marketplace-list',
@@ -17,7 +18,6 @@ export class MarketplaceListPage {
@ViewChild(IonContent) content: IonContent @ViewChild(IonContent) content: IonContent
pageLoading = true pageLoading = true
pkgsLoading = true pkgsLoading = true
error = ''
category = 'all' category = 'all'
query: string query: string
@@ -37,6 +37,7 @@ export class MarketplaceListPage {
constructor ( constructor (
private readonly apiService: ApiService, private readonly apiService: ApiService,
private readonly modalCtrl: ModalController, private readonly modalCtrl: ModalController,
private readonly errToast: ErrorToastService,
private readonly wizardBaker: WizardBaker, private readonly wizardBaker: WizardBaker,
public readonly patch: PatchDbModel, public readonly patch: PatchDbModel,
) { } ) { }
@@ -55,7 +56,7 @@ export class MarketplaceListPage {
this.pkgs = pkgs this.pkgs = pkgs
} catch (e) { } catch (e) {
console.error(e) console.error(e)
this.error = e.message this.errToast.present(e.message)
} finally { } finally {
this.pageLoading = false this.pageLoading = false
this.pkgsLoading = false this.pkgsLoading = false
@@ -106,7 +107,7 @@ export class MarketplaceListPage {
return pkgs return pkgs
} catch (e) { } catch (e) {
console.error(e) console.error(e)
this.error = e.message this.errToast.present(e.message)
} finally { } finally {
this.pkgsLoading = false this.pkgsLoading = false
} }

View File

@@ -12,213 +12,211 @@
<ion-content class="ion-padding"> <ion-content class="ion-padding">
<text-spinner *ngIf="!marketplaceService.pkgs[pkgId]" text="Loading Package"></text-spinner> <text-spinner *ngIf="loading; else loaded" text="Loading Package"></text-spinner>
<ng-container *ngIf="marketplaceService.pkgs[pkgId] as pkg"> <ng-template #loaded>
<ion-item *ngIf="error" style="margin-bottom: 16px;"> <ng-container *ngIf="marketplaceService.pkgs[pkgId] as pkg">
<ion-text class="ion-text-wrap" color="danger">{{ error }}</ion-text>
</ion-item>
<ion-grid>
<ion-row>
<ion-col sizeXl="9" sizeLg="9" sizeMd="9" sizeSm="12" sizeXs="12">
<div class="header">
<img [src]="pkg.icon" />
<div class="header-text">
<h1 class="header-title">{{ pkg.manifest.title }}</h1>
<p class="header-version">{{ pkg.manifest.version | displayEmver }}</p>
<div class="header-status">
<!-- no installedPkg -->
<p *ngIf="!installedPkg; else local">
<ion-text color="medium">Not Installed</ion-text>
</p>
<!-- installedPkg -->
<ng-template #local>
<p *ngIf="installedPkg.state !== PackageState.Installed; else installed">
<!-- installing, updating, removing -->
<ion-text [color]="installedPkg.state === PackageState.Removing ? 'danger' : 'primary'">{{ installedPkg.state }}</ion-text>
<ion-spinner class="dots dots-medium" name="dots" [color]="installedPkg.state === PackageState.Removing ? 'danger' : 'primary'"></ion-spinner>
</p>
<!-- installed -->
<ng-template #installed>
<p>
<ion-text color="medium">Installed at {{ installedPkg.manifest.version | displayEmver }}</ion-text>
</p>
</ng-template>
</ng-template>
</div>
</div>
</div>
</ion-col>
<ion-col sizeXl="3" sizeLg="3" sizeMd="3" sizeSm="12" sizeXs="12" class="ion-align-self-center">
<!-- no installedPkg -->
<ion-button *ngIf="!installedPkg; else installedPkg2" class="main-action-button" expand="block" (click)="install()">
Install
</ion-button>
<!-- installedPkg -->
<ng-template #installedPkg2>
<!-- not installing, updating, or removing -->
<ng-container *ngIf="installedPkg.state === PackageState.Installed">
<ion-button *ngIf="(installedPkg.manifest.version | compareEmver : pkg.manifest.version) === -1" class="main-action-button" expand="block" (click)="update('update')">
Update
</ion-button>
<ion-button *ngIf="(installedPkg.manifest.version | compareEmver : pkg.manifest.version) === 1" class="main-action-button" expand="block" color="warning" (click)="update('downgrade')">
Downgrade
</ion-button>
</ng-container>
</ng-template>
</ion-col>
</ion-row>
</ion-grid>
<!-- recommendation -->
<ion-item *ngIf="rec && showRec" class="rec-item">
<ion-label class="ion-text-wrap">
<h2 style="display: flex; align-items: center;">
<ion-avatar style="height: 3vh; width: 3vh; margin: 5px" slot="start">
<img [src]="rec.dependentIcon" [alt]="rec.dependentTitle"/>
</ion-avatar>
<ion-text style="margin: 5px; font-family: 'Montserrat'; font-size: smaller;">{{ rec.dependentTitle }}</ion-text>
</h2>
<div style="margin: 7px 5px;">
<p style="color: var(--ion-color-dark); font-size: small">{{ rec.description }}</p>
<p *ngIf="pkg.manifest.version | satisfiesEmver: rec.version" class="recommendation-text">{{ pkg.manifest.title }} version {{ pkg.manifest.version | displayEmver }} is compatible.</p>
<p *ngIf="!(pkg.manifest.version | satisfiesEmver: rec.version)" class="recommendation-text recommendation-error">{{ pkg.manifest.title }} version {{ pkg.manifest.version | displayEmver }} is NOT compatible.</p>
<ion-button style="position: absolute; right: 0; top: 0" color="primary" fill="clear" (click)="dismissRec()">
<ion-icon name="close-outline"></ion-icon>
</ion-button>
</div>
</ion-label>
</ion-item>
<ion-item-group>
<!-- release notes -->
<ion-item-divider>
New in {{ pkg.manifest.version | displayEmver }}
<ion-button [routerLink]="['notes']" style="position: absolute; right: 10px;" fill="clear" color="dark">
All Release Notes
<ion-icon slot="end" name="arrow-forward-outline"></ion-icon>
</ion-button>
</ion-item-divider>
<ion-item lines="none" color="transparent">
<ion-label class="ion-text-wrap" >
<div id='release-notes' [innerHTML]="pkg.manifest['release-notes'] | markdown"></div>
</ion-label>
</ion-item>
<!-- description -->
<ion-item-divider>Description</ion-item-divider>
<ion-item lines="none" color="transparent">
<ion-label class="ion-text-wrap">
<div id="release-notes" class="release-notes">{{ pkg.manifest.description.long }}</div>
</ion-label>
</ion-item>
<!-- dependencies -->
<ng-container *ngIf="!(pkg.manifest.dependencies | empty)">
<ion-item-divider>Dependencies</ion-item-divider>
<ion-grid>
<ion-row>
<ion-col *ngFor="let dep of pkg.manifest.dependencies | keyvalue" sizeSm="12" sizeMd="6">
<ion-item *ngIf="!dep.value.optional" [routerLink]="['/marketplace', dep.key]">
<ion-thumbnail slot="start">
<img [src]="pkg['dependency-metadata'][dep.key].icon" />
</ion-thumbnail>
<ion-label class="ion-text-wrap">
<h2>
{{ pkg['dependency-metadata'][dep.key].title }}
<span *ngIf="dep.value.recommended"> (recommended)</span>
</h2>
<p style="font-size: small">{{ dep.value.version | displayEmver }}</p>
<p>{{ dep.value.description }}</p>
</ion-label>
</ion-item>
</ion-col>
</ion-row>
</ion-grid>
</ng-container>
</ion-item-group>
<ion-item-divider>Additional Info</ion-item-divider>
<ion-card>
<ion-grid> <ion-grid>
<ion-row> <ion-row>
<ion-col sizeSm="12" sizeMd="6"> <ion-col sizeXs="12" sizeSm="12" sizeMd="9" sizeLg="9" sizeXl="9">
<ion-item-group> <div class="header">
<ion-item detail="false"> <img [src]="pkg.icon" />
<ion-label> <div class="header-text">
<h2>Service ID</h2> <h1 class="header-title">{{ pkg.manifest.title }}</h1>
<p>{{ pkg.manifest.id }}</p> <p class="header-version">{{ pkg.manifest.version | displayEmver }}</p>
</ion-label> <div class="header-status">
</ion-item> <!-- no installedPkg -->
<ion-item detail="false"> <p *ngIf="!installedPkg; else local">
<ion-label> <ion-text color="medium">Not Installed</ion-text>
<h2>Categories</h2> </p>
<p>{{ pkg.categories.join(', ') }}</p> <!-- installedPkg -->
</ion-label> <ng-template #local>
</ion-item> <p *ngIf="installedPkg.state !== PackageState.Installed; else installed">
<ion-item button detail="false" (click)="presentAlertVersions()"> <!-- installing, updating, removing -->
<ion-label> <ion-text [color]="installedPkg.state === PackageState.Removing ? 'danger' : 'primary'">{{ installedPkg.state }}</ion-text>
<h2>Other Versions</h2> <ion-spinner class="dots dots-medium" name="dots" [color]="installedPkg.state === PackageState.Removing ? 'danger' : 'primary'"></ion-spinner>
<p>Click to view other versions</p> </p>
</ion-label> <!-- installed -->
<ion-icon slot="end" name="chevron-forward-outline"></ion-icon> <ng-template #installed>
</ion-item> <p>
<ion-item button detail="false" (click)="presentModalMd('license')"> <ion-text color="medium">Installed at {{ installedPkg.manifest.version | displayEmver }}</ion-text>
<ion-label> </p>
<h2>License</h2> </ng-template>
<p>{{ pkg.manifest.license }}</p> </ng-template>
</ion-label> </div>
<ion-icon slot="end" name="chevron-forward-outline"></ion-icon> </div>
</ion-item> </div>
<ion-item button detail="false" (click)="presentModalMd('instructions')">
<ion-label>
<h2>Instructions</h2>
<p>Click to view instructions</p>
</ion-label>
<ion-icon slot="end" name="chevron-forward-outline"></ion-icon>
</ion-item>
</ion-item-group>
</ion-col> </ion-col>
<ion-col sizeSm="12" sizeMd="6"> <ion-col sizeXl="3" sizeLg="3" sizeMd="3" sizeSm="12" sizeXs="12" class="ion-align-self-center">
<ion-item-group> <!-- no installedPkg -->
<ion-item [href]="pkg.manifest['upstream-repo']" target="_blank" detail="false"> <ion-button *ngIf="!installedPkg; else installedPkg2" class="main-action-button" expand="block" (click)="install()">
<ion-label> Install
<h2>Source Repository</h2> </ion-button>
<p>{{ pkg.manifest['upstream-repo'] }}</p> <!-- installedPkg -->
</ion-label> <ng-template #installedPkg2>
<ion-icon slot="end" name="open-outline"></ion-icon> <!-- not installing, updating, or removing -->
</ion-item> <ng-container *ngIf="installedPkg.state === PackageState.Installed">
<ion-item [href]="pkg.manifest['wrapper-repo']" target="_blank" detail="false"> <ion-button *ngIf="(installedPkg.manifest.version | compareEmver : pkg.manifest.version) === -1" class="main-action-button" expand="block" (click)="update('update')">
<ion-label> Update
<h2>Wrapper Repository</h2> </ion-button>
<p>{{ pkg.manifest['wrapper-repo'] }}</p> <ion-button *ngIf="(installedPkg.manifest.version | compareEmver : pkg.manifest.version) === 1" class="main-action-button" expand="block" color="warning" (click)="update('downgrade')">
</ion-label> Downgrade
<ion-icon slot="end" name="open-outline"></ion-icon> </ion-button>
</ion-item> </ng-container>
<ion-item [href]="pkg.manifest['support-site']" target="_blank" detail="false"> </ng-template>
<ion-label>
<h2>Support Site</h2>
<p>{{ pkg.manifest['support-site'] }}</p>
</ion-label>
<ion-icon slot="end" name="open-outline"></ion-icon>
</ion-item>
<ion-item [href]="pkg.manifest['marketing-site']" target="_blank" detail="false">
<ion-label>
<h2>Marketing Site</h2>
<p>{{ pkg.manifest['marketing-site'] }}</p>
</ion-label>
<ion-icon slot="end" name="open-outline"></ion-icon>
</ion-item>
<ion-item *ngIf="pkg.manifest['donation-url'] as donationUrl" [href]="donationUrl" target="_blank" detail="false">
<ion-label>
<h2>Donation Site</h2>
<p>{{ donationUrl }}</p>
</ion-label>
<ion-icon slot="end" name="open-outline"></ion-icon>
</ion-item>
</ion-item-group>
</ion-col> </ion-col>
</ion-row> </ion-row>
</ion-grid> </ion-grid>
</ion-card>
</ng-container> <!-- recommendation -->
<ion-item *ngIf="rec && showRec" class="rec-item">
<ion-label class="ion-text-wrap">
<h2 style="display: flex; align-items: center;">
<ion-avatar style="height: 3vh; width: 3vh; margin: 5px" slot="start">
<img [src]="rec.dependentIcon" [alt]="rec.dependentTitle"/>
</ion-avatar>
<ion-text style="margin: 5px; font-family: 'Montserrat'; font-size: smaller;">{{ rec.dependentTitle }}</ion-text>
</h2>
<div style="margin: 7px 5px;">
<p style="color: var(--ion-color-dark); font-size: small">{{ rec.description }}</p>
<p *ngIf="pkg.manifest.version | satisfiesEmver: rec.version" class="recommendation-text">{{ pkg.manifest.title }} version {{ pkg.manifest.version | displayEmver }} is compatible.</p>
<p *ngIf="!(pkg.manifest.version | satisfiesEmver: rec.version)" class="recommendation-text recommendation-error">{{ pkg.manifest.title }} version {{ pkg.manifest.version | displayEmver }} is NOT compatible.</p>
<ion-button style="position: absolute; right: 0; top: 0" color="primary" fill="clear" (click)="dismissRec()">
<ion-icon name="close-outline"></ion-icon>
</ion-button>
</div>
</ion-label>
</ion-item>
<ion-item-group>
<!-- release notes -->
<ion-item-divider>
New in {{ pkg.manifest.version | displayEmver }}
<ion-button [routerLink]="['notes']" style="position: absolute; right: 10px;" fill="clear" color="dark">
All Release Notes
<ion-icon slot="end" name="arrow-forward-outline"></ion-icon>
</ion-button>
</ion-item-divider>
<ion-item lines="none" color="transparent">
<ion-label class="ion-text-wrap" >
<div id='release-notes' [innerHTML]="pkg.manifest['release-notes'] | markdown"></div>
</ion-label>
</ion-item>
<!-- description -->
<ion-item-divider>Description</ion-item-divider>
<ion-item lines="none" color="transparent">
<ion-label class="ion-text-wrap">
<div id="release-notes" class="release-notes">{{ pkg.manifest.description.long }}</div>
</ion-label>
</ion-item>
<!-- dependencies -->
<ng-container *ngIf="!(pkg.manifest.dependencies | empty)">
<ion-item-divider>Dependencies</ion-item-divider>
<ion-grid>
<ion-row>
<ion-col *ngFor="let dep of pkg.manifest.dependencies | keyvalue" sizeSm="12" sizeMd="6">
<ion-item *ngIf="!dep.value.optional" [routerLink]="['/marketplace', dep.key]">
<ion-thumbnail slot="start">
<img [src]="pkg['dependency-metadata'][dep.key].icon" />
</ion-thumbnail>
<ion-label class="ion-text-wrap">
<h2>
{{ pkg['dependency-metadata'][dep.key].title }}
<span *ngIf="dep.value.recommended"> (recommended)</span>
</h2>
<p style="font-size: small">{{ dep.value.version | displayEmver }}</p>
<p>{{ dep.value.description }}</p>
</ion-label>
</ion-item>
</ion-col>
</ion-row>
</ion-grid>
</ng-container>
</ion-item-group>
<ion-item-divider>Additional Info</ion-item-divider>
<ion-card>
<ion-grid>
<ion-row>
<ion-col sizeSm="12" sizeMd="6">
<ion-item-group>
<ion-item detail="false">
<ion-label>
<h2>Service ID</h2>
<p>{{ pkg.manifest.id }}</p>
</ion-label>
</ion-item>
<ion-item detail="false">
<ion-label>
<h2>Categories</h2>
<p>{{ pkg.categories.join(', ') }}</p>
</ion-label>
</ion-item>
<ion-item button detail="false" (click)="presentAlertVersions()">
<ion-label>
<h2>Other Versions</h2>
<p>Click to view other versions</p>
</ion-label>
<ion-icon slot="end" name="chevron-forward-outline"></ion-icon>
</ion-item>
<ion-item button detail="false" (click)="presentModalMd('license')">
<ion-label>
<h2>License</h2>
<p>{{ pkg.manifest.license }}</p>
</ion-label>
<ion-icon slot="end" name="chevron-forward-outline"></ion-icon>
</ion-item>
<ion-item button detail="false" (click)="presentModalMd('instructions')">
<ion-label>
<h2>Instructions</h2>
<p>Click to view instructions</p>
</ion-label>
<ion-icon slot="end" name="chevron-forward-outline"></ion-icon>
</ion-item>
</ion-item-group>
</ion-col>
<ion-col sizeSm="12" sizeMd="6">
<ion-item-group>
<ion-item [href]="pkg.manifest['upstream-repo']" target="_blank" detail="false">
<ion-label>
<h2>Source Repository</h2>
<p>{{ pkg.manifest['upstream-repo'] }}</p>
</ion-label>
<ion-icon slot="end" name="open-outline"></ion-icon>
</ion-item>
<ion-item [href]="pkg.manifest['wrapper-repo']" target="_blank" detail="false">
<ion-label>
<h2>Wrapper Repository</h2>
<p>{{ pkg.manifest['wrapper-repo'] }}</p>
</ion-label>
<ion-icon slot="end" name="open-outline"></ion-icon>
</ion-item>
<ion-item [href]="pkg.manifest['support-site']" target="_blank" detail="false">
<ion-label>
<h2>Support Site</h2>
<p>{{ pkg.manifest['support-site'] }}</p>
</ion-label>
<ion-icon slot="end" name="open-outline"></ion-icon>
</ion-item>
<ion-item [href]="pkg.manifest['marketing-site']" target="_blank" detail="false">
<ion-label>
<h2>Marketing Site</h2>
<p>{{ pkg.manifest['marketing-site'] }}</p>
</ion-label>
<ion-icon slot="end" name="open-outline"></ion-icon>
</ion-item>
<ion-item *ngIf="pkg.manifest['donation-url'] as donationUrl" [href]="donationUrl" target="_blank" detail="false">
<ion-label>
<h2>Donation Site</h2>
<p>{{ donationUrl }}</p>
</ion-label>
<ion-icon slot="end" name="open-outline"></ion-icon>
</ion-item>
</ion-item-group>
</ion-col>
</ion-row>
</ion-grid>
</ion-card>
</ng-container>
</ng-template>
</ion-content> </ion-content>

View File

@@ -1,12 +1,13 @@
import { Component, ViewChild } from '@angular/core' import { Component, ViewChild } from '@angular/core'
import { ActivatedRoute } from '@angular/router' import { ActivatedRoute } from '@angular/router'
import { AlertController, IonContent, ModalController, NavController } from '@ionic/angular' import { AlertController, IonContent, ModalController, NavController, ToastController } from '@ionic/angular'
import { wizardModal } from 'src/app/components/install-wizard/install-wizard.component' import { wizardModal } from 'src/app/components/install-wizard/install-wizard.component'
import { WizardBaker } from 'src/app/components/install-wizard/prebaked-wizards' import { WizardBaker } from 'src/app/components/install-wizard/prebaked-wizards'
import { Emver } from 'src/app/services/emver.service' import { Emver } from 'src/app/services/emver.service'
import { displayEmver } from 'src/app/pipes/emver.pipe' import { displayEmver } from 'src/app/pipes/emver.pipe'
import { pauseFor, Recommendation } from 'src/app/util/misc.util' import { pauseFor, Recommendation } from 'src/app/util/misc.util'
import { PatchDbModel } from 'src/app/services/patch-db/patch-db.service' import { PatchDbModel } from 'src/app/services/patch-db/patch-db.service'
import { ErrorToastService } from 'src/app/services/error-toast.service'
import { PackageDataEntry, PackageState } from 'src/app/services/patch-db/data-model' import { PackageDataEntry, PackageState } from 'src/app/services/patch-db/data-model'
import { MarketplaceService } from '../marketplace.service' import { MarketplaceService } from '../marketplace.service'
import { Subscription } from 'rxjs' import { Subscription } from 'rxjs'
@@ -19,7 +20,7 @@ import { MarkdownPage } from 'src/app/modals/markdown/markdown.page'
}) })
export class MarketplaceShowPage { export class MarketplaceShowPage {
@ViewChild(IonContent) content: IonContent @ViewChild(IonContent) content: IonContent
error = '' loading = true
pkgId: string pkgId: string
installedPkg: PackageDataEntry installedPkg: PackageDataEntry
PackageState = PackageState PackageState = PackageState
@@ -32,11 +33,12 @@ export class MarketplaceShowPage {
private readonly route: ActivatedRoute, private readonly route: ActivatedRoute,
private readonly alertCtrl: AlertController, private readonly alertCtrl: AlertController,
private readonly modalCtrl: ModalController, private readonly modalCtrl: ModalController,
private readonly errToast: ErrorToastService,
private readonly wizardBaker: WizardBaker, private readonly wizardBaker: WizardBaker,
private readonly navCtrl: NavController, private readonly navCtrl: NavController,
private readonly emver: Emver, private readonly emver: Emver,
private readonly patch: PatchDbModel, private readonly patch: PatchDbModel,
public readonly marketplaceService: MarketplaceService, private readonly marketplaceService: MarketplaceService,
) { } ) { }
async ngOnInit () { async ngOnInit () {
@@ -66,7 +68,10 @@ export class MarketplaceShowPage {
await this.marketplaceService.setPkg(this.pkgId, version) await this.marketplaceService.setPkg(this.pkgId, version)
} catch (e) { } catch (e) {
console.error(e) console.error(e)
this.error = e.message this.errToast.present(e.message)
} finally {
await pauseFor(100)
this.loading = false
} }
} }

View File

@@ -11,15 +11,10 @@
</ion-header> </ion-header>
<ion-content> <ion-content>
<ion-refresher slot="fixed" (ionRefresh)="doRefresh($event)"> <ion-refresher slot="fixed" (ionRefresh)="doRefresh($event)">
<ion-refresher-content pullingIcon="lines" refreshingSpinner="lines"></ion-refresher-content> <ion-refresher-content pullingIcon="lines" refreshingSpinner="lines"></ion-refresher-content>
</ion-refresher> </ion-refresher>
<ion-item *ngIf="error" style="margin-bottom: 16px;">
<ion-text class="ion-text-wrap" color="danger">{{ error }}</ion-text>
</ion-item>
<ion-spinner *ngIf="loading" class="center" name="lines" color="warning"></ion-spinner> <ion-spinner *ngIf="loading" class="center" name="lines" color="warning"></ion-spinner>
<ion-item-group *ngIf="!notifications.length && !loading"> <ion-item-group *ngIf="!notifications.length && !loading">

View File

@@ -4,6 +4,7 @@ import { LoaderService } from 'src/app/services/loader.service'
import { ServerNotification, ServerNotifications } from 'src/app/services/api/api-types' import { ServerNotification, ServerNotifications } from 'src/app/services/api/api-types'
import { AlertController } from '@ionic/angular' import { AlertController } from '@ionic/angular'
import { ActivatedRoute } from '@angular/router' import { ActivatedRoute } from '@angular/router'
import { ErrorToastService } from 'src/app/services/error-toast.service'
@Component({ @Component({
selector: 'notifications', selector: 'notifications',
@@ -11,7 +12,6 @@ import { ActivatedRoute } from '@angular/router'
styleUrls: ['notifications.page.scss'], styleUrls: ['notifications.page.scss'],
}) })
export class NotificationsPage { export class NotificationsPage {
error = ''
loading = true loading = true
notifications: ServerNotifications = [] notifications: ServerNotifications = []
page = 1 page = 1
@@ -22,6 +22,7 @@ export class NotificationsPage {
constructor ( constructor (
private readonly apiService: ApiService, private readonly apiService: ApiService,
private readonly loader: LoaderService, private readonly loader: LoaderService,
private readonly errToast: ErrorToastService,
private readonly alertCtrl: AlertController, private readonly alertCtrl: AlertController,
private readonly route: ActivatedRoute, private readonly route: ActivatedRoute,
) { } ) { }
@@ -50,10 +51,9 @@ export class NotificationsPage {
notifications = await this.apiService.getNotifications({ page: this.page, 'per-page': this.perPage }) notifications = await this.apiService.getNotifications({ page: this.page, 'per-page': this.perPage })
this.needInfinite = notifications.length >= this.perPage this.needInfinite = notifications.length >= this.perPage
this.page++ this.page++
this.error = ''
} catch (e) { } catch (e) {
console.error(e) console.error(e)
this.error = e.message this.errToast.present(e.message)
} finally { } finally {
return notifications return notifications
} }
@@ -67,11 +67,10 @@ export class NotificationsPage {
}).displayDuringP( }).displayDuringP(
this.apiService.deleteNotification({ id }).then(() => { this.apiService.deleteNotification({ id }).then(() => {
this.notifications.splice(index, 1) this.notifications.splice(index, 1)
this.error = ''
}), }),
).catch(e => { ).catch(e => {
console.error(e) console.error(e)
this.error = e.message this.errToast.present(e.message)
}) })
} }

View File

@@ -5,6 +5,7 @@ import { RouterModule, Routes } from '@angular/router'
import { DevSSHKeysPage } from './dev-ssh-keys.page' import { DevSSHKeysPage } from './dev-ssh-keys.page'
import { PwaBackComponentModule } from 'src/app/components/pwa-back-button/pwa-back.component.module' import { PwaBackComponentModule } from 'src/app/components/pwa-back-button/pwa-back.component.module'
import { SharingModule } from 'src/app/modules/sharing.module' import { SharingModule } from 'src/app/modules/sharing.module'
import { TextSpinnerComponentModule } from 'src/app/components/text-spinner/text-spinner.component.module'
const routes: Routes = [ const routes: Routes = [
{ {
@@ -20,6 +21,7 @@ const routes: Routes = [
RouterModule.forChild(routes), RouterModule.forChild(routes),
PwaBackComponentModule, PwaBackComponentModule,
SharingModule, SharingModule,
TextSpinnerComponentModule,
], ],
declarations: [DevSSHKeysPage], declarations: [DevSSHKeysPage],
}) })

View File

@@ -13,11 +13,7 @@
</ion-header> </ion-header>
<ion-content class="ion-padding-top"> <ion-content class="ion-padding-top">
<ion-item *ngIf="error" class="ion-margin-bottom"> <text-spinner *ngIf="loading" text="Loading Keys"></text-spinner>
<ion-text class="ion-text-wrap" color="danger">{{ error }}</ion-text>
</ion-item>
<ion-spinner *ngIf="loading" class="center" name="lines" color="warning"></ion-spinner>
<ion-item-group> <ion-item-group>
<ion-item-divider>Saved Keys</ion-item-divider> <ion-item-divider>Saved Keys</ion-item-divider>

View File

@@ -5,6 +5,7 @@ import { LoaderService } from 'src/app/services/loader.service'
import { SSHService } from './ssh.service' import { SSHService } from './ssh.service'
import { Subscription } from 'rxjs' import { Subscription } from 'rxjs'
import { SSHKeys } from 'src/app/services/api/api-types' import { SSHKeys } from 'src/app/services/api/api-types'
import { ErrorToastService } from 'src/app/services/error-toast.service'
@Component({ @Component({
selector: 'dev-ssh-keys', selector: 'dev-ssh-keys',
@@ -12,21 +13,19 @@ import { SSHKeys } from 'src/app/services/api/api-types'
styleUrls: ['dev-ssh-keys.page.scss'], styleUrls: ['dev-ssh-keys.page.scss'],
}) })
export class DevSSHKeysPage { export class DevSSHKeysPage {
error = ''
loading = true loading = true
sshKeys: SSHKeys sshKeys: SSHKeys
subs: Subscription[] = [] subs: Subscription[] = []
constructor ( constructor (
private readonly loader: LoaderService, private readonly loader: LoaderService,
private readonly errToast: ErrorToastService,
private readonly serverConfigService: ServerConfigService, private readonly serverConfigService: ServerConfigService,
private readonly alertCtrl: AlertController, private readonly alertCtrl: AlertController,
private readonly sshService: SSHService, private readonly sshService: SSHService,
) { } ) { }
async ngOnInit () { async ngOnInit () {
await this.sshService.getKeys()
this.subs = [ this.subs = [
this.sshService.watch$() this.sshService.watch$()
.subscribe(keys => { .subscribe(keys => {
@@ -34,6 +33,8 @@ export class DevSSHKeysPage {
}), }),
] ]
await this.sshService.getKeys()
this.loading = false this.loading = false
} }
@@ -68,7 +69,6 @@ export class DevSSHKeysPage {
} }
async delete (hash: string): Promise<void> { async delete (hash: string): Promise<void> {
this.error = ''
this.loader.of({ this.loader.of({
message: 'Deleting...', message: 'Deleting...',
spinner: 'lines', spinner: 'lines',
@@ -77,7 +77,7 @@ export class DevSSHKeysPage {
await this.sshService.delete(hash) await this.sshService.delete(hash)
}).catch(e => { }).catch(e => {
console.error(e) console.error(e)
this.error = '' this.errToast.present(e.message)
}) })
} }

View File

@@ -5,6 +5,7 @@ import { ServerBackupPage } from './server-backup.page'
import { RouterModule, Routes } from '@angular/router' import { RouterModule, Routes } from '@angular/router'
import { BackupConfirmationComponentModule } from 'src/app/modals/backup-confirmation/backup-confirmation.component.module' import { BackupConfirmationComponentModule } from 'src/app/modals/backup-confirmation/backup-confirmation.component.module'
import { PwaBackComponentModule } from 'src/app/components/pwa-back-button/pwa-back.component.module' import { PwaBackComponentModule } from 'src/app/components/pwa-back-button/pwa-back.component.module'
import { TextSpinnerComponentModule } from 'src/app/components/text-spinner/text-spinner.component.module'
const routes: Routes = [ const routes: Routes = [
{ {
@@ -20,6 +21,7 @@ const routes: Routes = [
RouterModule.forChild(routes), RouterModule.forChild(routes),
BackupConfirmationComponentModule, BackupConfirmationComponentModule,
PwaBackComponentModule, PwaBackComponentModule,
TextSpinnerComponentModule,
], ],
declarations: [ declarations: [
ServerBackupPage, ServerBackupPage,

View File

@@ -13,12 +13,7 @@
</ion-header> </ion-header>
<ion-content class="ion-padding-top"> <ion-content class="ion-padding-top">
<text-spinner *ngIf="loading; else loaded" text="Loading Drives"></text-spinner>
<ion-item *ngIf="error" style="margin-bottom: 16px;">
<ion-text class="ion-text-wrap" color="danger">{{ error }}</ion-text>
</ion-item>
<ion-spinner *ngIf="loading; else loaded" name="lines" color="warning" class="center"></ion-spinner>
<ng-template #loaded> <ng-template #loaded>

View File

@@ -4,6 +4,7 @@ import { Routes, RouterModule } from '@angular/router'
import { IonicModule } from '@ionic/angular' import { IonicModule } from '@ionic/angular'
import { ServerLogsPage } from './server-logs.page' import { ServerLogsPage } from './server-logs.page'
import { PwaBackComponentModule } from 'src/app/components/pwa-back-button/pwa-back.component.module' import { PwaBackComponentModule } from 'src/app/components/pwa-back-button/pwa-back.component.module'
import { TextSpinnerComponentModule } from 'src/app/components/text-spinner/text-spinner.component.module'
const routes: Routes = [ const routes: Routes = [
{ {
@@ -18,6 +19,7 @@ const routes: Routes = [
IonicModule, IonicModule,
RouterModule.forChild(routes), RouterModule.forChild(routes),
PwaBackComponentModule, PwaBackComponentModule,
TextSpinnerComponentModule,
], ],
declarations: [ServerLogsPage], declarations: [ServerLogsPage],
}) })

View File

@@ -13,11 +13,7 @@
</ion-header> </ion-header>
<ion-content class="ion-padding" color="light"> <ion-content class="ion-padding" color="light">
<ion-item *ngIf="error" style="margin-bottom: 16px;"> <text-spinner *ngIf="loading; else loaded" text="Loading Logs"></text-spinner>
<ion-text class="ion-text-wrap" color="danger">{{ error }}</ion-text>
</ion-item>
<ion-spinner *ngIf="loading; else loaded" class="center" name="lines" color="warning"></ion-spinner>
<ng-template #loaded> <ng-template #loaded>
<p style="white-space: pre-line;">{{ logs }}</p> <p style="white-space: pre-line;">{{ logs }}</p>

View File

@@ -1,6 +1,7 @@
import { Component, ViewChild } from '@angular/core' import { Component, ViewChild } from '@angular/core'
import { ApiService } from 'src/app/services/api/api.service' import { ApiService } from 'src/app/services/api/api.service'
import { IonContent } from '@ionic/angular' import { IonContent } from '@ionic/angular'
import { ErrorToastService } from 'src/app/services/error-toast.service'
@Component({ @Component({
selector: 'server-logs', selector: 'server-logs',
@@ -10,10 +11,10 @@ import { IonContent } from '@ionic/angular'
export class ServerLogsPage { export class ServerLogsPage {
@ViewChild(IonContent, { static: false }) private content: IonContent @ViewChild(IonContent, { static: false }) private content: IonContent
loading = true loading = true
error = ''
logs: string logs: string
constructor ( constructor (
private readonly errToast: ErrorToastService,
private readonly apiService: ApiService, private readonly apiService: ApiService,
) { } ) { }
@@ -27,11 +28,10 @@ export class ServerLogsPage {
try { try {
const logs = await this.apiService.getServerLogs({ }) const logs = await this.apiService.getServerLogs({ })
this.logs = logs.map(l => `${l.timestamp} ${l.log}`).join('\n\n') this.logs = logs.map(l => `${l.timestamp} ${l.log}`).join('\n\n')
this.error = ''
setTimeout(async () => await this.content.scrollToBottom(100), 200) setTimeout(async () => await this.content.scrollToBottom(100), 200)
} catch (e) { } catch (e) {
console.error(e) console.error(e)
this.error = e.message this.errToast.present(e.message)
} finally { } finally {
this.loading = false this.loading = false
} }

View File

@@ -4,6 +4,7 @@ import { Routes, RouterModule } from '@angular/router'
import { IonicModule } from '@ionic/angular' import { IonicModule } from '@ionic/angular'
import { ServerMetricsPage } from './server-metrics.page' import { ServerMetricsPage } from './server-metrics.page'
import { PwaBackComponentModule } from 'src/app/components/pwa-back-button/pwa-back.component.module' import { PwaBackComponentModule } from 'src/app/components/pwa-back-button/pwa-back.component.module'
import { TextSpinnerComponentModule } from 'src/app/components/text-spinner/text-spinner.component.module'
const routes: Routes = [ const routes: Routes = [
{ {
@@ -18,6 +19,7 @@ const routes: Routes = [
IonicModule, IonicModule,
RouterModule.forChild(routes), RouterModule.forChild(routes),
PwaBackComponentModule, PwaBackComponentModule,
TextSpinnerComponentModule,
], ],
declarations: [ServerMetricsPage], declarations: [ServerMetricsPage],
}) })

View File

@@ -8,11 +8,7 @@
</ion-header> </ion-header>
<ion-content> <ion-content>
<ion-item *ngIf="error" style="margin-bottom: 16px;"> <text-spinner *ngIf="loading; else loaded" text="Loading Metrics"></text-spinner>
<ion-text class="ion-text-wrap" color="danger">{{ error }}</ion-text>
</ion-item>
<ion-spinner *ngIf="loading; else loaded" class="center" name="lines" color="warning"></ion-spinner>
<ng-template #loaded> <ng-template #loaded>
<ion-item-group *ngFor="let metricGroup of metrics | keyvalue : asIsOrder"> <ion-item-group *ngFor="let metricGroup of metrics | keyvalue : asIsOrder">

View File

@@ -1,6 +1,7 @@
import { Component } from '@angular/core' import { Component } from '@angular/core'
import { Metrics } from 'src/app/services/api/api-types' import { Metrics } from 'src/app/services/api/api-types'
import { ApiService } from 'src/app/services/api/api.service' import { ApiService } from 'src/app/services/api/api.service'
import { ErrorToastService } from 'src/app/services/error-toast.service'
import { pauseFor } from 'src/app/util/misc.util' import { pauseFor } from 'src/app/util/misc.util'
@Component({ @Component({
@@ -9,17 +10,16 @@ import { pauseFor } from 'src/app/util/misc.util'
styleUrls: ['./server-metrics.page.scss'], styleUrls: ['./server-metrics.page.scss'],
}) })
export class ServerMetricsPage { export class ServerMetricsPage {
error = ''
loading = true loading = true
going = false going = false
metrics: Metrics = { } metrics: Metrics = { }
constructor ( constructor (
private readonly errToast: ErrorToastService,
private readonly apiService: ApiService, private readonly apiService: ApiService,
) { } ) { }
ngOnInit () { ngOnInit () {
this.loading = false
this.startDaemon() this.startDaemon()
} }
@@ -44,8 +44,10 @@ export class ServerMetricsPage {
this.metrics = await this.apiService.getServerMetrics({ }) this.metrics = await this.apiService.getServerMetrics({ })
} catch (e) { } catch (e) {
console.error(e) console.error(e)
this.error = e.message this.errToast.present(e.message)
this.stopDaemon() this.stopDaemon()
} finally {
this.loading = false
} }
} }

View File

@@ -8,11 +8,6 @@
</ion-header> </ion-header>
<ion-content class="ion-padding-top"> <ion-content class="ion-padding-top">
<ion-item *ngIf="error">
<ion-text class="ion-text-wrap" color="danger">{{ error }}</ion-text>
</ion-item>
<ion-item-group> <ion-item-group>
<ion-item> <ion-item>
<ion-label>Select Country</ion-label> <ion-label>Select Country</ion-label>

View File

@@ -3,6 +3,7 @@ import { NavController } from '@ionic/angular'
import { ApiService } from 'src/app/services/api/api.service' import { ApiService } from 'src/app/services/api/api.service'
import { WifiService } from '../wifi.service' import { WifiService } from '../wifi.service'
import { LoaderService } from 'src/app/services/loader.service' import { LoaderService } from 'src/app/services/loader.service'
import { ErrorToastService } from 'src/app/services/error-toast.service'
@Component({ @Component({
selector: 'wifi-add', selector: 'wifi-add',
@@ -14,17 +15,16 @@ export class WifiAddPage {
countryCode = 'US' countryCode = 'US'
ssid = '' ssid = ''
password = '' password = ''
error = ''
constructor ( constructor (
private readonly navCtrl: NavController, private readonly navCtrl: NavController,
private readonly errToast: ErrorToastService,
private readonly apiService: ApiService, private readonly apiService: ApiService,
private readonly loader: LoaderService, private readonly loader: LoaderService,
private readonly wifiService: WifiService, private readonly wifiService: WifiService,
) { } ) { }
async save (): Promise<void> { async save (): Promise<void> {
this.error = ''
this.loader.of({ this.loader.of({
message: 'Saving...', message: 'Saving...',
spinner: 'lines', spinner: 'lines',
@@ -40,12 +40,11 @@ export class WifiAddPage {
this.navCtrl.back() this.navCtrl.back()
}).catch(e => { }).catch(e => {
console.error(e) console.error(e)
this.error = e.message this.errToast.present(e.message)
}) })
} }
async saveAndConnect (): Promise<void> { async saveAndConnect (): Promise<void> {
this.error = ''
this.loader.of({ this.loader.of({
message: 'Connecting. This could take while...', message: 'Connecting. This could take while...',
spinner: 'lines', spinner: 'lines',
@@ -64,7 +63,7 @@ export class WifiAddPage {
} }
}).catch (e => { }).catch (e => {
console.error(e) console.error(e)
this.error = e.message this.errToast.present(e.message)
}) })
} }

View File

@@ -13,10 +13,6 @@
</ion-header> </ion-header>
<ion-content class="ion-padding-top"> <ion-content class="ion-padding-top">
<ion-item *ngIf="error">
<ion-text class="ion-text-wrap" color="danger">{{ error }}</ion-text>
</ion-item>
<ion-item-group> <ion-item-group>
<ion-item> <ion-item>
<ion-label class="ion-text-wrap"> <ion-label class="ion-text-wrap">

View File

@@ -7,6 +7,7 @@ import { LoaderService } from 'src/app/services/loader.service'
import { WiFiInfo } from 'src/app/services/patch-db/data-model' import { WiFiInfo } from 'src/app/services/patch-db/data-model'
import { PatchDbModel } from 'src/app/services/patch-db/patch-db.service' import { PatchDbModel } from 'src/app/services/patch-db/patch-db.service'
import { Subscription } from 'rxjs' import { Subscription } from 'rxjs'
import { ErrorToastService } from 'src/app/services/error-toast.service'
@Component({ @Component({
selector: 'wifi', selector: 'wifi',
@@ -14,12 +15,12 @@ import { Subscription } from 'rxjs'
styleUrls: ['wifi.page.scss'], styleUrls: ['wifi.page.scss'],
}) })
export class WifiListPage { export class WifiListPage {
error = ''
subs: Subscription[] = [] subs: Subscription[] = []
constructor ( constructor (
private readonly apiService: ApiService, private readonly apiService: ApiService,
private readonly loader: LoaderService, private readonly loader: LoaderService,
private readonly errToast: ErrorToastService,
private readonly actionCtrl: ActionSheetController, private readonly actionCtrl: ActionSheetController,
private readonly wifiService: WifiService, private readonly wifiService: WifiService,
public readonly patch: PatchDbModel, public readonly patch: PatchDbModel,
@@ -56,7 +57,6 @@ export class WifiListPage {
// Let's add country code here // Let's add country code here
async connect (ssid: string): Promise<void> { async connect (ssid: string): Promise<void> {
this.error = ''
this.loader.of({ this.loader.of({
message: 'Connecting. This could take while...', message: 'Connecting. This could take while...',
spinner: 'lines', spinner: 'lines',
@@ -66,22 +66,20 @@ export class WifiListPage {
this.wifiService.confirmWifi(ssid) this.wifiService.confirmWifi(ssid)
}).catch(e => { }).catch(e => {
console.error(e) console.error(e)
this.error = '' this.errToast.present(e.message)
}) })
} }
async delete (ssid: string): Promise<void> { async delete (ssid: string): Promise<void> {
this.error = ''
this.loader.of({ this.loader.of({
message: 'Deleting...', message: 'Deleting...',
spinner: 'lines', spinner: 'lines',
cssClass: 'loader', cssClass: 'loader',
}).displayDuringAsync(async () => { }).displayDuringAsync(async () => {
await this.apiService.deleteWifi({ ssid }) await this.apiService.deleteWifi({ ssid })
this.error = ''
}).catch(e => { }).catch(e => {
console.error(e) console.error(e)
this.error = '' this.errToast.present(e.message)
}) })
} }
} }

View File

@@ -494,8 +494,6 @@ export class MockApiService extends ApiService {
const url = `${registryURL}/sys/version/eos` const url = `${registryURL}/sys/version/eos`
let eos = await this.http.simpleGet(url) let eos = await this.http.simpleGet(url)
return (eos as any) return (eos as any)
await pauseFor(2000)
return Mock.MarketplaceEos
} }
async getAvailableList (params: RR.GetAvailableListReq): Promise<RR.GetAvailableListRes> { async getAvailableList (params: RR.GetAvailableListReq): Promise<RR.GetAvailableListRes> {
@@ -515,7 +513,7 @@ export class MockApiService extends ApiService {
await pauseFor(2000) await pauseFor(2000)
return Mock.AvailableShow[params.id][params.version || 'latest'] return Mock.AvailableShow[params.id][params.version || 'latest']
} }
const url = `${registryURL}/marketplace/available?id=${params.id}&version=${params.version || '1.3.0'}` const url = `${registryURL}/marketplace/available?id=${params.id}`
let res = await this.http.simpleGet(url) let res = await this.http.simpleGet(url)
console.log('res RES RES', res) console.log('res RES RES', res)
return (res as any) return (res as any)

View File

@@ -0,0 +1,47 @@
import { Injectable } from '@angular/core'
import { IonicSafeString, ToastController } from '@ionic/angular'
import { ToastButton } from '@ionic/core'
@Injectable({
providedIn: 'root',
})
export class ErrorToastService {
private toast: HTMLIonToastElement
constructor (
private readonly toastCtrl: ToastController,
) { }
async present (message: string | IonicSafeString, link?: string): Promise<void> {
if (this.toast) return
if (link) {
message = new IonicSafeString(message + `<br /><br /><a href=${link} target="_blank" style="color: white;">Get Help</a>`)
}
this.toast = await this.toastCtrl.create({
header: 'Error',
message,
duration: 0,
position: 'top',
cssClass: 'error-toast',
buttons: [
{
side: 'end',
icon: 'close',
handler: () => {
this.dismiss()
},
},
],
})
await this.toast.present()
}
async dismiss (): Promise<void> {
if (this.toast) {
await this.toast.dismiss()
this.toast = undefined
}
}
}

View File

@@ -73,6 +73,16 @@ ion-toast {
--border-color: var(--ion-color-warning); --border-color: var(--ion-color-warning);
} }
.error-toast {
--border-color: var(--ion-color-danger);
width: 40%;
min-width: 400px;
--end: 8px;
right: 8px;
left: unset;
top: 64px;
}
.sublist-spinner { .sublist-spinner {
text-align: center; text-align: center;
margin-top: 40px; margin-top: 40px;