mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 18:31:52 +00:00
complete flow
This commit is contained in:
committed by
Matt Hill
parent
bce87cc819
commit
f7b5c5fadd
@@ -13,6 +13,10 @@ const routes: Routes = [
|
||||
path: 'embassy',
|
||||
loadChildren: () => import('./pages/embassy/embassy.module').then( m => m.EmbassyPageModule),
|
||||
},
|
||||
{
|
||||
path: 'loading',
|
||||
loadChildren: () => import('./pages/loading/loading.module').then( m => m.LoadingPageModule),
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { ModalController, NavController } from '@ionic/angular'
|
||||
import { AlertController, 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'
|
||||
@@ -18,7 +18,8 @@ export class EmbassyPage {
|
||||
private readonly apiService: ApiService,
|
||||
private readonly navCtrl: NavController,
|
||||
private modalController: ModalController,
|
||||
private stateService: StateService
|
||||
private stateService: StateService,
|
||||
private readonly alertCtrl: AlertController,
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
@@ -30,12 +31,31 @@ export class EmbassyPage {
|
||||
const modal = await this.modalController.create({
|
||||
component: PasswordPage,
|
||||
componentProps: {
|
||||
embassyDrive: drive,
|
||||
verify: false
|
||||
embassyDrive: drive
|
||||
}
|
||||
})
|
||||
modal.onDidDismiss().then(async ret => {
|
||||
if (!ret.data) return
|
||||
if (!ret.data && !ret.data.success) return
|
||||
|
||||
if(!!this.stateService.recoveryDrive) {
|
||||
await this.navCtrl.navigateForward(`/loading`, { animationDirection: 'forward' })
|
||||
} else {
|
||||
const alert = await this.alertCtrl.create({
|
||||
cssClass: 'success-alert',
|
||||
header: 'Success!',
|
||||
subHeader: `Your Embassy is set up and ready to go.`,
|
||||
backdropDismiss: false,
|
||||
buttons: [
|
||||
{
|
||||
text: 'Go To Embassy',
|
||||
handler: () => {
|
||||
window.location.reload()
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
await alert.present()
|
||||
}
|
||||
})
|
||||
await modal.present();
|
||||
}
|
||||
|
||||
16
setup-wizard/src/app/pages/loading/loading-routing.module.ts
Normal file
16
setup-wizard/src/app/pages/loading/loading-routing.module.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { LoadingPage } from './loading.page';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: LoadingPage,
|
||||
}
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class LoadingPageRoutingModule {}
|
||||
19
setup-wizard/src/app/pages/loading/loading.module.ts
Normal file
19
setup-wizard/src/app/pages/loading/loading.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 { LoadingPage } from './loading.page';
|
||||
import { PasswordPageModule } from '../password/password.module';
|
||||
import { LoadingPageRoutingModule } from './loading-routing.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
IonicModule,
|
||||
LoadingPageRoutingModule,
|
||||
PasswordPageModule,
|
||||
],
|
||||
declarations: [LoadingPage]
|
||||
})
|
||||
export class LoadingPageModule {}
|
||||
36
setup-wizard/src/app/pages/loading/loading.page.html
Normal file
36
setup-wizard/src/app/pages/loading/loading.page.html
Normal file
@@ -0,0 +1,36 @@
|
||||
<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>
|
||||
<ion-card-title style="font-size: 40px;">Loading Embassy</ion-card-title>
|
||||
<ion-card-subtitle>Progress: {{(stateService.dataProgress * 100).toFixed(0) }}%</ion-card-subtitle>
|
||||
</ion-card-header>
|
||||
|
||||
<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>
|
||||
|
||||
</ion-card-content>
|
||||
</ion-card>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
</ion-content>
|
||||
|
||||
<!-- <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-button
|
||||
(click)="navToEmbassy()"
|
||||
[disabled]="stateService.dataProgress !== 1"
|
||||
style="text-align:center"
|
||||
class="claim-button"
|
||||
>
|
||||
Go To Embassy
|
||||
</ion-button>
|
||||
</div> -->
|
||||
43
setup-wizard/src/app/pages/loading/loading.page.scss
Normal file
43
setup-wizard/src/app/pages/loading/loading.page.scss
Normal file
@@ -0,0 +1,43 @@
|
||||
.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);
|
||||
}
|
||||
}
|
||||
45
setup-wizard/src/app/pages/loading/loading.page.ts
Normal file
45
setup-wizard/src/app/pages/loading/loading.page.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { AlertController, NavController } from '@ionic/angular'
|
||||
import { BehaviorSubject } from 'rxjs'
|
||||
import { StateService } from 'src/app/services/state.service'
|
||||
|
||||
@Component({
|
||||
selector: 'loading',
|
||||
templateUrl: 'loading.page.html',
|
||||
styleUrls: ['loading.page.scss'],
|
||||
})
|
||||
export class LoadingPage {
|
||||
constructor(
|
||||
private stateService: StateService,
|
||||
private alertCtrl: AlertController
|
||||
) {}
|
||||
|
||||
ngOnInit () {
|
||||
this.stateService.pollDataTransferProgress()
|
||||
const progSub = this.stateService.dataProgSubject.subscribe(async progress => {
|
||||
if(progress === 1) {
|
||||
await this.successAlert()
|
||||
progSub.unsubscribe()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async successAlert () {
|
||||
const alert = await this.alertCtrl.create({
|
||||
cssClass: 'success-alert',
|
||||
header: 'Success!',
|
||||
subHeader: `Your Embassy is set up and ready to go.`,
|
||||
backdropDismiss: false,
|
||||
buttons: [
|
||||
{
|
||||
text: 'Go To Embassy',
|
||||
handler: () => {
|
||||
window.location.reload()
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
await alert.present()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,26 @@
|
||||
<ion-header>
|
||||
<ion-toolbar color="light">
|
||||
<ion-title>
|
||||
<span *ngIf="verify">Verify Recovery Drive Password</span>
|
||||
<span *ngIf="!verify">Set Password</span>
|
||||
<span *ngIf="!!recoveryDrive">Verify Recovery Drive Password</span>
|
||||
<span *ngIf="!recoveryDrive">Set Password</span>
|
||||
</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content color="light">
|
||||
<div style="padding: 8px 24px;">
|
||||
<ng-container *ngIf="verify">
|
||||
<ng-container *ngIf="!!recoveryDrive">
|
||||
<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">
|
||||
<div *ngIf="!recoveryDrive">
|
||||
<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="*********"
|
||||
@@ -29,9 +28,7 @@
|
||||
|
||||
<p style="padding: 3px 0">Verify Password:</p>
|
||||
<ion-input
|
||||
(focusout)="validate()"
|
||||
color="light"
|
||||
(keyup)="error = ''"
|
||||
type="password"
|
||||
[(ngModel)]="passwordVer"
|
||||
placeholder="*********"
|
||||
@@ -47,8 +44,8 @@
|
||||
<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 class="claim-button" (click)="!!recoveryDrive ? verifyPw() : submitPw()">
|
||||
{{ !recoveryDrive ? 'Verify Password' : 'Submit' }}
|
||||
</ion-button>
|
||||
</ion-buttons>
|
||||
</ion-toolbar>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Component, Input } from '@angular/core'
|
||||
import { LoadingController, ModalController } from '@ionic/angular'
|
||||
import { ApiService, EmbassyDrive, RecoveryDrive } from 'src/app/services/api/api.service'
|
||||
import { StateService } from 'src/app/services/state.service'
|
||||
|
||||
@Component({
|
||||
selector: 'password',
|
||||
@@ -10,7 +11,6 @@ import { ApiService, EmbassyDrive, RecoveryDrive } from 'src/app/services/api/ap
|
||||
export class PasswordPage {
|
||||
@Input() recoveryDrive: RecoveryDrive
|
||||
@Input() embassyDrive: EmbassyDrive
|
||||
@Input() verify: boolean
|
||||
|
||||
error = ''
|
||||
password = ''
|
||||
@@ -19,7 +19,8 @@ export class PasswordPage {
|
||||
constructor(
|
||||
private modalController: ModalController,
|
||||
private apiService: ApiService,
|
||||
private loadingCtrl: LoadingController
|
||||
private loadingCtrl: LoadingController,
|
||||
private stateService: StateService
|
||||
) {}
|
||||
|
||||
ngOnInit() { }
|
||||
@@ -48,16 +49,34 @@ export class PasswordPage {
|
||||
|
||||
async submitPw () {
|
||||
this.validate()
|
||||
if (!this.error && this.password !== this.passwordVer) {
|
||||
this.error="*passwords dont match"
|
||||
}
|
||||
|
||||
if(this.error) return
|
||||
const loader = await this.loadingCtrl.create({
|
||||
message: 'Setting up your Embassy!'
|
||||
})
|
||||
|
||||
await loader.present()
|
||||
|
||||
|
||||
this.stateService.embassyDrive = this.embassyDrive
|
||||
this.stateService.embassyPassword = this.password
|
||||
|
||||
try {
|
||||
await this.stateService.setupEmbassy()
|
||||
this.modalController.dismiss({ success: true })
|
||||
} catch (e) {
|
||||
this.error = e.message
|
||||
} finally {
|
||||
loader.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
validate () {
|
||||
if (this.password.length < 12) {
|
||||
this.error="*passwords must be 12 characters or greater"
|
||||
} else if (this.password !== this.passwordVer) {
|
||||
this.error="*passwords dont match"
|
||||
this.error="*password must be 12 characters or greater"
|
||||
} else {
|
||||
this.error = ''
|
||||
}
|
||||
|
||||
@@ -60,18 +60,6 @@
|
||||
Next
|
||||
</ion-button>
|
||||
</ion-item-group>
|
||||
|
||||
<!-- <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-button
|
||||
(click)="navToEmbassy()"
|
||||
[disabled]="stateService.dataProgress !== 1"
|
||||
style="text-align:center"
|
||||
class="claim-button"
|
||||
>
|
||||
Go To Embassy
|
||||
</ion-button>
|
||||
</div> -->
|
||||
</ion-card-content>
|
||||
</ion-card>
|
||||
</ion-col>
|
||||
|
||||
@@ -41,8 +41,7 @@ export class RecoverPage {
|
||||
const modal = await this.modalController.create({
|
||||
component: PasswordPage,
|
||||
componentProps: {
|
||||
recoveryDrive: this.selectedDrive,
|
||||
verify: true
|
||||
recoveryDrive: this.selectedDrive
|
||||
}
|
||||
})
|
||||
modal.onDidDismiss().then(async ret => {
|
||||
|
||||
@@ -4,12 +4,15 @@ export abstract class ApiService {
|
||||
protected error$: Subject<string> = new Subject();
|
||||
watchError$ = this.error$.asObservable();
|
||||
abstract getEmbassyDrives (): Promise<EmbassyDrive[]>;
|
||||
abstract selectEmbassyDrive (logicalName: string): Promise<void>;
|
||||
abstract getRecoveryDrives (): Promise<RecoveryDrive[]>;
|
||||
abstract selectRecoveryDrive (logicalName: string, password: string): Promise<void>;
|
||||
abstract getDataTransferProgress (): Promise<TransferProgress>;
|
||||
abstract submitPassword (password: string): Promise<void>;
|
||||
abstract verifyRecoveryPassword (logicalname: string, password: string): Promise<boolean>;
|
||||
abstract setupEmbassy (setupInfo: {
|
||||
embassyLogicalname: string,
|
||||
embassyPassword: string
|
||||
recoveryLogicalname?: string,
|
||||
recoveryPassword?: string
|
||||
}): Promise<void>
|
||||
}
|
||||
|
||||
export interface TransferProgress {
|
||||
|
||||
@@ -58,11 +58,6 @@ export class MockApiService extends ApiService {
|
||||
]
|
||||
}
|
||||
|
||||
async selectEmbassyDrive(drive) {
|
||||
await pauseFor(2000)
|
||||
return
|
||||
}
|
||||
|
||||
async getRecoveryDrives() {
|
||||
await pauseFor(2000)
|
||||
return [
|
||||
@@ -79,20 +74,15 @@ export class MockApiService extends ApiService {
|
||||
]
|
||||
}
|
||||
|
||||
async selectRecoveryDrive(logicalName, password) {
|
||||
await pauseFor(2000)
|
||||
return
|
||||
}
|
||||
|
||||
async submitPassword(password) {
|
||||
await pauseFor(2000)
|
||||
return
|
||||
}
|
||||
|
||||
async verifyRecoveryPassword(logicalname, password) {
|
||||
await pauseFor(2000)
|
||||
return password.length > 8
|
||||
}
|
||||
|
||||
async setupEmbassy (setupInfo){
|
||||
await pauseFor(2000)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
let tries = 0
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { ApiService, EmbassyDrive, RecoveryDrive } from './api/api.service'
|
||||
|
||||
@Injectable({
|
||||
@@ -8,11 +9,12 @@ export class StateService {
|
||||
polling = false
|
||||
|
||||
embassyDrive: EmbassyDrive;
|
||||
embassyPassword: string
|
||||
recoveryDrive: RecoveryDrive;
|
||||
recoveryPassword: string
|
||||
dataTransferProgress: { bytesTransfered: number; totalBytes: number } | null;
|
||||
dataProgress = 0;
|
||||
|
||||
dataProgSubject = new BehaviorSubject(this.dataProgress)
|
||||
constructor(
|
||||
private readonly apiService: ApiService
|
||||
) {}
|
||||
@@ -21,15 +23,16 @@ export class StateService {
|
||||
this.polling = false
|
||||
|
||||
this.embassyDrive = null
|
||||
this.embassyPassword = null
|
||||
this.recoveryDrive = null
|
||||
this.recoveryPassword = null
|
||||
this.dataTransferProgress = null
|
||||
this.dataProgress = 0
|
||||
}
|
||||
|
||||
async pollDataTransferProgress() {
|
||||
async pollDataTransferProgress(callback?: () => void) {
|
||||
this.polling = true
|
||||
await pauseFor(7000)
|
||||
await pauseFor(1000)
|
||||
|
||||
if (
|
||||
this.dataTransferProgress?.totalBytes &&
|
||||
@@ -43,8 +46,18 @@ export class StateService {
|
||||
}
|
||||
if (this.dataTransferProgress.totalBytes) {
|
||||
this.dataProgress = this.dataTransferProgress.bytesTransfered / this.dataTransferProgress.totalBytes
|
||||
this.dataProgSubject.next(this.dataProgress)
|
||||
}
|
||||
this.pollDataTransferProgress()
|
||||
this.pollDataTransferProgress(callback)
|
||||
}
|
||||
|
||||
async setupEmbassy () {
|
||||
await this.apiService.setupEmbassy({
|
||||
embassyLogicalname: this.embassyDrive.logicalname,
|
||||
embassyPassword: this.embassyPassword,
|
||||
recoveryLogicalname: this.recoveryDrive?.logicalname,
|
||||
recoveryPassword: this.recoveryPassword
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -69,4 +69,14 @@ ion-item {
|
||||
font-size: 28px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.success-alert .alert-wrapper {
|
||||
box-shadow: 4px 4px 30px var(--ion-color-light);
|
||||
border: 1px solid rgba(255,255,255,.3);
|
||||
background: var(--ion-color-light);
|
||||
}
|
||||
|
||||
.success-alert .alert-title {
|
||||
color: var(--ion-color-success);
|
||||
}
|
||||
Reference in New Issue
Block a user