[MM 13161] Mobile: Add paging to Select Teams screen (#2468)

* Use updated getJoinableTeams function from mattermost-redux

* Use onEndReached for infinite scrolling of paginated teams

* Add onRefresh

* Update Settings screen to expect joinableTeams as array

* Fix linter issues

* Test that page is updated after getTeams() call

* Revert package-lock.json changes

* Update mattermost-redux dependency

* Update mattermost-redux dependency

* Only call getTeams if more can be loaded and only after a previous
call has completed

* Update snapshot due to  prop

* Update CustomList to support refreshing as well as option to not
render the separator

* Use CustomList in SelectTeam
This commit is contained in:
Miguel Alatzar 2019-02-20 09:12:36 -08:00 committed by GitHub
parent e82146d52f
commit c72ea6a281
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 2994 additions and 2966 deletions

View file

@ -21,6 +21,8 @@ export default class CustomList extends PureComponent {
loading: PropTypes.bool,
loadingComponent: PropTypes.element,
noResults: PropTypes.element,
refreshing: PropTypes.bool,
onRefresh: PropTypes.func,
onLoadMore: PropTypes.func,
onRowPress: PropTypes.func,
onRowSelect: PropTypes.func,
@ -30,11 +32,13 @@ export default class CustomList extends PureComponent {
]).isRequired,
selectable: PropTypes.bool,
theme: PropTypes.object.isRequired,
shouldRenderSeparator: PropTypes.bool,
};
static defaultProps = {
listType: FLATLIST,
showNoResults: true,
shouldRenderSeparator: true,
};
constructor(props) {
@ -104,7 +108,7 @@ export default class CustomList extends PureComponent {
};
renderFlatList = () => {
const {data, extraData, theme} = this.props;
const {data, extraData, theme, onRefresh, refreshing} = this.props;
const style = getStyleFromTheme(theme);
return (
@ -122,6 +126,8 @@ export default class CustomList extends PureComponent {
maxToRenderPerBatch={INITIAL_BATCH_TO_RENDER + 1}
onLayout={this.handleLayout}
onScroll={this.handleScroll}
onRefresh={onRefresh}
refreshing={refreshing}
ref={this.setListRef}
removeClippedSubviews={true}
renderItem={this.renderItem}
@ -185,6 +191,10 @@ export default class CustomList extends PureComponent {
};
renderSeparator = () => {
if (!this.props.shouldRenderSeparator) {
return null;
}
const style = getStyleFromTheme(this.props.theme);
return (

View file

@ -75,6 +75,7 @@ exports[`MoreChannels should match snapshot 1`] = `
onLoadMore={[Function]}
onRowPress={[Function]}
renderItem={[Function]}
shouldRenderSeparator={true}
showNoResults={true}
theme={
Object {

View file

@ -94,33 +94,52 @@ exports[`SelectTeam should match snapshot for teams 1`] = `
}
/>
</View>
<FlatList
data={
Array [
Object {
"id": "kemjcpu9bi877yegqjs18ndp4r",
"invite_id": "ojsnudhqzbfzpk6e4n6ip1hwae",
"name": "test",
},
]
<CustomList
data={Array []}
extraData={false}
listType="flat"
loading={false}
loadingComponent={
<Loading
color="grey"
size="large"
style={Object {}}
/>
}
disableVirtualization={false}
horizontal={false}
initialNumToRender={10}
keyExtractor={[Function]}
maxToRenderPerBatch={10}
numColumns={1}
onEndReachedThreshold={2}
onLoadMore={[Function]}
onRefresh={[Function]}
refreshing={false}
renderItem={[Function]}
scrollEventThrottle={50}
updateCellsBatchingPeriod={50}
viewabilityConfig={
shouldRenderSeparator={false}
showNoResults={true}
theme={
Object {
"viewAreaCoveragePercentThreshold": 3,
"waitForInteraction": false,
"awayIndicator": "#ffbc42",
"buttonBg": "#166de0",
"buttonColor": "#ffffff",
"centerChannelBg": "#ffffff",
"centerChannelColor": "#3d3c40",
"codeTheme": "github",
"dndIndicator": "#f74343",
"errorTextColor": "#fd5960",
"linkColor": "#2389d7",
"mentionBj": "#ffffff",
"mentionColor": "#145dbf",
"mentionHighlightBg": "#ffe577",
"mentionHighlightLink": "#166de0",
"newMessageSeparator": "#ff8800",
"onlineIndicator": "#06d6a0",
"sidebarBg": "#145dbf",
"sidebarHeaderBg": "#1153ab",
"sidebarHeaderTextColor": "#ffffff",
"sidebarText": "#ffffff",
"sidebarTextActiveBorder": "#579eff",
"sidebarTextActiveColor": "#ffffff",
"sidebarTextHoverBg": "#4578bf",
"sidebarUnreadText": "#ffffff",
"type": "Mattermost",
}
}
windowSize={21}
/>
</View>
`;

View file

@ -10,24 +10,12 @@ import {getTeams, joinTeam} from 'mattermost-redux/actions/teams';
import {logout} from 'mattermost-redux/actions/users';
import {getJoinableTeams} from 'mattermost-redux/selectors/entities/teams';
import {getCurrentLocale} from 'app/selectors/i18n';
import SelectTeam from './select_team.js';
function mapStateToProps(state) {
const locale = getCurrentLocale(state);
function sortTeams(a, b) {
const options = {
numeric: true,
sensitivity: 'base',
};
return a.display_name.localeCompare(b.display_name, locale, options);
}
return {
teamsRequest: state.requests.teams.getTeams,
teams: Object.values(getJoinableTeams(state)).sort(sortTeams),
teams: getJoinableTeams(state),
};
}

View file

@ -5,7 +5,6 @@ import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {
Alert,
FlatList,
InteractionManager,
Text,
TouchableOpacity,
@ -19,16 +18,14 @@ import FailedNetworkAction from 'app/components/failed_network_action';
import FormattedText from 'app/components/formatted_text';
import Loading from 'app/components/loading';
import StatusBar from 'app/components/status_bar';
import {ListTypes} from 'app/constants';
import {preventDoubleTap} from 'app/utils/tap';
import {changeOpacity, makeStyleSheetFromTheme, setNavigatorStyles} from 'app/utils/theme';
import {t} from 'app/utils/i18n';
import CustomList from 'app/components/custom_list';
import TeamIcon from 'app/components/team_icon';
const VIEWABILITY_CONFIG = ListTypes.VISIBILITY_CONFIG_DEFAULTS;
const TEAMS_PER_PAGE = 200;
const TEAMS_PER_PAGE = 50;
const errorTitle = {
id: t('error.team_not_found.title'),
@ -61,8 +58,11 @@ export default class SelectTeam extends PureComponent {
props.navigator.setOnNavigatorEvent(this.onNavigatorEvent);
this.state = {
loading: false,
joining: false,
teams: null,
teams: [],
page: 0,
refreshing: false,
};
}
@ -82,9 +82,12 @@ export default class SelectTeam extends PureComponent {
getTeams = () => {
this.setState({loading: true});
this.props.actions.getTeams(0, TEAMS_PER_PAGE).then(() => {
this.setState({loading: false});
this.buildData(this.props);
this.props.actions.getTeams(this.state.page, TEAMS_PER_PAGE).then(() => {
this.setState((state) => ({
loading: false,
refreshing: false,
page: state.page + 1,
}));
});
}
@ -166,6 +169,12 @@ export default class SelectTeam extends PureComponent {
}
};
onRefresh = () => {
this.setState({page: 0, refreshing: true}, () => {
this.getTeams();
});
}
renderItem = ({item}) => {
const {currentUrl, theme} = this.props;
const styles = getStyleSheet(theme);
@ -223,7 +232,7 @@ export default class SelectTeam extends PureComponent {
const {teams} = this.state;
const styles = getStyleSheet(theme);
if (this.state.joining || this.state.loading) {
if (this.state.joining) {
return <Loading/>;
}
@ -251,11 +260,17 @@ export default class SelectTeam extends PureComponent {
</View>
<View style={styles.line}/>
</View>
<FlatList
<CustomList
data={teams}
loading={this.state.loading}
loadingComponent={<Loading/>}
refreshing={this.state.refreshing}
onRefresh={this.onRefresh}
onLoadMore={this.getTeams}
renderItem={this.renderItem}
keyExtractor={(item) => item.id}
viewabilityConfig={VIEWABILITY_CONFIG}
theme={theme}
extraData={this.state.loading}
shouldRenderSeparator={false}
/>
</View>
);

View file

@ -73,7 +73,9 @@ describe('SelectTeam', () => {
const wrapper = shallow(
<SelectTeam {...props}/>,
);
expect(wrapper.state('page')).toEqual(0);
await getTeams();
expect(wrapper.state('page')).toEqual(1);
wrapper.update();
expect(wrapper.getElement()).toMatchSnapshot();
});

View file

@ -53,6 +53,7 @@ exports[`SelectorScreen should match snapshot for channels 1`] = `
onLoadMore={[Function]}
onRowPress={[Function]}
renderItem={[Function]}
shouldRenderSeparator={true}
showNoResults={true}
theme={
Object {
@ -139,6 +140,7 @@ exports[`SelectorScreen should match snapshot for channels 2`] = `
onLoadMore={[Function]}
onRowPress={[Function]}
renderItem={[Function]}
shouldRenderSeparator={true}
showNoResults={true}
theme={
Object {
@ -232,6 +234,7 @@ exports[`SelectorScreen should match snapshot for explicit options 1`] = `
onLoadMore={[Function]}
onRowPress={[Function]}
renderItem={[Function]}
shouldRenderSeparator={true}
showNoResults={true}
theme={
Object {
@ -318,6 +321,7 @@ exports[`SelectorScreen should match snapshot for searching 1`] = `
onLoadMore={[Function]}
onRowPress={[Function]}
renderItem={[Function]}
shouldRenderSeparator={true}
showNoResults={true}
theme={
Object {
@ -404,6 +408,7 @@ exports[`SelectorScreen should match snapshot for users 1`] = `
onLoadMore={[Function]}
onRowPress={[Function]}
renderItem={[Function]}
shouldRenderSeparator={true}
showNoResults={true}
theme={
Object {
@ -490,6 +495,7 @@ exports[`SelectorScreen should match snapshot for users 2`] = `
onLoadMore={[Function]}
onRowPress={[Function]}
renderItem={[Function]}
shouldRenderSeparator={true}
showNoResults={true}
theme={
Object {

5821
package-lock.json generated

File diff suppressed because it is too large Load diff