This commit is contained in:
Mustafa Buyukcelebi 2019-07-18 09:44:43 +03:00
commit 0c55adfbf7
27 changed files with 1056 additions and 234 deletions

View File

@ -2,11 +2,8 @@
iOS ![iOS](https://build.appcenter.ms/v0.1/apps/ef80aa2a-d4e1-4f43-a4f8-be12ea72ba9b/branches/master/badge)
Android ![Android](https://build.appcenter.ms/v0.1/apps/12aace32-b58a-49da-bf85-5477f89ae16e/branches/master/badge)
### Production 🚀
[IOS](https://itunes.apple.com/cy/app/esteem-v2/id1451896376?l=tr&mt=8)
[ANDROID](https://play.google.com/store/apps/details?id=app.esteem.mobile)
![images](https://esteem.app/images/07.png)
## Open Beta 🔥
[IOS](https://install.appcenter.ms/orgs/esteem.app/apps/esteem-1/distribution_groups/beta_testers)

View File

@ -24,6 +24,7 @@
"@babel/runtime": "^7.1.2",
"@esteemapp/esteem-render-helpers": "^1.0.9",
"@react-native-community/netinfo": "3.2.1",
"@ptomasroos/react-native-multi-slider": "^1.0.0",
"appcenter": "^1.10.0",
"appcenter-analytics": "^1.10.0",
"appcenter-crashes": "^1.10.0",
@ -38,12 +39,13 @@
"dsteem": "^0.10.1",
"intl": "^1.2.5",
"jsc-android": "^236355.1.1",
"lodash": "^4.17.11",
"lodash": "^4.17.13",
"moment": "^2.22.2",
"react": "16.8.3",
"react-intl": "^2.7.2",
"react-native": "0.59.8",
"react-native-actionsheet": "^2.4.2",
"react-native-autocomplete-input": "^4.1.0",
"react-native-code-push": "esteemapp/react-native-code-push",
"react-native-config": "^0.11.5",
"react-native-datepicker": "^1.7.2",

View File

@ -1,23 +1,27 @@
import Logo from './logo/logo';
import { FormInput } from './formInput';
import { CircularButton, TextButton, IconButton } from './buttons';
import { FormInput } from './formInput';
import { NumericKeyboard } from './numericKeyboard';
import { PinAnimatedInput } from './pinAnimatedInput';
import { SideMenu } from './sideMenu';
import Modal from './modal';
import { TextInput } from './textInput';
import Icon from './icon';
import Logo from './logo/logo';
import Modal from './modal';
import ScaleSlider from './scaleSlider/scaleSliderView';
import UserListItem from './basicUIElements/view/userListItem/userListItem';
export {
Logo,
UserListItem,
FormInput,
CircularButton,
TextButton,
FormInput,
Icon,
IconButton,
Logo,
Modal,
NumericKeyboard,
PinAnimatedInput,
ScaleSlider,
SideMenu,
Modal,
Icon,
TextButton,
TextInput,
UserListItem,
};

View File

@ -1,174 +0,0 @@
import React, { Component } from 'react';
import { Alert } from 'react-native';
import { connect } from 'react-redux';
import get from 'lodash/get';
// Services and Actions
import { getUser, getUserPoints, claim } from '../../../providers/esteem/ePoint';
import { openPinCodeModal } from '../../../redux/actions/applicationActions';
// Constant
import POINTS from '../../../constants/options/points';
// Component
import PointsView from '../view/pointsView';
// Constants
import ROUTES from '../../../constants/routeNames';
/*
* 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,
};
}
// Component Life Cycle Functions
componentDidMount() {
const { username, isConnected } = this.props;
if (isConnected) {
this._fetchuserPointActivities(username);
}
this.fetchInterval = setInterval(this._fetchuserPointActivities, 6 * 60 * 1000);
}
componentWillReceiveProps(nextProps) {
const { username } = this.props;
if (
nextProps.isConnected &&
((nextProps.activeBottomTab === ROUTES.TABBAR.POINTS && nextProps.username) ||
(nextProps.username !== username && nextProps.username))
) {
this._fetchuserPointActivities(nextProps.username);
}
}
componentWillUnmount() {
clearInterval(this.fetchInterval);
}
// Component Functions
_handleOnPressTransfer = () => {
const { dispatch } = this.props;
const { userPoints } = this.state;
dispatch(
openPinCodeModal({
navigateTo: ROUTES.SCREENS.TRANSFER,
navigateParams: {
transferType: 'points',
fundType: 'POINT',
balance: Math.round(get(userPoints, 'points') * 1000) / 1000,
},
}),
);
};
_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'),
}));
_fetchuserPointActivities = async username => {
if (!username) return;
this.setState({ refreshing: true });
await getUser(username)
.then(userPoints => {
this.setState({ userPoints });
})
.catch(err => {
Alert.alert(err);
});
await getUserPoints(username)
.then(userActivities => {
if (Object.entries(userActivities).length !== 0) {
this.setState({
userActivities: this._groomUserActivities(userActivities),
});
}
})
.catch(err => {
Alert.alert(err);
});
this.setState({
refreshing: false,
isLoading: false,
});
};
_claimPoints = async () => {
const { username } = this.props;
this.setState({ isClaiming: true });
await claim(username)
.then(() => {
this._fetchuserPointActivities(username);
})
.catch(error => {
Alert.alert(
`Fetching data from server failed, please try again or notify us at info@esteem.app \n${error.message.substr(
0,
20,
)}`,
);
});
this.setState({ isClaiming: false });
};
render() {
const {
isClaiming,
isDarkTheme,
isLoading,
refreshing,
userActivities,
userPoints,
} = this.state;
return (
<PointsView
claimPoints={this._claimPoints}
fetchUserActivity={this._fetchuserPointActivities}
isClaiming={isClaiming}
isDarkTheme={isDarkTheme}
isLoading={isLoading}
refreshing={refreshing}
userActivities={userActivities}
userPoints={userPoints}
handleOnPressTransfer={this._handleOnPressTransfer}
/>
);
}
}
const mapStateToProps = state => ({
username: state.account.currentAccount.name,
isDarkTheme: state.application.isDarkTheme,
activeBottomTab: state.ui.activeBottomTab,
isConnected: state.application.isConnected,
});
export default connect(mapStateToProps)(PointsContainer);

View File

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

View File

@ -88,7 +88,7 @@ class PointsView extends Component {
<DropdownButton
isHasChildIcon
iconName="arrow-drop-down"
options={['Transfer']}
options={['Transfer', 'Promote']}
noHighlight
dropdownButtonStyle={styles.dropdownButtonStyle}
onSelect={handleOnPressTransfer}

View File

@ -0,0 +1,51 @@
import EStyleSheet from 'react-native-extended-stylesheet';
export default EStyleSheet.create({
active: {
textAlign: 'center',
fontSize: 17,
bottom: 10,
color: '$primaryDarkText',
},
inactive: {
flex: 1,
textAlignVertical: 'center',
textAlign: 'center',
fontWeight: 'normal',
color: '$primaryLightBackground',
},
line: {
width: 14,
height: 14,
backgroundColor: '$primaryBlue',
borderRadius: 7,
borderWidth: 0,
top: 12,
zIndex: 999,
},
container: {
justifyContent: 'center',
alignItems: 'center',
},
column: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
bottom: -20,
},
marker: {
width: 35,
height: 35,
borderRadius: 17,
backgroundColor: '$white',
borderWidth: 1,
position: 'absolute',
borderColor: '$primaryBlue',
},
selected: {
backgroundColor: '$primaryBlue',
},
track: {
backgroundColor: '$primaryLightGray',
},
});

View File

@ -0,0 +1,82 @@
import React, { Component } from 'react';
import { View, Dimensions, Text } from 'react-native';
import MultiSlider from '@ptomasroos/react-native-multi-slider';
import get from 'lodash/get';
import styles from './scaleSliderStyles';
export default class ScaleSliderView extends Component {
constructor(props) {
super(props);
this.state = {
activeValue: get(props, 'activeValue') || props.values[0],
activeIndex: get(props, 'values', '').indexOf(get(props, 'activeValue')) || 0,
};
}
_renderMarker = () => <View style={styles.marker} />;
_valueChange = _values => {
const { handleOnValueChange, values } = this.props;
const index = _values[0] - 1;
this.setState({
activeValue: values && values[index],
activeIndex: index,
});
if (handleOnValueChange) handleOnValueChange(values[index]);
};
_renderItem = (value, index, activeIndex) => {
const isActive = index <= activeIndex || index === 0;
return (
<View>
<Text style={[isActive ? styles.active : styles.inactive]}>{value}</Text>
<View style={[isActive ? styles.line : {}]} />
</View>
);
};
_renderScale = () => {
const { values } = this.props;
const { activeIndex } = this.state;
const items = [];
for (let i = 1; i <= values.length; i++) {
items.push(this._renderItem(values[i - 1], i - 1, activeIndex));
}
return items;
};
render() {
const { LRpadding, values } = this.props;
const { activeIndex } = this.state;
return (
<View>
<View style={[styles.column, { marginLeft: LRpadding, marginRight: LRpadding }]}>
{this._renderScale()}
</View>
<View style={styles.container}>
<MultiSlider
trackStyle={styles.track}
selectedStyle={styles.selected}
sliderLength={Dimensions.get('window').width - LRpadding * 2}
onValuesChange={this._valueChange}
values={[activeIndex + 1]}
min={1}
max={values && values.length}
step={1}
allowOverlap={false}
customMarker={this._renderMarker}
snapped
/>
</View>
</View>
);
}
}

View File

@ -0,0 +1,19 @@
import EStyleSheet from 'react-native-extended-stylesheet';
export default EStyleSheet.create({
optionsWrapper: {
backgroundColor: '$white',
borderTopWidth: 0,
borderColor: '$black',
left: 0,
position: 'absolute',
right: 0,
top: 50,
zIndex: 999,
flex: 1,
},
input: {
flex: 1,
minHeight: 50,
},
});

View File

@ -2,8 +2,16 @@ import React from 'react';
import { TextInput } from 'react-native';
import { connect } from 'react-redux';
// Styles
import styles from './textInputStyles';
const TextInputView = ({ isDarkTheme, innerRef, ...props }) => (
<TextInput ref={innerRef} keyboardAppearance={isDarkTheme ? 'dark' : 'light'} {...props} />
<TextInput
style={styles.input}
ref={innerRef}
keyboardAppearance={isDarkTheme ? 'dark' : 'light'}
{...props}
/>
);
const mapStateToProps = state => ({

View File

@ -308,5 +308,12 @@
"incoming_funds": "Incoming Funds",
"sc_power_down_error": "Steem connect setWithdrawVestingRoute not implemented yet.",
"estimated_weekly": "Estimated Weekly"
},
"promote": {
"title": "Promote",
"days": "days",
"user": "User",
"permlink": "Permlink",
"information": "Are you sure to transfer to promote?"
}
}

View File

@ -107,3 +107,24 @@ export const POINTS_KEYS = [
type: 30,
},
];
export const PROMOTE_PRICING = [
{ duration: 1, price: 150 },
{ duration: 2, price: 250 },
{ duration: 3, price: 350 },
{ duration: 7, price: 500 },
{ duration: 14, price: 1000 },
];
export const PROMOTE_DAYS = [1, 2, 3, 7, 14];
export const PROMOTE_STATUS_PENDING = 1;
export const PROMOTE_STATUS_SUCCESS = 2;
export const PROMOTE_STATUS_USER_ERR = 3;
export const PROMOTE_STATUS_INSUFFICIENT_ERR = 4;
export const PROMOTE_STATUS_POST_ERR = 5;
export const PROMOTE_STATUS_POST_DUPLICATE = 6;
export const PROMOTE_STATUS_FORMAT_ERR = 7;
export const PROMOTED_POST_STATUS_ON = 1;
export const PROMOTED_POST_STATUS_EXPIRED = 2;
export const PROMOTED_POST_STATUS_DISABLED = 3;

View File

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

4
src/containers/index.js Normal file
View File

@ -0,0 +1,4 @@
import PointsContainer from './pointsContainer';
import TransferContainer from './transferContainer';
export { PointsContainer, TransferContainer };

View File

@ -0,0 +1,248 @@
import React, { 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";
// Services and Actions
import { getUser, getUserPoints, claim } from "../providers/esteem/ePoint";
import { openPinCodeModal } from "../redux/actions/applicationActions";
import { promote, getAccount } from "../providers/steem/dsteem";
import { getUserDataWithUsername } from "../realm/realm";
import { toastNotification } from "../redux/actions/uiAction";
// Constant
import POINTS from "../constants/options/points";
// Constants
import ROUTES from "../constants/routeNames";
/*
* 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
};
}
// Component Life Cycle Functions
componentDidMount() {
const { username, isConnected } = this.props;
if (isConnected) {
this._fetchuserPointActivities(username);
this.fetchInterval = setInterval(
this._fetchuserPointActivities,
6 * 60 * 1000
);
}
}
componentWillReceiveProps(nextProps) {
const { username } = this.props;
if (
nextProps.isConnected &&
((nextProps.activeBottomTab === ROUTES.TABBAR.POINTS &&
nextProps.username) ||
(nextProps.username !== username && nextProps.username))
) {
this._fetchuserPointActivities(nextProps.username);
}
}
componentWillUnmount() {
clearInterval(this.fetchInterval);
}
// Component Functions
_handleOnPressTransfer = index => {
const { dispatch } = this.props;
const { balance } = this.state;
let navigateTo;
let navigateParams;
switch (Number(index)) {
case 0:
navigateTo = ROUTES.SCREENS.TRANSFER;
navigateParams = {
transferType: "points",
fundType: "POINT",
balance
};
break;
case 1:
navigateTo = ROUTES.SCREENS.PROMOTE;
navigateParams = {
balance
};
break;
default:
break;
}
dispatch(
openPinCodeModal({
navigateTo,
navigateParams
})
);
};
_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")
}));
_fetchuserPointActivities = async username => {
if (!username) return;
this.setState({ refreshing: true });
await getUser(username)
.then(userPoints => {
const balance = Math.round(get(userPoints, "points") * 1000) / 1000;
this.setState({ userPoints, balance });
})
.catch(err => {
Alert.alert(err.message);
});
await getUserPoints(username)
.then(userActivities => {
if (Object.entries(userActivities).length !== 0) {
this.setState({
userActivities: this._groomUserActivities(userActivities)
});
}
})
.catch(err => {
Alert.alert(err);
});
this.setState({
refreshing: false,
isLoading: false
});
};
_getUserBalance = async username => {
await getUser(username)
.then(userPoints => {
const balance = Math.round(get(userPoints, "points") * 1000) / 1000;
return balance;
})
.catch(err => {
Alert.alert(err);
});
};
_claimPoints = async () => {
const { username } = this.props;
this.setState({ isClaiming: true });
await claim(username)
.then(() => {
this._fetchuserPointActivities(username);
})
.catch(error => {
Alert.alert(
`Fetching data from server failed, please try again or notify us at info@esteem.app \n${error.message.substr(
0,
20
)}`
);
});
this.setState({ isClaiming: false });
};
_promote = async (duration, permlink, author, user) => {
const { currentAccount, pinCode, dispatch, intl, navigation } = this.props;
this.setState({ isLoading: true });
await promote(user || currentAccount, pinCode, duration, permlink, author)
.then(() => {
this.setState({ isLoading: false });
dispatch(
toastNotification(intl.formatMessage({ id: "alert.successful" }))
);
navigation.goBack();
})
.catch(error => {
Alert.alert(
`Fetching data from server failed, please try again or notify us at info@esteem.app \n${error.message.substr(
0,
20
)}`
);
});
};
render() {
const {
isClaiming,
isDarkTheme,
isLoading,
refreshing,
userActivities,
userPoints,
balance
} = this.state;
const { children, accounts, currentAccount } = this.props;
return (
children &&
children({
accounts,
currentAccount,
currentAccountName: currentAccount.name,
claimPoints: this._claimPoints,
fetchUserActivity: this._fetchuserPointActivities,
isClaiming,
isDarkTheme,
isLoading,
refreshing,
userActivities,
userPoints,
handleOnPressTransfer: this._handleOnPressTransfer,
balance,
getUserBalance: this._getUserBalance,
promote: this._promote,
getAccount,
getUserDataWithUsername
})
);
}
}
const mapStateToProps = state => ({
username: state.account.currentAccount.name,
isDarkTheme: state.application.isDarkTheme,
activeBottomTab: state.ui.activeBottomTab,
isConnected: state.application.isConnected,
accounts: state.account.otherAccounts,
currentAccount: state.account.currentAccount,
pinCode: state.account.pin
});
export default withNavigation(
connect(mapStateToProps)(injectIntl(PointsContainer))
);

View File

@ -14,12 +14,12 @@ import {
withdrawVesting,
delegateVestingShares,
setWithdrawVestingRoute,
} from '../../../providers/steem/dsteem';
import { toastNotification } from '../../../redux/actions/uiAction';
import { getUserDataWithUsername } from '../../../realm/realm';
} from '../providers/steem/dsteem';
import { toastNotification } from '../redux/actions/uiAction';
import { getUserDataWithUsername } from '../realm/realm';
// Utils
import { countDecimals } from '../../../utils/number';
import { countDecimals } from '../utils/number';
/*
* Props Name Description Value

View File

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

View File

@ -1,12 +1,6 @@
import { Alert } from 'react-native';
import { Client, PrivateKey } from 'dsteem';
import ePointApi from '../../config/ePoint';
// Utils
import { decryptKey } from '../../utils/crypto';
// const client = new Client(getItem('server', 'https://api.steemit.com'));
export const userActivity = (us, ty, bl = '', tx = '') =>
new Promise(resolve => {
const params = { us, ty };

View File

@ -226,6 +226,20 @@ export const search = data =>
});
});
export const searchPath = q =>
new Promise((resolve, reject) => {
searchApi
.post('/search-path', {
q,
})
.then(res => {
resolve(res.data);
})
.catch(error => {
reject(error);
});
});
// Schedule
export const schedule = (
user,

View File

@ -1108,6 +1108,60 @@ export const transferPoint = (currentAccount, pinCode, data) => {
return Promise.reject(new Error('Something went wrong!'));
};
export const promote = (currentAccount, pinCode, duration, permlink, author) => {
const pin = getDigitPinCode(pinCode);
const key = getActiveKey(get(currentAccount, 'local'), pin);
if (key) {
const privateKey = PrivateKey.fromString(key);
const user = get(currentAccount, 'name');
const json = {
id: 'esteem_promote',
json: JSON.stringify({
user,
author,
permlink,
duration,
}),
required_auths: [user],
required_posting_auths: [],
};
return client.broadcast.json(json, privateKey);
}
return Promise.reject(new Error('Something went wrong!'));
};
export const boost = (currentAccount, pinCode, point, permlink, author) => {
const pin = getDigitPinCode(pinCode);
const key = getActiveKey(get(currentAccount, 'local'), pin);
if (key) {
const privateKey = PrivateKey.fromString(key);
const user = get(currentAccount, 'name');
const json = {
id: 'esteem_boost',
json: JSON.stringify({
user,
author,
permlink,
amount: `${point} POINT`,
}),
required_auths: [user],
required_posting_auths: [],
};
return client.broadcast.json(json, privateKey);
}
return Promise.reject(new Error('Something went wrong!'));
};
// HELPERS
const getAnyPrivateKey = (local, pin) => {
const { postingKey, activeKey } = local;

View File

@ -1,20 +1,21 @@
import { Bookmarks } from './bookmarks';
import { Drafts } from './drafts';
import { Editor } from './editor';
import { Follows } from './follows';
import { Home } from './home';
import { Launch } from './launch';
import { Login } from './login';
import { Points } from './points';
import { Notification } from './notification';
import { PinCode } from './pinCode';
import { Post } from './post';
import { Profile } from './profile';
import { Settings } from './settings';
import { Voters } from './voters';
import SteemConnect from './steem-connect/steemConnect';
import { SearchResult } from './searchResult';
import Transfer from './transfer';
import { Bookmarks } from "./bookmarks";
import { Drafts } from "./drafts";
import { Editor } from "./editor";
import { Follows } from "./follows";
import { Home } from "./home";
import { Launch } from "./launch";
import { Login } from "./login";
import { Points } from "./points";
import { Notification } from "./notification";
import { PinCode } from "./pinCode";
import { Post } from "./post";
import { Profile } from "./profile";
import { Settings } from "./settings";
import { Voters } from "./voters";
import SteemConnect from "./steem-connect/steemConnect";
import { SearchResult } from "./searchResult";
import Transfer from "./transfer";
import Promote from "./promote/screen/promoteScreen";
export {
Bookmarks,
@ -24,14 +25,15 @@ export {
Home,
Launch,
Login,
Points,
Notification,
PinCode,
Points,
Post,
Profile,
Promote,
SearchResult,
Settings,
SteemConnect,
Voters,
SearchResult,
Transfer,
Voters
};

View File

@ -1,7 +1,9 @@
import React, { PureComponent } from 'react';
import { injectIntl } from 'react-intl';
import { View } from 'react-native';
// Constants
// Containers
import { PointsContainer } from '../../../containers';
// Components
import { Header } from '../../../components/header';
@ -33,7 +35,31 @@ class PointsScreen extends PureComponent {
<View style={styles.container}>
<Header />
{isLoggedIn ? (
<Points />
<PointsContainer>
{({
handleOnPressTransfer,
claimPoints,
fetchUserActivity,
isClaiming,
isDarkTheme,
isLoading,
refreshing,
userActivities,
userPoints,
}) => (
<Points
claimPoints={claimPoints}
fetchUserActivity={fetchUserActivity}
isClaiming={isClaiming}
isDarkTheme={isDarkTheme}
isLoading={isLoading}
refreshing={refreshing}
userActivities={userActivities}
userPoints={userPoints}
handleOnPressTransfer={handleOnPressTransfer}
/>
)}
</PointsContainer>
) : (
<NoPost
style={styles.noPostContainer}

View File

@ -0,0 +1,282 @@
import React, { PureComponent, Fragment } from 'react';
import { injectIntl } from 'react-intl';
import { Text, View, WebView, ScrollView, TouchableOpacity, Alert } from 'react-native';
import get from 'lodash/get';
import ActionSheet from 'react-native-actionsheet';
import Autocomplete from 'react-native-autocomplete-input';
import { ScaleSlider, TextInput } from '../../../components';
import { steemConnectOptions } from '../../../constants/steemConnectOptions';
// Container
import { PointsContainer } from '../../../containers';
// Services and Actions
import { getUser } from '../../../providers/esteem/ePoint';
import { searchPath } from '../../../providers/esteem/esteem';
// Components
import { BasicHeader } from '../../../components/basicHeader';
import { TransferFormItem } from '../../../components/transferFormItem';
import { MainButton } from '../../../components/mainButton';
import { DropdownButton } from '../../../components/dropdownButton';
import { Modal } from '../../../components/modal';
import { PROMOTE_PRICING, PROMOTE_DAYS } from '../../../constants/options/points';
// Styles
import styles from './promoteStyles';
class PointsScreen extends PureComponent {
/* Props
* ------------------------------------------------
* @prop { type } name - Description....
*/
constructor(props) {
super(props);
this.state = {
permlink: '',
selectedUser: '',
balance: '',
day: 1,
isSCModalOpen: false,
SCPath: '',
permlinkSuggestions: [],
};
this.startActionSheet = React.createRef();
}
// Component Life Cycles
// Component Functions
_handleOnPermlinkChange = async text => {
searchPath(text).then(res => {
this.setState({ permlinkSuggestions: res && res.length > 10 ? res.slice(0, 7) : res });
});
if (!text || (text && text.length < 1)) this.setState({ permlinkSuggestions: [] });
this.setState({ permlink: text });
};
_renderDescription = text => <Text style={styles.description}>{text}</Text>;
_renderDropdown = (accounts, currentAccountName) => (
<DropdownButton
dropdownButtonStyle={styles.dropdownButtonStyle}
rowTextStyle={styles.rowTextStyle}
style={styles.dropdown}
dropdownStyle={styles.dropdownStyle}
textStyle={styles.dropdownText}
options={accounts.map(item => item.username)}
defaultText={currentAccountName}
selectedOptionIndex={accounts.findIndex(item => item.username === currentAccountName)}
onSelect={(index, value) => {
this.setState({ selectedUser: value });
this._getUserBalance(value);
}}
/>
);
_getUserBalance = async username => {
await getUser(username)
.then(userPoints => {
const balance = Math.round(get(userPoints, 'points') * 1000) / 1000;
this.setState({ balance });
})
.catch(err => {
Alert.alert(err);
});
};
_promote = async (promote, currentAccount, getUserDataWithUsername) => {
const { day, permlink, selectedUser } = this.state;
let _author;
let _permlink;
const seperatedPermlink = permlink.split('/');
if (seperatedPermlink && seperatedPermlink.length > 0) {
_author = seperatedPermlink[0];
_permlink = seperatedPermlink[1];
}
if (get(currentAccount, 'local.authType') === 'steemConnect') {
// const user = selectedUser;
const json = JSON.stringify({
user: selectedUser,
_author,
_permlink,
duration: day,
});
const uri = `sign/custom-json?authority=active&required_auths=%5B%22${selectedUser}%22%5D&required_posting_auths=%5B%5D&id=esteem_promote&json=${encodeURIComponent(
json,
)}`;
this.setState({
isSCModalOpen: true,
SCPath: uri,
});
} else if (promote) {
let userFromRealm;
if (selectedUser) {
userFromRealm = await getUserDataWithUsername(selectedUser);
}
const user = userFromRealm
? {
name: selectedUser,
local: userFromRealm[0],
}
: currentAccount;
promote(day, _permlink, _author, user);
}
};
render() {
const { intl } = this.props;
const {
selectedUser,
balance,
day,
SCPath,
isSCModalOpen,
permlinkSuggestions,
permlink,
} = this.state;
return (
<PointsContainer>
{({
isLoading,
accounts,
currentAccountName,
balance: _balance,
promote,
currentAccount,
getUserDataWithUsername,
}) => (
<Fragment>
<BasicHeader title={intl.formatMessage({ id: 'promote.title' })} />
<View style={styles.container}>
<ScrollView>
<View style={styles.middleContent}>
<TransferFormItem
label={intl.formatMessage({ id: 'promote.user' })}
rightComponent={() =>
this._renderDropdown(accounts, selectedUser || currentAccountName)
}
/>
<Text style={styles.balanceText}>{`${balance || _balance} eSteem Points`}</Text>
<Fragment>
<View style={styles.autocomplateLineContainer}>
<View style={styles.autocomplateLabelContainer}>
{
<Text style={styles.autocomplateLabelText}>
{intl.formatMessage({ id: 'promote.permlink' })}
</Text>
}
</View>
<Autocomplete
autoCapitalize="none"
autoCorrect={false}
inputContainerStyle={styles.autocomplate}
data={permlinkSuggestions}
listContainerStyle={styles.autocomplateListContainer}
listStyle={styles.autocomplateList}
onChangeText={text => this._handleOnPermlinkChange(text)}
renderTextInput={() => (
<TextInput
style={styles.input}
onChangeText={text => this._handleOnPermlinkChange(text)}
value={permlink}
placeholder={intl.formatMessage({ id: 'promote.permlink' })}
placeholderTextColor="#c1c5c7"
autoCapitalize="none"
/>
)}
renderItem={({ item }) => (
<TouchableOpacity
key={item}
onPress={() =>
this.setState({ permlink: item, permlinkSuggestions: [] })
}
>
<Text style={styles.autocomplateItemText}>{item}</Text>
</TouchableOpacity>
)}
/>
</View>
</Fragment>
<View style={styles.total}>
<Text style={styles.day}>
{`${day} ${intl.formatMessage({
id: 'promote.days',
})} `}
</Text>
<Text style={styles.price}>
{`${get(PROMOTE_PRICING[PROMOTE_DAYS.indexOf(day)], 'price')} eSteem points`}
</Text>
</View>
<ScaleSlider
values={[1, 2, 3, 7, 14]}
LRpadding={50}
activeValue={day}
handleOnValueChange={day => this.setState({ day })}
single
/>
</View>
<View style={styles.bottomContent}>
<MainButton
style={styles.button}
isDisable={isLoading}
onPress={() => this.startActionSheet.current.show()}
isLoading={isLoading}
>
<Text style={styles.buttonText}>
{intl.formatMessage({ id: 'transfer.next' })}
</Text>
</MainButton>
</View>
</ScrollView>
</View>
<ActionSheet
ref={this.startActionSheet}
options={[
intl.formatMessage({ id: 'alert.confirm' }),
intl.formatMessage({ id: 'alert.cancel' }),
]}
title={intl.formatMessage({ id: 'promote.information' })}
cancelButtonIndex={1}
destructiveButtonIndex={0}
onPress={index => {
index === 0
? this._promote(promote, currentAccount, getUserDataWithUsername)
: null;
}}
/>
<Modal
isOpen={isSCModalOpen}
isFullScreen
isCloseButton
handleOnModalClose={() => this.setState({ isSCModalOpen: false })}
title={intl.formatMessage({ id: 'transfer.steemconnect_title' })}
>
<WebView source={{ uri: `${steemConnectOptions.base_url}${SCPath}` }} />
</Modal>
</Fragment>
)}
</PointsContainer>
);
}
}
export default injectIntl(PointsScreen);

View File

@ -0,0 +1,162 @@
import EStyleSheet from 'react-native-extended-stylesheet';
export default EStyleSheet.create({
container: {
flex: 1,
backgroundColor: '$primaryBackgroundColor',
justifyContent: 'center',
},
balanceText: {
fontSize: 14,
color: '$primaryDarkGray',
alignSelf: 'center',
marginLeft: 70,
marginBottom: 10,
},
topContent: {
flexDirection: 'row',
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
middleContent: {
flex: 3,
justifyContent: 'center',
},
bottomContent: {
flex: 2,
justifyContent: 'center',
alignItems: 'center',
zIndex: -1,
},
input: {
borderWidth: 1,
borderColor: '$borderColor',
borderRadius: 8,
padding: 10,
color: '$primaryBlack',
width: 172,
},
autocomplate: {
borderWidth: 0,
borderColor: '$borderColor',
borderRadius: 8,
padding: 2,
color: '$primaryBlack',
width: 172,
marginRight: 33,
},
autocomplateLineContainer: {
flexDirection: 'row',
paddingHorizontal: 20,
zIndex: 999,
},
autocomplateLabelText: {
color: '$primaryBlack',
fontWeight: '600',
},
autocomplateListContainer: {
backgroundColor: '$primaryWhiteLightBackground',
width: 172,
zIndex: 999,
},
autocomplateItemText: {
color: '$primaryBlack',
padding: 3,
},
autocomplateList: {
zIndex: 999,
backgroundColor: '$primaryWhiteLightBackground',
},
autocomplateLabelContainer: {
flex: 1,
padding: 10,
justifyContent: 'center',
color: '$primaryBlack',
},
textarea: {
borderWidth: 1,
borderColor: '$borderColor',
borderRadius: 8,
padding: 10,
color: '$primaryBlack',
width: 172,
height: 75,
},
description: {
color: '$iconColor',
},
button: {
width: '$deviceWidth / 3',
marginTop: 30,
justifyContent: 'center',
alignItems: 'center',
},
buttonText: {
color: 'white',
},
icon: {
fontSize: 40,
color: '$iconColor',
marginHorizontal: 20,
},
rowTextStyle: {
fontSize: 12,
color: '$primaryDarkGray',
padding: 5,
},
dropdownText: {
fontSize: 14,
paddingLeft: 16,
paddingHorizontal: 14,
color: '$primaryDarkGray',
},
dropdownStyle: {
marginTop: 15,
minWidth: 192,
width: 192,
maxHeight: '$deviceHeight - 200',
},
dropdownButtonStyle: {
borderColor: '$borderColor',
borderWidth: 1,
height: 44,
width: 172,
borderRadius: 8,
marginHorizontal: 2,
},
dropdown: {
flexGrow: 1,
width: 150,
},
slider: {
flex: 1,
marginHorizontal: 30,
},
amountText: {
color: '$primaryBlue',
},
iconButton: {
borderColor: 'red',
borderWidth: 1,
width: 25,
height: 25,
borderRadius: 5,
},
total: {
marginVertical: 15,
flexDirection: 'row',
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
day: {
fontSize: 22,
color: '$primaryBlue',
fontWeight: 'bold',
},
price: {
fontSize: 15,
color: '$primaryBlue',
},
});

View File

@ -1,6 +1,6 @@
import React from 'react';
import TransferContainer from './container/transferContainer';
import { TransferContainer } from '../../containers';
import TransferView from './screen/transferScreen';
import PowerDownView from './screen/powerDownScreen';

View File

@ -16,6 +16,7 @@ import { Icon } from '../../../components/icon';
import { Modal } from '../../../components/modal';
import styles from './transferStyles';
/* Props
* ------------------------------------------------
* @prop { type } name - Description....

View File

@ -1256,6 +1256,11 @@
"@types/istanbul-reports" "^1.1.1"
"@types/yargs" "^12.0.9"
"@ptomasroos/react-native-multi-slider@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@ptomasroos/react-native-multi-slider/-/react-native-multi-slider-1.0.0.tgz#a4ea27b1320b93a1db9f696c24789695df6c9f30"
integrity sha512-NpX22rQLArg9widwMzGf7XsInTDf6mfY/D1XaDVjglNkVphj3NSN37+nF6MofArCxC++1P+jHv0SGWbmJQwy4g==
"@react-native-community/cli@^1.2.1":
version "1.9.11"
resolved "https://registry.yarnpkg.com/@react-native-community/cli/-/cli-1.9.11.tgz#b868b17201057b9cd16a3a20c30561176071f21a"
@ -5756,14 +5761,14 @@ lodash.isstring@^4.0.1:
integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=
lodash.merge@^4.6.0:
version "4.6.1"
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.1.tgz#adc25d9cb99b9391c59624f379fbba60d7111d54"
integrity sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ==
version "4.6.2"
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
lodash.mergewith@^4.6.1:
version "4.6.1"
resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz#639057e726c3afbdb3e7d42741caa8d6e4335927"
integrity sha512-eWw5r+PYICtEBgrBE5hhlT6aAa75f411bgDz/ZL2KZqYV03USvucsxcHUIlGTDTECs1eunpI7HOV7U+WLDvNdQ==
version "4.6.2"
resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55"
integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==
lodash.pad@^4.1.0:
version "4.5.1"
@ -5795,10 +5800,10 @@ lodash.unescape@4.0.1:
resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c"
integrity sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=
lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0, lodash@^4.6.1:
version "4.17.11"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0, lodash@^4.6.1:
version "4.17.13"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.13.tgz#0bdc3a6adc873d2f4e0c4bac285df91b64fc7b93"
integrity sha512-vm3/XWXfWtRua0FkUyEHBZy8kCPjErNBT9fJx8Zvs+U6zjqPbTUOpkaoum3O5uiA8sm+yNMHXfYkTUHFoMxFNA==
log-symbols@^1.0.2:
version "1.0.2"
@ -7492,6 +7497,11 @@ react-native-actionsheet@^2.4.2:
resolved "https://registry.yarnpkg.com/react-native-actionsheet/-/react-native-actionsheet-2.4.2.tgz#6a00dd51a75ef2c8974312130e405af73191500f"
integrity sha512-DBoWIvVwuWXuptF4t46pBqkFxaUxS+rsIdHiA05t0n4BdTIDV2R4s9bLEUVOGzb94D7VxIamsXZPA/3mmw+SXg==
react-native-autocomplete-input@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/react-native-autocomplete-input/-/react-native-autocomplete-input-4.1.0.tgz#979ece28d891b245ecb967b6d31f1f924445b8ab"
integrity sha512-Yn4GulZ9F6tde74UUGZHdVFeYWVuL7+EbUZy6kt+QHrzMc5B4OuRop1FT4RyWLpvbySW/vvqYgj9LAmlzkuEqA==
react-native-code-push@esteemapp/react-native-code-push:
version "1000.0.0-beta"
resolved "https://codeload.github.com/esteemapp/react-native-code-push/tar.gz/c07b7023c1212dc5d9231a0526a869d2501cb221"