From 1c39886deb772a320eadf1d06883efb70d2157a6 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Mon, 20 Nov 2017 20:18:00 -0200 Subject: [PATCH 1/4] 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 d0a621d6..ee58fd34 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 00000000..72345eeb --- /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 bdbf94d0..91c0ab3e 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 a4acb77d..b0835ebe 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 3321b848..15a063a6 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 00000000..45971fd8 --- /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 fc4aff76..e21b28ce 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 472f3e5d..876863f5 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 27a39ba9..80eddf1a 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 dde25bee..e81b4167 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) { From a4fbeaefff793a5b7acb2360d000c9b3f2c486ae Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Tue, 21 Nov 2017 14:55:32 -0200 Subject: [PATCH 2/4] user typing --- app/actions/actionsTypes.js | 4 +-- app/actions/room.js | 15 ++++++++ app/actions/rooms.js | 7 ---- app/containers/MessageBox.js | 15 ++++++-- app/lib/rocketchat.js | 9 ++--- app/reducers/room.js | 7 ++-- app/sagas/rooms.js | 66 ++++++++++++++++++++++++++++-------- app/views/RoomView.js | 20 +++++++---- 8 files changed, 104 insertions(+), 39 deletions(-) diff --git a/app/actions/actionsTypes.js b/app/actions/actionsTypes.js index ee58fd34..b727d935 100644 --- a/app/actions/actionsTypes.js +++ b/app/actions/actionsTypes.js @@ -26,8 +26,8 @@ export const FORGOT_PASSWORD = createRequestTypes('FORGOT_PASSWORD', [ ...defaultTypes, 'INIT' ]); -export const ROOMS = createRequestTypes('ROOMS', [...defaultTypes, 'OPEN']); -export const ROOM = createRequestTypes('ROOM', ['ADD_USER_TYPING', 'REMOVE_USER_TYPING', 'USER_TYPING']); +export const ROOMS = createRequestTypes('ROOMS'); +export const ROOM = createRequestTypes('ROOM', ['ADD_USER_TYPING', 'REMOVE_USER_TYPING', 'USER_TYPING', 'OPEN', 'IM_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 index 72345eeb..c98648a0 100644 --- a/app/actions/room.js +++ b/app/actions/room.js @@ -14,9 +14,24 @@ export function typing(data) { ...data }; } + export function addUserTyping(username) { return { type: types.ROOM.ADD_USER_TYPING, username }; } + +export function openRoom(room) { + return { + type: types.ROOM.OPEN, + room + }; +} + +export function imTyping(status = true) { + return { + type: types.ROOM.IM_TYPING, + status + }; +} diff --git a/app/actions/rooms.js b/app/actions/rooms.js index 91c0ab3e..e1d1c8fe 100644 --- a/app/actions/rooms.js +++ b/app/actions/rooms.js @@ -19,10 +19,3 @@ export function roomsFailure(err) { err }; } - -export function openRoom({ rid }) { - return { - type: types.ROOMS.OPEN, - rid - }; -} diff --git a/app/containers/MessageBox.js b/app/containers/MessageBox.js index 7fa265aa..9eb32435 100644 --- a/app/containers/MessageBox.js +++ b/app/containers/MessageBox.js @@ -3,11 +3,14 @@ import PropTypes from 'prop-types'; import { View, TextInput, StyleSheet, SafeAreaView } from 'react-native'; import Icon from 'react-native-vector-icons/MaterialIcons'; import ImagePicker from 'react-native-image-picker'; +import { connect } from 'react-redux'; +import { imTyping } from '../actions/room'; import RocketChat from '../lib/rocketchat'; const styles = StyleSheet.create({ textBox: { paddingTop: 1, + paddingHorizontal: 15, borderTopWidth: 1, borderTopColor: '#ccc', backgroundColor: '#fff' @@ -24,14 +27,19 @@ const styles = StyleSheet.create({ }, fileButton: { color: '#aaa', - paddingLeft: 23, - paddingRight: 20, paddingTop: 10, paddingBottom: 10, fontSize: 20 } }); +@connect( + null, + dispatch => ({ + typing: status => dispatch(imTyping(status)) + }) +) + export default class MessageBox extends React.PureComponent { static propTypes = { onSubmit: PropTypes.func.isRequired, @@ -80,7 +88,6 @@ export default class MessageBox extends React.PureComponent { return ( - this.component = component} style={styles.textBoxInput} @@ -88,9 +95,11 @@ export default class MessageBox extends React.PureComponent { onSubmitEditing={event => this.submit(event.nativeEvent.text)} blurOnSubmit={false} placeholder='New message' + onChangeText={text => this.props.typing(text.length > 0)} underlineColorAndroid='transparent' defaultValue='' /> + ); diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js index 366d00d9..481c10cd 100644 --- a/app/lib/rocketchat.js +++ b/app/lib/rocketchat.js @@ -65,7 +65,7 @@ const RocketChat = { Meteor.ddp.on('changed', (ddpMessage) => { const server = { id: reduxStore.getState().server.server }; if (ddpMessage.collection === 'stream-room-messages') { - realm.write(() => { + return realm.write(() => { const message = ddpMessage.fields.args[0]; message.temp = false; message._server = server; @@ -78,7 +78,7 @@ const RocketChat = { if (ev !== 'typing') { return; } - reduxStore.dispatch(typing({ _rid, username: ddpMessage.fields.args[0], typing: ddpMessage.fields.args[1] })); + return reduxStore.dispatch(typing({ _rid, username: ddpMessage.fields.args[0], typing: ddpMessage.fields.args[1] })); } if (ddpMessage.collection === 'stream-notify-user') { const [type, data] = ddpMessage.fields.args; @@ -436,8 +436,9 @@ const RocketChat = { subscribe(...args) { return Meteor.subscribe(...args); }, - unsubscribe(...args) { - return Meteor.unsubscribe(...args); + emitTyping(room, t = true) { + const { login } = reduxStore.getState(); + return call('stream-notify-room', `${ room }/typing`, login.user.username, t); } }; diff --git a/app/reducers/room.js b/app/reducers/room.js index 45971fd8..08b95b97 100644 --- a/app/reducers/room.js +++ b/app/reducers/room.js @@ -6,6 +6,11 @@ const initialState = { export default function room(state = initialState, action) { switch (action.type) { + case types.ROOM.OPEN: + return { + ...initialState, + ...action.room + }; case types.ROOM.ADD_USER_TYPING: return { ...state, @@ -16,8 +21,6 @@ export default function room(state = initialState, action) { ...state, usersTyping: [...state.usersTyping.filter(user => user !== action.username)] }; - // case types.LOGOUT: - // return initialState; default: return state; } diff --git a/app/sagas/rooms.js b/app/sagas/rooms.js index 876863f5..6742577e 100644 --- a/app/sagas/rooms.js +++ b/app/sagas/rooms.js @@ -1,4 +1,5 @@ -import { put, call, takeLatest, takeEvery, take, select, race, fork, cancel } from 'redux-saga/effects'; +import { put, call, takeLatest, take, select, race, fork, cancel } from 'redux-saga/effects'; +import { delay } from 'redux-saga'; import * as types from '../actions/actionsTypes'; import { roomsSuccess, roomsFailure } from '../actions/rooms'; import { addUserTyping, removeUserTyping } from '../actions/room'; @@ -17,43 +18,80 @@ const watchRoomsRequest = function* watchRoomsRequest() { yield put(roomsFailure(err.status)); } }; -const userTyping = function* userTyping({ rid }) { + +const cancelTyping = function* cancelTyping(username) { 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 { typing, timeout } = yield race({ + typing: take(types.ROOM.USER_TYPING), + timeout: yield call(delay, 5000) + }); + if (timeout || (typing.username === username && !typing.typing)) { + return yield put(removeUserTyping(username)); } } }; -const watchRoomOpen = function* watchRoomOpen({ rid }) { +const usersTyping = function* usersTyping({ rid }) { + while (true) { + const { _rid, username, typing } = yield take(types.ROOM.USER_TYPING); + if (_rid === rid) { + yield (typing ? put(addUserTyping(username)) : put(removeUserTyping(username))); + if (typing) { + fork(cancelTyping, username); + } + } + } +}; + +const watchRoomOpen = function* watchRoomOpen({ room }) { const auth = yield select(state => state.login.isAuthenticated); if (!auth) { yield take(types.LOGIN.SUCCESS); } + const subscriptions = []; - yield put(messagesRequest({ rid })); + yield put(messagesRequest({ rid: room.rid })); const { open } = yield race({ messages: take(types.MESSAGES.SUCCESS), - open: take(types.ROOMS.OPEN) + open: take(types.ROOM.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); + + RocketChat.readMessages(room.rid); + subscriptions.push(RocketChat.subscribe('stream-room-messages', room.rid, false)); + subscriptions.push(RocketChat.subscribe('stream-notify-room', `${ room.rid }/typing`, false)); + const thread = yield fork(usersTyping, { rid: room.rid }); + yield take(types.ROOM.OPEN); cancel(thread); subscriptions.forEach(sub => sub.stop()); }; +const watchImTyping = function* watchImTyping({ status }) { + const auth = yield select(state => state.login.isAuthenticated); + if (!auth) { + yield take(types.LOGIN.SUCCESS); + } + + const room = yield select(state => state.room); + + if (!room) { + return; + } + yield RocketChat.emitTyping(room.rid, status); + + if (status) { + yield call(delay, 5000); + yield RocketChat.emitTyping(room.rid, false); + } +}; const root = function* root() { + yield takeLatest(types.ROOM.IM_TYPING, watchImTyping); yield takeLatest(types.LOGIN.SUCCESS, watchRoomsRequest); - yield takeEvery(types.ROOMS.OPEN, watchRoomOpen); + yield takeLatest(types.ROOM.OPEN, watchRoomOpen); }; export default root; diff --git a/app/views/RoomView.js b/app/views/RoomView.js index 8640dc95..883a48ac 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 { openRoom } from '../actions/rooms'; +import { openRoom } from '../actions/room'; import realm from '../lib/realm'; import RocketChat from '../lib/rocketchat'; import Message from '../containers/Message'; @@ -15,6 +15,7 @@ import KeyboardView from '../presentation/KeyboardView'; const ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1._id !== r2._id }); const styles = StyleSheet.create({ + typing: { fontWeight: 'bold', paddingHorizontal: 15, height: 25 }, container: { flex: 1, backgroundColor: '#fff' @@ -48,6 +49,7 @@ const styles = StyleSheet.create({ @connect( state => ({ + username: state.login.user.username, usersTyping: state.room.usersTyping, server: state.server.server, Site_Url: state.settings.Site_Url, @@ -56,7 +58,7 @@ const styles = StyleSheet.create({ }), dispatch => ({ actions: bindActionCreators(actions, dispatch), - openRoom: rid => dispatch(openRoom({ rid })) + openRoom: room => dispatch(openRoom(room)) }) ) export default class RoomView extends React.Component { @@ -101,7 +103,7 @@ export default class RoomView extends React.Component { realm.objectForPrimaryKey('subscriptions', this.sid).name }); this.timer = setTimeout(() => this.setState({ slow: true }), 5000); - this.props.openRoom(this.rid); + this.props.openRoom({ rid: this.rid }); this.data.addListener(this.updateState); } componentDidMount() { @@ -135,7 +137,12 @@ export default class RoomView extends React.Component { }); }); } - }; + } + + get usersTyping() { + const users = this.props.usersTyping.filter(_username => this.props.username !== _username); + return users.length ? `${ users.join(' ,') } ${ users.length > 1 ? 'are' : 'is' } typing` : null; + } updateState = () => { this.setState({ @@ -190,8 +197,7 @@ export default class RoomView extends React.Component { if (this.state.end) { return Start of conversation; } - }; - + } render() { const { height } = Dimensions.get('window'); return ( @@ -208,9 +214,9 @@ export default class RoomView extends React.Component { renderRow={item => this.renderItem({ item })} initialListSize={10} /> - {this.props.usersTyping ? {this.props.usersTyping.join(',')} : null} {this.renderFooter()} + {this.usersTyping} ); } From a4f2f4f529da2627f6156200db1c19213e05a1a2 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Tue, 21 Nov 2017 15:09:22 -0200 Subject: [PATCH 3/4] lint --- app/containers/MessageBox.js | 3 ++- app/views/RoomView.js | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/containers/MessageBox.js b/app/containers/MessageBox.js index ce8474e0..b52a4ead 100644 --- a/app/containers/MessageBox.js +++ b/app/containers/MessageBox.js @@ -49,7 +49,8 @@ export default class MessageBox extends React.Component { rid: PropTypes.string.isRequired, editRequest: PropTypes.func.isRequired, message: PropTypes.object, - editing: PropTypes.bool + editing: PropTypes.bool, + typing: PropTypes.bool } componentWillReceiveProps(nextProps) { diff --git a/app/views/RoomView.js b/app/views/RoomView.js index 30782461..8faf1124 100644 --- a/app/views/RoomView.js +++ b/app/views/RoomView.js @@ -71,7 +71,9 @@ export default class RoomView extends React.Component { server: PropTypes.string, Site_Url: PropTypes.string, Message_TimeFormat: PropTypes.string, - loading: PropTypes.bool + loading: PropTypes.bool, + usersTyping: PropTypes.array, + username: PropTypes.string }; constructor(props) { From aafa01c8129b751b3f89d51d55c37b34a9936999 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Tue, 21 Nov 2017 15:09:22 -0200 Subject: [PATCH 4/4] lint --- app/actions/actionsTypes.js | 2 +- app/actions/room.js | 8 ++++---- app/containers/MessageBox.js | 7 ++++--- app/lib/rocketchat.js | 4 ++-- app/sagas/rooms.js | 8 ++++---- app/views/RoomView.js | 4 +++- 6 files changed, 18 insertions(+), 15 deletions(-) diff --git a/app/actions/actionsTypes.js b/app/actions/actionsTypes.js index 4250f88b..effed182 100644 --- a/app/actions/actionsTypes.js +++ b/app/actions/actionsTypes.js @@ -27,7 +27,7 @@ export const FORGOT_PASSWORD = createRequestTypes('FORGOT_PASSWORD', [ 'INIT' ]); export const ROOMS = createRequestTypes('ROOMS'); -export const ROOM = createRequestTypes('ROOM', ['ADD_USER_TYPING', 'REMOVE_USER_TYPING', 'USER_TYPING', 'OPEN', 'IM_TYPING']); +export const ROOM = createRequestTypes('ROOM', ['ADD_USER_TYPING', 'REMOVE_USER_TYPING', 'SOMEONE_TYPING', 'OPEN', 'USER_TYPING']); export const APP = createRequestTypes('APP', ['READY', 'INIT']); export const MESSAGES = createRequestTypes('MESSAGES', [ ...defaultTypes, diff --git a/app/actions/room.js b/app/actions/room.js index c98648a0..14e346fc 100644 --- a/app/actions/room.js +++ b/app/actions/room.js @@ -8,9 +8,9 @@ export function removeUserTyping(username) { }; } -export function typing(data) { +export function someoneTyping(data) { return { - type: types.ROOM.USER_TYPING, + type: types.ROOM.SOMEONE_TYPING, ...data }; } @@ -29,9 +29,9 @@ export function openRoom(room) { }; } -export function imTyping(status = true) { +export function userTyping(status = true) { return { - type: types.ROOM.IM_TYPING, + type: types.ROOM.USER_TYPING, status }; } diff --git a/app/containers/MessageBox.js b/app/containers/MessageBox.js index ce8474e0..65740a51 100644 --- a/app/containers/MessageBox.js +++ b/app/containers/MessageBox.js @@ -4,7 +4,7 @@ import { View, TextInput, StyleSheet, SafeAreaView } from 'react-native'; import Icon from 'react-native-vector-icons/MaterialIcons'; import ImagePicker from 'react-native-image-picker'; import { connect } from 'react-redux'; -import { imTyping } from '../actions/room'; +import { userTyping } from '../actions/room'; import RocketChat from '../lib/rocketchat'; import { editRequest } from '../actions/messages'; @@ -41,7 +41,7 @@ const styles = StyleSheet.create({ editing: state.messages.editing }), dispatch => ({ editRequest: message => dispatch(editRequest(message)), - typing: status => dispatch(imTyping(status)) + typing: status => dispatch(userTyping(status)) })) export default class MessageBox extends React.Component { static propTypes = { @@ -49,7 +49,8 @@ export default class MessageBox extends React.Component { rid: PropTypes.string.isRequired, editRequest: PropTypes.func.isRequired, message: PropTypes.object, - editing: PropTypes.bool + editing: PropTypes.bool, + typing: PropTypes.bool } componentWillReceiveProps(nextProps) { diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js index 376a6279..b58f4984 100644 --- a/app/lib/rocketchat.js +++ b/app/lib/rocketchat.js @@ -8,7 +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 { someoneTyping } from '../actions/room'; import { disconnect, connectSuccess } from '../actions/connect'; export { Accounts } from 'react-native-meteor'; @@ -79,7 +79,7 @@ const RocketChat = { if (ev !== 'typing') { return; } - return reduxStore.dispatch(typing({ _rid, username: ddpMessage.fields.args[0], typing: ddpMessage.fields.args[1] })); + 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; diff --git a/app/sagas/rooms.js b/app/sagas/rooms.js index 6742577e..4a4f88ef 100644 --- a/app/sagas/rooms.js +++ b/app/sagas/rooms.js @@ -22,7 +22,7 @@ const watchRoomsRequest = function* watchRoomsRequest() { const cancelTyping = function* cancelTyping(username) { while (true) { const { typing, timeout } = yield race({ - typing: take(types.ROOM.USER_TYPING), + typing: take(types.ROOM.SOMEONE_TYPING), timeout: yield call(delay, 5000) }); if (timeout || (typing.username === username && !typing.typing)) { @@ -33,7 +33,7 @@ const cancelTyping = function* cancelTyping(username) { const usersTyping = function* usersTyping({ rid }) { while (true) { - const { _rid, username, typing } = yield take(types.ROOM.USER_TYPING); + const { _rid, username, typing } = yield take(types.ROOM.SOMEONE_TYPING); if (_rid === rid) { yield (typing ? put(addUserTyping(username)) : put(removeUserTyping(username))); if (typing) { @@ -70,7 +70,7 @@ const watchRoomOpen = function* watchRoomOpen({ room }) { subscriptions.forEach(sub => sub.stop()); }; -const watchImTyping = function* watchImTyping({ status }) { +const watchuserTyping = function* watchuserTyping({ status }) { const auth = yield select(state => state.login.isAuthenticated); if (!auth) { yield take(types.LOGIN.SUCCESS); @@ -90,7 +90,7 @@ const watchImTyping = function* watchImTyping({ status }) { }; const root = function* root() { - yield takeLatest(types.ROOM.IM_TYPING, watchImTyping); + yield takeLatest(types.ROOM.USER_TYPING, watchuserTyping); yield takeLatest(types.LOGIN.SUCCESS, watchRoomsRequest); yield takeLatest(types.ROOM.OPEN, watchRoomOpen); }; diff --git a/app/views/RoomView.js b/app/views/RoomView.js index 30782461..8faf1124 100644 --- a/app/views/RoomView.js +++ b/app/views/RoomView.js @@ -71,7 +71,9 @@ export default class RoomView extends React.Component { server: PropTypes.string, Site_Url: PropTypes.string, Message_TimeFormat: PropTypes.string, - loading: PropTypes.bool + loading: PropTypes.bool, + usersTyping: PropTypes.array, + username: PropTypes.string }; constructor(props) {