Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: disabled download button when downloading #44760

Merged
merged 9 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/components/ContextMenuItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ 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 = {
Expand All @@ -78,6 +84,8 @@ function ContextMenuItem(
buttonRef = {current: null},
onFocus = () => {},
onBlur = () => {},
disabled = false,
shouldShowLoadingSpinnerIcon = false,
}: ContextMenuItemProps,
ref: ForwardedRef<ContextMenuItemHandle>,
) {
Expand Down Expand Up @@ -135,6 +143,8 @@ function ContextMenuItem(
interactive={isThrottledButtonActive}
onFocus={onFocus}
onBlur={onBlur}
disabled={disabled}
shouldShowLoadingSpinnerIcon={shouldShowLoadingSpinnerIcon}
/>
);
}
Expand Down
52 changes: 31 additions & 21 deletions src/components/MenuItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,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';
Expand Down Expand Up @@ -304,6 +304,8 @@ type MenuItemBaseProps = {

/** Render custom content inside the tooltip. */
renderTooltipContent?: () => ReactNode;

shouldShowLoadingSpinnerIcon?: boolean;
};

type MenuItemProps = (IconProps | AvatarProps | NoIcon) & MenuItemBaseProps;
Expand Down Expand Up @@ -376,6 +378,7 @@ function MenuItem(
shouldEscapeText = undefined,
shouldGreyOutWhenDisabled = true,
shouldUseDefaultCursorWhenDisabled = false,
shouldShowLoadingSpinnerIcon = false,
isAnonymousAction = false,
shouldBlockSelection = false,
shouldParseTitle = false,
Expand Down Expand Up @@ -579,26 +582,33 @@ function MenuItem(
)}
{icon && !Array.isArray(icon) && (
<View style={[styles.popoverMenuIcon, iconStyles, StyleUtils.getAvatarWidthStyle(avatarSize)]}>
{typeof icon !== 'string' && iconType === CONST.ICON_TYPE_ICON && (
<Icon
contentFit={contentFit}
hovered={isHovered}
pressed={pressed}
src={icon}
width={iconWidth}
height={iconHeight}
fill={
displayInDefaultIconColor
? undefined
: iconFill ??
StyleUtils.getIconFillColor(
getButtonState(focused || isHovered, pressed, success, disabled, interactive),
true,
isPaneMenu,
)
}
/>
)}
{typeof icon !== 'string' &&
iconType === CONST.ICON_TYPE_ICON &&
(!shouldShowLoadingSpinnerIcon ? (
<Icon
contentFit={contentFit}
hovered={isHovered}
pressed={pressed}
src={icon}
width={iconWidth}
height={iconHeight}
fill={
displayInDefaultIconColor
? undefined
: iconFill ??
StyleUtils.getIconFillColor(
getButtonState(focused || isHovered, pressed, success, disabled, interactive),
true,
isPaneMenu,
)
}
/>
) : (
<ActivityIndicator
size="small"
color={theme.textSupporting}
/>
))}
{icon && iconType === CONST.ICON_TYPE_WORKSPACE && (
<Avatar
imageStyles={[styles.alignSelfCenter]}
Expand Down
10 changes: 10 additions & 0 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -7089,6 +7090,14 @@ function findPolicyExpenseChatByPolicyID(policyID: string): OnyxEntry<Report> {
return Object.values(ReportConnection.getAllReports() ?? {}).find((report) => isPolicyExpenseChat(report) && report?.policyID === policyID);
}

function getSourceIDFromReportAction(reportAction: OnyxEntry<ReportAction>): string {
const message = Array.isArray(reportAction?.message) ? reportAction?.message?.at(-1) ?? null : reportAction?.message ?? null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's still duplicated code in ContextMenuActions

function getActionHtml(reportAction: OnyxInputOrEntry<ReportAction>): string {
const message = Array.isArray(reportAction?.message) ? reportAction?.message?.at(-1) ?? null : reportAction?.message ?? null;
return message?.html ?? '';
}

const html = getActionHtml(reportAction);
const {originalFileName, sourceURL} = getAttachmentDetails(html);
const sourceURLWithAuth = addEncryptedAuthTokenToURL(sourceURL ?? '');
const sourceID = (sourceURL?.match(CONST.REGEX.ATTACHMENT_ID) ?? [])[1];

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@situchan I think using the utils function in the onPress function does not bring much benefit because variables like originalFileName, sourceURL, sourceURLWithAuth, etc., are all reused below.

const html = message?.html ?? '';
const {sourceURL} = getAttachmentDetails(html);
const sourceID = (sourceURL?.match(CONST.REGEX.ATTACHMENT_ID) ?? [])[1];
return sourceID;
}

export {
addDomainToShortMention,
areAllRequestsBeingSmartScanned,
Expand Down Expand Up @@ -7370,6 +7379,7 @@ export {
findPolicyExpenseChatByPolicyID,
hasOnlyNonReimbursableTransactions,
getMostRecentlyVisitedReport,
getSourceIDFromReportAction,
};

export type {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -135,6 +135,10 @@ function BaseReportActionContextMenu({
return reportActions[reportActionID];
}, [reportActions, reportActionID]);

const sourceID = ReportUtils.getSourceIDFromReportAction(reportAction);

const [download] = useOnyx(`${ONYXKEYS.COLLECTION.DOWNLOAD}${sourceID}`);

const originalReportID = useMemo(() => ReportUtils.getOriginalReportID(reportID, reportAction), [reportID, reportAction]);

const shouldEnableArrowNavigation = !isMini && (isVisible || shouldKeepOpen);
Expand Down Expand Up @@ -287,6 +291,8 @@ function BaseReportActionContextMenu({
shouldPreventDefaultFocusOnPress={contextAction.shouldPreventDefaultFocusOnPress}
onFocus={() => 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}
/>
);
})}
Expand Down
4 changes: 3 additions & 1 deletion src/pages/home/report/ContextMenu/ContextMenuActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import CONST from '@src/CONST';
import type {TranslationPaths} from '@src/languages/types';
import ONYXKEYS from '@src/ONYXKEYS';
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';
Expand Down Expand Up @@ -108,6 +108,7 @@ type ContextMenuAction = (ContextMenuActionWithContent | ContextMenuActionWithIc
isAnonymousAction: boolean;
shouldShow: ShouldShow;
shouldPreventDefaultFocusOnPress?: boolean;
shouldDisable?: (download: OnyxEntry<DownloadOnyx>) => boolean;
};

// A list of all the context actions in this menu.
Expand Down Expand Up @@ -528,6 +529,7 @@ const ContextMenuActions: ContextMenuAction[] = [
}
},
getDescription: () => {},
shouldDisable: (download) => download?.isDownloading ?? false,
},
{
isAnonymousAction: true,
Expand Down
Loading