RN-268 Fixed teams list not always rerendering when team member changes (#970)

* RN-268 Fixed teams list not always rerendering when team member changes

* Removed unused prop from ChannelDrawer

* RN-268 Passed fewer props into TeamsListItem
This commit is contained in:
Harrison Healey
2017-09-29 11:46:03 -04:00
committed by enahum
parent e3761fc529
commit c93f04a708
4 changed files with 211 additions and 139 deletions

View File

@@ -5,7 +5,7 @@ import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {getCurrentUrl} from 'mattermost-redux/selectors/entities/general';
import {getCurrentTeamId, getJoinableTeams, getTeamMemberships} from 'mattermost-redux/selectors/entities/teams';
import {getCurrentTeamId, getJoinableTeams} from 'mattermost-redux/selectors/entities/teams';
import {handleTeamChange} from 'app/actions/views/select_team';
import {getTheme} from 'app/selectors/preferences';
@@ -16,13 +16,11 @@ import TeamsList from './teams_list';
function mapStateToProps(state, ownProps) {
return {
canCreateTeams: false,
joinableTeams: getJoinableTeams(state),
currentTeamId: getCurrentTeamId(state),
currentUrl: removeProtocol(getCurrentUrl(state)),
teams: getMySortedTeams(state),
theme: getTheme(state),
myTeamMembers: getTeamMemberships(state),
...ownProps
};
}

View File

@@ -11,26 +11,24 @@ import {
View
} from 'react-native';
import {injectIntl, intlShape} from 'react-intl';
import IonIcon from 'react-native-vector-icons/Ionicons';
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
import Badge from 'app/components/badge';
import FormattedText from 'app/components/formatted_text';
import {preventDoubleTap} from 'app/utils/tap';
import {wrapWithPreventDoubleTap} from 'app/utils/tap';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
import TeamsListItem from './teams_list_item';
class TeamsList extends PureComponent {
static propTypes = {
actions: PropTypes.shape({
handleTeamChange: PropTypes.func.isRequired
}).isRequired,
canCreateTeams: PropTypes.bool.isRequired,
closeChannelDrawer: PropTypes.func.isRequired,
currentTeamId: PropTypes.string.isRequired,
currentUrl: PropTypes.string.isRequired,
intl: intlShape.isRequired,
joinableTeams: PropTypes.object.isRequired,
myTeamMembers: PropTypes.object.isRequired,
navigator: PropTypes.object.isRequired,
teams: PropTypes.array.isRequired,
theme: PropTypes.object.isRequired
@@ -55,7 +53,7 @@ class TeamsList extends PureComponent {
});
};
goToSelectTeam = () => {
goToSelectTeam = wrapWithPreventDoubleTap(() => {
const {currentUrl, intl, navigator, theme} = this.props;
navigator.showModal({
@@ -81,79 +79,18 @@ class TeamsList extends PureComponent {
theme
}
});
};
});
keyExtractor = (team) => {
return team.id;
}
renderItem = ({item}) => {
const {currentTeamId, currentUrl, myTeamMembers, theme} = this.props;
const styles = getStyleSheet(theme);
let current;
let badge;
if (item.id === currentTeamId) {
current = (
<View style={styles.checkmarkContainer}>
<IonIcon
name='md-checkmark'
style={styles.checkmark}
/>
</View>
);
}
const member = myTeamMembers[item.id];
let badgeCount = 0;
if (member.mention_count) {
badgeCount = member.mention_count;
} else if (member.msg_count) {
badgeCount = -1;
}
if (badgeCount) {
badge = (
<Badge
style={styles.badge}
countStyle={styles.mention}
count={badgeCount}
minHeight={20}
minWidth={20}
/>
);
}
return (
<View style={styles.teamWrapper}>
<TouchableHighlight
underlayColor={changeOpacity(theme.sidebarTextHoverBg, 0.5)}
onPress={() => preventDoubleTap(this.selectTeam, this, item)}
>
<View style={styles.teamContainer}>
<View style={styles.teamIconContainer}>
<Text style={styles.teamIcon}>
{item.display_name.substr(0, 2).toUpperCase()}
</Text>
</View>
<View style={styles.teamNameContainer}>
<Text
numberOfLines={1}
ellipsizeMode='tail'
style={styles.teamName}
>
{item.display_name}
</Text>
<Text
numberOfLines={1}
ellipsizeMode='tail'
style={styles.teamUrl}
>
{`${currentUrl}/${item.name}`}
</Text>
</View>
{current}
</View>
</TouchableHighlight>
{badge}
</View>
<TeamsListItem
selectTeam={this.selectTeam}
team={item}
/>
);
};
@@ -166,7 +103,7 @@ class TeamsList extends PureComponent {
moreAction = (
<TouchableHighlight
style={styles.moreActionContainer}
onPress={() => preventDoubleTap(this.goToSelectTeam)}
onPress={this.goToSelectTeam}
underlayColor={changeOpacity(theme.sidebarHeaderBg, 0.5)}
>
<Text
@@ -193,7 +130,7 @@ class TeamsList extends PureComponent {
<FlatList
data={teams}
renderItem={this.renderItem}
keyExtractor={(item) => item.id}
keyExtractor={this.keyExtractor}
viewabilityConfig={{
viewAreaCoveragePercentThreshold: 3,
waitForInteraction: false
@@ -256,64 +193,6 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
moreAction: {
color: theme.sidebarHeaderTextColor,
fontSize: 30
},
teamWrapper: {
marginTop: 20
},
teamContainer: {
alignItems: 'center',
flex: 1,
flexDirection: 'row',
marginHorizontal: 16
},
teamIconContainer: {
alignItems: 'center',
backgroundColor: theme.sidebarText,
borderRadius: 2,
height: 40,
justifyContent: 'center',
width: 40
},
teamIcon: {
color: theme.sidebarBg,
fontFamily: 'OpenSans',
fontSize: 18,
fontWeight: '600'
},
teamNameContainer: {
flex: 1,
flexDirection: 'column',
marginLeft: 10
},
teamName: {
color: theme.sidebarText,
fontSize: 18
},
teamUrl: {
color: changeOpacity(theme.sidebarText, 0.5),
fontSize: 12
},
checkmarkContainer: {
alignItems: 'flex-end'
},
checkmark: {
color: theme.sidebarText,
fontSize: 20
},
badge: {
backgroundColor: theme.mentionBj,
borderColor: theme.sidebarHeaderBg,
borderRadius: 10,
borderWidth: 1,
flexDirection: 'row',
padding: 3,
position: 'absolute',
left: 45,
top: -7.5
},
mention: {
color: theme.mentionColor,
fontSize: 10
}
};
});

View File

@@ -0,0 +1,24 @@
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import {connect} from 'react-redux';
import {getCurrentUrl} from 'mattermost-redux/selectors/entities/general';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {getCurrentTeamId, getTeamMemberships} from 'mattermost-redux/selectors/entities/teams';
import {removeProtocol} from 'app/utils/url';
import TeamsListItem from './teams_list_item.js';
function mapStateToProps(state, ownProps) {
return {
currentTeamId: getCurrentTeamId(state),
currentUrl: removeProtocol(getCurrentUrl(state)),
teamMember: getTeamMemberships(state)[ownProps.team.id],
theme: getTheme(state),
...ownProps
};
}
export default connect(mapStateToProps)(TeamsListItem);

View File

@@ -0,0 +1,171 @@
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import React from 'react';
import PropTypes from 'prop-types';
import {
Text,
TouchableHighlight,
View
} from 'react-native';
import IonIcon from 'react-native-vector-icons/Ionicons';
import Badge from 'app/components/badge';
import {wrapWithPreventDoubleTap} from 'app/utils/tap';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
export default class TeamsListItem extends React.PureComponent {
static propTypes = {
currentTeamId: PropTypes.string.isRequired,
currentUrl: PropTypes.string.isRequired,
selectTeam: PropTypes.func.isRequired,
team: PropTypes.object.isRequired,
teamMember: PropTypes.object.isRequired,
theme: PropTypes.object.isRequired
};
selectTeam = wrapWithPreventDoubleTap(() => {
this.props.selectTeam(this.props.team);
});
render() {
const {
currentTeamId,
currentUrl,
team,
teamMember,
theme
} = this.props;
const styles = getStyleSheet(theme);
let current;
let badge;
if (team.id === currentTeamId) {
current = (
<View style={styles.checkmarkContainer}>
<IonIcon
name='md-checkmark'
style={styles.checkmark}
/>
</View>
);
}
let badgeCount = 0;
if (teamMember.mention_count) {
badgeCount = teamMember.mention_count;
} else if (teamMember.msg_count) {
badgeCount = -1;
}
if (badgeCount) {
badge = (
<Badge
style={styles.badge}
countStyle={styles.mention}
count={badgeCount}
minHeight={20}
minWidth={20}
/>
);
}
return (
<View style={styles.teamWrapper}>
<TouchableHighlight
underlayColor={changeOpacity(theme.sidebarTextHoverBg, 0.5)}
onPress={this.selectTeam}
>
<View style={styles.teamContainer}>
<View style={styles.teamIconContainer}>
<Text style={styles.teamIcon}>
{team.display_name.substr(0, 2).toUpperCase()}
</Text>
</View>
<View style={styles.teamNameContainer}>
<Text
numberOfLines={1}
ellipsizeMode='tail'
style={styles.teamName}
>
{team.display_name}
</Text>
<Text
numberOfLines={1}
ellipsizeMode='tail'
style={styles.teamUrl}
>
{`${currentUrl}/${team.name}`}
</Text>
</View>
{current}
</View>
</TouchableHighlight>
{badge}
</View>
);
}
}
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
return {
teamWrapper: {
marginTop: 20
},
teamContainer: {
alignItems: 'center',
flex: 1,
flexDirection: 'row',
marginHorizontal: 16
},
teamIconContainer: {
alignItems: 'center',
backgroundColor: theme.sidebarText,
borderRadius: 2,
height: 40,
justifyContent: 'center',
width: 40
},
teamIcon: {
color: theme.sidebarBg,
fontFamily: 'OpenSans',
fontSize: 18,
fontWeight: '600'
},
teamNameContainer: {
flex: 1,
flexDirection: 'column',
marginLeft: 10
},
teamName: {
color: theme.sidebarText,
fontSize: 18
},
teamUrl: {
color: changeOpacity(theme.sidebarText, 0.5),
fontSize: 12
},
checkmarkContainer: {
alignItems: 'flex-end'
},
checkmark: {
color: theme.sidebarText,
fontSize: 20
},
badge: {
backgroundColor: theme.mentionBj,
borderColor: theme.sidebarHeaderBg,
borderRadius: 10,
borderWidth: 1,
flexDirection: 'row',
padding: 3,
position: 'absolute',
left: 45,
top: -7.5
},
mention: {
color: theme.mentionColor,
fontSize: 10
}
};
});