diff --git a/web/modals/threads/subchannels/subchannels-modal.react.js b/lib/hooks/child-threads.js similarity index 54% copy from web/modals/threads/subchannels/subchannels-modal.react.js copy to lib/hooks/child-threads.js index 5813e232e..f837e446f 100644 --- a/web/modals/threads/subchannels/subchannels-modal.react.js +++ b/lib/hooks/child-threads.js @@ -1,157 +1,117 @@ // @flow import * as React from 'react'; import { fetchSingleMostRecentMessagesFromThreads, fetchSingleMostRecentMessagesFromThreadsActionTypes, -} from 'lib/actions/message-actions'; -import { useFilteredChatListData } from 'lib/selectors/chat-selectors'; -import { threadSearchIndex } from 'lib/selectors/nav-selectors'; -import { childThreadInfos } from 'lib/selectors/thread-selectors'; -import { threadIsChannel, threadInChatList } from 'lib/shared/thread-utils'; -import threadWatcher from 'lib/shared/thread-watcher'; +} from '../actions/message-actions'; import { - useServerCall, - useDispatchActionPromise, -} from 'lib/utils/action-utils'; - -import { useSelector } from '../../../redux/redux-utils'; -import SearchModal from '../../search-modal.react'; -import Subchannel from './subchannel.react'; -import css from './subchannels-modal.css'; - -type ContentProps = { - +searchText: string, - +threadID: string, + useFilteredChatListData, + type ChatThreadItem, +} from '../selectors/chat-selectors'; +import { threadSearchIndex } from '../selectors/nav-selectors'; +import { childThreadInfos } from '../selectors/thread-selectors'; +import { threadInChatList } from '../shared/thread-utils'; +import threadWatcher from '../shared/thread-watcher'; +import type { ThreadInfo } from '../types/thread-types'; +import { useDispatchActionPromise, useServerCall } from '../utils/action-utils'; +import { useSelector } from '../utils/redux-utils'; + +type ThreadFilter = { + +predicate?: (thread: ThreadInfo) => boolean, + +searchText?: string, }; -function SubchannelsModalContent(props: ContentProps): React.Node { - const { searchText, threadID } = props; +function useFilteredChildThreads( + threadID: string, + filter?: ThreadFilter, +): $ReadOnlyArray { + const defaultPredicate = React.useCallback(() => true, []); + const { predicate = defaultPredicate, searchText = '' } = filter ?? {}; const childThreads = useSelector(state => childThreadInfos(state)[threadID]); const subchannelIDs = React.useMemo(() => { if (!childThreads) { return new Set(); } return new Set( - childThreads.filter(threadIsChannel).map(threadInfo => threadInfo.id), + childThreads.filter(predicate).map(threadInfo => threadInfo.id), ); - }, [childThreads]); + }, [childThreads, predicate]); const filterSubchannels = React.useCallback( thread => subchannelIDs.has(thread?.id), [subchannelIDs], ); const allSubchannelsList = useFilteredChatListData(filterSubchannels); const searchIndex = useSelector(threadSearchIndex); const searchResultIDs = React.useMemo( () => searchIndex.getSearchResults(searchText), [searchIndex, searchText], ); const searchTextExists = !!searchText.length; const subchannelIDsNotInChatList = React.useMemo( () => new Set( allSubchannelsList .filter(item => !threadInChatList(item.threadInfo)) .map(item => item.threadInfo.id), ), [allSubchannelsList], ); React.useEffect(() => { if (!subchannelIDsNotInChatList.size) { return; } subchannelIDsNotInChatList.forEach(tID => threadWatcher.watchID(tID)); return () => subchannelIDsNotInChatList.forEach(tID => threadWatcher.removeID(tID)); }, [subchannelIDsNotInChatList]); const filteredSubchannelsChatList = React.useMemo(() => { if (!searchTextExists) { return allSubchannelsList; } return allSubchannelsList.filter(item => searchResultIDs.includes(item.threadInfo.id), ); }, [allSubchannelsList, searchResultIDs, searchTextExists]); const threadIDsWithNoMessages = React.useMemo( () => new Set( filteredSubchannelsChatList .filter(item => !item.mostRecentMessageInfo) .map(item => item.threadInfo.id), ), [filteredSubchannelsChatList], ); const dispatchActionPromise = useDispatchActionPromise(); const fetchSingleMostRecentMessages = useServerCall( fetchSingleMostRecentMessagesFromThreads, ); React.useEffect(() => { if (!threadIDsWithNoMessages.size) { return; } dispatchActionPromise( fetchSingleMostRecentMessagesFromThreadsActionTypes, fetchSingleMostRecentMessages(Array.from(threadIDsWithNoMessages)), ); }, [ threadIDsWithNoMessages, fetchSingleMostRecentMessages, dispatchActionPromise, ]); - const subchannels = React.useMemo(() => { - if (!filteredSubchannelsChatList.length) { - return ( -
- No matching subchannels were found in the channel! -
- ); - } - return filteredSubchannelsChatList.map(childThreadItem => ( - - )); - }, [filteredSubchannelsChatList]); - - return
{subchannels}
; -} -type Props = { - +threadID: string, - +onClose: () => void, -}; - -function SubchannelsModal(props: Props): React.Node { - const { threadID, onClose } = props; - const subchannelsContent = React.useCallback( - (searchText: string) => ( - - ), - [threadID], - ); - - return ( - - {subchannelsContent} - - ); + return filteredSubchannelsChatList; } -export default SubchannelsModal; +export { useFilteredChildThreads }; diff --git a/web/modals/threads/subchannels/subchannels-modal.react.js b/web/modals/threads/subchannels/subchannels-modal.react.js index 5813e232e..48ae1de06 100644 --- a/web/modals/threads/subchannels/subchannels-modal.react.js +++ b/web/modals/threads/subchannels/subchannels-modal.react.js @@ -1,157 +1,68 @@ // @flow import * as React from 'react'; -import { - fetchSingleMostRecentMessagesFromThreads, - fetchSingleMostRecentMessagesFromThreadsActionTypes, -} from 'lib/actions/message-actions'; -import { useFilteredChatListData } from 'lib/selectors/chat-selectors'; -import { threadSearchIndex } from 'lib/selectors/nav-selectors'; -import { childThreadInfos } from 'lib/selectors/thread-selectors'; -import { threadIsChannel, threadInChatList } from 'lib/shared/thread-utils'; -import threadWatcher from 'lib/shared/thread-watcher'; -import { - useServerCall, - useDispatchActionPromise, -} from 'lib/utils/action-utils'; +import { useFilteredChildThreads } from 'lib/hooks/child-threads'; +import { threadIsChannel } from 'lib/shared/thread-utils'; -import { useSelector } from '../../../redux/redux-utils'; import SearchModal from '../../search-modal.react'; import Subchannel from './subchannel.react'; import css from './subchannels-modal.css'; type ContentProps = { +searchText: string, +threadID: string, }; function SubchannelsModalContent(props: ContentProps): React.Node { const { searchText, threadID } = props; - const childThreads = useSelector(state => childThreadInfos(state)[threadID]); - const subchannelIDs = React.useMemo(() => { - if (!childThreads) { - return new Set(); - } - return new Set( - childThreads.filter(threadIsChannel).map(threadInfo => threadInfo.id), - ); - }, [childThreads]); - - const filterSubchannels = React.useCallback( - thread => subchannelIDs.has(thread?.id), - [subchannelIDs], - ); - const allSubchannelsList = useFilteredChatListData(filterSubchannels); - - const searchIndex = useSelector(threadSearchIndex); - - const searchResultIDs = React.useMemo( - () => searchIndex.getSearchResults(searchText), - [searchIndex, searchText], - ); - - const searchTextExists = !!searchText.length; - - const subchannelIDsNotInChatList = React.useMemo( - () => - new Set( - allSubchannelsList - .filter(item => !threadInChatList(item.threadInfo)) - .map(item => item.threadInfo.id), - ), - [allSubchannelsList], - ); - - React.useEffect(() => { - if (!subchannelIDsNotInChatList.size) { - return; - } - subchannelIDsNotInChatList.forEach(tID => threadWatcher.watchID(tID)); - - return () => - subchannelIDsNotInChatList.forEach(tID => threadWatcher.removeID(tID)); - }, [subchannelIDsNotInChatList]); - - const filteredSubchannelsChatList = React.useMemo(() => { - if (!searchTextExists) { - return allSubchannelsList; - } - return allSubchannelsList.filter(item => - searchResultIDs.includes(item.threadInfo.id), - ); - }, [allSubchannelsList, searchResultIDs, searchTextExists]); - - const threadIDsWithNoMessages = React.useMemo( - () => - new Set( - filteredSubchannelsChatList - .filter(item => !item.mostRecentMessageInfo) - .map(item => item.threadInfo.id), - ), - [filteredSubchannelsChatList], - ); - const dispatchActionPromise = useDispatchActionPromise(); - const fetchSingleMostRecentMessages = useServerCall( - fetchSingleMostRecentMessagesFromThreads, - ); - - React.useEffect(() => { - if (!threadIDsWithNoMessages.size) { - return; - } - dispatchActionPromise( - fetchSingleMostRecentMessagesFromThreadsActionTypes, - fetchSingleMostRecentMessages(Array.from(threadIDsWithNoMessages)), - ); - }, [ - threadIDsWithNoMessages, - fetchSingleMostRecentMessages, - dispatchActionPromise, - ]); + const subchannelList = useFilteredChildThreads(threadID, { + predicate: threadIsChannel, + searchText, + }); const subchannels = React.useMemo(() => { - if (!filteredSubchannelsChatList.length) { + if (!subchannelList.length) { return (
No matching subchannels were found in the channel!
); } - return filteredSubchannelsChatList.map(childThreadItem => ( + return subchannelList.map(childThreadItem => ( )); - }, [filteredSubchannelsChatList]); + }, [subchannelList]); return
{subchannels}
; } type Props = { +threadID: string, +onClose: () => void, }; function SubchannelsModal(props: Props): React.Node { const { threadID, onClose } = props; const subchannelsContent = React.useCallback( (searchText: string) => ( ), [threadID], ); return ( {subchannelsContent} ); } export default SubchannelsModal;