diff --git a/native/components/community-list.react.js b/native/components/community-list.react.js new file mode 100644 --- /dev/null +++ b/native/components/community-list.react.js @@ -0,0 +1,145 @@ +// @flow + +import invariant from 'invariant'; +import * as React from 'react'; +import { FlatList } from 'react-native'; +import { createSelector } from 'reselect'; + +import SearchIndex from 'lib/shared/search-index.js'; +import { reorderThreadSearchResults } from 'lib/shared/thread-utils.js'; +import type { ThreadInfo } from 'lib/types/minimally-encoded-thread-permissions-types.js'; + +import CommunityListItem from './community-list-item.react.js'; +import Search from './search.react.js'; +import { + type IndicatorStyle, + useIndicatorStyle, + useStyles, +} from '../themes/colors.js'; +import type { TextStyle, ViewStyle } from '../types/styles.js'; + +const unboundStyles = { + search: { + marginBottom: 8, + }, +}; + +type BaseProps = { + +threadInfos: $ReadOnlyArray, + +itemStyle?: ViewStyle, + +itemTextStyle?: TextStyle, + +searchIndex?: SearchIndex, +}; +type Props = { + ...BaseProps, + // Redux state + +styles: $ReadOnly, + +indicatorStyle: IndicatorStyle, +}; +type State = { + +searchText: string, + +searchResults: $ReadOnlyArray, +}; +type PropsAndState = { ...Props, ...State }; +class CommunityList extends React.PureComponent { + state: State = { + searchText: '', + searchResults: [], + }; + + listDataSelector: PropsAndState => $ReadOnlyArray = + createSelector( + (propsAndState: PropsAndState) => propsAndState.threadInfos, + (propsAndState: PropsAndState) => propsAndState.searchText, + (propsAndState: PropsAndState) => propsAndState.searchResults, + (propsAndState: PropsAndState) => propsAndState.itemStyle, + (propsAndState: PropsAndState) => propsAndState.itemTextStyle, + ( + threadInfos: $ReadOnlyArray, + text: string, + searchResults: $ReadOnlyArray, + ): $ReadOnlyArray => + // We spread to make sure the result of this selector updates when + // any input param (namely itemStyle or itemTextStyle) changes + text ? [...searchResults] : [...threadInfos], + ); + + get listData(): $ReadOnlyArray { + return this.listDataSelector({ ...this.props, ...this.state }); + } + + render(): React.Node { + let searchBar = null; + if (this.props.searchIndex) { + searchBar = ( + + ); + } + return ( + + {searchBar} + + + ); + } + + static keyExtractor = (threadInfo: ThreadInfo): string => { + return threadInfo.id; + }; + + renderItem = (row: { +item: ThreadInfo, ... }): React.Node => { + return ( + + ); + }; + + static getItemLayout = ( + data: ?$ReadOnlyArray, + index: number, + ): { length: number, offset: number, index: number } => { + return { length: 24, offset: 24 * index, index }; + }; + + onChangeSearchText = (searchText: string) => { + invariant(this.props.searchIndex, 'should be set'); + const results = this.props.searchIndex.getSearchResults(searchText); + const threadInfoResults = reorderThreadSearchResults( + this.props.threadInfos, + results, + ); + this.setState({ searchText, searchResults: threadInfoResults }); + }; +} + +const ConnectedCommunityList: React.ComponentType = + React.memo(function ConnectedCommunityList(props: BaseProps) { + const styles = useStyles(unboundStyles); + const indicatorStyle = useIndicatorStyle(); + + return ( + + ); + }); + +export default ConnectedCommunityList;