lock by PIN

This commit is contained in:
Gabriel Bazán 2017-10-05 18:10:42 -03:00
parent 94dd6fd3e8
commit 6a0555773f
11 changed files with 283 additions and 16 deletions

View File

@ -1,14 +1,16 @@
import { Component } from '@angular/core';
import { Platform } from 'ionic-angular';
import { Platform, ModalController } from 'ionic-angular';
import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';
import { Logger } from '@nsalaun/ng-logger';
import { AppProvider } from '../providers/app/app';
import { ProfileProvider } from '../providers/profile/profile';
import { ConfigProvider } from '../providers/config/config';
import { TabsPage } from '../pages/tabs/tabs';
import { OnboardingPage } from '../pages/onboarding/onboarding';
import { PinModalPage } from '../pages/pin/pin';
@Component({
templateUrl: 'app.html'
@ -22,7 +24,9 @@ export class CopayApp {
private splashScreen: SplashScreen,
private logger: Logger,
private app: AppProvider,
private profile: ProfileProvider
private profile: ProfileProvider,
private config: ConfigProvider,
private modalCtrl: ModalController
) {
this.initializeApp();
@ -44,6 +48,7 @@ export class CopayApp {
this.profile.get().then((profile: any) => {
if (profile) {
this.logger.info('Profile read. Go to HomePage.');
this.openLockModal();
this.rootPage = TabsPage;
} else {
// TODO: go to onboarding page
@ -53,4 +58,16 @@ export class CopayApp {
});
});
}
openLockModal() {
let config = this.config.get();
let lockMethod = config['lock'] && config['lock']['method'];
if (!config['lock']['method']) return;
if (config['lock']['method'] == 'PIN') this.openPINModal('checkPin');
}
openPINModal(action) {
let modal = this.modalCtrl.create(PinModalPage, { action }, { showBackdrop: false, enableBackdropDismiss: false });
modal.present();
}
}

View File

@ -42,6 +42,8 @@ import { SettingsPage } from '../pages/settings/settings';
import { AboutPage } from '../pages/settings/about/about';
import { AdvancedPage } from '../pages/settings/advanced/advanced';
import { AltCurrencyPage } from '../pages/settings/alt-currency/alt-currency';
import { LockPage } from '../pages/settings/lock/lock';
import { PinModalPage } from '../pages/pin/pin';
import { TermsOfUsePage } from '../pages/settings/about/terms-of-use/terms-of-use';
/* Send */
@ -84,7 +86,9 @@ let pages: any = [
DisclaimerPage,
EmailPage,
HomePage,
LockPage,
OnboardingPage,
PinModalPage,
ReceivePage,
SendPage,
ScanPage,

46
src/pages/pin/pin.html Normal file
View File

@ -0,0 +1,46 @@
<ion-header>
<ion-navbar>
<ion-buttons start>
<button *ngIf="action === 'pinSetUp'" (click)="goBack()" ion-button icon-only>
<ion-icon name="arrow-back"></ion-icon>
</button>
</ion-buttons>
<ion-title *ngIf="!confirmingPin">Please enter your PIN</ion-title>
<ion-title *ngIf="confirmingPin">Confirm your PIN</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<div class="block-code">
<div class="circle-{{appName}}" [ngClass]="getFilledClass(1)"></div>
<div class="circle-{{appName}}" [ngClass]="getFilledClass(2)"></div>
<div class="circle-{{appName}}" [ngClass]="getFilledClass(3)"></div>
<div class="circle-{{appName}}" [ngClass]="getFilledClass(4)"></div>
</div>
<div class="block-buttons">
<div class="row">
<div (click)="newEntry('1')">1</div>
<div (click)="newEntry('2')">2</div>
<div (click)="newEntry('3')">3</div>
</div>
<div class="row">
<div (click)="newEntry('4')">4</div>
<div (click)="newEntry('5')">5</div>
<div (click)="newEntry('6')">6</div>
</div>
<div class="row">
<div (click)="newEntry('7')">7</div>
<div (click)="newEntry('8')">8</div>
<div (click)="newEntry('9')">9</div>
</div>
<div class="row">
<div></div>
<div (click)="newEntry('0')">0</div>
<div (click)="delete()">
<ion-icon name="arrow-round-back"></ion-icon>
</div>
</div>
</div>
</ion-content>

41
src/pages/pin/pin.scss Normal file
View File

@ -0,0 +1,41 @@
page-pin {
.block-code {
display: flex;
justify-content: space-between;
max-width: 300px;
margin: auto;
}
.block-buttons{
.row {
font-size: 1.7rem;
cursor: pointer;
display: flex;
justify-content: space-around;
div {
padding: 40px;
}
}
}
@mixin circle {
border-radius: 50%;
box-shadow: 0 0 3px 0px #5b5b5b;
transition: background-color .2s ease-in-out;
width: 5rem;
height: 5rem;
margin: 10px;
}
.circle-copay {
@include circle;
border: 1px solid #1f3598;
}
.circle-bitpay {
@include circle;
border: 1px solid #1f3598;
}
.filled-copay {
background-color: #1f3598;
}
.filled-bitpay {
background-color: #1f3598;
}
}

95
src/pages/pin/pin.ts Normal file
View File

@ -0,0 +1,95 @@
import { Component } from '@angular/core';
import { NavController, NavParams, ViewController } from 'ionic-angular';
import { ConfigProvider } from '../../providers/config/config';
import { Logger } from '@nsalaun/ng-logger';
import * as _ from 'lodash';
@Component({
selector: 'page-pin',
templateUrl: 'pin.html',
})
export class PinModalPage {
public currentPin: string = '';
public firstPinEntered: string = '';
public confirmingPin: boolean = false;
public action: string = '';
public appName: string = 'copay';
constructor(
public navCtrl: NavController,
public navParams: NavParams,
private config: ConfigProvider,
private logger: Logger,
public viewCtrl: ViewController
) {
switch (this.navParams.get('action')) {
case 'checkPin':
this.action = 'checkPin';
break;
case 'pinSetUp':
this.action = 'pinSetUp';
break;
case 'removeLock':
this.action = 'removeLock'
}
}
goBack(): void {
this.navCtrl.pop();
}
newEntry(value: string): void {
this.currentPin = this.currentPin + value;
if (!this.isComplete()) return;
if (this.action === 'checkPin' || this.action === 'removeLock') this.checkIfCorrect();
if (this.action === 'pinSetUp') {
if (!this.confirmingPin) {
this.confirmingPin = true;
this.firstPinEntered = this.currentPin;
this.currentPin = '';
}
else if (this.firstPinEntered === this.currentPin) this.save();
else {
this.firstPinEntered = this.currentPin = '';
}
}
}
delete(): void {
this.currentPin = this.currentPin.substring(0, this.currentPin.length - 1);
}
isComplete(): boolean {
if (this.currentPin.length < 4) return false;
else return true;
};
save(): void {
let lock = { method: 'PIN', value: this.currentPin, bannedUntil: null };
this.config.set({ lock });
this.viewCtrl.dismiss();
}
checkIfCorrect(): void {
let config = this.config.get();
let pinValue = config['lock'] && config['lock']['value'];
if (pinValue == this.currentPin) {
if (this.action === 'removeLock') {
let lock = { method: 'Disabled', value: null, bannedUntil: null };
this.config.set({ lock });
this.viewCtrl.dismiss();
}
if (this.action === 'checkPin') this.viewCtrl.dismiss();
}
else this.currentPin = '';
}
getFilledClass(limit): string {
return this.currentPin.length >= limit ? 'filled-' + this.appName : null;
}
}

View File

@ -0,0 +1,20 @@
<ion-header>
<ion-navbar>
<ion-title>Lock</ion-title>
</ion-navbar>
</ion-header>
<ion-content>
<ion-list radio-group>
<ion-list-header>
Startup lock by
</ion-list-header>
<ion-item *ngFor="let opt of options">
<ion-label>{{opt.method}}</ion-label>
<ion-radio (click)="select(opt.method)" value="{{opt.method}}" checked="{{opt.enabled}}"></ion-radio>
</ion-item>
</ion-list>
</ion-content>

View File

@ -0,0 +1,47 @@
import { Component } from '@angular/core';
import { NavController, NavParams, ModalController } from 'ionic-angular';
import { ConfigProvider } from '../../../providers/config/config';
import { PinModalPage } from '../../pin/pin';
@Component({
selector: 'page-lock',
templateUrl: 'lock.html',
})
export class LockPage {
public options: Array<{ method: string, enabled: boolean }> = [];
public lockOptions: Object;
constructor(
private modalCtrl: ModalController,
private config: ConfigProvider
) {
this.lockOptions = this.config.get()['lock'];
this.options = [
{
method: 'Disabled',
enabled: this.lockOptions['method'] == 'Disabled' ? true : false
},
{
method: 'PIN',
enabled: this.lockOptions['method'] == 'PIN' ? true : false
},
];
}
select(method): void {
switch (method) {
case 'PIN':
this.openPinModal('pinSetUp');
break;
case 'Disabled':
this.openPinModal('removeLock');
}
}
openPinModal(action) {
let modal = this.modalCtrl.create(PinModalPage, { action });
modal.present();
}
}

View File

@ -57,18 +57,12 @@
<ion-option>Urgent</ion-option>
</ion-select>
</ion-item>
<ion-item>
<ion-item (click)="openLockPage()">
<ion-icon name="lock" item-start></ion-icon>
<ion-label>
Lock
</ion-label>
<ion-select>
<ion-option value="">Disabled</ion-option>
<ion-option value="pin">Lock by PIN</ion-option>
<ion-option value="finger">Lock by fingerprint</ion-option>
</ion-select>
</ion-item>
<ion-item-divider color="light">Wallets &amp; integrations</ion-item-divider>
<ion-item>
<ion-icon name="folder" item-start></ion-icon>
@ -81,13 +75,13 @@
<ion-item-divider color="light">More</ion-item-divider>
<ion-item (click)="openAdvancedPage()">
<ion-icon name="hammer" item-start></ion-icon>
Advanced
</ion-item>
<ion-icon name="hammer" item-start></ion-icon>
Advanced
</ion-item>
<ion-item (click)="openAboutPage()">
<ion-icon name="apps" item-start></ion-icon>
About {{appName}}
</ion-item>
</ion-list>
</ion-content>
</ion-content>

View File

@ -5,7 +5,7 @@ import { AppProvider } from '../../providers/app/app';
import { LanguageProvider } from '../../providers/language/language';
import { RateProvider } from '../../providers/rate/rate';
import { AltCurrencyPage } from './alt-currency/alt-currency';
import { LockPage } from './lock/lock';
import { AboutPage } from './about/about';
import { AdvancedPage } from './advanced/advanced';
@ -55,4 +55,8 @@ export class SettingsPage {
this.navCtrl.push(AboutPage);
}
openLockPage() {
this.navCtrl.push(LockPage);
}
}

View File

@ -75,6 +75,6 @@ export class AppProvider {
getInfo() {
return this.http.get(this.jsonPath)
.map((res:Response) => res.json());
.map((res: Response) => res.json());
}
}

View File

@ -123,7 +123,6 @@ export class ConfigProvider {
if (_.isString(newOpts)) {
newOpts = JSON.parse(newOpts);
}
_.merge(config, this.configCache, newOpts);
this.configCache = config;
this.persistence.storeConfig(this.configCache).then(() => {