mirror of https://github.com/BTCPrivate/copay.git
Merge branch 'v4' of github.com:bitpay/copay into polish/onboarding
This commit is contained in:
commit
7b02d1100c
|
@ -30,6 +30,7 @@ src/theme/overrides.scss
|
|||
src/assets/appConfig.json
|
||||
src/assets/externalServices.json
|
||||
src/assets/img/app
|
||||
src/assets/i18n/crowdin_api_key.txt
|
||||
src/index.html
|
||||
src/manifest.json
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
"glidera": true,
|
||||
"debitcard": true,
|
||||
"amazon": true,
|
||||
"mercadolibre": false,
|
||||
"mercadolibre": true,
|
||||
"shapeshift": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
$color-primary: #647ce8;
|
||||
$color-secondary: #111b49;
|
||||
$icon-border-radius: 50%;
|
||||
$txp-icon-border-radius: 2rem;
|
||||
$toolbar-background: #1e3186;
|
||||
$toolbar-scanner: rgba(30, 49, 134, 0.8);
|
||||
|
||||
|
|
|
@ -27,11 +27,11 @@
|
|||
"androidVersion": "40000002",
|
||||
"_extraCSS": null,
|
||||
"_enabledExtensions": {
|
||||
"coinbase": false,
|
||||
"glidera": false,
|
||||
"coinbase": true,
|
||||
"glidera": true,
|
||||
"debitcard": false,
|
||||
"amazon": false,
|
||||
"mercadolibre": false,
|
||||
"shapeshift": false
|
||||
"amazon": true,
|
||||
"mercadolibre": true,
|
||||
"shapeshift": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
$color-primary: #1abb9b;
|
||||
$color-secondary: #31465b;
|
||||
$icon-border-radius: 3px;
|
||||
$txp-icon-border-radius: 0.3rem;
|
||||
$toolbar-background: #192c3a;
|
||||
$toolbar-scanner: rgba(25, 44, 58, 0.8);
|
||||
|
||||
|
|
|
@ -125,41 +125,50 @@
|
|||
.gravatar {
|
||||
min-width: 3rem;
|
||||
min-height: 3rem;
|
||||
border-radius: $icon-border-radius;
|
||||
}
|
||||
|
||||
// Compatibility Ionic 1
|
||||
|
||||
.light,
|
||||
a.light {
|
||||
color: color($colors, light);
|
||||
}
|
||||
a.light,
|
||||
.stable,
|
||||
a.stable {
|
||||
color: color($colors, light);
|
||||
}
|
||||
|
||||
.positive,
|
||||
a.positive {
|
||||
color: color($colors, secondary);
|
||||
}
|
||||
|
||||
.calm,
|
||||
a.calm {
|
||||
color: color($colors, light);
|
||||
color: color($colors, grey);
|
||||
}
|
||||
|
||||
.positive,
|
||||
a.positive {
|
||||
color: color($colors, primary);
|
||||
}
|
||||
|
||||
.energized,
|
||||
a.energized {
|
||||
color: color($colors, primary);
|
||||
}
|
||||
|
||||
.assertive,
|
||||
a.assertive {
|
||||
color: color($colors, danger);
|
||||
}
|
||||
|
||||
.balanced,
|
||||
a.balanced {
|
||||
color: color($colors, success);
|
||||
}
|
||||
.royal,
|
||||
a.royal {
|
||||
color: color($colors, secondary);
|
||||
color: color($colors, dark);
|
||||
}
|
||||
|
||||
.text-gray {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,248 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
'use strict';
|
||||
|
||||
if (process.argv[2]) {
|
||||
var no_build = (process.argv[2].toLowerCase() == '--nobuild')
|
||||
if (no_build == false) {
|
||||
console.log('Incorrect arg. Please use --nobuild if you would like to download without api key.');
|
||||
process.exit(1);
|
||||
};
|
||||
} else {
|
||||
var no_build = false;
|
||||
console.log('\n' +
|
||||
'Please note: If you do not have the crowdin API key and would like to download the ' +
|
||||
'translations without building anyways, please make sure your English files are the same ' +
|
||||
'version as crowdin, and then run this script with --nobuild\n\n' +
|
||||
'eg. "node crowdin_download.js --nobuild"\n\n');
|
||||
};
|
||||
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var https = require('https');
|
||||
var AdmZip = require('adm-zip');
|
||||
|
||||
var crowdin_identifier = 'copay'
|
||||
|
||||
var local_file_name2 = path.join(__dirname, 'docs/appstore_en.txt')
|
||||
var local_file_name3 = path.join(__dirname, 'docs/updateinfo_en.txt')
|
||||
|
||||
try {
|
||||
fs.statSync(local_file_name2);
|
||||
fs.statSync(local_file_name3);
|
||||
}
|
||||
catch (e) {
|
||||
console.log('\n### ABORTING ### One of the following files does not exist:\n' + local_file_name2 + '\n' + local_file_name3);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
// obtain the crowdin api key
|
||||
var crowdin_api_key = fs.readFileSync(path.join(__dirname, 'crowdin_api_key.txt'), 'utf8')
|
||||
} catch (e) {
|
||||
console.log('### ERROR ### You do not have the crowdin api key in ./crowdin_api_key.txt');
|
||||
process.exit(1);
|
||||
};
|
||||
|
||||
if (no_build == false) { // Reminder: Any changes to the script below must also be made to the else clause and vice versa.
|
||||
|
||||
// This call will tell the server to generate a new zip file for you based on most recent translations.
|
||||
https.get('https://api.crowdin.com/api/project/' + crowdin_identifier + '/export?key=' + crowdin_api_key, function (res) {
|
||||
|
||||
console.log('Export Got response: ' + res.statusCode);
|
||||
|
||||
res.on('data', function (chunk) {
|
||||
var resxml = chunk.toString('utf8');
|
||||
console.log(resxml);
|
||||
|
||||
if (resxml.indexOf('status="skipped"') >= 0) {
|
||||
console.log('Translation build was skipped due to either:\n' +
|
||||
'1. No changes since last translation build, or\n' +
|
||||
'2. API limit of once per 30 minutes has not been waited.\n\n' +
|
||||
'Since we can not guarantee that translations have been built properly, this script will end here.\n' +
|
||||
'Log in to Copay\'s Crowdin Settings and click the "Build Project" button to assure it is built recently, and then run this ' +
|
||||
'script again with the --nobuild arg to download translations without checking if built.');
|
||||
process.exit(1);
|
||||
};
|
||||
|
||||
// Download most recent translations for all languages.
|
||||
https.get('https://crowdin.com/download/project/' + crowdin_identifier + '.zip', function (res) {
|
||||
var data = [], dataLen = 0;
|
||||
|
||||
res.on('data', function (chunk) {
|
||||
data.push(chunk);
|
||||
dataLen += chunk.length;
|
||||
}).on('end', function () {
|
||||
var buf = new Buffer(dataLen);
|
||||
for (var i = 0, len = data.length, pos = 0; i < len; i++) {
|
||||
data[i].copy(buf, pos);
|
||||
pos += data[i].length;
|
||||
};
|
||||
var zip = new AdmZip(buf);
|
||||
zip.extractAllTo('./', true);
|
||||
console.log('Done extracting ZIP file.');
|
||||
|
||||
var files = fs.readdirSync('./docs');
|
||||
|
||||
for (var i in files) {
|
||||
debugger;
|
||||
if (files[i].slice(0, 9) == 'v4appstore_' && files[i].slice(-4) == '.txt' && files[i] != 'appstore_en.txt') {
|
||||
var english_file = fs.readFileSync(local_file_name2, 'utf8');
|
||||
var compare_file = fs.readFileSync(path.join(__dirname, 'docs/' + files[i]), 'utf8')
|
||||
english_file = english_file.replace(/\r\n/g, '\n');
|
||||
compare_file = compare_file.replace(/\r\n/g, '\n');
|
||||
if (compare_file == english_file) {
|
||||
fs.unlinkSync(path.join(__dirname, 'docs/' + files[i]));
|
||||
};
|
||||
};
|
||||
if (files[i].slice(0, 11) == 'v4updateinfo_' && files[i].slice(-4) == '.txt' && files[i] != 'updateinfo_en.txt') {
|
||||
var english_file = fs.readFileSync(local_file_name3, 'utf8');
|
||||
var compare_file = fs.readFileSync(path.join(__dirname, 'docs/' + files[i]), 'utf8')
|
||||
english_file = english_file.replace(/\r\n/g, '\n');
|
||||
compare_file = compare_file.replace(/\r\n/g, '\n');
|
||||
if (compare_file == english_file) {
|
||||
fs.unlinkSync(path.join(__dirname, 'docs/' + files[i]));
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
console.log('Cleaned out completely untranslated appstore docs.');
|
||||
|
||||
var files = fs.readdirSync('./po');
|
||||
|
||||
for (var i in files) {
|
||||
if (files[i] != 'app.pot') {
|
||||
var po_file = fs.readFileSync(path.join(__dirname, 'po/' + files[i]), 'utf8');
|
||||
var po_array = po_file.split('\n');
|
||||
for (var j in po_array) {
|
||||
if (po_array[j].slice(0, 5) == 'msgid') {
|
||||
var source_text = po_array[j].slice(5);
|
||||
} else if (po_array[j].slice(0, 6) == 'msgstr') {
|
||||
var translate_text = po_array[j].slice(6);
|
||||
// if a line is not == English, it means there is translation. Keep this file.
|
||||
if (source_text != translate_text) {
|
||||
// erase email addresses of last translator for privacy
|
||||
po_file = po_file.replace(/ <.+@.+\..+>/, '')
|
||||
fs.writeFileSync(path.join(__dirname, 'po/' + files[i]), po_file);
|
||||
|
||||
// split the file into 3 parts, before locale, locale, and after locale.
|
||||
var lang_pos = po_file.search('"Language: ') + 11;
|
||||
var po_start = po_file.slice(0, lang_pos);
|
||||
var po_locale = po_file.slice(lang_pos, lang_pos + 5);
|
||||
var po_end = po_file.slice(lang_pos + 5);
|
||||
|
||||
// check for underscore, if it's there, only take the first 2 letters and reconstruct the po file.
|
||||
if (po_locale.search('_') > 0) {
|
||||
fs.writeFileSync(path.join(__dirname, 'po/' + files[i]), po_start + po_locale.slice(0, 2) + po_end);
|
||||
po_start = '';
|
||||
po_locale = '';
|
||||
po_end = '';
|
||||
};
|
||||
break;
|
||||
};
|
||||
};
|
||||
if (j == po_array.length - 1) { // All strings are exactly identical to English. Delete po file.
|
||||
fs.unlinkSync(path.join(__dirname, 'po/' + files[i]));
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
console.log('Cleaned out completely untranslated po files.');
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
}).on('error', function (e) {
|
||||
console.log('Export Got error: ' + e.message);
|
||||
});
|
||||
|
||||
} else { // Reminder: Any changes to the script below must also be made to the above and vice versa.
|
||||
|
||||
// Download most recent translations for all languages.
|
||||
https.get('https://api.crowdin.com/api/project/' + crowdin_identifier + '/download/all.zip?key=' + crowdin_api_key, function (res) {
|
||||
var data = [], dataLen = 0;
|
||||
|
||||
res.on('data', function (chunk) {
|
||||
data.push(chunk);
|
||||
dataLen += chunk.length;
|
||||
}).on('end', function () {
|
||||
var buf = new Buffer(dataLen);
|
||||
for (var i = 0, len = data.length, pos = 0; i < len; i++) {
|
||||
data[i].copy(buf, pos);
|
||||
pos += data[i].length;
|
||||
};
|
||||
var zip = new AdmZip(buf);
|
||||
zip.extractAllTo('./', true);
|
||||
console.log('Done extracting ZIP file.');
|
||||
|
||||
var files = fs.readdirSync('./docs');
|
||||
|
||||
for (var i in files) {
|
||||
if (files[i].slice(0, 9) == 'v4appstore_' && files[i].slice(-4) == '.txt' && files[i] != 'appstore_en.txt') {
|
||||
var english_file = fs.readFileSync(local_file_name2, 'utf8');
|
||||
var compare_file = fs.readFileSync(path.join(__dirname, 'docs/' + files[i]), 'utf8')
|
||||
english_file = english_file.replace(/\r\n/g, '\n');
|
||||
compare_file = compare_file.replace(/\r\n/g, '\n');
|
||||
if (compare_file == english_file) {
|
||||
fs.unlinkSync(path.join(__dirname, 'docs/' + files[i]));
|
||||
};
|
||||
};
|
||||
if (files[i].slice(0, 11) == 'v4updateinfo_' && files[i].slice(-4) == '.txt' && files[i] != 'updateinfo_en.txt') {
|
||||
var english_file = fs.readFileSync(local_file_name3, 'utf8');
|
||||
var compare_file = fs.readFileSync(path.join(__dirname, 'docs/' + files[i]), 'utf8')
|
||||
english_file = english_file.replace(/\r\n/g, '\n');
|
||||
compare_file = compare_file.replace(/\r\n/g, '\n');
|
||||
if (compare_file == english_file) {
|
||||
fs.unlinkSync(path.join(__dirname, 'docs/' + files[i]));
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
console.log('Cleaned out completely untranslated appstore docs.');
|
||||
|
||||
var files = fs.readdirSync('./po');
|
||||
|
||||
for (var i in files) {
|
||||
if (files[i] != 'app.pot') {
|
||||
var po_file = fs.readFileSync(path.join(__dirname, 'po/' + files[i]), 'utf8');
|
||||
var po_array = po_file.split('\n');
|
||||
for (var j in po_array) {
|
||||
if (po_array[j].slice(0, 5) == 'msgid') {
|
||||
var source_text = po_array[j].slice(5);
|
||||
} else if (po_array[j].slice(0, 6) == 'msgstr') {
|
||||
var translate_text = po_array[j].slice(6);
|
||||
// if a line is not == English, it means there is translation. Keep this file.
|
||||
if (source_text != translate_text) {
|
||||
// erase email addresses of last translator for privacy
|
||||
po_file = po_file.replace(/ <.+@.+\..+>/, '')
|
||||
fs.writeFileSync(path.join(__dirname, 'po/' + files[i]), po_file);
|
||||
|
||||
// split the file into 3 parts, before locale, locale, and after locale.
|
||||
var lang_pos = po_file.search('"Language: ') + 11;
|
||||
var po_start = po_file.slice(0, lang_pos);
|
||||
var po_locale = po_file.slice(lang_pos, lang_pos + 5);
|
||||
var po_end = po_file.slice(lang_pos + 5);
|
||||
|
||||
// check for underscore, if it's there, only take the first 2 letters and reconstruct the po file.
|
||||
if (po_locale.search('_') > 0) {
|
||||
fs.writeFileSync(path.join(__dirname, 'po/' + files[i]), po_start + po_locale.slice(0, 2) + po_end);
|
||||
po_start = '';
|
||||
po_locale = '';
|
||||
po_end = '';
|
||||
};
|
||||
break;
|
||||
};
|
||||
};
|
||||
if (j == po_array.length - 1) { // All strings are exactly identical to English. Delete po file.
|
||||
fs.unlinkSync(path.join(__dirname, 'po/' + files[i]));
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
console.log('Cleaned out completely untranslated po files.');
|
||||
|
||||
});
|
||||
});
|
||||
};
|
|
@ -0,0 +1,67 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
'use strict';
|
||||
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var https = require('https');
|
||||
var bhttp = require('bhttp');
|
||||
|
||||
var crowdin_identifier = 'copay'
|
||||
|
||||
var local_file_name1 = path.join(__dirname, 'app.pot')
|
||||
|
||||
// Similar to Github, normalize all line breaks to CRLF so that different people
|
||||
// using different OSes to update does not constantly swith format back and forth.
|
||||
var local_file1_text = fs.readFileSync(local_file_name1, 'utf8');
|
||||
local_file1_text = local_file1_text.replace(/\r\n/g, '\n');
|
||||
local_file1_text = local_file1_text.replace(/\n/g, '\r\n');
|
||||
fs.writeFileSync(local_file_name1, local_file1_text);
|
||||
|
||||
var local_file1 = fs.createReadStream(local_file_name1)
|
||||
|
||||
var local_file_name2 = path.join(__dirname, 'docs/appstore_en.txt')
|
||||
|
||||
var local_file2_text = fs.readFileSync(local_file_name2, 'utf8');
|
||||
local_file2_text = local_file2_text.replace(/\r\n/g, '\n');
|
||||
local_file2_text = local_file2_text.replace(/\n/g, '\r\n');
|
||||
fs.writeFileSync(local_file_name2, local_file2_text);
|
||||
|
||||
var local_file2 = fs.createReadStream(local_file_name2)
|
||||
|
||||
var local_file_name3 = path.join(__dirname, 'docs/updateinfo_en.txt')
|
||||
|
||||
var local_file3_text = fs.readFileSync(local_file_name3, 'utf8');
|
||||
local_file3_text = local_file3_text.replace(/\r\n/g, '\n');
|
||||
local_file3_text = local_file3_text.replace(/\n/g, '\r\n');
|
||||
fs.writeFileSync(local_file_name3, local_file3_text);
|
||||
|
||||
var local_file3 = fs.createReadStream(local_file_name3)
|
||||
|
||||
// obtain the crowdin api key
|
||||
var crowdin_api_key = fs.readFileSync(path.join(__dirname, 'crowdin_api_key.txt'))
|
||||
//console.log('api key: ' + crowdin_api_key);
|
||||
|
||||
if (crowdin_api_key != '') {
|
||||
|
||||
var payload = {
|
||||
'files[app.pot]': local_file1,
|
||||
'files[appstore/appstore_en.txt]': local_file2,
|
||||
'files[appstore/updateinfo_en.txt]': local_file3
|
||||
};
|
||||
|
||||
bhttp.post('https://api.crowdin.com/api/project/' + crowdin_identifier + '/update-file?key=' + crowdin_api_key, payload, {}, function (err, response) {
|
||||
if (!err) console.log('\nResponse from update file call:\n', response.body.toString());
|
||||
else console.log('\nError from update file call:\n', err.toString());
|
||||
|
||||
// This call will tell the server to generate a new zip file for you based on most recent translations.
|
||||
https.get('https://api.crowdin.com/api/project/' + crowdin_identifier + '/export?key=' + crowdin_api_key, function (res) {
|
||||
console.log('Export Got response: ' + res.statusCode);
|
||||
res.on('data', function (chunk) {
|
||||
console.log(chunk.toString('utf8'));
|
||||
});
|
||||
}).on('error', function (e) {
|
||||
console.log('Export Got error: ' + e.message);
|
||||
});
|
||||
})
|
||||
};
|
|
@ -4,7 +4,7 @@
|
|||
</ion-navbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
<ion-content no-bounce>
|
||||
<ion-list>
|
||||
<button class="list-button" ion-item (click)="goToCreateWallet(false)">
|
||||
<ion-icon item-start>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
</ion-header>
|
||||
|
||||
|
||||
<ion-content>
|
||||
<ion-content no-bounce>
|
||||
<div *ngIf="wallet" [hidden]="wallet.notAuthorized">
|
||||
<ion-list class="copayers-secret">
|
||||
<ion-item>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
</ion-navbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
<ion-content no-bounce>
|
||||
<form [formGroup]="createForm" (ngSubmit)="setOptsAndCreate()">
|
||||
<ion-item>
|
||||
<ion-label stacked>Wallet name</ion-label>
|
||||
|
@ -65,34 +65,6 @@
|
|||
<ion-input type="text" formControlName="recoveryPhrase"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label stacked>Add a password</ion-label>
|
||||
<ion-toggle formControlName="addPassword" (ionChange)="resetFormFields()"></ion-toggle>
|
||||
</ion-item>
|
||||
|
||||
<div *ngIf="createForm.value.addPassword">
|
||||
<ion-item>
|
||||
<ion-label stacked>Password</ion-label>
|
||||
<ion-input type="password" formControlName="password"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label stacked>Confirm password</ion-label>
|
||||
<ion-input type="password" formControlName="confirmPassword"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<div class="warning">
|
||||
<strong translate>This password cannot be recovered. If the password is lost, there is no way you could recover your funds.</strong>
|
||||
</div>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label stacked>I have written it down</ion-label>
|
||||
<ion-checkbox formControlName="recoveryPhraseBackedUp" checked="false"></ion-checkbox>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
<ion-item *ngIf="createForm.value.selectedSeed == 'new' && createForm.value.coin == 'btc'">
|
||||
<ion-label stacked>Testnet</ion-label>
|
||||
<ion-toggle formControlName="testnetEnabled" (ionChange)="setDerivationPath()"></ion-toggle>
|
||||
|
@ -112,5 +84,5 @@
|
|||
</ion-content>
|
||||
|
||||
<ion-footer>
|
||||
<button ion-button block class="button-footer" (click)="setOptsAndCreate()" [disabled]="!createForm.valid || validatePasswords()">Create wallet</button>
|
||||
<button ion-button block class="button-footer" (click)="setOptsAndCreate()" [disabled]="!createForm.valid">Create wallet</button>
|
||||
</ion-footer>
|
||||
|
|
|
@ -1,9 +1,2 @@
|
|||
.warning {
|
||||
background-color: #ef473a;
|
||||
border-color: #e42112;
|
||||
color: #fff;
|
||||
padding: 0.5rem;
|
||||
border: 1px solid;
|
||||
white-space: normal;
|
||||
text-align: center;
|
||||
page-create-wallet {
|
||||
}
|
|
@ -83,10 +83,6 @@ export class CreateWalletPage implements OnInit {
|
|||
bwsURL: [this.defaults.bws.url],
|
||||
selectedSeed: ['new'],
|
||||
recoveryPhrase: [null],
|
||||
addPassword: [false],
|
||||
password: [null],
|
||||
confirmPassword: [null],
|
||||
recoveryPhraseBackedUp: [null],
|
||||
derivationPath: [this.derivationPathByDefault],
|
||||
testnetEnabled: [false],
|
||||
singleAddress: [false],
|
||||
|
@ -95,52 +91,26 @@ export class CreateWalletPage implements OnInit {
|
|||
|
||||
this.setTotalCopayers(this.tc);
|
||||
this.updateRCSelect(this.tc);
|
||||
this.resetPasswordFields();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
if (this.isShared) {
|
||||
this.createForm.get('myName').setValidators([Validators.required]);
|
||||
}
|
||||
|
||||
this.createForm.get('addPassword').valueChanges.subscribe((addPassword: boolean) => {
|
||||
if (addPassword) {
|
||||
this.createForm.get('password').setValidators([Validators.required]);
|
||||
this.createForm.get('confirmPassword').setValidators([Validators.required]);
|
||||
} else {
|
||||
this.createForm.get('password').clearValidators();
|
||||
this.createForm.get('confirmPassword').clearValidators();
|
||||
}
|
||||
this.createForm.get('password').updateValueAndValidity();
|
||||
this.createForm.get('confirmPassword').updateValueAndValidity();
|
||||
});
|
||||
}
|
||||
|
||||
public validatePasswords(): boolean {
|
||||
if (this.createForm.value.addPassword) {
|
||||
if (this.createForm.value.password == this.createForm.value.confirmPassword) {
|
||||
if (this.createForm.value.recoveryPhraseBackedUp) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public setTotalCopayers(n: number): void {
|
||||
this.createForm.value.totalCopayers = n;
|
||||
this.createForm.controls['totalCopayers'].setValue(n);
|
||||
this.updateRCSelect(n);
|
||||
this.updateSeedSourceSelect();
|
||||
};
|
||||
|
||||
private updateRCSelect(n: number): void {
|
||||
this.createForm.value.totalCopayers = n;
|
||||
this.createForm.controls['totalCopayers'].setValue(n);
|
||||
var maxReq = this.COPAYER_PAIR_LIMITS[n];
|
||||
this.signatures = _.range(1, maxReq + 1);
|
||||
this.createForm.value.requiredCopayers = Math.min(Math.trunc(n / 2 + 1), maxReq);
|
||||
};
|
||||
this.createForm.controls['requiredCopayers'].setValue(Math.min(Math.trunc(n / 2 + 1), maxReq));
|
||||
|
||||
private resetPasswordFields(): void {
|
||||
this.createForm.value.password = this.createForm.value.confirmPassword = this.createForm.value.recoveryPhraseBackedUp = null;
|
||||
};
|
||||
|
||||
private updateSeedSourceSelect(): void {
|
||||
|
@ -153,7 +123,7 @@ export class CreateWalletPage implements OnInit {
|
|||
label: 'Specify Recovery Phrase',
|
||||
supportsTestnet: false
|
||||
}];
|
||||
this.createForm.value.selectedSeed = this.seedOptions[0].id;
|
||||
this.createForm.controls['selectedSeed'].setValue(this.seedOptions[0].id); // new or set
|
||||
};
|
||||
|
||||
public seedOptionsChange(seed: any): void {
|
||||
|
@ -162,21 +132,15 @@ export class CreateWalletPage implements OnInit {
|
|||
} else {
|
||||
this.createForm.get('recoveryPhrase').setValidators(null);
|
||||
}
|
||||
this.createForm.value.selectedSeed = seed; // new or set
|
||||
this.createForm.value.testnet = false;
|
||||
this.createForm.value.derivationPath = this.derivationPathByDefault;
|
||||
this.resetFormFields();
|
||||
}
|
||||
|
||||
public resetFormFields(): void {
|
||||
this.createForm.value.password = null;
|
||||
this.createForm.value.confirmPassword = null;
|
||||
this.createForm.value.recoveryPhraseBackedUp = null;
|
||||
this.createForm.value.recoveryPhrase = null;
|
||||
this.createForm.controls['selectedSeed'].setValue(seed); // new or set
|
||||
this.createForm.controls['testnet'].setValue(false);
|
||||
this.createForm.controls['derivationPath'].setValue(this.derivationPathByDefault);
|
||||
this.createForm.controls['recoveryPhrase'].setValue(null);
|
||||
}
|
||||
|
||||
public setDerivationPath(): void {
|
||||
this.createForm.value.derivationPath = this.createForm.value.testnet ? this.derivationPathForTestnet : this.derivationPathByDefault;
|
||||
let path: string = this.createForm.value.testnet ? this.derivationPathForTestnet : this.derivationPathByDefault;
|
||||
this.createForm.controls['derivationPath'].setValue(path);
|
||||
}
|
||||
|
||||
public setOptsAndCreate(): void {
|
||||
|
@ -201,7 +165,6 @@ export class CreateWalletPage implements OnInit {
|
|||
} else {
|
||||
opts.mnemonic = words;
|
||||
}
|
||||
opts.passphrase = this.createForm.value.password;
|
||||
|
||||
let pathData = this.derivationPathHelperProvider.parse(this.createForm.value.derivationPath);
|
||||
if (!pathData) {
|
||||
|
@ -212,8 +175,6 @@ export class CreateWalletPage implements OnInit {
|
|||
opts.networkName = pathData.networkName;
|
||||
opts.derivationStrategy = pathData.derivationStrategy;
|
||||
|
||||
} else {
|
||||
opts.passphrase = this.createForm.value.password;
|
||||
}
|
||||
|
||||
if (setSeed && !opts.mnemonic && !opts.extendedPrivateKey) {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
</ion-navbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
<ion-content no-bounce>
|
||||
<ion-segment [(ngModel)]="selectedTab" color="primary" (ionChange)="selectTab(selectedTab)">
|
||||
<ion-segment-button value="words" translate>
|
||||
Words
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
</ion-navbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
<ion-content no-bounce>
|
||||
<form [formGroup]="joinForm" (ngSubmit)="setOptsAndJoin()">
|
||||
<ion-item>
|
||||
<ion-label stacked>{{'Your name' | translate}}</ion-label>
|
||||
|
@ -50,34 +50,6 @@
|
|||
<ion-input type="text" formControlName="recoveryPhrase"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label stacked>{{'Add a password' | translate}}</ion-label>
|
||||
<ion-toggle formControlName="addPassword" (ionChange)="resetFormFields()"></ion-toggle>
|
||||
</ion-item>
|
||||
|
||||
<div *ngIf="joinForm.value.addPassword">
|
||||
<ion-item>
|
||||
<ion-label stacked>{{'Password' | translate}}</ion-label>
|
||||
<ion-input type="password" formControlName="password"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label stacked>{{'Confirm password' | translate}}</ion-label>
|
||||
<ion-input type="password" formControlName="confirmPassword"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<div class="warning">
|
||||
<strong translate>This password cannot be recovered. If the password is lost, there is no way you could recover your funds.</strong>
|
||||
</div>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label stacked>{{'I have written it down' | translate}}</ion-label>
|
||||
<ion-checkbox formControlName="recoveryPhraseBackedUp" checked="false"></ion-checkbox>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
<ion-item *ngIf="joinForm.value.selectedSeed == 'set'">
|
||||
<ion-label stacked>{{'Derivation path' | translate}}</ion-label>
|
||||
<ion-input type="text" formControlName="derivationPath"></ion-input>
|
||||
|
@ -87,6 +59,5 @@
|
|||
</ion-content>
|
||||
|
||||
<ion-footer>
|
||||
<button ion-button block class="button-footer" (click)="setOptsAndJoin()" [disabled]="!joinForm.valid || validatePasswords()"
|
||||
translate>Join wallet</button>
|
||||
<button ion-button block class="button-footer" (click)="setOptsAndJoin()" [disabled]="!joinForm.valid" translate>Join wallet</button>
|
||||
</ion-footer>
|
|
@ -1,4 +1,4 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component } from '@angular/core';
|
||||
import { NavController, NavParams } from 'ionic-angular';
|
||||
import { Validators, FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { Logger } from '../../../providers/logger/logger';
|
||||
|
@ -19,7 +19,7 @@ import { WalletProvider } from '../../../providers/wallet/wallet';
|
|||
selector: 'page-join-wallet',
|
||||
templateUrl: 'join-wallet.html'
|
||||
})
|
||||
export class JoinWalletPage implements OnInit {
|
||||
export class JoinWalletPage {
|
||||
|
||||
private defaults: any;
|
||||
public showAdvOpts: boolean;
|
||||
|
@ -54,10 +54,6 @@ export class JoinWalletPage implements OnInit {
|
|||
bwsURL: [this.defaults.bws.url],
|
||||
selectedSeed: ['new'],
|
||||
recoveryPhrase: [null],
|
||||
addPassword: [false],
|
||||
password: [null],
|
||||
confirmPassword: [null],
|
||||
recoveryPhraseBackedUp: [null],
|
||||
derivationPath: [this.derivationPathByDefault],
|
||||
coin: [this.navParams.data.coin ? this.navParams.data.coin : 'btc']
|
||||
});
|
||||
|
@ -75,7 +71,6 @@ export class JoinWalletPage implements OnInit {
|
|||
|
||||
ionViewDidLoad() {
|
||||
this.logger.info('ionViewDidLoad JoinWalletPage');
|
||||
this.resetFormFields();
|
||||
}
|
||||
|
||||
ionViewWillEnter() {
|
||||
|
@ -86,30 +81,6 @@ export class JoinWalletPage implements OnInit {
|
|||
}
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.joinForm.get('addPassword').valueChanges.subscribe((addPassword: boolean) => {
|
||||
if (addPassword) {
|
||||
this.joinForm.get('password').setValidators([Validators.required]);
|
||||
this.joinForm.get('confirmPassword').setValidators([Validators.required]);
|
||||
} else {
|
||||
this.joinForm.get('password').clearValidators();
|
||||
this.joinForm.get('confirmPassword').clearValidators();
|
||||
}
|
||||
this.joinForm.get('password').updateValueAndValidity();
|
||||
this.joinForm.get('confirmPassword').updateValueAndValidity();
|
||||
})
|
||||
}
|
||||
|
||||
public validatePasswords(): boolean {
|
||||
if (this.joinForm.value.addPassword) {
|
||||
if (this.joinForm.value.password == this.joinForm.value.confirmPassword) {
|
||||
if (this.joinForm.value.recoveryPhraseBackedUp) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public onQrCodeScannedJoin(data: string): void { // TODO
|
||||
this.joinForm.controls['invitationCode'].setValue(data);
|
||||
}
|
||||
|
@ -123,14 +94,6 @@ export class JoinWalletPage implements OnInit {
|
|||
this.joinForm.controls['selectedSeed'].setValue(seed);
|
||||
this.joinForm.controls['testnet'].setValue(false);
|
||||
this.joinForm.controls['derivationPath'].setValue(this.derivationPathByDefault);
|
||||
this.resetFormFields();
|
||||
}
|
||||
|
||||
public resetFormFields(): void {
|
||||
this.joinForm.controls['password'].setValue(null);
|
||||
this.joinForm.controls['confirmPassword'].setValue(null);
|
||||
this.joinForm.controls['recoveryPhraseBackedUp'].setValue(null);
|
||||
this.joinForm.controls['recoveryPhrase'].setValue(null);
|
||||
}
|
||||
|
||||
setDerivationPath() {
|
||||
|
@ -155,7 +118,6 @@ export class JoinWalletPage implements OnInit {
|
|||
} else {
|
||||
opts.mnemonic = words;
|
||||
}
|
||||
opts.passphrase = this.joinForm.value.password;
|
||||
|
||||
let pathData = this.derivationPathHelperProvider.parse(this.joinForm.value.derivationPath);
|
||||
if (!pathData) {
|
||||
|
@ -165,8 +127,6 @@ export class JoinWalletPage implements OnInit {
|
|||
|
||||
opts.networkName = pathData.networkName;
|
||||
opts.derivationStrategy = pathData.derivationStrategy;
|
||||
} else {
|
||||
opts.passphrase = this.joinForm.value.password;
|
||||
}
|
||||
|
||||
if (setSeed && !opts.mnemonic && !opts.extendedPrivateKey) {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
</ion-header>
|
||||
|
||||
|
||||
<ion-content>
|
||||
<ion-content no-bounce>
|
||||
<div *ngIf="deleted">
|
||||
<h1 class="deleted-title">{{'Wallet recovery phrase not available' |translate}}</h1>
|
||||
<ion-item-divider text-wrap>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
</ion-header>
|
||||
|
||||
|
||||
<ion-content>
|
||||
<ion-content no-bounce>
|
||||
<h1 translate>Are you being watched?</h1>
|
||||
<p translate>Now is a perfect time to assess your surroundings. Nearby windows? Hidden cameras? Shoulder-spies?</p>
|
||||
<img src='assets/img/backup/backup-warning.svg' />
|
||||
|
|
|
@ -61,34 +61,30 @@
|
|||
</ion-card>
|
||||
|
||||
<ion-list *ngIf="txps && txps[0]">
|
||||
<ion-item-divider>
|
||||
<ion-row>
|
||||
<ion-col class="title">
|
||||
<ion-list-header color="light">
|
||||
<div item-start>
|
||||
<span translate>Payment Proposals</span>
|
||||
</ion-col>
|
||||
<ion-col text-right (click)="openProposalsPage()">
|
||||
<ion-badge *ngIf="txpsN>3" item-end>{{txpsN}}</ion-badge>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-item-divider>
|
||||
</div>
|
||||
<button ion-button item-end (click)="openProposalsPage()" *ngIf="txpsN>3">
|
||||
<ion-badge>{{txpsN}}</ion-badge>
|
||||
</button>
|
||||
</ion-list-header>
|
||||
<page-txp *ngFor="let txp of txps" [tx]="txp" [addressbook]="addressbook"></page-txp>
|
||||
</ion-list>
|
||||
|
||||
<ion-list *ngIf="notifications && notifications[0] && recentTransactionsEnabled">
|
||||
<ion-item-divider>
|
||||
<ion-row>
|
||||
<ion-col class="title">
|
||||
<span translate>Recent Transactions</span>
|
||||
</ion-col>
|
||||
<ion-col text-right (click)="openActivityPage()">
|
||||
<ion-badge *ngIf="notificationsN>3" item-end>{{notificationsN}}</ion-badge>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-item-divider>
|
||||
<ion-list-header color="light">
|
||||
<div item-start>
|
||||
<span translate>Recent Transactions</span>
|
||||
</div>
|
||||
<button ion-button item-end (click)="openActivityPage()" *ngIf="notificationsN>3">
|
||||
<ion-badge>{{notificationsN}}</ion-badge>
|
||||
</button>
|
||||
</ion-list-header>
|
||||
|
||||
<button ion-item *ngFor="let notification of notifications" (click)="openNotificationModal(notification)">
|
||||
<span *ngFor="let notification of notifications" (click)="openNotificationModal(notification)">
|
||||
<page-wallet-activity [notification]="notification"></page-wallet-activity>
|
||||
</button>
|
||||
</span>
|
||||
|
||||
</ion-list>
|
||||
|
||||
|
@ -99,25 +95,25 @@
|
|||
</div>
|
||||
|
||||
<ion-list *ngIf="walletsBtc && walletsBtc[0]">
|
||||
<ion-item-divider>
|
||||
<ion-row>
|
||||
<ion-col class="title">
|
||||
<ion-list-header color="light">
|
||||
<div class="title" item-start>
|
||||
<img src="assets/img/icon-bitcoin.svg" alt="Bitcoin Wallets" width="18" />
|
||||
<span translate>Bitcoin Wallets</span>
|
||||
</ion-col>
|
||||
<ion-col text-right class="icons-add-reorder" *ngIf="!showReorderBtc">
|
||||
<div class="reorder-icon" (click)="reorderBtc()" *ngIf="walletsBtc.length > 1">
|
||||
</div>
|
||||
<div item-end *ngIf="!showReorderBtc">
|
||||
<button ion-button clear icon-only color="secondary" (click)="reorderBtc()" *ngIf="walletsBtc.length > 1">
|
||||
<ion-icon name="reorder"></ion-icon>
|
||||
</div>
|
||||
<div class="add-icon" (click)="goToAddView()">
|
||||
</button>
|
||||
<button ion-button clear icon-only color="primary" (click)="goToAddView()">
|
||||
<ion-icon name="add"></ion-icon>
|
||||
</div>
|
||||
</ion-col>
|
||||
<ion-col text-right class="icons-add-reorder" *ngIf="showReorderBtc" (click)="reorderBtc()">
|
||||
{{'Done'|translate}}
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-item-divider>
|
||||
</button>
|
||||
</div>
|
||||
<div item-end *ngIf="showReorderBtc">
|
||||
<button ion-button clear color="secondary" (click)="reorderBtc()">
|
||||
{{'Done'|translate}}
|
||||
</button>
|
||||
</div>
|
||||
</ion-list-header>
|
||||
|
||||
<ion-list reorder="{{showReorderBtc}}" (ionItemReorder)="reorderWalletsBtc($event)">
|
||||
<button ion-item *ngFor="let wallet of walletsBtc" (click)="goToWalletDetails(wallet)">
|
||||
|
@ -151,25 +147,25 @@
|
|||
</ion-list>
|
||||
|
||||
<ion-list *ngIf="walletsBch && walletsBch[0]">
|
||||
<ion-item-divider>
|
||||
<ion-row>
|
||||
<ion-col class="title">
|
||||
<img src="assets/img/bitcoin-cash-logo.svg" alt="Bitcoin Cash Wallets" width="22" />
|
||||
<span translate>Bitcoin Cash Wallets</span>
|
||||
</ion-col>
|
||||
<ion-col class="icons-add-reorder" *ngIf="!showReorderBch" text-right>
|
||||
<div class="reorder-icon" (click)="reorderBch()" *ngIf="walletsBch.length > 1">
|
||||
<ion-icon name="reorder"></ion-icon>
|
||||
</div>
|
||||
<div class="add-icon" (click)="goToAddView('bch')">
|
||||
<ion-icon name="add"></ion-icon>
|
||||
</div>
|
||||
</ion-col>
|
||||
<ion-col text-right class="icons-add-reorder" *ngIf="showReorderBch" (click)="reorderBch()">
|
||||
<ion-list-header color="light">
|
||||
<div class="title" item-start>
|
||||
<img src="assets/img/bitcoin-cash-logo.svg" alt="Bitcoin Cash Wallets" width="22" />
|
||||
<span translate>Bitcoin Cash Wallets</span>
|
||||
</div>
|
||||
<div item-end *ngIf="!showReorderBch">
|
||||
<button ion-button clear icon-only color="secondary" (click)="reorderBch()" *ngIf="walletsBch.length > 1">
|
||||
<ion-icon name="reorder"></ion-icon>
|
||||
</button>
|
||||
<button ion-button clear icon-only color="primary" (click)="goToAddView('bch')">
|
||||
<ion-icon name="add"></ion-icon>
|
||||
</button>
|
||||
</div>
|
||||
<div item-end *ngIf="showReorderBch">
|
||||
<button ion-button clear color="secondary" (click)="reorderBch()">
|
||||
{{'Done'|translate}}
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-item-divider>
|
||||
</button>
|
||||
</div>
|
||||
</ion-list-header>
|
||||
|
||||
<ion-list reorder="{{showReorderBch}}" (ionItemReorder)="reorderWalletsBch($event)">
|
||||
<button ion-item *ngFor="let wallet of walletsBch" (click)="goToWalletDetails(wallet)">
|
||||
|
@ -203,7 +199,7 @@
|
|||
</ion-list>
|
||||
|
||||
<ion-list *ngIf="wallets && wallets[0] && buyAndSellItems && buyAndSellItems.length>0">
|
||||
<ion-item-divider>
|
||||
<ion-item-divider color="light">
|
||||
<ion-row>
|
||||
<ion-col class="title">
|
||||
<span translate>Buy & Sell Bitcoin</span>
|
||||
|
@ -224,7 +220,7 @@
|
|||
</ion-list>
|
||||
|
||||
<ion-list *ngIf="homeIntegrations && homeIntegrations.length>0">
|
||||
<ion-item-divider>
|
||||
<ion-item-divider color="light">
|
||||
<ion-row>
|
||||
<ion-col class="title">
|
||||
<span translate>Services</span>
|
||||
|
@ -246,7 +242,7 @@
|
|||
</ion-list>
|
||||
|
||||
<ion-list *ngIf="nextStepsItems && nextStepsItems.length>0">
|
||||
<ion-item-divider>
|
||||
<ion-item-divider color="light">
|
||||
<ion-row>
|
||||
<ion-col class="title">
|
||||
<span translate>Next steps</span>
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
page-home {
|
||||
ion-item-divider {
|
||||
margin-top: 20px;
|
||||
}
|
||||
.home-logo {
|
||||
height: 24px;
|
||||
position: relative;
|
||||
|
@ -24,29 +21,9 @@ page-home {
|
|||
}
|
||||
}
|
||||
.title {
|
||||
color: color($colors, dark);
|
||||
font-weight: 700;
|
||||
&.col {
|
||||
padding: 0;
|
||||
}
|
||||
>img {
|
||||
width: 22px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
.icons-add-reorder {
|
||||
justify-content: flex-end;
|
||||
display: flex;
|
||||
.add-icon {
|
||||
padding-left: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.reorder-icon {
|
||||
cursor: pointer;
|
||||
vertical-align: sub;
|
||||
margin-right: 5px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
}
|
||||
.error {
|
||||
|
|
|
@ -12,6 +12,10 @@
|
|||
<span *ngIf="tx.merchant.pr.ca"><ion-icon class="fi-lock"></ion-icon> {{tx.merchant.domain}}</span>
|
||||
<span *ngIf="!tx.merchant.pr.ca"><ion-icon class="fion-icon-unlock"></ion-icon> {{tx.merchant.domain}}</span>
|
||||
</span>
|
||||
<div class="copayer">
|
||||
<span *ngIf="tx.creatorName">{{ tx.creatorName}}@</span>
|
||||
<span>{{tx.wallet.name}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ion-note item-end>
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
page-txp{
|
||||
img {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: $icon-border-radius;
|
||||
}
|
||||
.label {
|
||||
display: -webkit-inline-box;
|
||||
|
@ -9,6 +11,10 @@ page-txp{
|
|||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
.copayer {
|
||||
font-size: 12.5px;
|
||||
font-weight: 300;
|
||||
}
|
||||
}
|
||||
ion-note {
|
||||
text-align: end;
|
||||
|
|
|
@ -1,65 +1,55 @@
|
|||
<div class="wallet-activity">
|
||||
<div *ngIf="notification.type == 'NewCopayer' && notification.wallet.n>1" translate>
|
||||
Copayer joined
|
||||
<button ion-item>
|
||||
|
||||
<ion-icon class="wallet-icon-container" item-start>
|
||||
<div class="icon-color" [ngClass]="{'wallet-color-default': !notification.wallet.color}" [ngStyle]="{'background-color':notification.wallet.color}"></div>
|
||||
</ion-icon>
|
||||
|
||||
<div *ngIf="notification.type == 'NewCopayer' && notification.wallet.n>1">
|
||||
<span translate>Copayer joined</span>
|
||||
</div>
|
||||
|
||||
<div *ngIf="notification.type == 'NewCopayer' && notification.wallet.n==1" translate>
|
||||
Wallet created
|
||||
<div *ngIf="notification.type == 'NewCopayer' && notification.wallet.n==1">
|
||||
<span translate>Wallet created</span>
|
||||
</div>
|
||||
|
||||
<div *ngIf="notification.type == 'NewOutgoingTx'">
|
||||
<span translate>Payment Sent </span>
|
||||
<div class="wallet-activity-amount">
|
||||
{{notification.data.amount | satToUnit: notification.wallet.coin}}
|
||||
</div>
|
||||
<span translate>Payment Sent</span>
|
||||
</div>
|
||||
|
||||
<div *ngIf="notification.type == 'NewIncomingTx'">
|
||||
<span translate>Payment Received</span>
|
||||
<div class="wallet-activity-amount">
|
||||
{{notification.data.amount | satToUnit: notification.wallet.coin}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="notification.type == 'TxProposalRemoved'">
|
||||
<span translate>Proposal Deleted</span>:
|
||||
<b>{{notification.message}}</b>
|
||||
<div class="wallet-activity-amount">
|
||||
{{notification.data.amount | satToUnit: notification.wallet.coin}}:
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="notification.type == 'TxProposalRejectedBy'">
|
||||
<span translate>Proposal Rejected</span>:
|
||||
<b>{{notification.message}}</b>
|
||||
<div class="wallet-activity-amount">
|
||||
{{notification.data.amount | satToUnit: notification.wallet.coin}}:
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="notification.type == 'NewTxProposal'">
|
||||
<span translate>New Proposal</span>:
|
||||
<b>{{notification.message}}</b>
|
||||
<div class="wallet-activity-amount">
|
||||
{{notification.data.amount | satToUnit: notification.wallet.coin}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="notification.type == 'TxProposalAcceptedBy'">
|
||||
<span translate>Proposal Accepted</span>:
|
||||
<b>{{notification.message}}</b>
|
||||
<div class="wallet-activity-amount">
|
||||
{{notification.data.amount | satToUnit: notification.wallet.coin}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wallet-activity-note">
|
||||
<!-- {{notification.types}} -->
|
||||
<div>
|
||||
<span class="wallet-activity-note-child color" [ngClass]="{'wallet-color-default': !notification.wallet.color}" [ngStyle]="{'background-color':notification.wallet.color}"></span>
|
||||
<span *ngIf="notification.creatorName" class="wallet-activity-note-child">{{ notification.creatorName}}@</span>
|
||||
<span class="wallet-activity-note-child">{{notification.wallet.name}}</span>
|
||||
</div>
|
||||
<time class="wallet-activity-note-child">{{ notification.createdOn * 1000 | amTimeAgo}}</time>
|
||||
<div class="copayer">
|
||||
<span *ngIf="notification.creatorName">{{ notification.creatorName}}@</span>
|
||||
<span>{{notification.wallet.name}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ion-note item-end>
|
||||
<!-- {{notification.types}} -->
|
||||
<div class="amount">
|
||||
{{notification.data.amount | satToUnit: notification.wallet.coin}}
|
||||
</div>
|
||||
<time class="date">{{ notification.createdOn * 1000 | amTimeAgo}}</time>
|
||||
</ion-note>
|
||||
</button>
|
||||
|
|
|
@ -2,39 +2,30 @@ page-wallet-activity {
|
|||
.wallet-color-default {
|
||||
background-color: color($colors, primary);
|
||||
}
|
||||
.wallet-activity {
|
||||
|
||||
&-not-pending {
|
||||
opacity: 0.6;
|
||||
filter: alpha(opacity=60); /* For IE8 and earlier */
|
||||
}
|
||||
|
||||
&-amount {
|
||||
float: right;
|
||||
font-size: 18px;
|
||||
@media(max-width: 320px) {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
&-note {
|
||||
margin-top: 3px;
|
||||
font-size: 12px!important;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
&-child {
|
||||
line-height: 30px;
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
}
|
||||
.color {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
border-radius: 2rem;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
.copayer {
|
||||
font-size: 12.5px;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
ion-note {
|
||||
text-align: end;
|
||||
.amount {
|
||||
color: color($colors, dark);
|
||||
}
|
||||
.date {
|
||||
font-size: 12.5px;
|
||||
}
|
||||
}
|
||||
|
||||
.wallet-icon-container {
|
||||
width: 37px;
|
||||
}
|
||||
|
||||
.icon-color {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
border-radius: $txp-icon-border-radius;
|
||||
margin: 8px auto;
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
</ion-header>
|
||||
|
||||
|
||||
<ion-content>
|
||||
<ion-content no-bounce>
|
||||
<ion-list>
|
||||
<ion-item>
|
||||
<div class="sending-label">
|
||||
|
@ -48,6 +48,8 @@
|
|||
</ion-item>
|
||||
</div>
|
||||
|
||||
<ion-item-divider color="light"></ion-item-divider>
|
||||
|
||||
<ion-item *ngIf="fiatFee && feeRatePerStr">
|
||||
{{'Fee'|translate}}
|
||||
<ion-note item-end>
|
||||
|
@ -66,13 +68,11 @@
|
|||
</ion-note>
|
||||
</ion-item>
|
||||
|
||||
<ion-item-divider *ngIf="withdrawalStr" no-lines text-wrap>
|
||||
A total of {{amountStr}} ({{fiatAmount | currency}} {{currencyIsoCode}}) will be exchanged for {{withdrawalStr}} ({{fiatWithdrawal | currency}} {{currencyIsoCode}}). Would you like to proceed?
|
||||
</ion-item-divider>
|
||||
</ion-list>
|
||||
|
||||
<p class="text-confirm" *ngIf="withdrawalStr">
|
||||
A total of {{amountStr}} ({{fiatAmount | currency}} {{currencyIsoCode}}) will be exchanged for {{withdrawalStr}} ({{fiatWithdrawal
|
||||
| currency}} {{currencyIsoCode}}). Would you like to proceed?
|
||||
</p>
|
||||
|
||||
</ion-content>
|
||||
|
||||
<ion-footer *ngIf="fromWallet && toWallet && totalAmountStr">
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
</ion-header>
|
||||
|
||||
|
||||
<ion-content>
|
||||
<ion-content no-bounce>
|
||||
<div class="header-modal">
|
||||
<div class="title-modal">
|
||||
<img src="assets/img/shapeshift/icon-shapeshift.svg" alt="ShapeShift" width="50"> {{ssData.title}}
|
||||
|
@ -56,8 +56,8 @@
|
|||
<ion-item-divider color="light">Deposit</ion-item-divider>
|
||||
|
||||
<ion-item>
|
||||
Adddress
|
||||
<ion-note item-end>
|
||||
Address
|
||||
<ion-note item-end text-wrap>
|
||||
<span *ngIf="!ssData.address">...</span>
|
||||
<span *ngIf="ssData.address" copy-to-clipboard="{{ssData.address}}">
|
||||
{{ ssData.address }}
|
||||
|
@ -78,8 +78,8 @@
|
|||
<ion-item-divider color="light">Withdraw</ion-item-divider>
|
||||
|
||||
<ion-item>
|
||||
Adddress
|
||||
<ion-note item-end>
|
||||
Address
|
||||
<ion-note item-end text-wrap>
|
||||
<span *ngIf="!ssData.withdrawal">...</span>
|
||||
<span *ngIf="ssData.withdrawal" copy-to-clipboard="{{ssData.withdrawal}}">
|
||||
{{ ssData.withdrawal }}
|
||||
|
@ -99,11 +99,11 @@
|
|||
|
||||
<ion-item-divider color="light"></ion-item-divider>
|
||||
|
||||
<button ion-item *ngIf="ssData.transaction" (click)="openTransaction(ssData.transaction)">
|
||||
<button ion-item detail-none *ngIf="ssData.transaction" (click)="openTransaction(ssData.transaction)">
|
||||
See transaction
|
||||
</button>
|
||||
<ion-item-divider color="light"></ion-item-divider>
|
||||
<button ion-item (click)="remove()">
|
||||
<button class="assertive" ion-item detail-none (click)="remove()">
|
||||
Remove
|
||||
</button>
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
page-shapeshift-details {
|
||||
.header-modal {
|
||||
padding-top: 10px;
|
||||
.title-modal {
|
||||
font-size: 26px;
|
||||
text-align: center;
|
||||
|
@ -8,7 +9,7 @@ page-shapeshift-details {
|
|||
}
|
||||
}
|
||||
.subtitle-modal {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
}
|
||||
.status-modal {
|
||||
|
|
|
@ -73,6 +73,11 @@ export class ShapeshiftShiftPage {
|
|||
return hasCachedFunds;
|
||||
});
|
||||
|
||||
if (_.isEmpty(this.fromWallets)) {
|
||||
this.showErrorAndBack(null, 'No wallets with funds'); // TODO: gettextCatalog
|
||||
return;
|
||||
}
|
||||
|
||||
this.onFromWalletSelect(this.fromWallets[0]);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<ion-header>
|
||||
<ion-navbar>
|
||||
<ion-title>shapeShift</ion-title>
|
||||
<ion-title>ShapeShift</ion-title>
|
||||
</ion-navbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content *ngIf="!shifts.data">
|
||||
<ion-content *ngIf="!shifts.data" no-bounce>
|
||||
<div class="box-notification warning" *ngIf="network == 'testnet'">
|
||||
Sandbox version. Only for testing purpose.
|
||||
</div>
|
||||
|
@ -17,53 +17,46 @@
|
|||
<p>Trade any leading blockchain asset for any other. Protection by Design. No Account Needed.</p>
|
||||
</div>
|
||||
<div class="integration-onboarding-cta">
|
||||
<button ion-button no-low-fee (click)="goTo('Shift')">Start</button>
|
||||
<button ion-button clear color="light" (click)="openExternalLink('https://shapeshift.io')">Visit Shapeshift.io →</button>
|
||||
<button ion-button large outline block no-low-fee (click)="goTo('Shift')">Start</button>
|
||||
<button ion-button clear small block color="light" (click)="openExternalLink('https://shapeshift.io')">Visit Shapeshift.io →</button>
|
||||
</div>
|
||||
</div>
|
||||
</ion-content>
|
||||
|
||||
|
||||
<ion-content *ngIf="shifts.data">
|
||||
<ion-content *ngIf="shifts.data" no-bounce>
|
||||
|
||||
<div class="main-header" (click)="update()">
|
||||
<img src="assets/img/shapeshift/logo-shapeshift.svg" width="200">
|
||||
<div class="main-header">
|
||||
<img src="assets/img/shapeshift/logo-shapeshift.svg" width="180" (click)="update()">
|
||||
<button color="light" ion-button clear icon-right no-low-fee (click)="goTo('Shift')">
|
||||
Shift
|
||||
<ion-icon name="arrow-forward"></ion-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<ion-list class="shift">
|
||||
<ion-item-divider class="help" color="light">
|
||||
<div>
|
||||
<span>Having problems with a ShapeShift?</span>
|
||||
<a (click)="openExternalLink('https://shapeshift.zendesk.com/hc/en-us/requests/new')">
|
||||
Contact the ShapeShift support team.
|
||||
</a>
|
||||
</div>
|
||||
</ion-item-divider>
|
||||
<button class="shift-btn" ion-item no-low-fee (click)="goTo('Shift')">
|
||||
<img src="assets/img/shapeshift/icon-shapeshift.svg">
|
||||
<span>Shift</span>
|
||||
</button>
|
||||
</ion-list>
|
||||
|
||||
<ion-list>
|
||||
<ion-item-divider color="light">
|
||||
Transactions
|
||||
</ion-item-divider>
|
||||
|
||||
<ion-list-header color="light">Transactions</ion-list-header>
|
||||
<button ion-item *ngFor="let item of shifts.data | keys" (click)="openShiftModal(item.value)">
|
||||
<div class="shapeshift-address">
|
||||
<h2>
|
||||
<span class="item-amount">{{ item.value.amount }}</span>
|
||||
<span class="ellipsis">{{item.value.title || item.value.address}}</span>
|
||||
</h2>
|
||||
<ion-note>{{item.value.date | amTimeAgo}}</ion-note>
|
||||
<span>
|
||||
<ion-label>
|
||||
<div class="ellipsis">{{item.value.title || item.value.address}}</div>
|
||||
<div class="status">
|
||||
<span class="assertive" *ngIf="item.value.status == 'failed'">Failed</span>
|
||||
<span class="balanced" *ngIf="item.value.status == 'complete'">Completed</span>
|
||||
<span class="dark" *ngIf="item.value.status == 'received'">Pending</span>
|
||||
<span class="text-gray" *ngIf="item.value.status == 'no_deposits'">Pending</span>
|
||||
</span>
|
||||
<span class="royal" *ngIf="item.value.status == 'received'">Pending</span>
|
||||
<span class="calm" *ngIf="item.value.status == 'no_deposits'">Pending</span>
|
||||
</div>
|
||||
</ion-label>
|
||||
<div item-content text-end>
|
||||
<div class="text-bold">{{ item.value.amount }}</div>
|
||||
<div class="date calm">{{item.value.date | amTimeAgo}}</div>
|
||||
</div>
|
||||
</button>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
|
||||
<ion-footer *ngIf="shifts.data" class="shift-problems">
|
||||
<span>Having problems with a ShapeShift?</span>
|
||||
<a (click)="openExternalLink('https://shapeshift.zendesk.com/hc/en-us/requests/new')">
|
||||
Contact the ShapeShift support team.
|
||||
</a>
|
||||
</ion-footer>
|
||||
|
|
|
@ -26,40 +26,24 @@ page-shapeshift {
|
|||
&-cta {
|
||||
position: absolute;
|
||||
bottom: 5vh;
|
||||
width: 100%;
|
||||
button {
|
||||
width: 85%;
|
||||
max-width: 300px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
.main-header {
|
||||
height: 120px;
|
||||
padding: 15px 0;
|
||||
background: url('../assets/img/shapeshift/shapeshift_background.jpg') center center no-repeat #28394d;
|
||||
text-align: center;
|
||||
img {
|
||||
padding-top: 20px;
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
.shift {
|
||||
margin: 0rem;
|
||||
img {
|
||||
width: 30px;
|
||||
}
|
||||
&-btn .label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.help .label {
|
||||
text-align: center;
|
||||
white-space: normal;
|
||||
font-size: 12px;
|
||||
span {
|
||||
margin-right: 0.6rem;
|
||||
}
|
||||
}
|
||||
.shift-problems {
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
white-space: normal;
|
||||
font-size: 12.5px;
|
||||
}
|
||||
.status, .date {
|
||||
font-size: 12.5px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<ion-navbar hideBackButton="true"></ion-navbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
<ion-content no-bounce>
|
||||
<img src="assets/img/app/onboarding/warning.svg" />
|
||||
<h1 translate>No backup, no bitcoin.</h1>
|
||||
<p translate>Since only you control your money, you’ll need to save your backup phrase in case this app is deleted.</p>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
<ion-content no-bounce>
|
||||
<h1 translate>Wallet Created</h1>
|
||||
|
||||
<div *ngIf="!showConfirmForm">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<ion-header no-border>
|
||||
<ion-navbar hideBackButton="true"></ion-navbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-content no-bounce>
|
||||
<h1 *ngIf="resume" translate>Quick review!</h1>
|
||||
<h1 *ngIf="!resume" translate>Almost done! Let's review.</h1>
|
||||
<p translate>Bitcoin is different – it cannot be safely held with a bank or web service.</p>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<ion-content>
|
||||
<ion-content no-bounce>
|
||||
|
||||
<div class="logo-tagline">
|
||||
<img src='assets/img/app/logo-negative.svg' id="logo" />
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
<ion-content no-bounce>
|
||||
<ion-slides pager="true" (ionSlideDidChange)="slideChanged()">
|
||||
<ion-slide>
|
||||
<h1 translate>Bitcoin is secure,
|
||||
|
|
|
@ -2,25 +2,17 @@
|
|||
|
||||
<ion-navbar>
|
||||
<ion-title>{{'Send' | translate}}</ion-title>
|
||||
<ion-buttons end>
|
||||
<button *ngIf="hasBtcWallets || hasBchWallets" ion-button icon-only (click)="openScanner()">
|
||||
<ion-icon class="icon-scanner" name="qr-scanner"></ion-icon>
|
||||
</button>
|
||||
</ion-buttons>
|
||||
</ion-navbar>
|
||||
|
||||
</ion-header>
|
||||
<ion-content no-bounce>
|
||||
<div *ngIf="hasBtcWallets || hasBchWallets">
|
||||
<ion-list>
|
||||
<ion-list-header class="title">{{'Recipient' | translate}}</ion-list-header>
|
||||
<ion-searchbar placeholder="Search or enter bitcoin address" [(ngModel)]="search" (ngModelChange)="findContact(search)"></ion-searchbar>
|
||||
</ion-list>
|
||||
<ion-searchbar placeholder="Search or enter bitcoin address" [(ngModel)]="search" (ngModelChange)="findContact(search)"></ion-searchbar>
|
||||
|
||||
<ion-list *ngIf="filteredContactsList && filteredContactsList[0]">
|
||||
<ion-item-divider class="title">
|
||||
<ion-list-header color="light">
|
||||
<span translate>Transfer to Contact</span>
|
||||
</ion-item-divider>
|
||||
</ion-list-header>
|
||||
<button ion-item *ngFor="let item of filteredContactsList" (click)="goToAmount(item)">
|
||||
<ion-icon item-start>
|
||||
<gravatar [name]="item.name" [width]="30" [email]="item.email"></gravatar>
|
||||
|
@ -32,10 +24,10 @@
|
|||
</button>
|
||||
</ion-list>
|
||||
<ion-list *ngIf="walletBtcList && walletBtcList[0]">
|
||||
<ion-item-divider class="title">
|
||||
<ion-list-header class="title" color="light">
|
||||
<img src="assets/img/icon-bitcoin.svg" alt="Bitcoin Wallets" width="16" />
|
||||
<span translate>Transfer to Bitcoin Wallet</span>
|
||||
</ion-item-divider>
|
||||
</ion-list-header>
|
||||
|
||||
<button ion-item *ngFor="let wallet of walletBtcList" (click)="goToAmount(wallet)">
|
||||
<ion-icon item-start>
|
||||
|
@ -51,10 +43,10 @@
|
|||
</ion-list>
|
||||
|
||||
<ion-list *ngIf="walletBchList && walletBchList[0]">
|
||||
<ion-item-divider class="title">
|
||||
<ion-list-header class="title" color="light">
|
||||
<img src="assets/img/bitcoin-cash-logo.svg" alt="Bitcoin Cash Wallets" width="22" />
|
||||
<span translate>Transfer to Bitcoin Cash Wallet</span>
|
||||
</ion-item-divider>
|
||||
</ion-list-header>
|
||||
|
||||
<button ion-item *ngFor="let wallet of walletBchList" (click)="goToAmount(wallet)">
|
||||
<ion-icon item-start>
|
||||
|
|
|
@ -4,13 +4,9 @@ page-send {
|
|||
text-align: center;
|
||||
}
|
||||
.title {
|
||||
color: color($colors, dark);
|
||||
font-weight: 700;
|
||||
img {
|
||||
width: 22px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-right: 10px;
|
||||
vertical-align: sub;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
.no-wallet-message {
|
||||
|
|
|
@ -142,10 +142,6 @@ export class SendPage {
|
|||
});
|
||||
}
|
||||
|
||||
public openScanner(): void {
|
||||
this.navCtrl.parent.select(2);
|
||||
}
|
||||
|
||||
public showMore(): void {
|
||||
this.currentContactsPage++;
|
||||
this.updateContactsList();
|
||||
|
|
|
@ -6,8 +6,7 @@
|
|||
|
||||
<ion-content no-bounce>
|
||||
<ion-list>
|
||||
<ion-item-divider color="light"></ion-item-divider>
|
||||
<button ion-item (click)="openAddressBookPage()">
|
||||
<button ion-item no-lines (click)="openAddressBookPage()">
|
||||
<ion-icon name="ios-contacts-outline" item-start></ion-icon>
|
||||
{{'Address book' | translate}}
|
||||
</button>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
</ion-navbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
<ion-content no-bounce>
|
||||
<ion-card *ngIf="btx">
|
||||
<ion-card-content>
|
||||
<div class="sending-label" *ngIf="btx.confirmations > 0">
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
<ion-content no-bounce>
|
||||
|
||||
<ion-card>
|
||||
<ion-card-content>
|
||||
|
|
|
@ -69,43 +69,48 @@ page-wallet-details {
|
|||
.wallet-info {
|
||||
font-weight: 500;
|
||||
color: color($colors, light);
|
||||
height: 2.5rem;
|
||||
padding-left: 0.5rem;
|
||||
span {
|
||||
margin: 0 3px;
|
||||
padding-top: 2px;
|
||||
float: left;
|
||||
}
|
||||
height: 30px;
|
||||
img {
|
||||
float: left;
|
||||
margin-right: 4px;
|
||||
}
|
||||
.testnet {
|
||||
height: 2.5rem;
|
||||
width: 2.5rem;
|
||||
margin-top: 3px;
|
||||
}
|
||||
.testnet-text {
|
||||
height: 2.5rem;
|
||||
width: 4.5rem;
|
||||
margin-right: 5px;
|
||||
color: color($colors, light);
|
||||
height: 3rem;
|
||||
width: 5rem;
|
||||
}
|
||||
.read-only {
|
||||
height: 2.5rem;
|
||||
width: 1.5rem;
|
||||
margin-right: 5px;
|
||||
height: 3rem;
|
||||
width: 2rem;
|
||||
}
|
||||
.read-only-text {
|
||||
height: 2.5rem;
|
||||
width: 5rem;
|
||||
margin-right: 5px;
|
||||
height: 3rem;
|
||||
width: 6rem;
|
||||
}
|
||||
.auditable-text {
|
||||
height: 2.5rem;
|
||||
height: 3rem;
|
||||
width: 5rem;
|
||||
}
|
||||
.custom-bws {
|
||||
height: 3.2rem;
|
||||
width: 3.2rem;
|
||||
}
|
||||
.wallet-type {
|
||||
display: block;
|
||||
float: left;
|
||||
margin: 0 3px;
|
||||
padding-top: 5px;
|
||||
}
|
||||
ion-spinner {
|
||||
float: right;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin: 5px 5px 0 0;
|
||||
* {
|
||||
stroke: color($colors, light);
|
||||
}
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { LoadingController } from 'ionic-angular';
|
||||
import { Logger } from '../../providers/logger/logger';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
@Injectable()
|
||||
export class OnGoingProcessProvider {
|
||||
|
||||
private loading: any;
|
||||
private processNames: any;
|
||||
private pausedOngoingProcess: any;
|
||||
private ongoingProcess: any;
|
||||
|
||||
constructor(
|
||||
private loadingCtrl: LoadingController,
|
||||
|
@ -55,20 +58,29 @@ export class OnGoingProcessProvider {
|
|||
'topup': 'Top up in progress...',
|
||||
'duplicatingWallet': 'Duplicating wallet...',
|
||||
};
|
||||
this.ongoingProcess = {};
|
||||
}
|
||||
|
||||
public getShowName(processName: string): string {
|
||||
let showName = this.processNames[processName] || processName;
|
||||
return showName;
|
||||
}
|
||||
|
||||
public clear() {
|
||||
this.processNames = {};
|
||||
private clear() {
|
||||
this.ongoingProcess = {};
|
||||
this.loading.dismiss();
|
||||
};
|
||||
}
|
||||
|
||||
public pause(): void {
|
||||
this.pausedOngoingProcess = this.ongoingProcess;
|
||||
this.clear();
|
||||
}
|
||||
|
||||
public resume(): void {
|
||||
_.forEach(this.pausedOngoingProcess, (v, k) => {
|
||||
this.set(k, v);
|
||||
});
|
||||
this.pausedOngoingProcess = {};
|
||||
}
|
||||
|
||||
public set(processName: string, isOn: boolean): string {
|
||||
this.logger.debug('ongoingProcess', processName, isOn);
|
||||
this.ongoingProcess[processName] = isOn;
|
||||
let showName = this.processNames[processName] || processName;
|
||||
if (!isOn) {
|
||||
this.loading.dismiss();
|
||||
|
|
|
@ -117,7 +117,6 @@ export class FileStorage implements IStorage {
|
|||
v = v.toString();
|
||||
}
|
||||
|
||||
this.log.debug('Writing:', k, v);
|
||||
fileWriter.write(v);
|
||||
}, err => {
|
||||
this.log.error('Could not create writer', err);
|
||||
|
|
|
@ -20,6 +20,7 @@ export class LocalStorage implements IStorage {
|
|||
try {
|
||||
parsed = JSON.parse(v);
|
||||
} catch (e) {
|
||||
//TODO parse is not necessary
|
||||
}
|
||||
resolve(parsed || v);
|
||||
});
|
||||
|
|
|
@ -11,6 +11,8 @@ import { BwcErrorProvider } from '../bwc-error/bwc-error';
|
|||
import { PlatformProvider } from '../platform/platform';
|
||||
import { AppProvider } from '../../providers/app/app';
|
||||
import { LanguageProvider } from '../../providers/language/language';
|
||||
import { PopupProvider } from '../popup/popup';
|
||||
import { OnGoingProcessProvider } from '../on-going-process/on-going-process';
|
||||
|
||||
//models
|
||||
import { Profile } from '../../models/profile/profile.model';
|
||||
|
@ -34,7 +36,9 @@ export class ProfileProvider {
|
|||
private platformProvider: PlatformProvider,
|
||||
private appProvider: AppProvider,
|
||||
private languageProvider: LanguageProvider,
|
||||
private events: Events
|
||||
private events: Events,
|
||||
private popupProvider: PopupProvider,
|
||||
private onGoingProcessProvider: OnGoingProcessProvider
|
||||
) {
|
||||
this.throttledBwsEvent = _.throttle((n, wallet) => {
|
||||
this.newBwsEvent(n, wallet);
|
||||
|
@ -333,6 +337,60 @@ export class ProfileProvider {
|
|||
});
|
||||
}
|
||||
|
||||
// An alert dialog
|
||||
private askPassword(name: string, title: string): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
let opts = {
|
||||
type: 'password'
|
||||
}
|
||||
this.popupProvider.ionicPrompt(title, name, opts).then((res: any) => {
|
||||
return resolve(res);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private showWarningNoEncrypt(): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
let title = 'Are you sure?'; //TODO gettextcatalog
|
||||
let msg = 'Your wallet keys will be stored in plan text in this device, if an other app access the store it will be able to access your Bitcoin'; //TODO gettextcatalog
|
||||
let okText = 'Yes'; //TODO gettextcatalog
|
||||
let cancelText = 'No'; //TODO gettextcatalog
|
||||
this.popupProvider.ionicConfirm(title, msg, okText, cancelText).then((res: any) => {
|
||||
return resolve(res);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private encrypt(wallet: any): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
let title = 'Please enter a password to encrypt your wallet keys on this device storage'; //TODO gettextcatalog
|
||||
let warnMsg = 'Your wallet key will be encrypted. The Spending Password cannot be recovered. Be sure to write it down.'; //TODO gettextcatalog
|
||||
this.askPassword(warnMsg, title).then((password: string) => {
|
||||
if (!password) {
|
||||
this.showWarningNoEncrypt().then((res: any) => {
|
||||
if (res) return resolve(); //TODO gettextcatalog
|
||||
this.encrypt(wallet).then(() => {
|
||||
return resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
title = 'Confirm your new spending password'; //TODO gettextcatalog
|
||||
this.askPassword(warnMsg, title).then((password2: string) => {
|
||||
if (!password2 || password != password2) {
|
||||
this.encrypt(wallet).then(() => {
|
||||
return resolve();
|
||||
});
|
||||
} else {
|
||||
wallet.encryptPrivateKey(password);
|
||||
return resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Adds and bind a new client to the profile
|
||||
private addAndBindWalletClient(wallet: any, opts: any): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
@ -340,40 +398,45 @@ export class ProfileProvider {
|
|||
return reject('Could not access wallet'); // TODO gettextCatalog
|
||||
}
|
||||
|
||||
let walletId: string = wallet.credentials.walletId
|
||||
// Encrypt wallet
|
||||
this.onGoingProcessProvider.pause();
|
||||
this.encrypt(wallet).then(() => {
|
||||
this.onGoingProcessProvider.resume();
|
||||
|
||||
if (!this.profile.addWallet(JSON.parse(wallet.export()))) {
|
||||
return reject("Wallet already in " + this.appProvider.info.nameCase); // TODO gettextCatalog
|
||||
}
|
||||
let walletId: string = wallet.credentials.walletId
|
||||
|
||||
if (!this.profile.addWallet(JSON.parse(wallet.export()))) {
|
||||
return reject("Wallet already in " + this.appProvider.info.nameCase); // TODO gettextCatalog
|
||||
}
|
||||
|
||||
let skipKeyValidation: boolean = this.shouldSkipValidation(walletId);
|
||||
if (!skipKeyValidation)
|
||||
this.runValidation(wallet);
|
||||
let skipKeyValidation: boolean = this.shouldSkipValidation(walletId);
|
||||
if (!skipKeyValidation)
|
||||
this.runValidation(wallet);
|
||||
|
||||
this.bindWalletClient(wallet);
|
||||
this.bindWalletClient(wallet);
|
||||
|
||||
let saveBwsUrl = (): Promise<any> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let defaults: any = this.configProvider.getDefaults();
|
||||
let bwsFor: any = {};
|
||||
bwsFor[walletId] = opts.bwsurl || defaults.bws.url;
|
||||
let saveBwsUrl = (): Promise<any> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let defaults: any = this.configProvider.getDefaults();
|
||||
let bwsFor: any = {};
|
||||
bwsFor[walletId] = opts.bwsurl || defaults.bws.url;
|
||||
|
||||
// Dont save the default
|
||||
if (bwsFor[walletId] == defaults.bws.url) {
|
||||
// Dont save the default
|
||||
if (bwsFor[walletId] == defaults.bws.url) {
|
||||
return resolve();
|
||||
}
|
||||
|
||||
this.configProvider.set({ bwsFor: bwsFor });
|
||||
return resolve();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.configProvider.set({ bwsFor: bwsFor });
|
||||
return resolve();
|
||||
});
|
||||
};
|
||||
|
||||
saveBwsUrl().then(() => {
|
||||
this.persistenceProvider.storeProfile(this.profile).then(() => {
|
||||
return resolve(wallet);
|
||||
}).catch((err: any) => {
|
||||
return reject(err);
|
||||
saveBwsUrl().then(() => {
|
||||
this.persistenceProvider.storeProfile(this.profile).then(() => {
|
||||
return resolve(wallet);
|
||||
}).catch((err: any) => {
|
||||
return reject(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -105,10 +105,9 @@ export class ShapeshiftProvider {
|
|||
}
|
||||
|
||||
public getShapeshift(cb) {
|
||||
var network = this.getNetwork();
|
||||
let network = this.getNetwork();
|
||||
this.persistenceProvider.getShapeshift(network).then((ss: any) => {
|
||||
var _gcds = ss ? JSON.parse(ss) : null;
|
||||
return cb(null, _gcds);
|
||||
return cb(null, ss);
|
||||
}).catch((err: any) => {
|
||||
return cb(err, null);
|
||||
});
|
||||
|
|
|
@ -16,12 +16,6 @@ import { OnGoingProcessProvider } from '../on-going-process/on-going-process';
|
|||
import { TouchIdProvider } from '../touchid/touchid';
|
||||
import { FeeProvider } from '../fee/fee';
|
||||
|
||||
|
||||
/* TODO LIST:
|
||||
- onGoingProcess provider
|
||||
*/
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class WalletProvider {
|
||||
|
||||
|
@ -1089,13 +1083,11 @@ export class WalletProvider {
|
|||
let opts = {
|
||||
type: 'password'
|
||||
}
|
||||
this.popupProvider.ionicPrompt(title, name, opts, null, null).then((res: any) => {
|
||||
this.popupProvider.ionicPrompt(title, name, opts).then((res: any) => {
|
||||
return resolve(res);
|
||||
}).catch((err: any) => {
|
||||
return reject(err);
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
public encrypt(wallet: any): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
@ -1115,8 +1107,7 @@ export class WalletProvider {
|
|||
return reject(err);
|
||||
});
|
||||
});
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
public decrypt(wallet: any): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
|
|
@ -96,10 +96,6 @@ $list-md-border-color: color($colors, light);
|
|||
/* Ionic Overrides and Workarounds */
|
||||
// Please include a description of the problem solved by the workaround.
|
||||
|
||||
ion-navbar.hide {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
// Hide scrollbars on platforms which don't match iOS, Android, or Windows
|
||||
// (So scroll bars are not visible in Chrome, NW.js, or Electron.)
|
||||
.platform-core .scroll-content {
|
||||
|
|
Loading…
Reference in New Issue