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 c2313b4eb3
commit 6bbe19aed7
43 changed files with 463 additions and 403 deletions

View File

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

View File

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

View File

@@ -11,11 +11,8 @@
<ion-content>
<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>
</ng-template>
</ion-content>

View File

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

View File

@@ -18,7 +18,8 @@ export class OSWelcomePage {
) { }
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 true to show subsequent alert modals

View File

@@ -11,10 +11,6 @@
<text-spinner *ngIf="loading; else loaded" text="Loading Instructions"></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>
<div *ngIf="instructions" class="instuctions-padding" [innerHTML]="instructions | markdown"></div>
</ng-template>
</ion-content>

View File

@@ -1,10 +1,9 @@
import { Component, ViewChild } from '@angular/core'
import { ActivatedRoute } from '@angular/router'
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 { ApiService } from 'src/app/services/api/api.service'
import { ErrorToastService } from 'src/app/services/error-toast.service'
@Component({
selector: 'app-instructions',
@@ -14,42 +13,30 @@ import { ApiService } from 'src/app/services/api/api.service'
export class AppInstructionsPage {
instructions: string
loading = true
error = ''
@ViewChild(IonContent) content: IonContent
subs: Subscription[] = []
constructor (
private readonly route: ActivatedRoute,
private readonly errToast: ErrorToastService,
private readonly apiService: ApiService,
private readonly patch: PatchDbModel,
) { }
async ngOnInit () {
const pkgId = this.route.snapshot.paramMap.get('pkgId')
this.patch.watch$('package-data', pkgId)
.pipe(
concatMap(pkg => this.apiService.getStatic(pkg['static-files'].instructions)),
tap(instructions => {
this.instructions = instructions
}),
take(1),
)
.subscribe(
() => { this.loading = false },
e => {
this.error = e.message
this.loading = false
},
() => console.log('COMPLETE'),
)
const url = this.patch.data['package-data'][pkgId]['static-files'].instructions
try {
this.instructions = await this.apiService.getStatic(url)
} catch (e) {
console.error(e)
this.errToast.present(e.message)
} finally {
this.loading = false
}
}
ngAfterViewInit () {
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-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>
<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 { ApiService } from 'src/app/services/api/api.service'
import { IonContent } from '@ionic/angular'
import { ErrorToastService } from 'src/app/services/error-toast.service'
@Component({
selector: 'app-logs',
@@ -12,10 +13,10 @@ export class AppLogsPage {
@ViewChild(IonContent, { static: false }) private content: IonContent
pkgId: string
logs = ''
error = ''
constructor (
private readonly route: ActivatedRoute,
private readonly errToast: ErrorToastService,
private readonly apiService: ApiService,
) { }
@@ -32,7 +33,8 @@ export class AppLogsPage {
this.logs = logs.map(l => `${l.timestamp} ${l.log}`).join('\n\n')
setTimeout(async () => await this.content.scrollToBottom(100), 200)
} catch (e) {
this.error = e.message
console.error(e)
this.errToast.present(e.message)
}
}
}

View File

@@ -7,10 +7,8 @@
</ion-toolbar>
</ion-header>
<ion-content *ngIf="pkg">
<ion-item *ngIf="error" style="margin-bottom: 16px;">
<ion-text class="ion-text-wrap" color="danger">{{ error }}</ion-text>
</ion-item>
<ion-content>
<ion-card>
<ion-card-header>
<ion-card-title>
@@ -19,41 +17,61 @@
</ion-card-title>
</ion-card-header>
<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>
<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>
No health checks
</ion-label>
</ion-item>
<ng-template #noHealth>
No health checks
<ng-template #health>
<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>
</ion-card-content>
</ion-card>
<ion-card>
<ion-card-header>
<ion-card-title>
<ion-icon style="vertical-align: middle; padding-right: 12px;" name="analytics-outline"></ion-icon>
<span style="vertical-align: middle;">Service Metrics</span>
<ion-icon style="vertical-align: middle; padding-right: 12px;" name="pulse-outline"></ion-icon>
<span style="vertical-align: middle;">Metrics</span>
</ion-card-title>
</ion-card-header>
<ion-card-content>
<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>
<ng-container *ngIf="loading">
<div class="post">
<div class="label"></div>
<div class="note"></div>
</div>
<div class="post">
<div class="label"></div>
<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>
</ion-content>

View File

@@ -1,3 +1,44 @@
.metric-note {
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 { Metric } from 'src/app/services/api/api-types'
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 { PatchDbModel } from 'src/app/services/patch-db/patch-db.service'
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'],
})
export class AppMetricsPage {
loading = true
pkgId: string
pkg: PackageDataEntry
going = false
@@ -22,6 +24,7 @@ export class AppMetricsPage {
constructor (
private readonly route: ActivatedRoute,
private readonly errToast: ErrorToastService,
private readonly patch: PatchDbModel,
private readonly apiService: ApiService,
) { }
@@ -33,6 +36,10 @@ export class AppMetricsPage {
this.startDaemon()
}
ngAfterViewInit () {
this.content.scrollToPoint(undefined, 1)
}
ngOnDestroy () {
this.stopDaemon()
}
@@ -53,16 +60,14 @@ export class AppMetricsPage {
try {
this.metrics = await this.apiService.getPkgMetrics({ id: this.pkgId})
} catch (e) {
console.error(e)
this.errToast.present(e.message)
this.stopDaemon()
await pauseFor(1000)
this.startDaemon()
} finally {
this.loading = false
}
}
ngAfterViewInit () {
this.content.scrollToPoint(undefined, 1)
}
asIsOrder (a: any, b: any) {
return 0
}

View File

@@ -16,12 +16,8 @@
<text-spinner *ngIf="loading; else loaded" text="Loading Properties"></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>
<!-- not running -->
<ion-item *ngIf="notRunning" class="ion-margin-bottom">
<ion-item *ngIf="!running" class="ion-margin-bottom">
<ion-label class="ion-text-wrap">
<p><ion-text color="warning">Service not running. Information on this page could be inaccurate.</ion-text></p>
</ion-label>

View File

@@ -7,9 +7,9 @@ import { AlertController, IonContent, NavController, PopoverController, ToastCon
import { PackageProperties } from 'src/app/util/properties.util'
import { QRComponent } from 'src/app/components/qr/qr.component'
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 { ErrorToastService } from 'src/app/services/error-toast.service'
import * as JsonPointer from 'json-pointer'
@Component({
selector: 'app-properties',
@@ -17,11 +17,9 @@ import { PackageMainStatus } from 'src/app/services/patch-db/data-model'
styleUrls: ['./app-properties.page.scss'],
})
export class AppPropertiesPage {
error = ''
loading = true
pkgId: string
pointer: string
qrCode: string
properties: PackageProperties
node: PackageProperties
unmasked: { [key: string]: boolean } = { }
@@ -33,15 +31,17 @@ export class AppPropertiesPage {
constructor (
private readonly route: ActivatedRoute,
private readonly apiService: ApiService,
private readonly errToast: ErrorToastService,
private readonly alertCtrl: AlertController,
private readonly toastCtrl: ToastController,
private readonly popoverCtrl: PopoverController,
private readonly navCtrl: NavController,
public readonly patch: PatchDbModel,
private readonly patch: PatchDbModel,
) { }
async ngOnInit () {
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()
@@ -51,10 +51,6 @@ export class AppPropertiesPage {
this.pointer = queryParams['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]
}
asIsOrder (a: any, b: any) {
return 0
}
private async getProperties (): Promise<void> {
this.loading = true
try {
@@ -127,9 +119,13 @@ export class AppPropertiesPage {
this.node = JsonPointer.get(this.properties, this.pointer || '')
} catch (e) {
console.error(e)
this.error = e.message
this.errToast.present(e.message)
} finally {
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 { BackupConfirmationComponentModule } from 'src/app/modals/backup-confirmation/backup-confirmation.component.module'
import { SharingModule } from 'src/app/modules/sharing.module'
import { TextSpinnerComponentModule } from 'src/app/components/text-spinner/text-spinner.component.module'
const routes: Routes = [
{
@@ -22,6 +23,7 @@ const routes: Routes = [
RouterModule.forChild(routes),
BackupConfirmationComponentModule,
PwaBackComponentModule,
TextSpinnerComponentModule,
],
declarations: [
AppRestorePage,

View File

@@ -14,20 +14,9 @@
<ion-content class="ion-padding-top">
<ion-grid *ngIf="loading; else loaded" style="height: 100%;">
<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>
<text-spinner *ngIf="loading; else loaded" text="Loading Drives"></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>
<ion-item class="ion-margin-bottom">
<ion-label class="ion-text-wrap">
<p class="ion-padding-bottom"><ion-text color="warning">Warning</ion-text></p>

View File

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

View File

@@ -14,11 +14,6 @@
<text-spinner *ngIf="pageLoading; else pageLoaded" [text]="'loading marketplace'"></text-spinner>
<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-header>
<ion-card-subtitle>Now Available...</ion-card-subtitle>
@@ -50,7 +45,7 @@
<ng-template #loaded>
<ion-grid>
<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-thumbnail slot="start">
<img [src]="pkg.icon" />

View File

@@ -23,4 +23,5 @@
.cat-selected {
border-width: 0 0 1px 0;
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 { WizardBaker } from 'src/app/components/install-wizard/prebaked-wizards'
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 { ErrorToastService } from 'src/app/services/error-toast.service'
@Component({
selector: 'marketplace-list',
@@ -17,7 +18,6 @@ export class MarketplaceListPage {
@ViewChild(IonContent) content: IonContent
pageLoading = true
pkgsLoading = true
error = ''
category = 'all'
query: string
@@ -37,6 +37,7 @@ export class MarketplaceListPage {
constructor (
private readonly apiService: ApiService,
private readonly modalCtrl: ModalController,
private readonly errToast: ErrorToastService,
private readonly wizardBaker: WizardBaker,
public readonly patch: PatchDbModel,
) { }
@@ -55,7 +56,7 @@ export class MarketplaceListPage {
this.pkgs = pkgs
} catch (e) {
console.error(e)
this.error = e.message
this.errToast.present(e.message)
} finally {
this.pageLoading = false
this.pkgsLoading = false
@@ -106,7 +107,7 @@ export class MarketplaceListPage {
return pkgs
} catch (e) {
console.error(e)
this.error = e.message
this.errToast.present(e.message)
} finally {
this.pkgsLoading = false
}

View File

@@ -12,213 +12,211 @@
<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">
<ion-item *ngIf="error" style="margin-bottom: 16px;">
<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>
<ng-template #loaded>
<ng-container *ngIf="marketplaceService.pkgs[pkgId] as pkg">
<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 sizeXs="12" sizeSm="12" sizeMd="9" sizeLg="9" sizeXl="9">
<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 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 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>
</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>

View File

@@ -1,12 +1,13 @@
import { Component, ViewChild } from '@angular/core'
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 { WizardBaker } from 'src/app/components/install-wizard/prebaked-wizards'
import { Emver } from 'src/app/services/emver.service'
import { displayEmver } from 'src/app/pipes/emver.pipe'
import { pauseFor, Recommendation } from 'src/app/util/misc.util'
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 { MarketplaceService } from '../marketplace.service'
import { Subscription } from 'rxjs'
@@ -19,7 +20,7 @@ import { MarkdownPage } from 'src/app/modals/markdown/markdown.page'
})
export class MarketplaceShowPage {
@ViewChild(IonContent) content: IonContent
error = ''
loading = true
pkgId: string
installedPkg: PackageDataEntry
PackageState = PackageState
@@ -32,11 +33,12 @@ export class MarketplaceShowPage {
private readonly route: ActivatedRoute,
private readonly alertCtrl: AlertController,
private readonly modalCtrl: ModalController,
private readonly errToast: ErrorToastService,
private readonly wizardBaker: WizardBaker,
private readonly navCtrl: NavController,
private readonly emver: Emver,
private readonly patch: PatchDbModel,
public readonly marketplaceService: MarketplaceService,
private readonly marketplaceService: MarketplaceService,
) { }
async ngOnInit () {
@@ -66,7 +68,10 @@ export class MarketplaceShowPage {
await this.marketplaceService.setPkg(this.pkgId, version)
} catch (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-content>
<ion-refresher slot="fixed" (ionRefresh)="doRefresh($event)">
<ion-refresher-content pullingIcon="lines" refreshingSpinner="lines"></ion-refresher-content>
</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-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 { AlertController } from '@ionic/angular'
import { ActivatedRoute } from '@angular/router'
import { ErrorToastService } from 'src/app/services/error-toast.service'
@Component({
selector: 'notifications',
@@ -11,7 +12,6 @@ import { ActivatedRoute } from '@angular/router'
styleUrls: ['notifications.page.scss'],
})
export class NotificationsPage {
error = ''
loading = true
notifications: ServerNotifications = []
page = 1
@@ -22,6 +22,7 @@ export class NotificationsPage {
constructor (
private readonly apiService: ApiService,
private readonly loader: LoaderService,
private readonly errToast: ErrorToastService,
private readonly alertCtrl: AlertController,
private readonly route: ActivatedRoute,
) { }
@@ -50,10 +51,9 @@ export class NotificationsPage {
notifications = await this.apiService.getNotifications({ page: this.page, 'per-page': this.perPage })
this.needInfinite = notifications.length >= this.perPage
this.page++
this.error = ''
} catch (e) {
console.error(e)
this.error = e.message
this.errToast.present(e.message)
} finally {
return notifications
}
@@ -67,11 +67,10 @@ export class NotificationsPage {
}).displayDuringP(
this.apiService.deleteNotification({ id }).then(() => {
this.notifications.splice(index, 1)
this.error = ''
}),
).catch(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 { PwaBackComponentModule } from 'src/app/components/pwa-back-button/pwa-back.component.module'
import { SharingModule } from 'src/app/modules/sharing.module'
import { TextSpinnerComponentModule } from 'src/app/components/text-spinner/text-spinner.component.module'
const routes: Routes = [
{
@@ -20,6 +21,7 @@ const routes: Routes = [
RouterModule.forChild(routes),
PwaBackComponentModule,
SharingModule,
TextSpinnerComponentModule,
],
declarations: [DevSSHKeysPage],
})

View File

@@ -13,11 +13,7 @@
</ion-header>
<ion-content class="ion-padding-top">
<ion-item *ngIf="error" class="ion-margin-bottom">
<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>
<text-spinner *ngIf="loading" text="Loading Keys"></text-spinner>
<ion-item-group>
<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 { Subscription } from 'rxjs'
import { SSHKeys } from 'src/app/services/api/api-types'
import { ErrorToastService } from 'src/app/services/error-toast.service'
@Component({
selector: 'dev-ssh-keys',
@@ -12,21 +13,19 @@ import { SSHKeys } from 'src/app/services/api/api-types'
styleUrls: ['dev-ssh-keys.page.scss'],
})
export class DevSSHKeysPage {
error = ''
loading = true
sshKeys: SSHKeys
subs: Subscription[] = []
constructor (
private readonly loader: LoaderService,
private readonly errToast: ErrorToastService,
private readonly serverConfigService: ServerConfigService,
private readonly alertCtrl: AlertController,
private readonly sshService: SSHService,
) { }
async ngOnInit () {
await this.sshService.getKeys()
this.subs = [
this.sshService.watch$()
.subscribe(keys => {
@@ -34,6 +33,8 @@ export class DevSSHKeysPage {
}),
]
await this.sshService.getKeys()
this.loading = false
}
@@ -68,7 +69,6 @@ export class DevSSHKeysPage {
}
async delete (hash: string): Promise<void> {
this.error = ''
this.loader.of({
message: 'Deleting...',
spinner: 'lines',
@@ -77,7 +77,7 @@ export class DevSSHKeysPage {
await this.sshService.delete(hash)
}).catch(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 { 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 { TextSpinnerComponentModule } from 'src/app/components/text-spinner/text-spinner.component.module'
const routes: Routes = [
{
@@ -20,6 +21,7 @@ const routes: Routes = [
RouterModule.forChild(routes),
BackupConfirmationComponentModule,
PwaBackComponentModule,
TextSpinnerComponentModule,
],
declarations: [
ServerBackupPage,

View File

@@ -13,12 +13,7 @@
</ion-header>
<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>
<ion-spinner *ngIf="loading; else loaded" name="lines" color="warning" class="center"></ion-spinner>
<text-spinner *ngIf="loading; else loaded" text="Loading Drives"></text-spinner>
<ng-template #loaded>

View File

@@ -4,6 +4,7 @@ import { Routes, RouterModule } from '@angular/router'
import { IonicModule } from '@ionic/angular'
import { ServerLogsPage } from './server-logs.page'
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 = [
{
@@ -18,6 +19,7 @@ const routes: Routes = [
IonicModule,
RouterModule.forChild(routes),
PwaBackComponentModule,
TextSpinnerComponentModule,
],
declarations: [ServerLogsPage],
})

View File

@@ -13,11 +13,7 @@
</ion-header>
<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>
<ion-spinner *ngIf="loading; else loaded" class="center" name="lines" color="warning"></ion-spinner>
<text-spinner *ngIf="loading; else loaded" text="Loading Logs"></text-spinner>
<ng-template #loaded>
<p style="white-space: pre-line;">{{ logs }}</p>

View File

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

View File

@@ -4,6 +4,7 @@ import { Routes, RouterModule } from '@angular/router'
import { IonicModule } from '@ionic/angular'
import { ServerMetricsPage } from './server-metrics.page'
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 = [
{
@@ -18,6 +19,7 @@ const routes: Routes = [
IonicModule,
RouterModule.forChild(routes),
PwaBackComponentModule,
TextSpinnerComponentModule,
],
declarations: [ServerMetricsPage],
})

View File

@@ -8,11 +8,7 @@
</ion-header>
<ion-content>
<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" class="center" name="lines" color="warning"></ion-spinner>
<text-spinner *ngIf="loading; else loaded" text="Loading Metrics"></text-spinner>
<ng-template #loaded>
<ion-item-group *ngFor="let metricGroup of metrics | keyvalue : asIsOrder">

View File

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

View File

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

View File

@@ -13,10 +13,6 @@
</ion-header>
<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>
<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 { PatchDbModel } from 'src/app/services/patch-db/patch-db.service'
import { Subscription } from 'rxjs'
import { ErrorToastService } from 'src/app/services/error-toast.service'
@Component({
selector: 'wifi',
@@ -14,12 +15,12 @@ import { Subscription } from 'rxjs'
styleUrls: ['wifi.page.scss'],
})
export class WifiListPage {
error = ''
subs: Subscription[] = []
constructor (
private readonly apiService: ApiService,
private readonly loader: LoaderService,
private readonly errToast: ErrorToastService,
private readonly actionCtrl: ActionSheetController,
private readonly wifiService: WifiService,
public readonly patch: PatchDbModel,
@@ -56,7 +57,6 @@ export class WifiListPage {
// Let's add country code here
async connect (ssid: string): Promise<void> {
this.error = ''
this.loader.of({
message: 'Connecting. This could take while...',
spinner: 'lines',
@@ -66,22 +66,20 @@ export class WifiListPage {
this.wifiService.confirmWifi(ssid)
}).catch(e => {
console.error(e)
this.error = ''
this.errToast.present(e.message)
})
}
async delete (ssid: string): Promise<void> {
this.error = ''
this.loader.of({
message: 'Deleting...',
spinner: 'lines',
cssClass: 'loader',
}).displayDuringAsync(async () => {
await this.apiService.deleteWifi({ ssid })
this.error = ''
}).catch(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`
let eos = await this.http.simpleGet(url)
return (eos as any)
await pauseFor(2000)
return Mock.MarketplaceEos
}
async getAvailableList (params: RR.GetAvailableListReq): Promise<RR.GetAvailableListRes> {
@@ -515,7 +513,7 @@ export class MockApiService extends ApiService {
await pauseFor(2000)
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)
console.log('res RES RES', res)
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);
}
.error-toast {
--border-color: var(--ion-color-danger);
width: 40%;
min-width: 400px;
--end: 8px;
right: 8px;
left: unset;
top: 64px;
}
.sublist-spinner {
text-align: center;
margin-top: 40px;