diff --git a/app/components/member_list/index.js b/app/components/member_list/index.js index cf3df6ae1..0d8d90e23 100644 --- a/app/components/member_list/index.js +++ b/app/components/member_list/index.js @@ -12,12 +12,17 @@ import { import Client from 'service/client'; import {displayUsername} from 'service/utils/user_utils'; +import FormattedText from 'app/components/formatted_text'; + import MemberListRow from './member_list_row'; const style = StyleSheet.create({ listView: { flex: 1 }, + loadingText: { + opacity: 0.6 + }, sectionContainer: { backgroundColor: '#eaeaea', paddingLeft: 10, @@ -40,13 +45,20 @@ export default class MemberList extends PureComponent { onListEndReached: PropTypes.func, onListEndReachedThreshold: PropTypes.number, sections: PropTypes.bool, - preferences: PropTypes.object + preferences: PropTypes.object, + loadingMembers: PropTypes.bool, + listPageSize: PropTypes.number, + listInitialSize: PropTypes.number, + listScrollRenderAheadDistance: PropTypes.number } static defaultProps = { onListEndReached: () => true, - onListEndThreshold: 10, - sections: true + onListEndThreshold: 50, + sections: true, + listPageSize: 10, + listInitialSize: 10, + listScrollRenderAheadDistance: 200 } constructor(props) { @@ -73,7 +85,7 @@ export default class MemberList extends PureComponent { createSections = (data) => { const sections = {}; data.forEach((d) => { - const name = displayUsername(d, this.props.preferences); + const name = d.username; const sectionKey = name.substring(0, 1).toUpperCase(); if (!sections[sectionKey]) { @@ -87,6 +99,10 @@ export default class MemberList extends PureComponent { } renderSectionHeader = (sectionData, sectionId) => { + if (!this.props.sections) { + return null; + } + return ( {sectionId} @@ -111,7 +127,7 @@ export default class MemberList extends PureComponent { ); } - renderSeparator(sectionId, rowId) { + renderSeparator = (sectionId, rowId) => { return ( { + if (!this.props.loadingMembers) { + return null; + } + + const backgroundColor = this.props.members.length > 0 ? '#fff' : '#0000'; + + return ( + + + + ); + } + render() { return ( ); } diff --git a/app/components/member_list/member_list_row.js b/app/components/member_list/member_list_row.js index ad9ec11e3..93dee9131 100644 --- a/app/components/member_list/member_list_row.js +++ b/app/components/member_list/member_list_row.js @@ -8,6 +8,8 @@ import { } from 'react-native'; import Icon from 'react-native-vector-icons/FontAwesome'; +const placeholder = require('assets/images/profile.jpg'); + const style = StyleSheet.create({ avatar: { height: 40, @@ -96,6 +98,7 @@ function MemberListRow(props) { {StatusComponent} diff --git a/app/scenes/channel_members/channel_members.js b/app/scenes/channel_members/channel_members.js index 2649de0b7..47d53fb20 100644 --- a/app/scenes/channel_members/channel_members.js +++ b/app/scenes/channel_members/channel_members.js @@ -3,6 +3,7 @@ import React, {PropTypes, PureComponent} from 'react'; import { + InteractionManager, StyleSheet, View } from 'react-native'; @@ -19,29 +20,25 @@ export default class ChannelMembers extends PureComponent { static propTypes = { currentChannel: PropTypes.object, currentChannelMembers: PropTypes.array.isRequired, + currentChannelMemberCount: PropTypes.number.isRequired, currentTeam: PropTypes.object, preferences: PropTypes.object, + requestStatus: PropTypes.string, actions: PropTypes.shape({ getProfilesInChannel: PropTypes.func.isRequired }) } - state = { - currentChannelMemberCount: 0 - } - componentDidMount() { - this.props.actions.getProfilesInChannel(this.props.currentTeam.id, this.props.currentChannel.id, 0); - } - - componentWillReceiveProps(nextProps) { - this.setState({ - currentChannelMemberCount: this.state.currentChannelMemberCount + nextProps.currentChannelMembers.length + InteractionManager.runAfterInteractions(() => { + this.props.actions.getProfilesInChannel(this.props.currentTeam.id, this.props.currentChannel.id, 0); }); } loadMoreMembers = () => { - this.props.actions.getProfilesInChannel(this.props.currentTeam.id, this.props.currentChannel.id, this.state.currentChannelMemberCount); + if (this.props.requestStatus !== 'started' && this.props.currentChannelMembers.length < this.props.currentChannelMemberCount) { + this.props.actions.getProfilesInChannel(this.props.currentTeam.id, this.props.currentChannel.id, this.props.currentChannelMembers.length); + } } render() { @@ -51,6 +48,7 @@ export default class ChannelMembers extends PureComponent { members={this.props.currentChannelMembers} onListEndReached={this.loadMoreMembers} preferences={this.props.preferences} + loadingMembers={this.props.requestStatus === 'started'} /> ); diff --git a/app/scenes/channel_members/channel_members_container.js b/app/scenes/channel_members/channel_members_container.js index 6c8030d79..5eb8eb429 100644 --- a/app/scenes/channel_members/channel_members_container.js +++ b/app/scenes/channel_members/channel_members_container.js @@ -20,7 +20,8 @@ function mapStateToProps(state) { currentChannelMembers: getProfilesInCurrentChannel(state), currentChannelMemberCount, currentTeam: getCurrentTeam(state), - preferences: getMyPreferences(state) + preferences: getMyPreferences(state), + requestStatus: state.requests.users.getProfilesInChannel.status }; } diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json index 3d12a51c1..908fa1f82 100644 --- a/assets/base/i18n/en.json +++ b/assets/base/i18n/en.json @@ -1492,6 +1492,7 @@ "mobile.channel_list.privateChannel": "Private Channel", "mobile.channel_list.publicChannel": "Public Channel", "mobile.components.channels_list_view.yourChannels": "Your channels:", + "mobile.components.member_list.loading_members": "Loading Members...", "mobile.components.select_server_view.enterServerUrl": "Enter Server URL", "mobile.components.select_server_view.continue": "Continue", "mobile.components.select_server_view.siteUrlPlaceholder": "https://mattermost.example.com", diff --git a/assets/base/images/profile.jpg b/assets/base/images/profile.jpg new file mode 100644 index 000000000..3fca233d3 Binary files /dev/null and b/assets/base/images/profile.jpg differ diff --git a/service/selectors/entities/users.js b/service/selectors/entities/users.js index 1922cfd81..db5d56d06 100644 --- a/service/selectors/entities/users.js +++ b/service/selectors/entities/users.js @@ -5,7 +5,6 @@ import {createSelector} from 'reselect'; import {getCurrentChannelId} from './channels'; import {getMyPreferences} from './preferences'; -import {displayUsername} from 'service/utils/user_utils'; export function getCurrentUserId(state) { return state.entities.users.currentId; @@ -48,7 +47,7 @@ export const getProfilesInCurrentChannel = createSelector( getUserStatuses, getProfileSetInCurrentChannel, getMyPreferences, - (profiles, statuses, currentChannelProfileSet, preferences) => { + (profiles, statuses, currentChannelProfileSet) => { const currentProfiles = []; if (typeof currentChannelProfileSet === 'undefined') { return currentProfiles; @@ -61,19 +60,11 @@ export const getProfilesInCurrentChannel = createSelector( }); }); - // We could get rid of this if server side sorting is a possibility const sortedCurrentProfiles = currentProfiles.sort((a, b) => { - const nameA = displayUsername(a, preferences); - const nameB = displayUsername(b, preferences); + const nameA = a.username; + const nameB = b.username; - if (nameA.toUpperCase() < nameB.toUpperCase()) { - return -1; - } - if (nameA.toUpperCase() > nameB.toUpperCase()) { - return 1; - } - - return 0; + return nameA.localeCompare(nameB); }); return sortedCurrentProfiles;