From 0452b5c43152b99e41146586584b9d50bd0e4d6a Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Wed, 28 Feb 2024 23:38:24 +0100 Subject: [PATCH 01/29] initial commit --- .../images/avatars/group/default-avatar_1.svg | 7 + .../avatars/group/default-avatar_10.svg | 7 + .../avatars/group/default-avatar_11.svg | 7 + .../avatars/group/default-avatar_12.svg | 7 + .../avatars/group/default-avatar_13.svg | 7 + .../avatars/group/default-avatar_14.svg | 7 + .../avatars/group/default-avatar_15.svg | 7 + .../avatars/group/default-avatar_16.svg | 7 + .../avatars/group/default-avatar_17.svg | 7 + .../avatars/group/default-avatar_18.svg | 7 + .../images/avatars/group/default-avatar_2.svg | 7 + .../images/avatars/group/default-avatar_3.svg | 7 + .../images/avatars/group/default-avatar_4.svg | 7 + .../images/avatars/group/default-avatar_5.svg | 7 + .../images/avatars/group/default-avatar_6.svg | 7 + .../images/avatars/group/default-avatar_7.svg | 7 + .../images/avatars/group/default-avatar_8.svg | 7 + .../images/avatars/group/default-avatar_9.svg | 7 + src/CONST.ts | 1 + src/ONYXKEYS.ts | 4 + src/ROUTES.ts | 1 + src/SCREENS.ts | 1 + src/components/Icon/GroupDefaultAvatars.ts | 20 +++ src/languages/en.ts | 6 +- src/languages/es.ts | 5 +- .../AppNavigator/ModalStackNavigators.tsx | 1 + src/libs/Navigation/linkingConfig/config.ts | 4 + src/libs/ReportUtils.ts | 10 ++ src/libs/actions/Report.ts | 18 ++- src/pages/NewChatConfirmPage.tsx | 147 ++++++++++++++++++ src/pages/NewChatPage.tsx | 43 +++-- .../FloatingActionButtonAndPopover.js | 3 +- src/types/onyx/NewGroupChat.ts | 7 + src/types/onyx/index.ts | 2 + 34 files changed, 382 insertions(+), 17 deletions(-) create mode 100644 assets/images/avatars/group/default-avatar_1.svg create mode 100644 assets/images/avatars/group/default-avatar_10.svg create mode 100644 assets/images/avatars/group/default-avatar_11.svg create mode 100644 assets/images/avatars/group/default-avatar_12.svg create mode 100644 assets/images/avatars/group/default-avatar_13.svg create mode 100644 assets/images/avatars/group/default-avatar_14.svg create mode 100644 assets/images/avatars/group/default-avatar_15.svg create mode 100644 assets/images/avatars/group/default-avatar_16.svg create mode 100644 assets/images/avatars/group/default-avatar_17.svg create mode 100644 assets/images/avatars/group/default-avatar_18.svg create mode 100644 assets/images/avatars/group/default-avatar_2.svg create mode 100644 assets/images/avatars/group/default-avatar_3.svg create mode 100644 assets/images/avatars/group/default-avatar_4.svg create mode 100644 assets/images/avatars/group/default-avatar_5.svg create mode 100644 assets/images/avatars/group/default-avatar_6.svg create mode 100644 assets/images/avatars/group/default-avatar_7.svg create mode 100644 assets/images/avatars/group/default-avatar_8.svg create mode 100644 assets/images/avatars/group/default-avatar_9.svg create mode 100644 src/components/Icon/GroupDefaultAvatars.ts create mode 100644 src/pages/NewChatConfirmPage.tsx create mode 100644 src/types/onyx/NewGroupChat.ts 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, }; From 91de72d8c60162fc6c47a05f059fa471a9845b1b Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Tue, 5 Mar 2024 19:20:40 +0100 Subject: [PATCH 02/29] updates, api request --- src/libs/API/parameters/OpenReportParams.ts | 4 +++ src/libs/GroupChatUtils.ts | 15 ++++++++ src/libs/actions/Report.ts | 39 +++++++++++++++++---- src/pages/NewChatConfirmPage.tsx | 39 ++++++++++----------- src/pages/NewChatPage.tsx | 10 +++--- src/types/onyx/NewGroupChat.ts | 1 - 6 files changed, 74 insertions(+), 34 deletions(-) diff --git a/src/libs/API/parameters/OpenReportParams.ts b/src/libs/API/parameters/OpenReportParams.ts index 477a002516de..0db845fccdc5 100644 --- a/src/libs/API/parameters/OpenReportParams.ts +++ b/src/libs/API/parameters/OpenReportParams.ts @@ -7,6 +7,10 @@ type OpenReportParams = { createdReportActionID?: string; clientLastReadTime?: string; idempotencyKey?: string; + groupChatAdminLogins?: string; + reportName?: string; + chatType?: string; + optimisticAccountIDList?: string; }; export default OpenReportParams; diff --git a/src/libs/GroupChatUtils.ts b/src/libs/GroupChatUtils.ts index 58a82de3df53..ab3ed521d7cd 100644 --- a/src/libs/GroupChatUtils.ts +++ b/src/libs/GroupChatUtils.ts @@ -1,8 +1,22 @@ import type {OnyxEntry} from 'react-native-onyx'; +import type {OptionData} from '@libs/ReportUtils'; import type {Report} from '@src/types/onyx'; import localeCompare from './LocaleCompare'; import * as ReportUtils from './ReportUtils'; +/** + * Returns the group chat name for confirm page + */ +function getGroupChatConfirmName(participants: OptionData[]): string | undefined { + const isMultipleParticipantReport = participants.length > 1; + + return participants + .map((participant) => ReportUtils.getDisplayNameForParticipant(participant.accountID!, isMultipleParticipantReport)) + .sort((first, second) => localeCompare(first ?? '', second ?? '')) + .filter(Boolean) + .join(', '); +} + /** * Returns the report name if the report is a group chat */ @@ -20,4 +34,5 @@ function getGroupChatName(report: OnyxEntry): string | undefined { export { // eslint-disable-next-line import/prefer-default-export getGroupChatName, + getGroupChatConfirmName, }; diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 9dc5971701d1..d756edfe35d8 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -70,7 +70,16 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type {Route} from '@src/ROUTES'; import ROUTES from '@src/ROUTES'; import INPUT_IDS from '@src/types/form/NewRoomForm'; -import type {PersonalDetails, PersonalDetailsList, PolicyReportField, RecentlyUsedReportFields, ReportActionReactions, ReportMetadata, ReportUserIsTyping} from '@src/types/onyx'; +import type { + NewGroupChat, + PersonalDetails, + PersonalDetailsList, + PolicyReportField, + RecentlyUsedReportFields, + ReportActionReactions, + ReportMetadata, + ReportUserIsTyping, +} from '@src/types/onyx'; import type {Decision, OriginalMessageIOU} from '@src/types/onyx/OriginalMessage'; import type {NotificationPreference, RoomVisibility, WriteCapability} from '@src/types/onyx/Report'; import type Report from '@src/types/onyx/Report'; @@ -92,6 +101,7 @@ type ActionSubscriber = { let conciergeChatReportID: string | undefined; let currentUserAccountID = -1; +let currentUserEmail: string | undefined; Onyx.connect({ key: ONYXKEYS.SESSION, callback: (value) => { @@ -100,7 +110,7 @@ Onyx.connect({ conciergeChatReportID = undefined; return; } - + currentUserEmail = value.email; currentUserAccountID = value.accountID; }, }); @@ -218,6 +228,12 @@ Onyx.connect({ callback: (val) => (allRecentlyUsedReportFields = val), }); +let newGroupDraft: OnyxEntry; +Onyx.connect({ + key: ONYXKEYS.NEW_GROUP, + callback: (val) => (newGroupDraft = val), +}); + function startNewChat() { clearGroupChat(); Navigation.navigate(ROUTES.NEW); @@ -626,6 +642,13 @@ function openReport( idempotencyKey: `${SIDE_EFFECT_REQUEST_COMMANDS.OPEN_REPORT}_${reportID}`, }; + if (newReportObject.chatType === CONST.REPORT.CHAT_TYPE.GROUP_CHAT) { + parameters.chatType = CONST.REPORT.CHAT_TYPE.GROUP_CHAT; + parameters.groupChatAdminLogins = currentUserEmail; + parameters.optimisticAccountIDList = participantAccountIDList ? participantAccountIDList.join(',') : ''; + parameters.reportName = newReportObject.reportName ?? ''; + } + if (isFromDeepLink) { parameters.shouldRetry = false; } @@ -758,14 +781,18 @@ 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, memberRoles?: string[], reportName?: string) { +function navigateToAndOpenReport(userLogins: string[], shouldDismissModal = true, reportName?: string) { let newChat: ReportUtils.OptimisticChatReport | EmptyObject = {}; const participantAccountIDs = PersonalDetailsUtils.getAccountIDsByLogins(userLogins); const chat = ReportUtils.getChatByParticipants(participantAccountIDs); if (!chat) { - newChat = ReportUtils.buildOptimisticChatReport(participantAccountIDs); + if (newGroupDraft) { + newChat = ReportUtils.buildOptimisticChatReport(participantAccountIDs, reportName, CONST.REPORT.CHAT_TYPE.GROUP_CHAT); + } else { + newChat = ReportUtils.buildOptimisticChatReport(participantAccountIDs); + } } const report = chat ?? newChat; @@ -2927,8 +2954,8 @@ 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 setGroupDraft(invitedUsersIDs: number[], reportName: string = '') { + Onyx.set(ONYXKEYS.NEW_GROUP, {selectedOptions: invitedUsersIDs, reportName}); } function clearGroupChat() { diff --git a/src/pages/NewChatConfirmPage.tsx b/src/pages/NewChatConfirmPage.tsx index c48b6d62c053..0f83689e47c9 100644 --- a/src/pages/NewChatConfirmPage.tsx +++ b/src/pages/NewChatConfirmPage.tsx @@ -7,8 +7,10 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import OptionsSelector from '@components/OptionsSelector'; import ScreenWrapper from '@components/ScreenWrapper'; +import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; +import * as GroupChatUtils from '@libs/GroupChatUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as ReportUtils from '@libs/ReportUtils'; @@ -30,36 +32,33 @@ type NewChatConfirmPageOnyxProps = { 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(', '); + const personalData = useCurrentUserPersonalDetails() || CONST.EMPTY_OBJECT; - useEffect(() => { + const selectedOptions = useMemo(() => { 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); + return options; }, [newGroupDraft]); + const groupName = GroupChatUtils.getGroupChatConfirmName(selectedOptions); + const sections = useMemo(() => { const sectionsList = []; - if (options) { + if (selectedOptions) { sectionsList.push({ title: translate('common.members'), - data: options, + data: selectedOptions, shouldShow: true, indexOffset: 0, }); } return sectionsList; - }, [options, translate, selectedOptions]); + }, [translate, selectedOptions]); + /** * Removes a selected option from list if already selected. */ @@ -69,27 +68,25 @@ function NewChatConfirmPage({newGroupDraft, allPersonalDetails}: NewChatConfirmP } const isOptionInList = selectedOptions.some((selectedOption) => selectedOption.login === option.login); - let newSelectedOptions; - - if (isOptionInList && currentUserOption && option.accountID === currentUserOption.accountID) { + if (isOptionInList && personalData && option.accountID === personalData.accountID) { return; } if (isOptionInList) { - newSelectedOptions = selectedOptions.filter((selectedOption) => selectedOption.login !== option.login); - setSelectedOptions(newSelectedOptions); + const newSelectedAccountIDs = selectedOptions.filter((selectedOption) => selectedOption.login !== option.login).map((option) => option.accountID) as number[]; + Report.setGroupDraft(newSelectedAccountIDs); } }; const createGroup = () => { - const logins = selectedOptions.map((option) => option.login).filter((login): login is string => typeof login === 'string'); + const optionsWithoutCreator = selectedOptions.filter((selectedOption: OptionData) => selectedOption.accountID !== personalData.accountID); + const logins = optionsWithoutCreator.map((option: OptionData) => option.login) as 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, [], ''); + Report.setGroupDraft(accountIDs, groupName); + Report.navigateToAndOpenReport(logins, true, ''); }; const navigateBack = () => { diff --git a/src/pages/NewChatPage.tsx b/src/pages/NewChatPage.tsx index b6cd0385aeaf..4e017777181c 100755 --- a/src/pages/NewChatPage.tsx +++ b/src/pages/NewChatPage.tsx @@ -4,10 +4,10 @@ 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'; +import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useSearchTermAndSearch from '@hooks/useSearchTermAndSearch'; @@ -64,7 +64,7 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingF const {isSmallScreenWidth} = useWindowDimensions(); const [didScreenTransitionEnd, setDidScreenTransitionEnd] = useState(false); - const personalData = usePersonalDetails() || CONST.EMPTY_OBJECT; + const personalData = useCurrentUserPersonalDetails() || CONST.EMPTY_OBJECT; const maxParticipantsReached = selectedOptions.length === CONST.REPORT.MAXIMUM_PARTICIPANTS; const setSearchTermAndSearchInServer = useSearchTermAndSearch(setSearchTerm, maxParticipantsReached); @@ -179,10 +179,8 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingF return; } 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[]; + if (personalData) { + const accountIDs = [...selectedAccountIDs, personalData.accountID] as number[]; Report.setGroupDraft(accountIDs); Navigation.navigate(ROUTES.NEW_CHAT_CONFIRM); } diff --git a/src/types/onyx/NewGroupChat.ts b/src/types/onyx/NewGroupChat.ts index b9a0d88e107d..57fd2b1c93ae 100644 --- a/src/types/onyx/NewGroupChat.ts +++ b/src/types/onyx/NewGroupChat.ts @@ -1,6 +1,5 @@ type NewGroupChat = { selectedOptions: number[]; - groupChatAdminLogins: string[]; reportName: string; }; From a31e70aaaf4ac883f2181c21873823274e8e282c Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Wed, 6 Mar 2024 22:58:12 +0100 Subject: [PATCH 03/29] fix errors --- src/languages/es.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/languages/es.ts b/src/languages/es.ts index 54fa994ecb5f..a286314e6452 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -135,6 +135,7 @@ export default { twoFactorCode: 'Autenticación de dos factores', workspaces: 'Espacios de trabajo', chats: 'Chats', + group: 'Grupo', profile: 'Perfil', referral: 'Remisión', payments: 'Pagos', From 89040f142e2b0b7ca721a6cb1907cb92ecc1d06f Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Wed, 6 Mar 2024 23:31:17 +0100 Subject: [PATCH 04/29] fix lint --- src/libs/GroupChatUtils.ts | 2 +- src/libs/actions/Report.ts | 8 ++++---- src/pages/NewChatConfirmPage.tsx | 6 +++--- src/pages/NewChatPage.tsx | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/libs/GroupChatUtils.ts b/src/libs/GroupChatUtils.ts index ab3ed521d7cd..69a15c73d525 100644 --- a/src/libs/GroupChatUtils.ts +++ b/src/libs/GroupChatUtils.ts @@ -1,7 +1,7 @@ import type {OnyxEntry} from 'react-native-onyx'; -import type {OptionData} from '@libs/ReportUtils'; import type {Report} from '@src/types/onyx'; import localeCompare from './LocaleCompare'; +import type {OptionData} from './ReportUtils'; import * as ReportUtils from './ReportUtils'; /** diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index f3cb23c72f85..d23388ee1688 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -250,6 +250,10 @@ Onyx.connect({ callback: (val) => (newGroupDraft = val), }); +function clearGroupChat() { + Onyx.set(ONYXKEYS.NEW_GROUP, null); +} + function startNewChat() { clearGroupChat(); Navigation.navigate(ROUTES.NEW); @@ -2974,10 +2978,6 @@ function setGroupDraft(invitedUsersIDs: number[], reportName: string = '') { Onyx.set(ONYXKEYS.NEW_GROUP, {selectedOptions: invitedUsersIDs, reportName}); } -function clearGroupChat() { - Onyx.set(ONYXKEYS.NEW_GROUP, null); -} - export { searchInServer, addComment, diff --git a/src/pages/NewChatConfirmPage.tsx b/src/pages/NewChatConfirmPage.tsx index 0f83689e47c9..2a07f16b9696 100644 --- a/src/pages/NewChatConfirmPage.tsx +++ b/src/pages/NewChatConfirmPage.tsx @@ -1,4 +1,4 @@ -import React, {useEffect, useMemo, useState} from 'react'; +import React, {useMemo} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; @@ -42,7 +42,7 @@ function NewChatConfirmPage({newGroupDraft, allPersonalDetails}: NewChatConfirmP const currentUserOptionData = members.currentUserOption; const options = [...members.personalDetails, currentUserOptionData] as OptionData[]; return options; - }, [newGroupDraft]); + }, [newGroupDraft, allPersonalDetails]); const groupName = GroupChatUtils.getGroupChatConfirmName(selectedOptions); @@ -73,7 +73,7 @@ function NewChatConfirmPage({newGroupDraft, allPersonalDetails}: NewChatConfirmP } if (isOptionInList) { - const newSelectedAccountIDs = selectedOptions.filter((selectedOption) => selectedOption.login !== option.login).map((option) => option.accountID) as number[]; + const newSelectedAccountIDs = selectedOptions.filter((selectedOption) => selectedOption.login !== option.login).map((optionData) => optionData.accountID) as number[]; Report.setGroupDraft(newSelectedAccountIDs); } }; diff --git a/src/pages/NewChatPage.tsx b/src/pages/NewChatPage.tsx index 4e017777181c..fc106f08642d 100755 --- a/src/pages/NewChatPage.tsx +++ b/src/pages/NewChatPage.tsx @@ -239,7 +239,7 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingF const groupSelectedOptions = OptionsListUtils.getMemberInviteOptions(invitedUsersPersonalDetails).personalDetails; setSelectedOptions(groupSelectedOptions); } - }, [didScreenTransitionEnd, updateOptions]); + }, [didScreenTransitionEnd, updateOptions, newGroupDraft?.selectedOptions, personalDetails]); const {inputCallbackRef} = useAutoFocusInput(); From 24ae5ecf357b7e62a57b1c28fd2e5dc7d8b46161 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Wed, 6 Mar 2024 23:40:51 +0100 Subject: [PATCH 05/29] fix lint --- src/libs/GroupChatUtils.ts | 2 +- src/libs/actions/Report.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/GroupChatUtils.ts b/src/libs/GroupChatUtils.ts index 69a15c73d525..d694099989a6 100644 --- a/src/libs/GroupChatUtils.ts +++ b/src/libs/GroupChatUtils.ts @@ -11,7 +11,7 @@ function getGroupChatConfirmName(participants: OptionData[]): string | undefined const isMultipleParticipantReport = participants.length > 1; return participants - .map((participant) => ReportUtils.getDisplayNameForParticipant(participant.accountID!, isMultipleParticipantReport)) + .map((participant) => ReportUtils.getDisplayNameForParticipant(participant.accountID ?? undefined, isMultipleParticipantReport)) .sort((first, second) => localeCompare(first ?? '', second ?? '')) .filter(Boolean) .join(', '); diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index d23388ee1688..02f554308f89 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -2974,7 +2974,7 @@ function resolveActionableMentionWhisper(reportId: string, reportAction: OnyxEnt API.write(WRITE_COMMANDS.RESOLVE_ACTIONABLE_MENTION_WHISPER, parameters, {optimisticData, failureData}); } -function setGroupDraft(invitedUsersIDs: number[], reportName: string = '') { +function setGroupDraft(invitedUsersIDs: number[], reportName = '') { Onyx.set(ONYXKEYS.NEW_GROUP, {selectedOptions: invitedUsersIDs, reportName}); } From 1d690097fba01052520760254e64cb7ed21b16c9 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Thu, 7 Mar 2024 21:36:12 +0100 Subject: [PATCH 06/29] split changes, Join remove --- src/libs/ReportUtils.ts | 90 ++++++++++++++++++++++++++++++++ src/libs/actions/IOU.ts | 2 +- src/libs/actions/Report.ts | 19 +++++-- src/pages/NewChatConfirmPage.tsx | 3 +- src/pages/home/HeaderView.js | 8 ++- src/types/onyx/Report.ts | 2 +- 6 files changed, 114 insertions(+), 10 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 7f7ba31bd583..105402fcf59c 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -20,6 +20,7 @@ import type {Route} from '@src/ROUTES'; import ROUTES from '@src/ROUTES'; import type { Beta, + Participants, PersonalDetails, PersonalDetailsList, Policy, @@ -257,6 +258,39 @@ type OptimisticChatReport = Pick< isOptimisticReport: true; }; +type OptimisticGroupChatReport = Pick< + Report, + | 'type' + | 'chatType' + | 'chatReportID' + | 'iouReportID' + | 'isOwnPolicyExpenseChat' + | 'isPinned' + | 'lastActorAccountID' + | 'lastMessageTranslationKey' + | 'lastMessageHtml' + | 'lastMessageText' + | 'lastReadTime' + | 'lastVisibleActionCreated' + | 'notificationPreference' + | 'oldPolicyName' + | 'ownerAccountID' + | 'pendingFields' + | 'parentReportActionID' + | 'parentReportID' + | 'participants' + | 'policyID' + | 'reportID' + | 'reportName' + | 'stateNum' + | 'statusNum' + | 'visibility' + | 'description' + | 'writeCapability' +> & { + isOptimisticReport: true; +}; + type OptimisticTaskReportAction = Pick< ReportAction, | 'reportActionID' @@ -915,6 +949,10 @@ function isSelfDM(report: OnyxEntry): boolean { return getChatType(report) === CONST.REPORT.CHAT_TYPE.SELF_DM; } +function isGroupChatType(report: OnyxEntry): boolean { + return getChatType(report) === CONST.REPORT.CHAT_TYPE.GROUP_CHAT; +} + /** * Only returns true if this is our main 1:1 DM report with Concierge */ @@ -3520,6 +3558,56 @@ function buildOptimisticChatReport( }; } +/** + * Builds an optimistic group chat report with a randomly generated reportID and as much information as we currently have + */ +function buildOptimisticGroupChatReport( + participantList: Participants, + reportName: string = CONST.REPORT.DEFAULT_REPORT_NAME, + chatType: ValueOf | undefined = undefined, + policyID: string = CONST.POLICY.OWNER_EMAIL_FAKE, + ownerAccountID: number = CONST.REPORT.OWNER_ACCOUNT_ID_FAKE, + isOwnPolicyExpenseChat = false, + oldPolicyName = '', + visibility: ValueOf | undefined = undefined, + writeCapability: ValueOf | undefined = undefined, + notificationPreference: NotificationPreference = CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS, + parentReportActionID = '', + parentReportID = '', + description = '', +): OptimisticGroupChatReport { + const currentTime = DateUtils.getDBTime(); + const isNewlyCreatedWorkspaceChat = chatType === CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT && isOwnPolicyExpenseChat; + return { + isOptimisticReport: true, + type: CONST.REPORT.TYPE.CHAT, + chatType, + isOwnPolicyExpenseChat, + isPinned: reportName === CONST.REPORT.WORKSPACE_CHAT_ROOMS.ADMINS || isNewlyCreatedWorkspaceChat, + lastActorAccountID: 0, + lastMessageTranslationKey: '', + lastMessageHtml: '', + lastMessageText: undefined, + lastReadTime: currentTime, + lastVisibleActionCreated: currentTime, + notificationPreference, + oldPolicyName, + ownerAccountID: ownerAccountID || CONST.REPORT.OWNER_ACCOUNT_ID_FAKE, + parentReportActionID, + parentReportID, + // For group chats we need to have participants object + participants: participantList, + policyID, + reportID: generateReportID(), + reportName, + stateNum: 0, + statusNum: 0, + visibility, + description, + writeCapability, + }; +} + function getCurrentUserAvatarOrDefault(): UserUtils.AvatarSource { return allPersonalDetails?.[currentUserAccountID ?? '']?.avatar ?? UserUtils.getDefaultAvatarURL(currentUserAccountID); } @@ -5189,6 +5277,7 @@ export { buildOptimisticWorkspaceChats, buildOptimisticTaskReport, buildOptimisticChatReport, + buildOptimisticGroupChatReport, buildOptimisticClosedReportAction, buildOptimisticCreatedReportAction, buildOptimisticRenamedRoomReportAction, @@ -5327,6 +5416,7 @@ export { getDefaultGroupAvatar, canAddOrDeleteTransactions, shouldCreateNewMoneyRequestReport, + isGroupChatType, }; export type { diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index cb3aa20ab6a7..49ccc30a5439 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -1655,7 +1655,7 @@ function createSplitsAndOnyxData( const currentUserEmailForIOUSplit = PhoneNumber.addSMSDomainIfPhoneNumber(currentUserLogin); const participantAccountIDs = participants.map((participant) => Number(participant.accountID)); const existingSplitChatReport = - existingSplitChatReportID || participants[0].reportID + participants.length > 1 && (existingSplitChatReportID || participants[0].reportID) ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${existingSplitChatReportID || participants[0].reportID}`] : ReportUtils.getChatByParticipants(participantAccountIDs); const splitChatReport = existingSplitChatReport ?? ReportUtils.buildOptimisticChatReport(participantAccountIDs); diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 02f554308f89..9ebb1b12ab58 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -81,7 +81,7 @@ import type { ReportUserIsTyping, } from '@src/types/onyx'; import type {Decision, OriginalMessageIOU} from '@src/types/onyx/OriginalMessage'; -import type {NotificationPreference, RoomVisibility, WriteCapability} from '@src/types/onyx/Report'; +import type {NotificationPreference, Participant, Participants, RoomVisibility, WriteCapability} from '@src/types/onyx/Report'; import type Report from '@src/types/onyx/Report'; import type {Message, ReportActionBase, ReportActions} from '@src/types/onyx/ReportAction'; import type ReportAction from '@src/types/onyx/ReportAction'; @@ -803,13 +803,24 @@ function openReport( */ function navigateToAndOpenReport(userLogins: string[], shouldDismissModal = true, reportName?: string) { let newChat: ReportUtils.OptimisticChatReport | EmptyObject = {}; - + let chat: ReportUtils.OptimisticChatReport | EmptyObject = {}; const participantAccountIDs = PersonalDetailsUtils.getAccountIDsByLogins(userLogins); - const chat = ReportUtils.getChatByParticipants(participantAccountIDs); + if (!newGroupDraft) { + chat = ReportUtils.getChatByParticipants(participantAccountIDs); + } if (!chat) { if (newGroupDraft) { - newChat = ReportUtils.buildOptimisticChatReport(participantAccountIDs, reportName, CONST.REPORT.CHAT_TYPE.GROUP_CHAT); + const participants: Participants = participantAccountIDs.reduce((obj: Participants, accountID: number) => { + const participant: Participant = { + hidden: false, + role: accountID === currentUserAccountID ? 'admin' : 'member', + }; + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return {...obj, [accountID]: participant}; + }, {} as Participants); + + newChat = ReportUtils.buildOptimisticGroupChatReport(participants, reportName, CONST.REPORT.CHAT_TYPE.GROUP_CHAT); } else { newChat = ReportUtils.buildOptimisticChatReport(participantAccountIDs); } diff --git a/src/pages/NewChatConfirmPage.tsx b/src/pages/NewChatConfirmPage.tsx index 2a07f16b9696..fb90c96af228 100644 --- a/src/pages/NewChatConfirmPage.tsx +++ b/src/pages/NewChatConfirmPage.tsx @@ -79,8 +79,7 @@ function NewChatConfirmPage({newGroupDraft, allPersonalDetails}: NewChatConfirmP }; const createGroup = () => { - const optionsWithoutCreator = selectedOptions.filter((selectedOption: OptionData) => selectedOption.accountID !== personalData.accountID); - const logins = optionsWithoutCreator.map((option: OptionData) => option.login) as string[]; + const logins = selectedOptions.map((option: OptionData) => option.login) as string[]; if (logins.length < 1) { return; } diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index 10d2d1414c3a..36296b329d2c 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -94,6 +94,7 @@ function HeaderView(props) { const theme = useTheme(); const styles = useThemeStyles(); const isSelfDM = ReportUtils.isSelfDM(props.report); + const isGroupChat = ReportUtils.isGroupChat(props.report) || ReportUtils.isGroupChatType(props.report); // Currently, currentUser is not included in participantAccountIDs, so for selfDM, we need to add the currentUser as participants. const participants = isSelfDM ? [props.session.accountID] : lodashGet(props.report, 'participantAccountIDs', []); const participantPersonalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs(participants, props.personalDetails); @@ -106,7 +107,7 @@ function HeaderView(props) { const isTaskReport = ReportUtils.isTaskReport(props.report); const reportHeaderData = !isTaskReport && !isChatThread && props.report.parentReportID ? props.parentReport : props.report; // Use sorted display names for the title for group chats on native small screen widths - const title = ReportUtils.isGroupChat(props.report) ? getGroupChatName(props.report) : ReportUtils.getReportName(reportHeaderData); + const title = isGroupChat ? getGroupChatName(props.report) : ReportUtils.getReportName(reportHeaderData); const subtitle = ReportUtils.getChatRoomSubtitle(reportHeaderData); const parentNavigationSubtitleData = ReportUtils.getParentNavigationSubtitle(reportHeaderData); const isConcierge = ReportUtils.hasSingleParticipant(props.report) && _.contains(participants, CONST.ACCOUNT_ID.CONCIERGE); @@ -169,7 +170,10 @@ function HeaderView(props) { ), ); - const canJoinOrLeave = !isSelfDM && (isChatThread || isUserCreatedPolicyRoom || canLeaveRoom); + const canJoinOrLeave = !isSelfDM && !isGroupChat && (isChatThread || isUserCreatedPolicyRoom || canLeaveRoom); + console.log('canJoinOrLeave :>> ', canJoinOrLeave); + console.log('isGroupChat :>> ', isGroupChat); + console.log('props.report :>> ', props.report); const canJoin = canJoinOrLeave && !isWhisperAction && props.report.notificationPreference === CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN; const canLeave = canJoinOrLeave && ((isChatThread && props.report.notificationPreference.length) || isUserCreatedPolicyRoom || canLeaveRoom); if (canJoin) { diff --git a/src/types/onyx/Report.ts b/src/types/onyx/Report.ts index 22618bb357d0..f12bd0a7e38f 100644 --- a/src/types/onyx/Report.ts +++ b/src/types/onyx/Report.ts @@ -180,4 +180,4 @@ type ReportCollectionDataSet = CollectionDataSet Date: Thu, 7 Mar 2024 21:56:50 +0100 Subject: [PATCH 07/29] fix lint, ta --- src/pages/home/HeaderView.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index 36296b329d2c..ffe1238212d5 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -171,9 +171,6 @@ function HeaderView(props) { ); const canJoinOrLeave = !isSelfDM && !isGroupChat && (isChatThread || isUserCreatedPolicyRoom || canLeaveRoom); - console.log('canJoinOrLeave :>> ', canJoinOrLeave); - console.log('isGroupChat :>> ', isGroupChat); - console.log('props.report :>> ', props.report); const canJoin = canJoinOrLeave && !isWhisperAction && props.report.notificationPreference === CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN; const canLeave = canJoinOrLeave && ((isChatThread && props.report.notificationPreference.length) || isUserCreatedPolicyRoom || canLeaveRoom); if (canJoin) { From 0928709f023fc8f58cb3ae1dfa937b2f3b01d476 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Thu, 7 Mar 2024 22:00:40 +0100 Subject: [PATCH 08/29] ts fix --- src/libs/actions/Report.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 9ebb1b12ab58..368ef9b75b19 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -803,7 +803,7 @@ function openReport( */ function navigateToAndOpenReport(userLogins: string[], shouldDismissModal = true, reportName?: string) { let newChat: ReportUtils.OptimisticChatReport | EmptyObject = {}; - let chat: ReportUtils.OptimisticChatReport | EmptyObject = {}; + let chat: OnyxEntry | EmptyObject = {}; const participantAccountIDs = PersonalDetailsUtils.getAccountIDsByLogins(userLogins); if (!newGroupDraft) { From 481a49180550c1e2057433f781a02d3b301f5a4c Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Thu, 7 Mar 2024 23:06:29 +0100 Subject: [PATCH 09/29] updated to SelectionList --- src/pages/NewChatConfirmPage.tsx | 71 ++++++++++++++++++++------------ 1 file changed, 44 insertions(+), 27 deletions(-) diff --git a/src/pages/NewChatConfirmPage.tsx b/src/pages/NewChatConfirmPage.tsx index fb90c96af228..7b09152e7815 100644 --- a/src/pages/NewChatConfirmPage.tsx +++ b/src/pages/NewChatConfirmPage.tsx @@ -3,12 +3,16 @@ import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; import Avatar from '@components/Avatar'; +import Badge from '@components/Badge'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; -import OptionsSelector from '@components/OptionsSelector'; import ScreenWrapper from '@components/ScreenWrapper'; +import SelectionList from '@components/SelectionList'; +import TableListItem from '@components/SelectionList/TableListItem'; +import type {ListItem} from '@components/SelectionList/types'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; +import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import * as GroupChatUtils from '@libs/GroupChatUtils'; import Navigation from '@libs/Navigation/Navigation'; @@ -33,6 +37,7 @@ type NewChatConfirmPageProps = NewChatConfirmPageOnyxProps; function NewChatConfirmPage({newGroupDraft, allPersonalDetails}: NewChatConfirmPageProps) { const {translate} = useLocalize(); + const StyleUtils = useStyleUtils(); const styles = useThemeStyles(); const personalData = useCurrentUserPersonalDetails() || CONST.EMPTY_OBJECT; @@ -46,34 +51,50 @@ function NewChatConfirmPage({newGroupDraft, allPersonalDetails}: NewChatConfirmP const groupName = GroupChatUtils.getGroupChatConfirmName(selectedOptions); - const sections = useMemo(() => { - const sectionsList = []; - if (selectedOptions) { - sectionsList.push({ - title: translate('common.members'), - data: selectedOptions, - shouldShow: true, - indexOffset: 0, - }); - } - return sectionsList; - }, [translate, selectedOptions]); - + const sections = useMemo( + () => + selectedOptions + .map((selectedOption) => { + const accountID = selectedOption.accountID; + let roleBadge = null; + const isAdmin = personalData.accountID === selectedOption.accountID; + if (isAdmin) { + roleBadge = ( + + ); + } + return { + value: selectedOption?.text ?? '', + text: selectedOption?.text ?? '', + keyForList: selectedOption?.keyForList ?? '', + isSelected: !isAdmin, + rightElement: roleBadge, + accountID, + icons: selectedOption?.icons, + }; + }) + .sort((a, b) => a.text.toLowerCase().localeCompare(b.text.toLowerCase())), + [selectedOptions, personalData.accountID, translate, styles.textStrong, styles.justifyContentCenter, styles.badgeBordered, styles.activeItemBadge, StyleUtils], + ); /** * Removes a selected option from list if already selected. */ - const unselectOption = (option: OptionData) => { + const unselectOption = (option: ListItem) => { if (!selectedOptions) { return; } - const isOptionInList = selectedOptions.some((selectedOption) => selectedOption.login === option.login); + const isOptionInList = selectedOptions.some((selectedOption) => selectedOption.accountID === option.accountID); if (isOptionInList && personalData && option.accountID === personalData.accountID) { return; } if (isOptionInList) { - const newSelectedAccountIDs = selectedOptions.filter((selectedOption) => selectedOption.login !== option.login).map((optionData) => optionData.accountID) as number[]; + const newSelectedAccountIDs = selectedOptions.filter((selectedOption) => selectedOption.accountID !== option.accountID).map((optionData) => optionData.accountID) as number[]; Report.setGroupDraft(newSelectedAccountIDs); } }; @@ -114,17 +135,13 @@ function NewChatConfirmPage({newGroupDraft, allPersonalDetails}: NewChatConfirmP description={translate('groupConfirmPage.groupName')} /> - From e661e82ffd4fb79ba7814dd64d44795ad5caec97 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Fri, 8 Mar 2024 14:13:28 +0100 Subject: [PATCH 10/29] fixes --- src/libs/actions/Report.ts | 6 +++--- .../sidebar/SidebarScreen/FloatingActionButtonAndPopover.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 368ef9b75b19..6806c5354a0d 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -247,7 +247,7 @@ Onyx.connect({ let newGroupDraft: OnyxEntry; Onyx.connect({ key: ONYXKEYS.NEW_GROUP, - callback: (val) => (newGroupDraft = val), + callback: (value) => (newGroupDraft = value), }); function clearGroupChat() { @@ -811,13 +811,13 @@ function navigateToAndOpenReport(userLogins: string[], shouldDismissModal = true } if (!chat) { if (newGroupDraft) { - const participants: Participants = participantAccountIDs.reduce((obj: Participants, accountID: number) => { + const participants: Participants = participantAccountIDs.reduce((acc: Participants, accountID: number) => { const participant: Participant = { hidden: false, role: accountID === currentUserAccountID ? 'admin' : 'member', }; // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return {...obj, [accountID]: participant}; + return {...acc, [accountID]: participant}; }, {} as Participants); newChat = ReportUtils.buildOptimisticGroupChatReport(participants, reportName, CONST.REPORT.CHAT_TYPE.GROUP_CHAT); diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js index 2e82cdd97e01..1d3a6df1242b 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js @@ -160,7 +160,7 @@ function FloatingActionButtonAndPopover(props) { { icon: Expensicons.ChatBubble, text: translate('sidebarScreen.fabNewChat'), - onSelected: () => interceptAnonymousUser(() => Report.startNewChat()), + onSelected: () => interceptAnonymousUser(Report.startNewChat), }, { icon: Expensicons.MoneyCircle, From b07b55913278683db5a4d03eb6fc5d38dd520e84 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Tue, 12 Mar 2024 00:44:10 +0100 Subject: [PATCH 11/29] Fixes --- src/CONST.ts | 1 + src/ONYXKEYS.ts | 4 +- .../LHNOptionsList/OptionRowLHN.tsx | 2 +- src/libs/GroupChatUtils.ts | 21 +--- src/libs/ReportUtils.ts | 113 ++++-------------- src/libs/SidebarUtils.ts | 5 +- src/libs/actions/IOU.ts | 2 +- src/libs/actions/Report.ts | 30 ++--- src/pages/NewChatConfirmPage.tsx | 64 +++++----- src/pages/NewChatPage.tsx | 28 ++--- src/pages/home/HeaderView.tsx | 4 +- .../settings/Report/ReportSettingsPage.tsx | 2 +- src/types/onyx/NewGroupChat.ts | 6 - src/types/onyx/NewGroupChatDraft.ts | 6 + src/types/onyx/index.ts | 4 +- 15 files changed, 98 insertions(+), 194 deletions(-) delete mode 100644 src/types/onyx/NewGroupChat.ts create mode 100644 src/types/onyx/NewGroupChatDraft.ts diff --git a/src/CONST.ts b/src/CONST.ts index 9228a83de21e..cfbf50262ba9 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1376,6 +1376,7 @@ const CONST = { ADMIN: 'admin', AUDITOR: 'auditor', USER: 'user', + MEMBER: 'member', }, AUTO_REPORTING_FREQUENCIES: { INSTANT: 'instant', diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index b2f1facea713..61e6e9366e5d 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -278,7 +278,7 @@ const ONYXKEYS = { SHOULD_STORE_LOGS: 'shouldStoreLogs', /** Stores new group chat draft */ - NEW_GROUP: 'newGroupChat', + NEW_GROUP_CHAT_DRAFT: 'newGroupChatDraft', // Paths of PDF file that has been cached during one session CACHED_PDF_PATHS: 'cachedPDFPaths', @@ -508,7 +508,7 @@ type OnyxValuesMapping = { [ONYXKEYS.IOU]: OnyxTypes.IOU; [ONYXKEYS.MODAL]: OnyxTypes.Modal; [ONYXKEYS.NETWORK]: OnyxTypes.Network; - [ONYXKEYS.NEW_GROUP]: OnyxTypes.NewGroupChat; + [ONYXKEYS.NEW_GROUP_CHAT_DRAFT]: OnyxTypes.NewGroupChatDraft; [ONYXKEYS.CUSTOM_STATUS_DRAFT]: OnyxTypes.CustomStatusDraft; [ONYXKEYS.INPUT_FOCUSED]: boolean; [ONYXKEYS.PERSONAL_DETAILS_LIST]: OnyxTypes.PersonalDetailsList; diff --git a/src/components/LHNOptionsList/OptionRowLHN.tsx b/src/components/LHNOptionsList/OptionRowLHN.tsx index 923337ba9ada..a27424ceb392 100644 --- a/src/components/LHNOptionsList/OptionRowLHN.tsx +++ b/src/components/LHNOptionsList/OptionRowLHN.tsx @@ -116,7 +116,7 @@ function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, opti const isStatusVisible = !!emojiCode && ReportUtils.isOneOnOneChat(!isEmptyObject(report) ? report : null); const isGroupChat = optionItem.type === CONST.REPORT.TYPE.CHAT && !optionItem.chatType && !optionItem.isThread && (optionItem.displayNamesWithTooltips?.length ?? 0) > 2; - const fullTitle = isGroupChat ? getGroupChatName(!isEmptyObject(report) ? report : null) : optionItem.text; + const fullTitle = isGroupChat ? getGroupChatName(!isEmptyObject(report) && report.participantAccountIDs ? report.participantAccountIDs : []) : optionItem.text; const subscriptAvatarBorderColor = isFocused ? focusedBackgroundColor : theme.sidebar; return ( diff --git a/src/libs/GroupChatUtils.ts b/src/libs/GroupChatUtils.ts index d694099989a6..8c64100df153 100644 --- a/src/libs/GroupChatUtils.ts +++ b/src/libs/GroupChatUtils.ts @@ -1,27 +1,11 @@ -import type {OnyxEntry} from 'react-native-onyx'; -import type {Report} from '@src/types/onyx'; import localeCompare from './LocaleCompare'; -import type {OptionData} from './ReportUtils'; import * as ReportUtils from './ReportUtils'; -/** - * Returns the group chat name for confirm page - */ -function getGroupChatConfirmName(participants: OptionData[]): string | undefined { - const isMultipleParticipantReport = participants.length > 1; - - return participants - .map((participant) => ReportUtils.getDisplayNameForParticipant(participant.accountID ?? undefined, isMultipleParticipantReport)) - .sort((first, second) => localeCompare(first ?? '', second ?? '')) - .filter(Boolean) - .join(', '); -} - /** * Returns the report name if the report is a group chat */ -function getGroupChatName(report: OnyxEntry): string | undefined { - const participants = report?.participantAccountIDs ?? []; +function getGroupChatName(participantAccountIDs: number[]): string | undefined { + const participants = participantAccountIDs ?? []; const isMultipleParticipantReport = participants.length > 1; return participants @@ -34,5 +18,4 @@ function getGroupChatName(report: OnyxEntry): string | undefined { export { // eslint-disable-next-line import/prefer-default-export getGroupChatName, - getGroupChatConfirmName, }; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 7715917cee9e..63cf6269b09e 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -46,7 +46,7 @@ import type { ReimbursementDeQueuedMessage, } from '@src/types/onyx/OriginalMessage'; import type {Status} from '@src/types/onyx/PersonalDetails'; -import type {NotificationPreference, Participants, PendingChatMember} from '@src/types/onyx/Report'; +import type {NotificationPreference, Participants, PendingChatMember, Participant as ReportParticipant} from '@src/types/onyx/Report'; import type {Message, ReportActionBase, ReportActions} from '@src/types/onyx/ReportAction'; import type {Receipt, TransactionChanges, WaypointCollection} from '@src/types/onyx/Transaction'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; @@ -244,6 +244,7 @@ type OptimisticChatReport = Pick< | 'pendingFields' | 'parentReportActionID' | 'parentReportID' + | 'participants' | 'participantAccountIDs' | 'visibleChatMemberAccountIDs' | 'policyID' @@ -258,39 +259,6 @@ type OptimisticChatReport = Pick< isOptimisticReport: true; }; -type OptimisticGroupChatReport = Pick< - Report, - | 'type' - | 'chatType' - | 'chatReportID' - | 'iouReportID' - | 'isOwnPolicyExpenseChat' - | 'isPinned' - | 'lastActorAccountID' - | 'lastMessageTranslationKey' - | 'lastMessageHtml' - | 'lastMessageText' - | 'lastReadTime' - | 'lastVisibleActionCreated' - | 'notificationPreference' - | 'oldPolicyName' - | 'ownerAccountID' - | 'pendingFields' - | 'parentReportActionID' - | 'parentReportID' - | 'participants' - | 'policyID' - | 'reportID' - | 'reportName' - | 'stateNum' - | 'statusNum' - | 'visibility' - | 'description' - | 'writeCapability' -> & { - isOptimisticReport: true; -}; - type OptimisticTaskReportAction = Pick< ReportAction, | 'reportActionID' @@ -957,7 +925,7 @@ function isSelfDM(report: OnyxEntry): boolean { return getChatType(report) === CONST.REPORT.CHAT_TYPE.SELF_DM; } -function isGroupChatType(report: OnyxEntry): boolean { +function isGroupChat(report: OnyxEntry): boolean { return getChatType(report) === CONST.REPORT.CHAT_TYPE.GROUP_CHAT; } @@ -3575,6 +3543,16 @@ function buildOptimisticChatReport( parentReportID = '', description = '', ): OptimisticChatReport { + const participants: Participants = participantList.reduce((acc: Participants, accountID: number) => { + const participant: ReportParticipant = { + hidden: false, + role: accountID === currentUserAccountID ? CONST.POLICY.ROLE.ADMIN : CONST.POLICY.ROLE.MEMBER, + }; + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return {...acc, [accountID]: participant}; + }, {} as Participants); + const isGroup = chatType === CONST.REPORT.CHAT_TYPE.GROUP_CHAT; + const currentNotificationPreference = isGroup ? CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN : notificationPreference; const currentTime = DateUtils.getDBTime(); const isNewlyCreatedWorkspaceChat = chatType === CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT && isOwnPolicyExpenseChat; return { @@ -3589,64 +3567,16 @@ function buildOptimisticChatReport( lastMessageText: undefined, lastReadTime: currentTime, lastVisibleActionCreated: currentTime, - notificationPreference, + notificationPreference: currentNotificationPreference, oldPolicyName, ownerAccountID: ownerAccountID || CONST.REPORT.OWNER_ACCOUNT_ID_FAKE, parentReportActionID, parentReportID, // When creating a report the participantsAccountIDs and visibleChatMemberAccountIDs are the same - participantAccountIDs: participantList, - visibleChatMemberAccountIDs: participantList, - policyID, - reportID: generateReportID(), - reportName, - stateNum: 0, - statusNum: 0, - visibility, - description, - writeCapability, - }; -} - -/** - * Builds an optimistic group chat report with a randomly generated reportID and as much information as we currently have - */ -function buildOptimisticGroupChatReport( - participantList: Participants, - reportName: string = CONST.REPORT.DEFAULT_REPORT_NAME, - chatType: ValueOf | undefined = undefined, - policyID: string = CONST.POLICY.OWNER_EMAIL_FAKE, - ownerAccountID: number = CONST.REPORT.OWNER_ACCOUNT_ID_FAKE, - isOwnPolicyExpenseChat = false, - oldPolicyName = '', - visibility: ValueOf | undefined = undefined, - writeCapability: ValueOf | undefined = undefined, - notificationPreference: NotificationPreference = CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS, - parentReportActionID = '', - parentReportID = '', - description = '', -): OptimisticGroupChatReport { - const currentTime = DateUtils.getDBTime(); - const isNewlyCreatedWorkspaceChat = chatType === CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT && isOwnPolicyExpenseChat; - return { - isOptimisticReport: true, - type: CONST.REPORT.TYPE.CHAT, - chatType, - isOwnPolicyExpenseChat, - isPinned: reportName === CONST.REPORT.WORKSPACE_CHAT_ROOMS.ADMINS || isNewlyCreatedWorkspaceChat, - lastActorAccountID: 0, - lastMessageTranslationKey: '', - lastMessageHtml: '', - lastMessageText: undefined, - lastReadTime: currentTime, - lastVisibleActionCreated: currentTime, - notificationPreference, - oldPolicyName, - ownerAccountID: ownerAccountID || CONST.REPORT.OWNER_ACCOUNT_ID_FAKE, - parentReportActionID, - parentReportID, - // For group chats we need to have participants object - participants: participantList, + participantAccountIDs: !isGroup ? participantList : undefined, + visibleChatMemberAccountIDs: !isGroup ? participantList : undefined, + // For group chats we need to have participants object as we are migrating away from `participantAccountIDs` and `visibleChatMemberAccountIDs`. See https://github.com/Expensify/App/issues/34692 + participants: isGroup ? participants : undefined, policyID, reportID: generateReportID(), reportName, @@ -5022,7 +4952,7 @@ function getIOUReportActionDisplayMessage(reportAction: OnyxEntry) * - More than 2 participants. * */ -function isGroupChat(report: OnyxEntry): boolean { +function isDeprecatedGroupDM(report: OnyxEntry): boolean { return Boolean( report && !isChatThread(report) && @@ -5364,7 +5294,6 @@ export { buildOptimisticWorkspaceChats, buildOptimisticTaskReport, buildOptimisticChatReport, - buildOptimisticGroupChatReport, buildOptimisticClosedReportAction, buildOptimisticCreatedReportAction, buildOptimisticRenamedRoomReportAction, @@ -5464,7 +5393,7 @@ export { hasMissingSmartscanFields, getIOUReportActionDisplayMessage, isWaitingForAssigneeToCompleteTask, - isGroupChat, + isDeprecatedGroupDM, isOpenExpenseReport, shouldUseFullTitleToDisplay, parseReportRouteParams, @@ -5506,7 +5435,7 @@ export { isJoinRequestInAdminRoom, canAddOrDeleteTransactions, shouldCreateNewMoneyRequestReport, - isGroupChatType, + isGroupChat, }; export type { diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 71b3fd23a03c..012132ac124c 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -384,7 +384,10 @@ function getOptionData({ .join(' '); } - result.alternateText = ReportUtils.isGroupChat(report) && lastActorDisplayName ? `${lastActorDisplayName}: ${lastMessageText}` : lastMessageText || formattedLogin; + result.alternateText = + ReportUtils.isDeprecatedGroupDM(report) || (ReportUtils.isGroupChat(report) && lastActorDisplayName) + ? `${lastActorDisplayName}: ${lastMessageText}` + : lastMessageText || formattedLogin; } result.isIOUReportOwner = ReportUtils.isIOUOwnedByCurrentUser(result as Report); diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 40dcd18255bc..4b88bb7a77a8 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -1657,7 +1657,7 @@ function createSplitsAndOnyxData( const currentUserEmailForIOUSplit = PhoneNumber.addSMSDomainIfPhoneNumber(currentUserLogin); const participantAccountIDs = participants.map((participant) => Number(participant.accountID)); const existingSplitChatReport = - participants.length > 1 && (existingSplitChatReportID || participants[0].reportID) + existingSplitChatReportID || participants[0].reportID ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${existingSplitChatReportID || participants[0].reportID}`] : ReportUtils.getChatByParticipants(participantAccountIDs); const splitChatReport = existingSplitChatReport ?? ReportUtils.buildOptimisticChatReport(participantAccountIDs); diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 731d2b4f3f26..e2ca61f07fa6 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -71,7 +71,7 @@ import type {Route} from '@src/ROUTES'; import ROUTES from '@src/ROUTES'; import INPUT_IDS from '@src/types/form/NewRoomForm'; import type { - NewGroupChat, + NewGroupChatDraft, PersonalDetails, PersonalDetailsList, PolicyReportField, @@ -81,7 +81,7 @@ import type { ReportUserIsTyping, } from '@src/types/onyx'; import type {Decision, OriginalMessageIOU} from '@src/types/onyx/OriginalMessage'; -import type {NotificationPreference, Participant, Participants, RoomVisibility, WriteCapability} from '@src/types/onyx/Report'; +import type {NotificationPreference, RoomVisibility, WriteCapability} from '@src/types/onyx/Report'; import type Report from '@src/types/onyx/Report'; import type {Message, ReportActionBase, ReportActions} from '@src/types/onyx/ReportAction'; import type ReportAction from '@src/types/onyx/ReportAction'; @@ -244,14 +244,14 @@ Onyx.connect({ callback: (val) => (allRecentlyUsedReportFields = val), }); -let newGroupDraft: OnyxEntry; +let newGroupDraft: OnyxEntry; Onyx.connect({ - key: ONYXKEYS.NEW_GROUP, + key: ONYXKEYS.NEW_GROUP_CHAT_DRAFT, callback: (value) => (newGroupDraft = value), }); function clearGroupChat() { - Onyx.set(ONYXKEYS.NEW_GROUP, null); + Onyx.set(ONYXKEYS.NEW_GROUP_CHAT_DRAFT, null); } function startNewChat() { @@ -664,7 +664,7 @@ function openReport( if (newReportObject.chatType === CONST.REPORT.CHAT_TYPE.GROUP_CHAT) { parameters.chatType = CONST.REPORT.CHAT_TYPE.GROUP_CHAT; parameters.groupChatAdminLogins = currentUserEmail; - parameters.optimisticAccountIDList = participantAccountIDList ? participantAccountIDList.join(',') : ''; + parameters.optimisticAccountIDList = participantAccountIDList.join(','); parameters.reportName = newReportObject.reportName ?? ''; } @@ -808,23 +808,15 @@ function navigateToAndOpenReport(userLogins: string[], shouldDismissModal = true if (!newGroupDraft) { chat = ReportUtils.getChatByParticipants(participantAccountIDs); } - if (!chat) { + + if (isEmptyObject(chat)) { if (newGroupDraft) { - const participants: Participants = participantAccountIDs.reduce((acc: Participants, accountID: number) => { - const participant: Participant = { - hidden: false, - role: accountID === currentUserAccountID ? 'admin' : 'member', - }; - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return {...acc, [accountID]: participant}; - }, {} as Participants); - - newChat = ReportUtils.buildOptimisticGroupChatReport(participants, reportName, CONST.REPORT.CHAT_TYPE.GROUP_CHAT); + newChat = ReportUtils.buildOptimisticChatReport(participantAccountIDs, reportName, CONST.REPORT.CHAT_TYPE.GROUP_CHAT); } else { newChat = ReportUtils.buildOptimisticChatReport(participantAccountIDs); } } - const report = chat ?? newChat; + const report = isEmptyObject(chat) ? newChat : chat; // We want to pass newChat here because if anything is passed in that param (even an existing chat), we will try to create a chat on the server openReport(report.reportID, userLogins, newChat); @@ -3011,7 +3003,7 @@ function resolveActionableMentionWhisper(reportId: string, reportAction: OnyxEnt } function setGroupDraft(invitedUsersIDs: number[], reportName = '') { - Onyx.set(ONYXKEYS.NEW_GROUP, {selectedOptions: invitedUsersIDs, reportName}); + Onyx.merge(ONYXKEYS.NEW_GROUP_CHAT_DRAFT, {participantAccountIDs: invitedUsersIDs, reportName}); } export { diff --git a/src/pages/NewChatConfirmPage.tsx b/src/pages/NewChatConfirmPage.tsx index 7b09152e7815..905660d240ff 100644 --- a/src/pages/NewChatConfirmPage.tsx +++ b/src/pages/NewChatConfirmPage.tsx @@ -27,7 +27,7 @@ import type * as OnyxTypes from '@src/types/onyx'; type NewChatConfirmPageOnyxProps = { /** New group chat draft data */ - newGroupDraft: OnyxEntry; + newGroupDraft: OnyxEntry; /** All of the personal details for everyone */ allPersonalDetails: OnyxEntry; @@ -35,29 +35,34 @@ type NewChatConfirmPageOnyxProps = { type NewChatConfirmPageProps = NewChatConfirmPageOnyxProps; +type Section = ListItem & {value: string}; + function NewChatConfirmPage({newGroupDraft, allPersonalDetails}: NewChatConfirmPageProps) { const {translate} = useLocalize(); const StyleUtils = useStyleUtils(); const styles = useThemeStyles(); - const personalData = useCurrentUserPersonalDetails() || CONST.EMPTY_OBJECT; + const personalData = useCurrentUserPersonalDetails(); - const selectedOptions = useMemo(() => { - const invitedUsersPersonalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs(newGroupDraft?.selectedOptions, allPersonalDetails); + const selectedOptions = useMemo((): OptionData[] => { + const invitedUsersPersonalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs(newGroupDraft?.participantAccountIDs, allPersonalDetails); const members = OptionsListUtils.getMemberInviteOptions(invitedUsersPersonalDetails); const currentUserOptionData = members.currentUserOption; - const options = [...members.personalDetails, currentUserOptionData] as OptionData[]; + if (!currentUserOptionData) { + return members.personalDetails; + } + const options = [...members.personalDetails, currentUserOptionData]; return options; }, [newGroupDraft, allPersonalDetails]); - const groupName = GroupChatUtils.getGroupChatConfirmName(selectedOptions); + const groupName = GroupChatUtils.getGroupChatName(newGroupDraft?.participantAccountIDs ?? []); - const sections = useMemo( + const sections: Section[] = useMemo( () => selectedOptions - .map((selectedOption) => { + .map((selectedOption: OptionData) => { const accountID = selectedOption.accountID; + const isAdmin = personalData.accountID === accountID; let roleBadge = null; - const isAdmin = personalData.accountID === selectedOption.accountID; if (isAdmin) { roleBadge = ( ); } - return { + + const section: Section = { value: selectedOption?.text ?? '', text: selectedOption?.text ?? '', keyForList: selectedOption?.keyForList ?? '', @@ -76,27 +82,21 @@ function NewChatConfirmPage({newGroupDraft, allPersonalDetails}: NewChatConfirmP accountID, icons: selectedOption?.icons, }; + return section; }) .sort((a, b) => a.text.toLowerCase().localeCompare(b.text.toLowerCase())), [selectedOptions, personalData.accountID, translate, styles.textStrong, styles.justifyContentCenter, styles.badgeBordered, styles.activeItemBadge, StyleUtils], ); + /** * Removes a selected option from list if already selected. */ const unselectOption = (option: ListItem) => { - if (!selectedOptions) { - return; - } - const isOptionInList = selectedOptions.some((selectedOption) => selectedOption.accountID === option.accountID); - - if (isOptionInList && personalData && option.accountID === personalData.accountID) { + if (!newGroupDraft) { return; } - - if (isOptionInList) { - const newSelectedAccountIDs = selectedOptions.filter((selectedOption) => selectedOption.accountID !== option.accountID).map((optionData) => optionData.accountID) as number[]; - Report.setGroupDraft(newSelectedAccountIDs); - } + const newSelectedAccountIDs = newGroupDraft.participantAccountIDs.filter((participantAccountID) => participantAccountID !== option.accountID); + Report.setGroupDraft(newSelectedAccountIDs); }; const createGroup = () => { @@ -104,8 +104,6 @@ function NewChatConfirmPage({newGroupDraft, allPersonalDetails}: NewChatConfirmP if (logins.length < 1) { return; } - const accountIDs = selectedOptions.map((selectedOption: OptionData) => selectedOption.accountID) as number[]; - Report.setGroupDraft(accountIDs, groupName); Report.navigateToAndOpenReport(logins, true, ''); }; @@ -134,16 +132,14 @@ function NewChatConfirmPage({newGroupDraft, allPersonalDetails}: NewChatConfirmP shouldCheckActionAllowedOnPress={false} description={translate('groupConfirmPage.groupName')} /> - - - + 1} + confirmButtonText={translate('newChatPage.startGroup')} + onConfirm={createGroup} + /> ); } @@ -152,7 +148,7 @@ NewChatConfirmPage.displayName = 'NewChatConfirmPage'; export default withOnyx({ newGroupDraft: { - key: ONYXKEYS.NEW_GROUP, + key: ONYXKEYS.NEW_GROUP_CHAT_DRAFT, }, allPersonalDetails: { key: ONYXKEYS.PERSONAL_DETAILS_LIST, diff --git a/src/pages/NewChatPage.tsx b/src/pages/NewChatPage.tsx index fc106f08642d..254ab581b6aa 100755 --- a/src/pages/NewChatPage.tsx +++ b/src/pages/NewChatPage.tsx @@ -32,7 +32,7 @@ type NewChatPageWithOnyxProps = { reports: OnyxCollection; /** New group chat draft data */ - newGroupDraft: OnyxEntry; + newGroupDraft: OnyxEntry; /** All of the personal details for everyone */ personalDetails: OnyxEntry; @@ -64,7 +64,7 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingF const {isSmallScreenWidth} = useWindowDimensions(); const [didScreenTransitionEnd, setDidScreenTransitionEnd] = useState(false); - const personalData = useCurrentUserPersonalDetails() || CONST.EMPTY_OBJECT; + const personalData = useCurrentUserPersonalDetails(); const maxParticipantsReached = selectedOptions.length === CONST.REPORT.MAXIMUM_PARTICIPANTS; const setSearchTermAndSearchInServer = useSearchTermAndSearch(setSearchTerm, maxParticipantsReached); @@ -166,21 +166,21 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingF * or navigates to the existing chat if one with those participants already exists. */ const createChat = (option: OptionData) => { - if (!option.login) { + if ((!option.login && selectedOptions.length !== 1) || !selectedOptions[0].login) { return; } - Report.navigateToAndOpenReport([option.login]); + const login = option.login ?? selectedOptions[0].login; + Report.navigateToAndOpenReport([login]); }; /** * Navigates to create group confirm page */ const navigateToConfirmPage = () => { - if (selectedOptions.length < 1) { - return; - } - const selectedAccountIDs = selectedOptions.map((option) => option.accountID) as number[]; + const selectedAccountIDs: number[] = selectedOptions + .map((option: OptionData) => option.accountID) + .filter((accountID): accountID is number => accountID !== null && accountID !== undefined); if (personalData) { - const accountIDs = [...selectedAccountIDs, personalData.accountID] as number[]; + const accountIDs = [...selectedAccountIDs, personalData.accountID]; Report.setGroupDraft(accountIDs); Navigation.navigate(ROUTES.NEW_CHAT_CONFIRM); } @@ -234,12 +234,12 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingF return; } updateOptions(); - if (newGroupDraft?.selectedOptions) { - const invitedUsersPersonalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs(newGroupDraft?.selectedOptions, personalDetails); + if (newGroupDraft?.participantAccountIDs) { + const invitedUsersPersonalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs(newGroupDraft?.participantAccountIDs, personalDetails); const groupSelectedOptions = OptionsListUtils.getMemberInviteOptions(invitedUsersPersonalDetails).personalDetails; setSelectedOptions(groupSelectedOptions); } - }, [didScreenTransitionEnd, updateOptions, newGroupDraft?.selectedOptions, personalDetails]); + }, [didScreenTransitionEnd, updateOptions, newGroupDraft?.participantAccountIDs, personalDetails]); const {inputCallbackRef} = useAutoFocusInput(); @@ -281,7 +281,7 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingF referralContentType={CONST.REFERRAL_PROGRAM.CONTENT_TYPES.START_CHAT} confirmButtonText={selectedOptions.length > 1 ? translate('common.next') : translate('newChatPage.createChat')} textInputAlert={isOffline ? [`${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}`, {isTranslated: true}] : ''} - onConfirmSelection={navigateToConfirmPage} + onConfirmSelection={selectedOptions.length > 1 ? navigateToConfirmPage : createChat} textInputLabel={translate('optionsSelector.nameEmailOrPhoneNumber')} safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle} isLoadingNewOptions={isSearchingForReports} @@ -303,7 +303,7 @@ export default withOnyx({ selector: (data) => data?.dismissedReferralBanners ?? {}, }, newGroupDraft: { - key: ONYXKEYS.NEW_GROUP, + key: ONYXKEYS.NEW_GROUP_CHAT_DRAFT, }, reports: { key: ONYXKEYS.COLLECTION.REPORT, diff --git a/src/pages/home/HeaderView.tsx b/src/pages/home/HeaderView.tsx index 8322477df07b..e26108a456e8 100644 --- a/src/pages/home/HeaderView.tsx +++ b/src/pages/home/HeaderView.tsx @@ -74,7 +74,7 @@ function HeaderView({report, personalDetails, parentReport, policy, session, rep const theme = useTheme(); const styles = useThemeStyles(); const isSelfDM = ReportUtils.isSelfDM(report); - const isGroupChat = ReportUtils.isGroupChat(report) || ReportUtils.isGroupChatType(report); + const isGroupChat = ReportUtils.isGroupChat(report) || ReportUtils.isDeprecatedGroupDM(report); // Currently, currentUser is not included in participantAccountIDs, so for selfDM, we need to add the currentUser as participants. const participants = isSelfDM ? [session?.accountID ?? -1] : report?.participantAccountIDs ?? []; const participantPersonalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs(participants, personalDetails); @@ -87,7 +87,7 @@ function HeaderView({report, personalDetails, parentReport, policy, session, rep const isTaskReport = ReportUtils.isTaskReport(report); const reportHeaderData = !isTaskReport && !isChatThread && report.parentReportID ? parentReport : report; // Use sorted display names for the title for group chats on native small screen widths - const title = isGroupChat ? getGroupChatName(report) : ReportUtils.getReportName(reportHeaderData); + const title = isGroupChat ? getGroupChatName(report.participantAccountIDs ?? []) : ReportUtils.getReportName(reportHeaderData); const subtitle = ReportUtils.getChatRoomSubtitle(reportHeaderData); const parentNavigationSubtitleData = ReportUtils.getParentNavigationSubtitle(reportHeaderData); const isConcierge = ReportUtils.hasSingleParticipant(report) && participants.includes(CONST.ACCOUNT_ID.CONCIERGE); diff --git a/src/pages/settings/Report/ReportSettingsPage.tsx b/src/pages/settings/Report/ReportSettingsPage.tsx index 383cbbcb0833..12fcc5ee2bad 100644 --- a/src/pages/settings/Report/ReportSettingsPage.tsx +++ b/src/pages/settings/Report/ReportSettingsPage.tsx @@ -48,7 +48,7 @@ function ReportSettingsPage({report, policies}: ReportSettingsPageProps) { const shouldShowNotificationPref = !isMoneyRequestReport && report?.notificationPreference !== CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN; const roomNameLabel = translate(isMoneyRequestReport ? 'workspace.editor.nameInputLabel' : 'newRoomPage.roomName'); - const reportName = ReportUtils.isGroupChat(report) ? getGroupChatName(report) : ReportUtils.getReportName(report); + const reportName = ReportUtils.isDeprecatedGroupDM(report) || ReportUtils.isGroupChat(report) ? getGroupChatName(report.participantAccountIDs ?? []) : ReportUtils.getReportName(report); const shouldShowWriteCapability = !isMoneyRequestReport; diff --git a/src/types/onyx/NewGroupChat.ts b/src/types/onyx/NewGroupChat.ts deleted file mode 100644 index 57fd2b1c93ae..000000000000 --- a/src/types/onyx/NewGroupChat.ts +++ /dev/null @@ -1,6 +0,0 @@ -type NewGroupChat = { - selectedOptions: number[]; - reportName: string; -}; - -export default NewGroupChat; diff --git a/src/types/onyx/NewGroupChatDraft.ts b/src/types/onyx/NewGroupChatDraft.ts new file mode 100644 index 000000000000..6a948d398157 --- /dev/null +++ b/src/types/onyx/NewGroupChatDraft.ts @@ -0,0 +1,6 @@ +type NewGroupChatDraft = { + participantAccountIDs: number[]; + reportName: string; +}; + +export default NewGroupChatDraft; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index 9130520e99f1..ac17c962be62 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -26,7 +26,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 NewGroupChatDraft from './NewGroupChatDraft'; import type {OnyxUpdateEvent, OnyxUpdatesFromServer} from './OnyxUpdatesFromServer'; import type {DecisionName, OriginalMessageIOU} from './OriginalMessage'; import type PersonalBankAccount from './PersonalBankAccount'; @@ -161,7 +161,7 @@ export type { LastPaymentMethod, LastSelectedDistanceRates, InvitedEmailsToAccountIDs, - NewGroupChat, + NewGroupChatDraft, Log, PolicyJoinMember, }; From 0abab3e488503df34d087c1c098308307bfd1358 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Tue, 12 Mar 2024 19:08:21 +0100 Subject: [PATCH 12/29] fix ts --- src/pages/NewChatConfirmPage.tsx | 3 ++- src/pages/NewChatPage.tsx | 18 +++++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/pages/NewChatConfirmPage.tsx b/src/pages/NewChatConfirmPage.tsx index 905660d240ff..e0d60c0a6ee7 100644 --- a/src/pages/NewChatConfirmPage.tsx +++ b/src/pages/NewChatConfirmPage.tsx @@ -84,7 +84,8 @@ function NewChatConfirmPage({newGroupDraft, allPersonalDetails}: NewChatConfirmP }; return section; }) - .sort((a, b) => a.text.toLowerCase().localeCompare(b.text.toLowerCase())), + .filter((selectedOption) => selectedOption.text !== undefined) + .sort((a, b) => a.text?.toLowerCase().localeCompare(b.text?.toLowerCase() ?? '') ?? -1), [selectedOptions, personalData.accountID, translate, styles.textStrong, styles.justifyContentCenter, styles.badgeBordered, styles.activeItemBadge, StyleUtils], ); diff --git a/src/pages/NewChatPage.tsx b/src/pages/NewChatPage.tsx index 33352189b816..30aa11691b4c 100755 --- a/src/pages/NewChatPage.tsx +++ b/src/pages/NewChatPage.tsx @@ -124,6 +124,7 @@ function NewChatPage({isGroupChat, reports, isSearchingForReports}: NewChatPageP reports, isGroupChat, }); + const sections = useMemo(() => { const sectionsList = []; let indexOffset = 0; @@ -168,12 +169,11 @@ function NewChatPage({isGroupChat, reports, isSearchingForReports}: NewChatPageP * 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 = (option: OptionData) => { - if ((!option.login && selectedOptions.length !== 1) || !selectedOptions[0].login) { + const createChat = (option: ListItem) => { + if (!option.login) { return; } - const login = option.login ?? selectedOptions[0].login; - Report.navigateToAndOpenReport([login]); + Report.navigateToAndOpenReport([option.login]); }; /** @@ -251,6 +251,14 @@ function NewChatPage({isGroupChat, reports, isSearchingForReports}: NewChatPageP Navigation.navigate(ROUTES.NEW_CHAT_CONFIRM); } }; + + const createChatDM = () => { + if (selectedOptions.length !== 1 || !selectedOptions[0].login) { + return; + } + const login = selectedOptions[0].login; + Report.navigateToAndOpenReport([login]); + }; return ( <> 1 ? translate('common.next') : translate('newChatPage.createChat')} - onPress={navigateToConfirmPage} + onPress={selectedOptions.length > 1 ? navigateToConfirmPage : createChatDM} pressOnEnter /> )} From 4126f858aa025cf32ab589a7ad47ff20f118ed45 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Tue, 12 Mar 2024 23:10:35 +0100 Subject: [PATCH 13/29] resolve issues, change to logins instead of IDs --- src/libs/ReportUtils.ts | 3 ++- src/libs/actions/Report.ts | 5 +++-- src/pages/NewChatConfirmPage.tsx | 22 +++++++++++----------- src/pages/NewChatPage.tsx | 11 +++++------ src/types/onyx/NewGroupChatDraft.ts | 2 +- 5 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 36077bcecce2..b8205872df85 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4190,7 +4190,8 @@ function shouldReportBeInOptionList({ !isArchivedRoom(report) && !isMoneyRequestReport(report) && !isTaskReport(report) && - !isSelfDM(report)) + !isSelfDM(report) && + !isGroupChat(report)) ) { return false; } diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index ce8294920f13..c6e670ad88c9 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -805,6 +805,7 @@ function navigateToAndOpenReport(userLogins: string[], shouldDismissModal = true let chat: OnyxEntry | EmptyObject = {}; const participantAccountIDs = PersonalDetailsUtils.getAccountIDsByLogins(userLogins); + // If we are not creating a new Group Chat then we are creating a 1:1 DM and will look for an existing chat if (!newGroupDraft) { chat = ReportUtils.getChatByParticipants(participantAccountIDs); } @@ -2991,8 +2992,8 @@ function resolveActionableMentionWhisper(reportId: string, reportAction: OnyxEnt API.write(WRITE_COMMANDS.RESOLVE_ACTIONABLE_MENTION_WHISPER, parameters, {optimisticData, failureData}); } -function setGroupDraft(invitedUsersIDs: number[], reportName = '') { - Onyx.merge(ONYXKEYS.NEW_GROUP_CHAT_DRAFT, {participantAccountIDs: invitedUsersIDs, reportName}); +function setGroupDraft(invitedLogins: string[], reportName = '') { + Onyx.merge(ONYXKEYS.NEW_GROUP_CHAT_DRAFT, {participantLogins: invitedLogins, reportName}); } export { diff --git a/src/pages/NewChatConfirmPage.tsx b/src/pages/NewChatConfirmPage.tsx index e0d60c0a6ee7..95386f9a776a 100644 --- a/src/pages/NewChatConfirmPage.tsx +++ b/src/pages/NewChatConfirmPage.tsx @@ -17,6 +17,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import * as GroupChatUtils from '@libs/GroupChatUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; +import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as ReportUtils from '@libs/ReportUtils'; import type {OptionData} from '@libs/ReportUtils'; import * as Report from '@userActions/Report'; @@ -42,9 +43,9 @@ function NewChatConfirmPage({newGroupDraft, allPersonalDetails}: NewChatConfirmP const StyleUtils = useStyleUtils(); const styles = useThemeStyles(); const personalData = useCurrentUserPersonalDetails(); - + const participantAccountIDs = PersonalDetailsUtils.getAccountIDsByLogins(newGroupDraft?.participantLogins ?? []); const selectedOptions = useMemo((): OptionData[] => { - const invitedUsersPersonalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs(newGroupDraft?.participantAccountIDs, allPersonalDetails); + const invitedUsersPersonalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs(participantAccountIDs, allPersonalDetails); const members = OptionsListUtils.getMemberInviteOptions(invitedUsersPersonalDetails); const currentUserOptionData = members.currentUserOption; if (!currentUserOptionData) { @@ -52,9 +53,9 @@ function NewChatConfirmPage({newGroupDraft, allPersonalDetails}: NewChatConfirmP } const options = [...members.personalDetails, currentUserOptionData]; return options; - }, [newGroupDraft, allPersonalDetails]); + }, [participantAccountIDs, allPersonalDetails]); - const groupName = GroupChatUtils.getGroupChatName(newGroupDraft?.participantAccountIDs ?? []); + const groupName = GroupChatUtils.getGroupChatName(participantAccountIDs ?? []); const sections: Section[] = useMemo( () => @@ -74,7 +75,7 @@ function NewChatConfirmPage({newGroupDraft, allPersonalDetails}: NewChatConfirmP } const section: Section = { - value: selectedOption?.text ?? '', + value: selectedOption?.login ?? '', text: selectedOption?.text ?? '', keyForList: selectedOption?.keyForList ?? '', isSelected: !isAdmin, @@ -92,20 +93,19 @@ function NewChatConfirmPage({newGroupDraft, allPersonalDetails}: NewChatConfirmP /** * Removes a selected option from list if already selected. */ - const unselectOption = (option: ListItem) => { + const unselectOption = (option: Section) => { if (!newGroupDraft) { return; } - const newSelectedAccountIDs = newGroupDraft.participantAccountIDs.filter((participantAccountID) => participantAccountID !== option.accountID); - Report.setGroupDraft(newSelectedAccountIDs); + const newSelectedLogins = newGroupDraft.participantLogins.filter((participantLogin) => participantLogin !== option.value); + Report.setGroupDraft(newSelectedLogins); }; const createGroup = () => { - const logins = selectedOptions.map((option: OptionData) => option.login) as string[]; - if (logins.length < 1) { + if (!newGroupDraft) { return; } - Report.navigateToAndOpenReport(logins, true, ''); + Report.navigateToAndOpenReport(newGroupDraft.participantLogins, true, ''); }; const navigateBack = () => { diff --git a/src/pages/NewChatPage.tsx b/src/pages/NewChatPage.tsx index 30aa11691b4c..7293378bae96 100755 --- a/src/pages/NewChatPage.tsx +++ b/src/pages/NewChatPage.tsx @@ -242,12 +242,11 @@ function NewChatPage({isGroupChat, reports, isSearchingForReports}: NewChatPageP * Navigates to create group confirm page */ const navigateToConfirmPage = () => { - const selectedAccountIDs: number[] = selectedOptions - .map((option: OptionData) => option.accountID) - .filter((accountID): accountID is number => accountID !== null && accountID !== undefined); - if (personalData) { - const accountIDs = [...selectedAccountIDs, personalData.accountID]; - Report.setGroupDraft(accountIDs); + const selectedLogins: string[] = selectedOptions.map((option: OptionData) => option.login).filter((login): login is string => login !== null && login !== undefined); + if (personalData && personalData.login) { + const logins = [...selectedLogins, personalData.login]; + + Report.setGroupDraft(logins); Navigation.navigate(ROUTES.NEW_CHAT_CONFIRM); } }; diff --git a/src/types/onyx/NewGroupChatDraft.ts b/src/types/onyx/NewGroupChatDraft.ts index 6a948d398157..1ac36434757d 100644 --- a/src/types/onyx/NewGroupChatDraft.ts +++ b/src/types/onyx/NewGroupChatDraft.ts @@ -1,5 +1,5 @@ type NewGroupChatDraft = { - participantAccountIDs: number[]; + participantLogins: string[]; reportName: string; }; From c9116a606462a1acaafc6ec57b963bf6127a5415 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Wed, 13 Mar 2024 13:07:56 +0100 Subject: [PATCH 14/29] fixes --- src/CONST.ts | 4 ++++ src/components/LHNOptionsList/OptionRowLHN.tsx | 6 ++++-- src/libs/GroupChatUtils.ts | 7 +++---- src/libs/ReportUtils.ts | 11 +++++------ src/libs/SidebarUtils.ts | 2 +- src/libs/actions/IOU.ts | 9 +++++++-- src/libs/actions/Report.ts | 17 ++++++++++++++--- src/pages/NewChatConfirmPage.tsx | 2 +- src/pages/home/HeaderView.tsx | 2 +- 9 files changed, 40 insertions(+), 20 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index cb4eefcff984..672868214883 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -570,6 +570,10 @@ const CONST = { MAX_REPORT_PREVIEW_RECEIPTS: 3, }, REPORT: { + ROLE: { + ADMIN: 'admin', + MEMBER: 'member', + }, MAX_COUNT_BEFORE_FOCUS_UPDATE: 30, MAXIMUM_PARTICIPANTS: 8, SPLIT_REPORTID: '-2', diff --git a/src/components/LHNOptionsList/OptionRowLHN.tsx b/src/components/LHNOptionsList/OptionRowLHN.tsx index a27424ceb392..a1c0cb5cc92a 100644 --- a/src/components/LHNOptionsList/OptionRowLHN.tsx +++ b/src/components/LHNOptionsList/OptionRowLHN.tsx @@ -115,8 +115,10 @@ function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, opti const report = ReportUtils.getReport(optionItem.reportID ?? ''); const isStatusVisible = !!emojiCode && ReportUtils.isOneOnOneChat(!isEmptyObject(report) ? report : null); - const isGroupChat = optionItem.type === CONST.REPORT.TYPE.CHAT && !optionItem.chatType && !optionItem.isThread && (optionItem.displayNamesWithTooltips?.length ?? 0) > 2; - const fullTitle = isGroupChat ? getGroupChatName(!isEmptyObject(report) && report.participantAccountIDs ? report.participantAccountIDs : []) : optionItem.text; + const isGroupChat = + ReportUtils.isGroupChat(optionItem) || + (optionItem.type === CONST.REPORT.TYPE.CHAT && !optionItem.chatType && !optionItem.isThread && (optionItem.displayNamesWithTooltips?.length ?? 0) > 2); + const fullTitle = isGroupChat ? getGroupChatName(report?.participantAccountIDs ?? []) : optionItem.text; const subscriptAvatarBorderColor = isFocused ? focusedBackgroundColor : theme.sidebar; return ( diff --git a/src/libs/GroupChatUtils.ts b/src/libs/GroupChatUtils.ts index 8c64100df153..a29aeed45aff 100644 --- a/src/libs/GroupChatUtils.ts +++ b/src/libs/GroupChatUtils.ts @@ -4,11 +4,10 @@ import * as ReportUtils from './ReportUtils'; /** * Returns the report name if the report is a group chat */ -function getGroupChatName(participantAccountIDs: number[]): string | undefined { - const participants = participantAccountIDs ?? []; - const isMultipleParticipantReport = participants.length > 1; +function getGroupChatName(participantAccountIDs: number[]): string { + const isMultipleParticipantReport = participantAccountIDs.length > 1; - return participants + return participantAccountIDs .map((participant) => ReportUtils.getDisplayNameForParticipant(participant, isMultipleParticipantReport)) .sort((first, second) => localeCompare(first ?? '', second ?? '')) .filter(Boolean) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 2a182e943bf3..b96048dc182d 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -3553,16 +3553,15 @@ function buildOptimisticChatReport( parentReportID = '', description = '', ): OptimisticChatReport { - const participants: Participants = participantList.reduce((acc: Participants, accountID: number) => { + const participants = participantList.reduce((reportParticipants: Participants, accountID: number) => { const participant: ReportParticipant = { hidden: false, - role: accountID === currentUserAccountID ? CONST.POLICY.ROLE.ADMIN : CONST.POLICY.ROLE.MEMBER, + role: accountID === currentUserAccountID ? CONST.REPORT.ROLE.ADMIN : CONST.REPORT.ROLE.MEMBER, }; - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return {...acc, [accountID]: participant}; + + return {...reportParticipants, [accountID]: participant}; }, {} as Participants); const isGroup = chatType === CONST.REPORT.CHAT_TYPE.GROUP_CHAT; - const currentNotificationPreference = isGroup ? CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN : notificationPreference; const currentTime = DateUtils.getDBTime(); const isNewlyCreatedWorkspaceChat = chatType === CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT && isOwnPolicyExpenseChat; return { @@ -3577,7 +3576,7 @@ function buildOptimisticChatReport( lastMessageText: undefined, lastReadTime: currentTime, lastVisibleActionCreated: currentTime, - notificationPreference: currentNotificationPreference, + notificationPreference, oldPolicyName, ownerAccountID: ownerAccountID || CONST.REPORT.OWNER_ACCOUNT_ID_FAKE, parentReportActionID, diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index bc03f612eca6..0d5532cda36d 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -374,7 +374,7 @@ function getOptionData({ } result.alternateText = - ReportUtils.isDeprecatedGroupDM(report) || (ReportUtils.isGroupChat(report) && lastActorDisplayName) + (ReportUtils.isGroupChat(report) || ReportUtils.isDeprecatedGroupDM(report)) && lastActorDisplayName ? `${lastActorDisplayName}: ${lastMessageText}` : lastMessageText || formattedLogin; } diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index af5c40836c74..cba8804ddb1c 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -1652,10 +1652,14 @@ function createSplitsAndOnyxData( ): SplitsAndOnyxData { const currentUserEmailForIOUSplit = PhoneNumber.addSMSDomainIfPhoneNumber(currentUserLogin); const participantAccountIDs = participants.map((participant) => Number(participant.accountID)); + const existingSplitChatReport = + // eslint-disable-next-line no-nested-ternary existingSplitChatReportID || participants[0].reportID ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${existingSplitChatReportID || participants[0].reportID}`] - : ReportUtils.getChatByParticipants(participantAccountIDs); + : participants.length < 2 + ? ReportUtils.getChatByParticipants(participantAccountIDs) + : null; const splitChatReport = existingSplitChatReport ?? ReportUtils.buildOptimisticChatReport(participantAccountIDs); const isOwnPolicyExpenseChat = !!splitChatReport.isOwnPolicyExpenseChat; @@ -2060,7 +2064,8 @@ function splitBillAndOpenReport( ) { const currentCreated = DateUtils.enrichMoneyRequestTimestamp(created); const {splitData, splits, onyxData} = createSplitsAndOnyxData(participants, currentUserLogin, currentUserAccountID, amount, comment, currency, merchant, currentCreated, category, tag); - + console.log('splitData :>> ', splitData); + console.log('onyxData :>> ', onyxData); const parameters: SplitBillParams = { reportID: splitData.chatReportID, amount, diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index aeb7719dd06f..c28d0d754691 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -812,7 +812,18 @@ function navigateToAndOpenReport(userLogins: string[], shouldDismissModal = true if (isEmptyObject(chat)) { if (newGroupDraft) { - newChat = ReportUtils.buildOptimisticChatReport(participantAccountIDs, reportName, CONST.REPORT.CHAT_TYPE.GROUP_CHAT); + newChat = ReportUtils.buildOptimisticChatReport( + participantAccountIDs, + reportName, + CONST.REPORT.CHAT_TYPE.GROUP_CHAT, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, + ); } else { newChat = ReportUtils.buildOptimisticChatReport(participantAccountIDs); } @@ -2992,8 +3003,8 @@ function resolveActionableMentionWhisper(reportId: string, reportAction: OnyxEnt API.write(WRITE_COMMANDS.RESOLVE_ACTIONABLE_MENTION_WHISPER, parameters, {optimisticData, failureData}); } -function setGroupDraft(invitedLogins: string[], reportName = '') { - Onyx.merge(ONYXKEYS.NEW_GROUP_CHAT_DRAFT, {participantLogins: invitedLogins, reportName}); +function setGroupDraft(participantLogins: string[], reportName = '') { + Onyx.merge(ONYXKEYS.NEW_GROUP_CHAT_DRAFT, {participantLogins, reportName}); } export { diff --git a/src/pages/NewChatConfirmPage.tsx b/src/pages/NewChatConfirmPage.tsx index 95386f9a776a..18b452e8d0de 100644 --- a/src/pages/NewChatConfirmPage.tsx +++ b/src/pages/NewChatConfirmPage.tsx @@ -79,13 +79,13 @@ function NewChatConfirmPage({newGroupDraft, allPersonalDetails}: NewChatConfirmP text: selectedOption?.text ?? '', keyForList: selectedOption?.keyForList ?? '', isSelected: !isAdmin, + isDisabled: isAdmin, rightElement: roleBadge, accountID, icons: selectedOption?.icons, }; return section; }) - .filter((selectedOption) => selectedOption.text !== undefined) .sort((a, b) => a.text?.toLowerCase().localeCompare(b.text?.toLowerCase() ?? '') ?? -1), [selectedOptions, personalData.accountID, translate, styles.textStrong, styles.justifyContentCenter, styles.badgeBordered, styles.activeItemBadge, StyleUtils], ); diff --git a/src/pages/home/HeaderView.tsx b/src/pages/home/HeaderView.tsx index e26108a456e8..2a10a2a709e7 100644 --- a/src/pages/home/HeaderView.tsx +++ b/src/pages/home/HeaderView.tsx @@ -145,7 +145,7 @@ function HeaderView({report, personalDetails, parentReport, policy, session, rep const canJoinOrLeave = !isSelfDM && !isGroupChat && (isChatThread || isUserCreatedPolicyRoom || canLeaveRoom); const canJoin = canJoinOrLeave && !isWhisperAction && report.notificationPreference === CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN; - const canLeave = canJoinOrLeave && ((isChatThread && report.notificationPreference?.length) ?? isUserCreatedPolicyRoom ?? canLeaveRoom); + const canLeave = canJoinOrLeave && ((isChatThread && !!report.notificationPreference?.length) || isUserCreatedPolicyRoom || canLeaveRoom); if (canJoin) { threeDotMenuItems.push({ icon: Expensicons.ChatBubbles, From 0d9f7a259361d38625f4b447ac817bcfc26f426e Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Wed, 13 Mar 2024 13:24:42 +0100 Subject: [PATCH 15/29] lint fix --- src/libs/actions/IOU.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index cba8804ddb1c..e0fc0081ef42 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -2064,8 +2064,6 @@ function splitBillAndOpenReport( ) { const currentCreated = DateUtils.enrichMoneyRequestTimestamp(created); const {splitData, splits, onyxData} = createSplitsAndOnyxData(participants, currentUserLogin, currentUserAccountID, amount, comment, currency, merchant, currentCreated, category, tag); - console.log('splitData :>> ', splitData); - console.log('onyxData :>> ', onyxData); const parameters: SplitBillParams = { reportID: splitData.chatReportID, amount, From 1443b2645869fbd3e30ad0856de9b710ece794db Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Thu, 14 Mar 2024 19:30:29 +0100 Subject: [PATCH 16/29] fixes --- src/CONST.ts | 1 - src/libs/ReportUtils.ts | 3 ++- src/libs/actions/IOU.ts | 12 +++++------- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 672868214883..90a07b1a7a8a 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1381,7 +1381,6 @@ const CONST = { ADMIN: 'admin', AUDITOR: 'auditor', USER: 'user', - MEMBER: 'member', }, AUTO_REPORTING_FREQUENCIES: { INSTANT: 'instant', diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index b96048dc182d..6f89e58d3efe 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4278,7 +4278,8 @@ function getChatByParticipants(newParticipantList: number[], reports: OnyxCollec isTaskReport(report) || isMoneyRequestReport(report) || isChatRoom(report) || - isPolicyExpenseChat(report) + isPolicyExpenseChat(report) || + isGroupChat(report) ) { return false; } diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index e0fc0081ef42..9c77ee70b1cb 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -1653,13 +1653,11 @@ function createSplitsAndOnyxData( const currentUserEmailForIOUSplit = PhoneNumber.addSMSDomainIfPhoneNumber(currentUserLogin); const participantAccountIDs = participants.map((participant) => Number(participant.accountID)); - const existingSplitChatReport = - // eslint-disable-next-line no-nested-ternary - existingSplitChatReportID || participants[0].reportID - ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${existingSplitChatReportID || participants[0].reportID}`] - : participants.length < 2 - ? ReportUtils.getChatByParticipants(participantAccountIDs) - : null; + const existingChatReportID = existingSplitChatReportID || participants[0].reportID; + let existingSplitChatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${existingChatReportID}`]; + if (!existingSplitChatReport) { + existingSplitChatReport = participants.length < 2 ? ReportUtils.getChatByParticipants(participantAccountIDs) : null; + } const splitChatReport = existingSplitChatReport ?? ReportUtils.buildOptimisticChatReport(participantAccountIDs); const isOwnPolicyExpenseChat = !!splitChatReport.isOwnPolicyExpenseChat; From 65cf01e714b69c396e41fbc865e2ab6aeb6b57a6 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Tue, 19 Mar 2024 15:43:29 +0100 Subject: [PATCH 17/29] updates --- src/libs/API/parameters/SplitBillParams.ts | 1 + src/libs/ReportUtils.ts | 9 +++++++-- src/libs/actions/IOU.ts | 1 + src/pages/NewChatPage.tsx | 14 ++++++++++++-- 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/libs/API/parameters/SplitBillParams.ts b/src/libs/API/parameters/SplitBillParams.ts index 0ed7d252a2c6..6ebdeb518608 100644 --- a/src/libs/API/parameters/SplitBillParams.ts +++ b/src/libs/API/parameters/SplitBillParams.ts @@ -13,6 +13,7 @@ type SplitBillParams = { reportActionID: string; createdReportActionID?: string; policyID?: string; + chatType?: string; }; export default SplitBillParams; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 977cc134d808..3c900deeeae8 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4522,7 +4522,7 @@ function hasIOUWaitingOnCurrentUserBankAccount(chatReport: OnyxEntry): b */ function canRequestMoney(report: OnyxEntry, policy: OnyxEntry, otherParticipants: number[]): boolean { // User cannot request money in chat thread or in task report or in chat room - if (isChatThread(report) || isTaskReport(report) || isChatRoom(report) || isSelfDM(report)) { + if (isChatThread(report) || isTaskReport(report) || isChatRoom(report) || isSelfDM(report) || isGroupChat(report)) { return false; } @@ -4597,7 +4597,12 @@ function getMoneyRequestOptions(report: OnyxEntry, policy: OnyxEntry 0) || (isDM(report) && hasMultipleOtherParticipants) || (isPolicyExpenseChat(report) && report?.isOwnPolicyExpenseChat)) { + if ( + (isChatRoom(report) && otherParticipants.length > 0) || + (isDM(report) && hasMultipleOtherParticipants) || + isGroupChat(report) || + (isPolicyExpenseChat(report) && report?.isOwnPolicyExpenseChat) + ) { options = [CONST.IOU.TYPE.SPLIT]; } diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 27fcd8714b02..59bd216a50c9 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -2077,6 +2077,7 @@ function splitBillAndOpenReport( reportActionID: splitData.reportActionID, createdReportActionID: splitData.createdReportActionID, policyID: splitData.policyID, + chatType: participants.length > 1 ? CONST.REPORT.CHAT_TYPE.GROUP_CHAT : '', }; API.write(WRITE_COMMANDS.SPLIT_BILL_AND_OPEN_REPORT, parameters, onyxData); diff --git a/src/pages/NewChatPage.tsx b/src/pages/NewChatPage.tsx index 854de3d319d1..17c6bbbe69df 100755 --- a/src/pages/NewChatPage.tsx +++ b/src/pages/NewChatPage.tsx @@ -15,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 Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; @@ -167,10 +168,19 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingF * or navigates to the existing chat if one with those participants already exists. */ const createChat = (option: OptionData) => { - if ((!option.login && selectedOptions.length !== 1) || !selectedOptions[0].login) { + let login = ''; + + if (selectedOptions.length === 1) { + login = selectedOptions[0].login ?? ''; + } else if (option.login) { + login = option.login; + } + + if (!login) { + Log.warn('Tried to create chat with empty login'); return; } - const login = option.login ?? selectedOptions[0].login; + Report.navigateToAndOpenReport([login]); }; /** From 633ac9e741f8a44e31575feef1d49eac234a58db Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Wed, 20 Mar 2024 12:55:14 +0100 Subject: [PATCH 18/29] fix groupChat avatars --- src/libs/ReportUtils.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index e83d2d74c4d6..f0dd3fa588db 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1711,6 +1711,17 @@ function getIcons( return getIconsForParticipants([currentUserAccountID ?? 0], personalDetails); } + if (isGroupChat(report)) { + const groupChatIcon = { + source: getDefaultGroupAvatar(), + id: report?.ownerAccountID, + type: CONST.ICON_TYPE_AVATAR, + name: personalDetails?.[report?.ownerAccountID ?? -1]?.displayName ?? '', + fallbackIcon: personalDetails?.[report?.ownerAccountID ?? -1]?.fallbackIcon, + }; + return [groupChatIcon]; + } + return getIconsForParticipants(report?.participantAccountIDs ?? [], personalDetails); } From fd131e9b47639cabee62009e99a15292463f8f40 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Thu, 21 Mar 2024 14:43:17 +0100 Subject: [PATCH 19/29] fixed tooltip, updated CONSt --- src/CONST.ts | 2 +- src/libs/ReportUtils.ts | 7 +++---- src/libs/actions/IOU.ts | 2 +- src/libs/actions/Report.ts | 6 +++--- src/pages/NewChatConfirmPage.tsx | 2 +- src/pages/NewChatPage.tsx | 6 +++--- 6 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 28a68eaafa0f..5709c5f078fd 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -46,7 +46,7 @@ const KEYBOARD_SHORTCUT_NAVIGATION_TYPE = 'NAVIGATION_SHORTCUT'; const chatTypes = { POLICY_ANNOUNCE: 'policyAnnounce', POLICY_ADMINS: 'policyAdmins', - GROUP_CHAT: 'group', + GROUP: 'group', DOMAIN_ALL: 'domainAll', POLICY_ROOM: 'policyRoom', POLICY_EXPENSE_CHAT: 'policyExpenseChat', diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index f0dd3fa588db..a094cccdb9d4 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -934,7 +934,7 @@ function isSelfDM(report: OnyxEntry): boolean { } function isGroupChat(report: OnyxEntry): boolean { - return getChatType(report) === CONST.REPORT.CHAT_TYPE.GROUP_CHAT; + return getChatType(report) === CONST.REPORT.CHAT_TYPE.GROUP; } /** @@ -1716,8 +1716,7 @@ function getIcons( source: getDefaultGroupAvatar(), id: report?.ownerAccountID, type: CONST.ICON_TYPE_AVATAR, - name: personalDetails?.[report?.ownerAccountID ?? -1]?.displayName ?? '', - fallbackIcon: personalDetails?.[report?.ownerAccountID ?? -1]?.fallbackIcon, + name: report?.reportName ?? '', }; return [groupChatIcon]; } @@ -3617,7 +3616,7 @@ function buildOptimisticChatReport( return {...reportParticipants, [accountID]: participant}; }, {} as Participants); - const isGroup = chatType === CONST.REPORT.CHAT_TYPE.GROUP_CHAT; + const isGroup = chatType === CONST.REPORT.CHAT_TYPE.GROUP; const currentTime = DateUtils.getDBTime(); const isNewlyCreatedWorkspaceChat = chatType === CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT && isOwnPolicyExpenseChat; return { diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index e6334aa7f2bd..0fab45dceda1 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -2110,7 +2110,7 @@ function splitBillAndOpenReport( reportActionID: splitData.reportActionID, createdReportActionID: splitData.createdReportActionID, policyID: splitData.policyID, - chatType: participants.length > 1 ? CONST.REPORT.CHAT_TYPE.GROUP_CHAT : '', + chatType: participants.length > 1 ? CONST.REPORT.CHAT_TYPE.GROUP : '', }; API.write(WRITE_COMMANDS.SPLIT_BILL_AND_OPEN_REPORT, parameters, onyxData); diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 7503a2429597..85ff5abb622e 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -641,8 +641,8 @@ function openReport( idempotencyKey: `${SIDE_EFFECT_REQUEST_COMMANDS.OPEN_REPORT}_${reportID}`, }; - if (newReportObject.chatType === CONST.REPORT.CHAT_TYPE.GROUP_CHAT) { - parameters.chatType = CONST.REPORT.CHAT_TYPE.GROUP_CHAT; + if (newReportObject.chatType === CONST.REPORT.CHAT_TYPE.GROUP) { + parameters.chatType = CONST.REPORT.CHAT_TYPE.GROUP; parameters.groupChatAdminLogins = currentUserEmail; parameters.optimisticAccountIDList = participantAccountIDList.join(','); parameters.reportName = newReportObject.reportName ?? ''; @@ -795,7 +795,7 @@ function navigateToAndOpenReport(userLogins: string[], shouldDismissModal = true newChat = ReportUtils.buildOptimisticChatReport( participantAccountIDs, reportName, - CONST.REPORT.CHAT_TYPE.GROUP_CHAT, + CONST.REPORT.CHAT_TYPE.GROUP, undefined, undefined, undefined, diff --git a/src/pages/NewChatConfirmPage.tsx b/src/pages/NewChatConfirmPage.tsx index 18b452e8d0de..a8cac3417edd 100644 --- a/src/pages/NewChatConfirmPage.tsx +++ b/src/pages/NewChatConfirmPage.tsx @@ -105,7 +105,7 @@ function NewChatConfirmPage({newGroupDraft, allPersonalDetails}: NewChatConfirmP if (!newGroupDraft) { return; } - Report.navigateToAndOpenReport(newGroupDraft.participantLogins, true, ''); + Report.navigateToAndOpenReport(newGroupDraft.participantLogins, true, groupName); }; const navigateBack = () => { diff --git a/src/pages/NewChatPage.tsx b/src/pages/NewChatPage.tsx index 17c6bbbe69df..f4ff385170ac 100755 --- a/src/pages/NewChatPage.tsx +++ b/src/pages/NewChatPage.tsx @@ -170,10 +170,10 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingF const createChat = (option: OptionData) => { let login = ''; - if (selectedOptions.length === 1) { - login = selectedOptions[0].login ?? ''; - } else if (option.login) { + if (option.login) { login = option.login; + } else if (selectedOptions.length === 1) { + login = selectedOptions[0].login ?? ''; } if (!login) { From 85026a976b7047fa305772f6ffb24c7bbb594c4f Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Thu, 21 Mar 2024 18:51:01 +0100 Subject: [PATCH 20/29] fixed participants --- src/libs/actions/Report.ts | 4 ++-- src/pages/NewChatConfirmPage.tsx | 29 ++++++++++++++--------------- src/pages/NewChatPage.tsx | 11 +++++------ src/types/onyx/NewGroupChatDraft.ts | 5 ++++- 4 files changed, 25 insertions(+), 24 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 7de51b00160c..ef417bb8fb80 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -2981,8 +2981,8 @@ function resolveActionableMentionWhisper(reportId: string, reportAction: OnyxEnt API.write(WRITE_COMMANDS.RESOLVE_ACTIONABLE_MENTION_WHISPER, parameters, {optimisticData, failureData}); } -function setGroupDraft(participantLogins: string[], reportName = '') { - Onyx.merge(ONYXKEYS.NEW_GROUP_CHAT_DRAFT, {participantLogins, reportName}); +function setGroupDraft(participants: Array<{login: string; accountID: number}>, reportName = '') { + Onyx.merge(ONYXKEYS.NEW_GROUP_CHAT_DRAFT, {participants, reportName}); } export { diff --git a/src/pages/NewChatConfirmPage.tsx b/src/pages/NewChatConfirmPage.tsx index a8cac3417edd..98f48d9c1f6d 100644 --- a/src/pages/NewChatConfirmPage.tsx +++ b/src/pages/NewChatConfirmPage.tsx @@ -17,14 +17,13 @@ import useThemeStyles from '@hooks/useThemeStyles'; import * as GroupChatUtils from '@libs/GroupChatUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; -import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; 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'; +import type {Participant} from '@src/types/onyx/IOU'; type NewChatConfirmPageOnyxProps = { /** New group chat draft data */ @@ -43,24 +42,23 @@ function NewChatConfirmPage({newGroupDraft, allPersonalDetails}: NewChatConfirmP const StyleUtils = useStyleUtils(); const styles = useThemeStyles(); const personalData = useCurrentUserPersonalDetails(); - const participantAccountIDs = PersonalDetailsUtils.getAccountIDsByLogins(newGroupDraft?.participantLogins ?? []); - const selectedOptions = useMemo((): OptionData[] => { - const invitedUsersPersonalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs(participantAccountIDs, allPersonalDetails); - const members = OptionsListUtils.getMemberInviteOptions(invitedUsersPersonalDetails); - const currentUserOptionData = members.currentUserOption; - if (!currentUserOptionData) { - return members.personalDetails; + const participantAccountIDs = newGroupDraft?.participants.map((participant: {accountID: number}) => participant.accountID); + const selectedOptions = useMemo((): Participant[] => { + let options: Participant[] = []; + if (newGroupDraft?.participants) { + options = newGroupDraft?.participants.map((participant: {accountID: number; login: string}) => + OptionsListUtils.getParticipantsOption({accountID: participant.accountID, login: participant.login, reportID: ''}, allPersonalDetails), + ); } - const options = [...members.personalDetails, currentUserOptionData]; return options; - }, [participantAccountIDs, allPersonalDetails]); + }, [allPersonalDetails, newGroupDraft?.participants]); const groupName = GroupChatUtils.getGroupChatName(participantAccountIDs ?? []); const sections: Section[] = useMemo( () => selectedOptions - .map((selectedOption: OptionData) => { + .map((selectedOption: Participant) => { const accountID = selectedOption.accountID; const isAdmin = personalData.accountID === accountID; let roleBadge = null; @@ -97,15 +95,16 @@ function NewChatConfirmPage({newGroupDraft, allPersonalDetails}: NewChatConfirmP if (!newGroupDraft) { return; } - const newSelectedLogins = newGroupDraft.participantLogins.filter((participantLogin) => participantLogin !== option.value); - Report.setGroupDraft(newSelectedLogins); + const newSelectedParticipants = newGroupDraft.participants.filter((participant) => participant.login !== option.value); + Report.setGroupDraft(newSelectedParticipants); }; const createGroup = () => { if (!newGroupDraft) { return; } - Report.navigateToAndOpenReport(newGroupDraft.participantLogins, true, groupName); + const logins: string[] = newGroupDraft.participants.map((participant: {login: string}) => participant.login); + Report.navigateToAndOpenReport(logins, true, groupName); }; const navigateBack = () => { diff --git a/src/pages/NewChatPage.tsx b/src/pages/NewChatPage.tsx index f100fcbbe06f..afc32e914699 100755 --- a/src/pages/NewChatPage.tsx +++ b/src/pages/NewChatPage.tsx @@ -18,7 +18,6 @@ import doInteractionTask from '@libs/DoInteractionTask'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; -import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as ReportUtils from '@libs/ReportUtils'; import type {OptionData} from '@libs/ReportUtils'; import variables from '@styles/variables'; @@ -186,9 +185,9 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingF * Navigates to create group confirm page */ const navigateToConfirmPage = () => { - const selectedLogins: string[] = selectedOptions.map((option: OptionData) => option.login).filter((login): login is string => login !== null && login !== undefined); + const selectedParticipants = selectedOptions.map((option: OptionData) => ({login: option.login ?? '', accountID: option.accountID ?? -1})); if (personalData && personalData.login) { - const logins = [...selectedLogins, personalData.login]; + const logins = [...selectedParticipants, {login: personalData.login, accountID: personalData.accountID}]; Report.setGroupDraft(logins); Navigation.navigate(ROUTES.NEW_CHAT_CONFIRM); @@ -243,13 +242,13 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingF return; } updateOptions(); - if (newGroupDraft?.participantLogins) { - const accountIDs = PersonalDetailsUtils.getAccountIDsByLogins(newGroupDraft?.participantLogins ?? []); + if (newGroupDraft?.participants) { + const accountIDs = newGroupDraft?.participants.map((participant: {accountID: number}) => participant.accountID); const invitedUsersPersonalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs(accountIDs, personalDetails); const groupSelectedOptions = OptionsListUtils.getMemberInviteOptions(invitedUsersPersonalDetails).personalDetails; setSelectedOptions(groupSelectedOptions); } - }, [didScreenTransitionEnd, updateOptions, newGroupDraft?.participantLogins, personalDetails]); + }, [didScreenTransitionEnd, updateOptions, personalDetails, newGroupDraft?.participants]); const {inputCallbackRef} = useAutoFocusInput(); diff --git a/src/types/onyx/NewGroupChatDraft.ts b/src/types/onyx/NewGroupChatDraft.ts index 1ac36434757d..fe92018ad429 100644 --- a/src/types/onyx/NewGroupChatDraft.ts +++ b/src/types/onyx/NewGroupChatDraft.ts @@ -1,5 +1,8 @@ type NewGroupChatDraft = { - participantLogins: string[]; + participants: Array<{ + login: string; + accountID: number; + }>; reportName: string; }; From cca1b507ee68da70832211205bb294e2a8c77dea Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Sat, 23 Mar 2024 01:00:37 +0100 Subject: [PATCH 21/29] fix split bill --- .../LHNOptionsList/OptionRowLHN.tsx | 5 ++-- src/libs/ReportUtils.ts | 9 +++---- src/libs/actions/IOU.ts | 26 +++++++++++++++++-- src/libs/actions/Report.ts | 2 +- 4 files changed, 31 insertions(+), 11 deletions(-) diff --git a/src/components/LHNOptionsList/OptionRowLHN.tsx b/src/components/LHNOptionsList/OptionRowLHN.tsx index 170d6e3934a8..398935015cf8 100644 --- a/src/components/LHNOptionsList/OptionRowLHN.tsx +++ b/src/components/LHNOptionsList/OptionRowLHN.tsx @@ -107,9 +107,8 @@ function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, opti const report = ReportUtils.getReport(optionItem.reportID ?? ''); const isStatusVisible = !!emojiCode && ReportUtils.isOneOnOneChat(!isEmptyObject(report) ? report : null); - const isGroupChat = - ReportUtils.isGroupChat(optionItem) || - (optionItem.type === CONST.REPORT.TYPE.CHAT && !optionItem.chatType && !optionItem.isThread && (optionItem.displayNamesWithTooltips?.length ?? 0) > 2); + const isGroupChat = ReportUtils.isGroupChat(optionItem) || ReportUtils.isDeprecatedGroupDM(optionItem); + const fullTitle = isGroupChat ? getGroupChatName(report?.participantAccountIDs ?? []) : optionItem.text; const subscriptAvatarBorderColor = isFocused ? focusedBackgroundColor : theme.sidebar; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 859b910bd139..2d81db0e88c1 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -3704,7 +3704,6 @@ function buildOptimisticChatReport( return {...reportParticipants, [accountID]: participant}; }, {} as Participants); - const isGroup = chatType === CONST.REPORT.CHAT_TYPE.GROUP; const currentTime = DateUtils.getDBTime(); const isNewlyCreatedWorkspaceChat = chatType === CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT && isOwnPolicyExpenseChat; return { @@ -3725,10 +3724,10 @@ function buildOptimisticChatReport( parentReportActionID, parentReportID, // When creating a report the participantsAccountIDs and visibleChatMemberAccountIDs are the same - participantAccountIDs: !isGroup ? participantList : undefined, - visibleChatMemberAccountIDs: !isGroup ? participantList : undefined, + participantAccountIDs: participantList, + visibleChatMemberAccountIDs: participantList, // For group chats we need to have participants object as we are migrating away from `participantAccountIDs` and `visibleChatMemberAccountIDs`. See https://github.com/Expensify/App/issues/34692 - participants: isGroup ? participants : undefined, + participants, policyID, reportID: generateReportID(), reportName, @@ -4768,7 +4767,7 @@ function getMoneyRequestOptions(report: OnyxEntry, policy: OnyxEntry 0) || (isDM(report) && hasMultipleOtherParticipants) || - isGroupChat(report) || + (isGroupChat(report) && otherParticipants.length > 0) || (isPolicyExpenseChat(report) && report?.isOwnPolicyExpenseChat) ) { options = [CONST.IOU.TYPE.SPLIT]; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 3f5fa7523b36..19ac9e197f72 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -103,6 +103,7 @@ type SplitData = { reportActionID: string; policyID?: string; createdReportActionID?: string; + chatType?: string; }; type SplitsAndOnyxData = { @@ -2270,7 +2271,27 @@ function createSplitsAndOnyxData( if (!existingSplitChatReport) { existingSplitChatReport = participants.length < 2 ? ReportUtils.getChatByParticipants(participantAccountIDs) : null; } - const splitChatReport = existingSplitChatReport ?? ReportUtils.buildOptimisticChatReport(participantAccountIDs); + let newChat: ReportUtils.OptimisticChatReport = {}; + + if (!existingSplitChatReport) { + if (participants.length > 1) { + newChat = ReportUtils.buildOptimisticChatReport( + participantAccountIDs, + '', + CONST.REPORT.CHAT_TYPE.GROUP, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, + ); + } else { + newChat = ReportUtils.buildOptimisticChatReport(participantAccountIDs); + } + } + const splitChatReport = existingSplitChatReport ?? newChat; const isOwnPolicyExpenseChat = !!splitChatReport.isOwnPolicyExpenseChat; const splitTransaction = TransactionUtils.buildOptimisticTransaction( @@ -2599,6 +2620,7 @@ function createSplitsAndOnyxData( transactionID: splitTransaction.transactionID, reportActionID: splitIOUReportAction.reportActionID, policyID: splitChatReport.policyID, + chatType: splitChatReport.chatType, }; if (!existingSplitChatReport) { @@ -2721,7 +2743,7 @@ function splitBillAndOpenReport( reportActionID: splitData.reportActionID, createdReportActionID: splitData.createdReportActionID, policyID: splitData.policyID, - chatType: participants.length > 1 ? CONST.REPORT.CHAT_TYPE.GROUP : '', + chatType: splitData.chatType, }; API.write(WRITE_COMMANDS.SPLIT_BILL_AND_OPEN_REPORT, parameters, onyxData); diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 7a716754e0a4..978f8479ccff 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -640,7 +640,7 @@ function openReport( idempotencyKey: `${SIDE_EFFECT_REQUEST_COMMANDS.OPEN_REPORT}_${reportID}`, }; - if (newReportObject.chatType === CONST.REPORT.CHAT_TYPE.GROUP) { + if (ReportUtils.isGroupChat(newReportObject)) { parameters.chatType = CONST.REPORT.CHAT_TYPE.GROUP; parameters.groupChatAdminLogins = currentUserEmail; parameters.optimisticAccountIDList = participantAccountIDList.join(','); From a706309ac48f4ae7dd4f0ba89603a8d5de43d72b Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Mon, 25 Mar 2024 14:46:47 +0100 Subject: [PATCH 22/29] fix TS, update defaultAvatar function --- src/CONST.ts | 1 + src/libs/ReportUtils.ts | 19 +++++++++++++------ src/libs/actions/IOU.ts | 39 ++++++++++++++++++++------------------- src/pages/NewChatPage.tsx | 12 +++++++----- 4 files changed, 41 insertions(+), 30 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 7f35680a1e7b..6a78f667d519 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -118,6 +118,7 @@ const CONST = { NORMAL: 'normal', }, + DEFAULT_GROUP_AVATAR_COUNT: 18, DEFAULT_AVATAR_COUNT: 24, OLD_DEFAULT_AVATAR_COUNT: 8, diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 2d81db0e88c1..923078d62ab8 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -75,6 +75,8 @@ import * as TransactionUtils from './TransactionUtils'; import * as Url from './Url'; import * as UserUtils from './UserUtils'; +type AvatarRange = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18; + type WelcomeMessage = {showReportName: boolean; phrase1?: string; phrase2?: string}; type ExpenseOriginalMessage = { @@ -1537,8 +1539,12 @@ function getWorkspaceAvatar(report: OnyxEntry): UserUtils.AvatarSource { // * Helper method to return the default avatar associated with the given reportID // * TO REWORK! // */ -function getDefaultGroupAvatar(): IconAsset { - return defaultGroupAvatars.Avatar1; +function getDefaultGroupAvatar(reportID?: string): IconAsset { + if (!reportID) { + return defaultGroupAvatars.Avatar1; + } + const reportIDHashBucket: AvatarRange = ((Number(reportID) % CONST.DEFAULT_GROUP_AVATAR_COUNT) + 1) as AvatarRange; + return defaultGroupAvatars[`Avatar${reportIDHashBucket}`]; } /** @@ -1725,8 +1731,8 @@ function getIcons( if (isGroupChat(report)) { const groupChatIcon = { - source: getDefaultGroupAvatar(), - id: report?.ownerAccountID, + source: getDefaultGroupAvatar(report.reportID), + id: -1, type: CONST.ICON_TYPE_AVATAR, name: report?.reportName ?? '', }; @@ -3701,8 +3707,9 @@ function buildOptimisticChatReport( hidden: false, role: accountID === currentUserAccountID ? CONST.REPORT.ROLE.ADMIN : CONST.REPORT.ROLE.MEMBER, }; - - return {...reportParticipants, [accountID]: participant}; + // eslint-disable-next-line no-param-reassign + reportParticipants[accountID] = participant; + return reportParticipants; }, {} as Participants); const currentTime = DateUtils.getDBTime(); const isNewlyCreatedWorkspaceChat = chatType === CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT && isOwnPolicyExpenseChat; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 19ac9e197f72..b6a59601a247 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -31,6 +31,7 @@ import DateUtils from '@libs/DateUtils'; import DistanceRequestUtils from '@libs/DistanceRequestUtils'; import * as ErrorUtils from '@libs/ErrorUtils'; import * as FileUtils from '@libs/fileDownload/FileUtils'; +import * as GroupChatUtils from '@libs/GroupChatUtils'; import * as IOUUtils from '@libs/IOUUtils'; import * as LocalePhoneNumber from '@libs/LocalePhoneNumber'; import * as Localize from '@libs/Localize'; @@ -2271,25 +2272,25 @@ function createSplitsAndOnyxData( if (!existingSplitChatReport) { existingSplitChatReport = participants.length < 2 ? ReportUtils.getChatByParticipants(participantAccountIDs) : null; } - let newChat: ReportUtils.OptimisticChatReport = {}; - - if (!existingSplitChatReport) { - if (participants.length > 1) { - newChat = ReportUtils.buildOptimisticChatReport( - participantAccountIDs, - '', - CONST.REPORT.CHAT_TYPE.GROUP, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, - ); - } else { - newChat = ReportUtils.buildOptimisticChatReport(participantAccountIDs); - } + let newChat: ReportUtils.OptimisticChatReport | EmptyObject = {}; + + if (!existingSplitChatReport && participants.length > 1) { + const reportName = GroupChatUtils.getGroupChatName(participantAccountIDs); + newChat = ReportUtils.buildOptimisticChatReport( + participantAccountIDs, + reportName, + CONST.REPORT.CHAT_TYPE.GROUP, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, + ); + } + if (isEmptyObject(newChat)) { + newChat = ReportUtils.buildOptimisticChatReport(participantAccountIDs); } const splitChatReport = existingSplitChatReport ?? newChat; const isOwnPolicyExpenseChat = !!splitChatReport.isOwnPolicyExpenseChat; diff --git a/src/pages/NewChatPage.tsx b/src/pages/NewChatPage.tsx index afc32e914699..8e102ed8437c 100755 --- a/src/pages/NewChatPage.tsx +++ b/src/pages/NewChatPage.tsx @@ -243,12 +243,14 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingF } updateOptions(); if (newGroupDraft?.participants) { - const accountIDs = newGroupDraft?.participants.map((participant: {accountID: number}) => participant.accountID); - const invitedUsersPersonalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs(accountIDs, personalDetails); - const groupSelectedOptions = OptionsListUtils.getMemberInviteOptions(invitedUsersPersonalDetails).personalDetails; - setSelectedOptions(groupSelectedOptions); + const selectedParticipants = newGroupDraft?.participants.filter((participant: {accountID: number}) => participant.accountID !== personalData.accountID); + const options = selectedParticipants.map( + (participant: {accountID: number; login: string}) => + OptionsListUtils.getParticipantsOption({accountID: participant.accountID, login: participant.login, reportID: ''}, personalDetails) as OptionData, + ); + setSelectedOptions(options); } - }, [didScreenTransitionEnd, updateOptions, personalDetails, newGroupDraft?.participants]); + }, [didScreenTransitionEnd, updateOptions, personalDetails, newGroupDraft?.participants, personalData.accountID]); const {inputCallbackRef} = useAutoFocusInput(); From c50c7a00349fe52ba5ee86333929dc5d8bb58456 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Mon, 25 Mar 2024 15:06:33 +0100 Subject: [PATCH 23/29] fix TS --- src/libs/ReportUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 923078d62ab8..5efbd19d38f9 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -926,7 +926,7 @@ function isSelfDM(report: OnyxEntry): boolean { return getChatType(report) === CONST.REPORT.CHAT_TYPE.SELF_DM; } -function isGroupChat(report: OnyxEntry): boolean { +function isGroupChat(report: OnyxEntry | Partial): boolean { return getChatType(report) === CONST.REPORT.CHAT_TYPE.GROUP; } From 1323c75e02a5005218fce41021afadab1c1f4d9b Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Tue, 26 Mar 2024 18:27:46 +0100 Subject: [PATCH 24/29] fixed participants, resolved issues --- src/libs/API/parameters/SplitBillParams.ts | 4 +-- src/libs/ReportUtils.ts | 11 +++--- src/libs/actions/IOU.ts | 5 ++- src/pages/NewChatConfirmPage.tsx | 12 +++---- src/pages/NewChatPage.tsx | 42 +++++++++++++--------- 5 files changed, 40 insertions(+), 34 deletions(-) diff --git a/src/libs/API/parameters/SplitBillParams.ts b/src/libs/API/parameters/SplitBillParams.ts index 6ebdeb518608..310923093d5e 100644 --- a/src/libs/API/parameters/SplitBillParams.ts +++ b/src/libs/API/parameters/SplitBillParams.ts @@ -12,8 +12,8 @@ type SplitBillParams = { transactionID: string; reportActionID: string; createdReportActionID?: string; - policyID?: string; - chatType?: string; + policyID: string | undefined; + chatType: string | undefined; }; export default SplitBillParams; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 5efbd19d38f9..9b0b9eaa35dd 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -56,6 +56,8 @@ import * as store from './actions/ReimbursementAccount/store'; import * as CollectionUtils from './CollectionUtils'; import * as CurrencyUtils from './CurrencyUtils'; import DateUtils from './DateUtils'; +// eslint-disable-next-line import/no-cycle +import * as GroupChatUtils from './GroupChatUtils'; import isReportMessageAttachment from './isReportMessageAttachment'; import localeCompare from './LocaleCompare'; import * as LocalePhoneNumber from './LocalePhoneNumber'; @@ -1535,10 +1537,9 @@ 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! -// */ +/** + * Helper method to return the default avatar associated with the given reportID + */ function getDefaultGroupAvatar(reportID?: string): IconAsset { if (!reportID) { return defaultGroupAvatars.Avatar1; @@ -1734,7 +1735,7 @@ function getIcons( source: getDefaultGroupAvatar(report.reportID), id: -1, type: CONST.ICON_TYPE_AVATAR, - name: report?.reportName ?? '', + name: GroupChatUtils.getGroupChatName(report.participantAccountIDs ?? []), }; return [groupChatIcon]; } diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index b6a59601a247..5799d321c64d 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -31,7 +31,6 @@ import DateUtils from '@libs/DateUtils'; import DistanceRequestUtils from '@libs/DistanceRequestUtils'; import * as ErrorUtils from '@libs/ErrorUtils'; import * as FileUtils from '@libs/fileDownload/FileUtils'; -import * as GroupChatUtils from '@libs/GroupChatUtils'; import * as IOUUtils from '@libs/IOUUtils'; import * as LocalePhoneNumber from '@libs/LocalePhoneNumber'; import * as Localize from '@libs/Localize'; @@ -2275,10 +2274,9 @@ function createSplitsAndOnyxData( let newChat: ReportUtils.OptimisticChatReport | EmptyObject = {}; if (!existingSplitChatReport && participants.length > 1) { - const reportName = GroupChatUtils.getGroupChatName(participantAccountIDs); newChat = ReportUtils.buildOptimisticChatReport( participantAccountIDs, - reportName, + '', CONST.REPORT.CHAT_TYPE.GROUP, undefined, undefined, @@ -2686,6 +2684,7 @@ function splitBill( reportActionID: splitData.reportActionID, createdReportActionID: splitData.createdReportActionID, policyID: splitData.policyID, + chatType: splitData.chatType, }; API.write(WRITE_COMMANDS.SPLIT_BILL, parameters, onyxData); diff --git a/src/pages/NewChatConfirmPage.tsx b/src/pages/NewChatConfirmPage.tsx index 98f48d9c1f6d..20b2d4a832cf 100644 --- a/src/pages/NewChatConfirmPage.tsx +++ b/src/pages/NewChatConfirmPage.tsx @@ -35,8 +35,6 @@ type NewChatConfirmPageOnyxProps = { type NewChatConfirmPageProps = NewChatConfirmPageOnyxProps; -type Section = ListItem & {value: string}; - function NewChatConfirmPage({newGroupDraft, allPersonalDetails}: NewChatConfirmPageProps) { const {translate} = useLocalize(); const StyleUtils = useStyleUtils(); @@ -55,7 +53,7 @@ function NewChatConfirmPage({newGroupDraft, allPersonalDetails}: NewChatConfirmP const groupName = GroupChatUtils.getGroupChatName(participantAccountIDs ?? []); - const sections: Section[] = useMemo( + const sections: ListItem[] = useMemo( () => selectedOptions .map((selectedOption: Participant) => { @@ -72,8 +70,8 @@ function NewChatConfirmPage({newGroupDraft, allPersonalDetails}: NewChatConfirmP ); } - const section: Section = { - value: selectedOption?.login ?? '', + const section: ListItem = { + login: selectedOption?.login ?? '', text: selectedOption?.text ?? '', keyForList: selectedOption?.keyForList ?? '', isSelected: !isAdmin, @@ -91,11 +89,11 @@ function NewChatConfirmPage({newGroupDraft, allPersonalDetails}: NewChatConfirmP /** * Removes a selected option from list if already selected. */ - const unselectOption = (option: Section) => { + const unselectOption = (option: ListItem) => { if (!newGroupDraft) { return; } - const newSelectedParticipants = newGroupDraft.participants.filter((participant) => participant.login !== option.value); + const newSelectedParticipants = newGroupDraft.participants.filter((participant) => participant.login !== option.login); Report.setGroupDraft(newSelectedParticipants); }; diff --git a/src/pages/NewChatPage.tsx b/src/pages/NewChatPage.tsx index 8e102ed8437c..c90cf6e729e0 100755 --- a/src/pages/NewChatPage.tsx +++ b/src/pages/NewChatPage.tsx @@ -27,6 +27,11 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; +type SelectedParticipant = { + accountID: number; + login: string; +}; + type NewChatPageWithOnyxProps = { /** All reports shared with the user */ reports: OnyxCollection; @@ -185,16 +190,26 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingF * Navigates to create group confirm page */ const navigateToConfirmPage = () => { - const selectedParticipants = selectedOptions.map((option: OptionData) => ({login: option.login ?? '', accountID: option.accountID ?? -1})); - if (personalData && personalData.login) { - const logins = [...selectedParticipants, {login: personalData.login, accountID: personalData.accountID}]; - - Report.setGroupDraft(logins); - Navigation.navigate(ROUTES.NEW_CHAT_CONFIRM); + const selectedParticipants: SelectedParticipant[] = selectedOptions.map((option: OptionData) => ({login: option.login ?? '', accountID: option.accountID ?? -1})); + if (!personalData || !personalData.login || !personalData.accountID) { + return; } + const logins = [...selectedParticipants, {login: personalData.login, accountID: personalData.accountID}]; + Report.setGroupDraft(logins); + Navigation.navigate(ROUTES.NEW_CHAT_CONFIRM); }; const updateOptions = useCallback(() => { + let newSelectedOptions; + if (newGroupDraft?.participants) { + const selectedParticipants = newGroupDraft?.participants.filter((participant: SelectedParticipant) => participant.accountID !== personalData.accountID); + newSelectedOptions = selectedParticipants.map((participant: SelectedParticipant): OptionData => { + const baseOption = OptionsListUtils.getParticipantsOption({accountID: participant.accountID, login: participant.login, reportID: ''}, personalDetails); + return {...baseOption, reportID: baseOption.reportID ?? ''}; + }); + setSelectedOptions(newSelectedOptions); + } + const { recentReports, personalDetails: newChatPersonalDetails, @@ -204,7 +219,7 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingF personalDetails, betas ?? [], searchTerm, - selectedOptions, + newSelectedOptions ?? selectedOptions, isGroupChat ? excludedGroupEmails : [], false, true, @@ -216,12 +231,13 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingF [], true, ); + setFilteredRecentReports(recentReports); setFilteredPersonalDetails(newChatPersonalDetails); setFilteredUserToInvite(userToInvite); // props.betas is not added as dependency since it doesn't change during the component lifecycle // eslint-disable-next-line react-hooks/exhaustive-deps - }, [reports, personalDetails, searchTerm]); + }, [reports, personalDetails, searchTerm, newGroupDraft]); useEffect(() => { const interactionTask = doInteractionTask(() => { @@ -242,15 +258,7 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingF return; } updateOptions(); - if (newGroupDraft?.participants) { - const selectedParticipants = newGroupDraft?.participants.filter((participant: {accountID: number}) => participant.accountID !== personalData.accountID); - const options = selectedParticipants.map( - (participant: {accountID: number; login: string}) => - OptionsListUtils.getParticipantsOption({accountID: participant.accountID, login: participant.login, reportID: ''}, personalDetails) as OptionData, - ); - setSelectedOptions(options); - } - }, [didScreenTransitionEnd, updateOptions, personalDetails, newGroupDraft?.participants, personalData.accountID]); + }, [didScreenTransitionEnd, updateOptions]); const {inputCallbackRef} = useAutoFocusInput(); From c2f2ed923bab717faf91fff3e3229eba4ce0469e Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Tue, 26 Mar 2024 18:52:38 +0100 Subject: [PATCH 25/29] fix reportName IOS --- src/pages/NewChatConfirmPage.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/NewChatConfirmPage.tsx b/src/pages/NewChatConfirmPage.tsx index 20b2d4a832cf..0145043c7f55 100644 --- a/src/pages/NewChatConfirmPage.tsx +++ b/src/pages/NewChatConfirmPage.tsx @@ -126,7 +126,6 @@ function NewChatConfirmPage({newGroupDraft, allPersonalDetails}: NewChatConfirmP From fc683cb40f0ab235cacc6b71c789a5c60ef8cdce Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Wed, 27 Mar 2024 00:48:12 +0100 Subject: [PATCH 26/29] remove podfile --- .../LHNOptionsList/OptionRowLHN.tsx | 3 +- src/libs/GroupChatUtils.ts | 24 ---- src/libs/ReportUtils.ts | 120 ++++++++++-------- src/libs/actions/IOU.ts | 3 +- src/pages/NewChatConfirmPage.tsx | 17 ++- src/pages/NewChatPage.tsx | 8 +- src/pages/home/HeaderView.tsx | 3 +- .../settings/Report/ReportSettingsPage.tsx | 4 +- src/types/onyx/NewGroupChatDraft.ts | 7 +- 9 files changed, 89 insertions(+), 100 deletions(-) delete mode 100644 src/libs/GroupChatUtils.ts diff --git a/src/components/LHNOptionsList/OptionRowLHN.tsx b/src/components/LHNOptionsList/OptionRowLHN.tsx index 398935015cf8..b1abaf3f0b5b 100644 --- a/src/components/LHNOptionsList/OptionRowLHN.tsx +++ b/src/components/LHNOptionsList/OptionRowLHN.tsx @@ -19,7 +19,6 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import DateUtils from '@libs/DateUtils'; import DomUtils from '@libs/DomUtils'; -import {getGroupChatName} from '@libs/GroupChatUtils'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager'; import * as ReportUtils from '@libs/ReportUtils'; @@ -109,7 +108,7 @@ function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, opti const isGroupChat = ReportUtils.isGroupChat(optionItem) || ReportUtils.isDeprecatedGroupDM(optionItem); - const fullTitle = isGroupChat ? getGroupChatName(report?.participantAccountIDs ?? []) : optionItem.text; + const fullTitle = isGroupChat ? ReportUtils.getGroupChatName(report?.participantAccountIDs ?? []) : optionItem.text; const subscriptAvatarBorderColor = isFocused ? focusedBackgroundColor : theme.sidebar; return ( diff --git a/src/libs/GroupChatUtils.ts b/src/libs/GroupChatUtils.ts deleted file mode 100644 index 8eb2d0b9d5b0..000000000000 --- a/src/libs/GroupChatUtils.ts +++ /dev/null @@ -1,24 +0,0 @@ -import localeCompare from './LocaleCompare'; -import * as ReportUtils from './ReportUtils'; - -/** - * Returns the report name if the report is a group chat - */ -function getGroupChatName(participantAccountIDs: number[], shouldApplyLimit = false): string | undefined { - let participants = participantAccountIDs; - if (shouldApplyLimit) { - participants = participants.slice(0, 5); - } - const isMultipleParticipantReport = participants.length > 1; - - return participants - .map((participant) => ReportUtils.getDisplayNameForParticipant(participant, isMultipleParticipantReport)) - .sort((first, second) => localeCompare(first ?? '', second ?? '')) - .filter(Boolean) - .join(', '); -} - -export { - // eslint-disable-next-line import/prefer-default-export - getGroupChatName, -}; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 4f46540186bd..a9f829621766 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -57,8 +57,6 @@ import * as CollectionUtils from './CollectionUtils'; import * as CurrencyUtils from './CurrencyUtils'; import DateUtils from './DateUtils'; import originalGetReportPolicyID from './getReportPolicyID'; -// eslint-disable-next-line import/no-cycle -import * as GroupChatUtils from './GroupChatUtils'; import isReportMessageAttachment from './isReportMessageAttachment'; import localeCompare from './LocaleCompare'; import * as LocalePhoneNumber from './LocalePhoneNumber'; @@ -1612,6 +1610,71 @@ function getWorkspaceIcon(report: OnyxEntry, policy: OnyxEntry = return workspaceIcon; } +/** + * Gets the personal details for a login by looking in the ONYXKEYS.PERSONAL_DETAILS_LIST Onyx key (stored in the local variable, allPersonalDetails). If it doesn't exist in Onyx, + * then a default object is constructed. + */ +function getPersonalDetailsForAccountID(accountID: number): Partial { + if (!accountID) { + return {}; + } + return ( + allPersonalDetails?.[accountID] ?? { + avatar: UserUtils.getDefaultAvatar(accountID), + isOptimisticPersonalDetail: true, + } + ); +} + +/** + * Get the displayName for a single report participant. + */ +function getDisplayNameForParticipant(accountID?: number, shouldUseShortForm = false, shouldFallbackToHidden = true, shouldAddCurrentUserPostfix = false): string { + if (!accountID) { + return ''; + } + + const personalDetails = getPersonalDetailsForAccountID(accountID); + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + const formattedLogin = LocalePhoneNumber.formatPhoneNumber(personalDetails.login || ''); + // This is to check if account is an invite/optimistically created one + // and prevent from falling back to 'Hidden', so a correct value is shown + // when searching for a new user + if (personalDetails.isOptimisticPersonalDetail === true) { + return formattedLogin; + } + + // For selfDM, we display the user's displayName followed by '(you)' as a postfix + const shouldAddPostfix = shouldAddCurrentUserPostfix && accountID === currentUserAccountID; + + const longName = PersonalDetailsUtils.getDisplayNameOrDefault(personalDetails, formattedLogin, shouldFallbackToHidden, shouldAddPostfix); + + // If the user's personal details (first name) should be hidden, make sure we return "hidden" instead of the short name + if (shouldFallbackToHidden && longName === Localize.translateLocal('common.hidden')) { + return longName; + } + + const shortName = personalDetails.firstName ? personalDetails.firstName : longName; + return shouldUseShortForm ? shortName : longName; +} + +/** + * Returns the report name if the report is a group chat + */ +function getGroupChatName(participantAccountIDs: number[], shouldApplyLimit = false): string | undefined { + let participants = participantAccountIDs; + if (shouldApplyLimit) { + participants = participants.slice(0, 5); + } + const isMultipleParticipantReport = participants.length > 1; + + return participants + .map((participant) => getDisplayNameForParticipant(participant, isMultipleParticipantReport)) + .sort((first, second) => localeCompare(first ?? '', second ?? '')) + .filter(Boolean) + .join(', '); +} + /** * 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. @@ -1737,7 +1800,7 @@ function getIcons( source: getDefaultGroupAvatar(report.reportID), id: -1, type: CONST.ICON_TYPE_AVATAR, - name: GroupChatUtils.getGroupChatName(report.participantAccountIDs ?? []), + name: getGroupChatName(report.participantAccountIDs ?? []), }; return [groupChatIcon]; } @@ -1745,54 +1808,6 @@ function getIcons( return getIconsForParticipants(report?.participantAccountIDs ?? [], personalDetails); } -/** - * Gets the personal details for a login by looking in the ONYXKEYS.PERSONAL_DETAILS_LIST Onyx key (stored in the local variable, allPersonalDetails). If it doesn't exist in Onyx, - * then a default object is constructed. - */ -function getPersonalDetailsForAccountID(accountID: number): Partial { - if (!accountID) { - return {}; - } - return ( - allPersonalDetails?.[accountID] ?? { - avatar: UserUtils.getDefaultAvatar(accountID), - isOptimisticPersonalDetail: true, - } - ); -} - -/** - * Get the displayName for a single report participant. - */ -function getDisplayNameForParticipant(accountID?: number, shouldUseShortForm = false, shouldFallbackToHidden = true, shouldAddCurrentUserPostfix = false): string { - if (!accountID) { - return ''; - } - - const personalDetails = getPersonalDetailsForAccountID(accountID); - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const formattedLogin = LocalePhoneNumber.formatPhoneNumber(personalDetails.login || ''); - // This is to check if account is an invite/optimistically created one - // and prevent from falling back to 'Hidden', so a correct value is shown - // when searching for a new user - if (personalDetails.isOptimisticPersonalDetail === true) { - return formattedLogin; - } - - // For selfDM, we display the user's displayName followed by '(you)' as a postfix - const shouldAddPostfix = shouldAddCurrentUserPostfix && accountID === currentUserAccountID; - - const longName = PersonalDetailsUtils.getDisplayNameOrDefault(personalDetails, formattedLogin, shouldFallbackToHidden, shouldAddPostfix); - - // If the user's personal details (first name) should be hidden, make sure we return "hidden" instead of the short name - if (shouldFallbackToHidden && longName === Localize.translateLocal('common.hidden')) { - return longName; - } - - const shortName = personalDetails.firstName ? personalDetails.firstName : longName; - return shouldUseShortForm ? shortName : longName; -} - function getDisplayNamesWithTooltips( personalDetailsList: PersonalDetails[] | PersonalDetailsList | OptionData[], isMultipleParticipantReport: boolean, @@ -1860,8 +1875,6 @@ function getDeletedParentActionMessageForChatReport(reportAction: OnyxEntry, report: OnyxEntry, shouldUseShortDisplayName = true): string { const submitterDisplayName = getDisplayNameForParticipant(report?.ownerAccountID, shouldUseShortDisplayName) ?? ''; @@ -5786,6 +5799,7 @@ export { isGroupChat, isTrackExpenseReport, hasActionsWithErrors, + getGroupChatName, }; export type { diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index a102446a5189..56d90b0208d1 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -2288,8 +2288,9 @@ function createSplitsAndOnyxData( let newChat: ReportUtils.OptimisticChatReport | EmptyObject = {}; if (!existingSplitChatReport && participants.length > 1) { + const allParticipantsAccountIDs = [...participantAccountIDs, currentUserAccountID]; newChat = ReportUtils.buildOptimisticChatReport( - participantAccountIDs, + allParticipantsAccountIDs, '', CONST.REPORT.CHAT_TYPE.GROUP, undefined, diff --git a/src/pages/NewChatConfirmPage.tsx b/src/pages/NewChatConfirmPage.tsx index 0145043c7f55..5330a3979d26 100644 --- a/src/pages/NewChatConfirmPage.tsx +++ b/src/pages/NewChatConfirmPage.tsx @@ -14,7 +14,6 @@ import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails' import useLocalize from '@hooks/useLocalize'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as GroupChatUtils from '@libs/GroupChatUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as ReportUtils from '@libs/ReportUtils'; @@ -40,18 +39,18 @@ function NewChatConfirmPage({newGroupDraft, allPersonalDetails}: NewChatConfirmP const StyleUtils = useStyleUtils(); const styles = useThemeStyles(); const personalData = useCurrentUserPersonalDetails(); - const participantAccountIDs = newGroupDraft?.participants.map((participant: {accountID: number}) => participant.accountID); + const participantAccountIDs = newGroupDraft?.participants.map((participant) => participant.accountID); const selectedOptions = useMemo((): Participant[] => { - let options: Participant[] = []; - if (newGroupDraft?.participants) { - options = newGroupDraft?.participants.map((participant: {accountID: number; login: string}) => - OptionsListUtils.getParticipantsOption({accountID: participant.accountID, login: participant.login, reportID: ''}, allPersonalDetails), - ); + if (!newGroupDraft?.participants) { + return []; } + const options: Participant[] = newGroupDraft?.participants.map((participant) => + OptionsListUtils.getParticipantsOption({accountID: participant.accountID, login: participant.login, reportID: ''}, allPersonalDetails), + ); return options; }, [allPersonalDetails, newGroupDraft?.participants]); - const groupName = GroupChatUtils.getGroupChatName(participantAccountIDs ?? []); + const groupName = ReportUtils.getGroupChatName(participantAccountIDs ?? []); const sections: ListItem[] = useMemo( () => @@ -101,7 +100,7 @@ function NewChatConfirmPage({newGroupDraft, allPersonalDetails}: NewChatConfirmP if (!newGroupDraft) { return; } - const logins: string[] = newGroupDraft.participants.map((participant: {login: string}) => participant.login); + const logins: string[] = newGroupDraft.participants.map((participant) => participant.login); Report.navigateToAndOpenReport(logins, true, groupName); }; diff --git a/src/pages/NewChatPage.tsx b/src/pages/NewChatPage.tsx index c90cf6e729e0..cb4511657afd 100755 --- a/src/pages/NewChatPage.tsx +++ b/src/pages/NewChatPage.tsx @@ -190,10 +190,10 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingF * Navigates to create group confirm page */ const navigateToConfirmPage = () => { - const selectedParticipants: SelectedParticipant[] = selectedOptions.map((option: OptionData) => ({login: option.login ?? '', accountID: option.accountID ?? -1})); if (!personalData || !personalData.login || !personalData.accountID) { return; } + const selectedParticipants: SelectedParticipant[] = selectedOptions.map((option: OptionData) => ({login: option.login ?? '', accountID: option.accountID ?? -1})); const logins = [...selectedParticipants, {login: personalData.login, accountID: personalData.accountID}]; Report.setGroupDraft(logins); Navigation.navigate(ROUTES.NEW_CHAT_CONFIRM); @@ -202,8 +202,8 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingF const updateOptions = useCallback(() => { let newSelectedOptions; if (newGroupDraft?.participants) { - const selectedParticipants = newGroupDraft?.participants.filter((participant: SelectedParticipant) => participant.accountID !== personalData.accountID); - newSelectedOptions = selectedParticipants.map((participant: SelectedParticipant): OptionData => { + const selectedParticipants = newGroupDraft.participants.filter((participant) => participant.accountID !== personalData.accountID); + newSelectedOptions = selectedParticipants.map((participant): OptionData => { const baseOption = OptionsListUtils.getParticipantsOption({accountID: participant.accountID, login: participant.login, reportID: ''}, personalDetails); return {...baseOption, reportID: baseOption.reportID ?? ''}; }); @@ -337,3 +337,5 @@ export default withOnyx({ initWithStoredValues: false, }, })(NewChatPage); + +export type {SelectedParticipant}; diff --git a/src/pages/home/HeaderView.tsx b/src/pages/home/HeaderView.tsx index b9b4e416a0c0..8aa2b855176c 100644 --- a/src/pages/home/HeaderView.tsx +++ b/src/pages/home/HeaderView.tsx @@ -21,7 +21,6 @@ import useLocalize from '@hooks/useLocalize'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import {getGroupChatName} from '@libs/GroupChatUtils'; import * as HeaderUtils from '@libs/HeaderUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {ReportWithoutHasDraft} from '@libs/OnyxSelectors/reportWithoutHasDraftSelector'; @@ -90,7 +89,7 @@ function HeaderView({report, personalDetails, parentReport, parentReportAction, const isTaskReport = ReportUtils.isTaskReport(report); const reportHeaderData = !isTaskReport && !isChatThread && report.parentReportID ? parentReport : report; // Use sorted display names for the title for group chats on native small screen widths - const title = isGroupChat ? getGroupChatName(report.participantAccountIDs ?? [], true) : ReportUtils.getReportName(reportHeaderData); + const title = isGroupChat ? ReportUtils.getGroupChatName(report.participantAccountIDs ?? [], true) : ReportUtils.getReportName(reportHeaderData); const subtitle = ReportUtils.getChatRoomSubtitle(reportHeaderData); const parentNavigationSubtitleData = ReportUtils.getParentNavigationSubtitle(reportHeaderData); const isConcierge = ReportUtils.hasSingleParticipant(report) && participants.includes(CONST.ACCOUNT_ID.CONCIERGE); diff --git a/src/pages/settings/Report/ReportSettingsPage.tsx b/src/pages/settings/Report/ReportSettingsPage.tsx index 12fcc5ee2bad..f1c4047ae33e 100644 --- a/src/pages/settings/Report/ReportSettingsPage.tsx +++ b/src/pages/settings/Report/ReportSettingsPage.tsx @@ -11,7 +11,6 @@ import ScrollView from '@components/ScrollView'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import {getGroupChatName} from '@libs/GroupChatUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as ReportUtils from '@libs/ReportUtils'; import type {ReportSettingsNavigatorParamList} from '@navigation/types'; @@ -48,7 +47,8 @@ function ReportSettingsPage({report, policies}: ReportSettingsPageProps) { const shouldShowNotificationPref = !isMoneyRequestReport && report?.notificationPreference !== CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN; const roomNameLabel = translate(isMoneyRequestReport ? 'workspace.editor.nameInputLabel' : 'newRoomPage.roomName'); - const reportName = ReportUtils.isDeprecatedGroupDM(report) || ReportUtils.isGroupChat(report) ? getGroupChatName(report.participantAccountIDs ?? []) : ReportUtils.getReportName(report); + const reportName = + ReportUtils.isDeprecatedGroupDM(report) || ReportUtils.isGroupChat(report) ? ReportUtils.getGroupChatName(report.participantAccountIDs ?? []) : ReportUtils.getReportName(report); const shouldShowWriteCapability = !isMoneyRequestReport; diff --git a/src/types/onyx/NewGroupChatDraft.ts b/src/types/onyx/NewGroupChatDraft.ts index fe92018ad429..f2bc3e94a7ea 100644 --- a/src/types/onyx/NewGroupChatDraft.ts +++ b/src/types/onyx/NewGroupChatDraft.ts @@ -1,8 +1,7 @@ +import type {SelectedParticipant} from '@pages/NewChatPage'; + type NewGroupChatDraft = { - participants: Array<{ - login: string; - accountID: number; - }>; + participants: SelectedParticipant[]; reportName: string; }; From 24cf044fad3e7e2ee5d63203f7377af750b2f173 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Wed, 27 Mar 2024 00:52:16 +0100 Subject: [PATCH 27/29] change type location, resolved comments --- src/libs/actions/IOU.ts | 5 ++--- src/pages/NewChatConfirmPage.tsx | 2 +- src/pages/NewChatPage.tsx | 6 +----- src/types/onyx/NewGroupChatDraft.ts | 7 +++++-- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 56d90b0208d1..b1117ba09865 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -2286,9 +2286,8 @@ function createSplitsAndOnyxData( existingSplitChatReport = participants.length < 2 ? ReportUtils.getChatByParticipants(participantAccountIDs) : null; } let newChat: ReportUtils.OptimisticChatReport | EmptyObject = {}; - + const allParticipantsAccountIDs = [...participantAccountIDs, currentUserAccountID]; if (!existingSplitChatReport && participants.length > 1) { - const allParticipantsAccountIDs = [...participantAccountIDs, currentUserAccountID]; newChat = ReportUtils.buildOptimisticChatReport( allParticipantsAccountIDs, '', @@ -2303,7 +2302,7 @@ function createSplitsAndOnyxData( ); } if (isEmptyObject(newChat)) { - newChat = ReportUtils.buildOptimisticChatReport(participantAccountIDs); + newChat = ReportUtils.buildOptimisticChatReport(allParticipantsAccountIDs); } const splitChatReport = existingSplitChatReport ?? newChat; const isOwnPolicyExpenseChat = !!splitChatReport.isOwnPolicyExpenseChat; diff --git a/src/pages/NewChatConfirmPage.tsx b/src/pages/NewChatConfirmPage.tsx index 5330a3979d26..8570c061ebce 100644 --- a/src/pages/NewChatConfirmPage.tsx +++ b/src/pages/NewChatConfirmPage.tsx @@ -44,7 +44,7 @@ function NewChatConfirmPage({newGroupDraft, allPersonalDetails}: NewChatConfirmP if (!newGroupDraft?.participants) { return []; } - const options: Participant[] = newGroupDraft?.participants.map((participant) => + const options: Participant[] = newGroupDraft.participants.map((participant) => OptionsListUtils.getParticipantsOption({accountID: participant.accountID, login: participant.login, reportID: ''}, allPersonalDetails), ); return options; diff --git a/src/pages/NewChatPage.tsx b/src/pages/NewChatPage.tsx index cb4511657afd..59727945967d 100755 --- a/src/pages/NewChatPage.tsx +++ b/src/pages/NewChatPage.tsx @@ -26,11 +26,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; - -type SelectedParticipant = { - accountID: number; - login: string; -}; +import type {SelectedParticipant} from '@src/types/onyx/NewGroupChatDraft'; type NewChatPageWithOnyxProps = { /** All reports shared with the user */ diff --git a/src/types/onyx/NewGroupChatDraft.ts b/src/types/onyx/NewGroupChatDraft.ts index f2bc3e94a7ea..97dd63aa5f68 100644 --- a/src/types/onyx/NewGroupChatDraft.ts +++ b/src/types/onyx/NewGroupChatDraft.ts @@ -1,8 +1,11 @@ -import type {SelectedParticipant} from '@pages/NewChatPage'; +type SelectedParticipant = { + accountID: number; + login: string; +}; type NewGroupChatDraft = { participants: SelectedParticipant[]; reportName: string; }; - +export type {SelectedParticipant}; export default NewGroupChatDraft; From 416d9350c61428a8969cb58901874e5c39a60a2b Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Wed, 27 Mar 2024 12:34:46 +0100 Subject: [PATCH 28/29] remove type and fix group chat test --- src/pages/NewChatPage.tsx | 2 -- tests/actions/IOUTest.ts | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/pages/NewChatPage.tsx b/src/pages/NewChatPage.tsx index 59727945967d..9c2d47f684ab 100755 --- a/src/pages/NewChatPage.tsx +++ b/src/pages/NewChatPage.tsx @@ -333,5 +333,3 @@ export default withOnyx({ initWithStoredValues: false, }, })(NewChatPage); - -export type {SelectedParticipant}; diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index 33cdefb749ec..0a7be4fb98fb 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -1142,7 +1142,8 @@ describe('actions/IOU', () => { groupChat = Object.values(allReports ?? {}).find( (report) => - report?.type === CONST.REPORT.TYPE.CHAT && isEqual(report.participantAccountIDs, [CARLOS_ACCOUNT_ID, JULES_ACCOUNT_ID, VIT_ACCOUNT_ID]), + report?.type === CONST.REPORT.TYPE.CHAT && + isEqual(report.participantAccountIDs, [CARLOS_ACCOUNT_ID, JULES_ACCOUNT_ID, VIT_ACCOUNT_ID, RORY_ACCOUNT_ID]), ) ?? null; expect(isEmptyObject(groupChat)).toBe(false); expect(groupChat?.pendingFields).toStrictEqual({createChat: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD}); From 6b607a46824597aa557e1978088a277a543d0fc1 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Thu, 28 Mar 2024 12:56:40 +0100 Subject: [PATCH 29/29] update stack navigator --- src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index ef1fc3c2dfb0..6583097ac9b8 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -152,6 +152,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({