Update setup wizard styling (#1954)

* base srevampof home page

* update sembassy page

* update all  ephemeral pages

* matrix animation working

* wip success

* refactor styling  of success page

* modal and mobile adjustments

* cleanup

* make chnages to styles and copy (#1955)

* make chnages to styles and copy

* fix responsiveness of downloadable page

Co-authored-by: Lucy Cifferello <12953208+elvece@users.noreply.github.com>

* refactor success page

* cleanup headers

* revert isKiosk testing

* udpate patch DB

Co-authored-by: Matt Hill <MattDHill@users.noreply.github.com>
Co-authored-by: Matt Hill <matthewonthemoon@gmail.com>
This commit is contained in:
Lucy C
2022-11-20 20:37:19 -07:00
committed by Aiden McClelland
parent a3d1b2d671
commit 4042b8f026
23 changed files with 812 additions and 425 deletions

View File

@@ -1,8 +1,6 @@
<ion-header>
<ion-toolbar>
<ion-title>
Connect Shared Folder
</ion-title>
<ion-title> Connect Network Folder </ion-title>
</ion-toolbar>
</ion-header>
@@ -21,7 +19,10 @@
></ion-input>
</ion-item>
<p [hidden]="hostname.valid || hostname.pristine">
<ion-text color="danger">Hostname is required. e.g. 'My Computer' OR 'my-computer.local'</ion-text>
<ion-text color="danger"
>Hostname is required. e.g. 'My Computer' OR
'my-computer.local'</ion-text
>
</p>
<p>Path *</p>
@@ -71,12 +72,23 @@
<ion-footer>
<ion-toolbar>
<ion-button class="ion-padding-end" slot="end" color="dark" fill="clear" (click)="cancel()">
<ion-button
class="ion-padding-end"
slot="end"
color="warning"
(click)="cancel()"
>
Cancel
</ion-button>
<ion-button class="ion-padding-end" slot="end" color="dark" fill="clear" strong="true" [disabled]="!cifsForm.form.valid" (click)="submit()">
<ion-button
class="ion-padding-end"
slot="end"
color="primary"
strong="true"
[disabled]="!cifsForm.form.valid"
(click)="submit()"
>
Verify
</ion-button>
</ion-toolbar>
</ion-footer>

View File

@@ -1,3 +1,20 @@
ion-content {
--ion-text-color: var(--ion-color-dark);
.item-interactive {
--highlight-background: var(--ion-color-dark) !important;
}
ion-item {
&:hover {
transition-property: transform;
transform: none;
}
}
.item-has-focus {
--background: var(--ion-color-dark-tint) !important;
}
ion-modal {
--backdrop-opacity: 0.7;
}

View File

@@ -69,7 +69,6 @@ export class CifsModal {
const modal = await this.modalController.create({
component: PasswordPage,
componentProps: { target },
cssClass: 'alertlike-modal',
})
modal.onDidDismiss().then(res => {
if (res.role === 'success') {

View File

@@ -8,20 +8,17 @@
<ion-content>
<div style="padding: 8px 24px">
<div style="padding-bottom: 16px">
<ng-template #choose>
<p>
Choose a password for your Embassy.
<i>Make it good. Write it down.</i>
</p>
</ng-template>
<p *ngIf="!storageDrive else choose">
Enter the password that was used to encrypt this drive.
<p *ngIf="!storageDrive else choose">
Enter the password that was used to encrypt this drive.
</p>
<ng-template #choose>
<p>
Choose a password for your Embassy.
<i>Make it good. Write it down.</i>
</p>
</div>
</ng-template>
<form (ngSubmit)="storageDrive ? submitPw() : verifyPw()">
<p>Password</p>
<ion-item
[class]="pwError ? 'error-border' : password && storageDrive ? 'success-border' : ''"
>
@@ -42,13 +39,8 @@
></ion-icon>
</ion-button>
</ion-item>
<div style="height: 16px">
<p style="color: var(--ion-color-danger); font-size: x-small">
{{ pwError }}
</p>
</div>
<p *ngIf="pwError" class="error-message">{{ pwError }}</p>
<ng-container *ngIf="storageDrive">
<p>Confirm Password</p>
<ion-item
[class]="verError ? 'error-border' : passwordVer ? 'success-border' : ''"
>
@@ -72,11 +64,7 @@
></ion-icon>
</ion-button>
</ion-item>
<div style="height: 16px">
<p style="color: var(--ion-color-danger); font-size: x-small">
{{ verError }}
</p>
</div>
<p *ngIf="verError" class="error-message">{{ verError }}</p>
</ng-container>
<input type="submit" style="display: none" />
</form>
@@ -88,8 +76,7 @@
<ion-button
class="ion-padding-end"
slot="end"
color="dark"
fill="clear"
color="warning"
(click)="cancel()"
>
Cancel
@@ -97,8 +84,6 @@
<ion-button
class="ion-padding-end"
slot="end"
color="dark"
fill="clear"
strong="true"
(click)="storageDrive ? submitPw() : verifyPw()"
>

View File

@@ -1,3 +1,21 @@
ion-content {
--ion-text-color: var(--ion-color-dark);
.item-interactive {
--highlight-background: var(--ion-color-dark) !important;
}
ion-item {
&:hover {
transition-property: transform;
transform: none;
}
}
.item-has-focus {
--background: var(--ion-color-dark-tint) !important;
}
.error-message {
color: var(--ion-color-danger) !important;
font-size: .9rem !important;
margin-left: 36px;
margin-top: -16px;
}

View File

@@ -2,17 +2,15 @@
<ion-grid>
<ion-row>
<ion-col>
<div style="padding-bottom: 32px" class="ion-text-center">
<img src="assets/img/logo.png" style="max-width: 240px" />
</div>
<ion-card color="dark">
<ion-card-header class="ion-text-center">
<ion-card-title>Use Existing Drive</ion-card-title>
<ion-card-subtitle
>Select the physical drive containing your Embassy
data</ion-card-subtitle
>
<ion-card-title>Use existing drive</ion-card-title>
<div class="center-wrapper">
<ion-card-subtitle
>Select the physical drive containing your Embassy
data</ion-card-subtitle
>
</div>
</ion-card-header>
<ion-card-content class="ion-margin">
@@ -25,8 +23,6 @@
<!-- loaded -->
<ion-item-group *ngIf="!loading" class="ion-text-center">
<!-- drives -->
<h2 class="target-label">Available Drives</h2>
<p *ngIf="!drives.length">
No valid Embassy data drives found. Please make sure the drive
is a valid Embassy data drive (not a backup) and is firmly
@@ -44,7 +40,6 @@
slot="start"
name="save-outline"
size="large"
color="light"
></ion-icon>
<ion-label>
<h1>{{ drive.logicalname }}</h1>
@@ -58,7 +53,6 @@
</ng-container>
<ion-button
class="ion-margin-top"
fill="clear"
color="primary"
(click)="refresh()"
>

View File

@@ -1,4 +0,0 @@
.target-label {
font-weight: bold;
padding-bottom: 6px;
}

View File

@@ -60,7 +60,10 @@ export class AttachPage {
}
private async attachDrive(guid: string, password: string) {
const loader = await this.loadingCtrl.create()
const loader = await this.loadingCtrl.create({
message: 'Connecting to drive...',
cssClass: 'loader',
})
await loader.present()
try {
await this.stateService.importDrive(guid, password)

View File

@@ -2,21 +2,18 @@
<ion-grid>
<ion-row>
<ion-col class="ion-text-center">
<div style="padding-bottom: 32px">
<img src="assets/img/logo.png" style="max-width: 240px" />
</div>
<ion-card color="dark">
<ion-card-header
class="ion-text-center"
style="padding-bottom: 8px"
*ngIf="loading || storageDrives.length; else empty"
>
<ion-card-title>Select Storage Drive</ion-card-title>
<ion-card-subtitle
>Select the drive where your Embassy data will be
stored.</ion-card-subtitle
>
<ion-card-title>Select storage drive</ion-card-title>
<div class="center-wrapper">
<ion-card-subtitle>
This is the drive where your Embassy data will be stored.
</ion-card-subtitle>
</div>
</ion-card-header>
<ng-template #empty>
<ion-card-header
@@ -36,12 +33,15 @@
<ion-spinner
*ngIf="loading; else loaded"
class="center-spinner"
name="lines"
name="lines-sharp"
></ion-spinner>
<!-- not loading -->
<ng-template #loaded>
<ion-item-group *ngIf="storageDrives.length">
<ion-item-group
*ngIf="storageDrives.length"
class="ion-padding-bottom"
>
<ion-item
(click)="chooseDrive(drive)"
class="ion-margin-bottom"
@@ -54,7 +54,6 @@
slot="start"
name="save-outline"
size="large"
color="light"
></ion-icon>
<ion-label class="ion-text-wrap">
<h1>
@@ -73,7 +72,7 @@
</ion-label>
</ion-item>
</ion-item-group>
<ion-button fill="clear" color="primary" (click)="getDrives()">
<ion-button fill="solid" color="primary" (click)="getDrives()">
<ion-icon slot="start" name="refresh"></ion-icon>
Refresh
</ion-button>

View File

@@ -131,7 +131,10 @@ export class EmbassyPage {
logicalname: string,
password: string,
): Promise<void> {
const loader = await this.loadingCtrl.create()
const loader = await this.loadingCtrl.create({
message: 'Connecting to drive...',
cssClass: 'loader',
})
await loader.present()
try {

View File

@@ -3,7 +3,11 @@
<ion-row>
<ion-col class="ion-text-center">
<div style="padding-bottom: 32px">
<img src="assets/img/logo.png" style="max-width: 240px" />
<img
src="assets/img/logo.png"
class="pb-1"
style="max-width: 220px"
/>
</div>
<ion-card *ngIf="!loading" color="dark">
@@ -18,22 +22,27 @@
<ion-icon slot="icon-only" name="arrow-back"></ion-icon>
</ion-button>
<ion-card-title>
Embassy Setup
<span *ngIf="swiper?.activeIndex === 1"> (recover)</span>
{{ swiper?.activeIndex === 0 ? 'embassyOS Setup' : 'Recover
Options' }}
</ion-card-title>
</ion-card-header>
<ion-card-content class="ion-margin-bottom">
<swiper (swiper)="setSwiperInstance($event)">
<swiper
[autoHeight]="true"
[observeParents]="true"
(swiper)="setSwiperInstance($event)"
>
<!-- SLIDE 1 -->
<ng-template swiperSlide>
<!-- fresh -->
<ion-item
button
[disabled]="error"
detail="true"
detail="false"
lines="none"
routerLink="/embassy"
>
<ion-icon color="dark" slot="start" name="add"></ion-icon>
<ion-icon slot="start" name="add"></ion-icon>
<ion-label>
<h2><ion-text color="success">Start Fresh</ion-text></h2>
<p>Get started with a brand new Embassy</p>
@@ -44,11 +53,11 @@
<ion-item
button
[disabled]="error"
detail="true"
detail="false"
lines="none"
(click)="next()"
>
<ion-icon color="dark" slot="start" name="reload"></ion-icon>
<ion-icon slot="start" name="reload"></ion-icon>
<ion-label>
<h2><ion-text color="danger">Recover</ion-text></h2>
<p>Recover, restore, or transfer Embassy data</p>
@@ -59,12 +68,13 @@
<!-- SLIDE 2 -->
<ng-template swiperSlide>
<!-- restore from backup -->
<ion-item button detail="true" routerLink="/recover">
<ion-icon
color="dark"
slot="start"
name="save-outline"
></ion-icon>
<ion-item
button
lines="none"
detail="false"
routerLink="/recover"
>
<ion-icon slot="start" name="save-outline"></ion-icon>
<ion-label>
<h2>
<ion-text color="warning">Restore From Backup</ion-text>
@@ -76,15 +86,11 @@
<!-- attach -->
<ion-item
button
detail="true"
detail="false"
lines="none"
routerLink="/attach"
>
<ion-icon
color="dark"
slot="start"
name="cube-outline"
></ion-icon>
<ion-icon slot="start" name="cube-outline"></ion-icon>
<ion-label>
<h2>
<ion-text color="primary">Use Existing Drive</ion-text>
@@ -98,15 +104,11 @@
<!-- transfer -->
<ion-item
button
detail="true"
detail="false"
lines="none"
routerLink="/transfer"
>
<ion-icon
color="dark"
slot="start"
name="share-outline"
></ion-icon>
<ion-icon slot="start" name="share-outline"></ion-icon>
<ion-label>
<h2>
<ion-text color="success">Transfer</ion-text>

View File

@@ -5,11 +5,13 @@
z-index: 1000000;
}
ion-item {
--background: var(--ion-color-medium);
--color: var(--ion-color-dark);
.inline {
display: flex;
align-items: center;
text-align: center;
justify-content: center;
}
p {
color: var(--ion-color-dark);
ion-card-title {
font-variant-caps: all-small-caps;
}

View File

@@ -1,23 +1,20 @@
<ion-content color="light">
<ion-content>
<ion-grid>
<ion-row>
<ion-col class="ion-text-center">
<div style="padding-bottom: 32px">
<img src="assets/img/logo.png" style="max-width: 240px" />
</div>
<ion-card color="dark">
<ion-card-header>
<ion-card-title>Initializing Embassy</ion-card-title>
<ion-card-subtitle *ngIf="stateService.dataProgress"
>Progress: {{ (stateService.dataProgress * 100).toFixed(0)
}}%</ion-card-subtitle
>
<div class="center-wrapper">
<ion-card-subtitle *ngIf="stateService.dataProgress">
Progress: {{ (stateService.dataProgress * 100).toFixed(0)}}%
</ion-card-subtitle>
</div>
</ion-card-header>
<ion-card-content class="ion-margin">
<ion-progress-bar
color="primary"
color="tertiary"
style="
max-width: 700px;
margin: auto;

View File

@@ -2,10 +2,6 @@
<ion-grid>
<ion-row>
<ion-col>
<div style="padding-bottom: 32px" class="ion-text-center">
<img src="assets/img/logo.png" style="max-width: 240px" />
</div>
<ion-card color="dark">
<ion-card-header class="ion-text-center">
<ion-card-title>Restore from Backup</ion-card-title>
@@ -33,7 +29,6 @@
slot="start"
name="folder-open-outline"
size="large"
color="light"
></ion-icon>
<ion-label>
<b>Open</b>
@@ -71,7 +66,6 @@
slot="start"
name="save-outline"
size="large"
color="light"
></ion-icon>
<ion-label>
<h1>{{ drive.label || drive.logicalname }}</h1>
@@ -88,7 +82,6 @@
</ng-container>
<ion-button
class="ion-margin-top"
fill="clear"
color="primary"
(click)="refresh()"
>

View File

@@ -1,4 +1,5 @@
.target-label {
font-weight: bold;
font-weight: 500;
padding-bottom: 6px;
}
font-variant-caps: all-small-caps;
}

View File

@@ -1,269 +1,232 @@
<ion-content [scrollEvents]="true" (ionScrollEnd)="checkBottom()">
<ion-grid>
<ion-row>
<ion-col>
<!-- kiosk mode -->
<ng-container *ngIf="isKiosk; else notKiosk">
<ion-card color="dark">
<ion-card-header class="ion-text-center" color="success">
<ion-icon
style="font-size: 80px"
name="checkmark-circle-outline"
></ion-icon>
<ion-card-title>Setup Complete</ion-card-title>
<ion-card-subtitle
><b>You will be redirected momentarily</b></ion-card-subtitle
<canvas #canvas> Your browser does not support the canvas element. </canvas>
<ion-content>
<div class="grid-center-wrapper">
<ion-grid [fixed]="true">
<!-- kiosk mode -->
<ng-container *ngIf="isKiosk; else notKiosk">
<ion-card>
<ion-row>
<ion-col size-xs="12" class="ion-text-center">
<div class="inline">
<ion-icon
name="checkmark-circle-outline"
color="success"
></ion-icon>
<h1>Setup Complete!</h1>
</div>
<h3 class="ion-padding-top">
You will be redirected momentarily.
</h3>
</ion-col>
</ion-row>
</ion-card>
</ng-container>
<!-- not kiosk -->
<ng-template #notKiosk>
<ion-card>
<ion-row>
<ion-col size-xs="12" class="ion-text-center">
<div style="margin-bottom: 4rem">
<div class="inline">
<ion-icon
name="checkmark-circle-outline"
color="success"
></ion-icon>
<h1>Setup Complete!</h1>
</div>
<h3 *ngIf="recoverySource && recoverySource.type === 'backup'">
You can now safely unplug your backup drive
</h3>
<h3 *ngIf="recoverySource && recoverySource.type === 'migrate'">
You can now safely unplug your old drive
</h3>
</div>
<div class="card-container">
<ion-card id="information" (click)="download()">
<ion-card-content>
<ion-card-title
>Download your Embassy's address information for permanent
access</ion-card-title
>
<p>
<code>embassy.local</code> was for setup only. It will no
longer work.
</p>
</ion-card-content>
<ion-footer>
<div class="container">
<div class="inline">
<p>Download</p>
<ion-icon name="download-outline"></ion-icon>
</div>
</div>
</ion-footer>
</ion-card>
<ion-card id="launch" href="{{ lanAddress }}" target="_blank">
<ion-card-content>
<ion-card-title
>Launch to login to your Embassy</ion-card-title
>
<p class="ion-padding-bottom">
<span class="emphasis-warn">Important!</span><br />
Your browser will warn you that the page is untrusted. You
can safely bypass this warning. It will go away after you
<a
href="https://docs.start9.com/latest/user-manual/connecting/connecting-lan"
target="_blank"
rel="noreferrer"
>
setup a secure connection </a
>.
</p>
</ion-card-content>
<ion-footer>
<div class="container">
<div class="inline">
<p>Launch</p>
<ion-icon name="rocket-outline"></ion-icon>
</div>
</div>
</ion-footer>
</ion-card>
</div>
</ion-col>
</ion-row>
<!-- cert elem -->
<a hidden id="install-cert" download="embassy.crt"></a>
<!-- download elem -->
<div hidden id="downloadable">
<div
style="
font-family: Montserrat;
color: #333333;
display: flex;
flex-direction: column;
margin: auto;
width: clamp(900px, 35vw, 600px);
"
>
<h1
style="
font-variant-caps: all-small-caps;
text-align: center;
padding: 1rem;
"
>
<br />
</ion-card-header>
</ion-card>
</ng-container>
Embassy Address Info
</h1>
<!-- not kiosk -->
<ng-template #notKiosk>
<ion-card color="dark">
<ion-card-header class="ion-text-center" color="success">
<ion-icon
style="font-size: 80px"
name="checkmark-circle-outline"
></ion-icon>
<ion-card-title>Setup Complete</ion-card-title>
<ion-card-subtitle
><b>See below for next steps</b></ion-card-subtitle
<section
style="
padding: 1rem 3rem 2rem 3rem;
border: solid #c4c4c5 3px;
margin-bottom: 24px;
"
>
<br />
</ion-card-header>
<ion-card-content>
<br />
<br />
<h2 *ngIf="recoverySource" class="ion-padding-bottom">
<span *ngIf="recoverySource.type === 'backup'"
>You can now safely unplug your backup drive.</span
>
<span *ngIf="recoverySource.type === 'migrate'"
>You can now safely unplug your old drive.</span
>
</h2>
<h2 style="font-weight: bold">
Access your Embassy using the methods below. You should
<a (click)="download()" class="inline">
download this page
<ion-icon name="download-outline"></ion-icon>
</a>
for your records.
</h2>
<div class="line"></div>
<!-- LAN Instructions -->
<h1><b>From Home (LAN)</b></h1>
<div class="ion-padding ion-text-start">
<h2 style="font-variant-caps: all-small-caps">
Access from home (LAN)
</h2>
<p>
Visit the address below when you are connected to the same
WiFi or Local Area Network (LAN) as your Embassy:
</p>
<br />
<p>
<b>Note:</b> embassy.local was for setup purposes only, it
will no longer work.
</p>
<ion-item
lines="none"
color="dark"
class="ion-padding-top ion-padding-bottom"
<p
style="
padding: 16px;
font-weight: bold;
font-size: 1.1rem;
overflow: auto;
"
>
<ion-label class="ion-text-wrap">
<code
><ion-text color="light"
><b>{{ lanAddress }}</b></ion-text
></code
<code id="lan-addr"></code>
</p>
<div>
<h3 style="color: #f8546a; font-weight: bold">Important!</h3>
<p>
Your browser will warn you that the page is untrusted. You
can safely bypass this warning. It will go away after you
<a
href="https://docs.start9.com/latest/user-manual/connecting/connecting-lan"
target="_blank"
rel="noreferrer"
style="
color: #6866cc;
font-weight: bold;
text-decoration: none;
"
>
</ion-label>
<ion-button
color="light"
fill="clear"
[href]="lanAddress"
target="_blank"
>
<ion-icon slot="icon-only" name="open-outline"></ion-icon>
</ion-button>
<ion-button
color="light"
fill="clear"
(click)="copy(lanAddress)"
>
<ion-icon slot="icon-only" name="copy-outline"></ion-icon>
</ion-button>
</ion-item>
follow the instructions
</a>
to download and trust your Embassy's Root Certificate
Authority.
</p>
</div>
<p>
<b>Important!</b>
Your browser will warn you that the website is untrusted. You
can bypass this warning on most browsers. The warning will go
away after you
<a
href="https://docs.start9.com/latest/user-manual/connecting/connecting-lan"
target="_blank"
rel="noreferrer"
class="inline"
>
follow the instructions
<ion-icon name="open-outline"></ion-icon>
</a>
to download and trust your Embassy's Root Certificate
Authority.
</p>
<ion-button style="margin-top: 24px" (click)="installCert()">
Download Root CA
<ion-icon slot="end" name="download-outline"></ion-icon>
</ion-button>
</div>
<div class="line"></div>
<!-- Tor Instructions -->
<h1><b>On The Go (Tor)</b></h1>
<div class="ion-padding ion-text-start">
<p>Visit the address below when you are away from home:</p>
<ion-item
lines="none"
color="dark"
class="ion-padding-top ion-padding-bottom"
>
<ion-label class="ion-text-wrap">
<code
><ion-text color="light"
><b>{{ torAddress }}</b></ion-text
></code
>
</ion-label>
<ion-button
color="light"
fill="clear"
(click)="copy(torAddress)"
>
<ion-icon slot="icon-only" name="copy-outline"></ion-icon>
</ion-button>
</ion-item>
<p>
<b>Important!</b>
This address will only work from a
<a
href="https://docs.start9.com/latest/user-manual/connecting/connecting-tor"
target="_blank"
rel="noreferrer"
class="inline"
>
Tor-enabled browser
<ion-icon name="open-outline"></ion-icon> </a
>.
</p>
</div>
</ion-card-content>
<div id="bottom-div"></div>
</ion-card>
<!-- scroll down -->
<div
[ngStyle]="{
position: 'fixed',
bottom: isOnBottom ? '-42px' : '24px',
transition: 'bottom 0.15s ease-out 0s',
right: '50%',
width: '120px',
'margin-right': '-60px',
'z-index': '1000'
}"
>
<ion-button color="warning" (click)="scrollToBottom()">
More
<ion-icon slot="end" name="chevron-down"></ion-icon>
</ion-button>
</div>
<!-- cert elem -->
<a hidden id="install-cert" download="embassy.crt"></a>
<!-- download elem -->
<div hidden id="downloadable">
<div style="padding: 0 24px; font-family: Courier">
<h1>Embassy Info</h1>
<section style="padding: 16px; border: solid 1px">
<h2>Tor Info</h2>
<p>
To use your Embassy over Tor, visit its unique Tor address
from any Tor-enabled browser.
</p>
<p>
For more detailed instructions, click
<a
href="https://docs.start9.com/latest/user-manual/connecting/connecting-tor"
target="_blank"
rel="noreferrer"
><b>here</b></a
>.
</p>
<p><b>Tor Address: </b><code id="tor-addr"></code></p>
</section>
<section
style="padding: 16px; border: solid 1px; border-top: none"
>
<h2>LAN Info</h2>
<p>To use your Embassy locally, you must:</p>
<ol>
<li>
Currently be connected to the same Local Area Network (LAN)
as your Embassy.
</li>
<li>Download your Embassy's Root Certificate Authority.</li>
<li>
Trust your Embassy's Root CA on <i>both</i> your
computer/phone and in your browser settings.
</li>
</ol>
<p>
For step-by-step instructions, click
<a
href="https://docs.start9.com/latest/user-manual/connecting/connecting-lan"
target="_blank"
rel="noreferrer"
><b>here</b></a
>.
</p>
<div style="margin: 42px 0">
<div style="padding: 2rem; text-align: center">
<a
id="cert"
download="embassy.crt"
style="
background: #25272b;
padding: 10px;
display: inline-block;
padding: 1em 1.2em;
box-sizing: border-box;
font-size: 1rem;
text-decoration: none;
text-align: center;
border-radius: 4px;
color: white;
border-radius: clamp(2rem, 3rem, 4rem);
cursor: pointer;
box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 6px -1px,
rgba(0, 0, 0, 0.06) 0px 2px 4px -1px;
background: #6866cc;
color: #f4f4f5;
"
>
Download Root CA
</a>
</div>
</section>
<p><b>LAN Address: </b><code id="lan-addr"></code></p>
<section
style="padding: 1rem 3rem 2rem 3rem; border: solid #c4c4c5 3px"
>
<h2 style="font-variant-caps: all-small-caps">
Access on the go (Tor)
</h2>
<p>Visit the address below when you are away from home:</p>
<p
style="
padding: 16px;
font-weight: bold;
font-size: 1.1rem;
overflow: auto;
"
>
<code id="tor-addr"></code>
</p>
<div>
<h3 style="color: #f8546a; font-weight: bold">Important!</h3>
<p>
This address will only work from a Tor-enabled browser.
<a
href="https://docs.start9.com/latest/user-manual/connecting/connecting-tor"
target="_blank"
rel="noreferrer"
style="
color: #6866cc;
font-weight: bold;
text-decoration: none;
"
>
Follow the instructions
</a>
to get setup.
</p>
</div>
</section>
</div>
</div>
</ng-template>
</ion-col>
</ion-row>
</ion-grid>
</ion-card>
</ng-template>
</ion-grid>
</div>
</ion-content>

View File

@@ -1,15 +1,149 @@
p {
color: var(--ion-color-light);
canvas {
position: fixed;
left: 0;
top: 0;
z-index: -1;
}
a {
text-decoration: none;
font-weight: bold;
cursor: pointer;
h1 {
font-variant: all-small-caps;
margin: unset;
}
.line {
margin-bottom: 48px;
padding-bottom: 48px;
border-bottom: solid 1px;
ion-content {
position: absolute;
z-index: 0;
--background: transparent;
}
ion-grid {
padding-top: 2rem;
max-width: 760px;
margin: auto;
}
.grid-center-wrapper {
height: 100%;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
ion-card {
padding: 3rem;
h1 {
color: var(--ion-color-success);
padding-left: 0.5rem;
}
ion-icon {
font-size: 40px;
}
li {
margin-bottom: 2rem;
}
ion-card {
max-width: 91%;
background: #615F5F;
color: var(--ion-text-color);
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);
border-radius: 44px;
margin: auto;
text-align: left;
cursor: pointer;
position: relative;
padding: 1rem 2rem;
transition: all 350ms ease;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
&:hover {
transition-property: transform;
transform: scale(1.05);
transition-delay: 40ms;
}
ion-card-title {
color: var(--ion-text-color);
font-size: 1.3rem;
}
ion-card-content {
padding-bottom: 4rem;
p {
padding: 1rem 0;
}
}
p {
a {
text-decoration: none;
font-weight: bold;
color: var(--ion-text-color);
}
}
ion-footer {
position: absolute;
bottom: 10px;
left: 0;
color: var(--ion-text-color);
p {
font-size: 1.1rem;
font-weight: bold;
margin: unset;
}
ion-icon {
font-size: 1.6rem;
}
.container {
display: flex;
justify-content: center;
align-items: center;
}
}
.footer-md::before {
background-image: none;
}
}
#launch:after {
content: '';
position: absolute;
left: 0;
top: 80%;
width: 100%;
height: 100%;
background: var(--alt-blue);
}
#information:after {
content: '';
position: absolute;
left: 0;
top: 77%;
width: 100%;
height: 100%;
background: var(--color-accent);
}
}
.card-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 2rem;
}
.emphasis-warn {
font-weight: 600;
color: var(--ion-color-warning);
}

View File

@@ -1,8 +1,10 @@
import { DOCUMENT } from '@angular/common'
import {
Component,
ElementRef,
EventEmitter,
Inject,
NgZone,
Output,
ViewChild,
} from '@angular/core'
@@ -24,15 +26,23 @@ import { StateService } from 'src/app/services/state.service'
export class SuccessPage {
@ViewChild(IonContent)
private content?: IonContent
@ViewChild('canvas', { static: true })
private canvas: ElementRef<HTMLCanvasElement> = {} as ElementRef<HTMLCanvasElement>
private ctx: CanvasRenderingContext2D = {} as CanvasRenderingContext2D
@Output() onDownload = new EventEmitter()
torAddress = ''
lanAddress = ''
cert = ''
isOnBottom = true
tileSize = 16
// a higher fade factor will make the characters fade quicker
fadeFactor = 0.07
columns: any[] = []
maxStackHeight: any
constructor(
@Inject(DOCUMENT) private readonly document: Document,
private readonly toastCtrl: ToastController,
@@ -40,6 +50,7 @@ export class SuccessPage {
private readonly stateService: StateService,
private api: ApiService,
private readonly downloadHtml: DownloadHTMLService,
private ngZone: NgZone,
) {}
get recoverySource() {
@@ -51,6 +62,7 @@ export class SuccessPage {
}
async ngAfterViewInit() {
this.ngZone.runOutsideAngular(() => this.initMatrix())
try {
const ret = await this.api.complete()
if (!this.isKiosk) {
@@ -67,7 +79,6 @@ export class SuccessPage {
'data:application/x-x509-ca-cert;base64,' +
encodeURIComponent(this.cert),
)
this.download()
}
await this.api.exit()
} catch (e: any) {
@@ -118,7 +129,57 @@ export class SuccessPage {
bottomDiv.getBoundingClientRect().top - 192 < window.innerHeight
}
scrollToBottom() {
this.content?.scrollToBottom(250)
initMatrix() {
this.ctx = this.canvas.nativeElement.getContext('2d')!
this.canvas.nativeElement.width = window.innerWidth
this.canvas.nativeElement.height = window.innerHeight
this.setupMatrixGrid()
this.tick()
}
setupMatrixGrid() {
this.maxStackHeight = Math.ceil(this.ctx.canvas.height / this.tileSize)
// divide the canvas into columns
for (let i = 0; i < this.ctx.canvas.width / this.tileSize; ++i) {
const column = {} as any
// save the x position of the column
column.x = i * this.tileSize
// create a random stack height for the column
column.stackHeight = 10 + Math.random() * this.maxStackHeight
// add a counter to count the stack height
column.stackCounter = 0
// add the column to the list
this.columns.push(column)
}
}
draw() {
// draw a semi transparent black rectangle on top of the scene to slowly fade older characters
this.ctx.fillStyle = 'rgba( 0 , 0 , 0 , ' + this.fadeFactor + ' )'
this.ctx.fillRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height)
// pick a font slightly smaller than the tile size
this.ctx.font = this.tileSize - 2 + 'px monospace'
this.ctx.fillStyle = '#ff4961'
for (let i = 0; i < this.columns.length; ++i) {
// pick a random ascii character (change the 94 to a higher number to include more characters)
const randomCharacter = String.fromCharCode(
33 + Math.floor(Math.random() * 94),
)
this.ctx.fillText(
randomCharacter,
this.columns[i].x,
this.columns[i].stackCounter * this.tileSize + this.tileSize,
)
// if the stack is at its height limit, pick a new random height and reset the counter
if (++this.columns[i].stackCounter >= this.columns[i].stackHeight) {
this.columns[i].stackHeight = 10 + Math.random() * this.maxStackHeight
this.columns[i].stackCounter = 0
}
}
}
tick() {
this.draw()
setTimeout(this.tick.bind(this), 50)
}
}

View File

@@ -2,17 +2,14 @@
<ion-grid>
<ion-row>
<ion-col>
<div style="padding-bottom: 32px" class="ion-text-center">
<img src="assets/img/logo.png" style="max-width: 240px" />
</div>
<ion-card color="dark">
<ion-card-header class="ion-text-center">
<ion-card-title>Transfer</ion-card-title>
<ion-card-subtitle
>Select the physical drive containing your Embassy
data</ion-card-subtitle
>
<div class="center-wrapper">
<ion-card-subtitle>
Select the physical drive containing your Embassy data
</ion-card-subtitle>
</div>
</ion-card-header>
<ion-card-content class="ion-margin">
@@ -25,8 +22,6 @@
<!-- loaded -->
<ion-item-group *ngIf="!loading" class="ion-text-center">
<!-- drives -->
<h2 class="target-label">Available Drives</h2>
<ng-container *ngFor="let drive of drives">
<ion-item
*ngIf="drive | guid as guid"
@@ -38,7 +33,6 @@
slot="start"
name="save-outline"
size="large"
color="light"
></ion-icon>
<ion-label>
<h1>{{ drive.logicalname }}</h1>
@@ -52,7 +46,6 @@
</ng-container>
<ion-button
class="ion-margin-top"
fill="clear"
color="primary"
(click)="refresh()"
>

View File

@@ -1,4 +0,0 @@
.target-label {
font-weight: bold;
padding-bottom: 6px;
}

View File

@@ -44,7 +44,7 @@ export class TransferPage {
const alert = await this.alertCtrl.create({
header: 'Warning',
message:
'Data from this drive will not be deleted, but you will not be able to use this drive to run embassyOS after the data is transferred. Attempting to use this drive after data is transferred could cause transferred services to not function properly, and may cause data corruption.',
'After transferring data from this drive, <b>do not</b> use it again as an Embassy. This may result in services malfunctioning, data corruption, or loss of funds.',
buttons: [
{
role: 'cancel',

View File

@@ -12,6 +12,27 @@
src: url('/assets/fonts/Montserrat/Montserrat-Bold.ttf');
}
@font-face {
font-family: 'Montserrat';
font-style: normal;
font-weight: 600;
src: url('/assets/fonts/Montserrat/Montserrat-SemiBold.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: 500;
src: url('/assets/fonts/Montserrat/Montserrat-Medium.ttf');
}
@font-face {
font-family: 'Montserrat';
font-style: normal;
@@ -26,41 +47,92 @@
src: url('/assets/fonts/Benton_Sans/BentonSans-Regular.otf');
}
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: normal;
src: url('/assets/fonts/Open_Sans/OpenSans-Regular.ttf');
}
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: bold;
src: url('/assets/fonts/Open_Sans/OpenSans-Bold.ttf');
}
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 600;
src: url('/assets/fonts/Open_Sans/OpenSans-SemiBold.ttf');
}
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: thin;
src: url('/assets/fonts/Open_Sans/OpenSans-Light.ttf');
}
/** Ionic CSS Variables overrides **/
:root {
--ion-font-family: 'Benton Sans';
--ion-font-family: 'Montserrat';
--ion-background-color: #333333;
--ion-background-color-rgb: 51, 51, 51;
--ion-text-color: #F4F4F5;
--ion-text-color-rgb: 244, 244, 245;
--ion-color-step-50: #3d3d3d;
--ion-color-step-100: #464646;
--ion-color-step-150: #505050;
--ion-color-step-200: #5a5a5a;
--ion-color-step-250: #636364;
--ion-color-step-300: #6d6d6d;
--ion-color-step-350: #777777;
--ion-color-step-400: #808081;
--ion-color-step-450: #8a8a8a;
--ion-color-step-500: #949494;
--ion-color-step-550: #9d9d9e;
--ion-color-step-600: #a7a7a7;
--ion-color-step-650: #b0b0b1;
--ion-color-step-700: #bababb;
--ion-color-step-750: #c4c4c5;
--ion-color-step-800: #cdcdce;
--ion-color-step-850: #d7d7d8;
--ion-color-step-900: #e1e1e2;
--ion-color-step-950: #eaeaeb;
--ion-color-dark: var(--ion-color-step-50) !important;
// --ion-color-base-rgb:
--ion-color-dark-contrast: var(--ion-color-step-950) !important;
// --ion-color-dark-contrast-rgb:
--ion-color-dark-shade: var(--ion-color-step-100) !important;
--ion-color-dark-tint: var(--ion-color-step-250) !important;
--color-accent: #6866cc;
--color-dark-black: #121212;
--alt-red: #FF4961;
--alt-orange: #F89248;
--alt-yellow: #E5D53E;
--alt-green: #3DCF6F;
--alt-blue: #00A8A8;
--alt-purple: #9747FF;
}
ion-content {
--background: var(--ion-color-medium);
h1,
h2,
h3,
h4 {
font-weight: 400;
}
ion-grid {
padding-top: 32px;
height: 100%;
max-width: 640px;
}
ion-row {
height: 100%;
}
ion-item {
--color: var(--ion-color-light);
--highlight-color-valid: transparent;
--highlight-color-invalid: transparent;
}
ion-toolbar {
--ion-background-color: var(--ion-color-light);
ion-title {
color: var(--ion-color-dark);
}
}
ion-avatar {
width: 27px;
height: 27px;
h1 {
font-size: 42px;
}
ion-card-title {
@@ -70,6 +142,58 @@ ion-card-title {
--color: var(--ion-color-light);
}
ion-card-subtitle {
font-size: 20px;
font-weight: 200;
max-width: 400px;
padding: 0.7rem;
color: var(--ion-color-step-900) !important;
}
ion-label ion-text {
font-size: 1.2rem;
font-weight: 500;
}
p {
color: var(--ion-color-dark-contrast) !important;
font-size: 1.12rem !important;
font-family: 'Open Sans';
font-weight: normal;
}
ion-icon {
color: var(--ion-color-dark-contrast) !important;
}
.small-caps {
font-variant-caps: all-small-caps;
}
ion-grid {
padding-top: 32px;
height: 100%;
max-width: 695px;
}
ion-row {
height: 100%;
}
ion-card {
border-radius: 31px;
}
ion-item {
--highlight-color-valid: transparent;
--highlight-color-invalid: transparent;
}
ion-avatar {
width: 27px;
height: 27px;
}
ion-toast {
--background: var(--ion-color-light);
--button-color: var(--ion-color-dark);
@@ -79,7 +203,7 @@ ion-toast {
}
.center-spinner {
height: 20vh;
height: 6vh;
width: 100%;
}
@@ -87,6 +211,7 @@ ion-toast {
* {
display: inline-block;
vertical-align: middle;
padding: 0.3rem;
}
}
@@ -116,3 +241,97 @@ ion-toast {
border: 2px solid var(--ion-color-success);
border-radius: 4px;
}
ion-modal.stack-modal {
--box-shadow: 0 12px 24px rgba(0, 0, 0, 0.2);
--backdrop-opacity: var(--ion-backdrop-opacity, 0.32);
}
.sc-ion-label-md-s p {
line-height: 23px;
}
ion-button {
--padding-top: 1.3rem;
--padding-bottom: 1.3rem;
}
ion-item {
border: var(--ion-color-step-750) 1px solid;
margin: 2rem;
--background: transparent;
--border-color: var(--ion-item-border-color, var(--ion-border-color, var(--ion-color-step-150, rgba(0, 0, 0, 0.13))));
transition: all 350ms ease;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
&:hover {
transition-property: transform;
transform: scale(1.05);
transition-delay: 40ms;
}
ion-button {
--padding-top: unset;
--padding-bottom: unset;
}
}
.item.sc-ion-label-md-h,
.item .sc-ion-label-md-h {
white-space: normal;
}
.center-wrapper {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
}
.loader {
--spinner-color: var(--ion-color-tertiary) !important;
}
.toolbar-background {
background: #2a2a2a !important;
}
.toolbar-container {
padding-right: 2rem !important;
}
ion-header {
ion-toolbar {
--border-color: var(--ion-color-step-950);
--border-width: 0 0 1px 0;
--min-height: 80px;
--padding-top: 20px;
--padding-bottom: 20px;
--padding-end: 2rem;
}
}
ion-footer {
ion-toolbar {
--border-width: 0;
--padding-end: 2.3rem;
--padding-bottom: 2rem;
}
}
.footer-md::before {
content: none;
}
@media (max-width: 500px) {
h1 {
font-size: 36px;
}
ion-item {
margin: 0 0.5rem 2rem 0.5rem;
}
}

View File

@@ -34,7 +34,7 @@ ion-modal::part(content) {
border-radius: 6px;
border: 2px solid rgba(255, 255, 255, 0.03);
box-shadow: 0 0 70px 70px black;
box-shadow: 0 32px 64px rgba(0, 0, 0, 0.2);
}
.alertlike-modal {