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, isLoading,
isLoggedIn, isLoggedIn,
isModalHeader, isModalHeader,
isPostSending,
rightButtonText, rightButtonText,
isPreviewActive, isPreviewActive,
isReply, isReply,

View File

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

View File

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

View File

@ -52,6 +52,8 @@ class TransactionView extends PureComponent {
{transactions {transactions
&& transactions.map((item, index) => { && transactions.map((item, index) => {
const transactionData = groomingTransactionData(item, steemPerMVests, formatNumber); const transactionData = groomingTransactionData(item, steemPerMVests, formatNumber);
if (transactionData.length === 0) return null;
const value = transactionData.value.split(' '); const value = transactionData.value.split(' ');
return ( 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() { render() {
const { currentAccount, selectedUser, isDarkTheme } = this.props; const {
currentAccount, selectedUser, isDarkTheme, setPinCodeState,
} = this.props;
const { walletData, isClaiming, isRefreshing } = this.state; const { walletData, isClaiming, isRefreshing } = this.state;
return ( return (
@ -151,6 +153,7 @@ class WalletContainer extends Component {
handleOnWalletRefresh={this._handleOnWalletRefresh} handleOnWalletRefresh={this._handleOnWalletRefresh}
isRefreshing={isRefreshing} isRefreshing={isRefreshing}
isDarkTheme={isDarkTheme} isDarkTheme={isDarkTheme}
setPinCodeState={setPinCodeState}
/> />
); );
} }

View File

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

View File

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

View File

@ -181,7 +181,8 @@
"cancel": "Cancel", "cancel": "Cancel",
"delete": "Delete", "delete": "Delete",
"copied": "Copied!", "copied": "Copied!",
"no_internet": "No connection!" "no_internet": "No connection!",
"confirm": "Confirm"
}, },
"post": { "post": {
"reblog_alert": "Are you sure you want to reblog?" "reblog_alert": "Are you sure you want to reblog?"
@ -248,5 +249,21 @@
"reputation": "reputation", "reputation": "reputation",
"votes": "votes", "votes": "votes",
"age": "age" "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}`, VOTERS: `Voters${SCREEN_SUFFIX}`,
BOOKMARKS: `Bookmarks${SCREEN_SUFFIX}`, BOOKMARKS: `Bookmarks${SCREEN_SUFFIX}`,
SEARCH_RESULT: `SearchResult${SCREEN_SUFFIX}`, SEARCH_RESULT: `SearchResult${SCREEN_SUFFIX}`,
TRANSFER: `Transfer${SCREEN_SUFFIX}`,
}, },
DRAWER: { DRAWER: {
MAIN: `Main${DRAWER_SUFFIX}`, 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, SteemConnect,
Voters, Voters,
SearchResult, SearchResult,
Transfer,
} from '../screens'; } from '../screens';
// Components // Components
@ -99,6 +100,12 @@ const stackNavigatior = createStackNavigator(
header: () => null, header: () => null,
}, },
}, },
[ROUTES.SCREENS.TRANSFER]: {
screen: RootComponent()(Transfer),
navigationOptions: {
header: () => null,
},
},
}, },
{ {
headerMode: 'none', headerMode: 'none',

View File

@ -439,13 +439,10 @@ export const getPostWithComments = async (user, permlink) => {
* @param postingKey private posting key * @param postingKey private posting key
*/ */
export const vote = (account, pin, author, permlink, weight) => _vote( export const vote = (account, pin, author, permlink, weight) => _vote(account, pin, author, permlink, weight).then((resp) => {
account, pin, author, permlink, weight, userActivity(account.username, 120, resp.block_num, resp.id);
) return resp;
.then((resp) => { });
userActivity(account.username, 120, resp.block_num, resp.id);
return resp;
});
const _vote = async (currentAccount, pin, author, permlink, weight) => { const _vote = async (currentAccount, pin, author, permlink, weight) => {
const digitPinCode = getDigitPinCode(pin); const digitPinCode = getDigitPinCode(pin);
@ -513,18 +510,126 @@ export const upvoteAmount = async (input) => {
return estimated; return estimated;
}; };
export const transferToken = (data, activeKey) => { export const transferToken = (currentAccount, pin, data) => {
const key = PrivateKey.fromString(activeKey); const digitPinCode = getDigitPinCode(pin);
return new Promise((resolve, reject) => { const key = getAnyPrivateKey({ activeKey: currentAccount.local.activeKey }, digitPinCode);
client.broadcast
.transfer(data, key) if (key) {
.then((result) => { const privateKey = PrivateKey.fromString(key);
resolve(result); const args = {
}) from: data.from,
.catch((err) => { to: data.destination,
reject(err); 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) => { 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) => { export const withdrawVesting = (data, activeKey) => {
const privateKey = PrivateKey.fromString(activeKey); const privateKey = PrivateKey.fromString(activeKey);
const op = [ const op = [
@ -837,9 +918,7 @@ const _postContent = async (
// Re-blog // Re-blog
// TODO: remove pinCode // TODO: remove pinCode
export const reblog = (account, pinCode, author, permlink) => _reblog( export const reblog = (account, pinCode, author, permlink) => _reblog(account, pinCode, author, permlink).then((resp) => {
account, pinCode, author, permlink,
).then((resp) => {
userActivity(account.name, 130, resp.block_num, resp.id); userActivity(account.name, 130, resp.block_num, resp.id);
return resp; return resp;
}); });
@ -925,7 +1004,7 @@ const getAnyPrivateKey = (local, pin) => {
} }
if (activeKey) { if (activeKey) {
return decryptKey(local.postingKey, pin); return decryptKey(local.activeKey, pin);
} }
return false; return false;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -12,9 +12,7 @@ import {
setCurrency as setCurrency2DB, setCurrency as setCurrency2DB,
setServer, setServer,
setNotificationSettings, setNotificationSettings,
setDefaultFooter,
setLanguage as setLanguage2DB, setLanguage as setLanguage2DB,
setNotificationIsOpen,
setNsfw as setNsfw2DB, setNsfw as setNsfw2DB,
setTheme, setTheme,
} from '../../../realm/realm'; } from '../../../realm/realm';
@ -27,7 +25,6 @@ import {
setApi, setApi,
isDarkTheme, isDarkTheme,
isDefaultFooter, isDefaultFooter,
isNotificationOpen,
openPinCodeModal, openPinCodeModal,
setNsfw, setNsfw,
} from '../../../redux/actions/applicationActions'; } 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'; result.icon = 'reorder';
break; break;
default: default:
break; return [];
} }
return result; return result;
}; };