mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 20:14:49 +00:00
wizard edits
This commit is contained in:
committed by
Matt Hill
parent
d5b420be9c
commit
674e311a9d
@@ -21,6 +21,10 @@ const routes: Routes = [
|
|||||||
path: 'home',
|
path: 'home',
|
||||||
loadChildren: () => import('./pages/home/home.module').then( m => m.HomePageModule),
|
loadChildren: () => import('./pages/home/home.module').then( m => m.HomePageModule),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'success',
|
||||||
|
loadChildren: () => import('./pages/success/success.module').then( m => m.SuccessPageModule),
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { Component, OnInit } from '@angular/core'
|
import { Component, OnInit } from '@angular/core'
|
||||||
import { NavController } from '@ionic/angular'
|
import { NavController } from '@ionic/angular'
|
||||||
import { StateService } from './services/state.service'
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
@@ -11,12 +10,9 @@ export class AppComponent implements OnInit {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly navCtrl: NavController,
|
private readonly navCtrl: NavController,
|
||||||
private stateService: StateService
|
|
||||||
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.stateService.reset()
|
await this.navCtrl.navigateForward(`/product-key`)
|
||||||
await this.navCtrl.navigateForward(`/home`)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<ion-content color="light">
|
<ion-content color="light">
|
||||||
<ion-grid style="padding-top: 32px; height: 100%; max-width: 540px;">
|
<ion-grid style="padding-top: 32px; height: 100%; max-width: 540px;">
|
||||||
<ion-row class="ion-align-items-center" style="height: 100%;">
|
<ion-row style="height: 100%;">
|
||||||
<ion-col class="ion-text-center">
|
<ion-col class="ion-text-center">
|
||||||
|
|
||||||
<div style="padding-bottom: 32px;">
|
<div style="padding-bottom: 32px;">
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Component } from '@angular/core'
|
import { Component } from '@angular/core'
|
||||||
import { AlertController, ModalController, NavController } from '@ionic/angular'
|
import { AlertController, LoadingController, ModalController, NavController } from '@ionic/angular'
|
||||||
import { ApiService, EmbassyDrive } from 'src/app/services/api/api.service'
|
import { ApiService, EmbassyDrive } 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'
|
||||||
@@ -20,6 +20,7 @@ export class EmbassyPage {
|
|||||||
private modalController: ModalController,
|
private modalController: ModalController,
|
||||||
private stateService: StateService,
|
private stateService: StateService,
|
||||||
private readonly alertCtrl: AlertController,
|
private readonly alertCtrl: AlertController,
|
||||||
|
private loadingCtrl: LoadingController
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
@@ -35,26 +36,28 @@ export class EmbassyPage {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
modal.onDidDismiss().then(async ret => {
|
modal.onDidDismiss().then(async ret => {
|
||||||
if (!ret.data && !ret.data.success) return
|
if (!ret.data || !ret.data.password) return
|
||||||
|
|
||||||
if(!!this.stateService.recoveryDrive) {
|
const loader = await this.loadingCtrl.create({
|
||||||
await this.navCtrl.navigateForward(`/loading`, { animationDirection: 'forward' })
|
message: 'Setting up your Embassy!'
|
||||||
} else {
|
})
|
||||||
const alert = await this.alertCtrl.create({
|
|
||||||
cssClass: 'success-alert',
|
await loader.present()
|
||||||
header: 'Success!',
|
|
||||||
subHeader: `Your Embassy is set up and ready to go.`,
|
this.stateService.embassyDrive = drive
|
||||||
backdropDismiss: false,
|
this.stateService.embassyPassword = ret.data.password
|
||||||
buttons: [
|
|
||||||
{
|
try {
|
||||||
text: 'Go To Embassy',
|
this.stateService.torAddress = (await this.stateService.setupEmbassy()).torAddress
|
||||||
handler: () => {
|
} catch (e) {
|
||||||
window.location.reload()
|
console.log(e.message)
|
||||||
}
|
} finally {
|
||||||
}
|
loader.dismiss()
|
||||||
]
|
if(!!this.stateService.recoveryDrive) {
|
||||||
})
|
await this.navCtrl.navigateForward(`/loading`, { animationDirection: 'forward' })
|
||||||
await alert.present()
|
} else {
|
||||||
|
await this.navCtrl.navigateForward(`/success`, { animationDirection: 'forward' })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
await modal.present();
|
await modal.present();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<ion-content color="light">
|
<ion-content color="light">
|
||||||
<ion-grid style="padding-top: 32px; height: 100%; max-width: 540px;">
|
<ion-grid style="padding-top: 32px; height: 100%; max-width: 540px;">
|
||||||
<ion-row class="ion-align-items-center" style="height: 100%;">
|
<ion-row style="height: 100%;">
|
||||||
<ion-col class="ion-text-center">
|
<ion-col class="ion-text-center">
|
||||||
|
|
||||||
<div style="padding-bottom: 32px;">
|
<div style="padding-bottom: 32px;">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<ion-content color="light">
|
<ion-content color="light">
|
||||||
<ion-grid style="padding-top: 32px; height: 100%; max-width: 540px;">
|
<ion-grid style="padding-top: 32px; height: 100%; max-width: 540px;">
|
||||||
<ion-row class="ion-align-items-center" style="height: 100%;">
|
<ion-row style="height: 100%;">
|
||||||
<ion-col class="ion-text-center">
|
<ion-col class="ion-text-center">
|
||||||
|
|
||||||
<div style="padding-bottom: 32px;">
|
<div style="padding-bottom: 32px;">
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
<ion-card color="dark">
|
<ion-card color="dark">
|
||||||
<ion-card-header>
|
<ion-card-header>
|
||||||
<ion-card-title style="font-size: 40px;">Loading Embassy</ion-card-title>
|
<ion-card-title style="font-size: 40px;">Recovering From Backup</ion-card-title>
|
||||||
<ion-card-subtitle>Progress: {{(stateService.dataProgress * 100).toFixed(0) }}%</ion-card-subtitle>
|
<ion-card-subtitle>Progress: {{(stateService.dataProgress * 100).toFixed(0) }}%</ion-card-subtitle>
|
||||||
</ion-card-header>
|
</ion-card-header>
|
||||||
|
|
||||||
@@ -21,16 +21,4 @@
|
|||||||
</ion-col>
|
</ion-col>
|
||||||
</ion-row>
|
</ion-row>
|
||||||
</ion-grid>
|
</ion-grid>
|
||||||
</ion-content>
|
</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> -->
|
|
||||||
@@ -1,51 +1,74 @@
|
|||||||
<ion-header>
|
<ion-header>
|
||||||
<ion-toolbar color="light">
|
<ion-toolbar color="light">
|
||||||
<ion-title>
|
<ion-title>
|
||||||
<span *ngIf="!!recoveryDrive">Verify Recovery Drive Password</span>
|
<span *ngIf="!!recoveryDrive">Unlock Drive</span>
|
||||||
<span *ngIf="!recoveryDrive">Set Password</span>
|
<span *ngIf="!recoveryDrive">Set Password</span>
|
||||||
</ion-title>
|
</ion-title>
|
||||||
</ion-toolbar>
|
</ion-toolbar>
|
||||||
</ion-header>
|
</ion-header>
|
||||||
|
|
||||||
<ion-content color="light">
|
<ion-content color="light">
|
||||||
<div style="padding: 8px 24px;">
|
<form (ngSubmit)="!!recoveryDrive ? verifyPw() : submitPw()">
|
||||||
<ng-container *ngIf="!!recoveryDrive">
|
<div style="padding: 8px 24px;">
|
||||||
<p style="padding: 3px 0">Password:</p>
|
<p *ngIf="embassyDrive && 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>
|
||||||
<ion-input (click)="error = ''" color="light" type="password" [(ngModel)]="password" placeholder="*********"></ion-input>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<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()"
|
|
||||||
type="password"
|
|
||||||
[(ngModel)]="password"
|
|
||||||
placeholder="*********"
|
|
||||||
></ion-input>
|
|
||||||
|
|
||||||
<p style="padding: 3px 0">Verify Password:</p>
|
<h4 class="input-label">
|
||||||
<ion-input
|
Password:
|
||||||
color="light"
|
</h4>
|
||||||
type="password"
|
|
||||||
[(ngModel)]="passwordVer"
|
<ion-item
|
||||||
placeholder="*********"
|
color="dark"
|
||||||
></ion-input>
|
[class]="pwError ? 'error-border' : password && !recoveryDrive ? 'success-border' : ''"
|
||||||
|
>
|
||||||
|
<ion-input
|
||||||
|
[(ngModel)]="password"
|
||||||
|
[ngModelOptions]="{'standalone': true}"
|
||||||
|
[type]="!unmasked1 ? 'password' : 'text'"
|
||||||
|
placeholder="Enter Password"
|
||||||
|
debounce="500"
|
||||||
|
(ionChange)="validate()"
|
||||||
|
maxlength="64"
|
||||||
|
></ion-input>
|
||||||
|
<ion-button fill="clear" color="light" (click)="unmasked1 = !unmasked1">
|
||||||
|
<ion-icon slot="icon-only" [name]="unmasked1 ? 'eye-off-outline' : 'eye-outline'" size="small"></ion-icon>
|
||||||
|
</ion-button>
|
||||||
|
</ion-item>
|
||||||
|
<p style="color: var(--ion-color-danger);">{{pwError}}</p>
|
||||||
|
<ng-container *ngIf="!recoveryDrive">
|
||||||
|
<h4 class="input-label">
|
||||||
|
Verify Password:
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
<ion-item color="dark" [class]="verError ? 'error-border' : passwordVer ? 'success-border' : ''">
|
||||||
|
<ion-input
|
||||||
|
[(ngModel)]="passwordVer"
|
||||||
|
[ngModelOptions]="{'standalone': true}"
|
||||||
|
[type]="!unmasked2 ? 'password' : 'text'"
|
||||||
|
debounce="500"
|
||||||
|
[disabled]="password.length < 12"
|
||||||
|
(ionChange)="checkVer()"
|
||||||
|
maxlength="64"
|
||||||
|
placeholder="Retype Password"
|
||||||
|
></ion-input>
|
||||||
|
<ion-button fill="clear" color="light" (click)="unmasked2 = !unmasked2">
|
||||||
|
<ion-icon slot="icon-only" [name]="unmasked2 ? 'eye-off-outline' : 'eye-outline'" size="small"></ion-icon>
|
||||||
|
</ion-button>
|
||||||
|
</ion-item>
|
||||||
|
<p style="color: var(--ion-color-danger);">{{ verError }}</p>
|
||||||
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
<p style="color: #FF4961;">{{error}}</p>
|
<input type="submit" style="display: none" />
|
||||||
</div>
|
</form>
|
||||||
</ion-content>
|
</ion-content>
|
||||||
|
|
||||||
<ion-footer>
|
<ion-footer>
|
||||||
<ion-toolbar color="light">
|
<ion-toolbar color="light">
|
||||||
<ion-buttons slot="end" class="ion-padding-end">
|
<ion-buttons slot="end" class="ion-padding-end">
|
||||||
<ion-button class="claim-button" (click)="cancel()">
|
<ion-button color="dark" class="claim-button" (click)="cancel()">
|
||||||
Cancel
|
Cancel
|
||||||
</ion-button>
|
</ion-button>
|
||||||
<ion-button class="claim-button" (click)="!!recoveryDrive ? verifyPw() : submitPw()">
|
<ion-button color="dark" class="claim-button" (click)="!!recoveryDrive ? verifyPw() : submitPw()">
|
||||||
{{ !recoveryDrive ? 'Verify Password' : 'Submit' }}
|
{{ !!recoveryDrive ? 'Unlock' : 'Submit' }}
|
||||||
</ion-button>
|
</ion-button>
|
||||||
</ion-buttons>
|
</ion-buttons>
|
||||||
</ion-toolbar>
|
</ion-toolbar>
|
||||||
|
|||||||
@@ -3,7 +3,26 @@
|
|||||||
margin-inline-end: 0;
|
margin-inline-end: 0;
|
||||||
margin: 6px;
|
margin: 6px;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
--background: linear-gradient(45deg, var(--ion-color-light) 16%, var(--ion-color-medium) 150%);
|
}
|
||||||
|
|
||||||
|
.input-label {
|
||||||
|
color: var(--ion-color-dark);
|
||||||
|
// padding-top: 10px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
font-size: medium;
|
||||||
|
font-weight: 500;
|
||||||
|
* {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-border {
|
||||||
|
border: 2px solid var(--ion-color-danger);
|
||||||
|
}
|
||||||
|
|
||||||
|
.success-border {
|
||||||
|
border: 2px solid var(--ion-color-success);
|
||||||
}
|
}
|
||||||
|
|
||||||
ion-input {
|
ion-input {
|
||||||
|
|||||||
@@ -12,9 +12,13 @@ export class PasswordPage {
|
|||||||
@Input() recoveryDrive: RecoveryDrive
|
@Input() recoveryDrive: RecoveryDrive
|
||||||
@Input() embassyDrive: EmbassyDrive
|
@Input() embassyDrive: EmbassyDrive
|
||||||
|
|
||||||
error = ''
|
pwError = ''
|
||||||
password = ''
|
password = ''
|
||||||
|
unmasked1 = false
|
||||||
|
|
||||||
|
verError = ''
|
||||||
passwordVer = ''
|
passwordVer = ''
|
||||||
|
unmasked2 = false
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private modalController: ModalController,
|
private modalController: ModalController,
|
||||||
@@ -27,7 +31,7 @@ export class PasswordPage {
|
|||||||
|
|
||||||
async verifyPw () {
|
async verifyPw () {
|
||||||
|
|
||||||
if(!this.recoveryDrive) this.error = 'No recovery drive' // unreachable
|
if(!this.recoveryDrive) this.pwError = 'No recovery drive' // unreachable
|
||||||
const loader = await this.loadingCtrl.create({
|
const loader = await this.loadingCtrl.create({
|
||||||
message: 'Verifying Password'
|
message: 'Verifying Password'
|
||||||
})
|
})
|
||||||
@@ -38,10 +42,10 @@ export class PasswordPage {
|
|||||||
if(isCorrectPassword) {
|
if(isCorrectPassword) {
|
||||||
this.modalController.dismiss({ password: this.password })
|
this.modalController.dismiss({ password: this.password })
|
||||||
} else {
|
} else {
|
||||||
this.error = "Incorrect password provided"
|
this.pwError = "Incorrect password provided"
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.error = 'Error connecting to Embassy'
|
this.pwError = 'Error connecting to Embassy'
|
||||||
} finally {
|
} finally {
|
||||||
loader.dismiss()
|
loader.dismiss()
|
||||||
}
|
}
|
||||||
@@ -49,37 +53,31 @@ export class PasswordPage {
|
|||||||
|
|
||||||
async submitPw () {
|
async submitPw () {
|
||||||
this.validate()
|
this.validate()
|
||||||
if (!this.error && this.password !== this.passwordVer) {
|
console.log('here')
|
||||||
this.error="*passwords dont match"
|
if (this.password !== this.passwordVer) {
|
||||||
|
this.verError="*passwords do not match"
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.error) return
|
if(this.pwError || this.verError) return
|
||||||
const loader = await this.loadingCtrl.create({
|
this.modalController.dismiss({ password: this.password })
|
||||||
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 () {
|
validate () {
|
||||||
if (this.password.length < 12) {
|
if(!!this.recoveryDrive) return this.pwError = ''
|
||||||
this.error="*password must be 12 characters or greater"
|
|
||||||
} else {
|
if (this.passwordVer) {
|
||||||
this.error = ''
|
this.checkVer()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.password.length < 12) {
|
||||||
|
this.pwError="*password must be 12 characters or greater"
|
||||||
|
} else {
|
||||||
|
this.pwError = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checkVer () {
|
||||||
|
this.verError = this.password !== this.passwordVer ? "*passwords do not match" : ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<ion-content color="light">
|
<ion-content color="light">
|
||||||
<ion-grid style="padding-top: 32px; height: 100%; max-width: 540px;">
|
<ion-grid style="padding-top: 32px; height: 100%; max-width: 540px;">
|
||||||
<ion-row class="ion-align-items-center" style="height: 100%;">
|
<ion-row style="height: 100%;">
|
||||||
<ion-col class="ion-text-center">
|
<ion-col class="ion-text-center">
|
||||||
|
|
||||||
<div style="padding-bottom: 32px;">
|
<div style="padding-bottom: 32px;">
|
||||||
@@ -11,16 +11,20 @@
|
|||||||
<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>Enter Product Key</ion-card-title>
|
<ion-card-title>Enter Product Key</ion-card-title>
|
||||||
</ion-card-header>
|
</ion-card-header>
|
||||||
<ion-card-content>
|
|
||||||
<form (submit)="submit()">
|
<ion-card-content style="padding-bottom: 0px;" class="ion-margin">
|
||||||
<p class="input-label">Product Key</p>
|
<form (submit)="submit()" style="margin-bottom: 12px;">
|
||||||
<ion-item color="light" class="ion-padding-bottom">
|
<ion-item-group>
|
||||||
<ion-label>
|
<ion-item>
|
||||||
<ion-icon slot="start" name="key-outline" style="margin-right: 16px;"></ion-icon>
|
<ion-icon color="medium" slot="start" name="key-outline" style="margin-right: 16px;"></ion-icon>
|
||||||
<ion-input placeholder="Enter Product Key"></ion-input>
|
<ion-input (click)="error = ''" color="medium" maxlength="12" name="productKey" [(ngModel)]="productKey"></ion-input>
|
||||||
</ion-label>
|
</ion-item>
|
||||||
</ion-item>
|
<div class="ion-text-left">
|
||||||
<ion-button type="submit" color="light">
|
|
||||||
|
<p *ngIf="error" style="padding-top: 4px"><ion-text color="danger">*{{ error }}</ion-text></p>
|
||||||
|
</div>
|
||||||
|
</ion-item-group>
|
||||||
|
<ion-button style="margin-top: 25px;" type="submit" color="light">
|
||||||
Submit
|
Submit
|
||||||
</ion-button>
|
</ion-button>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
import { Component } from '@angular/core'
|
import { Component } from '@angular/core'
|
||||||
import { NavController } from '@ionic/angular'
|
import { LoadingController, NavController } from '@ionic/angular'
|
||||||
|
import { ApiService } from 'src/app/services/api/api.service'
|
||||||
|
import { StateService } from 'src/app/services/state.service'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-product-key',
|
selector: 'app-product-key',
|
||||||
@@ -7,10 +9,42 @@ import { NavController } from '@ionic/angular'
|
|||||||
styleUrls: ['product-key.page.scss'],
|
styleUrls: ['product-key.page.scss'],
|
||||||
})
|
})
|
||||||
export class ProductKeyPage {
|
export class ProductKeyPage {
|
||||||
|
productKey: string
|
||||||
|
error: string
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly navCtrl: NavController,
|
private readonly navCtrl: NavController,
|
||||||
|
private stateService: StateService,
|
||||||
|
private apiService: ApiService,
|
||||||
|
private loadingCtrl: LoadingController,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
async submit () {
|
||||||
|
if(!this.productKey) return this.error = "Must enter product key"
|
||||||
|
|
||||||
|
const loader = await this.loadingCtrl.create({
|
||||||
|
message: 'Verifying Product Key'
|
||||||
|
})
|
||||||
|
await loader.present()
|
||||||
|
|
||||||
|
try {
|
||||||
|
const state = await this.apiService.verifyProductKey(this.productKey)
|
||||||
|
this.stateService.productKey = this.productKey
|
||||||
|
if(state['is-recovering']) {
|
||||||
|
await this.navCtrl.navigateForward(`/loading`, { animationDirection: 'forward' })
|
||||||
|
} else if (!!state['tor-address']) {
|
||||||
|
this.stateService.torAddress = state['tor-address']
|
||||||
|
await this.navCtrl.navigateForward(`/success`, { animationDirection: 'forward' })
|
||||||
|
} else {
|
||||||
|
await this.navCtrl.navigateForward(`/home`, { animationDirection: 'forward' })
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.error = e.message
|
||||||
|
} finally {
|
||||||
|
loader.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async recoverNav () {
|
async recoverNav () {
|
||||||
await this.navCtrl.navigateForward(`/recover`, { animationDirection: 'forward' })
|
await this.navCtrl.navigateForward(`/recover`, { animationDirection: 'forward' })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<ion-content color="light">
|
<ion-content color="light">
|
||||||
<ion-grid style="padding-top: 32px; height: 100%; max-width: 540px;">
|
<ion-grid style="padding-top: 32px; height: 100%; max-width: 540px;">
|
||||||
<ion-row class="ion-align-items-center" style="height: 100%;">
|
<ion-row style="height: 100%;">
|
||||||
<ion-col class="ion-text-center">
|
<ion-col class="ion-text-center">
|
||||||
|
|
||||||
<div style="padding-bottom: 32px;">
|
<div style="padding-bottom: 32px;">
|
||||||
|
|||||||
16
setup-wizard/src/app/pages/success/success-routing.module.ts
Normal file
16
setup-wizard/src/app/pages/success/success-routing.module.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
import { SuccessPage } from './success.page';
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: SuccessPage,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(routes)],
|
||||||
|
exports: [RouterModule]
|
||||||
|
})
|
||||||
|
export class SuccessPageRoutingModule {}
|
||||||
21
setup-wizard/src/app/pages/success/success.module.ts
Normal file
21
setup-wizard/src/app/pages/success/success.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 { SuccessPage } from './success.page';
|
||||||
|
import { PasswordPageModule } from '../password/password.module';
|
||||||
|
|
||||||
|
import { SuccessPageRoutingModule } from './success-routing.module';
|
||||||
|
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
FormsModule,
|
||||||
|
IonicModule,
|
||||||
|
SuccessPageRoutingModule,
|
||||||
|
PasswordPageModule,
|
||||||
|
],
|
||||||
|
declarations: [SuccessPage]
|
||||||
|
})
|
||||||
|
export class SuccessPageModule {}
|
||||||
42
setup-wizard/src/app/pages/success/success.page.html
Normal file
42
setup-wizard/src/app/pages/success/success.page.html
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<ion-content color="light">
|
||||||
|
<ion-grid style="padding-top: 32px; height: 100%; max-width: 540px;">
|
||||||
|
<ion-row 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>Setup Complete!</ion-card-title>
|
||||||
|
</ion-card-header>
|
||||||
|
<div class="divider"></div>
|
||||||
|
<ion-card-content class="ion-margin">
|
||||||
|
<p class="input-label">Tor Address:</p>
|
||||||
|
<ion-item style="--border-radius: 8px 8px 0 0 !important;" color="light">
|
||||||
|
<ion-label>
|
||||||
|
<p>{{ stateService.torAddress }}</p>
|
||||||
|
</ion-label>
|
||||||
|
<ion-buttons slot="end">
|
||||||
|
<ion-button fill="clear" (click)="copy(lan)">
|
||||||
|
<ion-icon size="small" slot="icon-only" name="copy-outline"></ion-icon>
|
||||||
|
</ion-button>
|
||||||
|
</ion-buttons>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item style="--border-radius: 0 0 8px 8px !important;" color="medium" lines="none">
|
||||||
|
<ion-label class="ion-text-wrap">
|
||||||
|
<p>We suggest you copy and save your tor adress in a safe place. If you happen to loose it, no worries! You can always retrieve it by coming back to <b>{{ window.location.hostname }}</b>.</p>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
|
||||||
|
<ion-button (click)="goToEmbassy()" style="height: 60px; margin-top: 30px;" type="submit" expand="block">
|
||||||
|
<span style="font-size: larger; font-weight: bold; padding: 10px 20px">Go To Embassy!</span>
|
||||||
|
</ion-button>
|
||||||
|
|
||||||
|
</ion-card-content>
|
||||||
|
</ion-card>
|
||||||
|
</ion-col>
|
||||||
|
</ion-row>
|
||||||
|
</ion-grid>
|
||||||
|
</ion-content>
|
||||||
49
setup-wizard/src/app/pages/success/success.page.scss
Normal file
49
setup-wizard/src/app/pages/success/success.page.scss
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
.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: medium;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
margin: 0;
|
||||||
|
background: linear-gradient(90deg,var(--ion-color-dark) 0,var(--ion-color-medium) 50%,var(--ion-color-dark) 100%);
|
||||||
|
height: 1px;
|
||||||
|
}
|
||||||
62
setup-wizard/src/app/pages/success/success.page.ts
Normal file
62
setup-wizard/src/app/pages/success/success.page.ts
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import { Component } from '@angular/core'
|
||||||
|
import { NavController, ToastController } from '@ionic/angular'
|
||||||
|
import { StateService } from 'src/app/services/state.service'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'success',
|
||||||
|
templateUrl: 'success.page.html',
|
||||||
|
styleUrls: ['success.page.scss'],
|
||||||
|
})
|
||||||
|
export class SuccessPage {
|
||||||
|
constructor(
|
||||||
|
public stateService: StateService,
|
||||||
|
private readonly navCtrl: NavController,
|
||||||
|
private toastCtrl: ToastController
|
||||||
|
) {
|
||||||
|
this.stateService.torAddress = 'asdfasdfasdf.onion'
|
||||||
|
}
|
||||||
|
|
||||||
|
window = window
|
||||||
|
|
||||||
|
async copy (): Promise<void> {
|
||||||
|
let message = ''
|
||||||
|
await this.copyToClipboard(this.stateService.torAddress)
|
||||||
|
.then(success => message = success ? 'copied to clipboard!' : 'failed to copy')
|
||||||
|
|
||||||
|
const toast = await this.toastCtrl.create({
|
||||||
|
header: message,
|
||||||
|
position: 'bottom',
|
||||||
|
duration: 1000,
|
||||||
|
})
|
||||||
|
await toast.present()
|
||||||
|
}
|
||||||
|
|
||||||
|
async goToEmbassy () {
|
||||||
|
window.location.reload()
|
||||||
|
// await this.navCtrl.navigateForward(`/recover`, { animationDirection: 'forward' })
|
||||||
|
}
|
||||||
|
|
||||||
|
async copyToClipboard (str: string): Promise<boolean> {
|
||||||
|
if (window.isSecureContext) {
|
||||||
|
return navigator.clipboard.writeText(str)
|
||||||
|
.then(() => {
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
const el = document.createElement('textarea')
|
||||||
|
el.value = str
|
||||||
|
el.setAttribute('readonly', '')
|
||||||
|
el.style.position = 'absolute'
|
||||||
|
el.style.left = '-9999px'
|
||||||
|
document.body.appendChild(el)
|
||||||
|
el.select()
|
||||||
|
const copy = document.execCommand('copy')
|
||||||
|
document.body.removeChild(el)
|
||||||
|
return copy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -3,6 +3,7 @@ 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 verifyProductKey (key: string): Promise<{ "is-recovering": boolean, "tor-address": string }>;
|
||||||
abstract getEmbassyDrives (): Promise<EmbassyDrive[]>;
|
abstract getEmbassyDrives (): Promise<EmbassyDrive[]>;
|
||||||
abstract getRecoveryDrives (): Promise<RecoveryDrive[]>;
|
abstract getRecoveryDrives (): Promise<RecoveryDrive[]>;
|
||||||
abstract getDataTransferProgress (): Promise<TransferProgress>;
|
abstract getDataTransferProgress (): Promise<TransferProgress>;
|
||||||
@@ -12,7 +13,7 @@ export abstract class ApiService {
|
|||||||
embassyPassword: string
|
embassyPassword: string
|
||||||
recoveryLogicalname?: string,
|
recoveryLogicalname?: string,
|
||||||
recoveryPassword?: string
|
recoveryPassword?: string
|
||||||
}): Promise<void>
|
}): Promise<{ "tor-address": string }>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TransferProgress {
|
export interface TransferProgress {
|
||||||
|
|||||||
@@ -11,24 +11,11 @@ export class MockApiService extends ApiService {
|
|||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|
||||||
async getState() {
|
async verifyProductKey(key) {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return {
|
return {
|
||||||
'embassy-drive':
|
"is-recovering": false,
|
||||||
null,
|
"tor-address": null
|
||||||
// {
|
|
||||||
// logicalname: 'name1',
|
|
||||||
// labels: ['label 1', 'label 2'],
|
|
||||||
// capacity: 1600,
|
|
||||||
// used: 200,
|
|
||||||
// },
|
|
||||||
'recovery-drive':
|
|
||||||
null,
|
|
||||||
// {
|
|
||||||
// logicalname: 'name1',
|
|
||||||
// version: '0.3.3',
|
|
||||||
// name: 'My Embassy'
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,7 +28,6 @@ export class MockApiService extends ApiService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getEmbassyDrives() {
|
async getEmbassyDrives() {
|
||||||
await pauseFor(2000)
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
logicalname: 'Name1',
|
logicalname: 'Name1',
|
||||||
@@ -79,9 +65,9 @@ export class MockApiService extends ApiService {
|
|||||||
return password.length > 8
|
return password.length > 8
|
||||||
}
|
}
|
||||||
|
|
||||||
async setupEmbassy (setupInfo){
|
async setupEmbassy (setupInfo) {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
return
|
return { "tor-address": 'asdfasdfasdf.onion' }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import { ApiService, EmbassyDrive, RecoveryDrive } from './api/api.service'
|
|||||||
export class StateService {
|
export class StateService {
|
||||||
polling = false
|
polling = false
|
||||||
|
|
||||||
|
productKey: string
|
||||||
|
|
||||||
embassyDrive: EmbassyDrive;
|
embassyDrive: EmbassyDrive;
|
||||||
embassyPassword: string
|
embassyPassword: string
|
||||||
recoveryDrive: RecoveryDrive;
|
recoveryDrive: RecoveryDrive;
|
||||||
@@ -15,21 +17,13 @@ export class StateService {
|
|||||||
dataTransferProgress: { bytesTransfered: number; totalBytes: number } | null;
|
dataTransferProgress: { bytesTransfered: number; totalBytes: number } | null;
|
||||||
dataProgress = 0;
|
dataProgress = 0;
|
||||||
dataProgSubject = new BehaviorSubject(this.dataProgress)
|
dataProgSubject = new BehaviorSubject(this.dataProgress)
|
||||||
|
|
||||||
|
torAddress: string
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly apiService: ApiService
|
private readonly apiService: ApiService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
reset() {
|
|
||||||
this.polling = false
|
|
||||||
|
|
||||||
this.embassyDrive = null
|
|
||||||
this.embassyPassword = null
|
|
||||||
this.recoveryDrive = null
|
|
||||||
this.recoveryPassword = null
|
|
||||||
this.dataTransferProgress = null
|
|
||||||
this.dataProgress = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
async pollDataTransferProgress(callback?: () => void) {
|
async pollDataTransferProgress(callback?: () => void) {
|
||||||
this.polling = true
|
this.polling = true
|
||||||
await pauseFor(1000)
|
await pauseFor(1000)
|
||||||
@@ -51,13 +45,15 @@ export class StateService {
|
|||||||
this.pollDataTransferProgress(callback)
|
this.pollDataTransferProgress(callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
async setupEmbassy () {
|
async setupEmbassy () : Promise<{ torAddress: string }> {
|
||||||
await this.apiService.setupEmbassy({
|
const ret = await this.apiService.setupEmbassy({
|
||||||
embassyLogicalname: this.embassyDrive.logicalname,
|
embassyLogicalname: this.embassyDrive.logicalname,
|
||||||
embassyPassword: this.embassyPassword,
|
embassyPassword: this.embassyPassword,
|
||||||
recoveryLogicalname: this.recoveryDrive?.logicalname,
|
recoveryLogicalname: this.recoveryDrive?.logicalname,
|
||||||
recoveryPassword: this.recoveryPassword
|
recoveryPassword: this.recoveryPassword
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return { torAddress: ret['tor-address'] }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user