diff --git a/assets/images/avatars/group/default-avatar_1.svg b/assets/images/avatars/group/default-avatar_1.svg new file mode 100644 index 000000000000..5d97c5bf855b --- /dev/null +++ b/assets/images/avatars/group/default-avatar_1.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/images/avatars/group/default-avatar_10.svg b/assets/images/avatars/group/default-avatar_10.svg new file mode 100644 index 000000000000..12c9dd76ae31 --- /dev/null +++ b/assets/images/avatars/group/default-avatar_10.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/images/avatars/group/default-avatar_11.svg b/assets/images/avatars/group/default-avatar_11.svg new file mode 100644 index 000000000000..97f17f30f3a7 --- /dev/null +++ b/assets/images/avatars/group/default-avatar_11.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/images/avatars/group/default-avatar_12.svg b/assets/images/avatars/group/default-avatar_12.svg new file mode 100644 index 000000000000..f917fb136582 --- /dev/null +++ b/assets/images/avatars/group/default-avatar_12.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/images/avatars/group/default-avatar_13.svg b/assets/images/avatars/group/default-avatar_13.svg new file mode 100644 index 000000000000..9e59fb9123a5 --- /dev/null +++ b/assets/images/avatars/group/default-avatar_13.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/images/avatars/group/default-avatar_14.svg b/assets/images/avatars/group/default-avatar_14.svg new file mode 100644 index 000000000000..ca071e488416 --- /dev/null +++ b/assets/images/avatars/group/default-avatar_14.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/images/avatars/group/default-avatar_15.svg b/assets/images/avatars/group/default-avatar_15.svg new file mode 100644 index 000000000000..f227cc0717be --- /dev/null +++ b/assets/images/avatars/group/default-avatar_15.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/images/avatars/group/default-avatar_16.svg b/assets/images/avatars/group/default-avatar_16.svg new file mode 100644 index 000000000000..efbb85f0b13d --- /dev/null +++ b/assets/images/avatars/group/default-avatar_16.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/images/avatars/group/default-avatar_17.svg b/assets/images/avatars/group/default-avatar_17.svg new file mode 100644 index 000000000000..25c015c595ca --- /dev/null +++ b/assets/images/avatars/group/default-avatar_17.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/images/avatars/group/default-avatar_18.svg b/assets/images/avatars/group/default-avatar_18.svg new file mode 100644 index 000000000000..a58ee6e66eff --- /dev/null +++ b/assets/images/avatars/group/default-avatar_18.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/images/avatars/group/default-avatar_2.svg b/assets/images/avatars/group/default-avatar_2.svg new file mode 100644 index 000000000000..ff1cc3e6dd2d --- /dev/null +++ b/assets/images/avatars/group/default-avatar_2.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/images/avatars/group/default-avatar_3.svg b/assets/images/avatars/group/default-avatar_3.svg new file mode 100644 index 000000000000..dde31b5d02a0 --- /dev/null +++ b/assets/images/avatars/group/default-avatar_3.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/images/avatars/group/default-avatar_4.svg b/assets/images/avatars/group/default-avatar_4.svg new file mode 100644 index 000000000000..f6d02801bc6b --- /dev/null +++ b/assets/images/avatars/group/default-avatar_4.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/images/avatars/group/default-avatar_5.svg b/assets/images/avatars/group/default-avatar_5.svg new file mode 100644 index 000000000000..fdabd36e2058 --- /dev/null +++ b/assets/images/avatars/group/default-avatar_5.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/images/avatars/group/default-avatar_6.svg b/assets/images/avatars/group/default-avatar_6.svg new file mode 100644 index 000000000000..6f1c6b80eda6 --- /dev/null +++ b/assets/images/avatars/group/default-avatar_6.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/images/avatars/group/default-avatar_7.svg b/assets/images/avatars/group/default-avatar_7.svg new file mode 100644 index 000000000000..62d9a8b76bb8 --- /dev/null +++ b/assets/images/avatars/group/default-avatar_7.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/images/avatars/group/default-avatar_8.svg b/assets/images/avatars/group/default-avatar_8.svg new file mode 100644 index 000000000000..206b10c2322b --- /dev/null +++ b/assets/images/avatars/group/default-avatar_8.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/images/avatars/group/default-avatar_9.svg b/assets/images/avatars/group/default-avatar_9.svg new file mode 100644 index 000000000000..ffbe02ce57e8 --- /dev/null +++ b/assets/images/avatars/group/default-avatar_9.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/CONST.ts b/src/CONST.ts index 8abd4c087b16..ceb0e4d8e662 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -689,6 +689,7 @@ const CONST = { DOMAIN_ALL: 'domainAll', POLICY_ROOM: 'policyRoom', POLICY_EXPENSE_CHAT: 'policyExpenseChat', + GROUP_CHAT: 'group', }, WORKSPACE_CHAT_ROOMS: { ANNOUNCE: '#announce', diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 78f0e61e72a9..a75455460e40 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -272,6 +272,9 @@ const ONYXKEYS = { /** Indicates whether we should store logs or not */ SHOULD_STORE_LOGS: 'shouldStoreLogs', + /** Stores new group chat draft */ + NEW_GROUP: 'newGroupChat', + /** Collection Keys */ COLLECTION: { DOWNLOAD: 'download_', @@ -491,6 +494,7 @@ type OnyxValuesMapping = { [ONYXKEYS.IOU]: OnyxTypes.IOU; [ONYXKEYS.MODAL]: OnyxTypes.Modal; [ONYXKEYS.NETWORK]: OnyxTypes.Network; + [ONYXKEYS.NEW_GROUP]: OnyxTypes.NewGroupChat; [ONYXKEYS.CUSTOM_STATUS_DRAFT]: OnyxTypes.CustomStatusDraft; [ONYXKEYS.INPUT_FOCUSED]: boolean; [ONYXKEYS.PERSONAL_DETAILS_LIST]: OnyxTypes.PersonalDetailsList; diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 22ebffd52eec..b9201410b0c5 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -174,6 +174,7 @@ const ROUTES = { NEW: 'new', NEW_CHAT: 'new/chat', + NEW_CHAT_CONFIRM: 'new/chat/confirm', NEW_ROOM: 'new/room', REPORT: 'r', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index ac75968e68b9..ac74fe99802d 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -231,6 +231,7 @@ const SCREENS = { NEW_CHAT: { ROOT: 'NewChat_Root', NEW_CHAT: 'chat', + NEW_CHAT_CONFIRM: 'NewChat_Confirm', NEW_ROOM: 'room', }, diff --git a/src/components/Icon/GroupDefaultAvatars.ts b/src/components/Icon/GroupDefaultAvatars.ts new file mode 100644 index 000000000000..7b4afb7c7309 --- /dev/null +++ b/src/components/Icon/GroupDefaultAvatars.ts @@ -0,0 +1,20 @@ +import Avatar1 from '@assets/images/avatars/group/default-avatar_1.svg'; +import Avatar2 from '@assets/images/avatars/group/default-avatar_2.svg'; +import Avatar3 from '@assets/images/avatars/group/default-avatar_3.svg'; +import Avatar4 from '@assets/images/avatars/group/default-avatar_4.svg'; +import Avatar5 from '@assets/images/avatars/group/default-avatar_5.svg'; +import Avatar6 from '@assets/images/avatars/group/default-avatar_6.svg'; +import Avatar7 from '@assets/images/avatars/group/default-avatar_7.svg'; +import Avatar8 from '@assets/images/avatars/group/default-avatar_8.svg'; +import Avatar9 from '@assets/images/avatars/group/default-avatar_9.svg'; +import Avatar10 from '@assets/images/avatars/group/default-avatar_10.svg'; +import Avatar11 from '@assets/images/avatars/group/default-avatar_11.svg'; +import Avatar12 from '@assets/images/avatars/group/default-avatar_12.svg'; +import Avatar13 from '@assets/images/avatars/group/default-avatar_13.svg'; +import Avatar14 from '@assets/images/avatars/group/default-avatar_14.svg'; +import Avatar15 from '@assets/images/avatars/group/default-avatar_15.svg'; +import Avatar16 from '@assets/images/avatars/group/default-avatar_16.svg'; +import Avatar17 from '@assets/images/avatars/group/default-avatar_17.svg'; +import Avatar18 from '@assets/images/avatars/group/default-avatar_18.svg'; + +export {Avatar1, Avatar2, Avatar3, Avatar4, Avatar5, Avatar6, Avatar7, Avatar8, Avatar9, Avatar10, Avatar11, Avatar12, Avatar13, Avatar14, Avatar15, Avatar16, Avatar17, Avatar18}; diff --git a/src/languages/en.ts b/src/languages/en.ts index cf8823f5b2be..3215ca3d4020 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -145,6 +145,7 @@ export default { twoFactorCode: 'Two-factor code', workspaces: 'Workspaces', chats: 'Chats', + group: 'Group', profile: 'Profile', referral: 'Referral', payments: 'Payments', @@ -1143,6 +1144,9 @@ export default { roomDescriptionOptional: 'Room description (optional)', explainerText: 'Set a custom decription for the room.', }, + groupConfirmPage: { + groupName: 'Group name', + }, languagePage: { language: 'Language', languages: { @@ -1281,7 +1285,7 @@ export default { }, newChatPage: { createChat: 'Create chat', - createGroup: 'Create group', + startGroup: 'Start group', addToGroup: 'Add to group', }, yearPickerPage: { diff --git a/src/languages/es.ts b/src/languages/es.ts index b3a8eef73d7c..43acf23d7ea0 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1142,6 +1142,9 @@ export default { roomDescriptionOptional: 'Descripción de la sala de chat (opcional)', explainerText: 'Establece una descripción personalizada para la sala de chat.', }, + groupConfirmPage: { + groupName: 'Nombre del grupo', + }, languagePage: { language: 'Idioma', languages: { @@ -1282,7 +1285,7 @@ export default { }, newChatPage: { createChat: 'Crear chat', - createGroup: 'Crear grupo', + startGroup: 'Grupo de inicio', addToGroup: 'Añadir al grupo', }, yearPickerPage: { diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx index 3d0144d8cf77..adfd125e2fff 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx @@ -162,6 +162,7 @@ const SearchModalStackNavigator = createModalStackNavigator({ [SCREENS.NEW_CHAT.ROOT]: () => require('../../../pages/NewChatSelectorPage').default as React.ComponentType, + [SCREENS.NEW_CHAT.NEW_CHAT_CONFIRM]: () => require('../../../pages/NewChatConfirmPage').default as React.ComponentType, }); const NewTaskModalStackNavigator = createModalStackNavigator({ diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 7a6211ebd283..ad524bb96981 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -339,6 +339,10 @@ const config: LinkingOptions['config'] = { }, }, }, + [SCREENS.NEW_CHAT.NEW_CHAT_CONFIRM]: { + path: ROUTES.NEW_CHAT_CONFIRM, + exact: true, + }, }, }, [SCREENS.RIGHT_MODAL.NEW_TASK]: { diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 0f8656adfa51..689556b73bd1 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -9,6 +9,7 @@ import type {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import * as Expensicons from '@components/Icon/Expensicons'; +import * as defaultGroupAvatars from '@components/Icon/GroupDefaultAvatars'; import * as defaultWorkspaceAvatars from '@components/Icon/WorkspaceDefaultAvatars'; import CONST from '@src/CONST'; import type {ParentNavigationSummaryParams, TranslationPaths} from '@src/languages/types'; @@ -1430,6 +1431,14 @@ function getWorkspaceAvatar(report: OnyxEntry): UserUtils.AvatarSource { return !isEmpty(avatar) ? avatar : getDefaultWorkspaceAvatar(workspaceName); } +// /** +// * Helper method to return the default avatar associated with the given reportID +// * TO REWORK! +// */ +function getDefaultGroupAvatar(): IconAsset { + return defaultGroupAvatars.Avatar1; +} + /** * Returns the appropriate icons for the given chat report using the stored personalDetails. * The Avatar sources can be URLs or Icon components according to the chat type. @@ -5208,6 +5217,7 @@ export { canEditRoomVisibility, canEditPolicyDescription, getPolicyDescriptionText, + getDefaultGroupAvatar, }; export type { diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index f29f8a4fbaab..cd19054672cc 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -198,6 +198,11 @@ Onyx.connect({ callback: (val) => (allRecentlyUsedReportFields = val), }); +function startNewChat() { + clearGroupChat(); + Navigation.navigate(ROUTES.NEW); +} + /** Get the private pusher channel name for a Report. */ function getReportChannelName(reportID: string): string { return `${CONST.PUSHER.PRIVATE_REPORT_CHANNEL_PREFIX}${reportID}${CONFIG.PUSHER.SUFFIX}`; @@ -733,7 +738,7 @@ function openReport( * @param userLogins list of user logins to start a chat report with. * @param shouldDismissModal a flag to determine if we should dismiss modal before navigate to report or navigate to report directly. */ -function navigateToAndOpenReport(userLogins: string[], shouldDismissModal = true) { +function navigateToAndOpenReport(userLogins: string[], shouldDismissModal = true, memberRoles?: string[], reportName?: string) { let newChat: ReportUtils.OptimisticChatReport | EmptyObject = {}; const participantAccountIDs = PersonalDetailsUtils.getAccountIDsByLogins(userLogins); @@ -2899,6 +2904,14 @@ function resolveActionableMentionWhisper(reportId: string, reportAction: OnyxEnt API.write(WRITE_COMMANDS.RESOLVE_ACTIONABLE_MENTION_WHISPER, parameters, {optimisticData, failureData}); } +function setGroupDraft(invitedUsersIDs: number[], groupChatAdminLogins: string[] = [], reportName: string = '') { + Onyx.set(ONYXKEYS.NEW_GROUP, {selectedOptions: invitedUsersIDs, groupChatAdminLogins, reportName}); +} + +function clearGroupChat() { + Onyx.set(ONYXKEYS.NEW_GROUP, null); +} + export { searchInServer, addComment, @@ -2969,4 +2982,7 @@ export { updateReportName, resolveActionableMentionWhisper, updateRoomVisibility, + setGroupDraft, + clearGroupChat, + startNewChat, }; diff --git a/src/pages/NewChatConfirmPage.tsx b/src/pages/NewChatConfirmPage.tsx new file mode 100644 index 000000000000..c48b6d62c053 --- /dev/null +++ b/src/pages/NewChatConfirmPage.tsx @@ -0,0 +1,147 @@ +import React, {useEffect, useMemo, useState} from 'react'; +import {View} from 'react-native'; +import {withOnyx} from 'react-native-onyx'; +import type {OnyxEntry} from 'react-native-onyx'; +import Avatar from '@components/Avatar'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; +import OptionsSelector from '@components/OptionsSelector'; +import ScreenWrapper from '@components/ScreenWrapper'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import Navigation from '@libs/Navigation/Navigation'; +import * as OptionsListUtils from '@libs/OptionsListUtils'; +import * as ReportUtils from '@libs/ReportUtils'; +import type {OptionData} from '@libs/ReportUtils'; +import * as Report from '@userActions/Report'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; +import type * as OnyxTypes from '@src/types/onyx'; + +type NewChatConfirmPageOnyxProps = { + /** New group chat draft data */ + newGroupDraft: OnyxEntry; + + /** All of the personal details for everyone */ + allPersonalDetails: OnyxEntry; +}; + +type NewChatConfirmPageProps = NewChatConfirmPageOnyxProps; + +function NewChatConfirmPage({newGroupDraft, allPersonalDetails}: NewChatConfirmPageProps) { + const [selectedOptions, setSelectedOptions] = useState([]); + const [options, setOptions] = useState([]); + const [currentUserOption, setCurrentUserOption] = useState(); + const {translate} = useLocalize(); + const styles = useThemeStyles(); + const groupName = options.map((invitedUser) => (invitedUser.participantsList ? invitedUser.participantsList[0].firstName : '')).join(', '); + + useEffect(() => { + const invitedUsersPersonalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs(newGroupDraft?.selectedOptions, allPersonalDetails); + const members = OptionsListUtils.getMemberInviteOptions(invitedUsersPersonalDetails); + const currentUserOptionData = members.currentUserOption; + const options = [...members.personalDetails, currentUserOptionData] as OptionData[]; + + setCurrentUserOption(currentUserOptionData!); + setOptions(options); + setSelectedOptions(options); + }, [newGroupDraft]); + + const sections = useMemo(() => { + const sectionsList = []; + if (options) { + sectionsList.push({ + title: translate('common.members'), + data: options, + shouldShow: true, + indexOffset: 0, + }); + } + return sectionsList; + }, [options, translate, selectedOptions]); + /** + * Removes a selected option from list if already selected. + */ + const unselectOption = (option: OptionData) => { + if (!selectedOptions) { + return; + } + const isOptionInList = selectedOptions.some((selectedOption) => selectedOption.login === option.login); + + let newSelectedOptions; + + if (isOptionInList && currentUserOption && option.accountID === currentUserOption.accountID) { + return; + } + + if (isOptionInList) { + newSelectedOptions = selectedOptions.filter((selectedOption) => selectedOption.login !== option.login); + setSelectedOptions(newSelectedOptions); + } + }; + + const createGroup = () => { + const logins = selectedOptions.map((option) => option.login).filter((login): login is string => typeof login === 'string'); + if (logins.length < 1) { + return; + } + const accountIDs = selectedOptions.map((selectedOption: OptionData) => selectedOption.accountID) as number[]; + const creatorLogin = [currentUserOption?.login] as string[]; + Report.setGroupDraft(accountIDs, creatorLogin, groupName); + // Report.navigateToAndOpenReport(logins, true, [], ''); + }; + + const navigateBack = () => { + Navigation.goBack(ROUTES.NEW_CHAT); + }; + + return ( + + + + + + + + + + + ); +} + +NewChatConfirmPage.displayName = 'NewChatConfirmPage'; + +export default withOnyx({ + newGroupDraft: { + key: ONYXKEYS.NEW_GROUP, + }, + allPersonalDetails: { + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + }, +})(NewChatConfirmPage); diff --git a/src/pages/NewChatPage.tsx b/src/pages/NewChatPage.tsx index 054f229ac16a..996efa678d4d 100755 --- a/src/pages/NewChatPage.tsx +++ b/src/pages/NewChatPage.tsx @@ -4,6 +4,7 @@ import {withOnyx} from 'react-native-onyx'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import KeyboardAvoidingView from '@components/KeyboardAvoidingView'; import OfflineIndicator from '@components/OfflineIndicator'; +import {usePersonalDetails} from '@components/OnyxProvider'; import OptionsSelector from '@components/OptionsSelector'; import ScreenWrapper from '@components/ScreenWrapper'; import useAutoFocusInput from '@hooks/useAutoFocusInput'; @@ -14,6 +15,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import doInteractionTask from '@libs/DoInteractionTask'; +import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as ReportUtils from '@libs/ReportUtils'; import type {OptionData} from '@libs/ReportUtils'; @@ -21,6 +23,7 @@ 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'; import type * as OnyxTypes from '@src/types/onyx'; import type {DismissedReferralBanners} from '@src/types/onyx/Account'; @@ -28,6 +31,9 @@ type NewChatPageWithOnyxProps = { /** All reports shared with the user */ reports: OnyxCollection; + /** New group chat draft data */ + newGroupDraft: OnyxEntry; + /** All of the personal details for everyone */ personalDetails: OnyxEntry; @@ -46,7 +52,7 @@ type NewChatPageProps = NewChatPageWithOnyxProps & { const excludedGroupEmails = CONST.EXPENSIFY_EMAILS.filter((value) => value !== CONST.EMAIL.CONCIERGE); -function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingForReports, dismissedReferralBanners}: NewChatPageProps) { +function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingForReports, dismissedReferralBanners, newGroupDraft}: NewChatPageProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); const [searchTerm, setSearchTerm] = useState(''); @@ -58,6 +64,8 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingF const {isSmallScreenWidth} = useWindowDimensions(); const [didScreenTransitionEnd, setDidScreenTransitionEnd] = useState(false); + const personalData = usePersonalDetails() || CONST.EMPTY_OBJECT; + const maxParticipantsReached = selectedOptions.length === CONST.REPORT.MAXIMUM_PARTICIPANTS; const setSearchTermAndSearchInServer = useSearchTermAndSearch(setSearchTerm, maxParticipantsReached); @@ -147,7 +155,6 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingF [], true, ); - setSelectedOptions(newSelectedOptions); setFilteredRecentReports(recentReports); setFilteredPersonalDetails(newChatPersonalDetails); @@ -164,19 +171,21 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingF } Report.navigateToAndOpenReport([option.login]); }; - /** - * Creates a new group chat with all the selected options and the current user, - * or navigates to the existing chat if one with those participants already exists. + * Navigates to create group confirm page */ - const createGroup = () => { - const logins = selectedOptions.map((option) => option.login).filter((login): login is string => typeof login === 'string'); - - if (logins.length < 1) { + const navigateToConfirmPage = () => { + if (selectedOptions.length < 1) { return; } - - Report.navigateToAndOpenReport(logins); + const selectedAccountIDs = selectedOptions.map((option) => option.accountID) as number[]; + const user = OptionsListUtils.getMemberInviteOptions(personalData); + const currentUserOption = user.currentUserOption; + if (currentUserOption) { + const accountIDs = [...selectedAccountIDs, currentUserOption.accountID] as number[]; + Report.setGroupDraft(accountIDs); + Navigation.navigate(ROUTES.NEW_CHAT_CONFIRM); + } }; const updateOptions = useCallback(() => { @@ -227,6 +236,11 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingF return; } updateOptions(); + if (newGroupDraft?.selectedOptions) { + const invitedUsersPersonalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs(newGroupDraft?.selectedOptions, personalDetails); + const groupSelectedOptions = OptionsListUtils.getMemberInviteOptions(invitedUsersPersonalDetails).personalDetails; + setSelectedOptions(groupSelectedOptions); + } }, [didScreenTransitionEnd, updateOptions]); const {inputCallbackRef} = useAutoFocusInput(); @@ -267,9 +281,9 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingF shouldShowConfirmButton shouldShowReferralCTA={!dismissedReferralBanners[CONST.REFERRAL_PROGRAM.CONTENT_TYPES.START_CHAT]} referralContentType={CONST.REFERRAL_PROGRAM.CONTENT_TYPES.START_CHAT} - confirmButtonText={selectedOptions.length > 1 ? translate('newChatPage.createGroup') : translate('newChatPage.createChat')} + confirmButtonText={selectedOptions.length > 1 ? translate('common.next') : translate('newChatPage.createChat')} textInputAlert={isOffline ? [`${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}`, {isTranslated: true}] : ''} - onConfirmSelection={createGroup} + onConfirmSelection={navigateToConfirmPage} textInputLabel={translate('optionsSelector.nameEmailOrPhoneNumber')} safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle} isLoadingNewOptions={isSearchingForReports} @@ -289,6 +303,9 @@ export default withOnyx({ key: ONYXKEYS.ACCOUNT, selector: (data) => data?.dismissedReferralBanners ?? {}, }, + newGroupDraft: { + key: ONYXKEYS.NEW_GROUP, + }, reports: { key: ONYXKEYS.COLLECTION.REPORT, }, diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js index 573cbe370aa7..2e82cdd97e01 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js @@ -18,6 +18,7 @@ import * as ReportUtils from '@libs/ReportUtils'; import * as App from '@userActions/App'; import * as IOU from '@userActions/IOU'; import * as Policy from '@userActions/Policy'; +import * as Report from '@userActions/Report'; import * as Task from '@userActions/Task'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -159,7 +160,7 @@ function FloatingActionButtonAndPopover(props) { { icon: Expensicons.ChatBubble, text: translate('sidebarScreen.fabNewChat'), - onSelected: () => interceptAnonymousUser(() => Navigation.navigate(ROUTES.NEW)), + onSelected: () => interceptAnonymousUser(() => Report.startNewChat()), }, { icon: Expensicons.MoneyCircle, diff --git a/src/types/onyx/NewGroupChat.ts b/src/types/onyx/NewGroupChat.ts new file mode 100644 index 000000000000..b9a0d88e107d --- /dev/null +++ b/src/types/onyx/NewGroupChat.ts @@ -0,0 +1,7 @@ +type NewGroupChat = { + selectedOptions: number[]; + groupChatAdminLogins: string[]; + reportName: string; +}; + +export default NewGroupChat; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index 6846fc302639..c31cc24df24a 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -25,6 +25,7 @@ import type Login from './Login'; import type MapboxAccessToken from './MapboxAccessToken'; import type Modal from './Modal'; import type Network from './Network'; +import type NewGroupChat from './NewGroupChat'; import type {OnyxUpdateEvent, OnyxUpdatesFromServer} from './OnyxUpdatesFromServer'; import type PersonalBankAccount from './PersonalBankAccount'; import type {PersonalDetailsList} from './PersonalDetails'; @@ -153,5 +154,6 @@ export type { RecentlyUsedReportFields, LastPaymentMethod, InvitedEmailsToAccountIDs, + NewGroupChat, Log, };