diff --git a/app/actions/actionsTypes.js b/app/actions/actionsTypes.js
index 72bc1ffb..d0a621d6 100644
--- a/app/actions/actionsTypes.js
+++ b/app/actions/actionsTypes.js
@@ -12,10 +12,12 @@ function createRequestTypes(base, types = defaultTypes) {
export const LOGIN = createRequestTypes('LOGIN', [
...defaultTypes,
'SET_TOKEN',
+ 'RESTORE_TOKEN',
'SUBMIT',
'REGISTER_SUBMIT',
'REGISTER_REQUEST',
'REGISTER_SUCCESS',
+ 'REGISTER_INCOMPLETE',
'SET_USERNAME_SUBMIT',
'SET_USERNAME_REQUEST',
'SET_USERNAME_SUCCESS'
@@ -38,7 +40,13 @@ export const CREATE_CHANNEL = createRequestTypes('CREATE_CHANNEL', [
'RESET'
]);
export const NAVIGATION = createRequestTypes('NAVIGATION', ['SET']);
-export const SERVER = createRequestTypes('SERVER', [...defaultTypes, 'SELECT', 'CHANGED', 'ADD']);
+export const SERVER = createRequestTypes('SERVER', [
+ ...defaultTypes,
+ 'SELECT',
+ 'CHANGED',
+ 'ADD',
+ 'GOTO_ADD'
+]);
export const METEOR = createRequestTypes('METEOR_CONNECT', [...defaultTypes, 'DISCONNECT']);
export const LOGOUT = 'LOGOUT'; // logout is always success
diff --git a/app/actions/login.js b/app/actions/login.js
index 4a67acf8..c603f42b 100644
--- a/app/actions/login.js
+++ b/app/actions/login.js
@@ -32,6 +32,11 @@ export function registerSuccess(credentials) {
credentials
};
}
+export function registerIncomplete() {
+ return {
+ type: types.LOGIN.REGISTER_INCOMPLETE
+ };
+}
export function setUsernameSubmit(credentials) {
return {
@@ -76,6 +81,13 @@ export function setToken(user = {}) {
};
}
+export function restoreToken(token) {
+ return {
+ type: types.LOGIN.RESTORE_TOKEN,
+ token
+ };
+}
+
export function logout() {
return {
type: types.LOGOUT
diff --git a/app/actions/server.js b/app/actions/server.js
index cfc46ad8..b334981b 100644
--- a/app/actions/server.js
+++ b/app/actions/server.js
@@ -41,3 +41,9 @@ export function changedServer(server) {
server
};
}
+
+export function gotoAddServer() {
+ return {
+ type: SERVER.GOTO_ADD
+ };
+}
diff --git a/app/containers/Routes.js b/app/containers/Routes.js
index 83268096..e1e57e95 100644
--- a/app/containers/Routes.js
+++ b/app/containers/Routes.js
@@ -7,6 +7,7 @@ import { appInit } from '../actions';
import AuthRoutes from './routes/AuthRoutes';
import PublicRoutes from './routes/PublicRoutes';
import Loading from '../presentation/Loading';
+import * as NavigationService from './routes/NavigationService';
@connect(
state => ({
@@ -27,6 +28,11 @@ export default class Routes extends React.Component {
componentWillMount() {
this.props.appInit();
}
+
+ componentDidUpdate() {
+ NavigationService.setNavigator(this.navigator);
+ }
+
render() {
const { login, app } = this.props;
@@ -35,9 +41,9 @@ export default class Routes extends React.Component {
}
if ((login.token && !login.failure && !login.isRegistering) || app.ready) {
- return ();
+ return ( this.navigator = nav} />);
}
- return ();
+ return ( this.navigator = nav} />);
}
}
diff --git a/app/containers/Sidebar.js b/app/containers/Sidebar.js
index 46ece3c4..4d9f7eb7 100644
--- a/app/containers/Sidebar.js
+++ b/app/containers/Sidebar.js
@@ -5,7 +5,7 @@ import { DrawerItems } from 'react-navigation';
import { connect } from 'react-redux';
import realm from '../lib/realm';
-import { setServer } from '../actions/server';
+import { setServer, gotoAddServer } from '../actions/server';
import { logout } from '../actions/login';
const styles = StyleSheet.create({
@@ -41,14 +41,16 @@ const styles = StyleSheet.create({
server: state.server.server
}), dispatch => ({
selectServer: server => dispatch(setServer(server)),
- logout: () => dispatch(logout())
+ logout: () => dispatch(logout()),
+ gotoAddServer: () => dispatch(gotoAddServer())
}))
export default class Sidebar extends Component {
static propTypes = {
server: PropTypes.string.isRequired,
selectServer: PropTypes.func.isRequired,
navigation: PropTypes.object.isRequired,
- logout: PropTypes.func.isRequired
+ logout: PropTypes.func.isRequired,
+ gotoAddServer: PropTypes.func.isRequired
}
componentWillMount() {
@@ -117,6 +119,15 @@ export default class Sidebar extends Component {
+ { this.props.gotoAddServer(); }}
+ >
+
+
+ Add Server
+
+
+
);
diff --git a/app/containers/routes/NavigationService.js b/app/containers/routes/NavigationService.js
new file mode 100644
index 00000000..0be7f410
--- /dev/null
+++ b/app/containers/routes/NavigationService.js
@@ -0,0 +1,23 @@
+import { NavigationActions } from 'react-navigation';
+
+const config = {};
+
+export function setNavigator(nav) {
+ if (nav) {
+ config.navigator = nav;
+ }
+}
+
+export function navigate(routeName, params) {
+ if (config.navigator && routeName) {
+ const action = NavigationActions.navigate({ routeName, params });
+ config.navigator.dispatch(action);
+ }
+}
+
+export function goBack() {
+ if (config.navigator) {
+ const action = NavigationActions.back({});
+ config.navigator.dispatch(action);
+ }
+}
diff --git a/app/containers/routes/PublicRoutes.js b/app/containers/routes/PublicRoutes.js
index 92781c00..41c26717 100644
--- a/app/containers/routes/PublicRoutes.js
+++ b/app/containers/routes/PublicRoutes.js
@@ -7,6 +7,9 @@ import ListServerView from '../../views/ListServerView';
import NewServerView from '../../views/NewServerView';
import LoginView from '../../views/LoginView';
import RegisterView from '../../views/RegisterView';
+
+import TermsServiceView from '../../views/TermsServiceView';
+import PrivacyPolicyView from '../../views/PrivacyPolicyView';
import ForgotPasswordView from '../../views/ForgotPasswordView';
const PublicRoutes = StackNavigator(
@@ -45,6 +48,18 @@ const PublicRoutes = StackNavigator(
title: 'Register'
}
},
+ TermsService: {
+ screen: TermsServiceView,
+ navigationOptions: {
+ title: 'Terms of service'
+ }
+ },
+ PrivacyPolicy: {
+ screen: PrivacyPolicyView,
+ navigationOptions: {
+ title: 'Privacy policy'
+ }
+ },
ForgotPassword: {
screen: ForgotPasswordView,
navigationOptions: {
diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js
index 81bc55f4..22585a6f 100644
--- a/app/lib/rocketchat.js
+++ b/app/lib/rocketchat.js
@@ -23,6 +23,8 @@ const call = (method, ...params) => new Promise((resolve, reject) => {
const TOKEN_KEY = 'reactnativemeteor_usertoken';
const RocketChat = {
+ TOKEN_KEY,
+
createChannel({ name, users, type }) {
return new Promise((resolve, reject) => {
Meteor.call(type ? 'createChannel' : 'createPrivateGroup', name, users, type, (err, res) => (err ? reject(err) : resolve(res)));
@@ -122,6 +124,17 @@ const RocketChat = {
});
},
+ me({ server, token, userId }) {
+ return fetch(`${ server }/api/v1/me`, {
+ method: 'get',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'X-Auth-Token': token,
+ 'X-User-Id': userId
+ }
+ }).then(response => response.json());
+ },
+
register({ credentials }) {
return new Promise((resolve, reject) => {
Meteor.call('registerUser', credentials, (err, userId) => {
@@ -438,6 +451,7 @@ const RocketChat = {
},
logout({ server }) {
Meteor.logout();
+ Meteor.disconnect();
AsyncStorage.removeItem(TOKEN_KEY);
AsyncStorage.removeItem(`${ TOKEN_KEY }-${ server }`);
}
diff --git a/app/reducers/login.js b/app/reducers/login.js
index 81ff98c3..e90531ff 100644
--- a/app/reducers/login.js
+++ b/app/reducers/login.js
@@ -45,6 +45,11 @@ export default function login(state = initialState, action) {
token: action.token,
user: action.user
};
+ case types.LOGIN.RESTORE_TOKEN:
+ return {
+ ...state,
+ token: action.token
+ };
case types.LOGIN.REGISTER_SUBMIT:
return {
...state,
@@ -73,6 +78,11 @@ export default function login(state = initialState, action) {
isFetching: false,
isRegistering: false
};
+ case types.LOGIN.REGISTER_INCOMPLETE:
+ return {
+ ...state,
+ isRegistering: true
+ };
case types.FORGOT_PASSWORD.INIT:
return initialState;
case types.FORGOT_PASSWORD.REQUEST:
diff --git a/app/sagas/init.js b/app/sagas/init.js
index cb53af7d..c86b5caa 100644
--- a/app/sagas/init.js
+++ b/app/sagas/init.js
@@ -2,16 +2,23 @@ import { AsyncStorage } from 'react-native';
import { call, put, take } from 'redux-saga/effects';
import * as actions from '../actions';
import { setServer } from '../actions/server';
+import { restoreToken } from '../actions/login';
import { APP } from '../actions/actionsTypes';
const restore = function* restore() {
try {
yield take(APP.INIT);
+ const token = yield call([AsyncStorage, 'getItem'], 'reactnativemeteor_usertoken');
+ if (token) {
+ yield put(restoreToken(token));
+ }
+
const currentServer = yield call([AsyncStorage, 'getItem'], 'currentServer');
- yield put(actions.appReady({}));
if (currentServer) {
yield put(setServer(currentServer));
}
+
+ yield put(actions.appReady({}));
} catch (e) {
console.log(e);
}
diff --git a/app/sagas/login.js b/app/sagas/login.js
index c16e9f1b..bd9b5287 100644
--- a/app/sagas/login.js
+++ b/app/sagas/login.js
@@ -5,6 +5,7 @@ import {
loginRequest,
loginSubmit,
registerRequest,
+ registerIncomplete,
loginSuccess,
loginFailure,
setToken,
@@ -16,23 +17,24 @@ import {
forgotPasswordFailure
} from '../actions/login';
import RocketChat from '../lib/rocketchat';
+import * as NavigationService from '../containers/routes/NavigationService';
-const TOKEN_KEY = 'reactnativemeteor_usertoken';
const getUser = state => state.login;
const getServer = state => state.server.server;
const loginCall = args => (args.resume ? RocketChat.login(args) : RocketChat.loginWithPassword(args));
const registerCall = args => RocketChat.register(args);
const setUsernameCall = args => RocketChat.setUsername(args);
const logoutCall = args => RocketChat.logout(args);
+const meCall = args => RocketChat.me(args);
const forgotPasswordCall = args => RocketChat.forgotPassword(args);
const getToken = function* getToken() {
const currentServer = yield select(getServer);
- const user = yield call([AsyncStorage, 'getItem'], `${ TOKEN_KEY }-${ currentServer }`);
+ const user = yield call([AsyncStorage, 'getItem'], `${ RocketChat.TOKEN_KEY }-${ currentServer }`);
if (user) {
try {
yield put(setToken(JSON.parse(user)));
- yield call([AsyncStorage, 'setItem'], TOKEN_KEY, JSON.parse(user).token || '');
+ yield call([AsyncStorage, 'setItem'], RocketChat.TOKEN_KEY, JSON.parse(user).token || '');
return JSON.parse(user);
} catch (e) {
console.log('getTokenerr', e);
@@ -43,47 +45,41 @@ const getToken = function* getToken() {
};
const handleLoginWhenServerChanges = function* handleLoginWhenServerChanges() {
- // do {
try {
yield take(types.METEOR.SUCCESS);
yield call(getToken);
- // const { navigator } = yield select(state => state);
const user = yield select(getUser);
if (user.token) {
yield put(loginRequest({ resume: user.token }));
- // console.log('AEEEEEEEEOOOOO');
- // // wait for a response
- // const { error } = yield race({
- // success: take(types.LOGIN.SUCCESS),
- // error: take(types.LOGIN.FAILURE)
- // });
- // console.log('AEEEEEEEEOOOOO', error);
- // if (!error) {
- // navigator.resetTo({
- // screen: 'Rooms'
- // });
- // }
}
- // navigator.resetTo({
- // screen: 'Rooms'
- // });
} catch (e) {
console.log(e);
}
- // } while (true);
};
const saveToken = function* saveToken() {
const [server, user] = yield all([select(getServer), select(getUser)]);
- yield AsyncStorage.setItem(TOKEN_KEY, user.token);
- yield AsyncStorage.setItem(`${ TOKEN_KEY }-${ server }`, JSON.stringify(user));
+ yield AsyncStorage.setItem(RocketChat.TOKEN_KEY, user.token);
+ yield AsyncStorage.setItem(`${ RocketChat.TOKEN_KEY }-${ server }`, JSON.stringify(user));
};
const handleLoginRequest = function* handleLoginRequest({ credentials }) {
try {
- const response = yield call(loginCall, credentials);
- yield put(loginSuccess(response));
+ const server = yield select(getServer);
+ const user = yield call(loginCall, credentials);
+
+ // GET /me from REST API
+ const me = yield call(meCall, { server, token: user.token, userId: user.id });
+
+ // if user has username
+ if (me.username) {
+ user.username = me.username;
+ } else {
+ yield put(registerIncomplete());
+ }
+
+ yield put(loginSuccess(user));
} catch (err) {
if (err.error === 403) {
yield put(logout());
@@ -98,13 +94,7 @@ const handleLoginSubmit = function* handleLoginSubmit({ credentials }) {
};
const handleRegisterSubmit = function* handleRegisterSubmit({ credentials }) {
- // put a login request
yield put(registerRequest(credentials));
- // wait for a response
- // yield race({
- // success: take(types.LOGIN.REGISTER_SUCCESS),
- // error: take(types.LOGIN.FAILURE)
- // });
};
const handleRegisterRequest = function* handleRegisterRequest({ credentials }) {
@@ -141,6 +131,10 @@ const handleLogout = function* handleLogout() {
yield call(logoutCall, { server });
};
+const handleRegisterIncomplete = function* handleRegisterIncomplete() {
+ yield call(NavigationService.navigate, 'Register');
+};
+
const handleForgotPasswordRequest = function* handleForgotPasswordRequest({ email }) {
try {
yield call(forgotPasswordCall, email);
@@ -158,6 +152,7 @@ const root = function* root() {
yield takeLatest(types.LOGIN.REGISTER_REQUEST, handleRegisterRequest);
yield takeLatest(types.LOGIN.REGISTER_SUBMIT, handleRegisterSubmit);
yield takeLatest(types.LOGIN.REGISTER_SUCCESS, handleRegisterSuccess);
+ yield takeLatest(types.LOGIN.REGISTER_INCOMPLETE, handleRegisterIncomplete);
yield takeLatest(types.LOGIN.SET_USERNAME_SUBMIT, handleSetUsernameSubmit);
yield takeLatest(types.LOGIN.SET_USERNAME_REQUEST, handleSetUsernameRequest);
yield takeLatest(types.LOGOUT, handleLogout);
diff --git a/app/sagas/selectServer.js b/app/sagas/selectServer.js
index dc9b039e..84ad3a3e 100644
--- a/app/sagas/selectServer.js
+++ b/app/sagas/selectServer.js
@@ -1,12 +1,13 @@
import { put, takeEvery, call, takeLatest, race, take } from 'redux-saga/effects';
import { delay } from 'redux-saga';
import { AsyncStorage } from 'react-native';
-// import { Navigation } from 'react-native-navigation';
import { SERVER } from '../actions/actionsTypes';
import { connectRequest, disconnect } from '../actions/connect';
-import { changedServer, serverSuccess, serverFailure, serverRequest } from '../actions/server';
+import { changedServer, serverSuccess, serverFailure, serverRequest, setServer } from '../actions/server';
+import { logout } from '../actions/login';
import RocketChat from '../lib/rocketchat';
import realm from '../lib/realm';
+import * as NavigationService from '../containers/routes/NavigationService';
const validate = function* validate(server) {
return yield RocketChat.testServer(server);
@@ -42,13 +43,21 @@ const addServer = function* addServer({ server }) {
realm.write(() => {
realm.create('servers', { id: server, current: false }, true);
});
+ yield put(setServer(server));
}
};
+const handleGotoAddServer = function* handleGotoAddServer() {
+ yield put(logout());
+ yield call(AsyncStorage.removeItem, RocketChat.TOKEN_KEY);
+ yield delay(1000);
+ yield call(NavigationService.navigate, 'AddServer');
+};
const root = function* root() {
yield takeLatest(SERVER.REQUEST, validateServer);
yield takeEvery(SERVER.SELECT, selectServer);
yield takeEvery(SERVER.ADD, addServer);
+ yield takeEvery(SERVER.GOTO_ADD, handleGotoAddServer);
};
export default root;
diff --git a/app/views/LoginView.js b/app/views/LoginView.js
index a5e1ad92..0b508edd 100644
--- a/app/views/LoginView.js
+++ b/app/views/LoginView.js
@@ -1,6 +1,6 @@
import React from 'react';
-import Spinner from 'react-native-loading-spinner-overlay';
+// import Spinner from 'react-native-loading-spinner-overlay';
import PropTypes from 'prop-types';
import { Keyboard, Text, TextInput, View, TouchableOpacity, SafeAreaView } from 'react-native';
@@ -49,6 +49,14 @@ class LoginView extends React.Component {
this.props.navigation.navigate('Register');
}
+ termsService = () => {
+ this.props.navigation.navigate('TermsService');
+ }
+
+ privacyPolicy = () => {
+ this.props.navigation.navigate('PrivacyPolicy');
+ }
+
forgotPassword = () => {
this.props.navigation.navigate('ForgotPassword');
}
@@ -78,8 +86,8 @@ class LoginView extends React.Component {
contentContainerStyle={styles.container}
keyboardVerticalOffset={128}
>
-
-
+
+
- LOGIN
+
+ LOGIN
-
- REGISTER
+
+ REGISTER
+
+ TERMS OF SERVICE
+
+
+
+ PRIVACY POLICY
+
FORGOT MY PASSWORD
{this.props.login.failure && {this.props.login.error.reason}}
-
-
-
+
+
);
}
diff --git a/app/views/PrivacyPolicyView.js b/app/views/PrivacyPolicyView.js
new file mode 100644
index 00000000..e4f96abc
--- /dev/null
+++ b/app/views/PrivacyPolicyView.js
@@ -0,0 +1,28 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { WebView } from 'react-native';
+import { connect } from 'react-redux';
+
+class PrivacyPolicyView extends React.Component {
+ static propTypes = {
+ privacyPolicy: PropTypes.string
+ }
+
+ static navigationOptions = () => ({
+ title: 'Terms of service'
+ });
+
+ render() {
+ return (
+
+ );
+ }
+}
+
+function mapStateToProps(state) {
+ return {
+ privacyPolicy: state.settings.Layout_Privacy_Policy
+ };
+}
+
+export default connect(mapStateToProps)(PrivacyPolicyView);
diff --git a/app/views/RegisterView.js b/app/views/RegisterView.js
index 5ddd9668..ed262586 100644
--- a/app/views/RegisterView.js
+++ b/app/views/RegisterView.js
@@ -125,12 +125,14 @@ class RegisterView extends React.Component {
placeholder={this.props.Accounts_RepeatPasswordPlaceholder || 'Repeat Password'}
/>
-
+
REGISTER
@@ -158,12 +160,11 @@ class RegisterView extends React.Component {
placeholder={this.props.Accounts_UsernamePlaceholder || 'Username'}
/>
-
- REGISTER
-
+
+ REGISTER
{this.props.login.failure && {this.props.login.error.reason}}
diff --git a/app/views/TermsServiceView.js b/app/views/TermsServiceView.js
new file mode 100644
index 00000000..b56abca0
--- /dev/null
+++ b/app/views/TermsServiceView.js
@@ -0,0 +1,28 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { WebView } from 'react-native';
+import { connect } from 'react-redux';
+
+class TermsServiceView extends React.Component {
+ static propTypes = {
+ termsService: PropTypes.string
+ }
+
+ static navigationOptions = () => ({
+ title: 'Terms of service'
+ });
+
+ render() {
+ return (
+
+ );
+ }
+}
+
+function mapStateToProps(state) {
+ return {
+ termsService: state.settings.Layout_Terms_of_Service
+ };
+}
+
+export default connect(mapStateToProps)(TermsServiceView);