mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 12:11:56 +00:00
cosmetics plus a slew of little frontend rendering bugs
This commit is contained in:
committed by
Aiden McClelland
parent
c18a119c70
commit
7dc53a4e85
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 })
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user