Merge pull request #82 from esteemapp/account-management

Account management #24
This commit is contained in:
Feruz M 2018-11-02 16:06:20 +02:00 committed by GitHub
commit f6d65d6ee1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 177 additions and 50 deletions

View File

@ -31,7 +31,7 @@ class IconView extends Component {
};
_getIcon = () => {
const { iconType } = this.props;
const { iconType, children } = this.props;
const name = this._getIconName();
switch (iconType) {
@ -40,11 +40,11 @@ class IconView extends Component {
case 'FontAwesome':
return <FontAwesome {...this.props} />;
case 'SimpleLineIcons':
return <SimpleLineIcons {...this.props}>{this.props.children}</SimpleLineIcons>;
return <SimpleLineIcons {...this.props}>{children}</SimpleLineIcons>;
case 'MaterialCommunityIcons':
return (
<MaterialCommunityIcons name={name} {...this.props}>
{this.props.children}
{children}
</MaterialCommunityIcons>
);
default:

View File

@ -9,6 +9,7 @@ import { NumericKeyboard } from './numericKeyboard';
import { PinAnimatedInput } from './pinAnimatedInput';
import { SideMenu } from './sideMenu';
import Modal from './modal';
import Icon from './icon';
export {
Logo,
@ -24,4 +25,5 @@ export {
PinAnimatedInput,
SideMenu,
Modal,
Icon,
};

View File

@ -1,10 +1,20 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';
// Actions
import { getUserData } from '../../../realm/realm';
import { switchAccount } from '../../../providers/steem/auth';
import { updateCurrentAccount } from '../../../redux/actions/accountAction';
import { openPinCodeModal } from '../../../redux/actions/applicationActions';
// Constanst
import { default as ROUTES } from '../../../constants/routeNames';
// Component
import { SideMenuView } from '..';
const DEFAULT_IMAGE = require('../../../assets/esteem.png');
/*
* Props Name Description
*@props --> props name navigation coming from react-navigation
@ -26,7 +36,15 @@ class SideMenuContainer extends Component {
getUserData().then((userData) => {
userData.forEach((element) => {
accounts.push({ name: element.username, image: 'test' });
accounts.push({
name: `@${element.username}`,
image: element.avatar ? { uri: element.avatar } : DEFAULT_IMAGE,
});
});
accounts.push({
name: 'Add Account',
route: ROUTES.SCREENS.LOGIN,
icon: 'plus-square-o',
});
this.setState({ accounts });
});
@ -36,7 +54,19 @@ class SideMenuContainer extends Component {
_navigateToRoute = (route = null) => {
const { navigation } = this.props;
navigation.navigate(route);
if (route) {
navigation.navigate(route);
}
};
_switchAccount = (username = null) => {
const { dispatch } = this.props;
username = username.slice(1);
switchAccount(username).then((accountData) => {
dispatch(updateCurrentAccount(accountData));
dispatch(openPinCodeModal());
});
};
render() {
@ -50,14 +80,15 @@ class SideMenuContainer extends Component {
userAvatar={null}
accounts={accounts}
currentAccount={currentAccount}
switchAccount={this._switchAccount}
/>
);
}
}
const mapStateToProps = state => ({
isLoggedIn: state.application.isLoggedIn,
currentAccount: state.account.currentAccount,
isLoggedIn: state.application.isLoggedIn || false,
currentAccount: state.account.currentAccount || {},
});
export default connect(mapStateToProps)(SideMenuContainer);

View File

@ -1,11 +1,9 @@
import { StatusBar, Platform } from 'react-native';
import EStyleSheet from 'react-native-extended-stylesheet';
export default EStyleSheet.create({
container: {
flex: 1,
flexDirection: 'column',
marginTop: Platform.OS === 'android' && StatusBar.currentHeight,
},
headerView: {
flex: 1,
@ -26,6 +24,9 @@ export default EStyleSheet.create({
userAvatar: {
marginLeft: '$deviceWidth / 10',
},
otherUserAvatar: {
marginLeft: -15,
},
userInfoView: {
alignSelf: 'flex-end',
marginLeft: 15,
@ -73,4 +74,7 @@ export default EStyleSheet.create({
flex: 1,
flexDirection: 'row',
},
addAccountIcon: {
padding: 10,
},
});

View File

@ -3,11 +3,10 @@ import { View, Text } from 'react-native';
import {
Thumbnail, List, ListItem, Container,
} from 'native-base';
import Icon from 'react-native-vector-icons/FontAwesome';
import LinearGradient from 'react-native-linear-gradient';
// Components
import { IconButton } from '../..';
import { Icon, IconButton } from '../..';
// Constants
import { default as MENU } from '../../../constants/sideMenuItems';
@ -54,10 +53,32 @@ class SideMenuView extends Component {
}
};
_getNameOfUser = () => {
const { currentAccount } = this.props;
if (Object.keys(currentAccount).length === 0) return '';
const jsonMetadata = JSON.parse(currentAccount.json_metadata);
if (Object.keys(jsonMetadata).length !== 0) {
return jsonMetadata.profile.name;
}
return currentAccount.name;
};
_getUserAvatar = () => {
const { currentAccount } = this.props;
if (Object.keys(currentAccount).length === 0) return DEFAULT_IMAGE;
const jsonMetadata = JSON.parse(currentAccount.json_metadata);
if (Object.keys(jsonMetadata).length !== 0) {
return { uri: jsonMetadata.profile.cover_image };
}
return DEFAULT_IMAGE;
};
// Component Functions
render() {
const { userAvatar, navigateToRoute, currentAccount } = this.props;
const {
navigateToRoute, currentAccount, isLoggedIn, switchAccount,
} = this.props;
const { menuItems, isAddAccountIconActive } = this.state;
// TODO: Change dummy data
return (
@ -68,23 +89,28 @@ class SideMenuView extends Component {
colors={['#357ce6', '#2d5aa0']}
style={styles.headerView}
>
<View style={styles.headerContentView}>
<Thumbnail style={styles.userAvatar} source={userAvatar || DEFAULT_IMAGE} />
<View style={styles.userInfoView}>
<Text style={styles.username}>Mustafa</Text>
<Text style={styles.usernick}>@mistikk</Text>
{isLoggedIn && (
<View style={styles.headerContentView}>
<Thumbnail style={styles.userAvatar} source={this._getUserAvatar()} />
<View style={styles.userInfoView}>
<Text style={styles.username}>{this._getNameOfUser()}</Text>
<Text style={styles.usernick}>{`@${currentAccount.name}`}</Text>
</View>
<View style={styles.addAccountIconView}>
{/* TODO: delete android name */}
<IconButton
name={isAddAccountIconActive ? 'arrow-dropup' : 'add-circle-outline'}
androidName={
isAddAccountIconActive ? 'md-arrow-dropup' : 'ios-add-circle-outline'
}
color="white"
size={15}
handleOnPress={() => this._handleOnPressAddAccountIcon()}
style={styles.addAccountIcon}
/>
</View>
</View>
<View style={styles.addAccountIconView}>
{/* TODO: delete android name */}
<IconButton
name={isAddAccountIconActive ? 'arrow-dropup' : 'add-circle-outline'}
androidName={isAddAccountIconActive ? 'md-arrow-dropup' : 'ios-add-circle-outline'}
color="white"
size={15}
handleOnPress={() => this._handleOnPressAddAccountIcon()}
/>
</View>
</View>
)}
</LinearGradient>
<View style={styles.contentView}>
<List
@ -94,9 +120,18 @@ class SideMenuView extends Component {
<ListItem
noBorder
style={styles.listItem}
onPress={() => navigateToRoute(item.route)}
onPress={() => {
if (item.route) {
navigateToRoute(item.route);
} else {
switchAccount(item.name);
}
}}
>
<Icon style={styles.listItemIcon} name={item.icon} />
{item.icon && <Icon iconType="FontAwesome" style={styles.listItemIcon} name={item.icon} />}
{item.image && (
<Thumbnail small style={styles.otherUserAvatar} source={item.image} />
)}
<Text style={styles.listItemText}>{item.name}</Text>
</ListItem>
)}

View File

@ -36,18 +36,18 @@ const authMenuItems = [
route: 'Settings',
icon: 'gear',
},
{
name: 'LoginTest',
route: ROUTES.SCREENS.LOGIN,
icon: 'user-o',
},
];
const noAuthMenuItems = [
{
name: 'Login',
name: 'Add Account',
route: ROUTES.SCREENS.LOGIN,
icon: 'user-o',
icon: 'plus-square-o',
},
{
name: 'Settings',
route: 'Settings',
icon: 'gear',
},
];

View File

@ -7,6 +7,7 @@ import {
updateUserData,
setPinCode,
getPinCode,
updateCurrentUsername,
} from '../../realm/realm';
import { encryptKey, decryptKey } from '../../utils/crypto';
import steemConnect from './steemConnectAPI';
@ -21,6 +22,7 @@ export const Login = (username, password) => {
posting: null,
};
let loginFlag = false;
let avatar = '';
return new Promise((resolve, reject) => {
// Get user account data from STEEM Blockchain
@ -53,9 +55,14 @@ export const Login = (username, password) => {
}
});
const jsonMetadata = JSON.parse(account.json_metadata);
if (Object.keys(jsonMetadata).length !== 0) {
avatar = jsonMetadata.profile.cover_image;
}
if (loginFlag) {
const userData = {
username,
avatar,
authType: 'masterKey',
masterKey: '',
postingKey: '',
@ -85,10 +92,17 @@ export const Login = (username, password) => {
export const loginWithSC2 = async (accessToken) => {
await steemConnect.setAccessToken(accessToken);
const account = await steemConnect.me();
let avatar = '';
return new Promise((resolve, reject) => {
const jsonMetadata = JSON.parse(account.json_metadata);
if (Object.keys(jsonMetadata).length !== 0) {
avatar = jsonMetadata.profile.cover_image;
}
const userData = {
username: account.account.name,
avatar,
authType: 'steemConnect',
masterKey: '',
postingKey: '',
@ -99,6 +113,7 @@ export const loginWithSC2 = async (accessToken) => {
const authData = {
isLoggedIn: true,
currentUsername: account.account.name,
};
if (isLoggedInUser(account.account.name)) {
@ -154,6 +169,7 @@ export const setUserDataWithPinCode = data => new Promise((resolve, reject) => {
.then(() => {
const authData = {
isLoggedIn: true,
currentUsername: userData.username,
};
setAuthStatus(authData)
@ -227,6 +243,7 @@ export const verifyPinCode = async (data) => {
if (loginFlag) {
const authData = {
isLoggedIn: true,
currentUsername: data.username,
};
const response = {
accessToken: decryptKey(userData.accessToken, data.pinCode),
@ -249,6 +266,20 @@ export const verifyPinCode = async (data) => {
});
};
export const switchAccount = username => new Promise((resolve, reject) => {
getAccount(username)
.then((result) => {
const account = result[0];
updateCurrentUsername(username).then(() => {
resolve(account);
}).catch(() => {
reject(new Error('Unknown error, please contact to eSteem.'));
});
}).catch(() => {
reject(new Error('Unknown error, please contact to eSteem.'));
});
});
const getPrivateKeys = (username, password) => ({
active: dsteem.PrivateKey.fromLogin(username, password, 'active'),
memo: dsteem.PrivateKey.fromLogin(username, password, 'memo'),

View File

@ -8,6 +8,7 @@ const userSchema = {
name: USER_SCHEMA,
properties: {
username: { type: 'string' },
avatar: { type: 'string' },
authType: { type: 'string' },
postingKey: { type: 'string' },
activeKey: { type: 'string' },
@ -22,6 +23,7 @@ const authSchema = {
properties: {
isLoggedIn: { type: 'bool', default: false },
pinCode: { type: 'string' },
currentUsername: { type: 'string' },
},
};
@ -104,7 +106,7 @@ export const getAuthStatus = () => new Promise((resolve, reject) => {
try {
const auth = realm.objects(AUTH_SCHEMA);
if (auth['0']) {
resolve(auth['0'].isLoggedIn);
resolve(auth['0']);
} else {
resolve(false);
}
@ -116,8 +118,6 @@ export const getAuthStatus = () => new Promise((resolve, reject) => {
export const setAuthStatus = authStatus => new Promise((resolve, reject) => {
try {
const auth = realm.objects(AUTH_SCHEMA);
const test = Array.from(auth);
const test1 = Array.from(auth).length;
realm.write(() => {
if (Array.from(auth).length > 0) {
auth[0].isLoggedIn = authStatus.isLoggedIn;
@ -132,6 +132,28 @@ export const setAuthStatus = authStatus => new Promise((resolve, reject) => {
}
});
export const updateCurrentUsername = username => new Promise((resolve, reject) => {
try {
const auth = realm.objects(AUTH_SCHEMA);
realm.write(() => {
if (Array.from(auth).length > 0) {
auth[0].currentUsername = username;
resolve(auth[0]);
} else {
const authData = {
isLoggedIn: false,
pinCode: '',
currentUsername: username,
};
realm.create(AUTH_SCHEMA, { ...authData });
resolve(authData);
}
});
} catch (error) {
reject(error);
}
});
export const setPinCode = pinCode => new Promise((resolve, reject) => {
try {
const auth = realm.objects(AUTH_SCHEMA);

View File

@ -104,7 +104,7 @@ class AuthorScreen extends Component {
let user;
await getAuthStatus().then((res) => {
isLoggedIn = res;
isLoggedIn = res.isLoggedIn;
});
if (isLoggedIn) {

View File

@ -52,7 +52,7 @@ export default class HomeScreen extends PureComponent {
let isLoggedIn;
await getAuthStatus().then((res) => {
isLoggedIn = res;
isLoggedIn = res.isLoggedIn;
});
if (isLoggedIn) {

View File

@ -60,7 +60,7 @@ class PostContainer extends Component {
let isLoggedIn;
await getAuthStatus().then((res) => {
isLoggedIn = res;
isLoggedIn = res.isLoggedIn;
});
if (isLoggedIn) {

View File

@ -75,7 +75,7 @@ class ProfilePage extends React.Component {
let about;
await getAuthStatus().then((res) => {
isLoggedIn = res;
isLoggedIn = res.isLoggedIn;
});
if (isLoggedIn) {

View File

@ -147,7 +147,7 @@ class ProfileContainer extends Component {
let username;
await getAuthStatus().then((res) => {
isLoggedIn = res;
isLoggedIn = res.isLoggedIn;
});
if (selectedUser) {

View File

@ -26,13 +26,15 @@ class SplashContainer extends Component {
const { navigation, dispatch } = this.props;
getAuthStatus().then((res) => {
if (res) {
if (res.isLoggedIn) {
getUserData().then((response) => {
if (response.length > 0) {
response.forEach((accountData) => {
dispatch(addOtherAccount({ username: accountData.username }));
dispatch(
addOtherAccount({ username: accountData.username, avatar: accountData.avatar }),
);
});
getAccount(response[response.length - 1].username).then((accountData) => {
getAccount(res.currentUsername).then((accountData) => {
dispatch(updateCurrentAccount(...accountData));
dispatch(activeApplication());
dispatch(login());

View File

@ -42,7 +42,7 @@ class WalletPage extends Component {
let globalProperties;
await getAuthStatus().then((res) => {
isLoggedIn = res;
isLoggedIn = res.isLoggedIn;
});
if (isLoggedIn) {

View File

@ -2,7 +2,7 @@ import { getUserData, getAuthStatus } from '../realm/realm';
export const getUserIsLoggedIn = () => {
getAuthStatus()
.then(res => res)
.then(res => res.isLoggedIn)
.catch(() => null);
};