better backup reports

This commit is contained in:
Matt Hill
2021-10-22 10:00:51 -06:00
committed by Aiden McClelland
parent 502fdeea78
commit 54a65e465a
18 changed files with 125 additions and 130 deletions

View File

@@ -103,6 +103,7 @@
<ion-icon name="refresh"></ion-icon>
<ion-icon name="reload"></ion-icon>
<ion-icon name="remove"></ion-icon>
<ion-icon name="remove-circle-outline"></ion-icon>
<ion-icon name="save-outline"></ion-icon>
<ion-icon name="shield-checkmark-outline"></ion-icon>
<ion-icon name="storefront-outline"></ion-icon>

View File

@@ -0,0 +1,14 @@
import { NgModule } from '@angular/core'
import { CommonModule } from '@angular/common'
import { IonicModule } from '@ionic/angular'
import { BackupReportPage } from './backup-report.page'
@NgModule({
declarations: [BackupReportPage],
imports: [
CommonModule,
IonicModule,
],
exports: [BackupReportPage],
})
export class BackupReportPageModule { }

View File

@@ -0,0 +1,30 @@
<ion-header>
<ion-toolbar>
<ion-title>Backup Report</ion-title>
<ion-buttons slot="end">
<ion-button (click)="dismiss()">
<ion-icon slot="icon-only" name="close"></ion-icon>
</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-item-group>
<ion-item-divider>Completed: {{ timestamp | date : 'short' }}</ion-item-divider>
<ion-item>
<ion-label>
<h2>System data</h2>
<p><ion-text [color]="system.color">{{ system.result }}</ion-text></p>
</ion-label>
<ion-icon slot="end" [name]="system.icon" [color]="system.color"></ion-icon>
</ion-item>
<ion-item *ngFor="let pkg of report.packages | keyvalue">
<ion-label>
<h2>{{ pkg.key }}</h2>
<p><ion-text [color]="pkg.value.error ? 'danger' : 'success'">{{ pkg.value.error ? 'Failed: ' + pkg.value.error : 'Succeeded' }}</ion-text></p>
</ion-label>
<ion-icon slot="end" [name]="pkg.value.error ? 'remove-circle-outline' : 'checkmark'" [color]="pkg.value.error ? 'danger' : 'success'"></ion-icon>
</ion-item>
</ion-item-group>
</ion-content>

View File

@@ -0,0 +1,48 @@
import { Component, Input } from '@angular/core'
import { ModalController } from '@ionic/angular'
import { BackupReport } from 'src/app/services/api/api.types'
@Component({
selector: 'backup-report',
templateUrl: './backup-report.page.html',
styleUrls: ['./backup-report.page.scss'],
})
export class BackupReportPage {
@Input() report: BackupReport
@Input() timestamp: string
system: {
result: string
icon: 'remove' | 'remove-circle-outline' | 'checkmark'
color: 'dark' | 'danger' | 'success'
}
constructor (
private readonly modalCtrl: ModalController,
) { }
ngOnInit () {
if (!this.report.server.attempted) {
this.system = {
result: 'Not Attempted',
icon: 'remove',
color: 'dark',
}
} else if (this.report.server.error) {
this.system = {
result: `Failed: ${this.report.server.error}`,
icon: 'remove-circle-outline',
color: 'danger',
}
} else {
this.system = {
result: 'Succeeded',
icon: 'checkmark',
color: 'success',
}
}
}
async dismiss () {
return this.modalCtrl.dismiss(true)
}
}

View File

@@ -1,11 +1,11 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-title>{{ title | titlecase }}</ion-title>
<ion-buttons slot="end">
<ion-button (click)="dismiss()">
<ion-icon slot="icon-only" name="close"></ion-icon>
</ion-button>
</ion-buttons>
<ion-title>{{ title | titlecase }}</ion-title>
</ion-toolbar>
</ion-header>

View File

@@ -1,26 +0,0 @@
import { NgModule } from '@angular/core'
import { CommonModule } from '@angular/common'
import { Routes, RouterModule } from '@angular/router'
import { IonicModule } from '@ionic/angular'
import { AppInstructionsPage } from './app-instructions.page'
import { SharingModule } from 'src/app/modules/sharing.module'
const routes: Routes = [
{
path: '',
component: AppInstructionsPage,
},
]
@NgModule({
imports: [
CommonModule,
IonicModule,
RouterModule.forChild(routes),
SharingModule,
],
declarations: [
AppInstructionsPage,
],
})
export class AppInstructionsPageModule { }

View File

@@ -1,16 +0,0 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<pwa-back-button></pwa-back-button>
</ion-buttons>
<ion-title>Instructions</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<text-spinner *ngIf="loading; else loaded" text="Loading Instructions"></text-spinner>
<ng-template #loaded>
<div *ngIf="instructions" class="instuctions-padding" [innerHTML]="instructions | markdown"></div>
</ng-template>
</ion-content>

View File

@@ -1,3 +0,0 @@
.instructions-padding {
padding: 0 16px 16px 16px
}

View File

@@ -1,39 +0,0 @@
import { Component, ViewChild } from '@angular/core'
import { ActivatedRoute } from '@angular/router'
import { IonContent } from '@ionic/angular'
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import { ErrorToastService } from 'src/app/services/error-toast.service'
@Component({
selector: 'app-instructions',
templateUrl: './app-instructions.page.html',
styleUrls: ['./app-instructions.page.scss'],
})
export class AppInstructionsPage {
instructions: string
loading = true
@ViewChild(IonContent) content: IonContent
constructor (
private readonly route: ActivatedRoute,
private readonly errToast: ErrorToastService,
private readonly embassyApi: ApiService,
private readonly patch: PatchDbService,
) { }
async ngOnInit () {
const pkgId = this.route.snapshot.paramMap.get('pkgId')
const url = this.patch.getData()['package-data'][pkgId]['static-files'].instructions
try {
this.instructions = await this.embassyApi.getStatic(url)
} catch (e) {
this.errToast.present(e)
} finally {
this.loading = false
}
}
}

View File

@@ -7,6 +7,7 @@ import { StatusComponentModule } from 'src/app/components/status/status.componen
import { SharingModule } from 'src/app/modules/sharing.module'
import { InstallWizardComponentModule } from 'src/app/components/install-wizard/install-wizard.component.module'
import { AppConfigPageModule } from 'src/app/modals/app-config/app-config.module'
import { MarkdownPageModule } from 'src/app/modals/markdown/markdown.module'
const routes: Routes = [
{
@@ -24,6 +25,7 @@ const routes: Routes = [
InstallWizardComponentModule,
AppConfigPageModule,
SharingModule,
MarkdownPageModule,
],
declarations: [AppShowPage],
})

View File

@@ -15,6 +15,7 @@ import { ErrorToastService } from 'src/app/services/error-toast.service'
import { AppConfigPage } from 'src/app/modals/app-config/app-config.page'
import { PackageLoadingService, ProgressData } from 'src/app/services/package-loading.service'
import { filter } from 'rxjs/operators'
import { MarkdownPage } from 'src/app/modals/markdown/markdown.page'
@Component({
selector: 'app-show',
@@ -211,6 +212,18 @@ export class AppShowPage {
await modal.present()
}
async presentModalInstructions () {
const modal = await this.modalCtrl.create({
componentProps: {
title: 'Instructions',
contentUrl: this.pkg['static-files']['instructions'],
},
component: MarkdownPage,
})
await modal.present()
}
private setDepValues (id: string, errors: { [id: string]: DependencyError }): DependencyInfo {
let errorText = ''
let actionText = 'View'
@@ -330,7 +343,7 @@ export class AppShowPage {
this.buttons = [
// instructions
{
action: () => this.navCtrl.navigateForward(['instructions'], { relativeTo: this.route }),
action: () => this.presentModalInstructions(),
title: 'Instructions',
description: `Understand how to use ${pkgTitle}`,
icon: 'list-outline',

View File

@@ -19,10 +19,6 @@ const routes: Routes = [
path: ':pkgId/actions',
loadChildren: () => import('./app-actions/app-actions.module').then(m => m.AppActionsPageModule),
},
{
path: ':pkgId/instructions',
loadChildren: () => import('./app-instructions/app-instructions.module').then(m => m.AppInstructionsPageModule),
},
{
path: ':pkgId/interfaces',
loadChildren: () => import('./app-interfaces/app-interfaces.module').then(m => m.AppInterfacesPageModule),

View File

@@ -5,6 +5,7 @@ import { RouterModule, Routes } from '@angular/router'
import { NotificationsPage } from './notifications.page'
import { BadgeMenuComponentModule } from 'src/app/components/badge-menu-button/badge-menu.component.module'
import { SharingModule } from 'src/app/modules/sharing.module'
import { BackupReportPageModule } from 'src/app/modals/backup-report/backup-report.module'
const routes: Routes = [
{
@@ -20,6 +21,7 @@ const routes: Routes = [
RouterModule.forChild(routes),
BadgeMenuComponentModule,
SharingModule,
BackupReportPageModule,
],
declarations: [NotificationsPage],
})

View File

@@ -66,7 +66,7 @@
<h2 class="notification-message">
{{ not.message }}
<a *ngIf="not.code === 1" style="text-decoration: none; cursor: pointer;" (click)="viewBackupReport(not)">
View Report
- View Report
</a>
</h2>
<p>

View File

@@ -1,9 +1,10 @@
import { Component } from '@angular/core'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import { ServerNotification, ServerNotifications } from 'src/app/services/api/api.types'
import { AlertController, LoadingController, AlertButton } from '@ionic/angular'
import { LoadingController, ModalController } from '@ionic/angular'
import { ActivatedRoute } from '@angular/router'
import { ErrorToastService } from 'src/app/services/error-toast.service'
import { BackupReportPage } from 'src/app/modals/backup-report/backup-report.page'
@Component({
selector: 'notifications',
@@ -21,8 +22,8 @@ export class NotificationsPage {
constructor (
private readonly embassyApi: ApiService,
private readonly loadingCtrl: LoadingController,
private readonly modalCtrl: ModalController,
private readonly errToast: ErrorToastService,
private readonly alertCtrl: AlertController,
private readonly route: ActivatedRoute,
) { }
@@ -90,40 +91,14 @@ export class NotificationsPage {
}
async viewBackupReport (notification: ServerNotification<1>) {
const data = notification.data
const embassyFailed = !!data.server.error
const packagesFailed = Object.values(data.packages).some(val => val.error)
let message: string
if (embassyFailed || packagesFailed) {
message = 'There was an issue backing up one or more items. Click "Retry" to retry ONLY the items that failed.'
} else {
message = 'All items were successfully backed up'
}
const buttons: AlertButton[] = [
{
text: 'Dismiss',
role: 'cancel',
const modal = await this.modalCtrl.create({
component: BackupReportPage,
componentProps: {
report: notification.data,
timestamp: notification['created-at'],
},
]
if (embassyFailed || packagesFailed) {
buttons.push({
text: 'Retry',
})
}
const alert = await this.alertCtrl.create({
header: 'Backup Report',
message,
buttons,
})
await alert.present()
await modal.present()
}
}

View File

@@ -740,12 +740,12 @@ export module Mock {
message: 'Embassy and services have been successfully backed up.',
data: {
server: {
attempted: true,
attempted: false,
error: null,
},
packages: {
'bitcoind': {
error: null,
error: 'An error ocurred while backing up',
},
},
},

View File

@@ -27,8 +27,6 @@ export class PatchDbService {
private patchSub: Subscription
data: DataModel
getData () { return this.patchDb.store.cache.data }
constructor (
@Inject(PATCH_SOURCE) private readonly source: Source<DataModel>,
@Inject(PATCH_HTTP) private readonly http: ApiService,