From ea3b2cabd42388b859e61c782cff67512cc634e7 Mon Sep 17 00:00:00 2001 From: Chris Duarte Date: Wed, 1 Feb 2017 06:28:01 -0800 Subject: [PATCH] Channel members fix (#209) * Channel members fix * Review feedback --- app/components/member_list/index.js | 48 ++++++++++++++++-- app/components/member_list/member_list_row.js | 3 ++ app/scenes/channel_members/channel_members.js | 20 ++++---- .../channel_members_container.js | 3 +- assets/base/i18n/en.json | 1 + assets/base/images/profile.jpg | Bin 0 -> 4705 bytes service/selectors/entities/users.js | 17 ++----- 7 files changed, 62 insertions(+), 30 deletions(-) create mode 100644 assets/base/images/profile.jpg 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 0000000000000000000000000000000000000000..3fca233d3174af464a9d2aade7e302b43745e874 GIT binary patch literal 4705 zcmeHKc~nzp7QZhGVXF{`3KmFwECmXLMOF=AU#u)5j0>|ggoGeuAqf!V=%ln`3zZI7 zQKUKoYC&4G>HuTy97Uu+=g^`>1&az4ETW7MaHGV`7ebBN_MDkHXZ{-QJNdrfyWhR{ z-QVwf@0>51KFuJQuZ@b00t~|-23?>TGGYkia!C-ADUx|`(#2^!53X3qRB$9rFAq;9 ztYIr89Bvj*&Pd~BWQth${j=wAMrJw--{>3X87B$n2{L20NqHN##V2#OWpM-3adt3c zjUq@Pln8lp4nrZ#7RiDXEPTFr5W-qF6KBka$g^1Zrnp2#xLC?#_Ig+Unt4D5gkuL92D$|RwQiRmrJyG55hFAfRm1JJ;H8M)F8sW zT7Q}jk7;p+4$o<^G&w08aWhAlk?|Z(dk%92QXz^#%)vpJor`*yH!TxWo+xDlnsM~A z0U0DgG=u>IVj&X3!4JG35RHD|4&LB_?%-GQwY6;^9>ge@i$)0)fD|$z0|X!kH{{Jl z)p=;
ya*-)U>ALSv_>nh{>c+a~q(=Y(?O^s$6?J?y;0Cky0^J9%hqpk%Y4gj1N z>GQ?a009q>zfA9QI0Rt!4nT98-j}uypygcvt9}Vb%9(!-5sd^sA3#+H@XBTY%N_vZ zRGnsMO}h?BV{l2Dh1M#d&6pvfEv7?DUo{UZWs zbvs%I5|wPR(ks-!ay^HF=g_?0Dz7oL4?EeqH0jc$gHM{Y$jErvax3c+8QhH2nR@=;YX%wFP!M8F7WTSycwFN~0{B~B%gS9%#(gs!J>ax8K0 zw+v}vo3^w{QN;nHc=L!Z*UAubWWSy-rHQrU~~>y1+HzQCSJ}?A-Km z++^o&WVF^|UC-~1i&_Pmga;wXpiIvm_`pFMr$+(AgzX-K%aE9Oe0 zmj+lHpY%|wpMThVWgGwEG*#2F4*trHVRpwV>?2(>2K#o_PUqHBc#hu;vO8$lK+khs z>JrbDHg;B-R0nWITY9DJ6QdzEqdGRW^dezbRbg#FimIbm^~bWgF=GACHuUwadBxj# zj%VLoMt7NEm4d|IvrD|X&9y=vfGh1$xI^>KH;OM??KyqsJPo^*xwn|hv;BOqg6{Qz z)eW0ps%LzyQ4Dx{n_^p6XWry}~9) zJ^Bz=dSy&@tGrZ4N>V1ieg(`G{3m?{ry?yL{othZYiyLOm-HXaf38X20&zMI1;p=&n#79MnMcK^qO^*UXi^*ize6e8a<%=H`j!jn;hIWk8Gv^}=q>SJ#Z<=R#j+DgN&DN<7ko3c`O-f%Vj#O_m;dWY0jwOPndk{X_HP-Wl#=*RNY zJHnJ}ZtLqUe3mm=fotx@@So0Yy;WIlB+z}UHN&sD)DD#22$}1oUsVPKz5ERmznHvx z|B|ry=^c8x{cIw6Ru|wi7fxzf5ixb)=%FH7ZUCc)VtoFSrEiAXhE9YS=|2nf!V&7- kT9YjjLRR-sa)-toTP`y`;JME2uB-2IQm#UsnQ8j}1zTfBMgRZ+ literal 0 HcmV?d00001 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;