setup wizard improvements

This commit is contained in:
Matt Hill
2021-11-01 22:43:00 -06:00
committed by Aiden McClelland
parent db9d18766c
commit 305f3d8bcc
17 changed files with 124 additions and 71 deletions

View File

@@ -1,8 +1,7 @@
import { Component } from '@angular/core'
import { NavController } from '@ionic/angular'
import { interval, Observable } from 'rxjs'
import { finalize, skip, take } from 'rxjs/operators'
import { pauseFor } from 'src/app/util/misc.util'
import { finalize, take } from 'rxjs/operators'
@Component({
selector: 'app-init',
@@ -13,16 +12,14 @@ export class InitPage {
progress: Observable<number>
constructor (
private navCtrl: NavController,
private readonly navCtrl: NavController,
) { }
ngOnInit () {
this.progress = interval(500)
.pipe(
skip(1),
take(100),
finalize(async () => {
await pauseFor(1000)
take(101),
finalize(() => {
this.navCtrl.navigateForward('/success')
}),
)

View File

@@ -15,6 +15,7 @@
<ion-card-content class="ion-margin">
<ion-progress-bar color="primary" style="max-width: 700px; margin: auto; padding-bottom: 20px; margin-bottom: 40px;" [value]="stateService.dataProgress"></ion-progress-bar>
<p class="ion-text-start">After completion, you will be prompted to download a file from your Embassy. Save the file somewhere safe, it is the easiest way to recover your Embassy's addresses and SSL certificate in case you lose them.</p>
</ion-card-content>
</ion-card>
</ion-col>

View File

@@ -6,17 +6,17 @@
</ion-toolbar>
</ion-header>
<ion-content color="medium">
<form (ngSubmit)="!!storageDrive ? submitPw() : verifyPw()">
<div style="padding: 8px 24px;">
<div style="padding-bottom: 16px;">
<ng-container *ngIf="!!storageDrive">
<p>Choose a password for your Embassy. <i>Make it good. Write it down.</i></p>
<p style="color: var(--ion-color-warning);">Losing your password can result in total loss of data.</p>
</ng-container>
<p *ngIf="!storageDrive">Enter the password that was used to encrypt this drive.</p>
</div>
<ion-content>
<div style="padding: 8px 24px;">
<div style="padding-bottom: 16px;">
<ng-container *ngIf="!!storageDrive">
<p>Choose a password for your Embassy. <i>Make it good. Write it down.</i></p>
<p style="color: var(--ion-color-warning);">Losing your password can result in total loss of data.</p>
</ng-container>
<p *ngIf="!storageDrive">Enter the password that was used to encrypt this drive.</p>
</div>
<form (ngSubmit)="!!storageDrive ? submitPw() : verifyPw()">
<p class="input-label">
Password:
</p>
@@ -61,9 +61,9 @@
<p style="color: var(--ion-color-danger); font-size: x-small;">{{ verError }}</p>
</div>
</ng-container>
</div>
<input type="submit" style="display: none" />
</form>
<input type="submit" style="display: none" />
</form>
</div>
</ion-content>
<ion-footer>

View File

@@ -9,7 +9,7 @@ import * as argon2 from '@start9labs/argon2'
styleUrls: ['password.page.scss'],
})
export class PasswordPage {
@ViewChild('focusInput', { static: false }) elem: IonInput
@ViewChild('focusInput') elem: IonInput
@Input() recoveryPartition: PartitionInfo
@Input() storageDrive: DiskInfo

View File

@@ -12,24 +12,17 @@
<div style="padding-bottom: 16px;">
<p>Verify the product key for the chosen recovery drive.</p>
</div>
<ion-item
color="dark"
[class]="error ? 'error-border' : ''"
>
<ion-item color="dark">
<ion-input
#focusInput
[(ngModel)]="productKey"
[ngModelOptions]="{'standalone': true}"
[type]="!unmasked ? 'password' : 'text'"
placeholder="Enter Product Key"
maxlength="64"
maxlength="12"
></ion-input>
<ion-button fill="clear" color="light" (click)="unmasked = !unmasked">
<ion-icon slot="icon-only" [name]="unmasked ? 'eye-off-outline' : 'eye-outline'" size="small"></ion-icon>
</ion-button>
</ion-item>
<p style="color: var(--ion-color-danger);">{{ error }}</p>
<div style="height: 16px;">
<p style="color: var(--ion-color-danger); font-size: x-small;">{{ error }}</p>
</div>
</div>
<input type="submit" style="display: none" />
</form>

View File

@@ -9,7 +9,7 @@ import { HttpService } from 'src/app/services/api/http.service'
styleUrls: ['prod-key-modal.page.scss'],
})
export class ProdKeyModal {
@ViewChild('focusInput', { static: false }) elem: IonInput
@ViewChild('focusInput') elem: IonInput
@Input() recoveryPartition: PartitionInfo
error = ''
@@ -23,6 +23,10 @@ export class ProdKeyModal {
private readonly httpService: HttpService,
) { }
ngAfterViewInit () {
setTimeout(() => this.elem.setFocus(), 400)
}
async verifyProductKey () {
if (!this.productKey) return

View File

@@ -18,7 +18,14 @@
<p class="input-label">Product Key</p>
<ion-item color="dark">
<ion-icon slot="start" name="key-outline" style="margin-right: 16px;"></ion-icon>
<ion-input #focusInput color="medium" maxlength="12" name="productKey" [(ngModel)]="productKey" (ionChange)="error = ''"></ion-input>
<ion-input
#focusInput
name="productKey"
[(ngModel)]="productKey"
(ionChange)="error = ''"
maxlength="12"
>
</ion-input>
</ion-item>
<div class="ion-text-left">
<p *ngIf="error" style="padding-top: 4px"><ion-text color="danger">{{ error }}</ion-text></p>

View File

@@ -10,7 +10,7 @@ import { StateService } from 'src/app/services/state.service'
styleUrls: ['product-key.page.scss'],
})
export class ProductKeyPage {
@ViewChild('focusInput', { static: false }) elem: IonInput
@ViewChild('focusInput') elem: IonInput
productKey: string
error: string
@@ -22,7 +22,7 @@ export class ProductKeyPage {
private readonly httpService: HttpService,
) { }
ngAfterViewInit () {
ionViewDidEnter () {
setTimeout(() => this.elem.setFocus(), 400)
}

View File

@@ -1,14 +1,14 @@
<ion-content>
<ion-grid style="padding-top: 32px; height: 100%; max-width: 540px;">
<ion-row style="height: 100%;">
<ion-col class="ion-text-center">
<ion-col>
<div style="padding-bottom: 32px;">
<div style="padding-bottom: 32px;" class="ion-text-center">
<img src="assets/png/logo.png" style="max-width: 240px;" />
</div>
<ion-card color="dark">
<ion-card-header class="ion-text-center" style="padding-bottom: 8px;">
<ion-card-header class="ion-text-center">
<ion-card-title>Select Recovery Drive</ion-card-title>
<ion-card-subtitle>Select the drive containing the Embassy you want to recover.</ion-card-subtitle>
</ion-card-header>
@@ -29,10 +29,8 @@
<ion-item-group>
<ng-container *ngIf="loading">
<ion-item-divider color="light">
<ion-skeleton-text animated style="width: 120px; height: 16px;"></ion-skeleton-text>
</ion-item-divider>
<ion-item color="light">
<ion-skeleton-text animated class="skeleton-header"></ion-skeleton-text>
<ion-item color="light" style="padding-bottom: 10px;">
<ion-avatar slot="start" style="margin-right: 24px;">
<ion-skeleton-text animated style="width: 30px; height: 30px; border-radius: 0;"></ion-skeleton-text>
</ion-avatar>
@@ -45,18 +43,18 @@
</ng-container>
<!-- loaded -->
<div *ngFor="let drive of drives">
<ion-item-divider color="light">
<div *ngFor="let drive of drives" class="ion-padding-bottom">
<h2 class="drive-label">
{{ drive.vendor || 'Unknown Vendor' }} - {{ drive.model || 'Unknown Model' }} - {{ drive.capacity | convertBytes }}
</ion-item-divider>
<ion-item color="light" button *ngFor="let partition of drive.partitions" [disabled]="!partitionClickable(partition)" (click)="choosePartition(partition)">
</h2>
<ion-item lines="none" button *ngFor="let partition of drive.partitions" [disabled]="!partitionClickable(partition)" (click)="choosePartition(partition)">
<ion-icon slot="start" name="save-outline"></ion-icon>
<ion-label>
<h1>{{ partition.label || partition.logicalname }}</h1>
<h2>{{ drive.capacity | convertBytes }}</h2>
<h2>{{ partition.capacity | convertBytes }}</h2>
<p *ngIf="partition['embassy-os'] && partition['embassy-os'].full">
<ion-text color="success">
Embassy backups detected
Embassy backup detected
</ion-text>
</p>
</ion-label>

View File

@@ -2,3 +2,19 @@
border: 4px solid var(--ion-color-secondary);
box-shadow: 4px 4px 16px var(--ion-color-light);
}
.drive-label {
font-weight: bold;
padding-bottom: 6px;
}
.skeleton-header {
width: 180px;
height: 18px;
--ion-text-color-rgb: var(--ion-color-light-rgb);
margin-bottom: 6px;
}
ion-item {
padding-bottom: 6px;
}

View File

@@ -38,26 +38,25 @@ export class RecoverPage {
}
partitionClickable (partition: PartitionInfo) {
return partition['embassy-os']?.full && ((!this.stateService.hasProductKey && partition['embassy-os']?.version.startsWith('0.2') ) || this.stateService.hasProductKey)
return partition['embassy-os']?.full && (this.stateService.hasProductKey || this.is02x(partition))
}
async getDrives () {
try {
this.drives = await this.apiService.getDrives()
this.drives = (await this.apiService.getDrives()).filter(d => d.partitions.length)
const importableDrive = this.drives.filter(d => !!d.guid)[0]
const importableDrive = this.drives.find(d => !!d.guid)
if (!!importableDrive && !this.hasShownGuidAlert) {
const alert = await this.alertCtrl.create({
header: 'Warning',
subHeader: 'Drive contains data!',
message: 'All data stored on this drive will be permanently deleted.',
header: 'Embassy Drive Detected',
message: 'A valid EmbassyOS data drive has been detected. To use this drive in its current state, simply click "Use Drive" below.',
buttons: [
{
role: 'cancel',
text: 'Dismiss',
text: 'Cancel',
},
{
text: 'Use',
text: 'Use Drive',
handler: async () => {
await this.importDrive(importableDrive.guid)
},
@@ -113,6 +112,7 @@ export class RecoverPage {
})
await modal.present()
// if no product key, it means they are an upgrade kit user
} else {
const modal = await this.modalController.create({
component: ProdKeyModal,
@@ -140,4 +140,8 @@ export class RecoverPage {
}
await this.navCtrl.navigateForward(`/embassy`)
}
private is02x (partition: PartitionInfo): boolean {
return !this.stateService.hasProductKey && partition['embassy-os']?.version.startsWith('0.2')
}
}

View File

@@ -17,7 +17,7 @@ export class SuccessPage {
) { }
ngAfterViewInit () {
this.download()
setTimeout(() => this.download(), 500)
}
async copy (address: string): Promise<void> {