Changeset View
Changeset View
Standalone View
Standalone View
native/chat/chat-thread-list.react.js
Show All 12 Lines | import { | ||||
BackHandler, | BackHandler, | ||||
} from 'react-native'; | } from 'react-native'; | ||||
import { FloatingAction } from 'react-native-floating-action'; | import { FloatingAction } from 'react-native-floating-action'; | ||||
import Animated from 'react-native-reanimated'; | import Animated from 'react-native-reanimated'; | ||||
import { createSelector } from 'reselect'; | import { createSelector } from 'reselect'; | ||||
import { searchUsers } from 'lib/actions/user-actions.js'; | import { searchUsers } from 'lib/actions/user-actions.js'; | ||||
import { useLoggedInUserInfo } from 'lib/hooks/account-hooks.js'; | import { useLoggedInUserInfo } from 'lib/hooks/account-hooks.js'; | ||||
import { useFetchLatestMessages } from 'lib/hooks/message-hooks.js'; | |||||
import { | import { | ||||
type ChatThreadItem, | type ChatThreadItem, | ||||
useFlattenedChatListData, | useFlattenedChatListData, | ||||
} from 'lib/selectors/chat-selectors.js'; | } from 'lib/selectors/chat-selectors.js'; | ||||
import { useGlobalThreadSearchIndex } from 'lib/selectors/nav-selectors.js'; | import { useGlobalThreadSearchIndex } from 'lib/selectors/nav-selectors.js'; | ||||
import { usersWithPersonalThreadSelector } from 'lib/selectors/user-selectors.js'; | import { usersWithPersonalThreadSelector } from 'lib/selectors/user-selectors.js'; | ||||
import SearchIndex from 'lib/shared/search-index.js'; | import SearchIndex from 'lib/shared/search-index.js'; | ||||
import { | import { | ||||
createPendingThread, | createPendingThread, | ||||
getThreadListSearchResults, | getThreadListSearchResults, | ||||
} from 'lib/shared/thread-utils.js'; | } from 'lib/shared/thread-utils.js'; | ||||
import type { LoadingStatus } from 'lib/types/loading-types.js'; | |||||
import type { UserSearchResult } from 'lib/types/search-types.js'; | import type { UserSearchResult } from 'lib/types/search-types.js'; | ||||
import { threadTypes } from 'lib/types/thread-types-enum.js'; | import { threadTypes } from 'lib/types/thread-types-enum.js'; | ||||
import type { ThreadInfo } from 'lib/types/thread-types.js'; | import type { ThreadInfo } from 'lib/types/thread-types.js'; | ||||
import type { | import type { | ||||
GlobalAccountUserInfo, | GlobalAccountUserInfo, | ||||
UserInfo, | UserInfo, | ||||
LoggedInUserInfo, | LoggedInUserInfo, | ||||
} from 'lib/types/user-types.js'; | } from 'lib/types/user-types.js'; | ||||
▲ Show 20 Lines • Show All 67 Lines • ▼ Show 20 Lines | type Props = { | ||||
+loggedInUserInfo: ?LoggedInUserInfo, | +loggedInUserInfo: ?LoggedInUserInfo, | ||||
+threadSearchIndex: SearchIndex, | +threadSearchIndex: SearchIndex, | ||||
+styles: typeof unboundStyles, | +styles: typeof unboundStyles, | ||||
+indicatorStyle: IndicatorStyle, | +indicatorStyle: IndicatorStyle, | ||||
+usersWithPersonalThread: $ReadOnlySet<string>, | +usersWithPersonalThread: $ReadOnlySet<string>, | ||||
+navigateToThread: (params: MessageListParams) => void, | +navigateToThread: (params: MessageListParams) => void, | ||||
// async functions that hit server APIs | // async functions that hit server APIs | ||||
+searchUsers: (usernamePrefix: string) => Promise<UserSearchResult>, | +searchUsers: (usernamePrefix: string) => Promise<UserSearchResult>, | ||||
+fetchMoreLatestMessages: () => Promise<void>, | |||||
+loadingStatus: LoadingStatus, | |||||
}; | }; | ||||
type SearchStatus = 'inactive' | 'activating' | 'active'; | type SearchStatus = 'inactive' | 'activating' | 'active'; | ||||
type State = { | type State = { | ||||
+searchStatus: SearchStatus, | +searchStatus: SearchStatus, | ||||
+searchText: string, | +searchText: string, | ||||
+threadsSearchResults: Set<string>, | +threadsSearchResults: Set<string>, | ||||
+usersSearchResults: $ReadOnlyArray<GlobalAccountUserInfo>, | +usersSearchResults: $ReadOnlyArray<GlobalAccountUserInfo>, | ||||
+openedSwipeableId: string, | +openedSwipeableId: string, | ||||
▲ Show 20 Lines • Show All 87 Lines • ▼ Show 20 Lines | componentDidUpdate(prevProps: Props, prevState: State) { | ||||
const wasActiveOrActivating = | const wasActiveOrActivating = | ||||
prevSearchStatus === 'active' || prevSearchStatus === 'activating'; | prevSearchStatus === 'active' || prevSearchStatus === 'activating'; | ||||
if (isActiveOrActivating && !wasActiveOrActivating) { | if (isActiveOrActivating && !wasActiveOrActivating) { | ||||
this.searchCancelButtonOpen.setValue(1); | this.searchCancelButtonOpen.setValue(1); | ||||
} else if (!isActiveOrActivating && wasActiveOrActivating) { | } else if (!isActiveOrActivating && wasActiveOrActivating) { | ||||
this.searchCancelButtonOpen.setValue(0); | this.searchCancelButtonOpen.setValue(0); | ||||
} | } | ||||
if (this.listData.length < 3 && this.props.loadingStatus !== 'loading') { | |||||
this.props.fetchMoreLatestMessages(); | |||||
} | |||||
michal: If we can't display any threads at the start, `onEndReached` is not called so we have to fetch… | |||||
const { flatList } = this; | const { flatList } = this; | ||||
if (!flatList) { | if (!flatList) { | ||||
return; | return; | ||||
} | } | ||||
if (this.state.searchText !== prevState.searchText) { | if (this.state.searchText !== prevState.searchText) { | ||||
flatList.scrollToOffset({ offset: 0, animated: false }); | flatList.scrollToOffset({ offset: 0, animated: false }); | ||||
return; | return; | ||||
▲ Show 20 Lines • Show All 209 Lines • ▼ Show 20 Lines | get fullListData() { | ||||
return this.listDataSelector({ ...this.props, ...this.state }); | return this.listDataSelector({ ...this.props, ...this.state }); | ||||
} | } | ||||
get listData() { | get listData() { | ||||
return this.partialListDataSelector({ ...this.props, ...this.state }); | return this.partialListDataSelector({ ...this.props, ...this.state }); | ||||
} | } | ||||
onEndReached = () => { | onEndReached = () => { | ||||
if (this.listData.length === this.fullListData.length) { | if (this.listData.length !== this.fullListData.length) { | ||||
return; | |||||
} | |||||
this.setState(prevState => ({ | this.setState(prevState => ({ | ||||
numItemsToDisplay: prevState.numItemsToDisplay + 25, | numItemsToDisplay: prevState.numItemsToDisplay + 25, | ||||
})); | })); | ||||
} else if (this.props.loadingStatus !== 'loading') { | |||||
this.props.fetchMoreLatestMessages(); | |||||
} | |||||
}; | }; | ||||
render() { | render() { | ||||
let floatingAction; | let floatingAction; | ||||
if (Platform.OS === 'android') { | if (Platform.OS === 'android') { | ||||
floatingAction = ( | floatingAction = ( | ||||
<FloatingAction | <FloatingAction | ||||
actions={floatingActions} | actions={floatingActions} | ||||
Show All 26 Lines | return ( | ||||
initialNumToRender={11} | initialNumToRender={11} | ||||
keyboardShouldPersistTaps="handled" | keyboardShouldPersistTaps="handled" | ||||
onScroll={this.onScroll} | onScroll={this.onScroll} | ||||
style={this.props.styles.flatList} | style={this.props.styles.flatList} | ||||
indicatorStyle={this.props.indicatorStyle} | indicatorStyle={this.props.indicatorStyle} | ||||
scrollEnabled={scrollEnabled} | scrollEnabled={scrollEnabled} | ||||
onEndReached={this.onEndReached} | onEndReached={this.onEndReached} | ||||
onEndReachedThreshold={1} | onEndReachedThreshold={1} | ||||
refreshing={this.props.loadingStatus === 'loading'} | |||||
ref={this.flatListRef} | ref={this.flatListRef} | ||||
/> | /> | ||||
{floatingAction} | {floatingAction} | ||||
</View> | </View> | ||||
); | ); | ||||
} | } | ||||
flatListRef = (flatList: ?FlatList<Item>) => { | flatListRef = (flatList: ?FlatList<Item>) => { | ||||
▲ Show 20 Lines • Show All 125 Lines • ▼ Show 20 Lines | React.memo<BaseProps>(function ConnectedChatThreadList(props: BaseProps) { | ||||
const indicatorStyle = useSelector(indicatorStyleSelector); | const indicatorStyle = useSelector(indicatorStyleSelector); | ||||
const callSearchUsers = useServerCall(searchUsers); | const callSearchUsers = useServerCall(searchUsers); | ||||
const usersWithPersonalThread = useSelector( | const usersWithPersonalThread = useSelector( | ||||
usersWithPersonalThreadSelector, | usersWithPersonalThreadSelector, | ||||
); | ); | ||||
const navigateToThread = useNavigateToThread(); | const navigateToThread = useNavigateToThread(); | ||||
const { fetchMoreLatestMessages, loadingStatus } = useFetchLatestMessages( | |||||
props.route.name === 'HomeChatThreadList', | |||||
); | |||||
return ( | return ( | ||||
<ChatThreadList | <ChatThreadList | ||||
{...props} | {...props} | ||||
chatListData={boundChatListData} | chatListData={boundChatListData} | ||||
loggedInUserInfo={loggedInUserInfo} | loggedInUserInfo={loggedInUserInfo} | ||||
threadSearchIndex={threadSearchIndex} | threadSearchIndex={threadSearchIndex} | ||||
styles={styles} | styles={styles} | ||||
indicatorStyle={indicatorStyle} | indicatorStyle={indicatorStyle} | ||||
searchUsers={callSearchUsers} | searchUsers={callSearchUsers} | ||||
fetchMoreLatestMessages={fetchMoreLatestMessages} | |||||
loadingStatus={loadingStatus} | |||||
usersWithPersonalThread={usersWithPersonalThread} | usersWithPersonalThread={usersWithPersonalThread} | ||||
navigateToThread={navigateToThread} | navigateToThread={navigateToThread} | ||||
/> | /> | ||||
); | ); | ||||
}); | }); | ||||
export default ConnectedChatThreadList; | export default ConnectedChatThreadList; |
If we can't display any threads at the start, onEndReached is not called so we have to fetch manually (the < 3 check takes into account the search and empty list items)