From d6ee97c0c7e898ecb69fa916acd10f24f4e01482 Mon Sep 17 00:00:00 2001 From: Chris Duarte Date: Fri, 13 Apr 2018 05:03:02 -0700 Subject: [PATCH] Show FlatList when searching in More DM Screen (#1546) * Set searchbars to autoCapitalize='none' * Show flatlist when searching profiles for more dms * Fix lint errors --- app/components/custom_flat_list.js | 240 ++++++++++++++++++ app/components/emoji_picker/emoji_picker.js | 1 + .../channel_add_members.js | 1 + .../channel_members/channel_members.js | 1 + app/screens/more_channels/more_channels.js | 1 + app/screens/more_dms/more_dms.js | 62 +++-- .../extension_channels/extension_channels.js | 1 + share_extension/ios/extension_channels.js | 1 + 8 files changed, 292 insertions(+), 16 deletions(-) create mode 100644 app/components/custom_flat_list.js diff --git a/app/components/custom_flat_list.js b/app/components/custom_flat_list.js new file mode 100644 index 000000000..06100d7c2 --- /dev/null +++ b/app/components/custom_flat_list.js @@ -0,0 +1,240 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. +import React, {PureComponent} from 'react'; +import PropTypes from 'prop-types'; +import {FlatList, Platform, View} from 'react-native'; + +import Loading from 'app/components/loading'; +import FormattedText from 'app/components/formatted_text'; +import {emptyFunction} from 'app/utils/general'; +import {makeStyleSheetFromTheme, changeOpacity} from 'app/utils/theme'; + +export default class CustomFlatList extends 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 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 defaultProps = { + showNoResults: true, + keyExtractor: (item) => { + return item.id; + }, + onListEndReached: emptyFunction, + onListEndReachedThreshold: 50, + loadingText: null, + initialNumToRender: 10, + }; + + constructor(props) { + super(props); + + this.state = { + items: props.items, + }; + } + + componentWillReceiveProps(nextProps) { + if (nextProps.items !== this.props.items) { + this.setState({ + items: nextProps.items, + }); + } + } + + 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%', + }, + 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/components/emoji_picker/emoji_picker.js b/app/components/emoji_picker/emoji_picker.js index e0ef77fcf..fb305c24e 100644 --- a/app/components/emoji_picker/emoji_picker.js +++ b/app/components/emoji_picker/emoji_picker.js @@ -473,6 +473,7 @@ export default class EmojiPicker extends PureComponent { titleCancelColor={theme.centerChannelColor} onChangeText={this.changeSearchTerm} onCancelButtonPress={this.cancelSearch} + autoCapitalize='none' value={searchTerm} /> diff --git a/app/screens/channel_add_members/channel_add_members.js b/app/screens/channel_add_members/channel_add_members.js index 4bd531d54..826500c47 100644 --- a/app/screens/channel_add_members/channel_add_members.js +++ b/app/screens/channel_add_members/channel_add_members.js @@ -270,6 +270,7 @@ class ChannelAddMembers extends PureComponent { onChangeText={this.searchProfiles} onSearchButtonPress={this.searchProfiles} onCancelButtonPress={this.cancelSearch} + autoCapitalize='none' value={term} /> diff --git a/app/screens/channel_members/channel_members.js b/app/screens/channel_members/channel_members.js index e2cb55440..f9bc65389 100644 --- a/app/screens/channel_members/channel_members.js +++ b/app/screens/channel_members/channel_members.js @@ -317,6 +317,7 @@ class ChannelMembers extends PureComponent { onChangeText={this.searchProfiles} onSearchButtonPress={this.searchProfiles} onCancelButtonPress={this.cancelSearch} + autoCapitalize='none' value={term} /> diff --git a/app/screens/more_channels/more_channels.js b/app/screens/more_channels/more_channels.js index 4b22a7be0..245e2ee92 100644 --- a/app/screens/more_channels/more_channels.js +++ b/app/screens/more_channels/more_channels.js @@ -318,6 +318,7 @@ export default class MoreChannels extends PureComponent { onChangeText={this.searchProfiles} onSearchButtonPress={this.searchProfiles} onCancelButtonPress={this.cancelSearch} + autoCapitalize='none' value={term} /> diff --git a/app/screens/more_dms/more_dms.js b/app/screens/more_dms/more_dms.js index b523ad8b6..3a4afeac6 100644 --- a/app/screens/more_dms/more_dms.js +++ b/app/screens/more_dms/more_dms.js @@ -15,6 +15,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 CustomFlatList from 'app/components/custom_flat_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'; @@ -96,13 +97,21 @@ class MoreDirectMessages extends PureComponent { this.setState({profiles, showNoResults: true}); } else if (this.state.searching && nextProps.searchRequest.status === RequestStatus.SUCCESS) { - let results = filterProfilesMatchingTerm(nextProps.profiles, this.state.term); + const exactMatches = []; + let results = filterProfilesMatchingTerm(nextProps.profiles, this.state.term).filter((p) => { + if (p.username === this.state.term || p.username.startsWith(this.state.term)) { + exactMatches.push(p); + return false; + } + + return true; + }); if (this.state.selectedCount > 0) { results = this.removeCurrentUserFromProfiles(results); } - this.setState({profiles: results, showNoResults: true}); + this.setState({profiles: [...exactMatches, ...results], showNoResults: true}); } } @@ -466,6 +475,39 @@ class MoreDirectMessages extends PureComponent { }), }; + let listComponent; + if (term.length) { + listComponent = ( + + ); + } else { + listComponent = ( + + ); + } + return ( @@ -484,6 +526,7 @@ class MoreDirectMessages extends PureComponent { onChangeText={this.onSearch} onSearchButtonPress={this.onSearch} onCancelButtonPress={this.cancelSearch} + autoCapitalize='none' value={term} /> - + {listComponent} ); } diff --git a/share_extension/android/extension_channels/extension_channels.js b/share_extension/android/extension_channels/extension_channels.js index 36c974af4..83014092c 100644 --- a/share_extension/android/extension_channels/extension_channels.js +++ b/share_extension/android/extension_channels/extension_channels.js @@ -187,6 +187,7 @@ export default class ExtensionTeam extends PureComponent { tintColorDelete={changeOpacity(defaultTheme.centerChannelColor, 0.3)} titleCancelColor={defaultTheme.centerChannelColor} onChangeText={this.handleSearch} + autoCapitalize='none' value={this.state.term} /> diff --git a/share_extension/ios/extension_channels.js b/share_extension/ios/extension_channels.js index fb69dd52e..c312f6d63 100644 --- a/share_extension/ios/extension_channels.js +++ b/share_extension/ios/extension_channels.js @@ -221,6 +221,7 @@ export default class ExtensionChannels extends PureComponent { titleCancelColor={theme.linkColor} onChangeText={this.handleSearch} onCancelButtonPress={this.cancelSearch} + autoCapitalize='none' value={this.state.term} />