setup wizard first draft

This commit is contained in:
Drew Ansbacher
2021-06-29 17:02:11 -06:00
committed by Aiden McClelland
parent ce514646ff
commit b31d15bac5
43 changed files with 33533 additions and 0 deletions

View File

@@ -0,0 +1,30 @@
import { NgModule } from '@angular/core';
import { PreloadAllModules, RouterModule, Routes } from '@angular/router';
const routes: Routes = [
{
path: 'wizard',
loadChildren: () => import('./pages/home/home.module').then( m => m.HomePageModule)
},
{
path: 'recover',
loadChildren: () => import('./pages/recover/recover.module').then( m => m.RecoverPageModule)
},
{
path: 'password',
loadChildren: () => import('./pages/password/password.module').then( m => m.PasswordPageModule)
},
{
path: '',
redirectTo: 'wizard',
pathMatch: 'full'
},
];
@NgModule({
imports: [
RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules })
],
exports: [RouterModule]
})
export class AppRoutingModule { }

View File

@@ -0,0 +1,11 @@
<ion-app>
<ion-content *ngIf="stateService.loading; else loaded">
<h2 color="light">Connecting to Embassy</h2>
</ion-content>
<ng-template #loaded>
<ion-content *ngIf="error">
<h2 color="light">{{ error }}</h2>
</ion-content>
<ion-router-outlet *ngIf="!error"></ion-router-outlet>
</ng-template>
</ion-app>

View File

View File

@@ -0,0 +1,51 @@
import { Component } from '@angular/core';
import { NavController, ToastController } from '@ionic/angular';
import { ApiService } from './services/api/api.service';
import { StateService } from './services/state.sevice';
@Component({
selector: 'app-root',
templateUrl: 'app.component.html',
styleUrls: ['app.component.scss'],
})
export class AppComponent {
constructor(
private readonly apiService: ApiService,
private readonly stateService: StateService,
private readonly navCtrl: NavController,
public toastController: ToastController
) {
this.apiService.watchError$.subscribe(error => {
if(error) {
this.presentToast(error)
}
})
}
async ngOnInit() {
await this.stateService.getState()
if(this.stateService.hasPassword) {
//redirect to embassyOS
} else if (this.stateService.recoveryDrive) {
await this.navCtrl.navigateForward(`/recover`)
}
}
async presentToast(error: string) {
const toast = await this.toastController.create({
header: 'Error',
message: error,
position: 'bottom',
buttons: [
{
text: 'X',
role: 'cancel',
}
]
});
await toast.present();
await toast.onDidDismiss();
}
}

View File

@@ -0,0 +1,33 @@
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouteReuseStrategy } from '@angular/router';
import { HttpClientModule } from '@angular/common/http';
import { ApiService } from './services/api/api.service'
import { MockApiService } from './services/api/mock-api.service'
import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
import * as config from './config/config'
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
@NgModule({
declarations: [AppComponent],
entryComponents: [],
imports: [
BrowserModule,
IonicModule.forRoot(),
AppRoutingModule,
HttpClientModule,
],
providers: [
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
{
provide: ApiService ,
useFactory: () => {
return new MockApiService()
},
},
],
bootstrap: [AppComponent],
})
export class AppModule {}

View File

@@ -0,0 +1,7 @@
export const config: Config = {
"useMocks": true
}
interface Config {
useMocks: boolean
}

View File

@@ -0,0 +1,16 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HomePage } from './home.page';
const routes: Routes = [
{
path: '',
component: HomePage,
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class HomePageRoutingModule {}

View File

@@ -0,0 +1,19 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { IonicModule } from '@ionic/angular';
import { FormsModule } from '@angular/forms';
import { HomePage } from './home.page';
import { HomePageRoutingModule } from './home-routing.module';
@NgModule({
imports: [
CommonModule,
FormsModule,
IonicModule,
HomePageRoutingModule
],
declarations: [HomePage]
})
export class HomePageModule {}

View File

@@ -0,0 +1,52 @@
<ion-header [translucent]="true">
<ion-toolbar>
<ion-title>
Setup Wizard
</ion-title>
</ion-toolbar>
</ion-header>
<ion-content [fullscreen]="true">
<div *ngIf="!stateService.selectedDataDrive">
<h2 color="light">Select Data Drive</h2>
<ion-card
*ngFor="let drive of dataDrives"
(click)="selectDrive(drive['logical-name'])"
button="true"
[class.selected]="selectedDrive === drive['logical-name']"
color="light"
>
<ion-card-header>
<ion-card-title>{{drive["logical-name"]}}</ion-card-title>
<ion-card-subtitle>{{drive.labels}}</ion-card-subtitle>
</ion-card-header>
<ion-card-content>
Currently using {{drive.used}} out of {{drive.capacity}} bytes.
</ion-card-content>
</ion-card>
<ion-button (click)="warn()" color="primary" [disabled]="!selectedDrive">Next</ion-button>
</div>
<div *ngIf="stateService.selectedDataDrive">
<ion-card
[routerLink]="['/password']"
button="true"
color="light"
>
<ion-card-header>
<ion-card-title>Start Fresh</ion-card-title>
<ion-card-subtitle>Get started with a brand new Embassy</ion-card-subtitle>
</ion-card-header>
</ion-card>
<ion-card
[routerLink]="['/recover']"
button="true"
color="light"
>
<ion-card-header>
<ion-card-title>Recover</ion-card-title>
<ion-card-subtitle>Recover the data from an old embassy</ion-card-subtitle>
</ion-card-header>
</ion-card>
</div>
</ion-content>

View File

@@ -0,0 +1,31 @@
#container {
text-align: center;
position: absolute;
left: 0;
right: 0;
top: 50%;
transform: translateY(-50%);
}
#container strong {
font-size: 20px;
line-height: 26px;
}
#container p {
font-size: 16px;
line-height: 22px;
color: #8c8c8c;
margin: 0;
}
#container a {
text-decoration: none;
}
.selected {
border: 1px solid white;
}

View File

@@ -0,0 +1,66 @@
import { Component } from '@angular/core'
import { AlertController, NavController } from '@ionic/angular'
import { ApiService } from 'src/app/services/api/api.service'
import { StateService } from 'src/app/services/state.sevice'
@Component({
selector: 'app-home',
templateUrl: 'home.page.html',
styleUrls: ['home.page.scss'],
})
export class HomePage {
dataDrives = []
selectedDrive = null
constructor(
private readonly apiService: ApiService,
private readonly stateService: StateService,
public alertController: AlertController,
private readonly navCtrl: NavController,
) {}
async ngOnInit() {
if(!this.stateService.selectedDataDrive) {
this.dataDrives = await this.apiService.getStorageDisks()
}
}
selectDrive(name: string) {
if (name === this.selectedDrive) {
this.selectedDrive = null
} else {
this.selectedDrive = name
}
}
async warn() {
const alert = await this.alertController.create({
cssClass: 'my-custom-class',
header: 'Warning!',
message: 'This disk will be entirely wiped of all memory.',
buttons: [
{
text: 'Cancel',
role: 'cancel',
cssClass: 'secondary',
handler: () => {
this.selectedDrive = null
}
}, {
text: 'Okay',
handler: async () => {
await this.chooseDisk()
}
}
]
});
await alert.present();
}
async chooseDisk() {
await this.apiService.selectStorageDisk({logicalName: this.selectedDrive})
this.stateService.selectedDataDrive = this.selectedDrive
}
}

View File

@@ -0,0 +1,16 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { PasswordPage } from './password.page';
const routes: Routes = [
{
path: '',
component: PasswordPage,
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class PasswordPageRoutingModule {}

View File

@@ -0,0 +1,19 @@
import { NgModule } from '@angular/core'
import { CommonModule } from '@angular/common'
import { IonicModule } from '@ionic/angular'
import { FormsModule } from '@angular/forms'
import { PasswordPage } from './password.page'
import { PasswordPageRoutingModule } from './password-routing.module'
@NgModule({
imports: [
CommonModule,
FormsModule,
IonicModule,
PasswordPageRoutingModule
],
declarations: [PasswordPage]
})
export class PasswordPageModule {}

View File

@@ -0,0 +1,20 @@
<ion-header [translucent]="true">
<ion-toolbar>
<ion-title>
Setup Wizard
</ion-title>
</ion-toolbar>
</ion-header>
<ion-content [fullscreen]="true" color="light">
<div *ngIf="stateService.recoveryDrive">
<h2>Enter Password</h2>
<ion-input type="password" [(ngModel)]="password">Password:</ion-input>
</div>
<div *ngIf="!stateService.recoveryDrive">
<h2>Create Password</h2>
<ion-input type="password" [(ngModel)]="password">Password:</ion-input>
<ion-input type="password" [(ngModel)]="passwordVer">Verify Password:</ion-input>
</div>
<p>{{error}}</p>
<ion-button (click)="submitPassword()" [disabled]="!password">Submit</ion-button>
</ion-content>

View File

@@ -0,0 +1,31 @@
#container {
text-align: center;
position: absolute;
left: 0;
right: 0;
top: 50%;
transform: translateY(-50%);
}
#container strong {
font-size: 20px;
line-height: 26px;
}
#container p {
font-size: 16px;
line-height: 22px;
color: #8c8c8c;
margin: 0;
}
#container a {
text-decoration: none;
}
.selected {
border: 1px solid white;
}

View File

@@ -0,0 +1,33 @@
import { Component } from '@angular/core'
import { AlertController, NavController } from '@ionic/angular'
import { ApiService } from 'src/app/services/api/api.service'
import { StateService } from 'src/app/services/state.sevice'
@Component({
selector: 'password-page',
templateUrl: 'password.page.html',
styleUrls: ['password.page.scss'],
})
export class PasswordPage {
error = ''
password = ''
passwordVer = ''
constructor(
private readonly apiService: ApiService,
private readonly stateService: StateService,
public alertController: AlertController,
) {}
async submitPassword () {
if (!this.stateService.recoveryDrive && this.password !== this.passwordVer) {
console.log('here')
this.error="*passwords dont match"
} else {
console.log('submitting')
await this.apiService.submitPassword(this.password)
// @Todo navigate to embassy os
}
}
}

View File

@@ -0,0 +1,16 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { RecoverPage } from './recover.page';
const routes: Routes = [
{
path: '',
component: RecoverPage,
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class RecoverPageRoutingModule {}

View File

@@ -0,0 +1,19 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { IonicModule } from '@ionic/angular';
import { FormsModule } from '@angular/forms';
import { RecoverPage } from './recover.page';
import { RecoverPageRoutingModule } from './recover-routing.module';
@NgModule({
imports: [
CommonModule,
FormsModule,
IonicModule,
RecoverPageRoutingModule
],
declarations: [RecoverPage]
})
export class RecoverPageModule {}

View File

@@ -0,0 +1,40 @@
<ion-header [translucent]="true">
<ion-toolbar>
<ion-title>
Setup Wizard
</ion-title>
</ion-toolbar>
</ion-header>
<ion-content [fullscreen]="true">
<div *ngIf="!stateService.recoveryDrive">
<h2 color="light">Select Data Drive</h2>
<ion-card
*ngFor="let drive of dataDrives"
(click)="selectDrive(drive['logical-name'])"
button="true"
[class.selected]="selectedDrive === drive['logical-name']"
color="light"
>
<ion-card-header>
<ion-card-title>{{drive["logical-name"]}}</ion-card-title>
<ion-card-subtitle>{{drive.name}}</ion-card-subtitle>
</ion-card-header>
<ion-card-content>
Currently running {{drive.version}}
</ion-card-content>
</ion-card>
<ion-button (click)="warn()" color="primary" [disabled]="!selectedDrive">Next</ion-button>
</div>
<div *ngIf="stateService.recoveryDrive">
<h2>Progress: {{ 100 * stateService.dataProgress }}% <ion-spinner *ngIf="stateService.dataProgress != 1"></ion-spinner> </h2>
<ion-progress-bar value="{{stateService.dataProgress}}"></ion-progress-bar>
<ion-button
[routerLink]="['/password']"
color="primary"
[disabled]="stateService.dataProgress != 1"
>
Next
</ion-button>
</div>
</ion-content>

View File

@@ -0,0 +1,31 @@
#container {
text-align: center;
position: absolute;
left: 0;
right: 0;
top: 50%;
transform: translateY(-50%);
}
#container strong {
font-size: 20px;
line-height: 26px;
}
#container p {
font-size: 16px;
line-height: 22px;
color: #8c8c8c;
margin: 0;
}
#container a {
text-decoration: none;
}
.selected {
border: 1px solid white;
}

View File

@@ -0,0 +1,68 @@
import { Component } from '@angular/core'
import { AlertController, NavController } from '@ionic/angular'
import { ApiService } from 'src/app/services/api/api.service'
import { StateService } from 'src/app/services/state.sevice'
@Component({
selector: 'recover-page',
templateUrl: 'recover.page.html',
styleUrls: ['recover.page.scss'],
})
export class RecoverPage {
dataDrives = []
selectedDrive = null
constructor(
private readonly apiService: ApiService,
private readonly stateService: StateService,
public alertController: AlertController,
) {}
async ngOnInit() {
if(!this.stateService.recoveryDrive) {
this.dataDrives = await this.apiService.getEmbassyDrives()
} else {
this.stateService.pollDataTransferProgress()
}
}
selectDrive(name: string) {
if (name === this.selectedDrive) {
this.selectedDrive = null
} else {
this.selectedDrive = name
}
}
async warn() {
const alert = await this.alertController.create({
cssClass: 'my-custom-class',
header: 'Warning!',
message: 'Once you select this the recovery process will begin.',
buttons: [
{
text: 'Cancel',
role: 'cancel',
cssClass: 'secondary',
handler: () => {
this.selectedDrive = null
}
}, {
text: 'Okay',
handler: async () => {
await this.chooseDisk()
}
}
]
});
await alert.present();
}
async chooseDisk() {
await this.apiService.selectEmbassyDrive({logicalName: this.selectedDrive})
this.stateService.recoveryDrive = this.selectedDrive
this.stateService.pollDataTransferProgress()
}
}

View File

@@ -0,0 +1,37 @@
import { Subject } from "rxjs";
export abstract class ApiService {
protected error$: Subject<string> = new Subject()
watchError$ = this.error$.asObservable()
abstract getState (): Promise<State>
abstract getStorageDisks (): Promise<StorageDisk[]>
abstract selectStorageDisk (disk: { logicalName: string }): Promise<void>
abstract getEmbassyDrives (): Promise<EmbassyDrive[]>
abstract selectEmbassyDrive (disk: { logicalName: string }): Promise<void>
abstract getDataTransferProgress () : Promise<TransferProgress>
abstract submitPassword (password: string) : Promise<void>
}
export interface State {
'selected-data-drive': string | null,
'recovery-drive': string | null,
'has-password': boolean,
}
export interface TransferProgress {
'bytes-transfered': number
'total-bytes': number
}
export interface StorageDisk {
"logical-name": string
labels: string[]
capacity: number
used: number
}
export interface EmbassyDrive {
"logical-name": string
version: string
name: string
}

View File

@@ -0,0 +1,77 @@
import { Injectable } from "@angular/core";
import { pauseFor } from "../state.sevice";
import { ApiService } from "./api.service"
@Injectable({
providedIn: 'root'
})
export class MockApiService extends ApiService {
constructor () {
super()
}
async getState () {
await pauseFor(2000)
return {
'selected-data-drive': 'name1',
'recovery-drive': null,
'has-password': false,
'data-transfer-progress': null
}
}
async getDataTransferProgress () {
tries = Math.min(tries + 1, 4)
return {
'bytes-transfered': tries,
'total-bytes': 4
}
}
async getStorageDisks () {
await pauseFor(2000)
return [
{
"logical-name": 'name1',
labels: ['label 1', 'label 2'],
capacity: 1600,
used: 200,
},
{
"logical-name": 'name2',
labels: [],
capacity: 1600,
used: 0,
}
]
}
async selectStorageDisk(disk) {
await pauseFor(200)
return
}
async getEmbassyDrives () {
await pauseFor(2000)
return [
{
"logical-name": 'name1',
version: '0.2.3',
name: 'Embassy 0.2.3'
}
]
}
async selectEmbassyDrive(disk) {
await pauseFor(200)
return
}
async submitPassword(password) {
await pauseFor(200)
return
}
}
let tries = 4

View File

@@ -0,0 +1,51 @@
import { Injectable } from "@angular/core";
import { ApiService } from "./api/api.service"
@Injectable({
providedIn: 'root'
})
export class StateService {
loading = true
selectedDataDrive: string
recoveryDrive: string
hasPassword: Boolean
dataTransferProgress: { bytesTransfered: number, totalBytes: number } | null
dataProgress = 0
constructor (
private readonly apiService: ApiService
) {}
async getState () {
const state = await this.apiService.getState()
if(state) {
this.selectedDataDrive = state['selected-data-drive']
this.recoveryDrive = state['recovery-drive']
this.hasPassword = state['has-password']
this.loading = false
}
}
async pollDataTransferProgress () {
if (
this.dataTransferProgress?.totalBytes &&
this.dataTransferProgress.bytesTransfered === this.dataTransferProgress.totalBytes
) {return }
const progress = await this.apiService.getDataTransferProgress()
this.dataTransferProgress = {
bytesTransfered: progress['bytes-transfered'],
totalBytes: progress['total-bytes']
}
if (this.dataTransferProgress.totalBytes) {
this.dataProgress = this.dataTransferProgress.bytesTransfered / this.dataTransferProgress.totalBytes
}
await pauseFor(7000)
this.pollDataTransferProgress()
}
}
export function pauseFor (ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms))
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 930 B

View File

@@ -0,0 +1 @@
<svg width="350" height="140" xmlns="http://www.w3.org/2000/svg" style="background:#f6f7f9"><g fill="none" fill-rule="evenodd"><path fill="#F04141" style="mix-blend-mode:multiply" d="M61.905-34.23l96.194 54.51-66.982 54.512L22 34.887z"/><circle fill="#10DC60" style="mix-blend-mode:multiply" cx="155.5" cy="135.5" r="57.5"/><path fill="#3880FF" style="mix-blend-mode:multiply" d="M208.538 9.513l84.417 15.392L223.93 93.93z"/><path fill="#FFCE00" style="mix-blend-mode:multiply" d="M268.625 106.557l46.332-26.75 46.332 26.75v53.5l-46.332 26.75-46.332-26.75z"/><circle fill="#7044FF" style="mix-blend-mode:multiply" cx="299.5" cy="9.5" r="38.5"/><rect fill="#11D3EA" style="mix-blend-mode:multiply" transform="rotate(-60 148.47 37.886)" x="143.372" y="-7.056" width="10.196" height="89.884" rx="5.098"/><path d="M-25.389 74.253l84.86 8.107c5.498.525 9.53 5.407 9.004 10.905a10 10 0 0 1-.057.477l-12.36 85.671a10.002 10.002 0 0 1-11.634 8.42l-86.351-15.226c-5.44-.959-9.07-6.145-8.112-11.584l13.851-78.551a10 10 0 0 1 10.799-8.219z" fill="#7044FF" style="mix-blend-mode:multiply"/><circle fill="#0CD1E8" style="mix-blend-mode:multiply" cx="273.5" cy="106.5" r="20.5"/></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,3 @@
export const environment = {
production: true
};

View File

@@ -0,0 +1,16 @@
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: false
};
/*
* For easier debugging in development mode, you can import the following file
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
*
* This import should be commented out in production mode because it will have a negative impact
* on performance if an error is thrown.
*/
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.

View File

@@ -0,0 +1,26 @@
/*
* App Global CSS
* ----------------------------------------------------------------------------
* Put style rules here that you want to apply globally. These styles are for
* the entire app and not just one component. Additionally, this file can be
* used as an entry point to import other CSS/Sass files to be included in the
* output CSS.
* For more information on global stylesheets, visit the documentation:
* https://ionicframework.com/docs/layout/global-stylesheets
*/
/* Core CSS required for Ionic components to work properly */
@import "~@ionic/angular/css/core.css";
/* Basic CSS for apps built with Ionic */
@import "~@ionic/angular/css/normalize.css";
@import "~@ionic/angular/css/structure.css";
@import "~@ionic/angular/css/typography.css";
@import '~@ionic/angular/css/display.css';
/* Optional CSS utils that can be commented out */
@import "~@ionic/angular/css/padding.css";
@import "~@ionic/angular/css/float-elements.css";
@import "~@ionic/angular/css/text-alignment.css";
@import "~@ionic/angular/css/text-transformation.css";
@import "~@ionic/angular/css/flex-utils.css";

View File

@@ -0,0 +1,26 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Setup Wizard</title>
<base href="/" />
<meta name="color-scheme" content="light dark" />
<meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="format-detection" content="telephone=no" />
<meta name="msapplication-tap-highlight" content="no" />
<link rel="icon" type="image/png" href="assets/icon/favicon.png" />
<!-- add to homescreen for ios -->
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
</head>
<body>
<app-root></app-root>
</body>
</html>

12
setup-wizard/src/main.ts Normal file
View File

@@ -0,0 +1,12 @@
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.log(err));

View File

@@ -0,0 +1,65 @@
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/guide/browser-support
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/** IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/**
* Web Animations `@angular/platform-browser/animations`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
*/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
* because those flags need to be set before `zone.js` being loaded, and webpack
* will put import in the top of bundle, so user need to create a separate file
* in this directory (for example: zone-flags.ts), and put the following flags
* into that file, and then add the following code before importing zone.js.
* import './zone-flags';
*
* The flags allowed in zone-flags.ts are listed here.
*
* The following flags will work for all browsers.
*
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*
* (window as any).__Zone_enable_cross_context_check = true;
*
*/
import './zone-flags';
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js/dist/zone'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/

View File

@@ -0,0 +1,236 @@
// Ionic Variables and Theming. For more info, please see:
// http://ionicframework.com/docs/theming/
/** Ionic CSS Variables **/
:root {
/** primary **/
--ion-color-primary: #3880ff;
--ion-color-primary-rgb: 56, 128, 255;
--ion-color-primary-contrast: #ffffff;
--ion-color-primary-contrast-rgb: 255, 255, 255;
--ion-color-primary-shade: #3171e0;
--ion-color-primary-tint: #4c8dff;
/** secondary **/
--ion-color-secondary: #3dc2ff;
--ion-color-secondary-rgb: 61, 194, 255;
--ion-color-secondary-contrast: #ffffff;
--ion-color-secondary-contrast-rgb: 255, 255, 255;
--ion-color-secondary-shade: #36abe0;
--ion-color-secondary-tint: #50c8ff;
/** tertiary **/
--ion-color-tertiary: #5260ff;
--ion-color-tertiary-rgb: 82, 96, 255;
--ion-color-tertiary-contrast: #ffffff;
--ion-color-tertiary-contrast-rgb: 255, 255, 255;
--ion-color-tertiary-shade: #4854e0;
--ion-color-tertiary-tint: #6370ff;
/** success **/
--ion-color-success: #2dd36f;
--ion-color-success-rgb: 45, 211, 111;
--ion-color-success-contrast: #ffffff;
--ion-color-success-contrast-rgb: 255, 255, 255;
--ion-color-success-shade: #28ba62;
--ion-color-success-tint: #42d77d;
/** warning **/
--ion-color-warning: #ffc409;
--ion-color-warning-rgb: 255, 196, 9;
--ion-color-warning-contrast: #000000;
--ion-color-warning-contrast-rgb: 0, 0, 0;
--ion-color-warning-shade: #e0ac08;
--ion-color-warning-tint: #ffca22;
/** danger **/
--ion-color-danger: #eb445a;
--ion-color-danger-rgb: 235, 68, 90;
--ion-color-danger-contrast: #ffffff;
--ion-color-danger-contrast-rgb: 255, 255, 255;
--ion-color-danger-shade: #cf3c4f;
--ion-color-danger-tint: #ed576b;
/** dark **/
--ion-color-dark: #222428;
--ion-color-dark-rgb: 34, 36, 40;
--ion-color-dark-contrast: #ffffff;
--ion-color-dark-contrast-rgb: 255, 255, 255;
--ion-color-dark-shade: #1e2023;
--ion-color-dark-tint: #383a3e;
/** medium **/
--ion-color-medium: #92949c;
--ion-color-medium-rgb: 146, 148, 156;
--ion-color-medium-contrast: #ffffff;
--ion-color-medium-contrast-rgb: 255, 255, 255;
--ion-color-medium-shade: #808289;
--ion-color-medium-tint: #9d9fa6;
/** light **/
--ion-color-light: #f4f5f8;
--ion-color-light-rgb: 244, 245, 248;
--ion-color-light-contrast: #000000;
--ion-color-light-contrast-rgb: 0, 0, 0;
--ion-color-light-shade: #d7d8da;
--ion-color-light-tint: #f5f6f9;
}
@media (prefers-color-scheme: dark) {
/*
* Dark Colors
* -------------------------------------------
*/
body {
--ion-color-primary: #428cff;
--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;
--ion-color-secondary: #50c8ff;
--ion-color-secondary-rgb: 80,200,255;
--ion-color-secondary-contrast: #ffffff;
--ion-color-secondary-contrast-rgb: 255,255,255;
--ion-color-secondary-shade: #46b0e0;
--ion-color-secondary-tint: #62ceff;
--ion-color-tertiary: #6a64ff;
--ion-color-tertiary-rgb: 106,100,255;
--ion-color-tertiary-contrast: #ffffff;
--ion-color-tertiary-contrast-rgb: 255,255,255;
--ion-color-tertiary-shade: #5d58e0;
--ion-color-tertiary-tint: #7974ff;
--ion-color-success: #2fdf75;
--ion-color-success-rgb: 47,223,117;
--ion-color-success-contrast: #000000;
--ion-color-success-contrast-rgb: 0,0,0;
--ion-color-success-shade: #29c467;
--ion-color-success-tint: #44e283;
--ion-color-warning: #ffd534;
--ion-color-warning-rgb: 255,213,52;
--ion-color-warning-contrast: #000000;
--ion-color-warning-contrast-rgb: 0,0,0;
--ion-color-warning-shade: #e0bb2e;
--ion-color-warning-tint: #ffd948;
--ion-color-danger: #ff4961;
--ion-color-danger-rgb: 255,73,97;
--ion-color-danger-contrast: #ffffff;
--ion-color-danger-contrast-rgb: 255,255,255;
--ion-color-danger-shade: #e04055;
--ion-color-danger-tint: #ff5b71;
--ion-color-dark: #f4f5f8;
--ion-color-dark-rgb: 244,245,248;
--ion-color-dark-contrast: #000000;
--ion-color-dark-contrast-rgb: 0,0,0;
--ion-color-dark-shade: #d7d8da;
--ion-color-dark-tint: #f5f6f9;
--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;
--ion-color-light: #222428;
--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;
}
/*
* iOS Dark Theme
* -------------------------------------------
*/
.ios body {
--ion-background-color: #000000;
--ion-background-color-rgb: 0,0,0;
--ion-text-color: #ffffff;
--ion-text-color-rgb: 255,255,255;
--ion-color-step-50: #0d0d0d;
--ion-color-step-100: #1a1a1a;
--ion-color-step-150: #262626;
--ion-color-step-200: #333333;
--ion-color-step-250: #404040;
--ion-color-step-300: #4d4d4d;
--ion-color-step-350: #595959;
--ion-color-step-400: #666666;
--ion-color-step-450: #737373;
--ion-color-step-500: #808080;
--ion-color-step-550: #8c8c8c;
--ion-color-step-600: #999999;
--ion-color-step-650: #a6a6a6;
--ion-color-step-700: #b3b3b3;
--ion-color-step-750: #bfbfbf;
--ion-color-step-800: #cccccc;
--ion-color-step-850: #d9d9d9;
--ion-color-step-900: #e6e6e6;
--ion-color-step-950: #f2f2f2;
--ion-item-background: #000000;
--ion-card-background: #1c1c1d;
}
.ios ion-modal {
--ion-background-color: var(--ion-color-step-100);
--ion-toolbar-background: var(--ion-color-step-150);
--ion-toolbar-border-color: var(--ion-color-step-250);
}
/*
* Material Design Dark Theme
* -------------------------------------------
*/
.md body {
--ion-background-color: #121212;
--ion-background-color-rgb: 18,18,18;
--ion-text-color: #ffffff;
--ion-text-color-rgb: 255,255,255;
--ion-border-color: #222222;
--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;
--ion-item-background: #1e1e1e;
--ion-toolbar-background: #1f1f1f;
--ion-tab-bar-background: #1f1f1f;
--ion-card-background: #1e1e1e;
}
}

View File

@@ -0,0 +1,6 @@
/**
* Prevents Angular change detection from
* running with certain Web Component callbacks
*/
// eslint-disable-next-line no-underscore-dangle
(window as any).__Zone_disable_customElements = true;