Browse Source

ADD: Create BlueTransactionListItem

nonetwork
Marcos Rodriguez Vélez 6 years ago
parent
commit
2cb56b51e9
  1. 200
      BlueComponents.js
  2. 6
      index.js
  3. 233
      screen/wallets/list.js
  4. 179
      screen/wallets/transactions.js

200
BlueComponents.js

@ -988,6 +988,195 @@ export class NewWalletPanel extends Component {
}
}
export class BlueTransactionListItem extends Component {
static propTypes = {
item: PropTypes.shape().isRequired,
itemPriceUnit: PropTypes.string,
};
static defaultProps = {
itemPriceUnit: BitcoinUnit.BTC,
};
txMemo = () => {
if (BlueApp.tx_metadata[this.props.item.hash] && BlueApp.tx_metadata[this.props.item.hash]['memo']) {
return BlueApp.tx_metadata[this.props.item.hash]['memo'];
}
return '';
};
rowTitle = () => {
const item = this.props.item;
if (item.type === 'user_invoice' || item.type === 'payment_request') {
if (isNaN(item.value)) {
item.value = '0';
}
const currentDate = new Date();
const now = (currentDate.getTime() / 1000) | 0;
const invoiceExpiration = item.timestamp + item.expire_time;
if (invoiceExpiration > now) {
return loc.formatBalanceWithoutSuffix(item.value && item.value, this.props.itemPriceUnit, true).toString();
} else if (invoiceExpiration < now) {
if (item.ispaid) {
return loc.formatBalanceWithoutSuffix(item.value && item.value, this.props.itemPriceUnit, true).toString();
} else {
return loc.lnd.expired;
}
}
} else {
return loc.formatBalanceWithoutSuffix(item.value && item.value, this.props.itemPriceUnit, true).toString();
}
};
rowTitleStyle = () => {
const item = this.props.item;
let color = '#37c0a1';
if (item.type === 'user_invoice' || item.type === 'payment_request') {
const currentDate = new Date();
const now = (currentDate.getTime() / 1000) | 0;
const invoiceExpiration = item.timestamp + item.expire_time;
if (invoiceExpiration > now) {
color = '#37c0a1';
} else if (invoiceExpiration < now) {
if (item.ispaid) {
color = '#37c0a1';
} else {
color = '#FF0000';
}
}
} else if (item.value / 100000000 < 0) {
color = BlueApp.settings.foregroundColor;
}
return {
fontWeight: '600',
fontSize: 16,
color: color,
};
};
avatar = () => {
// is it lightning refill tx?
if (this.props.item.category === 'receive' && this.props.item.confirmations < 3) {
return (
<View style={{ width: 25 }}>
<BlueTransactionPendingIcon />
</View>
);
}
if (this.props.item.type && this.props.item.type === 'bitcoind_tx') {
return (
<View style={{ width: 25 }}>
<BlueTransactionOnchainIcon />
</View>
);
}
if (this.props.item.type === 'paid_invoice') {
// is it lightning offchain payment?
return (
<View style={{ width: 25 }}>
<BlueTransactionOffchainIcon />
</View>
);
}
if (this.props.item.type === 'user_invoice' || this.props.item.type === 'payment_request') {
if (!this.props.item.ispaid) {
const currentDate = new Date();
const now = (currentDate.getTime() / 1000) | 0;
const invoiceExpiration = this.props.item.timestamp + this.props.item.expire_time;
if (invoiceExpiration < now) {
return (
<View style={{ width: 25 }}>
<BlueTransactionExpiredIcon />
</View>
);
}
} else {
return (
<View style={{ width: 25 }}>
<BlueTransactionOffchainIncomingIcon />
</View>
);
}
}
if (!this.props.item.confirmations) {
return (
<View style={{ width: 25 }}>
<BlueTransactionPendingIcon />
</View>
);
} else if (this.props.item.value < 0) {
return (
<View style={{ width: 25 }}>
<BlueTransactionOutgoingIcon />
</View>
);
} else {
return (
<View style={{ width: 25 }}>
<BlueTransactionIncommingIcon />
</View>
);
}
};
subtitle = () => {
return (
(this.props.item.confirmations < 7 ? loc.transactions.list.conf + ': ' + this.props.item.confirmations + ' ' : '') +
this.txMemo() +
(this.props.item.memo || '')
);
};
onPress = () => {
if (this.props.item.hash) {
NavigationService.navigate('TransactionDetails', { hash: this.props.item.hash });
} else if (
this.props.item.type === 'user_invoice' ||
this.props.item.type === 'payment_request' ||
this.props.item.type === 'paid_invoice'
) {
const lightningWallet = BlueAddressInput.getWallets().filter(wallet => {
if (typeof wallet === 'object') {
if (wallet.hasOwnProperty('secret')) {
return wallet.getSecret() === this.props.item.fromWallet;
}
}
});
NavigationService.navigate('LNDViewInvoice', {
invoice: this.props.item,
fromWallet: lightningWallet[0],
isModal: false,
});
}
};
render() {
return (
<BlueListItem
avatar={this.avatar()}
title={loc.transactionTimeToReadable(this.props.item.received)}
subtitle={this.subtitle()}
onPress={this.onPress}
badge={{
value: 3,
textStyle: { color: 'orange' },
containerStyle: { marginTop: 0 },
}}
hideChevron
rightTitle={this.rowTitle()}
rightTitleStyle={this.rowTitleStyle()}
/>
);
}
}
const sliderWidth = width * 1;
const itemWidth = width * 0.82;
const sliderHeight = 190;
@ -1004,12 +1193,17 @@ export class WalletsCarousel extends Component {
_renderItem({ item, index }) {
let scaleValue = new Animated.Value(1.0);
let props = { toValue: 0.9, duration: 100 };
if (Platform.OS === 'android') {
props.push({ useNativeDriver: true });
}
this.onPressedIn = () => {
Animated.spring(scaleValue, { toValue: 0.9, duration: 100, useNativeDriver: Platform.OS === 'android' }).start();
scaleValue.toValue = 0.9;
Animated.spring(scaleValue, props).start();
};
this.onPressedOut = () => {
Animated.spring(scaleValue, { toValue: 1.0, duration: 100, useNativeDriver: Platform.OS === 'android' }).start();
scaleValue.toValue = 1.0;
Animated.spring(scaleValue, props).start();
};
if (!item) {

6
index.js

@ -24,13 +24,13 @@ class BlueAppComponent extends React.Component {
this.state = { isMigratingData: true };
}
async setIsMigratingData() {
setIsMigratingData = async () => {
await BlueApp.startAndDecrypt();
this.setState({ isMigratingData: false });
}
};
render() {
return this.state.isMigratingData ? <WalletMigrate onComplete={() => this.setIsMigratingData()} /> : <App />;
return this.state.isMigratingData ? <WalletMigrate onComplete={this.setIsMigratingData} /> : <App />;
}
}

233
screen/wallets/list.js

@ -1,20 +1,6 @@
import React, { Component } from 'react';
import { View, TouchableOpacity, Text, FlatList, RefreshControl, ScrollView } from 'react-native';
import {
BlueTransactionOnchainIcon,
BlueLoading,
SafeBlueArea,
WalletsCarousel,
BlueTransactionIncommingIcon,
BlueTransactionOutgoingIcon,
BlueTransactionPendingIcon,
BlueTransactionOffchainIcon,
BlueTransactionExpiredIcon,
BlueList,
BlueListItem,
BlueHeaderDefaultMain,
BlueTransactionOffchainIncomingIcon,
} from '../../BlueComponents';
import { BlueLoading, SafeBlueArea, WalletsCarousel, BlueList, BlueHeaderDefaultMain, BlueTransactionListItem } from '../../BlueComponents';
import { Icon } from 'react-native-elements';
import { NavigationEvents } from 'react-navigation';
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
@ -65,7 +51,7 @@ export default class WalletsList extends Component {
* Forcefully fetches TXs and balance for lastSnappedTo (i.e. current) wallet.
* Triggered manually by user on pull-to-refresh.
*/
refreshTransactions() {
refreshTransactions = () => {
if (!(this.lastSnappedTo < BlueApp.getWallets().length)) {
// last card, nop
console.log('last card, nop');
@ -76,28 +62,24 @@ export default class WalletsList extends Component {
{
isTransactionsLoading: true,
},
async function() {
let that = this;
setTimeout(async function() {
// more responsive
let noErr = true;
try {
await BlueApp.fetchWalletBalances(that.lastSnappedTo || 0);
let start = +new Date();
await BlueApp.fetchWalletTransactions(that.lastSnappedTo || 0);
let end = +new Date();
console.log('fetch tx took', (end - start) / 1000, 'sec');
} catch (err) {
noErr = false;
console.warn(err);
}
if (noErr) await BlueApp.saveToDisk(); // caching
async () => {
let noErr = true;
try {
await BlueApp.fetchWalletBalances(this.lastSnappedTo || 0);
let start = +new Date();
await BlueApp.fetchWalletTransactions(this.lastSnappedTo || 0);
let end = +new Date();
console.log('fetch tx took', (end - start) / 1000, 'sec');
} catch (err) {
noErr = false;
console.warn(err);
}
if (noErr) await BlueApp.saveToDisk(); // caching
that.refreshFunction();
}, 1);
this.refreshFunction();
},
);
}
};
/**
* Redraws the screen
@ -107,11 +89,14 @@ export default class WalletsList extends Component {
A(A.ENUM.GOT_NONZERO_BALANCE);
}
const dataSource = BlueApp.getTransactions();
const wallets = BlueApp.getWallets().concat(false);
this.setState({
isLoading: false,
isTransactionsLoading: false,
dataSource: BlueApp.getTransactions(),
wallets: BlueApp.getWallets().concat(false),
dataSource,
wallets,
});
}
@ -203,6 +188,10 @@ export default class WalletsList extends Component {
_keyExtractor = (_item, index) => index.toString();
_renderItem = rowData => {
return <BlueTransactionListItem item={rowData.item} itemPriceUnit={rowData.item.walletPreferredBalanceUnit} />;
};
renderListHeaderComponent = () => {
return (
<View>
@ -229,60 +218,7 @@ export default class WalletsList extends Component {
}
};
rowTitle = item => {
if (item.type === 'user_invoice' || item.type === 'payment_request') {
if (isNaN(item.value)) {
item.value = '0';
}
const currentDate = new Date();
const now = (currentDate.getTime() / 1000) | 0;
const invoiceExpiration = item.timestamp + item.expire_time;
if (invoiceExpiration > now) {
return loc.formatBalanceWithoutSuffix(item.value && item.value, item.walletPreferredBalanceUnit, true).toString();
} else if (invoiceExpiration < now) {
if (item.ispaid) {
return loc.formatBalanceWithoutSuffix(item.value && item.value, item.walletPreferredBalanceUnit, true).toString();
} else {
return loc.lnd.expired;
}
}
} else {
return loc.formatBalanceWithoutSuffix(item.value && item.value, item.walletPreferredBalanceUnit, true).toString();
}
};
rowTitleStyle = item => {
let color = '#37c0a1';
if (item.type === 'user_invoice' || item.type === 'payment_request') {
const currentDate = new Date();
const now = (currentDate.getTime() / 1000) | 0;
const invoiceExpiration = item.timestamp + item.expire_time;
if (invoiceExpiration > now) {
color = '#37c0a1';
} else if (invoiceExpiration < now) {
if (item.ispaid) {
color = '#37c0a1';
} else {
color = '#FF0000';
}
}
} else if (item.value / 100000000 < 0) {
color = BlueApp.settings.foregroundColor;
}
return {
fontWeight: '600',
fontSize: 16,
color: color,
};
};
render() {
const { navigate } = this.props.navigation;
if (this.state.isLoading) {
return <BlueLoading />;
}
@ -293,9 +229,7 @@ export default class WalletsList extends Component {
this.refreshFunction();
}}
/>
<ScrollView
refreshControl={<RefreshControl onRefresh={() => this.refreshTransactions()} refreshing={this.state.isTransactionsLoading} />}
>
<ScrollView refreshControl={<RefreshControl onRefresh={this.refreshTransactions} refreshing={this.state.isTransactionsLoading} />}>
<BlueHeaderDefaultMain leftText={loc.wallets.list.title} onNewWalletPress={() => this.props.navigation.navigate('AddWallet')} />
<WalletsCarousel
data={this.state.wallets}
@ -309,6 +243,7 @@ export default class WalletsList extends Component {
/>
<BlueList>
<FlatList
initialNumToRender={10}
ListHeaderComponent={this.renderListHeaderComponent}
ListEmptyComponent={
<View style={{ top: 50, height: 100 }}>
@ -335,117 +270,7 @@ export default class WalletsList extends Component {
data={this.state.dataSource}
extraData={this.state.dataSource}
keyExtractor={this._keyExtractor}
renderItem={rowData => {
return (
<BlueListItem
avatar={(() => {
// is it lightning refill tx?
if (rowData.item.category === 'receive' && rowData.item.confirmations < 3) {
return (
<View style={{ width: 25 }}>
<BlueTransactionPendingIcon />
</View>
);
}
if (rowData.item.type && rowData.item.type === 'bitcoind_tx') {
return (
<View style={{ width: 25 }}>
<BlueTransactionOnchainIcon />
</View>
);
}
if (rowData.item.type === 'paid_invoice') {
// is it lightning offchain payment?
return (
<View style={{ width: 25 }}>
<BlueTransactionOffchainIcon />
</View>
);
}
if (rowData.item.type === 'user_invoice' || rowData.item.type === 'payment_request') {
if (!rowData.item.ispaid) {
const currentDate = new Date();
const now = (currentDate.getTime() / 1000) | 0;
const invoiceExpiration = rowData.item.timestamp + rowData.item.expire_time;
if (invoiceExpiration < now) {
return (
<View style={{ width: 25 }}>
<BlueTransactionExpiredIcon />
</View>
);
}
} else {
return (
<View style={{ width: 25 }}>
<BlueTransactionOffchainIncomingIcon />
</View>
);
}
}
if (!rowData.item.confirmations) {
return (
<View style={{ width: 25 }}>
<BlueTransactionPendingIcon />
</View>
);
} else if (rowData.item.value < 0) {
return (
<View style={{ width: 25 }}>
<BlueTransactionOutgoingIcon />
</View>
);
} else {
return (
<View style={{ width: 25 }}>
<BlueTransactionIncommingIcon />
</View>
);
}
})()}
title={loc.transactionTimeToReadable(rowData.item.received)}
subtitle={
(rowData.item.confirmations < 7 ? loc.transactions.list.conf + ': ' + rowData.item.confirmations + ' ' : '') +
this.txMemo(rowData.item.hash) +
(rowData.item.memo || '')
}
onPress={() => {
if (rowData.item.hash) {
navigate('TransactionDetails', {
hash: rowData.item.hash,
});
} else if (
rowData.item.type === 'user_invoice' ||
rowData.item.type === 'payment_request' ||
rowData.item.type === 'paid_invoice'
) {
const lightningWallet = this.state.wallets.filter(wallet => {
if (typeof wallet === 'object') {
if (wallet.hasOwnProperty('secret')) {
return wallet.getSecret() === rowData.item.fromWallet;
}
}
});
this.props.navigation.navigate('LNDViewInvoice', {
invoice: rowData.item,
fromWallet: lightningWallet[0],
isModal: false,
});
}
}}
badge={{
value: 3,
textStyle: { color: 'orange' },
containerStyle: { marginTop: 0 },
}}
hideChevron
rightTitle={this.rowTitle(rowData.item)}
rightTitleStyle={this.rowTitleStyle(rowData.item)}
/>
);
}}
renderItem={this._renderItem}
/>
</BlueList>
</ScrollView>

179
screen/wallets/transactions.js

@ -3,20 +3,7 @@ import { Text, View, Image, FlatList, RefreshControl, TouchableOpacity, StatusBa
import LinearGradient from 'react-native-linear-gradient';
import PropTypes from 'prop-types';
import { NavigationEvents } from 'react-navigation';
import {
BlueText,
BlueTransactionOnchainIcon,
ManageFundsBigButton,
BlueTransactionExpiredIcon,
BlueTransactionIncommingIcon,
BlueTransactionOutgoingIcon,
BlueTransactionPendingIcon,
BlueTransactionOffchainIcon,
BlueSendButtonIcon,
BlueReceiveButtonIcon,
BlueListItem,
BlueTransactionOffchainIncomingIcon,
} from '../../BlueComponents';
import { BlueText, ManageFundsBigButton, BlueSendButtonIcon, BlueReceiveButtonIcon, BlueTransactionListItem } from '../../BlueComponents';
import { Icon } from 'react-native-elements';
import { BitcoinUnit } from '../../models/bitcoinUnits';
import { LightningCustodianWallet } from '../../class';
@ -269,13 +256,6 @@ export default class WalletTransactions extends Component {
);
};
txMemo(hash) {
if (BlueApp.tx_metadata[hash] && BlueApp.tx_metadata[hash]['memo']) {
return BlueApp.tx_metadata[hash]['memo'];
}
return '';
}
_keyExtractor = (_item, index) => index.toString();
renderListHeaderComponent = () => {
@ -305,55 +285,8 @@ export default class WalletTransactions extends Component {
this.onWillBlur();
}
rowTitle = item => {
if (item.type === 'user_invoice' || item.type === 'payment_request') {
if (isNaN(item.value)) {
item.value = 0;
}
const currentDate = new Date();
const now = (currentDate.getTime() / 1000) | 0;
const invoiceExpiration = item.timestamp + item.expire_time;
if (invoiceExpiration > now) {
return loc.formatBalanceWithoutSuffix(item.value && item.value, this.state.wallet.getPreferredBalanceUnit(), true).toString();
} else if (invoiceExpiration < now) {
if (item.ispaid) {
return loc.formatBalanceWithoutSuffix(item.value && item.value, this.state.wallet.getPreferredBalanceUnit(), true).toString();
} else {
return loc.lnd.expired;
}
}
} else {
return loc.formatBalanceWithoutSuffix(item.value && item.value, this.state.wallet.getPreferredBalanceUnit(), true).toString();
}
};
rowTitleStyle = item => {
let color = '#37c0a1';
if (item.type === 'user_invoice' || item.type === 'payment_request') {
const currentDate = new Date();
const now = (currentDate.getTime() / 1000) | 0;
const invoiceExpiration = item.timestamp + item.expire_time;
if (invoiceExpiration > now) {
color = '#37c0a1';
} else if (invoiceExpiration < now) {
if (item.ispaid) {
color = '#37c0a1';
} else {
color = '#FF0000';
}
}
} else if (item.value / 100000000 < 0) {
color = BlueApp.settings.foregroundColor;
}
return {
fontWeight: '600',
fontSize: 16,
color: color,
};
renderItem = item => {
return <BlueTransactionListItem item={item.item} itemPriceUnit={this.state.wallet.getPreferredBalanceUnit()} />;
};
render() {
@ -462,110 +395,8 @@ export default class WalletTransactions extends Component {
refreshControl={<RefreshControl onRefresh={() => this.refreshTransactions()} refreshing={this.state.isTransactionsLoading} />}
data={this.state.dataSource}
keyExtractor={this._keyExtractor}
renderItem={rowData => {
return (
<BlueListItem
avatar={(() => {
// is it lightning refill tx?
if (rowData.item.category === 'receive' && rowData.item.confirmations < 3) {
return (
<View style={{ width: 25 }}>
<BlueTransactionPendingIcon />
</View>
);
}
if (rowData.item.type && rowData.item.type === 'bitcoind_tx') {
return (
<View style={{ width: 25 }}>
<BlueTransactionOnchainIcon />
</View>
);
}
if (rowData.item.type === 'paid_invoice') {
// is it lightning offchain payment?
return (
<View style={{ width: 25 }}>
<BlueTransactionOffchainIcon />
</View>
);
}
if (rowData.item.type === 'user_invoice' || rowData.item.type === 'payment_request') {
if (!rowData.item.ispaid) {
const currentDate = new Date();
const now = (currentDate.getTime() / 1000) | 0;
const invoiceExpiration = rowData.item.timestamp + rowData.item.expire_time;
if (invoiceExpiration < now) {
return (
<View style={{ width: 25 }}>
<BlueTransactionExpiredIcon />
</View>
);
}
} else {
return (
<View style={{ width: 25 }}>
<BlueTransactionOffchainIncomingIcon />
</View>
);
}
}
if (!rowData.item.confirmations) {
return (
<View style={{ width: 25 }}>
<BlueTransactionPendingIcon />
</View>
);
} else if (rowData.item.value < 0) {
return (
<View style={{ width: 25 }}>
<BlueTransactionOutgoingIcon />
</View>
);
} else {
return (
<View style={{ width: 25 }}>
<BlueTransactionIncommingIcon />
</View>
);
}
})()}
title={loc.transactionTimeToReadable(rowData.item.received)}
subtitle={
(rowData.item.confirmations < 7 ? loc.transactions.list.conf + ': ' + rowData.item.confirmations + ' ' : '') +
this.txMemo(rowData.item.hash) +
(rowData.item.memo || '')
}
onPress={() => {
if (rowData.item.hash) {
navigate('TransactionDetails', {
hash: rowData.item.hash,
});
} else if (
rowData.item.type === 'user_invoice' ||
rowData.item.type === 'payment_request' ||
rowData.item.type === 'paid_invoice'
) {
this.props.navigation.navigate('LNDViewInvoice', {
invoice: rowData.item,
fromWallet: this.state.wallet,
isModal: false,
});
}
}}
badge={{
value: 3,
textStyle: { color: 'orange' },
containerStyle: { marginTop: 0 },
}}
hideChevron
rightTitle={this.rowTitle(rowData.item)}
rightTitleStyle={this.rowTitleStyle(rowData.item)}
/>
);
}}
initialNumToRender={10}
renderItem={this.renderItem}
/>
</View>
<View

Loading…
Cancel
Save