Skip to content

Commit

Permalink
Add room user list with infinitie scrolling
Browse files Browse the repository at this point in the history
  • Loading branch information
terrysahaidak committed Mar 27, 2016
1 parent ac1848f commit e9998c4
Showing 9 changed files with 181 additions and 20 deletions.
5 changes: 5 additions & 0 deletions app/api/gitter.js
Original file line number Diff line number Diff line change
@@ -111,10 +111,15 @@ export function getRoomUsers(token, roomId) {
return callApi(`rooms/${roomId}/users`, token)
}

export function getRoomUsersWithSkip(token, roomId, skip) {
return callApi(`rooms/${roomId}/users?skip=${skip}`, token)
}

export function searchRoomUsers(token, roomId, query) {
return callApi(`rooms/${roomId}/users?q=${query}`, token)
}


/**
* Private functions
*/
8 changes: 5 additions & 3 deletions app/components/RoomUsers/RoomUsersList.js
Original file line number Diff line number Diff line change
@@ -4,7 +4,6 @@ import React, {
ListView,
View
} from 'react-native'
import

import RoomUserItem from './RoomUserItem'

@@ -16,11 +15,12 @@ export default class RoomUsersList extends Component {
}

renderRow(rowData, rowId) {
const {onItemPress} = this.props
const {onItemPress, onUserItemPress} = this.props

return (
<RoomUserItem
onItemPress={onItemPress}
onUserItemPress={onUserItemPress}
{...rowData} />
)
}
@@ -35,6 +35,7 @@ export default class RoomUsersList extends Component {
return (
<ListView
ref="listView"
style={{flex: 1}}
dataSource={listViewData.dataSource}
onEndReached={this.props.onEndReached}
scrollRenderAheadDistance={1000}
@@ -49,5 +50,6 @@ export default class RoomUsersList extends Component {
RoomUsersList.propTypes = {
listViewData: PropTypes.object,
onItemPress: PropTypes.func,
onEndReached: PropTypes.func
onEndReached: PropTypes.func,
onUserItemPress: PropTypes.func
}
3 changes: 2 additions & 1 deletion app/modules/settings.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const initialState = {
limit: 30
limit: 30,
usersLimit: 30
}

export default function settings(state = initialState, action) {
110 changes: 110 additions & 0 deletions app/modules/users.js
Original file line number Diff line number Diff line change
@@ -17,6 +17,12 @@ export const CHAT_PRIVATELY_FAILED = 'users/CHAT_PRIVATELY_FAILED'
export const ROOM_USERS = 'users/ROOM_USERS'
export const ROOM_USERS_OK = 'users/ROOM_USERS_OK'
export const ROOM_USERS_FAILED = 'users/ROOM_USERS_FAILED'
export const PREPARE_LIST_VIEW = 'users/PREPARE_LIST_VIEW'
export const ROOM_USERS_WITH_SKIP = 'users/ROOM_USERS_WITH_SKIP'
export const ROOM_USERS_WITH_SKIP_OK = 'users/ROOM_USERS_WITH_SKIP_OK'
export const ROOM_USERS_WITH_SKIP_ERROR = 'users/ROOM_USERS_WITH_SKIP_ERROR'
export const NO_MORE_USERS_TO_LOAD = 'users/NO_MORE_USERS_TO_LOAD'


/**
* Actions
@@ -71,6 +77,41 @@ export function roomUsers(roomId) {
}
}

export function prepareListView(roomId, dataSource) {
return {
type: PREPARE_LIST_VIEW, dataSource, roomId
}
}

export function roomUsersWithSkip(roomId) {
return async (dispatch, getState) => {
if (getState().users.noMore[roomId] === true) {
return
}

const {token} = getState().auth
const {usersLimit} = getState().settings
const {ids} = getState().users.byRoom[roomId]
const skip = ids.length

dispatch({type: ROOM_USERS_WITH_SKIP, roomId, skip})
try {
const payload = await Api.getRoomUsersWithSkip(token, roomId, skip)

if (payload.length === 0) {
dispatch({type: NO_MORE_USERS_TO_LOAD, roomId})
} else {
dispatch({type: ROOM_USERS_WITH_SKIP_OK, roomId, payload})
if (payload.length < usersLimit) {
dispatch({type: NO_MORE_USERS_TO_LOAD, roomId})
}
}
} catch (error) {
dispatch({type: ROOM_USERS_WITH_SKIP_ERROR, error})
}
}
}


/**
* Reducer
@@ -87,6 +128,14 @@ const initialState = {
// entities: {}
// }
},
listView: {
// [roomId]: {
// dataSource,
// rowIds,
// data
// }
},
noMore: {},
error: false,
errors: {}
}
@@ -128,6 +177,67 @@ export default function users(state = initialState, action) {
}
}

case PREPARE_LIST_VIEW: {
const {roomId, dataSource} = action
const data = []
const rowIds = []
const {entities, ids} = state.byRoom[roomId]

for (let i = 0; i < ids.length && i < 30; i++) {
data.push(entities[ids[i]])
rowIds.push(data.length - 1)
}

return {...state,
listView: {...state.listView,
[roomId]: {
dataSource: dataSource.cloneWithRows(data, rowIds),
data,
rowIds
}
}
}
}

case ROOM_USERS_WITH_SKIP_OK: {
const {roomId, payload} = action
const listView = state.listView[roomId]
const byRoom = state.byRoom[roomId]
const data = [].concat(listView.data)
const rowIds = [].concat(listView.rowIds)

const {ids, entities} = normalize(payload)

for (let i = 0; i < ids.length && i < 30; i++) {
data.push(entities[ids[i]])
rowIds.push(data.length - 1)
}

return {...state,
byRoom: {...state.byRoom,
[roomId]: {
ids: byRoom.ids.concat(ids),
entities: Object.assign({}, byRoom.entities, entities)
}
},
listView: {...state.listView,
[roomId]: {
dataSource: state.listView[roomId].dataSource.cloneWithRows(data, rowIds),
data,
rowIds
}
}
}
}

case NO_MORE_USERS_TO_LOAD:
return {...state,
noMore: {...state.noMore,
[action.roomId]: true
}
}

case ROOM_USERS_WITH_SKIP_ERROR:
case ROOM_USERS_FAILED:
case USER_FAILED:
return {...state,
61 changes: 51 additions & 10 deletions app/screens/RoomUsersScreen.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import React, {
Component,
PropTypes,
View
View,
ListView
} from 'react-native'
import s from '../styles/screens/RoomUsers/RoomUsersScreenStyles'
import {connect} from 'react-redux'
import _ from 'lodash'

import * as Navigation from '../modules/navigation'
import {searchRoomUsers} from '../modules/search'
import {prepareListView, roomUsersWithSkip} from '../modules/users'

import CustomSearch from '../components/CustomSearch'
import RoomUsersSearchResult from '../components/RoomUsers/RoomUsersSearchResult'
import RoomUsersList from '../components/RoomUsers/RoomUsersList'

class RoomUsersScreen extends Component {
constructor(props) {
@@ -21,14 +24,26 @@ class RoomUsersScreen extends Component {
this.handleBackPress = this.handleBackPress.bind(this)
this.handleClearPress = this.handleClearPress.bind(this)
this.renderSearch = this.renderSearch.bind(this)
this.searchRequest = _.debounce(this.searchRequest.bind(this), 250)
this.searchRequest = _.debounce(this.searchRequest.bind(this), 400)
this.renderSearchResult = this.renderSearchResult.bind(this)
this.prepareDataSources = this.prepareDataSources.bind(this)
this.handleUserItemPress = this.handleUserItemPress.bind(this)
this.onEndReached = this.onEndReached.bind(this)

this.state = {
value: ''
}
}

componentDidMount() {
this.prepareDataSources()
}

onEndReached() {
const {dispatch, route: {roomId}} = this.props
dispatch(roomUsersWithSkip(roomId))
}

handleChange(event) {
this.setState({value: event.nativeEvent.text})
this.searchRequest(event.nativeEvent.text)
@@ -43,6 +58,19 @@ class RoomUsersScreen extends Component {
dispatch(Navigation.goBack())
}

handleUserItemPress(id, username) {
const {dispatch} = this.props
dispatch(Navigation.goTo({name: 'user', userId: id, username}))
}

prepareDataSources() {
const {listViewData, route: {roomId}, dispatch} = this.props
if (!listViewData[roomId]) {
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => !_.isEqual(r1, r2)})
dispatch(prepareListView(roomId, ds.cloneWithRows([])))
}
}

searchRequest(query) {
const {dispatch, route: {roomId}} = this.props

@@ -67,19 +95,30 @@ class RoomUsersScreen extends Component {
renderSearchResult() {
const {roomUsersResult, isLoading} = this.props
return (
<View style={s.bottomContainer}>
<RoomUsersSearchResult
isLoading={isLoading}
resultItems={roomUsersResult} />
</View>
<RoomUsersSearchResult
isLoading={isLoading}
resultItems={roomUsersResult}
onUserItemPress={this.handleUserItemPress.bind(this)} />
)
}

renderUserList() {
const {listViewData, route: {roomId}} = this.props
return (
<RoomUsersList
listViewData={listViewData[roomId]}
onEndReached={this.onEndReached.bind(this)}
onUserItemPress={this.handleUserItemPress.bind(this)} />
)
}

render() {
return (
<View style={s.container}>
{this.renderSearch()}
{!!this.state.value && this.renderSearchResult()}
<View style={s.bottomContainer}>
{!!this.state.value ? this.renderSearchResult() : this.renderUserList()}
</View>
</View>
)
}
@@ -89,14 +128,16 @@ RoomUsersScreen.propTypes = {
dispatch: PropTypes.func,
roomUsersResult: PropTypes.array,
route: PropTypes.object,
isLoading: PropTypes.bool
isLoading: PropTypes.bool,
listViewData: PropTypes.object
}

function mapStateToProps(state) {
const {roomUsersResult, isLoadingRoomUser} = state.search
return {
roomUsersResult,
isLoading: isLoadingRoomUser
isLoading: isLoadingRoomUser,
listViewData: state.users.listView
}
}

4 changes: 3 additions & 1 deletion app/screens/index.js
Original file line number Diff line number Diff line change
@@ -96,6 +96,8 @@ class App extends Component {
setTimeout(() => {
if (current.name === 'room' && route.name === 'room') {
dispatch(Navigation.goAndReplace(route))
} else if (route.name === 'room') {
dispatch(Navigation.resetWithStack([{name: 'home'}, route]))
} else {
dispatch(Navigation.goTo(route))
}
@@ -179,7 +181,7 @@ class App extends Component {
const {navigation} = this.props
// const initialRoute = {name: 'launch'}
// const initialRoute = {name: 'room', roomId: '56a41e0fe610378809bde160'}
const drawerLockMode = ['launch', 'login', 'loginByToken', 'user'].indexOf(navigation.current.name) === -1
const drawerLockMode = ['launch', 'login', 'loginByToken'].indexOf(navigation.current.name) === -1
? 'unlocked'
: 'locked-closed'

2 changes: 1 addition & 1 deletion app/styles/components/CustomSearchStyles.js
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ const styles = StyleSheet.create({
alignItems: 'center',
margin: 8,
backgroundColor: 'white',
elevation: 4,
elevation: 2,
borderRadius: 2
},
button: {
4 changes: 3 additions & 1 deletion app/styles/screens/RoomUsers/RoomUsersScreenStyles.js
Original file line number Diff line number Diff line change
@@ -7,7 +7,9 @@ const styles = StyleSheet.create({
bottomContainer: {
flex: 1,
borderRadius: 2,
elevation: 4
elevation: 2,
backgroundColor: 'white',
margin: 8
}
})

4 changes: 1 addition & 3 deletions app/styles/screens/RoomUsers/RoomUsersSearchResultStyles.js
Original file line number Diff line number Diff line change
@@ -2,9 +2,7 @@ import {StyleSheet} from 'react-native'

const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'white',
elevation: 4
flex: 1
},
loading: {
height: 150

0 comments on commit e9998c4

Please sign in to comment.