[WIP] remove meteor lib (#146)
* removed meteor lib * reconnect saga * Focused text input touch bug fixed
This commit is contained in:
parent
a15774c4ff
commit
8599d6a7cc
|
@ -75,7 +75,7 @@ export const SERVER = createRequestTypes('SERVER', [
|
|||
'ADD',
|
||||
'GOTO_ADD'
|
||||
]);
|
||||
export const METEOR = createRequestTypes('METEOR_CONNECT', [...defaultTypes, 'DISCONNECT']);
|
||||
export const METEOR = createRequestTypes('METEOR_CONNECT', [...defaultTypes, 'DISCONNECT', 'DISCONNECT_BY_USER']);
|
||||
export const LOGOUT = 'LOGOUT'; // logout is always success
|
||||
export const ACTIVE_USERS = createRequestTypes('ACTIVE_USERS', ['SET', 'REQUEST']);
|
||||
|
||||
|
|
|
@ -25,3 +25,8 @@ export function disconnect(err) {
|
|||
err
|
||||
};
|
||||
}
|
||||
export function disconnect_by_user() {
|
||||
return {
|
||||
type: types.METEOR.DISCONNECT_BY_USER
|
||||
};
|
||||
}
|
||||
|
|
|
@ -41,10 +41,9 @@ export default class Routes extends React.Component {
|
|||
return (<Loading />);
|
||||
}
|
||||
|
||||
if (login.token && !login.failure && !login.isRegistering) {
|
||||
return (<AuthRoutes ref={nav => this.navigator = nav} />);
|
||||
if (!login.token || login.isRegistering) {
|
||||
return (<PublicRoutes ref={nav => this.navigator = nav} />);
|
||||
}
|
||||
|
||||
return (<PublicRoutes ref={nav => this.navigator = nav} />);
|
||||
return (<AuthRoutes ref={nav => this.navigator = nav} />);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import RoomsListView from '../../views/RoomsListView';
|
|||
import RoomView from '../../views/RoomView';
|
||||
import CreateChannelView from '../../views/CreateChannelView';
|
||||
import SelectUsersView from '../../views/SelectUsersView';
|
||||
import NewServerView from '../../views/NewServerView';
|
||||
|
||||
const AuthRoutes = StackNavigator(
|
||||
{
|
||||
|
@ -26,6 +27,12 @@ const AuthRoutes = StackNavigator(
|
|||
navigationOptions: {
|
||||
title: 'Select Users'
|
||||
}
|
||||
},
|
||||
AddServer: {
|
||||
screen: NewServerView,
|
||||
navigationOptions: {
|
||||
title: 'New server'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
import EJSON from 'ejson';
|
||||
|
||||
class EventEmitter {
|
||||
constructor() {
|
||||
this.events = {};
|
||||
}
|
||||
on(event, listener) {
|
||||
if (typeof this.events[event] !== 'object') {
|
||||
this.events[event] = [];
|
||||
}
|
||||
this.events[event].push(listener);
|
||||
}
|
||||
removeListener(event, listener) {
|
||||
if (typeof this.events[event] === 'object') {
|
||||
const idx = this.events[event].indexOf(listener);
|
||||
if (idx > -1) {
|
||||
this.events[event].splice(idx, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
emit(event, ...args) {
|
||||
if (typeof this.events[event] === 'object') {
|
||||
this.events[event].forEach((listener) => {
|
||||
try {
|
||||
listener.apply(this, args);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
once(event, listener) {
|
||||
this.on(event, function g(...args) {
|
||||
this.removeListener(event, g);
|
||||
listener.apply(this, args);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default class Socket extends EventEmitter {
|
||||
constructor(url) {
|
||||
super();
|
||||
this.url = url.replace(/^http/, 'ws');
|
||||
this.id = 0;
|
||||
this.subscriptions = {};
|
||||
this._connect();
|
||||
this.ddp = new EventEmitter();
|
||||
this.on('ping', () => this.send({ msg: 'pong' }));
|
||||
this.on('result', data => this.ddp.emit(data.id, { result: data.result, error: data.error }));
|
||||
this.on('ready', data => this.ddp.emit(data.subs[0], data));
|
||||
}
|
||||
send(obj) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.id += 1;
|
||||
const id = obj.id || `${ this.id }`;
|
||||
this.connection.send(EJSON.stringify({ ...obj, id }));
|
||||
this.ddp.once(id, data => (data.error ? reject(data.error) : resolve(data.result || data.subs)));
|
||||
});
|
||||
}
|
||||
_connect() {
|
||||
const connection = new WebSocket(`${ this.url }/websocket`);
|
||||
connection.onopen = () => {
|
||||
this.emit('open');
|
||||
this.send({ msg: 'connect', version: '1', support: ['1', 'pre2', 'pre1'] });
|
||||
};
|
||||
connection.onclose = e => this.emit('disconnected', e);
|
||||
// connection.onerror = () => {
|
||||
// // alert(error.type);
|
||||
// // console.log(error);
|
||||
// // console.log(`WebSocket Error ${ JSON.stringify({...error}) }`);
|
||||
// };
|
||||
|
||||
connection.onmessage = (e) => {
|
||||
const data = EJSON.parse(e.data);
|
||||
this.emit(data.msg, data);
|
||||
return data.collection && this.emit(data.collection, data);
|
||||
};
|
||||
// this.on('disconnected', e => alert(JSON.stringify(e)));
|
||||
this.connection = connection;
|
||||
}
|
||||
logout() {
|
||||
return this.call('logout').then(() => this.subscriptions = {});
|
||||
}
|
||||
disconnect() {
|
||||
this.emit('disconnected_by_user');
|
||||
this.connection.close();
|
||||
}
|
||||
reconnect() {
|
||||
this.disconnect();
|
||||
this.once('connected', () => {
|
||||
Object.keys(this.subscriptions).forEach((key) => {
|
||||
const { name, params } = this.subscriptions[key];
|
||||
this.subscriptions[key].unsubscribe();
|
||||
this.subscribe(name, params);
|
||||
});
|
||||
});
|
||||
this._connect();
|
||||
}
|
||||
call(method, ...params) {
|
||||
return this.send({
|
||||
msg: 'method', method, params
|
||||
});
|
||||
}
|
||||
unsubscribe(id) {
|
||||
if (!this.subscriptions[id]) {
|
||||
return Promise.reject();
|
||||
}
|
||||
delete this.subscriptions[id];
|
||||
return this.send({
|
||||
msg: 'unsub',
|
||||
id
|
||||
});
|
||||
}
|
||||
subscribe(name, ...params) {
|
||||
return this.send({
|
||||
msg: 'sub', name, params
|
||||
}).then((data) => {
|
||||
this.subscriptions[data.id] = {
|
||||
name,
|
||||
params,
|
||||
unsubscribe: () => this.unsubscribe(data.id)
|
||||
};
|
||||
return this.subscriptions[data.id];
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
import Meteor from 'react-native-meteor';
|
||||
import Random from 'react-native-meteor/lib/Random';
|
||||
import { AsyncStorage, Platform } from 'react-native';
|
||||
import { hashPassword } from 'react-native-meteor/lib/utils';
|
||||
|
@ -11,19 +10,13 @@ import realm from './realm';
|
|||
import * as actions from '../actions';
|
||||
import { someoneTyping } from '../actions/room';
|
||||
import { setUser } from '../actions/login';
|
||||
import { disconnect, connectSuccess } from '../actions/connect';
|
||||
import { disconnect, disconnect_by_user, connectSuccess, connectFailure } from '../actions/connect';
|
||||
import { requestActiveUser } from '../actions/activeUsers';
|
||||
import Ddp from './ddp';
|
||||
|
||||
export { Accounts } from 'react-native-meteor';
|
||||
|
||||
const call = (method, ...params) => new Promise((resolve, reject) => {
|
||||
Meteor.call(method, ...params, (err, data) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
const call = (method, ...params) => RocketChat.ddp.call(method, ...params); // eslint-disable-line
|
||||
const TOKEN_KEY = 'reactnativemeteor_usertoken';
|
||||
const SERVER_TIMEOUT = 30000;
|
||||
|
||||
|
@ -67,92 +60,72 @@ const RocketChat = {
|
|||
activeUser[ddpMessage.id] = status;
|
||||
return reduxStore.dispatch(requestActiveUser(activeUser));
|
||||
},
|
||||
connect(_url) {
|
||||
reconnect() {
|
||||
if (this.ddp) {
|
||||
this.ddp.reconnect();
|
||||
}
|
||||
},
|
||||
connect(url) {
|
||||
if (this.ddp) {
|
||||
this.ddp.disconnect();
|
||||
}
|
||||
this.ddp = new Ddp(url);
|
||||
return new Promise((resolve) => {
|
||||
const url = `${ _url }/websocket`;
|
||||
|
||||
Meteor.connect(url, { autoConnect: true, autoReconnect: true });
|
||||
|
||||
Meteor.ddp.on('disconnected', () => {
|
||||
this.ddp.on('disconnected_by_user', () => {
|
||||
reduxStore.dispatch(disconnect_by_user());
|
||||
});
|
||||
this.ddp.on('disconnected', () => {
|
||||
reduxStore.dispatch(disconnect());
|
||||
});
|
||||
|
||||
Meteor.ddp.on('connected', () => {
|
||||
reduxStore.dispatch(connectSuccess());
|
||||
resolve();
|
||||
});
|
||||
|
||||
Meteor.ddp.on('connected', async() => {
|
||||
Meteor.ddp.on('added', (ddpMessage) => {
|
||||
if (ddpMessage.collection === 'users') {
|
||||
return RocketChat._setUser(ddpMessage);
|
||||
}
|
||||
});
|
||||
Meteor.ddp.on('removed', (ddpMessage) => {
|
||||
if (ddpMessage.collection === 'users') {
|
||||
return RocketChat._setUser(ddpMessage);
|
||||
}
|
||||
});
|
||||
Meteor.ddp.on('changed', (ddpMessage) => {
|
||||
if (ddpMessage.collection === 'stream-room-messages') {
|
||||
return realm.write(() => {
|
||||
const message = this._buildMessage(ddpMessage.fields.args[0]);
|
||||
realm.create('messages', message, true);
|
||||
});
|
||||
}
|
||||
if (ddpMessage.collection === 'stream-notify-room') {
|
||||
const [_rid, ev] = ddpMessage.fields.eventName.split('/');
|
||||
if (ev !== 'typing') {
|
||||
return;
|
||||
}
|
||||
return reduxStore.dispatch(someoneTyping({ _rid, username: ddpMessage.fields.args[0], typing: ddpMessage.fields.args[1] }));
|
||||
}
|
||||
if (ddpMessage.collection === 'stream-notify-user') {
|
||||
const [type, data] = ddpMessage.fields.args;
|
||||
const [, ev] = ddpMessage.fields.eventName.split('/');
|
||||
if (/subscriptions/.test(ev)) {
|
||||
if (data.roles) {
|
||||
data.roles = data.roles.map(role => ({ value: role }));
|
||||
}
|
||||
realm.write(() => {
|
||||
realm.create('subscriptions', data, true);
|
||||
});
|
||||
}
|
||||
if (/rooms/.test(ev) && type === 'updated') {
|
||||
const sub = realm.objects('subscriptions').filtered('rid == $0', data._id)[0];
|
||||
realm.write(() => {
|
||||
sub.roomUpdatedAt = data._updatedAt;
|
||||
});
|
||||
}
|
||||
}
|
||||
if (ddpMessage.collection === 'users') {
|
||||
return RocketChat._setUser(ddpMessage);
|
||||
}
|
||||
});
|
||||
this.ddp.on('open', async() => resolve(reduxStore.dispatch(connectSuccess())));
|
||||
this.ddp.on('connected', () => {
|
||||
RocketChat.getSettings();
|
||||
RocketChat.getPermissions();
|
||||
});
|
||||
})
|
||||
.catch(e => console.error(e));
|
||||
},
|
||||
login(params, callback) {
|
||||
return new Promise((resolve, reject) => {
|
||||
Meteor._startLoggingIn();
|
||||
return Meteor.call('login', params, (err, result) => {
|
||||
Meteor._endLoggingIn();
|
||||
Meteor._handleLoginCallback(err, result);
|
||||
if (err) {
|
||||
if (/user not found/i.test(err.reason)) {
|
||||
err.error = 1;
|
||||
err.reason = 'User or Password incorrect';
|
||||
err.message = 'User or Password incorrect';
|
||||
}
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(result);
|
||||
|
||||
this.ddp.on('error', (err) => {
|
||||
alert(JSON.stringify(err));
|
||||
reduxStore.dispatch(connectFailure());
|
||||
});
|
||||
|
||||
this.ddp.on('connected', () => this.ddp.subscribe('activeUsers', null, false));
|
||||
|
||||
|
||||
this.ddp.on('users', (ddpMessage) => {
|
||||
if (ddpMessage.collection === 'users') {
|
||||
return RocketChat._setUser(ddpMessage);
|
||||
}
|
||||
if (typeof callback === 'function') {
|
||||
callback(err, result);
|
||||
});
|
||||
|
||||
this.ddp.on('stream-room-messages', ddpMessage => realm.write(() => {
|
||||
const message = this._buildMessage(ddpMessage.fields.args[0]);
|
||||
realm.create('messages', message, true);
|
||||
}));
|
||||
|
||||
this.ddp.on('stream-notify-room', (ddpMessage) => {
|
||||
const [_rid, ev] = ddpMessage.fields.eventName.split('/');
|
||||
if (ev !== 'typing') {
|
||||
return;
|
||||
}
|
||||
return reduxStore.dispatch(someoneTyping({ _rid, username: ddpMessage.fields.args[0], typing: ddpMessage.fields.args[1] }));
|
||||
});
|
||||
|
||||
this.ddp.on('stream-notify-user', (ddpMessage) => {
|
||||
const [type, data] = ddpMessage.fields.args;
|
||||
const [, ev] = ddpMessage.fields.eventName.split('/');
|
||||
if (/subscriptions/.test(ev)) {
|
||||
if (data.roles) {
|
||||
data.roles = data.roles.map(role => ({ value: role }));
|
||||
}
|
||||
realm.write(() => {
|
||||
realm.create('subscriptions', data, true);
|
||||
});
|
||||
}
|
||||
if (/rooms/.test(ev) && type === 'updated') {
|
||||
const sub = realm.objects('subscriptions').filtered('rid == $0', data._id)[0];
|
||||
realm.write(() => {
|
||||
sub.roomUpdatedAt = data._updatedAt;
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -236,10 +209,7 @@ const RocketChat = {
|
|||
|
||||
loadSubscriptions(cb) {
|
||||
const { server } = reduxStore.getState().server;
|
||||
Meteor.call('subscriptions/get', (err, data) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
}
|
||||
this.ddp.call('subscriptions/get').then((data) => {
|
||||
if (data.length) {
|
||||
realm.write(() => {
|
||||
data.forEach((subscription) => {
|
||||
|
@ -305,32 +275,26 @@ const RocketChat = {
|
|||
return message;
|
||||
},
|
||||
loadMessagesForRoom(rid, end, cb) {
|
||||
return new Promise((resolve, reject) => {
|
||||
Meteor.call('loadHistory', rid, end, 20, (err, data) => {
|
||||
if (err) {
|
||||
if (cb) {
|
||||
cb({ end: true });
|
||||
}
|
||||
return reject(err);
|
||||
}
|
||||
if (data && data.messages.length) {
|
||||
const messages = data.messages.map(message => this._buildMessage(message));
|
||||
realm.write(() => {
|
||||
messages.forEach((message) => {
|
||||
realm.create('messages', message, true);
|
||||
});
|
||||
return this.ddp.call('loadHistory', rid, end, 20).then((data) => {
|
||||
if (data && data.messages.length) {
|
||||
const messages = data.messages.map(message => this._buildMessage(message));
|
||||
realm.write(() => {
|
||||
messages.forEach((message) => {
|
||||
realm.create('messages', message, true);
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
if (cb) {
|
||||
cb({ end: data && data.messages.length < 20 });
|
||||
}
|
||||
return data.message;
|
||||
}, (err) => {
|
||||
if (err) {
|
||||
if (cb) {
|
||||
if (data && data.messages.length < 20) {
|
||||
cb({ end: true });
|
||||
} else {
|
||||
cb({ end: false });
|
||||
}
|
||||
cb({ end: true });
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
return Promise.reject(err);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -484,14 +448,41 @@ const RocketChat = {
|
|||
data.forEach(subscription =>
|
||||
realm.create('subscriptions', subscription, true));
|
||||
});
|
||||
Meteor.subscribe('stream-notify-user', `${ login.user.id }/subscriptions-changed`, false);
|
||||
Meteor.subscribe('stream-notify-user', `${ login.user.id }/rooms-changed`, false);
|
||||
Meteor.subscribe('activeUsers', null, false);
|
||||
this.ddp.subscribe('stream-notify-user', `${ login.user.id }/subscriptions-changed`, false);
|
||||
this.ddp.subscribe('stream-notify-user', `${ login.user.id }/rooms-changed`, false);
|
||||
return data;
|
||||
},
|
||||
disconnect() {
|
||||
if (!this.ddp) {
|
||||
return;
|
||||
}
|
||||
reduxStore.dispatch(disconnect_by_user());
|
||||
delete this.ddp;
|
||||
return this.ddp.disconnect();
|
||||
},
|
||||
login(params, callback) {
|
||||
return this.ddp.call('login', params).then((result) => {
|
||||
if (typeof callback === 'function') {
|
||||
callback(null, result);
|
||||
}
|
||||
return result;
|
||||
}, (err) => {
|
||||
if (/user not found/i.test(err.reason)) {
|
||||
err.error = 1;
|
||||
err.reason = 'User or Password incorrect';
|
||||
err.message = 'User or Password incorrect';
|
||||
}
|
||||
if (typeof callback === 'function') {
|
||||
callback(err, null);
|
||||
}
|
||||
return Promise.reject(err);
|
||||
});
|
||||
},
|
||||
logout({ server }) {
|
||||
Meteor.logout();
|
||||
Meteor.disconnect();
|
||||
if (this.ddp) {
|
||||
this.ddp.logout();
|
||||
// this.disconnect();
|
||||
}
|
||||
AsyncStorage.removeItem(TOKEN_KEY);
|
||||
AsyncStorage.removeItem(`${ TOKEN_KEY }-${ server }`);
|
||||
},
|
||||
|
@ -570,7 +561,7 @@ const RocketChat = {
|
|||
return `${ room._server.id }/${ roomType }/${ room.name }?msg=${ message._id }`;
|
||||
},
|
||||
subscribe(...args) {
|
||||
return Meteor.subscribe(...args);
|
||||
return this.ddp.subscribe(...args);
|
||||
},
|
||||
emitTyping(room, t = true) {
|
||||
const { login } = reduxStore.getState();
|
||||
|
|
|
@ -4,6 +4,7 @@ const initialState = {
|
|||
connecting: false,
|
||||
connected: false,
|
||||
errorMessage: '',
|
||||
disconnected_by_user: false,
|
||||
failure: false
|
||||
};
|
||||
|
||||
|
@ -12,7 +13,8 @@ export default function connect(state = initialState, action) {
|
|||
case METEOR.REQUEST:
|
||||
return {
|
||||
...state,
|
||||
connecting: true
|
||||
connecting: true,
|
||||
disconnected_by_user: false
|
||||
};
|
||||
case METEOR.SUCCESS:
|
||||
return {
|
||||
|
@ -29,6 +31,11 @@ export default function connect(state = initialState, action) {
|
|||
failure: true,
|
||||
errorMessage: action.err
|
||||
};
|
||||
case METEOR.DISCONNECT_BY_USER:
|
||||
return {
|
||||
...state,
|
||||
disconnected_by_user: true
|
||||
};
|
||||
case METEOR.DISCONNECT:
|
||||
return initialState;
|
||||
default:
|
||||
|
|
|
@ -26,7 +26,7 @@ export default function login(state = initialState, action) {
|
|||
...state,
|
||||
isFetching: false,
|
||||
isAuthenticated: true,
|
||||
user: action.user,
|
||||
user: { ...state.user, ...action.user },
|
||||
token: action.user.token,
|
||||
failure: false,
|
||||
error: ''
|
||||
|
|
|
@ -1,27 +1,44 @@
|
|||
import { put, call, takeLatest, select } from 'redux-saga/effects';
|
||||
import { call, takeLatest, select, take, race } from 'redux-saga/effects';
|
||||
import { delay } from 'redux-saga';
|
||||
import { METEOR } from '../actions/actionsTypes';
|
||||
import RocketChat from '../lib/rocketchat';
|
||||
|
||||
import { connectSuccess, connectFailure } from '../actions/connect';
|
||||
|
||||
const getServer = ({ server }) => server.server;
|
||||
|
||||
|
||||
const connect = url => RocketChat.connect(url);
|
||||
const test = function* test() {
|
||||
try {
|
||||
const server = yield select(getServer);
|
||||
const response = yield call(connect, server);
|
||||
yield put(connectSuccess(response));
|
||||
} catch (err) {
|
||||
yield put(connectFailure(err.status));
|
||||
const watchConnect = function* watchConnect() {
|
||||
const { disconnect } = yield race({
|
||||
disconnect: take(METEOR.DISCONNECT),
|
||||
disconnected_by_user: take(METEOR.DISCONNECT_BY_USER)
|
||||
});
|
||||
if (disconnect) {
|
||||
while (true) {
|
||||
const { connected } = yield race({
|
||||
connected: take(METEOR.SUCCESS),
|
||||
timeout: call(delay, 1000)
|
||||
});
|
||||
if (connected) {
|
||||
return;
|
||||
}
|
||||
yield RocketChat.reconnect();
|
||||
}
|
||||
}
|
||||
};
|
||||
// const watchConnect = function* watchConnect() {
|
||||
// };
|
||||
const test = function* test() {
|
||||
// try {
|
||||
const server = yield select(getServer);
|
||||
// const response =
|
||||
yield call(connect, server);
|
||||
// yield put(connectSuccess(response));
|
||||
// } catch (err) {
|
||||
// yield put(connectFailure(err.status));
|
||||
// }
|
||||
};
|
||||
|
||||
const root = function* root() {
|
||||
yield takeLatest(METEOR.REQUEST, test);
|
||||
// yield fork(watchConnect);
|
||||
// yield fork(auto);
|
||||
// yield take(METEOR.SUCCESS, watchConnect);
|
||||
yield takeLatest(METEOR.SUCCESS, watchConnect);
|
||||
};
|
||||
export default root;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { AsyncStorage } from 'react-native';
|
||||
import { take, put, call, takeLatest, select, all } from 'redux-saga/effects';
|
||||
import { put, call, takeLatest, select, all } from 'redux-saga/effects';
|
||||
import * as types from '../actions/actionsTypes';
|
||||
import {
|
||||
loginRequest,
|
||||
|
@ -8,6 +8,7 @@ import {
|
|||
registerIncomplete,
|
||||
loginSuccess,
|
||||
loginFailure,
|
||||
logout,
|
||||
setToken,
|
||||
registerSuccess,
|
||||
setUsernameRequest,
|
||||
|
@ -40,16 +41,13 @@ const getToken = function* getToken() {
|
|||
console.log('getTokenerr', e);
|
||||
}
|
||||
} else {
|
||||
yield put(setToken());
|
||||
return yield put(setToken());
|
||||
}
|
||||
};
|
||||
|
||||
const handleLoginWhenServerChanges = function* handleLoginWhenServerChanges() {
|
||||
try {
|
||||
yield take(types.METEOR.SUCCESS);
|
||||
yield call(getToken);
|
||||
|
||||
const user = yield select(getUser);
|
||||
const user = yield call(getToken);
|
||||
if (user.token) {
|
||||
yield put(loginRequest({ resume: user.token }));
|
||||
}
|
||||
|
@ -76,17 +74,19 @@ const handleLoginRequest = function* handleLoginRequest({ credentials }) {
|
|||
|
||||
// if user has username
|
||||
if (me.username) {
|
||||
user.username = me.username;
|
||||
const userInfo = yield call(userInfoCall, { server, token: user.token, userId: user.id });
|
||||
user.username = userInfo.user.username;
|
||||
if (userInfo.user.roles) {
|
||||
user.roles = userInfo.user.roles;
|
||||
}
|
||||
} else {
|
||||
yield put(registerIncomplete());
|
||||
}
|
||||
|
||||
yield put(loginSuccess(user));
|
||||
} catch (err) {
|
||||
if (err.error === 403) {
|
||||
return yield put(logout());
|
||||
}
|
||||
yield put(loginFailure(err));
|
||||
}
|
||||
};
|
||||
|
@ -149,7 +149,7 @@ const handleForgotPasswordRequest = function* handleForgotPasswordRequest({ emai
|
|||
};
|
||||
|
||||
const root = function* root() {
|
||||
yield takeLatest(types.SERVER.CHANGED, handleLoginWhenServerChanges);
|
||||
yield takeLatest(types.METEOR.SUCCESS, handleLoginWhenServerChanges);
|
||||
yield takeLatest(types.LOGIN.REQUEST, handleLoginRequest);
|
||||
yield takeLatest(types.LOGIN.SUCCESS, saveToken);
|
||||
yield takeLatest(types.LOGIN.SUBMIT, handleLoginSubmit);
|
||||
|
|
|
@ -67,7 +67,7 @@ const watchRoomOpen = function* watchRoomOpen({ room }) {
|
|||
const thread = yield fork(usersTyping, { rid: room.rid });
|
||||
yield take(types.ROOM.OPEN);
|
||||
cancel(thread);
|
||||
subscriptions.forEach(sub => sub.stop());
|
||||
subscriptions.forEach(sub => sub.unsubscribe());
|
||||
};
|
||||
|
||||
const watchuserTyping = function* watchuserTyping({ status }) {
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import { put, takeEvery, call, takeLatest, race, take } from 'redux-saga/effects';
|
||||
import { put, call, takeLatest, race, take } from 'redux-saga/effects';
|
||||
import { delay } from 'redux-saga';
|
||||
import { AsyncStorage } from 'react-native';
|
||||
import { SERVER } from '../actions/actionsTypes';
|
||||
import { connectRequest, disconnect } from '../actions/connect';
|
||||
import { connectRequest, disconnect, disconnect_by_user } from '../actions/connect';
|
||||
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';
|
||||
|
@ -14,13 +13,13 @@ const validate = function* validate(server) {
|
|||
};
|
||||
|
||||
const selectServer = function* selectServer({ server }) {
|
||||
yield put(disconnect_by_user());
|
||||
yield put(disconnect());
|
||||
yield put(changedServer(server));
|
||||
yield call([AsyncStorage, 'setItem'], 'currentServer', server);
|
||||
yield put(connectRequest(server));
|
||||
};
|
||||
|
||||
|
||||
const validateServer = function* validateServer({ server }) {
|
||||
try {
|
||||
yield delay(1000);
|
||||
|
@ -48,16 +47,14 @@ const addServer = function* addServer({ 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);
|
||||
yield takeLatest(SERVER.SELECT, selectServer);
|
||||
yield takeLatest(SERVER.ADD, addServer);
|
||||
yield takeLatest(SERVER.GOTO_ADD, handleGotoAddServer);
|
||||
};
|
||||
export default root;
|
||||
|
|
|
@ -8,7 +8,6 @@ import { connect } from 'react-redux';
|
|||
import { setServer } from '../actions/server';
|
||||
import realm from '../lib/realm';
|
||||
import Fade from '../animations/fade';
|
||||
import Banner from '../containers/Banner';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
view: {
|
||||
|
@ -184,7 +183,6 @@ export default class ListServerView extends React.Component {
|
|||
render() {
|
||||
return (
|
||||
<View style={styles.view}>
|
||||
<Banner />
|
||||
<SafeAreaView style={styles.view}>
|
||||
<SectionList
|
||||
style={styles.list}
|
||||
|
|
|
@ -1,52 +1,13 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Text, TextInput, View, StyleSheet, Dimensions } from 'react-native';
|
||||
import { Text, TouchableOpacity, ScrollView, TextInput } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
import { serverRequest, addServer } from '../actions/server';
|
||||
import KeyboardView from '../presentation/KeyboardView';
|
||||
import styles from './Styles';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
view: {
|
||||
flex: 1,
|
||||
flexDirection: 'column',
|
||||
alignItems: 'stretch',
|
||||
backgroundColor: '#fff'
|
||||
},
|
||||
input: {
|
||||
height: 40,
|
||||
borderColor: '#aaa',
|
||||
margin: 20,
|
||||
padding: 5,
|
||||
borderWidth: 0,
|
||||
backgroundColor: '#f8f8f8'
|
||||
},
|
||||
text: {
|
||||
textAlign: 'center',
|
||||
color: '#888'
|
||||
},
|
||||
validateText: {
|
||||
position: 'absolute',
|
||||
color: 'green',
|
||||
textAlign: 'center',
|
||||
paddingLeft: 50,
|
||||
paddingRight: 50,
|
||||
width: '100%'
|
||||
},
|
||||
validText: {
|
||||
color: 'green'
|
||||
},
|
||||
invalidText: {
|
||||
color: 'red'
|
||||
},
|
||||
validatingText: {
|
||||
color: '#aaa'
|
||||
},
|
||||
spaceView: {
|
||||
flexGrow: 1
|
||||
}
|
||||
});
|
||||
@connect(state => ({
|
||||
validInstance: !state.server.failure,
|
||||
validInstance: !state.server.failure && !state.server.connecting,
|
||||
validating: state.server.connecting
|
||||
}), dispatch => ({
|
||||
validateServer: url => dispatch(serverRequest(url)),
|
||||
|
@ -83,7 +44,7 @@ export default class NewServerView extends React.Component {
|
|||
this.setState({ editable: true });
|
||||
this.adding = false;
|
||||
}
|
||||
if (this.props.validInstance && !this.props.validating) {
|
||||
if (this.props.validInstance) {
|
||||
this.props.navigation.goBack();
|
||||
this.adding = false;
|
||||
}
|
||||
|
@ -127,7 +88,7 @@ export default class NewServerView extends React.Component {
|
|||
if (this.props.validating) {
|
||||
return (
|
||||
<Text style={[styles.validateText, styles.validatingText]}>
|
||||
Validating {this.state.url} ...
|
||||
Validating {this.state.text} ...
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
@ -149,27 +110,37 @@ export default class NewServerView extends React.Component {
|
|||
render() {
|
||||
return (
|
||||
<KeyboardView
|
||||
scrollEnabled={false}
|
||||
contentContainerStyle={[styles.view, { height: Dimensions.get('window').height }]}
|
||||
contentContainerStyle={styles.container}
|
||||
keyboardVerticalOffset={128}
|
||||
>
|
||||
<View style={styles.spaceView} />
|
||||
<TextInput
|
||||
ref={ref => this.inputElement = ref}
|
||||
style={styles.input}
|
||||
onChangeText={this.onChangeText}
|
||||
keyboardType='url'
|
||||
autoCorrect={false}
|
||||
returnKeyType='done'
|
||||
autoCapitalize='none'
|
||||
autoFocus
|
||||
editable={this.state.editable}
|
||||
onSubmitEditing={this.submit}
|
||||
placeholder={this.state.defaultServer}
|
||||
/>
|
||||
<View style={styles.spaceView}>
|
||||
<ScrollView
|
||||
style={styles.loginView}
|
||||
keyboardDismissMode='interactive'
|
||||
keyboardShouldPersistTaps='always'
|
||||
>
|
||||
<TextInput
|
||||
ref={ref => this.inputElement = ref}
|
||||
style={styles.input_white}
|
||||
onChangeText={this.onChangeText}
|
||||
keyboardType='url'
|
||||
autoCorrect={false}
|
||||
returnKeyType='done'
|
||||
autoCapitalize='none'
|
||||
autoFocus
|
||||
editable={this.state.editable}
|
||||
placeholder={this.state.defaultServer}
|
||||
underlineColorAndroid='transparent'
|
||||
/>
|
||||
<TouchableOpacity
|
||||
disabled={!this.props.validInstance}
|
||||
style={[styles.buttonContainer, this.props.validInstance ? null
|
||||
: styles.disabledButton]}
|
||||
onPress={this.submit}
|
||||
>
|
||||
<Text style={styles.button} accessibilityTraits='button'>Add</Text>
|
||||
</TouchableOpacity>
|
||||
{this.renderValidation()}
|
||||
</View>
|
||||
</ScrollView>
|
||||
</KeyboardView>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import styles from './styles';
|
|||
|
||||
@connect(state => ({
|
||||
user: state.login.user,
|
||||
connected: state.meteor.connected,
|
||||
baseUrl: state.settings.Site_Url
|
||||
}), dispatch => ({
|
||||
setSearch: searchText => dispatch(setSearch(searchText))
|
||||
|
@ -23,6 +24,7 @@ export default class extends React.Component {
|
|||
static propTypes = {
|
||||
navigation: PropTypes.object.isRequired,
|
||||
user: PropTypes.object.isRequired,
|
||||
connected: PropTypes.bool,
|
||||
baseUrl: PropTypes.string,
|
||||
setSearch: PropTypes.func
|
||||
}
|
||||
|
@ -57,7 +59,7 @@ export default class extends React.Component {
|
|||
}
|
||||
|
||||
getUserStatus() {
|
||||
return this.props.user.status || 'offline';
|
||||
return (this.props.connected && this.props.user.status) || 'offline';
|
||||
}
|
||||
|
||||
getUserStatusLabel() {
|
||||
|
|
|
@ -156,5 +156,14 @@ export default StyleSheet.create({
|
|||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'space-around'
|
||||
},
|
||||
validText: {
|
||||
color: 'green'
|
||||
},
|
||||
invalidText: {
|
||||
color: 'red'
|
||||
},
|
||||
validatingText: {
|
||||
color: '#aaa'
|
||||
}
|
||||
});
|
||||
|
|
File diff suppressed because it is too large
Load Diff
21
package.json
21
package.json
|
@ -21,16 +21,17 @@
|
|||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@storybook/react-native": "^3.2.15",
|
||||
"@storybook/addons": "^3.2.18",
|
||||
"@storybook/react-native": "^3.2.18",
|
||||
"babel-plugin-transform-decorators-legacy": "^1.3.4",
|
||||
"babel-plugin-transform-remove-console": "^6.8.5",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"ejson": "^2.1.2",
|
||||
"moment": "^2.19.3",
|
||||
"moment": "^2.20.1",
|
||||
"prop-types": "^15.6.0",
|
||||
"react": "^16.2.0",
|
||||
"react-emojione": "^5.0.0",
|
||||
"react-native": "^0.50.4",
|
||||
"react-native": "^0.51.0",
|
||||
"react-native-action-button": "^2.8.3",
|
||||
"react-native-actionsheet": "^2.3.0",
|
||||
"react-native-animatable": "^1.2.4",
|
||||
|
@ -59,16 +60,16 @@
|
|||
"redux-immutable-state-invariant": "^2.1.0",
|
||||
"redux-logger": "^3.0.6",
|
||||
"redux-saga": "^0.16.0",
|
||||
"regenerator-runtime": "^0.11.0",
|
||||
"regenerator-runtime": "^0.11.1",
|
||||
"remote-redux-devtools": "^0.5.12",
|
||||
"simple-markdown": "^0.3.1",
|
||||
"snyk": "^1.41.1",
|
||||
"snyk": "^1.61.1",
|
||||
"strip-ansi": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@storybook/addon-storyshots": "^3.2.15",
|
||||
"@storybook/addon-storyshots": "^3.2.18",
|
||||
"babel-eslint": "^8.0.2",
|
||||
"babel-jest": "21.2.0",
|
||||
"babel-jest": "^22.0.3",
|
||||
"babel-plugin-transform-react-remove-prop-types": "^0.4.10",
|
||||
"babel-preset-es2015": "^6.24.1",
|
||||
"babel-preset-react-native": "4.0.0",
|
||||
|
@ -76,12 +77,12 @@
|
|||
"eslint": "^4.12.0",
|
||||
"eslint-config-airbnb": "^16.1.0",
|
||||
"eslint-plugin-import": "^2.8.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.0.2",
|
||||
"eslint-plugin-jsx-a11y": "^6.0.3",
|
||||
"eslint-plugin-react": "^7.5.1",
|
||||
"eslint-plugin-react-native": "^3.2.0",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"jest": "21.2.1",
|
||||
"jest-cli": "^21.2.1",
|
||||
"jest": "^22.0.3",
|
||||
"jest-cli": "^22.0.3",
|
||||
"react-dom": "^16.2.0",
|
||||
"react-test-renderer": "^16.2.0"
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue