mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-04-04 14:29:45 +00:00
checkpoiint
This commit is contained in:
committed by
Matt Hill
parent
1cc7cc439f
commit
bce87cc819
@@ -1,17 +1,17 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { PreloadAllModules, RouterModule, Routes } from '@angular/router';
|
import { PreloadAllModules, RouterModule, Routes } from '@angular/router';
|
||||||
import { CanActivateHome, CanActivateRecover } from './guards/guards'
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
path: 'wizard',
|
path: 'wizard',
|
||||||
loadChildren: () => import('./pages/home/home.module').then( m => m.HomePageModule),
|
loadChildren: () => import('./pages/home/home.module').then( m => m.HomePageModule),
|
||||||
canActivate: [CanActivateHome],
|
|
||||||
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'recover',
|
path: 'recover',
|
||||||
loadChildren: () => import('./pages/recover/recover.module').then( m => m.RecoverPageModule),
|
loadChildren: () => import('./pages/recover/recover.module').then( m => m.RecoverPageModule),
|
||||||
canActivate: [CanActivateRecover]
|
},
|
||||||
|
{
|
||||||
|
path: 'embassy',
|
||||||
|
loadChildren: () => import('./pages/embassy/embassy.module').then( m => m.EmbassyPageModule),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
<ion-app>
|
<ion-app>
|
||||||
<body>
|
<ion-content class="has-header">
|
||||||
<ion-content *ngIf="!stateService.loading" class="has-header">
|
<ion-router-outlet></ion-router-outlet>
|
||||||
<ion-router-outlet></ion-router-outlet>
|
</ion-content>
|
||||||
</ion-content>
|
|
||||||
</body>
|
|
||||||
</ion-app>
|
</ion-app>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { Component, OnInit } from '@angular/core'
|
import { Component, OnInit } from '@angular/core'
|
||||||
import { LoadingController, NavController, ToastController } from '@ionic/angular'
|
import { NavController } from '@ionic/angular'
|
||||||
import { ApiService } from './services/api/api.service'
|
|
||||||
import { StateService } from './services/state.service'
|
import { StateService } from './services/state.service'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -11,51 +10,13 @@ import { StateService } from './services/state.service'
|
|||||||
export class AppComponent implements OnInit {
|
export class AppComponent implements OnInit {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly apiService: ApiService,
|
|
||||||
private readonly stateService: StateService,
|
|
||||||
private readonly navCtrl: NavController,
|
private readonly navCtrl: NavController,
|
||||||
private readonly toastController: ToastController,
|
private stateService: StateService
|
||||||
private readonly loadingCtrl: LoadingController,
|
|
||||||
|
|
||||||
) {
|
) {}
|
||||||
this.apiService.watchError$.subscribe(error => {
|
|
||||||
if(error) {
|
|
||||||
this.presentToast(error)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
const loader = await this.loadingCtrl.create({
|
this.stateService.reset()
|
||||||
message: 'Connecting to your Embassy'
|
await this.navCtrl.navigateForward(`/wizard`)
|
||||||
})
|
|
||||||
await loader.present()
|
|
||||||
try {
|
|
||||||
await this.stateService.getState()
|
|
||||||
if (this.stateService.recoveryDrive) {
|
|
||||||
await this.navCtrl.navigateForward(`/recover`)
|
|
||||||
} else {
|
|
||||||
await this.navCtrl.navigateForward(`/wizard`)
|
|
||||||
}
|
|
||||||
} catch (e) {} finally {
|
|
||||||
loader.dismiss()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async presentToast(error: string) {
|
|
||||||
const toast = await this.toastController.create({
|
|
||||||
header: 'Error',
|
|
||||||
message: error,
|
|
||||||
position: 'bottom',
|
|
||||||
buttons: [
|
|
||||||
{
|
|
||||||
text: 'X',
|
|
||||||
role: 'cancel',
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
await toast.present()
|
|
||||||
|
|
||||||
await toast.onDidDismiss()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
import { Injectable } from '@angular/core'
|
|
||||||
import { CanActivate } from '@angular/router'
|
|
||||||
import { StateService } from '../services/state.service'
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root',
|
|
||||||
})
|
|
||||||
export class CanActivateHome implements CanActivate {
|
|
||||||
|
|
||||||
constructor (
|
|
||||||
private readonly stateService: StateService
|
|
||||||
) {}
|
|
||||||
|
|
||||||
canActivate (): boolean {
|
|
||||||
console.log(!!this.stateService.recoveryDrive)
|
|
||||||
return !!this.stateService.recoveryDrive ? false : true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root',
|
|
||||||
})
|
|
||||||
export class CanActivateRecover implements CanActivate {
|
|
||||||
|
|
||||||
constructor (
|
|
||||||
private readonly stateService: StateService
|
|
||||||
) {}
|
|
||||||
|
|
||||||
canActivate (): boolean {
|
|
||||||
return this.stateService.dataDrive ? true : false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
16
setup-wizard/src/app/pages/embassy/embassy-routing.module.ts
Normal file
16
setup-wizard/src/app/pages/embassy/embassy-routing.module.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
import { EmbassyPage } from './embassy.page';
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: EmbassyPage,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(routes)],
|
||||||
|
exports: [RouterModule]
|
||||||
|
})
|
||||||
|
export class EmbassyPageRoutingModule {}
|
||||||
21
setup-wizard/src/app/pages/embassy/embassy.module.ts
Normal file
21
setup-wizard/src/app/pages/embassy/embassy.module.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { IonicModule } from '@ionic/angular';
|
||||||
|
import { FormsModule } from '@angular/forms';
|
||||||
|
import { EmbassyPage } from './embassy.page';
|
||||||
|
import { PasswordPageModule } from '../password/password.module';
|
||||||
|
|
||||||
|
import { EmbassyPageRoutingModule } from './embassy-routing.module';
|
||||||
|
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
FormsModule,
|
||||||
|
IonicModule,
|
||||||
|
EmbassyPageRoutingModule,
|
||||||
|
PasswordPageModule,
|
||||||
|
],
|
||||||
|
declarations: [EmbassyPage]
|
||||||
|
})
|
||||||
|
export class EmbassyPageModule {}
|
||||||
57
setup-wizard/src/app/pages/embassy/embassy.page.html
Normal file
57
setup-wizard/src/app/pages/embassy/embassy.page.html
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
<ion-content color="light">
|
||||||
|
<ion-grid style="padding-top: 32px; height: 100%; max-width: 540px;">
|
||||||
|
<ion-row class="ion-align-items-center" style="height: 100%;">
|
||||||
|
<ion-col class="ion-text-center">
|
||||||
|
|
||||||
|
<div style="padding-bottom: 32px;">
|
||||||
|
<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-title>{{ loading ? 'Loading Embassy Drives' : 'Select Embassy Drive'}}</ion-card-title>
|
||||||
|
</ion-card-header>
|
||||||
|
|
||||||
|
<ion-card-content class="ion-margin">
|
||||||
|
<ng-container *ngIf="!loading && !embassyDrives.length">
|
||||||
|
<h2 color="light">No Embassy drives found</h2>
|
||||||
|
<p color="light">Please connect an Embassy drive to your embassy and refresh the page.</p>
|
||||||
|
<ion-button
|
||||||
|
(click)="window.location.reload()"
|
||||||
|
style="text-align:center"
|
||||||
|
class="claim-button"
|
||||||
|
>
|
||||||
|
Refresh
|
||||||
|
</ion-button>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ion-item-group>
|
||||||
|
<ng-container *ngIf="loading">
|
||||||
|
<ion-item button color="light" lines="none">
|
||||||
|
<ion-avatar slot="start">
|
||||||
|
<ion-skeleton-text animated></ion-skeleton-text>
|
||||||
|
</ion-avatar>
|
||||||
|
<ion-label class="ion-text-wrap">
|
||||||
|
<ion-skeleton-text style="width: 80%; margin: 13px 0;" animated></ion-skeleton-text>
|
||||||
|
<ion-skeleton-text style="width: 60%; margin: 10px 0;" animated></ion-skeleton-text>
|
||||||
|
<ion-skeleton-text style="width: 30%; margin: 8px 0;" animated></ion-skeleton-text>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container *ngIf="embassyDrives.length">
|
||||||
|
<ion-item (click)="chooseDrive(drive)" class="ion-margin-bottom" button color="light" lines="none" *ngFor="let drive of embassyDrives">
|
||||||
|
<ion-icon slot="start" name="save-outline"></ion-icon>
|
||||||
|
<ion-label class="ion-text-wrap">
|
||||||
|
<h1>{{ drive.logicalname }}</h1>
|
||||||
|
<h2 style="min-height: 19px;">{{ drive.labels.length ? drive.labels.join(' / ') : 'unnamed' }}</h2>
|
||||||
|
<p> Using {{drive.used.toFixed(2)}} of {{drive.capacity.toFixed(2)}} GiB</p>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
</ng-container>
|
||||||
|
</ion-item-group>
|
||||||
|
</ion-card-content>
|
||||||
|
</ion-card>
|
||||||
|
</ion-col>
|
||||||
|
</ion-row>
|
||||||
|
</ion-grid>
|
||||||
|
</ion-content>
|
||||||
44
setup-wizard/src/app/pages/embassy/embassy.page.scss
Normal file
44
setup-wizard/src/app/pages/embassy/embassy.page.scss
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
.selected {
|
||||||
|
border: 4px solid gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
ion-card-title {
|
||||||
|
margin: 24px 0;
|
||||||
|
font-family: 'Montserrat';
|
||||||
|
font-size: x-large;
|
||||||
|
--color: var(--ion-color-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ion-item {
|
||||||
|
// --border-radius: 4px;
|
||||||
|
// --border-style: solid;
|
||||||
|
// --border-width: 1px;
|
||||||
|
// --border-color: var(--ion-color-light);
|
||||||
|
// }
|
||||||
|
|
||||||
|
.input-label {
|
||||||
|
text-align: left;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
font-size: small;
|
||||||
|
color: var(--ion-color-light);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.claim-button {
|
||||||
|
margin-inline-start: 0;
|
||||||
|
margin-inline-end: 0;
|
||||||
|
margin-top: 24px;
|
||||||
|
height: 48px;
|
||||||
|
--background: linear-gradient(45deg, var(--ion-color-light) 16%, var(--ion-color-medium) 150%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-footer {
|
||||||
|
text-align: left;
|
||||||
|
--background: rgb(222, 222, 222);
|
||||||
|
border-top: solid;
|
||||||
|
border-width: 1px;
|
||||||
|
border-color: var(--ion-color-medium);
|
||||||
|
ion-item {
|
||||||
|
--border-color: var(--ion-color-medium);
|
||||||
|
}
|
||||||
|
}
|
||||||
42
setup-wizard/src/app/pages/embassy/embassy.page.ts
Normal file
42
setup-wizard/src/app/pages/embassy/embassy.page.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { Component } from '@angular/core'
|
||||||
|
import { ModalController, NavController } from '@ionic/angular'
|
||||||
|
import { ApiService, EmbassyDrive } from 'src/app/services/api/api.service'
|
||||||
|
import { StateService } from 'src/app/services/state.service'
|
||||||
|
import { PasswordPage } from '../password/password.page'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'embassy',
|
||||||
|
templateUrl: 'embassy.page.html',
|
||||||
|
styleUrls: ['embassy.page.scss'],
|
||||||
|
})
|
||||||
|
export class EmbassyPage {
|
||||||
|
embassyDrives = []
|
||||||
|
selectedDrive: EmbassyDrive = null
|
||||||
|
loading = true
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly apiService: ApiService,
|
||||||
|
private readonly navCtrl: NavController,
|
||||||
|
private modalController: ModalController,
|
||||||
|
private stateService: StateService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async ngOnInit() {
|
||||||
|
this.embassyDrives = await this.apiService.getEmbassyDrives()
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
|
||||||
|
async chooseDrive(drive: EmbassyDrive) {
|
||||||
|
const modal = await this.modalController.create({
|
||||||
|
component: PasswordPage,
|
||||||
|
componentProps: {
|
||||||
|
embassyDrive: drive,
|
||||||
|
verify: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
modal.onDidDismiss().then(async ret => {
|
||||||
|
if (!ret.data) return
|
||||||
|
})
|
||||||
|
await modal.present();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,107 +7,37 @@
|
|||||||
<img src="assets/png/logo.png" style="max-width: 240px;" />
|
<img src="assets/png/logo.png" style="max-width: 240px;" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ion-card>
|
<ion-card color="dark">
|
||||||
<ion-card-header class="ion-text-center" style="padding-bottom: 8px;">
|
|
||||||
<ion-card-title>{{ stateService.dataDrive ? 'Startup Options' : 'Select Data Drive'}}</ion-card-title>
|
|
||||||
</ion-card-header>
|
|
||||||
|
|
||||||
<ion-card-content class="ion-margin">
|
<ion-card-content class="ion-margin">
|
||||||
<div *ngIf="!stateService.loading && !stateService.dataDrive && dataDrives">
|
<!-- fresh -->
|
||||||
<div *ngIf="!dataDrives.length">
|
<ion-card
|
||||||
<h2 color="light">No data drives found</h2>
|
(click)="embassyNav()"
|
||||||
<p color="light">Please connect a data drive to your embassy and refresh the page.</p>
|
button="true"
|
||||||
<ion-button
|
color="light"
|
||||||
(click)="window.location.reload()"
|
style="text-align: center; background-color: #00919b !important; height: 160px; margin-bottom: 20px; box-shadow: 4px 4px 16px var(--ion-color-light);
|
||||||
style="text-align:center"
|
"
|
||||||
class="claim-button"
|
>
|
||||||
>
|
<ion-card-header>
|
||||||
Refresh
|
<ion-card-title style="font-size: 40px;">Start Fresh</ion-card-title>
|
||||||
</ion-button>
|
<ion-card-subtitle>Get started with a brand new Embassy</ion-card-subtitle>
|
||||||
</div>
|
</ion-card-header>
|
||||||
<div *ngIf="dataDrives.length">
|
|
||||||
<ion-card
|
<!-- recover -->
|
||||||
color="light"
|
</ion-card>
|
||||||
*ngFor="let drive of dataDrives"
|
<ion-card
|
||||||
(click)="selectDrive(drive)"
|
(click)="recoverNav()"
|
||||||
button="true"
|
button="true"
|
||||||
style="border-radius: 11px;"
|
color="light"
|
||||||
[class.selected]="selectedDrive?.logicalname === drive.logicalname"
|
style="text-align: center; background-color: #bf5900 !important; height: 160px; box-shadow: 4px 4px 16px var(--ion-color-light);
|
||||||
>
|
"
|
||||||
<ion-card-header>
|
>
|
||||||
<ion-card-title>{{drive.logicalname}}</ion-card-title>
|
<ion-card-header>
|
||||||
<ion-card-subtitle>{{drive.labels}}</ion-card-subtitle>
|
<ion-card-title style="font-size: 40px;">Recover</ion-card-title>
|
||||||
</ion-card-header>
|
<ion-card-subtitle>Recover the data from an old embassy</ion-card-subtitle>
|
||||||
|
</ion-card-header>
|
||||||
<ion-card-content>
|
</ion-card>
|
||||||
Using {{drive.used}} out of {{drive.capacity}} bytes.
|
|
||||||
</ion-card-content>
|
|
||||||
</ion-card>
|
|
||||||
<div style="width: 100%; text-align: center;">
|
|
||||||
<ion-button
|
|
||||||
(click)="warn()"
|
|
||||||
[disabled]="!selectedDrive"
|
|
||||||
class="claim-button"
|
|
||||||
>
|
|
||||||
Next
|
|
||||||
</ion-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div *ngIf="stateService.dataDrive">
|
|
||||||
<ion-card
|
|
||||||
(click)="presentPasswordModal()"
|
|
||||||
button="true"
|
|
||||||
color="light"
|
|
||||||
class="wiz-card"
|
|
||||||
style="text-align: center; background-color: #00919b !important; height: 160px;"
|
|
||||||
>
|
|
||||||
<ion-card-header>
|
|
||||||
<ion-card-title style="font-size: 40px">Start Fresh</ion-card-title>
|
|
||||||
<ion-card-subtitle>Get started with a brand new Embassy</ion-card-subtitle>
|
|
||||||
</ion-card-header>
|
|
||||||
</ion-card>
|
|
||||||
<ion-card
|
|
||||||
[routerLink]="['/recover']"
|
|
||||||
button="true"
|
|
||||||
color="light"
|
|
||||||
class="wiz-card"
|
|
||||||
style="text-align: center; background-color: #bf5900 !important; height: 160px;"
|
|
||||||
>
|
|
||||||
<ion-card-header>
|
|
||||||
<ion-card-title style="font-size: 40px">Recover</ion-card-title>
|
|
||||||
<ion-card-subtitle>Recover the data from an old embassy</ion-card-subtitle>
|
|
||||||
</ion-card-header>
|
|
||||||
</ion-card>
|
|
||||||
</div>
|
|
||||||
</ion-card-content>
|
</ion-card-content>
|
||||||
|
|
||||||
<ion-card-header class="card-footer">
|
|
||||||
<ion-item lines="none">
|
|
||||||
<ion-label class="ion-text-wrap">
|
|
||||||
<p *ngIf="!stateService.dataDrive">
|
|
||||||
Choose drive to save all Embassy data to.
|
|
||||||
</p>
|
|
||||||
<p *ngIf="stateService.dataDrive">
|
|
||||||
Spin up a brand new Embassy, or recover data from an old device.
|
|
||||||
</p>
|
|
||||||
</ion-label>
|
|
||||||
</ion-item>
|
|
||||||
</ion-card-header>
|
|
||||||
</ion-card>
|
</ion-card>
|
||||||
|
|
||||||
<div class="divider"></div>
|
|
||||||
|
|
||||||
<div class="footer">
|
|
||||||
<p style="margin-bottom: 30px;">Contact/Community</p>
|
|
||||||
<ion-icon name="logo-youtube"></ion-icon>
|
|
||||||
<ion-icon name="paper-plane"></ion-icon>
|
|
||||||
<ion-icon name="mail"></ion-icon>
|
|
||||||
<ion-icon name="logo-github"></ion-icon>
|
|
||||||
<ion-icon name="logo-twitter"></ion-icon>
|
|
||||||
<ion-icon name="logo-mastodon"></ion-icon>
|
|
||||||
<ion-icon name="logo-medium"></ion-icon>
|
|
||||||
</div>
|
|
||||||
</ion-col>
|
</ion-col>
|
||||||
</ion-row>
|
</ion-row>
|
||||||
</ion-grid>
|
</ion-grid>
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
import { Component } from '@angular/core'
|
import { Component } from '@angular/core'
|
||||||
import { AlertController, ModalController, LoadingController } from '@ionic/angular'
|
import { NavController } from '@ionic/angular'
|
||||||
import { ApiService, DataDrive } from 'src/app/services/api/api.service'
|
|
||||||
import { StateService } from 'src/app/services/state.service'
|
|
||||||
import { PasswordPage } from '../password/password.page'
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'home',
|
selector: 'home',
|
||||||
@@ -10,109 +7,16 @@ import { PasswordPage } from '../password/password.page'
|
|||||||
styleUrls: ['home.page.scss'],
|
styleUrls: ['home.page.scss'],
|
||||||
})
|
})
|
||||||
export class HomePage {
|
export class HomePage {
|
||||||
dataDrives = []
|
|
||||||
selectedDrive: DataDrive = null
|
|
||||||
console = console
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly apiService: ApiService,
|
private readonly navCtrl: NavController,
|
||||||
private readonly stateService: StateService,
|
|
||||||
private readonly alertController: AlertController,
|
|
||||||
private readonly modalController: ModalController,
|
|
||||||
private readonly loadingCtrl: LoadingController,
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async recoverNav () {
|
||||||
if(!this.stateService.dataDrive) {
|
await this.navCtrl.navigateForward(`/recover`, { animationDirection: 'forward' })
|
||||||
const loader = await this.loadingCtrl.create({
|
|
||||||
message: 'Fetching data drives'
|
|
||||||
})
|
|
||||||
await loader.present()
|
|
||||||
this.dataDrives = await this.apiService.getDataDrives()
|
|
||||||
loader.dismiss()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
selectDrive(drive: DataDrive) {
|
async embassyNav () {
|
||||||
if (drive.logicalname === this.selectedDrive?.logicalname) {
|
await this.navCtrl.navigateForward(`/embassy`, { animationDirection: 'forward' })
|
||||||
this.selectedDrive = null
|
|
||||||
} else {
|
|
||||||
this.selectedDrive = drive
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async warn() {
|
|
||||||
const alert = await this.alertController.create({
|
|
||||||
cssClass: 'my-custom-class',
|
|
||||||
header: 'Warning!',
|
|
||||||
message: 'This drive will be entirely wiped of all existing memory.',
|
|
||||||
backdropDismiss: false,
|
|
||||||
buttons: [
|
|
||||||
{
|
|
||||||
text: 'Cancel',
|
|
||||||
role: 'cancel',
|
|
||||||
cssClass: 'cancel-button',
|
|
||||||
handler: () => {
|
|
||||||
this.selectedDrive = null
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
text: 'Okay',
|
|
||||||
cssClass: 'okay-button',
|
|
||||||
handler: async () => {
|
|
||||||
await this.chooseDrive()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
await alert.present();
|
|
||||||
}
|
|
||||||
|
|
||||||
async chooseDrive() {
|
|
||||||
const loader = await this.loadingCtrl.create({
|
|
||||||
message: 'Selecting data drive'
|
|
||||||
})
|
|
||||||
await loader.present()
|
|
||||||
try {
|
|
||||||
await this.apiService.selectDataDrive(this.selectedDrive.logicalname)
|
|
||||||
this.stateService.dataDrive = this.selectedDrive
|
|
||||||
} catch (e) {
|
|
||||||
} finally {
|
|
||||||
loader.dismiss()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async presentPasswordModal() {
|
|
||||||
const modal = await this.modalController.create({
|
|
||||||
component: PasswordPage,
|
|
||||||
backdropDismiss: false,
|
|
||||||
cssClass: 'pw-modal',
|
|
||||||
})
|
|
||||||
modal.onDidDismiss().then(ret => {
|
|
||||||
if(ret.data) {
|
|
||||||
const pass = ret.data.password
|
|
||||||
if (pass) {
|
|
||||||
this.submitPassword(pass)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
await modal.present();
|
|
||||||
}
|
|
||||||
|
|
||||||
async submitPassword (pw: string) {
|
|
||||||
const loader = await this.loadingCtrl.create({
|
|
||||||
message: 'Setting up your Embassy'
|
|
||||||
})
|
|
||||||
await loader.present()
|
|
||||||
|
|
||||||
try {
|
|
||||||
await this.apiService.submitPassword(pw)
|
|
||||||
console.log('reloading')
|
|
||||||
location.reload()
|
|
||||||
} catch (e) {
|
|
||||||
} finally {
|
|
||||||
loader.dismiss()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,26 +1,56 @@
|
|||||||
<ion-header>
|
<ion-header>
|
||||||
<ion-toolbar color="light">
|
<ion-toolbar color="light">
|
||||||
<ion-title>
|
<ion-title>
|
||||||
<span *ngIf="needsVer">Enter Password</span>
|
<span *ngIf="verify">Verify Recovery Drive Password</span>
|
||||||
<span *ngIf="!needsVer">Create Password</span>
|
<span *ngIf="!verify">Set Password</span>
|
||||||
</ion-title>
|
</ion-title>
|
||||||
</ion-toolbar>
|
</ion-toolbar>
|
||||||
</ion-header>
|
</ion-header>
|
||||||
<ion-content color="light">
|
|
||||||
<div style="padding: 8px 24px; height: 100%;">
|
|
||||||
<div *ngIf="needsVer">
|
|
||||||
<ion-input style="color: #e6f4f1 !important;" type="password" [(ngModel)]="password" placeholder="_________">Password:</ion-input>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div *ngIf="!needsVer">
|
<ion-content color="light">
|
||||||
<ion-input (click)="error = ''" (focusout)="validate()" style="color: #e6f4f1 !important;" (keyup)="error = ''" type="password" [(ngModel)]="password" placeholder="_________">Password:</ion-input>
|
<div style="padding: 8px 24px;">
|
||||||
<ion-input (focusout)="checkMatch()" style="color: #e6f4f1 !important;" (keyup)="error = ''" type="password" [(ngModel)]="passwordVer" placeholder="_________">Verify Password:</ion-input>
|
<ng-container *ngIf="verify">
|
||||||
|
<p style="padding: 3px 0">Password:</p>
|
||||||
|
<ion-input (click)="error = ''" color="light" type="password" [(ngModel)]="password" placeholder="*********"></ion-input>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<div *ngIf="!verify">
|
||||||
|
<p *ngIf="embassyDrive.used > 0" style="padding-bottom: 15px;color: var(--ion-color-warning);"><b>Warning:</b> After submit, any data currently stored on <b>{{ embassyDrive.labels.length ? embassyDrive.labels.join(' / ') : embassyDrive.logicalname }}</b> will be wiped.</p>
|
||||||
|
<p style="padding: 3px 0">Password:</p>
|
||||||
|
<ion-input
|
||||||
|
(click)="error = ''"
|
||||||
|
color="light"
|
||||||
|
(focusout)="validate()"
|
||||||
|
(keyup)="error = ''"
|
||||||
|
type="password"
|
||||||
|
[(ngModel)]="password"
|
||||||
|
placeholder="*********"
|
||||||
|
></ion-input>
|
||||||
|
|
||||||
|
<p style="padding: 3px 0">Verify Password:</p>
|
||||||
|
<ion-input
|
||||||
|
(focusout)="validate()"
|
||||||
|
color="light"
|
||||||
|
(keyup)="error = ''"
|
||||||
|
type="password"
|
||||||
|
[(ngModel)]="passwordVer"
|
||||||
|
placeholder="*********"
|
||||||
|
></ion-input>
|
||||||
</div>
|
</div>
|
||||||
<p style="color: #FF4961;">{{error}}</p>
|
<p style="color: #FF4961;">{{error}}</p>
|
||||||
<div style="text-align: right; position: absolute; bottom: 10px; right: 24px;">
|
|
||||||
<ion-button class="claim-button" item-end (click)="cancel()" style="margin-right: 10px;">Cancel</ion-button>
|
|
||||||
<ion-button class="claim-button" item-end (click)="submitPassword()" [disabled]="!password">Submit</ion-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</ion-content>
|
</ion-content>
|
||||||
|
|
||||||
|
<ion-footer>
|
||||||
|
<ion-toolbar color="light">
|
||||||
|
<ion-buttons slot="end" class="ion-padding-end">
|
||||||
|
<ion-button class="claim-button" (click)="cancel()">
|
||||||
|
Cancel
|
||||||
|
</ion-button>
|
||||||
|
<ion-button class="claim-button" (click)="verify ? verifyPw() : submitPw()">
|
||||||
|
{{ verify ? 'Verify Password' : 'Submit' }}
|
||||||
|
</ion-button>
|
||||||
|
</ion-buttons>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-footer>
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,15 @@
|
|||||||
.claim-button {
|
.claim-button {
|
||||||
margin-inline-start: 0;
|
margin-inline-start: 0;
|
||||||
margin-inline-end: 0;
|
margin-inline-end: 0;
|
||||||
margin-top: 24px;
|
margin: 6px;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
--background: linear-gradient(45deg, var(--ion-color-light) 16%, var(--ion-color-medium) 150%);
|
--background: linear-gradient(45deg, var(--ion-color-light) 16%, var(--ion-color-medium) 150%);
|
||||||
|
}
|
||||||
|
|
||||||
|
ion-input {
|
||||||
|
font-weight: 500;
|
||||||
|
--placeholder-font-weight: 400;
|
||||||
|
width: 100%;
|
||||||
|
background: var(--ion-color-dark);
|
||||||
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Component, Input } from '@angular/core'
|
import { Component, Input } from '@angular/core'
|
||||||
import { ModalController } from '@ionic/angular'
|
import { LoadingController, ModalController } from '@ionic/angular'
|
||||||
import { RecoveryDrive } from 'src/app/services/api/api.service'
|
import { ApiService, EmbassyDrive, RecoveryDrive } from 'src/app/services/api/api.service'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'password',
|
selector: 'password',
|
||||||
@@ -9,51 +9,61 @@ import { RecoveryDrive } from 'src/app/services/api/api.service'
|
|||||||
})
|
})
|
||||||
export class PasswordPage {
|
export class PasswordPage {
|
||||||
@Input() recoveryDrive: RecoveryDrive
|
@Input() recoveryDrive: RecoveryDrive
|
||||||
|
@Input() embassyDrive: EmbassyDrive
|
||||||
needsVer: boolean
|
@Input() verify: boolean
|
||||||
|
|
||||||
error = ''
|
error = ''
|
||||||
password = ''
|
password = ''
|
||||||
passwordVer = ''
|
passwordVer = ''
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private modalController: ModalController
|
private modalController: ModalController,
|
||||||
|
private apiService: ApiService,
|
||||||
|
private loadingCtrl: LoadingController
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() { }
|
||||||
this.needsVer = !!this.recoveryDrive && !this.recoveryDrive.version.startsWith('0.2')
|
|
||||||
|
async verifyPw () {
|
||||||
|
|
||||||
|
if(!this.recoveryDrive) this.error = 'No recovery drive' // unreachable
|
||||||
|
const loader = await this.loadingCtrl.create({
|
||||||
|
message: 'Verifying Password'
|
||||||
|
})
|
||||||
|
await loader.present()
|
||||||
|
|
||||||
|
try {
|
||||||
|
const isCorrectPassword = await this.apiService.verifyRecoveryPassword(this.recoveryDrive.logicalname, this.password)
|
||||||
|
if(isCorrectPassword) {
|
||||||
|
this.modalController.dismiss({ password: this.password })
|
||||||
|
} else {
|
||||||
|
this.error = "Incorrect password provided"
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.error = 'Error connecting to Embassy'
|
||||||
|
} finally {
|
||||||
|
loader.dismiss()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async submitPassword () {
|
async submitPw () {
|
||||||
if(!this.needsVer) {
|
this.validate()
|
||||||
this.validate()
|
if(this.error) return
|
||||||
if(!this.error) {
|
|
||||||
this.checkMatch()
|
|
||||||
}
|
|
||||||
this.modalController.dismiss({
|
|
||||||
password: this.password,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
this.modalController.dismiss({
|
|
||||||
password: this.password,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
validate () {
|
validate () {
|
||||||
if (this.password.length < 12) {
|
if (this.password.length < 12) {
|
||||||
this.error="*passwords must be 12 characters or greater"
|
this.error="*passwords must be 12 characters or greater"
|
||||||
}
|
} else if (this.password !== this.passwordVer) {
|
||||||
}
|
|
||||||
|
|
||||||
checkMatch () {
|
|
||||||
if (this.password !== this.passwordVer) {
|
|
||||||
this.error="*passwords dont match"
|
this.error="*passwords dont match"
|
||||||
} else {
|
} else {
|
||||||
this.error = ''
|
this.error = ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
cancel () {
|
cancel () {
|
||||||
this.modalController.dismiss()
|
this.modalController.dismiss()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,50 +7,61 @@
|
|||||||
<img src="assets/png/logo.png" style="max-width: 240px;" />
|
<img src="assets/png/logo.png" style="max-width: 240px;" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ion-card>
|
<ion-card color="dark">
|
||||||
<ion-card-header class="ion-text-center" style="padding-bottom: 8px;">
|
<ion-card-header class="ion-text-center" style="padding-bottom: 8px;">
|
||||||
<ion-card-title>{{ stateService.recoveryDrive && stateService.polling ? 'Recovery Progress: ' + (100 * stateService.dataProgress) + '%' : 'Select Recovery Drive'}}</ion-card-title>
|
<ion-card-title>{{ loading ? 'Loading Recovery Drives' : 'Select Recovery Drive'}}</ion-card-title>
|
||||||
</ion-card-header>
|
</ion-card-header>
|
||||||
|
|
||||||
|
|
||||||
<ion-card-content class="ion-margin">
|
<ion-card-content class="ion-margin">
|
||||||
<div *ngIf="!loading && recoveryDrives && !recoveryDrives.length">
|
<ng-container *ngIf="!loading && !recoveryDrives.length">
|
||||||
<h2 color="light">No recovery drives found</h2>
|
<h2 color="light">No recovery drives found</h2>
|
||||||
<p color="light">Please connect a recovery drive to your embassy and refresh the page.</p>
|
<p color="light">Please connect a recovery drive to your embassy and refresh the page.</p>
|
||||||
</div>
|
<ion-button
|
||||||
<div *ngIf="!stateService.polling && recoveryDrives?.length">
|
(click)="window.location.reload()"
|
||||||
<div>
|
style="text-align:center"
|
||||||
<ion-card
|
class="claim-button"
|
||||||
class="wiz-card"
|
>
|
||||||
*ngFor="let drive of recoveryDrives"
|
Refresh
|
||||||
(click)="selectDrive(drive)"
|
</ion-button>
|
||||||
button="true"
|
</ng-container>
|
||||||
[class.selected]="selectedDrive?.logicalname === drive.logicalname"
|
|
||||||
color="light"
|
<ion-item-group>
|
||||||
>
|
<ng-container *ngIf="loading">
|
||||||
<ion-card-header>
|
<ion-item button color="light" lines="none">
|
||||||
<ion-card-title>{{drive.logicalname}}</ion-card-title>
|
<ion-avatar slot="start">
|
||||||
<ion-card-subtitle>{{drive.name}}</ion-card-subtitle>
|
<ion-skeleton-text animated></ion-skeleton-text>
|
||||||
</ion-card-header>
|
</ion-avatar>
|
||||||
|
<ion-label class="ion-text-wrap">
|
||||||
<ion-card-content>
|
<ion-skeleton-text style="width: 80%; margin: 13px 0;" animated></ion-skeleton-text>
|
||||||
Currently running {{drive.version}}
|
<ion-skeleton-text style="width: 60%; margin: 10px 0;" animated></ion-skeleton-text>
|
||||||
</ion-card-content>
|
<ion-skeleton-text style="width: 30%; margin: 8px 0;" animated></ion-skeleton-text>
|
||||||
</ion-card>
|
</ion-label>
|
||||||
<div style="width: 100%; text-align: center;;">
|
</ion-item>
|
||||||
<ion-button
|
</ng-container>
|
||||||
(click)="presentPasswordModal()"
|
<ng-container *ngIf="recoveryDrives.length">
|
||||||
[disabled]="!selectedDrive"
|
<ion-item (click)="chooseDrive(drive)" class="ion-margin-bottom" button color="light" lines="none" *ngFor="let drive of recoveryDrives" [ngClass]="drive.logicalname === selectedDrive?.logicalname ? 'selected' : null">
|
||||||
style="text-align:center"
|
<ion-icon slot="start" name="save-outline"></ion-icon>
|
||||||
size="large"
|
<ion-label class="ion-text-wrap">
|
||||||
|
<h1>{{ drive.logicalname }}</h1>
|
||||||
|
<h2>{{ drive.name }}</h2>
|
||||||
|
<p> Embassy version: {{drive.version}}</p>
|
||||||
|
</ion-label>
|
||||||
|
<ion-icon *ngIf="drive.version.startsWith('0.2') || passwords[drive.logicalname]" color="success" slot="end" name="lock-open-outline"></ion-icon>
|
||||||
|
<ion-icon *ngIf="!drive.version.startsWith('0.2') && !passwords[drive.logicalname]" color="danger" slot="end" name="lock-closed-outline"></ion-icon>
|
||||||
|
|
||||||
|
</ion-item>
|
||||||
|
</ng-container>
|
||||||
|
<ion-button
|
||||||
|
(click)="selectRecoveryDrive()"
|
||||||
|
[disabled]="!selectedDrive || (!passwords[selectedDrive.logicalname] && !selectedDrive.version.startsWith('0.2'))"
|
||||||
class="claim-button"
|
class="claim-button"
|
||||||
>
|
>
|
||||||
Next
|
Next
|
||||||
</ion-button>
|
</ion-button>
|
||||||
</div>
|
</ion-item-group>
|
||||||
</div>
|
|
||||||
|
<!-- <div *ngIf="stateService.polling" style="width: 100%; text-align: center;">
|
||||||
</div>
|
|
||||||
<div *ngIf="stateService.polling" style="width: 100%; text-align: center;">
|
|
||||||
<ion-progress-bar color="primary" style="max-width: 700px; margin: auto; padding-bottom: 20px; margin-bottom: 40px;" value="{{stateService.dataProgress}}"></ion-progress-bar>
|
<ion-progress-bar color="primary" style="max-width: 700px; margin: auto; padding-bottom: 20px; margin-bottom: 40px;" value="{{stateService.dataProgress}}"></ion-progress-bar>
|
||||||
<ion-button
|
<ion-button
|
||||||
(click)="navToEmbassy()"
|
(click)="navToEmbassy()"
|
||||||
@@ -60,35 +71,9 @@
|
|||||||
>
|
>
|
||||||
Go To Embassy
|
Go To Embassy
|
||||||
</ion-button>
|
</ion-button>
|
||||||
</div>
|
</div> -->
|
||||||
</ion-card-content>
|
</ion-card-content>
|
||||||
|
|
||||||
<ion-card-header class="card-footer">
|
|
||||||
<ion-item lines="none">
|
|
||||||
<ion-label class="ion-text-wrap">
|
|
||||||
<p *ngIf="!stateService.recoveryDrive">
|
|
||||||
Choose drive recover Embassy data from.
|
|
||||||
</p>
|
|
||||||
<p *ngIf="stateService.recoveryDrive && stateService.polling">
|
|
||||||
Recovering old Embassy data.
|
|
||||||
</p>
|
|
||||||
</ion-label>
|
|
||||||
</ion-item>
|
|
||||||
</ion-card-header>
|
|
||||||
</ion-card>
|
</ion-card>
|
||||||
|
|
||||||
<div class="divider"></div>
|
|
||||||
|
|
||||||
<div class="footer">
|
|
||||||
<p style="margin-bottom: 30px;">Contact/Community</p>
|
|
||||||
<ion-icon name="logo-youtube"></ion-icon>
|
|
||||||
<ion-icon name="paper-plane"></ion-icon>
|
|
||||||
<ion-icon name="mail"></ion-icon>
|
|
||||||
<ion-icon name="logo-github"></ion-icon>
|
|
||||||
<ion-icon name="logo-twitter"></ion-icon>
|
|
||||||
<ion-icon name="logo-mastodon"></ion-icon>
|
|
||||||
<ion-icon name="logo-medium"></ion-icon>
|
|
||||||
</div>
|
|
||||||
</ion-col>
|
</ion-col>
|
||||||
</ion-row>
|
</ion-row>
|
||||||
</ion-grid>
|
</ion-grid>
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
.selected {
|
.selected {
|
||||||
border: 4px solid gray;
|
border-radius: 8px;
|
||||||
|
border: 4px solid var(--ion-color-secondary);
|
||||||
|
box-shadow: 4px 4px 16px var(--ion-color-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
ion-card-title {
|
ion-card-title {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Component } from '@angular/core'
|
import { Component } from '@angular/core'
|
||||||
import { AlertController, ModalController, LoadingController } from '@ionic/angular'
|
import { ModalController, NavController } from '@ionic/angular'
|
||||||
import { ApiService, RecoveryDrive } from 'src/app/services/api/api.service'
|
import { ApiService, RecoveryDrive } from 'src/app/services/api/api.service'
|
||||||
import { StateService } from 'src/app/services/state.service'
|
import { StateService } from 'src/app/services/state.service'
|
||||||
import { PasswordPage } from '../password/password.page'
|
import { PasswordPage } from '../password/password.page'
|
||||||
@@ -10,86 +10,58 @@ import { PasswordPage } from '../password/password.page'
|
|||||||
styleUrls: ['recover.page.scss'],
|
styleUrls: ['recover.page.scss'],
|
||||||
})
|
})
|
||||||
export class RecoverPage {
|
export class RecoverPage {
|
||||||
|
passwords = {}
|
||||||
recoveryDrives = []
|
recoveryDrives = []
|
||||||
selectedDrive: RecoveryDrive = null
|
selectedDrive: RecoveryDrive = null
|
||||||
loading = true
|
loading = true
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly apiService: ApiService,
|
private readonly apiService: ApiService,
|
||||||
private readonly stateService: StateService,
|
private readonly navCtrl: NavController,
|
||||||
public alertController: AlertController,
|
|
||||||
private modalController: ModalController,
|
private modalController: ModalController,
|
||||||
private readonly loadingCtrl: LoadingController,
|
private stateService: StateService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
if(!this.stateService.recoveryDrive) {
|
this.recoveryDrives = await this.apiService.getRecoveryDrives()
|
||||||
const loader = await this.loadingCtrl.create({
|
this.loading = false
|
||||||
message: 'Fetching recovery drives'
|
|
||||||
})
|
|
||||||
await loader.present()
|
|
||||||
this.recoveryDrives = await this.apiService.getRecoveryDrives()
|
|
||||||
|
|
||||||
loader.dismiss()
|
|
||||||
this.loading = false
|
|
||||||
} else {
|
|
||||||
this.loading = false
|
|
||||||
this.stateService.pollDataTransferProgress()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
selectDrive(drive: RecoveryDrive) {
|
async chooseDrive(drive: RecoveryDrive) {
|
||||||
if (drive.logicalname === this.selectedDrive?.logicalname) {
|
|
||||||
|
if (this.selectedDrive?.logicalname === drive.logicalname) {
|
||||||
this.selectedDrive = null
|
this.selectedDrive = null
|
||||||
|
return
|
||||||
} else {
|
} else {
|
||||||
this.selectedDrive = drive
|
this.selectedDrive = drive
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
async chooseDrive() {
|
if (drive.version.startsWith('0.2') || this.passwords[drive.logicalname]) return
|
||||||
this.presentPasswordModal()
|
|
||||||
}
|
|
||||||
|
|
||||||
async presentPasswordModal() {
|
|
||||||
const modal = await this.modalController.create({
|
const modal = await this.modalController.create({
|
||||||
component: PasswordPage,
|
component: PasswordPage,
|
||||||
backdropDismiss: false,
|
|
||||||
cssClass: 'pw-modal',
|
|
||||||
componentProps: {
|
componentProps: {
|
||||||
recoveryDrive: this.selectedDrive,
|
recoveryDrive: this.selectedDrive,
|
||||||
|
verify: true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
modal.onDidDismiss().then(ret => {
|
modal.onDidDismiss().then(async ret => {
|
||||||
if(ret.data) {
|
if (!ret.data) {
|
||||||
const pass = ret.data.password
|
this.selectedDrive = null
|
||||||
if(pass) {
|
} else if(ret.data.password) {
|
||||||
this.submitPWAndDrive(pass)
|
this.passwords[drive.logicalname] = ret.data.password
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
await modal.present();
|
await modal.present();
|
||||||
}
|
}
|
||||||
|
|
||||||
async submitPWAndDrive(pw: string) {
|
async selectRecoveryDrive() {
|
||||||
const loader = await this.loadingCtrl.create({
|
this.stateService.recoveryDrive = this.selectedDrive
|
||||||
message: 'Validating password'
|
const pw = this.passwords[this.selectedDrive.logicalname]
|
||||||
})
|
if(pw) {
|
||||||
await loader.present()
|
this.stateService.recoveryPassword = pw
|
||||||
|
}
|
||||||
try {
|
await this.navCtrl.navigateForward(`/embassy`, { animationDirection: 'forward' })
|
||||||
this.stateService.recoveryDrive = this.selectedDrive
|
|
||||||
await this.apiService.selectRecoveryDrive(this.selectedDrive.logicalname, pw)
|
|
||||||
this.stateService.pollDataTransferProgress()
|
|
||||||
} catch (e) {
|
|
||||||
} finally {
|
|
||||||
loader.dismiss()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async navToEmbassy() {
|
|
||||||
location.reload()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,18 +3,13 @@ import { Subject } from 'rxjs'
|
|||||||
export abstract class ApiService {
|
export abstract class ApiService {
|
||||||
protected error$: Subject<string> = new Subject();
|
protected error$: Subject<string> = new Subject();
|
||||||
watchError$ = this.error$.asObservable();
|
watchError$ = this.error$.asObservable();
|
||||||
abstract getState (): Promise<State>;
|
abstract getEmbassyDrives (): Promise<EmbassyDrive[]>;
|
||||||
abstract getDataDrives (): Promise<DataDrive[]>;
|
abstract selectEmbassyDrive (logicalName: string): Promise<void>;
|
||||||
abstract selectDataDrive (logicalName: string): Promise<void>;
|
|
||||||
abstract getRecoveryDrives (): Promise<RecoveryDrive[]>;
|
abstract getRecoveryDrives (): Promise<RecoveryDrive[]>;
|
||||||
abstract selectRecoveryDrive (logicalName: string, password: string): Promise<void>;
|
abstract selectRecoveryDrive (logicalName: string, password: string): Promise<void>;
|
||||||
abstract getDataTransferProgress (): Promise<TransferProgress>;
|
abstract getDataTransferProgress (): Promise<TransferProgress>;
|
||||||
abstract submitPassword (password: string): Promise<void>;
|
abstract submitPassword (password: string): Promise<void>;
|
||||||
}
|
abstract verifyRecoveryPassword (logicalname: string, password: string): Promise<boolean>;
|
||||||
|
|
||||||
export interface State {
|
|
||||||
'data-drive': DataDrive | null;
|
|
||||||
'recovery-drive': RecoveryDrive | null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TransferProgress {
|
export interface TransferProgress {
|
||||||
@@ -22,7 +17,7 @@ export interface TransferProgress {
|
|||||||
'total-bytes': number;
|
'total-bytes': number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DataDrive {
|
export interface EmbassyDrive {
|
||||||
logicalname: string;
|
logicalname: string;
|
||||||
labels: string[];
|
labels: string[];
|
||||||
capacity: number;
|
capacity: number;
|
||||||
|
|||||||
@@ -14,14 +14,14 @@ export class MockApiService extends ApiService {
|
|||||||
async getState() {
|
async getState() {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return {
|
return {
|
||||||
'data-drive':
|
'embassy-drive':
|
||||||
// null,
|
null,
|
||||||
{
|
// {
|
||||||
logicalname: 'name1',
|
// logicalname: 'name1',
|
||||||
labels: ['label 1', 'label 2'],
|
// labels: ['label 1', 'label 2'],
|
||||||
capacity: 1600,
|
// capacity: 1600,
|
||||||
used: 200,
|
// used: 200,
|
||||||
},
|
// },
|
||||||
'recovery-drive':
|
'recovery-drive':
|
||||||
null,
|
null,
|
||||||
// {
|
// {
|
||||||
@@ -40,25 +40,25 @@ export class MockApiService extends ApiService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getDataDrives() {
|
async getEmbassyDrives() {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
logicalname: 'Name1',
|
logicalname: 'Name1',
|
||||||
labels: ['label 1', 'label 2'],
|
labels: ['label 1', 'label 2'],
|
||||||
capacity: 1600,
|
capacity: 1600.66666,
|
||||||
used: 200,
|
used: 200.1255312,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
logicalname: 'Name2',
|
logicalname: 'Name2',
|
||||||
labels: [],
|
labels: [],
|
||||||
capacity: 1600,
|
capacity: 1600.01234,
|
||||||
used: 0,
|
used: 0.00,
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
async selectDataDrive(drive) {
|
async selectEmbassyDrive(drive) {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -67,9 +67,14 @@ export class MockApiService extends ApiService {
|
|||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
logicalname: 'name1',
|
logicalname: 'Name1',
|
||||||
version: '0.3.3',
|
version: '0.3.3',
|
||||||
name: 'My Embassy'
|
name: 'My Embassy'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
logicalname: 'Name2',
|
||||||
|
version: '0.2.7',
|
||||||
|
name: 'My Embassy'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -83,6 +88,11 @@ export class MockApiService extends ApiService {
|
|||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async verifyRecoveryPassword(logicalname, password) {
|
||||||
|
await pauseFor(2000)
|
||||||
|
return password.length > 8
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let tries = 0
|
let tries = 0
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { ApiService, DataDrive, RecoveryDrive } from './api/api.service'
|
import { ApiService, EmbassyDrive, RecoveryDrive } from './api/api.service'
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class StateService {
|
export class StateService {
|
||||||
loading = true
|
|
||||||
polling = false
|
polling = false
|
||||||
|
|
||||||
dataDrive: DataDrive;
|
embassyDrive: EmbassyDrive;
|
||||||
recoveryDrive: RecoveryDrive;
|
recoveryDrive: RecoveryDrive;
|
||||||
|
recoveryPassword: string
|
||||||
dataTransferProgress: { bytesTransfered: number; totalBytes: number } | null;
|
dataTransferProgress: { bytesTransfered: number; totalBytes: number } | null;
|
||||||
dataProgress = 0;
|
dataProgress = 0;
|
||||||
|
|
||||||
@@ -17,15 +17,14 @@ export class StateService {
|
|||||||
private readonly apiService: ApiService
|
private readonly apiService: ApiService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async getState() {
|
reset() {
|
||||||
this.loading = true
|
this.polling = false
|
||||||
const state = await this.apiService.getState()
|
|
||||||
if(state) {
|
|
||||||
this.dataDrive = state['data-drive']
|
|
||||||
this.recoveryDrive = state['recovery-drive']
|
|
||||||
|
|
||||||
this.loading = false
|
this.embassyDrive = null
|
||||||
}
|
this.recoveryDrive = null
|
||||||
|
this.recoveryPassword = null
|
||||||
|
this.dataTransferProgress = null
|
||||||
|
this.dataProgress = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
async pollDataTransferProgress() {
|
async pollDataTransferProgress() {
|
||||||
|
|||||||
@@ -3,6 +3,9 @@
|
|||||||
|
|
||||||
/** Ionic CSS Variables **/
|
/** Ionic CSS Variables **/
|
||||||
:root {
|
:root {
|
||||||
|
--ion-text-color: var(--ion-color-dark);
|
||||||
|
--ion-text-color-rgb: var(--ion-color-dark-rgb);
|
||||||
|
|
||||||
--ion-font-family: 'Benton Sans';
|
--ion-font-family: 'Benton Sans';
|
||||||
/** primary **/
|
/** primary **/
|
||||||
--ion-color-primary: #428cff;
|
--ion-color-primary: #428cff;
|
||||||
@@ -53,12 +56,12 @@
|
|||||||
--ion-color-danger-tint: #ff5b71;
|
--ion-color-danger-tint: #ff5b71;
|
||||||
|
|
||||||
/** dark **/
|
/** dark **/
|
||||||
--ion-color-dark: #f4f5f8;
|
--ion-color-dark: #e0e0e0;
|
||||||
--ion-color-dark-rgb: 244,245,248;
|
--ion-color-dark-rgb: 224,224,224;
|
||||||
--ion-color-dark-contrast: #000000;
|
--ion-color-dark-contrast: #000000;
|
||||||
--ion-color-dark-contrast-rgb: 0,0,0;
|
--ion-color-dark-contrast-rgb: 0,0,0;
|
||||||
--ion-color-dark-shade: #d7d8da;
|
--ion-color-dark-shade: #bfbfbf;
|
||||||
--ion-color-dark-tint: #f5f6f9;
|
--ion-color-dark-tint: #d8d8d8;
|
||||||
|
|
||||||
/** medium **/
|
/** medium **/
|
||||||
--ion-color-medium: #989aa2;
|
--ion-color-medium: #989aa2;
|
||||||
|
|||||||
Reference in New Issue
Block a user