diff --git a/native/chat/sidebar-list-modal.react.js b/native/chat/sidebar-list-modal.react.js index af33a6213..08be3527c 100644 --- a/native/chat/sidebar-list-modal.react.js +++ b/native/chat/sidebar-list-modal.react.js @@ -1,200 +1,145 @@ // @flow import * as React from 'react'; -import { TextInput, FlatList, View } from 'react-native'; +import { View } from 'react-native'; import { useSearchSidebars } from 'lib/hooks/search-threads'; import type { ThreadInfo, SidebarInfo } from 'lib/types/thread-types'; import ExtendedArrow from '../components/arrow-extended.react'; import Arrow from '../components/arrow.react'; import Button from '../components/button.react'; -import Modal from '../components/modal.react'; -import Search from '../components/search.react'; import type { RootNavigationProp } from '../navigation/root-navigator.react'; import type { NavigationRoute } from '../navigation/route-names'; -import { useColors, useIndicatorStyle, useStyles } from '../themes/colors'; -import { waitForModalInputFocus } from '../utils/timers'; -import { useNavigateToThread } from './message-list-types'; +import { useColors, useStyles } from '../themes/colors'; import { SidebarItem } from './sidebar-item.react'; +import ThreadListModal from './thread-list-modal.react'; export type SidebarListModalParams = { +threadInfo: ThreadInfo, }; -function keyExtractor(sidebarInfo: SidebarInfo) { - return sidebarInfo.threadInfo.id; -} -function getItemLayout(data: ?$ReadOnlyArray, index: number) { - return { length: 24, offset: 24 * index, index }; -} - type Props = { +navigation: RootNavigationProp<'SidebarListModal'>, +route: NavigationRoute<'SidebarListModal'>, }; function SidebarListModal(props: Props): React.Node { const { listData, searchState, setSearchState, onChangeSearchInputText, } = useSearchSidebars(props.route.params.threadInfo); - const searchTextInputRef = React.useRef(); - const setSearchTextInputRef = React.useCallback( - async (textInput: ?React.ElementRef) => { - searchTextInputRef.current = textInput; - if (!textInput) { - return; - } - await waitForModalInputFocus(); - if (searchTextInputRef.current) { - searchTextInputRef.current.focus(); - } - }, - [], - ); - - const navigateToThread = useNavigateToThread(); - const onPressItem = React.useCallback( - (threadInfo: ThreadInfo) => { - setSearchState({ - text: '', - results: new Set(), - }); - if (searchTextInputRef.current) { - searchTextInputRef.current.blur(); - } - navigateToThread({ threadInfo }); - }, - [navigateToThread, setSearchState], - ); - - const styles = useStyles(unboundStyles); - - const numOfSidebarsWithExtendedArrow = React.useMemo( - () => listData.length - 1, - [listData], - ); + const numOfSidebarsWithExtendedArrow = listData.length - 1; - const renderItem = React.useCallback( - (row: { item: SidebarInfo, index: number, ... }) => { + const createRenderItem = React.useCallback( + ( + onPressItem: (threadInfo: ThreadInfo) => void, + // eslint-disable-next-line react/display-name + ) => (row: { +item: SidebarInfo, +index: number, ... }) => { let extendArrow: boolean = false; if (row.index < numOfSidebarsWithExtendedArrow) { extendArrow = true; } return ( ); }, - [onPressItem, numOfSidebarsWithExtendedArrow], + [numOfSidebarsWithExtendedArrow], ); - const indicatorStyle = useIndicatorStyle(); return ( - - - - + ); } function Item(props: { item: SidebarInfo, onPressItem: (threadInfo: ThreadInfo) => void, extendArrow: boolean, }): React.Node { const { item, onPressItem, extendArrow } = props; const { threadInfo } = item; const onPressButton = React.useCallback(() => onPressItem(threadInfo), [ onPressItem, threadInfo, ]); const colors = useColors(); const styles = useStyles(unboundStyles); let arrow; if (extendArrow) { arrow = ( ); } else { arrow = ( ); } return ( ); } const unboundStyles = { arrow: { position: 'absolute', top: -12, }, extendedArrow: { position: 'absolute', top: -6, }, - search: { - marginBottom: 8, - }, sidebar: { backgroundColor: 'listBackground', paddingLeft: 0, paddingRight: 5, }, sidebarItemContainer: { flex: 1, }, sidebarRowContainer: { flex: 1, flexDirection: 'row', }, spacer: { width: 30, }, }; export default SidebarListModal; diff --git a/native/chat/thread-list-modal.react.js b/native/chat/thread-list-modal.react.js new file mode 100644 index 000000000..a9c964de5 --- /dev/null +++ b/native/chat/thread-list-modal.react.js @@ -0,0 +1,118 @@ +// @flow + +import * as React from 'react'; +import { TextInput, FlatList, StyleSheet } from 'react-native'; + +import type { ThreadSearchState } from 'lib/hooks/search-threads'; +import type { ChatThreadItem } from 'lib/selectors/chat-selectors'; +import type { SetState } from 'lib/types/hook-types'; +import type { ThreadInfo, SidebarInfo } from 'lib/types/thread-types'; + +import Modal from '../components/modal.react'; +import Search from '../components/search.react'; +import { useIndicatorStyle } from '../themes/colors'; +import { waitForModalInputFocus } from '../utils/timers'; +import { useNavigateToThread } from './message-list-types'; + +function keyExtractor(sidebarInfo: SidebarInfo | ChatThreadItem) { + return sidebarInfo.threadInfo.id; +} +function getItemLayout( + data: ?$ReadOnlyArray, + index: number, +) { + return { length: 24, offset: 24 * index, index }; +} + +type Props = { + +threadInfo: ThreadInfo, + +createRenderItem: ( + onPressItem: (threadInfo: ThreadInfo) => void, + ) => (row: { + +item: U, + +index: number, + ... + }) => React.Node, + +listData: $ReadOnlyArray, + +searchState: ThreadSearchState, + +setSearchState: SetState, + +onChangeSearchInputText: (text: string) => mixed, + +searchPlaceholder: string, +}; +function ThreadListModal( + props: Props, +): React.Node { + const { + searchState, + setSearchState, + onChangeSearchInputText, + listData, + createRenderItem, + searchPlaceholder, + } = props; + + const searchTextInputRef = React.useRef(); + const setSearchTextInputRef = React.useCallback( + async (textInput: ?React.ElementRef) => { + searchTextInputRef.current = textInput; + if (!textInput) { + return; + } + await waitForModalInputFocus(); + if (searchTextInputRef.current) { + searchTextInputRef.current.focus(); + } + }, + [], + ); + + const navigateToThread = useNavigateToThread(); + const onPressItem = React.useCallback( + (threadInfo: ThreadInfo) => { + setSearchState({ + text: '', + results: new Set(), + }); + if (searchTextInputRef.current) { + searchTextInputRef.current.blur(); + } + navigateToThread({ threadInfo }); + }, + [navigateToThread, setSearchState], + ); + + const renderItem = React.useMemo(() => createRenderItem(onPressItem), [ + createRenderItem, + onPressItem, + ]); + + const indicatorStyle = useIndicatorStyle(); + return ( + + + + + ); +} + +const styles = StyleSheet.create({ + search: { + marginBottom: 8, + }, +}); + +export default ThreadListModal;