Merge branch 'master' of https://github.com/esteemapp/esteem-mobile into markdownToHtml

This commit is contained in:
u-e 2018-12-27 15:17:17 +03:00
commit 4562771a83
34 changed files with 697 additions and 580 deletions

View File

@ -5,3 +5,4 @@ OLD_IMAGE_API=
SEARCH_API_TOKEN=
SEARCH_API_URL=
SERVER_LIST_API=
PIN_KEY=

View File

@ -2,6 +2,8 @@
printf "post-clone.sh\n"
node -v
# please specify required Node.js version
NODE_VERSION=8.12.0
@ -10,3 +12,7 @@ npm config delete prefix
. ~/.bashrc
nvm install "$NODE_VERSION"
nvm alias node8 "$NODE_VERSION"
nvm alias default v8.12.0
node -v
printf "end of post-clone.sh\n"

View File

@ -3,7 +3,7 @@
printf "Old .env file:\n"
cat .env
printf "Started script:\n"
ENV_WHITELIST=${ENV_WHITELIST:-"/ACTIVITY|WEBSOCKET|BACKEND|API|TOKEN|URL/"}
ENV_WHITELIST=${ENV_WHITELIST:-"/ACTIVITY|WEBSOCKET|BACKEND|API|TOKEN|PIN|URL/"}
printf "Creating an .env file with the following whitelist:\n"
printf "%s\n\n" $ENV_WHITELIST
set | egrep -e $ENV_WHITELIST | egrep -v "^_" | egrep -v "WHITELIST" | egrep -v "USER-DEFINED" > .env

View File

@ -4,19 +4,27 @@ import { Icon } from '../../../icon';
import styles from './textWithIconStyles';
const TextWithIcon = ({
iconName, text, isClickable, onPress, iconStyle, iconType,
iconName, text, isClickable, onPress, iconStyle, iconType, iconSize,
}) => (
<View style={styles.container}>
{isClickable || onPress ? (
<TouchableHighlight underlayColor="transparent" onPress={() => onPress && onPress()}>
<View style={styles.wrapper}>
<Icon style={[styles.icon, iconStyle]} name={iconName} iconType={iconType} />
<Icon
style={[styles.icon, iconStyle, iconSize && { fontSize: iconSize }]}
name={iconName}
iconType={iconType}
/>
<Text style={[styles.text]}>{text}</Text>
</View>
</TouchableHighlight>
) : (
<View style={styles.wrapper}>
<Icon style={[styles.icon, iconStyle]} name={iconName} iconType={iconType} />
<Icon
style={[styles.icon, iconStyle, iconSize && { fontSize: iconSize }]}
name={iconName}
iconType={iconType}
/>
<Text style={styles.text}>{text}</Text>
</View>
)}

View File

@ -54,7 +54,7 @@ const WalletLineItem = ({
</Text>
</View>
)}
{description && (
{!!description && (
<Text style={[styles.description, !iconName && styles.onlyText]}>{description}</Text>
)}
</View>

View File

@ -57,9 +57,9 @@ class CollapsibleCardView extends PureComponent {
_getMinValue = () => 0;
_toggleOnPress = () => {
const { handleOnExpanded } = this.props;
const { handleOnExpanded, moreHeight } = this.props;
Animated.timing(this.anime.height, {
toValue: this.anime.expanded ? this._getMinValue() : this._getMaxValue(),
toValue: this.anime.expanded ? this._getMinValue() : this._getMaxValue() + moreHeight,
duration: 200,
}).start();
this.anime.expanded = !this.anime.expanded;

View File

@ -9,7 +9,6 @@ export default EStyleSheet.create({
backgroundColor: '$primaryBlue',
},
input: {
alignItems: 'center',
justifyContent: 'center',
height: 18,
margin: 10,

View File

@ -1,5 +1,5 @@
import React, { PureComponent } from 'react';
import { Animated } from 'react-native';
import { Animated, Easing, View } from 'react-native';
// Styles
import styles from './pinAnimatedInputStyles';
@ -13,38 +13,73 @@ class PinAnimatedInput extends PureComponent {
constructor(props) {
super(props);
this.state = {};
this.dots = [];
this.dots[0] = new Animated.Value(0);
this.dots[1] = new Animated.Value(0);
this.dots[2] = new Animated.Value(0);
this.dots[3] = new Animated.Value(0);
}
componentWillReceiveProps(nextProps) {
const { loading } = this.props;
if (loading !== nextProps.loading) {
if (nextProps.loading) {
this._startLoadingAnimation();
} else {
this._stopLoadingAnimation();
}
}
}
_startLoadingAnimation = () => {
const { loading } = this.props;
[...Array(4)].map((item, index) => {
this.dots[index].setValue(0);
});
Animated.sequence([
...this.dots.map(item => Animated.timing(item, {
toValue: 1,
duration: 250,
easing: Easing.linear,
})),
]).start(() => {
if (loading) this._startLoadingAnimation();
});
};
_stopLoadingAnimation = () => {
[...Array(4)].map((item, index) => {
this.dots[index].stopAnimation();
});
};
render() {
const { pin } = this.props;
const test = new Animated.Value(0);
const tilt = test.interpolate({
inputRange: [0, 0.3, 0.6, 0.9],
outputRange: [0, -50, 50, 0],
const marginBottom = [];
[...Array(4)].map((item, index) => {
marginBottom[index] = this.dots[index].interpolate({
inputRange: [0, 0.5, 1],
outputRange: [0, 20, 0],
});
});
return (
<Animated.View
style={[
{
transform: [{ translateX: tilt }],
},
styles.container,
]}
>
{[...Array(4)].map((val, index) => {
<View style={[styles.container]}>
{this.dots.map((val, index) => {
if (pin.length > index) {
return (
<Animated.View key={`passwordItem-${index}`} style={styles.input}>
<Animated.View
key={`passwordItem-${index}`}
style={[styles.input, styles.inputWithBackground]}
style={[styles.input, styles.inputWithBackground, { bottom: marginBottom[index] }]}
/>
</Animated.View>
);
}
return <Animated.View key={`passwordItem-${index}`} style={styles.input} />;
return <View key={`passwordItem-${index}`} style={styles.input} />;
})}
</Animated.View>
</View>
);
}
}

View File

@ -5,6 +5,9 @@ import { PostHeaderDescription } from '../../postElements';
import { PostDropdown } from '../../postDropdown';
import { Icon } from '../../icon';
// Utils
import { makeCountFriendly } from '../../../utils/formatter';
// STEEM
import { Upvote } from '../../upvote';
// Styles
@ -59,6 +62,9 @@ class PostCard extends Component {
const _image = content && content.image
? { uri: content.image, priority: FastImage.priority.high }
: DEFAULT_IMAGE;
const reblogedBy = content.reblogged_by && content.reblogged_by[0];
// repeat icon
// text rebloged by ${reblogedBy}
return (
<View style={styles.post}>
@ -72,6 +78,7 @@ class PostCard extends Component {
reputation={content.author_reputation}
size={32}
tag={content.category}
reblogedBy={reblogedBy}
/>
<View style={styles.dropdownWrapper}>
<PostDropdown content={content} />
@ -103,12 +110,12 @@ class PostCard extends Component {
iconType="MaterialIcons"
name="people"
/>
<Text style={styles.comment}>{content.vote_count}</Text>
<Text style={styles.comment}>{makeCountFriendly(content.vote_count)}</Text>
</TouchableOpacity>
</View>
<TouchableOpacity style={styles.commentButton}>
<Icon style={[styles.commentIcon]} iconType="MaterialIcons" name="comment" />
<Text style={styles.comment}>{content.children}</Text>
<Text style={styles.comment}>{makeCountFriendly(content.children)}</Text>
</TouchableOpacity>
</View>
</View>

View File

@ -55,10 +55,12 @@ class PostDropdownContainer extends PureComponent {
};
_reblog = () => {
const { currentAccount, content, isLoggedIn } = this.props;
const {
currentAccount, content, isLoggedIn, pinCode,
} = this.props;
if (isLoggedIn) {
reblog(currentAccount, content.author, content.permlink)
.then((result) => {
reblog(currentAccount, pinCode, content.author, content.permlink)
.then(() => {
Alert.alert('Success', 'Rebloged!');
})
.catch((error) => {
@ -98,5 +100,6 @@ class PostDropdownContainer extends PureComponent {
const mapStateToProps = state => ({
isLoggedIn: state.application.isLoggedIn,
currentAccount: state.account.currentAccount,
pinCode: state.account.pin,
});
export default withNavigation(connect(mapStateToProps)(PostDropdownContainer));

View File

@ -5,7 +5,7 @@ import { withNavigation } from 'react-navigation';
import FastImage from 'react-native-fast-image';
// Components
import { Tag } from '../../../basicUIElements';
import { Tag, TextWithIcon } from '../../../basicUIElements';
// Styles
import styles from './postHeaderDescriptionStyles';
@ -43,7 +43,15 @@ class PostHeaderDescription extends PureComponent {
render() {
const {
date, avatar, name, reputation, size, tag, tagOnPress, isHideImage,
avatar,
date,
isHideImage,
name,
reblogedBy,
reputation,
size,
tag,
tagOnPress,
} = this.props;
const _reputationText = `(${reputation})`;
@ -77,6 +85,7 @@ class PostHeaderDescription extends PureComponent {
</TouchableOpacity>
)}
<Text style={styles.date}>{date}</Text>
{!!reblogedBy && <TextWithIcon text={reblogedBy} iconType="MaterialIcons" iconName="repeat" />}
</View>
);
}

View File

@ -22,8 +22,8 @@ export default EStyleSheet.create({
width: '$deviceWidth - 24',
flexDirection: 'row',
justifyContent: 'space-between',
marginBottom: 17,
marginTop: 10,
marginVertical: 10,
height: 30,
},
leftIcons: {
flexDirection: 'row',
@ -35,10 +35,11 @@ export default EStyleSheet.create({
justifyContent: 'flex-end',
},
insetIconStyle: {
marginRight: 12,
marginRight: 20,
color: '$primaryDarkText',
},
activityIndicator: {
marginRight: 12,
marginRight: 20,
width: 30,
},
followCountWrapper: {
@ -58,6 +59,13 @@ export default EStyleSheet.create({
},
// TODO: look at here
dropdownIconStyle: {
marginBottom: 7,
width: 25,
height: 25,
left: -5,
marginBottom: 3,
color: '#c1c5c7',
},
dropdownStyle: {
maxWidth: 150,
},
});

View File

@ -17,6 +17,10 @@ import DARK_COVER_IMAGE from '../../../assets/dark_cover_image.png';
import { TextWithIcon } from '../../basicUIElements';
import { PercentBar } from '../../percentBar';
import { IconButton } from '../../iconButton';
import { DropdownButton } from '../../dropdownButton';
// Utils
import { makeCountFriendly } from '../../../utils/formatter';
// Styles
import styles from './profileSummaryStyles';
@ -43,6 +47,16 @@ class ProfileSummaryView extends PureComponent {
Linking.openURL(url);
};
_handleOnDropdownSelect = (index) => {
const { isMuted, handleMuteUnmuteUser } = this.props;
// This funciton should have switch case but now only has one option therefor
// temporarily I created with if statments
if (index === '0' && handleMuteUnmuteUser) {
handleMuteUnmuteUser(!isMuted);
}
};
render() {
const { isShowPercentText } = this.state;
const {
@ -51,8 +65,8 @@ class ProfileSummaryView extends PureComponent {
followerCount,
followingCount,
handleFollowUnfollowUser,
handleMuteUnmuteUser,
handleOnFollowsPress,
handleUIChange,
hoursRC,
hoursVP,
intl,
@ -66,40 +80,43 @@ class ProfileSummaryView extends PureComponent {
location,
percentRC,
percentVP,
handleUIChange,
} = this.props;
const dropdownOpions = [];
const votingPowerHoursText = hoursVP && `• Full in ${hoursVP} hours`;
const votingPowerText = `Voting power: ${percentVP}% ${votingPowerHoursText || ''}`;
const rcPowerHoursText = hoursRC && `• Full in ${hoursRC} hours`;
const rcPowerText = `RCs: ${percentRC}% ${rcPowerHoursText || ''}`;
/* eslint-disable */
const rowLength = location
? location.length
: null + link
? link.length
: null + date
? date.length
: null;
const rowLength = (location ? location.length : 0) + (link ? link.length : 0) + (date ? date.length : 0);
const isColumn = rowLength && DEVICE_WIDTH / rowLength <= 7.3;
const isColumn = rowLength && DEVICE_WIDTH / rowLength <= 15;
const followButtonIcon = !isFollowing ? 'user-follow' : 'user-unfollow';
const ignoreButtonIcon = !isMuted ? 'ban' : 'minus';
const followButtonIcon = !isFollowing ? 'account-plus' : 'account-minus';
const coverImageUrl = `http://img.esteem.app/400x0/${coverImage}`;
dropdownOpions.push(!isMuted ? 'MUTE' : 'UNMUTE');
return (
<Fragment>
<View style={[isColumn ? styles.textWithIconWrapperColumn : styles.textWithIconWrapper]}>
{!!location && <TextWithIcon text={location} iconName="md-navigate" />}
{!!location && (
<TextWithIcon
text={location}
iconName="near-me"
iconType="MaterialIcons"
iconSize={14}
/>
)}
{!!link && (
<TextWithIcon
isClickable
onPress={() => this._handleOnPressLink(link)}
text={link}
iconName="md-globe"
iconSize={14}
iconName="earth"
iconType="MaterialCommunityIcons"
/>
)}
{!!date && <TextWithIcon text={date} iconName="md-calendar" />}
{!!date && <TextWithIcon text={date} iconName="md-calendar" iconSize={14} />}
</View>
<Image
style={styles.longImage}
@ -107,9 +124,8 @@ class ProfileSummaryView extends PureComponent {
defaultSource={isDarkTheme ? DARK_COVER_IMAGE : LIGHT_COVER_IMAGE}
/>
<TouchableOpacity
onPress={() =>
this.setState({ isShowPercentText: !isShowPercentText }, () => {
handleUIChange(isShowPercentText ? 0 : 30);
onPress={() => this.setState({ isShowPercentText: !isShowPercentText }, () => {
handleUIChange(!isShowPercentText ? 30 : 0);
})
}
>
@ -136,7 +152,7 @@ class ProfileSummaryView extends PureComponent {
<Fragment>
<TouchableOpacity onPress={() => handleOnFollowsPress(false)}>
<View style={styles.followCountWrapper}>
<Text style={styles.followCount}>{followerCount}</Text>
<Text style={styles.followCount}>{makeCountFriendly(followerCount)}</Text>
<Text style={styles.followText}>
{' '}
{intl.formatMessage({
@ -147,7 +163,7 @@ class ProfileSummaryView extends PureComponent {
</TouchableOpacity>
<TouchableOpacity onPress={() => handleOnFollowsPress(true)}>
<View style={styles.followCountWrapper}>
<Text style={styles.followCount}>{followingCount}</Text>
<Text style={styles.followCount}>{makeCountFriendly(followingCount)}</Text>
<Text style={styles.followText}>
{intl.formatMessage({
id: 'profile.following',
@ -163,9 +179,9 @@ class ProfileSummaryView extends PureComponent {
<Fragment>
<IconButton
backgroundColor="transparent"
name="heart"
iconType="SimpleLineIcons"
size={16}
name="favorite-border"
iconType="MaterialIcons"
size={20}
style={[styles.insetIconStyle]}
color="#c1c5c7"
/>
@ -175,24 +191,23 @@ class ProfileSummaryView extends PureComponent {
<IconButton
backgroundColor="transparent"
name={followButtonIcon}
iconType="SimpleLineIcons"
onPress={() => handleFollowUnfollowUser(isFollowing ? false : true)}
size={16}
style={styles.insetIconStyle}
iconType="MaterialCommunityIcons"
onPress={() => handleFollowUnfollowUser(!isFollowing)}
size={20}
color="#c1c5c7"
/>
)}
{isProfileLoading ? (
<ActivityIndicator style={styles.activityIndicator} />
) : (
<IconButton
backgroundColor="transparent"
name={ignoreButtonIcon}
iconType="SimpleLineIcons"
onPress={() => handleMuteUnmuteUser(isMuted ? false : true)}
size={16}
style={styles.insetIconStyle}
color="#c1c5c7"
<DropdownButton
isHasChildIcon
iconName="more-vert"
options={dropdownOpions}
onSelect={this._handleOnDropdownSelect}
noHighlight
iconStyle={styles.dropdownIconStyle}
dropdownStyle={styles.dropdownStyle}
/>
)}
</Fragment>

View File

@ -43,6 +43,7 @@ class UpvoteContainer extends PureComponent {
isLoggedIn,
isShowPayoutValue,
upvotePercent,
pinCode,
} = this.props;
let author;
let isVoted;
@ -50,10 +51,10 @@ class UpvoteContainer extends PureComponent {
let permlink;
if (content) {
author = content.author;
({ author } = content);
isVoted = content.is_voted;
pendingPayoutValue = content.pending_payout_value;
permlink = content.permlink;
({ permlink } = content);
}
return (
@ -68,6 +69,7 @@ class UpvoteContainer extends PureComponent {
pendingPayoutValue={pendingPayoutValue}
permlink={permlink}
upvotePercent={upvotePercent}
pinCode={pinCode}
/>
);
}
@ -76,7 +78,7 @@ class UpvoteContainer extends PureComponent {
const mapStateToProps = state => ({
isLoggedIn: state.application.isLoggedIn,
upvotePercent: state.application.upvotePercent,
pinCode: state.account.pin,
currentAccount: state.account.currentAccount,
});

View File

@ -75,7 +75,7 @@ class UpvoteView extends Component {
_upvoteContent = async () => {
const {
author, currentAccount, fetchPost, handleSetUpvotePercent, permlink,
author, currentAccount, fetchPost, handleSetUpvotePercent, permlink, pinCode,
} = this.props;
const { sliderValue } = this.state;
@ -92,6 +92,7 @@ class UpvoteView extends Component {
vote(
currentAccount,
pinCode,
author,
permlink,
weight,

View File

@ -6,6 +6,7 @@
"claim_reward_balance": "Claim Reward Balance",
"transfer": "Transfer",
"transfer_to_vesting": "Transfer To Vesting",
"transfer_from_savings": "Transfer From Savings",
"withdraw_vesting": "Power Down",
"fill_order": "Fill Order"
},

View File

@ -1,13 +1,16 @@
{
"wallet": {
"curation_reward": "Curation Reward",
"author_reward": "Author Reward",
"comment_benefactor_reward": "Comment Benefactor Reward",
"claim_reward_balance_ok": "Reward balance claimed",
"claim_reward_balance": "Claim Reward Balance",
"transfer": "Transfer",
"comment_benefactor_reward": "Comment Benefactor Reward",
"curation_reward": "Curation Reward",
"fill_order": "Fill Order",
"transactions": "Transactions",
"transfer_from_savings": "Tasarruflardan Transfer",
"transfer_to_vesting": "Transfer To Vesting",
"withdraw_vesting": "withdraw_vesting",
"fill_order": "Fill Order"
"transfer": "Transfer",
"withdraw_vesting": "Withdraw Vesting"
},
"notification": {
"vote": "beğendi",

View File

@ -5,17 +5,13 @@ import {
setAuthStatus,
getUserDataWithUsername,
updateUserData,
setPinCode,
getPinCode,
updateCurrentUsername,
getUserData,
} from '../../realm/realm';
import { encryptKey, decryptKey } from '../../utils/crypto';
import steemConnect from './steemConnectAPI';
export const Login = (username, password) => {
let publicKeys;
let privateKeys;
export const login = async (username, password) => {
const resultKeys = {
active: null,
memo: null,
@ -24,17 +20,17 @@ export const Login = (username, password) => {
};
let loginFlag = false;
let avatar = '';
return new Promise((resolve, reject) => {
// Get user account data from STEEM Blockchain
getUser(username)
.then((account) => {
const account = await getUser(username);
if (!account) {
return Promise.reject(new Error('Invalid pin code, please check and try again'));
}
if (isLoggedInUser(username)) {
reject(new Error('You are already logged in, please try to add another account'));
return Promise.reject(new Error('You are already logged in, please try to add another account'));
}
// Public keys of user
publicKeys = {
const publicKeys = {
active: account.active.key_auths.map(x => x[0]),
memo: account.memo_key,
owner: account.owner.key_auths.map(x => x[0]),
@ -42,7 +38,7 @@ export const Login = (username, password) => {
};
// Set private keys of user
privateKeys = getPrivateKeys(username, password);
const privateKeys = getPrivateKeys(username, password);
// Check all keys
Object.keys(publicKeys).map((pubKey) => {
@ -51,13 +47,12 @@ export const Login = (username, password) => {
resultKeys[pubKey] = publicKeys[pubKey];
}
});
let jsonMetadata;
try {
jsonMetadata = JSON.parse(account.json_metadata) || '';
} catch (err) {
jsonMetadata = '';
// TODO: handle wrong json format properly
// reject(new Error(err));
}
if (Object.keys(jsonMetadata).length !== 0) {
avatar = jsonMetadata.profile.profile_image || '';
@ -77,22 +72,11 @@ export const Login = (username, password) => {
account.local = userData;
// Save user data to Realm DB
setUserData(userData)
.then(() => {
resolve({ ...account, password });
updateCurrentUsername(account.name);
})
.catch(() => {
reject(new Error('Invalid credentials, please check and try again'));
});
} else {
reject(new Error('Invalid credentials, please check and try again'));
await setUserData(userData);
await updateCurrentUsername(account.name);
return ({ ...account, password });
}
})
.catch(() => {
reject(new Error('Invalid credentials, please check and try again'));
});
});
return Promise.reject(new Error('Invalid pin code, please check and try again'));
};
export const loginWithSC2 = async (accessToken) => {
@ -104,7 +88,7 @@ export const loginWithSC2 = async (accessToken) => {
try {
const jsonMetadata = JSON.parse(account.account.json_metadata);
if (Object.keys(jsonMetadata).length !== 0) {
avatar = jsonMetadata.profile.profile_image;
avatar = jsonMetadata.profile.profile_image || '';
}
} catch (error) {
reject(new Error('Invalid credentials, please check and try again'));
@ -120,26 +104,15 @@ export const loginWithSC2 = async (accessToken) => {
accessToken: '',
};
const authData = {
isLoggedIn: true,
currentUsername: account.account.name,
};
if (isLoggedInUser(account.account.name)) {
reject(new Error('You are already logged in, please try to add another account'));
}
setAuthStatus(authData)
.then(() => {
setUserData(userData)
.then(() => {
account.account.username = account.account.name;
resolve({ ...account.account, accessToken });
updateCurrentUsername(account.account.name);
})
.catch((error) => {
reject(error);
});
resolve({ ...account.account, accessToken });
})
.catch((error) => {
reject(error);
@ -147,89 +120,21 @@ export const loginWithSC2 = async (accessToken) => {
});
};
export const setUserDataWithPinCode = data => new Promise((resolve, reject) => {
let updatedUserData;
export const setUserDataWithPinCode = async (data) => {
const result = getUserDataWithUsername(data.username);
const userData = result[0];
const privateKeys = getPrivateKeys(userData.username, data.password);
if (userData.authType === 'masterKey') {
updatedUserData = {
const updatedUserData = {
username: userData.username,
authType: 'masterKey',
masterKey: encryptKey(data.password, data.pinCode),
authType: userData.authType,
accessToken: userData.authType === 'steemConnect' ? encryptKey(data.accessToken, data.pinCode) : '',
masterKey: userData.authType === 'masterKey' ? encryptKey(data.password, data.pinCode) : '',
postingKey: encryptKey(privateKeys.posting.toString(), data.pinCode),
activeKey: encryptKey(privateKeys.active.toString(), data.pinCode),
memoKey: encryptKey(privateKeys.memo.toString(), data.pinCode),
};
} else if (userData.authType === 'steemConnect') {
updatedUserData = {
username: userData.username,
authType: 'steemConnect',
accessToken: encryptKey(data.accessToken, data.pinCode),
masterKey: '',
postingKey: encryptKey(privateKeys.posting.toString(), data.pinCode),
activeKey: encryptKey(privateKeys.active.toString(), data.pinCode),
memoKey: encryptKey(privateKeys.memo.toString(), data.pinCode),
};
}
updateUserData(updatedUserData)
.then((response) => {
const authData = {
isLoggedIn: true,
currentUsername: userData.username,
};
setAuthStatus(authData)
.then(() => {
const encriptedPinCode = encryptKey(data.pinCode, 'pin-code');
setPinCode(encriptedPinCode)
.then(() => {
resolve(response);
})
.catch((error) => {
reject(error);
});
})
.catch((error) => {
reject(error);
});
})
.catch((err) => {
reject(err);
});
});
export const updatePinCode = async (data) => {
let updatedUserData;
const users = await getUserData();
if (users.length > 0) {
users.forEach(async (userData) => {
const password = decryptKey(userData.masterKey, data.oldPinCode);
const privateKeys = getPrivateKeys(userData.username, password);
if (userData.authType === 'masterKey') {
updatedUserData = {
username: userData.username,
authType: 'masterKey',
masterKey: encryptKey(password, data.pinCode),
postingKey: encryptKey(privateKeys.posting.toString(), data.pinCode),
activeKey: encryptKey(privateKeys.active.toString(), data.pinCode),
memoKey: encryptKey(privateKeys.memo.toString(), data.pinCode),
};
} else if (userData.authType === 'steemConnect') {
updatedUserData = {
username: userData.username,
authType: 'steemConnect',
accessToken: encryptKey(data.accessToken, data.pinCode),
masterKey: '',
postingKey: encryptKey(privateKeys.posting.toString(), data.pinCode),
activeKey: encryptKey(privateKeys.active.toString(), data.pinCode),
memoKey: encryptKey(privateKeys.memo.toString(), data.pinCode),
};
}
const response = await updateUserData(updatedUserData);
const authData = {
@ -238,9 +143,37 @@ export const updatePinCode = async (data) => {
};
await setAuthStatus(authData);
const encriptedPinCode = encryptKey(data.pinCode, 'pin-code');
await setPinCode(encriptedPinCode);
return (response);
};
export const updatePinCode = async (data) => {
let password = null;
let accessToken = null;
const users = await getUserData();
if (users.length > 0) {
users.forEach(async (userData) => {
if (userData.authType === 'masterKey') {
password = decryptKey(userData.masterKey, data.oldPinCode);
} else if (userData.authType === 'steemConnect') {
accessToken = decryptKey(userData.accessToken, data.oldPinCode);
}
const privateKeys = getPrivateKeys(userData.username, password);
const updatedUserData = {
username: userData.username,
authType: userData.authType,
accessToken: userData.authType === 'steemConnect' ? encryptKey(accessToken, data.pinCode) : '',
masterKey: userData.authType === 'masterKey' ? encryptKey(password, data.pinCode) : '',
postingKey: encryptKey(privateKeys.posting.toString(), data.pinCode),
activeKey: encryptKey(privateKeys.active.toString(), data.pinCode),
memoKey: encryptKey(privateKeys.memo.toString(), data.pinCode),
};
const response = await updateUserData(updatedUserData);
const authData = {
isLoggedIn: true,
currentUsername: userData.username,
};
await setAuthStatus(authData);
return response;
});
}
@ -252,9 +185,13 @@ export const verifyPinCode = async (data) => {
let account = null;
let loginFlag = false;
if (result.length > 0) {
if (userData.masterKey || userData.accessToken) {
if (userData.authType === 'steemConnect') {
const accessToken = decryptKey(userData.accessToken, data.pinCode);
let accessToken;
try {
accessToken = decryptKey(userData.accessToken, data.pinCode);
} catch (error) {
return Promise.reject(new Error('Invalid pin code, please check and try again'));
}
await steemConnect.setAccessToken(accessToken);
account = await steemConnect.me();
if (account) {
@ -263,7 +200,6 @@ export const verifyPinCode = async (data) => {
} else if (userData.authType === 'masterKey') {
const password = decryptKey(userData.masterKey, data.pinCode);
account = await getUser(data.username);
// Public keys of user
const publicKeys = {
active: account.active.key_auths.map(x => x[0]),
@ -281,19 +217,7 @@ export const verifyPinCode = async (data) => {
}
});
}
} else {
const encriptedPinCode = await getPinCode();
const pinCode = decryptKey(encriptedPinCode, 'pin-code');
if (pinCode === data.pinCode) {
const res = await setUserDataWithPinCode(data);
if (res) {
loginFlag = true;
}
}
}
}
return new Promise((resolve, reject) => {
if (loginFlag) {
const authData = {
isLoggedIn: true,
@ -306,18 +230,10 @@ export const verifyPinCode = async (data) => {
activeKey: decryptKey(userData.activeKey, data.pinCode),
memoKey: decryptKey(userData.memoKey, data.pinCode),
};
setAuthStatus(authData)
.then(() => {
resolve(response);
})
.catch(() => {
// TODO: create function for throw error
reject(new Error('Unknown error, please contact to eSteem.'));
});
} else {
reject(new Error('Invalid pin code, please check and try again'));
await setAuthStatus(authData);
return (response);
}
});
return Promise.reject(new Error('Invalid pin code, please check and try again'));
};
export const switchAccount = username => new Promise((resolve, reject) => {

View File

@ -1,5 +1,7 @@
import { Client, PrivateKey } from 'dsteem';
import steemConnect from 'steemconnect';
import Config from 'react-native-config';
import { getServer, getPinCode } from '../../realm/realm';
import { getUnreadActivityCount } from '../esteem/esteem';
@ -31,7 +33,7 @@ const _getClient = async () => {
_getClient();
export const getDigitPinCode = async () => decryptKey(await getPinCode(), 'pin-code');
export const getDigitPinCode = pin => decryptKey(pin, Config.PIN_KEY);
/**
* @method getAccount get account data
@ -194,8 +196,8 @@ export const getIsMuted = async (username, targetUsername) => {
return false;
};
export const ignoreUser = async (currentAccount, data) => {
const digitPinCode = await getDigitPinCode();
export const ignoreUser = async (currentAccount, pin, data) => {
const digitPinCode = getDigitPinCode(pin);
if (currentAccount.local.authType === AUTH_TYPE.MASTER_KEY) {
const key = decryptKey(currentAccount.local.postingKey, digitPinCode);
@ -358,8 +360,8 @@ export const getPostWithComments = async (user, permlink) => {
* @param vote vote object(author, permlink, voter, weight)
* @param postingKey private posting key
*/
export const vote = async (currentAccount, author, permlink, weight) => {
const digitPinCode = await getDigitPinCode();
export const vote = async (currentAccount, pin, author, permlink, weight) => {
const digitPinCode = getDigitPinCode(pin);
if (currentAccount.local.authType === AUTH_TYPE.MASTER_KEY) {
const key = decryptKey(currentAccount.local.postingKey, digitPinCode);
@ -443,8 +445,8 @@ export const transferToken = (data, activeKey) => {
});
};
export const followUser = async (currentAccount, data) => {
const digitPinCode = await getDigitPinCode();
export const followUser = async (currentAccount, pin, data) => {
const digitPinCode = getDigitPinCode(pin);
if (currentAccount.local.authType === AUTH_TYPE.MASTER_KEY) {
const key = decryptKey(currentAccount.local.postingKey, digitPinCode);
@ -484,8 +486,8 @@ export const followUser = async (currentAccount, data) => {
}
};
export const unfollowUser = async (currentAccount, data) => {
const digitPinCode = await getDigitPinCode();
export const unfollowUser = async (currentAccount, pin, data) => {
const digitPinCode = getDigitPinCode(pin);
if (currentAccount.local.authType === AUTH_TYPE.MASTER_KEY) {
const key = decryptKey(currentAccount.local.postingKey, digitPinCode);
@ -620,6 +622,7 @@ export const lookupAccounts = async (username) => {
*/
export const postContent = async (
account,
pin,
parentAuthor,
parentPermlink,
permlink,
@ -630,7 +633,7 @@ export const postContent = async (
voteWeight = null,
) => {
const { name: author } = account;
const digitPinCode = await getDigitPinCode();
const digitPinCode = getDigitPinCode(pin);
if (account.local.authType === AUTH_TYPE.MASTER_KEY) {
const opArray = [
@ -722,8 +725,8 @@ export const postContent = async (
};
// Re-blog
export const reblog = async (account, author, permlink) => {
const pin = await getDigitPinCode();
export const reblog = async (account, pinCode, author, permlink) => {
const pin = getDigitPinCode(pinCode);
if (account.local.authType === AUTH_TYPE.MASTER_KEY) {
const key = decryptKey(account.local.postingKey, pin);

View File

@ -6,6 +6,7 @@ import {
UPDATE_CURRENT_ACCOUNT,
UPDATE_UNREAD_ACTIVITY_COUNT,
REMOVE_OTHER_ACCOUNT,
SET_PIN_CODE,
} from '../constants/constants';
export const fetchAccountFromSteem = (username, password) => (dispatch) => {
@ -43,3 +44,8 @@ export const removeOtherAccount = data => ({
type: REMOVE_OTHER_ACCOUNT,
payload: data,
});
export const setPinCode = data => ({
type: SET_PIN_CODE,
payload: data,
});

View File

@ -28,6 +28,7 @@ export const FETCHING_ACCOUNT = 'FETCHING_ACCOUNT';
export const REMOVE_OTHER_ACCOUNT = 'REMOVE_OTHER_ACCOUNT';
export const UPDATE_CURRENT_ACCOUNT = 'UPDATE_CURRENT_ACCOUNT';
export const UPDATE_UNREAD_ACTIVITY_COUNT = 'UPDATE_UNREAD_ACTIVITY_COUNT';
export const SET_PIN_CODE = 'SET_PIN_CODE';
// UI
export const IS_COLLAPSE_POST_BUTTON = 'IS_COLLAPSE_POST_BUTTON';

View File

@ -4,9 +4,9 @@ import {
ADD_OTHER_ACCOUNT,
UPDATE_CURRENT_ACCOUNT,
UPDATE_UNREAD_ACTIVITY_COUNT,
LOGOUT,
REMOVE_OTHER_ACCOUNT,
LOGOUT_FAIL,
SET_PIN_CODE,
} from '../constants/constants';
const initialState = {
@ -16,6 +16,7 @@ const initialState = {
hasError: false,
errorMessage: null,
isLogingOut: false,
pin: null,
};
export default function (state = initialState, action) {
@ -77,11 +78,12 @@ export default function (state = initialState, action) {
...state,
isLogingOut: true,
};
// case LOGOUT_SUCCESS:
// return {
// ...state,
// initialState,
// };
case SET_PIN_CODE:
return {
...state,
pin: action.payload,
};
default:
return state;

View File

@ -91,38 +91,39 @@ class ApplicationContainer extends Component {
};
_getUserData = async () => {
const { dispatch } = this.props;
const { dispatch, pinCode } = this.props;
let realmData;
let authStatus;
let currentUsername;
await getAuthStatus().then((res) => {
authStatus = res;
currentUsername = res.currentUsername;
({ currentUsername } = res);
if (res) {
getUserData().then((userData) => {
if (userData.length > 0) {
realmData = userData;
userData.forEach((accountData) => {
dispatch(
addOtherAccount({ username: accountData.username }),
);
dispatch(addOtherAccount({ username: accountData.username }));
});
}
});
}
});
if (realmData) {
await getUser(currentUsername)
.then((accountData) => {
.then(async (accountData) => {
dispatch(login(true));
const isExistUser = await getExistUser();
const realmObject = realmData.filter(data => data.username === currentUsername);
accountData.local = realmObject[0];
[accountData.local] = realmObject;
dispatch(updateCurrentAccount(accountData));
// If in dev mode pin code does not show
if (__DEV__ === false) {
// eslint-disable-next-line
if (!isExistUser || !pinCode) {
dispatch(openPinCodeModal());
}
this._connectNotificationServer(accountData.name);
@ -142,12 +143,12 @@ class ApplicationContainer extends Component {
getSettings().then((response) => {
if (response) {
response.isDarkTheme && dispatch(isDarkTheme(response.isDarkTheme));
response.language && dispatch(setLanguage(response.language));
response.currency && dispatch(setCurrency(response.currency));
response.notification && dispatch(isNotificationOpen(response.notification));
response.server && dispatch(setApi(response.server));
response.upvotePercent && dispatch(setUpvotePercent(Number(response.upvotePercent)));
if (response.isDarkTheme) dispatch(isDarkTheme(response.isDarkTheme));
if (response.language) dispatch(setLanguage(response.language));
if (response.currency) dispatch(setCurrency(response.currency));
if (response.notification) dispatch(isNotificationOpen(response.notification));
if (response.server) dispatch(setApi(response.server));
if (response.upvotePercent) dispatch(setUpvotePercent(Number(response.upvotePercent)));
this.setState({ isReady: true });
}
@ -158,7 +159,7 @@ class ApplicationContainer extends Component {
const { dispatch, unreadActivityCount } = this.props;
const ws = new WebSocket(`${Config.ACTIVITY_WEBSOCKET_URL}?user=${username}`);
ws.onmessage = (e) => {
ws.onmessage = () => {
// a message was received
dispatch(updateUnreadActivityCount(unreadActivityCount + 1));
};
@ -188,9 +189,7 @@ class ApplicationContainer extends Component {
};
_logout = () => {
const {
otherAccounts, currentAccountUsername, dispatch,
} = this.props;
const { otherAccounts, currentAccountUsername, dispatch } = this.props;
removeUserData(currentAccountUsername)
.then(() => {
@ -210,8 +209,7 @@ class ApplicationContainer extends Component {
dispatch(removeOtherAccount(currentAccountUsername));
dispatch(logoutDone());
})
.catch((err) => {
alert('err');
.catch(() => {
});
};
@ -222,11 +220,11 @@ class ApplicationContainer extends Component {
const realmData = getUserDataWithUsername(targetAccountUsername);
const _currentAccount = accountData;
_currentAccount.username = accountData.name;
_currentAccount.local = realmData[0];
[_currentAccount.local] = realmData;
dispatch(updateCurrentAccount(_currentAccount));
});
}
};
render() {
const { selectedLanguage } = this.props;
@ -256,6 +254,7 @@ const mapStateToProps = state => ({
unreadActivityCount: state.account.currentAccount.unread_activity_count,
currentAccountUsername: state.account.currentAccount.name,
otherAccounts: state.account.otherAccounts,
pinCode: state.account.pin,
});
export default connect(mapStateToProps)(ApplicationContainer);

View File

@ -203,7 +203,7 @@ class EditorContainer extends Component {
};
_submitPost = async (fields) => {
const { navigation, currentAccount } = this.props;
const { navigation, currentAccount, pinCode } = this.props;
if (currentAccount) {
this.setState({ isPostSending: true });
@ -217,6 +217,7 @@ class EditorContainer extends Component {
await postContent(
currentAccount,
pinCode,
'',
parentPermlink,
permlink,
@ -237,7 +238,7 @@ class EditorContainer extends Component {
};
_submitReply = async (fields) => {
const { currentAccount } = this.props;
const { currentAccount, pinCode } = this.props;
if (currentAccount) {
this.setState({ isPostSending: true });
@ -253,6 +254,7 @@ class EditorContainer extends Component {
await postContent(
currentAccount,
pinCode,
parentAuthor,
parentPermlink,
permlink,
@ -343,7 +345,7 @@ class EditorContainer extends Component {
const mapStateToProps = state => ({
isLoggedIn: state.application.isLoggedIn,
pinCode: state.account.pin,
currentAccount: state.account.currentAccount,
});

View File

@ -1,11 +1,21 @@
import React, { PureComponent } from 'react';
import { Alert, Linking } from 'react-native';
import { connect } from 'react-redux';
// Services and Actions
import { login } from '../../../providers/steem/auth';
import { lookupAccounts } from '../../../providers/steem/dsteem';
import {
failedAccount,
addOtherAccount,
updateCurrentAccount,
} from '../../../redux/actions/accountAction';
import { login as loginAction, openPinCodeModal } from '../../../redux/actions/applicationActions';
// Middleware
// Constants
import ROUTES from '../../../constants/routeNames';
// Utilities
@ -21,15 +31,61 @@ import LoginScreen from '../screen/loginScreen';
class LoginContainer extends PureComponent {
constructor(props) {
super(props);
this.state = {};
this.state = {
isLoading: false,
};
}
// Component Life Cycle Functions
// Component Functions
_handleOnPressLogin = (username, password) => {
const { dispatch, setPinCodeState } = this.props;
this.setState({ isLoading: true });
login(username, password)
.then((result) => {
if (result) {
dispatch(updateCurrentAccount({ ...result }));
dispatch(addOtherAccount({ username: result.name }));
dispatch(openPinCodeModal());
setPinCodeState({ navigateTo: ROUTES.DRAWER.MAIN });
dispatch(loginAction(true));
}
})
.catch((err) => {
// TODO: Change with global error handling
Alert.alert('Error', err.message);
dispatch(failedAccount(err.message));
this.setState({ isLoading: false });
});
};
_getAccountsWithUsername = async (username) => {
const validUsers = await lookupAccounts(username);
return validUsers;
};
_handleSignUp = () => {
Linking.openURL('https://signup.steemit.com/?ref=esteem').catch(err => alert('An error occurred', err));
};
render() {
return <LoginScreen {...this.props} />;
const { isLoading } = this.state;
const { navigation, setPinCodeState } = this.props;
return (
<LoginScreen
handleOnPressLogin={this._handleOnPressLogin}
getAccountsWithUsername={this._getAccountsWithUsername}
handleSignUp={this._handleSignUp}
isLoading={isLoading}
navigation={navigation}
setPinCodeState={setPinCodeState}
/>
);
}
}

View File

@ -1,30 +1,20 @@
import React, { PureComponent } from 'react';
import {
View, Linking, StatusBar, Platform, Alert,
} from 'react-native';
import { View, StatusBar, Platform } from 'react-native';
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
import ScrollableTabView from '@esteemapp/react-native-scrollable-tab-view';
import { injectIntl } from 'react-intl';
// Actions
import {
failedAccount,
addOtherAccount,
updateCurrentAccount,
} from '../../../redux/actions/accountAction';
import { login as loginAction, openPinCodeModal } from '../../../redux/actions/applicationActions';
import SteemConnect from '../../steem-connect/steemConnect';
// Internal Components
import { FormInput } from '../../../components/formInput';
import { InformationArea } from '../../../components/informationArea';
import { Login } from '../../../providers/steem/auth';
import { LoginHeader } from '../../../components/loginHeader';
import { lookupAccounts } from '../../../providers/steem/dsteem';
import { MainButton } from '../../../components/mainButton';
import { Modal } from '../../../components';
import { TabBar } from '../../../components/tabBar';
import { TextButton } from '../../../components/buttons';
import SteemConnect from '../../steem-connect/steemConnect';
// Constants
import { default as ROUTES } from '../../../constants/routeNames';
@ -40,51 +30,26 @@ class LoginScreen extends PureComponent {
this.state = {
username: '',
password: '',
isLoading: false,
isUsernameValid: true,
keyboardIsOpen: false,
isModalOpen: false,
};
}
_handleOnPressLogin = () => {
const { dispatch, setPinCodeState } = this.props;
const { password, username } = this.state;
this.setState({ isLoading: true });
Login(username, password)
.then((result) => {
if (result) {
dispatch(updateCurrentAccount({ ...result }));
dispatch(addOtherAccount({ username: result.name }));
dispatch(openPinCodeModal());
setPinCodeState({ navigateTo: ROUTES.DRAWER.MAIN });
dispatch(loginAction(true));
}
})
.catch((err) => {
// TODO: Change with global error handling
Alert.alert('Error', err.message);
dispatch(failedAccount(err.message));
this.setState({ isLoading: false });
});
};
_handleUsernameChange = async (username) => {
await this.setState({ username });
const validUsers = await lookupAccounts(username);
const isValid = validUsers.includes(username);
this.setState({ isUsernameValid: isValid });
};
_handleOnPasswordChange = (value) => {
this.setState({ password: value });
};
_handleSignUp = () => {
Linking.openURL('https://signup.steemit.com/?ref=esteem').catch(err => alert('An error occurred', err));
_handleUsernameChange = (username) => {
const { getAccountsWithUsername } = this.props;
this.setState({ username });
getAccountsWithUsername(username).then((res) => {
const isValid = res.includes(username);
this.setState({ isUsernameValid: isValid });
});
};
_handleOnModalToggle = () => {
@ -93,14 +58,16 @@ class LoginScreen extends PureComponent {
};
render() {
const { navigation, intl, setPinCodeState } = this.props;
const {
navigation,
intl,
setPinCodeState,
handleOnPressLogin,
handleSignUp,
isLoading,
username,
isUsernameValid,
keyboardIsOpen,
password,
isModalOpen,
} = this.props;
const {
username, isUsernameValid, keyboardIsOpen, password, isModalOpen,
} = this.state;
return (
@ -114,7 +81,7 @@ class LoginScreen extends PureComponent {
description={intl.formatMessage({
id: 'login.signin_title',
})}
onPress={() => this._handleSignUp()}
onPress={() => handleSignUp()}
rightButtonText={intl.formatMessage({
id: 'login.signup',
})}
@ -125,7 +92,7 @@ class LoginScreen extends PureComponent {
renderTabBar={() => (
<TabBar
style={styles.tabbar}
tabUnderlineDefaultWidth={100} // default containerWidth / (numberOfTabs * 4)
tabUnderlineDefaultWidth={100}
tabUnderlineScaleX={2} // default 3
activeColor="#357ce6"
inactiveColor="#222"
@ -187,7 +154,7 @@ class LoginScreen extends PureComponent {
})}
/>
<MainButton
onPress={this._handleOnPressLogin}
onPress={() => handleOnPressLogin(username, password)}
iconName="md-person"
iconColor="white"
text={intl.formatMessage({

View File

@ -2,6 +2,7 @@ import React, { Component } from 'react';
import { Alert } from 'react-native';
import { connect } from 'react-redux';
import { injectIntl } from 'react-intl';
import Config from 'react-native-config';
import {
setUserDataWithPinCode,
@ -12,7 +13,10 @@ import {
// Actions & Services
import { closePinCodeModal } from '../../../redux/actions/applicationActions';
import { getExistUser, setExistUser, getUserDataWithUsername } from '../../../realm/realm';
import { updateCurrentAccount } from '../../../redux/actions/accountAction';
import { updateCurrentAccount, setPinCode as savePinCode } from '../../../redux/actions/accountAction';
// Utils
import { encryptKey, decryptKey } from '../../../utils/crypto';
// Component
import PinCodeScreen from '../screen/pinCodeScreen';
@ -33,9 +37,10 @@ class PinCodeContainer extends Component {
// TODO: these text should move to view!
componentDidMount() {
this._getDataFromStorage().then(() => {
const { intl, isReset } = this.props;
const { intl } = this.props;
const { isExistUser } = this.state;
if (isExistUser || !isReset) {
if (isExistUser) {
this.setState({
informationText: intl.formatMessage({
id: 'pincode.enter_text',
@ -62,22 +67,17 @@ class PinCodeContainer extends Component {
});
});
_setPinCode = pin => new Promise((resolve, reject) => {
_resetPinCode = pin => new Promise((resolve, reject) => {
const {
currentAccount,
dispatch,
accessToken,
setWrappedComponentState,
navigateTo,
navigation,
intl,
isReset,
} = this.props;
const {
isExistUser, pinCode, isOldPinVerified, oldPinCode,
} = this.state;
const { isOldPinVerified, oldPinCode } = this.state;
if (isReset) {
const pinData = {
pinCode: pin,
password: currentAccount ? currentAccount.password : '',
@ -92,6 +92,7 @@ class PinCodeContainer extends Component {
_currentAccount.local = response;
dispatch(updateCurrentAccount({ ..._currentAccount }));
this._savePinCode(pin);
dispatch(closePinCodeModal());
if (navigateTo) {
@ -117,43 +118,17 @@ class PinCodeContainer extends Component {
reject(err);
});
}
} else if (isExistUser) {
// If the user is exist, we are just checking to pin and navigating to home screen
const pinData = {
pinCode: pin,
password: currentAccount ? currentAccount.password : '',
username: currentAccount ? currentAccount.name : '',
});
_setFirstPinCode = pin => new Promise((resolve) => {
const {
currentAccount,
dispatch,
accessToken,
};
verifyPinCode(pinData)
.then((res) => {
setWrappedComponentState(res);
dispatch(closePinCodeModal());
navigateTo,
navigation,
} = this.props;
const realmData = getUserDataWithUsername(currentAccount.name);
const _currentAccount = currentAccount;
_currentAccount.username = _currentAccount.name;
_currentAccount.local = realmData[0];
dispatch(updateCurrentAccount({ ..._currentAccount }));
if (navigateTo) {
navigation.navigate(navigateTo);
}
})
.catch((err) => {
Alert.alert('Warning', err.message);
reject(err);
});
} else if (!pinCode) {
// If the user is logging in for the first time, the user should set to pin
this.setState({
informationText: intl.formatMessage({
id: 'pincode.write_again',
}),
pinCode: pin,
});
resolve();
} else if (pinCode === pin) {
const pinData = {
pinCode: pin,
password: currentAccount ? currentAccount.password : '',
@ -167,6 +142,7 @@ class PinCodeContainer extends Component {
dispatch(updateCurrentAccount({ ..._currentAccount }));
setExistUser(true).then(() => {
this._savePinCode(pin);
dispatch(closePinCodeModal());
if (navigateTo) {
navigation.navigate(navigateTo);
@ -174,32 +150,120 @@ class PinCodeContainer extends Component {
resolve();
});
});
});
_verifyPinCode = pin => new Promise((resolve, reject) => {
const {
currentAccount,
dispatch,
accessToken,
navigateTo,
navigation,
setWrappedComponentState,
} = this.props;
// If the user is exist, we are just checking to pin and navigating to home screen
const pinData = {
pinCode: pin,
password: currentAccount ? currentAccount.password : '',
username: currentAccount ? currentAccount.name : '',
accessToken,
};
verifyPinCode(pinData)
.then((res) => {
setWrappedComponentState(res);
this._savePinCode(pin);
const realmData = getUserDataWithUsername(currentAccount.name);
const _currentAccount = currentAccount;
_currentAccount.username = _currentAccount.name;
[_currentAccount.local] = realmData;
dispatch(updateCurrentAccount({ ..._currentAccount }));
dispatch(closePinCodeModal());
if (navigateTo) {
navigation.navigate(navigateTo);
}
})
.catch((err) => {
Alert.alert('Warning', err.message);
reject(err);
});
});
_savePinCode = (pin) => {
const { dispatch } = this.props;
const encryptedPin = encryptKey(pin, Config.PIN_KEY);
dispatch(savePinCode(encryptedPin));
}
_setPinCode = async (pin, isReset) => {
const {
intl, currentAccount, applicationPinCode,
} = this.props;
const {
isExistUser, pinCode,
} = this.state;
const realmData = getUserDataWithUsername(currentAccount.name);
const userData = realmData[0];
// For exist users
if (isReset) { await this._resetPinCode(pin); return true; }
if (isExistUser) {
if (!userData.accessToken && !userData.masterKey && applicationPinCode) {
const verifiedPin = decryptKey(applicationPinCode, Config.PIN_KEY);
if (verifiedPin === pin) {
await this._setFirstPinCode(pin);
} else {
this.setState({
Alert.alert('Warning', 'Invalid pin code, please check and try again');
}
} else {
await this._verifyPinCode(pin);
}
return true;
}
// For new users
if (pinCode === pin) { await this._setFirstPinCode(pin); return true; }
if (!pinCode) {
// If the user is logging in for the first time, the user should set to pin
await this.setState({
informationText: intl.formatMessage({
id: 'pincode.write_again',
}),
pinCode: pin,
});
return Promise.resolve();
}
await this.setState({
informationText: intl.formatMessage({
id: 'pincode.write_again',
}),
});
setTimeout(() => {
await setTimeout(() => {
this.setState({
informationText: 'setup screen',
informationText: intl.formatMessage({
id: 'pincode.set_new',
}),
pinCode: null,
});
resolve();
return Promise.resolve();
}, 1000);
}
});
};
render() {
const { currentAccount, intl } = this.props;
const { currentAccount, intl, isReset } = this.props;
const { informationText, isExistUser } = this.state;
return (
<PinCodeScreen
informationText={informationText}
setPinCode={this._setPinCode}
setPinCode={pin => this._setPinCode(pin, isReset)}
showForgotButton={isExistUser}
username={currentAccount.name}
intl={intl}
{...this.props}
/>
);
}
@ -207,6 +271,7 @@ class PinCodeContainer extends Component {
const mapStateToProps = state => ({
currentAccount: state.account.currentAccount,
applicationPinCode: state.account.pin,
});
export default injectIntl(connect(mapStateToProps)(PinCodeContainer));

View File

@ -11,6 +11,7 @@ class PinCodeScreen extends PureComponent {
super(props);
this.state = {
pin: '',
loading: false,
};
}
@ -18,10 +19,12 @@ class PinCodeScreen extends PureComponent {
// Component Functions
_handleKeyboardOnPress = (value) => {
_handleKeyboardOnPress = async (value) => {
const { setPinCode } = this.props;
const { pin } = this.state;
const { pin, loading } = this.state;
if (loading) {
return;
}
if (value === 'clear') {
this.setState({ pin: '' });
return;
@ -31,14 +34,15 @@ class PinCodeScreen extends PureComponent {
if (pin.length < 3) {
this.setState({ pin: newPin });
} else if (pin.length === 3) {
this.setState({ pin: newPin });
await this.setState({ pin: newPin, loading: true });
setPinCode(`${pin}${value}`)
.then(() => {
// TODO: fix unmounted component error
this.setState({ pin: '' });
this.setState({ pin: '', loading: false });
})
.catch(() => {
this.setState({ pin: '' });
this.setState({ pin: '', loading: false });
});
} else if (pin.length > 3) {
this.setState({ pin: `${value}` });
@ -49,7 +53,7 @@ class PinCodeScreen extends PureComponent {
const {
informationText, showForgotButton, username, intl,
} = this.props;
const { pin } = this.state;
const { pin, loading } = this.state;
return (
<View style={styles.container}>
@ -63,7 +67,7 @@ class PinCodeScreen extends PureComponent {
<Text style={styles.informationText}>{informationText}</Text>
</View>
<View style={styles.animatedView}>
<PinAnimatedInput pin={pin} />
<PinAnimatedInput pin={pin} loading={loading} />
</View>
<View style={styles.numericKeyboardView}>
<NumericKeyboard onPress={this._handleKeyboardOnPress} />

View File

@ -62,8 +62,12 @@ class ProfileContainer extends Component {
};
componentWillReceiveProps(nextProps) {
const { navigation, currentAccount, activeBottomTab, isLoggedIn } = this.props;
const currentUsername = currentAccount.name !== nextProps.currentAccount.name && nextProps.currentAccount.name;
const {
navigation, currentAccount, activeBottomTab, isLoggedIn,
} = this.props;
const currentUsername = currentAccount.name
!== nextProps.currentAccount.name
&& nextProps.currentAccount.name;
const isParamsChange = nextProps.navigation.state
&& navigation.state
&& nextProps.navigation.state.params
@ -88,10 +92,6 @@ class ProfileContainer extends Component {
this._loadProfile(selectedUser && selectedUser.username);
}
}
_getReplies = async (user) => {
@ -102,39 +102,39 @@ class ProfileContainer extends Component {
comments: result,
});
})
.catch((err) => {});
.catch(() => {});
};
_handleFollowUnfollowUser = async (isFollowAction) => {
const { username, isFollowing } = this.state;
const { currentAccount } = this.props;
const { currentAccount, pinCode } = this.props;
this.setState({
isProfileLoading: true,
});
if (isFollowAction && !isFollowing) {
this._followUser(currentAccount, currentAccount.name, username);
this._followUser(currentAccount, pinCode, currentAccount.name, username);
} else {
this._unfollowUser(currentAccount, currentAccount.name, username);
this._unfollowUser(currentAccount, pinCode, currentAccount.name, username);
}
};
_handleMuteUnmuteUser = async (isMuteAction) => {
const { username, isMuted } = this.state;
const { currentAccount } = this.props;
const { username } = this.state;
const { currentAccount, pinCode } = this.props;
this.setState({
isProfileLoading: true,
});
if (isMuteAction) {
this._muteUser(currentAccount, currentAccount.name, username);
this._muteUser(currentAccount, pinCode, currentAccount.name, username);
}
};
_unfollowUser = (currentAccount, follower, following) => {
unfollowUser(currentAccount, {
_unfollowUser = (currentAccount, pinCode, follower, following) => {
unfollowUser(currentAccount, pinCode, {
follower,
following,
})
@ -146,8 +146,8 @@ class ProfileContainer extends Component {
});
};
_followUser = (currentAccount, follower, following) => {
followUser(currentAccount, {
_followUser = (currentAccount, pinCode, follower, following) => {
followUser(currentAccount, pinCode, {
follower,
following,
})
@ -159,8 +159,8 @@ class ProfileContainer extends Component {
});
};
_muteUser = async (currentAccount, follower, following) => {
ignoreUser(currentAccount, {
_muteUser = async (currentAccount, pinCode, follower, following) => {
ignoreUser(currentAccount, pinCode, {
follower,
following,
})
@ -307,6 +307,7 @@ class ProfileContainer extends Component {
const mapStateToProps = state => ({
isLoggedIn: state.application.isLoggedIn,
currentAccount: state.account.currentAccount,
pinCode: state.account.pin,
isDarkTheme: state.application.isDarkTheme,
activeBottomTab: state.ui.activeBottomTab,
});

View File

@ -85,7 +85,7 @@ class SettingsContainer extends Component {
};
_handleToggleChanged = (action, actionType) => {
const { dispatch, setPinCodeState } = this.props;
const { dispatch } = this.props;
switch (actionType) {
case 'notification':
@ -97,11 +97,6 @@ class SettingsContainer extends Component {
dispatch(isDarkTheme(action));
setTheme(action);
break;
case 'pincode':
// test
dispatch(openPinCodeModal());
setPinCodeState({ isReset: true });
break;
default:
break;
}
@ -111,8 +106,8 @@ class SettingsContainer extends Component {
const { dispatch, setPinCodeState } = this.props;
switch (actionType) {
case 'pincode':
dispatch(openPinCodeModal());
setPinCodeState({ isReset: true });
dispatch(openPinCodeModal());
break;
default:
break;

View File

@ -39,7 +39,7 @@ class SteemConnect extends PureComponent {
if (result) {
dispatch(updateCurrentAccount({ ...result }));
dispatch(addOtherAccount({ username: result.name }));
dispatch(loginAction());
dispatch(loginAction(true));
dispatch(openPinCodeModal());
setPinCodeState({ accessToken, navigateTo: ROUTES.DRAWER.MAIN });
} else {

View File

@ -1,11 +1,3 @@
// TODO: Move all formats functions here!
// const imgRegex = /(https?:\/\/.*\.(?:tiff?|jpe?g|gif|png|svg|ico))/gim;
// const postRegex = /^https?:\/\/(.*)\/(.*)\/(@[\w\.\d-]+)\/(.*)/i;
// const youTubeRegex = /(?:https?:\/\/)?(?:www\.)?(?:youtube\.com\/watch\?v=|youtu\.be\/)([^& \n<]+)(?:[^ \n<]+)?/g;
// const vimeoRegex = /(https?:\/\/)?(www\.)?(?:vimeo)\.com.*(?:videos|video|channels|)\/([\d]+)/i;
// const dTubeRegex = /(https?:\/\/d.tube.#!\/v\/)(\w+)\/(\w+)/g;
export const getPostSummary = (postBody, length, isQuote) => {
if (!postBody) {
return '';
@ -24,3 +16,13 @@ export const getPostSummary = (postBody, length, isQuote) => {
}
return isQuote ? `"${postBody}..."` : `${postBody}...`;
};
export const makeCountFriendly = (value) => {
if (!value) return value;
if (value >= 1000000) return `${intlFormat(value / 1000000)}M`;
if (value >= 1000) return `${intlFormat(value / 1000)}K`;
return intlFormat(value);
};
const intlFormat = num => new Intl.NumberFormat().format(Math.round(num * 10) / 10);

View File

@ -14,7 +14,7 @@ export const parsePost = (post, currentUserName, isSummary = false) => {
_post.json_metadata = JSON.parse(post.json_metadata);
_post.image = postImage(post.json_metadata, post.body);
_post.pending_payout_value = parseFloat(post.pending_payout_value).toFixed(2);
_post.pending_payout_value = parseFloat(post.pending_payout_value).toFixed(3);
_post.created = getTimeFromNow(post.created);
_post.vote_count = post.active_votes.length;
_post.author_reputation = getReputation(post.author_reputation);
@ -121,7 +121,7 @@ export const protocolUrl2Obj = (url) => {
export const parseComments = (comments) => {
comments.map((comment) => {
comment.pending_payout_value = parseFloat(comment.pending_payout_value).toFixed(2);
comment.pending_payout_value = parseFloat(comment.pending_payout_value).toFixed(3);
comment.created = getTimeFromNow(comment.created);
comment.vote_count = comment.active_votes.length;
comment.author_reputation = getReputation(comment.author_reputation);

View File

@ -1,5 +1,5 @@
// import parseDate from './parseDate';
import parseToken from './parseToken';
import parseDate from './parseDate';
import { vestsToSp } from './conversions';
export const getTransactionData = (transaction, walletData, formatNumber) => {
@ -14,7 +14,7 @@ export const getTransactionData = (transaction, walletData, formatNumber) => {
const opData = transaction[1].op[1];
const { timestamp } = transaction[1];
result.transDate = parseDate(timestamp);
result.transDate = timestamp;
result.icon = 'local-activity';
switch (result.opName) {
@ -85,7 +85,7 @@ export const getTransactionData = (transaction, walletData, formatNumber) => {
result.value = `${formatNumber(vestsToSp(opVestingShares, walletData.steemPerMVests), {
minimumFractionDigits: 3,
})} SP`;
result.icon = 'money';
result.icon = 'attach-money';
result.details = `@${acc}`;
break;
case 'fill_order':