-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Fix - Update the Group Creation flow #55030
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
9bf59e7
75d700d
9e68afc
79b0534
25e1d32
6df9e9b
ef09dba
ceecfc6
e5de3ad
444384d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,13 +25,22 @@ import useSafeAreaInsets from '@hooks/useSafeAreaInsets'; | |
| import useScreenWrapperTranstionStatus from '@hooks/useScreenWrapperTransitionStatus'; | ||
| import useStyledSafeAreaInsets from '@hooks/useStyledSafeAreaInsets'; | ||
| import useThemeStyles from '@hooks/useThemeStyles'; | ||
| import * as DeviceCapabilities from '@libs/DeviceCapabilities'; | ||
| import {navigateToAndOpenReport, searchInServer, setGroupDraft} from '@libs/actions/Report'; | ||
| import {canUseTouchScreen} from '@libs/DeviceCapabilities'; | ||
| import Log from '@libs/Log'; | ||
| import Navigation from '@libs/Navigation/Navigation'; | ||
| import * as OptionsListUtils from '@libs/OptionsListUtils'; | ||
| import type {Option, Section} from '@libs/OptionsListUtils'; | ||
| import { | ||
| filterAndOrderOptions, | ||
| formatSectionsFromSearchTerm, | ||
| getFirstKeyForList, | ||
| getHeaderMessage, | ||
| getPersonalDetailSearchTerms, | ||
| getUserToInviteOption, | ||
| getValidOptions, | ||
| } from '@libs/OptionsListUtils'; | ||
| import type {OptionData} from '@libs/ReportUtils'; | ||
| import variables from '@styles/variables'; | ||
| import * as Report from '@userActions/Report'; | ||
| import CONST from '@src/CONST'; | ||
| import ONYXKEYS from '@src/ONYXKEYS'; | ||
| import ROUTES from '@src/ROUTES'; | ||
|
|
@@ -51,7 +60,7 @@ function useOptions() { | |
| }); | ||
|
|
||
| const defaultOptions = useMemo(() => { | ||
| const filteredOptions = OptionsListUtils.getValidOptions( | ||
| const filteredOptions = getValidOptions( | ||
| { | ||
| reports: listOptions.reports ?? [], | ||
| personalDetails: listOptions.personalDetails ?? [], | ||
|
|
@@ -66,7 +75,7 @@ function useOptions() { | |
| }, [betas, listOptions.personalDetails, listOptions.reports, selectedOptions]); | ||
|
|
||
| const options = useMemo(() => { | ||
| const filteredOptions = OptionsListUtils.filterAndOrderOptions(defaultOptions, debouncedSearchTerm, { | ||
| const filteredOptions = filterAndOrderOptions(defaultOptions, debouncedSearchTerm, { | ||
| selectedOptions, | ||
| maxRecentReportsToShow: CONST.IOU.MAX_RECENT_REPORTS_TO_SHOW, | ||
| }); | ||
|
|
@@ -75,11 +84,11 @@ function useOptions() { | |
| }, [debouncedSearchTerm, defaultOptions, selectedOptions]); | ||
| const cleanSearchTerm = useMemo(() => debouncedSearchTerm.trim().toLowerCase(), [debouncedSearchTerm]); | ||
| const headerMessage = useMemo(() => { | ||
| return OptionsListUtils.getHeaderMessage( | ||
| return getHeaderMessage( | ||
| options.personalDetails.length + options.recentReports.length !== 0, | ||
| !!options.userToInvite, | ||
| debouncedSearchTerm.trim(), | ||
| selectedOptions.some((participant) => OptionsListUtils.getPersonalDetailSearchTerms(participant).join(' ').toLowerCase?.().includes(cleanSearchTerm)), | ||
| selectedOptions.some((participant) => getPersonalDetailSearchTerms(participant).join(' ').toLowerCase?.().includes(cleanSearchTerm)), | ||
| ); | ||
| }, [cleanSearchTerm, debouncedSearchTerm, options.personalDetails.length, options.recentReports.length, options.userToInvite, selectedOptions]); | ||
|
|
||
|
|
@@ -88,7 +97,7 @@ function useOptions() { | |
| return; | ||
| } | ||
|
|
||
| Report.searchInServer(debouncedSearchTerm); | ||
| searchInServer(debouncedSearchTerm); | ||
| }, [debouncedSearchTerm]); | ||
|
|
||
| useEffect(() => { | ||
|
|
@@ -102,7 +111,7 @@ function useOptions() { | |
| } | ||
| let participantOption: OptionData | undefined | null = listOptions.personalDetails.find((option) => option.accountID === participant.accountID); | ||
| if (!participantOption) { | ||
| participantOption = OptionsListUtils.getUserToInviteOption({ | ||
| participantOption = getUserToInviteOption({ | ||
| searchValue: participant?.login, | ||
| }); | ||
| } | ||
|
|
@@ -146,23 +155,23 @@ function NewChatPage() { | |
| useOptions(); | ||
|
|
||
| const [sections, firstKeyForList] = useMemo(() => { | ||
| const sectionsList: OptionsListUtils.Section[] = []; | ||
| const sectionsList: Section[] = []; | ||
| let firstKey = ''; | ||
|
|
||
| const formatResults = OptionsListUtils.formatSectionsFromSearchTerm(debouncedSearchTerm, selectedOptions, recentReports, personalDetails); | ||
| const formatResults = formatSectionsFromSearchTerm(debouncedSearchTerm, selectedOptions, recentReports, personalDetails); | ||
| sectionsList.push(formatResults.section); | ||
|
|
||
| if (!firstKey) { | ||
| firstKey = OptionsListUtils.getFirstKeyForList(formatResults.section.data); | ||
| firstKey = getFirstKeyForList(formatResults.section.data); | ||
| } | ||
|
|
||
| sectionsList.push({ | ||
| title: translate('common.recents'), | ||
| data: recentReports, | ||
| data: selectedOptions.length ? recentReports.filter((option) => !option.isSelfDM) : recentReports, | ||
| shouldShow: !isEmpty(recentReports), | ||
| }); | ||
| if (!firstKey) { | ||
| firstKey = OptionsListUtils.getFirstKeyForList(recentReports); | ||
| firstKey = getFirstKeyForList(recentReports); | ||
| } | ||
|
|
||
| sectionsList.push({ | ||
|
|
@@ -171,7 +180,7 @@ function NewChatPage() { | |
| shouldShow: !isEmpty(personalDetails), | ||
| }); | ||
| if (!firstKey) { | ||
| firstKey = OptionsListUtils.getFirstKeyForList(personalDetails); | ||
| firstKey = getFirstKeyForList(personalDetails); | ||
| } | ||
|
|
||
| if (userToInvite) { | ||
|
|
@@ -181,23 +190,52 @@ function NewChatPage() { | |
| shouldShow: true, | ||
| }); | ||
| if (!firstKey) { | ||
| firstKey = OptionsListUtils.getFirstKeyForList([userToInvite]); | ||
| firstKey = getFirstKeyForList([userToInvite]); | ||
| } | ||
| } | ||
|
|
||
| return [sectionsList, firstKey]; | ||
| }, [debouncedSearchTerm, selectedOptions, recentReports, personalDetails, translate, userToInvite]); | ||
|
|
||
| /** | ||
| * Creates a new 1:1 chat with the option and the current user, | ||
| * Removes a selected option from list if already selected. If not already selected add this option to the list. | ||
| */ | ||
| const toggleOption = useCallback( | ||
| (option: ListItem & Partial<OptionData>) => { | ||
| const isOptionInList = !!option.isSelected; | ||
|
|
||
| let newSelectedOptions; | ||
|
|
||
| if (isOptionInList) { | ||
| newSelectedOptions = reject(selectedOptions, (selectedOption) => selectedOption.login === option.login); | ||
| } else { | ||
| newSelectedOptions = [...selectedOptions, {...option, isSelected: true, selected: true, reportID: option.reportID ?? `${CONST.DEFAULT_NUMBER_ID}`}]; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @FitseTLT Please note that we shouldn't use https://expensify.slack.com/archives/C01GTK53T8Q/p1737913879440099
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. BTW I did not make that change I only moved the function.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's weird. I saw it came from this commit: ef09dba Do you know which added that? We should remind them. |
||
| } | ||
|
|
||
| selectionListRef?.current?.clearInputAfterSelect?.(); | ||
| selectionListRef.current?.focusTextInput(); | ||
| selectionListRef?.current?.scrollToIndex(Math.max(newSelectedOptions.length - 1, 0), true); | ||
| setSelectedOptions(newSelectedOptions); | ||
| }, | ||
| [selectedOptions, setSelectedOptions], | ||
| ); | ||
|
|
||
| /** | ||
| * If there are selected options already then it will toggle the option otherwise | ||
| * creates a new 1:1 chat with the option and the current user, | ||
| * or navigates to the existing chat if one with those participants already exists. | ||
| */ | ||
| const createChat = useCallback( | ||
| (option?: OptionsListUtils.Option) => { | ||
| const selectOption = useCallback( | ||
| (option?: Option) => { | ||
| if (option?.isSelfDM) { | ||
| Navigation.dismissModal(option.reportID); | ||
| return; | ||
| } | ||
| if (selectedOptions.length && option) { | ||
| toggleOption(option); | ||
| return; | ||
| } | ||
|
|
||
| let login = ''; | ||
|
|
||
| if (option?.login) { | ||
|
|
@@ -209,37 +247,16 @@ function NewChatPage() { | |
| Log.warn('Tried to create chat with empty login'); | ||
| return; | ||
| } | ||
| Report.navigateToAndOpenReport([login]); | ||
| navigateToAndOpenReport([login]); | ||
| }, | ||
| [selectedOptions], | ||
| [selectedOptions, toggleOption], | ||
| ); | ||
|
|
||
| const itemRightSideComponent = useCallback( | ||
| (item: ListItem & OptionsListUtils.Option, isFocused?: boolean) => { | ||
| (item: ListItem & Option, isFocused?: boolean) => { | ||
| if (!!item.isSelfDM || (item.login && excludedGroupEmails.includes(item.login))) { | ||
| return null; | ||
| } | ||
| /** | ||
| * Removes a selected option from list if already selected. If not already selected add this option to the list. | ||
| * @param option | ||
| */ | ||
| function toggleOption(option: ListItem & Partial<OptionData>) { | ||
| const isOptionInList = !!option.isSelected; | ||
|
|
||
| let newSelectedOptions; | ||
|
|
||
| if (isOptionInList) { | ||
| newSelectedOptions = reject(selectedOptions, (selectedOption) => selectedOption.login === option.login); | ||
| } else { | ||
| newSelectedOptions = [...selectedOptions, {...option, isSelected: true, selected: true, reportID: option.reportID ?? `${CONST.DEFAULT_NUMBER_ID}`}]; | ||
| } | ||
|
|
||
| selectionListRef?.current?.clearInputAfterSelect?.(); | ||
|
|
||
| selectionListRef.current?.focusTextInput(); | ||
| selectionListRef?.current?.scrollToIndex(Math.max(newSelectedOptions.length - 1, 0), true); | ||
| setSelectedOptions(newSelectedOptions); | ||
| } | ||
|
|
||
| if (item.isSelected) { | ||
| return ( | ||
|
|
@@ -268,7 +285,7 @@ function NewChatPage() { | |
| /> | ||
| ); | ||
| }, | ||
| [selectedOptions, setSelectedOptions, styles.alignItemsCenter, styles.buttonDefaultHovered, styles.flexRow, styles.ml0, styles.ml5, styles.optionSelectCircle, styles.pl2, translate], | ||
| [toggleOption, styles.alignItemsCenter, styles.buttonDefaultHovered, styles.flexRow, styles.ml0, styles.ml5, styles.optionSelectCircle, styles.pl2, translate], | ||
| ); | ||
|
|
||
| const createGroup = useCallback(() => { | ||
|
|
@@ -280,7 +297,7 @@ function NewChatPage() { | |
| accountID: option.accountID ?? CONST.DEFAULT_NUMBER_ID, | ||
| })); | ||
| const logins = [...selectedParticipants, {login: personalData.login, accountID: personalData.accountID}]; | ||
| Report.setGroupDraft({participants: logins}); | ||
| setGroupDraft({participants: logins}); | ||
| Keyboard.dismiss(); | ||
| Navigation.navigate(ROUTES.NEW_CHAT_CONFIRM); | ||
| }, [selectedOptions, personalData]); | ||
|
|
@@ -327,7 +344,7 @@ function NewChatPage() { | |
| // This is because when wrapping whole screen the screen was freezing when changing Tabs. | ||
| keyboardVerticalOffset={variables.contentHeaderHeight + top + variables.tabSelectorButtonHeight + variables.tabSelectorButtonPadding} | ||
| > | ||
| <SelectionList<OptionsListUtils.Option & ListItem> | ||
| <SelectionList<Option & ListItem> | ||
| ref={selectionListRef} | ||
| ListItem={UserListItem} | ||
| sections={areOptionsInitialized ? sections : CONST.EMPTY_ARRAY} | ||
|
|
@@ -336,13 +353,13 @@ function NewChatPage() { | |
| onChangeText={setSearchTerm} | ||
| textInputLabel={translate('selectionList.nameEmailOrPhoneNumber')} | ||
| headerMessage={headerMessage} | ||
| onSelectRow={createChat} | ||
| onSelectRow={selectOption} | ||
| shouldSingleExecuteRowSelect | ||
| onConfirm={(e, option) => (selectedOptions.length > 0 ? createGroup() : createChat(option))} | ||
| onConfirm={(e, option) => (selectedOptions.length > 0 ? createGroup() : selectOption(option))} | ||
| rightHandSideComponent={itemRightSideComponent} | ||
| footerContent={footerContent} | ||
| showLoadingPlaceholder={!areOptionsInitialized} | ||
| shouldPreventDefaultFocusOnSelectRow={!DeviceCapabilities.canUseTouchScreen()} | ||
| shouldPreventDefaultFocusOnSelectRow={!canUseTouchScreen()} | ||
| isLoadingNewOptions={!!isSearchingForReports} | ||
| initiallyFocusedOptionKey={firstKeyForList} | ||
| shouldTextInputInterceptSwipe | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.