From 2465982fbc624a011e53b0bb51ee302b213d58c7 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Wed, 3 Jul 2024 10:36:39 +0700 Subject: [PATCH 1/3] fix: disabled download button when downloading --- src/components/ContextMenuItem.tsx | 4 ++++ .../ContextMenu/BaseReportActionContextMenu.tsx | 11 ++++++++++- .../home/report/ContextMenu/ContextMenuActions.tsx | 4 +++- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/components/ContextMenuItem.tsx b/src/components/ContextMenuItem.tsx index 2cc6a0ecec44..2fc6dd72b545 100644 --- a/src/components/ContextMenuItem.tsx +++ b/src/components/ContextMenuItem.tsx @@ -55,6 +55,8 @@ type ContextMenuItemProps = { /** Handles what to do when the item loose focus */ onBlur?: () => void; + + disabled?: boolean; }; type ContextMenuItemHandle = { @@ -78,6 +80,7 @@ function ContextMenuItem( buttonRef = {current: null}, onFocus = () => {}, onBlur = () => {}, + disabled = false, }: ContextMenuItemProps, ref: ForwardedRef, ) { @@ -135,6 +138,7 @@ function ContextMenuItem( interactive={isThrottledButtonActive} onFocus={onFocus} onBlur={onBlur} + disabled={disabled} /> ); } diff --git a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx index 136d8e5b59eb..16719f7b4a7f 100755 --- a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx +++ b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx @@ -5,7 +5,7 @@ import {InteractionManager, View} from 'react-native'; // eslint-disable-next-line no-restricted-imports import type {GestureResponderEvent, Text as RNText, View as ViewType} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; -import {withOnyx} from 'react-native-onyx'; +import {useOnyx, withOnyx} from 'react-native-onyx'; import type {ContextMenuItemHandle} from '@components/ContextMenuItem'; import ContextMenuItem from '@components/ContextMenuItem'; import FocusTrapForModal from '@components/FocusTrap/FocusTrapForModal'; @@ -15,6 +15,7 @@ import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useStyleUtils from '@hooks/useStyleUtils'; import useWindowDimensions from '@hooks/useWindowDimensions'; +import getAttachmentDetails from '@libs/fileDownload/getAttachmentDetails'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as Session from '@userActions/Session'; @@ -135,6 +136,13 @@ function BaseReportActionContextMenu({ return reportActions[reportActionID]; }, [reportActions, reportActionID]); + const message = Array.isArray(reportAction?.message) ? reportAction?.message?.at(-1) ?? null : reportAction?.message ?? null; + const html = message?.html ?? ''; + const {sourceURL} = getAttachmentDetails(html); + const sourceID = (sourceURL?.match(CONST.REGEX.ATTACHMENT_ID) ?? [])[1]; + + const [download] = useOnyx(`${ONYXKEYS.COLLECTION.DOWNLOAD}${sourceID}`); + const originalReportID = useMemo(() => ReportUtils.getOriginalReportID(reportID, reportAction), [reportID, reportAction]); const shouldEnableArrowNavigation = !isMini && (isVisible || shouldKeepOpen); @@ -288,6 +296,7 @@ function BaseReportActionContextMenu({ shouldPreventDefaultFocusOnPress={contextAction.shouldPreventDefaultFocusOnPress} onFocus={() => setFocusedIndex(index)} onBlur={() => (index === filteredContextMenuActions.length - 1 || index === 1) && setFocusedIndex(-1)} + disabled={contextAction?.shouldDisable ? contextAction?.shouldDisable(download) : false} /> ); })} diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx index bf634b4ac8ae..68c3b1d1e89a 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx @@ -28,7 +28,7 @@ import * as Report from '@userActions/Report'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ROUTES from '@src/ROUTES'; -import type {Beta, OnyxInputOrEntry, ReportAction, ReportActionReactions, Transaction} from '@src/types/onyx'; +import type {Beta, Download as DownloadOnyx, OnyxInputOrEntry, ReportAction, ReportActionReactions, Transaction} from '@src/types/onyx'; import type IconAsset from '@src/types/utils/IconAsset'; import type {ContextMenuAnchor} from './ReportActionContextMenu'; import {hideContextMenu, showDeleteModal} from './ReportActionContextMenu'; @@ -105,6 +105,7 @@ type ContextMenuAction = (ContextMenuActionWithContent | ContextMenuActionWithIc isAnonymousAction: boolean; shouldShow: ShouldShow; shouldPreventDefaultFocusOnPress?: boolean; + shouldDisable?: (download: OnyxEntry) => boolean; }; // A list of all the context actions in this menu. @@ -512,6 +513,7 @@ const ContextMenuActions: ContextMenuAction[] = [ } }, getDescription: () => {}, + shouldDisable: (download) => download?.isDownloading ?? false, }, { isAnonymousAction: false, From 4687fc61ce7f05c004efd967aeae7009220e7a0f Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Thu, 4 Jul 2024 11:33:00 +0700 Subject: [PATCH 2/3] fix: add loading spinner --- src/components/ContextMenuItem.tsx | 6 +++ src/components/MenuItem.tsx | 52 +++++++++++-------- .../BaseReportActionContextMenu.tsx | 1 + 3 files changed, 38 insertions(+), 21 deletions(-) diff --git a/src/components/ContextMenuItem.tsx b/src/components/ContextMenuItem.tsx index 2fc6dd72b545..fe7be45e1b57 100644 --- a/src/components/ContextMenuItem.tsx +++ b/src/components/ContextMenuItem.tsx @@ -56,7 +56,11 @@ type ContextMenuItemProps = { /** Handles what to do when the item loose focus */ onBlur?: () => void; + /** Whether the menu item is disabled or not */ disabled?: boolean; + + /** Whether the menu item should show loading icon */ + shouldShowLoadingSpinnerIcon?: boolean; }; type ContextMenuItemHandle = { @@ -81,6 +85,7 @@ function ContextMenuItem( onFocus = () => {}, onBlur = () => {}, disabled = false, + shouldShowLoadingSpinnerIcon = false, }: ContextMenuItemProps, ref: ForwardedRef, ) { @@ -139,6 +144,7 @@ function ContextMenuItem( onFocus={onFocus} onBlur={onBlur} disabled={disabled} + shouldShowLoadingSpinnerIcon={shouldShowLoadingSpinnerIcon} /> ); } diff --git a/src/components/MenuItem.tsx b/src/components/MenuItem.tsx index 9fd18524158d..c2b89d1dbfcc 100644 --- a/src/components/MenuItem.tsx +++ b/src/components/MenuItem.tsx @@ -3,7 +3,7 @@ import type {ImageContentFit} from 'expo-image'; import type {ReactElement, ReactNode} from 'react'; import React, {forwardRef, useContext, useMemo} from 'react'; import type {GestureResponderEvent, StyleProp, TextStyle, ViewStyle} from 'react-native'; -import {View} from 'react-native'; +import {ActivityIndicator, View} from 'react-native'; import type {AnimatedStyle} from 'react-native-reanimated'; import type {ValueOf} from 'type-fest'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; @@ -301,6 +301,8 @@ type MenuItemBaseProps = { /** Render custom content inside the tooltip. */ renderTooltipContent?: () => ReactNode; + + shouldShowLoadingSpinnerIcon?: boolean; }; type MenuItemProps = (IconProps | AvatarProps | NoIcon) & MenuItemBaseProps; @@ -372,6 +374,7 @@ function MenuItem( shouldEscapeText = undefined, shouldGreyOutWhenDisabled = true, shouldUseDefaultCursorWhenDisabled = false, + shouldShowLoadingSpinnerIcon = false, isAnonymousAction = false, shouldBlockSelection = false, shouldParseTitle = false, @@ -577,26 +580,33 @@ function MenuItem( )} {icon && !Array.isArray(icon) && ( - {typeof icon !== 'string' && iconType === CONST.ICON_TYPE_ICON && ( - - )} + {typeof icon !== 'string' && + iconType === CONST.ICON_TYPE_ICON && + (!shouldShowLoadingSpinnerIcon ? ( + + ) : ( + + ))} {icon && iconType === CONST.ICON_TYPE_WORKSPACE && ( setFocusedIndex(index)} onBlur={() => (index === filteredContextMenuActions.length - 1 || index === 1) && setFocusedIndex(-1)} disabled={contextAction?.shouldDisable ? contextAction?.shouldDisable(download) : false} + shouldShowLoadingSpinnerIcon={contextAction?.shouldDisable ? contextAction?.shouldDisable(download) : false} /> ); })} From 7dc244b94c232a8289d032e3b779d46d0aeb82ce Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Thu, 11 Jul 2024 11:09:11 +0700 Subject: [PATCH 3/3] create new utils func --- src/libs/ReportUtils.ts | 10 ++++++++++ .../report/ContextMenu/BaseReportActionContextMenu.tsx | 6 +----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index afe384b87531..e4c81cf6d45a 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -55,6 +55,7 @@ import * as store from './actions/ReimbursementAccount/store'; import * as CurrencyUtils from './CurrencyUtils'; import DateUtils from './DateUtils'; import {hasValidDraftComment} from './DraftCommentUtils'; +import getAttachmentDetails from './fileDownload/getAttachmentDetails'; import isReportMessageAttachment from './isReportMessageAttachment'; import localeCompare from './LocaleCompare'; import * as LocalePhoneNumber from './LocalePhoneNumber'; @@ -7089,6 +7090,14 @@ function findPolicyExpenseChatByPolicyID(policyID: string): OnyxEntry { return Object.values(ReportConnection.getAllReports() ?? {}).find((report) => isPolicyExpenseChat(report) && report?.policyID === policyID); } +function getSourceIDFromReportAction(reportAction: OnyxEntry): string { + const message = Array.isArray(reportAction?.message) ? reportAction?.message?.at(-1) ?? null : reportAction?.message ?? null; + const html = message?.html ?? ''; + const {sourceURL} = getAttachmentDetails(html); + const sourceID = (sourceURL?.match(CONST.REGEX.ATTACHMENT_ID) ?? [])[1]; + return sourceID; +} + export { addDomainToShortMention, areAllRequestsBeingSmartScanned, @@ -7370,6 +7379,7 @@ export { findPolicyExpenseChatByPolicyID, hasOnlyNonReimbursableTransactions, getMostRecentlyVisitedReport, + getSourceIDFromReportAction, }; export type { diff --git a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx index 2cbb95d1bcc4..c675b056342f 100755 --- a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx +++ b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx @@ -15,7 +15,6 @@ import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useStyleUtils from '@hooks/useStyleUtils'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import getAttachmentDetails from '@libs/fileDownload/getAttachmentDetails'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as Session from '@userActions/Session'; @@ -136,10 +135,7 @@ function BaseReportActionContextMenu({ return reportActions[reportActionID]; }, [reportActions, reportActionID]); - const message = Array.isArray(reportAction?.message) ? reportAction?.message?.at(-1) ?? null : reportAction?.message ?? null; - const html = message?.html ?? ''; - const {sourceURL} = getAttachmentDetails(html); - const sourceID = (sourceURL?.match(CONST.REGEX.ATTACHMENT_ID) ?? [])[1]; + const sourceID = ReportUtils.getSourceIDFromReportAction(reportAction); const [download] = useOnyx(`${ONYXKEYS.COLLECTION.DOWNLOAD}${sourceID}`);