From f651cac58330e5dc16c839a47ad826eee46ca101 Mon Sep 17 00:00:00 2001 From: enahum Date: Wed, 22 Nov 2017 12:24:12 -0300 Subject: [PATCH] Fix more dms screen (#1193) --- app/components/custom_section_list.js | 310 ++++++++++++++++++++++++++ app/screens/more_dms/more_dms.js | 2 +- 2 files changed, 311 insertions(+), 1 deletion(-) create mode 100644 app/components/custom_section_list.js diff --git a/app/components/custom_section_list.js b/app/components/custom_section_list.js new file mode 100644 index 000000000..3831ab407 --- /dev/null +++ b/app/components/custom_section_list.js @@ -0,0 +1,310 @@ +// 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 { + Platform, + SectionList, + Text, + View +} from 'react-native'; + +import Loading from 'app/components/loading'; +import FormattedText from 'app/components/formatted_text'; +import {makeStyleSheetFromTheme, changeOpacity} from 'app/utils/theme'; + +export default class CustomSectionList extends React.PureComponent { + static propTypes = { + + /* + * The current theme. + */ + theme: PropTypes.object.isRequired, + + /* + * An array of items to be rendered. + */ + items: PropTypes.array.isRequired, + + /* + * A function or React element used to render the items in the list. + */ + renderItem: PropTypes.func, + + /* + * Whether or not to render "No results" when the list contains no items. + */ + showNoResults: PropTypes.bool, + + /* + * A function to get a section identifier for each item in the list. Items sharing a section identifier will be grouped together. + */ + sectionKeyExtractor: PropTypes.func.isRequired, + + /* + * A comparison function used when sorting items in the list. + */ + compareItems: PropTypes.func.isRequired, + + /* + * A function to get a unique key for each item in the list. If not provided, the id field of the item will be used as the key. + */ + keyExtractor: PropTypes.func, + + /* + * Any extra data needed to render the list. If this value changes, all items of a list will be re-rendered. + */ + extraData: PropTypes.object, + + /* + * A function called when an item in the list is pressed. Receives the item that was pressed as an argument. + */ + onRowPress: PropTypes.func, + + /* + * A function called when the end of the list is reached. This can be triggered before this list end is reached + * by changing onListEndReachedThreshold. + */ + onListEndReached: PropTypes.func, + + /* + * How soon before the end of the list onListEndReached should be called. + */ + onListEndReachedThreshold: PropTypes.number, + + /* + * Whether or not to display the loading text. + */ + loading: PropTypes.bool, + + /* + * The text displayed when loading is set to true. + */ + loadingText: PropTypes.object, + + /* + * How many items to render when the list is first rendered. + */ + initialNumToRender: PropTypes.number + }; + + static defaultKeyExtractor = (item) => { + return item.id; + }; + + static defaultProps = { + showNoResults: true, + keyExtractor: CustomSectionList.defaultKeyExtractor, + onListEndReached: () => true, + onListEndReachedThreshold: 50, + loadingText: null, + initialNumToRender: 10 + }; + + constructor(props) { + super(props); + + this.state = { + sections: this.extractSections(props.items) + }; + } + + componentWillReceiveProps(nextProps) { + if (nextProps.items !== this.props.items) { + this.setState({ + sections: this.extractSections(nextProps.items) + }); + } + } + + extractSections = (items) => { + const sections = {}; + const sectionKeys = []; + for (const item of items) { + const sectionKey = this.props.sectionKeyExtractor(item); + + if (!sections[sectionKey]) { + sections[sectionKey] = []; + sectionKeys.push(sectionKey); + } + + sections[sectionKey].push(item); + } + + sectionKeys.sort(); + + return sectionKeys.map((sectionKey) => { + return { + key: sectionKey, + data: sections[sectionKey].sort(this.props.compareItems) + }; + }); + } + + renderSectionHeader = ({section}) => { + const {theme} = this.props; + const style = getStyleFromTheme(theme); + + return ( + + + {section.key} + + + ); + }; + + renderItem = ({item}) => { + const props = { + id: item.id, + item, + onPress: this.props.onRowPress + }; + + // Allow passing in a component like UserListRow or ChannelListRow + if (this.props.renderItem.prototype.isReactElement) { + const RowComponent = this.props.renderItem; + return ; + } + + return this.props.renderItem(props); + }; + + renderItemSeparator = () => { + const {theme} = this.props; + const style = getStyleFromTheme(theme); + + return ( + + ); + }; + + renderFooter = () => { + const {theme} = this.props; + const style = getStyleFromTheme(theme); + + if (!this.props.loading || !this.props.loadingText) { + return null; + } + + if (this.props.items.length === 0) { + return null; + } + + return ( + + + + ); + }; + + renderEmptyList = () => { + const {theme} = this.props; + const style = getStyleFromTheme(theme); + + if (this.props.loading) { + return ( + + + + ); + } + + if (this.props.showNoResults) { + return ( + + + + ); + } + + return null; + } + + render() { + const style = getStyleFromTheme(this.props.theme); + + return ( + + ); + } +} + +const getStyleFromTheme = makeStyleSheetFromTheme((theme) => { + return { + listView: { + flex: 1, + backgroundColor: theme.centerChannelBg, + ...Platform.select({ + android: { + marginBottom: 20 + } + }) + }, + loading: { + height: 70, + backgroundColor: theme.centerChannelBg, + alignItems: 'center', + justifyContent: 'center' + }, + loadingText: { + color: changeOpacity(theme.centerChannelColor, 0.6) + }, + searching: { + backgroundColor: theme.centerChannelBg, + height: '100%', + position: 'absolute', + width: '100%' + }, + sectionContainer: { + backgroundColor: changeOpacity(theme.centerChannelColor, 0.07), + paddingLeft: 10, + paddingVertical: 2 + }, + sectionWrapper: { + backgroundColor: theme.centerChannelBg + }, + sectionText: { + fontWeight: '600', + color: theme.centerChannelColor + }, + separator: { + height: 1, + flex: 1, + backgroundColor: changeOpacity(theme.centerChannelColor, 0.1) + }, + noResultContainer: { + flex: 1, + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center' + }, + noResultText: { + fontSize: 26, + color: changeOpacity(theme.centerChannelColor, 0.5) + } + }; +}); diff --git a/app/screens/more_dms/more_dms.js b/app/screens/more_dms/more_dms.js index fc84202f2..8863106fa 100644 --- a/app/screens/more_dms/more_dms.js +++ b/app/screens/more_dms/more_dms.js @@ -14,7 +14,7 @@ import EventEmitter from 'mattermost-redux/utils/event_emitter'; import {getGroupDisplayNameFromUserIds} from 'mattermost-redux/utils/channel_utils'; import {displayUsername, filterProfilesMatchingTerm} from 'mattermost-redux/utils/user_utils'; -import CustomSectionList from 'app/components/custom_list'; +import CustomSectionList from 'app/components/custom_section_list'; import UserListRow from 'app/components/custom_list/user_list_row'; import Loading from 'app/components/loading'; import SearchBar from 'app/components/search_bar';