cosmetics plus a slew of little frontend rendering bugs

This commit is contained in:
Matt Hill
2021-08-13 16:16:24 -06:00
committed by Aiden McClelland
parent c18a119c70
commit 7dc53a4e85
44 changed files with 518 additions and 618 deletions

View File

@@ -1,8 +1,9 @@
<ion-header>
<ion-header style="min-height: unset;">
<ion-toolbar *ngIf="patch.data['package-data'][pkgId] as pkg">
<ion-title>Config</ion-title>
<ion-buttons slot="end" class="ion-padding-end">
<ion-button fill="clear" [disabled]="loadingText" (click)="resetDefaults()">
<ion-icon slot="start" name="refresh-outline"></ion-icon>
Reset Defaults
</ion-button>
</ion-buttons>
@@ -20,7 +21,7 @@
<ng-container *ngIf="patch.data['package-data'][pkgId] as pkg">
<ng-container *ngIf="pkg.manifest.config && !pkg.installed.status.configured && !edited">
<ion-item class="notifier-item">
<ion-label class="ion-text-wrap">
<ion-label>
<h2 style="display: flex; align-items: center; margin-bottom: 3px;">
<ion-icon size="small" style="margin-right: 5px" slot="start" color="dark" slot="start" name="alert-circle-outline"></ion-icon>
<ion-text style="font-size: smaller;">Initial Config</ion-text>
@@ -32,7 +33,7 @@
<ng-container *ngIf="rec && showRec">
<ion-item class="rec-item">
<ion-label class="ion-text-wrap">
<ion-label>
<h2 style="display: flex; align-items: center;">
<ion-icon size="small" style="margin: 4px" slot="start" color="primary" slot="start" name="ellipse"></ion-icon>
<ion-thumbnail style="width: 3vh; height: 3vh; margin: 0px 2px 0px 5px;" slot="start">
@@ -60,7 +61,7 @@
<!-- no config -->
<ion-item *ngIf="!hasConfig">
<ion-label class="ion-text-wrap">
<ion-label>
<p>No config options for {{ pkg.manifest.title }} {{ pkg.manifest.version }}.</p>
</ion-label>
</ion-item>

View File

@@ -18,43 +18,37 @@
<ion-content *ngIf="!loading && !submitting">
<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>
<ion-label>
<h2>
Restoring from backup will overwrite all current data for {{ patch.data['package-data'][pkgId].manifest.title }} .
Select the drive containing the backup you would like to restore.
<br />
<br />
<ion-text color="warning">
Warning! All current data for {{ patch.data['package-data'][pkgId].manifest.title }} will be overwritten by the backup.
</ion-text>
</h2>
</ion-label>
</ion-item>
<ion-item-divider>Select Backup Drive</ion-item-divider>
<ion-item *ngIf="allPartitionsMounted">
<ion-text class="ion-text-wrap" color="warning">No partitions available. Insert the storage device containing the backup you intend to restore.</ion-text>
<ion-text class="ion-text-wrap" color="warning">No drives found containing a valid backup for {{ patch.data['package-data'][pkgId].manifest.title }}. Insert the storage device containing the backup you intend to restore.</ion-text>
</ion-item>
<ion-card *ngFor="let disk of disks | keyvalue">
<ion-card-header>
<ion-card-title>
{{ disk.value.size }}
</ion-card-title>
<ion-card-subtitle>
{{ disk.key }}
</ion-card-subtitle>
</ion-card-header>
<ion-card-content>
<ion-item-group>
<ion-item button *ngFor="let partition of disk.value.partitions | keyvalue" [disabled]="partition.value['is-mounted']" (click)="presentModal(partition.key)">
<ion-icon slot="start" name="save-outline"></ion-icon>
<ion-label>
<h2>{{ partition.value.label || partition.key }} ({{ partition.value.size || 'unknown size' }})</h2>
<p *ngIf="!partition.value['is-mounted']; else unavailable"><ion-text color="success">Available</ion-text></p>
<ng-template #unavailable>
<p><ion-text color="danger">Unavailable</ion-text></p>
</ng-template>
</ion-label>
</ion-item>
</ion-item-group>
</ion-card-content>
</ion-card>
<ion-item-group>
<div *ngFor="let disk of disks | keyvalue">
<ion-item-divider>{{ disk.key }} - {{ disk.value.size }}</ion-item-divider>
<ion-item button *ngFor="let partition of disk.value.partitions | keyvalue" [disabled]="partition.value['is-mounted']" (click)="presentModal(partition.key)">
<ion-icon slot="start" name="save-outline" size="large"></ion-icon>
<ion-label>
<h1>{{ partition.value.label || partition.key }}</h1>
<h2>{{ partition.value.size || 'unknown size' }}</h2>
<p *ngIf="!partition.value['is-mounted']; else unavailable"><ion-text color="success">Available</ion-text></p>
<ng-template #unavailable>
<p><ion-text color="danger">Unavailable</ion-text></p>
</ng-template>
</ion-label>
</ion-item>
</div>
</ion-item-group>
</ion-content>
</ion-content>

View File

@@ -1,10 +1,9 @@
import { Component, Input } from '@angular/core'
import { LoadingController, ModalController } from '@ionic/angular'
import { ModalController } from '@ionic/angular'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import { BackupConfirmationComponent } from 'src/app/modals/backup-confirmation/backup-confirmation.component'
import { DiskInfo } from 'src/app/services/api/api.types'
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
import { Subscription } from 'rxjs'
import { ErrorToastService } from 'src/app/services/error-toast.service'
@Component({
@@ -15,29 +14,22 @@ import { ErrorToastService } from 'src/app/services/error-toast.service'
export class AppRestoreComponent {
@Input() pkgId: string
disks: DiskInfo
title: string
loading = true
submitting = false
allPartitionsMounted: boolean
subs: Subscription[] = []
modal: HTMLIonModalElement
constructor (
private readonly modalCtrl: ModalController,
private readonly embassyApi: ApiService,
private readonly loadingCtrl: LoadingController,
private readonly errToast: ErrorToastService,
public readonly patch: PatchDbService,
) { }
ngOnInit () {
async ngOnInit () {
this.getExternalDisks()
this.modal = await this.modalCtrl.getTop()
}
// ngAfterViewInit () {
// this.content.scrollToPoint(undefined, 1)
// }
async refresh () {
this.loading = true
await this.getExternalDisks()
@@ -55,42 +47,36 @@ export class AppRestoreComponent {
}
async presentModal (logicalname: string): Promise<void> {
const m = await this.modalCtrl.create({
const modal = await this.modalCtrl.create({
componentProps: {
type: 'restore',
title: 'Enter Password',
message: 'Backup encrypted. Enter the password that was originally used to encrypt this backup.',
label: 'Password',
useMask: true,
buttonText: 'Restore',
submitFn: async (value: string) => await this.restore(logicalname, value),
},
cssClass: 'alertlike-modal',
presentingElement: await this.modalCtrl.getTop(),
component: BackupConfirmationComponent,
backdropDismiss: false,
})
m.onWillDismiss().then(res => {
const data = res.data
if (data.cancel) return
this.restore(logicalname, data.password)
modal.onWillDismiss().then(res => {
if (res.role === 'success') this.modal.dismiss(undefined, 'success')
})
await m.present()
await modal.present()
}
dismiss () {
this.modalCtrl.dismiss({ })
this.modalCtrl.dismiss()
}
private async restore (logicalname: string, password: string): Promise<void> {
this.submitting = true
// await loader.present()
try {
await this.embassyApi.restorePackage({
id: this.pkgId,
logicalname,
password,
})
} catch (e) {
this.modalCtrl.dismiss({ error: e })
} finally {
this.modalCtrl.dismiss({ })
}
await this.embassyApi.restorePackage({
id: this.pkgId,
logicalname,
password,
})
}
}

View File

@@ -1,31 +1,37 @@
<ion-content>
<div style="height: 85%; margin: 24px; display: flex; flex-direction: column; justify-content: space-between;">
<div *ngIf="type === 'backup'">
<h4><ion-text color="dark">Encrypt Backup</ion-text></h4>
<p><ion-text>Enter your master password to create an encrypted backup.</ion-text></p>
</div>
<div *ngIf="type === 'restore'">
<h4><ion-text color="dark">Decrypt Backup</ion-text></h4>
<p><ion-text>Enter the password that was originally used to encrypt this backup.</ion-text></p>
</div>
<div style="margin: 24px 24px 12px 24px; display: flex; flex-direction: column;">
<div>
<ion-item lines="none" style="--background: var(--ion-background-color); --border-color: var(--ion-color-medium);">
<ion-label style="font-size: small" position="floating">Master Password</ion-label>
<ion-input style="border-style: solid; border-width: 0px 0px 1px 0px; border-color: var(--ion-color-dark);" [(ngModel)]="password" type="password" (ionChange)="error = ''"></ion-input>
</ion-item>
<ion-item *ngIf="error" lines="none" style="--background: var(--ion-background-color);">
<ion-label style="font-size: small" color="danger" class="ion-text-wrap">{{ error }}</ion-label>
</ion-item>
</div>
<ion-item style="padding-bottom: 8px;">
<ion-label>
<h1>{{ title }}</h1>
<br />
<p>{{ message }}</p>
</ion-label>
</ion-item>
<div style="display: flex; justify-content: flex-end; align-items: center;">
<ion-button fill="clear" (click)="cancel()">
Cancel
</ion-button>
<ion-button fill="clear" (click)="submit()" [disabled]="!password.length">
{{ type === 'backup' ? 'Create Backup' : 'Restore Backup' }}
</ion-button>
</div>
<form (ngSubmit)="submit()">
<div style="margin-left: 16px;">
<p class="input-label">{{ label }}</p>
<ion-item lines="none" color="dark">
<ion-input [type]="useMask && !unmasked ? 'password' : 'text'" [(ngModel)]="value" name="value" (ionChange)="error = ''"></ion-input>
<ion-button slot="end" *ngIf="useMask" fill="clear" color="light" (click)="toggleMask()">
<ion-icon slot="icon-only" [name]="unmasked ? 'eye-off-outline' : 'eye-outline'" size="small"></ion-icon>
</ion-button>
</ion-item>
<!-- error -->
<p>
<ion-text [color]="error ? 'danger' : 'medium'">{{ error || 'placeholder' }}</ion-text>
</p>
</div>
<div class="ion-text-right">
<ion-button fill="clear" (click)="cancel()">
Cancel
</ion-button>
<ion-button fill="clear" type="submit" [disabled]="!value">
{{ buttonText }}
</ion-button>
</div>
</form>
</div>
</ion-content>

View File

@@ -1,5 +1,6 @@
import { Component, Input } from '@angular/core'
import { ModalController } from '@ionic/angular'
import { IonicSafeString, LoadingController, ModalController } from '@ionic/angular'
import { getErrorMessage } from 'src/app/services/error-toast.service'
@Component({
selector: 'backup-confirmation',
@@ -7,13 +8,19 @@ import { ModalController } from '@ionic/angular'
styleUrls: ['./backup-confirmation.component.scss'],
})
export class BackupConfirmationComponent {
@Input() type: 'backup' | 'restore'
@Input() title: string
@Input() message: string
@Input() label = 'Enter value'
@Input() buttonText = 'Submit'
@Input() useMask = false
@Input() value = ''
@Input() submitFn: (value: string) => Promise<any>
unmasked = false
password = ''
error = ''
error: string | IonicSafeString
constructor (
private readonly modalCtrl: ModalController,
private readonly loadingCtrl: LoadingController,
) { }
toggleMask () {
@@ -21,15 +28,23 @@ export class BackupConfirmationComponent {
}
cancel () {
this.modalCtrl.dismiss({ cancel: true })
this.modalCtrl.dismiss()
}
submit () {
if (!this.password || this.password.length < 12) {
this.error = 'Password must be at least 12 characters in length.'
return
async submit () {
const loader = await this.loadingCtrl.create({
spinner: 'lines',
cssClass: 'loader',
})
loader.present()
try {
await this.submitFn(this.value)
this.modalCtrl.dismiss(undefined, 'success')
} catch (e) {
this.error = getErrorMessage(e)
} finally {
loader.dismiss()
}
const { password } = this
this.modalCtrl.dismiss({ password })
}
}