Merge pull request #2756 from BlueWallet/limpbrains-sendmany-index

FIX: recipients index
This commit is contained in:
GLaDOS 2021-03-12 15:29:58 +00:00 committed by GitHub
commit 95e26c38d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 63 additions and 34 deletions

View File

@ -57,6 +57,7 @@ const SendDetails = () => {
const navigation = useNavigation();
const { name, params: routeParams } = useRoute();
const scrollView = useRef();
const scrollIndex = useRef(0);
const { colors } = useTheme();
// state
@ -68,7 +69,6 @@ const SendDetails = () => {
const [isFeeSelectionModalVisible, setIsFeeSelectionModalVisible] = useState(false);
const [optionsVisible, setOptionsVisible] = useState(false);
const [isTransactionReplaceable, setIsTransactionReplaceable] = useState(false);
const [recipientsScrollIndex, setRecipientsScrollIndex] = useState(0);
const [addresses, setAddresses] = useState([]);
const [units, setUnits] = useState([]);
const [memo, setMemo] = useState('');
@ -137,7 +137,7 @@ const SendDetails = () => {
if (routeParams.uri) {
try {
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;
setAddresses(addresses);
setMemo(initialMemo);
@ -148,13 +148,13 @@ const SendDetails = () => {
Alert.alert(loc.errors.error, loc.send.details_error_decode);
}
} else if (routeParams.address) {
addresses.push({ address: routeParams.address });
addresses.push({ address: routeParams.address, key: String(Math.random()) });
if (routeParams.memo) initialMemo = routeParams.memo;
setAddresses(addresses);
setMemo(initialMemo);
setAmountUnit(BitcoinUnit.BTC);
} else {
setAddresses([{ address: '' }]);
setAddresses([{ address: '', key: String(Math.random()) }]);
}
// we are ready!
@ -343,8 +343,8 @@ const SendDetails = () => {
const unitsCopy = [...units];
const dataWithoutSchema = data.replace('bitcoin:', '').replace('BITCOIN:', '');
if (wallet.isAddressValid(dataWithoutSchema)) {
recipients[recipientsScrollIndex].address = dataWithoutSchema;
unitsCopy[recipientsScrollIndex] = amountUnit;
recipients[scrollIndex.current].address = dataWithoutSchema;
unitsCopy[scrollIndex.current] = amountUnit;
setAddresses(recipients);
setUnits(unitsCopy);
setIsLoading(false);
@ -368,10 +368,10 @@ const SendDetails = () => {
console.log('options', options);
if (btcAddressRx.test(address) || address.startsWith('bc1') || address.startsWith('BC1')) {
unitsCopy[recipientsScrollIndex] = BitcoinUnit.BTC; // also resetting current unit to BTC
recipients[recipientsScrollIndex].address = address;
recipients[recipientsScrollIndex].amount = options.amount;
recipients[recipientsScrollIndex].amountSats = new BigNumber(options.amount).multipliedBy(100000000).toNumber();
unitsCopy[scrollIndex.current] = BitcoinUnit.BTC; // also resetting current unit to BTC
recipients[scrollIndex.current].address = address;
recipients[scrollIndex.current].amount = options.amount;
recipients[scrollIndex.current].amountSats = new BigNumber(options.amount).multipliedBy(100000000).toNumber();
setAddresses(recipients);
setMemo(options.label || options.message);
setUnits(unitsCopy);
@ -755,24 +755,27 @@ const SendDetails = () => {
});
};
const handleAddRecipient = () => {
addresses.push({ address: '' });
const handleAddRecipient = async () => {
addresses.push({ address: '', key: String(Math.random()) }); // key is for the FlatList
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut, () => scrollView.current.scrollToEnd());
setAddresses(addresses);
setAddresses([...addresses]);
setOptionsVisible(false);
scrollView.current.scrollToEnd();
if (addresses.length > 1) scrollView.current.flashScrollIndicators();
setRecipientsScrollIndex(addresses.length - 1);
if (addresses.length === 0) return;
await sleep(200); // wait for animation
scrollView.current.flashScrollIndicators();
};
const handleRemoveRecipient = () => {
addresses.splice(recipientsScrollIndex, 1);
const handleRemoveRecipient = async () => {
const last = scrollIndex.current === addresses.length - 1;
addresses.splice(scrollIndex.current, 1);
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
setAddresses(addresses);
setAddresses([...addresses]);
setOptionsVisible(false);
if (addresses.length > 1) scrollView.current.flashScrollIndicators();
// after deletion it automatically scrolls to the last one
setRecipientsScrollIndex(addresses.length - 1);
if (addresses.length === 0) return;
await sleep(200); // wait for animation
scrollView.current.flashScrollIndicators();
if (last && Platform.OS === 'android') scrollView.current.scrollToEnd(); // fix white screen on android
};
const handleCoinControl = () => {
@ -832,14 +835,22 @@ const SendDetails = () => {
setIsTransactionReplaceable(value);
};
const scrollViewCurrentIndex = () => {
Keyboard.dismiss();
const offset = scrollView.current.contentOffset;
if (offset) {
const page = Math.round(offset.x / Dimensions.get('window').width);
return page;
}
return 0;
// because of https://github.com/facebook/react-native/issues/21718 we use
// onScroll for android and onMomentumScrollEnd for iOS
const handleRecipientsScrollEnds = e => {
if (Platform.OS === 'android') return; // for android we use handleRecipientsScroll
const contentOffset = e.nativeEvent.contentOffset;
const viewSize = e.nativeEvent.layoutMeasurement;
const index = Math.floor(contentOffset.x / viewSize.width);
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 = () => {
@ -852,7 +863,7 @@ const SendDetails = () => {
text: loc._.ok,
onPress: async () => {
Keyboard.dismiss();
const recipient = addresses[scrollViewCurrentIndex()];
const recipient = addresses[scrollIndex.current];
recipient.amount = BitcoinUnit.MAX;
recipient.amountSats = BitcoinUnit.MAX;
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
@ -1114,7 +1125,7 @@ const SendDetails = () => {
{isLoading ? (
<ActivityIndicator />
) : (
<BlueButton onPress={() => createTransaction()} title={loc.send.details_next} testID="CreateTransactionButton" />
<BlueButton onPress={createTransaction} title={loc.send.details_next} testID="CreateTransactionButton" />
)}
</View>
);
@ -1260,13 +1271,15 @@ const SendDetails = () => {
scrollEnabled={addresses.length > 1}
data={addresses}
renderItem={renderBitcoinTransactionInfoFields}
keyExtractor={(_item, index) => `${index}`}
ref={scrollView}
horizontal
pagingEnabled
removeClippedSubviews={false}
onMomentumScrollBegin={Keyboard.dismiss}
scrollIndicatorInsets={{ top: 0, left: 8, bottom: 0, right: 8 }}
onMomentumScrollEnd={handleRecipientsScrollEnds}
onScroll={handleRecipientsScroll}
scrollEventThrottle={200}
scrollIndicatorInsets={styles.scrollViewIndicator}
contentContainerStyle={styles.scrollViewContent}
/>
<View style={[styles.memo, stylesHook.memo]}>
@ -1338,6 +1351,12 @@ const styles = StyleSheet.create({
scrollViewContent: {
flexDirection: 'row',
},
scrollViewIndicator: {
top: 0,
left: 8,
bottom: 0,
right: 8,
},
modalContent: {
padding: 22,
borderTopLeftRadius: 16,

View File

@ -596,6 +596,16 @@ describe('BlueWallet UI Tests', () => {
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('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('AddRecipient')).tap();
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(transaction.outs[0].value, 50000);
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: