mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-01-19 05:45:15 +01:00
Merge branch 'master' into confirmationslabel
This commit is contained in:
commit
c66017c500
@ -5,6 +5,8 @@ import Share from 'react-native-share';
|
||||
import loc from '../loc';
|
||||
import DocumentPicker from 'react-native-document-picker';
|
||||
import isCatalyst from 'react-native-is-catalyst';
|
||||
import { launchCamera, launchImageLibrary } from 'react-native-image-picker';
|
||||
import { presentCameraNotAuthorizedAlert } from '../class/camera';
|
||||
const LocalQRCode = require('@remobile/react-native-qrcode-local-image');
|
||||
|
||||
const writeFileAndExport = async function (filename, contents) {
|
||||
@ -92,6 +94,56 @@ const _readPsbtFileIntoBase64 = async function (uri) {
|
||||
}
|
||||
};
|
||||
|
||||
const showImagePickerAndReadImage = () => {
|
||||
return new Promise((resolve, reject) =>
|
||||
launchImageLibrary(
|
||||
{
|
||||
title: null,
|
||||
mediaType: 'photo',
|
||||
takePhotoButtonTitle: null,
|
||||
},
|
||||
response => {
|
||||
if (response.uri) {
|
||||
const uri = Platform.OS === 'ios' ? response.uri.toString().replace('file://', '') : response.path.toString();
|
||||
LocalQRCode.decode(uri, (error, result) => {
|
||||
if (!error) {
|
||||
resolve(result);
|
||||
} else {
|
||||
reject(new Error(loc.send.qr_error_no_qrcode));
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
const takePhotoWithImagePickerAndReadPhoto = () => {
|
||||
return new Promise((resolve, reject) =>
|
||||
launchCamera(
|
||||
{
|
||||
title: null,
|
||||
mediaType: 'photo',
|
||||
takePhotoButtonTitle: null,
|
||||
},
|
||||
response => {
|
||||
if (response.uri) {
|
||||
const uri = Platform.OS === 'ios' ? response.uri.toString().replace('file://', '') : response.path.toString();
|
||||
LocalQRCode.decode(uri, (error, result) => {
|
||||
if (!error) {
|
||||
resolve(result);
|
||||
} else {
|
||||
reject(new Error(loc.send.qr_error_no_qrcode));
|
||||
}
|
||||
});
|
||||
} else if (response.error) {
|
||||
presentCameraNotAuthorizedAlert(response.error);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
const showFilePickerAndReadFile = async function () {
|
||||
try {
|
||||
const res = await DocumentPicker.pick({
|
||||
@ -141,3 +193,5 @@ const showFilePickerAndReadFile = async function () {
|
||||
module.exports.writeFileAndExport = writeFileAndExport;
|
||||
module.exports.openSignedTransaction = openSignedTransaction;
|
||||
module.exports.showFilePickerAndReadFile = showFilePickerAndReadFile;
|
||||
module.exports.showImagePickerAndReadImage = showImagePickerAndReadImage;
|
||||
module.exports.takePhotoWithImagePickerAndReadPhoto = takePhotoWithImagePickerAndReadPhoto;
|
||||
|
@ -277,6 +277,7 @@
|
||||
"network_electrum": "Electrum Server",
|
||||
"not_a_valid_uri": "Not a valid URI",
|
||||
"notifications": "Notifications",
|
||||
"open_link_in_explorer" : "Open link in explorer",
|
||||
"password": "Password",
|
||||
"password_explain": "Create the password you will use to decrypt the storage",
|
||||
"passwords_do_not_match": "Passwords do not match",
|
||||
@ -291,7 +292,8 @@
|
||||
"push_notifications": "Push Notifications",
|
||||
"retype_password": "Re-type password",
|
||||
"save": "Save",
|
||||
"saved": "Saved"
|
||||
"saved": "Saved",
|
||||
"success_transaction_broadcasted" : "Success! You transaction has been broadcasted!"
|
||||
},
|
||||
"notifications": {
|
||||
"would_you_like_to_receive_notifications": "Would you like to receive notifications when you get incoming payments?",
|
||||
|
@ -277,6 +277,7 @@
|
||||
"network_electrum": "سرور الکترام",
|
||||
"not_a_valid_uri": "یوآرآی معتبر نیست",
|
||||
"notifications": "اعلانات",
|
||||
"open_link_in_explorer" : "بازکردن پیوند در مرورگر",
|
||||
"password": "گذرواژه",
|
||||
"password_explain": "گذرواژهای را که برای رمزگشایی فضای ذخیرهسازی استفاده خواهید کرد ایجاد کنید",
|
||||
"passwords_do_not_match": "گذرواژهها مطابقت ندارند",
|
||||
@ -291,7 +292,8 @@
|
||||
"push_notifications": "پوش نوتیفیکیشن",
|
||||
"retype_password": "گذرواژه را دوباره بنویسید",
|
||||
"save": "ذخیره",
|
||||
"saved": "ذخیره شد"
|
||||
"saved": "ذخیره شد",
|
||||
"success_transaction_broadcasted" : "موفقیتآمیز بود! تراکنش شما منتشر شد."
|
||||
},
|
||||
"notifications": {
|
||||
"would_you_like_to_receive_notifications": "آیا میخواهید هنگام دریافت وجه اعلان دریافت کنید؟",
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { ActivityIndicator, Linking, StyleSheet, View, KeyboardAvoidingView, Platform, Text, TextInput } from 'react-native';
|
||||
import { ActivityIndicator, Linking, StyleSheet, View, KeyboardAvoidingView, Platform, TextInput } from 'react-native';
|
||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||
import loc from '../../loc';
|
||||
import { HDSegwitBech32Wallet } from '../../class';
|
||||
@ -14,10 +14,11 @@ import {
|
||||
BlueTextCentered,
|
||||
BlueBigCheckmark,
|
||||
BlueNavigationStyle,
|
||||
BlueButtonLink,
|
||||
} from '../../BlueComponents';
|
||||
import { BlueCurrentTheme } from '../../components/themes';
|
||||
import BlueElectrum from '../../blue_modules/BlueElectrum';
|
||||
import Notifications from '../../blue_modules/notifications';
|
||||
import { useTheme } from '@react-navigation/native';
|
||||
const bitcoin = require('bitcoinjs-lib');
|
||||
|
||||
const BROADCAST_RESULT = Object.freeze({
|
||||
@ -28,9 +29,23 @@ const BROADCAST_RESULT = Object.freeze({
|
||||
});
|
||||
|
||||
const Broadcast = () => {
|
||||
const [tx, setTx] = useState('');
|
||||
const [txHex, setTxHex] = useState('');
|
||||
const [tx, setTx] = useState();
|
||||
const [txHex, setTxHex] = useState();
|
||||
const { colors } = useTheme();
|
||||
const [broadcastResult, setBroadcastResult] = useState(BROADCAST_RESULT.none);
|
||||
const stylesHooks = StyleSheet.create({
|
||||
blueArea: {
|
||||
backgroundColor: colors.background,
|
||||
},
|
||||
text: {
|
||||
color: colors.foregroundColor,
|
||||
},
|
||||
input: {
|
||||
borderColor: colors.formBorder,
|
||||
borderBottomColor: colors.formBorder,
|
||||
backgroundColor: colors.inputBackgroundColor,
|
||||
},
|
||||
});
|
||||
const handleUpdateTxHex = nextValue => setTxHex(nextValue.trim());
|
||||
const handleBroadcast = async () => {
|
||||
setBroadcastResult(BROADCAST_RESULT.pending);
|
||||
@ -43,7 +58,9 @@ const Broadcast = () => {
|
||||
const tx = bitcoin.Transaction.fromHex(txHex);
|
||||
const txid = tx.getId();
|
||||
setTx(txid);
|
||||
|
||||
setBroadcastResult(BROADCAST_RESULT.success);
|
||||
ReactNativeHapticFeedback.trigger('notificationSuccess', { ignoreAndroidSystemSettings: false });
|
||||
Notifications.majorTomToGroundControl([], [], [txid]);
|
||||
} else {
|
||||
setBroadcastResult(BROADCAST_RESULT.error);
|
||||
@ -82,17 +99,21 @@ const Broadcast = () => {
|
||||
<BlueFormLabel>{status}</BlueFormLabel>
|
||||
{BROADCAST_RESULT.pending === broadcastResult && <ActivityIndicator size="small" />}
|
||||
</View>
|
||||
<TextInput
|
||||
style={styles.text}
|
||||
maxHeight={100}
|
||||
minHeight={100}
|
||||
maxWidth="100%"
|
||||
minWidth="100%"
|
||||
multiline
|
||||
editable
|
||||
value={txHex}
|
||||
onChangeText={handleUpdateTxHex}
|
||||
/>
|
||||
|
||||
<View style={[styles.input, stylesHooks.input]}>
|
||||
<TextInput
|
||||
style={styles.text}
|
||||
maxHeight={100}
|
||||
minHeight={100}
|
||||
maxWidth="100%"
|
||||
minWidth="100%"
|
||||
multiline
|
||||
editable
|
||||
placeholderTextColor="#81868e"
|
||||
value={txHex}
|
||||
onChangeText={handleUpdateTxHex}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<BlueSpacing10 />
|
||||
<BlueButton
|
||||
@ -133,9 +154,6 @@ const styles = StyleSheet.create({
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
},
|
||||
link: {
|
||||
color: BlueCurrentTheme.colors.foregroundColor,
|
||||
},
|
||||
mainCard: {
|
||||
padding: 0,
|
||||
display: 'flex',
|
||||
@ -155,41 +173,39 @@ const styles = StyleSheet.create({
|
||||
height: 30,
|
||||
maxHeight: 30,
|
||||
},
|
||||
text: {
|
||||
flex: 1,
|
||||
borderColor: '#ebebeb',
|
||||
backgroundColor: '#d2f8d6',
|
||||
input: {
|
||||
flexDirection: 'row',
|
||||
borderWidth: 1,
|
||||
borderBottomWidth: 0.5,
|
||||
alignItems: 'center',
|
||||
borderRadius: 4,
|
||||
marginTop: 20,
|
||||
color: BlueCurrentTheme.colors.foregroundColor,
|
||||
fontWeight: '500',
|
||||
fontSize: 14,
|
||||
paddingHorizontal: 16,
|
||||
paddingBottom: 16,
|
||||
paddingTop: 16,
|
||||
},
|
||||
text: {
|
||||
padding: 8,
|
||||
minHeight: 33,
|
||||
color: '#81868e',
|
||||
},
|
||||
});
|
||||
|
||||
function SuccessScreen({ tx }) {
|
||||
const SuccessScreen = ({ tx }) => {
|
||||
if (!tx) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.wrapper}>
|
||||
<BlueCard>
|
||||
<View style={styles.broadcastResultWrapper}>
|
||||
<BlueBigCheckmark />
|
||||
<BlueSpacing20 />
|
||||
<BlueTextCentered>Success! You transaction has been broadcasted!</BlueTextCentered>
|
||||
<BlueTextCentered>{loc.settings.success_transaction_broadcasted}</BlueTextCentered>
|
||||
<BlueSpacing10 />
|
||||
<Text style={styles.link} onPress={() => Linking.openURL(`https://blockstream.info/tx/${tx}`)}>
|
||||
Open link in explorer
|
||||
</Text>
|
||||
<BlueButtonLink title={loc.settings.open_link_in_explorer} onPress={() => Linking.openURL(`https://blockstream.info/tx/${tx}`)} />
|
||||
</View>
|
||||
</BlueCard>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
SuccessScreen.propTypes = {
|
||||
tx: PropTypes.string.isRequired,
|
||||
|
@ -1,17 +1,16 @@
|
||||
/* global alert */
|
||||
import React, { useState } from 'react';
|
||||
import { ActivityIndicator, Platform, ScrollView, StyleSheet, View } from 'react-native';
|
||||
import { ActivityIndicator, ScrollView, StyleSheet, View } from 'react-native';
|
||||
import { BlueNavigationStyle, BlueSpacing20, SafeBlueArea } from '../../BlueComponents';
|
||||
import { DynamicQRCode } from '../../components/DynamicQRCode';
|
||||
import { SquareButton } from '../../components/SquareButton';
|
||||
import { getSystemName } from 'react-native-device-info';
|
||||
import loc from '../../loc';
|
||||
import { launchCamera } from 'react-native-image-picker';
|
||||
import ScanQRCode from './ScanQRCode';
|
||||
import { useNavigation, useRoute, useTheme } from '@react-navigation/native';
|
||||
import ActionSheet from '../ActionSheet';
|
||||
const bitcoin = require('bitcoinjs-lib');
|
||||
|
||||
const fs = require('../../blue_modules/fs');
|
||||
const LocalQRCode = require('@remobile/react-native-qrcode-local-image');
|
||||
const isDesktop = getSystemName() === 'Mac OS X';
|
||||
|
||||
const PsbtMultisigQRCode = () => {
|
||||
@ -41,35 +40,33 @@ const PsbtMultisigQRCode = () => {
|
||||
} else if (ret.data.indexOf('+') === -1 && ret.data.indexOf('=') === -1 && ret.data.indexOf('=') === -1) {
|
||||
// this looks like NOT base64, so maybe its transaction's hex
|
||||
// we dont support it in this flow
|
||||
alert(loc.wallets.import_error);
|
||||
} else {
|
||||
// psbt base64?
|
||||
navigate('PsbtMultisig', { receivedPSBTBase64: ret.data });
|
||||
}
|
||||
};
|
||||
|
||||
const showActionSheet = () => {
|
||||
const options = [loc._.cancel, loc.wallets.take_photo, loc.wallets.list_long_choose, loc.wallets.import_file];
|
||||
|
||||
ActionSheet.showActionSheetWithOptions({ options, cancelButtonIndex: 0 }, async buttonIndex => {
|
||||
if (buttonIndex === 1) {
|
||||
fs.takePhotoWithImagePickerAndReadPhoto.then(onBarScanned);
|
||||
} else if (buttonIndex === 2) {
|
||||
fs.showImagePickerAndReadImage(onBarScanned).catch(error => alert(error.message));
|
||||
} else if (buttonIndex === 3) {
|
||||
const { data } = await fs.showFilePickerAndReadFile();
|
||||
if (data) {
|
||||
onBarScanned({ data });
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const openScanner = () => {
|
||||
if (isDesktop) {
|
||||
launchCamera(
|
||||
{
|
||||
title: null,
|
||||
mediaType: 'photo',
|
||||
takePhotoButtonTitle: null,
|
||||
},
|
||||
response => {
|
||||
if (response.uri) {
|
||||
const uri = Platform.OS === 'ios' ? response.uri.toString().replace('file://', '') : response.uri;
|
||||
LocalQRCode.decode(uri, (error, result) => {
|
||||
if (!error) {
|
||||
onBarScanned(result);
|
||||
} else {
|
||||
alert(loc.send.qr_error_no_qrcode);
|
||||
}
|
||||
});
|
||||
} else if (response.error) {
|
||||
ScanQRCode.presentCameraNotAuthorizedAlert(response.error);
|
||||
}
|
||||
},
|
||||
);
|
||||
showActionSheet();
|
||||
} else {
|
||||
navigate('ScanQRCodeRoot', {
|
||||
screen: 'ScanQRCode',
|
||||
|
@ -84,7 +84,7 @@ const TransactionsDetails = () => {
|
||||
let foundTx = {};
|
||||
let from = [];
|
||||
let to = [];
|
||||
for (const tx of getTransactions()) {
|
||||
for (const tx of getTransactions(null, Infinity, true)) {
|
||||
if (tx.hash === hash) {
|
||||
foundTx = tx;
|
||||
for (const input of foundTx.inputs) {
|
||||
|
@ -87,7 +87,7 @@ const TransactionsStatus = () => {
|
||||
}
|
||||
}
|
||||
|
||||
for (const tx of getTransactions()) {
|
||||
for (const tx of getTransactions(null, Infinity, true)) {
|
||||
if (tx.hash === hash) {
|
||||
setTX(tx);
|
||||
break;
|
||||
|
@ -29,8 +29,6 @@ import { HDSegwitBech32Wallet, MultisigCosigner, MultisigHDWallet } from '../../
|
||||
import { useNavigation, useRoute, useTheme } from '@react-navigation/native';
|
||||
import loc from '../../loc';
|
||||
import { getSystemName } from 'react-native-device-info';
|
||||
import { launchCamera } from 'react-native-image-picker';
|
||||
import ScanQRCode from '../send/ScanQRCode';
|
||||
import QRCode from 'react-native-qrcode-svg';
|
||||
import { SquareButton } from '../../components/SquareButton';
|
||||
import BottomModal from '../../components/BottomModal';
|
||||
@ -38,6 +36,7 @@ import MultipleStepsListItem, {
|
||||
MultipleStepsListItemButtohType,
|
||||
MultipleStepsListItemDashType,
|
||||
} from '../../components/MultipleStepsListItem';
|
||||
import ActionSheet from '../ActionSheet';
|
||||
import Clipboard from '@react-native-community/clipboard';
|
||||
import showPopupMenu from 'react-native-popup-menu-android';
|
||||
import ToolTip from 'react-native-tooltip';
|
||||
@ -48,7 +47,6 @@ const prompt = require('../../blue_modules/prompt');
|
||||
const A = require('../../blue_modules/analytics');
|
||||
const fs = require('../../blue_modules/fs');
|
||||
const isDesktop = getSystemName() === 'Mac OS X';
|
||||
const LocalQRCode = require('@remobile/react-native-qrcode-local-image');
|
||||
const staticCache = {};
|
||||
|
||||
const WalletsAddMultisigStep2 = () => {
|
||||
@ -315,7 +313,7 @@ const WalletsAddMultisigStep2 = () => {
|
||||
|
||||
const onBarScanned = ret => {
|
||||
setIsProvideMnemonicsModalVisible(false);
|
||||
navigation.dangerouslyGetParent().pop();
|
||||
if (!isDesktop) navigation.dangerouslyGetParent().pop();
|
||||
if (!ret.data) ret = { data: ret };
|
||||
if (ret.data.toUpperCase().startsWith('UR')) {
|
||||
alert('BC-UR not decoded. This should never happen');
|
||||
@ -392,29 +390,8 @@ const WalletsAddMultisigStep2 = () => {
|
||||
};
|
||||
|
||||
const scanOrOpenFile = () => {
|
||||
setIsProvideMnemonicsModalVisible(false);
|
||||
if (isDesktop) {
|
||||
launchCamera(
|
||||
{
|
||||
title: null,
|
||||
mediaType: 'photo',
|
||||
takePhotoButtonTitle: null,
|
||||
},
|
||||
response => {
|
||||
if (response.uri) {
|
||||
const uri = Platform.OS === 'ios' ? response.uri.toString().replace('file://', '') : response.uri;
|
||||
LocalQRCode.decode(uri, (error, result) => {
|
||||
if (!error) {
|
||||
onBarScanned(result);
|
||||
} else {
|
||||
alert(loc.send.qr_error_no_qrcode);
|
||||
}
|
||||
});
|
||||
} else if (response.error) {
|
||||
ScanQRCode.presentCameraNotAuthorizedAlert(response.error);
|
||||
}
|
||||
},
|
||||
);
|
||||
showActionSheet();
|
||||
} else {
|
||||
navigation.navigate('ScanQRCodeRoot', {
|
||||
screen: 'ScanQRCode',
|
||||
@ -426,6 +403,23 @@ const WalletsAddMultisigStep2 = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const showActionSheet = () => {
|
||||
const options = [loc._.cancel, loc.wallets.take_photo, loc.wallets.list_long_choose, loc.wallets.import_file];
|
||||
|
||||
ActionSheet.showActionSheetWithOptions({ options, cancelButtonIndex: 0 }, async buttonIndex => {
|
||||
if (buttonIndex === 1) {
|
||||
fs.takePhotoWithImagePickerAndReadPhoto.then(onBarScanned);
|
||||
} else if (buttonIndex === 2) {
|
||||
fs.showImagePickerAndReadImage(onBarScanned).catch(error => alert(error.message));
|
||||
} else if (buttonIndex === 3) {
|
||||
const { data } = await fs.showFilePickerAndReadFile();
|
||||
if (data) {
|
||||
onBarScanned({ data });
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const _renderKeyItem = el => {
|
||||
const renderProvideKeyButtons = el.index === cosigners.length;
|
||||
const isChecked = el.index < cosigners.length;
|
||||
|
Loading…
Reference in New Issue
Block a user