diff --git a/src/components/MultipleAvatars.tsx b/src/components/MultipleAvatars.tsx index f8169503f932..158970d1e9f3 100644 --- a/src/components/MultipleAvatars.tsx +++ b/src/components/MultipleAvatars.tsx @@ -5,7 +5,7 @@ import type {ValueOf} from 'type-fest'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as ReportUtils from '@libs/ReportUtils'; +import {getUserDetailTooltipText} from '@libs/ReportUtils'; import type {AvatarSource} from '@libs/UserUtils'; import variables from '@styles/variables'; import CONST from '@src/CONST'; @@ -37,6 +37,9 @@ type MultipleAvatarsProps = { /** Whether the avatars are hovered */ isHovered?: boolean; + /** Whether the avatars are actived */ + isActive?: boolean; + /** Whether the avatars are in an element being pressed */ isPressed?: boolean; @@ -76,6 +79,7 @@ function MultipleAvatars({ shouldStackHorizontally = false, shouldDisplayAvatarsInRows = false, isHovered = false, + isActive = false, isPressed = false, isFocusMode = false, isInReportAction = false, @@ -110,7 +114,7 @@ function MultipleAvatars({ let avatarContainerStyles = StyleUtils.getContainerStyles(size, isInReportAction); const {singleAvatarStyle, secondAvatarStyles} = useMemo(() => avatarSizeToStylesMap[size as AvatarSizeToStyles] ?? avatarSizeToStylesMap.default, [size, avatarSizeToStylesMap]); - const tooltipTexts = useMemo(() => (shouldShowTooltip ? icons.map((icon) => ReportUtils.getUserDetailTooltipText(Number(icon.id), icon.name)) : ['']), [shouldShowTooltip, icons]); + const tooltipTexts = useMemo(() => (shouldShowTooltip ? icons.map((icon) => getUserDetailTooltipText(Number(icon.id), icon.name)) : ['']), [shouldShowTooltip, icons]); const avatarSize = useMemo(() => { if (isFocusMode) { @@ -173,7 +177,6 @@ function MultipleAvatars({ const oneAvatarSize = StyleUtils.getAvatarStyle(size); const oneAvatarBorderWidth = StyleUtils.getAvatarBorderWidth(size).borderWidth ?? 0; const overlapSize = oneAvatarSize.width / overlapDivider; - if (shouldStackHorizontally) { // Height of one avatar + border space const height = oneAvatarSize.height + 2 * oneAvatarBorderWidth; @@ -206,6 +209,7 @@ function MultipleAvatars({ isPressed, isInReportAction, shouldUseCardBackground, + isActive, }), StyleUtils.getAvatarBorderWidth(size), ]} diff --git a/src/pages/home/report/PureReportActionItem.tsx b/src/pages/home/report/PureReportActionItem.tsx index e093850ec276..c36391ecec70 100644 --- a/src/pages/home/report/PureReportActionItem.tsx +++ b/src/pages/home/report/PureReportActionItem.tsx @@ -371,6 +371,7 @@ function PureReportActionItem({ const downloadedPreviews = useRef([]); const prevDraftMessage = usePrevious(draftMessage); const isReportActionLinked = linkedReportActionID && action.reportActionID && linkedReportActionID === action.reportActionID; + const [isReportActionActive, setIsReportActionActive] = useState(!!isReportActionLinked); const isActionableWhisper = isActionableMentionWhisper(action) || isActionableTrackExpense(action) || isActionableReportMentionWhisper(action); const highlightedBackgroundColorIfNeeded = useMemo( @@ -410,7 +411,6 @@ function PureReportActionItem({ } clearAllRelatedReportActionErrors(reportID, action); }; - useEffect( () => () => { // ReportActionContextMenu, EmojiPicker and PopoverReactionList are global components, @@ -1010,9 +1010,10 @@ function PureReportActionItem({ childReportID={`${action.childReportID}`} numberOfReplies={numberOfThreadReplies} mostRecentReply={`${action.childLastVisibleActionCreated}`} - isHovered={hovered} + isHovered={hovered || isContextMenuActive} icons={getIconsForParticipants(oldestFourAccountIDs, personalDetails)} onSecondaryInteraction={showPopover} + isActive={isReportActionActive && !isContextMenuActive} /> )} @@ -1044,7 +1045,8 @@ function PureReportActionItem({ shouldShowSubscriptAvatar={shouldShowSubscriptAvatar} report={report} iouReport={iouReport} - isHovered={hovered} + isHovered={hovered || isContextMenuActive} + isActive={isReportActionActive && !isContextMenuActive} hasBeenFlagged={ ![CONST.MODERATION.MODERATOR_DECISION_APPROVED, CONST.MODERATION.MODERATOR_DECISION_PENDING].some((item) => item === moderationDecision) && !isPendingRemove(action) } @@ -1130,6 +1132,12 @@ function PureReportActionItem({ shouldHandleScroll isDisabled={draftMessage !== undefined} shouldFreezeCapture={isPaymentMethodPopoverActive} + onHoverIn={() => { + setIsReportActionActive(false); + }} + onHoverOut={() => { + setIsReportActionActive(!!isReportActionLinked); + }} > {(hovered) => ( diff --git a/src/pages/home/report/ReportActionItemSingle.tsx b/src/pages/home/report/ReportActionItemSingle.tsx index 2eae046fe007..e417823262de 100644 --- a/src/pages/home/report/ReportActionItemSingle.tsx +++ b/src/pages/home/report/ReportActionItemSingle.tsx @@ -70,6 +70,9 @@ type ReportActionItemSingleProps = Partial & { /** If the action is being hovered */ isHovered?: boolean; + + /** If the action is being actived */ + isActive?: boolean; }; const showUserDetails = (accountID: number | undefined) => { @@ -93,6 +96,7 @@ function ReportActionItemSingle({ report, iouReport, isHovered = false, + isActive = false, }: ReportActionItemSingleProps) { const theme = useTheme(); const styles = useThemeStyles(); @@ -223,6 +227,15 @@ function ReportActionItemSingle({ [action, isWorkspaceActor, actorAccountID], ); + const getBackgroundColor = () => { + if (isActive) { + return theme.messageHighlightBG; + } + if (isHovered) { + return theme.hoverComponentBG; + } + return theme.sidebar; + }; const getAvatar = () => { if (shouldShowSubscriptAvatar) { return ( @@ -230,6 +243,7 @@ function ReportActionItemSingle({ mainAvatar={icon} secondaryAvatar={secondaryAvatar} noMargin + backgroundColor={getBackgroundColor()} /> ); } diff --git a/src/pages/home/report/ReportActionItemThread.tsx b/src/pages/home/report/ReportActionItemThread.tsx index 13072a653749..223c2713ac70 100644 --- a/src/pages/home/report/ReportActionItemThread.tsx +++ b/src/pages/home/report/ReportActionItemThread.tsx @@ -6,9 +6,9 @@ import PressableWithSecondaryInteraction from '@components/PressableWithSecondar import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; +import {navigateToAndOpenChildReport} from '@libs/actions/Report'; import Timing from '@libs/actions/Timing'; import Performance from '@libs/Performance'; -import * as Report from '@userActions/Report'; import CONST from '@src/CONST'; import type {Icon} from '@src/types/onyx/OnyxCommon'; @@ -28,11 +28,14 @@ type ReportActionItemThreadProps = { /** Whether the thread item / message is being hovered */ isHovered: boolean; + /** Whether the thread item / message is being actived */ + isActive?: boolean; + /** The function that should be called when the thread is LongPressed or right-clicked */ onSecondaryInteraction: (event: GestureResponderEvent | MouseEvent) => void; }; -function ReportActionItemThread({numberOfReplies, icons, mostRecentReply, childReportID, isHovered, onSecondaryInteraction}: ReportActionItemThreadProps) { +function ReportActionItemThread({numberOfReplies, icons, mostRecentReply, childReportID, isHovered, onSecondaryInteraction, isActive}: ReportActionItemThreadProps) { const styles = useThemeStyles(); const {translate, datetimeToCalendarTime} = useLocalize(); @@ -48,7 +51,7 @@ function ReportActionItemThread({numberOfReplies, icons, mostRecentReply, childR onPress={() => { Performance.markStart(CONST.TIMING.OPEN_REPORT_THREAD); Timing.start(CONST.TIMING.OPEN_REPORT_THREAD); - Report.navigateToAndOpenChildReport(childReportID); + navigateToAndOpenChildReport(childReportID); }} role={CONST.ROLE.BUTTON} accessibilityLabel={`${numberOfReplies} ${replyText}`} @@ -60,6 +63,7 @@ function ReportActionItemThread({numberOfReplies, icons, mostRecentReply, childR icons={icons} shouldStackHorizontally isHovered={isHovered} + isActive={isActive} isInReportAction /> diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts index bbfa1e5515be..e70dbc436fb6 100644 --- a/src/styles/utils/index.ts +++ b/src/styles/utils/index.ts @@ -753,14 +753,25 @@ type AvatarBorderStyleParams = { isPressed: boolean; isInReportAction: boolean; shouldUseCardBackground: boolean; + isActive?: boolean; }; -function getHorizontalStackedAvatarBorderStyle({theme, isHovered, isPressed, isInReportAction = false, shouldUseCardBackground = false}: AvatarBorderStyleParams): ViewStyle { +function getHorizontalStackedAvatarBorderStyle({ + theme, + isHovered, + isPressed, + isInReportAction = false, + shouldUseCardBackground = false, + isActive = false, +}: AvatarBorderStyleParams): ViewStyle { let borderColor = shouldUseCardBackground ? theme.cardBG : theme.appBG; if (isHovered) { borderColor = isInReportAction ? theme.hoverComponentBG : theme.border; } + if (isActive) { + borderColor = theme.messageHighlightBG; + } if (isPressed) { borderColor = isInReportAction ? theme.hoverComponentBG : theme.buttonPressedBG;