Wallet implementation (#92)

* Created walletAddress and walletBalance components
* Added walletSend and transaction list components.
* added transaction history page
This commit is contained in:
akinwale 2018-05-03 19:58:31 +01:00 committed by GitHub
parent 351e39826b
commit 3706bc5936
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 845 additions and 15 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View file

@ -1,10 +1,12 @@
import React from 'react';
import AboutPage from '../page/about';
import DiscoverPage from '../page/discover';
import FilePage from '../page/file';
import SearchPage from '../page/search';
import SettingsPage from '../page/settings';
import AboutPage from '../page/about';
import SplashScreen from '../page/splash';
import TransactionHistoryPage from '../page/transactionHistory';
import WalletPage from '../page/wallet';
import SearchInput from '../component/searchInput';
import {
addNavigationHelpers,
@ -19,9 +21,10 @@ import {
AsyncStorage,
BackHandler,
NativeModules,
TextInput
TextInput,
ToastAndroid
} from 'react-native';
import { SETTINGS } from 'lbry-redux';
import { SETTINGS, doHideNotification, selectNotification } from 'lbry-redux';
import { makeSelectClientSetting } from '../redux/selectors/settings';
import Feather from 'react-native-vector-icons/Feather';
import discoverStyle from '../styles/discover';
@ -55,8 +58,29 @@ const discoverStack = StackNavigator({
headerMode: 'screen',
});
const walletStack = StackNavigator({
Wallet: {
screen: WalletPage,
navigationOptions: ({ navigation }) => ({
title: 'Wallet',
headerLeft: <Feather name="menu" size={24} style={discoverStyle.drawerHamburger} onPress={() => navigation.navigate('DrawerOpen')} />,
headerRight: <Feather name="search" size={24} style={discoverStyle.rightHeaderIcon} onPress={() => navigation.navigate('Search')} />
})
},
TransactionHistory: {
screen: TransactionHistoryPage,
navigationOptions: {
title: 'Transaction History',
drawerLockMode: 'locked-closed'
}
}
}, {
headerMode: 'screen'
});
const drawer = DrawerNavigator({
Discover: { screen: discoverStack },
Wallet: { screen: walletStack },
Settings: { screen: SettingsPage, navigationOptions: { drawerLockMode: 'locked-closed' } },
About: { screen: AboutPage, navigationOptions: { drawerLockMode: 'locked-closed' } }
}, {
@ -79,6 +103,8 @@ export const AppNavigator = new StackNavigator({
});
class AppWithNavigationState extends React.Component {
static supportedDisplayTypes = ['toast'];
componentWillMount() {
AppState.addEventListener('change', this._handleAppStateChange);
BackHandler.addEventListener('hardwareBackPress', function() {
@ -107,6 +133,32 @@ class AppWithNavigationState extends React.Component {
BackHandler.removeEventListener('hardwareBackPress');
}
componentWillUpdate(nextProps) {
const { dispatch } = this.props;
const { notification } = nextProps;
if (notification) {
const { displayType, message } = notification;
let currentDisplayType;
if (displayType.length) {
for (let i = 0; i < displayType.length; i++) {
const type = displayType[i];
if (AppWithNavigationState.supportedDisplayTypes.indexOf(type) > -1) {
currentDisplayType = type;
break;
}
}
} else if (AppWithNavigationState.supportedDisplayTypes.indexOf(displayType) > -1) {
currentDisplayType = displayType;
}
if ('toast' === currentDisplayType) {
ToastAndroid.show(message, ToastAndroid.SHORT);
}
dispatch(doHideNotification());
}
}
_handleAppStateChange = (nextAppState) => {
// Check if the app was suspended
if (AppState.currentState && AppState.currentState.match(/inactive|background/)) {
@ -136,6 +188,7 @@ class AppWithNavigationState extends React.Component {
const mapStateToProps = state => ({
nav: state.nav,
notification: selectNotification(state),
keepDaemonRunning: makeSelectClientSetting(SETTINGS.KEEP_DAEMON_RUNNING)(state),
showNsfw: makeSelectClientSetting(SETTINGS.SHOW_NSFW)(state)
});

View file

@ -0,0 +1,7 @@
import { connect } from 'react-redux';
import { doNotify } from 'lbry-redux';
import Address from './view';
export default connect(null, {
doNotify,
})(Address);

View file

@ -0,0 +1,29 @@
// @flow
import * as React from 'react';
import { Clipboard, Text, View } from 'react-native';
import Button from '../button';
import walletStyle from '../../styles/wallet';
type Props = {
address: string,
doNotify: ({ message: string, displayType: Array<string> }) => void,
};
export default class Address extends React.PureComponent<Props> {
render() {
const { address, doNotify, style } = this.props;
return (
<View style={[walletStyle.row, style]}>
<Text selectable={true} numberOfLines={1} style={walletStyle.address}>{address || ''}</Text>
<Button icon={'clipboard'} style={walletStyle.button} onPress={() => {
Clipboard.setString(address);
doNotify({
message: 'Address copied',
displayType: ['toast'],
});
}} />
</View>
);
}
}

View file

@ -0,0 +1,4 @@
import { connect } from 'react-redux';
import Button from './view';
export default connect(null, null)(Button);

View file

@ -0,0 +1,39 @@
import React from 'react';
import { Text, TouchableOpacity } from 'react-native';
import buttonStyle from '../../styles/button';
import Icon from 'react-native-vector-icons/FontAwesome';
export default class Button extends React.PureComponent<Props> {
render() {
const {
disabled,
style,
text,
icon,
onPress
} = this.props;
let styles = [buttonStyle.button, buttonStyle.row];
if (style.length) {
styles = styles.concat(style);
} else {
styles.push(style);
}
if (disabled) {
styles.push(buttonStyle.disabled);
}
const textStyles = [buttonStyle.text];
if (icon && icon.trim().length > 0) {
textStyles.push(buttonStyle.textWithIcon);
}
return (
<TouchableOpacity disabled={disabled} style={styles} onPress={onPress}>
{icon && <Icon name={icon} size={18} color='#ffffff' class={buttonStyle.icon} /> }
{text && (text.trim().length > 0) && <Text style={textStyles}>{text}</Text>}
</TouchableOpacity>
);
}
};

View file

@ -0,0 +1,13 @@
import { connect } from 'react-redux';
//import { selectClaimedRewardsByTransactionId } from 'redux/selectors/rewards';
import { selectAllMyClaimsByOutpoint } from 'lbry-redux';
import TransactionList from './view';
const select = state => ({
//rewards: selectClaimedRewardsByTransactionId(state),
myClaims: selectAllMyClaimsByOutpoint(state),
});
const perform = dispatch => ({});
export default connect(select, perform)(TransactionList);

View file

@ -0,0 +1,45 @@
// @flow
import React from 'react';
import { Text, View } from 'react-native';
import { formatCredits } from 'lbry-redux';
import moment from 'moment';
import transactionListStyle from '../../../styles/transactionList';
class TransactionListItem extends React.PureComponent {
capitalize(string: string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
render() {
const { transaction } = this.props;
const { amount, claim_id: claimId, claim_name: name, date, fee, txid, type } = transaction;
return (
<View style={transactionListStyle.listItem}>
<View style={[transactionListStyle.row, transactionListStyle.topRow]}>
<View style={transactionListStyle.col}>
<Text style={transactionListStyle.text}>{this.capitalize(type)}</Text>
</View>
<View style={transactionListStyle.col}>
<Text style={[transactionListStyle.amount, transactionListStyle.text]}>{formatCredits(amount, 8)}</Text>
{ fee !== 0 && (<Text style={[transactionListStyle.amount, transactionListStyle.text]}>fee {formatCredits(fee, 8)}</Text>) }
</View>
</View>
<View style={transactionListStyle.row}>
<View style={transactionListStyle.col}>
<Text style={[transactionListStyle.smallText, transactionListStyle.txid]}>{txid.substring(0, 8)}</Text>
</View>
<View style={transactionListStyle.col}>
{date ? (
<Text style={transactionListStyle.smallText}>{moment(date).format('MMM D')}</Text>
) : (
<Text style={transactionListStyle.smallText}>Pending</Text>
)}
</View>
</View>
</View>
);
}
}
export default TransactionListItem;

View file

@ -0,0 +1,69 @@
// @flow
import React from 'react';
import { Text, View } from 'react-native';
import TransactionListItem from './internal/transaction-list-item';
import transactionListStyle from '../../styles/transactionList';
export type Transaction = {
amount: number,
claim_id: string,
claim_name: string,
fee: number,
nout: number,
txid: string,
type: string,
date: Date,
};
class TransactionList extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
filter: 'all',
};
(this: any).handleFilterChanged = this.handleFilterChanged.bind(this);
(this: any).filterTransaction = this.filterTransaction.bind(this);
}
handleFilterChanged(event: React.SyntheticInputEvent<*>) {
this.setState({
filter: event.target.value,
});
}
filterTransaction(transaction: Transaction) {
const { filter } = this.state;
return filter === 'all' || filter === transaction.type;
}
render() {
const { emptyMessage, rewards, transactions } = this.props;
const { filter } = this.state;
const transactionList = transactions.filter(this.filterTransaction);
return (
<View>
{!transactionList.length && (
<Text style={transactionListStyle.noTransactions}>{emptyMessage || 'No transactions to list.'}</Text>
)}
{!!transactionList.length && (
<View>
{transactionList.map(t => (
<TransactionListItem
key={`${t.txid}:${t.nout}`}
transaction={t}
reward={rewards && rewards[t.txid]}
/>
))}
</View>
)}
</View>
);
}
}
export default TransactionList;

View file

@ -0,0 +1,20 @@
import { connect } from 'react-redux';
import {
doFetchTransactions,
selectRecentTransactions,
selectHasTransactions,
selectIsFetchingTransactions,
} from 'lbry-redux';
import TransactionListRecent from './view';
const select = state => ({
fetchingTransactions: selectIsFetchingTransactions(state),
transactions: selectRecentTransactions(state),
hasTransactions: selectHasTransactions(state),
});
const perform = dispatch => ({
fetchTransactions: () => dispatch(doFetchTransactions()),
});
export default connect(select, perform)(TransactionListRecent);

View file

@ -0,0 +1,46 @@
// @flow
import React from 'react';
//import BusyIndicator from 'component/common/busy-indicator';
import { Text, View } from 'react-native';
import Button from '../button';
import TransactionList from '../transactionList';
import type { Transaction } from '../transactionList/view';
import walletStyle from '../../styles/wallet';
type Props = {
fetchTransactions: () => void,
fetchingTransactions: boolean,
hasTransactions: boolean,
transactions: Array<Transaction>,
};
class TransactionListRecent extends React.PureComponent<Props> {
componentDidMount() {
this.props.fetchTransactions();
}
render() {
const { fetchingTransactions, hasTransactions, transactions, navigation } = this.props;
return (
<View style={walletStyle.transactionsCard}>
<View style={[walletStyle.row, walletStyle.transactionsHeader]}>
<Text style={walletStyle.transactionsTitle}>Recent Transactions</Text>
<Button style={walletStyle.button} text={'View All'}
onPress={() => navigation.navigate('TransactionHistory')} />
</View>
{fetchingTransactions && (
<Text style={walletStyle.infoText}>Fetching transactions...</Text>
)}
{!fetchingTransactions && (
<TransactionList
transactions={transactions}
emptyMessage={"Looks like you don't have any recent transactions."}
/>
)}
</View>
);
}
}
export default TransactionListRecent;

View file

@ -0,0 +1,20 @@
import { connect } from 'react-redux';
import {
doCheckAddressIsMine,
doGetNewAddress,
selectReceiveAddress,
selectGettingNewAddress,
} from 'lbry-redux';
import WalletAddress from './view';
const select = state => ({
receiveAddress: selectReceiveAddress(state),
gettingNewAddress: selectGettingNewAddress(state),
});
const perform = dispatch => ({
checkAddressIsMine: address => dispatch(doCheckAddressIsMine(address)),
getNewAddress: () => dispatch(doGetNewAddress()),
});
export default connect(select, perform)(WalletAddress);

View file

@ -0,0 +1,47 @@
// @flow
import React from 'react';
import { Text, View } from 'react-native';
import Address from '../address';
import Button from '../button';
import walletStyle from '../../styles/wallet';
type Props = {
checkAddressIsMine: string => void,
receiveAddress: string,
getNewAddress: () => void,
gettingNewAddress: boolean,
};
class WalletAddress extends React.PureComponent<Props> {
componentWillMount() {
const { checkAddressIsMine, receiveAddress, getNewAddress } = this.props;
if (!receiveAddress) {
getNewAddress();
} else {
checkAddressIsMine(receiveAddress);
}
}
render() {
const { receiveAddress, getNewAddress, gettingNewAddress } = this.props;
return (
<View style={walletStyle.card}>
<Text style={walletStyle.title}>Receive Credits</Text>
<Text style={[walletStyle.text, walletStyle.bottomMarginMedium]}>Use this wallet address to receive credits sent by another user (or yourself).</Text>
<Address address={receiveAddress} style={walletStyle.bottomMarginSmall} />
<Button style={[walletStyle.button, walletStyle.bottomMarginLarge]}
icon={'refresh'}
text={'Get New Address'}
onPress={getNewAddress}
disabled={gettingNewAddress}
/>
<Text style={walletStyle.smallText}>
You can generate a new address at any time, and any previous addresses will continue to work. Using multiple addresses can be helpful for keeping track of incoming payments from multiple sources.
</Text>
</View>
);
}
}
export default WalletAddress;

View file

@ -0,0 +1,9 @@
import { connect } from 'react-redux';
import { selectBalance } from 'lbry-redux';
import WalletBalance from './view';
const select = state => ({
balance: selectBalance(state),
});
export default connect(select, null)(WalletBalance);

View file

@ -0,0 +1,29 @@
// @flow
import React from 'react';
import { Image, Text, View } from 'react-native';
import { formatCredits } from 'lbry-redux'
import Address from '../address';
import Button from '../button';
import walletStyle from '../../styles/wallet';
type Props = {
balance: number,
};
class WalletBalance extends React.PureComponent<Props> {
render() {
const { balance } = this.props;
return (
<View style={walletStyle.balanceCard}>
<Image style={walletStyle.balanceBackground} resizeMode={'cover'} source={require('../../assets/stripe.png')} />
<Text style={walletStyle.balanceTitle}>Balance</Text>
<Text style={walletStyle.balanceCaption}>You currently have</Text>
<Text style={walletStyle.balance}>
{(balance || balance === 0) && (formatCredits(balance, 2) + ' LBC')}
</Text>
</View>
);
}
}
export default WalletBalance;

View file

@ -0,0 +1,22 @@
import { connect } from 'react-redux';
import {
doNotify,
doSendDraftTransaction,
selectDraftTransaction,
selectDraftTransactionError,
selectBalance
} from 'lbry-redux';
import WalletSend from './view';
const perform = dispatch => ({
sendToAddress: (address, amount) => dispatch(doSendDraftTransaction(address, amount)),
notify: (data) => dispatch(doNotify(data))
});
const select = state => ({
balance: selectBalance(state),
draftTransaction: selectDraftTransaction(state),
transactionError: selectDraftTransactionError(state),
});
export default connect(select, perform)(WalletSend);

View file

@ -0,0 +1,89 @@
// @flow
import React from 'react';
import { regexAddress } from 'lbry-redux';
import { TextInput, Text, View } from 'react-native';
import Button from '../button';
import walletStyle from '../../styles/wallet';
type DraftTransaction = {
address: string,
amount: ?number, // So we can use a placeholder in the input
};
type Props = {
sendToAddress: (string, number) => void,
balance: number,
};
class WalletSend extends React.PureComponent<Props> {
state = {
amount: null,
address: null
};
componentWillUpdate(nextProps) {
const { draftTransaction, transactionError } = nextProps;
if (transactionError && transactionError.trim().length > 0) {
this.setState({ address: draftTransaction.address, amount: draftTransaction.amount });
}
}
handleSend = () => {
const { balance, sendToAddress, notify } = this.props;
const { address, amount } = this.state;
if (address && !regexAddress.test(address)) {
notify({
message: 'The recipient address is not a valid LBRY address.',
displayType: ['toast']
});
return;
}
if (amount > balance) {
notify({
message: 'Insufficient credits',
displayType: ['toast']
});
return;
}
if (amount && address) {
sendToAddress(address, parseFloat(amount));
this.setState({ address: null, amount: null });
}
}
render() {
const { balance } = this.props;
const canSend = this.state.address &&
this.state.amount > 0 &&
this.state.address.trim().length > 0;
return (
<View style={walletStyle.card}>
<Text style={walletStyle.title}>Send Credits</Text>
<Text style={walletStyle.text}>Amount</Text>
<View style={[walletStyle.amountRow, walletStyle.bottomMarginMedium]}>
<TextInput onChangeText={value => this.setState({amount: value})}
keyboardType={'numeric'}
value={this.state.amount}
style={[walletStyle.input, walletStyle.amountInput]} />
<Text style={[walletStyle.text, walletStyle.currency]}>LBC</Text>
</View>
<Text style={walletStyle.text}>Recipient address</Text>
<View style={walletStyle.row}>
<TextInput onChangeText={value => this.setState({address: value})}
placeholder={'bbFxRyXXXXXXXXXXXZD8nE7XTLUxYnddTs'}
value={this.state.address}
style={[walletStyle.input, walletStyle.addressInput, walletStyle.bottomMarginMedium]} />
<Button text={'Send'}
style={[walletStyle.button, walletStyle.sendButton]}
disabled={!canSend}
onPress={this.handleSend} />
</View>
</View>
);
}
}
export default WalletSend;

View file

@ -24,6 +24,7 @@ import {
claimsReducer,
costInfoReducer,
fileInfoReducer,
notificationsReducer,
searchReducer,
walletReducer
} from 'lbry-redux';
@ -71,6 +72,7 @@ const reducers = combineReducers({
claims: claimsReducer,
costInfo: costInfoReducer,
fileInfo: fileInfoReducer,
notifications: notificationsReducer,
search: searchReducer,
wallet: walletReducer,
nav: navigatorReducer,
@ -96,12 +98,13 @@ const compressor = createCompressor();
const saveClaimsFilter = createFilter('claims', ['byId', 'claimsByUri']);
const subscriptionsFilter = createFilter('subscriptions', ['subscriptions']);
const settingsFilter = createFilter('settings', ['clientSettings']);
const walletFilter = createFilter('wallet', ['receiveAddress']);
const persistOptions = {
whitelist: ['claims', 'subscriptions', 'settings'],
whitelist: ['claims', 'subscriptions', 'settings', 'wallet'],
// Order is important. Needs to be compressed last or other transforms can't
// read the data
transforms: [saveClaimsFilter, subscriptionsFilter, settingsFilter, compressor],
transforms: [saveClaimsFilter, subscriptionsFilter, settingsFilter, walletFilter, compressor],
debounce: 10000,
storage: AsyncStorage
};

View file

@ -7,7 +7,7 @@ import discoverStyle from '../../styles/discover';
import Feather from 'react-native-vector-icons/Feather';
class DiscoverPage extends React.PureComponent {
componentWillMount() {
componentDidMount() {
// Track the total time taken if this is the first launch
AsyncStorage.getItem('firstLaunchTime').then(startTime => {
if (startTime !== null && !isNaN(parseInt(startTime, 10))) {

View file

@ -1,7 +1,9 @@
import { connect } from 'react-redux';
import { doBalanceSubscribe } from 'lbry-redux';
import SplashScreen from './view';
const select = state => ({});
const perform = dispatch => ({});
const perform = dispatch => ({
balanceSubscribe: () => dispatch(doBalanceSubscribe())
});
export default connect(select, perform)(SplashScreen);
export default connect(null, perform)(SplashScreen);

View file

@ -40,7 +40,8 @@ class SplashScreen extends React.PureComponent {
Lbry.resolve({ uri: 'lbry://one' }).then(() => {
// Leave the splash screen
const { navigation } = this.props;
const { balanceSubscribe, navigation } = this.props;
balanceSubscribe();
navigation.navigate('Main');
});
return;

View file

@ -0,0 +1,18 @@
import { connect } from 'react-redux';
import {
doFetchTransactions,
selectTransactionItems,
selectIsFetchingTransactions,
} from 'lbry-redux';
import TransactionHistoryPage from './view';
const select = state => ({
fetchingTransactions: selectIsFetchingTransactions(state),
transactions: selectTransactionItems(state),
});
const perform = dispatch => ({
fetchTransactions: () => dispatch(doFetchTransactions()),
});
export default connect(select, perform)(TransactionHistoryPage);

View file

@ -0,0 +1,29 @@
import React from 'react';
import { View, ScrollView, Text } from 'react-native';
import TransactionList from '../../component/transactionList';
import walletStyle from '../../styles/wallet';
class TransactionHistoryPage extends React.PureComponent {
componentDidMount() {
this.props.fetchTransactions();
}
render() {
const { fetchingTransactions, transactions } = this.props;
return (
<ScrollView>
<View style={walletStyle.historyList}>
{fetchingTransactions && !transactions.length && (
<Text style={walletStyle.infoText}>Loading transactions...</Text>
)}
{transactions && transactions.length && (
<TransactionList transactions={transactions} />
)}
</View>
</ScrollView>
);
}
}
export default TransactionHistoryPage;

View file

@ -0,0 +1,4 @@
import { connect } from 'react-redux';
import WalletPage from './view';
export default connect(null, null)(WalletPage);

View file

@ -0,0 +1,21 @@
import React from 'react';
import { ScrollView } from 'react-native';
import TransactionListRecent from '../../component/transactionListRecent';
import WalletAddress from '../../component/walletAddress';
import WalletBalance from '../../component/walletBalance';
import WalletSend from '../../component/walletSend';
class WalletPage extends React.PureComponent {
render() {
return (
<ScrollView>
<WalletBalance />
<WalletAddress />
<WalletSend />
<TransactionListRecent navigation={this.props.navigation} />
</ScrollView>
);
}
}
export default WalletPage;

View file

@ -1,9 +1,9 @@
import {
ACTIONS,
Lbry,
selectBalance,
makeSelectCostInfoForUri,
makeSelectFileInfoForUri,
selectTotalDownloadProgress,
selectDownloadingByOutpoint,
} from 'lbry-redux';
import { NativeModules } from 'react-native';
@ -187,7 +187,7 @@ export function doLoadVideo(uri) {
export function doPurchaseUri(uri, specificCostInfo) {
return (dispatch, getState) => {
const state = getState();
const balance = 0;//selectBalance(state);
const balance = selectBalance(state);
const fileInfo = makeSelectFileInfoForUri(uri)(state);
const downloadingByOutpoint = selectDownloadingByOutpoint(state);
const alreadyDownloading = fileInfo && !!downloadingByOutpoint[fileInfo.outpoint];

30
app/src/styles/button.js Normal file
View file

@ -0,0 +1,30 @@
import { StyleSheet } from 'react-native';
const buttonStyle = StyleSheet.create({
button: {
borderRadius: 24,
padding: 8,
paddingLeft: 12,
paddingRight: 12
},
disabled: {
backgroundColor: '#999999'
},
row: {
alignSelf: 'flex-start',
flexDirection: 'row',
},
icon: {
color: '#ffffff',
},
text: {
color: '#ffffff',
fontFamily: 'Metropolis-Regular',
fontSize: 14
},
textWithIcon: {
marginLeft: 8
}
});
export default buttonStyle;

View file

@ -0,0 +1,46 @@
import { StyleSheet } from 'react-native';
import Colors from './colors';
const transactionListStyle = StyleSheet.create({
listItem: {
borderBottomWidth: 1,
borderBottomColor: '#eeeeee',
paddingLeft: 16,
paddingRight: 16,
paddingTop: 12,
paddingBottom: 12
},
row: {
flexDirection: 'row',
justifyContent: 'space-between'
},
topRow: {
marginBottom: 4
},
col: {
alignSelf: 'stretch'
},
text: {
fontFamily: 'Metropolis-Regular',
fontSize: 14
},
amount: {
textAlign: 'right'
},
txid: {
color: Colors.LbryGreen
},
smallText: {
fontFamily: 'Metropolis-Regular',
fontSize: 12,
color: '#aaaaaa'
},
noTransactions: {
fontFamily: 'Metropolis-Regular',
textAlign: 'center',
padding: 16,
color: '#aaaaaa'
}
});
export default transactionListStyle;

133
app/src/styles/wallet.js Normal file
View file

@ -0,0 +1,133 @@
import { StyleSheet } from 'react-native';
import Colors from './colors';
const walletStyle = StyleSheet.create({
row: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center'
},
amountRow: {
flexDirection: 'row'
},
address: {
fontFamily: 'Metropolis-Regular',
borderWidth: 1,
borderStyle: 'dashed',
borderColor: '#cccccc',
backgroundColor: '#f9f9f9',
padding: 8,
width: '85%'
},
button: {
backgroundColor: Colors.LbryGreen
},
historyList: {
backgroundColor: '#ffffff'
},
card: {
backgroundColor: '#ffffff',
marginTop: 16,
marginLeft: 16,
marginRight: 16,
padding: 16
},
transactionsCard: {
backgroundColor: '#ffffff',
margin: 16
},
title: {
fontFamily: 'Metropolis-Bold',
fontSize: 20,
marginBottom: 24
},
transactionsTitle: {
fontFamily: 'Metropolis-Bold',
fontSize: 20
},
transactionsHeader: {
paddingTop: 12,
paddingBottom: 12,
paddingLeft: 16,
paddingRight: 16,
borderBottomWidth: 1,
borderBottomColor: '#eeeeee'
},
text: {
fontFamily: 'Metropolis-Regular',
fontSize: 14
},
smallText: {
fontFamily: 'Metropolis-Regular',
fontSize: 12
},
balanceCard: {
marginTop: 16,
marginLeft: 16,
marginRight: 16
},
balanceBackground: {
position: 'absolute',
alignSelf: 'stretch',
width: '100%',
height: '100%',
},
balanceTitle: {
color: '#ffffff',
fontFamily: 'Metropolis-Bold',
fontSize: 18,
marginLeft: 16,
marginTop: 16
},
balanceCaption: {
color: '#caedB9',
fontFamily: 'Metropolis-Medium',
fontSize: 14,
marginLeft: 16,
marginTop: 8,
marginBottom: 96
},
balance: {
color: '#ffffff',
fontFamily: 'Metropolis-Bold',
fontSize: 36,
marginLeft: 16,
marginBottom: 16
},
infoText: {
color: '#aaaaaa',
fontFamily: 'Metropolis-Regular',
fontSize: 14,
padding: 16,
textAlign: 'center'
},
input: {
fontFamily: 'Metropolis-Regular',
fontSize: 14
},
amountInput: {
alignSelf: 'flex-start',
width: 150
},
addressInput: {
width: '80%'
},
currency: {
alignSelf: 'flex-start',
marginTop: 17
},
sendButton: {
marginTop: 8
},
bottomMarginSmall: {
marginBottom: 8
},
bottomMarginMedium: {
marginBottom: 16
},
bottomMarginLarge: {
marginBottom: 24
}
});
export default walletStyle;

View file

@ -16,7 +16,9 @@ import org.json.JSONException;
public class MixpanelModule extends ReactContextBaseJavaModule {
private static final String MIXPANEL_TOKEN = "93b81fb957cb0ddcd3198c10853a6a95";
// TODO: Detect dev / debug and release mode and update value accordingly
//private static final String MIXPANEL_TOKEN = "93b81fb957cb0ddcd3198c10853a6a95"; // Production
private static final String MIXPANEL_TOKEN = "bc1630b8be64c5dfaa4700b3a62969f3"; // Dev Testing
private Context context;

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB