import React, { useMemo, useState, useContext, useEffect, useRef } from 'react'; import PropTypes from 'prop-types'; import _debounce from 'lodash/debounce'; import Modal from 'react-native-modal'; import { ListItem, Avatar, Badge } from 'react-native-elements'; import { StyleSheet, FlatList, KeyboardAvoidingView, View, TextInput, Platform, Keyboard, TouchableWithoutFeedback } from 'react-native'; import { useRoute, useTheme, useNavigation } from '@react-navigation/native'; import loc, { formatBalanceWithoutSuffix } from '../../loc'; import { BitcoinUnit } from '../../models/bitcoinUnits'; import { BlueNavigationStyle, SafeBlueArea, BlueSpacing20, BlueButton, BlueListItem } from '../../BlueComponents'; import { BlueStorageContext } from '../../blue_modules/storage-context'; const oStyles = StyleSheet.create({ avatar: { borderColor: 'rgba(0, 0, 0, 0.5)', borderWidth: StyleSheet.hairlineWidth }, amount: { fontWeight: 'bold' }, memo: { fontSize: 13, marginTop: 3 }, }); const Output = ({ item: { txid, value, vout }, oMemo, frozen, full = false, onPress }) => { const { colors } = useTheme(); const { txMetadata } = useContext(BlueStorageContext); const memo = txMetadata[txid]?.memo || oMemo || ''; const fullId = `${txid}:${vout}`; const shortId = `${txid.substring(0, 6)}...${txid.substr(txid.length - 6)}:${vout}`; const color = `#${txid.substring(0, 6)}`; const amount = formatBalanceWithoutSuffix(value, BitcoinUnit.BTC, true); return ( {amount} {full ? ( <> {memo ? memo + '\n' : null} {fullId} ) : ( {memo || shortId} )} {frozen && } ); }; Output.propTypes = { item: PropTypes.shape({ txid: PropTypes.string.isRequired, value: PropTypes.number.isRequired, vout: PropTypes.number.isRequired, }), oMemo: PropTypes.string, frozen: PropTypes.bool, full: PropTypes.bool, onPress: PropTypes.func, }; const mStyles = StyleSheet.create({ memoTextInput: { flexDirection: 'row', borderWidth: 1, borderBottomWidth: 0.5, minHeight: 44, height: 44, alignItems: 'center', marginVertical: 8, borderRadius: 4, paddingHorizontal: 8, color: '#81868e', }, }); const OutputModalContent = ({ output, wallet, onUseCoin }) => { const { colors } = useTheme(); const { txMetadata, saveToDisk } = useContext(BlueStorageContext); const [frozen, setFrozen] = useState(wallet.getUTXOMetadata(output.txid, output.vout).frozen || false); const [memo, setMemo] = useState(wallet.getUTXOMetadata(output.txid, output.vout).memo || txMetadata[output.txid]?.memo || ''); const onFreeze = value => setFrozen(value); const onMemoChange = value => setMemo(value); // save on form change. Because effect called on each event, debounce it. const debouncedSave = useRef( _debounce(async (frozen, memo) => { wallet.setUTXOMetadata(output.txid, output.vout, { frozen, memo }); await saveToDisk(); }, 500), ); useEffect(() => { debouncedSave.current(frozen, memo); }, [frozen, memo]); return ( <> onUseCoin([output])} /> ); }; OutputModalContent.propTypes = { output: PropTypes.object, wallet: PropTypes.object, onUseCoin: PropTypes.func.isRequired, }; const CoinControl = () => { const { colors } = useTheme(); const navigation = useNavigation(); const { walletId, onUTXOChoose } = useRoute().params; const { wallets } = useContext(BlueStorageContext); const wallet = wallets.find(w => w.getID() === walletId); const utxo = useMemo(() => wallet.getUtxo({ frozen: true }), [wallet]); const [output, setOutput] = useState(); const handleChoose = item => setOutput(item); const handleUseCoin = utxo => { setOutput(null); navigation.pop(); onUTXOChoose(utxo); }; const renderItem = p => { const { memo, frozen } = wallet.getUTXOMetadata(p.item.txid, p.item.vout); return handleChoose(p.item)} />; }; return ( { Keyboard.dismiss(); setOutput(false); }} > {output && } `${item.txid}:${item.vout}`} /> ); }; const styles = StyleSheet.create({ bottomModal: { justifyContent: 'flex-end', margin: 0, }, modalContent: { padding: 22, justifyContent: 'center', borderTopLeftRadius: 16, borderTopRightRadius: 16, borderColor: 'rgba(0, 0, 0, 0.1)', minHeight: 350, height: 350, }, }); CoinControl.navigationOptions = () => ({ ...BlueNavigationStyle(null, false), title: loc.cc.header, gestureEnabled: false, }); export default CoinControl;