diff --git a/setup-wizard/src/app/app-routing.module.ts b/setup-wizard/src/app/app-routing.module.ts index d6db906b8..51c88cf7a 100644 --- a/setup-wizard/src/app/app-routing.module.ts +++ b/setup-wizard/src/app/app-routing.module.ts @@ -2,6 +2,10 @@ import { NgModule } from '@angular/core' import { PreloadAllModules, RouterModule, Routes } from '@angular/router' const routes: Routes = [ + { + path: 'init', + loadChildren: () => import('./pages/init/init.module').then( m => m.InitPageModule), + }, { path: 'product-key', loadChildren: () => import('./pages/product-key/product-key.module').then( m => m.ProductKeyPageModule), diff --git a/setup-wizard/src/app/pages/embassy/embassy.page.html b/setup-wizard/src/app/pages/embassy/embassy.page.html index 75d8026ce..55cca0947 100644 --- a/setup-wizard/src/app/pages/embassy/embassy.page.html +++ b/setup-wizard/src/app/pages/embassy/embassy.page.html @@ -1,4 +1,4 @@ - + @@ -15,8 +15,8 @@ -

No drives found

-

Please connect an storage drive to your Embassy and refresh the page.

+

No drives found

+

Please connect an storage drive to your Embassy and refresh the page.

Refresh @@ -24,7 +24,7 @@ - + @@ -35,15 +35,11 @@ - + -

{{ drive.logicalname }} - {{ drive.capacity | convertBytes }}

-

- {{ drive.vendor }} - - - {{ drive.model }} -

+

{{ drive.vendor || 'Unknown Vendor' }} - {{ drive.model || 'Unknown Model' }}

+

{{ drive.logicalname }} - {{ drive.capacity | convertBytes }}

diff --git a/setup-wizard/src/app/pages/embassy/embassy.page.scss b/setup-wizard/src/app/pages/embassy/embassy.page.scss index 839eca922..e69de29bb 100644 --- a/setup-wizard/src/app/pages/embassy/embassy.page.scss +++ b/setup-wizard/src/app/pages/embassy/embassy.page.scss @@ -1,10 +0,0 @@ -.selected { - border: 4px solid gray; -} - -ion-card-title { - margin: 24px 0; - font-family: 'Montserrat'; - font-size: x-large; - --color: var(--ion-color-light); -} \ No newline at end of file diff --git a/setup-wizard/src/app/pages/embassy/embassy.page.ts b/setup-wizard/src/app/pages/embassy/embassy.page.ts index 95dc19a69..9fe5c974d 100644 --- a/setup-wizard/src/app/pages/embassy/embassy.page.ts +++ b/setup-wizard/src/app/pages/embassy/embassy.page.ts @@ -82,7 +82,7 @@ export class EmbassyPage { if (!ret.data || !ret.data.password) return const loader = await this.loadingCtrl.create({ - message: 'Setting up your Embassy!', + message: 'Transferring encrypted data', }) await loader.present() @@ -99,9 +99,9 @@ export class EmbassyPage { } finally { loader.dismiss() if (!!this.stateService.recoveryPartition) { - await this.navCtrl.navigateForward(`/loading`, { animationDirection: 'forward' }) + await this.navCtrl.navigateForward(`/loading`) } else { - await this.navCtrl.navigateForward(`/success`, { animationDirection: 'forward' }) + await this.navCtrl.navigateForward(`/init`) } } }) diff --git a/setup-wizard/src/app/pages/home/home.page.html b/setup-wizard/src/app/pages/home/home.page.html index 7de7bcd34..e2ab9a9d0 100644 --- a/setup-wizard/src/app/pages/home/home.page.html +++ b/setup-wizard/src/app/pages/home/home.page.html @@ -1,4 +1,4 @@ - + @@ -12,7 +12,6 @@ @@ -24,8 +23,7 @@ diff --git a/setup-wizard/src/app/pages/home/home.page.scss b/setup-wizard/src/app/pages/home/home.page.scss index a2f80017a..e69de29bb 100644 --- a/setup-wizard/src/app/pages/home/home.page.scss +++ b/setup-wizard/src/app/pages/home/home.page.scss @@ -1,16 +0,0 @@ -.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); -} \ No newline at end of file diff --git a/setup-wizard/src/app/pages/init/init-routing.module.ts b/setup-wizard/src/app/pages/init/init-routing.module.ts new file mode 100644 index 000000000..65c6adb97 --- /dev/null +++ b/setup-wizard/src/app/pages/init/init-routing.module.ts @@ -0,0 +1,16 @@ +import { NgModule } from '@angular/core' +import { RouterModule, Routes } from '@angular/router' +import { InitPage } from './init.page' + +const routes: Routes = [ + { + path: '', + component: InitPage, + }, +] + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], +}) +export class InitPageRoutingModule { } diff --git a/setup-wizard/src/app/pages/init/init.module.ts b/setup-wizard/src/app/pages/init/init.module.ts new file mode 100644 index 000000000..ba0808a5f --- /dev/null +++ b/setup-wizard/src/app/pages/init/init.module.ts @@ -0,0 +1,15 @@ +import { NgModule } from '@angular/core' +import { CommonModule } from '@angular/common' +import { IonicModule } from '@ionic/angular' +import { InitPage } from './init.page' +import { InitPageRoutingModule } from './init-routing.module' + +@NgModule({ + imports: [ + CommonModule, + IonicModule, + InitPageRoutingModule, + ], + declarations: [InitPage], +}) +export class InitPageModule { } diff --git a/setup-wizard/src/app/pages/init/init.page.html b/setup-wizard/src/app/pages/init/init.page.html new file mode 100644 index 000000000..39aeb5a7a --- /dev/null +++ b/setup-wizard/src/app/pages/init/init.page.html @@ -0,0 +1,24 @@ + + + + + +
+ +
+ + + + Initializing Embassy + Progress: {{ progress | async }}% + + + + +

After completion, you will be prompted to download a file from your Embassy. Save the file somewhere safe, it is the easiest way to recover your Embassy's addresses and SSL certificate in case you lose them.

+
+
+
+
+
+
\ No newline at end of file diff --git a/setup-wizard/src/app/pages/init/init.page.scss b/setup-wizard/src/app/pages/init/init.page.scss new file mode 100644 index 000000000..e69de29bb diff --git a/setup-wizard/src/app/pages/init/init.page.ts b/setup-wizard/src/app/pages/init/init.page.ts new file mode 100644 index 000000000..791554c6f --- /dev/null +++ b/setup-wizard/src/app/pages/init/init.page.ts @@ -0,0 +1,31 @@ +import { Component } from '@angular/core' +import { NavController } from '@ionic/angular' +import { interval, Observable } from 'rxjs' +import { finalize, skip, take } from 'rxjs/operators' +import { pauseFor } from 'src/app/util/misc.util' + +@Component({ + selector: 'app-init', + templateUrl: 'init.page.html', + styleUrls: ['init.page.scss'], +}) +export class InitPage { + progress: Observable + + constructor ( + private navCtrl: NavController, + ) { } + + ngOnInit () { + this.progress = interval(500) + .pipe( + skip(1), + take(100), + finalize(async () => { + await pauseFor(1000) + this.navCtrl.navigateForward('/success') + }), + ) + } +} + diff --git a/setup-wizard/src/app/pages/loading/loading.module.ts b/setup-wizard/src/app/pages/loading/loading.module.ts index 165ba2f70..298ce90fb 100644 --- a/setup-wizard/src/app/pages/loading/loading.module.ts +++ b/setup-wizard/src/app/pages/loading/loading.module.ts @@ -3,7 +3,6 @@ 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({ @@ -12,7 +11,6 @@ import { LoadingPageRoutingModule } from './loading-routing.module' FormsModule, IonicModule, LoadingPageRoutingModule, - PasswordPageModule, ], declarations: [LoadingPage], }) diff --git a/setup-wizard/src/app/pages/loading/loading.page.html b/setup-wizard/src/app/pages/loading/loading.page.html index c34d32494..12530c147 100644 --- a/setup-wizard/src/app/pages/loading/loading.page.html +++ b/setup-wizard/src/app/pages/loading/loading.page.html @@ -10,12 +10,11 @@ Recovering From Backup - Progress: {{(stateService.dataProgress * 100).toFixed(0) }}% + Progress: {{ (stateService.dataProgress * 100).toFixed(0) }}% - - +
diff --git a/setup-wizard/src/app/pages/loading/loading.page.scss b/setup-wizard/src/app/pages/loading/loading.page.scss index a2f80017a..e69de29bb 100644 --- a/setup-wizard/src/app/pages/loading/loading.page.scss +++ b/setup-wizard/src/app/pages/loading/loading.page.scss @@ -1,16 +0,0 @@ -.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); -} \ No newline at end of file diff --git a/setup-wizard/src/app/pages/password/password.page.html b/setup-wizard/src/app/pages/password/password.page.html index a838ea1b5..4bb43db1c 100644 --- a/setup-wizard/src/app/pages/password/password.page.html +++ b/setup-wizard/src/app/pages/password/password.page.html @@ -1,26 +1,25 @@ - + {{ !!storageDrive ? 'Set Password' : 'Unlock Drive' }} - +
-

Choose a password for your Embassy. Make it good. Write it down. If you lose this password, you may be permanently locked out of your Embassy.

-

Warning: data on this drive will be permanently deleted.

+

Choose a password for your Embassy. Make it good. Write it down.

+

Losing your password can result in total loss of data.

Enter the password that was used to encrypt this drive.

-

+

Password: -

- +

-

{{ pwError }}

+
+

{{ pwError }}

+
-

- Verify Password: -

- +

+ Confirm Password: +

-

{{ verError }}

+
+

{{ verError }}

+
@@ -64,15 +66,13 @@ - - - - Cancel - - - {{ !!storageDrive ? 'Finish' : 'Unlock' }} - - + + + Cancel + + + {{ !!storageDrive ? 'Finish' : 'Unlock' }} + diff --git a/setup-wizard/src/app/pages/password/password.page.scss b/setup-wizard/src/app/pages/password/password.page.scss index d917cc480..e69de29bb 100644 --- a/setup-wizard/src/app/pages/password/password.page.scss +++ b/setup-wizard/src/app/pages/password/password.page.scss @@ -1,26 +0,0 @@ -.password-input { - color: var(--ion-color-dark); - 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 { - font-weight: 500; - --placeholder-font-weight: 400; - width: 100%; - background: var(--ion-color-dark); - border-radius: 3px; -} \ No newline at end of file diff --git a/setup-wizard/src/app/pages/password/password.page.ts b/setup-wizard/src/app/pages/password/password.page.ts index d3c7a735d..3b23ed41d 100644 --- a/setup-wizard/src/app/pages/password/password.page.ts +++ b/setup-wizard/src/app/pages/password/password.page.ts @@ -1,6 +1,6 @@ import { Component, Input } from '@angular/core' -import { LoadingController, ModalController } from '@ionic/angular' -import { ApiService, DiskInfo, PartitionInfo } from 'src/app/services/api/api.service' +import { ModalController } from '@ionic/angular' +import { DiskInfo, PartitionInfo } from 'src/app/services/api/api.service' import * as argon2 from '@start9labs/argon2' @Component({ @@ -20,25 +20,15 @@ export class PasswordPage { passwordVer = '' unmasked2 = false - hasData: boolean - constructor ( private modalController: ModalController, - private apiService: ApiService, - private loadingCtrl: LoadingController, ) { } - ngOnInit () { - if (this.storageDrive && this.storageDrive.partitions.find(p => p.used)) { - this.hasData = true - } - } - async verifyPw () { if (!this.recoveryPartition || !this.recoveryPartition['embassy-os']) this.pwError = 'No recovery drive' // unreachable try { - argon2.verify( this.recoveryPartition['embassy-os']['password-hash'], this.password) + argon2.verify(this.recoveryPartition['embassy-os']['password-hash'], this.password) this.modalController.dismiss({ password: this.password }) } catch (e) { this.pwError = 'Incorrect password provided' @@ -63,14 +53,14 @@ export class PasswordPage { } if (this.password.length < 12) { - this.pwError = '*password must be 12 characters or greater' + this.pwError = 'Must be 12 characters or greater' } else { this.pwError = '' } } checkVer () { - this.verError = this.password !== this.passwordVer ? '*passwords do not match' : '' + this.verError = this.password !== this.passwordVer ? 'Passwords do not match' : '' } cancel () { diff --git a/setup-wizard/src/app/pages/prod-key-modal/prod-key-modal.page.html b/setup-wizard/src/app/pages/prod-key-modal/prod-key-modal.page.html index ebe272a05..7e9ce9c55 100644 --- a/setup-wizard/src/app/pages/prod-key-modal/prod-key-modal.page.html +++ b/setup-wizard/src/app/pages/prod-key-modal/prod-key-modal.page.html @@ -1,12 +1,12 @@ - + Verify Recovery Product Key - +
@@ -35,7 +35,7 @@ - + Cancel diff --git a/setup-wizard/src/app/pages/prod-key-modal/prod-key-modal.page.scss b/setup-wizard/src/app/pages/prod-key-modal/prod-key-modal.page.scss index d917cc480..e69de29bb 100644 --- a/setup-wizard/src/app/pages/prod-key-modal/prod-key-modal.page.scss +++ b/setup-wizard/src/app/pages/prod-key-modal/prod-key-modal.page.scss @@ -1,26 +0,0 @@ -.password-input { - color: var(--ion-color-dark); - 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 { - font-weight: 500; - --placeholder-font-weight: 400; - width: 100%; - background: var(--ion-color-dark); - border-radius: 3px; -} \ No newline at end of file diff --git a/setup-wizard/src/app/pages/product-key/product-key.page.html b/setup-wizard/src/app/pages/product-key/product-key.page.html index 4f6bd5032..8ef198116 100644 --- a/setup-wizard/src/app/pages/product-key/product-key.page.html +++ b/setup-wizard/src/app/pages/product-key/product-key.page.html @@ -1,4 +1,4 @@ - + @@ -12,16 +12,16 @@ Enter Product Key - + - - - - + +

Product Key

+ + +
- -

*{{ error }}

+

{{ error }}

diff --git a/setup-wizard/src/app/pages/product-key/product-key.page.scss b/setup-wizard/src/app/pages/product-key/product-key.page.scss index a2f80017a..e69de29bb 100644 --- a/setup-wizard/src/app/pages/product-key/product-key.page.scss +++ b/setup-wizard/src/app/pages/product-key/product-key.page.scss @@ -1,16 +0,0 @@ -.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); -} \ No newline at end of file diff --git a/setup-wizard/src/app/pages/recover/recover.page.html b/setup-wizard/src/app/pages/recover/recover.page.html index 3c6964bcf..1e6a5dbf7 100644 --- a/setup-wizard/src/app/pages/recover/recover.page.html +++ b/setup-wizard/src/app/pages/recover/recover.page.html @@ -1,4 +1,4 @@ - + @@ -16,8 +16,8 @@ -

No recovery drives found

-

Please connect a recovery drive to your Embassy and refresh the page.

+

No recovery drives found

+

Please connect a recovery drive to your Embassy and refresh the page.

d.partitions.map(p => ({ partition: p, vendor: d.vendor, model: d.model})).filter(p => p.partition['embassy-os']?.full)).flat() // if theres no product key, only show 0.2s diff --git a/setup-wizard/src/app/pages/success/success.page.html b/setup-wizard/src/app/pages/success/success.page.html index 4e56b24cb..cb62d2e75 100644 --- a/setup-wizard/src/app/pages/success/success.page.html +++ b/setup-wizard/src/app/pages/success/success.page.html @@ -1,91 +1,171 @@ - + - + -
+
- - Setup Complete! + + + Setup Complete! - - -

Congratulations! In a few minutes, you will be able to access your Embassy via TOR or LAN.

-

Tor Address:

- - -

{{ stateService.torAddress }}

-
- - - - - -
-

We suggest you copy and save your tor adress in a safe place. This page will no longer be available once you access your Embassy.

- -
+ +
+ +
+

Tor Instructions:

-

LAN Instructions (Optional):

+
- - - -

- Connecting to your Embassy over LAN provides a lightning fast experience and is a reliable fallback in case Tor is having problems. To connect to your Embassy's .local address, you must: -
    -
  1. Be connected to the same Local Area Network (LAN) as your Embassy.
  2. -
  3. Download and trust your Embassy's SSL Certificate Authority (below).
  4. -
- View the full instructions. -

-
- - - - -

Download Root CA

-

Download and trust your Embassy's Root Cert

+
+

+ To use your Embassy over Tor, visit its unique Tor address from any Tor-enabled browser. + For a list of recommended browsers, click here. +

+
+

Tor Address

+ + + {{ stateService.torAddress }} + + + - -

LAN Address:

- - - -

{{ stateService.lanAddress }}

-
- - - - - -
- +
+
+
+ + +
+

LAN Instructions (Slightly Advanced):

+ +
+ +
+
+

To use your Embassy locally, you must:

+
    +
  1. Currently be connected to the same Local Area Network (LAN) as your Embassy.
  2. +
  3. Download your Embassy's Root Certificate Authority.
  4. +
  5. Trust your Embassy's Root CA on both your computer/phone and in your browser settings.
  6. +
+

+ For step-by-step instructions, click + here. +

+ + + Download Root CA + + + +

LAN Address

+ + + {{ stateService.lanAddress }} + + + + + +
+
+
+
+
+ + Download this page + + +
+
- - - + + + + + + diff --git a/setup-wizard/src/app/pages/success/success.page.scss b/setup-wizard/src/app/pages/success/success.page.scss index 9cbc9cad6..9083c1314 100644 --- a/setup-wizard/src/app/pages/success/success.page.scss +++ b/setup-wizard/src/app/pages/success/success.page.scss @@ -1,30 +1,29 @@ -.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); -} - -.divider { - margin: 0; - background: linear-gradient(90deg,var(--ion-color-dark) 0,var(--ion-color-medium) 50%,var(--ion-color-dark) 100%); - height: 1px; -} - -.addr-label { - text-align: left; - padding-bottom: 2px; - font-size: small; +p { color: var(--ion-color-light); - font-weight: bold; +} + +a { + text-decoration: none; +} + +.toggle-label { + padding: 24px 0 8px 0; + cursor: pointer; + display: flex; + flex-direction: row; + justify-content: space-between; + + * { + display: inline-block; + vertical-align: middle; + } + + h2 { + font-weight: bold; + } + + ion-icon { + text-align: right; + font-size: 24px; + } } \ No newline at end of file diff --git a/setup-wizard/src/app/pages/success/success.page.ts b/setup-wizard/src/app/pages/success/success.page.ts index c4f57b32d..a38699344 100644 --- a/setup-wizard/src/app/pages/success/success.page.ts +++ b/setup-wizard/src/app/pages/success/success.page.ts @@ -1,5 +1,5 @@ import { Component } from '@angular/core' -import { AlertController, ToastController } from '@ionic/angular' +import { ToastController } from '@ionic/angular' import { StateService } from 'src/app/services/state.service' @Component({ @@ -8,19 +8,21 @@ import { StateService } from 'src/app/services/state.service' styleUrls: ['success.page.scss'], }) export class SuccessPage { + torOpen = true + lanOpen = false + constructor ( private readonly toastCtrl: ToastController, - private readonly alertCtrl: AlertController, public readonly stateService: StateService, ) { } - window = window - lanInstructionsOpen = false + ngAfterViewInit () { + this.download() + } async copy (address: string): Promise { - let message = '' - await this.copyToClipboard(address) - .then(success => message = success ? 'copied to clipboard!' : 'failed to copy') + const success = await this.copyToClipboard(address) + const message = success ? 'copied to clipboard!' : 'failed to copy' const toast = await this.toastCtrl.create({ header: message, @@ -30,51 +32,46 @@ export class SuccessPage { await toast.present() } - async goToEmbassy () { - window.location.reload() - } - - async copyToClipboard (str: string): Promise { - const alert = await this.alertCtrl.create({ - header: 'Please Save', - message: 'Make sure you save the address to your Embassy in a safe place.', - buttons: [ - { - text: 'OK', - role: 'cancel', - }, - ], - }) - - await alert.present() - 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 - } + toggleTor () { + this.torOpen = !this.torOpen } toggleLan () { - this.lanInstructionsOpen = !this.lanInstructionsOpen + this.lanOpen = !this.lanOpen } installCert () { document.getElementById('install-cert').click() } + + download () { + document.getElementById('tor-addr').innerHTML = this.stateService.torAddress + document.getElementById('lan-addr').innerHTML = this.stateService.lanAddress + document.getElementById('cert').setAttribute('href', 'data:application/x-x509-ca-cert;base64,' + encodeURIComponent(this.stateService.cert)) + let html = document.getElementById('downloadable').innerHTML + const filename = 'embassy-info.html' + + const elem = document.createElement('a') + elem.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(html)) + elem.setAttribute('download', filename) + elem.style.display = 'none' + + document.body.appendChild(elem) + elem.click() + document.body.removeChild(elem) + } + + private async copyToClipboard (str: string): Promise { + 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 + } } diff --git a/setup-wizard/src/app/services/api/api.service.ts b/setup-wizard/src/app/services/api/api.service.ts index af6a40453..361ad3658 100644 --- a/setup-wizard/src/app/services/api/api.service.ts +++ b/setup-wizard/src/app/services/api/api.service.ts @@ -26,6 +26,7 @@ export interface SetupEmbassyReq { export interface SetupEmbassyRes { 'tor-address': string 'lan-address': string + 'root-ca': string } export interface DiskInfo { diff --git a/setup-wizard/src/app/services/api/mock-api.service.ts b/setup-wizard/src/app/services/api/mock-api.service.ts index 08c8f4b9f..e67218ba6 100644 --- a/setup-wizard/src/app/services/api/mock-api.service.ts +++ b/setup-wizard/src/app/services/api/mock-api.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core' -import { pauseFor } from '../state.service' +import { pauseFor } from 'src/app/util/misc.util' import { ApiService, SetupEmbassyReq } from './api.service' let tries = 0 @@ -18,7 +18,7 @@ export class MockApiService extends ApiService { async getStatus () { await pauseFor(1000) return { - 'product-key': false, + 'product-key': true, migrating: false, } } @@ -148,6 +148,7 @@ export class MockApiService extends ApiService { return { 'tor-address': 'asdfasdfasdf.onion', 'lan-address': 'embassy-dfasdf.local', + 'root-ca': rootCA, } } @@ -167,3 +168,5 @@ export class MockApiService extends ApiService { ] } } + +const rootCA = 'LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURwekNDQW8rZ0F3SUJBZ0lSQUlJdU9hcmxRRVRsVVFFT1pKR1pZZEl3RFFZSktvWklodmNOQVFFTEJRQXcKYlRFTE1Ba0dBMVVFQmhNQ1ZWTXhGVEFUQmdOVkJBb01ERVY0WVcxd2JHVWdRMjl5Y0RFT01Bd0dBMVVFQ3d3RgpVMkZzWlhNeEN6QUpCZ05WQkFnTUFsZEJNUmd3RmdZRFZRUUREQTkzZDNjdVpYaGhiWEJzWlM1amIyMHhFREFPCkJnTlZCQWNNQjFObFlYUjBiR1V3SGhjTk1qRXdNekE0TVRVME5qSTNXaGNOTWpJd016QTRNVFkwTmpJM1dqQnQKTVFzd0NRWURWUVFHRXdKVlV6RVZNQk1HQTFVRUNnd01SWGhoYlhCc1pTQkRiM0p3TVE0d0RBWURWUVFMREFWVApZV3hsY3pFTE1Ba0dBMVVFQ0F3Q1YwRXhHREFXQmdOVkJBTU1EM2QzZHk1bGVHRnRjR3hsTG1OdmJURVFNQTRHCkExVUVCd3dIVTJWaGRIUnNaVENDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFNUDcKdDVBS0ZaUTdhYnFrZXlVanNCVklXUmE5dENoOG9nZTl1L0x2Q2J4VTczOEc0anNzVCtPdWQzV01haklqdU5vdwpjcGMrMFEvZTQyVUxPLzZnVE5yVHM2T0NPbzlsVjZHMERwcmYvZTkxRFdvS2dQYXRlbS9wVWpOeXJhaWZIWmZ1CmI1bUxIQ2ZhaGpXWFVRdGMvc2ptRFFhWlJLM0thcjZsamxVQkUvTGU5TkV5T0FJa1NMUHpEdFc4TFhtNGl3Y1UKQlpyYjgyOHJLZDFBdzlvSTErM2JmekI2eFhtelp4YzVSTFh2ZU9DRWhLR0QzMmpLWi9STkZTQzhBWkF3SmUreApiVHN5cy9sVU9ZRlR1VDhCbjBUR3hSOHg3WTRINzUrRjlCYXZZM3YrV2tMajRNK29sTjlkTVI3RXQ5Rk10NHU0CllSb2t2NXpwOHpJYjVpVG5lMWtDQXdFQUFhTkNNRUF3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEUKRmdRVWFXMytyMzI4dVRMb2tvZzJUa2xtb0JLK3l0NHdEZ1lEVlIwUEFRSC9CQVFEQWdHR01BMEdDU3FHU0liMwpEUUVCQ3dVQUE0SUJBUUFYamQvN1VaOFJERStQTFdTRE5HUWRMZW1PQlRjYXdGK3RLK1B6QTRFdmxtbjlWdU5jCmcreDNvWnZWWlNEUUJBTlV6MGI5b1BlbzU0YUUzOGRXMXpRbTJxZlRhYjg4MjJhcWVXTUx5SjFkTXNBZ3FZWDIKdDkrdTZ3M056UkN3OFB2ejE4VjY5K2RGRTVBZVhtTlAwWjUvZ2R6OEgvTlNwY3RqbHpvcGJTY1JaS0NTbFBpZApSZjNaT1BtOVFQOTJZcFd5WURrZkFVMDR4ZERvMXZSME1ZaktQa2w0TGpScVNVL3RjQ0puUE1iSml3cStiV3BYCjJXSm9FQlhCL3AxNUtuNkp4akkwemUyU25TSTQ4Slo4aXQ0ZnZ4cmhPbzBWb0xOSXVDdU5YSk93VTE3UmRsMVcKWUppZGFxN2plNmsxOEFkZ1BBMEtoOHkxWHRmVUgzZlRhVnc0Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=' diff --git a/setup-wizard/src/app/services/global-error-handler.service.ts b/setup-wizard/src/app/services/global-error-handler.service.ts index ac0bb9824..d7234f0b5 100644 --- a/setup-wizard/src/app/services/global-error-handler.service.ts +++ b/setup-wizard/src/app/services/global-error-handler.service.ts @@ -4,7 +4,7 @@ import { ErrorHandler, Injectable } from '@angular/core' export class GlobalErrorHandler implements ErrorHandler { handleError (error: any): void { - const chunkFailedMessage = /Loading chunk [\d]+ failed/ + const chunkFailedMessage = /Loading chunk [\d]+ failed/ if (chunkFailedMessage.test(error.message)) { window.location.reload() diff --git a/setup-wizard/src/app/services/state.service.ts b/setup-wizard/src/app/services/state.service.ts index c09cf2305..6df7147ea 100644 --- a/setup-wizard/src/app/services/state.service.ts +++ b/setup-wizard/src/app/services/state.service.ts @@ -2,6 +2,7 @@ import { Injectable } from '@angular/core' import { BehaviorSubject } from 'rxjs' import { ApiService, DiskInfo, PartitionInfo } from './api/api.service' import { ErrorToastService } from './error-toast.service' +import { pauseFor } from '../util/misc.util' @Injectable({ providedIn: 'root', @@ -22,13 +23,14 @@ export class StateService { torAddress: string lanAddress: string + cert: string constructor ( private readonly apiService: ApiService, private readonly errorToastService: ErrorToastService, ) { } - async pollDataTransferProgress (callback?: () => void) { + async pollDataTransferProgress () { this.polling = true await pauseFor(1000) @@ -54,7 +56,7 @@ export class StateService { this.dataProgSubject.next(this.dataProgress) } } - this.pollDataTransferProgress(callback) + this.pollDataTransferProgress() } async setupEmbassy () : Promise { @@ -64,12 +66,8 @@ export class StateService { 'recovery-partition': this.recoveryPartition, 'recovery-password': this.recoveryPassword, }) - this.torAddress = ret['tor-address'] - this.lanAddress = ret['lan-address'] + this.torAddress = 'http://' + ret['tor-address'] + this.lanAddress = 'https://' + ret['lan-address'] + this.cert = ret['root-ca'] } } - -export const pauseFor = (ms: number) => { - const promise = new Promise(resolve => setTimeout(resolve, ms)) - return promise -} diff --git a/setup-wizard/src/app/util/misc.util.ts b/setup-wizard/src/app/util/misc.util.ts new file mode 100644 index 000000000..1a21a3ab1 --- /dev/null +++ b/setup-wizard/src/app/util/misc.util.ts @@ -0,0 +1,3 @@ +export const pauseFor = (ms: number) => { + return new Promise(resolve => setTimeout(resolve, ms)) +} \ No newline at end of file diff --git a/setup-wizard/src/assets/fonts/Montserrat/Montserrat-Bold.ttf b/setup-wizard/src/assets/fonts/Montserrat/Montserrat-Bold.ttf new file mode 100644 index 000000000..221819bca Binary files /dev/null and b/setup-wizard/src/assets/fonts/Montserrat/Montserrat-Bold.ttf differ diff --git a/setup-wizard/src/assets/fonts/Montserrat/Montserrat-Light.ttf b/setup-wizard/src/assets/fonts/Montserrat/Montserrat-Light.ttf new file mode 100644 index 000000000..990857de8 Binary files /dev/null and b/setup-wizard/src/assets/fonts/Montserrat/Montserrat-Light.ttf differ diff --git a/setup-wizard/src/assets/shapes.svg b/setup-wizard/src/assets/shapes.svg deleted file mode 100644 index d370b4dcc..000000000 --- a/setup-wizard/src/assets/shapes.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/setup-wizard/src/global.scss b/setup-wizard/src/global.scss index fb8c737e2..edceae566 100644 --- a/setup-wizard/src/global.scss +++ b/setup-wizard/src/global.scss @@ -25,32 +25,41 @@ @import "~@ionic/angular/css/text-transformation.css"; @import "~@ionic/angular/css/flex-utils.css"; +ion-toolbar { + --ion-background-color: var(--ion-color-light); +} + ion-avatar { width: 27px; height: 27px; } +ion-alert { + .alert-button { + color: var(--ion-color-dark) !important; + } +} + +ion-button { + --color: var(--ion-color-dark) !important; +} + ion-item { --highlight-color-valid: transparent; --highlight-color-invalid: transparent; --highlight-color-focused: var(--ion-color-light); + + --border-radius: 4px; + --border-style: solid; + --border-width: 1px; + --border-color: var(--ion-color-light); } -.claim-button { - margin-inline-start: 0; - margin-inline-end: 0; - margin-top: 24px; - min-width: 140px; -} - -.alertlike-modal { - .modal-wrapper { - max-height: 380px !important; - top: 25% !important; - width: 90% !important; - left: 5% !important; - --box-shadow: none !important; - } +ion-card-title { + margin: 16px 0; + font-family: 'Montserrat'; + font-size: x-large; + --color: var(--ion-color-light); } ion-toast { @@ -61,6 +70,20 @@ ion-toast { --color: white; } +.inline { + * { + display: inline-block; + vertical-align: middle; + } +} + +.claim-button { + margin-inline-start: 0; + margin-inline-end: 0; + margin-top: 24px; + min-width: 140px; +} + .error-toast { --border-color: var(--ion-color-danger); width: 40%; @@ -71,11 +94,64 @@ ion-toast { top: 64px; } +.input-label { + text-align: left; + padding-bottom: 2px; + font-size: small; + font-weight: bold; +} + +.error-border { + border: 2px solid var(--ion-color-danger); + border-radius: 4px; +} + +.success-border { + border: 2px solid var(--ion-color-success); + border-radius: 4px; +} + +.modal-wrapper.sc-ion-modal-md { + border-radius: 6px; + border: 2px solid rgba(255,255,255,.03); + box-shadow: 0 0 70px 70px black; +} + +.modal-wrapper { + position: absolute; + height: 90% !important; + top: 5%; + width: 90% !important; + left: 5%; + display: block; +} + +@media (min-width:1000px) { + .modal-wrapper { + position: absolute; + height: 80% !important; + top: 10%; + width: 50% !important; + left: 25%; + display: block; + } +} + +.alertlike-modal { + .modal-wrapper { + max-height: 380px !important; + top: 25% !important; + width: 90% !important; + left: 5% !important; + --box-shadow: none !important; + } +} + @media (min-width:1000px) { .alertlike-modal { .modal-wrapper { - width: 40% !important; - left: 30% !important; + width: 60% !important; + left: 20% !important; } } } \ No newline at end of file diff --git a/setup-wizard/src/index.html b/setup-wizard/src/index.html index 7aac63265..03ef883e0 100644 --- a/setup-wizard/src/index.html +++ b/setup-wizard/src/index.html @@ -7,7 +7,6 @@ - @@ -15,10 +14,6 @@ var global = window; - - - - diff --git a/setup-wizard/src/theme/variables.scss b/setup-wizard/src/theme/variables.scss index 544905625..776843bd4 100644 --- a/setup-wizard/src/theme/variables.scss +++ b/setup-wizard/src/theme/variables.scss @@ -4,17 +4,19 @@ /** Ionic CSS Variables **/ :root { --ion-font-family: 'Benton Sans'; + --ion-background-color: var(--ion-color-medium); + --ion-background-color-rgb: var(--ion-color-medium-rgb); --ion-text-color: var(--ion-color-dark); --ion-text-color-rgb: var(--ion-color-dark-rgb); - /** primary **/ - --ion-color-primary: #428cff; + --ion-backdrop-opacity: .75; + + --ion-color-primary: #0075e1; --ion-color-primary-rgb: 66,140,255; --ion-color-primary-contrast: #ffffff; --ion-color-primary-contrast-rgb: 255,255,255; --ion-color-primary-shade: #3a7be0; --ion-color-primary-tint: #5598ff; - /** secondary **/ --ion-color-secondary: #50c8ff; --ion-color-secondary-rgb: 80,200,255; --ion-color-secondary-contrast: #ffffff; @@ -22,7 +24,6 @@ --ion-color-secondary-shade: #46b0e0; --ion-color-secondary-tint: #62ceff; - /** tertiary **/ --ion-color-tertiary: #6a64ff; --ion-color-tertiary-rgb: 106,100,255; --ion-color-tertiary-contrast: #ffffff; @@ -30,7 +31,6 @@ --ion-color-tertiary-shade: #5d58e0; --ion-color-tertiary-tint: #7974ff; - /** success **/ --ion-color-success: #2fdf75; --ion-color-success-rgb: 47,223,117; --ion-color-success-contrast: #000000; @@ -38,7 +38,6 @@ --ion-color-success-shade: #29c467; --ion-color-success-tint: #44e283; - /** warning **/ --ion-color-warning: #ffd534; --ion-color-warning-rgb: 255,213,52; --ion-color-warning-contrast: #000000; @@ -46,7 +45,6 @@ --ion-color-warning-shade: #e0bb2e; --ion-color-warning-tint: #ffd948; - /** danger **/ --ion-color-danger: #ff4961; --ion-color-danger-rgb: 255,73,97; --ion-color-danger-contrast: #ffffff; @@ -54,7 +52,20 @@ --ion-color-danger-shade: #e04055; --ion-color-danger-tint: #ff5b71; - /** dark **/ + --ion-color-light: #181818; + --ion-color-light-rgb: 24,24,24; + --ion-color-light-contrast: #ffffff; + --ion-color-light-contrast-rgb: 0,0,0; + --ion-color-light-shade: #000000; + --ion-color-light-tint: #000000; + + --ion-color-medium: #222428; + --ion-color-medium-rgb: 34,36,40; + --ion-color-medium-contrast: #ffffff; + --ion-color-medium-contrast-rgb: 255,255,255; + --ion-color-medium-shade: #1e2023; + --ion-color-medium-tint: #383a3e; + --ion-color-dark: #e0e0e0; --ion-color-dark-rgb: 224,224,224; --ion-color-dark-contrast: #000000; @@ -62,21 +73,25 @@ --ion-color-dark-shade: #bfbfbf; --ion-color-dark-tint: #d8d8d8; - /** medium **/ - --ion-color-medium: #989aa2; - --ion-color-medium-rgb: 152,154,162; - --ion-color-medium-contrast: #000000; - --ion-color-medium-contrast-rgb: 0,0,0; - --ion-color-medium-shade: #86888f; - --ion-color-medium-tint: #a2a4ab; - - /** light **/ - --ion-color-light: #25272b; - --ion-color-light-rgb: 34,36,40; - --ion-color-light-contrast: #ffffff; - --ion-color-light-contrast-rgb: 255,255,255; - --ion-color-light-shade: #1e2023; - --ion-color-light-tint: #383a3e; + --ion-color-step-50: #1e1e1e; + --ion-color-step-100: #2a2a2a; + --ion-color-step-150: #363636; + --ion-color-step-200: #414141; + --ion-color-step-250: #4d4d4d; + --ion-color-step-300: #595959; + --ion-color-step-350: #656565; + --ion-color-step-400: #717171; + --ion-color-step-450: #7d7d7d; + --ion-color-step-500: #898989; + --ion-color-step-550: #949494; + --ion-color-step-600: #a0a0a0; + --ion-color-step-650: #acacac; + --ion-color-step-700: #b8b8b8; + --ion-color-step-750: #c4c4c4; + --ion-color-step-800: #d0d0d0; + --ion-color-step-850: #dbdbdb; + --ion-color-step-900: #e7e7e7; + --ion-color-step-950: #f3f3f3; } @font-face { @@ -86,6 +101,20 @@ src: url('../assets/fonts/Montserrat/Montserrat-Regular.ttf'); } +@font-face { + font-family: 'Montserrat'; + font-style: normal; + font-weight: bold; + src: url('../assets/fonts/Montserrat/Montserrat-Bold.ttf'); +} + +@font-face { + font-family: 'Montserrat'; + font-style: normal; + font-weight: thin; + src: url('../assets/fonts/Montserrat/Montserrat-Light.ttf'); +} + @font-face { font-family: 'Benton Sans'; font-style: normal;