From 441d4953d0e1513bce57d2450ce83b2335fdb6fc Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Fri, 29 Mar 2024 09:38:20 +0100 Subject: [PATCH 001/146] simplify avatar logic and remove unnecessary calls to getAvatar() fn --- src/components/Avatar.tsx | 12 +++- src/components/AvatarWithIndicator.tsx | 7 ++- src/components/MultipleAvatars.tsx | 2 + src/components/ReportActionItem/TaskView.tsx | 2 +- .../BaseUserDetailsTooltip/index.tsx | 4 +- src/libs/OptionsListUtils.ts | 59 +++++++++---------- src/libs/ReportUtils.ts | 29 ++++----- src/libs/SidebarUtils.ts | 3 +- src/libs/UserUtils.ts | 15 ++--- src/libs/actions/Report.ts | 2 - src/pages/DetailsPage.tsx | 4 +- src/pages/ProfilePage.tsx | 6 +- src/pages/ReportParticipantsPage.tsx | 7 +-- src/pages/RoomMembersPage.tsx | 19 +++--- .../report/ReactionList/BaseReactionList.tsx | 58 +++++++++--------- .../ReportActionCompose/SuggestionMention.tsx | 4 +- .../home/report/ReportActionItemSingle.tsx | 16 +++-- .../sidebar/ProfileAvatarWithIndicator.js | 0 src/pages/workspace/WorkspaceMembersPage.tsx | 4 +- .../members/WorkspaceMemberDetailsPage.tsx | 5 +- .../WorkspaceWorkflowsApproverPage.tsx | 4 +- .../workflows/WorkspaceWorkflowsPayerPage.tsx | 4 +- src/types/onyx/OnyxCommon.ts | 2 +- 23 files changed, 139 insertions(+), 129 deletions(-) create mode 100644 src/pages/home/sidebar/ProfileAvatarWithIndicator.js diff --git a/src/components/Avatar.tsx b/src/components/Avatar.tsx index 358f5333bfba..6937e281da7a 100644 --- a/src/components/Avatar.tsx +++ b/src/components/Avatar.tsx @@ -7,6 +7,7 @@ import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import * as ReportUtils from '@libs/ReportUtils'; import type {AvatarSource} from '@libs/UserUtils'; +import * as UserUtils from '@libs/UserUtils'; import type {AvatarSizeName} from '@styles/utils'; import CONST from '@src/CONST'; import type {AvatarType} from '@src/types/onyx/OnyxCommon'; @@ -49,6 +50,9 @@ type AvatarProps = { /** Owner of the avatar. If user, displayName. If workspace, policy name */ name?: string; + + /** Optional account id if it's user avatar */ + accountID?: number; }; function Avatar({ @@ -62,6 +66,7 @@ function Avatar({ fallbackIconTestID = '', type = CONST.ICON_TYPE_AVATAR, name = '', + accountID, }: AvatarProps) { const theme = useTheme(); const styles = useThemeStyles(); @@ -75,16 +80,17 @@ function Avatar({ }, [source]); const isWorkspace = type === CONST.ICON_TYPE_WORKSPACE; - const iconSize = StyleUtils.getAvatarSize(size); + const isEmptySource = !source; + const iconSize = StyleUtils.getAvatarSize(size); const imageStyle: StyleProp = [StyleUtils.getAvatarStyle(size), imageStyles, styles.noBorderRadius]; const iconStyle = imageStyles ? [StyleUtils.getAvatarStyle(size), styles.bgTransparent, imageStyles] : undefined; // We pass the color styles down to the SVG for the workspace and fallback avatar. - const useFallBackAvatar = imageError || source === Expensicons.FallbackAvatar || !source; + const useFallBackAvatar = imageError || isEmptySource || source === Expensicons.FallbackAvatar; const fallbackAvatar = isWorkspace ? ReportUtils.getDefaultWorkspaceAvatar(name) : fallbackIcon || Expensicons.FallbackAvatar; const fallbackAvatarTestID = isWorkspace ? ReportUtils.getDefaultWorkspaceAvatarTestID(name) : fallbackIconTestID || 'SvgFallbackAvatar Icon'; - const avatarSource = useFallBackAvatar ? fallbackAvatar : source; + const avatarSource = useFallBackAvatar ? fallbackAvatar : UserUtils.getAvatar(source, accountID) ?? Expensicons.FallbackAvatar; let iconColors; if (isWorkspace) { diff --git a/src/components/AvatarWithIndicator.tsx b/src/components/AvatarWithIndicator.tsx index 42b91b3d2d71..ea29f41b0d80 100644 --- a/src/components/AvatarWithIndicator.tsx +++ b/src/components/AvatarWithIndicator.tsx @@ -13,6 +13,9 @@ type AvatarWithIndicatorProps = { /** URL for the avatar */ source: UserUtils.AvatarSource; + /** account id if it's user avatar */ + accountID?: number; + /** To show a tooltip on hover */ tooltipText?: string; @@ -23,7 +26,7 @@ type AvatarWithIndicatorProps = { isLoading?: boolean; }; -function AvatarWithIndicator({source, tooltipText = '', fallbackIcon = Expensicons.FallbackAvatar, isLoading = true}: AvatarWithIndicatorProps) { +function AvatarWithIndicator({source, accountID, tooltipText = '', fallbackIcon = Expensicons.FallbackAvatar, isLoading = true}: AvatarWithIndicatorProps) { const styles = useThemeStyles(); return ( @@ -35,7 +38,7 @@ function AvatarWithIndicator({source, tooltipText = '', fallbackIcon = Expensico <> diff --git a/src/components/MultipleAvatars.tsx b/src/components/MultipleAvatars.tsx index dedaba500a9c..31d3d35af58d 100644 --- a/src/components/MultipleAvatars.tsx +++ b/src/components/MultipleAvatars.tsx @@ -158,6 +158,7 @@ function MultipleAvatars({ name={icons[0].name} type={icons[0].type} fallbackIcon={icons[0].fallbackIcon} + accountID={icons[0].id} /> @@ -207,6 +208,7 @@ function MultipleAvatars({ name={icon.name} type={icon.type} fallbackIcon={icon.fallbackIcon} + accountID={icon.id} /> diff --git a/src/components/ReportActionItem/TaskView.tsx b/src/components/ReportActionItem/TaskView.tsx index 9711e126907f..e3e07ab0d7ad 100644 --- a/src/components/ReportActionItem/TaskView.tsx +++ b/src/components/ReportActionItem/TaskView.tsx @@ -156,7 +156,7 @@ function TaskView({report, shouldShowHorizontalRule, ...props}: TaskViewProps) { {title} diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index b2e243053bbc..4ea33940aa9a 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -7,6 +7,7 @@ import lodashSet from 'lodash/set'; import lodashSortBy from 'lodash/sortBy'; import Onyx from 'react-native-onyx'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; +import {FallbackAvatar} from '@components/Icon/Expensicons'; import type {SelectedTagOption} from '@components/TagPicker'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; @@ -303,17 +304,24 @@ function getAvatarsForAccountIDs(accountIDs: number[], personalDetails: OnyxEntr Object.entries(defaultValues).forEach((item) => { reversedDefaultValues[item[1]] = item[0]; }); - return accountIDs.map((accountID) => { - const login = reversedDefaultValues[accountID] ?? ''; - const userPersonalDetail = personalDetails?.[accountID] ?? {login, accountID, avatar: ''}; - return { - id: accountID, - source: UserUtils.getAvatar(userPersonalDetail.avatar, userPersonalDetail.accountID), - type: CONST.ICON_TYPE_AVATAR, - name: userPersonalDetail.login ?? '', - }; - }); + return accountIDs + .map((accountID) => { + const login = reversedDefaultValues[accountID] ?? ''; + const userPersonalDetail = personalDetails?.[accountID] ?? {login, accountID, avatar: ''}; + + if (!userPersonalDetail.avatar) { + return; + } + + return { + id: accountID, + source: userPersonalDetail.avatar, + type: CONST.ICON_TYPE_AVATAR, + name: userPersonalDetail.login ?? '', + }; + }) + .filter((icon): icon is OnyxCommon.Icon => icon !== undefined); } /** @@ -332,9 +340,7 @@ function getPersonalDetailsForAccountIDs(accountIDs: number[] | undefined, perso } let personalDetail: OnyxEntry = personalDetails[accountID]; if (!personalDetail) { - personalDetail = { - avatar: UserUtils.getDefaultAvatar(cleanAccountID), - } as PersonalDetails; + personalDetail = {} as PersonalDetails; } if (cleanAccountID === CONST.ACCOUNT_ID.CONCIERGE) { @@ -363,6 +369,7 @@ function getParticipantsOption(participant: ReportUtils.OptionData | Participant // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const login = detail?.login || participant.login || ''; const displayName = PersonalDetailsUtils.getDisplayNameOrDefault(detail, LocalePhoneNumber.formatPhoneNumber(login)); + return { keyForList: String(detail?.accountID), login, @@ -373,7 +380,7 @@ function getParticipantsOption(participant: ReportUtils.OptionData | Participant alternateText: LocalePhoneNumber.formatPhoneNumber(login) || displayName, icons: [ { - source: UserUtils.getAvatar(detail?.avatar ?? '', detail?.accountID ?? -1), + source: detail?.avatar ?? FallbackAvatar, name: login, type: CONST.ICON_TYPE_AVATAR, id: detail?.accountID, @@ -757,13 +764,7 @@ function createOption( // Disabling this line for safeness as nullish coalescing works only if the value is undefined or null // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing result.searchText = getSearchText(report, reportName, personalDetailList, !!result.isChatRoom || !!result.isPolicyExpenseChat, !!result.isThread); - result.icons = ReportUtils.getIcons( - report, - personalDetails, - UserUtils.getAvatar(personalDetail?.avatar ?? '', personalDetail?.accountID), - personalDetail?.login, - personalDetail?.accountID, - ); + result.icons = ReportUtils.getIcons(report, personalDetails, personalDetail?.avatar, personalDetail?.login, personalDetail?.accountID); result.subtitle = subtitle; return result; @@ -1851,7 +1852,6 @@ function getOptions( [optimisticAccountID]: { accountID: optimisticAccountID, login: searchValue, - avatar: UserUtils.getDefaultAvatar(optimisticAccountID), }, }; userToInvite = createOption([optimisticAccountID], personalDetailsExtended, null, reportActions, { @@ -1864,10 +1864,10 @@ function getOptions( // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing userToInvite.alternateText = userToInvite.alternateText || searchValue; - // If user doesn't exist, use a default avatar + // If user doesn't exist, use a fallback avatar userToInvite.icons = [ { - source: UserUtils.getAvatar('', optimisticAccountID), + source: FallbackAvatar, name: searchValue, type: CONST.ICON_TYPE_AVATAR, }, @@ -1941,17 +1941,12 @@ function getShareLogOptions(options: OptionList, searchValue = '', betas: Beta[] */ function getIOUConfirmationOptionsFromPayeePersonalDetail(personalDetail: PersonalDetails | EmptyObject, amountText?: string): PayeePersonalDetails { const formattedLogin = LocalePhoneNumber.formatPhoneNumber(personalDetail.login ?? ''); + const icons = personalDetail.avatar ? [{source: personalDetail.avatar, name: personalDetail.login ?? '', type: CONST.ICON_TYPE_AVATAR, id: personalDetail.accountID}] : []; + return { text: PersonalDetailsUtils.getDisplayNameOrDefault(personalDetail, formattedLogin), alternateText: formattedLogin || PersonalDetailsUtils.getDisplayNameOrDefault(personalDetail, '', false), - icons: [ - { - source: UserUtils.getAvatar(personalDetail.avatar, personalDetail.accountID), - name: personalDetail.login ?? '', - type: CONST.ICON_TYPE_AVATAR, - id: personalDetail.accountID, - }, - ], + icons, descriptiveText: amountText ?? '', login: personalDetail.login ?? '', accountID: personalDetail.accountID, diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index b03abbcdf9bb..695da35555eb 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -11,6 +11,7 @@ import Onyx from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import type {FileObject} from '@components/AttachmentModal'; import * as Expensicons from '@components/Icon/Expensicons'; +import {FallbackAvatar} from '@components/Icon/Expensicons'; import * as defaultGroupAvatars from '@components/Icon/GroupDefaultAvatars'; import * as defaultWorkspaceAvatars from '@components/Icon/WorkspaceDefaultAvatars'; import CONST from '@src/CONST'; @@ -1629,7 +1630,7 @@ function getIconsForParticipants(participants: number[], personalDetails: OnyxCo const participantsList = participants || []; for (const accountID of participantsList) { - const avatarSource = UserUtils.getAvatar(personalDetails?.[accountID]?.avatar ?? '', accountID); + const avatarSource = personalDetails?.[accountID]?.avatar ?? FallbackAvatar; const displayNameLogin = personalDetails?.[accountID]?.displayName ? personalDetails?.[accountID]?.displayName : personalDetails?.[accountID]?.login; participantDetails.push([accountID, displayNameLogin ?? '', avatarSource, personalDetails?.[accountID]?.fallbackIcon ?? '']); } @@ -1690,12 +1691,12 @@ function getPersonalDetailsForAccountID(accountID: number): Partial = {}; const visibleReportActionItems: ReportActions = {}; @@ -414,7 +413,7 @@ function getOptionData({ result.subtitle = subtitle; result.participantsList = participantPersonalDetailList; - result.icons = ReportUtils.getIcons(report, personalDetails, UserUtils.getAvatar(personalDetail?.avatar ?? {}, personalDetail?.accountID), '', -1, policy); + result.icons = ReportUtils.getIcons(report, personalDetails, personalDetail?.avatar, '', -1, policy); result.searchText = OptionsListUtils.getSearchText(report, reportName, participantPersonalDetailList, result.isChatRoom || result.isPolicyExpenseChat, result.isThread); result.displayNamesWithTooltips = displayNamesWithTooltips; diff --git a/src/libs/UserUtils.ts b/src/libs/UserUtils.ts index ce7e4963afc7..ee4a9c7a275f 100644 --- a/src/libs/UserUtils.ts +++ b/src/libs/UserUtils.ts @@ -2,7 +2,7 @@ import Str from 'expensify-common/lib/str'; import type {OnyxEntry} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import * as defaultAvatars from '@components/Icon/DefaultAvatars'; -import {ConciergeAvatar, FallbackAvatar, NotificationsAvatar} from '@components/Icon/Expensicons'; +import {ConciergeAvatar, NotificationsAvatar} from '@components/Icon/Expensicons'; import CONST from '@src/CONST'; import type {LoginList} from '@src/types/onyx'; import type Login from '@src/types/onyx/Login'; @@ -82,10 +82,7 @@ function generateAccountID(searchValue: string): number { * @param [accountID] * @returns */ -function getDefaultAvatar(accountID = -1, avatarURL?: string): IconAsset { - if (accountID <= 0) { - return FallbackAvatar; - } +function getDefaultAvatar(accountID = -1, avatarURL?: string): IconAsset | undefined { if (Number(accountID) === CONST.ACCOUNT_ID.CONCIERGE) { return ConciergeAvatar; } @@ -155,7 +152,7 @@ function isDefaultAvatar(avatarSource?: AvatarSource): avatarSource is string | * @param avatarSource - the avatar source from user's personalDetails * @param accountID - the accountID of the user */ -function getAvatar(avatarSource?: AvatarSource, accountID?: number): AvatarSource { +function getAvatar(avatarSource?: AvatarSource, accountID?: number): AvatarSource | undefined { return isDefaultAvatar(avatarSource) ? getDefaultAvatar(accountID, avatarSource) : avatarSource; } @@ -163,7 +160,7 @@ function getAvatar(avatarSource?: AvatarSource, accountID?: number): AvatarSourc * Provided an avatar URL, if avatar is a default avatar, return NewDot default avatar URL. * Otherwise, return the URL pointing to a user-uploaded avatar. * - * @param avatarURL - the avatar source from user's personalDetails + * @param avatarSource - the avatar source from user's personalDetails * @param accountID - the accountID of the user */ function getAvatarUrl(avatarSource: AvatarSource | undefined, accountID: number): AvatarSource { @@ -174,7 +171,7 @@ function getAvatarUrl(avatarSource: AvatarSource | undefined, accountID: number) * Avatars uploaded by users will have a _128 appended so that the asset server returns a small version. * This removes that part of the URL so the full version of the image can load. */ -function getFullSizeAvatar(avatarSource: AvatarSource | undefined, accountID?: number): AvatarSource { +function getFullSizeAvatar(avatarSource: AvatarSource | undefined, accountID?: number): AvatarSource | undefined { const source = getAvatar(avatarSource, accountID); if (typeof source !== 'string') { return source; @@ -186,7 +183,7 @@ function getFullSizeAvatar(avatarSource: AvatarSource | undefined, accountID?: n * Small sized avatars end with _128.. This adds the _128 at the end of the * source URL (before the file type) if it doesn't exist there already. */ -function getSmallSizeAvatar(avatarSource: AvatarSource, accountID?: number): AvatarSource { +function getSmallSizeAvatar(avatarSource: AvatarSource, accountID?: number): AvatarSource | undefined { const source = getAvatar(avatarSource, accountID); if (typeof source !== 'string') { return source; diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index ac7edc38980d..c6c232c097a9 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -68,7 +68,6 @@ import * as ReportUtils from '@libs/ReportUtils'; import {doesReportBelongToWorkspace} from '@libs/ReportUtils'; import type {OptimisticAddCommentReportAction} from '@libs/ReportUtils'; import shouldSkipDeepLinkNavigation from '@libs/shouldSkipDeepLinkNavigation'; -import * as UserUtils from '@libs/UserUtils'; import Visibility from '@libs/Visibility'; import CONFIG from '@src/CONFIG'; import CONST from '@src/CONST'; @@ -766,7 +765,6 @@ function openReport( optimisticPersonalDetails[accountID] = allPersonalDetails?.[accountID] ?? { login, accountID, - avatar: UserUtils.getDefaultAvatarURL(accountID), displayName: login, isOptimisticPersonalDetail: true, }; diff --git a/src/pages/DetailsPage.tsx b/src/pages/DetailsPage.tsx index 49b3e856c65d..338e51cb408e 100755 --- a/src/pages/DetailsPage.tsx +++ b/src/pages/DetailsPage.tsx @@ -70,7 +70,6 @@ function DetailsPage({personalDetails, route, session}: DetailsPageProps) { accountID: optimisticAccountID, login, displayName: login, - avatar: UserUtils.getDefaultAvatar(optimisticAccountID), }; } @@ -115,9 +114,10 @@ function DetailsPage({personalDetails, route, session}: DetailsPageProps) { diff --git a/src/pages/ProfilePage.tsx b/src/pages/ProfilePage.tsx index 6ad3860132d2..d2967de8360a 100755 --- a/src/pages/ProfilePage.tsx +++ b/src/pages/ProfilePage.tsx @@ -25,7 +25,6 @@ import Navigation from '@libs/Navigation/Navigation'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import {parsePhoneNumber} from '@libs/PhoneNumber'; import * as ReportUtils from '@libs/ReportUtils'; -import * as UserUtils from '@libs/UserUtils'; import * as ValidationUtils from '@libs/ValidationUtils'; import type {ProfileNavigatorParamList} from '@navigation/types'; import * as PersonalDetailsActions from '@userActions/PersonalDetails'; @@ -97,7 +96,7 @@ function ProfilePage({route}: ProfilePageProps) { const displayName = PersonalDetailsUtils.getDisplayNameOrDefault(details, undefined, undefined, isCurrentUser); // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const avatar = details?.avatar || UserUtils.getDefaultAvatar(); // we can have an empty string and in this case, we need to show the default avatar + const avatar = details.avatar; const fallbackIcon = details?.fallbackIcon ?? ''; const login = details?.login ?? ''; const timezone = details?.timezone; @@ -163,9 +162,10 @@ function ProfilePage({route}: ProfilePageProps) { diff --git a/src/pages/ReportParticipantsPage.tsx b/src/pages/ReportParticipantsPage.tsx index f635499a286f..b69eb88b3244 100755 --- a/src/pages/ReportParticipantsPage.tsx +++ b/src/pages/ReportParticipantsPage.tsx @@ -26,7 +26,6 @@ import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as ReportUtils from '@libs/ReportUtils'; -import * as UserUtils from '@libs/UserUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -93,14 +92,14 @@ function ReportParticipantsPage({report, personalDetails, session}: ReportPartic alternateText: formatPhoneNumber(details?.login ?? ''), rightElement: roleBadge, pendingAction: pendingChatMember?.pendingAction, - icons: [ + icons: details?.avatar ? [ { - source: UserUtils.getAvatar(details?.avatar, accountID), + source: details.avatar, name: formatPhoneNumber(details?.login ?? ''), type: CONST.ICON_TYPE_AVATAR, id: accountID, }, - ], + ] : [], }); }); diff --git a/src/pages/RoomMembersPage.tsx b/src/pages/RoomMembersPage.tsx index d025a3bde265..a3005561b0ca 100644 --- a/src/pages/RoomMembersPage.tsx +++ b/src/pages/RoomMembersPage.tsx @@ -26,7 +26,6 @@ import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as PolicyUtils from '@libs/PolicyUtils'; import * as ReportUtils from '@libs/ReportUtils'; -import * as UserUtils from '@libs/UserUtils'; import * as Report from '@userActions/Report'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -207,14 +206,16 @@ function RoomMembersPage({report, session, policies}: RoomMembersPageProps) { isDisabled: accountID === session?.accountID, text: formatPhoneNumber(PersonalDetailsUtils.getDisplayNameOrDefault(details)), alternateText: details?.login ? formatPhoneNumber(details.login) : '', - icons: [ - { - source: UserUtils.getAvatar(details.avatar, accountID), - name: details.login ?? '', - type: CONST.ICON_TYPE_AVATAR, - id: Number(accountID), - }, - ], + icons: details.avatar + ? [ + { + source: details.avatar, + name: details.login ?? '', + type: CONST.ICON_TYPE_AVATAR, + id: Number(accountID), + }, + ] + : [], pendingAction: pendingChatMember?.pendingAction, }); }); diff --git a/src/pages/home/report/ReactionList/BaseReactionList.tsx b/src/pages/home/report/ReactionList/BaseReactionList.tsx index aaa8ca132ee0..996868b6fd6c 100755 --- a/src/pages/home/report/ReactionList/BaseReactionList.tsx +++ b/src/pages/home/report/ReactionList/BaseReactionList.tsx @@ -7,7 +7,6 @@ import OptionRow from '@components/OptionRow'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import Navigation from '@libs/Navigation/Navigation'; -import * as UserUtils from '@libs/UserUtils'; import variables from '@styles/variables'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; @@ -54,33 +53,38 @@ function BaseReactionList({hasUserReacted = false, users, isVisible = false, emo * so that the sticky headers function properly * */ - const renderItem: FlatListProps['renderItem'] = ({item}) => ( - { - onClose?.(); + const renderItem: FlatListProps['renderItem'] = ({item}) => { + const icons = item.avatar + ? [ + { + id: item.accountID, + source: item.avatar, + name: item.login ?? '', + type: CONST.ICON_TYPE_AVATAR, + }, + ] + : []; + return ( + { + onClose?.(); - Navigation.navigate(ROUTES.PROFILE.getRoute(item.accountID)); - }} - option={{ - reportID: String(item.accountID), - text: Str.removeSMSDomain(item.displayName ?? ''), - alternateText: Str.removeSMSDomain(item.login ?? ''), - participantsList: [item], - icons: [ - { - id: item.accountID, - source: UserUtils.getAvatar(item.avatar, item.accountID), - name: item.login ?? '', - type: CONST.ICON_TYPE_AVATAR, - }, - ], - keyForList: item.login ?? String(item.accountID), - }} - /> - ); + Navigation.navigate(ROUTES.PROFILE.getRoute(item.accountID)); + }} + option={{ + reportID: String(item.accountID), + text: Str.removeSMSDomain(item.displayName ?? ''), + alternateText: Str.removeSMSDomain(item.login ?? ''), + participantsList: [item], + icons, + keyForList: item.login ?? String(item.accountID), + }} + /> + ); + }; return ( <> diff --git a/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx b/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx index 147799ee118b..e1e390950730 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx +++ b/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx @@ -5,6 +5,7 @@ import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useRef, import {withOnyx} from 'react-native-onyx'; import type {OnyxCollection} from 'react-native-onyx'; import * as Expensicons from '@components/Icon/Expensicons'; +import {FallbackAvatar} from '@components/Icon/Expensicons'; import type {Mention} from '@components/MentionSuggestions'; import MentionSuggestions from '@components/MentionSuggestions'; import {usePersonalDetails} from '@components/OnyxProvider'; @@ -238,9 +239,10 @@ function SuggestionMention( icons: [ { name: detail?.login, - source: UserUtils.getAvatar(detail?.avatar, detail?.accountID), + source: detail?.avatar ?? FallbackAvatar, type: CONST.ICON_TYPE_AVATAR, fallbackIcon: detail?.fallbackIcon, + id: detail?.accountID, }, ], }); diff --git a/src/pages/home/report/ReportActionItemSingle.tsx b/src/pages/home/report/ReportActionItemSingle.tsx index 1e0dc432b3fc..4f5c6ef6c602 100644 --- a/src/pages/home/report/ReportActionItemSingle.tsx +++ b/src/pages/home/report/ReportActionItemSingle.tsx @@ -3,6 +3,7 @@ import type {StyleProp, ViewStyle} from 'react-native'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import Avatar from '@components/Avatar'; +import {FallbackAvatar} from '@components/Icon/Expensicons'; import MultipleAvatars from '@components/MultipleAvatars'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; import {usePersonalDetails} from '@components/OnyxProvider'; @@ -19,7 +20,6 @@ import ControlSelection from '@libs/ControlSelection'; import DateUtils from '@libs/DateUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as ReportUtils from '@libs/ReportUtils'; -import * as UserUtils from '@libs/UserUtils'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; import type {Report, ReportAction} from '@src/types/onyx'; @@ -86,7 +86,8 @@ function ReportActionItemSingle({ let actorHint = (login || (displayName ?? '')).replace(CONST.REGEX.MERGED_ACCOUNT_PREFIX, ''); const displayAllActors = useMemo(() => action?.actionName === CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW && iouReport, [action?.actionName, iouReport]); const isWorkspaceActor = ReportUtils.isPolicyExpenseChat(report) && (!actorAccountID || displayAllActors); - let avatarSource = UserUtils.getAvatar(avatar ?? '', actorAccountID); + let avatarSource = avatar; + let avatarAccountId = actorAccountID; if (isWorkspaceActor) { displayName = ReportUtils.getPolicyName(report); @@ -99,7 +100,8 @@ function ReportActionItemSingle({ const delegateDisplayName = delegateDetails?.displayName; actorHint = `${delegateDisplayName} (${translate('reportAction.asCopilot')} ${displayName})`; displayName = actorHint; - avatarSource = UserUtils.getAvatar(delegateDetails?.avatar ?? '', Number(action.delegateAccountID)); + avatarSource = delegateDetails?.avatar; + avatarAccountId = Number(action.delegateAccountID); } // If this is a report preview, display names and avatars of both people involved @@ -112,7 +114,7 @@ function ReportActionItemSingle({ const secondaryDisplayName = ReportUtils.getDisplayNameForParticipant(secondaryAccountId); displayName = `${primaryDisplayName} & ${secondaryDisplayName}`; secondaryAvatar = { - source: UserUtils.getAvatar(secondaryUserAvatar, secondaryAccountId), + source: secondaryUserAvatar, type: CONST.ICON_TYPE_AVATAR, name: secondaryDisplayName ?? '', id: secondaryAccountId, @@ -126,11 +128,12 @@ function ReportActionItemSingle({ } else { secondaryAvatar = {name: '', source: '', type: 'avatar'}; } + const icon = { - source: avatarSource, + source: avatarSource ?? FallbackAvatar, type: isWorkspaceActor ? CONST.ICON_TYPE_WORKSPACE : CONST.ICON_TYPE_AVATAR, name: primaryDisplayName ?? '', - id: isWorkspaceActor ? '' : actorAccountID, + id: isWorkspaceActor ? undefined : avatarAccountId, }; // Since the display name for a report action message is delivered with the report history as an array of fragments @@ -201,6 +204,7 @@ function ReportActionItemSingle({ type={icon.type} name={icon.name} fallbackIcon={fallbackIcon} + accountID={icon.id} /> diff --git a/src/pages/home/sidebar/ProfileAvatarWithIndicator.js b/src/pages/home/sidebar/ProfileAvatarWithIndicator.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/src/pages/workspace/WorkspaceMembersPage.tsx b/src/pages/workspace/WorkspaceMembersPage.tsx index dfaf50c0bcf6..c6a744ce5580 100644 --- a/src/pages/workspace/WorkspaceMembersPage.tsx +++ b/src/pages/workspace/WorkspaceMembersPage.tsx @@ -12,6 +12,7 @@ import ButtonWithDropdownMenu from '@components/ButtonWithDropdownMenu'; import type {DropdownOption, WorkspaceMemberBulkActionType} from '@components/ButtonWithDropdownMenu/types'; import ConfirmModal from '@components/ConfirmModal'; import * as Expensicons from '@components/Icon/Expensicons'; +import {FallbackAvatar} from '@components/Icon/Expensicons'; import * as Illustrations from '@components/Icon/Illustrations'; import MessagesRow from '@components/MessagesRow'; import SelectionList from '@components/SelectionList'; @@ -33,7 +34,6 @@ import type {WorkspacesCentralPaneNavigatorParamList} from '@libs/Navigation/typ import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as PolicyUtils from '@libs/PolicyUtils'; -import * as UserUtils from '@libs/UserUtils'; import * as Policy from '@userActions/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -361,7 +361,7 @@ function WorkspaceMembersPage({policyMembers, personalDetails, invitedEmailsToAc rightElement: roleBadge, icons: [ { - source: UserUtils.getAvatar(details.avatar, accountID), + source: details.avatar ?? FallbackAvatar, name: formatPhoneNumber(details?.login ?? ''), type: CONST.ICON_TYPE_AVATAR, id: accountID, diff --git a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx index 57a416b7d130..371867c1b31b 100644 --- a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx +++ b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx @@ -19,7 +19,6 @@ import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as UserUtils from '@libs/UserUtils'; import Navigation from '@navigation/Navigation'; import type {SettingsNavigatorParamList} from '@navigation/types'; import AdminPolicyAccessOrNotFoundWrapper from '@pages/workspace/AdminPolicyAccessOrNotFoundWrapper'; @@ -59,7 +58,6 @@ function WorkspaceMemberDetailsPage({personalDetails, policyMembers, policy, rou const member = policyMembers?.[accountID]; const details = personalDetails?.[accountID] ?? ({} as PersonalDetails); - const avatar = details.avatar ?? UserUtils.getDefaultAvatar(); const fallbackIcon = details.fallbackIcon ?? ''; const displayName = details.displayName ?? ''; const isSelectedMemberOwner = policy?.owner === details.login; @@ -143,9 +141,10 @@ function WorkspaceMemberDetailsPage({personalDetails, policyMembers, policy, rou {Boolean(details.displayName ?? '') && ( diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsApproverPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsApproverPage.tsx index 089099d12ce7..7670e8d56f6b 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsApproverPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsApproverPage.tsx @@ -6,6 +6,7 @@ import type {OnyxEntry} from 'react-native-onyx'; import Badge from '@components/Badge'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import {FallbackAvatar} from '@components/Icon/Expensicons'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; import type {ListItem, Section} from '@components/SelectionList/types'; @@ -22,7 +23,6 @@ import type {WorkspacesCentralPaneNavigatorParamList} from '@libs/Navigation/typ import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as PolicyUtils from '@libs/PolicyUtils'; -import * as UserUtils from '@libs/UserUtils'; import FeatureEnabledAccessOrNotFoundWrapper from '@pages/workspace/FeatureEnabledAccessOrNotFoundWrapper'; import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; @@ -97,7 +97,7 @@ function WorkspaceWorkflowsApproverPage({policy, policyMembers, personalDetails, rightElement: roleBadge, icons: [ { - source: UserUtils.getAvatar(details.avatar, accountID), + source: details.avatar ?? FallbackAvatar, name: formatPhoneNumber(details?.login ?? ''), type: CONST.ICON_TYPE_AVATAR, id: accountID, diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx index f2d8cda48ef0..62ea4c83a34b 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx @@ -6,6 +6,7 @@ import {withOnyx} from 'react-native-onyx'; import Badge from '@components/Badge'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import {FallbackAvatar} from '@components/Icon/Expensicons'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; import type {ListItem, Section} from '@components/SelectionList/types'; @@ -21,7 +22,6 @@ import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as PolicyUtils from '@libs/PolicyUtils'; -import * as UserUtils from '@libs/UserUtils'; import type {SettingsNavigatorParamList} from '@navigation/types'; import AdminPolicyAccessOrNotFoundWrapper from '@pages/workspace/AdminPolicyAccessOrNotFoundWrapper'; import PaidPolicyAccessOrNotFoundWrapper from '@pages/workspace/PaidPolicyAccessOrNotFoundWrapper'; @@ -99,7 +99,7 @@ function WorkspaceWorkflowsPayerPage({route, policy, policyMembers, personalDeta rightElement: roleBadge, icons: [ { - source: UserUtils.getAvatar(details?.avatar, accountID), + source: details?.avatar ?? FallbackAvatar, name: formatPhoneNumber(details?.login ?? ''), type: CONST.ICON_TYPE_AVATAR, id: accountID, diff --git a/src/types/onyx/OnyxCommon.ts b/src/types/onyx/OnyxCommon.ts index 8b96a89a2a1b..9ac596a85777 100644 --- a/src/types/onyx/OnyxCommon.ts +++ b/src/types/onyx/OnyxCommon.ts @@ -34,7 +34,7 @@ type Icon = { name?: string; /** Avatar id */ - id?: number | string; + id?: number; /** A fallback avatar icon to display when there is an error on loading avatar from remote URL. */ fallbackIcon?: AvatarSource; From 3248bd2356efb8ed8a8346bbc5958dda4cab970d Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Fri, 12 Apr 2024 15:56:25 +0200 Subject: [PATCH 002/146] Do not set default avatar URL for optimistic data to have consistent avatars --- src/libs/PersonalDetailsUtils.ts | 11 +++++------ src/libs/ReportUtils.ts | 5 ++--- src/libs/actions/IOU.ts | 5 ----- src/libs/actions/Task.ts | 2 +- src/pages/RoomMembersPage.tsx | 19 +++++++++---------- 5 files changed, 17 insertions(+), 25 deletions(-) diff --git a/src/libs/PersonalDetailsUtils.ts b/src/libs/PersonalDetailsUtils.ts index ffa0605f1eba..6a3980c58ca7 100644 --- a/src/libs/PersonalDetailsUtils.ts +++ b/src/libs/PersonalDetailsUtils.ts @@ -150,12 +150,11 @@ function getPersonalDetailsOnyxDataForOptimisticUsers(newLogins: string[], newAc newLogins.forEach((login, index) => { const accountID = newAccountIDs[index]; - personalDetailsNew[accountID] = { - login, - accountID, - avatar: UserUtils.getDefaultAvatarURL(accountID), - displayName: LocalePhoneNumber.formatPhoneNumber(login), - }; + personalDetailsNew[accountID] = { + login, + accountID, + displayName: LocalePhoneNumber.formatPhoneNumber(login), + }; /** * Cleanup the optimistic user to ensure it does not permanently persist. diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 695da35555eb..74812518f863 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -10,7 +10,6 @@ import type {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import type {FileObject} from '@components/AttachmentModal'; -import * as Expensicons from '@components/Icon/Expensicons'; import {FallbackAvatar} from '@components/Icon/Expensicons'; import * as defaultGroupAvatars from '@components/Icon/GroupDefaultAvatars'; import * as defaultWorkspaceAvatars from '@components/Icon/WorkspaceDefaultAvatars'; @@ -1818,7 +1817,7 @@ function getIcons( ): Icon[] { if (isEmptyObject(report)) { const fallbackIcon: Icon = { - source: defaultIcon ?? Expensicons.FallbackAvatar, + source: defaultIcon ?? FallbackAvatar, type: CONST.ICON_TYPE_AVATAR, name: defaultName, id: defaultAccountID, @@ -3192,7 +3191,7 @@ function buildOptimisticAddCommentReportAction(text?: string, file?: FileObject, }, ], automatic: false, - avatar: allPersonalDetails?.[accountID ?? -1]?.avatar ?? UserUtils.getDefaultAvatarURL(accountID), + avatar: allPersonalDetails?.[accountID ?? -1]?.avatar, created: DateUtils.getDBTimeWithSkew(), message: [ { diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index cd0264ddb6ea..1ac602b81c77 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -44,7 +44,6 @@ import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; import type {OptimisticChatReport, OptimisticCreatedReportAction, OptimisticIOUReportAction, TransactionDetails} from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; -import * as UserUtils from '@libs/UserUtils'; import ViolationsUtils from '@libs/Violations/ViolationsUtils'; import type {NavigationPartialRoute} from '@navigation/types'; import CONST from '@src/CONST'; @@ -1272,7 +1271,6 @@ function getMoneyRequestInformation( ? { [payerAccountID]: { accountID: payerAccountID, - avatar: UserUtils.getDefaultAvatarURL(payerAccountID), // Disabling this line since participant.displayName can be an empty string // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing displayName: LocalePhoneNumber.formatPhoneNumber(participant.displayName || payerEmail), @@ -2804,7 +2802,6 @@ function createSplitsAndOnyxData( ? { [accountID]: { accountID, - avatar: UserUtils.getDefaultAvatarURL(accountID), // Disabling this line since participant.displayName can be an empty string // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing displayName: LocalePhoneNumber.formatPhoneNumber(participant.displayName || email), @@ -3248,7 +3245,6 @@ function startSplitBill({ value: { [accountID]: { accountID, - avatar: UserUtils.getDefaultAvatarURL(accountID), // Disabling this line since participant.displayName can be an empty string // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing displayName: LocalePhoneNumber.formatPhoneNumber(participant.displayName || email), @@ -4578,7 +4574,6 @@ function getSendMoneyParams( value: { [recipientAccountID]: { accountID: recipientAccountID, - avatar: UserUtils.getDefaultAvatarURL(recipient.accountID), // Disabling this line since participant.displayName can be an empty string // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing displayName: recipient.displayName || recipient.login, diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index 9cf7b0b78008..d0b7e3032558 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -682,7 +682,7 @@ function setAssigneeValue( // If this is an optimistic report, we likely don't have their personal details yet so we set it here optimistically as well const optimisticPersonalDetailsListAction = { accountID: assigneeAccountID, - avatar: allPersonalDetails?.[assigneeAccountID]?.avatar ?? UserUtils.getDefaultAvatarURL(assigneeAccountID), + avatar: allPersonalDetails?.[assigneeAccountID]?.avatar, displayName: allPersonalDetails?.[assigneeAccountID]?.displayName ?? assigneeEmail, login: assigneeEmail, }; diff --git a/src/pages/RoomMembersPage.tsx b/src/pages/RoomMembersPage.tsx index a3005561b0ca..3df7a697a454 100644 --- a/src/pages/RoomMembersPage.tsx +++ b/src/pages/RoomMembersPage.tsx @@ -36,6 +36,7 @@ import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type {WithReportOrNotFoundProps} from './home/report/withReportOrNotFound'; import withReportOrNotFound from './home/report/withReportOrNotFound'; import SearchInputManager from './workspace/SearchInputManager'; +import {FallbackAvatar} from '@components/Icon/Expensicons'; type RoomMembersPageOnyxProps = { session: OnyxEntry; @@ -206,16 +207,14 @@ function RoomMembersPage({report, session, policies}: RoomMembersPageProps) { isDisabled: accountID === session?.accountID, text: formatPhoneNumber(PersonalDetailsUtils.getDisplayNameOrDefault(details)), alternateText: details?.login ? formatPhoneNumber(details.login) : '', - icons: details.avatar - ? [ - { - source: details.avatar, - name: details.login ?? '', - type: CONST.ICON_TYPE_AVATAR, - id: Number(accountID), - }, - ] - : [], + icons: [ + { + source: details.avatar ?? FallbackAvatar, + name: details.login ?? '', + type: CONST.ICON_TYPE_AVATAR, + id: Number(accountID), + }, + ], pendingAction: pendingChatMember?.pendingAction, }); }); From c8fd46ba73a541159c937d14e02837485c79a39e Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Mon, 15 Apr 2024 10:32:24 +0200 Subject: [PATCH 003/146] Add small fixes to avatars logic --- src/components/AvatarWithIndicator.tsx | 2 +- src/libs/PersonalDetailsUtils.ts | 10 +++++----- src/libs/UserUtils.ts | 2 +- src/libs/actions/Task.ts | 1 - src/pages/ReportParticipantDetailsPage.tsx | 6 +++--- src/pages/ReportParticipantsPage.tsx | 7 ++++--- src/pages/RoomMembersPage.tsx | 2 +- .../report/ReportActionCompose/SuggestionMention.tsx | 1 - src/pages/home/sidebar/ProfileAvatarWithIndicator.js | 0 src/pages/home/sidebar/ProfileAvatarWithIndicator.tsx | 4 ++-- 10 files changed, 17 insertions(+), 18 deletions(-) delete mode 100644 src/pages/home/sidebar/ProfileAvatarWithIndicator.js diff --git a/src/components/AvatarWithIndicator.tsx b/src/components/AvatarWithIndicator.tsx index ea29f41b0d80..1bf18afb70ff 100644 --- a/src/components/AvatarWithIndicator.tsx +++ b/src/components/AvatarWithIndicator.tsx @@ -11,7 +11,7 @@ import Tooltip from './Tooltip'; type AvatarWithIndicatorProps = { /** URL for the avatar */ - source: UserUtils.AvatarSource; + source?: UserUtils.AvatarSource; /** account id if it's user avatar */ accountID?: number; diff --git a/src/libs/PersonalDetailsUtils.ts b/src/libs/PersonalDetailsUtils.ts index 6a3980c58ca7..961b3db13487 100644 --- a/src/libs/PersonalDetailsUtils.ts +++ b/src/libs/PersonalDetailsUtils.ts @@ -150,11 +150,11 @@ function getPersonalDetailsOnyxDataForOptimisticUsers(newLogins: string[], newAc newLogins.forEach((login, index) => { const accountID = newAccountIDs[index]; - personalDetailsNew[accountID] = { - login, - accountID, - displayName: LocalePhoneNumber.formatPhoneNumber(login), - }; + personalDetailsNew[accountID] = { + login, + accountID, + displayName: LocalePhoneNumber.formatPhoneNumber(login), + }; /** * Cleanup the optimistic user to ensure it does not permanently persist. diff --git a/src/libs/UserUtils.ts b/src/libs/UserUtils.ts index ee4a9c7a275f..205d84fff519 100644 --- a/src/libs/UserUtils.ts +++ b/src/libs/UserUtils.ts @@ -183,7 +183,7 @@ function getFullSizeAvatar(avatarSource: AvatarSource | undefined, accountID?: n * Small sized avatars end with _128.. This adds the _128 at the end of the * source URL (before the file type) if it doesn't exist there already. */ -function getSmallSizeAvatar(avatarSource: AvatarSource, accountID?: number): AvatarSource | undefined { +function getSmallSizeAvatar(avatarSource?: AvatarSource, accountID?: number): AvatarSource | undefined { const source = getAvatar(avatarSource, accountID); if (typeof source !== 'string') { return source; diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index d0b7e3032558..03c6095e552c 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -15,7 +15,6 @@ import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; import playSound, {SOUNDS} from '@libs/Sound'; -import * as UserUtils from '@libs/UserUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; diff --git a/src/pages/ReportParticipantDetailsPage.tsx b/src/pages/ReportParticipantDetailsPage.tsx index 2b1411641faa..563f24635759 100644 --- a/src/pages/ReportParticipantDetailsPage.tsx +++ b/src/pages/ReportParticipantDetailsPage.tsx @@ -18,7 +18,6 @@ import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import * as Report from '@libs/actions/Report'; import * as ReportUtils from '@libs/ReportUtils'; -import * as UserUtils from '@libs/UserUtils'; import Navigation from '@navigation/Navigation'; import type {ParticipantsNavigatorParamList} from '@navigation/types'; import CONST from '@src/CONST'; @@ -51,7 +50,7 @@ function ReportParticipantDetails({personalDetails, report, route}: ReportPartic const member = report?.participants?.[accountID]; const details = personalDetails?.[accountID] ?? ({} as PersonalDetails); - const avatar = details.avatar ?? UserUtils.getDefaultAvatar(); + const avatar = details.avatar; const fallbackIcon = details.fallbackIcon ?? ''; const displayName = details.displayName ?? ''; const isCurrentUserAdmin = ReportUtils.isGroupChatAdmin(report, currentUserPersonalDetails?.accountID); @@ -81,7 +80,8 @@ function ReportParticipantDetails({personalDetails, report, route}: ReportPartic diff --git a/src/pages/ReportParticipantsPage.tsx b/src/pages/ReportParticipantsPage.tsx index b69eb88b3244..b7c712913687 100755 --- a/src/pages/ReportParticipantsPage.tsx +++ b/src/pages/ReportParticipantsPage.tsx @@ -12,6 +12,7 @@ import type {DropdownOption, WorkspaceMemberBulkActionType} from '@components/Bu import ConfirmModal from '@components/ConfirmModal'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Expensicons from '@components/Icon/Expensicons'; +import {FallbackAvatar} from '@components/Icon/Expensicons'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; import TableListItem from '@components/SelectionList/TableListItem'; @@ -92,14 +93,14 @@ function ReportParticipantsPage({report, personalDetails, session}: ReportPartic alternateText: formatPhoneNumber(details?.login ?? ''), rightElement: roleBadge, pendingAction: pendingChatMember?.pendingAction, - icons: details?.avatar ? [ + icons: [ { - source: details.avatar, + source: details.avatar ?? FallbackAvatar, name: formatPhoneNumber(details?.login ?? ''), type: CONST.ICON_TYPE_AVATAR, id: accountID, }, - ] : [], + ], }); }); diff --git a/src/pages/RoomMembersPage.tsx b/src/pages/RoomMembersPage.tsx index 3df7a697a454..13f95c12e1fe 100644 --- a/src/pages/RoomMembersPage.tsx +++ b/src/pages/RoomMembersPage.tsx @@ -8,6 +8,7 @@ import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView import Button from '@components/Button'; import ConfirmModal from '@components/ConfirmModal'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import {FallbackAvatar} from '@components/Icon/Expensicons'; import {usePersonalDetails} from '@components/OnyxProvider'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; @@ -36,7 +37,6 @@ import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type {WithReportOrNotFoundProps} from './home/report/withReportOrNotFound'; import withReportOrNotFound from './home/report/withReportOrNotFound'; import SearchInputManager from './workspace/SearchInputManager'; -import {FallbackAvatar} from '@components/Icon/Expensicons'; type RoomMembersPageOnyxProps = { session: OnyxEntry; diff --git a/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx b/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx index e1e390950730..dfd1088075ce 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx +++ b/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx @@ -16,7 +16,6 @@ import * as LoginUtils from '@libs/LoginUtils'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as SuggestionsUtils from '@libs/SuggestionUtils'; -import * as UserUtils from '@libs/UserUtils'; import {isValidRoomName} from '@libs/ValidationUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; diff --git a/src/pages/home/sidebar/ProfileAvatarWithIndicator.js b/src/pages/home/sidebar/ProfileAvatarWithIndicator.js deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/src/pages/home/sidebar/ProfileAvatarWithIndicator.tsx b/src/pages/home/sidebar/ProfileAvatarWithIndicator.tsx index e7726fb89537..b0287efb8990 100644 --- a/src/pages/home/sidebar/ProfileAvatarWithIndicator.tsx +++ b/src/pages/home/sidebar/ProfileAvatarWithIndicator.tsx @@ -5,7 +5,6 @@ import AvatarWithIndicator from '@components/AvatarWithIndicator'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as UserUtils from '@libs/UserUtils'; import ONYXKEYS from '@src/ONYXKEYS'; type ProfileAvatarWithIndicatorProps = { @@ -23,7 +22,8 @@ function ProfileAvatarWithIndicator({isSelected = false}: ProfileAvatarWithIndic From 6b3528a2946c75030220824e9e41c44a211529a5 Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Tue, 16 Apr 2024 11:11:53 +0200 Subject: [PATCH 004/146] Add fixes after review and always generate icons property --- src/components/Avatar.tsx | 10 ++-- src/libs/OptionsListUtils.ts | 28 ++++----- .../report/ReactionList/BaseReactionList.tsx | 58 +++++++++---------- .../home/report/ReportActionItemSingle.tsx | 4 +- 4 files changed, 45 insertions(+), 55 deletions(-) diff --git a/src/components/Avatar.tsx b/src/components/Avatar.tsx index 6937e281da7a..9a76384cc516 100644 --- a/src/components/Avatar.tsx +++ b/src/components/Avatar.tsx @@ -56,7 +56,7 @@ type AvatarProps = { }; function Avatar({ - source, + source: originalSource, imageStyles, iconAdditionalStyles, containerStyles, @@ -77,20 +77,20 @@ function Avatar({ useEffect(() => { setImageError(false); - }, [source]); + }, [originalSource]); const isWorkspace = type === CONST.ICON_TYPE_WORKSPACE; - const isEmptySource = !source; const iconSize = StyleUtils.getAvatarSize(size); const imageStyle: StyleProp = [StyleUtils.getAvatarStyle(size), imageStyles, styles.noBorderRadius]; const iconStyle = imageStyles ? [StyleUtils.getAvatarStyle(size), styles.bgTransparent, imageStyles] : undefined; // We pass the color styles down to the SVG for the workspace and fallback avatar. - const useFallBackAvatar = imageError || isEmptySource || source === Expensicons.FallbackAvatar; + const source = isWorkspace ? originalSource : UserUtils.getAvatar(originalSource, accountID); + const useFallBackAvatar = imageError || !source || source === Expensicons.FallbackAvatar; const fallbackAvatar = isWorkspace ? ReportUtils.getDefaultWorkspaceAvatar(name) : fallbackIcon || Expensicons.FallbackAvatar; const fallbackAvatarTestID = isWorkspace ? ReportUtils.getDefaultWorkspaceAvatarTestID(name) : fallbackIconTestID || 'SvgFallbackAvatar Icon'; - const avatarSource = useFallBackAvatar ? fallbackAvatar : UserUtils.getAvatar(source, accountID) ?? Expensicons.FallbackAvatar; + const avatarSource = useFallBackAvatar ? fallbackAvatar : source; let iconColors; if (isWorkspace) { diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 4ea33940aa9a..4a55d69267dc 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -305,23 +305,17 @@ function getAvatarsForAccountIDs(accountIDs: number[], personalDetails: OnyxEntr reversedDefaultValues[item[1]] = item[0]; }); - return accountIDs - .map((accountID) => { - const login = reversedDefaultValues[accountID] ?? ''; - const userPersonalDetail = personalDetails?.[accountID] ?? {login, accountID, avatar: ''}; + return accountIDs.map((accountID) => { + const login = reversedDefaultValues[accountID] ?? ''; + const userPersonalDetail = personalDetails?.[accountID] ?? {login, accountID, avatar: ''}; - if (!userPersonalDetail.avatar) { - return; - } - - return { - id: accountID, - source: userPersonalDetail.avatar, - type: CONST.ICON_TYPE_AVATAR, - name: userPersonalDetail.login ?? '', - }; - }) - .filter((icon): icon is OnyxCommon.Icon => icon !== undefined); + return { + id: accountID, + source: userPersonalDetail.avatar ?? FallbackAvatar, + type: CONST.ICON_TYPE_AVATAR, + name: userPersonalDetail.login ?? '', + }; + }); } /** @@ -1941,7 +1935,7 @@ function getShareLogOptions(options: OptionList, searchValue = '', betas: Beta[] */ function getIOUConfirmationOptionsFromPayeePersonalDetail(personalDetail: PersonalDetails | EmptyObject, amountText?: string): PayeePersonalDetails { const formattedLogin = LocalePhoneNumber.formatPhoneNumber(personalDetail.login ?? ''); - const icons = personalDetail.avatar ? [{source: personalDetail.avatar, name: personalDetail.login ?? '', type: CONST.ICON_TYPE_AVATAR, id: personalDetail.accountID}] : []; + const icons = [{source: personalDetail.avatar ?? FallbackAvatar, name: personalDetail.login ?? '', type: CONST.ICON_TYPE_AVATAR, id: personalDetail.accountID}]; return { text: PersonalDetailsUtils.getDisplayNameOrDefault(personalDetail, formattedLogin), diff --git a/src/pages/home/report/ReactionList/BaseReactionList.tsx b/src/pages/home/report/ReactionList/BaseReactionList.tsx index 996868b6fd6c..de1735ec97ed 100755 --- a/src/pages/home/report/ReactionList/BaseReactionList.tsx +++ b/src/pages/home/report/ReactionList/BaseReactionList.tsx @@ -3,6 +3,7 @@ import Str from 'expensify-common/lib/str'; import React from 'react'; import {FlatList} from 'react-native'; import type {FlatListProps} from 'react-native'; +import {FallbackAvatar} from '@components/Icon/Expensicons'; import OptionRow from '@components/OptionRow'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; @@ -53,38 +54,33 @@ function BaseReactionList({hasUserReacted = false, users, isVisible = false, emo * so that the sticky headers function properly * */ - const renderItem: FlatListProps['renderItem'] = ({item}) => { - const icons = item.avatar - ? [ - { - id: item.accountID, - source: item.avatar, - name: item.login ?? '', - type: CONST.ICON_TYPE_AVATAR, - }, - ] - : []; - return ( - { - onClose?.(); + const renderItem: FlatListProps['renderItem'] = ({item}) => ( + { + onClose?.(); - Navigation.navigate(ROUTES.PROFILE.getRoute(item.accountID)); - }} - option={{ - reportID: String(item.accountID), - text: Str.removeSMSDomain(item.displayName ?? ''), - alternateText: Str.removeSMSDomain(item.login ?? ''), - participantsList: [item], - icons, - keyForList: item.login ?? String(item.accountID), - }} - /> - ); - }; + Navigation.navigate(ROUTES.PROFILE.getRoute(item.accountID)); + }} + option={{ + reportID: String(item.accountID), + text: Str.removeSMSDomain(item.displayName ?? ''), + alternateText: Str.removeSMSDomain(item.login ?? ''), + participantsList: [item], + icons: [ + { + id: item.accountID, + source: item.avatar ?? FallbackAvatar, + name: item.login ?? '', + type: CONST.ICON_TYPE_AVATAR, + }, + ], + keyForList: item.login ?? String(item.accountID), + }} + /> + ); return ( <> diff --git a/src/pages/home/report/ReportActionItemSingle.tsx b/src/pages/home/report/ReportActionItemSingle.tsx index 4f5c6ef6c602..5fa003af69d0 100644 --- a/src/pages/home/report/ReportActionItemSingle.tsx +++ b/src/pages/home/report/ReportActionItemSingle.tsx @@ -101,7 +101,7 @@ function ReportActionItemSingle({ actorHint = `${delegateDisplayName} (${translate('reportAction.asCopilot')} ${displayName})`; displayName = actorHint; avatarSource = delegateDetails?.avatar; - avatarAccountId = Number(action.delegateAccountID); + avatarAccountId = action.delegateAccountID; } // If this is a report preview, display names and avatars of both people involved @@ -133,7 +133,7 @@ function ReportActionItemSingle({ source: avatarSource ?? FallbackAvatar, type: isWorkspaceActor ? CONST.ICON_TYPE_WORKSPACE : CONST.ICON_TYPE_AVATAR, name: primaryDisplayName ?? '', - id: isWorkspaceActor ? undefined : avatarAccountId, + id: avatarAccountId, }; // Since the display name for a report action message is delivered with the report history as an array of fragments From 445a70e923134e4a19444e84b61e595fff0b6af2 Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Tue, 16 Apr 2024 11:31:39 +0200 Subject: [PATCH 005/146] Fix wrong avatar on workspace invite page --- src/libs/OptionsListUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 4a55d69267dc..2db2d2047c36 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -307,7 +307,7 @@ function getAvatarsForAccountIDs(accountIDs: number[], personalDetails: OnyxEntr return accountIDs.map((accountID) => { const login = reversedDefaultValues[accountID] ?? ''; - const userPersonalDetail = personalDetails?.[accountID] ?? {login, accountID, avatar: ''}; + const userPersonalDetail = personalDetails?.[accountID] ?? {login, accountID}; return { id: accountID, From d42fc2d27e771784d4fa3758688c015a12e67176 Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Thu, 18 Apr 2024 16:10:09 +0200 Subject: [PATCH 006/146] Fixes sometimes getting default avatar when source was undefined --- src/components/Avatar.tsx | 2 +- src/pages/home/report/ReportActionItemSingle.tsx | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/Avatar.tsx b/src/components/Avatar.tsx index 9a76384cc516..99c585dbe672 100644 --- a/src/components/Avatar.tsx +++ b/src/components/Avatar.tsx @@ -87,7 +87,7 @@ function Avatar({ // We pass the color styles down to the SVG for the workspace and fallback avatar. const source = isWorkspace ? originalSource : UserUtils.getAvatar(originalSource, accountID); - const useFallBackAvatar = imageError || !source || source === Expensicons.FallbackAvatar; + const useFallBackAvatar = imageError || !originalSource || !source || source === Expensicons.FallbackAvatar; const fallbackAvatar = isWorkspace ? ReportUtils.getDefaultWorkspaceAvatar(name) : fallbackIcon || Expensicons.FallbackAvatar; const fallbackAvatarTestID = isWorkspace ? ReportUtils.getDefaultWorkspaceAvatarTestID(name) : fallbackIconTestID || 'SvgFallbackAvatar Icon'; const avatarSource = useFallBackAvatar ? fallbackAvatar : source; diff --git a/src/pages/home/report/ReportActionItemSingle.tsx b/src/pages/home/report/ReportActionItemSingle.tsx index 5fa003af69d0..fe4ac414a457 100644 --- a/src/pages/home/report/ReportActionItemSingle.tsx +++ b/src/pages/home/report/ReportActionItemSingle.tsx @@ -93,6 +93,7 @@ function ReportActionItemSingle({ displayName = ReportUtils.getPolicyName(report); actorHint = displayName; avatarSource = ReportUtils.getWorkspaceAvatar(report); + avatarAccountId = undefined; } else if (action?.delegateAccountID && personalDetails[action?.delegateAccountID]) { // We replace the actor's email, name, and avatar with the Copilot manually for now. And only if we have their // details. This will be improved upon when the Copilot feature is implemented. From c23def12232b5847272e0bb3790af5b1910bb27e Mon Sep 17 00:00:00 2001 From: Rodrigo Lino da Costa Date: Thu, 18 Apr 2024 18:09:24 -0300 Subject: [PATCH 007/146] checking if room is from a policy in case the policy type is emtpy --- .../HTMLRenderers/MentionReportRenderer.tsx | 4 +++- .../home/report/ReportActionCompose/ReportActionCompose.tsx | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx index 52377fa20b4d..738987df538b 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx @@ -63,7 +63,9 @@ function MentionReportRenderer({style, tnode, TDefaultRenderer, reports, ...defa const currentReportID = useCurrentReportID(); const currentReport = getReport(currentReportID?.currentReportID); - const isGroupPolicyReport = useMemo(() => (currentReport && !isEmptyObject(currentReport) ? ReportUtils.isGroupPolicy(currentReport) : false), [currentReport]); + + // When we invite someone to a room they don't have the policy type, but we still want them to be able to mention other reports they are members of, so we also check the reportType in case the policyType was empty. + const isGroupPolicyReport = useMemo(() => (currentReport && !isEmptyObject(currentReport) ? (ReportUtils.isGroupPolicy(currentReport) || ReportUtils.isChatRoom(currentReport)) : false), [currentReport]); const mentionDetails = getMentionDetails(htmlAttributeReportID, currentReport, reports, tnode); if (!mentionDetails) { diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx index b9b9025bb02b..1a996498c4c4 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx @@ -342,7 +342,8 @@ function ReportActionCompose({ [], ); - const isGroupPolicyReport = useMemo(() => ReportUtils.isGroupPolicy(report), [report]); + // When we invite someone to a room they don't have the policy type, but we still want them to be able to mention other reports they are members of, so we also check the reportType in case the policyType was empty. + const isGroupPolicyReport = useMemo(() => (ReportUtils.isGroupPolicy(report) || ReportUtils.isChatRoom(report)), [report]); const reportRecipientAcountIDs = ReportUtils.getReportRecipientAccountIDs(report, currentUserPersonalDetails.accountID); const reportRecipient = personalDetails[reportRecipientAcountIDs[0]]; const shouldUseFocusedColor = !isBlockedFromConcierge && !disabled && isFocused; From d99c451ec0034cae9a80057af97b1f394df65e3c Mon Sep 17 00:00:00 2001 From: Rodrigo Lino da Costa Date: Thu, 18 Apr 2024 18:11:45 -0300 Subject: [PATCH 008/146] better comment in rendere --- .../HTMLRenderers/MentionReportRenderer.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx index 738987df538b..5341f4708a74 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx @@ -63,8 +63,8 @@ function MentionReportRenderer({style, tnode, TDefaultRenderer, reports, ...defa const currentReportID = useCurrentReportID(); const currentReport = getReport(currentReportID?.currentReportID); - - // When we invite someone to a room they don't have the policy type, but we still want them to be able to mention other reports they are members of, so we also check the reportType in case the policyType was empty. + + // When we invite someone to a room they don't have the policy type, but we still want them to be able see and click on report mentions, so we also check the reportType in case the policyType was empty. const isGroupPolicyReport = useMemo(() => (currentReport && !isEmptyObject(currentReport) ? (ReportUtils.isGroupPolicy(currentReport) || ReportUtils.isChatRoom(currentReport)) : false), [currentReport]); const mentionDetails = getMentionDetails(htmlAttributeReportID, currentReport, reports, tnode); From 2936567146d0b172c68c20221e45c2e71b341b03 Mon Sep 17 00:00:00 2001 From: Rodrigo Lino da Costa Date: Thu, 18 Apr 2024 18:27:58 -0300 Subject: [PATCH 009/146] prettier --- .../HTMLRenderers/MentionReportRenderer.tsx | 5 ++++- .../home/report/ReportActionCompose/ReportActionCompose.tsx | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx index 5341f4708a74..d1710505c42c 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx @@ -65,7 +65,10 @@ function MentionReportRenderer({style, tnode, TDefaultRenderer, reports, ...defa const currentReport = getReport(currentReportID?.currentReportID); // When we invite someone to a room they don't have the policy type, but we still want them to be able see and click on report mentions, so we also check the reportType in case the policyType was empty. - const isGroupPolicyReport = useMemo(() => (currentReport && !isEmptyObject(currentReport) ? (ReportUtils.isGroupPolicy(currentReport) || ReportUtils.isChatRoom(currentReport)) : false), [currentReport]); + const isGroupPolicyReport = useMemo( + () => (currentReport && !isEmptyObject(currentReport) ? ReportUtils.isGroupPolicy(currentReport) || ReportUtils.isChatRoom(currentReport) : false), + [currentReport], + ); const mentionDetails = getMentionDetails(htmlAttributeReportID, currentReport, reports, tnode); if (!mentionDetails) { diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx index 1a996498c4c4..64038ce19852 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx @@ -343,7 +343,7 @@ function ReportActionCompose({ ); // When we invite someone to a room they don't have the policy type, but we still want them to be able to mention other reports they are members of, so we also check the reportType in case the policyType was empty. - const isGroupPolicyReport = useMemo(() => (ReportUtils.isGroupPolicy(report) || ReportUtils.isChatRoom(report)), [report]); + const isGroupPolicyReport = useMemo(() => ReportUtils.isGroupPolicy(report) || ReportUtils.isChatRoom(report), [report]); const reportRecipientAcountIDs = ReportUtils.getReportRecipientAccountIDs(report, currentUserPersonalDetails.accountID); const reportRecipient = personalDetails[reportRecipientAcountIDs[0]]; const shouldUseFocusedColor = !isBlockedFromConcierge && !disabled && isFocused; From 3ec0542bf73eb70e661b022fda578e51fc9ad278 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Fri, 19 Apr 2024 15:14:43 +0800 Subject: [PATCH 010/146] replace policy.submitsTo with PolicyUtils.getSubmitToAccountID --- src/libs/NextStepUtils.ts | 8 +++++--- src/libs/PolicyUtils.ts | 32 +++++++++++++++++++++++++++++++- src/libs/ReportUtils.ts | 9 +++++---- src/libs/actions/IOU.ts | 2 +- tests/unit/NextStepUtilsTest.ts | 32 ++++++++++++++++++++++++++------ 5 files changed, 68 insertions(+), 15 deletions(-) diff --git a/src/libs/NextStepUtils.ts b/src/libs/NextStepUtils.ts index 3b8fce748f45..66f63ddfd27f 100644 --- a/src/libs/NextStepUtils.ts +++ b/src/libs/NextStepUtils.ts @@ -12,6 +12,7 @@ import type {EmptyObject} from '@src/types/utils/EmptyObject'; import DateUtils from './DateUtils'; import EmailUtils from './EmailUtils'; import * as PersonalDetailsUtils from './PersonalDetailsUtils'; +import * as PolicyUtils from './PolicyUtils'; import * as ReportUtils from './ReportUtils'; let currentUserAccountID = -1; @@ -81,12 +82,13 @@ function buildNextStep( const {policyID = '', ownerAccountID = -1, managerID = -1} = report; const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`] ?? ({} as Policy); - const {submitsTo, harvesting, preventSelfApproval, autoReportingFrequency, autoReportingOffset} = policy; + const {harvesting, preventSelfApproval, autoReportingFrequency, autoReportingOffset} = policy; + const submitToAccountID = PolicyUtils.getSubmitToAccountID(policy, ownerAccountID); const isOwner = currentUserAccountID === ownerAccountID; const isManager = currentUserAccountID === managerID; - const isSelfApproval = currentUserAccountID === submitsTo; + const isSelfApproval = currentUserAccountID === submitToAccountID; const ownerLogin = PersonalDetailsUtils.getLoginsByAccountIDs([ownerAccountID])[0] ?? ''; - const managerDisplayName = isSelfApproval ? 'you' : ReportUtils.getDisplayNameForParticipant(submitsTo) ?? ''; + const managerDisplayName = isSelfApproval ? 'you' : ReportUtils.getDisplayNameForParticipant(submitToAccountID) ?? ''; const type: ReportNextStep['type'] = 'neutral'; let optimisticNextStep: ReportNextStep | null; diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index b4cf4b164a19..40c7f0b6a2e1 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -11,7 +11,7 @@ import {isEmptyObject} from '@src/types/utils/EmptyObject'; import getPolicyIDFromState from './Navigation/getPolicyIDFromState'; import Navigation, {navigationRef} from './Navigation/Navigation'; import type {RootStackParamList, State} from './Navigation/types'; -import {getPersonalDetailByEmail} from './PersonalDetailsUtils'; +import {getAccountIDsByLogins, getLoginsByAccountIDs, getPersonalDetailByEmail} from './PersonalDetailsUtils'; type MemberEmailsToAccountIDs = Record; @@ -311,6 +311,35 @@ function isPolicyFeatureEnabled(policy: OnyxEntry | EmptyObject, feature return Boolean(policy?.[featureName]); } +function getApprovalWorkflow(policy: OnyxEntry | EmptyObject): ValueOf { + if (policy?.type === CONST.POLICY.TYPE.PERSONAL) { + return CONST.POLICY.APPROVAL_MODE.OPTIONAL; + } + + return policy?.approvalMode ?? CONST.POLICY.APPROVAL_MODE.ADVANCED; +} + +function getDefaultApprover(policy: OnyxEntry | EmptyObject): string { + return policy?.approver ?? policy?.owner ?? ''; +} + +function getSubmitToAccountID(policy: OnyxEntry | EmptyObject, employeeAccountID: number): number { + const employeeLogin = getLoginsByAccountIDs([employeeAccountID])[0]; + const defaultApprover = getDefaultApprover(policy); + // For policy using the optional or basic workflow, the manager is the policy default approver. + if (([CONST.POLICY.APPROVAL_MODE.OPTIONAL, CONST.POLICY.APPROVAL_MODE.BASIC] as Array>).includes(getApprovalWorkflow(policy))) { + return getAccountIDsByLogins([defaultApprover])[0]; + } + + const employee = policy?.employeeList?.[employeeLogin]; + + if (!employee) { + return -1; + } + + return getAccountIDsByLogins([employee.submitsTo ?? defaultApprover])[0]; +} + /** * Get the currently selected policy ID stored in the navigation state. */ @@ -357,6 +386,7 @@ export { getTaxByID, hasPolicyCategoriesError, getPolicyIDFromNavigationState, + getSubmitToAccountID, }; export type {MemberEmailsToAccountIDs}; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index a15e1937dbe2..6c4dcfc71a57 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -3430,9 +3430,10 @@ function buildOptimisticExpenseReport(chatReportID: string, policyID: string, pa lastVisibleActionCreated: DateUtils.getDBTime(), }; + const submitToAccountID = PolicyUtils.getSubmitToAccountID(policy, payeeAccountID); // The account defined in the policy submitsTo field is the approver/ manager for this report - if (policy?.submitsTo) { - expenseReport.managerID = policy.submitsTo; + if (submitToAccountID) { + expenseReport.managerID = submitToAccountID; } const titleReportField = getTitleReportField(getReportFieldsByPolicyID(policyID) ?? {}); @@ -5870,9 +5871,9 @@ function isAllowedToApproveExpenseReport(report: OnyxEntry, approverAcco function isAllowedToSubmitDraftExpenseReport(report: OnyxEntry): boolean { const policy = getPolicy(report?.policyID); - const {submitsTo} = policy; + const submitToAccountID = PolicyUtils.getSubmitToAccountID(policy, report?.ownerAccountID ?? -1); - return isAllowedToApproveExpenseReport(report, submitsTo); + return isAllowedToApproveExpenseReport(report, submitToAccountID); } /** diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 7fed15335e2a..e74d2978d771 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -5612,7 +5612,7 @@ function submitReport(expenseReport: OnyxTypes.Report) { const parameters: SubmitReportParams = { reportID: expenseReport.reportID, - managerAccountID: policy.submitsTo ?? expenseReport.managerID, + managerAccountID: PolicyUtils.getSubmitToAccountID(policy, expenseReport.ownerAccountID ?? -1) ?? expenseReport.managerID, reportActionID: optimisticSubmittedReportAction.reportActionID, }; diff --git a/tests/unit/NextStepUtilsTest.ts b/tests/unit/NextStepUtilsTest.ts index 072a06748da9..7af6d35302bf 100644 --- a/tests/unit/NextStepUtilsTest.ts +++ b/tests/unit/NextStepUtilsTest.ts @@ -22,7 +22,6 @@ describe('libs/NextStepUtils', () => { // Important props id: policyID, owner: currentUserEmail, - submitsTo: currentUserAccountID, harvesting: { enabled: false, }, @@ -51,6 +50,11 @@ describe('libs/NextStepUtils', () => { login: strangeEmail, avatar: '', }, + [currentUserAccountID]: { + accountID: currentUserAccountID, + login: currentUserEmail, + avatar: '', + }, }, ...policyCollectionDataSet, }).then(waitForBatchedUpdates); @@ -341,10 +345,14 @@ describe('libs/NextStepUtils', () => { ]; return Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, { - submitsTo: currentUserAccountID, preventSelfApproval: true, + employeeList: { + [currentUserEmail]: { + submitsTo: currentUserEmail, + }, + }, }).then(() => { - const result = NextStepUtils.buildNextStep(report, CONST.REPORT.STATUS_NUM.OPEN); + const result = NextStepUtils.buildNextStep(report, CONST.REPORT.STATUS_NUM.OPEN, undefined, true); expect(result).toMatchObject(optimisticNextStep); }); @@ -403,7 +411,11 @@ describe('libs/NextStepUtils', () => { ]; return Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, { - submitsTo: strangeAccountID, + employeeList: { + [currentUserEmail]: { + submitsTo: strangeEmail, + }, + }, }).then(() => { const result = NextStepUtils.buildNextStep(report, CONST.REPORT.STATUS_NUM.SUBMITTED); @@ -438,9 +450,17 @@ describe('libs/NextStepUtils', () => { }, ]; - const result = NextStepUtils.buildNextStep(report, CONST.REPORT.STATUS_NUM.SUBMITTED); + return Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, { + employeeList: { + [strangeEmail]: { + submitsTo: currentUserEmail, + }, + }, + }).then(() => { + const result = NextStepUtils.buildNextStep(report, CONST.REPORT.STATUS_NUM.SUBMITTED); - expect(result).toMatchObject(optimisticNextStep); + expect(result).toMatchObject(optimisticNextStep); + }); }); test('submit and close approval mode', () => { From 28e8945cc5138ec1e91d2b5b81b28ed43272a373 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Fri, 19 Apr 2024 15:38:33 +0800 Subject: [PATCH 011/146] remove code that is added when debugging --- tests/unit/NextStepUtilsTest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/NextStepUtilsTest.ts b/tests/unit/NextStepUtilsTest.ts index 7af6d35302bf..65ce7352b6e6 100644 --- a/tests/unit/NextStepUtilsTest.ts +++ b/tests/unit/NextStepUtilsTest.ts @@ -352,7 +352,7 @@ describe('libs/NextStepUtils', () => { }, }, }).then(() => { - const result = NextStepUtils.buildNextStep(report, CONST.REPORT.STATUS_NUM.OPEN, undefined, true); + const result = NextStepUtils.buildNextStep(report, CONST.REPORT.STATUS_NUM.OPEN); expect(result).toMatchObject(optimisticNextStep); }); From 674af05630be8c35cba14235d0d7393462a99dbb Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Fri, 19 Apr 2024 09:47:29 +0200 Subject: [PATCH 012/146] add more fixes after review --- src/components/Avatar.tsx | 2 +- src/libs/UserUtils.ts | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/components/Avatar.tsx b/src/components/Avatar.tsx index 99c585dbe672..9a76384cc516 100644 --- a/src/components/Avatar.tsx +++ b/src/components/Avatar.tsx @@ -87,7 +87,7 @@ function Avatar({ // We pass the color styles down to the SVG for the workspace and fallback avatar. const source = isWorkspace ? originalSource : UserUtils.getAvatar(originalSource, accountID); - const useFallBackAvatar = imageError || !originalSource || !source || source === Expensicons.FallbackAvatar; + const useFallBackAvatar = imageError || !source || source === Expensicons.FallbackAvatar; const fallbackAvatar = isWorkspace ? ReportUtils.getDefaultWorkspaceAvatar(name) : fallbackIcon || Expensicons.FallbackAvatar; const fallbackAvatarTestID = isWorkspace ? ReportUtils.getDefaultWorkspaceAvatarTestID(name) : fallbackIconTestID || 'SvgFallbackAvatar Icon'; const avatarSource = useFallBackAvatar ? fallbackAvatar : source; diff --git a/src/libs/UserUtils.ts b/src/libs/UserUtils.ts index 205d84fff519..03066b21fa71 100644 --- a/src/libs/UserUtils.ts +++ b/src/libs/UserUtils.ts @@ -122,7 +122,7 @@ function getDefaultAvatarURL(accountID: string | number = ''): string { } /** - * Given a user's avatar path, returns true if user doesn't have an avatar or if URL points to a default avatar + * Given a user's avatar path, returns true if URL points to a default avatar, false otherwise * @param avatarSource - the avatar source from user's personalDetails */ function isDefaultAvatar(avatarSource?: AvatarSource): avatarSource is string | undefined { @@ -137,11 +137,6 @@ function isDefaultAvatar(avatarSource?: AvatarSource): avatarSource is string | } } - if (!avatarSource) { - // If source is undefined, we should also use a default avatar - return true; - } - return false; } From bae212fd87ccb52eb6a1a12f82f9b5b417c2dffc Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Fri, 19 Apr 2024 22:52:53 +0800 Subject: [PATCH 013/146] tidying up and update comment --- src/libs/PolicyUtils.ts | 2 +- src/libs/ReportUtils.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 40c7f0b6a2e1..225e422a0ffc 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -326,13 +326,13 @@ function getDefaultApprover(policy: OnyxEntry | EmptyObject): string { function getSubmitToAccountID(policy: OnyxEntry | EmptyObject, employeeAccountID: number): number { const employeeLogin = getLoginsByAccountIDs([employeeAccountID])[0]; const defaultApprover = getDefaultApprover(policy); + // For policy using the optional or basic workflow, the manager is the policy default approver. if (([CONST.POLICY.APPROVAL_MODE.OPTIONAL, CONST.POLICY.APPROVAL_MODE.BASIC] as Array>).includes(getApprovalWorkflow(policy))) { return getAccountIDsByLogins([defaultApprover])[0]; } const employee = policy?.employeeList?.[employeeLogin]; - if (!employee) { return -1; } diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 6c4dcfc71a57..64aceba13ac4 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -3430,8 +3430,8 @@ function buildOptimisticExpenseReport(chatReportID: string, policyID: string, pa lastVisibleActionCreated: DateUtils.getDBTime(), }; + // Get the approver/manager for this report to properly display the optimistic data const submitToAccountID = PolicyUtils.getSubmitToAccountID(policy, payeeAccountID); - // The account defined in the policy submitsTo field is the approver/ manager for this report if (submitToAccountID) { expenseReport.managerID = submitToAccountID; } From 2e085f3cadee326deb24fa7d2c97391092278e0b Mon Sep 17 00:00:00 2001 From: Rodrigo Lino da Costa Date: Fri, 19 Apr 2024 14:08:57 -0300 Subject: [PATCH 014/146] checking only the policyID instead of checking types --- .../HTMLRenderers/MentionReportRenderer.tsx | 4 ++-- .../home/report/ReportActionCompose/ReportActionCompose.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx index d1710505c42c..53828e1690fa 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx @@ -64,9 +64,9 @@ function MentionReportRenderer({style, tnode, TDefaultRenderer, reports, ...defa const currentReportID = useCurrentReportID(); const currentReport = getReport(currentReportID?.currentReportID); - // When we invite someone to a room they don't have the policy type, but we still want them to be able see and click on report mentions, so we also check the reportType in case the policyType was empty. + // When we invite someone to a room they don't have the policy object, but we still want them to be able to see and click on report mentions, so we only check if the policyID in the report is from a workspace const isGroupPolicyReport = useMemo( - () => (currentReport && !isEmptyObject(currentReport) ? ReportUtils.isGroupPolicy(currentReport) || ReportUtils.isChatRoom(currentReport) : false), + () => (currentReport && !isEmptyObject(currentReport) ? !!currentReport.policyID && currentReport.policyID !== CONST.POLICY.ID_FAKE : false), [currentReport], ); diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx index 64038ce19852..75d0c703b5b1 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx @@ -342,8 +342,8 @@ function ReportActionCompose({ [], ); - // When we invite someone to a room they don't have the policy type, but we still want them to be able to mention other reports they are members of, so we also check the reportType in case the policyType was empty. - const isGroupPolicyReport = useMemo(() => ReportUtils.isGroupPolicy(report) || ReportUtils.isChatRoom(report), [report]); + // When we invite someone to a room they don't have the policy object, but we still want them to be able to mention other reports they are members of, so we only check if the policyID in the report is from a workspace + const isGroupPolicyReport = useMemo(() => !!report?.policyID && report.policyID !== CONST.POLICY.ID_FAKE, [report]); const reportRecipientAcountIDs = ReportUtils.getReportRecipientAccountIDs(report, currentUserPersonalDetails.accountID); const reportRecipient = personalDetails[reportRecipientAcountIDs[0]]; const shouldUseFocusedColor = !isBlockedFromConcierge && !disabled && isFocused; From 6caa78cda2c71d805647936a5aed4ed7a05ef7c9 Mon Sep 17 00:00:00 2001 From: Rodrigo Lino da Costa Date: Fri, 19 Apr 2024 14:39:45 -0300 Subject: [PATCH 015/146] Removed unused import --- .../HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx index 53828e1690fa..6dd7e0b0666b 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx @@ -11,7 +11,6 @@ import useCurrentReportID from '@hooks/useCurrentReportID'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import {getReport} from '@libs/ReportUtils'; -import * as ReportUtils from '@libs/ReportUtils'; import Navigation from '@navigation/Navigation'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; From 23497bf11664532f62065caf20f2d584124e8e39 Mon Sep 17 00:00:00 2001 From: Rodrigo Lino da Costa Date: Fri, 19 Apr 2024 22:50:04 -0300 Subject: [PATCH 016/146] Using the reportID from the HTML in case the report doesn't have a reportID --- .../HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx index 6dd7e0b0666b..9d8d82ff5042 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx @@ -36,7 +36,7 @@ const getMentionDetails = (htmlAttributeReportID: string, currentReport: OnyxEnt if (!isEmpty(htmlAttributeReportID)) { const report = getReport(htmlAttributeReportID); - reportID = report?.reportID ?? undefined; + reportID = report?.reportID ?? htmlAttributeReportID; mentionDisplayText = removeLeadingLTRAndHash(report?.reportName ?? report?.displayName ?? htmlAttributeReportID); // Get mention details from name inside tnode } else if ('data' in tnode && !isEmptyObject(tnode.data)) { From 0da9f7ff607bc223ec5ef80a7497678593ad0d52 Mon Sep 17 00:00:00 2001 From: Yauheni Date: Sun, 21 Apr 2024 01:09:21 +0200 Subject: [PATCH 017/146] Fix bug with Not here error appears briefly when enabling features --- src/components/Switch.tsx | 14 ++++-- src/libs/actions/Policy.ts | 4 +- .../FeatureEnabledAccessOrNotFoundWrapper.tsx | 30 ++++++++++- src/pages/workspace/WorkspaceInitialPage.tsx | 50 ++++++++++++++++--- 4 files changed, 86 insertions(+), 12 deletions(-) diff --git a/src/components/Switch.tsx b/src/components/Switch.tsx index 1693bafe323d..30616e64755d 100644 --- a/src/components/Switch.tsx +++ b/src/components/Switch.tsx @@ -1,5 +1,5 @@ import React, {useEffect, useRef} from 'react'; -import {Animated} from 'react-native'; +import {Animated, InteractionManager} from 'react-native'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useNativeDriver from '@libs/useNativeDriver'; @@ -44,8 +44,16 @@ function Switch({isOn, onToggle, accessibilityLabel, disabled}: SwitchProps) { onToggle(!isOn)} - onLongPress={() => onToggle(!isOn)} + onPress={() => { + InteractionManager.runAfterInteractions(() => { + onToggle(!isOn); + }); + }} + onLongPress={() => { + InteractionManager.runAfterInteractions(() => { + onToggle(!isOn); + }); + }} role={CONST.ROLE.SWITCH} aria-checked={isOn} accessibilityLabel={accessibilityLabel} diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index cd375b580d85..0c6c371c0f09 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -3806,7 +3806,9 @@ function navigateWhenEnableFeature(policyID: string, featureRoute: Route) { new Promise((resolve) => { resolve(); }).then(() => { - Navigation.navigate(featureRoute); + requestAnimationFrame(() => { + Navigation.navigate(featureRoute); + }); }); } diff --git a/src/pages/workspace/FeatureEnabledAccessOrNotFoundWrapper.tsx b/src/pages/workspace/FeatureEnabledAccessOrNotFoundWrapper.tsx index 3bcdc1fe3303..a1337a38795a 100644 --- a/src/pages/workspace/FeatureEnabledAccessOrNotFoundWrapper.tsx +++ b/src/pages/workspace/FeatureEnabledAccessOrNotFoundWrapper.tsx @@ -1,9 +1,11 @@ /* eslint-disable rulesdir/no-negated-variables */ -import React, {useEffect} from 'react'; +import {useIsFocused} from '@react-navigation/native'; +import React, {useEffect, useState} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; +import useNetwork from '@hooks/useNetwork'; import Navigation from '@libs/Navigation/Navigation'; import * as PolicyUtils from '@libs/PolicyUtils'; import * as Policy from '@userActions/Policy'; @@ -35,7 +37,31 @@ type FeatureEnabledAccessOrNotFoundComponentProps = FeatureEnabledAccessOrNotFou function FeatureEnabledAccessOrNotFoundComponent(props: FeatureEnabledAccessOrNotFoundComponentProps) { const isPolicyIDInRoute = !!props.policyID?.length; const shouldShowFullScreenLoadingIndicator = props.isLoadingReportData !== false && (!Object.entries(props.policy ?? {}).length || !props.policy?.id); - const shouldShowNotFoundPage = isEmptyObject(props.policy) || !props.policy?.id || !PolicyUtils.isPolicyFeatureEnabled(props.policy, props.featureName); + const isFeatureEnabled = PolicyUtils.isPolicyFeatureEnabled(props.policy, props.featureName); + const [isPolicyFeatureEnabled, setIsPolicyFeatureEnabled] = useState(isFeatureEnabled); + const shouldShowNotFoundPage = isEmptyObject(props.policy) || !props.policy?.id || !isPolicyFeatureEnabled; + const pendingField = props.policy?.pendingFields?.[props.featureName]; + const [isFeatureScreenOpen, setIsFeatureScreenOpen] = useState(false); + const isFocused = useIsFocused(); + const {isOffline} = useNetwork(); + + useEffect(() => { + if (!isFeatureScreenOpen && isFocused) { + setIsFeatureScreenOpen(true); + setIsPolicyFeatureEnabled(isFeatureEnabled); + return; + } + if (!isFocused) { + setIsFeatureScreenOpen(false); + return; + } + setIsPolicyFeatureEnabled((isPrevFeatureEnabled) => { + if (!pendingField || isOffline) { + return isFeatureEnabled; + } + return isPrevFeatureEnabled; + }); + }, [isFocused, pendingField, isOffline, isFeatureEnabled, isFeatureScreenOpen]); useEffect(() => { if (!isPolicyIDInRoute || !isEmptyObject(props.policy)) { diff --git a/src/pages/workspace/WorkspaceInitialPage.tsx b/src/pages/workspace/WorkspaceInitialPage.tsx index 2f32034391a5..0eb5a38c3270 100644 --- a/src/pages/workspace/WorkspaceInitialPage.tsx +++ b/src/pages/workspace/WorkspaceInitialPage.tsx @@ -14,6 +14,7 @@ import OfflineWithFeedback from '@components/OfflineWithFeedback'; import ScreenWrapper from '@components/ScreenWrapper'; import ScrollView from '@components/ScrollView'; import useLocalize from '@hooks/useLocalize'; +import useNetwork from '@hooks/useNetwork'; import usePermissions from '@hooks/usePermissions'; import usePrevious from '@hooks/usePrevious'; import useSingleExecution from '@hooks/useSingleExecution'; @@ -33,6 +34,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; +import type {PolicyFeatureName} from '@src/types/onyx/Policy'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type IconAsset from '@src/types/utils/IconAsset'; import type {WithPolicyAndFullscreenLoadingProps} from './withPolicyAndFullscreenLoading'; @@ -86,6 +88,20 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, reimbursementAcc const activeRoute = useNavigationState(getTopmostWorkspacesCentralPaneName); const {translate} = useLocalize(); const {canUseAccountingIntegrations} = usePermissions(); + const {isOffline} = useNetwork(); + + const prevPendingFields = usePrevious(policy?.pendingFields); + const policyFeatureStates = useMemo( + () => ({ + [CONST.POLICY.MORE_FEATURES.ARE_DISTANCE_RATES_ENABLED]: policy?.areDistanceRatesEnabled, + [CONST.POLICY.MORE_FEATURES.ARE_WORKFLOWS_ENABLED]: policy?.areWorkflowsEnabled, + [CONST.POLICY.MORE_FEATURES.ARE_CATEGORIES_ENABLED]: policy?.areCategoriesEnabled, + [CONST.POLICY.MORE_FEATURES.ARE_TAGS_ENABLED]: policy?.areTagsEnabled, + [CONST.POLICY.MORE_FEATURES.ARE_TAXES_ENABLED]: policy?.tax?.trackingEnabled, + [CONST.POLICY.MORE_FEATURES.ARE_CONNECTIONS_ENABLED]: policy?.areConnectionsEnabled, + }), + [policy], + ) as Record; const policyID = policy?.id ?? ''; const policyName = policy?.name ?? ''; @@ -122,6 +138,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, reimbursementAcc const shouldShowProtectedItems = PolicyUtils.isPolicyAdmin(policy); const isPaidGroupPolicy = PolicyUtils.isPaidGroupPolicy(policy); const isFreeGroupPolicy = PolicyUtils.isFreeGroupPolicy(policy); + const [featureStates, setFeatureStates] = useState(policyFeatureStates); const protectedFreePolicyMenuItems: WorkspaceMenuItem[] = [ { @@ -167,7 +184,28 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, reimbursementAcc const protectedCollectPolicyMenuItems: WorkspaceMenuItem[] = []; - if (policy?.areDistanceRatesEnabled) { + useEffect(() => { + setFeatureStates((currentFeatureStates) => { + const newFeatureStates = {} as Record; + const keys = Object.keys(policy?.pendingFields ?? {}) as PolicyFeatureName[]; + keys.forEach((key) => { + const isFeatureEnabled = PolicyUtils.isPolicyFeatureEnabled(policy, key); + if (prevPendingFields?.[key] !== policy?.pendingFields?.[key] || isOffline || !policy?.pendingFields?.[key]) { + newFeatureStates[key] = isFeatureEnabled; + + return; + } + + newFeatureStates[key] = currentFeatureStates[key]; + }); + return { + ...policyFeatureStates, + ...newFeatureStates, + }; + }); + }, [policy, isOffline, policyFeatureStates, prevPendingFields]); + + if (featureStates?.[CONST.POLICY.MORE_FEATURES.ARE_DISTANCE_RATES_ENABLED]) { protectedCollectPolicyMenuItems.push({ translationKey: 'workspace.common.distanceRates', icon: Expensicons.Car, @@ -176,7 +214,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, reimbursementAcc }); } - if (policy?.areWorkflowsEnabled) { + if (featureStates?.[CONST.POLICY.MORE_FEATURES.ARE_WORKFLOWS_ENABLED]) { protectedCollectPolicyMenuItems.push({ translationKey: 'workspace.common.workflows', icon: Expensicons.Workflows, @@ -186,7 +224,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, reimbursementAcc }); } - if (policy?.areCategoriesEnabled) { + if (featureStates?.[CONST.POLICY.MORE_FEATURES.ARE_CATEGORIES_ENABLED]) { protectedCollectPolicyMenuItems.push({ translationKey: 'workspace.common.categories', icon: Expensicons.Folder, @@ -196,7 +234,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, reimbursementAcc }); } - if (policy?.areTagsEnabled) { + if (featureStates?.[CONST.POLICY.MORE_FEATURES.ARE_TAGS_ENABLED]) { protectedCollectPolicyMenuItems.push({ translationKey: 'workspace.common.tags', icon: Expensicons.Tag, @@ -205,7 +243,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, reimbursementAcc }); } - if (policy?.tax?.trackingEnabled) { + if (featureStates?.[CONST.POLICY.MORE_FEATURES.ARE_TAXES_ENABLED]) { protectedCollectPolicyMenuItems.push({ translationKey: 'workspace.common.taxes', icon: Expensicons.Tax, @@ -215,7 +253,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, reimbursementAcc }); } - if (policy?.areConnectionsEnabled && canUseAccountingIntegrations) { + if (featureStates?.[CONST.POLICY.MORE_FEATURES.ARE_CONNECTIONS_ENABLED] && canUseAccountingIntegrations) { protectedCollectPolicyMenuItems.push({ translationKey: 'workspace.common.accounting', icon: Expensicons.Sync, From 3f893fdacbedb9d1d446c43f5b966774b03a1b03 Mon Sep 17 00:00:00 2001 From: Yauheni Date: Sun, 21 Apr 2024 01:15:10 +0200 Subject: [PATCH 018/146] Refactor switch --- src/components/Switch.tsx | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/components/Switch.tsx b/src/components/Switch.tsx index 30616e64755d..e6ab9a9f1c1c 100644 --- a/src/components/Switch.tsx +++ b/src/components/Switch.tsx @@ -32,6 +32,12 @@ function Switch({isOn, onToggle, accessibilityLabel, disabled}: SwitchProps) { const offsetX = useRef(new Animated.Value(isOn ? OFFSET_X.ON : OFFSET_X.OFF)); const theme = useTheme(); + const handleSwitchPress = () => { + InteractionManager.runAfterInteractions(() => { + onToggle(!isOn); + }); + }; + useEffect(() => { Animated.timing(offsetX.current, { toValue: isOn ? OFFSET_X.ON : OFFSET_X.OFF, @@ -44,16 +50,8 @@ function Switch({isOn, onToggle, accessibilityLabel, disabled}: SwitchProps) { { - InteractionManager.runAfterInteractions(() => { - onToggle(!isOn); - }); - }} - onLongPress={() => { - InteractionManager.runAfterInteractions(() => { - onToggle(!isOn); - }); - }} + onPress={handleSwitchPress} + onLongPress={handleSwitchPress} role={CONST.ROLE.SWITCH} aria-checked={isOn} accessibilityLabel={accessibilityLabel} From 0744c28555ce75b949505f68569472b107ae0d97 Mon Sep 17 00:00:00 2001 From: Yauheni Date: Sun, 21 Apr 2024 01:23:19 +0200 Subject: [PATCH 019/146] Refactor changes in FeatureEnabledAccessOrNotFoundComponent --- .../FeatureEnabledAccessOrNotFoundWrapper.tsx | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/pages/workspace/FeatureEnabledAccessOrNotFoundWrapper.tsx b/src/pages/workspace/FeatureEnabledAccessOrNotFoundWrapper.tsx index a1337a38795a..f3974b353a5c 100644 --- a/src/pages/workspace/FeatureEnabledAccessOrNotFoundWrapper.tsx +++ b/src/pages/workspace/FeatureEnabledAccessOrNotFoundWrapper.tsx @@ -41,27 +41,17 @@ function FeatureEnabledAccessOrNotFoundComponent(props: FeatureEnabledAccessOrNo const [isPolicyFeatureEnabled, setIsPolicyFeatureEnabled] = useState(isFeatureEnabled); const shouldShowNotFoundPage = isEmptyObject(props.policy) || !props.policy?.id || !isPolicyFeatureEnabled; const pendingField = props.policy?.pendingFields?.[props.featureName]; - const [isFeatureScreenOpen, setIsFeatureScreenOpen] = useState(false); const isFocused = useIsFocused(); const {isOffline} = useNetwork(); useEffect(() => { - if (!isFeatureScreenOpen && isFocused) { - setIsFeatureScreenOpen(true); - setIsPolicyFeatureEnabled(isFeatureEnabled); - return; - } if (!isFocused) { - setIsFeatureScreenOpen(false); return; } - setIsPolicyFeatureEnabled((isPrevFeatureEnabled) => { - if (!pendingField || isOffline) { - return isFeatureEnabled; - } - return isPrevFeatureEnabled; - }); - }, [isFocused, pendingField, isOffline, isFeatureEnabled, isFeatureScreenOpen]); + if (!pendingField || isOffline) { + setIsPolicyFeatureEnabled(isFeatureEnabled); + } + }, [isFocused, pendingField, isOffline, isFeatureEnabled]); useEffect(() => { if (!isPolicyIDInRoute || !isEmptyObject(props.policy)) { From 224c9eab5e50f21e42ceb86893d17798733f70a3 Mon Sep 17 00:00:00 2001 From: Yauheni Date: Sun, 21 Apr 2024 01:25:42 +0200 Subject: [PATCH 020/146] Refactor changes in FeatureEnabledAccessOrNotFoundComponent x2 --- .../workspace/FeatureEnabledAccessOrNotFoundWrapper.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/pages/workspace/FeatureEnabledAccessOrNotFoundWrapper.tsx b/src/pages/workspace/FeatureEnabledAccessOrNotFoundWrapper.tsx index f3974b353a5c..eebdb3a280db 100644 --- a/src/pages/workspace/FeatureEnabledAccessOrNotFoundWrapper.tsx +++ b/src/pages/workspace/FeatureEnabledAccessOrNotFoundWrapper.tsx @@ -35,15 +35,17 @@ type FeatureEnabledAccessOrNotFoundComponentProps = FeatureEnabledAccessOrNotFou }; function FeatureEnabledAccessOrNotFoundComponent(props: FeatureEnabledAccessOrNotFoundComponentProps) { + const pendingField = props.policy?.pendingFields?.[props.featureName]; const isPolicyIDInRoute = !!props.policyID?.length; - const shouldShowFullScreenLoadingIndicator = props.isLoadingReportData !== false && (!Object.entries(props.policy ?? {}).length || !props.policy?.id); const isFeatureEnabled = PolicyUtils.isPolicyFeatureEnabled(props.policy, props.featureName); + const [isPolicyFeatureEnabled, setIsPolicyFeatureEnabled] = useState(isFeatureEnabled); - const shouldShowNotFoundPage = isEmptyObject(props.policy) || !props.policy?.id || !isPolicyFeatureEnabled; - const pendingField = props.policy?.pendingFields?.[props.featureName]; const isFocused = useIsFocused(); const {isOffline} = useNetwork(); + const shouldShowNotFoundPage = isEmptyObject(props.policy) || !props.policy?.id || !isPolicyFeatureEnabled; + const shouldShowFullScreenLoadingIndicator = props.isLoadingReportData !== false && (!Object.entries(props.policy ?? {}).length || !props.policy?.id); + useEffect(() => { if (!isFocused) { return; From b72180c831990875ee5991cccfeb84312f47f0a2 Mon Sep 17 00:00:00 2001 From: Yauheni Date: Sun, 21 Apr 2024 12:18:10 +0200 Subject: [PATCH 021/146] Add clearPendingFieldsForMoreFeatures and refactor WorkspaceInitialPage --- src/libs/actions/Policy.ts | 14 +++++++++++++ src/pages/workspace/WorkspaceInitialPage.tsx | 22 ++++++++++---------- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 0c6c371c0f09..30027f626901 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -2382,6 +2382,19 @@ function openPolicyWorkflowsPage(policyID: string) { API.read(READ_COMMANDS.OPEN_POLICY_WORKFLOWS_PAGE, params, onyxData); } +function clearPendingFieldsForMoreFeatures(policyID: string) { + Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, { + pendingFields: { + areDistanceRatesEnabled: null, + areWorkflowsEnabled: null, + areCategoriesEnabled: null, + areTagsEnabled: null, + tax: null, + areConnectionsEnabled: null, + }, + }); +} + function setPolicyIDForReimburseView(policyID: string) { Onyx.merge(ONYXKEYS.WORKSPACE_RATE_AND_UNIT, {policyID, rate: null, unit: null}); } @@ -5075,6 +5088,7 @@ export { updatePolicyDistanceRateValue, setPolicyDistanceRatesEnabled, deletePolicyDistanceRates, + clearPendingFieldsForMoreFeatures, }; export type {NewCustomUnit}; diff --git a/src/pages/workspace/WorkspaceInitialPage.tsx b/src/pages/workspace/WorkspaceInitialPage.tsx index 0eb5a38c3270..a11e723145c5 100644 --- a/src/pages/workspace/WorkspaceInitialPage.tsx +++ b/src/pages/workspace/WorkspaceInitialPage.tsx @@ -73,6 +73,8 @@ type WorkspaceInitialPageOnyxProps = { type WorkspaceInitialPageProps = WithPolicyAndFullscreenLoadingProps & WorkspaceInitialPageOnyxProps & StackScreenProps; +type PolicyFeatureStates = Record; + function dismissError(policyID: string) { PolicyUtils.goBackFromInvalidPolicy(); Policy.removeWorkspace(policyID); @@ -101,7 +103,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, reimbursementAcc [CONST.POLICY.MORE_FEATURES.ARE_CONNECTIONS_ENABLED]: policy?.areConnectionsEnabled, }), [policy], - ) as Record; + ) as PolicyFeatureStates; const policyID = policy?.id ?? ''; const policyName = policy?.name ?? ''; @@ -125,6 +127,10 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, reimbursementAcc setIsCurrencyModalOpen(false); }, [policy?.outputCurrency, isCurrencyModalOpen]); + useEffect(() => { + Policy.clearPendingFieldsForMoreFeatures(policyID); + }, [policyID]); + /** Call update workspace currency and hide the modal */ const confirmCurrencyChangeAndHideModal = useCallback(() => { Policy.updateGeneralSettings(policyID, policyName, CONST.CURRENCY.USD); @@ -186,17 +192,11 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, reimbursementAcc useEffect(() => { setFeatureStates((currentFeatureStates) => { - const newFeatureStates = {} as Record; - const keys = Object.keys(policy?.pendingFields ?? {}) as PolicyFeatureName[]; - keys.forEach((key) => { + const newFeatureStates = {} as PolicyFeatureStates; + (Object.keys(policy?.pendingFields ?? {}) as PolicyFeatureName[]).forEach((key) => { const isFeatureEnabled = PolicyUtils.isPolicyFeatureEnabled(policy, key); - if (prevPendingFields?.[key] !== policy?.pendingFields?.[key] || isOffline || !policy?.pendingFields?.[key]) { - newFeatureStates[key] = isFeatureEnabled; - - return; - } - - newFeatureStates[key] = currentFeatureStates[key]; + newFeatureStates[key] = + prevPendingFields?.[key] !== policy?.pendingFields?.[key] || isOffline || !policy?.pendingFields?.[key] ? isFeatureEnabled : currentFeatureStates[key]; }); return { ...policyFeatureStates, From 578b27e5e75cf559e2410d5b1eb1ddafe5caa530 Mon Sep 17 00:00:00 2001 From: Yauheni Date: Sun, 21 Apr 2024 15:36:06 +0200 Subject: [PATCH 022/146] Update condition for clearPendingFieldsForMoreFeatures --- src/pages/workspace/WorkspaceInitialPage.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/pages/workspace/WorkspaceInitialPage.tsx b/src/pages/workspace/WorkspaceInitialPage.tsx index a11e723145c5..9343432a4836 100644 --- a/src/pages/workspace/WorkspaceInitialPage.tsx +++ b/src/pages/workspace/WorkspaceInitialPage.tsx @@ -128,7 +128,11 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, reimbursementAcc }, [policy?.outputCurrency, isCurrencyModalOpen]); useEffect(() => { + if (isOffline) { + return; + } Policy.clearPendingFieldsForMoreFeatures(policyID); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [policyID]); /** Call update workspace currency and hide the modal */ From cf28c35257980e6d9ad20791f3f7aeeb1efbbab5 Mon Sep 17 00:00:00 2001 From: Yauheni Date: Sun, 21 Apr 2024 17:33:20 +0200 Subject: [PATCH 023/146] Remove clearPendingFieldsForMoreFeatures --- src/libs/actions/Policy.ts | 14 -------------- src/pages/workspace/WorkspaceInitialPage.tsx | 8 -------- 2 files changed, 22 deletions(-) diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 30027f626901..0c6c371c0f09 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -2382,19 +2382,6 @@ function openPolicyWorkflowsPage(policyID: string) { API.read(READ_COMMANDS.OPEN_POLICY_WORKFLOWS_PAGE, params, onyxData); } -function clearPendingFieldsForMoreFeatures(policyID: string) { - Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, { - pendingFields: { - areDistanceRatesEnabled: null, - areWorkflowsEnabled: null, - areCategoriesEnabled: null, - areTagsEnabled: null, - tax: null, - areConnectionsEnabled: null, - }, - }); -} - function setPolicyIDForReimburseView(policyID: string) { Onyx.merge(ONYXKEYS.WORKSPACE_RATE_AND_UNIT, {policyID, rate: null, unit: null}); } @@ -5088,7 +5075,6 @@ export { updatePolicyDistanceRateValue, setPolicyDistanceRatesEnabled, deletePolicyDistanceRates, - clearPendingFieldsForMoreFeatures, }; export type {NewCustomUnit}; diff --git a/src/pages/workspace/WorkspaceInitialPage.tsx b/src/pages/workspace/WorkspaceInitialPage.tsx index 9343432a4836..517c9b5251b9 100644 --- a/src/pages/workspace/WorkspaceInitialPage.tsx +++ b/src/pages/workspace/WorkspaceInitialPage.tsx @@ -127,14 +127,6 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, reimbursementAcc setIsCurrencyModalOpen(false); }, [policy?.outputCurrency, isCurrencyModalOpen]); - useEffect(() => { - if (isOffline) { - return; - } - Policy.clearPendingFieldsForMoreFeatures(policyID); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [policyID]); - /** Call update workspace currency and hide the modal */ const confirmCurrencyChangeAndHideModal = useCallback(() => { Policy.updateGeneralSettings(policyID, policyName, CONST.CURRENCY.USD); From 90834be4db9ffdcf72d07bf94a91676d4bcfa83e Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 22 Apr 2024 13:33:21 +0700 Subject: [PATCH 024/146] fix allow to search selfDM in new chat page --- src/libs/OptionsListUtils.ts | 8 ++++++-- src/pages/NewChatPage.tsx | 14 +++++++++++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 2aad4179c337..049e538cb18a 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -1505,7 +1505,8 @@ function createOptionList(personalDetails: OnyxEntry, repor } function createOptionFromReport(report: Report, personalDetails: OnyxEntry) { - const accountIDs = report.participantAccountIDs ?? []; + const isSelfDM = ReportUtils.isSelfDM(report); + const accountIDs = isSelfDM ? [currentUserAccountID ?? 0] : report.participantAccountIDs ?? []; return { item: report, @@ -1741,7 +1742,10 @@ function getOptions( } // Exclude the current user from the personal details list - const optionsToExclude: Option[] = [{login: currentUserLogin}, {login: CONST.EMAIL.NOTIFICATIONS}]; + const optionsToExclude: Option[] = [{login: CONST.EMAIL.NOTIFICATIONS}]; + if (!includeSelfDM) { + optionsToExclude.push({login: currentUserLogin}); + } // If we're including selected options from the search results, we only want to exclude them if the search input is empty // This is because on certain pages, we show the selected options at the top when the search input is empty diff --git a/src/pages/NewChatPage.tsx b/src/pages/NewChatPage.tsx index 470afc28d76e..b3ac000daea7 100755 --- a/src/pages/NewChatPage.tsx +++ b/src/pages/NewChatPage.tsx @@ -25,7 +25,7 @@ import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; -import type {OptionData} from '@libs/ReportUtils'; +import {getReport, type OptionData} from '@libs/ReportUtils'; import variables from '@styles/variables'; import * as Report from '@userActions/Report'; import CONST from '@src/CONST'; @@ -68,6 +68,10 @@ function useOptions({isGroupChat}: NewChatPageProps) { {}, [], true, + undefined, + undefined, + undefined, + true, ); const maxParticipantsReached = selectedOptions.length === CONST.REPORT.MAXIMUM_PARTICIPANTS; @@ -182,6 +186,11 @@ function NewChatPage({isGroupChat}: NewChatPageProps) { */ const createChat = useCallback( (option?: OptionsListUtils.Option) => { + const report = getReport(option?.reportID); + if (option?.isSelfDM && report) { + Navigation.dismissModalWithReport(report); + return; + } let login = ''; if (option?.login) { @@ -200,6 +209,9 @@ function NewChatPage({isGroupChat}: NewChatPageProps) { const itemRightSideComponent = useCallback( (item: ListItem & OptionsListUtils.Option) => { + if (item.isSelfDM) { + return null; + } /** * Removes a selected option from list if already selected. If not already selected add this option to the list. * @param option From 4fa9decddd651f15dd24cc19aef277acaf837288 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 22 Apr 2024 13:51:13 +0700 Subject: [PATCH 025/146] fix lint --- src/pages/NewChatPage.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/pages/NewChatPage.tsx b/src/pages/NewChatPage.tsx index b3ac000daea7..f0b983033c94 100755 --- a/src/pages/NewChatPage.tsx +++ b/src/pages/NewChatPage.tsx @@ -25,7 +25,8 @@ import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; -import {getReport, type OptionData} from '@libs/ReportUtils'; +import type {OptionData} from '@libs/ReportUtils'; +import * as ReportUtils from '@libs/ReportUtils'; import variables from '@styles/variables'; import * as Report from '@userActions/Report'; import CONST from '@src/CONST'; @@ -186,9 +187,9 @@ function NewChatPage({isGroupChat}: NewChatPageProps) { */ const createChat = useCallback( (option?: OptionsListUtils.Option) => { - const report = getReport(option?.reportID); + const report = ReportUtils.getReport(option?.reportID); if (option?.isSelfDM && report) { - Navigation.dismissModalWithReport(report); + Navigation.dismissModal(report.reportID); return; } let login = ''; From 84605aad893eab7948de04f89f980eca8e372583 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 22 Apr 2024 14:30:01 +0700 Subject: [PATCH 026/146] fix use option.reportID in createChat --- src/pages/NewChatPage.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/pages/NewChatPage.tsx b/src/pages/NewChatPage.tsx index f0b983033c94..3e5a99c1b222 100755 --- a/src/pages/NewChatPage.tsx +++ b/src/pages/NewChatPage.tsx @@ -187,9 +187,8 @@ function NewChatPage({isGroupChat}: NewChatPageProps) { */ const createChat = useCallback( (option?: OptionsListUtils.Option) => { - const report = ReportUtils.getReport(option?.reportID); - if (option?.isSelfDM && report) { - Navigation.dismissModal(report.reportID); + if (option?.isSelfDM) { + Navigation.dismissModal(option.reportID); return; } let login = ''; From d40e0cf3c16eb0737d18b26b682d99cd192dd6c2 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 22 Apr 2024 14:36:26 +0700 Subject: [PATCH 027/146] fix lint --- src/pages/NewChatPage.tsx | 1 - tests/unit/OptionsListUtilsTest.ts | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/pages/NewChatPage.tsx b/src/pages/NewChatPage.tsx index 3e5a99c1b222..3f0c9a23da3f 100755 --- a/src/pages/NewChatPage.tsx +++ b/src/pages/NewChatPage.tsx @@ -26,7 +26,6 @@ import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import type {OptionData} from '@libs/ReportUtils'; -import * as ReportUtils from '@libs/ReportUtils'; import variables from '@styles/variables'; import * as Report from '@userActions/Report'; import CONST from '@src/CONST'; diff --git a/tests/unit/OptionsListUtilsTest.ts b/tests/unit/OptionsListUtilsTest.ts index 75ed7fa9d5b1..4c332d0e2e07 100644 --- a/tests/unit/OptionsListUtilsTest.ts +++ b/tests/unit/OptionsListUtilsTest.ts @@ -368,7 +368,7 @@ describe('OptionsListUtils', () => { // When we filter in the Search view without providing a searchValue let results = OptionsListUtils.getSearchOptions(OPTIONS, '', [CONST.BETAS.ALL]); // Then the 2 personalDetails that don't have reports should be returned - expect(results.personalDetails.length).toBe(2); + expect(results.personalDetails.length).toBe(3); // Then all of the reports should be shown including the archived rooms. expect(results.recentReports.length).toBe(Object.values(OPTIONS.reports).length); @@ -2618,7 +2618,7 @@ describe('OptionsListUtils', () => { const options = OptionsListUtils.getSearchOptions(OPTIONS, '', [CONST.BETAS.ALL]); const filteredOptions = OptionsListUtils.filterOptions(options, searchText); - expect(filteredOptions.recentReports.length).toBe(5); + expect(filteredOptions.recentReports.length).toBe(6); expect(filteredOptions.recentReports[0].text).toBe('Invisible Woman'); expect(filteredOptions.recentReports[1].text).toBe('Spider-Man'); expect(filteredOptions.recentReports[2].text).toBe('Black Widow'); From d3eeff38c7d8bf1d576b7f3faca6aac06fbd6c06 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 22 Apr 2024 14:49:07 +0700 Subject: [PATCH 028/146] fix unread issue in combine report --- .../LHNOptionsList/LHNOptionsList.tsx | 3 +++ .../LHNOptionsList/OptionRowLHNData.tsx | 2 ++ src/components/LHNOptionsList/types.ts | 3 +++ src/libs/SidebarUtils.ts | 4 +++- .../ContextMenu/BaseReportActionContextMenu.tsx | 5 +++++ .../report/ContextMenu/ContextMenuActions.tsx | 6 +++++- .../PopoverReportActionContextMenu.tsx | 4 ++++ .../ContextMenu/ReportActionContextMenu.ts | 3 +++ src/pages/home/report/ReportActionsList.tsx | 17 ++++++++++++++++- tests/perf-test/SidebarUtils.perf-test.ts | 1 + 10 files changed, 45 insertions(+), 3 deletions(-) diff --git a/src/components/LHNOptionsList/LHNOptionsList.tsx b/src/components/LHNOptionsList/LHNOptionsList.tsx index 8c43ae542932..469f17258a7f 100644 --- a/src/components/LHNOptionsList/LHNOptionsList.tsx +++ b/src/components/LHNOptionsList/LHNOptionsList.tsx @@ -116,6 +116,8 @@ function LHNOptionsList({ const hasDraftComment = DraftCommentUtils.isValidDraftComment(draftComments?.[`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`]); const sortedReportActions = ReportActionsUtils.getSortedReportActionsForDisplay(itemReportActions); const lastReportAction = sortedReportActions[0]; + const transactionThreadReportID = ReportActionsUtils.getOneTransactionThreadReportID(reportID, itemReportActions); + const transactionThreadReport = reports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`] ?? null; // Get the transaction for the last report action let lastReportActionTransactionID = ''; @@ -129,6 +131,7 @@ function LHNOptionsList({ ; + /** The transaction thread report associated with the current report, if any */ + transactionThreadReport: OnyxEntry; + /** The policy which the user has access to and which the report could be tied to */ policy?: OnyxEntry; diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 7b0c28b76653..43b83a775b60 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -191,6 +191,7 @@ function getOptionData({ policy, parentReportAction, hasViolations, + transactionThreadReport, }: { report: OnyxEntry; reportActions: OnyxEntry; @@ -199,6 +200,7 @@ function getOptionData({ policy: OnyxEntry | undefined; parentReportAction: OnyxEntry | undefined; hasViolations: boolean; + transactionThreadReport: OnyxEntry; }): ReportUtils.OptionData | undefined { // When a user signs out, Onyx is cleared. Due to the lazy rendering with a virtual list, it's possible for // this method to be called after the Onyx data has been cleared out. In that case, it's fine to do @@ -267,7 +269,7 @@ function getOptionData({ result.statusNum = report.statusNum; // When the only message of a report is deleted lastVisibileActionCreated is not reset leading to wrongly // setting it Unread so we add additional condition here to avoid empty chat LHN from being bold. - result.isUnread = ReportUtils.isUnread(report) && !!report.lastActorAccountID; + result.isUnread = (ReportUtils.isUnread(report) && !!report.lastActorAccountID) || (ReportUtils.isUnread(transactionThreadReport) && !!transactionThreadReport?.lastActorAccountID); result.isUnreadWithMention = ReportUtils.isUnreadWithMention(report); result.isPinned = report.isPinned; result.iouReportID = report.iouReportID; diff --git a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx index 46ebdd751762..8e2c9d6f09b3 100755 --- a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx +++ b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx @@ -49,6 +49,9 @@ type BaseReportActionContextMenuProps = BaseReportActionContextMenuOnyxProps & { // eslint-disable-next-line react/no-unused-prop-types originalReportID: string; + /** The ID of transaction thread report associated with the current report, if any */ + transactionThreadReportID: string; + /** * If true, this component will be a small, row-oriented menu that displays icons but not text. * If false, this component will be a larger, column-oriented menu that displays icons alongside text in each row. @@ -117,6 +120,7 @@ function BaseReportActionContextMenu({ checkIfContextMenuActive, disabledActions = [], setIsEmojiPickerActive, + transactionThreadReportID, }: BaseReportActionContextMenuProps) { const StyleUtils = useStyleUtils(); const {translate} = useLocalize(); @@ -245,6 +249,7 @@ function BaseReportActionContextMenu({ interceptAnonymousUser, openOverflowMenu, setIsEmojiPickerActive, + transactionThreadReportID, }; if ('renderContent' in contextAction) { diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx index eb1e60461005..59c4042280ec 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx @@ -79,6 +79,7 @@ type ContextMenuActionPayload = { event?: GestureResponderEvent | MouseEvent | KeyboardEvent; setIsEmojiPickerActive?: (state: boolean) => void; anchorRef?: MutableRefObject; + transactionThreadReportID?: string; }; type OnPress = (closePopover: boolean, payload: ContextMenuActionPayload, selection?: string, reportID?: string, draftMessage?: string) => void; @@ -273,8 +274,11 @@ const ContextMenuActions: ContextMenuAction[] = [ successIcon: Expensicons.Checkmark, shouldShow: (type, reportAction, isArchivedRoom, betas, menuTarget, isChronosReport, reportID, isPinnedChat, isUnreadChat) => type === CONST.CONTEXT_MENU_TYPES.REPORT && isUnreadChat, - onPress: (closePopover, {reportID}) => { + onPress: (closePopover, {reportID, transactionThreadReportID}) => { Report.readNewestAction(reportID); + if (transactionThreadReportID && transactionThreadReportID !== '0') { + Report.readNewestAction(transactionThreadReportID); + } if (closePopover) { hideContextMenu(true, ReportActionComposeFocusManager.focus); } diff --git a/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.tsx b/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.tsx index 6cb688ff2558..38fd37ee4d26 100644 --- a/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.tsx +++ b/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.tsx @@ -37,6 +37,7 @@ function PopoverReportActionContextMenu(_props: unknown, ref: ForwardedRef>(null); const reportActionIDRef = useRef('0'); const originalReportIDRef = useRef('0'); + const transactionThreadReportIDRef = useRef('0'); const selectionRef = useRef(''); const reportActionDraftMessageRef = useRef(); @@ -171,6 +172,7 @@ function PopoverReportActionContextMenu(_props: unknown, ref: ForwardedRef {}, isOverflowMenu = false, + transactionThreadReportID = undefined, ) => { const {pageX = 0, pageY = 0} = extractPointerEvent(event); contextMenuAnchorRef.current = contextMenuAnchor; @@ -212,6 +214,7 @@ function PopoverReportActionContextMenu(_props: unknown, ref: ForwardedRef void, isOverflowMenu?: boolean, + transactionThreadReportID?: string, ) => void; type ReportActionContextMenu = { @@ -119,6 +120,7 @@ function showContextMenu( shouldCloseOnTarget = false, setIsEmojiPickerActive = () => {}, isOverflowMenu = false, + transactionThreadReportID = '0', ) { if (!contextMenuRef.current) { return; @@ -149,6 +151,7 @@ function showContextMenu( shouldCloseOnTarget, setIsEmojiPickerActive, isOverflowMenu, + transactionThreadReportID, ); } diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx index 206f7558baac..2f30db46c56b 100644 --- a/src/pages/home/report/ReportActionsList.tsx +++ b/src/pages/home/report/ReportActionsList.tsx @@ -294,6 +294,16 @@ function ReportActionsList({ setMessageManuallyMarkedUnread(0); }); + const unreadActionSubscriptionForTransactionThread = DeviceEventEmitter.addListener(`unreadAction_${transactionThreadReport?.reportID}`, (newLastReadTime) => { + resetUnreadMarker(newLastReadTime); + setMessageManuallyMarkedUnread(new Date().getTime()); + }); + + const readNewestActionSubscriptionForTransactionThread = DeviceEventEmitter.addListener(`readNewestAction_${transactionThreadReport?.reportID}`, (newLastReadTime) => { + resetUnreadMarker(newLastReadTime); + setMessageManuallyMarkedUnread(0); + }); + const deletedReportActionSubscription = DeviceEventEmitter.addListener(`deletedReportAction_${report.reportID}`, (reportActionID) => { if (cacheUnreadMarkers.get(report.reportID) !== reportActionID) { return; @@ -305,9 +315,11 @@ function ReportActionsList({ return () => { unreadActionSubscription.remove(); readNewestActionSubscription.remove(); + unreadActionSubscriptionForTransactionThread.remove(); + readNewestActionSubscriptionForTransactionThread.remove(); deletedReportActionSubscription.remove(); }; - }, [report.reportID]); + }, [report.reportID, transactionThreadReport?.reportID]); useEffect(() => { if (linkedReportActionID) { @@ -393,6 +405,9 @@ function ReportActionsList({ reportScrollManager.scrollToBottom(); readActionSkipped.current = false; Report.readNewestAction(report.reportID); + if (transactionThreadReport?.reportID) { + Report.readNewestAction(transactionThreadReport?.reportID); + } }; /** diff --git a/tests/perf-test/SidebarUtils.perf-test.ts b/tests/perf-test/SidebarUtils.perf-test.ts index 8566abb97c7f..b19e59dbdcc0 100644 --- a/tests/perf-test/SidebarUtils.perf-test.ts +++ b/tests/perf-test/SidebarUtils.perf-test.ts @@ -106,6 +106,7 @@ describe('SidebarUtils', () => { policy, parentReportAction, hasViolations: false, + transactionThreadReport: null, }), ); }); From ac68346d8a8bd3b0e91d5547af3dd295b1d090d0 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 22 Apr 2024 14:50:03 +0700 Subject: [PATCH 029/146] fix revert fix jest --- tests/unit/OptionsListUtilsTest.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/OptionsListUtilsTest.ts b/tests/unit/OptionsListUtilsTest.ts index 4c332d0e2e07..75ed7fa9d5b1 100644 --- a/tests/unit/OptionsListUtilsTest.ts +++ b/tests/unit/OptionsListUtilsTest.ts @@ -368,7 +368,7 @@ describe('OptionsListUtils', () => { // When we filter in the Search view without providing a searchValue let results = OptionsListUtils.getSearchOptions(OPTIONS, '', [CONST.BETAS.ALL]); // Then the 2 personalDetails that don't have reports should be returned - expect(results.personalDetails.length).toBe(3); + expect(results.personalDetails.length).toBe(2); // Then all of the reports should be shown including the archived rooms. expect(results.recentReports.length).toBe(Object.values(OPTIONS.reports).length); @@ -2618,7 +2618,7 @@ describe('OptionsListUtils', () => { const options = OptionsListUtils.getSearchOptions(OPTIONS, '', [CONST.BETAS.ALL]); const filteredOptions = OptionsListUtils.filterOptions(options, searchText); - expect(filteredOptions.recentReports.length).toBe(6); + expect(filteredOptions.recentReports.length).toBe(5); expect(filteredOptions.recentReports[0].text).toBe('Invisible Woman'); expect(filteredOptions.recentReports[1].text).toBe('Spider-Man'); expect(filteredOptions.recentReports[2].text).toBe('Black Widow'); From a97a864a62c601732208fa68a3a570cf8de976a6 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 22 Apr 2024 15:14:05 +0700 Subject: [PATCH 030/146] add transaction thread report ID to option item --- src/components/LHNOptionsList/OptionRowLHN.tsx | 5 +++++ src/components/LHNOptionsList/OptionRowLHNData.tsx | 1 + src/libs/ReportUtils.ts | 1 + src/libs/SidebarUtils.ts | 1 + src/pages/home/report/ReportActionsList.tsx | 3 ++- 5 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/components/LHNOptionsList/OptionRowLHN.tsx b/src/components/LHNOptionsList/OptionRowLHN.tsx index 9946dea9d5a7..2eba8b62dfd7 100644 --- a/src/components/LHNOptionsList/OptionRowLHN.tsx +++ b/src/components/LHNOptionsList/OptionRowLHN.tsx @@ -96,6 +96,11 @@ function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, opti false, optionItem.isPinned, !!optionItem.isUnread, + [], + false, + () => {}, + false, + optionItem.transactionThreadReportID, ); }; diff --git a/src/components/LHNOptionsList/OptionRowLHNData.tsx b/src/components/LHNOptionsList/OptionRowLHNData.tsx index 2d369ce02ab2..1d6cc636f939 100644 --- a/src/components/LHNOptionsList/OptionRowLHNData.tsx +++ b/src/components/LHNOptionsList/OptionRowLHNData.tsx @@ -72,6 +72,7 @@ function OptionRowLHNData({ transactionViolations, canUseViolations, receiptTransactions, + transactionThreadReport, ]); return ( diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index fc677dedc96e..ed3f217b38c8 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -440,6 +440,7 @@ type OptionData = { reportID?: string; enabled?: boolean; data?: Partial; + transactionThreadReportID?: string | null; } & Report; type OnyxDataTaskAssigneeChat = { diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 43b83a775b60..a250a5cb2e1c 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -237,6 +237,7 @@ function getOptionData({ isWaitingOnBankAccount: false, isAllowedToComment: true, isDeletedParentAction: false, + transactionThreadReportID: transactionThreadReport?.reportID, }; let participantAccountIDs = report.participantAccountIDs ?? []; diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx index 2f30db46c56b..9792af1dae40 100644 --- a/src/pages/home/report/ReportActionsList.tsx +++ b/src/pages/home/report/ReportActionsList.tsx @@ -196,7 +196,8 @@ function ReportActionsList({ ); const lastActionIndex = sortedVisibleReportActions[0]?.reportActionID; const reportActionSize = useRef(sortedVisibleReportActions.length); - const hasNewestReportAction = sortedReportActions?.[0].created === report.lastVisibleActionCreated; + const hasNewestReportAction = + sortedReportActions?.[0].created === report.lastVisibleActionCreated || sortedReportActions?.[0].created === transactionThreadReport?.lastVisibleActionCreated; const hasNewestReportActionRef = useRef(hasNewestReportAction); hasNewestReportActionRef.current = hasNewestReportAction; const previousLastIndex = useRef(lastActionIndex); From 6a16d6d1aca61fe8a5b395a18b17c789f5ae346c Mon Sep 17 00:00:00 2001 From: Yauheni Date: Mon, 22 Apr 2024 10:15:51 +0200 Subject: [PATCH 031/146] Remove isFocuesd from FeatureEnabledAccessOrNotFoundComponent --- .../FeatureEnabledAccessOrNotFoundWrapper.tsx | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/pages/workspace/FeatureEnabledAccessOrNotFoundWrapper.tsx b/src/pages/workspace/FeatureEnabledAccessOrNotFoundWrapper.tsx index eebdb3a280db..cf5af04ac846 100644 --- a/src/pages/workspace/FeatureEnabledAccessOrNotFoundWrapper.tsx +++ b/src/pages/workspace/FeatureEnabledAccessOrNotFoundWrapper.tsx @@ -1,5 +1,4 @@ /* eslint-disable rulesdir/no-negated-variables */ -import {useIsFocused} from '@react-navigation/native'; import React, {useEffect, useState} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; @@ -40,20 +39,17 @@ function FeatureEnabledAccessOrNotFoundComponent(props: FeatureEnabledAccessOrNo const isFeatureEnabled = PolicyUtils.isPolicyFeatureEnabled(props.policy, props.featureName); const [isPolicyFeatureEnabled, setIsPolicyFeatureEnabled] = useState(isFeatureEnabled); - const isFocused = useIsFocused(); const {isOffline} = useNetwork(); const shouldShowNotFoundPage = isEmptyObject(props.policy) || !props.policy?.id || !isPolicyFeatureEnabled; const shouldShowFullScreenLoadingIndicator = props.isLoadingReportData !== false && (!Object.entries(props.policy ?? {}).length || !props.policy?.id); useEffect(() => { - if (!isFocused) { + if (pendingField && !isOffline) { return; } - if (!pendingField || isOffline) { - setIsPolicyFeatureEnabled(isFeatureEnabled); - } - }, [isFocused, pendingField, isOffline, isFeatureEnabled]); + setIsPolicyFeatureEnabled(isFeatureEnabled); + }, [pendingField, isOffline, isFeatureEnabled]); useEffect(() => { if (!isPolicyIDInRoute || !isEmptyObject(props.policy)) { From 27c8a30421b491bab331e652b01b419f31eb93b4 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 22 Apr 2024 15:31:59 +0700 Subject: [PATCH 032/146] add missing prop --- src/pages/home/report/ReportActionItem.tsx | 1 + src/pages/home/report/ReportActionsList.tsx | 10 +++++----- src/pages/home/report/ReportActionsView.tsx | 13 +++++++------ 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/pages/home/report/ReportActionItem.tsx b/src/pages/home/report/ReportActionItem.tsx index 8d11744740bd..3444063c48aa 100644 --- a/src/pages/home/report/ReportActionItem.tsx +++ b/src/pages/home/report/ReportActionItem.tsx @@ -914,6 +914,7 @@ function ReportActionItem({ isChronosReport={ReportUtils.chatIncludesChronos(originalReport)} checkIfContextMenuActive={toggleContextMenuFromActiveReportAction} setIsEmojiPickerActive={setIsEmojiPickerActive} + transactionThreadReportID={transactionThreadReport?.reportID ?? '0'} /> { - resetUnreadMarker(newLastReadTime); - setMessageManuallyMarkedUnread(0); - }); + // const readNewestActionSubscriptionForTransactionThread = DeviceEventEmitter.addListener(`readNewestAction_${transactionThreadReport?.reportID}`, (newLastReadTime) => { + // resetUnreadMarker(newLastReadTime); + // setMessageManuallyMarkedUnread(0); + // }); const deletedReportActionSubscription = DeviceEventEmitter.addListener(`deletedReportAction_${report.reportID}`, (reportActionID) => { if (cacheUnreadMarkers.get(report.reportID) !== reportActionID) { @@ -317,7 +317,7 @@ function ReportActionsList({ unreadActionSubscription.remove(); readNewestActionSubscription.remove(); unreadActionSubscriptionForTransactionThread.remove(); - readNewestActionSubscriptionForTransactionThread.remove(); + // readNewestActionSubscriptionForTransactionThread.remove(); deletedReportActionSubscription.remove(); }; }, [report.reportID, transactionThreadReport?.reportID]); diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx index 3e3ebf1a9cc3..65808e4e009f 100755 --- a/src/pages/home/report/ReportActionsView.tsx +++ b/src/pages/home/report/ReportActionsView.tsx @@ -197,7 +197,8 @@ function ReportActionsView({ } // If this is a one transaction report, ensure we load newer actions for both this report and the report associated with the transaction - if (!isEmptyObject(transactionThreadReport)) { + if (transactionThreadReport?.reportID) { + console.log("123455"); // Get newer actions based on the newest reportAction for the current report const newestActionCurrentReport = reportActionIDMap.find((item) => item.reportID === reportID); Report.getNewerActions(newestActionCurrentReport?.reportID ?? '0', newestActionCurrentReport?.reportActionID ?? '0'); @@ -209,7 +210,7 @@ function ReportActionsView({ Report.getNewerActions(reportID, newestReportAction.reportActionID); } }, - [isLoadingNewerReportActions, isLoadingInitialReportActions, reportID, transactionThreadReport, reportActionIDMap], + [isLoadingNewerReportActions, isLoadingInitialReportActions, reportID, transactionThreadReport?.reportID, reportActionIDMap], ); const hasMoreCached = reportActions.length < combinedReportActions.length; @@ -308,19 +309,19 @@ function ReportActionsView({ return; } - if (!isEmptyObject(transactionThreadReport)) { + if (transactionThreadReport?.reportID) { // Get newer actions based on the newest reportAction for the current report const oldestActionCurrentReport = reportActionIDMap.findLast((item) => item.reportID === reportID); - Report.getNewerActions(oldestActionCurrentReport?.reportID ?? '0', oldestActionCurrentReport?.reportActionID ?? '0'); + Report.getOlderActions(oldestActionCurrentReport?.reportID ?? '0', oldestActionCurrentReport?.reportActionID ?? '0'); // Get newer actions based on the newest reportAction for the transaction thread report const oldestActionTransactionThreadReport = reportActionIDMap.findLast((item) => item.reportID === transactionThreadReport.reportID); - Report.getNewerActions(oldestActionTransactionThreadReport?.reportID ?? '0', oldestActionTransactionThreadReport?.reportActionID ?? '0'); + Report.getOlderActions(oldestActionTransactionThreadReport?.reportID ?? '0', oldestActionTransactionThreadReport?.reportActionID ?? '0'); } else { // Retrieve the next REPORT.ACTIONS.LIMIT sized page of comments Report.getOlderActions(reportID, oldestReportAction.reportActionID); } - }, [network.isOffline, isLoadingOlderReportActions, isLoadingInitialReportActions, oldestReportAction, hasCreatedAction, reportID, reportActionIDMap, transactionThreadReport]); + }, [network.isOffline, isLoadingOlderReportActions, isLoadingInitialReportActions, oldestReportAction, hasCreatedAction, reportID, reportActionIDMap, transactionThreadReport?.reportID]); const loadNewerChats = useCallback(() => { if (isLoadingInitialReportActions || isLoadingOlderReportActions || network.isOffline || newestReportAction.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) { From bf3a0aaaa67bf42ab25465c6700ca470457710ed Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 22 Apr 2024 15:39:41 +0700 Subject: [PATCH 033/146] reset hard code --- src/pages/home/report/ReportActionsList.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx index 5b118aeda63a..9792af1dae40 100644 --- a/src/pages/home/report/ReportActionsList.tsx +++ b/src/pages/home/report/ReportActionsList.tsx @@ -300,10 +300,10 @@ function ReportActionsList({ setMessageManuallyMarkedUnread(new Date().getTime()); }); - // const readNewestActionSubscriptionForTransactionThread = DeviceEventEmitter.addListener(`readNewestAction_${transactionThreadReport?.reportID}`, (newLastReadTime) => { - // resetUnreadMarker(newLastReadTime); - // setMessageManuallyMarkedUnread(0); - // }); + const readNewestActionSubscriptionForTransactionThread = DeviceEventEmitter.addListener(`readNewestAction_${transactionThreadReport?.reportID}`, (newLastReadTime) => { + resetUnreadMarker(newLastReadTime); + setMessageManuallyMarkedUnread(0); + }); const deletedReportActionSubscription = DeviceEventEmitter.addListener(`deletedReportAction_${report.reportID}`, (reportActionID) => { if (cacheUnreadMarkers.get(report.reportID) !== reportActionID) { @@ -317,7 +317,7 @@ function ReportActionsList({ unreadActionSubscription.remove(); readNewestActionSubscription.remove(); unreadActionSubscriptionForTransactionThread.remove(); - // readNewestActionSubscriptionForTransactionThread.remove(); + readNewestActionSubscriptionForTransactionThread.remove(); deletedReportActionSubscription.remove(); }; }, [report.reportID, transactionThreadReport?.reportID]); From bd426b3fcd302074d9c6c8591224a1c86dda5384 Mon Sep 17 00:00:00 2001 From: Yauheni Date: Mon, 22 Apr 2024 11:36:14 +0200 Subject: [PATCH 034/146] Update condition for FeatureEnabledAccessOrNotFoundComponent --- src/pages/workspace/FeatureEnabledAccessOrNotFoundWrapper.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/FeatureEnabledAccessOrNotFoundWrapper.tsx b/src/pages/workspace/FeatureEnabledAccessOrNotFoundWrapper.tsx index cf5af04ac846..e7ea95ba44f4 100644 --- a/src/pages/workspace/FeatureEnabledAccessOrNotFoundWrapper.tsx +++ b/src/pages/workspace/FeatureEnabledAccessOrNotFoundWrapper.tsx @@ -45,7 +45,7 @@ function FeatureEnabledAccessOrNotFoundComponent(props: FeatureEnabledAccessOrNo const shouldShowFullScreenLoadingIndicator = props.isLoadingReportData !== false && (!Object.entries(props.policy ?? {}).length || !props.policy?.id); useEffect(() => { - if (pendingField && !isOffline) { + if (pendingField && !isOffline && !isFeatureEnabled) { return; } setIsPolicyFeatureEnabled(isFeatureEnabled); From 363d88b0a10449432094c5b28ffb183fc828e22d Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 22 Apr 2024 16:51:43 +0700 Subject: [PATCH 035/146] fix lint --- src/pages/home/report/ReportActionsView.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx index 65808e4e009f..fac51b33ed7d 100755 --- a/src/pages/home/report/ReportActionsView.tsx +++ b/src/pages/home/report/ReportActionsView.tsx @@ -198,7 +198,6 @@ function ReportActionsView({ // If this is a one transaction report, ensure we load newer actions for both this report and the report associated with the transaction if (transactionThreadReport?.reportID) { - console.log("123455"); // Get newer actions based on the newest reportAction for the current report const newestActionCurrentReport = reportActionIDMap.find((item) => item.reportID === reportID); Report.getNewerActions(newestActionCurrentReport?.reportID ?? '0', newestActionCurrentReport?.reportActionID ?? '0'); From 9e93ce6af10f261d204847e4dd5172648d60a346 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 22 Apr 2024 17:22:08 +0700 Subject: [PATCH 036/146] revert hard code --- src/pages/home/report/ReportActionsView.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx index fac51b33ed7d..3e3ebf1a9cc3 100755 --- a/src/pages/home/report/ReportActionsView.tsx +++ b/src/pages/home/report/ReportActionsView.tsx @@ -197,7 +197,7 @@ function ReportActionsView({ } // If this is a one transaction report, ensure we load newer actions for both this report and the report associated with the transaction - if (transactionThreadReport?.reportID) { + if (!isEmptyObject(transactionThreadReport)) { // Get newer actions based on the newest reportAction for the current report const newestActionCurrentReport = reportActionIDMap.find((item) => item.reportID === reportID); Report.getNewerActions(newestActionCurrentReport?.reportID ?? '0', newestActionCurrentReport?.reportActionID ?? '0'); @@ -209,7 +209,7 @@ function ReportActionsView({ Report.getNewerActions(reportID, newestReportAction.reportActionID); } }, - [isLoadingNewerReportActions, isLoadingInitialReportActions, reportID, transactionThreadReport?.reportID, reportActionIDMap], + [isLoadingNewerReportActions, isLoadingInitialReportActions, reportID, transactionThreadReport, reportActionIDMap], ); const hasMoreCached = reportActions.length < combinedReportActions.length; @@ -308,19 +308,19 @@ function ReportActionsView({ return; } - if (transactionThreadReport?.reportID) { + if (!isEmptyObject(transactionThreadReport)) { // Get newer actions based on the newest reportAction for the current report const oldestActionCurrentReport = reportActionIDMap.findLast((item) => item.reportID === reportID); - Report.getOlderActions(oldestActionCurrentReport?.reportID ?? '0', oldestActionCurrentReport?.reportActionID ?? '0'); + Report.getNewerActions(oldestActionCurrentReport?.reportID ?? '0', oldestActionCurrentReport?.reportActionID ?? '0'); // Get newer actions based on the newest reportAction for the transaction thread report const oldestActionTransactionThreadReport = reportActionIDMap.findLast((item) => item.reportID === transactionThreadReport.reportID); - Report.getOlderActions(oldestActionTransactionThreadReport?.reportID ?? '0', oldestActionTransactionThreadReport?.reportActionID ?? '0'); + Report.getNewerActions(oldestActionTransactionThreadReport?.reportID ?? '0', oldestActionTransactionThreadReport?.reportActionID ?? '0'); } else { // Retrieve the next REPORT.ACTIONS.LIMIT sized page of comments Report.getOlderActions(reportID, oldestReportAction.reportActionID); } - }, [network.isOffline, isLoadingOlderReportActions, isLoadingInitialReportActions, oldestReportAction, hasCreatedAction, reportID, reportActionIDMap, transactionThreadReport?.reportID]); + }, [network.isOffline, isLoadingOlderReportActions, isLoadingInitialReportActions, oldestReportAction, hasCreatedAction, reportID, reportActionIDMap, transactionThreadReport]); const loadNewerChats = useCallback(() => { if (isLoadingInitialReportActions || isLoadingOlderReportActions || network.isOffline || newestReportAction.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) { From 2e68cfb9717f8cf074e245c6eebb76e184b72170 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Mon, 22 Apr 2024 13:21:46 +0200 Subject: [PATCH 037/146] fix nav options --- src/libs/Navigation/AppNavigator/AuthScreens.tsx | 8 ++++++-- .../getOnboardingModalScreenOptions/index.native.ts | 9 +++++++++ .../Navigation/getOnboardingModalScreenOptions/index.ts | 9 +++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 src/libs/Navigation/getOnboardingModalScreenOptions/index.native.ts create mode 100644 src/libs/Navigation/getOnboardingModalScreenOptions/index.ts diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index 096a88254eae..17f7b946c7ab 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -10,6 +10,7 @@ import useWindowDimensions from '@hooks/useWindowDimensions'; import KeyboardShortcut from '@libs/KeyboardShortcut'; import Log from '@libs/Log'; import getCurrentUrl from '@libs/Navigation/currentUrl'; +import getOnboardingModalScreenOptions from '@libs/Navigation/getOnboardingModalScreenOptions'; import Navigation from '@libs/Navigation/Navigation'; import type {AuthScreensParamList} from '@libs/Navigation/types'; import NetworkConnection from '@libs/NetworkConnection'; @@ -160,7 +161,10 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie const {isSmallScreenWidth} = useWindowDimensions(); const {shouldUseNarrowLayout} = useOnboardingLayout(); const screenOptions = getRootNavigatorScreenOptions(isSmallScreenWidth, styles, StyleUtils); - const onboardingScreenOptions = useMemo(() => screenOptions.onboardingModalNavigator(shouldUseNarrowLayout), [screenOptions, shouldUseNarrowLayout]); + const onboardingScreenOptions = useMemo( + () => getOnboardingModalScreenOptions(isSmallScreenWidth, styles, StyleUtils, shouldUseNarrowLayout), + [StyleUtils, isSmallScreenWidth, shouldUseNarrowLayout, styles], + ); const isInitialRender = useRef(true); if (isInitialRender.current) { @@ -369,7 +373,7 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie /> Date: Mon, 22 Apr 2024 13:22:14 +0200 Subject: [PATCH 038/146] fix reply counter --- src/libs/actions/Report.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 8b0dbf8a37a9..4bc72b59293c 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3047,6 +3047,7 @@ function completeOnboarding( }; const tasksData = data.tasks.map((task, index) => { + const hasSubtitle = !!task.subtitle; const currentTask = ReportUtils.buildOptimisticTaskReport( actorAccountID, undefined, @@ -3066,13 +3067,13 @@ function completeOnboarding( actorAccountID, index + 3, { - childVisibleActionCount: 2, + childVisibleActionCount: hasSubtitle ? 2 : 1, childCommenterCount: 1, childLastVisibleActionCreated: DateUtils.getDBTime(), childOldestFourAccountIDs: `${actorAccountID}`, }, ); - const subtitleComment = task.subtitle ? ReportUtils.buildOptimisticAddCommentReportAction(task.subtitle, undefined, actorAccountID) : null; + const subtitleComment = hasSubtitle ? ReportUtils.buildOptimisticAddCommentReportAction(task.subtitle, undefined, actorAccountID) : null; const isTaskMessageFunction = typeof task.message === 'function'; const taskMessage = isTaskMessageFunction ? task.message({ From 0ef0b24fc8c14cd6e8999192e352aa76f8fc05aa Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Mon, 22 Apr 2024 14:00:03 +0200 Subject: [PATCH 039/146] fix subtitle bold texts --- src/CONST.ts | 48 +++++++++++++++++++------------------- src/libs/actions/Report.ts | 2 +- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index ab5a67274955..724e93bc740b 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -3624,24 +3624,24 @@ const CONST = { type: 'createWorkspace', autoCompleted: true, title: 'Create a workspace', - subtitle: 'Create a workspace to track expenses, scan receipts, chat, and more.', + subtitle: 'Create a workspace to track expenses, scan receipts, chat, and more.', message: 'Here’s how to create a workspace:\n' + '\n' + '1. Click your profile picture.\n' + '2. Click Workspaces > New workspace.\n' + '\n' + - 'Your new workspace is ready! It’ll keep all of your spend (and chats) in one place.', + 'Your new workspace is ready! It’ll keep all of your spend (and chats) in one place.', }, { type: 'trackExpense', autoCompleted: false, title: 'Track an expense', - subtitle: 'Track an expense in any currency, in just a few clicks.', + subtitle: 'Track an expense in any currency, in just a few clicks.', message: 'Here’s how to track an expense:\n' + '\n' + - '1. Click the green + button.\n' + + '1. Click the green + button.\n' + '2. Choose Track expense.\n' + '3. Enter an amount or scan a receipt.\n' + '4. Click Track.\n' + @@ -3664,11 +3664,11 @@ const CONST = { type: 'submitExpense', autoCompleted: false, title: 'Submit an expense', - subtitle: 'Submit an expense by entering an amount or scanning a receipt.', + subtitle: 'Submit an expense by entering an amount or scanning a receipt.', message: 'Here’s how to submit an expense:\n' + '\n' + - '1. Click the green + button.\n' + + '1. Click the green + button.\n' + '2. Choose Submit expense.\n' + '3. Enter an amount or scan a receipt.\n' + '4. Add your reimburser to the request.\n' + @@ -3679,7 +3679,7 @@ const CONST = { type: 'enableWallet', autoCompleted: false, title: 'Enable your wallet', - subtitle: 'You’ll need to enable your Expensify Wallet to get paid back. Don’t worry, it’s easy!', + subtitle: 'You’ll need to enable your Expensify Wallet to get paid back. Don’t worry, it’s easy!', message: 'Here’s how to set up your wallet:\n' + '\n' + @@ -3705,14 +3705,14 @@ const CONST = { type: 'createWorkspace', autoCompleted: true, title: 'Create a workspace', - subtitle: 'Create a workspace to track expenses, scan receipts, chat, and more.', + subtitle: 'Create a workspace to track expenses, scan receipts, chat, and more.', message: 'Here’s how to create a workspace:\n' + '\n' + '1. Click your profile picture.\n' + '2. Click Workspaces > New workspace.\n' + '\n' + - 'Your new workspace is ready! It’ll keep all of your spend (and chats) in one place.', + 'Your new workspace is ready! It’ll keep all of your spend (and chats) in one place.', }, { type: 'meetGuide', @@ -3728,7 +3728,7 @@ const CONST = { type: 'setupCategories', autoCompleted: false, title: 'Set up categories', - subtitle: 'Set up categories so your team can code expenses for easy reporting.', + subtitle: 'Set up categories so your team can code expenses for easy reporting.', message: 'Here’s how to set up categories:\n' + '\n' + @@ -3738,21 +3738,21 @@ const CONST = { '4. Enable and disable default categories.\n' + '5. Click Add categories to make your own.\n' + '\n' + - 'For more controls like requiring a category for every expense, click Settings.', + 'For more controls like requiring a category for every expense, click Settings.', }, { type: 'addExpenseApprovals', autoCompleted: false, title: 'Add expense approvals', - subtitle: 'Add expense approvals to review your team’s spend and keep it under control.', + subtitle: 'Add expense approvals to review your team’s spend and keep it under control.', message: 'Here’s how to add expense approvals:\n' + '\n' + '1. Click your profile picture.\n' + - '2. Go to Workspaces > [your workspace].\n' + + '2. Go to Workspaces > [your workspace].\n' + '3. Click More features.\n' + '4. Enable Workflows.\n' + - '5. In Workflows, enable Add approvals.\n' + + '5. In Workflows, enable Add approvals.\n' + '\n' + 'You’ll be set as the expense approver. You can change this to any admin once you invite your team.', }, @@ -3760,7 +3760,7 @@ const CONST = { type: 'inviteTeam', autoCompleted: false, title: 'Invite your team', - subtitle: 'Invite your team to Expensify so they can start tracking expenses today.', + subtitle: 'Invite your team to Expensify so they can start tracking expenses today.', message: 'Here’s how to invite your team:\n' + '\n' + @@ -3788,14 +3788,14 @@ const CONST = { type: 'trackExpense', autoCompleted: false, title: 'Track an expense', - subtitle: 'Track an expense in any currency, whether you have a receipt or not.', + subtitle: 'Track an expense in any currency, whether you have a receipt or not.', message: 'Here’s how to track an expense:\n' + '\n' + - '1. Click the green + button.\n' + + '1. Click the green + button.\n' + '2. Choose Track expense.\n' + '3. Enter an amount or scan a receipt.\n' + - '4. Click Track.\n' + + '4. Click Track.\n' + '\n' + 'And you’re done! Yep, it’s that easy.', }, @@ -3815,15 +3815,15 @@ const CONST = { type: 'startChat', autoCompleted: false, title: 'Start a chat', - subtitle: 'Start a chat with a friend or group using their email or phone number.', + subtitle: 'Start a chat with a friend or group using their email or phone number.', message: 'Here’s how to start a chat:\n' + '\n' + - '1. Click the green + button.\n' + + '1. Click the green + button.\n' + '2. Choose Start chat.\n' + '3. Enter emails or phone numbers.\n' + '\n' + - 'If any of your friends aren’t using Expensify already, they’ll be invited automatically. \n' + + 'If any of your friends aren’t using Expensify already, they’ll be invited automatically.\n' + '\n' + 'Every chat will also turn into an email or text that they can respond to directly.', }, @@ -3831,11 +3831,11 @@ const CONST = { type: 'splitExpense', autoCompleted: false, title: 'Split an expense', - subtitle: 'Split an expense right in your chat with one or more friends.', + subtitle: 'Split an expense right in your chat with one or more friends.', message: 'Here’s how to request money:\n' + '\n' + - '1. Click the green + button.\n' + + '1. Click the green + button.\n' + '2. Choose Split expense.\n' + '3. Scan a receipt or enter an amount.\n' + '4. Add your friend(s) to the request.\n' + @@ -3846,7 +3846,7 @@ const CONST = { type: 'enableWallet', autoCompleted: false, title: 'Enable your wallet', - subtitle: 'You’ll need to enable your Expensify Wallet to get paid back. Don’t worry, it’s easy!', + subtitle: 'You’ll need to enable your Expensify Wallet to get paid back. Don’t worry, it’s easy!', message: 'Here’s how to enable your wallet:\n' + '\n' + diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 4bc72b59293c..4f1f2c41b258 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3073,7 +3073,7 @@ function completeOnboarding( childOldestFourAccountIDs: `${actorAccountID}`, }, ); - const subtitleComment = hasSubtitle ? ReportUtils.buildOptimisticAddCommentReportAction(task.subtitle, undefined, actorAccountID) : null; + const subtitleComment = hasSubtitle ? ReportUtils.buildOptimisticAddCommentReportAction(task.subtitle, undefined, actorAccountID, 0, false) : null; const isTaskMessageFunction = typeof task.message === 'function'; const taskMessage = isTaskMessageFunction ? task.message({ From ad3e0415f9c1b75e69c8286aafaf3995c9497af3 Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Mon, 22 Apr 2024 15:37:07 +0200 Subject: [PATCH 040/146] remove unneeded variable in ProfilePage --- src/pages/ProfilePage.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/pages/ProfilePage.tsx b/src/pages/ProfilePage.tsx index d2967de8360a..e0a4b55ce149 100755 --- a/src/pages/ProfilePage.tsx +++ b/src/pages/ProfilePage.tsx @@ -95,8 +95,6 @@ function ProfilePage({route}: ProfilePageProps) { const details: PersonalDetails | EmptyObject = personalDetails?.[accountID] ?? (ValidationUtils.isValidAccountRoute(accountID) ? {} : {accountID: 0, avatar: ''}); const displayName = PersonalDetailsUtils.getDisplayNameOrDefault(details, undefined, undefined, isCurrentUser); - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const avatar = details.avatar; const fallbackIcon = details?.fallbackIcon ?? ''; const login = details?.login ?? ''; const timezone = details?.timezone; @@ -162,7 +160,7 @@ function ProfilePage({route}: ProfilePageProps) { Date: Mon, 22 Apr 2024 10:51:24 -0300 Subject: [PATCH 041/146] simpler condition --- .../HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx index 9d8d82ff5042..b36ee89acc02 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx @@ -65,7 +65,7 @@ function MentionReportRenderer({style, tnode, TDefaultRenderer, reports, ...defa // When we invite someone to a room they don't have the policy object, but we still want them to be able to see and click on report mentions, so we only check if the policyID in the report is from a workspace const isGroupPolicyReport = useMemo( - () => (currentReport && !isEmptyObject(currentReport) ? !!currentReport.policyID && currentReport.policyID !== CONST.POLICY.ID_FAKE : false), + () => currentReport && !isEmptyObject(currentReport) && !!currentReport.policyID && currentReport.policyID !== CONST.POLICY.ID_FAKE, [currentReport], ); From 8c1633407dde167c79814bd2b96d3e8050201b83 Mon Sep 17 00:00:00 2001 From: Rodrigo Lino da Costa Date: Mon, 22 Apr 2024 10:58:49 -0300 Subject: [PATCH 042/146] prettier --- .../HTMLRenderers/MentionReportRenderer.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx index b36ee89acc02..345bd338f365 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx @@ -64,10 +64,7 @@ function MentionReportRenderer({style, tnode, TDefaultRenderer, reports, ...defa const currentReport = getReport(currentReportID?.currentReportID); // When we invite someone to a room they don't have the policy object, but we still want them to be able to see and click on report mentions, so we only check if the policyID in the report is from a workspace - const isGroupPolicyReport = useMemo( - () => currentReport && !isEmptyObject(currentReport) && !!currentReport.policyID && currentReport.policyID !== CONST.POLICY.ID_FAKE, - [currentReport], - ); + const isGroupPolicyReport = useMemo(() => currentReport && !isEmptyObject(currentReport) && !!currentReport.policyID && currentReport.policyID !== CONST.POLICY.ID_FAKE, [currentReport]); const mentionDetails = getMentionDetails(htmlAttributeReportID, currentReport, reports, tnode); if (!mentionDetails) { From 10ff1935692695a473f78ffaffb336db1e2975d9 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Mon, 22 Apr 2024 23:13:25 +0800 Subject: [PATCH 043/146] add comment --- src/libs/PolicyUtils.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index de2731bba752..731dc5700c8e 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -333,6 +333,9 @@ function getDefaultApprover(policy: OnyxEntry | EmptyObject): string { return policy?.approver ?? policy?.owner ?? ''; } +/** + * Returns the accountID to whom the given employeeAccountID submits reports to in the given Policy. + */ function getSubmitToAccountID(policy: OnyxEntry | EmptyObject, employeeAccountID: number): number { const employeeLogin = getLoginsByAccountIDs([employeeAccountID])[0]; const defaultApprover = getDefaultApprover(policy); From 7ee2d7aefb90e3158a560a2aaa1bb3af0486f1a3 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Mon, 22 Apr 2024 18:35:33 +0200 Subject: [PATCH 044/146] implement failure data --- src/libs/actions/Report.ts | 75 +++++++++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 4f1f2c41b258..bd3e749d0473 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3220,6 +3220,33 @@ function completeOnboarding( [], ); + const tasksForFailureData = tasksData.reduce((acc, {currentTask, taskReportAction}) => { + const tasksForFailureDataAcc: OnyxUpdate[] = [ + ...acc, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, + value: { + [taskReportAction.reportAction.reportActionID]: { + errors: ErrorUtils.getMicroSecondOnyxError('report.genericAddCommentFailureMessage'), + } as ReportAction, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${currentTask.reportID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${currentTask.reportID}`, + value: null, + }, + ]; + + return tasksForFailureDataAcc; + }, []); + const optimisticData: OnyxUpdate[] = [ ...tasksForOptimisticData, { @@ -3255,6 +3282,52 @@ function completeOnboarding( }, }, ]; + let failureReport: Partial = { + lastMessageTranslationKey: '', + lastMessageText: '', + lastVisibleActionCreated: '', + }; + const {lastMessageText = '', lastMessageTranslationKey = ''} = ReportActionsUtils.getLastVisibleMessage(targetChatReportID); + if (lastMessageText || lastMessageTranslationKey) { + const lastVisibleAction = ReportActionsUtils.getLastVisibleAction(targetChatReportID); + const lastVisibleActionCreated = lastVisibleAction?.created; + const lastActorAccountID = lastVisibleAction?.actorAccountID; + failureReport = { + lastMessageTranslationKey, + lastMessageText, + lastVisibleActionCreated, + lastActorAccountID, + }; + } + + const failureData: OnyxUpdate[] = [ + ...tasksForFailureData, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${targetChatReportID}`, + value: failureReport, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, + value: { + [mentionCommentAction.reportActionID]: { + errors: ErrorUtils.getMicroSecondOnyxError('report.genericAddCommentFailureMessage'), + } as ReportAction, + [textCommentAction.reportActionID]: { + errors: ErrorUtils.getMicroSecondOnyxError('report.genericAddCommentFailureMessage'), + } as ReportAction, + [videoCommentAction.reportActionID]: { + errors: ErrorUtils.getMicroSecondOnyxError('report.genericAddCommentFailureMessage'), + } as ReportAction, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.NVP_INTRO_SELECTED, + value: {choice: null}, + }, + ]; const guidedSetupData: GuidedSetupData = [ {type: 'message', ...mentionMessage}, @@ -3270,7 +3343,7 @@ function completeOnboarding( guidedSetupData: JSON.stringify(guidedSetupData), }; - API.write(WRITE_COMMANDS.COMPLETE_GUIDED_SETUP, parameters, {optimisticData, successData}); + API.write(WRITE_COMMANDS.COMPLETE_GUIDED_SETUP, parameters, {optimisticData, successData, failureData}); } /** From 8773807626ebde15f27ff4b946fd1113e7a0e780 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Mon, 22 Apr 2024 18:27:27 +0100 Subject: [PATCH 045/146] swap withPolicy to withPolicyConnections --- .../accounting/qbo/import/QuickbooksChartOfAccountsPage.tsx | 4 ++-- .../workspace/accounting/qbo/import/QuickbooksClassesPage.tsx | 4 ++-- .../accounting/qbo/import/QuickbooksCustomersPage.tsx | 4 ++-- .../workspace/accounting/qbo/import/QuickbooksImportPage.tsx | 4 ++-- .../accounting/qbo/import/QuickbooksLocationsPage.tsx | 4 ++-- .../workspace/accounting/qbo/import/QuickbooksTaxesPage.tsx | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/pages/workspace/accounting/qbo/import/QuickbooksChartOfAccountsPage.tsx b/src/pages/workspace/accounting/qbo/import/QuickbooksChartOfAccountsPage.tsx index 723ac4822424..0a59d258ab0c 100644 --- a/src/pages/workspace/accounting/qbo/import/QuickbooksChartOfAccountsPage.tsx +++ b/src/pages/workspace/accounting/qbo/import/QuickbooksChartOfAccountsPage.tsx @@ -11,8 +11,8 @@ import useThemeStyles from '@hooks/useThemeStyles'; import * as Connections from '@libs/actions/connections'; import AdminPolicyAccessOrNotFoundWrapper from '@pages/workspace/AdminPolicyAccessOrNotFoundWrapper'; import FeatureEnabledAccessOrNotFoundWrapper from '@pages/workspace/FeatureEnabledAccessOrNotFoundWrapper'; -import withPolicy from '@pages/workspace/withPolicy'; import type {WithPolicyProps} from '@pages/workspace/withPolicy'; +import withPolicyConnections from '@pages/workspace/withPolicyConnections'; import variables from '@styles/variables'; import CONST from '@src/CONST'; @@ -70,4 +70,4 @@ function QuickbooksChartOfAccountsPage({policy}: WithPolicyProps) { QuickbooksChartOfAccountsPage.displayName = 'QuickbooksChartOfAccountsPage'; -export default withPolicy(QuickbooksChartOfAccountsPage); +export default withPolicyConnections(QuickbooksChartOfAccountsPage); diff --git a/src/pages/workspace/accounting/qbo/import/QuickbooksClassesPage.tsx b/src/pages/workspace/accounting/qbo/import/QuickbooksClassesPage.tsx index 42f4e7e3f728..3f547fa9c6de 100644 --- a/src/pages/workspace/accounting/qbo/import/QuickbooksClassesPage.tsx +++ b/src/pages/workspace/accounting/qbo/import/QuickbooksClassesPage.tsx @@ -12,8 +12,8 @@ import useThemeStyles from '@hooks/useThemeStyles'; import * as Connections from '@libs/actions/connections'; import AdminPolicyAccessOrNotFoundWrapper from '@pages/workspace/AdminPolicyAccessOrNotFoundWrapper'; import FeatureEnabledAccessOrNotFoundWrapper from '@pages/workspace/FeatureEnabledAccessOrNotFoundWrapper'; -import withPolicy from '@pages/workspace/withPolicy'; import type {WithPolicyProps} from '@pages/workspace/withPolicy'; +import withPolicyConnections from '@pages/workspace/withPolicyConnections'; import variables from '@styles/variables'; import CONST from '@src/CONST'; @@ -79,4 +79,4 @@ function QuickbooksClassesPage({policy}: WithPolicyProps) { QuickbooksClassesPage.displayName = 'QuickbooksClassesPage'; -export default withPolicy(QuickbooksClassesPage); +export default withPolicyConnections(QuickbooksClassesPage); diff --git a/src/pages/workspace/accounting/qbo/import/QuickbooksCustomersPage.tsx b/src/pages/workspace/accounting/qbo/import/QuickbooksCustomersPage.tsx index 644bd070327c..14fe0ba07bc4 100644 --- a/src/pages/workspace/accounting/qbo/import/QuickbooksCustomersPage.tsx +++ b/src/pages/workspace/accounting/qbo/import/QuickbooksCustomersPage.tsx @@ -12,8 +12,8 @@ import useThemeStyles from '@hooks/useThemeStyles'; import * as Connections from '@libs/actions/connections'; import AdminPolicyAccessOrNotFoundWrapper from '@pages/workspace/AdminPolicyAccessOrNotFoundWrapper'; import FeatureEnabledAccessOrNotFoundWrapper from '@pages/workspace/FeatureEnabledAccessOrNotFoundWrapper'; -import withPolicy from '@pages/workspace/withPolicy'; import type {WithPolicyProps} from '@pages/workspace/withPolicy'; +import withPolicyConnections from '@pages/workspace/withPolicyConnections'; import variables from '@styles/variables'; import CONST from '@src/CONST'; @@ -78,4 +78,4 @@ function QuickbooksCustomersPage({policy}: WithPolicyProps) { QuickbooksCustomersPage.displayName = 'QuickbooksCustomersPage'; -export default withPolicy(QuickbooksCustomersPage); +export default withPolicyConnections(QuickbooksCustomersPage); diff --git a/src/pages/workspace/accounting/qbo/import/QuickbooksImportPage.tsx b/src/pages/workspace/accounting/qbo/import/QuickbooksImportPage.tsx index c5003dcdd246..f579ca94e1cc 100644 --- a/src/pages/workspace/accounting/qbo/import/QuickbooksImportPage.tsx +++ b/src/pages/workspace/accounting/qbo/import/QuickbooksImportPage.tsx @@ -10,8 +10,8 @@ import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@navigation/Navigation'; import AdminPolicyAccessOrNotFoundWrapper from '@pages/workspace/AdminPolicyAccessOrNotFoundWrapper'; import FeatureEnabledAccessOrNotFoundWrapper from '@pages/workspace/FeatureEnabledAccessOrNotFoundWrapper'; -import withPolicy from '@pages/workspace/withPolicy'; import type {WithPolicyProps} from '@pages/workspace/withPolicy'; +import withPolicyConnections from '@pages/workspace/withPolicyConnections'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; @@ -104,4 +104,4 @@ function QuickbooksImportPage({policy}: WithPolicyProps) { QuickbooksImportPage.displayName = 'PolicyQuickbooksImportPage'; -export default withPolicy(QuickbooksImportPage); +export default withPolicyConnections(QuickbooksImportPage); diff --git a/src/pages/workspace/accounting/qbo/import/QuickbooksLocationsPage.tsx b/src/pages/workspace/accounting/qbo/import/QuickbooksLocationsPage.tsx index 06635b1fe7db..b7e9724bb8a0 100644 --- a/src/pages/workspace/accounting/qbo/import/QuickbooksLocationsPage.tsx +++ b/src/pages/workspace/accounting/qbo/import/QuickbooksLocationsPage.tsx @@ -12,8 +12,8 @@ import useThemeStyles from '@hooks/useThemeStyles'; import * as Connections from '@libs/actions/connections'; import AdminPolicyAccessOrNotFoundWrapper from '@pages/workspace/AdminPolicyAccessOrNotFoundWrapper'; import FeatureEnabledAccessOrNotFoundWrapper from '@pages/workspace/FeatureEnabledAccessOrNotFoundWrapper'; -import withPolicy from '@pages/workspace/withPolicy'; import type {WithPolicyProps} from '@pages/workspace/withPolicy'; +import withPolicyConnections from '@pages/workspace/withPolicyConnections'; import variables from '@styles/variables'; import CONST from '@src/CONST'; @@ -82,4 +82,4 @@ function QuickbooksLocationsPage({policy}: WithPolicyProps) { QuickbooksLocationsPage.displayName = 'QuickbooksLocationsPage'; -export default withPolicy(QuickbooksLocationsPage); +export default withPolicyConnections(QuickbooksLocationsPage); diff --git a/src/pages/workspace/accounting/qbo/import/QuickbooksTaxesPage.tsx b/src/pages/workspace/accounting/qbo/import/QuickbooksTaxesPage.tsx index 714e2638583e..98b4f5d8c6f7 100644 --- a/src/pages/workspace/accounting/qbo/import/QuickbooksTaxesPage.tsx +++ b/src/pages/workspace/accounting/qbo/import/QuickbooksTaxesPage.tsx @@ -11,8 +11,8 @@ import useThemeStyles from '@hooks/useThemeStyles'; import * as Connections from '@libs/actions/connections'; import AdminPolicyAccessOrNotFoundWrapper from '@pages/workspace/AdminPolicyAccessOrNotFoundWrapper'; import FeatureEnabledAccessOrNotFoundWrapper from '@pages/workspace/FeatureEnabledAccessOrNotFoundWrapper'; -import withPolicy from '@pages/workspace/withPolicy'; import type {WithPolicyProps} from '@pages/workspace/withPolicy'; +import withPolicyConnections from '@pages/workspace/withPolicyConnections'; import variables from '@styles/variables'; import CONST from '@src/CONST'; @@ -66,4 +66,4 @@ function QuickbooksTaxesPage({policy}: WithPolicyProps) { QuickbooksTaxesPage.displayName = 'QuickbooksTaxesPage'; -export default withPolicy(QuickbooksTaxesPage); +export default withPolicyConnections(QuickbooksTaxesPage); From f73b2b276c0292a11941843b2513c0f1edc5a589 Mon Sep 17 00:00:00 2001 From: Hayata Suenaga Date: Mon, 22 Apr 2024 15:24:37 -0400 Subject: [PATCH 046/146] chore: use withPolicyConnections --- src/pages/workspace/tags/TagSettingsPage.tsx | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/pages/workspace/tags/TagSettingsPage.tsx b/src/pages/workspace/tags/TagSettingsPage.tsx index 3e0b8eae30ee..65ae2dcb2151 100644 --- a/src/pages/workspace/tags/TagSettingsPage.tsx +++ b/src/pages/workspace/tags/TagSettingsPage.tsx @@ -1,7 +1,7 @@ import type {StackScreenProps} from '@react-navigation/stack'; import React, {useMemo} from 'react'; import {View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; +import {useOnyx, withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; import ConfirmModal from '@components/ConfirmModal'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; @@ -28,16 +28,17 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -import type {PolicyTagList} from '@src/types/onyx'; +import type {Policy as PolicyType} from '@src/types/onyx'; type TagSettingsPageOnyxProps = { /** All policy tags */ - policyTags: OnyxEntry; + policy: OnyxEntry; }; type TagSettingsPageProps = TagSettingsPageOnyxProps & StackScreenProps; -function TagSettingsPage({route, policyTags}: TagSettingsPageProps) { +function TagSettingsPage({route, policy}: TagSettingsPageProps) { + const [policyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${route.params.policyID}`); const styles = useThemeStyles(); const {translate} = useLocalize(); const policyTag = useMemo(() => PolicyUtils.getTagList(policyTags, 0), [policyTags]); @@ -138,8 +139,4 @@ function TagSettingsPage({route, policyTags}: TagSettingsPageProps) { TagSettingsPage.displayName = 'TagSettingsPage'; -export default withOnyx({ - policyTags: { - key: ({route}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${route.params.policyID}`, - }, -})(TagSettingsPage); +export default withPolicyConnections(TagSettingsPage); From e132d3aacf3cd404f3d114679ebbb78d5c4813c0 Mon Sep 17 00:00:00 2001 From: Hayata Suenaga Date: Mon, 22 Apr 2024 15:30:13 -0400 Subject: [PATCH 047/146] chore: use environment uri --- src/pages/workspace/tags/TagSettingsPage.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pages/workspace/tags/TagSettingsPage.tsx b/src/pages/workspace/tags/TagSettingsPage.tsx index 65ae2dcb2151..33734f841414 100644 --- a/src/pages/workspace/tags/TagSettingsPage.tsx +++ b/src/pages/workspace/tags/TagSettingsPage.tsx @@ -11,6 +11,7 @@ import OfflineWithFeedback from '@components/OfflineWithFeedback'; import ScreenWrapper from '@components/ScreenWrapper'; import Switch from '@components/Switch'; import Text from '@components/Text'; +import useEnvironment from '@hooks/useEnvironment'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; @@ -42,6 +43,7 @@ function TagSettingsPage({route, policy}: TagSettingsPageProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const policyTag = useMemo(() => PolicyUtils.getTagList(policyTags, 0), [policyTags]); + const {environmentURL} = useEnvironment(); const {windowWidth} = useWindowDimensions(); From 352d844eebb90b0cb62d1555e8806552e0250688 Mon Sep 17 00:00:00 2001 From: Krishna Gupta Date: Tue, 23 Apr 2024 02:16:54 +0530 Subject: [PATCH 048/146] fix: Quick action - 'Pay elsewhere' button is small when paying someone via Quick action. Signed-off-by: Krishna Gupta --- src/components/ButtonWithDropdownMenu/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ButtonWithDropdownMenu/index.tsx b/src/components/ButtonWithDropdownMenu/index.tsx index a4e6e2c87fec..e516b415786e 100644 --- a/src/components/ButtonWithDropdownMenu/index.tsx +++ b/src/components/ButtonWithDropdownMenu/index.tsx @@ -66,7 +66,7 @@ function ButtonWithDropdownMenu({ }, [windowWidth, windowHeight, isMenuVisible, anchorAlignment.vertical]); return ( - + {shouldAlwaysShowDropdownMenu || options.length > 1 ? (