Merge pull request #9 from sansegkh/master

merged from master
This commit is contained in:
San Segkhoonthod 2019-01-20 13:03:28 +00:00 committed by GitHub
commit 664a6d961b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 714 additions and 684 deletions

View file

@ -105,6 +105,14 @@ const WalletsStackNavigator = createStackNavigator(
screen: LightningSettings,
path: 'LightningSettings',
},
LNDViewInvoice: {
screen: LNDViewInvoice,
swipeEnabled: false,
gesturesEnabled: false,
},
LNDViewAdditionalInvoiceInformation: {
screen: LNDViewAdditionalInvoiceInformation,
},
},
{ headerBackTitleVisible: false },
);
@ -149,17 +157,6 @@ const ManageFundsStackNavigator = createStackNavigator({
},
});
const LNDViewInvoiceStackNavigator = createStackNavigator({
LNDViewInvoice: {
screen: LNDViewInvoice,
swipeEnabled: false,
gesturesEnabled: false,
},
LNDViewAdditionalInvoiceInformation: {
screen: LNDViewAdditionalInvoiceInformation,
},
});
const LNDCreateInvoiceStackNavigator = createStackNavigator({
LNDCreateInvoice: {
screen: LNDCreateInvoice,
@ -260,12 +257,6 @@ const MainBottomTabs = createStackNavigator(
header: null,
},
},
LNDViewExistingInvoice: {
screen: LNDViewInvoiceStackNavigator,
navigationOptions: {
header: null,
},
},
},
{
mode: 'modal',

View file

@ -102,8 +102,8 @@ android {
applicationId "io.bluewallet.bluewallet"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 17
versionName "3.5.7"
versionCode 18
versionName "3.6.0"
ndk {
abiFilters "armeabi-v7a", "x86"
}

View file

@ -1,6 +1,6 @@
module.exports = {
_: {
storage_is_encrypted: 'ストレージが暗号化されています。復号にはパスワードが必要です。',
storage_is_encrypted: 'ウォレットは暗号化されています。復号にはパスワードが必要です。',
enter_password: 'パスワードを入力',
bad_password: 'パスワードが間違っています。',
never: 'データなし',
@ -13,7 +13,7 @@ module.exports = {
createBitcoinWallet:
'Bitcoin ウォレットを持っていません。Lightning ウォレットへ課金する場合は Bitcoin ウォレットを新規作成するかインポートする必要があります。続行しますか?',
list: {
app_name: 'Blue Wallet',
app_name: 'BlueWallet',
title: 'ウォレット',
header: 'ウォレットは秘密鍵(プライベートキー)とウォレットアドレスのペアで構成されています。' + 'コインを受信するために共有できます。',
add: 'ウォレットの追加',
@ -32,7 +32,7 @@ module.exports = {
title: 'ウォレットの追加',
description:
'ペーパーウォレット(WIF 形式- Wallet Import Format)をスキャンするかウォレットを新規作成できます。既定で Segwit ウォレットが作成されます。',
scan: 'スキャン',
scan: '読取り',
create: '作成',
label_new_segwit: '新規 SegWit',
label_new_lightning: '新規 Lightning',
@ -77,7 +77,7 @@ module.exports = {
error: 'インポートに失敗しました。入力したデータが有効か確認してください。',
success: '成功',
do_import: 'インポート',
scan_qr: '代わりに QR コードをスキャンしますか?',
scan_qr: 'QR コードの読み取り',
},
scanQrWif: {
go_back: '戻る',
@ -125,7 +125,7 @@ module.exports = {
fee_placeholder: '取引手数料 (BTC)',
note_placeholder: 'ラベル',
cancel: '中止',
scan: 'スキャン',
scan: '読取り',
send: '送金',
create: '作成',
remaining_balance: '残高',
@ -160,7 +160,7 @@ module.exports = {
share: '共有',
copiedToClipboard: 'クリップボードにコピーしました。',
label: '概要',
create: 'Create',
create: '作成',
setAmount: '入金額',
},
},
@ -172,13 +172,13 @@ module.exports = {
settings: {
header: '設定',
plausible_deniability: '隠匿設定...',
storage_not_encrypted: 'ストレージ: 暗号化されていません',
storage_encrypted: 'ストレージ: 暗号化されています',
storage_not_encrypted: 'ウォレット: 暗号化されていません',
storage_encrypted: 'ウォレット: 暗号化されています',
password: 'パスワード',
password_explain: 'ストレージの復号に使用するパスワードを作成',
password_explain: 'ウォレットの復号に使用するパスワードを作成',
retype_password: 'パスワードの再入力',
passwords_do_not_match: 'パスワードが一致しません',
encrypt_storage: 'ストレージの暗号化',
encrypt_storage: 'ウォレットの暗号化',
lightning_settings: 'Lightning 設定',
lightning_settings_explain:
'他の LND ノードへ接続するには LndHub をインストール後、' +
@ -192,18 +192,20 @@ module.exports = {
plausibledeniability: {
title: '隠匿設定',
help:
'のウォレットの復号に必要なパスワードを第三者に強要される場合、' +
'コインを安全に保護するために BlueWallet はメインとは異なるパスワードで' +
'暗号化されたダミーのストレージを作成します。' +
'第三者へこのパスワードを提供することで、第三者が BlueWallet のダミーの' +
'暗号化ストレージを復号することで、メインのストレージを隠匿することが可能となり' +
'BuleWallet のウォレットの復号に必要なパスワードを第三者に強要される場合、' +
'コインを安全に保護するためにメインのウォレットとは異なるパスワードで' +
'暗号化されたダミーのウォレットを作成することが可能です。' +
'第三者へ異なるパスワードを提供すれば、BlueWallet のダミーの' +
'暗号化ウォレットを復号することとなり、メインのウォレットは隠匿され' +
'コインは安全に保護されます。',
help2: 'この新規ダミーのストレージは完全に機能します。少額のコインを保管することで' + 'ダミーと疑われないようにすることが可能です。',
create_fake_storage: 'ダミーの暗号化ストレージの作成',
help2:
'新規のダミーのウォレットはメインと同様に機能します。少額のコインを入金しておくことで' +
'ダミーと疑われないようにすることが可能です。',
create_fake_storage: 'ダミーの暗号化ウォレットの作成',
go_back: '戻る',
create_password: 'パスワードの作成',
create_password_explanation: 'ダミーの暗号化ストレージのパスワードはメインのストレージのパスワードと異なる必要があります。',
password_should_not_match: 'ダミーのストレージのパスワードはメインのストレージのパスワードと異なる必要があります。',
create_password_explanation: 'ダミーのウォレットのパスワードはメインのウォレットのパスワードと異なる必要があります。',
password_should_not_match: 'ダミーのウォレットのパスワードはメインのウォレットのパスワードと異なる必要があります。',
retype_password: 'パスワードの再入力',
passwords_do_not_match: 'パスワードが一致しません',
success: '成功',

View file

@ -9,8 +9,10 @@ export const FiatUnit = Object.freeze({
HRK: { endPointKey: 'HRK', symbol: 'HRK', locale: 'hr-HR' },
INR: { endPointKey: 'INR', symbol: '₹', locale: 'hi-HN' },
JPY: { endPointKey: 'JPY', symbol: '¥', locale: 'ja-JP' },
PLN: { endPointKey: 'PLN', symbol: 'zł', locale: 'pl-PL' },
RUB: { endPointKey: 'RUB', symbol: '₽', locale: 'ru-RU' },
SGD: { endPointKey: 'SGD', symbol: 'S$', locale: 'zh-SG' },
SEK: { endPointKey: 'SEK', symbol: 'kr', locale: 'sv-SE' },
THB: { endPointKey: 'THB', symbol: '฿', locale: 'th-TH' },
VEF: { endPointKey: 'VEF', symbol: 'Bs.', locale: 'es-VE' },
ZAR: { endPointKey: 'ZAR', symbol: 'R', locale: 'en-ZA' },

907
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -43,7 +43,7 @@
"buffer": "^5.2.1",
"buffer-reverse": "^1.0.1",
"crypto-js": "^3.1.9-1",
"dayjs": "^1.7.8",
"dayjs": "^1.8.0",
"eslint-config-prettier": "^2.10.0",
"eslint-config-standard": "^12.0.0",
"eslint-config-standard-react": "^7.0.2",

View file

@ -1,12 +1,71 @@
import React, { Component } from 'react';
import { View, Alert, Dimensions } from 'react-native';
import { TouchableOpacity, ActivityIndicator, View, Alert, Dimensions } from 'react-native';
import { WebView } from 'react-native-webview';
import { BlueNavigationStyle } from '../../BlueComponents';
import { BlueNavigationStyle, SafeBlueArea } from '../../BlueComponents';
import { FormInput } from 'react-native-elements';
import Ionicons from 'react-native-vector-icons/Ionicons';
import PropTypes from 'prop-types';
const { width } = Dimensions.get('window');
let processedInvoices = {};
let lastTimeTriedToPay = 0;
/// ///////////////////////////////////////////////////////////////////////
// this code has no use in RN, it gets copypasted in webview injected code
//
let bluewalletResponses = {};
// eslint-disable-next-line
var webln = {
enable: function() {
window.postMessage('enable');
return new Promise(function(resolve, reject) {
resolve(true);
});
},
getInfo: function() {
window.postMessage('getInfo');
return new Promise(function(resolve, reject) {
reject(new Error('not implemented'));
});
},
sendPayment: function(paymentRequest) {
window.postMessage(JSON.stringify({ sendPayment: paymentRequest }));
return new Promise(function(resolve, reject) {
/* nop. intentionally, forever hang promise.
lapp page usually asynchroniously checks payment itself, via ajax,
so atm there's no need to pass payment preimage from RN to webview and fullfill promise.
might change in future */
});
},
makeInvoice: function(RequestInvoiceArgs) {
var id = Math.random();
window.postMessage(JSON.stringify({ makeInvoice: RequestInvoiceArgs, id: id }));
return new Promise(function(resolve, reject) {
var interval = setInterval(function() {
if (bluewalletResponses[id]) {
clearInterval(interval);
resolve(bluewalletResponses[id]);
}
}, 1000);
});
},
signMessage: function() {
window.postMessage('signMessage');
return new Promise(function(resolve, reject) {
reject(new Error('not implemented'));
});
},
verifyMessage: function() {
window.postMessage('verifyMessage');
return new Promise(function(resolve, reject) {
reject(new Error('not implemented'));
});
},
};
// end injected code
/// /////////////////
/// /////////////////
export default class Browser extends Component {
static navigationOptions = ({ navigation }) => ({
...BlueNavigationStyle(navigation, true),
@ -17,32 +76,40 @@ export default class Browser extends Component {
constructor(props) {
super(props);
if (!props.navigation.getParam('fromSecret')) throw new Error('Invalid param');
if (!props.navigation.getParam('fromWallet')) throw new Error('Invalid param');
this.state = { url: '' };
this.state = {
url: 'https://bluewallet.io/marketplace/',
pageIsLoading: false,
fromSecret: props.navigation.getParam('fromSecret'),
fromWallet: props.navigation.getParam('fromWallet'),
};
}
render() {
return (
<React.Fragment>
<SafeBlueArea>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<Ionicons
<TouchableOpacity
onPress={() => {
this.webview.goBack();
}}
name={'ios-arrow-round-back'}
size={26}
style={{
color: 'red',
backgroundColor: 'transparent',
left: 8,
top: 1,
}}
/>
>
<Ionicons
name={'ios-arrow-round-back'}
size={36}
style={{
color: 'red',
backgroundColor: 'transparent',
paddingLeft: 10,
}}
/>
</TouchableOpacity>
<FormInput
inputStyle={{ color: '#0c2550', fontSize: 16 }}
inputStyle={{ color: '#0c2550', maxWidth: width - 150, fontSize: 16 }}
containerStyle={{
maxWidth: width - 100,
maxWidth: width - 150,
borderColor: '#d2d2d2',
borderWidth: 0.5,
backgroundColor: '#f5f5f5',
@ -50,35 +117,73 @@ export default class Browser extends Component {
value={this.state.url}
/>
<Ionicons
<TouchableOpacity
onPress={() => {
this.setState({ url: 'https://bluewallet.io/marketplace/' });
}}
>
<Ionicons
name={'ios-home'}
size={36}
style={{
color: 'red',
backgroundColor: 'transparent',
}}
/>
</TouchableOpacity>
<TouchableOpacity
onPress={() => {
this.webview.reload();
}}
name={'ios-sync'}
size={26}
style={{
color: 'red',
backgroundColor: 'transparent',
left: 8,
top: 1,
}}
/>
>
{(!this.state.pageIsLoading && (
<Ionicons
name={'ios-sync'}
size={36}
style={{
color: 'red',
backgroundColor: 'transparent',
paddingLeft: 15,
}}
/>
)) || (
<View style={{ paddingLeft: 20 }}>
<ActivityIndicator />
</View>
)}
</TouchableOpacity>
</View>
<WebView
ref={ref => (this.webview = ref)}
source={{ uri: 'https://bluewallet.io/marketplace/' }}
source={{ uri: this.state.url }}
mixedContentMode={'compatibility'}
onMessage={e => {
// this is a handler which receives messages sent from within the browser
console.log('---- message from the bus:', e.nativeEvent.data);
let json = false;
try {
json = JSON.parse(e.nativeEvent.data);
} catch (_) {}
// message from browser has ln invoice
if (json && json.pay) {
if (json && json.sendPayment) {
// checking that we do not trigger alert too often:
if (+new Date() - lastTimeTriedToPay < 3000) {
return;
}
lastTimeTriedToPay = +new Date();
// checking that already asked about this invoice:
if (processedInvoices[json.sendPayment]) {
return;
} else {
processedInvoices[json.sendPayment] = 1;
}
Alert.alert(
'Page',
'This page asks for permission to pay this invoice',
'This page asks for permission to pay an invoice',
[
{ text: 'Cancel', onPress: () => console.log('Cancel Pressed'), style: 'cancel' },
{
@ -88,7 +193,8 @@ export default class Browser extends Component {
this.props.navigation.navigate({
routeName: 'ScanLndInvoice',
params: {
uri: json.pay,
uri: json.sendPayment,
fromSecret: this.state.fromSecret,
},
});
},
@ -97,75 +203,174 @@ export default class Browser extends Component {
{ cancelable: false },
);
}
if (json && json.makeInvoice) {
let amount = Math.max(+json.makeInvoice.minimumAmount, +json.makeInvoice.maximumAmount, +json.makeInvoice.defaultAmount);
Alert.alert(
'Page',
'This page wants to pay you ' + amount + ' sats (' + json.makeInvoice.defaultMemo + ')',
[
{ text: 'No thanks', onPress: () => console.log('Cancel Pressed'), style: 'cancel' },
{
text: 'Accept',
onPress: async () => {
/** @type {LightningCustodianWallet} */
const fromWallet = this.state.fromWallet;
const payreq = await fromWallet.addInvoice(amount, json.makeInvoice.defaultMemo || ' ');
this.webview.postMessage(JSON.stringify({ bluewalletResponse: { paymentRequest: payreq }, id: json.id }));
},
},
],
{ cancelable: false },
);
}
}}
injectedJavaScript={`
/* rules:
no 'let', only 'var'
no arrow functions
globals without 'var'
should work if compressed to single line
*/
/* this is a storage of responses from OUTER code (react native)
it gets written by message bus handler callback:
webview makes a call through bus to RN, each request with a unique ID.
RN processes the request from the bus, and posts response to the bus, with the same ID.
webview callback handler writes it in this hashmap. Then, some other code that patiently
waits for a response will see that the answer with such ID is present, and will fulfill a promise */
bluewalletResponses = {};
/* this is injected WEBLN provider */
webln = {
enable : function () {
window.postMessage('enable');
return new Promise(function(resolve, reject){
resolve(true);
})
},
getInfo : function () {
window.postMessage('getInfo');
return new Promise(function(resolve, reject){
reject('not implemented');
})
},
sendPayment: function(paymentRequest) {
window.postMessage(JSON.stringify({ sendPayment: paymentRequest }));
return new Promise(function(resolve, reject) {
/* nop. intentionally, forever hang promise.
lapp page usually asynchroniously checks payment itself, via ajax,
so atm there's no need to pass payment preimage from RN to webview and fullfill promise.
might change in future */
});
},
makeInvoice: function (RequestInvoiceArgs) {
var id = Math.random();
window.postMessage(JSON.stringify({makeInvoice: RequestInvoiceArgs, id: id}));
return new Promise(function(resolve, reject) {
var interval = setInterval(function () {
if (bluewalletResponses[id]) {
clearInterval(interval);
resolve(bluewalletResponses[id]);
}
}, 1000);
});
},
signMessage: function () {
window.postMessage('signMessage');
return new Promise(function(resolve, reject){
reject('not implemented');
})
},
verifyMessage: function () {
window.postMessage('verifyMessage');
return new Promise(function(resolve, reject){
reject('not implemented');
})
},
};
/* end WEBLN */
/* listening to events that might come from RN: */
document.addEventListener("message", function(event) {
window.postMessage("inside webview, received post message: " + event.data);
var json;
try {
json = JSON.parse(event.data);
} catch (_) {}
if (json && json.bluewalletResponse) {
/* this is an answer to one of our inside-webview calls.
we store it in answers hashmap for someone who cares about it */
bluewalletResponses[json.id] = json.bluewalletResponse
}
}, false);
function tryToPay(invoice) {
window.postMessage(JSON.stringify({sendPayment:invoice}));
}
/* for non-webln compatible pages we do it oldschool,
searching for all bolt11 manually */
setInterval(function() {
var searchText = "lnbc";
var aTags = document.getElementsByTagName("span");
var i;
for (i = 0; i < aTags.length; i++) {
if (aTags[i].textContent.indexOf(searchText) === 0) {
tryToPay(aTags[i].textContent);
}
}
/* ------------------------- */
aTags = document.getElementsByTagName("input");
for (i = 0; i < aTags.length; i++) {
if (aTags[i].value.indexOf(searchText) === 0) {
tryToPay(aTags[i].value);
}
}
/* ------------------------- */
aTags = document.getElementsByTagName("a");
searchText = "lightning:lnbc";
for (i = 0; i < aTags.length; i++) {
var href = aTags[i].getAttribute('href') + '';
if (href.indexOf(searchText) === 0) {
tryToPay(href.replace('lightning:', ''));
}
}
}, 1000);
`}
onLoadStart={e => {
this.setState({ pageIsLoading: true });
}}
onLoadEnd={e => {
this.setState({ url: e.nativeEvent.url });
this.webview.injectJavaScript(`
lastTimeTriedToPay = 0;
function tryToPay(invoice) {
if (+new Date() - lastTimeTriedToPay >= 3000) {
window.postMessage(JSON.stringify({pay:invoice}));
lastTimeTriedToPay = +new Date();
}
}
setInterval(function(){
var searchText = "lnbc";
var aTags = document.getElementsByTagName("span");
for (var i = 0; i < aTags.length; i++) {
if (aTags[i].textContent.indexOf(searchText) === 0) {
tryToPay(aTags[i].textContent);
aTags[i].replaceWith('Invoice intercepted by BlueWallet');
break;
}
}
//////////////////////////////////
//////////////////////////////////
//////////////////////////////////
var aTags = document.getElementsByTagName("input");
for (var i = 0; i < aTags.length; i++) {
if (aTags[i].value.indexOf(searchText) === 0) {
tryToPay(aTags[i].value);
aTags[i].replaceWith('Invoice intercepted by BlueWallet');
break;
}
}
//////////////////////////////////
//////////////////////////////////
//////////////////////////////////
var aTags = document.getElementsByTagName("a");
var searchText = "lightning:lnbc";
for (var i = 0; i < aTags.length; i++) {
let href = aTags[i].getAttribute('href') + '';
if (href.indexOf(searchText) === 0) {
tryToPay(href.replace('lightning:', ''));
aTags[i].replaceWith('Invoice intercepted by BlueWallet');
break;
}
}
}, 1000);
`);
this.setState({ url: e.nativeEvent.url, pageIsLoading: false });
}}
/>
</React.Fragment>
</SafeBlueArea>
);
}
}

View file

@ -35,6 +35,7 @@ export default class LNDCreateInvoice extends Component {
this.props.navigation.navigate('LNDViewInvoice', {
invoice: invoiceRequest,
fromWallet: this.state.fromWallet,
isModal: true,
});
} catch (_error) {
ReactNativeHapticFeedback.trigger('notificationError', false);

View file

@ -10,7 +10,7 @@ const loc = require('../../loc');
export default class LNDViewAdditionalInvoiceInformation extends Component {
static navigationOptions = ({ navigation }) => ({
...BlueNavigationStyle(navigation, true, () => navigation.dismiss()),
...BlueNavigationStyle(),
title: 'Additional Information',
});

View file

@ -12,11 +12,14 @@ const QRFast = require('react-native-qrcode');
const { width, height } = Dimensions.get('window');
export default class LNDViewInvoice extends Component {
static navigationOptions = ({ navigation }) => ({
...BlueNavigationStyle(navigation, true, () => navigation.dismiss()),
title: 'Lightning Invoice',
headerLeft: null,
});
static navigationOptions = ({ navigation }) =>
navigation.getParam('isModal') === true
? {
...BlueNavigationStyle(navigation, true, () => navigation.dismiss()),
title: 'Lightning Invoice',
headerLeft: null,
}
: { ...BlueNavigationStyle(), title: 'Lightning Invoice' };
constructor(props) {
super(props);

View file

@ -39,6 +39,7 @@ export default class ScanLndInvoice extends React.Component {
const lightningWallets = BlueApp.getWallets().filter(item => item.type === LightningCustodianWallet.type);
if (lightningWallets.length > 0) {
fromSecret = lightningWallets[0].getSecret();
console.warn('warning: using ln wallet index 0');
}
}

View file

@ -430,6 +430,7 @@ export default class WalletsList extends Component {
this.props.navigation.navigate('LNDViewInvoice', {
invoice: rowData.item,
fromWallet: lightningWallet[0],
isModal: false,
});
}
}}

View file

@ -371,7 +371,7 @@ export default class WalletTransactions extends Component {
style={{ alignSelf: 'flex-start', left: 10, top: 15, flexDirection: 'row' }}
onPress={() => {
console.log('navigating to LappBrowser');
navigate('LappBrowser', { fromSecret: this.state.wallet.getSecret() });
navigate('LappBrowser', { fromSecret: this.state.wallet.getSecret(), fromWallet: this.state.wallet });
}}
>
<BlueText style={{ fontWeight: '600', fontSize: 16 }}>{'marketplace'}</BlueText>
@ -544,9 +544,10 @@ export default class WalletTransactions extends Component {
rowData.item.type === 'payment_request' ||
rowData.item.type === 'paid_invoice'
) {
this.props.navigation.navigate('LNDViewExistingInvoice', {
this.props.navigation.navigate('LNDViewInvoice', {
invoice: rowData.item,
fromWallet: this.state.wallet,
isModal: false,
});
}
}}