Page MenuHomePhabricator

D7774.id26419.diff
No OneTemporary

D7774.id26419.diff

diff --git a/native/chat/message-result.react.js b/native/chat/message-result.react.js
--- a/native/chat/message-result.react.js
+++ b/native/chat/message-result.react.js
@@ -7,7 +7,7 @@
import { type ThreadInfo } from 'lib/types/thread-types.js';
import { longAbsoluteDate } from 'lib/utils/date-utils.js';
-import type { ChatNavigationProp } from './chat.react';
+import { type ChatNavigationProp } from './chat.react.js';
import { MessageListContextProvider } from './message-list-types.js';
import { Message } from './message.react.js';
import { modifyItemForResultScreen } from './utils.js';
@@ -22,10 +22,12 @@
+threadInfo: ThreadInfo,
+navigation:
| AppNavigationProp<'TogglePinModal'>
- | ChatNavigationProp<'MessageResultsScreen'>,
+ | ChatNavigationProp<'MessageResultsScreen'>
+ | ChatNavigationProp<'MessageSearch'>,
+route:
| NavigationRoute<'TogglePinModal'>
- | NavigationRoute<'MessageResultsScreen'>,
+ | NavigationRoute<'MessageResultsScreen'>
+ | NavigationRoute<'MessageSearch'>,
+messageVerticalBounds: ?VerticalBounds,
};
diff --git a/native/chat/message.react.js b/native/chat/message.react.js
--- a/native/chat/message.react.js
+++ b/native/chat/message.react.js
@@ -32,11 +32,13 @@
+navigation:
| ChatNavigationProp<'MessageList'>
| AppNavigationProp<'TogglePinModal'>
- | ChatNavigationProp<'MessageResultsScreen'>,
+ | ChatNavigationProp<'MessageResultsScreen'>
+ | ChatNavigationProp<'MessageSearch'>,
+route:
| NavigationRoute<'MessageList'>
| NavigationRoute<'TogglePinModal'>
- | NavigationRoute<'MessageResultsScreen'>,
+ | NavigationRoute<'MessageResultsScreen'>
+ | NavigationRoute<'MessageSearch'>,
+toggleFocus: (messageKey: string) => void,
+verticalBounds: ?VerticalBounds,
shouldDisplayPinIndicator: boolean,
diff --git a/native/chat/robotext-message.react.js b/native/chat/robotext-message.react.js
--- a/native/chat/robotext-message.react.js
+++ b/native/chat/robotext-message.react.js
@@ -31,11 +31,13 @@
+navigation:
| ChatNavigationProp<'MessageList'>
| AppNavigationProp<'TogglePinModal'>
- | ChatNavigationProp<'MessageResultsScreen'>,
+ | ChatNavigationProp<'MessageResultsScreen'>
+ | ChatNavigationProp<'MessageSearch'>,
+route:
| NavigationRoute<'MessageList'>
| NavigationRoute<'TogglePinModal'>
- | NavigationRoute<'MessageResultsScreen'>,
+ | NavigationRoute<'MessageResultsScreen'>
+ | NavigationRoute<'MessageSearch'>,
+focused: boolean,
+toggleFocus: (messageKey: string) => void,
+verticalBounds: ?VerticalBounds,
diff --git a/native/chat/text-message.react.js b/native/chat/text-message.react.js
--- a/native/chat/text-message.react.js
+++ b/native/chat/text-message.react.js
@@ -41,11 +41,13 @@
+navigation:
| ChatNavigationProp<'MessageList'>
| AppNavigationProp<'TogglePinModal'>
- | ChatNavigationProp<'MessageResultsScreen'>,
+ | ChatNavigationProp<'MessageResultsScreen'>
+ | ChatNavigationProp<'MessageSearch'>,
+route:
| NavigationRoute<'MessageList'>
| NavigationRoute<'TogglePinModal'>
- | NavigationRoute<'MessageResultsScreen'>,
+ | NavigationRoute<'MessageResultsScreen'>
+ | NavigationRoute<'MessageSearch'>,
+focused: boolean,
+toggleFocus: (messageKey: string) => void,
+verticalBounds: ?VerticalBounds,
diff --git a/native/search/search-content.react.js b/native/search/search-content.react.js
new file mode 100644
--- /dev/null
+++ b/native/search/search-content.react.js
@@ -0,0 +1,207 @@
+// @flow
+
+import * as React from 'react';
+import { View } from 'react-native';
+import { FlatList } from 'react-native-gesture-handler';
+
+import { messageListData } from 'lib/selectors/chat-selectors.js';
+import { createMessageInfo } from 'lib/shared/message-utils.js';
+import { useSearchMessages } from 'lib/shared/search-utils.js';
+import type { MessageInfo } from 'lib/types/message-types.js';
+
+import SearchFooter from './search-footer.react.js';
+import { useHeightMeasurer } from '../chat/chat-context.js';
+import type { ChatNavigationProp } from '../chat/chat.react.js';
+import { MessageListContextProvider } from '../chat/message-list-types.js';
+import MessageResult from '../chat/message-result.react.js';
+import ListLoadingIndicator from '../components/list-loading-indicator.react.js';
+import type { NavigationRoute } from '../navigation/route-names.js';
+import { useSelector } from '../redux/redux-utils.js';
+import { useStyles } from '../themes/colors.js';
+import type { ChatMessageItemWithHeight } from '../types/chat-types.js';
+
+type MessageSearchContentProps = {
+ +navigation: ChatNavigationProp<'MessageSearch'>,
+ +route: NavigationRoute<'MessageSearch'>,
+ +query: string,
+ +lastSearchResultsID?: string,
+ +setLastSearchResultsID: (id?: string) => void,
+ +searchResults: $ReadOnlyArray<MessageInfo>,
+ +appendSearchResults: (results: $ReadOnlyArray<MessageInfo>) => void,
+};
+
+function MessageSearchContent(props: MessageSearchContentProps): React.Node {
+ const { threadInfo } = props.route.params;
+ const {
+ query,
+ lastSearchResultsID,
+ setLastSearchResultsID,
+ searchResults,
+ appendSearchResults,
+ } = props;
+
+ const [measuredMessages, setMeasuredMessages] = React.useState([]);
+
+ const { messages, endReached } = useSearchMessages(
+ query,
+ threadInfo.id,
+ lastSearchResultsID,
+ );
+
+ const userInfos = useSelector(state => state.userStore.userInfos);
+
+ const onEndOfLoadedMessagesReached = React.useCallback(() => {
+ if (endReached) {
+ return;
+ }
+ setLastSearchResultsID(oldestMessageID(measuredMessages));
+ }, [endReached, measuredMessages, setLastSearchResultsID]);
+
+ React.useEffect(() => {
+ const threadInfos = { [threadInfo.id]: threadInfo };
+ const translatedSearchResults = messages
+ .map(rawMessageInfo =>
+ createMessageInfo(rawMessageInfo, null, userInfos, threadInfos),
+ )
+ .filter(Boolean);
+
+ appendSearchResults(translatedSearchResults);
+ }, [appendSearchResults, messages, threadInfo, userInfos]);
+
+ const chatMessageInfos = useSelector(
+ messageListData(threadInfo.id, searchResults),
+ );
+
+ const filteredChatMessageInfos = React.useMemo(() => {
+ if (!chatMessageInfos) {
+ return null;
+ }
+
+ const IDSet = new Set(searchResults.map(item => item.id));
+
+ const chatMessageInfoItems = chatMessageInfos.filter(
+ item => item.messageInfo && IDSet.has(item.messageInfo.id),
+ );
+
+ const uniqueChatMessageInfoItemsMap = new Map();
+ chatMessageInfoItems.forEach(
+ item =>
+ item.messageInfo &&
+ item.messageInfo.id &&
+ uniqueChatMessageInfoItemsMap.set(item.messageInfo.id, item),
+ );
+
+ const sortedChatMessageInfoItems = [];
+ for (let i = 0; i < searchResults.length; i++) {
+ sortedChatMessageInfoItems.push(
+ uniqueChatMessageInfoItemsMap.get(searchResults[i].id),
+ );
+ }
+ const loader = chatMessageInfos.find(item => item.itemType === 'loader');
+ if (loader && !endReached) {
+ sortedChatMessageInfoItems.push(loader);
+ }
+
+ return sortedChatMessageInfoItems.filter(Boolean);
+ }, [chatMessageInfos, endReached, searchResults]);
+
+ const measureMessages = useHeightMeasurer();
+ const measureCallback = React.useCallback(
+ (listDataWithHeights: $ReadOnlyArray<ChatMessageItemWithHeight>) => {
+ setMeasuredMessages(listDataWithHeights);
+ },
+ [setMeasuredMessages],
+ );
+
+ React.useEffect(() => {
+ measureMessages(filteredChatMessageInfos, threadInfo, measureCallback);
+ }, [filteredChatMessageInfos, measureCallback, measureMessages, threadInfo]);
+
+ const [messageVerticalBounds, setMessageVerticalBounds] = React.useState();
+ const scrollViewContainerRef = React.useRef();
+
+ const onLayout = React.useCallback(() => {
+ scrollViewContainerRef.current?.measure(
+ (x, y, width, height, pageX, pageY) => {
+ if (
+ height === null ||
+ height === undefined ||
+ pageY === null ||
+ pageY === undefined
+ ) {
+ return;
+ }
+
+ setMessageVerticalBounds({ height, y: pageY });
+ },
+ );
+ }, []);
+
+ const renderItem = React.useCallback(
+ ({ item }) => {
+ if (item.itemType === 'loader') {
+ return <ListLoadingIndicator />;
+ }
+ return (
+ <MessageResult
+ key={item.messageInfo.id}
+ item={item}
+ threadInfo={threadInfo}
+ navigation={props.navigation}
+ route={props.route}
+ messageVerticalBounds={messageVerticalBounds}
+ />
+ );
+ },
+ [messageVerticalBounds, props.navigation, props.route, threadInfo],
+ );
+
+ const footer = React.useMemo(() => {
+ if (!endReached) {
+ return null;
+ }
+ if (measuredMessages.length > 0) {
+ return <SearchFooter text="End of results" />;
+ }
+ const text =
+ 'No results, please try using different\nkeywords to refine your search';
+ return <SearchFooter text={text} />;
+ }, [measuredMessages.length, endReached]);
+
+ const styles = useStyles(unboundStyles);
+
+ return (
+ <MessageListContextProvider threadInfo={threadInfo}>
+ <View
+ style={styles.content}
+ ref={scrollViewContainerRef}
+ onLayout={onLayout}
+ >
+ <FlatList
+ renderItem={renderItem}
+ data={measuredMessages}
+ onEndReached={onEndOfLoadedMessagesReached}
+ ListFooterComponent={footer}
+ />
+ </View>
+ </MessageListContextProvider>
+ );
+}
+
+function oldestMessageID(data: $ReadOnlyArray<ChatMessageItemWithHeight>) {
+ for (let i = data.length - 1; i >= 0; i--) {
+ if (data[i].itemType === 'message' && data[i].messageInfo.id) {
+ return data[i].messageInfo.id;
+ }
+ }
+ return undefined;
+}
+
+const unboundStyles = {
+ content: {
+ height: '100%',
+ backgroundColor: 'panelBackground',
+ },
+};
+
+export default MessageSearchContent;

File Metadata

Mime Type
text/plain
Expires
Wed, Nov 27, 7:10 AM (20 h, 37 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2589305
Default Alt Text
D7774.id26419.diff (9 KB)

Event Timeline