Cleaning up refactors code, fixing sagas. By Gazzo

This commit is contained in:
Gabriel Delavald 2017-08-18 18:30:16 -03:00
parent c498901aac
commit 46b491cf32
22 changed files with 218 additions and 365 deletions

View File

@ -10,9 +10,10 @@ function createRequestTypes(base, types = defaultTypes) {
} }
// Login events // Login events
export const LOGIN = createRequestTypes('LOGIN'); export const LOGIN = createRequestTypes('LOGIN', [...defaultTypes, 'SET_TOKEN']);
export const ROOMS = createRequestTypes('ROOMS'); export const ROOMS = createRequestTypes('ROOMS');
export const MESSAGES = createRequestTypes('MESSAGES'); export const MESSAGES = createRequestTypes('MESSAGES');
export const SERVER = createRequestTypes('SERVER', ['SELECT', 'CHANGED']);
export const METEOR = createRequestTypes('METEOR_CONNECT', [...defaultTypes, 'DISCONNECT']); export const METEOR = createRequestTypes('METEOR_CONNECT', [...defaultTypes, 'DISCONNECT']);
export const LOGOUT = 'LOGOUT'; // logout is always success export const LOGOUT = 'LOGOUT'; // logout is always success

View File

@ -10,7 +10,8 @@ export function loginRequest(credentials) {
export function loginSuccess(user) { export function loginSuccess(user) {
return { return {
type: types.LOGIN.SUCCESS, type: types.LOGIN.SUCCESS,
user user,
token: user.token
}; };
} }
@ -21,8 +22,14 @@ export function loginFailure(err) {
}; };
} }
export function setToken(token) {
return {
type: types.LOGIN.SET_TOKEN,
token
};
}
export function logout() { export function logout() {
console.log('LOGOUT');
return { return {
type: types.LOGOUT type: types.LOGOUT
}; };

14
app/actions/server.js Normal file
View File

@ -0,0 +1,14 @@
import { SERVER } from './actionsTypes';
export function setServer(server) {
return {
type: SERVER.SELECT,
server
};
}
export function changedServer(server) {
return {
type: SERVER.CHANGED,
server
};
}

View File

@ -2,7 +2,7 @@ import React from 'react';
import { CachedImage } from 'react-native-img-cache'; import { CachedImage } from 'react-native-img-cache';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'; import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { View, Text, StyleSheet } from 'react-native'; import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
import avatarInitialsAndColor from '../utils/avatarInitialsAndColor'; import avatarInitialsAndColor from '../utils/avatarInitialsAndColor';
@ -106,16 +106,17 @@ export default class RoomItem extends React.PureComponent {
} }
render() { render() {
const { item } = this.props;
let extraSpace = {}; let extraSpace = {};
if (this.props.item.unread) { if (item.unread) {
extraSpace = { paddingRight: 92 }; extraSpace = { paddingRight: 92 };
} }
return ( return (
<View style={[styles.container, extraSpace]}> <TouchableOpacity key={item._id} onPress={() => this.props.onPress(item._id, item)} style={[styles.container, extraSpace]}>
{this.icon} {this.icon}
<Text style={styles.roomName} ellipsizeMode='tail' numberOfLines={1}>{ this.props.item.name }</Text> <Text style={styles.roomName} ellipsizeMode='tail' numberOfLines={1}>{ this.props.item.name }</Text>
{this.renderNumber(this.props.item)} {this.renderNumber(this.props.item)}
</View> </TouchableOpacity>
); );
} }
} }

View File

@ -14,8 +14,8 @@ const styles = StyleSheet.create({
}); });
@connect(state => ({ @connect(state => ({
connecting: state.meteor && state.meteor.connecting, connecting: state.meteor.connecting,
authenticating: state.login && state.login.isFetching, authenticating: state.login.isFetching,
offline: !state.meteor.connected offline: !state.meteor.connected
})) }))

View File

@ -1,6 +1,7 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import Meteor from 'react-native-meteor'; import Meteor from 'react-native-meteor';
import { connect } from 'react-redux';
import { CachedImage } from 'react-native-img-cache'; import { CachedImage } from 'react-native-img-cache';
import { Text, TouchableOpacity } from 'react-native'; import { Text, TouchableOpacity } from 'react-native';
import { Navigation } from 'react-native-navigation'; import { Navigation } from 'react-native-navigation';
@ -24,6 +25,11 @@ const CustomButton = ({ text }) => (
); );
Navigation.registerComponent('CustomButton', () => CustomButton); Navigation.registerComponent('CustomButton', () => CustomButton);
@connect(state => ({
base: state.settings.Site_Url,
canShowList: state.login.token.length || state.login.user.token
}))
export default class Cards extends React.PureComponent { export default class Cards extends React.PureComponent {
static propTypes = { static propTypes = {
data: PropTypes.object.isRequired data: PropTypes.object.isRequired
@ -33,7 +39,7 @@ export default class Cards extends React.PureComponent {
const user = Meteor.user(); const user = Meteor.user();
this.state = {}; this.state = {};
RocketChat.getUserToken().then((token) => { RocketChat.getUserToken().then((token) => {
this.setState({ img: `${ RocketChat.currentServer }${ this.props.data.image_url }?rc_uid=${ user._id }&rc_token=${ token }` }); this.setState({ img: `${ this.props.base }${ this.props.data.image_url }?rc_uid=${ user._id }&rc_token=${ token }` });
}); });
} }
_onPressButton() { _onPressButton() {

View File

@ -20,6 +20,6 @@ if (__DEV__) {
export default createStore( export default createStore(
reducers, reducers,
applyMiddleware(sagaMiddleware) applyMiddleware(...middleware)
); );
sagaMiddleware.run(sagas); sagaMiddleware.run(sagas);

View File

@ -9,7 +9,7 @@ import settingsType from '../constants/settings';
import realm from './realm'; import realm from './realm';
import * as actions from '../actions'; import * as actions from '../actions';
import { disconnect, connectSuccess } from '../actions/connect'; import { disconnect, connectSuccess } from '../actions/connect';
import { logout, loginSuccess } from '../actions/login'; import { loginSuccess } from '../actions/login';
export { Accounts } from 'react-native-meteor'; export { Accounts } from 'react-native-meteor';
@ -29,18 +29,6 @@ const RocketChat = {
}); });
}, },
get currentServer() {
const current = realm.objects('servers').filtered('current = true').slice(0, 1)[0];
return current && current.id;
},
set currentServer(server) {
realm.write(() => {
realm.objects('servers').filtered('current = true').forEach(item => (item.current = false));
realm.create('servers', { id: server, current: true }, true);
});
},
async getUserToken() { async getUserToken() {
const TOKEN_KEY = 'reactnativemeteor_usertoken'; const TOKEN_KEY = 'reactnativemeteor_usertoken';
try { try {
@ -50,23 +38,20 @@ const RocketChat = {
} }
}, },
connect(cb) { connect(_url) {
return new Promise((resolve, reject) => { return new Promise((resolve) => {
const url = `${ RocketChat.currentServer }/websocket`; const url = `${ _url }/websocket`;
Meteor.connect(url, { autoConnect: true, autoReconnect: true }); Meteor.connect(url, { autoConnect: true, autoReconnect: true });
// , { autoConnect: false, autoReconnect: false } // , { autoConnect: false, autoReconnect: false }
Meteor.ddp.on('disconnected', () => { Meteor.ddp.on('disconnected', () => {
console.log('disconnected');
reduxStore.dispatch(disconnect()); reduxStore.dispatch(disconnect());
}); });
Meteor.ddp.on('connected', (err) => { Meteor.ddp.on('connected', () => {
console.log('connected');
reduxStore.dispatch(connectSuccess()); reduxStore.dispatch(connectSuccess());
resolve(); resolve();
}); });
Meteor.ddp.on('loggin', () => { Meteor.ddp.on('loggin', () => {
console.log('Meteor.ddp.on(\'loggin\',');
reduxStore.dispatch(loginSuccess({})); reduxStore.dispatch(loginSuccess({}));
}); });
Meteor.ddp.on('connected', () => { Meteor.ddp.on('connected', () => {
@ -81,7 +66,7 @@ const RocketChat = {
const setting = { const setting = {
_id: item._id _id: item._id
}; };
setting._server = { id: RocketChat.currentServer }; setting._server = { id: reduxStore.getState().server };
if (settingsType[item.type]) { if (settingsType[item.type]) {
setting[settingsType[item.type]] = item.value; setting[settingsType[item.type]] = item.value;
realm.create('settings', setting, true); realm.create('settings', setting, true);
@ -91,10 +76,6 @@ const RocketChat = {
}); });
}); });
reduxStore.dispatch(actions.setAllSettings(settings)); reduxStore.dispatch(actions.setAllSettings(settings));
if (typeof cb === 'function') {
cb();
}
}); });
Meteor.ddp.on('changed', (ddbMessage) => { Meteor.ddp.on('changed', (ddbMessage) => {
@ -103,7 +84,7 @@ const RocketChat = {
realm.write(() => { realm.write(() => {
const message = ddbMessage.fields.args[0]; const message = ddbMessage.fields.args[0];
message.temp = false; message.temp = false;
message._server = { id: RocketChat.currentServer }; message._server = { id: reduxStore.getState().server };
// write('messages', message); // write('messages', message);
realm.create('messages', message, true); realm.create('messages', message, true);
}); });
@ -113,24 +94,27 @@ const RocketChat = {
// console.log(ddbMessage); // console.log(ddbMessage);
realm.write(() => { realm.write(() => {
const data = ddbMessage.fields.args[1]; const data = ddbMessage.fields.args[1];
data._server = { id: RocketChat.currentServer }; data._server = { id: reduxStore.getState().server };
realm.create('subscriptions', data, true); realm.create('subscriptions', data, true);
}); });
} }
}); });
}); });
}); })
.catch(e => console.error(e));
}, },
login(params, callback) { login(params, callback) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
Meteor._startLoggingIn(); Meteor._startLoggingIn();
console.log('meteor login', params);
return Meteor.call('login', params, (err, result) => { return Meteor.call('login', params, (err, result) => {
Meteor._endLoggingIn(); Meteor._endLoggingIn();
Meteor._handleLoginCallback(err, result); Meteor._handleLoginCallback(err, result);
console.log(result); if (err) {
err ? reject(err) : resolve(result); reject(err);
} else {
resolve(result);
}
if (typeof callback === 'function') { if (typeof callback === 'function') {
callback(err, result); callback(err, result);
} }
@ -194,7 +178,7 @@ const RocketChat = {
// if (typeof item.value === 'string') { // if (typeof item.value === 'string') {
// subscription.value = item.value; // subscription.value = item.value;
// } // }
subscription._server = { id: RocketChat.currentServer }; subscription._server = { id: reduxStore.getState().server };
// write('subscriptions', subscription); // write('subscriptions', subscription);
realm.create('subscriptions', subscription, true); realm.create('subscriptions', subscription, true);
}); });
@ -218,7 +202,7 @@ const RocketChat = {
realm.write(() => { realm.write(() => {
data.messages.forEach((message) => { data.messages.forEach((message) => {
message.temp = false; message.temp = false;
message._server = { id: RocketChat.currentServer }; message._server = { id: reduxStore.getState().server };
// write('messages', message); // write('messages', message);
realm.create('messages', message, true); realm.create('messages', message, true);
}); });
@ -240,7 +224,6 @@ const RocketChat = {
getMessage(rid, msg = {}) { getMessage(rid, msg = {}) {
const _id = Random.id(); const _id = Random.id();
const user = Meteor.user();
const message = { const message = {
_id, _id,
rid, rid,
@ -248,10 +231,10 @@ const RocketChat = {
ts: new Date(), ts: new Date(),
_updatedAt: new Date(), _updatedAt: new Date(),
temp: true, temp: true,
_server: { id: RocketChat.currentServer }, _server: { id: reduxStore.getState().server },
u: { u: {
_id: user._id, _id: reduxStore.getState()._id,
username: user.username username: reduxStore.getState()._id
} }
}; };
@ -400,16 +383,13 @@ const RocketChat = {
// if (typeof item.value === 'string') { // if (typeof item.value === 'string') {
// subscription.value = item.value; // subscription.value = item.value;
// } // }
subscription._server = { id: RocketChat.currentServer }; subscription._server = { id: reduxStore.getState().server };
// write('subscriptions', subscription); // write('subscriptions', subscription);
realm.create('subscriptions', subscription, true); realm.create('subscriptions', subscription, true);
}); });
}); });
return data; return data;
}).then((data) => { }).then(data => data);
console.log('subscriptions done.');
return data;
});
// }); // });
}, },
logout() { logout() {
@ -418,9 +398,3 @@ const RocketChat = {
}; };
export default RocketChat; export default RocketChat;
if (RocketChat.currentServer) {
reduxStore.dispatch(actions.setCurrentServer(RocketChat.currentServer));
}
// Use for logout
// AsyncStorage.clear();

View File

@ -20,8 +20,8 @@ Navigation.registerComponent('CreateChannel', () => CreateChannel, store, Provid
Navigation.startSingleScreenApp({ Navigation.startSingleScreenApp({
screen: { screen: {
screen: 'Rooms', screen: 'ListServer',
title: 'Channels' title: 'ListServer'
}, },
animationType: 'none' animationType: 'none'
}); });

View File

@ -3,10 +3,9 @@ import * as reducers from './reducers';
import login from './login'; import login from './login';
import meteor from './connect'; import meteor from './connect';
import messages from './messages'; import messages from './messages';
import server from './server';
console.log(Object.keys({
...reducers, login, meteor, messages
}));
export default combineReducers({ export default combineReducers({
...reducers, login, meteor, messages ...reducers, login, meteor, messages, server
}); });

View File

@ -32,11 +32,14 @@ export default function login(state = initialState, action) {
errorMessage: action.err errorMessage: action.err
}; };
case types.LOGOUT: case types.LOGOUT:
console.log('LOGOUT');
return { ...state, return { ...state,
isFetching: false, isFetching: false,
isAuthenticated: false isAuthenticated: false
}; };
case types.LOGIN.SET_TOKEN:
return { ...state,
token: action.token
};
default: default:
return state; return state;
} }

View File

@ -1,17 +1,7 @@
import RocketChat from '../lib/rocketchat';
import * as types from '../constants/types'; import * as types from '../constants/types';
import initialState from './initialState'; import initialState from './initialState';
export function server(state = initialState.server, action) { export default function settings(state = initialState.settings, action) {
if (action.type === types.SET_CURRENT_SERVER) {
RocketChat.currentServer = action.payload;
return action.payload;
}
return state;
}
export function settings(state = initialState.settings, action) {
if (action.type === types.SET_ALL_SETTINGS) { if (action.type === types.SET_ALL_SETTINGS) {
return { ...state, return { ...state,
...action.payload ...action.payload

10
app/reducers/server.js Normal file
View File

@ -0,0 +1,10 @@
import { SERVER } from '../actions/actionsTypes';
export default function server(state = '', action) {
switch (action.type) {
case SERVER.SELECT:
return action.server;
default:
return state;
}
}

View File

@ -1,33 +1,26 @@
import { take, put, call, fork, takeLatest } from 'redux-saga/effects'; import { take, put, call, fork, takeLatest, select } from 'redux-saga/effects';
import { METEOR } from '../actions/actionsTypes'; import { METEOR } from '../actions/actionsTypes';
import RocketChat from '../lib/rocketchat'; import RocketChat from '../lib/rocketchat';
import { connectSuccess, connectRequest, connectFailure } from '../actions/connect'; import { connectSuccess, connectFailure } from '../actions/connect';
function connect(...args) { const getServer = ({ server }) => server;
return RocketChat.connect(...args);
}
const auto = function* auto() { const connect = url => RocketChat.connect(url);
while (true) { const test = function* test() {
yield take(METEOR.DISCONNECT); try {
console.log('\n\n[METEOR DISCONNECT]\n\n'); const server = yield select(getServer);
yield put(connectRequest()); const response = yield call(connect, server);
yield put(connectSuccess(response));
} catch (err) {
yield put(connectFailure(err.status));
} }
}; };
const test = function* test() {
const response = yield call(connect);
yield put(connectSuccess(response));
console.log('\n\n[METEOR CONNECTED]\n\n');
};
const watchConnect = function* watchConnect() { const watchConnect = function* watchConnect() {
yield takeLatest(METEOR.REQUEST, test);
while (true) { while (true) {
try {
yield takeLatest(METEOR.REQUEST, test);
} catch (err) {
yield put(connectFailure(err.status));
}
yield take(METEOR.DISCONNECT); yield take(METEOR.DISCONNECT);
console.log('\n\n[METEOR DISCONNECT]\n\n');
} }
}; };
const root = function* root() { const root = function* root() {

View File

@ -3,16 +3,16 @@ import hello from './hello';
import login from './login'; import login from './login';
import connect from './connect'; import connect from './connect';
import rooms from './rooms'; import rooms from './rooms';
import logger from './logger';
import messages from './messages'; import messages from './messages';
import selectServer from './selectServer';
const root = function* root() { const root = function* root() {
yield fork(hello); yield fork(hello);
yield fork(rooms); yield fork(rooms);
yield fork(login); yield fork(login);
yield fork(connect); yield fork(connect);
yield fork(logger);
yield fork(messages); yield fork(messages);
yield fork(selectServer);
}; };
// Consider using takeEvery // Consider using takeEvery
export default root; export default root;

View File

@ -1,12 +0,0 @@
import { select, takeEvery } from 'redux-saga/effects';
const root = function* watchAndLog() {
// yield takeEvery('*', function* logger(action) {
// const state = yield select();
// const tmp = { ...state };
// delete tmp.settings;
// console.log('action', action);
// console.log('state after', tmp);
// });
};
export default root;

View File

@ -1,48 +1,55 @@
import React from 'react'; import { AsyncStorage } from 'react-native';
import { take, put, call, takeLast, fork, select } from 'redux-saga/effects'; import { take, put, call, takeEvery, fork, select, all } from 'redux-saga/effects';
import * as types from '../actions/actionsTypes'; import * as types from '../actions/actionsTypes';
import { loginRequest, loginSuccess, loginFailure, logout } from '../actions/login'; import { loginRequest, loginSuccess, loginFailure, setToken } from '../actions/login';
import RocketChat from '../lib/rocketchat'; import RocketChat from '../lib/rocketchat';
const TOKEN_KEY = 'reactnativemeteor_usertoken';
const getUser = state => state.login.user; const getUser = state => state.login.user;
function loginCall(args) { const getServer = state => state.server;
return args.resume ? RocketChat.login(args) : RocketChat.loginWithPassword(args); const loginCall = args => (args.resume ? RocketChat.login(args) : RocketChat.loginWithPassword(args));
}
const auto = function* auto() { const getToken = function* getToken() {
while (true) { const currentServer = yield select(getServer);
yield take(types.LOGOUT); console.log('currentServer', currentServer);
yield take(types.METEOR.SUCCESS); const token = yield call([AsyncStorage, 'getItem'], `${ TOKEN_KEY }-${ currentServer }`);
const user = yield select(getUser); console.log('currentServer TOKEN', token);
if (user.token) { if (token) { yield put(setToken(token)); }
yield put(loginRequest({ resume: user.token })); // yield call([AsyncStorage, 'setItem'], TOKEN_KEY, token || '');
} return token;
};
const sagaLogin = function* sagaLogin(payload) {
try {
const response = yield call(loginCall, payload);
yield put(loginSuccess(response));
} catch (err) {
yield put(loginFailure(err));
} }
}; };
const watchLoginRequest = function* watchLoginRequest() { const watchLoginRequest = function* watchLoginRequest() {
while (true) { do {
try { try {
yield take(types.METEOR.SUCCESS); yield all([take(types.METEOR.SUCCESS), take(types.SERVER.CHANGED)]);
// console.log('\n\n[LOGIN METEOR CONNECTED]\n\n'); const token = yield call(getToken);
const payload = yield take(types.LOGIN.REQUEST); if (token) {
try { yield put(loginRequest({ resume: token }));
const response = yield call(loginCall, payload);
yield put(loginSuccess(response));
console.log('\n\n[LOGIN SUCCESS]\n\n');
} catch (err) {
// console.log('\n\n[LOGIN FAILURE]\n\n', err);
yield put(loginFailure(err.status));
} }
yield take(types.METEOR.DISCONNECT);
console.log('\n\n[METEOR DISCONNECT LOGOUT]\n\n');
yield put(logout());
} catch (e) { } catch (e) {
console.error(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 }`, user.token);
}; };
const root = function* root() { const root = function* root() {
yield fork(watchLoginRequest); yield fork(watchLoginRequest);
yield fork(auto); yield takeEvery(types.LOGIN.REQUEST, sagaLogin);
yield takeEvery(types.LOGIN.SUCCESS, saveToken);
}; };
export default root; export default root;

View File

@ -1,28 +1,21 @@
import { take, put, call, fork } from 'redux-saga/effects'; import { put, call, takeEvery } from 'redux-saga/effects';
import * as types from '../actions/actionsTypes'; import * as types from '../actions/actionsTypes';
import { roomsSuccess, roomsFailure } from '../actions/rooms'; import { roomsSuccess, roomsFailure } from '../actions/rooms';
import RocketChat from '../lib/rocketchat'; import RocketChat from '../lib/rocketchat';
function getRooms(...args) { const getRooms = function* getRooms() {
// console.log('\n\n\n\n\n\naqui\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n'); return yield RocketChat.getRooms();
return RocketChat.getRooms(...args);
}
const watchRoomsRequest = function* watchRoomsRequest() {
// console.log('\n\n\n\n\n\n\n\nWAINTING FOR LOGINsss\n\n\n\n\n\n\n\n');
while (true) {
// console.log('\n\n\n\n\n\n\n\nWAINTING FOR LOGIN\n\n\n\n\n\n\n\n');
yield take(types.LOGIN.SUCCESS);
// console.log('\n\n\n\n\n\n\n\nWAINTING FOR LOGIN NO MORE\n\n\n\n\n\n\n\n');
// const payload = yield take(types.ROOMS.REQUEST);
try {
yield call(getRooms);
yield put(roomsSuccess());
} catch (err) {
console.log(err);
yield put(roomsFailure(err.status));
}
}
}; };
export default watchRoomsRequest; const watchRoomsRequest = function* watchRoomsRequest() {
try {
yield call(getRooms);
yield put(roomsSuccess());
} catch (err) {
yield put(roomsFailure(err.status));
}
};
const root = function* root() {
yield takeEvery(types.LOGIN.SUCCESS, watchRoomsRequest);
};
export default root;

21
app/sagas/selectServer.js Normal file
View File

@ -0,0 +1,21 @@
import { put, takeEvery } from 'redux-saga/effects';
import { SERVER } from '../actions/actionsTypes';
import { connectRequest, disconnect } from '../actions/connect';
import { changedServer } from '../actions/server';
const selectServer = function* selectServer(server) {
try {
yield put(disconnect());
yield put(changedServer(server));
yield (server && put(connectRequest(server)));
// console.log(Actions.login());
// Actions.replace('login', {});
} catch (e) {
console.log(e);
}
};
const root = function* root() {
yield takeEvery(SERVER.SELECT, selectServer);
};
export default root;

View File

@ -68,27 +68,6 @@ class LoginView extends React.Component {
console.log({ username, password, code }); console.log({ username, password, code });
this.props.loginRequest({ username, password, code }); this.props.loginRequest({ username, password, code });
this.props.navigator.dismissModal(); this.props.navigator.dismissModal();
//
//
// this.setState({
// error: undefined
// });
//
//
// RocketChat.loginWithPassword(credentials, (error) => {
// if (error) {
// if (error.error === 'totp-required') {
// this.setState({ totp: true });
// this.codeInput.focus();
// } else {
// this.setState({
// error: error.reason
// });
// }
// } else {
// this.props.navigator.dismissModal();
// }
// });
} }
renderTOTP = () => { renderTOTP = () => {
@ -147,7 +126,7 @@ function mapStateToProps(state) {
server: state.server, server: state.server,
Accounts_EmailOrUsernamePlaceholder: state.settings.Accounts_EmailOrUsernamePlaceholder, Accounts_EmailOrUsernamePlaceholder: state.settings.Accounts_EmailOrUsernamePlaceholder,
Accounts_PasswordPlaceholder: state.settings.Accounts_PasswordPlaceholder, Accounts_PasswordPlaceholder: state.settings.Accounts_PasswordPlaceholder,
login: state.login || state.default login: state.login
}; };
} }

View File

@ -1,20 +1,16 @@
import ActionButton from 'react-native-action-button'; import ActionButton from 'react-native-action-button';
import { Navigation } from 'react-native-navigation'; // import { Navigation } from 'react-native-navigation';
import { ListView } from 'realm/react-native'; import { ListView } from 'realm/react-native';
import Icon from 'react-native-vector-icons/Ionicons';
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Text, View, StyleSheet, TouchableOpacity, Platform, TextInput } from 'react-native'; import { View, StyleSheet, TextInput } from 'react-native';
import Meteor from 'react-native-meteor';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import * as actions from '../actions'; import * as actions from '../actions';
import * as meteor from '../actions/connect'; import * as server from '../actions/connect';
import realm from '../lib/realm'; import realm from '../lib/realm';
import RocketChat from '../lib/rocketchat'; import RocketChat from '../lib/rocketchat';
import RoomItem from '../components/RoomItem'; import RoomItem from '../components/RoomItem';
import Banner from '../components/banner'; import Banner from '../components/banner';
// import debounce from '../utils/debounce';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
@ -58,106 +54,37 @@ const styles = StyleSheet.create({
}); });
const ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 }); const ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 });
class RoomsListItem extends React.PureComponent {
static propTypes = {
item: PropTypes.object.isRequired,
onPress: PropTypes.func.isRequired,
baseUrl: PropTypes.string
}
_onPress = (...args) => {
this.props.onPress(...args);
};
render() {
const { item } = this.props;
return (
<TouchableOpacity key={item._id} onPress={() => this.props.onPress(item._id, item)}>
<RoomItem
id={item._id}
item={item}
baseUrl={this.props.baseUrl}
/>
</TouchableOpacity>
);
}
}
@connect(state => ({ @connect(state => ({
server: state.server, server: state.server,
Site_Url: state.settings.Site_Url login: state.login,
Site_Url: state.settings.Site_Url,
canShowList: state.login.token.length || state.login.user.token
}), dispatch => ({ }), dispatch => ({
actions: bindActionCreators(actions, dispatch),
login: () => dispatch(actions.login()), login: () => dispatch(actions.login()),
connect: () => dispatch(meteor.connectRequest()) connect: () => dispatch(server.connectRequest())
})) }))
export default class RoomsListView extends React.Component { export default class RoomsListView extends React.Component {
static propTypes = { static propTypes = {
navigator: PropTypes.object.isRequired, // navigator: PropTypes.object.isRequired,
server: PropTypes.string, server: PropTypes.string
Site_Url: PropTypes.string
} }
constructor(props) { constructor(props) {
super(props); super(props);
this.data = realm.objects('subscriptions').filtered('_server.id = $0', this.props.server);
// this.data = realm.objects('subscriptions').filtered('_server.id = $0', this.props.server);
this.state = { this.state = {
dataSource: ds.cloneWithRows([]), dataSource: ds.cloneWithRows(this.data),
searching: false, searching: false,
searchDataSource: [], searchDataSource: [],
searchText: '' searchText: '',
login: false
}; };
this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this));
}
componentWillMount() {
const button = Platform.OS === 'ios' ? 'leftButtons' : 'rightButtons';
this.props.navigator.setButtons({
[button]: [{
id: 'servers',
title: 'Servers'
}],
animated: true
});
if (this.props.server) {
this.setInitialData();
} else {
Navigation.showModal({
screen: 'ListServer',
passProps: {},
navigatorStyle: {},
navigatorButtons: {},
animationType: 'none'
});
}
}
componentWillReceiveProps(nextProps) {
if (nextProps.server !== this.props.server) {
this.setInitialData(nextProps);
}
} }
componentWillUnmount() { componentWillUnmount() {
this.state.data.removeListener(this.updateState); this.data.removeListener(this.updateState);
}
onNavigatorEvent = (event) => {
if (event.type === 'NavBarButtonPress') {
if (event.id === 'servers') {
Navigation.showModal({
screen: 'ListServer',
passProps: {},
navigatorStyle: {},
navigatorButtons: {},
animationType: 'slide-up'
// animationType: 'none'
});
}
}
} }
onSearchChangeText = (text) => { onSearchChangeText = (text) => {
@ -168,11 +95,11 @@ export default class RoomsListView extends React.Component {
}); });
if (searchText === '') { if (searchText === '') {
return this.setState({ return this.setState({
dataSource: ds.cloneWithRows(this.state.data) dataSource: ds.cloneWithRows(this.data)
}); });
} }
const data = this.state.data.filtered('name CONTAINS[c] $0', searchText).slice(); const data = this.data.filtered('name CONTAINS[c] $0', searchText).slice();
const usernames = []; const usernames = [];
const dataSource = data.map((sub) => { const dataSource = data.map((sub) => {
@ -218,31 +145,6 @@ export default class RoomsListView extends React.Component {
}); });
} }
setInitialData = (props = this.props) => {
// console.log(this.props);
this.props.connect();
props.navigator.setSubTitle({
subtitle: props.server
});
RocketChat.getUserToken().then((token) => {
if (!token) {
Navigation.showModal({
screen: 'Login',
animationType: 'slide-up'
});
}
const data = realm.objects('subscriptions').filtered('_server.id = $0', props.server).sorted('_updatedAt', true);
this.setState({
dataSource: ds.cloneWithRows(data),
data
});
data.addListener(this.updateState);
});
}
updateState = () => { updateState = () => {
this.setState({ this.setState({
@ -252,10 +154,10 @@ export default class RoomsListView extends React.Component {
_onPressItem = (id, item = {}) => { _onPressItem = (id, item = {}) => {
const navigateToRoom = (room) => { const navigateToRoom = (room) => {
this.props.navigator.push({ // this.props.navigator.push({
screen: 'Room', // screen: 'Room',
passProps: room // passProps: room
}); // });
}; };
const clearSearch = () => { const clearSearch = () => {
@ -297,26 +199,10 @@ export default class RoomsListView extends React.Component {
clearSearch(); clearSearch();
} }
_createChannel = () => {
this.props.navigator.showModal({
screen: 'CreateChannel'
});
}
renderItem = ({ item }) => (
<RoomsListItem
item={item}
onPress={() => this._onPressItem(item._id, item)}
baseUrl={this.props.Site_Url}
/>
);
renderSeparator = () => (
<View style={styles.separator} />
);
renderSearchBar = () => ( renderSearchBar = () => (
<View style={styles.searchBoxView}> <View style={styles.searchBoxView}>
<TextInput <TextInput
underlineColorAndroid='transparent'
style={styles.searchBox} style={styles.searchBox}
value={this.state.searchText} value={this.state.searchText}
onChangeText={this.onSearchChangeText} onChangeText={this.onSearchChangeText}
@ -328,44 +214,35 @@ export default class RoomsListView extends React.Component {
</View> </View>
); );
// if (!this.state.searching && !this.state.dataSource.length) { renderItem = item => (
// return ( <RoomItem
// <View style={styles.emptyView}> id={item._id}
// <Text style={styles.emptyText}>No rooms</Text> item={item}
// </View> onPress={() => this._onPressItem(item._id, item)}
// ); />
// } )
renderList = () => ( renderList = () => (
// data={this.state.searching ? this.state.searchDataSource : this.state.dataSource}
// keyExtractor={item => item._id}
// ItemSeparatorComponent={this.renderSeparator}
// renderItem={this.renderItem}
<ListView <ListView
dataSource={this.state.dataSource} dataSource={this.state.dataSource}
style={styles.list} style={styles.list}
renderRow={item => this.renderItem({ item })} renderRow={this.renderItem}
renderHeader={this.renderSearchBar} renderHeader={this.renderSearchBar}
contentOffset={{ x: 0, y: 20 }} contentOffset={{ x: 0, y: 20 }}
enableEmptySections enableEmptySections
keyboardShouldPersistTaps='always' keyboardShouldPersistTaps='always'
/> />
) )
renderCreateButtons = () => (
renderCreateButtons() { <ActionButton buttonColor='rgba(231,76,60,1)' />);
return ( render= () => {
<ActionButton buttonColor='rgba(231,76,60,1)'> if (this.props.canShowList) {
<ActionButton.Item buttonColor='#9b59b6' title='Create Channel' onPress={() => { this.props.login(); }} > return (
<Icon name='md-chatbubbles' style={styles.actionButtonIcon} /> <View style={styles.container}>
</ActionButton.Item> <Banner />
</ActionButton>); {this.renderList()}
} {this.renderCreateButtons()}
render() { </View>);
return ( }
<View style={styles.container}> return null;
<Banner />
{this.renderList()}
{this.renderCreateButtons()}
</View>
);
} }
} }

View File

@ -1,14 +1,11 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Navigation } from 'react-native-navigation'; import { Navigation } from 'react-native-navigation';
import { bindActionCreators } from 'redux';
import Zeroconf from 'react-native-zeroconf'; import Zeroconf from 'react-native-zeroconf';
import { View, Text, SectionList, Platform, StyleSheet } from 'react-native'; import { View, Text, SectionList, Platform, StyleSheet } from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { setServer } from '../actions/server';
import * as actions from '../actions';
import realm from '../lib/realm'; import realm from '../lib/realm';
import RocketChat from '../lib/rocketchat';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
view: { view: {
@ -56,7 +53,7 @@ const zeroconf = new Zeroconf();
@connect(state => ({ @connect(state => ({
server: state.server server: state.server
}), dispatch => ({ }), dispatch => ({
actions: bindActionCreators(actions, dispatch) selectServer: server => dispatch(setServer(server))
})) }))
export default class ListServerView extends React.Component { export default class ListServerView extends React.Component {
static propTypes = { static propTypes = {
@ -131,14 +128,7 @@ export default class ListServerView extends React.Component {
} }
onPressItem = (item) => { onPressItem = (item) => {
RocketChat.logout(); this.props.selectServer(item.id);
Navigation.dismissModal({
animationType: 'slide-down'
});
this.setState({
server: item.id
});
} }
getState = () => { getState = () => {
@ -177,7 +167,7 @@ export default class ListServerView extends React.Component {
renderItem = ({ item }) => ( renderItem = ({ item }) => (
<Text <Text
style={styles.listItem} style={[styles.listItem, this.props.server === item.id ? { backgroundColor: 'red' } : {}]}
onPress={() => { this.onPressItem(item); }} onPress={() => { this.onPressItem(item); }}
> >
{item.id} {item.id}