From 1c39886deb772a320eadf1d06883efb70d2157a6 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Mon, 20 Nov 2017 20:18:00 -0200 Subject: [PATCH] user typing no style --- app/actions/actionsTypes.js | 3 ++- app/actions/room.js | 22 +++++++++++++++++++ app/actions/rooms.js | 8 +++++++ app/lib/rocketchat.js | 28 ++++++++++++++++------- app/reducers/index.js | 3 ++- app/reducers/room.js | 24 ++++++++++++++++++++ app/sagas/messages.js | 1 - app/sagas/rooms.js | 44 +++++++++++++++++++++++++++++++++---- app/views/RoomView.js | 10 +++++---- app/views/RoomsListView.js | 8 +++---- 10 files changed, 127 insertions(+), 24 deletions(-) create mode 100644 app/actions/room.js create mode 100644 app/reducers/room.js diff --git a/app/actions/actionsTypes.js b/app/actions/actionsTypes.js index d0a621d6c..ee58fd34a 100644 --- a/app/actions/actionsTypes.js +++ b/app/actions/actionsTypes.js @@ -26,7 +26,8 @@ export const FORGOT_PASSWORD = createRequestTypes('FORGOT_PASSWORD', [ ...defaultTypes, 'INIT' ]); -export const ROOMS = createRequestTypes('ROOMS'); +export const ROOMS = createRequestTypes('ROOMS', [...defaultTypes, 'OPEN']); +export const ROOM = createRequestTypes('ROOM', ['ADD_USER_TYPING', 'REMOVE_USER_TYPING', 'USER_TYPING']); export const APP = createRequestTypes('APP', ['READY', 'INIT']); export const MESSAGES = createRequestTypes('MESSAGES'); export const CREATE_CHANNEL = createRequestTypes('CREATE_CHANNEL', [ diff --git a/app/actions/room.js b/app/actions/room.js new file mode 100644 index 000000000..72345eeb9 --- /dev/null +++ b/app/actions/room.js @@ -0,0 +1,22 @@ +import * as types from './actionsTypes'; + + +export function removeUserTyping(username) { + return { + type: types.ROOM.REMOVE_USER_TYPING, + username + }; +} + +export function typing(data) { + return { + type: types.ROOM.USER_TYPING, + ...data + }; +} +export function addUserTyping(username) { + return { + type: types.ROOM.ADD_USER_TYPING, + username + }; +} diff --git a/app/actions/rooms.js b/app/actions/rooms.js index bdbf94d05..91c0ab3e6 100644 --- a/app/actions/rooms.js +++ b/app/actions/rooms.js @@ -1,5 +1,6 @@ import * as types from './actionsTypes'; + export function roomsRequest() { return { type: types.ROOMS.REQUEST @@ -18,3 +19,10 @@ export function roomsFailure(err) { err }; } + +export function openRoom({ rid }) { + return { + type: types.ROOMS.OPEN, + rid + }; +} diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js index a4acb77d3..b0835ebef 100644 --- a/app/lib/rocketchat.js +++ b/app/lib/rocketchat.js @@ -8,6 +8,7 @@ import reduxStore from './createStore'; import settingsType from '../constants/settings'; import realm from './realm'; import * as actions from '../actions'; +import { typing } from '../actions/room'; import { disconnect, connectSuccess } from '../actions/connect'; export { Accounts } from 'react-native-meteor'; @@ -61,19 +62,25 @@ const RocketChat = { }); Meteor.ddp.on('connected', async() => { - Meteor.ddp.on('changed', (ddbMessage) => { - if (ddbMessage.collection === 'stream-room-messages') { + Meteor.ddp.on('changed', (ddpMessage) => { + if (ddpMessage.collection === 'stream-room-messages') { realm.write(() => { - const message = ddbMessage.fields.args[0]; + const message = ddpMessage.fields.args[0]; message.temp = false; message._server = { id: reduxStore.getState().server.server }; realm.create('messages', message, true); }); } - - if (ddbMessage.collection === 'stream-notify-user') { + if (ddpMessage.collection === 'stream-notify-room') { + const [_rid, ev] = ddpMessage.fields.eventName.split('/'); + if (ev !== 'typing') { + return; + } + reduxStore.dispatch(typing({ _rid, username: ddpMessage.fields.args[0], typing: ddpMessage.fields.args[1] })); + } + if (ddpMessage.collection === 'stream-notify-user') { realm.write(() => { - const data = ddbMessage.fields.args[1]; + const data = ddpMessage.fields.args[1]; data._server = { id: reduxStore.getState().server.server }; realm.create('subscriptions', data, true); }); @@ -241,7 +248,6 @@ const RocketChat = { } } resolve(); - Meteor.subscribe('stream-room-messages', rid, false); }); }); }, @@ -399,7 +405,13 @@ const RocketChat = { return setting; }); }, - _filterSettings: settings => settings.filter(setting => settingsType[setting.type] && setting.value) + _filterSettings: settings => settings.filter(setting => settingsType[setting.type] && setting.value), + subscribe(...args) { + return Meteor.subscribe(...args); + }, + unsubscribe(...args) { + return Meteor.unsubscribe(...args); + } }; export default RocketChat; diff --git a/app/reducers/index.js b/app/reducers/index.js index 3321b8481..15a063a61 100644 --- a/app/reducers/index.js +++ b/app/reducers/index.js @@ -3,6 +3,7 @@ import settings from './reducers'; import login from './login'; import meteor from './connect'; import messages from './messages'; +import room from './room'; import server from './server'; import navigator from './navigator'; import createChannel from './createChannel'; @@ -10,5 +11,5 @@ import app from './app'; export default combineReducers({ - settings, login, meteor, messages, server, navigator, createChannel, app + settings, login, meteor, messages, server, navigator, createChannel, app, room }); diff --git a/app/reducers/room.js b/app/reducers/room.js new file mode 100644 index 000000000..45971fd83 --- /dev/null +++ b/app/reducers/room.js @@ -0,0 +1,24 @@ +import * as types from '../actions/actionsTypes'; + +const initialState = { + usersTyping: [] +}; + +export default function room(state = initialState, action) { + switch (action.type) { + case types.ROOM.ADD_USER_TYPING: + return { + ...state, + usersTyping: [...state.usersTyping.filter(user => user !== action.username), action.username] + }; + case types.ROOM.REMOVE_USER_TYPING: + return { + ...state, + usersTyping: [...state.usersTyping.filter(user => user !== action.username)] + }; + // case types.LOGOUT: + // return initialState; + default: + return state; + } +} diff --git a/app/sagas/messages.js b/app/sagas/messages.js index fc4aff769..e21b28ce9 100644 --- a/app/sagas/messages.js +++ b/app/sagas/messages.js @@ -10,7 +10,6 @@ const get = function* get({ rid }) { } try { yield RocketChat.loadMessagesForRoom(rid, null); - yield RocketChat.readMessages(rid); yield put(messagesSuccess()); } catch (err) { console.log(err); diff --git a/app/sagas/rooms.js b/app/sagas/rooms.js index 472f3e5d3..876863f52 100644 --- a/app/sagas/rooms.js +++ b/app/sagas/rooms.js @@ -1,6 +1,8 @@ -import { put, call, takeEvery } from 'redux-saga/effects'; +import { put, call, takeLatest, takeEvery, take, select, race, fork, cancel } from 'redux-saga/effects'; import * as types from '../actions/actionsTypes'; import { roomsSuccess, roomsFailure } from '../actions/rooms'; +import { addUserTyping, removeUserTyping } from '../actions/room'; +import { messagesRequest } from '../actions/messages'; import RocketChat from '../lib/rocketchat'; const getRooms = function* getRooms() { @@ -9,15 +11,49 @@ const getRooms = function* getRooms() { const watchRoomsRequest = function* watchRoomsRequest() { try { - console.log('getRooms'); yield call(getRooms); yield put(roomsSuccess()); } catch (err) { - console.log(err); yield put(roomsFailure(err.status)); } }; +const userTyping = function* userTyping({ rid }) { + while (true) { + const { _rid, username, typing } = yield take(types.ROOM.USER_TYPING); + if (_rid === rid) { + const tmp = yield (typing ? put(addUserTyping(username)) : put(removeUserTyping(username))); + } + } +}; + +const watchRoomOpen = function* watchRoomOpen({ rid }) { + const auth = yield select(state => state.login.isAuthenticated); + if (!auth) { + yield take(types.LOGIN.SUCCESS); + } + const subscriptions = []; + yield put(messagesRequest({ rid })); + + const { open } = yield race({ + messages: take(types.MESSAGES.SUCCESS), + open: take(types.ROOMS.OPEN) + }); + + if (open) { + return; + } + RocketChat.readMessages(rid); + subscriptions.push(RocketChat.subscribe('stream-room-messages', rid, false)); + subscriptions.push(RocketChat.subscribe('stream-notify-room', `${ rid }/typing`, false)); + const thread = yield fork(userTyping, { rid }); + yield take(types.ROOMS.OPEN); + cancel(thread); + subscriptions.forEach(sub => sub.stop()); +}; + + const root = function* root() { - yield takeEvery(types.LOGIN.SUCCESS, watchRoomsRequest); + yield takeLatest(types.LOGIN.SUCCESS, watchRoomsRequest); + yield takeEvery(types.ROOMS.OPEN, watchRoomOpen); }; export default root; diff --git a/app/views/RoomView.js b/app/views/RoomView.js index 27a39ba9b..80eddf1ac 100644 --- a/app/views/RoomView.js +++ b/app/views/RoomView.js @@ -6,7 +6,7 @@ import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import * as actions from '../actions'; -import { messagesRequest } from '../actions/messages'; +import { openRoom } from '../actions/rooms'; import realm from '../lib/realm'; import RocketChat from '../lib/rocketchat'; import Message from '../containers/Message'; @@ -48,6 +48,7 @@ const styles = StyleSheet.create({ @connect( state => ({ + usersTyping: state.room.usersTyping, server: state.server.server, Site_Url: state.settings.Site_Url, Message_TimeFormat: state.settings.Message_TimeFormat, @@ -55,13 +56,13 @@ const styles = StyleSheet.create({ }), dispatch => ({ actions: bindActionCreators(actions, dispatch), - getMessages: rid => dispatch(messagesRequest({ rid })) + openRoom: rid => dispatch(openRoom({ rid })) }) ) export default class RoomView extends React.Component { static propTypes = { navigation: PropTypes.object.isRequired, - getMessages: PropTypes.func.isRequired, + openRoom: PropTypes.func.isRequired, rid: PropTypes.string, sid: PropTypes.string, name: PropTypes.string, @@ -100,7 +101,7 @@ export default class RoomView extends React.Component { realm.objectForPrimaryKey('subscriptions', this.sid).name }); this.timer = setTimeout(() => this.setState({ slow: true }), 5000); - this.props.getMessages(this.rid); + this.props.openRoom(this.rid); this.data.addListener(this.updateState); } componentDidMount() { @@ -206,6 +207,7 @@ export default class RoomView extends React.Component { renderRow={item => this.renderItem({ item })} initialListSize={10} /> + {this.props.usersTyping ? {this.props.usersTyping.join(',')} : null} {this.renderFooter()} diff --git a/app/views/RoomsListView.js b/app/views/RoomsListView.js index dde25beea..e81b41670 100644 --- a/app/views/RoomsListView.js +++ b/app/views/RoomsListView.js @@ -100,22 +100,20 @@ export default class RoomsListView extends React.Component { super(props); this.state = { - dataSource: [], + dataSource: ds.cloneWithRows([]), searchText: '' }; this.data = realm.objects('subscriptions').filtered('_server.id = $0', this.props.server).sorted('_updatedAt', true); } - componentWillMount() { + componentDidMount() { this.data.addListener(this.updateState); this.props.navigation.setParams({ createChannel: () => this._createChannel() }); - this.setState({ - dataSource: ds.cloneWithRows(this.data) - }); + this.updateState(); } componentWillReceiveProps(props) {