434 lines
17 KiB
TypeScript
434 lines
17 KiB
TypeScript
import * as React from 'react';
|
|
import forEach from 'lodash/forEach';
|
|
import isNull from 'lodash/isNull';
|
|
import {
|
|
StyleSheet,
|
|
ScrollView,
|
|
Text,
|
|
TouchableOpacity,
|
|
View
|
|
} from 'react-native';
|
|
import { inject, observer } from 'mobx-react';
|
|
import { Route } from '@react-navigation/native';
|
|
import { StackNavigationProp } from '@react-navigation/stack';
|
|
|
|
import { Row } from '../components/layout/Row';
|
|
import Amount from '../components/Amount';
|
|
import Button from '../components/Button';
|
|
import Header from '../components/Header';
|
|
import KeyValue from '../components/KeyValue';
|
|
import Screen from '../components/Screen';
|
|
|
|
import BackendUtils from '../utils/BackendUtils';
|
|
import PrivacyUtils from '../utils/PrivacyUtils';
|
|
import Transaction from '../models/Transaction';
|
|
import UrlUtils from '../utils/UrlUtils';
|
|
|
|
import NodeInfoStore from '../stores/NodeInfoStore';
|
|
import TransactionsStore from '../stores/TransactionsStore';
|
|
|
|
import { localeString } from '../utils/LocaleUtils';
|
|
import { themeColor } from '../utils/ThemeUtils';
|
|
|
|
import CaretRight from '../assets/images/SVG/Caret Right.svg';
|
|
import EditNotes from '../assets/images/SVG/Pen.svg';
|
|
|
|
interface TransactionProps {
|
|
navigation: StackNavigationProp<any, any>;
|
|
NodeInfoStore: NodeInfoStore;
|
|
TransactionsStore: TransactionsStore;
|
|
route: Route<'Transaction', { transaction: Transaction }>;
|
|
}
|
|
|
|
interface TransactionState {
|
|
storedNote: string;
|
|
}
|
|
|
|
@inject('NodeInfoStore', 'TransactionsStore')
|
|
@observer
|
|
export default class TransactionView extends React.Component<
|
|
TransactionProps,
|
|
TransactionState
|
|
> {
|
|
state = {
|
|
storedNote: ''
|
|
};
|
|
async componentDidMount() {
|
|
const { navigation, route } = this.props;
|
|
const transaction = route.params?.transaction;
|
|
navigation.addListener('focus', () => {
|
|
this.props.TransactionsStore.resetBroadcast();
|
|
const note = transaction.getNote;
|
|
this.setState({ storedNote: note });
|
|
});
|
|
}
|
|
render() {
|
|
const { NodeInfoStore, navigation, route } = this.props;
|
|
const transaction = route.params?.transaction;
|
|
const { storedNote } = this.state;
|
|
const { testnet } = NodeInfoStore;
|
|
|
|
const {
|
|
tx,
|
|
isConfirmed,
|
|
block_hash,
|
|
getBlockHeight,
|
|
num_confirmations,
|
|
time_stamp,
|
|
destAddresses,
|
|
getFee,
|
|
getFeePercentage,
|
|
status,
|
|
getOutpoint,
|
|
raw_tx_hex,
|
|
getNoteKey
|
|
} = transaction;
|
|
const amount = transaction.getAmount;
|
|
const date = time_stamp && new Date(Number(time_stamp) * 1000);
|
|
const addresses: Array<any> = [];
|
|
|
|
destAddresses.length > 1
|
|
? `${localeString('views.Transaction.destAddresses')}:`
|
|
: `${localeString('views.Transaction.destAddress')}:`;
|
|
|
|
forEach(destAddresses, (address: any, key: string) =>
|
|
addresses.push(
|
|
<KeyValue
|
|
keyValue={
|
|
destAddresses.length > 1
|
|
? `${localeString(
|
|
'views.Transaction.destAddresses'
|
|
)} #${Number(key) + 1}`
|
|
: localeString('views.Transaction.destAddress')
|
|
}
|
|
value={
|
|
<TouchableOpacity
|
|
key={`${address}-${key}`}
|
|
onPress={() =>
|
|
UrlUtils.goToBlockExplorerAddress(
|
|
address,
|
|
testnet
|
|
)
|
|
}
|
|
>
|
|
<Text
|
|
style={{
|
|
...styles.valueWithLink,
|
|
color: themeColor('highlight')
|
|
}}
|
|
>
|
|
{`${
|
|
typeof address === 'string' &&
|
|
PrivacyUtils.sensitiveValue({
|
|
input: address
|
|
})
|
|
}`}
|
|
</Text>
|
|
</TouchableOpacity>
|
|
}
|
|
key={key}
|
|
sensitive
|
|
/>
|
|
)
|
|
);
|
|
const EditNotesButton = () => (
|
|
<TouchableOpacity
|
|
onPress={() =>
|
|
navigation.navigate('AddNotes', { noteKey: getNoteKey })
|
|
}
|
|
>
|
|
<EditNotes
|
|
style={{ alignSelf: 'center' }}
|
|
fill={themeColor('text')}
|
|
/>
|
|
</TouchableOpacity>
|
|
);
|
|
|
|
return (
|
|
<Screen>
|
|
<Header
|
|
leftComponent="Back"
|
|
centerComponent={{
|
|
text: localeString('views.Transaction.title'),
|
|
style: {
|
|
color: themeColor('text'),
|
|
fontFamily: 'PPNeueMontreal-Book'
|
|
}
|
|
}}
|
|
rightComponent={<EditNotesButton />}
|
|
navigation={navigation}
|
|
/>
|
|
<View style={styles.center}>
|
|
<Amount
|
|
sats={amount}
|
|
debit={Number(amount) <= 0}
|
|
credit={Number(amount) > 0}
|
|
sensitive
|
|
jumboText
|
|
toggleable
|
|
/>
|
|
</View>
|
|
|
|
<ScrollView
|
|
style={styles.content}
|
|
keyboardShouldPersistTaps="handled"
|
|
>
|
|
{getFee ? (
|
|
<KeyValue
|
|
keyValue={localeString(
|
|
'views.Transaction.totalFees'
|
|
)}
|
|
value={
|
|
<Row>
|
|
<Amount
|
|
sats={getFee || 0}
|
|
debit
|
|
toggleable
|
|
sensitive
|
|
/>
|
|
{getFeePercentage && (
|
|
<Text
|
|
style={{
|
|
fontFamily:
|
|
'PPNeueMontreal-Book',
|
|
color: themeColor('text')
|
|
}}
|
|
>
|
|
{` (${getFeePercentage})`}
|
|
</Text>
|
|
)}
|
|
</Row>
|
|
}
|
|
/>
|
|
) : null}
|
|
|
|
<KeyValue
|
|
keyValue={localeString(
|
|
'views.Transaction.transactionHash'
|
|
)}
|
|
value={
|
|
<TouchableOpacity
|
|
onPress={() =>
|
|
UrlUtils.goToBlockExplorerTXID(tx, testnet)
|
|
}
|
|
>
|
|
<Text
|
|
style={{
|
|
...styles.valueWithLink,
|
|
color: themeColor('highlight')
|
|
}}
|
|
>
|
|
{`${
|
|
typeof tx === 'string' &&
|
|
PrivacyUtils.sensitiveValue({
|
|
input: tx
|
|
})
|
|
}`}
|
|
</Text>
|
|
</TouchableOpacity>
|
|
}
|
|
/>
|
|
|
|
{!!block_hash && (
|
|
<KeyValue
|
|
keyValue={localeString(
|
|
'views.Transaction.blockHash'
|
|
)}
|
|
value={
|
|
<TouchableOpacity
|
|
onPress={() =>
|
|
UrlUtils.goToBlockExplorerBlockHash(
|
|
block_hash,
|
|
testnet
|
|
)
|
|
}
|
|
>
|
|
<Text
|
|
style={{
|
|
...styles.valueWithLink,
|
|
color: themeColor('highlight')
|
|
}}
|
|
>
|
|
{`${
|
|
typeof block_hash === 'string' &&
|
|
PrivacyUtils.sensitiveValue({
|
|
input: block_hash
|
|
})
|
|
}`}
|
|
</Text>
|
|
</TouchableOpacity>
|
|
}
|
|
/>
|
|
)}
|
|
|
|
{!!getBlockHeight && (
|
|
<KeyValue
|
|
keyValue={localeString(
|
|
'views.Transaction.blockHeight'
|
|
)}
|
|
value={
|
|
<TouchableOpacity
|
|
onPress={() =>
|
|
UrlUtils.goToBlockExplorerBlockHeight(
|
|
getBlockHeight.toString(),
|
|
testnet
|
|
)
|
|
}
|
|
>
|
|
<Text
|
|
style={{
|
|
...styles.valueWithLink,
|
|
color: themeColor('highlight')
|
|
}}
|
|
>
|
|
{`${
|
|
typeof getBlockHeight ===
|
|
'string' &&
|
|
PrivacyUtils.sensitiveValue({
|
|
input: getBlockHeight.toString(),
|
|
fixedLength: 5,
|
|
numberSet: true
|
|
})
|
|
}`}
|
|
</Text>
|
|
</TouchableOpacity>
|
|
}
|
|
/>
|
|
)}
|
|
|
|
{!!num_confirmations && !isNull(num_confirmations) && (
|
|
<KeyValue
|
|
keyValue={localeString('views.Transaction.numConf')}
|
|
value={num_confirmations}
|
|
color={num_confirmations > 0 ? 'green' : 'red'}
|
|
sensitive
|
|
/>
|
|
)}
|
|
|
|
{!!status && (
|
|
<KeyValue
|
|
keyValue={localeString('views.Transaction.status')}
|
|
value={status}
|
|
/>
|
|
)}
|
|
|
|
{!!date && (
|
|
<KeyValue
|
|
keyValue={localeString(
|
|
'views.Transaction.timestamp'
|
|
)}
|
|
value={date.toString()}
|
|
sensitive
|
|
/>
|
|
)}
|
|
|
|
{!!destAddresses && (
|
|
<React.Fragment>{addresses}</React.Fragment>
|
|
)}
|
|
|
|
{raw_tx_hex && (
|
|
<>
|
|
<TouchableOpacity
|
|
onPress={() =>
|
|
navigation.navigate('RawTxHex', {
|
|
value: raw_tx_hex
|
|
})
|
|
}
|
|
>
|
|
<View
|
|
style={{
|
|
marginTop: 10,
|
|
marginBottom: 10
|
|
}}
|
|
>
|
|
<Row justify="space-between">
|
|
<View style={{ flex: 1 }}>
|
|
<KeyValue
|
|
keyValue={localeString(
|
|
'views.Transaction.rawTxHex'
|
|
)}
|
|
/>
|
|
</View>
|
|
<CaretRight
|
|
fill={themeColor('text')}
|
|
width="20"
|
|
height="20"
|
|
/>
|
|
</Row>
|
|
</View>
|
|
</TouchableOpacity>
|
|
</>
|
|
)}
|
|
|
|
{storedNote && (
|
|
<TouchableOpacity
|
|
onPress={() =>
|
|
navigation.navigate('AddNotes', {
|
|
noteKey: getNoteKey
|
|
})
|
|
}
|
|
>
|
|
<KeyValue
|
|
keyValue={localeString('general.note')}
|
|
value={storedNote}
|
|
sensitive
|
|
/>
|
|
</TouchableOpacity>
|
|
)}
|
|
</ScrollView>
|
|
<View style={{ bottom: 15 }}>
|
|
{!isConfirmed && BackendUtils.supportsBumpFee() && (
|
|
<Button
|
|
title={localeString('views.BumpFee.title')}
|
|
onPress={() =>
|
|
navigation.navigate('BumpFee', {
|
|
outpoint: getOutpoint
|
|
})
|
|
}
|
|
noUppercase
|
|
containerStyle={{ marginTop: 20 }}
|
|
/>
|
|
)}
|
|
|
|
{getNoteKey && (
|
|
<Button
|
|
title={
|
|
storedNote
|
|
? localeString(
|
|
'views.SendingLightning.UpdateNote'
|
|
)
|
|
: localeString(
|
|
'views.SendingLightning.AddANote'
|
|
)
|
|
}
|
|
onPress={() =>
|
|
navigation.navigate('AddNotes', {
|
|
noteKey: getNoteKey
|
|
})
|
|
}
|
|
containerStyle={{ marginTop: 20 }}
|
|
noUppercase
|
|
/>
|
|
)}
|
|
</View>
|
|
</Screen>
|
|
);
|
|
}
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
content: {
|
|
paddingLeft: 20,
|
|
paddingRight: 20
|
|
},
|
|
center: {
|
|
alignItems: 'center',
|
|
paddingTop: 15,
|
|
paddingBottom: 15
|
|
},
|
|
valueWithLink: {
|
|
paddingBottom: 5,
|
|
fontFamily: 'PPNeueMontreal-Book'
|
|
}
|
|
});
|