mirror of https://github.com/BTCPrivate/copay.git
lock by PIN
This commit is contained in:
parent
94dd6fd3e8
commit
6a0555773f
|
@ -1,14 +1,16 @@
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { Platform } from 'ionic-angular';
|
import { Platform, ModalController } from 'ionic-angular';
|
||||||
import { StatusBar } from '@ionic-native/status-bar';
|
import { StatusBar } from '@ionic-native/status-bar';
|
||||||
import { SplashScreen } from '@ionic-native/splash-screen';
|
import { SplashScreen } from '@ionic-native/splash-screen';
|
||||||
|
|
||||||
import { Logger } from '@nsalaun/ng-logger';
|
import { Logger } from '@nsalaun/ng-logger';
|
||||||
import { AppProvider } from '../providers/app/app';
|
import { AppProvider } from '../providers/app/app';
|
||||||
import { ProfileProvider } from '../providers/profile/profile';
|
import { ProfileProvider } from '../providers/profile/profile';
|
||||||
|
import { ConfigProvider } from '../providers/config/config';
|
||||||
|
|
||||||
import { TabsPage } from '../pages/tabs/tabs';
|
import { TabsPage } from '../pages/tabs/tabs';
|
||||||
import { OnboardingPage } from '../pages/onboarding/onboarding';
|
import { OnboardingPage } from '../pages/onboarding/onboarding';
|
||||||
|
import { PinModalPage } from '../pages/pin/pin';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: 'app.html'
|
templateUrl: 'app.html'
|
||||||
|
@ -22,7 +24,9 @@ export class CopayApp {
|
||||||
private splashScreen: SplashScreen,
|
private splashScreen: SplashScreen,
|
||||||
private logger: Logger,
|
private logger: Logger,
|
||||||
private app: AppProvider,
|
private app: AppProvider,
|
||||||
private profile: ProfileProvider
|
private profile: ProfileProvider,
|
||||||
|
private config: ConfigProvider,
|
||||||
|
private modalCtrl: ModalController
|
||||||
) {
|
) {
|
||||||
|
|
||||||
this.initializeApp();
|
this.initializeApp();
|
||||||
|
@ -44,6 +48,7 @@ export class CopayApp {
|
||||||
this.profile.get().then((profile: any) => {
|
this.profile.get().then((profile: any) => {
|
||||||
if (profile) {
|
if (profile) {
|
||||||
this.logger.info('Profile read. Go to HomePage.');
|
this.logger.info('Profile read. Go to HomePage.');
|
||||||
|
this.openLockModal();
|
||||||
this.rootPage = TabsPage;
|
this.rootPage = TabsPage;
|
||||||
} else {
|
} else {
|
||||||
// TODO: go to onboarding page
|
// 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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,8 @@ import { SettingsPage } from '../pages/settings/settings';
|
||||||
import { AboutPage } from '../pages/settings/about/about';
|
import { AboutPage } from '../pages/settings/about/about';
|
||||||
import { AdvancedPage } from '../pages/settings/advanced/advanced';
|
import { AdvancedPage } from '../pages/settings/advanced/advanced';
|
||||||
import { AltCurrencyPage } from '../pages/settings/alt-currency/alt-currency';
|
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';
|
import { TermsOfUsePage } from '../pages/settings/about/terms-of-use/terms-of-use';
|
||||||
|
|
||||||
/* Send */
|
/* Send */
|
||||||
|
@ -84,7 +86,9 @@ let pages: any = [
|
||||||
DisclaimerPage,
|
DisclaimerPage,
|
||||||
EmailPage,
|
EmailPage,
|
||||||
HomePage,
|
HomePage,
|
||||||
|
LockPage,
|
||||||
OnboardingPage,
|
OnboardingPage,
|
||||||
|
PinModalPage,
|
||||||
ReceivePage,
|
ReceivePage,
|
||||||
SendPage,
|
SendPage,
|
||||||
ScanPage,
|
ScanPage,
|
||||||
|
|
|
@ -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>
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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>
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -57,18 +57,12 @@
|
||||||
<ion-option>Urgent</ion-option>
|
<ion-option>Urgent</ion-option>
|
||||||
</ion-select>
|
</ion-select>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item>
|
<ion-item (click)="openLockPage()">
|
||||||
<ion-icon name="lock" item-start></ion-icon>
|
<ion-icon name="lock" item-start></ion-icon>
|
||||||
<ion-label>
|
<ion-label>
|
||||||
Lock
|
Lock
|
||||||
</ion-label>
|
</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>
|
||||||
|
|
||||||
<ion-item-divider color="light">Wallets & integrations</ion-item-divider>
|
<ion-item-divider color="light">Wallets & integrations</ion-item-divider>
|
||||||
<ion-item>
|
<ion-item>
|
||||||
<ion-icon name="folder" item-start></ion-icon>
|
<ion-icon name="folder" item-start></ion-icon>
|
||||||
|
@ -81,9 +75,9 @@
|
||||||
|
|
||||||
<ion-item-divider color="light">More</ion-item-divider>
|
<ion-item-divider color="light">More</ion-item-divider>
|
||||||
<ion-item (click)="openAdvancedPage()">
|
<ion-item (click)="openAdvancedPage()">
|
||||||
<ion-icon name="hammer" item-start></ion-icon>
|
<ion-icon name="hammer" item-start></ion-icon>
|
||||||
Advanced
|
Advanced
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item (click)="openAboutPage()">
|
<ion-item (click)="openAboutPage()">
|
||||||
<ion-icon name="apps" item-start></ion-icon>
|
<ion-icon name="apps" item-start></ion-icon>
|
||||||
About {{appName}}
|
About {{appName}}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { AppProvider } from '../../providers/app/app';
|
||||||
import { LanguageProvider } from '../../providers/language/language';
|
import { LanguageProvider } from '../../providers/language/language';
|
||||||
import { RateProvider } from '../../providers/rate/rate';
|
import { RateProvider } from '../../providers/rate/rate';
|
||||||
import { AltCurrencyPage } from './alt-currency/alt-currency';
|
import { AltCurrencyPage } from './alt-currency/alt-currency';
|
||||||
|
import { LockPage } from './lock/lock';
|
||||||
import { AboutPage } from './about/about';
|
import { AboutPage } from './about/about';
|
||||||
import { AdvancedPage } from './advanced/advanced';
|
import { AdvancedPage } from './advanced/advanced';
|
||||||
|
|
||||||
|
@ -55,4 +55,8 @@ export class SettingsPage {
|
||||||
this.navCtrl.push(AboutPage);
|
this.navCtrl.push(AboutPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
openLockPage() {
|
||||||
|
this.navCtrl.push(LockPage);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,6 +75,6 @@ export class AppProvider {
|
||||||
|
|
||||||
getInfo() {
|
getInfo() {
|
||||||
return this.http.get(this.jsonPath)
|
return this.http.get(this.jsonPath)
|
||||||
.map((res:Response) => res.json());
|
.map((res: Response) => res.json());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,7 +123,6 @@ export class ConfigProvider {
|
||||||
if (_.isString(newOpts)) {
|
if (_.isString(newOpts)) {
|
||||||
newOpts = JSON.parse(newOpts);
|
newOpts = JSON.parse(newOpts);
|
||||||
}
|
}
|
||||||
|
|
||||||
_.merge(config, this.configCache, newOpts);
|
_.merge(config, this.configCache, newOpts);
|
||||||
this.configCache = config;
|
this.configCache = config;
|
||||||
this.persistence.storeConfig(this.configCache).then(() => {
|
this.persistence.storeConfig(this.configCache).then(() => {
|
||||||
|
|
Loading…
Reference in New Issue