Merge pull request #1324 from esteemapp/new/wallet

New/wallet
This commit is contained in:
Feruz M 2019-11-28 07:06:30 +02:00 committed by GitHub
commit 6222238130
61 changed files with 1598 additions and 1313 deletions

View File

@ -71,6 +71,7 @@
"react-native-slider": "^0.11.0",
"react-native-snap-carousel": "^3.8.0",
"react-native-svg": "^9.5.3",
"react-native-swiper": "^1.5.14",
"react-native-vector-icons": "^6.6.0",
"react-native-version": "^3.1.0",
"react-native-version-number": "^0.3.5",

View File

@ -4,9 +4,5 @@ export default EStyleSheet.create({
wrapper: {
flexDirection: 'column',
backgroundColor: '$primaryBackgroundColor',
shadowOpacity: 0.2,
shadowColor: '#e7e7e7',
paddingHorizontal: 8,
paddingVertical: 8,
},
});

View File

@ -20,7 +20,7 @@ const BoostPlaceHolder = () => {
{({ isDarkTheme }) => {
const color = isDarkTheme ? '#2e3d51' : '#f5f5f5';
return (
<View style={styles.container} key={i}>
<View style={styles.container} key={i.toString()}>
<View style={styles.line}>
<Placeholder.Box color={color} width={90} height={40} animate="fade" />
<View style={styles.paragraphWrapper}>

View File

@ -9,6 +9,9 @@ export default EStyleSheet.create({
fitContent: {
marginVertical: 0,
},
textWrapper: {
flexDirection: 'row',
},
iconTextWrapper: {
flexDirection: 'row',
alignSelf: 'center',
@ -35,6 +38,7 @@ export default EStyleSheet.create({
text: {
fontFamily: '$primaryFont',
color: '$primaryBlack',
alignSelf: 'center',
fontSize: 16,
marginLeft: 8,
},
@ -46,6 +50,7 @@ export default EStyleSheet.create({
marginLeft: 8,
fontFamily: '$primaryFont',
color: '$iconColor',
width: '100%',
},
onlyText: {
marginLeft: 40,
@ -84,4 +89,9 @@ export default EStyleSheet.create({
dropdownStyle: {
minWidth: '$deviceWidth * 0.7',
},
hintIcon: {
marginLeft: 10,
justifyContent: 'center',
alignItems: 'center',
},
});

View File

@ -2,9 +2,7 @@ import React from 'react';
import { View, Text } from 'react-native';
// Components
import GrayWrapper from '../grayWrapper/grayWrapperView';
import { Icon } from '../../../icon';
import { DropdownButton } from '../../../dropdownButton';
import { DropdownButton, PopoverWrapper, Icon, GrayWrapper } from '../../..';
// Styles
import styles from './walletLineItemStyles';
@ -27,6 +25,8 @@ const WalletLineItem = ({
isHasdropdown,
dropdownOptions,
onDropdownSelect,
hintIconName,
hintDescription,
}) => (
<GrayWrapper isGray={index && index % 2 !== 0}>
<View style={[styles.container, fitContent && styles.fitContent, style]}>
@ -44,7 +44,7 @@ const WalletLineItem = ({
)}
<View>
{!!text && (
<View>
<View style={styles.textWrapper}>
<Text
style={[
styles.text,
@ -58,6 +58,15 @@ const WalletLineItem = ({
>
{text}
</Text>
{!!hintIconName && (
<PopoverWrapper text={hintDescription}>
<Icon
style={[styles.hintIcon, styles.icon]}
name={hintIconName}
iconType={iconType}
/>
</PopoverWrapper>
)}
</View>
)}
{!!description && (

View File

@ -1,12 +1,16 @@
import React, { Fragment } from 'react';
import { connect } from 'react-redux';
const FormattedCurrency = ({ value, fixAt = 3, currency }) => {
const FormattedCurrency = ({ value, fixAt = 3, currency, isApproximate = false }) => {
const { currencyRate, currencySymbol } = currency;
const valueInCurrency = value * currencyRate;
const toFixedValue = valueInCurrency.toFixed(fixAt);
return <Fragment key={toFixedValue.toString()}>{`${currencySymbol} ${toFixedValue}`}</Fragment>;
return (
<Fragment key={toFixedValue.toString()}>{`${
isApproximate ? '~' : ''
}${currencySymbol} ${toFixedValue}`}</Fragment>
);
};
const mapStateToProps = state => ({

View File

@ -0,0 +1,55 @@
import EStyleSheet from 'react-native-extended-stylesheet';
export default EStyleSheet.create({
iconsWrapper: {
marginVertical: 10,
marginHorizontal: 0,
justifyContent: 'center',
alignSelf: 'center',
flexDirection: 'row',
},
iconsList: {
height: 55,
},
subText: {
color: '$darkIconColor',
fontSize: 8,
justifyContent: 'center',
alignSelf: 'center',
marginTop: 5,
},
iconWrapper: {
marginHorizontal: 16,
width: 36,
height: 36,
borderRadius: 36 / 2,
backgroundColor: '$primaryLightBackground',
justifyContent: 'center',
alignItems: 'center',
},
iconButton: {
marginTop: 1,
marginLeft: 1,
},
icon: {
fontSize: 24,
color: '$primaryDarkText',
},
badge: {
position: 'absolute',
right: -9,
top: 20,
backgroundColor: '$primaryBlue',
justifyContent: 'center',
alignItems: 'center',
padding: 2,
height: 12,
minWidth: 18,
borderRadius: 12 / 2,
},
badgeText: {
fontSize: 8,
color: '$white',
fontWeight: '600',
},
});

View File

@ -0,0 +1,57 @@
import React from 'react';
import { useIntl } from 'react-intl';
import { View, FlatList, Text } from 'react-native';
import get from 'lodash/get';
import styles from './horizontalIconListStyles';
import { IconButton, PopoverWrapper } from '..';
const HorizontalIconList = ({ options, optionsKeys }) => {
const intl = useIntl();
const _getTranslation = id => {
let translation;
try {
translation = intl.formatMessage({ id });
} catch (error) {
translation = '';
}
return translation;
};
return (
<View style={styles.iconsWrapper}>
<FlatList
style={styles.iconsList}
data={optionsKeys}
keyExtractor={item => get(item, 'type', Math.random()).toString()}
horizontal
renderItem={({ item }) => (
<PopoverWrapper text={_getTranslation(get(options[get(item, 'type')], 'descriptionKey'))}>
<View styles={styles.iconWrapper} key={get(item, 'type')}>
<View style={styles.iconWrapper}>
<IconButton
iconStyle={styles.icon}
style={styles.iconButton}
iconType={get(options[get(item, 'type')], 'iconType')}
name={get(options[get(item, 'type')], 'icon')}
badgeCount={get(options[get(item, 'type')], 'point')}
badgeStyle={styles.badge}
badgeTextStyle={styles.badgeText}
disabled
/>
</View>
<Text style={styles.subText}>
{_getTranslation(get(options[get(item, 'type')], 'nameKey'))}
</Text>
</View>
</PopoverWrapper>
)}
/>
</View>
);
};
export { HorizontalIconList };

View File

@ -51,6 +51,8 @@ import PostButton from './postButton/postButtonView';
import ProfileEditForm from './profileEditForm/profileEditFormView';
import ScaleSlider from './scaleSlider/scaleSliderView';
import { ProductItemLine } from './productItemLine/productItemLineView';
import { HorizontalIconList } from './horizontalIconList/horizontalIconListView';
import { PopoverWrapper } from './popoverWrapper/popoverWrapperView';
// View
import { Comment } from './comment';
@ -58,7 +60,7 @@ import { Comments } from './comments';
import { CommentsDisplay } from './commentsDisplay';
import { LeaderBoard } from './leaderboard';
import { Notification } from './notification';
import { Points } from './points';
import { WalletHeader } from './walletHeader';
import { Posts } from './posts';
import { Transaction } from './transaction';
import { VotersDisplay } from './votersDisplay';
@ -135,7 +137,7 @@ export {
ParentPost,
PercentBar,
PinAnimatedInput,
Points,
WalletHeader,
PostBody,
PostBoost,
PostButton,
@ -187,4 +189,6 @@ export {
WalletDetailsPlaceHolder,
WalletLineItem,
WalletUnclaimedPlaceHolder,
HorizontalIconList,
PopoverWrapper,
};

View File

@ -1,3 +0,0 @@
import Points from './view/pointsView';
export { Points };

View File

@ -1,149 +0,0 @@
import EStyleSheet from 'react-native-extended-stylesheet';
export default EStyleSheet.create({
pointText: {
color: '$primaryBlue',
fontSize: 26,
marginTop: 24,
justifyContent: 'center',
alignSelf: 'center',
fontWeight: 'bold',
},
pointsWrapper: {
flexDirection: 'row',
justifyContent: 'center',
alignSelf: 'center',
flex: 1,
},
dropdownRowText: {
fontSize: 14,
color: '$primaryDarkGray',
textAlign: 'center',
},
dropdownRowStyle: {
marginLeft: 0,
},
dropdownButtonStyle: {
justifyContent: 'center',
alignSelf: 'center',
position: 'absolute',
right: -40,
top: 20,
},
subText: {
color: '$darkIconColor',
fontSize: 8,
justifyContent: 'center',
alignSelf: 'center',
marginTop: 5,
},
iconsWrapper: {
marginVertical: 24,
marginHorizontal: 32,
justifyContent: 'center',
alignSelf: 'center',
flexDirection: 'row',
},
iconsList: {
height: 55,
},
iconWrapper: {
marginHorizontal: 16,
width: 36,
height: 36,
borderRadius: 36 / 2,
backgroundColor: '$primaryGrayBackground',
justifyContent: 'center',
alignItems: 'center',
},
iconButton: {
marginTop: 1,
marginLeft: 1,
},
activeIconWrapper: {
backgroundColor: '$primaryBlue',
},
activeIcon: {
color: '$white',
},
icon: {
fontSize: 24,
color: '$primaryDarkText',
},
badge: {
position: 'absolute',
right: -9,
top: 20,
backgroundColor: '$primaryBlue',
justifyContent: 'center',
alignItems: 'center',
padding: 2,
height: 12,
minWidth: 18,
borderRadius: 12 / 2,
},
badgeText: {
fontSize: 8,
color: '$white',
fontWeight: '600',
},
activeBadge: {
backgroundColor: '$black',
},
listWrapper: {
marginHorizontal: 8,
},
mainButton: {
marginVertical: 8,
alignSelf: 'center',
paddingHorizontal: 24,
},
mainButtonWrapper: {
flexDirection: 'row',
},
unclaimedText: {
color: '$pureWhite',
fontSize: 14,
fontWeight: 'bold',
alignSelf: 'center',
},
mainIconWrapper: {
backgroundColor: '$pureWhite',
justifyContent: 'center',
alignSelf: 'center',
alignItems: 'center',
borderRadius: 20,
marginLeft: 20,
width: 24,
height: 24,
},
scrollContainer: {
flex: 1,
},
scrollContentContainer: {
paddingBottom: 60,
},
popoverDetails: {
flexDirection: 'row',
height: 130,
width: '$deviceWidth / 2',
borderRadius: 20,
paddingHorizontal: 26,
backgroundColor: '$primaryBackgroundColor',
},
arrow: {
borderTopColor: '$primaryBackgroundColor',
},
popoverWrapper: {
flex: 1,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
overlay: {
backgroundColor: '#403c4449',
},
popoverText: {
color: '$primaryDarkText',
},
});

View File

@ -1,265 +0,0 @@
/* eslint-disable react/jsx-wrap-multilines */
import React, { Component, Fragment } from 'react';
import { Text, View, FlatList, ScrollView, RefreshControl, TouchableOpacity } from 'react-native';
import { injectIntl } from 'react-intl';
import { Popover, PopoverController } from 'react-native-modal-popover';
import { get } from 'lodash';
import { withNavigation } from 'react-navigation';
// Components
import { LineBreak, WalletLineItem, ListPlaceHolder } from '../../basicUIElements';
import { IconButton } from '../../iconButton';
import { Icon } from '../../icon';
import { MainButton } from '../../mainButton';
import { DropdownButton } from '../../dropdownButton';
import { CollapsibleCard } from '../../collapsibleCard';
import { ThemeContainer } from '../../../containers';
// Utils
import { getTimeFromNow } from '../../../utils/time';
// Constants
import POINTS, { POINTS_KEYS } from '../../../constants/options/points';
import { default as ROUTES } from '../../../constants/routeNames';
// Styles
import styles from './pointsStyles';
class PointsView extends Component {
/* Props
* ------------------------------------------------
* @prop { type } name - Description....
*/
constructor(props) {
super(props);
this.state = {};
this.dropdownRef = React.createRef();
}
// Component Functions
refreshControl = ({ isDarkTheme }) => {
const { fetchUserActivity, refreshing } = this.props;
return (
<RefreshControl
refreshing={refreshing}
onRefresh={fetchUserActivity}
progressBackgroundColor="#357CE6"
tintColor={!isDarkTheme ? '#357ce6' : '#96c0ff'}
titleColor="#fff"
colors={['#fff']}
/>
);
};
_getTranslation = id => {
const { intl } = this.props;
let translation;
try {
translation = intl.formatMessage({ id });
} catch (error) {
translation = '';
}
return translation;
};
_renderLoading = () => {
const { isLoading, intl } = this.props;
if (isLoading) {
return <ListPlaceHolder />;
}
return <Text style={styles.subText}>{intl.formatMessage({ id: 'points.no_activity' })}</Text>;
};
_showDropdown = () => {
this.dropdownRef.current.show();
};
render() {
const {
claimPoints,
isClaiming,
userActivities,
userPoints,
handleOnDropdownSelected,
navigation,
intl,
} = this.props;
const unclaimedPoints = get(userPoints, 'unclaimed_points', 0);
return (
<Fragment>
<LineBreak height={12} />
<ThemeContainer>
{isDarkTheme => (
<ScrollView
style={styles.scrollContainer}
refreshControl={this.refreshControl({ isDarkTheme })}
contentContainerStyle={styles.scrollContentContainer}
>
<View style={styles.pointsWrapper}>
<Text onPress={this._showDropdown} style={styles.pointText}>
{get(userPoints, 'points')}
</Text>
<DropdownButton
dropdownRowWrapper={styles.dropdownRowStyle}
dropdownRef={this.dropdownRef}
isHasChildIcon
iconName="arrow-drop-down"
options={[
intl.formatMessage({ id: 'points.dropdown_transfer' }),
intl.formatMessage({ id: 'points.dropdown_promote' }),
intl.formatMessage({ id: 'points.dropdown_boost' }),
]}
noHighlight
dropdownButtonStyle={styles.dropdownButtonStyle}
onSelect={handleOnDropdownSelected}
rowTextStyle={styles.dropdownRowText}
dropdownStyle={styles.dropdownStyle}
/>
</View>
<Text style={styles.subText}>
{intl.formatMessage({ id: 'points.esteemPoints' })}
</Text>
<MainButton
isLoading={isClaiming}
isDisable={isClaiming}
style={styles.mainButton}
height={50}
onPress={() =>
unclaimedPoints > 0 ? claimPoints() : navigation.navigate(ROUTES.SCREENS.BOOST)
}
>
<View style={styles.mainButtonWrapper}>
<Text style={styles.unclaimedText}>
{unclaimedPoints > 0
? unclaimedPoints
: intl.formatMessage({ id: 'boost.buy' })}
</Text>
<View style={styles.mainIconWrapper}>
<Icon name="add" iconType="MaterialIcons" color="#357ce6" size={23} />
</View>
</View>
</MainButton>
<View style={styles.iconsWrapper}>
<FlatList
style={styles.iconsList}
data={POINTS_KEYS}
keyExtractor={item => get(item, 'type', Math.random()).toString()}
horizontal
renderItem={({ item }) => (
<PopoverController key={get(item, 'type')}>
{({
openPopover,
closePopover,
popoverVisible,
setPopoverAnchor,
popoverAnchorRect,
}) => (
<View styles={styles.iconWrapper} key={get(item, 'type')}>
<View style={styles.iconWrapper}>
<TouchableOpacity ref={setPopoverAnchor} onPress={openPopover}>
<IconButton
iconStyle={styles.icon}
style={styles.iconButton}
iconType={get(POINTS[get(item, 'type')], 'iconType')}
name={get(POINTS[get(item, 'type')], 'icon')}
badgeCount={get(POINTS[get(item, 'type')], 'point')}
badgeStyle={styles.badge}
badgeTextStyle={styles.badgeText}
disabled
/>
</TouchableOpacity>
</View>
<Text style={styles.subText}>
{this._getTranslation(get(POINTS[get(item, 'type')], 'nameKey'))}
</Text>
<Popover
backgroundStyle={styles.overlay}
contentStyle={styles.popoverDetails}
arrowStyle={styles.arrow}
visible={popoverVisible}
onClose={() => closePopover()}
fromRect={popoverAnchorRect}
placement="top"
supportedOrientations={['portrait', 'landscape']}
>
<View style={styles.popoverWrapper}>
<Text style={styles.popoverText}>
{this._getTranslation(
get(POINTS[get(item, 'type')], 'descriptionKey'),
)}
</Text>
</View>
</Popover>
</View>
)}
</PopoverController>
)}
/>
</View>
<View style={styles.listWrapper}>
<FlatList
data={userActivities}
keyExtractor={item => item.id.toString()}
ListEmptyComponent={this._renderLoading()}
renderItem={({ item, index }) => (
<CollapsibleCard
noBorder
noContainer
key={item.id.toString()}
titleComponent={
<WalletLineItem
index={index + 1}
text={this._getTranslation(get(item, 'textKey'))}
description={getTimeFromNow(get(item, 'created'))}
isCircleIcon
isThin
isBlackText
iconName={get(item, 'icon')}
iconType={get(item, 'iconType')}
rightText={`${get(item, 'amount')} ESTM`}
/>
}
>
{(get(item, 'memo') || get(item, 'sender')) && (
<WalletLineItem
isBlackText
isThin
text={
get(item, 'sender')
? `${intl.formatMessage({ id: 'points.from' })} @${get(
item,
'sender',
)}`
: get(item, 'receiver') &&
`${intl.formatMessage({ id: 'points.to' })} @${get(
item,
'receiver',
)}`
}
description={get(item, 'memo')}
/>
)}
</CollapsibleCard>
)}
/>
</View>
</ScrollView>
)}
</ThemeContainer>
</Fragment>
);
}
}
export default withNavigation(injectIntl(PointsView));

View File

@ -0,0 +1,27 @@
import EStyleSheet from 'react-native-extended-stylesheet';
export default EStyleSheet.create({
popoverDetails: {
flexDirection: 'row',
height: 130,
width: '$deviceWidth / 2',
borderRadius: 20,
paddingHorizontal: 26,
backgroundColor: '$primaryBackgroundColor',
},
arrow: {
borderTopColor: '$primaryBackgroundColor',
},
popoverWrapper: {
flex: 1,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
overlay: {
backgroundColor: '#403c4449',
},
popoverText: {
color: '$primaryDarkText',
},
});

View File

@ -0,0 +1,35 @@
import React, { Fragment } from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import { Popover, PopoverController } from 'react-native-modal-popover';
import styles from './popoverWrapperStyles';
const PopoverWrapper = ({ children, text }) => {
return (
<PopoverController>
{({ openPopover, closePopover, popoverVisible, setPopoverAnchor, popoverAnchorRect }) => (
<Fragment>
<TouchableOpacity ref={setPopoverAnchor} onPress={openPopover}>
{children}
</TouchableOpacity>
<Popover
backgroundStyle={styles.overlay}
contentStyle={styles.popoverDetails}
arrowStyle={styles.arrow}
visible={popoverVisible}
onClose={() => closePopover()}
fromRect={popoverAnchorRect}
placement="top"
supportedOrientations={['portrait', 'landscape']}
>
<View style={styles.popoverWrapper}>
<Text style={styles.popoverText}>{text}</Text>
</View>
</Popover>
</Fragment>
)}
</PopoverController>
);
};
export { PopoverWrapper };

View File

@ -25,7 +25,7 @@ export default EStyleSheet.create({
alignContent: 'center',
alignItems: 'center',
marginTop: 10,
marginBottom: 40,
marginBottom: 60,
borderColor: '$borderColor',
},
noImage: {

View File

@ -160,6 +160,7 @@ const PostsView = ({
setIsLoading(false);
return;
}
setIsLoading(true);
const filter = type || selectedFilterValue;
let options;

View File

@ -1,7 +1,6 @@
import React from 'react';
import { View, Text } from 'react-native';
import get from 'lodash/get';
import { useIntl } from 'react-intl';
// Components
import { MainButton, Icon } from '..';
@ -12,10 +11,8 @@ import styles from './productItemLineStyles';
const DEALS = { '9999points': 'BEST DEAL!', '4999points': 'POPULAR!' };
const ProductItemLineView = ({ disabled, handleOnButtonPress, product, title }) => {
const intl = useIntl();
return (
<View style={styles.boostLine} key={get(product, 'productId')}>
<View style={styles.boostLine} key={get(product, 'productId').toString()}>
{_renderDeal(product)}
<View style={styles.buttonWrapper}>
<MainButton

View File

@ -1,35 +0,0 @@
import React, { PureComponent } from 'react';
import { connect } from 'react-redux';
// Component
import TransactionView from '../view/transactionView';
/**
* Props Name Description Value
* @props --> intl Function for language support function
* @props --> walletData User wallet data object
*
*/
class TransactionContainer extends PureComponent {
constructor(props) {
super(props);
this.state = {};
}
// Component Life Cycle Functions
// Component Functions
render() {
const { walletData, steemPerMVests } = this.props;
return <TransactionView walletData={walletData} steemPerMVests={steemPerMVests} />;
}
}
const mapStateToProps = state => ({
steemPerMVests: state.account.globalProps.steemPerMVests,
});
export default connect(mapStateToProps)(TransactionContainer);

View File

@ -1,5 +1,4 @@
import TransactionView from './view/transactionView';
import Transaction from './container/transactionContainer';
import Transaction from './transactionView';
export { TransactionView, Transaction };
export { Transaction };
export default Transaction;

View File

@ -0,0 +1,96 @@
/* eslint-disable react/jsx-wrap-multilines */
import React from 'react';
import { useIntl } from 'react-intl';
import { View, Text, FlatList, RefreshControl } from 'react-native';
import get from 'lodash/get';
// Utilities
import { getTimeFromNow } from '../../utils/time';
// Components
import { WalletLineItem, ListPlaceHolder } from '../basicUIElements';
import { CollapsibleCard } from '..';
import { ThemeContainer } from '../../containers';
import globalStyles from '../../globalStyles';
const TransactionView = ({ transactions, type, refreshing, setRefreshing, isLoading }) => {
const intl = useIntl();
const _renderLoading = () => {
if (isLoading) {
return <ListPlaceHolder />;
}
return (
<Text style={globalStyles.hintText}>{intl.formatMessage({ id: 'wallet.no_activity' })}</Text>
);
};
const refreshControl = () => (
<ThemeContainer>
{isDarkTheme => (
<RefreshControl
refreshing={refreshing}
onRefresh={() => setRefreshing(true)}
progressBackgroundColor="#357CE6"
tintColor={!isDarkTheme ? '#357ce6' : '#96c0ff'}
titleColor="#fff"
colors={['#fff']}
/>
)}
</ThemeContainer>
);
const _renderItem = (item, index) => {
return (
<CollapsibleCard
key={item.created.toString()}
noBorder
noContainer
titleComponent={
<WalletLineItem
key={item.created.toString()}
index={index + 1}
text={intl.formatMessage({
id: `wallet.${get(item, 'textKey')}`,
})}
description={getTimeFromNow(get(item, 'created'))}
isCircleIcon
isThin
circleIconColor="white"
isBlackText
iconName={get(item, 'icon')}
iconType={get(item, 'iconType')}
rightText={get(item, 'value', '').trim()}
/>
}
>
{(get(item, 'details') || get(item, 'memo')) && (
<WalletLineItem
key={index.toString()}
text={get(item, 'details', '')}
isBlackText
isThin
description={get(item, 'memo')}
/>
)}
</CollapsibleCard>
);
};
return (
<View style={globalStyles.listWrapper}>
<FlatList
data={transactions}
style={globalStyles.tabBarBottom}
ListEmptyComponent={_renderLoading()}
onRefresh={refreshControl}
refreshing={refreshing}
renderItem={({ item, index }) => _renderItem(item, index)}
/>
</View>
);
};
export default TransactionView;

View File

@ -1,7 +0,0 @@
import EStyleSheet from 'react-native-extended-stylesheet';
export default EStyleSheet.create({
container: {
marginTop: 8,
},
});

View File

@ -1,103 +0,0 @@
/* eslint-disable react/jsx-wrap-multilines */
import React, { PureComponent } from 'react';
import { injectIntl } from 'react-intl';
import { View } from 'react-native';
import get from 'lodash/get';
// Utilities
import { groomingTransactionData } from '../../../utils/wallet';
import { getTimeFromNow } from '../../../utils/time';
// Components
// import { FilterBar } from '../../filterBar';
import { WalletLineItem, Card } from '../../basicUIElements';
import { CollapsibleCard } from '../../collapsibleCard';
import styles from './transactionStyles';
class TransactionView extends PureComponent {
/* Props
* ------------------------------------------------
* @prop { type } name - Description....
*/
constructor(props) {
super(props);
this.state = {};
}
// Component Life Cycles
// Component Functions
// _handleOnDropdownSelect = () => {};
render() {
const {
walletData: { transactions },
intl,
intl: { formatNumber },
steemPerMVests,
} = this.props;
return (
<View style={styles.container}>
{/* this feature not implemented yet */}
{/* <FilterBar
dropdownIconName="arrow-drop-down"
options={['ALL TRANSACTIONS', 'VOTES', 'REPLIES']}
defaultText="ALL TRANSACTIONS"
onDropdownSelect={() => this._handleOnDropdownSelect()}
rightIconName="ios-lock"
iconSize={16}
/> */}
<Card>
{transactions &&
transactions.map((item, index) => {
const transactionData = groomingTransactionData(item, steemPerMVests, formatNumber);
if (transactionData.length === 0) return null;
const value = transactionData.value.trim().split(' ');
return (
<CollapsibleCard
noBorder
noContainer
key={transactionData.transDate.toString()}
titleComponent={
<WalletLineItem
key={transactionData.transDate.toString()}
index={index}
text={intl.formatMessage({
id: `wallet.${transactionData.opName}`,
})}
// description={intl.formatRelative(transactionData.transDate)}
description={getTimeFromNow(get(transactionData, 'transDate'))}
isCircleIcon
isThin
circleIconColor="white"
isBlackText
iconName={transactionData.icon}
iconType="MaterialIcons"
rightText={transactionData.value.trim()}
/>
}
>
{(get(transactionData, 'details') || get(transactionData, 'memo')) && (
<WalletLineItem
key={index.toString()}
text={get(transactionData, 'details', '')}
isBlackText
isThin
description={get(transactionData, 'memo')}
/>
)}
</CollapsibleCard>
);
})}
</Card>
</View>
);
}
}
export default injectIntl(TransactionView);

View File

@ -8,6 +8,7 @@ import get from 'lodash/get';
// Utils
import parseToken from '../../../utils/parseToken';
import { vestsToRshares } from '../../../utils/conversions';
import { getEstimatedAmount } from '../../../utils/vote';
// Components
import { Icon } from '../../icon';
@ -71,20 +72,9 @@ class UpvoteView extends Component {
if (currentAccount && Object.entries(currentAccount).length !== 0) {
const { sliderValue } = this.state;
const { fundRecentClaims, fundRewardBalance, base, quote } = globalProps;
const votingPower = currentAccount.voting_power;
const totalVests =
parseToken(get(currentAccount, 'vesting_shares')) +
parseToken(get(currentAccount, 'received_vesting_shares')) -
parseToken(get(currentAccount, 'delegated_vesting_shares'));
const votePct = sliderValue * 10000;
const rShares = vestsToRshares(totalVests, votingPower, votePct);
const estimated = (rShares / fundRecentClaims) * fundRewardBalance * (base / quote);
this.setState({
amount: estimated.toFixed(5),
amount: getEstimatedAmount(currentAccount, globalProps, sliderValue),
});
}
};

View File

@ -4,7 +4,7 @@ import { connect } from 'react-redux';
import FastImage from 'react-native-fast-image';
import styles from './userAvatarStyles';
import NavigationService from '../../../navigation/service';
import { navigate } from '../../../navigation/service';
// Constants
import ROUTES from '../../../constants/routeNames';
@ -35,7 +35,7 @@ class UserAvatarView extends Component {
const routeName = name === username ? ROUTES.TABBAR.PROFILE : ROUTES.SCREENS.PROFILE;
NavigationService.navigate({
navigate({
routeName: routeName,
params: {
username,

View File

@ -1,164 +0,0 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { injectIntl } from 'react-intl';
import { toastNotification } from '../../../redux/actions/uiAction';
// Dsteem
import { getAccount, claimRewardBalance } from '../../../providers/steem/dsteem';
// Utils
import { groomingWalletData } from '../../../utils/wallet';
import parseToken from '../../../utils/parseToken';
// Component
import WalletView from '../view/walletView';
/*
* Props Name Description Value
* @props --> currentAccountUsername description here Value Type Here
@ props --> selectedUsername
@ props --> walletData
*
*/
class WalletContainer extends Component {
constructor(props) {
super(props);
this.state = {
walletData: null,
isClaiming: false,
isRefreshing: false,
};
}
componentDidMount() {
const { selectedUser } = this.props;
this._getWalletData(selectedUser);
}
UNSAFE_componentWillReceiveProps(nextProps) {
const { selectedUser } = this.props;
if (selectedUser.name !== nextProps.selectedUser.name) {
this._getWalletData(nextProps.selectedUser);
}
}
// Components functions
_getWalletData = async selectedUser => {
const { setEstimatedWalletValue, globalProps } = this.props;
const walletData = await groomingWalletData(selectedUser, globalProps);
this.setState({ walletData });
setEstimatedWalletValue(walletData.estimatedValue);
};
_isHasUnclaimedRewards = account =>
parseToken(account.reward_steem_balance) > 0 ||
parseToken(account.reward_sbd_balance) > 0 ||
parseToken(account.reward_vesting_steem) > 0;
_claimRewardBalance = async () => {
const { currentAccount, intl, pinCode, dispatch } = this.props;
const { isClaiming } = this.state;
let isHasUnclaimedRewards;
if (isClaiming) {
return;
}
await this.setState({ isClaiming: true });
getAccount(currentAccount.name)
.then(account => {
isHasUnclaimedRewards = this._isHasUnclaimedRewards(account[0]);
if (isHasUnclaimedRewards) {
const {
reward_steem_balance: steemBal,
reward_sbd_balance: sbdBal,
reward_vesting_balance: vestingBal,
} = account[0];
return claimRewardBalance(currentAccount, pinCode, steemBal, sbdBal, vestingBal);
}
this.setState({ isClaiming: false });
})
.then(() => getAccount(currentAccount.name))
.then(account => {
this._getWalletData(account && account[0]);
if (isHasUnclaimedRewards) {
dispatch(
toastNotification(
intl.formatMessage({
id: 'alert.claim_reward_balance_ok',
}),
),
);
}
})
.then(account => {
this._getWalletData(account && account[0]);
this.setState({ isClaiming: false });
})
.catch(() => {
this.setState({ isClaiming: false });
dispatch(
toastNotification(
intl.formatMessage({
id: 'alert.fail',
}),
),
);
});
};
_handleOnWalletRefresh = () => {
const { selectedUser, dispatch, intl } = this.props;
this.setState({ isRefreshing: true });
getAccount(selectedUser.name)
.then(account => {
this._getWalletData(account && account[0]);
this.setState({ isRefreshing: false });
})
.catch(() => {
dispatch(
toastNotification(
intl.formatMessage({
id: 'alert.fail',
}),
),
);
this.setState({ isRefreshing: false });
});
};
render() {
const { currentAccount, selectedUser, handleOnScroll } = this.props;
const { walletData, isClaiming, isRefreshing } = this.state;
return (
<WalletView
currentAccountUsername={currentAccount.name}
selectedUsername={selectedUser && selectedUser.name}
walletData={walletData}
claimRewardBalance={this._claimRewardBalance}
isClaiming={isClaiming}
handleOnWalletRefresh={this._handleOnWalletRefresh}
isRefreshing={isRefreshing}
handleOnScroll={handleOnScroll}
/>
);
}
}
const mapStateToProps = state => ({
currentAccount: state.account.currentAccount,
pinCode: state.application.pin,
globalProps: state.account.globalProps,
});
export default injectIntl(connect(mapStateToProps)(WalletContainer));

View File

@ -1,5 +1,4 @@
import WalletView from './view/walletView';
import Wallet from './container/walletContainer';
import Wallet from './view/walletView';
export { WalletView, Wallet };
export { Wallet };
export default Wallet;

View File

@ -1,7 +1,7 @@
/* eslint-disable react/jsx-wrap-multilines */
import React, { PureComponent, Fragment } from 'react';
import React, { Fragment } from 'react';
import { View, Text, ScrollView, RefreshControl } from 'react-native';
import { injectIntl } from 'react-intl';
import { useIntl } from 'react-intl';
// Components
import { Icon } from '../../icon';
@ -9,28 +9,16 @@ import { MainButton } from '../../mainButton';
import { CollapsibleCard } from '../../collapsibleCard';
import { WalletDetails } from '../../walletDetails';
import { Transaction } from '../../transaction';
import { WalletDetailsPlaceHolder } from '../../basicUIElements';
import { ThemeContainer } from '../../../containers';
import { WalletDetailsPlaceHolder, Card } from '../../basicUIElements';
import { ThemeContainer, SteemWalletContainer } from '../../../containers';
// Styles
import styles from './walletStyles';
class WalletView extends PureComponent {
/* Props
* ------------------------------------------------
* @prop { type } name - Description....
*/
const WalletView = ({ setEstimatedWalletValue, selectedUser, handleOnScroll }) => {
const intl = useIntl();
constructor(props) {
super(props);
this.state = {};
}
// Component Life Cycles
// Component Functions
_getUnclaimedText = (walletData, isPreview) => (
const _getUnclaimedText = (walletData, isPreview) => (
<Text style={[isPreview ? styles.unclaimedTextPreview : styles.unclaimedText]}>
{walletData.rewardSteemBalance
? `${Math.round(walletData.rewardSteemBalance * 1000) / 1000} STEEM`
@ -44,93 +32,102 @@ class WalletView extends PureComponent {
</Text>
);
render() {
const {
isClaiming,
claimRewardBalance,
currentAccountUsername,
handleOnWalletRefresh,
intl,
isRefreshing,
selectedUsername,
walletData,
handleOnScroll,
} = this.props;
return (
<ThemeContainer>
{isDarkTheme => (
<ScrollView
onScroll={handleOnScroll && handleOnScroll}
style={styles.scrollView}
refreshControl={
<RefreshControl
refreshing={isRefreshing}
onRefresh={handleOnWalletRefresh}
progressBackgroundColor="#357CE6"
tintColor={!isDarkTheme ? '#357ce6' : '#96c0ff'}
titleColor="#fff"
colors={['#fff']}
/>
}
contentContainerStyle={styles.scrollContentContainer}
>
{!walletData ? (
<Fragment>
<WalletDetailsPlaceHolder />
</Fragment>
) : (
<Fragment>
{walletData.hasUnclaimedRewards && (
return (
<SteemWalletContainer
setEstimatedWalletValue={setEstimatedWalletValue}
selectedUser={selectedUser}
handleOnScroll={handleOnScroll}
>
{({
isClaiming,
claimRewardBalance,
currentAccountUsername,
handleOnWalletRefresh,
refreshing,
selectedUsername,
walletData,
userActivities,
}) => (
<ThemeContainer>
{isDarkTheme => (
<ScrollView
onScroll={handleOnScroll && handleOnScroll}
style={styles.scrollView}
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={handleOnWalletRefresh}
progressBackgroundColor="#357CE6"
tintColor={!isDarkTheme ? '#357ce6' : '#96c0ff'}
titleColor="#fff"
colors={['#fff']}
/>
}
contentContainerStyle={styles.scrollContentContainer}
>
{!walletData ? (
<Fragment>
<WalletDetailsPlaceHolder />
</Fragment>
) : (
<Fragment>
{walletData.hasUnclaimedRewards && (
<CollapsibleCard
titleColor="#788187"
isBoldTitle
defaultTitle={intl.formatMessage({
id: 'profile.unclaimed_rewards',
})}
expanded
>
{currentAccountUsername === selectedUsername ? (
<MainButton
isLoading={isClaiming}
isDisable={isClaiming}
style={styles.mainButton}
height={50}
onPress={() => claimRewardBalance()}
>
<View style={styles.mainButtonWrapper}>
{_getUnclaimedText(walletData)}
<View style={styles.mainIconWrapper}>
<Icon name="add" iconType="MaterialIcons" color="#357ce6" size={23} />
</View>
</View>
</MainButton>
) : (
_getUnclaimedText(walletData, true)
)}
</CollapsibleCard>
)}
<CollapsibleCard
titleColor="#788187"
isBoldTitle
defaultTitle={intl.formatMessage({
id: 'profile.unclaimed_rewards',
title={intl.formatMessage({
id: 'profile.wallet_details',
})}
expanded
>
{currentAccountUsername === selectedUsername ? (
<MainButton
isLoading={isClaiming}
isDisable={isClaiming}
style={styles.mainButton}
height={50}
onPress={() => claimRewardBalance()}
>
<View style={styles.mainButtonWrapper}>
{this._getUnclaimedText(walletData)}
<View style={styles.mainIconWrapper}>
<Icon name="add" iconType="MaterialIcons" color="#357ce6" size={23} />
</View>
</View>
</MainButton>
) : (
this._getUnclaimedText(walletData, true)
)}
<WalletDetails
intl={intl}
walletData={walletData}
isShowDropdowns={currentAccountUsername === selectedUsername}
/>
</CollapsibleCard>
)}
<CollapsibleCard
titleColor="#788187"
title={intl.formatMessage({
id: 'profile.wallet_details',
})}
expanded
>
<WalletDetails
intl={intl}
walletData={walletData}
isShowDropdowns={currentAccountUsername === selectedUsername}
/>
</CollapsibleCard>
<Transaction walletData={walletData} />
</Fragment>
)}
</ScrollView>
)}
</ThemeContainer>
);
}
}
<Card>
<Transaction
refreshing={refreshing}
type="wallet"
transactions={userActivities}
/>
</Card>
</Fragment>
)}
</ScrollView>
)}
</ThemeContainer>
)}
</SteemWalletContainer>
);
};
export default injectIntl(WalletView);
export default WalletView;

View File

@ -1,4 +1,4 @@
import React, { PureComponent } from 'react';
import React from 'react';
import { View } from 'react-native';
// Components
@ -10,130 +10,120 @@ import { vestsToSp } from '../../../utils/conversions';
// Styles
import styles from './walletDetailsStyles';
class WalletDetailsView extends PureComponent {
// Component Life Cycles
// Component Functions
render() {
const { walletData, intl, navigate, isShowDropdowns } = this.props;
const steemDropdown = ['transfer_token', 'transfer_to_saving', 'powerUp'];
const sbdDropdown = ['transfer_token', 'transfer_to_saving'];
const savingSteemDropdown = ['withdraw_steem'];
const savingSbdDropdown = ['withdraw_sbd'];
const steemPowerDropdown = ['delegate', 'power_down'];
return (
<View style={styles.container}>
<WalletLineItem
text="Steem"
isBlackText
iconName="ios-information-circle-outline"
rightText={`${Math.round(walletData.balance * 1000) / 1000} STEEM`}
isBoldText
isHasdropdown={isShowDropdowns}
dropdownOptions={steemDropdown.map(item =>
intl.formatMessage({ id: `transfer.${item}` }),
)}
onDropdownSelect={index => navigate(steemDropdown[index], 'STEEM')}
/>
<GrayWrapper isGray>
<WalletLineItem
text={intl.formatMessage({
id: 'profile.steem_power',
})}
isBlackText
iconName="ios-information-circle-outline"
rightText={`${Math.round(
vestsToSp(walletData.vestingShares, walletData.steemPerMVests) * 1000,
) / 1000} SP`}
isBoldText
isHasdropdown={isShowDropdowns}
dropdownOptions={steemPowerDropdown.map(item =>
intl.formatMessage({ id: `transfer.${item}` }),
)}
onDropdownSelect={a => navigate(steemPowerDropdown[a], 'STEEM_POWER')}
/>
{walletData.vestingSharesDelegated > 0 && (
<WalletLineItem
rightText={`- ${Math.round(
vestsToSp(walletData.vestingSharesDelegated, walletData.steemPerMVests) * 1000,
) / 1000} SP`}
style={styles.walletLineDetail}
/>
)}
{walletData.vestingSharesReceived > 0 && (
<WalletLineItem
rightText={`+ ${Math.round(
vestsToSp(walletData.vestingSharesReceived, walletData.steemPerMVests) * 1000,
) / 1000} SP`}
style={styles.walletLineDetail}
/>
)}
{(walletData.vestingSharesDelegated > 0 || walletData.vestingSharesReceived > 0) && (
<WalletLineItem
rightText={`= ${Math.round(
vestsToSp(walletData.vestingSharesTotal, walletData.steemPerMVests) * 1000,
) / 1000} SP`}
rightTextColor="#357ce6"
style={styles.walletLineDetail}
/>
)}
</GrayWrapper>
const WalletDetailsView = ({ walletData, intl, navigate, isShowDropdowns }) => {
const steemDropdown = ['transfer_token', 'transfer_to_saving', 'powerUp'];
const sbdDropdown = ['transfer_token', 'transfer_to_saving'];
const savingSteemDropdown = ['withdraw_steem'];
const savingSbdDropdown = ['withdraw_sbd'];
const steemPowerDropdown = ['delegate', 'power_down'];
return (
<View style={styles.container}>
<WalletLineItem
text="Steem"
isBlackText
iconName="ios-information-circle-outline"
rightText={`${Math.round(walletData.balance * 1000) / 1000} STEEM`}
isBoldText
isHasdropdown={isShowDropdowns}
dropdownOptions={steemDropdown.map(item => intl.formatMessage({ id: `transfer.${item}` }))}
onDropdownSelect={index => navigate(steemDropdown[index], 'STEEM')}
/>
<GrayWrapper isGray>
<WalletLineItem
text={intl.formatMessage({
id: 'profile.steem_dollars',
id: 'profile.steem_power',
})}
isBlackText
iconName="ios-information-circle-outline"
rightText={`$${Math.round(walletData.sbdBalance * 1000) / 1000}`}
rightText={`${Math.round(
vestsToSp(walletData.vestingShares, walletData.steemPerMVests) * 1000,
) / 1000} SP`}
isBoldText
isHasdropdown={isShowDropdowns}
dropdownOptions={sbdDropdown.map(item => intl.formatMessage({ id: `transfer.${item}` }))}
onDropdownSelect={a => navigate(sbdDropdown[a], 'SBD')}
dropdownOptions={steemPowerDropdown.map(item =>
intl.formatMessage({ id: `transfer.${item}` }),
)}
onDropdownSelect={a => navigate(steemPowerDropdown[a], 'STEEM_POWER')}
/>
<GrayWrapper isGray>
{walletData.vestingSharesDelegated > 0 && (
<WalletLineItem
text={intl.formatMessage({
id: 'profile.savings',
})}
isBlackText
iconName="ios-information-circle-outline"
rightText={`${Math.round(walletData.savingBalance * 1000) / 1000} STEEM`}
isBoldText
isHasdropdown={isShowDropdowns}
dropdownOptions={savingSteemDropdown.map(item =>
intl.formatMessage({ id: `transfer.${item}` }),
)}
onDropdownSelect={a => navigate(savingSteemDropdown[a], 'SAVING_STEEM')}
/>
<WalletLineItem
rightText={`$${Math.round(walletData.savingBalanceSbd * 1000) / 1000}`}
rightText={`- ${Math.round(
vestsToSp(walletData.vestingSharesDelegated, walletData.steemPerMVests) * 1000,
) / 1000} SP`}
style={styles.walletLineDetail}
isHasdropdown={isShowDropdowns}
dropdownOptions={savingSbdDropdown.map(item =>
intl.formatMessage({ id: `transfer.${item}` }),
)}
onDropdownSelect={a => navigate(savingSbdDropdown[a], 'SAVING_SBD')}
/>
</GrayWrapper>
{walletData.showPowerDown && (
<WalletLineItem
text={`${intl.formatMessage({
id: 'profile.next_power_text',
})} ${walletData.nextVestingWithdrawal} ${intl.formatMessage({
id: 'profile.day',
})}`}
textColor="#788187"
iconName="ios-information-circle-outline"
/>
)}
</View>
);
}
}
{walletData.vestingSharesReceived > 0 && (
<WalletLineItem
rightText={`+ ${Math.round(
vestsToSp(walletData.vestingSharesReceived, walletData.steemPerMVests) * 1000,
) / 1000} SP`}
style={styles.walletLineDetail}
/>
)}
{(walletData.vestingSharesDelegated > 0 || walletData.vestingSharesReceived > 0) && (
<WalletLineItem
rightText={`= ${Math.round(
vestsToSp(walletData.vestingSharesTotal, walletData.steemPerMVests) * 1000,
) / 1000} SP`}
rightTextColor="#357ce6"
style={styles.walletLineDetail}
/>
)}
</GrayWrapper>
<WalletLineItem
text={intl.formatMessage({
id: 'profile.steem_dollars',
})}
isBlackText
iconName="ios-information-circle-outline"
rightText={`$${Math.round(walletData.sbdBalance * 1000) / 1000}`}
isBoldText
isHasdropdown={isShowDropdowns}
dropdownOptions={sbdDropdown.map(item => intl.formatMessage({ id: `transfer.${item}` }))}
onDropdownSelect={a => navigate(sbdDropdown[a], 'SBD')}
/>
<GrayWrapper isGray>
<WalletLineItem
text={intl.formatMessage({
id: 'profile.savings',
})}
isBlackText
iconName="ios-information-circle-outline"
rightText={`${Math.round(walletData.savingBalance * 1000) / 1000} STEEM`}
isBoldText
isHasdropdown={isShowDropdowns}
dropdownOptions={savingSteemDropdown.map(item =>
intl.formatMessage({ id: `transfer.${item}` }),
)}
onDropdownSelect={a => navigate(savingSteemDropdown[a], 'SAVING_STEEM')}
/>
<WalletLineItem
rightText={`$${Math.round(walletData.savingBalanceSbd * 1000) / 1000}`}
style={styles.walletLineDetail}
isHasdropdown={isShowDropdowns}
dropdownOptions={savingSbdDropdown.map(item =>
intl.formatMessage({ id: `transfer.${item}` }),
)}
onDropdownSelect={a => navigate(savingSbdDropdown[a], 'SAVING_SBD')}
/>
</GrayWrapper>
{walletData.showPowerDown && (
<WalletLineItem
text={`${intl.formatMessage({
id: 'profile.next_power_text',
})} ${walletData.nextVestingWithdrawal} ${intl.formatMessage({
id: 'profile.day',
})}`}
textColor="#788187"
iconName="ios-information-circle-outline"
/>
)}
</View>
);
};
export default WalletDetailsView;

View File

@ -0,0 +1,3 @@
import WalletHeader from './view/walletHeaderView';
export { WalletHeader };

View File

@ -0,0 +1,80 @@
import EStyleSheet from 'react-native-extended-stylesheet';
export default EStyleSheet.create({
balanceText: {
color: '$primaryBlue',
fontSize: 26,
marginTop: 24,
justifyContent: 'center',
alignSelf: 'center',
fontWeight: 'bold',
},
balanceWrapper: {
flexDirection: 'column',
justifyContent: 'center',
alignSelf: 'center',
},
dropdownRowText: {
fontSize: 14,
color: '$primaryDarkGray',
textAlign: 'center',
},
dropdownRowStyle: {
marginLeft: 0,
},
dropdownButtonStyle: {
justifyContent: 'center',
alignSelf: 'center',
position: 'absolute',
right: -40,
top: 20,
},
subText: {
color: '$darkIconColor',
fontSize: 8,
justifyContent: 'center',
alignSelf: 'center',
marginTop: 5,
},
icon: {
fontSize: 24,
color: '$primaryDarkText',
},
mainButton: {
marginVertical: 8,
alignSelf: 'center',
paddingHorizontal: 24,
},
mainButtonWrapper: {
flexDirection: 'row',
},
unclaimedText: {
color: '$pureWhite',
fontSize: 14,
fontWeight: 'bold',
alignSelf: 'center',
},
mainIconWrapper: {
backgroundColor: '$pureWhite',
justifyContent: 'center',
alignSelf: 'center',
alignItems: 'center',
borderRadius: 20,
marginLeft: 20,
width: 24,
height: 24,
},
scrollContainer: {
flex: 0.935,
width: '100%',
backgroundColor: '$primaryBackgroundColor',
},
scrollContentContainer: {
paddingBottom: 60,
},
valueDescriptions: {
marginLeft: -30,
marginTop: 20,
marginBottom: -10,
},
});

View File

@ -0,0 +1,117 @@
/* eslint-disable react/jsx-wrap-multilines */
import React, { useRef, Fragment, useEffect } from 'react';
import { Text, View } from 'react-native';
import { useIntl } from 'react-intl';
import { withNavigation } from 'react-navigation';
import get from 'lodash/get';
// Components
import { Icon, MainButton, DropdownButton, HorizontalIconList, WalletLineItem } from '../..';
// Constants
import POINTS, { POINTS_KEYS } from '../../../constants/options/points';
import { default as ROUTES } from '../../../constants/routeNames';
// Styles
import styles from './walletHeaderStyles';
const WalletHeaderView = ({
claim,
isClaiming,
handleOnDropdownSelected,
navigation,
unclaimedBalance,
userBalance,
type = '',
componentDidUpdate,
showIconList,
currentIndex,
valueDescriptions,
showBuyButton,
index,
}) => {
const intl = useIntl();
const dropdownRef = useRef();
useEffect(() => {
if (index === currentIndex) {
componentDidUpdate();
}
}, [componentDidUpdate, currentIndex, index]);
const _getBalanceItem = (balance, options, key) =>
balance !== undefined && (
<View style={styles.balanceWrapper}>
<Text style={styles.balanceText}>{balance}</Text>
<DropdownButton
dropdownRowWrapper={styles.dropdownRowStyle}
dropdownRef={dropdownRef}
isHasChildIcon
iconName="arrow-drop-down"
options={options.map(itemKey => intl.formatMessage({ id: `wallet.${itemKey}` }))}
noHighlight
dropdownButtonStyle={styles.dropdownButtonStyle}
onSelect={selectedIndex => handleOnDropdownSelected(options[selectedIndex])}
rowTextStyle={styles.dropdownRowText}
dropdownStyle={styles.dropdownStyle}
iconStyle={styles.dropdownIconStyle}
/>
<Text style={styles.subText}>{intl.formatMessage({ id: `wallet.${key}.title` })}</Text>
</View>
);
return (
<Fragment>
<View style={styles.scrollContainer} contentContainerStyle={styles.scrollContentContainer}>
{userBalance.map(item =>
_getBalanceItem(
get(item, 'balance', 0),
get(item, 'options', []),
get(item, 'nameKey', 'estm'),
),
)}
{showBuyButton && (
<MainButton
isLoading={isClaiming}
isDisable={isClaiming}
style={styles.mainButton}
height={50}
onPress={() => (unclaimedBalance ? claim() : navigation.navigate(ROUTES.SCREENS.BOOST))}
>
<View style={styles.mainButtonWrapper}>
<Text style={styles.unclaimedText}>
{unclaimedBalance
? unclaimedBalance
: intl.formatMessage({ id: `wallet.${type}.buy` })}
</Text>
<View style={styles.mainIconWrapper}>
<Icon name="add" iconType="MaterialIcons" color="#357ce6" size={23} />
</View>
</View>
</MainButton>
)}
{valueDescriptions &&
valueDescriptions.map(item => (
<WalletLineItem
fitContent
style={styles.valueDescriptions}
text={intl.formatMessage({ id: `wallet.${get(item, 'textKey')}` })}
hintDescription={
get(item, 'subTextKey') &&
intl.formatMessage({ id: `wallet.${get(item, 'subTextKey')}` })
}
rightText={get(item, 'value')}
hintIconName={get(item, 'subTextKey') && 'ios-information-circle-outline'}
isBlackText
isThin
/>
))}
{showIconList && <HorizontalIconList options={POINTS} optionsKeys={POINTS_KEYS} />}
</View>
</Fragment>
);
};
export default withNavigation(WalletHeaderView);

View File

@ -8,36 +8,8 @@
"transfer_to_vesting": "Transfer To Vesting",
"transfer_from_savings": "Transfer From Savings",
"withdraw_vesting": "Power Down",
"fill_order": "Fill Order"
},
"notification": {
"vote": "likes your post",
"unvote": "unvoted your post",
"reply": "replied to your post",
"mention": "mentioned you",
"follow": "followed you",
"unfollow": "unfollowed you",
"ignore": "ignored you",
"reblog": "reblogged your post",
"transfer": "transferred",
"notification": "Notifications",
"leaderboard": "Leaderboard",
"epoint": "Points",
"leaderboard_title": "Top Users",
"recent": "Recent",
"yesterday": "Yesterday",
"this_week": "This Week",
"this_month": "This Month",
"older_then": "Older Than A Month"
},
"leaderboard": {
"daily": "Daily",
"weekly": "Weekly",
"monthly": "Monthly"
},
"points": {
"fill_order": "Fill Order",
"post": "Post",
"esteemPoints": "eSteem Points",
"comment": "Comment",
"checkin": "Check-in",
"vote": "Vote",
@ -70,7 +42,68 @@
"dropdown_promote": "Promote",
"dropdown_boost": "Boost",
"from": "From",
"to": "To"
"to": "To",
"estimated_value_desc": "Determined by purchase value",
"estimated_value": "Estimated value",
"estimated_amount": "Vote value",
"amount_information": "Drag the slider to adjust the amount",
"amount": "Amount",
"memo": "Memo",
"information": "Are you sure to transfer funds?",
"amount_desc": "Balance",
"memo_desc": "This memo is public",
"to_placeholder": "Username",
"memo_placeholder": "Enter your notes here",
"transfer_token": "Transfer",
"points": "Gift ESTM to someone",
"transfer_to_saving": "Transfer To Saving",
"powerUp": "Power Up",
"withdraw_to_saving": "Withdraw To Saving",
"steemconnect_title": "Steemconnect Transfer",
"next": "NEXT",
"delegate": "Delegate",
"power_down": "Power Down",
"withdraw_steem": "Withdraw Steem",
"withdraw_sbd": "Withdraw Steem Dollar",
"estm": {
"title": "eSteem Points",
"buy": "GET ESTM"
},
"saving": {
"title": "STEEM Savings"
},
"steem": {
"title": "STEEM",
"buy": "GET STEEM"
},
"steem_power": {
"title": "STEEM POWER"
}
},
"notification": {
"vote": "likes your post",
"unvote": "unvoted your post",
"reply": "replied to your post",
"mention": "mentioned you",
"follow": "followed you",
"unfollow": "unfollowed you",
"ignore": "ignored you",
"reblog": "reblogged your post",
"transfer": "transferred",
"notification": "Notifications",
"leaderboard": "Leaderboard",
"epoint": "Points",
"leaderboard_title": "Top Users",
"recent": "Recent",
"yesterday": "Yesterday",
"this_week": "This Week",
"this_month": "This Month",
"older_then": "Older Than A Month"
},
"leaderboard": {
"daily": "Daily",
"weekly": "Weekly",
"monthly": "Monthly"
},
"messages": {
"comingsoon": "Messages feature is coming soon!"
@ -321,6 +354,13 @@
"powerUp": "Power Up",
"withdraw_to_saving": "Withdraw To Saving",
"steemconnect_title": "Steemconnect Transfer",
"estimated_weekly": "Estimated Weekly",
"destination_accounts": "Destination Accounts",
"stop_information": "Are you sure want to stop?",
"percent": "Percent",
"auto_vests": "Auto Vests",
"save": "SAVE",
"percent_information": "Percent info",
"next": "NEXT",
"delegate": "Delegate",
"power_down": "Power Down",

View File

@ -1,81 +1,81 @@
export default {
999: {
icon: 'compare-arrows',
textKey: 'points.incoming_transfer_title',
nameKey: 'points.incoming_transfer',
descriptionKey: 'points.incoming_transfer_description',
textKey: 'incoming_transfer_title',
nameKey: 'wallet.incoming_transfer',
descriptionKey: 'wallet.incoming_transfer_description',
iconType: 'MaterialIcons',
point: 0.1,
},
998: {
icon: 'compare-arrows',
textKey: 'points.outgoing_transfer_title',
nameKey: 'points.outgoing_transfer',
descriptionKey: 'points.outgoing_transfer_description',
textKey: 'outgoing_transfer_title',
nameKey: 'wallet.outgoing_transfer',
descriptionKey: 'wallet.outgoing_transfer_description',
iconType: 'MaterialIcons',
point: 0.1,
},
150: {
icon: 'local-activity',
textKey: 'points.delegation_title',
nameKey: 'points.delegation',
descriptionKey: 'points.delegation_desc',
textKey: 'delegation_title',
nameKey: 'wallet.delegation',
descriptionKey: 'wallet.delegation_desc',
iconType: 'MaterialIcons',
point: 1,
},
100: {
icon: 'pencil',
textKey: 'points.post_title',
nameKey: 'points.post',
descriptionKey: 'points.post_desc',
textKey: 'post_title',
nameKey: 'wallet.post',
descriptionKey: 'wallet.post_desc',
iconType: 'MaterialCommunityIcons',
point: 15,
},
110: {
icon: 'comment-text-outline',
textKey: 'points.comment_title',
nameKey: 'points.comment',
descriptionKey: 'points.comment_desc',
textKey: 'comment_title',
nameKey: 'wallet.comment',
descriptionKey: 'wallet.comment_desc',
iconType: 'MaterialCommunityIcons',
point: 5,
},
120: {
icon: 'upcircleo',
textKey: 'points.vote_title',
nameKey: 'points.vote',
descriptionKey: 'points.vote_desc',
textKey: 'vote_title',
nameKey: 'wallet.vote',
descriptionKey: 'wallet.vote_desc',
iconType: 'AntDesign',
point: 0.3,
},
130: {
icon: 'repeat',
textKey: 'points.reblog_title',
nameKey: 'points.reblog',
descriptionKey: 'points.reblog_desc',
textKey: 'reblog_title',
nameKey: 'wallet.reblog',
descriptionKey: 'wallet.reblog_desc',
iconType: 'MaterialIcons',
point: 1,
},
10: {
icon: 'favorite-border',
textKey: 'points.checkin_title',
nameKey: 'points.checkin',
descriptionKey: 'points.checkin_desc',
textKey: 'checkin_title',
nameKey: 'wallet.checkin',
descriptionKey: 'wallet.checkin_desc',
iconType: 'MaterialIcons',
point: 0.25,
},
20: {
icon: 'person-outline',
textKey: 'points.login_title',
nameKey: 'points.login',
descriptionKey: 'points.login_desc',
textKey: 'login_title',
nameKey: 'wallet.login',
descriptionKey: 'wallet.login_desc',
iconType: 'MaterialIcons',
point: 100,
},
30: {
icon: 'check-all',
textKey: 'points.checkin_extra_title',
nameKey: 'points.checkin_extra',
descriptionKey: 'points.checkin_extra_desc',
textKey: 'checkin_extra_title',
nameKey: 'wallet.checkin_extra',
descriptionKey: 'wallet.checkin_extra_desc',
iconType: 'MaterialCommunityIcons',
point: 10,
},

View File

@ -31,7 +31,7 @@ export default {
TABBAR: {
FEED: `Feed${TABBAR_SUFFIX}`,
NOTIFICATION: `Notification${TABBAR_SUFFIX}`,
POINTS: `Points${TABBAR_SUFFIX}`,
WALLET: `Wallet${TABBAR_SUFFIX}`,
POST_BUTTON: `PostButton${TABBAR_SUFFIX}`,
PROFILE: `Profile${TABBAR_SUFFIX}`,
},

View File

@ -1,21 +1,25 @@
import AccountContainer from './accountContainer';
import InAppPurchaseContainer from './inAppPurchaseContainer';
import LoggedInContainer from './loggedInContainer';
import PointsContainer from './pointsContainer';
import ProfileContainer from './profileContainer';
import ProfileEditContainer from './profileEditContainer';
import RedeemContainer from './redeemContainer';
import SpinGameContainer from './spinGameContainer';
import TransferContainer from './transferContainer';
import SteemWalletContainer from './steemWalletContainer';
import ThemeContainer from './themeContainer';
import TransferContainer from './transferContainer';
export {
AccountContainer,
InAppPurchaseContainer,
LoggedInContainer,
PointsContainer,
ProfileContainer,
ProfileEditContainer,
RedeemContainer,
SpinGameContainer,
TransferContainer,
SteemWalletContainer,
ThemeContainer,
TransferContainer,
};

View File

@ -0,0 +1,40 @@
/* eslint-disable no-unused-vars */
import React from 'react';
import { useIntl } from 'react-intl';
import { connect } from 'react-redux';
import ROUTES from '../constants/routeNames';
import { navigate } from '../navigation/service';
import { NoPost } from '../components';
const LoggedInContainer = ({ isLoggedIn, isLoginDone, children }) => {
const intl = useIntl();
if (!isLoggedIn) {
return (
<NoPost
style={{ flex: 1 }}
isButtonText
defaultText={intl.formatMessage({
id: 'profile.login_to_see',
})}
handleOnButtonPress={() => navigate(ROUTES.SCREENS.LOGIN)}
/>
);
}
return (
children &&
children({
isLoggedIn,
isLoginDone,
})
);
};
const mapStateToProps = state => ({
isLoggedIn: state.application.isLoggedIn,
isLoginDone: state.application.isLoginDone,
});
export default connect(mapStateToProps)(LoggedInContainer);

View File

@ -1,8 +1,8 @@
import { Component } from 'react';
import React, { useState, useEffect, useCallback } from 'react';
import { Alert } from 'react-native';
import { connect } from 'react-redux';
import { connect, useDispatch } from 'react-redux';
import get from 'lodash/get';
import { injectIntl } from 'react-intl';
import { useIntl } from 'react-intl';
import { withNavigation } from 'react-navigation';
// Services and Actions
@ -18,63 +18,67 @@ import POINTS from '../constants/options/points';
// Constants
import ROUTES from '../constants/routeNames';
// Utils
import { groomingPointsTransactionData } from '../utils/wallet';
/*
* Props Name Description Value
*@props --> props name here description here Value Type Here
*
*/
class PointsContainer extends Component {
constructor(props) {
super(props);
this.state = {
userPoints: {},
userActivities: null,
refreshing: false,
isClaiming: false,
isLoading: true,
navigationParams: {},
};
}
// Component Life Cycle Functions
componentDidMount() {
const { username, isConnected, navigation } = this.props;
const PointsContainer = ({
username,
isConnected,
navigation,
children,
accounts,
currentAccount,
user,
activeBottomTab,
isPinCodeOpen,
globalProps,
pinCode,
}) => {
const [userPoints, setUserPoints] = useState({});
const [userActivities, setUserActivities] = useState(null);
const [refreshing, setRefreshing] = useState(false);
const [isClaiming, setIsClaiming] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const [navigationParams, setNavigationParams] = useState({});
const [balance, setBalance] = useState(0);
const intl = useIntl();
const dispatch = useDispatch();
const fetchInterval = useCallback(() => setInterval(_fetchUserPointActivities, 6 * 60 * 1000), [
_fetchUserPointActivities,
]);
useEffect(() => {
if (isConnected) {
this._fetchUserPointActivities(username);
this.fetchInterval = setInterval(this._fetchUserPointActivities, 6 * 60 * 1000);
_fetchUserPointActivities(username);
fetchInterval();
}
if (get(navigation, 'state.params', null)) {
const navigationParams = get(navigation, 'state.params');
const _navigationParams = get(navigation, 'state.params');
this.setState({ navigationParams });
setNavigationParams(_navigationParams);
}
}
}, [_fetchUserPointActivities, fetchInterval, isConnected, navigation, username]);
UNSAFE_componentWillReceiveProps(nextProps) {
const { username } = this.props;
const _username = get(nextProps, 'username');
if (
nextProps.isConnected &&
((nextProps.activeBottomTab === ROUTES.TABBAR.POINTS && _username) ||
(_username !== username && _username))
) {
this._fetchUserPointActivities(_username);
useEffect(() => {
if (isConnected && activeBottomTab === ROUTES.TABBAR.WALLET && username) {
_fetchUserPointActivities(username);
}
}
}, [isConnected, username, _fetchUserPointActivities, activeBottomTab]);
componentWillUnmount() {
clearInterval(this.fetchInterval);
}
useEffect(() => {
return clearInterval(fetchInterval);
});
// Component Functions
_handleOnDropdownSelected = index => {
const { dispatch, isPinCodeOpen, navigation } = this.props;
const { balance } = this.state;
const _handleOnDropdownSelected = index => {
let navigateTo;
let navigateParams;
@ -123,35 +127,36 @@ class PointsContainer extends Component {
}
};
_groomUserActivities = userActivities =>
userActivities.map(item => ({
...item,
icon: get(POINTS[get(item, 'type')], 'icon'),
iconType: get(POINTS[get(item, 'type')], 'iconType'),
textKey: get(POINTS[get(item, 'type')], 'textKey'),
}));
const _groomUserActivities = _userActivities =>
_userActivities.map(item =>
groomingPointsTransactionData({
...item,
icon: get(POINTS[get(item, 'type')], 'icon'),
iconType: get(POINTS[get(item, 'type')], 'iconType'),
textKey: get(POINTS[get(item, 'type')], 'textKey'),
}),
);
_fetchUserPointActivities = async username => {
if (!username) {
const _fetchUserPointActivities = useCallback(async _username => {
if (!_username) {
return;
}
this.setState({ refreshing: true });
setRefreshing(true);
await getUser(username)
await getUser(_username)
.then(userPoints => {
const balance = Math.round(get(userPoints, 'points') * 1000) / 1000;
this.setState({ userPoints, balance });
const _balance = Math.round(get(userPoints, 'points') * 1000) / 1000;
setUserPoints(userPoints);
setBalance(_balance);
})
.catch(err => {
Alert.alert(get(err, 'message', 'Error'));
});
await getUserPoints(username)
await getUserPoints(_username)
.then(userActivities => {
if (Object.entries(userActivities).length !== 0) {
this.setState({
userActivities: this._groomUserActivities(userActivities),
});
setUserActivities(_groomUserActivities(userActivities));
}
})
.catch(err => {
@ -160,17 +165,15 @@ class PointsContainer extends Component {
}
});
this.setState({
refreshing: false,
isLoading: false,
});
};
setRefreshing(false);
setIsLoading(false);
}, []);
_getUserBalance = async username => {
await getUser(username)
.then(userPoints => {
const balance = Math.round(get(userPoints, 'points') * 1000) / 1000;
return balance;
const _getUserBalance = async _username => {
await getUser(_username)
.then(_userPoints => {
const _balance = Math.round(get(_userPoints, 'points') * 1000) / 1000;
return _balance;
})
.catch(err => {
if (err) {
@ -179,14 +182,12 @@ class PointsContainer extends Component {
});
};
_claimPoints = async () => {
const { username } = this.props;
this.setState({ isClaiming: true });
const _claimPoints = async () => {
setIsClaiming(true);
await claim(username)
.then(() => {
this._fetchUserPointActivities(username);
_fetchUserPointActivities(username);
})
.catch(error => {
if (error) {
@ -199,75 +200,62 @@ class PointsContainer extends Component {
}
});
this.setState({ isClaiming: false });
setIsClaiming(false);
};
_boost = async (point, permlink, author, user) => {
const { currentAccount, pinCode, dispatch, intl, navigation } = this.props;
this.setState({ isLoading: true });
const _boost = async (point, permlink, author, _user) => {
setIsLoading(true);
await boost(user || currentAccount, pinCode, point, permlink, author)
await boost(_user || currentAccount, pinCode, point, permlink, author)
.then(() => {
this.setState({ isLoading: false });
setIsLoading(false);
navigation.goBack();
dispatch(toastNotification(intl.formatMessage({ id: 'alert.successful' })));
})
.catch(error => {
if (error) {
this.setState({ isLoading: false });
setIsLoading(false);
dispatch(toastNotification(intl.formatMessage({ id: 'alert.fail' })));
}
});
};
_getESTMPrice = points => {
const { globalProps } = this.props;
const _getESTMPrice = points => {
const { base, quote } = globalProps;
return points * 0.01 * (base / quote);
};
render() {
const {
return (
children &&
children({
accounts,
balance,
boost: _boost,
claim: _claimPoints,
currentAccount,
currentAccountName: currentAccount.name,
fetchUserActivity: _fetchUserPointActivities,
getAccount,
getESTMPrice: _getESTMPrice,
getUserBalance: _getUserBalance,
getUserDataWithUsername,
handleOnDropdownSelected: _handleOnDropdownSelected,
isClaiming,
isLoading,
navigationParams,
refreshing,
userActivities,
userPoints,
} = this.state;
const { children, accounts, currentAccount } = this.props;
return (
children &&
children({
accounts,
balance,
boost: this._boost,
claimPoints: this._claimPoints,
currentAccount,
currentAccountName: currentAccount.name,
fetchUserActivity: this._fetchUserPointActivities,
getAccount,
getESTMPrice: this._getESTMPrice,
getUserBalance: this._getUserBalance,
getUserDataWithUsername,
handleOnDropdownSelected: this._handleOnDropdownSelected,
handleOnPressTransfer: this._handleOnPressTransfer,
isClaiming,
isLoading,
navigationParams,
refreshing,
userActivities,
userPoints,
redeemType: get(navigationParams, 'redeemType'),
})
);
}
}
redeemType: get(navigationParams, 'redeemType'),
user,
dropdownOptions: ['dropdown_transfer', 'dropdown_promote', 'dropdown_boost'],
})
);
};
const mapStateToProps = state => ({
user: state.account.currentAccount,
username: state.account.currentAccount.name,
activeBottomTab: state.ui.activeBottomTab,
isConnected: state.application.isConnected,
@ -278,4 +266,4 @@ const mapStateToProps = state => ({
globalProps: state.account.globalProps,
});
export default withNavigation(connect(mapStateToProps)(injectIntl(PointsContainer)));
export default withNavigation(connect(mapStateToProps)(PointsContainer));

View File

@ -0,0 +1,275 @@
import React, { useState, useEffect, useCallback } from 'react';
import { connect } from 'react-redux';
import { useIntl } from 'react-intl';
import { useDispatch } from 'react-redux';
import get from 'lodash/get';
import { toastNotification } from '../redux/actions/uiAction';
// Dsteem
import { getAccount, claimRewardBalance } from '../providers/steem/dsteem';
// Actions
import { openPinCodeModal } from '../redux/actions/applicationActions';
// Utils
import { groomingWalletData, groomingTransactionData } from '../utils/wallet';
import parseToken from '../utils/parseToken';
import { vestsToSp } from '../utils/conversions';
import { navigate } from '../navigation/service';
import { getEstimatedAmount } from '../utils/vote';
// Constants
import ROUTES from '../constants/routeNames';
const STEEM_DROPDOWN = ['transfer_token', 'transfer_to_saving', 'powerUp'];
const SBD_DROPDOWN = ['transfer_token', 'transfer_to_saving'];
const SAVING_STEEM_DROPDOWN = ['withdraw_steem'];
const SAVING_SBD_DROPDOWN = ['withdraw_sbd'];
const STEEM_POWER_DROPDOWN = ['delegate', 'power_down'];
const WalletContainer = ({
children,
currentAccount,
globalProps,
handleOnScroll,
pinCode,
selectedUser,
setEstimatedWalletValue,
steemPerMVests,
isPinCodeOpen,
}) => {
const [isClaiming, setIsClaiming] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false);
const [walletData, setWalletData] = useState(null);
const [userActivities, setUserActivities] = useState([]);
const [sbdBalance, setSbdBalance] = useState(0);
const [steemBalance, setSteemBalance] = useState(0);
const [spBalance, setSpBalance] = useState(0);
const [steemSavingBalance, setSteemSavingBalance] = useState(0);
const [estimatedValue, setEstimatedValue] = useState(0);
const [unclaimedBalance, setUnclaimedBalance] = useState('');
const [estimatedAmount, setEstimatedAmount] = useState(0);
const [transferHistory, setTransferHistory] = useState([]);
const intl = useIntl();
const dispatch = useDispatch();
useEffect(() => {
setEstimatedAmount(getEstimatedAmount(currentAccount, globalProps));
}, [currentAccount, globalProps]);
useEffect(() => {
_getWalletData(selectedUser);
}, [_getWalletData, selectedUser]);
useEffect(() => {
_getWalletData(selectedUser);
}, [_getWalletData, selectedUser]);
useEffect(() => {
const _transferHistory = userActivities.filter(
item => get(item, 'textKey') === 'transfer' || get(item, 'textKey') === 'transfer_to_vesting',
);
setTransferHistory(_transferHistory);
setSbdBalance(Math.round(get(walletData, 'sbdBalance', 0) * 1000) / 1000);
setSteemBalance(Math.round(get(walletData, 'balance', 0) * 1000) / 1000);
setSteemSavingBalance(Math.round(get(walletData, 'savingBalance', 0) * 1000) / 1000);
setSpBalance(
Math.round(
vestsToSp(get(walletData, 'vestingShares', 0), get(walletData, 'steemPerMVests', 0)) * 1000,
) / 1000,
);
setEstimatedValue(get(walletData, 'estimatedValue', 0));
setUnclaimedBalance(
`${
get(walletData, 'rewardSteemBalance', 0)
? `${Math.round(get(walletData, 'rewardSteemBalance', 0) * 1000) / 1000} STEEM`
: ''
}
${
get(walletData, 'rewardSbdBalance', 0)
? ` ${Math.round(get(walletData, 'rewardSbdBalance', 0) * 1000) / 1000} SBD`
: ''
}
${
get(walletData, 'rewardVestingSteem', 0)
? ` ${Math.round(get(walletData, 'rewardVestingSteem', 0) * 1000) / 1000} SP`
: ''
}`,
);
}, [userActivities, walletData]);
// Components functions
const _getWalletData = useCallback(
async _selectedUser => {
const _walletData = await groomingWalletData(_selectedUser, globalProps);
setWalletData(_walletData);
setIsLoading(false);
setUserActivities(
get(_walletData, 'transactions', []).map(item =>
groomingTransactionData(item, steemPerMVests, intl.formatNumber),
),
);
setEstimatedWalletValue && setEstimatedWalletValue(_walletData.estimatedValue);
},
[globalProps, intl.formatNumber, setEstimatedWalletValue, steemPerMVests],
);
const _isHasUnclaimedRewards = account => {
return (
parseToken(get(account, 'reward_steem_balance')) > 0 ||
parseToken(get(account, 'reward_sbd_balance')) > 0 ||
parseToken(get(account, 'reward_vesting_steem')) > 0
);
};
const _claimRewardBalance = async () => {
let isHasUnclaimedRewards;
if (isClaiming) {
return;
}
await setIsClaiming(true);
getAccount(currentAccount.name)
.then(account => {
isHasUnclaimedRewards = _isHasUnclaimedRewards(account[0]);
if (isHasUnclaimedRewards) {
const {
reward_steem_balance: steemBal,
reward_sbd_balance: sbdBal,
reward_vesting_balance: vestingBal,
} = account[0];
return claimRewardBalance(currentAccount, pinCode, steemBal, sbdBal, vestingBal);
}
setIsClaiming(false);
})
.then(() => getAccount(currentAccount.name))
.then(account => {
_getWalletData(selectedUser);
if (isHasUnclaimedRewards) {
dispatch(
toastNotification(
intl.formatMessage({
id: 'alert.claim_reward_balance_ok',
}),
),
);
}
})
.then(account => {
_getWalletData(selectedUser);
setIsClaiming(false);
})
.catch(() => {
setIsClaiming(false);
dispatch(
toastNotification(
intl.formatMessage({
id: 'alert.fail',
}),
),
);
});
};
const _handleOnWalletRefresh = () => {
setRefreshing(true);
getAccount(selectedUser.name)
.then(account => {
_getWalletData(selectedUser);
setRefreshing(false);
})
.catch(() => {
dispatch(
toastNotification(
intl.formatMessage({
id: 'alert.fail',
}),
),
);
setRefreshing(false);
});
};
const _navigate = async (transferType, fundType) => {
let balance;
switch (fundType) {
case 'STEEM':
balance = Math.round(walletData.balance * 1000) / 1000;
break;
case 'SBD':
balance = Math.round(walletData.sbdBalance * 1000) / 1000;
break;
case 'SAVING_STEEM':
balance = Math.round(walletData.savingBalance * 1000) / 1000;
break;
case 'SAVING_SBD':
balance = Math.round(walletData.savingBalanceSbd * 1000) / 1000;
break;
default:
break;
}
if (isPinCodeOpen) {
dispatch(
openPinCodeModal({
navigateTo: ROUTES.SCREENS.TRANSFER,
navigateParams: { transferType, fundType, balance },
}),
);
} else {
navigate({
routeName: ROUTES.SCREENS.TRANSFER,
params: { transferType, fundType, balance },
});
}
};
return (
children &&
children({
claimRewardBalance: _claimRewardBalance,
currentAccountUsername: currentAccount.name,
handleOnWalletRefresh: _handleOnWalletRefresh,
isClaiming: isClaiming,
refreshing: refreshing,
selectedUsername: get(selectedUser, 'name', ''),
isLoading,
walletData,
steemPerMVests,
userActivities,
transferHistory,
steemBalance,
spBalance,
sbdBalance,
steemSavingBalance,
estimatedValue,
navigate: _navigate,
steemDropdown: STEEM_DROPDOWN,
sbdDropdown: SBD_DROPDOWN,
savingSteemDropdown: SAVING_STEEM_DROPDOWN,
savingSbdDropdown: SAVING_SBD_DROPDOWN,
steemPowerDropdown: STEEM_POWER_DROPDOWN,
unclaimedBalance: unclaimedBalance && unclaimedBalance.trim(),
estimatedAmount,
})
);
};
const mapStateToProps = state => ({
currentAccount: state.account.currentAccount,
pinCode: state.application.pin,
globalProps: state.account.globalProps,
steemPerMVests: state.account.globalProps.steemPerMVests,
isPinCodeOpen: state.application.isPinCodeOpen,
});
export default connect(mapStateToProps)(WalletContainer);

View File

@ -7,6 +7,12 @@ export default EStyleSheet.create({
},
defaultContainer: {
flex: 1,
backgroundColor: '$primaryLightBackground',
},
listWrapper: {
paddingHorizontal: 8,
backgroundColor: '$primaryBackgroundColor',
flex: 1.7,
},
text: {
fontFamily: '$primaryFont',
@ -65,4 +71,12 @@ export default EStyleSheet.create({
alignSelf: 'center',
backgroundColor: '$primaryBackgroundColor',
},
swipeItemWrapper: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
tabBarBottom: {
marginBottom: 60,
},
});

View File

@ -6,7 +6,7 @@ import ROUTES from '../constants/routeNames';
// Components
import { Icon, IconContainer } from '../components/icon';
import { Feed, Notification, Profile, Points } from '../screens';
import { Feed, Notification, Profile, Wallet } from '../screens';
import { PostButton, BottomTabBar } from '../components';
const BaseNavigator = createBottomTabNavigator(
@ -40,8 +40,8 @@ const BaseNavigator = createBottomTabNavigator(
tabBarIcon: ({ tintColor }) => <PostButton />,
},
},
[ROUTES.TABBAR.POINTS]: {
screen: Points,
[ROUTES.TABBAR.WALLET]: {
screen: Wallet,
navigationOptions: () => ({
tabBarIcon: ({ tintColor }) => (
<Icon iconType="MaterialCommunityIcons" name="gift-outline" color={tintColor} size={26} />

View File

@ -16,7 +16,4 @@ const navigate = navigationProps => {
// add other navigation functions that you need and export them
export default {
navigate,
setTopLevelNavigator,
};
export { navigate, setTopLevelNavigator };

View File

@ -38,7 +38,7 @@ import {
import { getUser, getPost } from '../../../providers/steem/dsteem';
import { switchAccount } from '../../../providers/steem/auth';
import { setPushToken } from '../../../providers/esteem/esteem';
import NavigationService from '../../../navigation/service';
import { navigate } from '../../../navigation/service';
// Actions
import {
@ -261,7 +261,7 @@ class ApplicationContainer extends Component {
if (routeName && (profile || content)) {
this.navigationTimeout = setTimeout(() => {
clearTimeout(this.navigationTimeout);
NavigationService.navigate({
navigate({
routeName,
params,
key: permlink || author,
@ -401,7 +401,7 @@ class ApplicationContainer extends Component {
}
if (!some(params, isEmpty)) {
NavigationService.navigate({
navigate({
routeName,
params,
key,

View File

@ -8,13 +8,15 @@ import { Modal } from '../../components';
import { PinCode } from '../pinCode';
const Application = () => {
const [showAnimation, setShowAnimation] = useState(true);
const [showAnimation, setShowAnimation] = useState(process.env.NODE_ENV !== 'development');
useEffect(() => {
setTimeout(() => {
setShowAnimation(false);
}, 500);
}, []);
if (!showAnimation) {
setTimeout(() => {
setShowAnimation(false);
}, 2000);
}
}, [showAnimation]);
return (
<ApplicationContainer>

View File

@ -5,7 +5,7 @@ import { connect } from 'react-redux';
import { createAppContainer } from 'react-navigation';
import AppNavitation from '../../../navigation/routes';
import NavigationService from '../../../navigation/service';
import { setTopLevelNavigator } from '../../../navigation/service';
// Services
import { toastNotification as toastNotificationAction } from '../../../redux/actions/uiAction';
@ -62,7 +62,7 @@ class ApplicationScreen extends Component {
{!isConnected && <NoInternetConnection />}
<Navigation
ref={navigatorRef => {
NavigationService.setTopLevelNavigator(navigatorRef);
setTopLevelNavigator(navigatorRef);
}}
/>
</Fragment>

View File

@ -7,7 +7,7 @@ import { Launch } from './launch';
import { Login } from './login';
import { Notification } from './notification';
import { PinCode } from './pinCode';
import { Points } from './points';
import { Wallet } from './wallet';
import { Post } from './post';
import { SearchResult } from './searchResult';
import { Settings } from './settings';
@ -32,7 +32,7 @@ export {
Login,
Notification,
PinCode,
Points,
Wallet,
Post,
Profile,
ProfileEdit,

View File

@ -6,9 +6,11 @@ export default EStyleSheet.create({
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '$pureWhite',
zIndex: 999,
},
darkContainer: {
flex: 1,
zIndex: 999,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#1e2835',

View File

@ -4,7 +4,8 @@ import ScrollableTabView from 'react-native-scrollable-tab-view';
import { injectIntl } from 'react-intl';
// Components
import { TabBar, LeaderBoard, Notification, Header, NoPost } from '../../../components';
import { TabBar, LeaderBoard, Notification, Header } from '../../../components';
import { LoggedInContainer } from '../../../containers';
// Styles
import styles from './notificationStyles';
@ -23,8 +24,6 @@ class NotificationScreen extends PureComponent {
intl,
navigateToNotificationRoute,
readAllNotification,
handleLoginPress,
isLoggedIn,
isNotificationRefreshing,
changeSelectedFilter,
} = this.props;
@ -45,24 +44,18 @@ class NotificationScreen extends PureComponent {
})}
style={styles.tabbarItem}
>
{isLoggedIn ? (
<Notification
getActivities={getActivities}
notifications={notifications}
navigateToNotificationRoute={navigateToNotificationRoute}
readAllNotification={readAllNotification}
isNotificationRefreshing={isNotificationRefreshing}
changeSelectedFilter={changeSelectedFilter}
/>
) : (
<NoPost
isButtonText
defaultText={intl.formatMessage({
id: 'profile.login_to_see',
})}
handleOnButtonPress={handleLoginPress}
/>
)}
<LoggedInContainer>
{() => (
<Notification
getActivities={getActivities}
notifications={notifications}
navigateToNotificationRoute={navigateToNotificationRoute}
readAllNotification={readAllNotification}
isNotificationRefreshing={isNotificationRefreshing}
changeSelectedFilter={changeSelectedFilter}
/>
)}
</LoggedInContainer>
</View>
<View
tabLabel={intl.formatMessage({

View File

@ -6,7 +6,7 @@ import Config from 'react-native-config';
import get from 'lodash/get';
// Actions & Services
import NavigationService from '../../../navigation/service';
import { navigate } from '../../../navigation/service';
import {
setUserDataWithPinCode,
verifyPinCode,
@ -113,7 +113,7 @@ class PinCodeContainer extends Component {
}
dispatch(closePinCodeModal());
if (navigateTo) {
NavigationService.navigate({
navigate({
routeName: navigateTo,
params: navigateParams,
});
@ -176,7 +176,7 @@ class PinCodeContainer extends Component {
}
dispatch(closePinCodeModal());
if (navigateTo) {
NavigationService.navigate({
navigate({
routeName: navigateTo,
params: navigateParams,
});
@ -213,9 +213,11 @@ class PinCodeContainer extends Component {
[_currentAccount.local] = realmData;
dispatch(updateCurrentAccount({ ..._currentAccount }));
dispatch(closePinCodeModal());
if (callback) callback(pin, oldPinCode);
if (callback) {
callback(pin, oldPinCode);
}
if (navigateTo) {
NavigationService.navigate({
navigate({
routeName: navigateTo,
params: navigateParams,
});

View File

@ -1,22 +0,0 @@
import React from 'react';
import { connect } from 'react-redux';
// Component
import PointsScreen from '../screen/pointsScreen';
// Constants
import ROUTES from '../../../constants/routeNames';
const PointsContainer = ({ isLoggedIn, navigation }) => {
const _handleOnPressLogin = () => {
navigation.navigate(ROUTES.SCREENS.LOGIN);
};
return <PointsScreen isLoggedIn={isLoggedIn} handleLoginPress={_handleOnPressLogin} />;
};
const matStateToProps = state => ({
isLoggedIn: state.application.isLoggedIn,
});
export default connect(matStateToProps)(PointsContainer);

View File

@ -1,5 +0,0 @@
import PointsScreen from './screen/pointsScreen';
import Points from './container/pointsContainer';
export { PointsScreen, Points };
export default Points;

View File

@ -1,60 +0,0 @@
import React, { Fragment } from 'react';
import { useIntl } from 'react-intl';
import { SafeAreaView } from 'react-native';
// Containers
import { PointsContainer } from '../../../containers';
// Components
import { Header, Points, NoPost } from '../../../components';
// Styles
import styles from './pointsStyles';
const PointsScreen = ({ isLoggedIn, handleLoginPress }) => {
const intl = useIntl();
return (
<Fragment>
<Header />
<SafeAreaView style={styles.container}>
{isLoggedIn ? (
<PointsContainer>
{({
handleOnDropdownSelected,
claimPoints,
fetchUserActivity,
isClaiming,
isLoading,
refreshing,
userActivities,
userPoints,
}) => (
<Points
claimPoints={claimPoints}
fetchUserActivity={fetchUserActivity}
isClaiming={isClaiming}
isLoading={isLoading}
refreshing={refreshing}
userActivities={userActivities}
userPoints={userPoints}
handleOnDropdownSelected={handleOnDropdownSelected}
/>
)}
</PointsContainer>
) : (
<NoPost
style={styles.noPostContainer}
isButtonText
defaultText={intl.formatMessage({
id: 'profile.login_to_see',
})}
handleOnButtonPress={handleLoginPress}
/>
)}
</SafeAreaView>
</Fragment>
);
};
export default PointsScreen;

View File

@ -1,19 +0,0 @@
import EStyleSheet from 'react-native-extended-stylesheet';
export default EStyleSheet.create({
container: {
flex: 1,
backgroundColor: '$primaryLightBackground',
},
image: {
width: 193,
height: 189,
},
text: {
color: '#788187',
fontWeight: 'bold',
},
noPostContainer: {
flex: 1,
},
});

View File

@ -174,6 +174,7 @@ export default EStyleSheet.create({
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '$primaryBackgroundColor',
},
checkView: {
flexDirection: 'row',

View File

@ -0,0 +1,4 @@
import Wallet from './screen/walletScreen';
export { Wallet };
export default Wallet;

View File

@ -0,0 +1,50 @@
import React from 'react';
import get from 'lodash/get';
import { View } from 'react-native';
import { WalletHeader } from '../../../components';
import { PointsContainer } from '../../../containers';
import globalStyles from '../../../globalStyles';
const EstmView = ({ handleOnSelected, index, currentIndex }) => (
<View style={globalStyles.swipeItemWrapper}>
<PointsContainer>
{({
handleOnDropdownSelected,
claim,
fetchUserActivity,
isClaiming,
isLoading,
refreshing,
userActivities,
userPoints,
dropdownOptions,
}) => (
<WalletHeader
componentDidUpdate={() => handleOnSelected(userActivities, isLoading, fetchUserActivity)}
index={index}
showIconList
claim={claim}
fetchUserActivity={fetchUserActivity}
isClaiming={isClaiming}
isLoading={isLoading}
refreshing={refreshing}
userActivities={userActivities}
unclaimedBalance={
get(userPoints, 'unclaimed_points') > 0 && get(userPoints, 'unclaimed_points')
}
userBalance={[
{ balance: get(userPoints, 'points'), nameKey: 'estm', options: dropdownOptions },
]}
handleOnDropdownSelected={handleOnDropdownSelected}
type="estm"
currentIndex={currentIndex}
showBuyButton
/>
)}
</PointsContainer>
</View>
);
export default EstmView;

View File

@ -0,0 +1,64 @@
import React from 'react';
import { View } from 'react-native';
import { WalletHeader, FormatedCurrency } from '../../../components';
import { SteemWalletContainer, AccountContainer } from '../../../containers';
import globalStyles from '../../../globalStyles';
const SpView = ({ handleOnSelected, index, currentIndex }) => (
<View style={globalStyles.swipeItemWrapper}>
<AccountContainer>
{({ currentAccount }) => (
<SteemWalletContainer selectedUser={currentAccount}>
{({
isClaiming,
claimRewardBalance,
handleOnWalletRefresh,
refreshing,
userActivities,
spBalance,
isLoading,
estimatedValue,
steemPowerDropdown,
unclaimedBalance,
navigate,
estimatedAmount,
}) => (
<WalletHeader
componentDidUpdate={() => handleOnSelected(userActivities, isLoading)}
index={index}
claim={claimRewardBalance}
fetchUserActivity={handleOnWalletRefresh}
isClaiming={isClaiming}
isLoading={isLoading}
refreshing={refreshing}
userActivities={userActivities}
unclaimedBalance={unclaimedBalance}
showBuyButton={unclaimedBalance.length > 0}
userBalance={[
{ balance: spBalance, nameKey: 'steem_power', options: steemPowerDropdown },
]}
handleOnDropdownSelected={option => navigate(option, 'STEEM_POWER')}
type="steem_power"
currentIndex={currentIndex}
showIconList={false}
valueDescriptions={[
{
textKey: 'estimated_value',
value: <FormatedCurrency isApproximate value={estimatedValue} />,
},
{
textKey: 'estimated_amount',
value: <FormatedCurrency isApproximate value={estimatedAmount} />,
},
]}
/>
)}
</SteemWalletContainer>
)}
</AccountContainer>
</View>
);
export default SpView;

View File

@ -0,0 +1,60 @@
import React from 'react';
import { View } from 'react-native';
import { WalletHeader, FormatedCurrency } from '../../../components';
import { SteemWalletContainer, AccountContainer } from '../../../containers';
import globalStyles from '../../../globalStyles';
const SteeemView = ({ handleOnSelected, index, currentIndex }) => (
<View style={globalStyles.swipeItemWrapper}>
<AccountContainer>
{({ currentAccount }) => (
<SteemWalletContainer selectedUser={currentAccount}>
{({
isClaiming,
claimRewardBalance,
handleOnWalletRefresh,
refreshing,
transferHistory,
steemBalance,
isLoading,
steemSavingBalance,
estimatedValue,
steemDropdown,
savingSteemDropdown,
navigate,
}) => (
<WalletHeader
componentDidUpdate={() => handleOnSelected(transferHistory, isLoading)}
index={index}
claim={claimRewardBalance}
fetchUserActivity={handleOnWalletRefresh}
isClaiming={isClaiming}
isLoading={isLoading}
refreshing={refreshing}
unclaimedBalance={0}
userBalance={[
{ balance: steemBalance, nameKey: 'steem', options: steemDropdown },
{ balance: steemSavingBalance, nameKey: 'saving', options: savingSteemDropdown },
]}
handleOnDropdownSelected={option => navigate(option, 'STEEM')}
type="steem"
currentIndex={currentIndex}
showIconList={false}
valueDescriptions={[
{
textKey: 'estimated_value',
value: <FormatedCurrency isApproximate value={estimatedValue} />,
subTextKey: 'estimated_value_desc',
},
]}
/>
)}
</SteemWalletContainer>
)}
</AccountContainer>
</View>
);
export default SteeemView;

View File

@ -0,0 +1,75 @@
import React, { Fragment, useState } from 'react';
import Swiper from 'react-native-swiper';
import { SafeAreaView, View } from 'react-native';
// Containers
import { LoggedInContainer } from '../../../containers';
// Components
import { Header, Transaction } from '../../../components';
import EstmView from './estmView';
import SteemView from './steemView';
import SpView from './spView';
// Styles
import globalStyles from '../../../globalStyles';
const WalletScreen = () => {
const [selectedUserActivities, setSelectedUserActivities] = useState(null);
const [isLoading, setIsLoading] = useState('points');
const [currentIndex, setCurrentIndex] = useState(0);
const [refreshing, setRefreshing] = useState(false);
const _handleSwipeItemChange = (userActivities, _isLoading) => {
setSelectedUserActivities(userActivities);
setIsLoading(_isLoading);
};
return (
<Fragment>
<Header />
<SafeAreaView style={globalStyles.defaultContainer}>
<LoggedInContainer>
{() => (
<>
<Swiper
loop={false}
showsPagination={true}
index={0}
onIndexChanged={index => setCurrentIndex(index)}
>
<EstmView
index={0}
handleOnSelected={_handleSwipeItemChange}
refreshing={refreshing}
currentIndex={currentIndex}
/>
<SteemView
index={1}
handleOnSelected={_handleSwipeItemChange}
refreshing={refreshing}
currentIndex={currentIndex}
/>
<SpView
index={2}
refreshing={refreshing}
handleOnSelected={_handleSwipeItemChange}
currentIndex={currentIndex}
/>
</Swiper>
<Transaction
type="wallet"
transactions={selectedUserActivities}
refreshing={refreshing}
setRefreshing={setRefreshing}
isLoading={isLoading}
/>
</>
)}
</LoggedInContainer>
</SafeAreaView>
</Fragment>
);
};
export default WalletScreen;

16
src/utils/vote.js Normal file
View File

@ -0,0 +1,16 @@
import parseToken from './parseToken';
import get from 'lodash/get';
import { vestsToRshares } from './conversions';
export const getEstimatedAmount = (account, globalProps, value = 100) => {
const { fundRecentClaims, fundRewardBalance, base, quote } = globalProps;
const votingPower = account.voting_power;
const totalVests =
parseToken(get(account, 'vesting_shares')) +
parseToken(get(account, 'received_vesting_shares')) -
parseToken(get(account, 'delegated_vesting_shares'));
const votePct = value * 10000;
const rShares = vestsToRshares(totalVests, votingPower, votePct);
return ((rShares / fundRecentClaims) * fundRewardBalance * (base / quote)).toFixed(5);
};

View File

@ -2,6 +2,7 @@ import get from 'lodash/get';
import parseDate from './parseDate';
import parseToken from './parseToken';
import { vestsToSp } from './conversions';
import ss from 'react-intl';
import { getState, getFeedHistory } from '../providers/steem/dsteem';
export const groomingTransactionData = (transaction, steemPerMVests, formatNumber) => {
@ -9,16 +10,16 @@ export const groomingTransactionData = (transaction, steemPerMVests, formatNumbe
return [];
}
const result = {};
let result = { iconType: 'MaterialIcons' };
[result.opName] = transaction[1].op;
[result.textKey] = transaction[1].op;
const opData = transaction[1].op[1];
const { timestamp } = transaction[1];
result.transDate = timestamp;
result.created = timestamp;
result.icon = 'local-activity';
switch (result.opName) {
switch (result.textKey) {
case 'curation_reward':
const { reward } = opData;
const { comment_author: commentAuthor, comment_permlink: commentPermlink } = opData;
@ -55,7 +56,7 @@ export const groomingTransactionData = (transaction, steemPerMVests, formatNumbe
} ${vestingPayout > 0 ? `${vestingPayout} SP` : ''}`;
result.details = author && permlink ? `@${author}/${permlink}` : null;
if (result.opName === 'comment_benefactor_reward') {
if (result.textKey === 'comment_benefactor_reward') {
result.icon = 'comment';
}
break;
@ -167,3 +168,18 @@ export const groomingWalletData = async (user, globalProps) => {
return walletData;
};
export const groomingPointsTransactionData = transaction => {
if (!transaction) {
return null;
}
let result = { ...transaction };
result.details = get(transaction, 'sender')
? `from @${get(transaction, 'sender')}`
: get(transaction, 'receiver') && `to @${get(transaction, 'receiver')}`;
result.value = `${get(transaction, 'amount')} ESTM`;
return result;
};

View File

@ -7641,6 +7641,13 @@ react-native-svg@^9.5.3:
resolved "https://registry.yarnpkg.com/react-native-svg/-/react-native-svg-9.11.1.tgz#b1ccf48de413ff8c4f476f202aaa3893f4c8b59a"
integrity sha512-BmNCM81SSzhj1+N5rYiy7sxrkmybgiT8Cu8yVRB7zVoWze/i1lbCWJah+Gk0OHHwR35ZA31oVKf5jtO4G1n94Q==
react-native-swiper@^1.5.14:
version "1.5.14"
resolved "https://registry.yarnpkg.com/react-native-swiper/-/react-native-swiper-1.5.14.tgz#1c6f949ca377186300f972bb0f30d24062c899aa"
integrity sha512-Kn0fxKooN7Shwu1qJYHB+Y8ssXXnvrIwReHXU5jCdyYNfz2QbBv0Cv3sa2Mqzr+XgzORCFFIokc8uCCUITDrVA==
dependencies:
prop-types "^15.5.10"
react-native-tab-view@^2.9.0:
version "2.10.0"
resolved "https://registry.yarnpkg.com/react-native-tab-view/-/react-native-tab-view-2.10.0.tgz#5e249e5650502010013449ffd4e5edc18a95364b"