mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 10:21:52 +00:00
setup wizard improvements
This commit is contained in:
committed by
Aiden McClelland
parent
db9d18766c
commit
305f3d8bcc
@@ -1,8 +1,7 @@
|
||||
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'
|
||||
import { finalize, take } from 'rxjs/operators'
|
||||
|
||||
@Component({
|
||||
selector: 'app-init',
|
||||
@@ -13,16 +12,14 @@ export class InitPage {
|
||||
progress: Observable<number>
|
||||
|
||||
constructor (
|
||||
private navCtrl: NavController,
|
||||
private readonly navCtrl: NavController,
|
||||
) { }
|
||||
|
||||
ngOnInit () {
|
||||
this.progress = interval(500)
|
||||
.pipe(
|
||||
skip(1),
|
||||
take(100),
|
||||
finalize(async () => {
|
||||
await pauseFor(1000)
|
||||
take(101),
|
||||
finalize(() => {
|
||||
this.navCtrl.navigateForward('/success')
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
<ion-card-content class="ion-margin">
|
||||
<ion-progress-bar color="primary" style="max-width: 700px; margin: auto; padding-bottom: 20px; margin-bottom: 40px;" [value]="stateService.dataProgress"></ion-progress-bar>
|
||||
<p class="ion-text-start">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.</p>
|
||||
</ion-card-content>
|
||||
</ion-card>
|
||||
</ion-col>
|
||||
|
||||
@@ -6,17 +6,17 @@
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content color="medium">
|
||||
<form (ngSubmit)="!!storageDrive ? submitPw() : verifyPw()">
|
||||
<div style="padding: 8px 24px;">
|
||||
<div style="padding-bottom: 16px;">
|
||||
<ng-container *ngIf="!!storageDrive">
|
||||
<p>Choose a password for your Embassy. <i>Make it good. Write it down.</i></p>
|
||||
<p style="color: var(--ion-color-warning);">Losing your password can result in total loss of data.</p>
|
||||
</ng-container>
|
||||
<p *ngIf="!storageDrive">Enter the password that was used to encrypt this drive.</p>
|
||||
</div>
|
||||
|
||||
<ion-content>
|
||||
<div style="padding: 8px 24px;">
|
||||
<div style="padding-bottom: 16px;">
|
||||
<ng-container *ngIf="!!storageDrive">
|
||||
<p>Choose a password for your Embassy. <i>Make it good. Write it down.</i></p>
|
||||
<p style="color: var(--ion-color-warning);">Losing your password can result in total loss of data.</p>
|
||||
</ng-container>
|
||||
<p *ngIf="!storageDrive">Enter the password that was used to encrypt this drive.</p>
|
||||
</div>
|
||||
|
||||
<form (ngSubmit)="!!storageDrive ? submitPw() : verifyPw()">
|
||||
<p class="input-label">
|
||||
Password:
|
||||
</p>
|
||||
@@ -61,9 +61,9 @@
|
||||
<p style="color: var(--ion-color-danger); font-size: x-small;">{{ verError }}</p>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
<input type="submit" style="display: none" />
|
||||
</form>
|
||||
<input type="submit" style="display: none" />
|
||||
</form>
|
||||
</div>
|
||||
</ion-content>
|
||||
|
||||
<ion-footer>
|
||||
|
||||
@@ -9,7 +9,7 @@ import * as argon2 from '@start9labs/argon2'
|
||||
styleUrls: ['password.page.scss'],
|
||||
})
|
||||
export class PasswordPage {
|
||||
@ViewChild('focusInput', { static: false }) elem: IonInput
|
||||
@ViewChild('focusInput') elem: IonInput
|
||||
@Input() recoveryPartition: PartitionInfo
|
||||
@Input() storageDrive: DiskInfo
|
||||
|
||||
|
||||
@@ -12,24 +12,17 @@
|
||||
<div style="padding-bottom: 16px;">
|
||||
<p>Verify the product key for the chosen recovery drive.</p>
|
||||
</div>
|
||||
<ion-item
|
||||
color="dark"
|
||||
[class]="error ? 'error-border' : ''"
|
||||
>
|
||||
<ion-item color="dark">
|
||||
<ion-input
|
||||
#focusInput
|
||||
[(ngModel)]="productKey"
|
||||
[ngModelOptions]="{'standalone': true}"
|
||||
[type]="!unmasked ? 'password' : 'text'"
|
||||
placeholder="Enter Product Key"
|
||||
maxlength="64"
|
||||
maxlength="12"
|
||||
></ion-input>
|
||||
<ion-button fill="clear" color="light" (click)="unmasked = !unmasked">
|
||||
<ion-icon slot="icon-only" [name]="unmasked ? 'eye-off-outline' : 'eye-outline'" size="small"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-item>
|
||||
<p style="color: var(--ion-color-danger);">{{ error }}</p>
|
||||
<div style="height: 16px;">
|
||||
<p style="color: var(--ion-color-danger); font-size: x-small;">{{ error }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<input type="submit" style="display: none" />
|
||||
</form>
|
||||
|
||||
@@ -9,7 +9,7 @@ import { HttpService } from 'src/app/services/api/http.service'
|
||||
styleUrls: ['prod-key-modal.page.scss'],
|
||||
})
|
||||
export class ProdKeyModal {
|
||||
@ViewChild('focusInput', { static: false }) elem: IonInput
|
||||
@ViewChild('focusInput') elem: IonInput
|
||||
@Input() recoveryPartition: PartitionInfo
|
||||
|
||||
error = ''
|
||||
@@ -23,6 +23,10 @@ export class ProdKeyModal {
|
||||
private readonly httpService: HttpService,
|
||||
) { }
|
||||
|
||||
ngAfterViewInit () {
|
||||
setTimeout(() => this.elem.setFocus(), 400)
|
||||
}
|
||||
|
||||
async verifyProductKey () {
|
||||
if (!this.productKey) return
|
||||
|
||||
|
||||
@@ -18,7 +18,14 @@
|
||||
<p class="input-label">Product Key</p>
|
||||
<ion-item color="dark">
|
||||
<ion-icon slot="start" name="key-outline" style="margin-right: 16px;"></ion-icon>
|
||||
<ion-input #focusInput color="medium" maxlength="12" name="productKey" [(ngModel)]="productKey" (ionChange)="error = ''"></ion-input>
|
||||
<ion-input
|
||||
#focusInput
|
||||
name="productKey"
|
||||
[(ngModel)]="productKey"
|
||||
(ionChange)="error = ''"
|
||||
maxlength="12"
|
||||
>
|
||||
</ion-input>
|
||||
</ion-item>
|
||||
<div class="ion-text-left">
|
||||
<p *ngIf="error" style="padding-top: 4px"><ion-text color="danger">{{ error }}</ion-text></p>
|
||||
|
||||
@@ -10,7 +10,7 @@ import { StateService } from 'src/app/services/state.service'
|
||||
styleUrls: ['product-key.page.scss'],
|
||||
})
|
||||
export class ProductKeyPage {
|
||||
@ViewChild('focusInput', { static: false }) elem: IonInput
|
||||
@ViewChild('focusInput') elem: IonInput
|
||||
productKey: string
|
||||
error: string
|
||||
|
||||
@@ -22,7 +22,7 @@ export class ProductKeyPage {
|
||||
private readonly httpService: HttpService,
|
||||
) { }
|
||||
|
||||
ngAfterViewInit () {
|
||||
ionViewDidEnter () {
|
||||
setTimeout(() => this.elem.setFocus(), 400)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<ion-content>
|
||||
<ion-grid style="padding-top: 32px; height: 100%; max-width: 540px;">
|
||||
<ion-row style="height: 100%;">
|
||||
<ion-col class="ion-text-center">
|
||||
<ion-col>
|
||||
|
||||
<div style="padding-bottom: 32px;">
|
||||
<div style="padding-bottom: 32px;" class="ion-text-center">
|
||||
<img src="assets/png/logo.png" style="max-width: 240px;" />
|
||||
</div>
|
||||
|
||||
<ion-card color="dark">
|
||||
<ion-card-header class="ion-text-center" style="padding-bottom: 8px;">
|
||||
<ion-card-header class="ion-text-center">
|
||||
<ion-card-title>Select Recovery Drive</ion-card-title>
|
||||
<ion-card-subtitle>Select the drive containing the Embassy you want to recover.</ion-card-subtitle>
|
||||
</ion-card-header>
|
||||
@@ -29,10 +29,8 @@
|
||||
|
||||
<ion-item-group>
|
||||
<ng-container *ngIf="loading">
|
||||
<ion-item-divider color="light">
|
||||
<ion-skeleton-text animated style="width: 120px; height: 16px;"></ion-skeleton-text>
|
||||
</ion-item-divider>
|
||||
<ion-item color="light">
|
||||
<ion-skeleton-text animated class="skeleton-header"></ion-skeleton-text>
|
||||
<ion-item color="light" style="padding-bottom: 10px;">
|
||||
<ion-avatar slot="start" style="margin-right: 24px;">
|
||||
<ion-skeleton-text animated style="width: 30px; height: 30px; border-radius: 0;"></ion-skeleton-text>
|
||||
</ion-avatar>
|
||||
@@ -45,18 +43,18 @@
|
||||
</ng-container>
|
||||
|
||||
<!-- loaded -->
|
||||
<div *ngFor="let drive of drives">
|
||||
<ion-item-divider color="light">
|
||||
<div *ngFor="let drive of drives" class="ion-padding-bottom">
|
||||
<h2 class="drive-label">
|
||||
{{ drive.vendor || 'Unknown Vendor' }} - {{ drive.model || 'Unknown Model' }} - {{ drive.capacity | convertBytes }}
|
||||
</ion-item-divider>
|
||||
<ion-item color="light" button *ngFor="let partition of drive.partitions" [disabled]="!partitionClickable(partition)" (click)="choosePartition(partition)">
|
||||
</h2>
|
||||
<ion-item lines="none" button *ngFor="let partition of drive.partitions" [disabled]="!partitionClickable(partition)" (click)="choosePartition(partition)">
|
||||
<ion-icon slot="start" name="save-outline"></ion-icon>
|
||||
<ion-label>
|
||||
<h1>{{ partition.label || partition.logicalname }}</h1>
|
||||
<h2>{{ drive.capacity | convertBytes }}</h2>
|
||||
<h2>{{ partition.capacity | convertBytes }}</h2>
|
||||
<p *ngIf="partition['embassy-os'] && partition['embassy-os'].full">
|
||||
<ion-text color="success">
|
||||
Embassy backups detected
|
||||
Embassy backup detected
|
||||
</ion-text>
|
||||
</p>
|
||||
</ion-label>
|
||||
|
||||
@@ -2,3 +2,19 @@
|
||||
border: 4px solid var(--ion-color-secondary);
|
||||
box-shadow: 4px 4px 16px var(--ion-color-light);
|
||||
}
|
||||
|
||||
.drive-label {
|
||||
font-weight: bold;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
|
||||
.skeleton-header {
|
||||
width: 180px;
|
||||
height: 18px;
|
||||
--ion-text-color-rgb: var(--ion-color-light-rgb);
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
ion-item {
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
|
||||
@@ -38,26 +38,25 @@ export class RecoverPage {
|
||||
}
|
||||
|
||||
partitionClickable (partition: PartitionInfo) {
|
||||
return partition['embassy-os']?.full && ((!this.stateService.hasProductKey && partition['embassy-os']?.version.startsWith('0.2') ) || this.stateService.hasProductKey)
|
||||
return partition['embassy-os']?.full && (this.stateService.hasProductKey || this.is02x(partition))
|
||||
}
|
||||
|
||||
async getDrives () {
|
||||
try {
|
||||
this.drives = await this.apiService.getDrives()
|
||||
this.drives = (await this.apiService.getDrives()).filter(d => d.partitions.length)
|
||||
|
||||
const importableDrive = this.drives.filter(d => !!d.guid)[0]
|
||||
const importableDrive = this.drives.find(d => !!d.guid)
|
||||
if (!!importableDrive && !this.hasShownGuidAlert) {
|
||||
const alert = await this.alertCtrl.create({
|
||||
header: 'Warning',
|
||||
subHeader: 'Drive contains data!',
|
||||
message: 'All data stored on this drive will be permanently deleted.',
|
||||
header: 'Embassy Drive Detected',
|
||||
message: 'A valid EmbassyOS data drive has been detected. To use this drive in its current state, simply click "Use Drive" below.',
|
||||
buttons: [
|
||||
{
|
||||
role: 'cancel',
|
||||
text: 'Dismiss',
|
||||
text: 'Cancel',
|
||||
},
|
||||
{
|
||||
text: 'Use',
|
||||
text: 'Use Drive',
|
||||
handler: async () => {
|
||||
await this.importDrive(importableDrive.guid)
|
||||
},
|
||||
@@ -113,6 +112,7 @@ export class RecoverPage {
|
||||
|
||||
})
|
||||
await modal.present()
|
||||
// if no product key, it means they are an upgrade kit user
|
||||
} else {
|
||||
const modal = await this.modalController.create({
|
||||
component: ProdKeyModal,
|
||||
@@ -140,4 +140,8 @@ export class RecoverPage {
|
||||
}
|
||||
await this.navCtrl.navigateForward(`/embassy`)
|
||||
}
|
||||
|
||||
private is02x (partition: PartitionInfo): boolean {
|
||||
return !this.stateService.hasProductKey && partition['embassy-os']?.version.startsWith('0.2')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ export class SuccessPage {
|
||||
) { }
|
||||
|
||||
ngAfterViewInit () {
|
||||
this.download()
|
||||
setTimeout(() => this.download(), 500)
|
||||
}
|
||||
|
||||
async copy (address: string): Promise<void> {
|
||||
|
||||
@@ -58,16 +58,26 @@ export class LiveApiService extends ApiService {
|
||||
}
|
||||
|
||||
async importDrive (guid: string) {
|
||||
return this.http.rpcRequest<SetupEmbassyRes>({
|
||||
const res = await this.http.rpcRequest<SetupEmbassyRes>({
|
||||
method: 'setup.execute',
|
||||
params: { guid },
|
||||
})
|
||||
|
||||
return {
|
||||
...res,
|
||||
'root-ca': btoa(res['root-ca']),
|
||||
}
|
||||
}
|
||||
|
||||
async setupEmbassy (setupInfo: SetupEmbassyReq) {
|
||||
return this.http.rpcRequest<SetupEmbassyRes>({
|
||||
const res = await this.http.rpcRequest<SetupEmbassyRes>({
|
||||
method: 'setup.execute',
|
||||
params: setupInfo as any,
|
||||
})
|
||||
|
||||
return {
|
||||
...res,
|
||||
'root-ca': btoa(res['root-ca']),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ export class MockApiService extends ApiService {
|
||||
{
|
||||
logicalname: 'sdc1',
|
||||
label: 'label 1',
|
||||
capacity: null,
|
||||
capacity: 0,
|
||||
used: null,
|
||||
'embassy-os': {
|
||||
version: '0.3.3',
|
||||
@@ -77,18 +77,19 @@ export class MockApiService extends ApiService {
|
||||
{
|
||||
logicalname: 'sdc1MOCKTESTER',
|
||||
label: 'label 1',
|
||||
capacity: null,
|
||||
capacity: 0,
|
||||
used: null,
|
||||
'embassy-os': {
|
||||
version: '0.3.6',
|
||||
full: true,
|
||||
// password is 'asdfasdf'
|
||||
'password-hash': '$argon2d$v=19$m=1024,t=1,p=1$YXNkZmFzZGZhc2RmYXNkZg$Ceev1I901G6UwU+hY0sHrFZ56D+o+LNJ',
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalname: 'sdc1',
|
||||
label: 'label 1',
|
||||
capacity: null,
|
||||
capacity: 0,
|
||||
used: null,
|
||||
'embassy-os': {
|
||||
version: '0.3.3',
|
||||
@@ -152,7 +153,7 @@ export class MockApiService extends ApiService {
|
||||
return {
|
||||
'tor-address': 'asdfasdfasdf.onion',
|
||||
'lan-address': 'embassy-dfasdf.local',
|
||||
'root-ca': rootCA,
|
||||
'root-ca': btoa(rootCA),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,7 +162,7 @@ export class MockApiService extends ApiService {
|
||||
return {
|
||||
'tor-address': 'asdfasdfasdf.onion',
|
||||
'lan-address': 'embassy-dfasdf.local',
|
||||
'root-ca': rootCA,
|
||||
'root-ca': btoa(rootCA),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,4 +183,26 @@ export class MockApiService extends ApiService {
|
||||
}
|
||||
}
|
||||
|
||||
const rootCA = 'LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURwekNDQW8rZ0F3SUJBZ0lSQUlJdU9hcmxRRVRsVVFFT1pKR1pZZEl3RFFZSktvWklodmNOQVFFTEJRQXcKYlRFTE1Ba0dBMVVFQmhNQ1ZWTXhGVEFUQmdOVkJBb01ERVY0WVcxd2JHVWdRMjl5Y0RFT01Bd0dBMVVFQ3d3RgpVMkZzWlhNeEN6QUpCZ05WQkFnTUFsZEJNUmd3RmdZRFZRUUREQTkzZDNjdVpYaGhiWEJzWlM1amIyMHhFREFPCkJnTlZCQWNNQjFObFlYUjBiR1V3SGhjTk1qRXdNekE0TVRVME5qSTNXaGNOTWpJd016QTRNVFkwTmpJM1dqQnQKTVFzd0NRWURWUVFHRXdKVlV6RVZNQk1HQTFVRUNnd01SWGhoYlhCc1pTQkRiM0p3TVE0d0RBWURWUVFMREFWVApZV3hsY3pFTE1Ba0dBMVVFQ0F3Q1YwRXhHREFXQmdOVkJBTU1EM2QzZHk1bGVHRnRjR3hsTG1OdmJURVFNQTRHCkExVUVCd3dIVTJWaGRIUnNaVENDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFNUDcKdDVBS0ZaUTdhYnFrZXlVanNCVklXUmE5dENoOG9nZTl1L0x2Q2J4VTczOEc0anNzVCtPdWQzV01haklqdU5vdwpjcGMrMFEvZTQyVUxPLzZnVE5yVHM2T0NPbzlsVjZHMERwcmYvZTkxRFdvS2dQYXRlbS9wVWpOeXJhaWZIWmZ1CmI1bUxIQ2ZhaGpXWFVRdGMvc2ptRFFhWlJLM0thcjZsamxVQkUvTGU5TkV5T0FJa1NMUHpEdFc4TFhtNGl3Y1UKQlpyYjgyOHJLZDFBdzlvSTErM2JmekI2eFhtelp4YzVSTFh2ZU9DRWhLR0QzMmpLWi9STkZTQzhBWkF3SmUreApiVHN5cy9sVU9ZRlR1VDhCbjBUR3hSOHg3WTRINzUrRjlCYXZZM3YrV2tMajRNK29sTjlkTVI3RXQ5Rk10NHU0CllSb2t2NXpwOHpJYjVpVG5lMWtDQXdFQUFhTkNNRUF3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEUKRmdRVWFXMytyMzI4dVRMb2tvZzJUa2xtb0JLK3l0NHdEZ1lEVlIwUEFRSC9CQVFEQWdHR01BMEdDU3FHU0liMwpEUUVCQ3dVQUE0SUJBUUFYamQvN1VaOFJERStQTFdTRE5HUWRMZW1PQlRjYXdGK3RLK1B6QTRFdmxtbjlWdU5jCmcreDNvWnZWWlNEUUJBTlV6MGI5b1BlbzU0YUUzOGRXMXpRbTJxZlRhYjg4MjJhcWVXTUx5SjFkTXNBZ3FZWDIKdDkrdTZ3M056UkN3OFB2ejE4VjY5K2RGRTVBZVhtTlAwWjUvZ2R6OEgvTlNwY3RqbHpvcGJTY1JaS0NTbFBpZApSZjNaT1BtOVFQOTJZcFd5WURrZkFVMDR4ZERvMXZSME1ZaktQa2w0TGpScVNVL3RjQ0puUE1iSml3cStiV3BYCjJXSm9FQlhCL3AxNUtuNkp4akkwemUyU25TSTQ4Slo4aXQ0ZnZ4cmhPbzBWb0xOSXVDdU5YSk93VTE3UmRsMVcKWUppZGFxN2plNmsxOEFkZ1BBMEtoOHkxWHRmVUgzZlRhVnc0Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0='
|
||||
const rootCA =
|
||||
`-----BEGIN CERTIFICATE-----
|
||||
MIIDpzCCAo+gAwIBAgIRAIIuOarlQETlUQEOZJGZYdIwDQYJKoZIhvcNAQELBQAw
|
||||
bTELMAkGA1UEBhMCVVMxFTATBgNVBAoMDEV4YW1wbGUgQ29ycDEOMAwGA1UECwwF
|
||||
U2FsZXMxCzAJBgNVBAgMAldBMRgwFgYDVQQDDA93d3cuZXhhbXBsZS5jb20xEDAO
|
||||
BgNVBAcMB1NlYXR0bGUwHhcNMjEwMzA4MTU0NjI3WhcNMjIwMzA4MTY0NjI3WjBt
|
||||
MQswCQYDVQQGEwJVUzEVMBMGA1UECgwMRXhhbXBsZSBDb3JwMQ4wDAYDVQQLDAVT
|
||||
YWxlczELMAkGA1UECAwCV0ExGDAWBgNVBAMMD3d3dy5leGFtcGxlLmNvbTEQMA4G
|
||||
A1UEBwwHU2VhdHRsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMP7
|
||||
t5AKFZQ7abqkeyUjsBVIWRa9tCh8oge9u/LvCbxU738G4jssT+Oud3WMajIjuNow
|
||||
cpc+0Q/e42ULO/6gTNrTs6OCOo9lV6G0Dprf/e91DWoKgPatem/pUjNyraifHZfu
|
||||
b5mLHCfahjWXUQtc/sjmDQaZRK3Kar6ljlUBE/Le9NEyOAIkSLPzDtW8LXm4iwcU
|
||||
BZrb828rKd1Aw9oI1+3bfzB6xXmzZxc5RLXveOCEhKGD32jKZ/RNFSC8AZAwJe+x
|
||||
bTsys/lUOYFTuT8Bn0TGxR8x7Y4H75+F9BavY3v+WkLj4M+olN9dMR7Et9FMt4u4
|
||||
YRokv5zp8zIb5iTne1kCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E
|
||||
FgQUaW3+r328uTLokog2TklmoBK+yt4wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3
|
||||
DQEBCwUAA4IBAQAXjd/7UZ8RDE+PLWSDNGQdLemOBTcawF+tK+PzA4Evlmn9VuNc
|
||||
g+x3oZvVZSDQBANUz0b9oPeo54aE38dW1zQm2qfTab8822aqeWMLyJ1dMsAgqYX2
|
||||
t9+u6w3NzRCw8Pvz18V69+dFE5AeXmNP0Z5/gdz8H/NSpctjlzopbScRZKCSlPid
|
||||
Rf3ZOPm9QP92YpWyYDkfAU04xdDo1vR0MYjKPkl4LjRqSU/tcCJnPMbJiwq+bWpX
|
||||
2WJoEBXB/p15Kn6JxjI0ze2SnSI48JZ8it4fvxrhOo0VoLNIuCuNXJOwU17Rdl1W
|
||||
YJidaq7je6k18AdgPA0Kh8y1XtfUH3fTaVw4
|
||||
-----END CERTIFICATE-----`
|
||||
|
||||
@@ -3,10 +3,11 @@ import { ErrorHandler, Injectable } from '@angular/core'
|
||||
@Injectable()
|
||||
export class GlobalErrorHandler implements ErrorHandler {
|
||||
|
||||
handleError (error: any): void {
|
||||
handleError (e: any): void {
|
||||
console.error(e)
|
||||
const chunkFailedMessage = /Loading chunk [\d]+ failed/
|
||||
|
||||
if (chunkFailedMessage.test(error.message)) {
|
||||
if (chunkFailedMessage.test(e.message)) {
|
||||
window.location.reload()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,6 @@ ion-button {
|
||||
ion-item {
|
||||
--highlight-color-valid: transparent;
|
||||
--highlight-color-invalid: transparent;
|
||||
--highlight-color-focused: var(--ion-color-light);
|
||||
|
||||
--border-radius: 4px;
|
||||
--border-style: solid;
|
||||
|
||||
@@ -8,7 +8,7 @@ import { getErrorMessage } from 'src/app/services/error-toast.service'
|
||||
styleUrls: ['./generic-input.component.scss'],
|
||||
})
|
||||
export class GenericInputComponent {
|
||||
@ViewChild('mainInput', { static: false }) elem: IonInput
|
||||
@ViewChild('mainInput') elem: IonInput
|
||||
@Input() title: string
|
||||
@Input() message: string
|
||||
@Input() warning: string
|
||||
|
||||
Reference in New Issue
Block a user