@@ -2,41 +2,35 @@ import lodashPick from 'lodash/pick';
22import React , { memo , useCallback , useEffect , useMemo } from 'react' ;
33import type { GestureResponderEvent } from 'react-native' ;
44import EmptySelectionListContent from '@components/EmptySelectionListContent' ;
5- import { usePersonalDetails } from '@components/OnyxListItemProvider' ;
6- import { useOptionsList } from '@components/OptionListContextProvider' ;
75import InviteMemberListItem from '@components/SelectionList/ListItem/InviteMemberListItem' ;
86import SelectionListWithSections from '@components/SelectionList/SelectionListWithSections' ;
7+ import type { Section } from '@components/SelectionList/SelectionListWithSections/types' ;
98import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails' ;
109import useDebouncedState from '@hooks/useDebouncedState' ;
1110import useLocalize from '@hooks/useLocalize' ;
1211import useNetwork from '@hooks/useNetwork' ;
1312import useOnyx from '@hooks/useOnyx' ;
14- import usePrivateIsArchivedMap from '@hooks/usePrivateIsArchivedMap' ;
15- import useReportAttributes from '@hooks/useReportAttributes' ;
13+ import usePersonalDetailOptions from '@hooks/usePersonalDetailOptions' ;
1614import useScreenWrapperTransitionStatus from '@hooks/useScreenWrapperTransitionStatus' ;
17- import useUserToInviteReports from '@hooks/useUserToInviteReports' ;
1815import { canUseTouchScreen } from '@libs/DeviceCapabilities' ;
1916import memoize from '@libs/memoize' ;
20- import {
21- filterAndOrderOptions ,
22- formatSectionsFromSearchTerm ,
23- getEmptyOptions ,
24- getHeaderMessage ,
25- getParticipantsOption ,
26- getPolicyExpenseReportOption ,
27- getValidOptions ,
28- isCurrentUser ,
29- orderOptions ,
30- } from '@libs/OptionsListUtils' ;
31- import type { SelectionListSections } from '@libs/OptionsListUtils/types' ;
17+ import { getHeaderMessage , getValidOptions } from '@libs/PersonalDetailOptionsListUtils' ;
18+ import type { OptionData } from '@libs/PersonalDetailOptionsListUtils/types' ;
3219import { searchUserInServer } from '@userActions/Report' ;
33- import type { IOUAction , IOUType } from '@src/CONST' ;
20+ import type { IOUType } from '@src/CONST' ;
3421import CONST from '@src/CONST' ;
3522import ONYXKEYS from '@src/ONYXKEYS' ;
3623import type { Accountant } from '@src/types/onyx/IOU' ;
3724
3825const memoizedGetValidOptions = memoize ( getValidOptions , { maxSize : 5 , monitoringName : 'MoneyRequestAccountantSelector.getValidOptions' } ) ;
3926
27+ const defaultListOptions = {
28+ userToInvite : null ,
29+ recentOptions : [ ] as OptionData [ ] ,
30+ personalDetails : [ ] as OptionData [ ] ,
31+ selectedOptions : [ ] as OptionData [ ] ,
32+ } ;
33+
4034type MoneyRequestAccountantSelectorProps = {
4135 /** Callback to request parent modal to go to next step */
4236 onFinish : ( value ?: string ) => void ;
@@ -46,196 +40,71 @@ type MoneyRequestAccountantSelectorProps = {
4640
4741 /** The type of IOU report, i.e. split, request, send, track */
4842 iouType : IOUType ;
49-
50- /** The action of the IOU, i.e. create, split, move */
51- action : IOUAction ;
5243} ;
5344
54- function MoneyRequestAccountantSelector ( { onFinish, onAccountantSelected, iouType, action } : MoneyRequestAccountantSelectorProps ) {
55- const { translate} = useLocalize ( ) ;
45+ function MoneyRequestAccountantSelector ( { onFinish, onAccountantSelected, iouType} : MoneyRequestAccountantSelectorProps ) {
46+ const { translate, formatPhoneNumber } = useLocalize ( ) ;
5647 const [ searchTerm , debouncedSearchTerm , setSearchTerm ] = useDebouncedState ( '' ) ;
5748 const { isOffline} = useNetwork ( ) ;
58- const personalDetails = usePersonalDetails ( ) ;
5949 const { didScreenTransitionEnd} = useScreenWrapperTransitionStatus ( ) ;
6050 const [ countryCode = CONST . DEFAULT_COUNTRY_CODE ] = useOnyx ( ONYXKEYS . COUNTRY_CODE ) ;
61- const [ betas ] = useOnyx ( ONYXKEYS . BETAS ) ;
6251 const [ isSearchingForReports ] = useOnyx ( ONYXKEYS . RAM_ONLY_IS_SEARCHING_FOR_REPORTS ) ;
63- const { options, areOptionsInitialized} = useOptionsList ( {
64- shouldInitialize : didScreenTransitionEnd ,
65- } ) ;
66- const offlineMessage : string = isOffline ? `${ translate ( 'common.youAppearToBeOffline' ) } ${ translate ( 'search.resultsAreLimited' ) } ` : '' ;
67- const reportAttributesDerived = useReportAttributes ( ) ;
68- const [ draftComments ] = useOnyx ( ONYXKEYS . COLLECTION . REPORT_DRAFT_COMMENT ) ;
69- const [ nvpDismissedProductTraining ] = useOnyx ( ONYXKEYS . NVP_DISMISSED_PRODUCT_TRAINING ) ;
7052 const [ loginList ] = useOnyx ( ONYXKEYS . LOGIN_LIST ) ;
7153 const currentUserPersonalDetails = useCurrentUserPersonalDetails ( ) ;
7254 const currentUserEmail = currentUserPersonalDetails . email ?? '' ;
73- const currentUserAccountID = currentUserPersonalDetails . accountID ;
74- const [ allPolicies ] = useOnyx ( ONYXKEYS . COLLECTION . POLICY ) ;
75- const [ conciergeReportID ] = useOnyx ( ONYXKEYS . CONCIERGE_REPORT_ID ) ;
76- const privateIsArchivedMap = usePrivateIsArchivedMap ( ) ;
55+ const { options : personalDetailOptions } = usePersonalDetailOptions ( { enabled : didScreenTransitionEnd } ) ;
56+ const areOptionsInitialized = ( personalDetailOptions ?. length ?? 0 ) > 0 ;
57+ const offlineMessage : string = isOffline ? `${ translate ( 'common.youAppearToBeOffline' ) } ${ translate ( 'search.resultsAreLimited' ) } ` : '' ;
7758
7859 useEffect ( ( ) => {
7960 searchUserInServer ( debouncedSearchTerm . trim ( ) ) ;
8061 } , [ debouncedSearchTerm ] ) ;
8162
82- const defaultOptions = useMemo ( ( ) => {
63+ const optionsList = useMemo ( ( ) => {
8364 if ( ! areOptionsInitialized || ! didScreenTransitionEnd ) {
84- getEmptyOptions ( ) ;
65+ return defaultListOptions ;
8566 }
8667
87- const optionList = memoizedGetValidOptions (
88- {
89- reports : options . reports ,
90- personalDetails : options . personalDetails ,
91- } ,
92- allPolicies ,
93- draftComments ,
94- nvpDismissedProductTraining ,
95- loginList ,
96- currentUserAccountID ,
97- currentUserEmail ,
98- conciergeReportID ,
99- {
100- betas,
101- excludeLogins : CONST . EXPENSIFY_EMAILS_OBJECT ,
102- action,
103- personalDetails,
104- countryCode,
105- } ,
106- ) ;
107-
108- const orderedOptions = orderOptions ( optionList ) ;
109-
110- return {
111- ...optionList ,
112- ...orderedOptions ,
113- } ;
114- } , [
115- areOptionsInitialized ,
116- didScreenTransitionEnd ,
117- options . reports ,
118- options . personalDetails ,
119- allPolicies ,
120- draftComments ,
121- nvpDismissedProductTraining ,
122- loginList ,
123- betas ,
124- action ,
125- countryCode ,
126- currentUserAccountID ,
127- currentUserEmail ,
128- conciergeReportID ,
129- personalDetails ,
130- ] ) ;
131-
132- const chatOptions = useMemo ( ( ) => {
133- if ( ! areOptionsInitialized ) {
134- return {
135- userToInvite : null ,
136- recentReports : [ ] ,
137- personalDetails : [ ] ,
138- currentUserOption : null ,
139- headerMessage : '' ,
140- } ;
141- }
142- const newOptions = filterAndOrderOptions ( defaultOptions , debouncedSearchTerm , countryCode , loginList , currentUserEmail , currentUserAccountID , personalDetails , {
68+ return memoizedGetValidOptions ( personalDetailOptions ?? [ ] , currentUserEmail , formatPhoneNumber , countryCode , loginList , {
14369 excludeLogins : CONST . EXPENSIFY_EMAILS_OBJECT ,
144- maxRecentReportsToShow : CONST . IOU . MAX_RECENT_REPORTS_TO_SHOW ,
70+ searchString : debouncedSearchTerm ,
71+ includeUserToInvite : true ,
14572 } ) ;
146- return newOptions ;
147- } , [ areOptionsInitialized , defaultOptions , debouncedSearchTerm , countryCode , loginList , currentUserAccountID , currentUserEmail , personalDetails ] ) ;
148-
149- const { userToInviteExpenseReport} = useUserToInviteReports ( chatOptions ?. userToInvite ) ;
150- const userToInviteExpenseReportPolicy = allPolicies ?. [ `${ ONYXKEYS . COLLECTION . POLICY } ${ userToInviteExpenseReport ?. policyID } ` ] ;
73+ } , [ areOptionsInitialized , didScreenTransitionEnd , personalDetailOptions , currentUserEmail , formatPhoneNumber , countryCode , loginList , debouncedSearchTerm ] ) ;
15174
152- /**
153- * Returns the sections needed for the OptionsSelector
154- */
155- const [ sections , header ] = useMemo ( ( ) => {
156- const newSections : SelectionListSections = [ ] ;
75+ const sections = useMemo ( ( ) => {
76+ const newSections : Array < Section < OptionData > > = [ ] ;
15777 if ( ! areOptionsInitialized || ! didScreenTransitionEnd ) {
158- return [ newSections , '' ] ;
78+ return newSections ;
15979 }
160- const fiveRecents = [ ...chatOptions . recentReports ] . slice ( 0 , 5 ) ;
161- const restOfRecents = [ ...chatOptions . recentReports ] . slice ( 5 ) ;
162- const contactsWithRestOfRecents = [ ...restOfRecents , ...chatOptions . personalDetails ] ;
163-
164- const formatResults = formatSectionsFromSearchTerm (
165- debouncedSearchTerm ,
166- [ ] ,
167- chatOptions . recentReports ,
168- chatOptions . personalDetails ,
169- privateIsArchivedMap ,
170- currentUserAccountID ,
171- allPolicies ,
172- personalDetails ,
173- true ,
174- undefined ,
175- reportAttributesDerived ,
176- ) ;
177- newSections . push ( { ...formatResults . section , sectionIndex : 0 } ) ;
17880
179- newSections . push ( {
180- title : translate ( 'common.recents' ) ,
181- data : fiveRecents ,
182- sectionIndex : 1 ,
183- } ) ;
81+ let sectionIndex = 0 ;
18482
185- newSections . push ( {
186- title : translate ( 'common.contacts' ) ,
187- data : contactsWithRestOfRecents ,
188- sectionIndex : 2 ,
189- } ) ;
83+ if ( optionsList . recentOptions . length > 0 ) {
84+ newSections . push ( {
85+ title : translate ( 'common.recents' ) ,
86+ data : optionsList . recentOptions ,
87+ sectionIndex : sectionIndex ++ ,
88+ } ) ;
89+ }
19090
191- if (
192- chatOptions . userToInvite &&
193- ! isCurrentUser (
194- { ...chatOptions . userToInvite , accountID : chatOptions . userToInvite ?. accountID ?? CONST . DEFAULT_NUMBER_ID , status : chatOptions . userToInvite ?. status ?? undefined } ,
195- loginList ,
196- currentUserEmail ,
197- )
198- ) {
91+ if ( optionsList . personalDetails . length > 0 ) {
19992 newSections . push ( {
200- data : [ chatOptions . userToInvite ] . map ( ( participant ) => {
201- const isPolicyExpenseChat = participant ?. isPolicyExpenseChat ?? false ;
202- const privateIsArchived = privateIsArchivedMap [ `${ ONYXKEYS . COLLECTION . REPORT_NAME_VALUE_PAIRS } ${ userToInviteExpenseReport ?. reportID } ` ] ;
203- return isPolicyExpenseChat
204- ? getPolicyExpenseReportOption ( participant , privateIsArchived , personalDetails , userToInviteExpenseReport , userToInviteExpenseReportPolicy , reportAttributesDerived )
205- : getParticipantsOption ( participant , personalDetails ) ;
206- } ) ,
207- sectionIndex : 3 ,
93+ title : translate ( 'common.contacts' ) ,
94+ data : optionsList . personalDetails ,
95+ sectionIndex : sectionIndex ++ ,
20896 } ) ;
20997 }
21098
211- const headerMessage = getHeaderMessage (
212- ( chatOptions . personalDetails ?? [ ] ) . length + ( chatOptions . recentReports ?? [ ] ) . length !== 0 ,
213- ! ! chatOptions ?. userToInvite ,
214- debouncedSearchTerm . trim ( ) ,
215- countryCode ,
216- false ,
217- ) ;
99+ if ( optionsList . userToInvite ) {
100+ newSections . push ( {
101+ data : [ optionsList . userToInvite ] ,
102+ sectionIndex : sectionIndex ++ ,
103+ } ) ;
104+ }
218105
219- return [ newSections , headerMessage ] ;
220- } , [
221- areOptionsInitialized ,
222- didScreenTransitionEnd ,
223- chatOptions . recentReports ,
224- chatOptions . personalDetails ,
225- chatOptions . userToInvite ,
226- debouncedSearchTerm ,
227- personalDetails ,
228- userToInviteExpenseReport ,
229- userToInviteExpenseReportPolicy ,
230- reportAttributesDerived ,
231- translate ,
232- loginList ,
233- countryCode ,
234- privateIsArchivedMap ,
235- currentUserAccountID ,
236- currentUserEmail ,
237- allPolicies ,
238- ] ) ;
106+ return newSections ;
107+ } , [ areOptionsInitialized , didScreenTransitionEnd , optionsList . recentOptions , optionsList . personalDetails , optionsList . userToInvite , translate ] ) ;
239108
240109 const selectAccountant = useCallback (
241110 ( option : Accountant ) => {
@@ -256,6 +125,17 @@ function MoneyRequestAccountantSelector({onFinish, onAccountantSelected, iouType
256125 [ selectAccountant ] ,
257126 ) ;
258127
128+ const getHeaderMessageText = ( ) => {
129+ if ( sections . length > 0 ) {
130+ return '' ;
131+ }
132+ const searchValue = debouncedSearchTerm . trim ( ) . toLowerCase ( ) ;
133+ if ( CONST . EXPENSIFY_EMAILS_OBJECT [ searchValue ] ) {
134+ return translate ( 'messages.errorMessageInvalidEmail' ) ;
135+ }
136+ return getHeaderMessage ( translate , debouncedSearchTerm , countryCode ) ;
137+ } ;
138+
259139 const shouldShowLoadingPlaceholder = useMemo ( ( ) => ! areOptionsInitialized || ! didScreenTransitionEnd , [ areOptionsInitialized , didScreenTransitionEnd ] ) ;
260140
261141 const optionLength = useMemo ( ( ) => {
@@ -271,7 +151,7 @@ function MoneyRequestAccountantSelector({onFinish, onAccountantSelected, iouType
271151 value : searchTerm ,
272152 label : translate ( 'selectionList.nameEmailOrPhoneNumber' ) ,
273153 onChangeText : setSearchTerm ,
274- headerMessage : header ,
154+ headerMessage : getHeaderMessageText ( ) ,
275155 hint : offlineMessage ,
276156 } ;
277157
0 commit comments