Merge pull request #18 from esteemapp/steemconnect

Steemconnect Login
This commit is contained in:
Feruz M 2018-09-15 19:44:40 +03:00 committed by GitHub
commit c273a22195
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 1026 additions and 553 deletions

1
.gitignore vendored
View File

@ -35,6 +35,7 @@ local.properties
node_modules/
npm-debug.log
yarn-error.log
config.js
# BUCK
buck-out/

View File

@ -19,6 +19,12 @@
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="esteem"/>
</intent-filter>
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
</application>

View File

@ -8,7 +8,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
classpath 'com.android.tools.build:gradle:3.1.4'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files

View File

@ -17,5 +17,4 @@
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
android.useDeprecatedNdk=true
android.enableAapt2=false
android.useDeprecatedNdk=true

View File

@ -22,6 +22,17 @@
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>app.esteem.mobile</string>
<key>CFBundleURLSchemes</key>
<array>
<string>esteem</string>
</array>
</dict>
</array>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>

29
package-lock.json generated
View File

@ -2455,7 +2455,7 @@
},
"buffer": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-3.6.0.tgz",
"resolved": "http://registry.npmjs.org/buffer/-/buffer-3.6.0.tgz",
"integrity": "sha1-pyyTb3e5a/UvX357RnGAYoVR3vs=",
"requires": {
"base64-js": "0.0.8",
@ -3021,6 +3021,22 @@
"gud": "1.0.0"
}
},
"cross-fetch": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-1.1.1.tgz",
"integrity": "sha512-+VJE04+UfxxmBfcnmAu/lKor53RUCx/1ilOti4p+JgrnLQ4AZZIRoe2OEd76VaHyWQmQxqKnV+TaqjHC4r0HWw==",
"requires": {
"node-fetch": "1.7.3",
"whatwg-fetch": "2.0.3"
},
"dependencies": {
"whatwg-fetch": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz",
"integrity": "sha1-nITsLc9oGH/wC8ZOEnS0QhduHIQ="
}
}
},
"cross-spawn": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
@ -9914,6 +9930,15 @@
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
},
"sc2-sdk": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/sc2-sdk/-/sc2-sdk-1.0.2.tgz",
"integrity": "sha512-QADFzQYCsm6OOQE3cxd/NEhSTEtcohzJ6KQN4E5YaEO/bl2WgWy1Dgz4SFRhjIwTh5luzad2TqfUN7uF4IL8xg==",
"requires": {
"cross-fetch": "^1.1.1",
"debug": "^2.2.0"
}
},
"secp256k1": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-3.5.0.tgz",
@ -9939,7 +9964,7 @@
"dependencies": {
"commander": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz",
"resolved": "http://registry.npmjs.org/commander/-/commander-2.8.1.tgz",
"integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=",
"requires": {
"graceful-readlink": "1.0.1"

View File

@ -42,7 +42,8 @@
"redux-promise": "^0.6.0",
"redux-thunk": "^2.3.0",
"remarkable": "^1.7.1",
"rn-placeholder": "^1.2.0"
"rn-placeholder": "^1.2.0",
"sc2-sdk": "^1.0.2"
},
"devDependencies": {
"babel-eslint": "^8.2.6",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -11,6 +11,7 @@ import {
import Ionicons from "react-native-vector-icons/Ionicons";
import { Navigation } from "react-native-navigation";
import { lookupAccounts } from "../../providers/steem/dsteem";
import { SEARCH_API_TOKEN } from "../../../config";
export default class Search extends Component {
constructor() {
@ -30,12 +31,15 @@ export default class Search extends Component {
};
handleSearch = async text => {
if (text.length < 3) return null;
if (text.length < 3) return;
let users;
let posts;
let scroll_id;
await this.setState({ loading: true });
await this.setState({
loading: true,
text: text
});
users = await lookupAccounts(text);
@ -47,7 +51,7 @@ export default class Search extends Component {
headers: {
// TODO: Create a config file for authorization
Authorization: "KEY",
Authorization: SEARCH_API_TOKEN,
"Content-Type": "application/json",
},
body: JSON.stringify(data),
@ -73,9 +77,10 @@ export default class Search extends Component {
return (
<View
style={{
backgroundColor: "rgba(0, 0, 0, 0.7)",
backgroundColor: "rgba(0, 0, 0, 0.8)",
height: Dimensions.get("window").height,
paddingTop: 25,
flex: 1
}}
>
<View

View File

@ -323,7 +323,9 @@ export const goToNoAuthScreens = () =>
],
options: {
topBar: {
visible: false,
visible: true,
noBorder: true,
elevation: 0
},
},
},

View File

@ -1,170 +1,212 @@
/*eslint-disable no-unused-vars*/
/*eslint-disable no-console*/
import * as dsteem from "dsteem";
import { getAccount } from "./dsteem";
import { setUserData, setAuthStatus } from "../../realm/realm";
/*eslint-disable-next-line no-unused-vars*/
import { encryptKey, decryptKey } from "../../utils/crypto";
import { encryptKey } from "../../utils/crypto";
import steemConnect from "./steemConnectAPI";
/*eslint-enable no-unused-vars*/
export const Login = (username, password) => {
let account;
let publicKeys;
let privateKeys;
let isPassword;
let isPostingKey;
let pinCode = "pinCode";
let account;
let publicKeys;
let privateKeys;
let isPassword;
let isPostingKey;
let pinCode = "pinCode";
return new Promise((resolve, reject) => {
// Get user account data from STEEM Blockchain
getAccount(username)
.then(result => {
if (result.length < 1) {
reject(new Error("Wrong @username"));
}
return new Promise((resolve, reject) => {
// Get user account data from STEEM Blockchain
getAccount(username)
.then(result => {
if (result.length < 1) {
reject(new Error("Wrong @username"));
}
account = result[0];
account = result[0];
// Public keys of user
publicKeys = {
active: account["active"].key_auths.map(x => x[0]),
memo: account["memo_key"],
owner: account["owner"].key_auths.map(x => x[0]),
posting: account["posting"].key_auths.map(x => x[0]),
};
})
.then(() => {
try {
// Set private keys of user
privateKeys = {
active: dsteem.PrivateKey.fromLogin(
username,
password,
"active"
).toString(),
memo: dsteem.PrivateKey.fromLogin(
username,
password,
"memo"
).toString(),
owner: dsteem.PrivateKey.fromLogin(
username,
password,
"owner"
).toString(),
posting: dsteem.PrivateKey.fromLogin(
username,
password,
"posting"
).toString(),
};
} catch (error) {
reject(new Error("Wrong Key/Password"));
}
})
.then(() => {
// Validate Pasword/Key
try {
// Validate Master Key
/*eslint-disable no-mixed-spaces-and-tabs*/
isPassword =
// Public keys of user
publicKeys = {
active: account["active"].key_auths.map(x => x[0]),
memo: account["memo_key"],
owner: account["owner"].key_auths.map(x => x[0]),
posting: account["posting"].key_auths.map(x => x[0]),
};
})
.then(() => {
try {
// Set private keys of user
privateKeys = {
active: dsteem.PrivateKey.fromLogin(
username,
password,
"active"
).toString(),
memo: dsteem.PrivateKey.fromLogin(
username,
password,
"memo"
).toString(),
owner: dsteem.PrivateKey.fromLogin(
username,
password,
"owner"
).toString(),
posting: dsteem.PrivateKey.fromLogin(
username,
password,
"posting"
).toString(),
};
} catch (error) {
reject(new Error("Wrong Key/Password"));
}
})
.then(() => {
// Validate Pasword/Key
try {
// Validate Master Key
/*eslint-disable no-mixed-spaces-and-tabs*/
isPassword =
dsteem.PrivateKey.fromLogin(
username,
password,
"posting"
username,
password,
"posting"
)
.createPublic()
.toString() === publicKeys.posting.toString();
.createPublic()
.toString() === publicKeys.posting.toString();
if (isPassword) {
/**
if (isPassword) {
/**
* User data
* TODO: Encryption
*/
let userData = {
username: username,
authType: "masterKey",
masterKey: encryptKey(password, pinCode),
postingKey: encryptKey(
privateKeys.posting,
pinCode
),
activeKey: encryptKey(privateKeys.active, pinCode),
memoKey: encryptKey(privateKeys.memo, pinCode),
};
let authData = {
isLoggedIn: true,
};
let userData = {
username: username,
authType: "masterKey",
masterKey: encryptKey(password, pinCode),
postingKey: encryptKey(
privateKeys.posting,
pinCode
),
activeKey: encryptKey(privateKeys.active, pinCode),
memoKey: encryptKey(privateKeys.memo, pinCode),
accessToken: ""
};
let authData = {
isLoggedIn: true,
};
// Set auth state to true
setAuthStatus(authData)
.then(() => {
// Save user data to Realm DB
setUserData(userData)
.then(() => {
resolve(isPassword);
})
.catch(err => {
reject(err);
});
})
.catch(err => {
reject(err);
});
} else {
// Validate Posting Key
isPostingKey =
// Set auth state to true
setAuthStatus(authData)
.then(() => {
// Save user data to Realm DB
setUserData(userData)
.then(() => {
resolve(isPassword);
})
.catch(err => {
reject(err);
});
})
.catch(err => {
reject(err);
});
} else {
// Validate Posting Key
isPostingKey =
publicKeys.posting.toString() ===
dsteem.PrivateKey.fromString(password)
.createPublic()
.toString();
.createPublic()
.toString();
/**
/**
* User data
* TODO: Encryption
*/
let userData = {
username: username,
authType: "postingKey",
postingKey: privateKeys.posting,
masterKey: "",
activeKey: "",
memoKey: "",
};
let userData = {
username: username,
authType: "postingKey",
postingKey: privateKeys.posting,
masterKey: "",
activeKey: "",
memoKey: "",
accessToken: ""
};
let authData = {
isLoggedIn: true,
};
let authData = {
isLoggedIn: true,
};
try {
if (isPostingKey) {
// Set auth state to true
setAuthStatus(authData)
.then(() => {
// Save user data to Realm DB
setUserData(userData)
.then(() => {
resolve(isPostingKey);
})
.catch(err => {
reject(err);
});
})
.catch(err => {
reject(err);
});
} else {
reject(new Error("Wrong Key/Password"));
}
} catch (error) {
reject(new Error("Wrong Key/Password"));
}
}
} catch (error) {
reject(new Error("Wrong Key/Password"));
}
})
.catch(err => {
// eslint-disable-next-line
try {
if (isPostingKey) {
// Set auth state to true
setAuthStatus(authData)
.then(() => {
// Save user data to Realm DB
setUserData(userData)
.then(() => {
resolve(isPostingKey);
})
.catch(err => {
reject(err);
});
})
.catch(err => {
reject(err);
});
} else {
reject(new Error("Wrong Key/Password"));
}
} catch (error) {
reject(new Error("Wrong Key/Password"));
}
}
} catch (error) {
reject(new Error("Wrong Key/Password"));
}
})
.catch(err => {
// eslint-disable-next-line
console.log(err);
reject(new Error("Check your username"));
});
});
reject(new Error("Check your username"));
});
});
};
export const loginWithSC2 = async (access_token, pinCode) => {
let account;
await steemConnect.setAccessToken(access_token);
account = await steemConnect.me();
console.log(account._id);
console.log(account.name);
return new Promise((resolve, reject) => {
let userData = {
username: account.name,
authType: "steemConnect",
accessToken: encryptKey(access_token, pinCode),
postingKey: "",
masterKey: "",
activeKey: "",
memoKey: ""
};
let authData = {
isLoggedIn: true
};
setAuthStatus(authData).then(() => {
setUserData(userData).then(() => {
resolve(true);
}).catch(error => {
reject(error);
});
}).catch(error => {
reject(error);
});
});
};

View File

@ -9,7 +9,7 @@ import { parsePosts, parseComments } from "../../utils/postParser";
let rewardFund = null;
let medianPrice = null;
let client;
let client = new Client("https://api.steemit.com");
getClient = async () => {
let server = await AsyncStorage.getItem("server");

View File

@ -0,0 +1,188 @@
import steemConnect from "./steemConnectAPI";
/**
* @method to upvote/unvote a content
* @param {*} vote
*/
export const vote = (vote) => {
return new Promise((resolve, reject) => {
steemConnect.vote(vote.voter, vote.author, vote.permlink, vote.weight).then(result => {
console.log(result);
resolve(result);
}).catch(error => {
reject(error);
});
});
};
/**
* @method to submit a comment/reply
* @param {*} comment
*/
export const comment = (comment) => {
return new Promise((resolve, reject) => {
steemConnect.comment(comment.parentAuthor, comment.parentPermlink, comment.author, comment.permlink, comment.title, comment.body, comment.jsonMetadata).then(result => {
console.log(result);
resolve(result);
}).catch(error => {
reject(error);
});
});
};
export const post = (post) => {
// Create empty array for the operations
const operations = [];
// Create the object for the post
const commentOp = [
"comment",
{
parent_author: "", // Since it is a post, parent author is empty
parent_permlink: post.tags[0], // Parent permlink will be the 0th index in the tags array
author: post.author, // Author is the current logged in username
permlink: post.permlink, // Permlink of the post
title: post.title, // Title of the post
body: post.description, // Description of the post
json_metadata: post.json_metadata, // JSON string with the tags, app, and format
},
];
operations.push(commentOp);
const commentOptionsConfig = prepareBeneficiaries(post);
operations.push(commentOptionsConfig);
return new Promise((resolve, reject) => {
steemConnect.broadcast(operations).then(result => {
console.log(result);
resolve(result);
}).catch(error => {
reject(error);
});
});
};
export const prepareBeneficiaries = (post) => {
let beneficiariesObject = {
author: post.author,
permlink: post.permlink,
allow_votes: true,
allow_curation_rewards: true,
max_accepted_payout: "1000000.000 SBD",
percent_steem_dollars: "10000",
extensions: [
[
0, {
beneficiaries: [
{
account: "esteemapp",
weight: 1000 // 10%
}
]
}
]
]
};
return ["comment_options", beneficiariesObject];
};
export const follow = (data) => {
return new Promise((resolve, reject) => {
steemConnect.follow(data.follower, data.following).then(result => {
resolve(result);
}).catch(error => {
reject(error);
});
});
};
export const unFollow = (data) => {
return new Promise((resolve, reject) => {
steemConnect.unfollow(data.unfollower, data.unfollowing).then(result => {
resolve(result);
}).catch(error => {
reject(error);
});
});
};
/**
* @method to claim rewards
* @param {*} data
*/
export const claimRewards = (data) => {
return new Promise((resolve, reject) => {
steemConnect.claimRewardBalance(data.account, data.rewardSteem, data.rewardSBD, data.VESTS).then(result => {
resolve(result);
}).catch(error => {
reject(error);
});
});
};
/**
* @method to mute/block an user
* @param {*} data
*/
export const muteUser = (data) => {
return new Promise((resolve, reject) => {
steemConnect.ignore(data.follower, data.following).then(result => {
resolve(result);
}).catch(error => {
reject(error);
});
});
};
export const reblogPost = (data) => {
return new Promise((resolve, reject) => {
steemConnect.reblog(data.account, data.author, data.permlink).then(result => {
resolve(result);
}).catch(error => {
reject(error);
});
});
};
export const removeAccessToken = (data) => {
return new Promise((resolve, reject) => {
try {
steemConnect.removeAccessToken();
resolve();
} catch (error) {
reject(error);
}
});
};
/**
* @method to revoke access token (SteemConnect logout function)
*/
export const revokeToken = () => {
return new Promise((resolve, reject) => {
try {
steemConnect.revokeToken();
resolve();
} catch (error) {
reject(error);
}
});
};
/**
* @method to update user profile data
* @param {*} data
*/
export const updateUserMetadata = (data) => {
return new Promise((resolve, reject) => {
steemConnect.updateUserMetadata(data).then(result => {
resolve(result);
}).catch(error => {
reject(error);
});
});
};

View File

@ -0,0 +1,8 @@
import sc2 from "sc2-sdk";
const api = sc2.Initialize({
app: "esteem-app",
callbackURL: "http://localhost:3415",
});
export default api;

View File

@ -5,91 +5,92 @@ const USER_SCHEMA = "user";
const AUTH_SCHEMA = "auth";
const userSchema = {
name: USER_SCHEMA,
properties: {
username: { type: "string" },
authType: { type: "string" },
postingKey: { type: "string" },
activeKey: { type: "string" },
memoKey: { type: "string" },
masterKey: { type: "string" },
},
name: USER_SCHEMA,
properties: {
username: { type: "string" },
authType: { type: "string" },
postingKey: { type: "string" },
activeKey: { type: "string" },
memoKey: { type: "string" },
masterKey: { type: "string" },
accessToken: { type: "string" },
},
};
const authSchema = {
name: AUTH_SCHEMA,
properties: {
isLoggedIn: { type: "bool", default: false },
},
name: AUTH_SCHEMA,
properties: {
isLoggedIn: { type: "bool", default: false },
},
};
let realm = new Realm({ schema: [userSchema, authSchema] });
export const getUserData = () => {
return new Promise((resolve, reject) => {
try {
let user = realm.objects(USER_SCHEMA);
resolve(user);
} catch (error) {
reject(error);
}
});
return new Promise((resolve, reject) => {
try {
let user = realm.objects(USER_SCHEMA);
resolve(user);
} catch (error) {
reject(error);
}
});
};
export const setUserData = userData => {
return new Promise((resolve, reject) => {
try {
realm.write(() => {
realm.create(userSchema.name, userData);
resolve(userData);
});
} catch (error) {
reject(error);
}
});
return new Promise((resolve, reject) => {
try {
realm.write(() => {
realm.create(userSchema.name, userData);
resolve(userData);
});
} catch (error) {
reject(error);
}
});
};
export const removeUserData = () => {
return new Promise((resolve, reject) => {
setAuthStatus({ isLoggedIn: true }).then(() => {
try {
realm.write(() => {
realm.deleteAll();
});
resolve();
} catch (error) {
alert(error);
reject(error);
}
});
});
return new Promise((resolve, reject) => {
setAuthStatus({ isLoggedIn: true }).then(() => {
try {
realm.write(() => {
realm.deleteAll();
});
resolve();
} catch (error) {
alert(error);
reject(error);
}
});
});
};
export const getAuthStatus = () => {
return new Promise((resolve, reject) => {
try {
let auth = realm.objects(AUTH_SCHEMA);
if (auth["0"]) {
resolve(auth["0"].isLoggedIn);
} else {
resolve(false);
}
} catch (error) {
reject(error);
}
});
return new Promise((resolve, reject) => {
try {
let auth = realm.objects(AUTH_SCHEMA);
if (auth["0"]) {
resolve(auth["0"].isLoggedIn);
} else {
resolve(false);
}
} catch (error) {
reject(error);
}
});
};
export const setAuthStatus = authStatus => {
return new Promise((resolve, reject) => {
try {
realm.write(() => {
realm.create(authSchema.name, authStatus);
resolve(authStatus);
});
} catch (error) {
alert(error);
reject(error);
}
});
return new Promise((resolve, reject) => {
try {
realm.write(() => {
realm.create(authSchema.name, authStatus);
resolve(authStatus);
});
} catch (error) {
alert(error);
reject(error);
}
});
};

View File

@ -36,6 +36,8 @@ export default class Home extends React.PureComponent {
animate: true,
hideOnScroll: true,
drawBehind: false,
noBorder: true,
elevation: 0,
},
layout: {
backgroundColor: "#f5fcff",

View File

@ -14,6 +14,7 @@ import Editor from "./editor/editor";
import Discover from "./discover/discover";
import Settings from "./settings/settings";
import Notifications from "./notifications/notification";
import SteemConnect from "./steem-connect/steemConnect";
import PostCard from "../components/post-card/postCard";
import Search from "../components/search/search";
@ -40,6 +41,7 @@ function registerScreens() {
Navigation.registerComponent("navigation.eSteem.Author", () => Author);
Navigation.registerComponent("navigation.eSteem.PostCard", () => PostCard);
Navigation.registerComponent("navigation.eSteem.Search", () => Search);
Navigation.registerComponent("navigation.eSteem.SteemConnect", () => SteemConnect);
}
module.exports = {

View File

@ -5,36 +5,86 @@ import {
Text,
StyleSheet,
Image,
StatusBar,
TouchableOpacity,
Linking,
BackHandler,
Dimensions,
TextInput,
WebView
} from "react-native";
import {
Item,
Header,
Input,
Card,
Button,
Container,
Icon,
Left,
Right,
Body,
Label,
Thumbnail,
} from "native-base";
import Ionicons from "react-native-vector-icons/Ionicons";
import Tabs from "../home/customTab";
import ScrollableTabView from "@esteemapp/react-native-scrollable-tab-view";
import { Login } from "../../providers/steem/auth";
import { lookupAccounts } from "../../providers/steem/dsteem";
import { goToAuthScreens } from "../../navigation";
import RNRestart from "react-native-restart";
import { Navigation } from "react-native-navigation";
import FastImage from "react-native-fast-image";
class LoginPage extends Component {
static get options() {
return {
_statusBar: {
visible: true,
drawBehind: false,
},
topBar: {
animate: true,
hideOnScroll: false,
drawBehind: false,
noBorder: true,
visible: true,
elevation: 0,
leftButtons: {},
rightButtons: [
{
id: "signup",
text: "Sign Up",
color: "#a7adaf",
marginRight: 50
}
],
},
layout: {
backgroundColor: "#f5fcff",
},
bottomTabs: {
visible: false,
drawBehind: true,
},
};
}
constructor(props) {
super(props);
Navigation.events().bindComponent(this);
this.handleUsername = this.handleUsername.bind(this);
this.state = {
username: "",
password: "",
isLoading: false,
isUsernameValid: true,
usernameBorderColor: "#c1c5c7",
passwordBorderColor: "#c1c5c7"
};
}
componentDidMount() {
BackHandler.addEventListener("hardwareBackPress", () => {
Navigation.pop(this.props.componentId);
return true;
});
Linking.getInitialURL().then((url) => {
console.log(url);
});
}
componentWillUnmount() {
BackHandler.removeEventListener("hardwareBackPress");
}
doLogin = () => {
this.setState({ isLoading: true });
@ -53,239 +103,310 @@ class LoginPage extends Component {
});
};
handleUsername = async (username) => {
await this.setState({ username });
let validUsers = await lookupAccounts(username);
await this.setState({ isUsernameValid: validUsers.includes(username) });
}
navigationButtonPressed({ buttonId }) {
if (buttonId === "signup") {
Linking.openURL("https://signup.steemit.com/?ref=esteem")
.catch(err => console.error('An error occurred', err));
}
}
loginwithSc2 = () => {
Navigation.showModal({
stack: {
children: [{
component: {
name: "navigation.eSteem.SteemConnect",
passProps: {},
options: {
topBar: {
title: {
text: "Login via SC2"
}
}
}
}
}]
}
});
}
render() {
return (
<Container style={styles.container}>
<Header style={{ backgroundColor: "white", height: 80 }}>
<Left>
<Button
transparent
onPress={() => this.props.navigation.toggleDrawer()}
>
<Thumbnail
style={{
width: 32,
height: 32,
borderRadius: 16,
margin: 10,
}}
source={require("../../assets/esteem.jpg")}
/>
</Button>
</Left>
<Body />
<Right>
<Text
style={{
color: "#a7adaf",
marginHorizontal: 20,
fontWeight: "bold",
}}
>
Sign Up
</Text>
</Right>
</Header>
<View style={styles.header}>
<View
style={{
flex: 0.5,
alignItems: "center",
paddingLeft: 10,
}}
>
<Text
style={{
fontSize: 40,
fontWeight: "600",
color: "#626262",
marginTop: 35,
}}
>
Sign in
</Text>
<Text style={{ color: "#a7adaf", marginTop: 20 }}>
with your username {"\n"} and password {"\n"} to get
all the {"\n"}{" "}
<Text
style={{ fontWeight: "bold", color: "#a7adaf" }}
>
benefits of eSteem
</Text>{" "}
</Text>
</View>
<View style={{ flex: 0.5, overflow: "hidden", padding: 0 }}>
<Image
style={{
width: 220,
height: 304,
marginTop: 10,
marginLeft: 20,
}}
source={require("../../assets/love_mascot.png")}
<View style={{ flex: 1 }}>
<ScrollableTabView
style={styles.tabView}
renderTabBar={() => (
<Tabs
style={styles.tabbar}
tabUnderlineDefaultWidth={100} // default containerWidth / (numberOfTabs * 4)
tabUnderlineScaleX={2} // default 3
activeColor={"#357ce6"}
inactiveColor={"#222"}
/>
</View>
</View>
)}>
<View tabLabel="Sign in" style={styles.tabbarItem}>
<View
style={{ padding: 30, backgroundColor: "white", flex: 0.4 }}
>
<View>
<Item
rounded
<View
style={{
margin: 5,
backgroundColor: "#f6f6f6",
height: 40,
marginVertical: 10,
overflow: "hidden",
borderColor: "white",
}}
>
<Icon
name="at"
style={{
backgroundColor: "#ececec",
height: 40,
width: 40,
alignItems: "center",
padding: 8,
color: "#a7adaf",
fontWeight: "bold",
}}
/>
<Input
backgroundColor: '#f5f5f5',
height: 60,
borderBottomWidth: 2,
borderBottomColor: this.state.isUsernameValid ? (this.state.usernameBorderColor) : ('red'),
borderTopLeftRadius: 8,
borderTopRightRadius: 8,
marginHorizontal: 30,
marginVertical: 20,
flexDirection: 'row'
}}>
{ (this.state.username.length > 2) ? (
<View style={{ flex: 0.15 }}>
<FastImage
style={{
width: 24,
height: 24,
borderRadius: 12,
top: 15,
marginLeft: 12,
}}
source={ { uri: `https://steemitimages.com/u/${this.state.username}/avatar/small`, priority: FastImage.priority.high } }
resizeMode={FastImage.resizeMode.cover}
/>
</View>
) : (
<Ionicons
name="md-at"
style={{
flex: 0.15,
fontSize: 25,
top: 18,
left: 12,
color: "#c1c5c7",
}}
/>
)}
<TextInput
onFocus={() => this.setState({ usernameBorderColor: '#357ce6' })}
onSubmitEditing={() => this.setState({ usernameBorderColor: '#c1c5c7' })}
autoCapitalize="none"
placeholder="username"
onChangeText={text =>
this.setState({ username: text })
}
editable={true}
textContentType='username'
onChangeText={text => { this.handleUsername(text) }}
value={this.state.username}
/>
</Item>
style={{
height: 60,
flex: 0.7
}}/>
<Item
rounded
{ (this.state.username.length > 0) ? (
<Ionicons
onPress={() => this.setState({ username: '' })}
name="md-close-circle"
style={{
flex: 0.15,
fontSize: 25,
top: 18,
left: 8,
color: "#c1c5c7",
}}
/>
) : (
null
)}
</View>
<View
style={{
margin: 5,
backgroundColor: "#f6f6f6",
height: 40,
marginVertical: 10,
overflow: "hidden",
borderColor: "white",
}}
>
<Icon
backgroundColor: '#f5f5f5',
height: 60,
borderBottomWidth: 2,
borderBottomColor: this.state.passwordBorderColor,
borderTopLeftRadius: 8,
borderTopRightRadius: 8,
marginHorizontal: 30,
marginVertical: 20,
flexDirection: 'row'
}}>
<Ionicons
name="md-lock"
style={{
backgroundColor: "#ececec",
height: 40,
width: 40,
alignItems: "center",
paddingVertical: 7,
paddingLeft: 13,
color: "#a7adaf",
fontWeight: "bold",
flex: 0.15,
fontSize: 25,
top: 18,
left: 14,
color: "#c1c5c7",
}}
/>
<Input
<TextInput
onFocus={() => this.setState({ passwordBorderColor: '#357ce6' })}
onSubmitEditing={() => this.setState({ passwordBorderColor: '#c1c5c7' })}
secureTextEntry={true}
placeholder="Password or WIF"
textContentType='password'
onChangeText={text =>
this.setState({ password: text })
}
value={this.state.password}
/>
</Item>
<View />
</View>
<View
style={{
borderBottomColor: "lightgray",
borderBottomWidth: 0.7,
marginVertical: 20,
}}
/>
<View
style={{ flexDirection: "row", alignItems: "center" }}
>
<Icon
name="information-circle"
style={{
flex: 0.15,
color: "#a7adaf",
fontSize: 25,
paddingLeft: 10,
}}
/>
<Text style={{ flex: 0.85, color: "#a7adaf" }}>
Don't worry! {"\n"}
Your password is kept locally on your device and
removed upon logout!
</Text>
</View>
</View>
style={{
height: 60,
flex: 0.7,
width: '100%'
}}/>
<View style={styles.footer}>
<View style={{ flex: 0.6, alignItems: "flex-end" }}>
<Text
onPress={() => {
this.props.navigation.goBack();
}}
style={{
color: "#a7adaf",
fontSize: 18,
margin: 25,
}}
>
Skip this screen
</Text>
</View>
<View style={{ flex: 0.4, alignItems: "center" }}>
{this.state.isLoading ? (
<Button
style={{
borderRadius: 25,
padding: 5,
backgroundColor: "#007EE5",
width: 130,
height: 35,
marginTop: 20,
}}
>
<ActivityIndicator
color="white"
style={{ marginHorizontal: 50 }}
/>
</Button>
) : (
<Button
style={{
borderRadius: 25,
padding: 5,
backgroundColor: "#007EE5",
width: 130,
height: 35,
marginTop: 20,
}}
onPress={() => {
this.doLogin();
}}
>
<Text
{ (this.state.password.length > 0) ? (
<Ionicons
onPress={() => this.setState({ password: '' })}
name="md-close-circle"
style={{
color: "white",
fontWeight: "bold",
marginHorizontal: 40,
flex: 0.15,
fontSize: 25,
top: 18,
left: 8,
color: "#c1c5c7",
}}
>
Login
</Text>
</Button>
)}
/>
) : (
null
)}
</View>
<View style={{ flexDirection: 'row', marginHorizontal: 30, paddingLeft: 10 }}>
<Ionicons
color='#c1c5c7'
style={{ flex: 0.125, fontSize: 25, alignSelf: 'center' }}
name='ios-information-circle-outline'/>
<Text
style={{ flex: 0.875, color: '#788187' }}>
User credentials are kept locally on the device. Credentials are removed upon logout!
</Text>
</View>
<View style={{ flexDirection: 'row', margin: 30 }}>
<View style={{ flex: 0.6 }}>
<TouchableOpacity
onPress={goToAuthScreens}
style={{ alignContent: 'center', padding: '9%' }}>
<Text
style={{ color: '#788187', alignSelf: 'center', fontWeight: 'bold' }}>
Cancel
</Text>
</TouchableOpacity>
</View>
<TouchableOpacity
onPress={this.doLogin}
style={{
flex: 0.4,
width: 100,
height: 50,
borderRadius: 30,
backgroundColor: '#357ce6',
flexDirection: 'row',
}}>
{ !this.state.isLoading ? (
<View style={{ flex: 1, flexDirection: 'row' }}>
<Ionicons
color='white'
name='md-person'
style={{
alignSelf: 'center',
fontSize: 25,
flex: 0.4,
left: 15
}}/>
<Text style={{
color: 'white',
fontWeight: '600',
alignSelf: 'center',
fontSize: 16,
flex: 0.6,
}}>
LOGIN
</Text>
</View>
) : (
<ActivityIndicator color='white'
style={{ alignSelf: 'center', flex: 1 }}/>
) }
</TouchableOpacity>
</View>
</View>
</View>
</Container>
<View tabLabel="SteemConnect" style={styles.steemConnectTab}>
<View
style={{ flex: 1, flexDirection: 'row', maxHeight: 200, overflow: 'hidden', backgroundColor: '#ffffff' }}>
<View style={{ flex: 0.4 }}>
<View style={{ alignSelf: 'center', height: 100, top: 70, left: 20 }}>
<Text style={{ textAlign: 'center', textAlignVertical: 'center', fontSize: 20, fontWeight: 'bold', color: '#788187', bottom: 10 }}>
Sign in
</Text>
<Text style={{ textAlign: 'center', textAlignVertical: 'center', color: '#788187', fontSize: 14, fontWeight: '400' }}>
To get all
the benefits
using eSteem
</Text>
</View>
</View>
<View style={{ flex: 0.6 }}>
<Image style={{ width: 217, height: 300, left: 55, top: 10 }} source={require('../../assets/love_mascot.png')} />
</View>
</View>
<View style={{ flex: 1, backgroundColor: '#ffffff', marginTop: 10 }}>
<View style={{ flexDirection: 'row', marginHorizontal: 30, paddingLeft: 10, marginTop: 20 }}>
<Ionicons
color='#c1c5c7'
style={{ flex: 0.125, fontSize: 25, alignSelf: 'center' }}
name='ios-information-circle-outline'/>
<Text
style={{ flex: 0.875, color: '#788187' }}>
If you don't want to keep your password encrypted and saved on your device, you can use Steemconnect.
</Text>
</View>
</View>
<View style={{ alignItems: 'flex-end', backgroundColor: '#ffffff' }}>
<TouchableOpacity
onPress={this.loginwithSc2}
style={{
width: 200,
height: 50,
borderRadius: 30,
backgroundColor: '#357ce6',
flexDirection: 'row',
margin: 20
}}>
<View style={{ flex: 1, flexDirection: 'row' }}>
<Ionicons
color='white'
name='md-person'
style={{
alignSelf: 'center',
fontSize: 25,
marginHorizontal: 20
}}/>
<Text style={{
color: 'white',
fontWeight: '400',
alignSelf: 'center',
fontSize: 16,
}}>
steem
<Text style={{ fontWeight: '800' }}>
connect
</Text>
</Text>
</View>
</TouchableOpacity>
</View>
</View>
</ScrollableTabView>
</View>
);
}
}
@ -300,7 +421,7 @@ const styles = StyleSheet.create({
flexDirection: "row",
padding: 0,
backgroundColor: "white",
marginVertical: 10,
marginBottom: 10,
height: 200,
flex: 0.4,
},
@ -312,5 +433,24 @@ const styles = StyleSheet.create({
backgroundColor: "white",
flexDirection: "row",
},
tabView: {
alignSelf: "center",
backgroundColor: "transparent",
},
tabbar: {
alignSelf: "center",
height: 40,
backgroundColor: "white",
},
tabbarItem: {
flex: 1,
backgroundColor: "#ffffff",
minWidth: Dimensions.get("window").width / 1,
},
steemConnectTab: {
flex: 1,
backgroundColor: "#e9e9e9",
minWidth: Dimensions.get("window").width / 1,
},
});
export default LoginPage;

View File

@ -2,119 +2,119 @@
import React, { Component } from "react";
import { Image } from "react-native";
import {
Content,
Text,
List,
ListItem,
Icon,
Container,
Left,
Right,
Badge,
Content,
Text,
List,
ListItem,
Icon,
Container,
Left,
Right,
Badge,
} from "native-base";
import styles from "./style";
import { Navigation } from "react-native-navigation";
const drawerCover = require("../../assets/drawer-cover.png");
const drawerImage = require("../../assets/esteem.jpg");
const drawerImage = require("../../assets/esteem.png");
const datas = [
{
name: "Login",
route: "Login",
icon: "log-in",
bg: "#C5F442",
},
{
name: "Login",
route: "Login",
icon: "log-in",
bg: "#C5F442",
},
];
export default class LoggedOutSideBar extends Component {
constructor(props) {
super(props);
this.state = {
shadowOffsetWidth: 1,
shadowRadius: 4,
};
}
constructor(props) {
super(props);
this.state = {
shadowOffsetWidth: 1,
shadowRadius: 4,
};
}
hideSideMenu() {
Navigation.mergeOptions("Component14", {
sideMenu: {
["right"]: {
visible: false,
},
},
});
}
hideSideMenu() {
Navigation.mergeOptions("Component14", {
sideMenu: {
["right"]: {
visible: false,
},
},
});
}
render() {
return (
<Container>
<Content
bounces={false}
style={{ flex: 1, backgroundColor: "#fff" }}
>
<Image source={drawerCover} style={styles.drawerCover} />
<Image
square
style={styles.drawerImage}
source={drawerImage}
/>
render() {
return (
<Container>
<Content
bounces={false}
style={{ flex: 1, backgroundColor: "#fff" }}
>
<Image source={drawerCover} style={styles.drawerCover} />
<Image
square
style={styles.drawerImage}
source={drawerImage}
/>
<List
dataArray={datas}
renderRow={data => (
<ListItem
button
noBorder
onPress={() => {
Navigation.push("tab1Stack", {
component: {
name: `navigation.eSteem.${
data.route
}`,
passProps: {},
options: {
topBar: {
title: {},
},
},
},
});
this.hideSideMenu();
}}
>
<Left>
<Icon
active
name={data.icon}
style={{
color: "#777",
fontSize: 26,
width: 30,
}}
/>
<Text style={styles.text}>{data.name}</Text>
</Left>
{data.types && (
<Right style={{ flex: 1 }}>
<Badge
style={{
borderRadius: 3,
height: 25,
width: 72,
backgroundColor: data.bg,
}}
>
<Text style={styles.badgeText}>{`${
data.types
} Types`}</Text>
</Badge>
</Right>
)}
</ListItem>
)}
/>
</Content>
</Container>
);
}
<List
dataArray={datas}
renderRow={data => (
<ListItem
button
noBorder
onPress={() => {
Navigation.push("tab1Stack", {
component: {
name: `navigation.eSteem.${
data.route
}`,
passProps: {},
options: {
topBar: {
title: {},
},
},
},
});
this.hideSideMenu();
}}
>
<Left>
<Icon
active
name={data.icon}
style={{
color: "#777",
fontSize: 26,
width: 30,
}}
/>
<Text style={styles.text}>{data.name}</Text>
</Left>
{data.types && (
<Right style={{ flex: 1 }}>
<Badge
style={{
borderRadius: 3,
height: 25,
width: 72,
backgroundColor: data.bg,
}}
>
<Text style={styles.badgeText}>{`${
data.types
} Types`}</Text>
</Badge>
</Right>
)}
</ListItem>
)}
/>
</Content>
</Container>
);
}
}

View File

@ -0,0 +1,40 @@
import React, { Component } from "react";
import { View, WebView } from "react-native";
import { loginWithSC2 } from "../../providers/steem/auth";
import { steemConnectOptions } from "./config";
import RNRestart from "react-native-restart";
import { Navigation } from "react-native-navigation";
export default class SteemConnect extends Component {
constructor(props) {
super(props);
this.state = {
};
}
onNavigationStateChange(event) {
let access_token = event.url.match(/\?(?:access_token)\=([\S\s]*?)\&/)[1];
if(access_token) {
loginWithSC2(access_token, "pinCode").then(result => {
if(result === true) {
// TODO: Handle pinCode and navigate to home page
} else {
Navigation.dismissModal(this.props.componentId);
}
});
}
}
render() {
return (
<View style={{ flex: 1 }}>
<WebView
onNavigationStateChange={state => this.onNavigationStateChange(state)}
source={{
uri: `${steemConnectOptions.base_url}?client_id=${steemConnectOptions.client_id}&redirect_uri=${steemConnectOptions.redirect_uri}&${steemConnectOptions.scope}`
}}/>
</View>
);
}
}