Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add realtime functionality to room #12

Merged
merged 3 commits into from
Mar 5, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions app/components/Message.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,17 @@ class Message extends Component {
}

render() {
const {fromUser, sending, failed} = this.props
const {fromUser, sending, failed, readBy} = this.props
const opacity = sending === true ? 0.4 : 1
const backgroundColor = failed === true ? 'rgba(255, 0, 0, 0.2)' : 'transparent'

let backgroundColor
if (failed === true) {
backgroundColor = 'rgba(255, 0, 0, 0.2)'
} else if (readBy === 0) {
backgroundColor = 'rgba(200, 200, 200, 0.2)'
} else {
backgroundColor = 'transparent'
}

return (
<TouchableNativeFeedback
Expand Down Expand Up @@ -105,6 +112,7 @@ Message.propTypes = {
text: PropTypes.string,
sent: PropTypes.string,
fromUser: PropTypes.object,
readBy: PropTypes.number,
sending: PropTypes.bool,
failed: PropTypes.bool,
dispatch: PropTypes.func,
Expand Down
8 changes: 8 additions & 0 deletions app/components/SendMessageField.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,19 @@ export default class SendMessageField extends Component {
this.sendMessage = this.sendMessage.bind(this)
this.focus = this.focus.bind(this)
this.blur = this.blur.bind(this)
this.onBlur = this.onBlur.bind(this)

this.state = {
height: 56
}
}

// triggers when text input is blured
// because the first one just will hide keyboard
onBlur() {
this.refs.textInput.blur()
}

focus() {
this.refs.textInput.focus()
}
Expand All @@ -46,6 +53,7 @@ export default class SendMessageField extends Component {
multiline
style={[s.textInput, {height: this.state.height > 90 ? 90 : Math.max(56, this.state.height)}]}
value={value}
onBlur={this.onBlur}
underlineColorAndroid={colors.androidGray}
onChange={(event) => {
this.setState({
Expand Down
110 changes: 15 additions & 95 deletions app/modules/app.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import {getItem} from '../utils/storage'
import {getCurrentUser} from './viewer'
import {getRooms, getSuggestedRooms, subscribeToRooms, updateRoomState} from './rooms'
import {appendMessages} from './messages'
import FayeGitter from '../../libs/react-native-gitter-faye'
import {DeviceEventEmitter, NetInfo, AppState} from 'react-native'
import {getRooms, getSuggestedRooms} from './rooms'
import {NetInfo, AppState} from 'react-native'
import {setupFayeEvents, setupFaye, onNetStatusChangeFaye} from './realtime'

/**
* Constants
Expand All @@ -12,7 +11,6 @@ import {DeviceEventEmitter, NetInfo, AppState} from 'react-native'
export const INITIALIZED = 'app/INITIALIZED'
export const CHANGE_NET_STATUS = 'app/CHANGE_NET_STATUS'
export const CHANGE_APP_STATE = 'app/CHANGE_APP_STATE'
export const FAYE_CONNECT = 'app/FAYE_CONNECT'

/**
* Action Creators
Expand All @@ -24,8 +22,8 @@ export function init() {
dispatch(setupFayeEvents())
try {
const token = await getItem('token')
const netStatus = await NetInfo.fetch()
dispatch({ type: INITIALIZED, token, netStatus })
// const netStatus = await NetInfo.fetch()
dispatch({ type: INITIALIZED, token })

// TODO: do things belowe only if the internet is awailible (netStatus)

Expand All @@ -45,33 +43,6 @@ export function init() {
}
}

function setupFaye() {
return async (dispatch, getState) => {
FayeGitter.setAccessToken(getState().auth.token)
FayeGitter.create()
FayeGitter.logger()
try {
const result = await FayeGitter.connect()
dispatch({type: FAYE_CONNECT, payload: result})
dispatch(subscribeToRooms())
} catch (err) {
console.log(err) // eslint-disable-line no-console
}
}
}

function onNetStatusChangeFaye(status) {
return async (dispatch, getState) => {
const {fayeConnected} = getState().app
if (!status && fayeConnected) {
dispatch({type: FAYE_CONNECT, payload: status})
}
if (status && !fayeConnected) {
dispatch(setupFaye())
}
}
}

function setupNetStatusListener() {
return dispatch => {
NetInfo.isConnected.addEventListener('change',
Expand All @@ -84,59 +55,12 @@ function setupNetStatusListener() {
}

function setupAppStatusListener() {
return dispatch => {
AppState.addEventListener('change',
status => dispatch({type: CHANGE_APP_STATE, payload: status})
);
}
}


function setupFayeEvents() {
return (dispatch, getState) => {
DeviceEventEmitter
.addListener('FayeGitter:onDisconnected', log => {
console.warn(log) // eslint-disable-line no-console
dispatch(setupFaye())
})

DeviceEventEmitter
.addListener('FayeGitter:onFailedToCreate', log => {
console.warn(log) // eslint-disable-line no-console
if (getState().app.netStatus) {
dispatch(setupFaye())
}
})
DeviceEventEmitter
.addListener('FayeGitter:Message', event => {
dispatch(parseEvent(event))
})
DeviceEventEmitter
.addListener('FayeGitter:SubscribtionFailed', log => console.warn(log)) // eslint-disable-line no-console
DeviceEventEmitter
.addListener('FayeGitter:Subscribed', log => console.log(log)) // eslint-disable-line no-console
DeviceEventEmitter
.addListener('FayeGitter:Unsubscribed', log => console.log(log)) // eslint-disable-line no-console
DeviceEventEmitter
.addListener('FayeGitter:log', log => console.log(log)) // eslint-disable-line no-console
}
}

function parseEvent(event) {
return (dispatch, getState) => {
const message = JSON.parse(event.json)
const {id} = getState().viewer.user
const {activeRoom} = getState().rooms
const roomsChannel = `/api/v1/user/${id}/rooms`
const chatMessages = `/api/v1/rooms/${activeRoom}/chatMessages`

if (event.channel.match(roomsChannel)) {
dispatch(updateRoomState(message))
}

if (event.channel.match(chatMessages)) {
dispatch(appendMessages(activeRoom, [message.model]))
}
AppState.addEventListener('change', status => {
// TODO: Update drawer rooms state and messages in current room
// if app status changes from backgrount to active
dispatch({type: CHANGE_APP_STATE, payload: status})
})
}
}

Expand All @@ -146,17 +70,18 @@ function parseEvent(event) {
*/

const initialState = {
online: null,
online: false,
appState: null,
fayeConnected: false
}

export default function app(state = initialState, action) {
switch (action.type) {
case INITIALIZED:
return {...state,
online: action.netStatus
}
// return {...state,
// online: action.netStatus
// }
return state

case CHANGE_NET_STATUS:
return {...state,
Expand All @@ -168,11 +93,6 @@ export default function app(state = initialState, action) {
appState: action.payload
}

case FAYE_CONNECT:
return {...state,
fayeConnected: action.payload
}

default:
return state
}
Expand Down
4 changes: 3 additions & 1 deletion app/modules/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import rooms from './rooms'
import viewer from './viewer'
import messages from './messages'
import settings from './settings'
import realtime from './realtime'


const rootReducer = combineReducers({
Expand All @@ -13,7 +14,8 @@ const rootReducer = combineReducers({
rooms,
viewer,
messages,
settings
settings,
realtime
})

export default rootReducer
41 changes: 23 additions & 18 deletions app/modules/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ export const ROOM_HAS_NO_MORE_MESSAGES = 'messages/ROOM_HAS_NO_MORE_MESSAGES'
export const ROOM_MESSAGES_RETURN_FROM_CACHE = 'messages/ROOM_MESSAGES_RETURN_FROM_CACHE'
export const ROOM_MESSAGES_APPEND = 'messages/ROOM_MESSAGES_APPEND'
export const PREPARE_LIST_VIEW = 'messages/PREPARE_LIST_VIEW'
export const SUBSCRIBE_TO_CHAT_MESSAGES = 'messages/SUBSCRIBE_TO_CHAT_MESSAGES'
export const SEND_MESSAGE = 'messages/SEND_MESSAGE'
export const SEND_MESSAGE_RECEIVED = 'messages/SEND_MESSAGE_RECEIVED'
export const SEND_MESSAGE_FAILED = 'messages/SEND_MESSAGE_FAILED'
Expand Down Expand Up @@ -153,16 +152,6 @@ export function prepareListView(roomId, ds) {
}
}

/**
* Subscribe for new room's messages => faye chat messages endpoint
*/

export function subscribeToChatMessages(roomId) {
return dispatch => {
FayeGitter.subscribe(`/api/v1/rooms/${roomId}/chatMessages`)
dispatch({type: SUBSCRIBE_TO_CHAT_MESSAGES, roomId})
}
}

/**
* Send messages
Expand All @@ -188,16 +177,16 @@ export function sendMessage(roomId, text) {
* Resend messages
*/

export function updateMessage(roomId, messageId, text, rowId) {
export function updateMessage(roomId, messageId, text) {
return async (dispatch, getState) => {
const {token} = getState().auth
dispatch({type: UPDATE_MESSAGE, roomId, messageId, rowId, text})
dispatch({type: UPDATE_MESSAGE, roomId, messageId, text})

try {
const payload = await Api.updateMessage(token, roomId, messageId, text)
dispatch({type: UPDATE_MESSAGE_OK, payload, roomId, messageId, rowId})
dispatch({type: UPDATE_MESSAGE_OK, payload, roomId, messageId})
} catch (error) {
dispatch({type: UPDATE_MESSAGE_OK, error, roomId, messageId, rowId})
dispatch({type: UPDATE_MESSAGE_OK, error, roomId, messageId})
}
}
}
Expand All @@ -222,6 +211,19 @@ export function resendMessage(roomId, rowId, text) {
}
}

/**
* Update message by faye event
*/

export function updateMessageRealtime(roomId, message) {
return dispatch => {
const {id} = message
dispatch({type: UPDATE_MESSAGE, roomId, messageId: id})
const payload = message
dispatch({type: UPDATE_MESSAGE_OK, payload, roomId, messageId: id})
}
}

/**
* Reducer
*/
Expand Down Expand Up @@ -496,16 +498,19 @@ export default function messages(state = initialState, action) {
}

case UPDATE_MESSAGE_OK: {
const {messageId, roomId, payload, rowId} = action
const {messageId, roomId, payload} = action

const rowIds = [].concat(state.listView[roomId].rowIds)
const data = [].concat(state.listView[roomId].data)
const rowId = _.findIndex(data, ['id', messageId])

data[rowId] = payload
const message = data[rowId]
const newMessage = _.merge({}, message, payload)
data[rowId] = newMessage

return {...state,
entities: {...state.entities,
[messageId]: payload
[messageId]: newMessage
},
listView: {...state.listView,
[roomId]: {
Expand Down
Loading