mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2024-11-19 18:00:17 +01:00
Merge pull request #2756 from BlueWallet/limpbrains-sendmany-index
FIX: recipients index
This commit is contained in:
commit
95e26c38d4
@ -57,6 +57,7 @@ const SendDetails = () => {
|
|||||||
const navigation = useNavigation();
|
const navigation = useNavigation();
|
||||||
const { name, params: routeParams } = useRoute();
|
const { name, params: routeParams } = useRoute();
|
||||||
const scrollView = useRef();
|
const scrollView = useRef();
|
||||||
|
const scrollIndex = useRef(0);
|
||||||
const { colors } = useTheme();
|
const { colors } = useTheme();
|
||||||
|
|
||||||
// state
|
// state
|
||||||
@ -68,7 +69,6 @@ const SendDetails = () => {
|
|||||||
const [isFeeSelectionModalVisible, setIsFeeSelectionModalVisible] = useState(false);
|
const [isFeeSelectionModalVisible, setIsFeeSelectionModalVisible] = useState(false);
|
||||||
const [optionsVisible, setOptionsVisible] = useState(false);
|
const [optionsVisible, setOptionsVisible] = useState(false);
|
||||||
const [isTransactionReplaceable, setIsTransactionReplaceable] = useState(false);
|
const [isTransactionReplaceable, setIsTransactionReplaceable] = useState(false);
|
||||||
const [recipientsScrollIndex, setRecipientsScrollIndex] = useState(0);
|
|
||||||
const [addresses, setAddresses] = useState([]);
|
const [addresses, setAddresses] = useState([]);
|
||||||
const [units, setUnits] = useState([]);
|
const [units, setUnits] = useState([]);
|
||||||
const [memo, setMemo] = useState('');
|
const [memo, setMemo] = useState('');
|
||||||
@ -137,7 +137,7 @@ const SendDetails = () => {
|
|||||||
if (routeParams.uri) {
|
if (routeParams.uri) {
|
||||||
try {
|
try {
|
||||||
const { address, amount, memo, payjoinUrl } = DeeplinkSchemaMatch.decodeBitcoinUri(routeParams.uri);
|
const { address, amount, memo, payjoinUrl } = DeeplinkSchemaMatch.decodeBitcoinUri(routeParams.uri);
|
||||||
addresses.push({ address, amount, amountSats: currency.btcToSatoshi(amount) });
|
addresses.push({ address, amount, amountSats: currency.btcToSatoshi(amount), key: String(Math.random()) });
|
||||||
initialMemo = memo;
|
initialMemo = memo;
|
||||||
setAddresses(addresses);
|
setAddresses(addresses);
|
||||||
setMemo(initialMemo);
|
setMemo(initialMemo);
|
||||||
@ -148,13 +148,13 @@ const SendDetails = () => {
|
|||||||
Alert.alert(loc.errors.error, loc.send.details_error_decode);
|
Alert.alert(loc.errors.error, loc.send.details_error_decode);
|
||||||
}
|
}
|
||||||
} else if (routeParams.address) {
|
} else if (routeParams.address) {
|
||||||
addresses.push({ address: routeParams.address });
|
addresses.push({ address: routeParams.address, key: String(Math.random()) });
|
||||||
if (routeParams.memo) initialMemo = routeParams.memo;
|
if (routeParams.memo) initialMemo = routeParams.memo;
|
||||||
setAddresses(addresses);
|
setAddresses(addresses);
|
||||||
setMemo(initialMemo);
|
setMemo(initialMemo);
|
||||||
setAmountUnit(BitcoinUnit.BTC);
|
setAmountUnit(BitcoinUnit.BTC);
|
||||||
} else {
|
} else {
|
||||||
setAddresses([{ address: '' }]);
|
setAddresses([{ address: '', key: String(Math.random()) }]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// we are ready!
|
// we are ready!
|
||||||
@ -343,8 +343,8 @@ const SendDetails = () => {
|
|||||||
const unitsCopy = [...units];
|
const unitsCopy = [...units];
|
||||||
const dataWithoutSchema = data.replace('bitcoin:', '').replace('BITCOIN:', '');
|
const dataWithoutSchema = data.replace('bitcoin:', '').replace('BITCOIN:', '');
|
||||||
if (wallet.isAddressValid(dataWithoutSchema)) {
|
if (wallet.isAddressValid(dataWithoutSchema)) {
|
||||||
recipients[recipientsScrollIndex].address = dataWithoutSchema;
|
recipients[scrollIndex.current].address = dataWithoutSchema;
|
||||||
unitsCopy[recipientsScrollIndex] = amountUnit;
|
unitsCopy[scrollIndex.current] = amountUnit;
|
||||||
setAddresses(recipients);
|
setAddresses(recipients);
|
||||||
setUnits(unitsCopy);
|
setUnits(unitsCopy);
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
@ -368,10 +368,10 @@ const SendDetails = () => {
|
|||||||
|
|
||||||
console.log('options', options);
|
console.log('options', options);
|
||||||
if (btcAddressRx.test(address) || address.startsWith('bc1') || address.startsWith('BC1')) {
|
if (btcAddressRx.test(address) || address.startsWith('bc1') || address.startsWith('BC1')) {
|
||||||
unitsCopy[recipientsScrollIndex] = BitcoinUnit.BTC; // also resetting current unit to BTC
|
unitsCopy[scrollIndex.current] = BitcoinUnit.BTC; // also resetting current unit to BTC
|
||||||
recipients[recipientsScrollIndex].address = address;
|
recipients[scrollIndex.current].address = address;
|
||||||
recipients[recipientsScrollIndex].amount = options.amount;
|
recipients[scrollIndex.current].amount = options.amount;
|
||||||
recipients[recipientsScrollIndex].amountSats = new BigNumber(options.amount).multipliedBy(100000000).toNumber();
|
recipients[scrollIndex.current].amountSats = new BigNumber(options.amount).multipliedBy(100000000).toNumber();
|
||||||
setAddresses(recipients);
|
setAddresses(recipients);
|
||||||
setMemo(options.label || options.message);
|
setMemo(options.label || options.message);
|
||||||
setUnits(unitsCopy);
|
setUnits(unitsCopy);
|
||||||
@ -755,24 +755,27 @@ const SendDetails = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAddRecipient = () => {
|
const handleAddRecipient = async () => {
|
||||||
addresses.push({ address: '' });
|
addresses.push({ address: '', key: String(Math.random()) }); // key is for the FlatList
|
||||||
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut, () => scrollView.current.scrollToEnd());
|
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut, () => scrollView.current.scrollToEnd());
|
||||||
setAddresses(addresses);
|
setAddresses([...addresses]);
|
||||||
setOptionsVisible(false);
|
setOptionsVisible(false);
|
||||||
scrollView.current.scrollToEnd();
|
scrollView.current.scrollToEnd();
|
||||||
if (addresses.length > 1) scrollView.current.flashScrollIndicators();
|
if (addresses.length === 0) return;
|
||||||
setRecipientsScrollIndex(addresses.length - 1);
|
await sleep(200); // wait for animation
|
||||||
|
scrollView.current.flashScrollIndicators();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRemoveRecipient = () => {
|
const handleRemoveRecipient = async () => {
|
||||||
addresses.splice(recipientsScrollIndex, 1);
|
const last = scrollIndex.current === addresses.length - 1;
|
||||||
|
addresses.splice(scrollIndex.current, 1);
|
||||||
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
|
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
|
||||||
setAddresses(addresses);
|
setAddresses([...addresses]);
|
||||||
setOptionsVisible(false);
|
setOptionsVisible(false);
|
||||||
if (addresses.length > 1) scrollView.current.flashScrollIndicators();
|
if (addresses.length === 0) return;
|
||||||
// after deletion it automatically scrolls to the last one
|
await sleep(200); // wait for animation
|
||||||
setRecipientsScrollIndex(addresses.length - 1);
|
scrollView.current.flashScrollIndicators();
|
||||||
|
if (last && Platform.OS === 'android') scrollView.current.scrollToEnd(); // fix white screen on android
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCoinControl = () => {
|
const handleCoinControl = () => {
|
||||||
@ -832,14 +835,22 @@ const SendDetails = () => {
|
|||||||
setIsTransactionReplaceable(value);
|
setIsTransactionReplaceable(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const scrollViewCurrentIndex = () => {
|
// because of https://github.com/facebook/react-native/issues/21718 we use
|
||||||
Keyboard.dismiss();
|
// onScroll for android and onMomentumScrollEnd for iOS
|
||||||
const offset = scrollView.current.contentOffset;
|
const handleRecipientsScrollEnds = e => {
|
||||||
if (offset) {
|
if (Platform.OS === 'android') return; // for android we use handleRecipientsScroll
|
||||||
const page = Math.round(offset.x / Dimensions.get('window').width);
|
const contentOffset = e.nativeEvent.contentOffset;
|
||||||
return page;
|
const viewSize = e.nativeEvent.layoutMeasurement;
|
||||||
}
|
const index = Math.floor(contentOffset.x / viewSize.width);
|
||||||
return 0;
|
scrollIndex.current = index;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRecipientsScroll = e => {
|
||||||
|
if (Platform.OS === 'ios') return; // for iOS we use handleRecipientsScrollEnds
|
||||||
|
const contentOffset = e.nativeEvent.contentOffset;
|
||||||
|
const viewSize = e.nativeEvent.layoutMeasurement;
|
||||||
|
const index = Math.floor(contentOffset.x / viewSize.width);
|
||||||
|
scrollIndex.current = index;
|
||||||
};
|
};
|
||||||
|
|
||||||
const onUseAllPressed = () => {
|
const onUseAllPressed = () => {
|
||||||
@ -852,7 +863,7 @@ const SendDetails = () => {
|
|||||||
text: loc._.ok,
|
text: loc._.ok,
|
||||||
onPress: async () => {
|
onPress: async () => {
|
||||||
Keyboard.dismiss();
|
Keyboard.dismiss();
|
||||||
const recipient = addresses[scrollViewCurrentIndex()];
|
const recipient = addresses[scrollIndex.current];
|
||||||
recipient.amount = BitcoinUnit.MAX;
|
recipient.amount = BitcoinUnit.MAX;
|
||||||
recipient.amountSats = BitcoinUnit.MAX;
|
recipient.amountSats = BitcoinUnit.MAX;
|
||||||
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
|
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
|
||||||
@ -1114,7 +1125,7 @@ const SendDetails = () => {
|
|||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<ActivityIndicator />
|
<ActivityIndicator />
|
||||||
) : (
|
) : (
|
||||||
<BlueButton onPress={() => createTransaction()} title={loc.send.details_next} testID="CreateTransactionButton" />
|
<BlueButton onPress={createTransaction} title={loc.send.details_next} testID="CreateTransactionButton" />
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
@ -1260,13 +1271,15 @@ const SendDetails = () => {
|
|||||||
scrollEnabled={addresses.length > 1}
|
scrollEnabled={addresses.length > 1}
|
||||||
data={addresses}
|
data={addresses}
|
||||||
renderItem={renderBitcoinTransactionInfoFields}
|
renderItem={renderBitcoinTransactionInfoFields}
|
||||||
keyExtractor={(_item, index) => `${index}`}
|
|
||||||
ref={scrollView}
|
ref={scrollView}
|
||||||
horizontal
|
horizontal
|
||||||
pagingEnabled
|
pagingEnabled
|
||||||
removeClippedSubviews={false}
|
removeClippedSubviews={false}
|
||||||
onMomentumScrollBegin={Keyboard.dismiss}
|
onMomentumScrollBegin={Keyboard.dismiss}
|
||||||
scrollIndicatorInsets={{ top: 0, left: 8, bottom: 0, right: 8 }}
|
onMomentumScrollEnd={handleRecipientsScrollEnds}
|
||||||
|
onScroll={handleRecipientsScroll}
|
||||||
|
scrollEventThrottle={200}
|
||||||
|
scrollIndicatorInsets={styles.scrollViewIndicator}
|
||||||
contentContainerStyle={styles.scrollViewContent}
|
contentContainerStyle={styles.scrollViewContent}
|
||||||
/>
|
/>
|
||||||
<View style={[styles.memo, stylesHook.memo]}>
|
<View style={[styles.memo, stylesHook.memo]}>
|
||||||
@ -1338,6 +1351,12 @@ const styles = StyleSheet.create({
|
|||||||
scrollViewContent: {
|
scrollViewContent: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
},
|
},
|
||||||
|
scrollViewIndicator: {
|
||||||
|
top: 0,
|
||||||
|
left: 8,
|
||||||
|
bottom: 0,
|
||||||
|
right: 8,
|
||||||
|
},
|
||||||
modalContent: {
|
modalContent: {
|
||||||
padding: 22,
|
padding: 22,
|
||||||
borderTopLeftRadius: 16,
|
borderTopLeftRadius: 16,
|
||||||
|
@ -596,6 +596,16 @@ describe('BlueWallet UI Tests', () => {
|
|||||||
await element(by.id('AddressInput').withAncestor(by.id('Transaction1'))).replaceText('bc1q063ctu6jhe5k4v8ka99qac8rcm2tzjjnuktyrl');
|
await element(by.id('AddressInput').withAncestor(by.id('Transaction1'))).replaceText('bc1q063ctu6jhe5k4v8ka99qac8rcm2tzjjnuktyrl');
|
||||||
await element(by.id('BitcoinAmountInput').withAncestor(by.id('Transaction1'))).typeText('0.0002\n');
|
await element(by.id('BitcoinAmountInput').withAncestor(by.id('Transaction1'))).typeText('0.0002\n');
|
||||||
|
|
||||||
|
await element(by.id('advancedOptionsMenuButton')).tap();
|
||||||
|
await element(by.id('AddRecipient')).tap();
|
||||||
|
await yo('Transaction2'); // adding a recipient autoscrolls it to the last one
|
||||||
|
|
||||||
|
// remove last output, check if second output is shown
|
||||||
|
await element(by.id('advancedOptionsMenuButton')).tap();
|
||||||
|
await element(by.id('RemoveRecipient')).tap();
|
||||||
|
await yo('Transaction1');
|
||||||
|
|
||||||
|
// adding it again
|
||||||
await element(by.id('advancedOptionsMenuButton')).tap();
|
await element(by.id('advancedOptionsMenuButton')).tap();
|
||||||
await element(by.id('AddRecipient')).tap();
|
await element(by.id('AddRecipient')).tap();
|
||||||
await yo('Transaction2'); // adding a recipient autoscrolls it to the last one
|
await yo('Transaction2'); // adding a recipient autoscrolls it to the last one
|
||||||
@ -619,7 +629,7 @@ describe('BlueWallet UI Tests', () => {
|
|||||||
assert.strictEqual(bitcoin.address.fromOutputScript(transaction.outs[0].script), 'bc1qnapskphjnwzw2w3dk4anpxntunc77v6qrua0f7');
|
assert.strictEqual(bitcoin.address.fromOutputScript(transaction.outs[0].script), 'bc1qnapskphjnwzw2w3dk4anpxntunc77v6qrua0f7');
|
||||||
assert.strictEqual(transaction.outs[0].value, 50000);
|
assert.strictEqual(transaction.outs[0].value, 50000);
|
||||||
assert.strictEqual(bitcoin.address.fromOutputScript(transaction.outs[1].script), 'bc1q063ctu6jhe5k4v8ka99qac8rcm2tzjjnuktyrl');
|
assert.strictEqual(bitcoin.address.fromOutputScript(transaction.outs[1].script), 'bc1q063ctu6jhe5k4v8ka99qac8rcm2tzjjnuktyrl');
|
||||||
assert.strictEqual(transaction.outs[1].value, 20000);
|
assert.strictEqual(transaction.outs[1].value, 30000);
|
||||||
|
|
||||||
// now, testing sendMAX feature:
|
// now, testing sendMAX feature:
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user