This commit is contained in:
Mustafa Buyukcelebi 2019-10-08 13:06:19 +03:00
commit b91b478ab0
35 changed files with 857 additions and 140 deletions

View File

@ -28,7 +28,7 @@ buildscript {
jcenter()
}
dependencies {
classpath('com.android.tools.build:gradle:3.5.0')
classpath('com.android.tools.build:gradle:3.5.1')
classpath 'com.google.gms:google-services:4.0.2'
classpath 'com.bugsnag:bugsnag-android-gradle-plugin:4.+'

View File

@ -53,8 +53,8 @@
"react-native-datepicker": "^1.7.2",
"react-native-extended-stylesheet": "^0.10.0",
"react-native-fast-image": "^4.0.14",
"react-native-iap": "3.4.15",
"react-native-gesture-handler": "^1.4.1",
"react-native-iap": "^3.3.8",
"react-native-image-crop-picker": "^0.25.2",
"react-native-keyboard-aware-scroll-view": "^0.8.0",
"react-native-linear-gradient": "^2.4.2",

BIN
src/assets/estmTags.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
src/assets/estmTags@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
src/assets/estmTags@3x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

@ -0,0 +1,44 @@
import React from 'react';
import { View, Image } from 'react-native';
import { SpinIndicator } from '../spinIndicator/spinIndicator';
// Styles
import styles from './boostIndicatorStyles';
const BoostIndicatorAnimation = ({ isSpinning }) => {
return (
<View style={styles.spinIndicatorContainer}>
<SpinIndicator
size={230}
animationDuration={2400}
color={!isSpinning ? '#f2f2f2' : '#1a509a'}
breadth={12}
animating={isSpinning}
initStart={0}
/>
<SpinIndicator
size={180}
animationDuration={2000}
color={!isSpinning ? '#f2f2f2' : '#357ce6'}
breadth={12}
animating={isSpinning}
initStart={20}
/>
<SpinIndicator
size={130}
animationDuration={1700}
color={!isSpinning ? '#f2f2f2' : '#4da1f1'}
breadth={12}
animating={isSpinning}
initStart={40}
/>
<Image
style={{ width: 80, height: 80 }}
source={require('../../../assets/esteem_logo_transparent.png')}
/>
</View>
);
};
export { BoostIndicatorAnimation };

View File

@ -0,0 +1,11 @@
import EStyleSheet from 'react-native-extended-stylesheet';
export default EStyleSheet.create({
spinIndicatorContainer: {
backgroundColor: '$primaryBackgroundColor',
justifyContent: 'center',
alignItems: 'center',
width: '$deviceWidth',
flex: 1,
},
});

View File

@ -1,4 +1,5 @@
import PulseAnimation from './pulse/pulseAnimation';
import { PulseAnimation } from './pulse/pulseAnimation';
import { SpinIndicator } from './spinIndicator/spinIndicator';
import { BoostIndicatorAnimation } from './boostIndicator/boostIndicatorAnimation';
export { PulseAnimation };
export default PulseAnimation;
export { PulseAnimation, SpinIndicator, BoostIndicatorAnimation };

View File

@ -14,7 +14,7 @@ const styles = StyleSheet.create({
},
});
export default class PulseAnimation extends Component {
class PulseAnimation extends Component {
static defaultProps = {
color: 'blue',
diameter: 400,
@ -151,3 +151,5 @@ export default class PulseAnimation extends Component {
);
}
}
export { PulseAnimation };

View File

@ -0,0 +1,104 @@
import React, { PureComponent } from 'react';
import { Animated, Easing } from 'react-native';
export default class Indicator extends PureComponent {
constructor(props) {
super(props);
this.state = {
progress: new Animated.Value(0),
};
this.mounted = false;
}
componentDidMount() {
const { animating } = this.props;
this.mounted = true;
if (animating) {
this._startAnimation();
}
}
componentDidUpdate(prevProps) {
const { animating } = this.props;
if (animating !== prevProps.animating) {
if (animating) {
this._stopAnimation();
} else {
this._startAnimation();
}
}
}
componentWillUnmount() {
this.mounted = false;
}
_startAnimation = ({ finished } = {}) => {
const { progress } = this.state;
const { interaction, animationEasing, animationDuration } = this.props;
if (!this.mounted || finished === false) {
return;
}
const animation = Animated.timing(progress, {
duration: animationDuration,
easing: animationEasing,
useNativeDriver: true,
isInteraction: interaction,
toValue: 1,
});
Animated.loop(animation).start();
this.setState({ animation });
};
_stopAnimation = () => {
const { animation } = this.state;
if (animation == null) {
return;
}
animation.stop();
this.setState({ animation: null });
};
_renderComponent = (undefined, index) => {
const { progress } = this.state;
const { renderComponent } = this.props;
if (renderComponent) {
return renderComponent({ index, progress });
}
return null;
};
render() {
const { count, ...props } = this.props;
return (
<Animated.View {...props}>
{Array.from(new Array(count), this._renderComponent)}
</Animated.View>
);
}
}
Indicator.defaultProps = {
animationEasing: Easing.linear,
animationDuration: 1200,
animating: true,
interaction: true,
count: 1,
};
export { Indicator };

View File

@ -0,0 +1,125 @@
import React, { PureComponent } from 'react';
import { View, Animated, Easing } from 'react-native';
import { Indicator } from './indicator';
import styles from './spinIndicatorStyles';
class SpinIndicator extends PureComponent {
_renderComponent = ({ index, progress }) => {
const { size, color, animationDuration, breadth, animating, initStart } = this.props;
const frames = (60 * animationDuration) / 1000;
const easing = Easing.bezier(0.4, 0.0, 0.7, 1.0);
const inputRange = Array.from(
new Array(frames),
(undefined, frameIndex) => frameIndex / (frames - 1),
);
const outputRange = Array.from(new Array(frames), (undefined, frameIndex) => {
let _progress = (2 * frameIndex) / (frames - 1);
const rotation = index ? +(360 - 15) : -(180 - 15);
if (_progress > 1.0) {
_progress = 2.0 - _progress;
}
const direction = index ? -1 : +1;
return `${direction * (180 - 30) * easing(_progress) + rotation}deg`;
});
const layerStyle = {
width: size,
height: size,
transform: [
{
rotate: progress.interpolate({
inputRange: [0, 1],
outputRange: [`${initStart + 30 + 15}deg`, `${2 * (360 + initStart)}deg`],
}),
},
],
};
const viewportStyle = {
width: size,
height: size,
transform: [
{
translateY: index ? -size / 2 : 0,
},
{
rotate: progress.interpolate({ inputRange, outputRange }),
},
],
};
const containerStyle = {
width: !animating ? 300 : size,
height: !animating ? 300 : size / 2,
position: 'absolute',
overflow: 'hidden',
};
const offsetStyle = index ? { top: size / 2 } : null;
const lineStyle = {
width: size,
height: size,
borderColor: color,
borderWidth: breadth || size / 10,
borderRadius: size / 2,
};
return (
<Animated.View style={styles.layer} {...{ key: index }}>
<Animated.View style={layerStyle}>
<Animated.View style={[containerStyle, offsetStyle]} collapsable={false}>
<Animated.View style={viewportStyle}>
<Animated.View style={containerStyle} collapsable={false}>
<Animated.View style={lineStyle} />
</Animated.View>
</Animated.View>
</Animated.View>
</Animated.View>
</Animated.View>
);
};
render() {
const {
style,
size: width,
size: height,
animationDuration,
interaction,
animating,
animationEasing,
} = this.props;
return (
<View style={[styles.container, style]}>
<Indicator
style={{ width, height }}
renderComponent={this._renderComponent}
interaction={interaction}
animationEasing={animationEasing}
animationDuration={animationDuration}
animating={animating}
count={2}
/>
</View>
);
}
}
SpinIndicator.defaultProps = {
animationDuration: 2400,
color: '#1a509a',
animating: true,
size: 40,
initStart: 0,
};
export { SpinIndicator };

View File

@ -0,0 +1,14 @@
import { StyleSheet } from 'react-native';
export default StyleSheet.create({
container: {
justifyContent: 'center',
alignItems: 'center',
position: 'absolute',
},
layer: {
...StyleSheet.absoluteFillObject,
justifyContent: 'center',
alignItems: 'center',
},
});

View File

@ -1,5 +1,6 @@
import { AvatarHeader } from './avatarHeader';
import { BasicHeader } from './basicHeader';
import { BoostIndicatorAnimation } from './animations';
import { BottomTabBar } from './bottomTabBar';
import { CheckBox } from './checkbox';
import { CircularButton, TextButton, SquareButton } from './buttons';
@ -36,6 +37,7 @@ import { SearchInput } from './searchInput';
import { SearchModal } from './searchModal';
import { SettingsItem } from './settingsItem';
import { SideMenu } from './sideMenu';
import { SpinIndicator } from './animations';
import { SummaryArea, TagArea, TextArea, TitleArea } from './editorElements';
import { TabBar } from './tabBar';
import { TextInput } from './textInput';
@ -48,6 +50,7 @@ import Logo from './logo/logo';
import PostButton from './postButton/postButtonView';
import ProfileEditForm from './profileEditForm/profileEditFormView';
import ScaleSlider from './scaleSlider/scaleSliderView';
import { ProductItemLine } from './productItemLine/productItemLineView';
// View
import { Comment } from './comment';
@ -64,6 +67,7 @@ import { WalletDetails } from './walletDetails';
import PostBoost from './postBoost/postBoostView';
import Profile from './profile/profileView';
import Promote from './promote/promoteView';
import { SpinGame } from './spinGame/spinGameView';
// Basic UI Elements
import {
@ -89,30 +93,14 @@ import {
} from './basicUIElements';
export {
Card,
Chip,
GrayWrapper,
LineBreak,
ListItemPlaceHolder,
ListPlaceHolder,
BoostPlaceHolder,
NoInternetConnection,
NoPost,
PostCardPlaceHolder,
PostPlaceHolder,
ProfileSummaryPlaceHolder,
StickyBar,
Tag,
TextWithIcon,
UserListItem,
WalletDetailsPlaceHolder,
WalletLineItem,
WalletUnclaimedPlaceHolder,
AvatarHeader,
BasicHeader,
InformationBox,
BoostIndicatorAnimation,
BoostPlaceHolder,
BottomTabBar,
Card,
CheckBox,
Chip,
CircularButton,
CollapsibleCard,
Comment,
@ -124,16 +112,23 @@ export {
FilterBar,
FormatedCurrency,
FormInput,
GrayWrapper,
Header,
Icon,
IconButton,
InformationArea,
InformationBox,
LeaderBoard,
LineBreak,
ListItemPlaceHolder,
ListPlaceHolder,
LoginHeader,
Logo,
MainButton,
MarkdownEditor,
Modal,
NoInternetConnection,
NoPost,
Notification,
NotificationLine,
NumericKeyboard,
@ -145,15 +140,19 @@ export {
PostBoost,
PostButton,
PostCard,
PostCardPlaceHolder,
PostDisplay,
PostDropdown,
PostForm,
PostHeaderDescription,
PostListItem,
PostPlaceHolder,
Posts,
ProductItemLine,
Profile,
ProfileEditForm,
ProfileSummary,
ProfileSummaryPlaceHolder,
Promote,
PulseAnimation,
ScaleSlider,
@ -161,14 +160,19 @@ export {
SearchModal,
SettingsItem,
SideMenu,
SpinGame,
SpinIndicator,
SquareButton,
StickyBar,
SummaryArea,
TabBar,
Tag,
TagArea,
Tags,
TextArea,
TextButton,
TextInput,
TextWithIcon,
TitleArea,
ToastNotification,
ToggleSwitch,
@ -176,7 +180,11 @@ export {
TransferFormItem,
Upvote,
UserAvatar,
UserListItem,
VotersDisplay,
Wallet,
WalletDetails,
WalletDetailsPlaceHolder,
WalletLineItem,
WalletUnclaimedPlaceHolder,
};

View File

@ -26,7 +26,7 @@ export default EStyleSheet.create({
alignSelf: 'center',
fontSize: 14,
paddingLeft: 10,
paddingRight: 20,
paddingRight: 10,
},
secondText: {
fontWeight: 'bold',

View File

@ -41,7 +41,9 @@ class MainButton extends Component {
_handleOnPress = () => {
const { onPress } = this.props;
if (onPress) onPress();
if (onPress) {
onPress();
}
};
_getBody = () => {
@ -57,12 +59,14 @@ class MainButton extends Component {
{source ? (
<Image source={source} style={styles.image} resizeMode="contain" />
) : (
<Icon
iconType={iconType || 'MaterialIcons'}
color={iconColor}
name={iconName}
style={styles.icon}
/>
iconName && (
<Icon
iconType={iconType || 'MaterialIcons'}
color={iconColor}
name={iconName}
style={styles.icon}
/>
)
)}
<Text style={styles.text}>
{text}
@ -92,7 +96,7 @@ class MainButton extends Component {
<View style={wrapperStyle}>
<TouchableOpacity
disabled={isLoading || isDisable}
onPress={() => this._handleOnPress()}
onPress={this._handleOnPress}
style={[
styles.touchable,
isDisable && styles.disableTouchable,

View File

@ -1,10 +1,11 @@
import EStyleSheet from 'react-native-extended-stylesheet';
export default EStyleSheet.create({
wrapper: {
buttonWrapper: {
minWidth: '$deviceWidth / 2.4',
flexDirection: 'row',
justifyContent: 'flex-end',
flex: 1,
position: 'absolute',
top: '$deviceHeight / 3',
},
boostLine: {
flexDirection: 'row',
@ -14,16 +15,12 @@ export default EStyleSheet.create({
button: {
marginVertical: 12,
paddingHorizontal: 18,
justifyContent: 'center',
alignItems: 'center',
},
buttonContent: {
flexDirection: 'row',
},
buttonWrapper: {
minWidth: '$deviceWidth / 2.4',
flexDirection: 'row',
justifyContent: 'flex-end',
flex: 1,
},
buttonText: {
color: '$pureWhite',
fontSize: 14,

View File

@ -0,0 +1,59 @@
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 '..';
import styles from './productItemLineStyles';
// TODO: move to translation
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')}>
{_renderDeal(product)}
<View style={styles.buttonWrapper}>
<MainButton
style={styles.button}
onPress={() => handleOnButtonPress(get(product, 'productId'))}
height={50}
isDisable={disabled}
isLoading={false}
>
<View style={styles.buttonContent}>
<Text style={styles.buttonText}>{title}</Text>
<View style={styles.buttonIconWrapper}>
<Icon name="add" iconType="MaterialIcons" color="#357ce6" size={23} />
</View>
</View>
</MainButton>
</View>
<View style={styles.priceWrapper}>
{get(product, 'localizedPrice', null) && (
<Text style={styles.priceText}>{get(product, 'localizedPrice', 0)}</Text>
)}
</View>
</View>
);
};
const _renderDeal = item => {
if (DEALS[item.productId]) {
return (
<View style={styles.descriptionWrapper}>
<Text style={styles.description}>{DEALS[item.productId]}</Text>
<View style={styles.triangle} />
</View>
);
}
return null;
};
export { ProductItemLineView as ProductItemLine };

View File

@ -0,0 +1,86 @@
import EStyleSheet from 'react-native-extended-stylesheet';
export default EStyleSheet.create({
container: {
flex: 1,
backgroundColor: '$primaryBackgroundColor',
alignItems: 'center',
},
textWrapper: {
flex: 0.2,
justifyContent: 'center',
alignItems: 'center',
},
count: {
fontSize: 72,
fontWeight: '700',
color: '$primaryDarkGray',
},
countDesc: {
color: '$primaryDarkGray',
fontSize: 16,
marginTop: 5,
fontWeight: '700',
},
spinnerWrapper: {
flex: 1,
marginTop: 10,
},
backgroundTags: {
position: 'absolute',
width: '$deviceWidth',
height: 320,
left: 0,
top: 16,
right: 0,
zIndex: 998,
},
descriptionWrapper: {
backgroundColor: '$primaryDarkBlue',
width: 75,
height: 30,
justifyContent: 'center',
paddingHorizontal: 5,
borderTopRightRadius: 0,
borderBottomRightRadius: 0,
position: 'absolute',
top: '$deviceHeight / 5',
right: 0,
},
description: {
fontSize: 10,
color: '$pureWhite',
fontWeight: 'bold',
},
triangle: {
width: 0,
height: 0,
backgroundColor: 'transparent',
borderStyle: 'solid',
borderLeftWidth: 15,
borderRightWidth: 15,
borderBottomWidth: 15,
borderLeftColor: 'transparent',
borderRightColor: 'transparent',
borderBottomColor: '$primaryDarkBlue',
transform: [{ rotate: '-90deg' }],
position: 'absolute',
left: -22,
},
productWrapper: {
flex: 0.8,
zIndex: 998,
alignItems: 'center',
},
spinButton: {
width: 150,
justifyContent: 'center',
alignItems: 'center',
},
nextDate: {
marginTop: 50,
color: '$primaryDarkGray',
fontSize: 16,
fontWeight: '600',
},
});

View File

@ -0,0 +1,98 @@
import React, { useState, Fragment } from 'react';
import { Image, Text, View } from 'react-native';
import moment from 'moment';
import { useIntl } from 'react-intl';
// Components
import { BoostIndicatorAnimation, MainButton, BasicHeader, ProductItemLine } from '..';
import ESTM_TAGS from '../../assets/estmTags.png';
// Styles
import styles from './spinGameStyles';
const SpinGameView = ({
gameRight,
score,
isLoading,
spinProduct,
isProcessing,
buyItem,
nextDate,
startGame,
}) => {
const intl = useIntl();
const [isSpinning, setIsSpinning] = useState(false);
const _handleOnSpinPress = () => {
startGame('spin');
setIsSpinning(true);
this.spinTimeout = setTimeout(() => {
clearTimeout(this.spinTimeout);
setIsSpinning(false);
}, 8 * 1000);
};
return (
<Fragment>
<BasicHeader title={intl.formatMessage({ id: 'free_estm.title' })} />
<View style={styles.container}>
<View style={styles.textWrapper}>
{!isSpinning && !isLoading && (
<Fragment>
<Text style={styles.count}>{gameRight}</Text>
<Text style={styles.countDesc}>
{intl.formatMessage({ id: 'free_estm.spin_right' })}
</Text>
</Fragment>
)}
</View>
<View style={styles.spinnerWrapper}>
{!isSpinning && !isLoading && gameRight > 0 && (
<Image source={ESTM_TAGS} style={styles.backgroundTags} />
)}
<BoostIndicatorAnimation key={gameRight} isSpinning={isSpinning} />
{!isSpinning && score > 0 && (
<View style={styles.descriptionWrapper}>
<Fragment>
<Text style={styles.description}>{`${score} ESTM`}</Text>
<View style={styles.triangle} />
</Fragment>
</View>
)}
</View>
<View style={styles.productWrapper}>
{!isSpinning && !isLoading && (
<Fragment>
{gameRight > 0 ? (
<MainButton
style={styles.spinButton}
onPress={_handleOnSpinPress}
text={intl.formatMessage({ id: 'free_estm.button' })}
/>
) : (
<Fragment>
{spinProduct.map(product => (
<ProductItemLine
product={product}
title={intl.formatMessage({ id: 'free_estm.get_spin' })}
disabled={isProcessing}
handleOnButtonPress={id => buyItem(id)}
/>
))}
<Text style={styles.nextDate}>{`${intl.formatMessage({
id: 'free_estm.timer_text',
})} ${moment.utc(moment(nextDate).diff(new Date())).format('H:m')}`}</Text>
</Fragment>
)}
</Fragment>
)}
</View>
</View>
</Fragment>
);
};
export { SpinGameView as SpinGame };

View File

@ -5,12 +5,12 @@ import { connect } from 'react-redux';
// Styles
import styles from './textInputStyles';
const TextInputView = ({ isDarkTheme, innerRef, height, ...props }) => (
const TextInputView = ({ isDarkTheme, innerRef, height, style, ...props }) => (
<TextInput
style={[styles.input, { minHeight: height }]}
ref={innerRef}
keyboardAppearance={isDarkTheme ? 'dark' : 'light'}
{...props}
style={[styles.input, { minHeight: height }, style]}
/>
);

View File

@ -333,6 +333,13 @@
"buy": "GET ESTM",
"next": "NEXT"
},
"free_estm": {
"title": "Free ESTM",
"button": "SPIN & WIN",
"get_spin": "5 SPINS",
"spin_right": "Spin Left",
"timer_text": "Next free spin in"
},
"promote": {
"title": "Promote",
"days": "days",

View File

@ -10,7 +10,7 @@ export default {
DRAFTS: `Drafts${SCREEN_SUFFIX}`,
EDITOR: `Editor${SCREEN_SUFFIX}`,
FOLLOWS: `Follows${SCREEN_SUFFIX}`,
FREE_ESTM: `FreeEstm${SCREEN_SUFFIX}`,
SPIN_GAME: `SpinGame${SCREEN_SUFFIX}`,
HOME: `Home${SCREEN_SUFFIX}`,
LOGIN: `Login${SCREEN_SUFFIX}`,
PINCODE: `PinCode${SCREEN_SUFFIX}`,

View File

@ -14,12 +14,7 @@ import { purchaseOrder } from '../providers/esteem/esteem';
// Utilities
import { default as ROUTES } from '../constants/routeNames';
const ITEM_SKUS = Platform.select({
ios: ['099points', '199points', '499points', '999points', '4999points', '9999points'],
android: ['099points', '199points', '499points', '999points', '4999points', '9999points'],
});
class BoostContainer extends Component {
class InAppPurchaseContainer extends Component {
constructor(props) {
super(props);
this.state = {
@ -53,6 +48,7 @@ class BoostContainer extends Component {
const {
currentAccount: { name },
intl,
fetchData,
} = this.props;
this.purchaseUpdateSubscription = purchaseUpdatedListener(purchase => {
@ -75,6 +71,10 @@ class BoostContainer extends Component {
RNIap.consumePurchaseAndroid(token);
}
this.setState({ isProcessing: false });
if (fetchData) {
fetchData();
}
})
.catch(err =>
bugsnag.notify(err, report => {
@ -109,8 +109,10 @@ class BoostContainer extends Component {
};
_getItems = async () => {
const { skus } = this.props;
try {
const products = await RNIap.getProducts(ITEM_SKUS);
const products = await RNIap.getProducts(skus);
products.sort((a, b) => parseFloat(a.price) - parseFloat(b.price)).reverse();
await this.setState({ productList: products });
@ -128,9 +130,9 @@ class BoostContainer extends Component {
_buyItem = async sku => {
const { navigation } = this.props;
await this.setState({ isProcessing: true });
if (sku !== 'freePoints') {
await this.setState({ isProcessing: true });
try {
RNIap.requestPurchase(sku, false);
} catch (err) {
@ -142,7 +144,7 @@ class BoostContainer extends Component {
}
} else {
navigation.navigate({
routeName: ROUTES.SCREENS.FREE_ESTM,
routeName: ROUTES.SCREENS.SPIN_GAME,
});
}
};
@ -150,16 +152,17 @@ class BoostContainer extends Component {
render() {
const { children } = this.props;
const { productList, isLoading, isProcessing } = this.state;
// const FREE_ESTM = { productId: 'freePoints', title: 'free estm' };
const FREE_ESTM = { productId: 'freePoints', title: 'free estm' };
return (
children &&
children({
// productList: [...productList, FREE_ESTM],
productList,
productList: [...productList, FREE_ESTM],
buyItem: this._buyItem,
isLoading,
isProcessing,
getItems: this._getItems,
spinProduct: productList.filter(item => item.productId.includes('spins')),
})
);
}
@ -169,4 +172,4 @@ const mapStateToProps = state => ({
currentAccount: state.account.currentAccount,
});
export default withNavigation(injectIntl(connect(mapStateToProps)(BoostContainer)));
export default withNavigation(injectIntl(connect(mapStateToProps)(InAppPurchaseContainer)));

View File

@ -3,6 +3,7 @@ import PointsContainer from './pointsContainer';
import ProfileContainer from './profileContainer';
import ProfileEditContainer from './profileEditContainer';
import RedeemContainer from './redeemContainer';
import SpinGameContainer from './spinGameContainer';
import TransferContainer from './transferContainer';
export {
@ -11,5 +12,6 @@ export {
ProfileContainer,
ProfileEditContainer,
RedeemContainer,
SpinGameContainer,
TransferContainer,
};

View File

@ -42,8 +42,8 @@ class PointsContainer extends Component {
const { username, isConnected, navigation } = this.props;
if (isConnected) {
this._fetchuserPointActivities(username);
this.fetchInterval = setInterval(this._fetchuserPointActivities, 6 * 60 * 1000);
this._fetchUserPointActivities(username);
this.fetchInterval = setInterval(this._fetchUserPointActivities, 6 * 60 * 1000);
}
if (get(navigation, 'state.params', null)) {
@ -62,7 +62,7 @@ class PointsContainer extends Component {
((nextProps.activeBottomTab === ROUTES.TABBAR.POINTS && _username) ||
(_username !== username && _username))
) {
this._fetchuserPointActivities(_username);
this._fetchUserPointActivities(_username);
}
}
@ -131,8 +131,10 @@ class PointsContainer extends Component {
textKey: get(POINTS[get(item, 'type')], 'textKey'),
}));
_fetchuserPointActivities = async username => {
if (!username) return;
_fetchUserPointActivities = async username => {
if (!username) {
return;
}
this.setState({ refreshing: true });
await getUser(username)
@ -153,7 +155,9 @@ class PointsContainer extends Component {
}
})
.catch(err => {
if (err) Alert.alert(get(err, 'message') || err.toString());
if (err) {
Alert.alert(get(err, 'message') || err.toString());
}
});
this.setState({
@ -169,7 +173,9 @@ class PointsContainer extends Component {
return balance;
})
.catch(err => {
if (err) Alert.alert(get(err, 'message') || err.toString());
if (err) {
Alert.alert(get(err, 'message') || err.toString());
}
});
};
@ -180,7 +186,7 @@ class PointsContainer extends Component {
await claim(username)
.then(() => {
this._fetchuserPointActivities(username);
this._fetchUserPointActivities(username);
})
.catch(error => {
if (error) {
@ -243,7 +249,7 @@ class PointsContainer extends Component {
claimPoints: this._claimPoints,
currentAccount,
currentAccountName: currentAccount.name,
fetchUserActivity: this._fetchuserPointActivities,
fetchUserActivity: this._fetchUserPointActivities,
getAccount,
getESTMPrice: this._getESTMPrice,
getUserBalance: this._getUserBalance,

View File

@ -0,0 +1,103 @@
import { Component } from 'react';
import { Alert } from 'react-native';
import { connect } from 'react-redux';
import get from 'lodash/get';
import { injectIntl } from 'react-intl';
import { withNavigation } from 'react-navigation';
// Providers
import { gameStatusCheck, gameClaim } from '../providers/esteem/ePoint';
class RedeemContainer extends Component {
constructor(props) {
super(props);
this.state = {
score: 0,
nextDate: null,
gameRight: 1,
isLoading: true,
};
}
// Component Life Cycle Functions
componentDidMount() {
this._statusCheck();
}
// Component Functions
_statusCheck = async () => {
const { username } = this.props;
await gameStatusCheck(username, 'spin')
.then(res => {
this.setState({
gameRight: get(res, 'remaining', 0),
nextDate: get(res, 'next_date', null),
isLoading: false,
});
})
.catch(err => {
if (err) {
Alert.alert(get(err, 'message') || err.toString());
}
});
};
_startGame = async type => {
const { username } = this.props;
let gameStatus;
await gameStatusCheck(username, type)
.then(res => {
gameStatus = res;
})
.catch(err => {
if (err) {
Alert.alert(get(err, 'message') || err.toString());
}
});
if (get(gameStatus, 'status') !== 18) {
await gameClaim(username, type, get(gameStatus, 'key'))
.then(res => {
this.setState(
{
gameRight: get(gameStatus, 'status') !== 3 ? 0 : 5,
score: get(res, 'score'),
},
() => this._statusCheck(),
);
})
.catch(err => {
if (err) {
Alert.alert(get(err, 'message') || err.toString());
}
});
} else {
this.setState({ nextDate: get(gameStatus, 'next_date'), gameRight: 0 });
}
};
render() {
const { children } = this.props;
const { score, gameRight, nextDate, isLoading } = this.state;
return (
children &&
children({
score,
startGame: this._startGame,
gameRight,
nextDate,
isLoading,
})
);
}
}
const mapStateToProps = state => ({
username: state.account.currentAccount.name,
});
export default withNavigation(connect(mapStateToProps)(injectIntl(RedeemContainer)));

View File

@ -8,10 +8,10 @@ import ROUTES from '../constants/routeNames';
import {
Bookmarks,
Boost,
BoostPost,
Drafts,
Editor,
Follows,
SpinGame,
Login,
PinCode,
Post,
@ -40,7 +40,7 @@ const mainNavigation = createDrawerNavigator(
},
);
const stackNavigatior = createStackNavigator(
const stackNavigator = createStackNavigator(
{
[ROUTES.DRAWER.MAIN]: {
screen: mainNavigation,
@ -132,6 +132,12 @@ const stackNavigatior = createStackNavigator(
header: () => null,
},
},
[ROUTES.SCREENS.SPIN_GAME]: {
screen: SpinGame,
navigationOptions: {
header: () => null,
},
},
},
{
headerMode: 'none',
@ -139,7 +145,7 @@ const stackNavigatior = createStackNavigator(
);
export default createSwitchNavigator({
stackNavigatior,
stackNavigator,
[ROUTES.SCREENS.LOGIN]: { screen: Login },
[ROUTES.SCREENS.PINCODE]: { screen: PinCode },
[ROUTES.SCREENS.STEEM_CONNECT]: { screen: SteemConnect },

View File

@ -60,3 +60,27 @@ export const claim = username =>
reject(error);
});
});
export const gameStatusCheck = (username, type) =>
new Promise((resolve, reject) => {
ePointApi
.get(`/game/${username}`, { params: { type } })
.then(res => {
resolve(res.data);
})
.catch(error => {
reject(error);
});
});
export const gameClaim = (username, type, key) =>
new Promise((resolve, reject) => {
ePointApi
.post(`/game/${username}?type=${type}`, { key })
.then(res => {
resolve(res.data);
})
.catch(error => {
reject(error);
});
});

View File

@ -357,7 +357,7 @@ export const getSCAccessToken = code =>
api.post('/sc-token-refresh', { code }).then(resp => resolve(resp.data));
});
export const getPromotePosts = () => api.get(`/promoted-posts`).then(resp => resp.data);
export const getPromotePosts = () => api.get('/promoted-posts').then(resp => resp.data);
export const purchaseOrder = data => api.post('/purchase-order', data).then(resp => resp.data);

View File

@ -1,34 +1,21 @@
import React, { Fragment } from 'react';
import { View, Text } from 'react-native';
import React from 'react';
import { View, Platform } from 'react-native';
import get from 'lodash/get';
import { useIntl } from 'react-intl';
// Components
import { BasicHeader, Icon, MainButton, BoostPlaceHolder } from '../../../components';
import { BasicHeader, BoostPlaceHolder, ProductItemLine } from '../../../components';
// Container
import { InAppPurchaseContainer } from '../../../containers';
// Styles
import globalStyles from '../../../globalStyles';
import styles from './boostScreenStyles';
const DEALS = { '9999points': 'BEST DEAL!', '4999points': 'POPULAR!' };
const _renderDeal = item => {
if (DEALS[item.productId]) {
return (
<View style={styles.descriptionWrapper}>
<Fragment>
<Text style={styles.description}>{DEALS[item.productId]}</Text>
<View style={styles.triangle} />
</Fragment>
</View>
);
}
return null;
};
const ITEM_SKUS = Platform.select({
ios: ['099points', '199points', '499points', '999points', '4999points', '9999points'],
android: ['099points', '199points', '499points', '999points', '4999points', '9999points'],
});
const _getTitle = title => {
let _title = title.toUpperCase();
@ -44,7 +31,7 @@ const BoostScreen = () => {
const intl = useIntl();
return (
<InAppPurchaseContainer>
<InAppPurchaseContainer skus={ITEM_SKUS}>
{({ buyItem, productList, isLoading, isProcessing }) => (
<View style={globalStyles.container}>
<BasicHeader
@ -57,35 +44,14 @@ const BoostScreen = () => {
{isLoading ? (
<BoostPlaceHolder />
) : (
productList.map(item => (
<View style={styles.boostLine} key={get(item, 'productId')}>
{_renderDeal(item)}
<View style={styles.buttonWrapper}>
<MainButton
style={styles.button}
onPress={() => buyItem(item.productId)}
height={50}
text={intl.formatMessage({
id: 'boost.buy',
})}
isDisable={isProcessing}
isLoading={false}
>
<View style={styles.buttonContent}>
<Text style={styles.buttonText}>{_getTitle(get(item, 'title'))}</Text>
<View style={styles.buttonIconWrapper}>
<Icon name="add" iconType="MaterialIcons" color="#357ce6" size={23} />
</View>
</View>
</MainButton>
</View>
<View style={styles.priceWrapper}>
{get(item, 'localizedPrice', null) && (
<Text style={styles.priceText}>{get(item, 'localizedPrice', 0)}</Text>
)}
</View>
</View>
productList.map(product => (
<ProductItemLine
isLoading={isLoading}
disabled={isProcessing}
product={product}
title={_getTitle(get(product, 'title'))}
handleOnButtonPress={id => buyItem(id)}
/>
))
)}
</View>

View File

@ -11,6 +11,7 @@ import { Points } from './points';
import { Post } from './post';
import { SearchResult } from './searchResult';
import { Settings } from './settings';
import { SpinGame } from './spinGame/screen/spinGameScreen';
import Boost from './boost/screen/boostScreen';
import Profile from './profile/screen/profileScreen';
import ProfileEdit from './profileEdit/screen/profileEditScreen';
@ -39,6 +40,7 @@ export {
Redeem,
SearchResult,
Settings,
SpinGame,
SteemConnect,
Transfer,
Voters,

View File

@ -0,0 +1,32 @@
import React from 'react';
// Container
import { SpinGameContainer, InAppPurchaseContainer } from '../../../containers';
import { SpinGame } from '../../../components';
const SpinGameScreen = () => {
return (
<SpinGameContainer>
{({ startGame, score, gameRight, nextDate, isLoading, statusCheck }) => (
<InAppPurchaseContainer fetchData={statusCheck} skus={['499spins']}>
{({ buyItem, getItems, spinProduct, isProcessing }) => (
<SpinGame
buyItem={buyItem}
isLoading={isLoading}
score={score}
startGame={startGame}
gameRight={gameRight}
nextDate={nextDate}
getItems={getItems}
isProcessing={isProcessing}
spinProduct={spinProduct}
/>
)}
</InAppPurchaseContainer>
)}
</SpinGameContainer>
);
};
export { SpinGameScreen as SpinGame };

View File

@ -12,15 +12,21 @@ const THIS_MONTH = moment()
.startOf('day');
export const getTimeFromNow = (value, isWithoutUtc) => {
if (!value) return null;
if (!value) {
return null;
}
if (isWithoutUtc) return moment(value).fromNow();
if (isWithoutUtc) {
return moment(value).fromNow();
}
return moment.utc(value).fromNow();
};
export const getFormatedCreatedDate = value => {
if (!value) return null;
if (!value) {
return null;
}
return moment(value).format('DD MMM, YYYY');
};
@ -36,7 +42,9 @@ export const isThisWeek = value => moment(value).isSameOrAfter(THIS_WEEK);
export const isThisMonth = value => moment(value).isSameOrAfter(THIS_MONTH);
export const isEmptyContentDate = value => {
if (!value) return false;
if (!value) {
return false;
}
return parseInt(value.split('-')[0], 10) < 1980;
};

View File

@ -2693,7 +2693,12 @@ command-line-args@^4.0.6:
find-replace "^1.0.3"
typical "^2.6.1"
commander@^2.11.0, commander@^2.14.1, commander@^2.19.0, commander@^2.9.0, commander@~2.20.0:
commander@2.20.0:
version "2.20.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422"
integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==
commander@^2.11.0, commander@^2.14.1, commander@^2.19.0, commander@^2.9.0:
version "2.20.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.1.tgz#3863ce3ca92d0831dcf2a102f5fb4b5926afd0f9"
integrity sha512-cCuLsMhJeWQ/ZpsFTbE765kvVfoeSddc4nU3up4fV+fDBcfUXnbITJ+JzhkdjzOqhURjZgujxaioam4RM9yGUg==
@ -4627,9 +4632,9 @@ hoist-non-react-statics@^3.0.1, hoist-non-react-statics@^3.3.0:
react-is "^16.7.0"
hosted-git-info@^2.1.4:
version "2.8.4"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.4.tgz#44119abaf4bc64692a16ace34700fed9c03e2546"
integrity sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ==
version "2.8.5"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.5.tgz#759cfcf2c4d156ade59b0b2dfabddc42a6b9c70c"
integrity sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==
html-encoding-sniffer@^1.0.2:
version "1.0.2"
@ -7734,10 +7739,10 @@ react-native-gesture-handler@^1.4.1:
invariant "^2.2.4"
prop-types "^15.7.2"
react-native-iap@^3.3.8:
version "3.5.9"
resolved "https://registry.yarnpkg.com/react-native-iap/-/react-native-iap-3.5.9.tgz#1a49702f5c5ed444da9d3c7b8db9b5e7c7066deb"
integrity sha512-LtL6NX4y8JCg9pjUDxMz2om03r0PaJHfzpT6Wd437ck5sr+cIAbC8QH5MgXUkyoBin+MxTSX0w92TbuZAeHp1Q==
react-native-iap@3.4.15:
version "3.4.15"
resolved "https://registry.yarnpkg.com/react-native-iap/-/react-native-iap-3.4.15.tgz#0bf0f83c0591634ab562a5ec14d5a2b4e9c53c1d"
integrity sha512-hqGvG2UNWi5zrb/BLtQ0V4Hdd3Odo3s5GcAKHESv03ZzMPMoPYyTZ4rk6vRclDBnaybiij9Pb70KYH/KCWRTUA==
dependencies:
dooboolab-welcome "^1.1.1"
@ -9478,11 +9483,11 @@ uglify-es@^3.1.9:
source-map "~0.6.1"
uglify-js@^3.1.4:
version "3.6.0"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.6.0.tgz#704681345c53a8b2079fb6cec294b05ead242ff5"
integrity sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==
version "3.6.1"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.6.1.tgz#ae7688c50e1bdcf2f70a0e162410003cf9798311"
integrity sha512-+dSJLJpXBb6oMHP+Yvw8hUgElz4gLTh82XuX68QiJVTXaE5ibl6buzhNkQdYhBlIhozWOC9ge16wyRmjG4TwVQ==
dependencies:
commander "~2.20.0"
commander "2.20.0"
source-map "~0.6.1"
ultron@1.0.x: