include backup flow

This commit is contained in:
JDonadio 2017-08-16 16:16:01 -03:00
parent 42aff9757e
commit 8f36bc6ad6
No known key found for this signature in database
GPG Key ID: EC1F4E04B2BFA730
25 changed files with 959 additions and 72 deletions

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="247px" height="247px" viewBox="0 0 247 247" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 42 (36781) - http://www.bohemiancoding.com/sketch -->
<title>backup-warning</title>
<desc>Created with Sketch.</desc>
<defs>
<linearGradient x1="50%" y1="0%" x2="50%" y2="89.4673583%" id="linearGradient-1">
<stop stop-color="#192C3A" offset="0%"></stop>
<stop stop-color="#192C3A" offset="100%"></stop>
</linearGradient>
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-2">
<stop stop-color="#BCBCBC" offset="0%"></stop>
<stop stop-color="#707D89" offset="100%"></stop>
</linearGradient>
</defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="backup-warning" fill-rule="nonzero">
<g id="Onboarding">
<g id="0.5.a.1--Backup-Warning">
<g id="being-watched">
<path d="M123.5,247 C191.707167,247 247,191.707167 247,123.5 C247,55.2928334 191.707167,1.42108547e-14 123.5,1.42108547e-14 C55.2928334,1.42108547e-14 1.42108547e-14,55.2928334 1.42108547e-14,123.5 C1.42108547e-14,191.707167 55.2928334,247 123.5,247 Z" id="Oval-2" fill="url(#linearGradient-1)" opacity="0.300000012"></path>
<path d="M157.693521,46.9114761 C157.371158,45.8451979 156.16141,44.9808396 155.003028,45.000323 L114.123129,53.1444209 L91.810993,45.2164126 C90.6490687,45.2358961 89.43755,46.1020257 89.1151868,47.1700751 L74.3927551,96.5571737 L172.607245,96.5571737 L157.693521,46.9114761 L157.693521,46.9114761 Z M123.5,107.871766 C94.152554,107.871766 67.1927208,114.280062 46,124.997752 L201,124.997752 C179.786024,114.280062 152.847446,107.871766 123.5,107.871766 L123.5,107.871766 Z" id="Shape" fill="url(#linearGradient-2)"></path>
<path d="M155.018969,132.110996 C143.095075,132.110996 133.220489,140.938787 131.51657,152.396849 C126.220603,151.222526 120.738658,151.215441 115.440921,152.370281 C113.613016,141.055688 103.805736,132.37668 91.982802,132.37668 C78.8651011,132.37668 68.1916924,143.050089 68.1916924,156.16779 C68.1916924,169.28549 78.8633299,179.958899 91.982802,179.958899 C103.956291,179.958899 113.86453,171.058488 115.508228,159.531348 C120.765227,158.119681 126.298537,158.140936 131.54668,159.596883 C133.330305,170.962842 143.162381,179.693215 155.02074,179.693215 C168.138441,179.693215 178.81185,169.021578 178.81185,155.902106 C178.808308,142.784405 168.13667,132.110996 155.018969,132.110996 Z M91.9810307,173.570086 C82.3685864,173.570086 74.5769626,165.778463 74.5769626,156.166018 C74.5769626,146.553574 82.3685864,138.76195 91.9810307,138.76195 C101.593475,138.76195 109.385099,146.553574 109.385099,156.166018 C109.385099,165.778463 101.593475,173.570086 91.9810307,173.570086 Z M155.018969,173.306174 C145.406525,173.306174 137.614901,165.51455 137.614901,155.902106 C137.614901,146.289661 145.406525,138.498038 155.018969,138.498038 C164.631414,138.498038 172.423037,146.289661 172.423037,155.902106 C172.421266,165.51455 164.629642,173.306174 155.018969,173.306174 Z" id="Combined-Shape" fill="#707D89" opacity="0.400000006"></path>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="109px" height="108px" viewBox="0 0 109 108" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 39.1 (31720) - http://www.bohemiancoding.com/sketch -->
<title>no-screenshot</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Onboarding" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="0.5.a.2--Backup-Warning-2" transform="translate(-133.000000, -103.000000)">
<g id="Overlay/Modal/Success" transform="translate(24.000000, 75.000000)">
<g id="Modal/Success">
<g id="no-screenshot" transform="translate(111.000000, 30.000000)">
<g id="camera" opacity="0.5" transform="translate(21.000000, 28.000000)" fill="#FFFFFF">
<path d="M58.8307692,7.809375 L48.4461538,7.809375 L42.2307692,1.561875 C42.2307692,1.561875 42.2,1.53125 42.1846154,1.53125 L42.1538462,1.500625 L42.1538462,1.500625 C41.2307692,0.581875 39.9846154,0 38.5692308,0 L25.6461538,0 C24.1384615,0 22.8,0.643125 21.8615385,1.6690625 L21.8615385,1.684375 L15.7846154,7.809375 L5.16923077,7.809375 C2.30769231,7.809375 0,10.045 0,12.893125 L0,43.8396875 C0,46.6878125 2.30769231,49 5.16923077,49 L58.8307692,49 C61.6769231,49 64,46.6878125 64,43.8396875 L64,12.893125 C64,10.045 61.6769231,7.809375 58.8307692,7.809375 L58.8307692,7.809375 Z M32,41.2671875 C24.1692308,41.2671875 17.7846154,34.8971875 17.7846154,27.0878125 C17.7846154,19.263125 24.1692308,12.9084375 32,12.9084375 C39.8461538,12.9084375 46.2153846,19.263125 46.2153846,27.0878125 C46.2153846,34.8971875 39.8461538,41.2671875 32,41.2671875 L32,41.2671875 Z M57.8615385,16.0015625 C56.6769231,16.0015625 55.7076923,15.036875 55.7076923,13.8425 C55.7076923,12.648125 56.6769231,11.6834375 57.8615385,11.6834375 C59.0461538,11.6834375 60.0153846,12.648125 60.0153846,13.8425 C60.0153846,15.036875 59.0461538,16.0015625 57.8615385,16.0015625 L57.8615385,16.0015625 Z" id="Shape"></path>
<path d="M32,16.3690625 C26.0615385,16.3690625 21.2615385,21.161875 21.2615385,27.0878125 C21.2615385,32.9984375 26.0615385,37.8065625 32,37.8065625 C37.9230769,37.8065625 42.7384615,33.01375 42.7384615,27.0878125 C42.7384615,21.161875 37.9230769,16.3690625 32,16.3690625 L32,16.3690625 Z" id="Shape"></path>
</g>
<g id="ui-24px-outline-2_ban" stroke-width="3" stroke="#FFFFFF">
<g id="Group">
<path d="M89.7272727,15.2727273 L15.2727273,89.7272727" id="Shape"></path>
<circle id="Oval" cx="52.5" cy="52.5" r="52.5"></circle>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -0,0 +1,22 @@
<ion-header>
</ion-header>
<ion-content>
<div class="popup-modal-header popup-modal-header-success">
<div class="popup-modal-header-img-success popup-modal-header-img">
<img src='assets/img/onboarding-success.svg'/>
</div>
</div>
<div class="popup-modal-content popup-modal-content-success">
<div>
<div class="popup-modal-heading">
<span translate>Your bitcoin wallet is backed up!</span>
</div>
<div class="popup-modal-message">
<span translate>Be sure to store your recovery phrase in a secure place. If this app is deleted, your money cannot be recovered without it.</span>
</div>
<button class="button button-clear" (click)="closeBackupResultModal()" translate>Got it</button>
</div>
</div>
</ion-content>

View File

@ -0,0 +1,13 @@
import { NgModule } from '@angular/core';
import { IonicPageModule } from 'ionic-angular';
import { BackupConfirmModalPage } from './backup-confirm-modal';
@NgModule({
declarations: [
BackupConfirmModalPage,
],
imports: [
IonicPageModule.forChild(BackupConfirmModalPage),
],
})
export class BackupConfirmModalPageModule {}

View File

@ -0,0 +1,39 @@
page-backup-confirm-modal {
.popup-modal-header-success {
background-color: $v-background-success;
display: flex;
justify-content: center;
padding: 1.5rem;
max-height: 10rem;
img {
max-height: 10rem;
height: 100%;
}
}
.popup-modal-content-success {
background-color: $v-onboarding-bar-header-color;
text-align: center;
height: 65%;
overflow-y: scroll;
padding: 3rem;
display: flex;
align-items: center;
.popup-modal-heading {
font-weight: bold;
font-size: 2rem;
padding-bottom: 1rem;
color: $v-dark-gray;
}
.popup-modal-message {
padding: 1.5rem;
font-size: 1.6rem;
color: $v-mid-gray;
}
.button-clear {
padding-top: 2rem;
background: none;
color: $v-background-success;
font-size: 1.6rem;
}
}
}

View File

@ -0,0 +1,31 @@
import { Component } from '@angular/core';
import { IonicPage, ViewController, NavParams } from 'ionic-angular';
@IonicPage()
@Component({
selector: 'page-backup-confirm-modal',
templateUrl: 'backup-confirm-modal.html',
})
export class BackupConfirmModalPage {
constructor(public viewCtrl: ViewController, public navParams: NavParams) {
}
ionViewDidLoad() {
}
closeBackupResultModal() {
// TODO waiting for bwc
// profileService.isDisclaimerAccepted(function(val) {
// if (val) {
// $ionicHistory.removeBackView();
// $state.go('tabs.home');
// } else $state.go('onboarding.disclaimer', {
// walletId: $stateParams.walletId,
// backedUp: true
// });
// });
this.viewCtrl.dismiss();
}
}

View File

@ -0,0 +1,90 @@
<!--
Generated template for the BackupGamePage page.
See http://ionicframework.com/docs/components/#navigation for more info on
Ionic pages and navigation.
-->
<ion-header>
<ion-navbar class="navbarc">
<ion-title>{{wallet.name || 'Backup'}}</ion-title>
</ion-navbar>
</ion-header>
<ion-content>
<div *ngIf="deleted">
<div class="phrase-unavailable" translate>Wallet recovery phrase not available.</div>
<div class="phrase-unavailable-instructions" translate>You can still export it from Advanced &gt; Export.</div>
</div>
<ion-slides pager="true" class="pages" *ngIf="!deleted">
<ion-slide>
<div class="wallet-backup-phrase1" *ngIf="mnemonicWords[0] || !wallet.credentials.mnemonicEncrypted">
<div class="initial-prompt" translate>Please carefully write down this phrase.</div>
<div class="backup-phrase">
<!-- TODO copy-to-clipboard function -->
<!-- <div class="backup-phrase" copy-to-clipboard="copyRecoveryPhrase()"> -->
<div class="backup-phrase-content">
<span class="backup-phrase-content-word-readonly" *ngFor="let word of mnemonicWords">
<span style="white-space:nowrap">{{word}}</span>
<span *ngIf="useIdeograms">&#x3000;</span>
</span>
</div>
</div>
<div *ngIf="wallet.mnemonicHasPassphrase">
<div class="password-required" translate>
This recovery phrase was created with a password. To recover this wallet both the recovery phrase and password are needed.
</div>
</div>
<div class="cta-buttons">
<div class="tldr-prompt" translate>We'll confirm on the next screen.</div>
<button [disabled]="wallet.credentials.mnemonicEncrypted || error" class="button primary" (click)="slideNext()" translate>I've written it down</button>
</div>
</div>
</ion-slide>
<ion-slide>
<div class="wallet-backup-phrase1">
<div class="initial-prompt" translate>Let's verify your backup phrase.</div>
<div class="backup-phrase">
<div class="backup-phrase-content">
<button class="button select-word" *ngFor="let customWord of customWords; let i = index"
(click)="removeButton(i, customWord)">{{customWord.word}}
</button>
</div>
</div>
<div class="cta-buttons select-phrase">
<div *ngIf="!selectComplete" class="tldr-prompt" translate>Please tap each word in the correct order.</div>
<div *ngIf="selectComplete" class="tldr-prompt" translate>Is this correct?</div>
<div *ngIf="!selectComplete">
<button class="button select-word" *ngFor="let shuffledWord of shuffledMnemonicWords; let i = index"
(click)="addButton(i, shuffledWord)"
[disabled]="shuffledWord.selected">{{shuffledWord.word}}
</button>
</div>
<div *ngIf="selectComplete">
<button class="button primary" (click)="slideNext();" translate>Confirm</button>
<button class="button button-clear" (click)="initFlow();" translate>Clear</button>
</div>
</div>
</div>
</ion-slide>
<ion-slide *ngIf="wallet.mnemonicHasPassphrase">
<div class="wallet-backup-phrase1">
<div class="initial-prompt" translate>Enter your password</div>
<div class="password-prompt">
<div class="description" translate>In order to verify your wallet backup, please type your password.</div>
<div class="input">
<input type="password" id="passphrase" [(ngModel)]="passphrase" autocapitalize="off" spellcheck="false"/>
</div>
</div>
<div class="cta-buttons confirm">
<button [disabled]="!passphrase" class="button primary" (click)="finalStep();" translate>Confirm</button>
</div>
</div>
</ion-slide>
</ion-slides>
</ion-content>

View File

@ -0,0 +1,13 @@
import { NgModule } from '@angular/core';
import { IonicPageModule } from 'ionic-angular';
import { BackupGamePage } from './backup-game';
@NgModule({
declarations: [
BackupGamePage,
],
imports: [
IonicPageModule.forChild(BackupGamePage),
],
})
export class BackupGamePageModule {}

View File

@ -0,0 +1,183 @@
page-backup-game {
font-size: 16px;
.header-md::after {
background: none;
}
.navbarc {
.bar-button {
color: $v-onboarding-bar-header-button-color;
}
.toolbar-background {
background: $v-primary-color;
}
.toolbar-content {
.title {
text-align: center;
margin-right: 56px;
.toolbar-title {
color: $v-onboarding-bar-header-color;
}
}
}
}
.pages {
.swiper-pagination {
display: none;
}
.slide-zoom {
height: 100%;
.wallet-backup-phrase1 {
height: 70%;
color: #445;
.initial-prompt {
padding: 3rem;
}
.backup-phrase {
background: #f2f2f2;
margin: auto;
border: 2px dashed #d9d9d9;
border-radius: 3px;
color: #445;
text-align: center;
max-width: 500px;
min-height: 12rem;
display: flex;
justify-content: center;
align-items: center;
}
.password-required {
padding: 2rem;
font-size: 14px;
color: #DB5B44;
}
.password-prompt {
background-color: #f2f2f2;
padding: 2rem;
.description {
padding-bottom: 3rem;
}
}
.cta-buttons {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
padding-bottom: 5vh;
.tldr-prompt {
padding: 1rem;
}
&.select-phrase {
background: #f2f2f2;
}
&.confirm {
margin-top: 1rem;
}
}
.button.primary {
width: 85%;
max-width: 300px;
min-height: 5rem;
font-size: 1.6rem;
border-radius: 3px;
cursor: pointer;
border-color: transparent;
background-color: $v-accent-color;
color: $v-onboarding-color;
&:active {
background-color: $v-btn-active-color;
}
}
.button.button-clear {
width: 85%;
margin-top: 0.6rem;
max-width: 300px;
min-height: 5rem;
font-size: 1.6rem;
border-radius: 3px;
cursor: pointer;
border-color: transparent;
background-color: inherit;
color: $v-accent-color;
&:active {
opacity: 0.5;
}
}
.button.select-word {
background: #1abb9b;
color: #192c3a;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
display: inline-block;
margin: 3px 2px;
min-height: 35px;
line-height: 33px;
padding: 0 8px;
border-width: 1px;
border-color: transparent;
border-style: solid;
border-radius: 3px;
vertical-align: top;
text-align: center;
text-overflow: ellipsis;
font-size: 16px;
&:active {
background-color: $v-btn-active-color;
}
}
}
}
}
}
@media not all and (min-height: 600px) and (min-width: 768px) {
.backup-modal-success ion-backdrop {
visibility: visible;
z-index: 0;
}
.backup-modal-success {
display: flex;
justify-content: center;
align-items: center;
.modal-wrapper {
width: 90%;
max-width: 350px;
height: 40%;
min-height: 350px;
}
}
}
.backup-modal-success {
.modal-wrapper {
max-height: 40rem;
ion-content {
max-height: 40rem;
text-align: center;
border-radius: 12px;
.scroll-content {
overflow-y: hidden;
border-radius: 12px;
padding: 0rem;
.popup-modal-content-success {
overflow-y: hidden;
}
}
}
}
}

View File

@ -0,0 +1,249 @@
import { Component, ViewChild } from '@angular/core';
import { IonicPage, NavController, NavParams, Slides, Navbar, AlertController, ModalController } from 'ionic-angular';
import * as _ from 'lodash';
@IonicPage()
@Component({
selector: 'page-backup-game',
templateUrl: 'backup-game.html',
})
export class BackupGamePage {
@ViewChild(Slides) slides: Slides;
@ViewChild(Navbar) navBar: Navbar;
public currentIndex: number;
public deleted: boolean;
public wallet: any;
public mnemonicWords: Array<String>;
public shuffledMnemonicWords: Array<any>;
public passphrase: String;
public customWords: Array<any>;
public selectComplete: boolean;
public error: boolean;
private keys: any;
private useIdeograms: any;
constructor(public navCtrl: NavController, public navParams: NavParams, public alertCtrl: AlertController, public modalCtrl: ModalController) {
// TODO replace for the original wallet object
this.wallet = {
name: 'Wallet name',
credentials: {
mnemonic: 'uno dos tres cuatro cinco seis siete ocho nueve diez once doce',
//mnemonic: 'turtle provide boat sick popular brisk test devote gossip embark endorse corn',
mnemonicEncrypted: false,
},
n: 1,
// isPrivKeyEncrypted: this.isPrivKeyEncrypted(),
// mnemonicHasPassphrase: this.mnemonicHasPassphrase(),
isPrivKeyEncrypted: false,
mnemonicHasPassphrase: false,
network: 'livenet',
};
// TODO waiting for bwc
// walletService.getKeys($scope.wallet, function(err, k) {
// if (err || !k) {
// $log.error('Could not get keys: ', err);
// $ionicHistory.goBack();
// return;
// }
// $scope.credentialsEncrypted = false;
// keys = k;
// $scope.initFlow();
// });
this.keys = null;
// this.deleted = this.isDeletedSeed();
this.deleted = false;
}
ngOnInit() {
this.currentIndex = 0;
this.navBar.backButtonClick = (e: UIEvent) => {
this.slidePrev();
}
this.initFlow();
}
ionViewDidLoad() {
}
initFlow() {
// if (!this.keys) return;
// var words = keys.mnemonic;
var words = this.wallet.credentials.mnemonic;
// $scope.data = {};
this.mnemonicWords = words.split(/[\u3000\s]+/);
this.shuffledMnemonicWords = this.shuffledWords(this.mnemonicWords);
this.useIdeograms = words.indexOf("\u3000") >= 0;
this.passphrase = null;
this.customWords = [];
this.selectComplete = false;
this.error = false;
// words = _.repeat('x', 300);
if (this.currentIndex == 2) this.slidePrev();
};
shuffledWords(words: Array<String>) {
var sort = _.sortBy(words);
return _.map(sort, (w) => {
return {
word: w,
selected: false
};
});
};
addButton(index: number, item: any) {
var newWord = {
word: item.word,
prevIndex: index
};
this.customWords.push(newWord);
this.shuffledMnemonicWords[index].selected = true;
this.shouldContinue();
};
removeButton(index: number, item: any) {
// if ($scope.loading) return;
this.customWords.splice(index, 1);
this.shuffledMnemonicWords[item.prevIndex].selected = false;
this.shouldContinue();
};
shouldContinue() {
if (this.customWords.length == this.shuffledMnemonicWords.length)
this.selectComplete = true;
else
this.selectComplete = false;
};
finalStep() {
// ongoingProcess.set('validatingWords', true);
this.confirm((err) => {
// ongoingProcess.set('validatingWords', false);
if (err) {
this.backupError(err);
}
setTimeout(() => {
this.showBackupResult();
return;
});
});
};
confirm(cb: Function) {
this.error = false;
var customWordList = _.map(this.customWords, 'word');
if (!_.isEqual(this.mnemonicWords, customWordList)) {
return cb('Mnemonic string mismatch');
}
setTimeout(() => {
// TODO waiting for bwc
// if (this.mnemonicHasPassphrase) {
// var walletClient = bwcService.getClient();
// var separator = this.useIdeograms ? '\u3000' : ' ';
// var customSentence = customWordList.join(separator);
// var passphrase = this.data.passphrase || '';
//
// try {
// walletClient.seedFromMnemonic(customSentence, {
// network: this.wallet.credentials.network,
// passphrase: passphrase,
// account: this.wallet.credentials.account
// });
// } catch (err) {
// walletClient.credentials.xPrivKey = _.repeat('x', 64);
// return cb(err);
// }
//
// if (walletClient.credentials.xPrivKey.substr(walletClient.credentials.xPrivKey) != keys.xPrivKey) {
// delete walletClient.credentials;
// return cb('Private key mismatch');
// }
// }
// profileService.setBackupFlag($scope.wallet.credentials.walletId);
return cb();
});
};
backupError(err: any) {
// ongoingProcess.set('validatingWords', false);
console.log('Failed to verify backup: ', err);
this.error = true;
};
showBackupResult() {
if (this.error) {
let alert = this.alertCtrl.create({
title: "Uh oh...",
subTitle: "It's important that you write your backup phrase down correctly. If something happens to your wallet, you'll need this backup to recover your money. Please review your backup and try again.",
buttons: [{
text: 'Ok',
role: 'cancel',
handler: () => {
this.initFlow();
}
}]
});
alert.present();
} else {
const myModal = this.modalCtrl.create('BackupConfirmModalPage', {}, {
showBackdrop: true,
enableBackdropDismiss: true,
cssClass: "backup-modal-success"
});
myModal.present();
}
};
/*********************************
* Hardcoded methods
*/
mnemonicHasPassphrase() {
return false;
}
isPrivKeyEncrypted() {
return false;
}
/*
* Hardcoded methods
*********************************/
isDeletedSeed() {
if (!this.wallet.credentials.mnemonic && !this.wallet.credentials.mnemonicEncrypted)
return true;
return false;
}
slidePrev() {
if (this.currentIndex == 0) this.navCtrl.pop();
else {
this.slides.slidePrev();
this.currentIndex = this.slides.getActiveIndex();
}
}
slideNext() {
if (this.currentIndex == 1 && !this.wallet.mnemonicHasPassphrase)
this.finalStep();
else
this.slides.slideNext();
this.currentIndex = this.slides.getActiveIndex();
}
}

View File

@ -0,0 +1,20 @@
<ion-header>
</ion-header>
<ion-content>
<div class="popup-modal-header popup-modal-header-warning">
<img src='assets/img/no-screenshot.svg'/>
</div>
<div class="popup-modal-content popup-modal-content-warning">
<div>
<div class="popup-modal-heading">
<span translate>Screenshots are not secure</span>
</div>
<div class="popup-modal-message">
<span translate>If you take a screenshot, your backup may be viewed by other apps. You can make a safe backup with physical paper and a pen.</span>
</div>
<button class="button button-clear" (click)="close()" translate>I understand</button>
</div>
</div>
</ion-content>

View File

@ -0,0 +1,13 @@
import { NgModule } from '@angular/core';
import { IonicPageModule } from 'ionic-angular';
import { BackupWarningModalPage } from './backup-warning-modal';
@NgModule({
declarations: [
BackupWarningModalPage,
],
imports: [
IonicPageModule.forChild(BackupWarningModalPage),
],
})
export class BackupWarningModalPageModule {}

View File

@ -0,0 +1,36 @@
page-backup-warning-modal {
.popup-modal-header-warning {
background: #ffa500;
}
.popup-modal-header {
padding: 1rem;
border-radius: 12px 12px 0 0;
min-height: 120px;
}
.popup-modal-content-warning {
background-color: $v-onboarding-bar-header-color;
text-align: center;
height: 65%;
overflow-y: scroll;
padding: 1rem;
display: flex;
align-items: center;
.popup-modal-heading {
font-weight: bold;
font-size: 2rem;
padding-bottom: 1rem;
color: $v-dark-gray;
}
.popup-modal-message {
padding: 1.5rem;
font-size: 1.6rem;
color: $v-mid-gray;
}
.button-clear {
padding-top: 2rem;
background: none;
color: $v-background-warning;
font-size: 1.6rem;
}
}
}

View File

@ -0,0 +1,22 @@
import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams, ViewController } from 'ionic-angular';
@IonicPage()
@Component({
selector: 'page-backup-warning-modal',
templateUrl: 'backup-warning-modal.html',
})
export class BackupWarningModalPage {
constructor(public navCtrl: NavController, public navParams: NavParams, public viewCtrl: ViewController) {
}
ionViewDidLoad() {
}
close() {
this.navCtrl.push('BackupGamePage');
this.viewCtrl.dismiss();
}
}

View File

@ -0,0 +1,19 @@
<ion-header>
<ion-navbar transparent class="navbarc">
</ion-navbar>
</ion-header>
<ion-content class="onboarding">
<div class="onboarding-topic" translate>Are you being watched?</div>
<div class="onboarding-description" translate>Now is a perfect time to assess your surroundings. Nearby windows? Hidden cameras? Shoulder-spies?</div>
<div class="onboarding-illustration-backup-warning">
<img src='assets/img/backup-warning.svg'/>
</div>
<div class="buttons">
<div class="onboarding-tldr" translate>Anyone with your backup phrase can access or spend your bitcoin.</div>
<button class="primary" ion-button block (click)="openWarningModal()" translate>Got it</button>
</div>
</ion-content>

View File

@ -0,0 +1,13 @@
import { NgModule } from '@angular/core';
import { IonicPageModule } from 'ionic-angular';
import { BackupWarningPage } from './backup-warning';
@NgModule({
declarations: [
BackupWarningPage,
],
imports: [
IonicPageModule.forChild(BackupWarningPage),
],
})
export class BackupWarningPageModule {}

View File

@ -0,0 +1,64 @@
page-backup-warning {
position: relative;
.buttons {
padding: 15px;
width: 100%;
position: absolute;
bottom: 0px;
padding: 15px 15px 4vh 15px;
}
.backup-illustration-backup-warning {
height: inherit;
img {
padding-top: 10%;
width: 65%;
max-width: 400px;
}
}
.navbarc {
.bar-button {
color: $v-accent-color;
}
}
.header-md::after {
background: none;
}
}
@media not all and (min-height: 600px) and (min-width: 768px) {
.backup-modal-warning ion-backdrop {
visibility: visible;
z-index: 0;
}
.backup-modal-warning {
display: flex;
justify-content: center;
align-items: center;
.modal-wrapper {
width: 90%;
max-width: 350px;
height: 40%;
min-height: 350px;
}
}
}
.backup-modal-warning {
.modal-wrapper {
max-height: 40rem;
ion-content {
max-height: 40rem;
text-align: center;
border-radius: 12px;
.scroll-content {
overflow-y: hidden;
border-radius: 12px;
padding: 0rem;
.popup-modal-content-warning {
overflow-y: hidden;
}
}
}
}
}

View File

@ -0,0 +1,25 @@
import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams, ModalController } from 'ionic-angular';
@IonicPage()
@Component({
selector: 'page-backup-warning',
templateUrl: 'backup-warning.html',
})
export class BackupWarningPage {
public currentIndex: number;
constructor(public navCtrl: NavController, public navParams: NavParams, public modalCtrl: ModalController) {
}
ionViewDidLoad() {
console.log('ionViewDidLoad BackupWarningPage');
}
openWarningModal() {
const myModal = this.modalCtrl.create('BackupWarningModalPage', {}, { showBackdrop: true, enableBackdropDismiss: true, cssClass: "backup-modal-warning" });
myModal.present();
}
}

View File

@ -6,4 +6,5 @@
<ion-content padding>
<button ion-button block (click)="showOnboardingFlow()" translate>Onboarding</button>
<button ion-button block (click)="showBackupFlow()" translate>Backup</button>
</ion-content>

View File

@ -14,4 +14,8 @@ export class HomePage {
showOnboardingFlow() {
this.navCtrl.push('WelcomePage');
}
showBackupFlow() {
this.navCtrl.push('BackupWarningPage');
}
}

View File

@ -10,7 +10,7 @@
</ion-header>
<ion-content class="onboarding tour">
<ion-content class="onboarding">
<ion-slides pager="true" class="pages">
<ion-slide>
<div>

View File

@ -1,23 +1,21 @@
page-tour {
.tour {
position: relative;
position: relative;
.pages {
position: absolute;
}
.buttons {
padding: 15px;
width: 100%;
position: absolute;
bottom: 0px;
padding: 15px 15px 9vh 15px;
}
.slide-zoom {
height: inherit;
img {
padding-top: 10%;
width: 65%;
}
.pages {
position: absolute;
}
.buttons {
padding: 15px;
width: 100%;
position: absolute;
bottom: 0px;
padding: 15px 15px 9vh 15px;
}
.slide-zoom {
height: inherit;
img {
padding-top: 10%;
width: 65%;
}
}

View File

@ -2,7 +2,7 @@
</ion-header>
<ion-content class="onboarding welcome">
<ion-content class="onboarding">
<div class="logo-tagline">
<img src='assets/img/logo-negative.svg' id="logo" />
<p class="onboarding-description" translate>Take control of your money,<br />get started with bitcoin.</p>

View File

@ -1,33 +1,31 @@
page-welcome {
.welcome {
position: relative;
position: relative;
.logo-tagline {
position: absolute;
width: 100%;
height: 70%;
display: flex;
flex-direction: column;
justify-content: space-around;
.logo-tagline {
position: absolute;
width: 100%;
height: 70%;
display: flex;
flex-direction: column;
justify-content: space-around;
img {
width: 50%;
max-width: 200px;
margin: 5rem auto 0;
}
p {
line-height: 1.6;
font-size: 18px;
text-align: center;
}
img {
width: 50%;
max-width: 200px;
margin: 5rem auto 0;
}
.buttons {
width: 100%;
position: absolute;
bottom: 0px;
padding: 15px 15px 4vw 15px;
p {
line-height: 1.6;
font-size: 18px;
text-align: center;
}
}
.buttons {
width: 100%;
position: absolute;
bottom: 0px;
padding: 15px 15px 4vw 15px;
}
}

View File

@ -21,7 +21,11 @@ $v-accent-color: #1abb9b;
$v-dark-gray: #445 !default;
$v-mid-gray: #667 !default;
$v-light-gray: #9b9bab !default;
$v-title-gray: #c2c9d1 !default;
$v-subtle-gray: darken(#ffffff, 5%) !default;
$v-btn-active-color: #148e76;
$v-background-warning: #ffa500;
$v-background-success: #1abb9b;
$v-onboarding-color: #ffffff !default;
$v-onboarding-bar-header-color: #ffffff !default;
@ -35,33 +39,6 @@ $v-button-primary-bg: $v-accent-color !default;
$v-visible-radius: 6px !default;
// Shared Variables
// --------------------------------------------------
// To customize the look and feel of this app, you can override
// the Sass variables found in Ionic's source scss files.
// To view all the possible Ionic variables, see:
// http://ionicframework.com/docs/theming/overriding-ionic-variables/
// Named Color Variables
// --------------------------------------------------
// Named colors makes it easy to reuse colors on various components.
// It's highly recommended to change the default colors
// to match your app's branding. Ionic uses a Sass map of
// colors so you can add, rename and remove colors as needed.
// The "primary" color is the only required color in the map.
$colors: (
primary: #488aff,
secondary: #32db64,
danger: #f53d3d,
light: #f4f4f4,
dark: #222
);
// App iOS Variables
// --------------------------------------------------
// iOS only Sass variables can go here
@ -137,6 +114,7 @@ $colors: (
.onboarding-topic,
.onboarding-description,
.onboarding-tldr {
color: $v-title-gray;
margin-left: 3rem;
margin-right: 3rem;
}