Merge pull request #762 from esteemapp/feature/transfer

Feature/transfer
This commit is contained in:
Mustafa Buyukcelebi 2019-04-26 16:19:00 +03:00 committed by GitHub
commit b7e5d692b7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 675 additions and 89 deletions

View File

@ -89,7 +89,6 @@ class BasicHeaderView extends Component {
isLoading,
isLoggedIn,
isModalHeader,
isPostSending,
rightButtonText,
isPreviewActive,
isReply,

View File

@ -4,7 +4,6 @@ export default EStyleSheet.create({
container: {
marginVertical: 15,
flexDirection: 'row',
justifyContent: 'space-between',
paddingHorizontal: 16,
},
fitContent: {
@ -12,9 +11,9 @@ export default EStyleSheet.create({
},
iconTextWrapper: {
flexDirection: 'row',
justifyContent: 'space-between',
alignSelf: 'center',
alignItems: 'center',
alignItems: 'flex-start',
flex: 5,
},
iconWrapper: {
alignSelf: 'center',
@ -29,8 +28,9 @@ export default EStyleSheet.create({
},
rightTextWrapper: {
flexDirection: 'row',
justifyContent: 'center',
justifyContent: 'flex-end',
alignSelf: 'center',
flex: 5,
},
text: {
fontFamily: '$primaryFont',
@ -74,4 +74,7 @@ export default EStyleSheet.create({
justifyContent: 'center',
alignSelf: 'center',
},
dropdownWrapper: {
flex: 1,
},
});

View File

@ -4,6 +4,7 @@ import { View, Text } from 'react-native';
// Components
import GrayWrapper from '../grayWrapper/grayWrapperView';
import { Icon } from '../../../icon';
import { DropdownButton } from '../../../dropdownButton';
// Styles
import styles from './walletLineItemStyles';
@ -23,6 +24,9 @@ const WalletLineItem = ({
textColor,
index,
style,
dropdown,
dropdownOptions,
onDropdownSelect,
}) => (
<GrayWrapper isGray={index && index % 2 !== 0}>
<View style={[styles.container, fitContent && styles.fitContent, style]}>
@ -73,6 +77,17 @@ const WalletLineItem = ({
</Text>
</View>
)}
{dropdown && (
<View style={styles.dropdownWrapper}>
<DropdownButton
isHasChildIcon
iconName="arrow-drop-down"
options={dropdownOptions}
noHighlight
onSelect={onDropdownSelect}
/>
</View>
)}
</View>
</GrayWrapper>
);

View File

@ -52,6 +52,8 @@ class TransactionView extends PureComponent {
{transactions
&& transactions.map((item, index) => {
const transactionData = groomingTransactionData(item, steemPerMVests, formatNumber);
if (transactionData.length === 0) return null;
const value = transactionData.value.split(' ');
return (

View File

@ -0,0 +1 @@
export { default as TransferFormItem } from './view/transferFormItemView';

View File

@ -0,0 +1,21 @@
import EStyleSheet from 'react-native-extended-stylesheet';
export default EStyleSheet.create({
container: {
flexDirection: 'row',
paddingHorizontal: 20,
},
leftPart: {
flex: 1,
padding: 10,
justifyContent: 'center',
color: '$primaryBlack',
},
text: {
color: '$primaryBlack',
},
rightPart: {
flex: 2,
padding: 10,
},
});

View File

@ -0,0 +1,17 @@
import React from 'react';
import { View, Text } from 'react-native';
import styles from './transferFormItemStyles';
/* Props
* ------------------------------------------------
* @prop { type } name - Description....
*/
const TransferFormItemView = ({ rightComponent, label }) => (
<View style={styles.container}>
<View style={styles.leftPart}>{label && <Text style={styles.text}>{label}</Text>}</View>
<View style={styles.rightPart}>{rightComponent && rightComponent()}</View>
</View>
);
export default TransferFormItemView;

View File

@ -138,7 +138,9 @@ class WalletContainer extends Component {
};
render() {
const { currentAccount, selectedUser, isDarkTheme } = this.props;
const {
currentAccount, selectedUser, isDarkTheme, setPinCodeState,
} = this.props;
const { walletData, isClaiming, isRefreshing } = this.state;
return (
@ -151,6 +153,7 @@ class WalletContainer extends Component {
handleOnWalletRefresh={this._handleOnWalletRefresh}
isRefreshing={isRefreshing}
isDarkTheme={isDarkTheme}
setPinCodeState={setPinCodeState}
/>
);
}

View File

@ -55,6 +55,7 @@ class WalletView extends PureComponent {
selectedUsername,
walletData,
isDarkTheme,
setPinCodeState,
} = this.props;
return (
@ -113,7 +114,11 @@ class WalletView extends PureComponent {
})}
expanded
>
<WalletDetails intl={intl} walletData={walletData} />
<WalletDetails
intl={intl}
walletData={walletData}
setPinCodeState={setPinCodeState}
/>
</CollapsibleCard>
<Transaction walletData={walletData} />
</Fragment>

View File

@ -1,4 +1,11 @@
import React, { PureComponent } from 'react';
import { withNavigation } from 'react-navigation';
import { connect } from 'react-redux';
// Constants
import ROUTES from '../../../constants/routeNames';
import { openPinCodeModal } from '../../../redux/actions/applicationActions';
// Component
import WalletDetailsView from '../view/walletDetailsView';
@ -19,12 +26,33 @@ class WalletContainer extends PureComponent {
// Component Life Cycle Functions
// Component Functions
_navigate = (transferType, fundType) => {
const { dispatch, setPinCodeState, walletData } = this.props;
let balance;
switch (fundType) {
case 'STEEM':
balance = Math.round(walletData.balance * 1000) / 1000;
break;
case 'SBD':
balance = Math.round(walletData.sbdBalance * 1000) / 1000;
break;
default:
break;
}
setPinCodeState({
navigateTo: ROUTES.SCREENS.TRANSFER,
navigateParams: { transferType, fundType, balance },
});
dispatch(openPinCodeModal());
};
render() {
const { intl, walletData } = this.props;
return <WalletDetailsView intl={intl} walletData={walletData} />;
return <WalletDetailsView intl={intl} walletData={walletData} navigate={this._navigate} />;
}
}
export default WalletContainer;
export default connect()(withNavigation(WalletContainer));

View File

@ -29,7 +29,11 @@ class WalletDetailsView extends PureComponent {
// Component Functions
render() {
const { walletData, intl } = this.props;
const { walletData, intl, navigate } = this.props;
const steemDropdown = ['transferToken', 'transferToSaving', 'powerUp'];
const sbdDropdown = ['transferToken', 'transferToSaving'];
return (
<View style={styles.container}>
<WalletLineItem
@ -38,6 +42,9 @@ class WalletDetailsView extends PureComponent {
iconName="ios-information-circle-outline"
rightText={`${Math.round(walletData.balance * 1000) / 1000} STEEM`}
isBoldText
dropdown
dropdownOptions={steemDropdown.map(item => intl.formatMessage({ id: `transfer.${item}` }))}
onDropdownSelect={index => navigate(steemDropdown[index], 'STEEM')}
/>
<GrayWrapper isGray>
<WalletLineItem
@ -87,6 +94,9 @@ class WalletDetailsView extends PureComponent {
iconName="ios-information-circle-outline"
rightText={`$${Math.round(walletData.sbdBalance * 1000) / 1000}`}
isBoldText
dropdown
dropdownOptions={sbdDropdown.map(item => intl.formatMessage({ id: `transfer.${item}` }))}
onDropdownSelect={a => navigate(steemDropdown[a], 'SBD')}
/>
<GrayWrapper isGray>
<WalletLineItem

View File

@ -181,7 +181,8 @@
"cancel": "Cancel",
"delete": "Delete",
"copied": "Copied!",
"no_internet": "No connection!"
"no_internet": "No connection!",
"confirm": "Confirm"
},
"post": {
"reblog_alert": "Are you sure you want to reblog?"
@ -248,5 +249,21 @@
"reputation": "reputation",
"votes": "votes",
"age": "age"
},
"transfer": {
"title": "Transfer To Account",
"from": "From",
"to": "To",
"amount": "Amount",
"memo": "Memo",
"information": "Are you sure to transfer to funds?",
"amount_desc": "Balance",
"memo_desc": "This memo is public",
"to_placeholder": "Username",
"memo_placeholder": "Enter your notes here",
"transferToken": "Transfer",
"transferToSaving": "Transfer To Saving",
"powerUp": "Power Up",
"steemconnect_title": "Steemconnect Transfer"
}
}

View File

@ -18,6 +18,7 @@ export default {
VOTERS: `Voters${SCREEN_SUFFIX}`,
BOOKMARKS: `Bookmarks${SCREEN_SUFFIX}`,
SEARCH_RESULT: `SearchResult${SCREEN_SUFFIX}`,
TRANSFER: `Transfer${SCREEN_SUFFIX}`,
},
DRAWER: {
MAIN: `Main${DRAWER_SUFFIX}`,

View File

@ -0,0 +1,6 @@
export const steemConnectOptions = {
base_url: 'https://app.steemconnect.com/',
client_id: 'esteem-app',
redirect_uri: 'http://127.0.0.1:3415/', // http://127.0.0.1:3415
scope: 'vote,comment,delete_comment,comment_options,custom_json,claim_reward_balance,offline',
};

View File

@ -21,6 +21,7 @@ import {
SteemConnect,
Voters,
SearchResult,
Transfer,
} from '../screens';
// Components
@ -99,6 +100,12 @@ const stackNavigatior = createStackNavigator(
header: () => null,
},
},
[ROUTES.SCREENS.TRANSFER]: {
screen: RootComponent()(Transfer),
navigationOptions: {
header: () => null,
},
},
},
{
headerMode: 'none',

View File

@ -439,13 +439,10 @@ export const getPostWithComments = async (user, permlink) => {
* @param postingKey private posting key
*/
export const vote = (account, pin, author, permlink, weight) => _vote(
account, pin, author, permlink, weight,
)
.then((resp) => {
userActivity(account.username, 120, resp.block_num, resp.id);
return resp;
});
export const vote = (account, pin, author, permlink, weight) => _vote(account, pin, author, permlink, weight).then((resp) => {
userActivity(account.username, 120, resp.block_num, resp.id);
return resp;
});
const _vote = async (currentAccount, pin, author, permlink, weight) => {
const digitPinCode = getDigitPinCode(pin);
@ -513,18 +510,126 @@ export const upvoteAmount = async (input) => {
return estimated;
};
export const transferToken = (data, activeKey) => {
const key = PrivateKey.fromString(activeKey);
return new Promise((resolve, reject) => {
client.broadcast
.transfer(data, key)
.then((result) => {
resolve(result);
})
.catch((err) => {
reject(err);
});
});
export const transferToken = (currentAccount, pin, data) => {
const digitPinCode = getDigitPinCode(pin);
const key = getAnyPrivateKey({ activeKey: currentAccount.local.activeKey }, digitPinCode);
if (key) {
const privateKey = PrivateKey.fromString(key);
const args = {
from: data.from,
to: data.destination,
amount: data.amount,
memo: data.memo,
};
return new Promise((resolve, reject) => {
client.broadcast
.transfer(args, privateKey)
.then((result) => {
resolve(result);
})
.catch((err) => {
reject(err);
});
});
}
return Promise.reject(new Error('You dont have permission!'));
};
export const transferToSavings = (currentAccount, pin, data) => {
const digitPinCode = getDigitPinCode(pin);
const key = getAnyPrivateKey({ activeKey: currentAccount.local.activeKey }, digitPinCode);
if (key) {
const privateKey = PrivateKey.fromString(key);
const args = [[
'transfer_to_savings',
{
from: data.from,
to: data.destination,
amount: data.amount,
memo: data.memo,
},
]];
return new Promise((resolve, reject) => {
client.broadcast
.sendOperations(args, privateKey)
.then((result) => {
resolve(result);
})
.catch((err) => {
reject(err);
});
});
}
return Promise.reject(new Error('You dont have permission!'));
};
export const transferFromSavings = (currentAccount, pin, data) => {
const digitPinCode = getDigitPinCode(pin);
const key = getAnyPrivateKey({ activeKey: currentAccount.local.activeKey }, digitPinCode);
if (key) {
const privateKey = PrivateKey.fromString(key);
const args = [[
'transfer_from_savings',
{
from: data.from,
to: data.destination,
amount: data.amount,
memo: data.memo,
request_id: data.requestId,
},
]];
return new Promise((resolve, reject) => {
client.broadcast
.sendOperations(args, privateKey)
.then((result) => {
resolve(result);
})
.catch((err) => {
reject(err);
});
});
}
return Promise.reject(new Error('You dont have permission!'));
};
export const transferToVesting = (currentAccount, pin, data) => {
const digitPinCode = getDigitPinCode(pin);
const key = getAnyPrivateKey({ activeKey: currentAccount.local.activeKey }, digitPinCode);
if (key) {
const privateKey = PrivateKey.fromString(key);
const args = [[
'transfer_to_vesting',
{
from: data.from,
to: data.destination,
amount: data.amount,
},
]];
return new Promise((resolve, reject) => {
client.broadcast
.sendOperations(args, privateKey)
.then((result) => {
resolve(result);
})
.catch((err) => {
reject(err);
});
});
}
return Promise.reject(new Error('You dont have permission!'));
};
export const followUser = async (currentAccount, pin, data) => {
@ -631,30 +736,6 @@ export const delegate = (data, activeKey) => {
});
};
export const transferToVesting = (data, activeKey) => {
const privateKey = PrivateKey.fromString(activeKey);
const op = [
'transfer_to_vesting',
{
from: data.from,
to: data.to,
amount: data.amount,
},
];
return new Promise((resolve, reject) => {
client.broadcast
.sendOperations([op], privateKey)
.then((result) => {
resolve(result);
})
.catch((error) => {
reject(error);
});
});
};
export const withdrawVesting = (data, activeKey) => {
const privateKey = PrivateKey.fromString(activeKey);
const op = [
@ -837,9 +918,7 @@ const _postContent = async (
// Re-blog
// TODO: remove pinCode
export const reblog = (account, pinCode, author, permlink) => _reblog(
account, pinCode, author, permlink,
).then((resp) => {
export const reblog = (account, pinCode, author, permlink) => _reblog(account, pinCode, author, permlink).then((resp) => {
userActivity(account.name, 130, resp.block_num, resp.id);
return resp;
});
@ -925,7 +1004,7 @@ const getAnyPrivateKey = (local, pin) => {
}
if (activeKey) {
return decryptKey(local.postingKey, pin);
return decryptKey(local.activeKey, pin);
}
return false;

View File

@ -127,12 +127,7 @@ export const unFollow = data => new Promise((resolve, reject) => {
*/
export const claimRewards = data => new Promise((resolve, reject) => {
steemConnect
.claimRewardBalance(
data.account,
data.rewardSteem,
data.rewardSBD,
data.VESTS,
)
.claimRewardBalance(data.account, data.rewardSteem, data.rewardSBD, data.VESTS)
.then((result) => {
resolve(result);
})

View File

@ -15,6 +15,7 @@ import { Voters } from './voters';
import RootComponent from './root';
import SteemConnect from './steem-connect/steemConnect';
import { SearchResult } from './searchResult';
import Transfer from './transfer';
export {
Bookmarks,
@ -34,4 +35,5 @@ export {
SteemConnect,
Voters,
SearchResult,
Transfer,
};

View File

@ -80,7 +80,7 @@ class PinCodeContainer extends Component {
_resetPinCode = pin => new Promise((resolve, reject) => {
const {
currentAccount, dispatch, accessToken, navigateTo, navigation, intl,
currentAccount, dispatch, accessToken, navigateTo, navigateParams, navigation, intl,
} = this.props;
const { isOldPinVerified, oldPinCode } = this.state;
@ -102,7 +102,7 @@ class PinCodeContainer extends Component {
dispatch(closePinCodeModal());
if (navigateTo) {
navigation.navigate(navigateTo);
navigation.navigate(navigateTo, navigateParams);
}
resolve();
});
@ -135,7 +135,7 @@ class PinCodeContainer extends Component {
_setFirstPinCode = pin => new Promise((resolve) => {
const {
currentAccount, dispatch, accessToken, navigateTo, navigation,
currentAccount, dispatch, accessToken, navigateTo, navigateParams, navigation,
} = this.props;
const pinData = {
@ -155,7 +155,7 @@ class PinCodeContainer extends Component {
this._savePinCode(pin);
dispatch(closePinCodeModal());
if (navigateTo) {
navigation.navigate(navigateTo);
navigation.navigate(navigateTo, navigateParams);
}
resolve();
});
@ -165,7 +165,7 @@ class PinCodeContainer extends Component {
_verifyPinCode = pin => new Promise((resolve, reject) => {
const {
currentAccount, dispatch, accessToken, navigateTo, navigation, intl,
currentAccount, dispatch, accessToken, navigateTo, navigateParams, navigation, intl,
} = this.props;
// If the user is exist, we are just checking to pin and navigating to home screen
@ -184,8 +184,9 @@ class PinCodeContainer extends Component {
[_currentAccount.local] = realmData;
dispatch(updateCurrentAccount({ ..._currentAccount }));
dispatch(closePinCodeModal());
console.log('navigateParams :', navigateParams);
if (navigateTo) {
navigation.navigate(navigateTo);
navigation.navigate(navigateTo, navigateParams);
}
})
.catch((err) => {

View File

@ -76,10 +76,7 @@ class ProfileContainer extends Component {
componentWillReceiveProps(nextProps) {
const {
navigation,
currentAccount,
activeBottomTab,
isLoggedIn,
navigation, currentAccount, activeBottomTab, isLoggedIn,
} = this.props;
const currentUsername = currentAccount.name !== nextProps.currentAccount.name && nextProps.currentAccount.name;
@ -201,19 +198,19 @@ class ProfileContainer extends Component {
const { username } = this.state;
if (error) {
this.setState({
error,
}, () => alert(error));
this.setState(
{
error,
},
() => alert(error),
);
} else {
this._fetchProfile(username, true);
}
};
_fetchProfile = async (username = null, isProfileAction = false) => {
const {
username: _username, isFollowing, isMuted,
} = this.state;
const { username: _username, isFollowing, isMuted } = this.state;
if (username) {
const { isLoggedIn, currentAccount } = this.props;
@ -239,9 +236,9 @@ class ProfileContainer extends Component {
}
/**
* This follow code totally a work arround
* Ceated for server response delay.
*/
* This follow code totally a work arround
* Ceated for server response delay.
*/
if (isProfileAction && (isFollowing === _isFollowing && isMuted === _isMuted)) {
this._fetchProfile(_username, true);
} else {
@ -355,7 +352,7 @@ class ProfileContainer extends Component {
username,
} = this.state;
const {
isDarkTheme, isLoggedIn, currency, navigation,
isDarkTheme, isLoggedIn, currency, navigation, setPinCodeState,
} = this.props;
const activePage = (navigation.state.params && navigation.state.params.activePage) || 0;
@ -385,6 +382,7 @@ class ProfileContainer extends Component {
selectedQuickProfile={selectedQuickProfile}
selectedUser={user}
username={username}
setPinCodeState={setPinCodeState}
/>
);
}

View File

@ -84,6 +84,7 @@ class ProfileScreen extends PureComponent {
selectedUser,
username,
activePage,
setPinCodeState,
} = this.props;
const {
@ -249,6 +250,7 @@ class ProfileScreen extends PureComponent {
<Wallet
setEstimatedWalletValue={this._setEstimatedWalletValue}
selectedUser={selectedUser}
setPinCodeState={setPinCodeState}
/>
) : (
<WalletDetailsPlaceHolder />

View File

@ -12,9 +12,7 @@ import {
setCurrency as setCurrency2DB,
setServer,
setNotificationSettings,
setDefaultFooter,
setLanguage as setLanguage2DB,
setNotificationIsOpen,
setNsfw as setNsfw2DB,
setTheme,
} from '../../../realm/realm';
@ -27,7 +25,6 @@ import {
setApi,
isDarkTheme,
isDefaultFooter,
isNotificationOpen,
openPinCodeModal,
setNsfw,
} from '../../../redux/actions/applicationActions';

View File

@ -0,0 +1,120 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';
// Services and Actions
import {
lookupAccounts,
transferToken,
transferFromSavings,
transferToSavings,
transferToVesting,
} from '../../../providers/steem/dsteem';
import { toastNotification } from '../../../redux/actions/uiAction';
// Middleware
// Constants
// Utilities
// Component
import TransferView from '../screen/transferScreen';
/*
* Props Name Description Value
*@props --> props name here description here Value Type Here
*
*/
class ExampleContainer extends Component {
constructor(props) {
super(props);
this.state = {};
}
// Component Life Cycle Functions
// Component Functions
_getAccountsWithUsername = async (username) => {
const validUsers = await lookupAccounts(username);
return validUsers;
};
_transferToAccount = (from, destination, amount, memo) => {
const {
currentAccount, pinCode, navigation, dispatch,
} = this.props;
const transferType = navigation.getParam('transferType', '');
const fundType = navigation.getParam('fundType', '');
let func;
const data = {
from,
destination,
amount,
memo,
};
data.amount = `${data.amount} ${fundType}`;
switch (transferType) {
case 'transferToken':
func = transferToken;
break;
case 'transferToSaving':
func = transferToSavings;
break;
case 'powerUp':
func = transferToVesting;
break;
case 'withdrawToSaving':
func = transferFromSavings;
data.requestId = new Date().getTime() >>> 0;
break;
default:
break;
}
return func(currentAccount, pinCode, data)
.then(() => {
dispatch(toastNotification('Successfull'));
navigation.goBack();
})
.catch((err) => {
dispatch(toastNotification(err.message));
});
};
_handleOnModalClose = () => {
const { navigation } = this.props;
navigation.goBack();
};
render() {
const { accounts, currentAccount, navigation } = this.props;
const fundType = navigation.getParam('fundType', '');
const balance = navigation.getParam('balance', '');
return (
<TransferView
accounts={accounts}
getAccountsWithUsername={this._getAccountsWithUsername}
transferToAccount={this._transferToAccount}
handleOnModalClose={this._handleOnModalClose}
accountType={currentAccount.local.authType}
balance={balance}
fundType={fundType}
/>
);
}
}
const mapStateToProps = state => ({
accounts: state.account.otherAccounts,
currentAccount: state.account.currentAccount,
pinCode: state.account.pin,
});
export default connect(mapStateToProps)(ExampleContainer);

View File

@ -0,0 +1,5 @@
import TransferScreen from './screen/transferScreen';
import Transfer from './container/transferContainer';
export { TransferScreen, Transfer };
export default Transfer;

View File

@ -0,0 +1,195 @@
/* eslint-disable no-restricted-globals */
import React, { Fragment, Component } from 'react';
import { Text, View, WebView } from 'react-native';
import ActionSheet from 'react-native-actionsheet';
import { injectIntl } from 'react-intl';
import { steemConnectOptions } from '../../../constants/steemConnectOptions';
import AUTH_TYPE from '../../../constants/authType';
import { BasicHeader } from '../../../components/basicHeader';
import { TextInput } from '../../../components/textInput';
import { TransferFormItem } from '../../../components/transferFormItem';
import { MainButton } from '../../../components/mainButton';
import { DropdownButton } from '../../../components/dropdownButton';
import { UserAvatar } from '../../../components/userAvatar';
import { Icon } from '../../../components/icon';
import { Modal } from '../../../components/modal';
import styles from './transferStyles';
/* Props
* ------------------------------------------------
* @prop { type } name - Description....
*/
class TransferView extends Component {
constructor(props) {
super(props);
this.state = {
from: props.accounts[0].username,
destination: '',
amount: '',
memo: '',
isUsernameValid: false,
steemConnectTransfer: false,
};
}
// Component Life Cycles
// Component Functions
_setState = (key, value) => {
const { getAccountsWithUsername, balance } = this.props;
if (key) {
switch (key) {
case 'destination':
getAccountsWithUsername(value).then((res) => {
const isValid = res.includes(value);
this.setState({ isUsernameValid: isValid });
});
this.setState({ [key]: value });
break;
case 'amount':
if (!isNaN(value) && parseFloat(value) <= parseFloat(balance)) this.setState({ [key]: value });
break;
default:
this.setState({ [key]: value });
break;
}
}
};
_handleTransferAction = () => {
const { transferToAccount, accountType } = this.props;
const {
from, destination, amount, memo,
} = this.state;
if (accountType === AUTH_TYPE.STEEM_CONNECT) {
this.setState({ steemConnectTransfer: true });
} else {
transferToAccount(from, destination, amount, memo);
}
};
_renderInput = (placeholder, state) => (
<TextInput
style={styles.input}
onChangeText={text => this._setState(state, text)}
value={this.state[state]}
placeholder={placeholder}
placeholderTextColor="#c1c5c7"
autoCapitalize="none"
keyboardType="numeric"
/>
);
_renderDropdown = accounts => (
<DropdownButton
style={styles.dropdown}
options={accounts.map(item => item.username)}
defaultText={accounts[0].username}
selectedOptionIndex={0}
onSelect={(index, value) => this.setState({ from: value })}
/>
);
_renderDescription = text => <Text style={styles.description}>{text}</Text>;
render() {
const { accounts, intl, handleOnModalClose, balance, fundType } = this.props;
const {
destination, isUsernameValid, amount, steemConnectTransfer, memo,
} = this.state;
const path = `sign/transfer?from=${
accounts[0].username
}&to=${destination}&amount=${encodeURIComponent(`${amount} STEEM`)}&memo=${encodeURIComponent(
memo,
)}`;
return (
<Fragment>
<BasicHeader title={intl.formatMessage({ id: 'transfer.title' })} />
<View style={styles.container}>
<View style={styles.topContent}>
<UserAvatar
username={accounts[0].username}
size="xl"
style={styles.userAvatar}
noAction
/>
<Icon style={styles.icon} name="arrow-forward" iconType="MaterialIcons" />
<UserAvatar username={destination} size="xl" style={styles.userAvatar} noAction />
</View>
<View style={styles.middleContent}>
<TransferFormItem
label={intl.formatMessage({ id: 'transfer.from' })}
rightComponent={() => this._renderDropdown(accounts)}
/>
<TransferFormItem
label={intl.formatMessage({ id: 'transfer.to' })}
rightComponent={() => this._renderInput(
intl.formatMessage({ id: 'transfer.to_placeholder' }),
'destination',
)
}
/>
<TransferFormItem
label={intl.formatMessage({ id: 'transfer.amount' })}
rightComponent={() => this._renderInput(intl.formatMessage({ id: 'transfer.amount' }), 'amount')}
/>
<TransferFormItem
rightComponent={() => this._renderDescription(`${intl.formatMessage({ id: 'transfer.amount_desc' })} ${balance} ${fundType}`)}
/>
<TransferFormItem
label={intl.formatMessage({ id: 'transfer.memo' })}
rightComponent={() => this._renderInput(intl.formatMessage({ id: 'transfer.memo_placeholder' }), 'memo')
}
/>
<TransferFormItem
rightComponent={() => this._renderDescription(intl.formatMessage({ id: 'transfer.memo_desc' }))
}
/>
</View>
<View style={styles.bottomContent}>
<MainButton
style={styles.button}
isDisable={!(amount && isUsernameValid)}
onPress={() => this.ActionSheet.show()}
>
<Text style={styles.buttonText}>NEXT</Text>
</MainButton>
</View>
</View>
<ActionSheet
ref={o => (this.ActionSheet = o)}
options={[
intl.formatMessage({ id: 'alert.confirm' }),
intl.formatMessage({ id: 'alert.cancel' }),
]}
title={intl.formatMessage({ id: 'transfer.information' })}
cancelButtonIndex={1}
destructiveButtonIndex={0}
onPress={(index) => {
index === 0 ? this._handleTransferAction() : null;
}}
/>
<Modal
isOpen={steemConnectTransfer}
isFullScreen
isCloseButton
handleOnModalClose={handleOnModalClose}
title={intl.formatMessage({ id: 'transfer.steemconnect_title' })}
>
<WebView source={{ uri: `${steemConnectOptions.base_url}${path}` }} />
</Modal>
</Fragment>
);
}
}
export default injectIntl(TransferView);

View File

@ -0,0 +1,57 @@
import EStyleSheet from 'react-native-extended-stylesheet';
export default EStyleSheet.create({
container: {
flex: 1,
backgroundColor: '$primaryBackgroundColor',
justifyContent: 'center',
alignItems: 'center',
},
topContent: {
flexDirection: 'row',
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
middleContent: {
flex: 3,
justifyContent: 'center',
alignItems: 'center',
},
bottomContent: {
flex: 2,
justifyContent: 'center',
alignItems: 'center',
},
input: {
borderWidth: 1,
borderColor: '$borderColor',
borderRadius: 10,
padding: 10,
color: '$primaryBlack',
},
description: {
color: '$iconColor',
},
button: {
width: '$deviceWidth / 3',
marginTop: 30,
justifyContent: 'center',
alignItems: 'center',
},
buttonText: {
color: 'white',
},
dropdown: {
borderWidth: 1,
borderColor: '$borderColor',
borderRadius: 10,
flex: 1,
padding: 10,
},
icon: {
fontSize: 40,
color: '$iconColor',
marginHorizontal: 20,
},
});

View File

@ -95,7 +95,7 @@ export const groomingTransactionData = (transaction, steemPerMVests, formatNumbe
result.icon = 'reorder';
break;
default:
break;
return [];
}
return result;
};