From 40f2482e51a0a3aab5d3b22f6dcf4a10d4b68d5b Mon Sep 17 00:00:00 2001 From: Olgierd Date: Fri, 28 Nov 2025 18:12:32 +0100 Subject: [PATCH 1/7] Make ShareTab use new SelectionList --- src/pages/Share/ShareTab.tsx | 55 +++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/src/pages/Share/ShareTab.tsx b/src/pages/Share/ShareTab.tsx index c767fc7a433d..afbe96e9591f 100644 --- a/src/pages/Share/ShareTab.tsx +++ b/src/pages/Share/ShareTab.tsx @@ -1,9 +1,9 @@ import type {Ref} from 'react'; import React, {useEffect, useImperativeHandle, useMemo, useRef} from 'react'; import {useOptionsList} from '@components/OptionListContextProvider'; -import SelectionList from '@components/SelectionListWithSections'; -import InviteMemberListItem from '@components/SelectionListWithSections/InviteMemberListItem'; -import type {SelectionListHandle} from '@components/SelectionListWithSections/types'; +import SelectionList from '@components/SelectionList'; +import InviteMemberListItem from '@components/SelectionList/ListItem/InviteMemberListItem'; +import type {SelectionListHandle} from '@components/SelectionList/types'; import useDebouncedState from '@hooks/useDebouncedState'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; @@ -48,10 +48,6 @@ function ShareTab({ref}: ShareTabProps) { const [draftComments] = useOnyx(ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT, {canBeMissing: true}); const [nvpDismissedProductTraining] = useOnyx(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, {canBeMissing: true}); - useImperativeHandle(ref, () => ({ - focus: selectionListRef.current?.focusTextInput, - })); - const {options, areOptionsInitialized} = useOptionsList(); const {didScreenTransitionEnd} = useScreenWrapperTransitionStatus(); const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false, canBeMissing: true}); @@ -97,18 +93,19 @@ function ShareTab({ref}: ShareTabProps) { searchInServer(debouncedTextInputValue.trim()); }, [debouncedTextInputValue]); - const styledRecentReports = recentReportsOptions.map((item) => ({ - ...item, - pressableStyle: styles.br2, - text: StringUtils.lineBreaksToSpaces(item.text), - wrapperStyle: [styles.pr3, styles.pl3], - })); - - const [sections, header] = useMemo(() => { - const newSections = []; - newSections.push({title: textInputValue.trim() === '' ? translate('search.recentChats') : undefined, data: styledRecentReports}); + const styledRecentReports = useMemo(() => { + return recentReportsOptions.map((item, index) => ({ + ...item, + pressableStyle: styles.br2, + text: StringUtils.lineBreaksToSpaces(item.text), + wrapperStyle: [styles.pr3, styles.pl3], + keyForList: String(item.reportID) + String(index), + })); + }, [recentReportsOptions, styles]); + + const header = useMemo(() => { const headerMessage = getHeaderMessage(styledRecentReports.length !== 0, false, textInputValue.trim(), countryCode, false); - return [newSections, headerMessage]; + return headerMessage; }, [textInputValue, styledRecentReports, translate, countryCode]); const onSelectRow = (item: OptionData) => { @@ -128,21 +125,27 @@ function ShareTab({ref}: ShareTabProps) { } }; + const textInputOptions = useMemo( + () => ({ + value: textInputValue, + label: translate('selectionList.nameEmailOrPhoneNumber'), + hint: offlineMessage, + onChangeText: setTextInputValue, + headerMessage: header, + }), + [textInputValue, setTextInputValue, translate, offlineMessage, setTextInputValue, header], + ); + return ( ); From 7dd107275efb457f5045030a5bbbeef3c0bea16d Mon Sep 17 00:00:00 2001 From: Olgierd Date: Mon, 1 Dec 2025 14:21:41 +0100 Subject: [PATCH 2/7] Add customListHeaderContent with 'Recent chats' text in ShareTab --- src/pages/Share/ShareTab.tsx | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/pages/Share/ShareTab.tsx b/src/pages/Share/ShareTab.tsx index afbe96e9591f..747d74687a5d 100644 --- a/src/pages/Share/ShareTab.tsx +++ b/src/pages/Share/ShareTab.tsx @@ -1,9 +1,10 @@ -import type {Ref} from 'react'; -import React, {useEffect, useImperativeHandle, useMemo, useRef} from 'react'; +import React, {useEffect, useMemo, useRef} from 'react'; +import {View} from 'react-native'; import {useOptionsList} from '@components/OptionListContextProvider'; import SelectionList from '@components/SelectionList'; import InviteMemberListItem from '@components/SelectionList/ListItem/InviteMemberListItem'; import type {SelectionListHandle} from '@components/SelectionList/types'; +import Text from '@components/Text'; import useDebouncedState from '@hooks/useDebouncedState'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; @@ -28,16 +29,7 @@ const defaultListOptions = { categoryOptions: [], }; -type ShareTabRef = { - focus?: () => void; -}; - -type ShareTabProps = { - /** Reference to the outer element */ - ref?: Ref; -}; - -function ShareTab({ref}: ShareTabProps) { +function ShareTab() { const styles = useThemeStyles(); const {translate} = useLocalize(); const {isOffline} = useNetwork(); @@ -106,7 +98,7 @@ function ShareTab({ref}: ShareTabProps) { const header = useMemo(() => { const headerMessage = getHeaderMessage(styledRecentReports.length !== 0, false, textInputValue.trim(), countryCode, false); return headerMessage; - }, [textInputValue, styledRecentReports, translate, countryCode]); + }, [textInputValue, styledRecentReports, countryCode]); const onSelectRow = (item: OptionData) => { let reportID = item?.reportID ?? CONST.DEFAULT_NUMBER_ID; @@ -133,12 +125,20 @@ function ShareTab({ref}: ShareTabProps) { onChangeText: setTextInputValue, headerMessage: header, }), - [textInputValue, setTextInputValue, translate, offlineMessage, setTextInputValue, header], + [textInputValue, setTextInputValue, translate, offlineMessage, header], ); + const customListHeader = + textInputValue.trim() === '' ? ( + + {translate('search.recentChats')} + + ) : undefined; + return ( Date: Mon, 1 Dec 2025 16:40:38 +0100 Subject: [PATCH 3/7] Bring back the ref usage --- .../SelectionList/BaseSelectionList.tsx | 8 +++++++- src/components/SelectionList/types.ts | 3 +++ src/pages/Share/ShareTab.tsx | 19 +++++++++++++++++-- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/components/SelectionList/BaseSelectionList.tsx b/src/components/SelectionList/BaseSelectionList.tsx index 48e732d4a182..87ce5aa91d37 100644 --- a/src/components/SelectionList/BaseSelectionList.tsx +++ b/src/components/SelectionList/BaseSelectionList.tsx @@ -406,7 +406,13 @@ function BaseSelectionList({ } }, [onSelectAll, shouldShowTextInput, shouldPreventDefaultFocusOnSelectRow]); - useImperativeHandle(ref, () => ({scrollAndHighlightItem, scrollToIndex, updateFocusedIndex}), [scrollAndHighlightItem, scrollToIndex, updateFocusedIndex]); + useImperativeHandle(ref, () => ({scrollAndHighlightItem, scrollToIndex, updateFocusedIndex, focusTextInput}), [ + focusTextInput, + scrollAndHighlightItem, + scrollToIndex, + updateFocusedIndex, + ]); + return ( {textInputComponent({shouldBeInsideList: false})} diff --git a/src/components/SelectionList/types.ts b/src/components/SelectionList/types.ts index 3823399aad25..cf1770060d3e 100644 --- a/src/components/SelectionList/types.ts +++ b/src/components/SelectionList/types.ts @@ -219,6 +219,9 @@ type SelectionListHandle = { /** Updates the focused index and optionally scrolls to it */ updateFocusedIndex: (newFocusedIndex: number, shouldScroll?: boolean) => void; + + /** Sets the focus to the textInput component */ + focusTextInput: () => void; }; type DataDetailsType = { diff --git a/src/pages/Share/ShareTab.tsx b/src/pages/Share/ShareTab.tsx index 747d74687a5d..26785bf67338 100644 --- a/src/pages/Share/ShareTab.tsx +++ b/src/pages/Share/ShareTab.tsx @@ -1,4 +1,5 @@ -import React, {useEffect, useMemo, useRef} from 'react'; +import type {Ref} from 'react'; +import React, {useEffect, useImperativeHandle, useMemo, useRef} from 'react'; import {View} from 'react-native'; import {useOptionsList} from '@components/OptionListContextProvider'; import SelectionList from '@components/SelectionList'; @@ -29,7 +30,16 @@ const defaultListOptions = { categoryOptions: [], }; -function ShareTab() { +type ShareTabRef = { + focus?: () => void; +}; + +type ShareTabProps = { + /** Reference to the outer element */ + ref?: Ref; +}; + +function ShareTab({ref}: ShareTabProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const {isOffline} = useNetwork(); @@ -40,6 +50,10 @@ function ShareTab() { const [draftComments] = useOnyx(ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT, {canBeMissing: true}); const [nvpDismissedProductTraining] = useOnyx(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, {canBeMissing: true}); + useImperativeHandle(ref, () => ({ + focus: selectionListRef.current?.focusTextInput, + })); + const {options, areOptionsInitialized} = useOptionsList(); const {didScreenTransitionEnd} = useScreenWrapperTransitionStatus(); const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false, canBeMissing: true}); @@ -124,6 +138,7 @@ function ShareTab() { hint: offlineMessage, onChangeText: setTextInputValue, headerMessage: header, + disableAutoFocus: true, }), [textInputValue, setTextInputValue, translate, offlineMessage, header], ); From ffe356b0ef7f40642a44e0d79ee315eac85f4d61 Mon Sep 17 00:00:00 2001 From: Olgierd Date: Mon, 1 Dec 2025 17:00:45 +0100 Subject: [PATCH 4/7] Use more specific prop in dependency array --- src/pages/Share/ShareTab.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Share/ShareTab.tsx b/src/pages/Share/ShareTab.tsx index 26785bf67338..502ece307130 100644 --- a/src/pages/Share/ShareTab.tsx +++ b/src/pages/Share/ShareTab.tsx @@ -112,7 +112,7 @@ function ShareTab({ref}: ShareTabProps) { const header = useMemo(() => { const headerMessage = getHeaderMessage(styledRecentReports.length !== 0, false, textInputValue.trim(), countryCode, false); return headerMessage; - }, [textInputValue, styledRecentReports, countryCode]); + }, [textInputValue, styledRecentReports.length, countryCode]); const onSelectRow = (item: OptionData) => { let reportID = item?.reportID ?? CONST.DEFAULT_NUMBER_ID; From 23d87413bb21f0f3fc030a4aafb59d570c068f64 Mon Sep 17 00:00:00 2001 From: Olgierd Date: Wed, 3 Dec 2025 11:58:24 +0100 Subject: [PATCH 5/7] Improve keyForList string in ShareTab's styledRecentReports --- src/pages/Share/ShareTab.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Share/ShareTab.tsx b/src/pages/Share/ShareTab.tsx index 502ece307130..3c4ac7178ea3 100644 --- a/src/pages/Share/ShareTab.tsx +++ b/src/pages/Share/ShareTab.tsx @@ -105,7 +105,7 @@ function ShareTab({ref}: ShareTabProps) { pressableStyle: styles.br2, text: StringUtils.lineBreaksToSpaces(item.text), wrapperStyle: [styles.pr3, styles.pl3], - keyForList: String(item.reportID) + String(index), + keyForList: `${item.reportID}-${index}`, })); }, [recentReportsOptions, styles]); From af751f115291e06958372ae7b4e1bb5230f71564 Mon Sep 17 00:00:00 2001 From: Olgierd Date: Wed, 3 Dec 2025 14:00:01 +0100 Subject: [PATCH 6/7] Memorize customListHeader in ShareTab component --- src/pages/Share/ShareTab.tsx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/pages/Share/ShareTab.tsx b/src/pages/Share/ShareTab.tsx index 3c4ac7178ea3..4e262c56169b 100644 --- a/src/pages/Share/ShareTab.tsx +++ b/src/pages/Share/ShareTab.tsx @@ -143,12 +143,15 @@ function ShareTab({ref}: ShareTabProps) { [textInputValue, setTextInputValue, translate, offlineMessage, header], ); - const customListHeader = - textInputValue.trim() === '' ? ( - - {translate('search.recentChats')} - - ) : undefined; + const customListHeader = useMemo( + () => + textInputValue.trim() === '' ? ( + + {translate('search.recentChats')} + + ) : undefined, + [textInputValue, styles, translate], + ); return ( Date: Thu, 4 Dec 2025 16:52:04 +0100 Subject: [PATCH 7/7] Fix blinking 'No results found' when changing the text input --- src/components/SelectionList/components/TextInput.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/SelectionList/components/TextInput.tsx b/src/components/SelectionList/components/TextInput.tsx index c2ebd6c66ce2..1a602c9bfa38 100644 --- a/src/components/SelectionList/components/TextInput.tsx +++ b/src/components/SelectionList/components/TextInput.tsx @@ -68,7 +68,7 @@ function TextInput({ const {label, value, onChangeText, errorText, headerMessage, hint, disableAutoFocus, placeholder, maxLength, inputMode, ref: optionsRef} = options ?? {}; const resultsFound = headerMessage !== translate('common.noResultsFound'); const noData = dataLength === 0 && !showLoadingPlaceholder; - const shouldShowHeaderMessage = !!headerMessage && (!isLoadingNewOptions || resultsFound || !noData); + const shouldShowHeaderMessage = !!headerMessage && (!isLoadingNewOptions || resultsFound || noData); const focusTimeoutRef = useRef(null); const mergedRef = mergeRefs(ref, optionsRef);