login page and fix autoscrolling on nav

This commit is contained in:
Matt Hill
2021-06-14 14:28:35 -06:00
committed by Aiden McClelland
parent 594d93eb3b
commit 8b84bdefac
22 changed files with 112 additions and 227 deletions

23
ui/package-lock.json generated
View File

@@ -51,24 +51,6 @@
}
},
"../../patch-db-client": {},
"../../patch-db/client": {
"name": "patch-db",
"version": "1.0.0",
"extraneous": true,
"license": "MIT",
"dependencies": {
"rxjs": "^6.6.3",
"sorted-btree": "^1.5.0",
"uuid": "^8.3.2"
},
"devDependencies": {
"@types/node": "^15.0.0",
"@types/uuid": "^8.3.0",
"ts-node": "^9.1.1",
"tslint": "^6.1.0",
"typescript": "4.1.5"
}
},
"node_modules/@angular-devkit/architect": {
"version": "0.1102.14",
"dev": true,
@@ -2402,8 +2384,7 @@
},
"node_modules/@ngrx/component": {
"version": "11.1.1",
"resolved": "https://registry.npmjs.org/@ngrx/component/-/component-11.1.1.tgz",
"integrity": "sha512-Vw2jyz5O8g61OcDtX8FcJh+EV9VDJsk/q61WQyOuwr0yi3SbRnn/77bHzF27RVwiQn8lq3yteNnh32H/vRdFWQ==",
"license": "MIT",
"dependencies": {
"tslib": "^2.0.0"
},
@@ -17039,8 +17020,6 @@
},
"@ngrx/component": {
"version": "11.1.1",
"resolved": "https://registry.npmjs.org/@ngrx/component/-/component-11.1.1.tgz",
"integrity": "sha512-Vw2jyz5O8g61OcDtX8FcJh+EV9VDJsk/q61WQyOuwr0yi3SbRnn/77bHzF27RVwiQn8lq3yteNnh32H/vRdFWQ==",
"requires": {
"tslib": "^2.0.0"
}

View File

@@ -12,9 +12,9 @@ const routes: Routes = [
path: '',
},
{
path: 'auth',
path: 'login',
canActivate: [UnauthGuard],
loadChildren: () => import('./pages/auth-routes/auth-routing.module').then(m => m.AuthRoutingModule),
loadChildren: () => import('./pages/login/login.module').then(m => m.LoginPageModule),
},
{
path: 'embassy',

View File

@@ -9,7 +9,7 @@
</ng-template>
</ion-toolbar>
</ion-header>
<ion-content scroll-y="false" class="menu-style">
<ion-content scrollY="false" class="menu-style">
<ng-container>
<ion-list style="padding: 0px">
<ion-menu-toggle auto-hide="false" *ngFor="let page of appPages; let i = index">

View File

@@ -96,7 +96,7 @@ export class AppComponent {
this.showMenu = false
this.patch.stop()
this.storage.clear()
this.router.navigate(['/auth'], { replaceUrl: true })
this.router.navigate(['/login'], { replaceUrl: true })
}
})

View File

@@ -47,7 +47,7 @@ export class AppActionsPage {
})
await alert.present()
} else {
const statuses = [...action.allowedStatuses]
const statuses = [...action.value['allowedStatuses']]
const last = statuses.pop()
let statusesStr = statuses.join(', ')
let error = null
@@ -63,7 +63,7 @@ export class AppActionsPage {
}
const alert = await this.alertCtrl.create({
header: 'Forbidden',
message: error || `Action "${action.name}" can only be executed when service is ${statusesStr}`,
message: error || `Action "${action.value.name}" can only be executed when service is ${statusesStr}`,
buttons: ['OK'],
cssClass: 'alert-error-message',
})

View File

@@ -41,7 +41,7 @@
</ion-card-content>
</ion-card>
<ion-spinner *ngIf="pkgsLoading; else pkgsLoaded" class="center" name="lines" color="warning"></ion-spinner>
<ion-spinner *ngIf="pkgsLoading; else pkgsLoaded" style="padding-top: 200px;" name="lines" color="warning"></ion-spinner>
<ng-template #pkgsLoaded>
<ion-card *ngFor="let pkg of pkgs" style="margin: 10px 10px;" [routerLink]="[pkg.id]">

View File

@@ -145,11 +145,6 @@
<!-- versions -->
<ion-item-divider></ion-item-divider>
<ion-item *ngIf="vars.licenseLink" lines="none" button [href]="vars.licenseLink" target="_blank">
<ion-icon slot="start" name="newspaper-outline"></ion-icon>
<ion-label>License</ion-label>
<ion-note slot="end">{{ vars.licenseName }}</ion-note>
</ion-item>
<ion-item lines="none" button (click)="presentAlertVersions()">
<ion-icon color="dark" slot="start" name="file-tray-stacked-outline"></ion-icon>
<ion-label color="dark">Other versions</ion-label>

View File

@@ -104,7 +104,7 @@
<ion-label><ion-text color="primary">Restore from Backup</ion-text></ion-label>
</ion-item>
<!-- donate -->
<ion-item button [href]="manifest['donation-url']" target="_blank">
<ion-item button (click)="donate(manifest)">
<ion-icon slot="start" name="shapes-outline" color="primary"></ion-icon>
<ion-label><ion-text color="primary">Donate</ion-text></ion-label>
</ion-item>

View File

@@ -2,7 +2,7 @@ import { Component, ViewChild } from '@angular/core'
import { AlertController, NavController, ModalController, IonContent, PopoverController } from '@ionic/angular'
import { ApiService } from 'src/app/services/api/api.service'
import { ActivatedRoute, NavigationExtras } from '@angular/router'
import { chill } from 'src/app/util/misc.util'
import { chill, pauseFor } from 'src/app/util/misc.util'
import { LoaderService } from 'src/app/services/loader.service'
import { Observable, of, Subscription } from 'rxjs'
import { wizardModal } from 'src/app/components/install-wizard/install-wizard.component'
@@ -10,7 +10,7 @@ import { WizardBaker } from 'src/app/components/install-wizard/prebaked-wizards'
import { InformationPopoverComponent } from 'src/app/components/information-popover/information-popover.component'
import { ConfigService } from 'src/app/services/config.service'
import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model'
import { DependencyErrorConfigUnsatisfied, DependencyErrorNotInstalled, DependencyErrorType, PackageDataEntry, PackageState } from 'src/app/models/patch-db/data-model'
import { DependencyErrorConfigUnsatisfied, DependencyErrorNotInstalled, DependencyErrorType, Manifest, PackageDataEntry, PackageState } from 'src/app/models/patch-db/data-model'
import { FEStatus } from 'src/app/services/pkg-status-rendering.service'
import { ConnectionService } from 'src/app/services/connection.service'
import { Recommendation } from 'src/app/components/recommendation-button/recommendation-button.component'
@@ -54,6 +54,10 @@ export class AppInstalledShowPage {
this.pkgSub = this.patch.watch$('package-data', this.pkgId).subscribe(pkg => this.pkg = pkg)
}
async ngAfterViewInit () {
this.content.scrollToPoint(undefined, 1)
}
async ngOnDestroy () {
this.pkgSub.unsubscribe()
}
@@ -114,6 +118,20 @@ export class AppInstalledShowPage {
return this.navCtrl.navigateRoot('/services/installed')
}
async donate (manifest: Manifest): Promise<void> {
const url = manifest['donation-url']
if (url) {
window.open(url, '_blank')
} else {
const alert = await this.alertCtrl.create({
header: 'Not Accepting Donations',
message: `The developers of ${manifest.title} have not provided a donation URL. Please contact them directly if you insist on giving them money.`,
buttons: ['OK'],
})
await alert.present()
}
}
async presentPopover (information: string, ev: any) {
const popover = await this.popoverController.create({
component: InformationPopoverComponent,

View File

@@ -1,6 +1,6 @@
import { Component } from '@angular/core'
import { Component, ViewChild } from '@angular/core'
import { ActivatedRoute } from '@angular/router'
import { ToastController } from '@ionic/angular'
import { IonContent, ToastController } from '@ionic/angular'
import { InstalledPackageDataEntry } from 'src/app/models/patch-db/data-model'
import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model'
import { ConfigService } from 'src/app/services/config.service'
@@ -14,6 +14,8 @@ import { copyToClipboard } from 'src/app/util/web.util'
export class AppInterfacesPage {
pkgId: string
@ViewChild(IonContent) content: IonContent
constructor (
private readonly route: ActivatedRoute,
private readonly toastCtrl: ToastController,
@@ -25,6 +27,10 @@ export class AppInterfacesPage {
this.pkgId = this.route.snapshot.paramMap.get('pkgId')
}
async ngAfterViewInit () {
this.content.scrollToPoint(undefined, 1)
}
async copy (address: string): Promise<void> {
let message = ''
await copyToClipboard(address || '')

View File

@@ -14,51 +14,62 @@
<ion-content class="ion-padding-top" *ngIf="patch.watch$('package-data', pkgId) | ngrxPush as pkg">
<ion-item *ngIf="error" style="margin-bottom: 16px;">
<ion-text class="ion-text-wrap" color="danger">{{ error }}</ion-text>
</ion-item>
<ion-item class="ion-margin-bottom">
<ion-label class="ion-text-wrap">
<p><ion-text color="dark">About</ion-text></p>
<p>
Select a location from which to restore {{ pkg.installed.manifest.title }}. This will overwrite all current data.
</p>
</ion-label>
</ion-item>
<ion-spinner *ngIf="loading; else loaded" name="lines" color="warning" class="center"></ion-spinner>
<ion-grid *ngIf="loading; else loaded" style="height: 100%;">
<ion-row class="ion-align-items-center ion-text-center" style="height: 100%;">
<ion-col>
<ion-spinner name="lines" color="warning"></ion-spinner>
<p>Loading Drives</p>
</ion-col>
</ion-row>
</ion-grid>
<ng-template #loaded>
<ion-item *ngIf="allPartitionsMounted">
<ion-text *ngIf="type === 'restore'" class="ion-text-wrap" color="warning">No partitions available. Insert the storage device containing the backup you intend to restore.</ion-text>
<ion-item *ngIf="error" style="margin-bottom: 16px;">
<ion-text class="ion-text-wrap" color="danger">{{ error }}</ion-text>
</ion-item>
<ion-card *ngFor="let disk of disks | keyvalue">
<ion-card-header>
<ion-card-title>
{{ disk.value.size }}
</ion-card-title>
<ion-card-subtitle>
{{ disk.key }}
</ion-card-subtitle>
</ion-card-header>
<ion-card-content>
<ion-item-group>
<ion-item button *ngFor="let partition of disk.value.partitions | keyvalue" [disabled]="partition.value['is-mounted']" (click)="presentModal(partition.key, partition.value)">
<ion-icon slot="start" name="save-outline"></ion-icon>
<ion-label>
<h2>{{ partition.value.label || partition.key }} ({{ partition.value.size || 'unknown size' }})</h2>
<p *ngIf="!partition.value['is-mounted']; else unavailable"><ion-text color="success">Available</ion-text></p>
<ng-template #unavailable>
<p><ion-text color="danger">Unavailable</ion-text></p>
</ng-template>
</ion-label>
</ion-item>
</ion-item-group>
</ion-card-content>
</ion-card>
<ion-item class="ion-margin-bottom">
<ion-label class="ion-text-wrap">
<p><ion-text color="dark">About</ion-text></p>
<p>
Select a location from which to restore {{ pkg.installed.manifest.title }}. This will overwrite all current data.
</p>
</ion-label>
</ion-item>
<ion-spinner *ngIf="loading; else loaded" name="lines" color="warning" class="center"></ion-spinner>
<ng-template #loaded>
<ion-item *ngIf="allPartitionsMounted">
<ion-text *ngIf="type === 'restore'" class="ion-text-wrap" color="warning">No partitions available. Insert the storage device containing the backup you intend to restore.</ion-text>
</ion-item>
<ion-card *ngFor="let disk of disks | keyvalue">
<ion-card-header>
<ion-card-title>
{{ disk.value.size }}
</ion-card-title>
<ion-card-subtitle>
{{ disk.key }}
</ion-card-subtitle>
</ion-card-header>
<ion-card-content>
<ion-item-group>
<ion-item button *ngFor="let partition of disk.value.partitions | keyvalue" [disabled]="partition.value['is-mounted']" (click)="presentModal(partition.key, partition.value)">
<ion-icon slot="start" name="save-outline"></ion-icon>
<ion-label>
<h2>{{ partition.value.label || partition.key }} ({{ partition.value.size || 'unknown size' }})</h2>
<p *ngIf="!partition.value['is-mounted']; else unavailable"><ion-text color="success">Available</ion-text></p>
<ng-template #unavailable>
<p><ion-text color="danger">Unavailable</ion-text></p>
</ng-template>
</ion-label>
</ion-item>
</ion-item-group>
</ion-card-content>
</ion-card>
</ng-template>
</ng-template>
</ion-content>

View File

@@ -1,5 +1,5 @@
import { Component } from '@angular/core'
import { LoadingController, ModalController } from '@ionic/angular'
import { Component, ViewChild } from '@angular/core'
import { IonContent, LoadingController, ModalController } from '@ionic/angular'
import { ApiService } from 'src/app/services/api/api.service'
import { BackupConfirmationComponent } from 'src/app/modals/backup-confirmation/backup-confirmation.component'
import { DiskInfo, PartitionInfoEntry } from 'src/app/services/api/api-types'
@@ -18,6 +18,8 @@ export class AppRestorePage {
error: string
allPartitionsMounted: boolean
@ViewChild(IonContent) content: IonContent
constructor (
private readonly route: ActivatedRoute,
private readonly modalCtrl: ModalController,
@@ -31,6 +33,10 @@ export class AppRestorePage {
this.getExternalDisks()
}
async ngAfterViewInit () {
this.content.scrollToPoint(undefined, 1)
}
async doRefresh () {
this.loading = true
await this.getExternalDisks()

View File

@@ -1,26 +0,0 @@
import { NgModule } from '@angular/core'
import { RouterModule, Routes } from '@angular/router'
import { CommonModule } from '@angular/common'
import { FormsModule } from '@angular/forms'
import { IonicModule } from '@ionic/angular'
import { AuthPasswordPage } from './auth-password.page'
import { SharingModule } from 'src/app/modules/sharing.module'
const routes: Routes = [
{
path: '',
component: AuthPasswordPage,
},
]
@NgModule({
imports: [
CommonModule,
FormsModule,
IonicModule,
RouterModule.forChild(routes),
SharingModule,
],
declarations: [AuthPasswordPage],
})
export class AuthPasswordPageModule { }

View File

@@ -1,34 +0,0 @@
<ion-content class="ion-padding">
<ion-grid style="height: 100%; max-width: 500px;">
<ion-row class="ion-align-items-center" style="height: 100%;">
<ion-col>
<ion-card>
<div style="padding: 20px;">
<ion-card-header class="ion-text-center">
<!-- <ion-card-title style="padding-bottom: 36px;">Enter Pin</ion-card-title> -->
<img src="assets/img/logo.png" style="max-width: 120px;" />
</ion-card-header>
<ion-card-content style="padding-top: 30px;">
<form (submit)="submit()">
<ion-item-group>
<ion-item color="light">
<ion-input [type]="unmasked ? 'text' : 'password'" name="pin" placeholder="Enter Pin" [(ngModel)]="pin" (ionChange)="error = ''"></ion-input>
<ion-button fill="clear" color="dark" (click)="toggleMask()">
<ion-icon slot="icon-only" [name]="unmasked ? 'eye-off-outline' : 'eye-outline'" size="small"></ion-icon>
</ion-button>
</ion-item>
<ion-item *ngIf="error" lines="none">
<ion-label class="ion-text-wrap" color="danger">{{ error }}</ion-label>
</ion-item>
</ion-item-group>
<ion-button color="dark" class="sharp-button" type="submit" [disabled]="!pin" style="margin-top: 60px" expand="block" fill="outline">
Next
</ion-button>
</form>
</ion-card-content>
</div>
</ion-card>
</ion-col>
</ion-row>
</ion-grid>
</ion-content>

View File

@@ -1,3 +0,0 @@
.sharp-button {
--border-radius: 1px;
}

View File

@@ -1,41 +0,0 @@
import { Component } from '@angular/core'
import { NavController } from '@ionic/angular'
import { AuthService } from 'src/app/services/auth.service'
import { LoaderService } from 'src/app/services/loader.service'
@Component({
selector: 'auth-pin',
templateUrl: './auth-pin.page.html',
styleUrls: ['./auth-pin.page.scss'],
})
export class AuthPinPage {
pin = ''
unmasked = false
error = ''
constructor (
private readonly authService: AuthService,
private readonly loader: LoaderService,
private readonly navCtrl: NavController,
) { }
ionViewDidEnter () {
this.error = ''
}
toggleMask () {
this.unmasked = !this.unmasked
}
async submit () {
try {
await this.loader.displayDuringP(
this.authService.submitPin(this.pin),
)
this.pin = ''
await this.navCtrl.navigateForward(['/auth/password'])
} catch (e) {
this.error = e.message
}
}
}

View File

@@ -1,24 +0,0 @@
import { NgModule } from '@angular/core'
import { Routes, RouterModule } from '@angular/router'
const routes: Routes = [
{
path: '',
redirectTo: 'pin',
pathMatch: 'full',
},
{
path: 'pin',
loadChildren: () => import('./auth-pin/auth-pin.module').then(m => m.AuthPinPageModule),
},
{
path: 'password',
loadChildren: () => import('./auth-password/auth-password.module').then(m => m.AuthPasswordPageModule),
},
]
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class AuthRoutingModule { }

View File

@@ -3,13 +3,13 @@ import { RouterModule, Routes } from '@angular/router'
import { CommonModule } from '@angular/common'
import { FormsModule } from '@angular/forms'
import { IonicModule } from '@ionic/angular'
import { AuthPinPage } from './auth-pin.page'
import { LoginPage } from './login.page'
import { SharingModule } from 'src/app/modules/sharing.module'
const routes: Routes = [
{
path: '',
component: AuthPinPage,
component: LoginPage,
},
]
@@ -21,6 +21,6 @@ const routes: Routes = [
RouterModule.forChild(routes),
SharingModule,
],
declarations: [AuthPinPage],
declarations: [LoginPage],
})
export class AuthPinPageModule { }
export class LoginPageModule { }

View File

@@ -5,9 +5,7 @@
<ion-card>
<div style="padding: 20px;">
<ion-card-header class="ion-text-center">
<ion-card-title style="padding-bottom: 36px;">Confirm Nym</ion-card-title>
<img src="assets/img/service-icons/bitcoind.png" style="max-width: 120px;" />
<ion-card-subtitle class="ion-text-center">Chuck Tender</ion-card-subtitle>
<img src="assets/img/logo.png" style="max-width: 120px;" />
</ion-card-header>
<ion-card-content style="padding-top: 30px;">
<form (submit)="submit()">
@@ -23,7 +21,7 @@
</ion-item>
</ion-item-group>
<ion-button color="dark" class="sharp-button" type="submit" [disabled]="!password" style="margin-top: 60px" expand="block" fill="outline">
Login
Next
</ion-button>
</form>
</ion-card-content>

View File

@@ -1,15 +1,15 @@
import { Component } from '@angular/core'
import { NavController } from '@ionic/angular'
import { AuthService } from 'src/app/services/auth.service'
import { LoaderService } from 'src/app/services/loader.service'
import { NavController } from '@ionic/angular'
@Component({
selector: 'auth-password',
templateUrl: './auth-password.page.html',
styleUrls: ['./auth-password.page.scss'],
selector: 'login',
templateUrl: './login.page.html',
styleUrls: ['./login.page.scss'],
})
export class AuthPasswordPage {
password: string = ''
export class LoginPage {
password = ''
unmasked = false
error = ''
@@ -33,7 +33,7 @@ export class AuthPasswordPage {
this.authService.submitPassword(this.password),
)
this.password = ''
return this.navCtrl.navigateForward([''])
await this.navCtrl.navigateForward(['/'])
} catch (e) {
this.error = e.message
}

View File

@@ -179,7 +179,7 @@ export module Mock {
'upstream-repo': 'https://github.com/lightningnetwork/lnd',
'support-site': 'https://lightning.engineering/',
'marketing-site': 'https://lightning.engineering/',
'donation-url': 'https://start9.com',
'donation-url': null,
alerts: {
install: null,
uninstall: null,