mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 20:14:49 +00:00
setup wizard first draft
This commit is contained in:
committed by
Aiden McClelland
parent
77340ce769
commit
834abdc330
30
setup-wizard/src/app/app-routing.module.ts
Normal file
30
setup-wizard/src/app/app-routing.module.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { PreloadAllModules, RouterModule, Routes } from '@angular/router';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: 'wizard',
|
||||
loadChildren: () => import('./pages/home/home.module').then( m => m.HomePageModule)
|
||||
},
|
||||
{
|
||||
path: 'recover',
|
||||
loadChildren: () => import('./pages/recover/recover.module').then( m => m.RecoverPageModule)
|
||||
},
|
||||
{
|
||||
path: 'password',
|
||||
loadChildren: () => import('./pages/password/password.module').then( m => m.PasswordPageModule)
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
redirectTo: 'wizard',
|
||||
pathMatch: 'full'
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules })
|
||||
],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class AppRoutingModule { }
|
||||
11
setup-wizard/src/app/app.component.html
Normal file
11
setup-wizard/src/app/app.component.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<ion-app>
|
||||
<ion-content *ngIf="stateService.loading; else loaded">
|
||||
<h2 color="light">Connecting to Embassy</h2>
|
||||
</ion-content>
|
||||
<ng-template #loaded>
|
||||
<ion-content *ngIf="error">
|
||||
<h2 color="light">{{ error }}</h2>
|
||||
</ion-content>
|
||||
<ion-router-outlet *ngIf="!error"></ion-router-outlet>
|
||||
</ng-template>
|
||||
</ion-app>
|
||||
0
setup-wizard/src/app/app.component.scss
Normal file
0
setup-wizard/src/app/app.component.scss
Normal file
51
setup-wizard/src/app/app.component.ts
Normal file
51
setup-wizard/src/app/app.component.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { NavController, ToastController } from '@ionic/angular';
|
||||
import { ApiService } from './services/api/api.service';
|
||||
import { StateService } from './services/state.sevice';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: 'app.component.html',
|
||||
styleUrls: ['app.component.scss'],
|
||||
})
|
||||
export class AppComponent {
|
||||
|
||||
constructor(
|
||||
private readonly apiService: ApiService,
|
||||
private readonly stateService: StateService,
|
||||
private readonly navCtrl: NavController,
|
||||
public toastController: ToastController
|
||||
) {
|
||||
this.apiService.watchError$.subscribe(error => {
|
||||
if(error) {
|
||||
this.presentToast(error)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
await this.stateService.getState()
|
||||
if(this.stateService.hasPassword) {
|
||||
//redirect to embassyOS
|
||||
} else if (this.stateService.recoveryDrive) {
|
||||
await this.navCtrl.navigateForward(`/recover`)
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
33
setup-wizard/src/app/app.module.ts
Normal file
33
setup-wizard/src/app/app.module.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { RouteReuseStrategy } from '@angular/router';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { ApiService } from './services/api/api.service'
|
||||
import { MockApiService } from './services/api/mock-api.service'
|
||||
|
||||
import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
|
||||
import * as config from './config/config'
|
||||
import { AppComponent } from './app.component';
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [AppComponent],
|
||||
entryComponents: [],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
IonicModule.forRoot(),
|
||||
AppRoutingModule,
|
||||
HttpClientModule,
|
||||
],
|
||||
providers: [
|
||||
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
|
||||
{
|
||||
provide: ApiService ,
|
||||
useFactory: () => {
|
||||
return new MockApiService()
|
||||
},
|
||||
},
|
||||
],
|
||||
bootstrap: [AppComponent],
|
||||
})
|
||||
export class AppModule {}
|
||||
7
setup-wizard/src/app/config/config.ts
Normal file
7
setup-wizard/src/app/config/config.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export const config: Config = {
|
||||
"useMocks": true
|
||||
}
|
||||
|
||||
interface Config {
|
||||
useMocks: boolean
|
||||
}
|
||||
16
setup-wizard/src/app/pages/home/home-routing.module.ts
Normal file
16
setup-wizard/src/app/pages/home/home-routing.module.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { HomePage } from './home.page';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: HomePage,
|
||||
}
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class HomePageRoutingModule {}
|
||||
19
setup-wizard/src/app/pages/home/home.module.ts
Normal file
19
setup-wizard/src/app/pages/home/home.module.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { IonicModule } from '@ionic/angular';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { HomePage } from './home.page';
|
||||
|
||||
import { HomePageRoutingModule } from './home-routing.module';
|
||||
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
IonicModule,
|
||||
HomePageRoutingModule
|
||||
],
|
||||
declarations: [HomePage]
|
||||
})
|
||||
export class HomePageModule {}
|
||||
52
setup-wizard/src/app/pages/home/home.page.html
Normal file
52
setup-wizard/src/app/pages/home/home.page.html
Normal file
@@ -0,0 +1,52 @@
|
||||
<ion-header [translucent]="true">
|
||||
<ion-toolbar>
|
||||
<ion-title>
|
||||
Setup Wizard
|
||||
</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content [fullscreen]="true">
|
||||
<div *ngIf="!stateService.selectedDataDrive">
|
||||
<h2 color="light">Select Data Drive</h2>
|
||||
<ion-card
|
||||
*ngFor="let drive of dataDrives"
|
||||
(click)="selectDrive(drive['logical-name'])"
|
||||
button="true"
|
||||
[class.selected]="selectedDrive === drive['logical-name']"
|
||||
color="light"
|
||||
>
|
||||
<ion-card-header>
|
||||
<ion-card-title>{{drive["logical-name"]}}</ion-card-title>
|
||||
<ion-card-subtitle>{{drive.labels}}</ion-card-subtitle>
|
||||
</ion-card-header>
|
||||
|
||||
<ion-card-content>
|
||||
Currently using {{drive.used}} out of {{drive.capacity}} bytes.
|
||||
</ion-card-content>
|
||||
</ion-card>
|
||||
<ion-button (click)="warn()" color="primary" [disabled]="!selectedDrive">Next</ion-button>
|
||||
</div>
|
||||
<div *ngIf="stateService.selectedDataDrive">
|
||||
<ion-card
|
||||
[routerLink]="['/password']"
|
||||
button="true"
|
||||
color="light"
|
||||
>
|
||||
<ion-card-header>
|
||||
<ion-card-title>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"
|
||||
>
|
||||
<ion-card-header>
|
||||
<ion-card-title>Recover</ion-card-title>
|
||||
<ion-card-subtitle>Recover the data from an old embassy</ion-card-subtitle>
|
||||
</ion-card-header>
|
||||
</ion-card>
|
||||
</div>
|
||||
</ion-content>
|
||||
31
setup-wizard/src/app/pages/home/home.page.scss
Normal file
31
setup-wizard/src/app/pages/home/home.page.scss
Normal file
@@ -0,0 +1,31 @@
|
||||
#container {
|
||||
text-align: center;
|
||||
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
#container strong {
|
||||
font-size: 20px;
|
||||
line-height: 26px;
|
||||
}
|
||||
|
||||
#container p {
|
||||
font-size: 16px;
|
||||
line-height: 22px;
|
||||
|
||||
color: #8c8c8c;
|
||||
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#container a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.selected {
|
||||
border: 1px solid white;
|
||||
}
|
||||
66
setup-wizard/src/app/pages/home/home.page.ts
Normal file
66
setup-wizard/src/app/pages/home/home.page.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { AlertController, NavController } from '@ionic/angular'
|
||||
import { ApiService } from 'src/app/services/api/api.service'
|
||||
import { StateService } from 'src/app/services/state.sevice'
|
||||
|
||||
@Component({
|
||||
selector: 'app-home',
|
||||
templateUrl: 'home.page.html',
|
||||
styleUrls: ['home.page.scss'],
|
||||
})
|
||||
export class HomePage {
|
||||
dataDrives = []
|
||||
selectedDrive = null
|
||||
|
||||
constructor(
|
||||
private readonly apiService: ApiService,
|
||||
private readonly stateService: StateService,
|
||||
public alertController: AlertController,
|
||||
private readonly navCtrl: NavController,
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
if(!this.stateService.selectedDataDrive) {
|
||||
this.dataDrives = await this.apiService.getStorageDisks()
|
||||
}
|
||||
}
|
||||
|
||||
selectDrive(name: string) {
|
||||
if (name === this.selectedDrive) {
|
||||
this.selectedDrive = null
|
||||
} else {
|
||||
this.selectedDrive = name
|
||||
}
|
||||
}
|
||||
|
||||
async warn() {
|
||||
const alert = await this.alertController.create({
|
||||
cssClass: 'my-custom-class',
|
||||
header: 'Warning!',
|
||||
message: 'This disk will be entirely wiped of all memory.',
|
||||
buttons: [
|
||||
{
|
||||
text: 'Cancel',
|
||||
role: 'cancel',
|
||||
cssClass: 'secondary',
|
||||
handler: () => {
|
||||
this.selectedDrive = null
|
||||
}
|
||||
}, {
|
||||
text: 'Okay',
|
||||
handler: async () => {
|
||||
await this.chooseDisk()
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
await alert.present();
|
||||
}
|
||||
|
||||
async chooseDisk() {
|
||||
await this.apiService.selectStorageDisk({logicalName: this.selectedDrive})
|
||||
this.stateService.selectedDataDrive = this.selectedDrive
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { PasswordPage } from './password.page';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: PasswordPage,
|
||||
}
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class PasswordPageRoutingModule {}
|
||||
19
setup-wizard/src/app/pages/password/password.module.ts
Normal file
19
setup-wizard/src/app/pages/password/password.module.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { PasswordPage } from './password.page'
|
||||
|
||||
import { PasswordPageRoutingModule } from './password-routing.module'
|
||||
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
IonicModule,
|
||||
PasswordPageRoutingModule
|
||||
],
|
||||
declarations: [PasswordPage]
|
||||
})
|
||||
export class PasswordPageModule {}
|
||||
20
setup-wizard/src/app/pages/password/password.page.html
Normal file
20
setup-wizard/src/app/pages/password/password.page.html
Normal file
@@ -0,0 +1,20 @@
|
||||
<ion-header [translucent]="true">
|
||||
<ion-toolbar>
|
||||
<ion-title>
|
||||
Setup Wizard
|
||||
</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content [fullscreen]="true" color="light">
|
||||
<div *ngIf="stateService.recoveryDrive">
|
||||
<h2>Enter Password</h2>
|
||||
<ion-input type="password" [(ngModel)]="password">Password:</ion-input>
|
||||
</div>
|
||||
<div *ngIf="!stateService.recoveryDrive">
|
||||
<h2>Create Password</h2>
|
||||
<ion-input type="password" [(ngModel)]="password">Password:</ion-input>
|
||||
<ion-input type="password" [(ngModel)]="passwordVer">Verify Password:</ion-input>
|
||||
</div>
|
||||
<p>{{error}}</p>
|
||||
<ion-button (click)="submitPassword()" [disabled]="!password">Submit</ion-button>
|
||||
</ion-content>
|
||||
31
setup-wizard/src/app/pages/password/password.page.scss
Normal file
31
setup-wizard/src/app/pages/password/password.page.scss
Normal file
@@ -0,0 +1,31 @@
|
||||
#container {
|
||||
text-align: center;
|
||||
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
#container strong {
|
||||
font-size: 20px;
|
||||
line-height: 26px;
|
||||
}
|
||||
|
||||
#container p {
|
||||
font-size: 16px;
|
||||
line-height: 22px;
|
||||
|
||||
color: #8c8c8c;
|
||||
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#container a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.selected {
|
||||
border: 1px solid white;
|
||||
}
|
||||
33
setup-wizard/src/app/pages/password/password.page.ts
Normal file
33
setup-wizard/src/app/pages/password/password.page.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { AlertController, NavController } from '@ionic/angular'
|
||||
import { ApiService } from 'src/app/services/api/api.service'
|
||||
import { StateService } from 'src/app/services/state.sevice'
|
||||
|
||||
@Component({
|
||||
selector: 'password-page',
|
||||
templateUrl: 'password.page.html',
|
||||
styleUrls: ['password.page.scss'],
|
||||
})
|
||||
export class PasswordPage {
|
||||
error = ''
|
||||
password = ''
|
||||
passwordVer = ''
|
||||
|
||||
constructor(
|
||||
private readonly apiService: ApiService,
|
||||
private readonly stateService: StateService,
|
||||
public alertController: AlertController,
|
||||
) {}
|
||||
|
||||
async submitPassword () {
|
||||
if (!this.stateService.recoveryDrive && this.password !== this.passwordVer) {
|
||||
console.log('here')
|
||||
this.error="*passwords dont match"
|
||||
} else {
|
||||
console.log('submitting')
|
||||
await this.apiService.submitPassword(this.password)
|
||||
// @Todo navigate to embassy os
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
16
setup-wizard/src/app/pages/recover/recover-routing.module.ts
Normal file
16
setup-wizard/src/app/pages/recover/recover-routing.module.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { RecoverPage } from './recover.page';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: RecoverPage,
|
||||
}
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class RecoverPageRoutingModule {}
|
||||
19
setup-wizard/src/app/pages/recover/recover.module.ts
Normal file
19
setup-wizard/src/app/pages/recover/recover.module.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { IonicModule } from '@ionic/angular';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { RecoverPage } from './recover.page';
|
||||
|
||||
import { RecoverPageRoutingModule } from './recover-routing.module';
|
||||
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
IonicModule,
|
||||
RecoverPageRoutingModule
|
||||
],
|
||||
declarations: [RecoverPage]
|
||||
})
|
||||
export class RecoverPageModule {}
|
||||
40
setup-wizard/src/app/pages/recover/recover.page.html
Normal file
40
setup-wizard/src/app/pages/recover/recover.page.html
Normal file
@@ -0,0 +1,40 @@
|
||||
<ion-header [translucent]="true">
|
||||
<ion-toolbar>
|
||||
<ion-title>
|
||||
Setup Wizard
|
||||
</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content [fullscreen]="true">
|
||||
<div *ngIf="!stateService.recoveryDrive">
|
||||
<h2 color="light">Select Data Drive</h2>
|
||||
<ion-card
|
||||
*ngFor="let drive of dataDrives"
|
||||
(click)="selectDrive(drive['logical-name'])"
|
||||
button="true"
|
||||
[class.selected]="selectedDrive === drive['logical-name']"
|
||||
color="light"
|
||||
>
|
||||
<ion-card-header>
|
||||
<ion-card-title>{{drive["logical-name"]}}</ion-card-title>
|
||||
<ion-card-subtitle>{{drive.name}}</ion-card-subtitle>
|
||||
</ion-card-header>
|
||||
|
||||
<ion-card-content>
|
||||
Currently running {{drive.version}}
|
||||
</ion-card-content>
|
||||
</ion-card>
|
||||
<ion-button (click)="warn()" color="primary" [disabled]="!selectedDrive">Next</ion-button>
|
||||
</div>
|
||||
<div *ngIf="stateService.recoveryDrive">
|
||||
<h2>Progress: {{ 100 * stateService.dataProgress }}% <ion-spinner *ngIf="stateService.dataProgress != 1"></ion-spinner> </h2>
|
||||
<ion-progress-bar value="{{stateService.dataProgress}}"></ion-progress-bar>
|
||||
<ion-button
|
||||
[routerLink]="['/password']"
|
||||
color="primary"
|
||||
[disabled]="stateService.dataProgress != 1"
|
||||
>
|
||||
Next
|
||||
</ion-button>
|
||||
</div>
|
||||
</ion-content>
|
||||
31
setup-wizard/src/app/pages/recover/recover.page.scss
Normal file
31
setup-wizard/src/app/pages/recover/recover.page.scss
Normal file
@@ -0,0 +1,31 @@
|
||||
#container {
|
||||
text-align: center;
|
||||
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
#container strong {
|
||||
font-size: 20px;
|
||||
line-height: 26px;
|
||||
}
|
||||
|
||||
#container p {
|
||||
font-size: 16px;
|
||||
line-height: 22px;
|
||||
|
||||
color: #8c8c8c;
|
||||
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#container a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.selected {
|
||||
border: 1px solid white;
|
||||
}
|
||||
68
setup-wizard/src/app/pages/recover/recover.page.ts
Normal file
68
setup-wizard/src/app/pages/recover/recover.page.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { AlertController, NavController } from '@ionic/angular'
|
||||
import { ApiService } from 'src/app/services/api/api.service'
|
||||
import { StateService } from 'src/app/services/state.sevice'
|
||||
|
||||
@Component({
|
||||
selector: 'recover-page',
|
||||
templateUrl: 'recover.page.html',
|
||||
styleUrls: ['recover.page.scss'],
|
||||
})
|
||||
export class RecoverPage {
|
||||
dataDrives = []
|
||||
selectedDrive = null
|
||||
|
||||
constructor(
|
||||
private readonly apiService: ApiService,
|
||||
private readonly stateService: StateService,
|
||||
public alertController: AlertController,
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
if(!this.stateService.recoveryDrive) {
|
||||
this.dataDrives = await this.apiService.getEmbassyDrives()
|
||||
} else {
|
||||
this.stateService.pollDataTransferProgress()
|
||||
}
|
||||
}
|
||||
|
||||
selectDrive(name: string) {
|
||||
if (name === this.selectedDrive) {
|
||||
this.selectedDrive = null
|
||||
} else {
|
||||
this.selectedDrive = name
|
||||
}
|
||||
}
|
||||
|
||||
async warn() {
|
||||
const alert = await this.alertController.create({
|
||||
cssClass: 'my-custom-class',
|
||||
header: 'Warning!',
|
||||
message: 'Once you select this the recovery process will begin.',
|
||||
buttons: [
|
||||
{
|
||||
text: 'Cancel',
|
||||
role: 'cancel',
|
||||
cssClass: 'secondary',
|
||||
handler: () => {
|
||||
this.selectedDrive = null
|
||||
}
|
||||
}, {
|
||||
text: 'Okay',
|
||||
handler: async () => {
|
||||
await this.chooseDisk()
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
await alert.present();
|
||||
}
|
||||
|
||||
async chooseDisk() {
|
||||
await this.apiService.selectEmbassyDrive({logicalName: this.selectedDrive})
|
||||
this.stateService.recoveryDrive = this.selectedDrive
|
||||
this.stateService.pollDataTransferProgress()
|
||||
}
|
||||
|
||||
}
|
||||
37
setup-wizard/src/app/services/api/api.service.ts
Normal file
37
setup-wizard/src/app/services/api/api.service.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { Subject } from "rxjs";
|
||||
|
||||
export abstract class ApiService {
|
||||
protected error$: Subject<string> = new Subject()
|
||||
watchError$ = this.error$.asObservable()
|
||||
abstract getState (): Promise<State>
|
||||
abstract getStorageDisks (): Promise<StorageDisk[]>
|
||||
abstract selectStorageDisk (disk: { logicalName: string }): Promise<void>
|
||||
abstract getEmbassyDrives (): Promise<EmbassyDrive[]>
|
||||
abstract selectEmbassyDrive (disk: { logicalName: string }): Promise<void>
|
||||
abstract getDataTransferProgress () : Promise<TransferProgress>
|
||||
abstract submitPassword (password: string) : Promise<void>
|
||||
}
|
||||
|
||||
export interface State {
|
||||
'selected-data-drive': string | null,
|
||||
'recovery-drive': string | null,
|
||||
'has-password': boolean,
|
||||
}
|
||||
|
||||
export interface TransferProgress {
|
||||
'bytes-transfered': number
|
||||
'total-bytes': number
|
||||
}
|
||||
|
||||
export interface StorageDisk {
|
||||
"logical-name": string
|
||||
labels: string[]
|
||||
capacity: number
|
||||
used: number
|
||||
}
|
||||
|
||||
export interface EmbassyDrive {
|
||||
"logical-name": string
|
||||
version: string
|
||||
name: string
|
||||
}
|
||||
77
setup-wizard/src/app/services/api/mock-api.service.ts
Normal file
77
setup-wizard/src/app/services/api/mock-api.service.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import { Injectable } from "@angular/core";
|
||||
import { pauseFor } from "../state.sevice";
|
||||
import { ApiService } from "./api.service"
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class MockApiService extends ApiService {
|
||||
|
||||
constructor () {
|
||||
super()
|
||||
}
|
||||
|
||||
async getState () {
|
||||
await pauseFor(2000)
|
||||
return {
|
||||
'selected-data-drive': 'name1',
|
||||
'recovery-drive': null,
|
||||
'has-password': false,
|
||||
'data-transfer-progress': null
|
||||
}
|
||||
}
|
||||
|
||||
async getDataTransferProgress () {
|
||||
tries = Math.min(tries + 1, 4)
|
||||
return {
|
||||
'bytes-transfered': tries,
|
||||
'total-bytes': 4
|
||||
}
|
||||
}
|
||||
|
||||
async getStorageDisks () {
|
||||
await pauseFor(2000)
|
||||
return [
|
||||
{
|
||||
"logical-name": 'name1',
|
||||
labels: ['label 1', 'label 2'],
|
||||
capacity: 1600,
|
||||
used: 200,
|
||||
},
|
||||
{
|
||||
"logical-name": 'name2',
|
||||
labels: [],
|
||||
capacity: 1600,
|
||||
used: 0,
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
async selectStorageDisk(disk) {
|
||||
await pauseFor(200)
|
||||
return
|
||||
}
|
||||
|
||||
async getEmbassyDrives () {
|
||||
await pauseFor(2000)
|
||||
return [
|
||||
{
|
||||
"logical-name": 'name1',
|
||||
version: '0.2.3',
|
||||
name: 'Embassy 0.2.3'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
async selectEmbassyDrive(disk) {
|
||||
await pauseFor(200)
|
||||
return
|
||||
}
|
||||
|
||||
async submitPassword(password) {
|
||||
await pauseFor(200)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
let tries = 4
|
||||
51
setup-wizard/src/app/services/state.sevice.ts
Normal file
51
setup-wizard/src/app/services/state.sevice.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { Injectable } from "@angular/core";
|
||||
import { ApiService } from "./api/api.service"
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class StateService {
|
||||
loading = true
|
||||
|
||||
selectedDataDrive: string
|
||||
recoveryDrive: string
|
||||
hasPassword: Boolean
|
||||
dataTransferProgress: { bytesTransfered: number, totalBytes: number } | null
|
||||
dataProgress = 0
|
||||
|
||||
constructor (
|
||||
private readonly apiService: ApiService
|
||||
) {}
|
||||
|
||||
async getState () {
|
||||
const state = await this.apiService.getState()
|
||||
if(state) {
|
||||
this.selectedDataDrive = state['selected-data-drive']
|
||||
this.recoveryDrive = state['recovery-drive']
|
||||
this.hasPassword = state['has-password']
|
||||
|
||||
this.loading = false
|
||||
}
|
||||
}
|
||||
|
||||
async pollDataTransferProgress () {
|
||||
if (
|
||||
this.dataTransferProgress?.totalBytes &&
|
||||
this.dataTransferProgress.bytesTransfered === this.dataTransferProgress.totalBytes
|
||||
) {return }
|
||||
const progress = await this.apiService.getDataTransferProgress()
|
||||
this.dataTransferProgress = {
|
||||
bytesTransfered: progress['bytes-transfered'],
|
||||
totalBytes: progress['total-bytes']
|
||||
}
|
||||
if (this.dataTransferProgress.totalBytes) {
|
||||
this.dataProgress = this.dataTransferProgress.bytesTransfered / this.dataTransferProgress.totalBytes
|
||||
}
|
||||
await pauseFor(7000)
|
||||
this.pollDataTransferProgress()
|
||||
}
|
||||
}
|
||||
|
||||
export function pauseFor (ms: number): Promise<void> {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
Reference in New Issue
Block a user