diff --git a/src/components/Badge.tsx b/src/components/Badge.tsx index 47e2a2f74963..bfdc63cf7119 100644 --- a/src/components/Badge.tsx +++ b/src/components/Badge.tsx @@ -17,6 +17,12 @@ type BadgeProps = { /** Is Error type */ error?: boolean; + /** Whether badge uses strong (filled) style instead of outlined */ + isStrong?: boolean; + + /** Whether badge uses condensed (smaller) sizing */ + isCondensed?: boolean; + /** Whether badge is clickable */ pressable?: boolean; @@ -51,6 +57,8 @@ type BadgeProps = { function Badge({ success = false, error = false, + isStrong = false, + isCondensed = false, pressable = false, text, environment = CONST.ENVIRONMENT.DEV, @@ -68,17 +76,20 @@ function Badge({ const isDeleted = style && Array.isArray(style) ? style.includes(styles.offlineFeedbackDeleted) : false; - const iconColor = StyleUtils.getIconColorStyle(success, error); + const iconColor = StyleUtils.getIconColorStyle(success, error, isStrong); + + const iconSize = isCondensed || shouldUseXXSmallIcon ? variables.iconSizeXXSmall : variables.iconSizeExtraSmall; const wrapperStyles: (state: PressableStateCallbackType) => StyleProp = useCallback( ({pressed}) => [ styles.defaultBadge, + isCondensed && styles.condensedBadge, styles.alignSelfCenter, styles.ml2, - StyleUtils.getBadgeColorStyle(success, error, pressed, environment === CONST.ENVIRONMENT.ADHOC), + StyleUtils.getBadgeColorStyle(success, error, pressed, environment === CONST.ENVIRONMENT.ADHOC, isStrong), badgeStyles, ], - [styles.defaultBadge, styles.alignSelfCenter, styles.ml2, StyleUtils, success, error, environment, badgeStyles], + [styles.defaultBadge, styles.condensedBadge, styles.alignSelfCenter, styles.ml2, StyleUtils, success, error, environment, badgeStyles, isCondensed, isStrong], ); return ( @@ -93,15 +104,25 @@ function Badge({ {!!icon && ( )} {text} diff --git a/src/components/Domain/DomainsListRow.tsx b/src/components/Domain/DomainsListRow.tsx index 2ce1d2eaf357..f3dd5068eb97 100644 --- a/src/components/Domain/DomainsListRow.tsx +++ b/src/components/Domain/DomainsListRow.tsx @@ -45,7 +45,7 @@ function DomainsListRow({title, isHovered, badgeText, brickRoadIndicator, menuIt {!!badgeText && ( @@ -53,7 +53,7 @@ function DomainsListRow({title, isHovered, badgeText, brickRoadIndicator, menuIt )} diff --git a/src/components/MenuItem.tsx b/src/components/MenuItem.tsx index 98da6f1b5648..ae9ac11f917e 100644 --- a/src/components/MenuItem.tsx +++ b/src/components/MenuItem.tsx @@ -85,7 +85,13 @@ type MenuItemBaseProps = ForwardedFSClassProps & badgeIcon?: IconAsset; /** Whether the badge should be shown as success */ - badgeSuccess?: boolean; + isBadgeSuccess?: boolean; + + /** Whether the badge should use strong (filled) variant */ + isBadgeStrong?: boolean; + + /** Whether the badge should use condensed (smaller) sizing */ + isBadgeCondensed?: boolean; /** Callback to fire when the badge is pressed */ onBadgePress?: (event?: GestureResponderEvent | KeyboardEvent) => void; @@ -109,7 +115,7 @@ type MenuItemBaseProps = ForwardedFSClassProps & titleStyle?: StyleProp; /** Any additional styles to apply on the badge element */ - badgeStyle?: ViewStyle; + badgeStyle?: StyleProp; /** Any additional styles to apply to the label */ labelStyle?: StyleProp; @@ -451,7 +457,9 @@ function MenuItem({ onPress, badgeText, badgeIcon, - badgeSuccess, + isBadgeSuccess, + isBadgeStrong, + isBadgeCondensed, onBadgePress, shouldShowBadgeInSeparateRow = false, shouldShowBadgeBelow = false, @@ -980,8 +988,16 @@ function MenuItem({ @@ -996,8 +1012,10 @@ function MenuItem({ @@ -1094,8 +1112,10 @@ function MenuItem({ diff --git a/src/components/ParentNavigationSubtitle.tsx b/src/components/ParentNavigationSubtitle.tsx index b62fe4be392f..24c03756d986 100644 --- a/src/components/ParentNavigationSubtitle.tsx +++ b/src/components/ParentNavigationSubtitle.tsx @@ -21,6 +21,7 @@ import NAVIGATORS from '@src/NAVIGATORS'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; +import StatusBadge from './StatusBadge'; import Text from './Text'; import TextLink from './TextLink'; @@ -189,18 +190,12 @@ function ParentNavigationSubtitle({ return ( {!!statusText && ( - - {statusText} - + )} {shouldShowReportStatus && ( - - - {reportStatus} - - + )} {!shouldShowAccessPlaceHolder && {expenseCount}} diff --git a/src/components/SelectionListWithSections/Search/ActionCell/BadgeActionCell.tsx b/src/components/SelectionListWithSections/Search/ActionCell/BadgeActionCell.tsx index 20f9b160fff9..0dcb058e7406 100644 --- a/src/components/SelectionListWithSections/Search/ActionCell/BadgeActionCell.tsx +++ b/src/components/SelectionListWithSections/Search/ActionCell/BadgeActionCell.tsx @@ -1,7 +1,6 @@ import React from 'react'; import {View} from 'react-native'; import Badge from '@components/Badge'; -import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; @@ -14,19 +13,18 @@ import actionTranslationsMap from './actionTranslationsMap'; type BadgeActionCellProps = { action: SearchTransactionAction; isLargeScreenWidth: boolean; - isSelected: boolean; - extraSmall: boolean; shouldDisablePointerEvents?: boolean; }; -function BadgeActionCell({action, isLargeScreenWidth, isSelected, extraSmall, shouldDisablePointerEvents}: BadgeActionCellProps) { +function BadgeActionCell({action, isLargeScreenWidth, shouldDisablePointerEvents}: BadgeActionCellProps) { const {translate} = useLocalize(); const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); - const expensifyIcons = useMemoizedLazyExpensifyIcons(['Checkmark', 'Checkbox']); const text = translate(actionTranslationsMap[action]); + const badgeTheme = action === CONST.SEARCH.ACTION_TYPES.PAID ? theme.reportStatusBadge.paid : theme.reportStatusBadge.approved; + return ( ); diff --git a/src/components/SelectionListWithSections/Search/ActionCell/index.tsx b/src/components/SelectionListWithSections/Search/ActionCell/index.tsx index f18305168a73..bf8547071771 100644 --- a/src/components/SelectionListWithSections/Search/ActionCell/index.tsx +++ b/src/components/SelectionListWithSections/Search/ActionCell/index.tsx @@ -52,8 +52,6 @@ function ActionCell({ ); diff --git a/src/components/SelectionListWithSections/Search/StatusCell.tsx b/src/components/SelectionListWithSections/Search/StatusCell.tsx index f26d321cd5b5..87b3797a61fd 100644 --- a/src/components/SelectionListWithSections/Search/StatusCell.tsx +++ b/src/components/SelectionListWithSections/Search/StatusCell.tsx @@ -1,6 +1,6 @@ import React, {useMemo} from 'react'; import {View} from 'react-native'; -import Text from '@components/Text'; +import StatusBadge from '@components/StatusBadge'; import useLocalize from '@hooks/useLocalize'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -25,29 +25,17 @@ function StatusCell({stateNum, statusNum, isPending}: StatusCellProps) { const statusText = useMemo(() => getReportStatusTranslation({stateNum, statusNum, translate}), [stateNum, statusNum, translate]); const reportStatusColorStyle = useMemo(() => getReportStatusColorStyle(theme, stateNum, statusNum), [theme, stateNum, statusNum]); - const backgroundColorStyle = useMemo( - () => ({ - backgroundColor: reportStatusColorStyle?.backgroundColor, - }), - [reportStatusColorStyle?.backgroundColor], - ); - - const textColorStyle = useMemo( - () => ({ - color: reportStatusColorStyle?.textColor, - }), - [reportStatusColorStyle?.textColor], - ); - if (!statusText || !reportStatusColorStyle) { return null; } return ( - - - {statusText} - + + ); } diff --git a/src/components/SelectionListWithSections/Search/TaskListItemRow.tsx b/src/components/SelectionListWithSections/Search/TaskListItemRow.tsx index 884b0762d718..a08605877b45 100644 --- a/src/components/SelectionListWithSections/Search/TaskListItemRow.tsx +++ b/src/components/SelectionListWithSections/Search/TaskListItemRow.tsx @@ -70,7 +70,6 @@ function DescriptionCell({taskItem, showTooltip, isLargeScreenWidth}: TaskCellPr } function ActionCell({taskItem, isLargeScreenWidth}: TaskCellProps) { - const icons = useMemoizedLazyExpensifyIcons(['Checkmark'] as const); const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); @@ -87,20 +86,15 @@ function ActionCell({taskItem, isLargeScreenWidth}: TaskCellProps) { return ( ); diff --git a/src/components/SelectionListWithSections/Search/WithdrawalIDListItemHeader.tsx b/src/components/SelectionListWithSections/Search/WithdrawalIDListItemHeader.tsx index b4b8f034e20a..af36dd3d19a3 100644 --- a/src/components/SelectionListWithSections/Search/WithdrawalIDListItemHeader.tsx +++ b/src/components/SelectionListWithSections/Search/WithdrawalIDListItemHeader.tsx @@ -6,7 +6,7 @@ import getBankIcon from '@components/Icon/BankIcons'; import RenderHTML from '@components/RenderHTML'; import type {SearchColumnType} from '@components/Search/types'; import type {ListItem, TransactionWithdrawalIDGroupListItemType} from '@components/SelectionListWithSections/types'; -import Text from '@components/Text'; +import StatusBadge from '@components/StatusBadge'; import TextWithTooltip from '@components/TextWithTooltip'; import useEnvironment from '@hooks/useEnvironment'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; @@ -84,9 +84,11 @@ function WithdrawalIDListItemHeader({ const badgeProps = getSettlementStatusBadgeProps(withdrawalIDItem.state, translate, theme); const settlementStatus = getSettlementStatus(withdrawalIDItem.state); const statusBadge = !!badgeProps && ( - - {badgeProps.text} - + ); const withdrawalInfoText = translate('settlement.withdrawalInfo', {date: formattedWithdrawalDate, withdrawalID: withdrawalIDItem.entryID}); diff --git a/src/components/StatusBadge.tsx b/src/components/StatusBadge.tsx new file mode 100644 index 000000000000..e0386a7a5933 --- /dev/null +++ b/src/components/StatusBadge.tsx @@ -0,0 +1,40 @@ +import React from 'react'; +import type {ColorValue, StyleProp, ViewStyle} from 'react-native'; +import useStyleUtils from '@hooks/useStyleUtils'; +import useTheme from '@hooks/useTheme'; +import useThemeStyles from '@hooks/useThemeStyles'; +import Badge from './Badge'; + +type StatusBadgeProps = { + /** Status text to display */ + text: string; + + /** Background color for the status badge */ + backgroundColor?: ColorValue; + + /** Text color for the status badge */ + textColor?: ColorValue; + + /** Additional badge styles */ + badgeStyles?: StyleProp; +}; + +function StatusBadge({text, backgroundColor, textColor, badgeStyles}: StatusBadgeProps) { + const styles = useThemeStyles(); + const theme = useTheme(); + const StyleUtils = useStyleUtils(); + + const bgColor = (backgroundColor ?? theme.transparent) as string; + const txtColor = (textColor ?? theme.text) as string; + + return ( + + ); +} + +export default StatusBadge; diff --git a/src/components/TextPill.tsx b/src/components/TextPill.tsx deleted file mode 100644 index 7f6665dd9649..000000000000 --- a/src/components/TextPill.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import React from 'react'; -import type {StyleProp, TextStyle} from 'react-native'; -// eslint-disable-next-line no-restricted-imports -import useThemeStyles from '@hooks/useThemeStyles'; -import colors from '@styles/theme/colors'; -import Text from './Text'; - -type TextPillProps = { - /** The color of the text/ */ - color?: string; - - /** Styles to apply to the text */ - textStyles?: StyleProp; - - children: React.ReactNode; -}; - -function TextPill({color, textStyles, children}: TextPillProps) { - const styles = useThemeStyles(); - return ( - - {children} - - ); -} - -export default TextPill; diff --git a/src/hooks/useSearchTypeMenu.tsx b/src/hooks/useSearchTypeMenu.tsx index 388f5d142d4e..142a2a5f1224 100644 --- a/src/hooks/useSearchTypeMenu.tsx +++ b/src/hooks/useSearchTypeMenu.tsx @@ -216,6 +216,7 @@ export default function useSearchTypeMenu(queryJSON: SearchQueryJSON) { sectionItems.push({ badgeText: getItemBadgeText(item.key, reportCounts), + isBadgeSuccess: true, text: translate(item.translationPath), isSelected, icon, diff --git a/src/pages/Search/SearchTypeMenu.tsx b/src/pages/Search/SearchTypeMenu.tsx index 0c38d8d797ea..b9f63768189a 100644 --- a/src/pages/Search/SearchTypeMenu.tsx +++ b/src/pages/Search/SearchTypeMenu.tsx @@ -148,6 +148,7 @@ function SearchTypeMenu({queryJSON}: SearchTypeMenuProps) { interactive title={translate(item.translationPath)} badgeStyle={styles.todoBadge} + isBadgeSuccess icon={icon} iconWidth={variables.iconSizeNormal} iconHeight={variables.iconSizeNormal} diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index d2941b3342ad..5f30b954cbc1 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -90,6 +90,9 @@ type MenuData = WithSentryLabel & { iconRight?: IconAsset; badgeText?: string; badgeStyle?: ViewStyle; + isBadgeSuccess?: boolean; + isBadgeStrong?: boolean; + isBadgeCondensed?: boolean; }; type Menu = {sectionStyle: StyleProp; sectionTranslationKey: TranslationPaths; items: MenuData[]}; @@ -294,7 +297,8 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, badgeText: freeTrialText, - badgeStyle: freeTrialText ? styles.badgeSuccess : undefined, + isBadgeSuccess: !!freeTrialText, + isBadgeCondensed: !!freeTrialText, sentryLabel: CONST.SENTRY_LABEL.ACCOUNT.SUBSCRIPTION, action: () => Navigation.navigate(ROUTES.SETTINGS_SUBSCRIPTION.route), }); @@ -457,6 +461,9 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr iconStyles={item.iconStyles} badgeText={item.badgeText} badgeStyle={item.badgeStyle} + isBadgeSuccess={item.isBadgeSuccess} + isBadgeStrong={item.isBadgeStrong} + isBadgeCondensed={item.isBadgeCondensed} fallbackIcon={item.fallbackIcon} brickRoadIndicator={item.brickRoadIndicator} shouldStackHorizontally={item.shouldStackHorizontally} diff --git a/src/pages/settings/Subscription/FreeTrial.tsx b/src/pages/settings/Subscription/FreeTrial.tsx index 3a5c402fdc7d..eed86d24c05e 100644 --- a/src/pages/settings/Subscription/FreeTrial.tsx +++ b/src/pages/settings/Subscription/FreeTrial.tsx @@ -1,4 +1,4 @@ -import React, {useEffect, useState} from 'react'; +import React, {useMemo} from 'react'; import type {StyleProp, ViewStyle} from 'react-native'; import {View} from 'react-native'; import Badge from '@components/Badge'; @@ -30,16 +30,15 @@ function FreeTrial({badgeStyles, pressable = false, addSpacing = false, success const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); const privateSubscription = usePrivateSubscription(); - const [freeTrialText, setFreeTrialText] = useState(undefined); const {isOffline} = useNetwork(); const {translate} = useLocalize(); const icons = useMemoizedLazyExpensifyIcons(['Star'] as const); - useEffect(() => { + const freeTrialText = useMemo(() => { if (!privateSubscription && !isOffline) { return; } - setFreeTrialText(getFreeTrialText(translate, policies, introSelected, firstDayFreeTrial, lastDayFreeTrial)); + return getFreeTrialText(translate, policies, introSelected, firstDayFreeTrial, lastDayFreeTrial); }, [isOffline, privateSubscription, translate, policies, firstDayFreeTrial, lastDayFreeTrial, introSelected]); if (!freeTrialText) { @@ -57,6 +56,7 @@ function FreeTrial({badgeStyles, pressable = false, addSpacing = false, success ) : ( diff --git a/src/pages/settings/Wallet/PaymentMethodListItem.tsx b/src/pages/settings/Wallet/PaymentMethodListItem.tsx index 66b95fc5e7bf..10f52b8f393f 100644 --- a/src/pages/settings/Wallet/PaymentMethodListItem.tsx +++ b/src/pages/settings/Wallet/PaymentMethodListItem.tsx @@ -165,8 +165,7 @@ function PaymentMethodListItem({item, shouldShowDefaultBadge, threeDotsMenuItems iconStyles={item.iconStyles} badgeText={badgeText} badgeIcon={badgeIcon} - badgeSuccess={isInSetupState ? true : undefined} - badgeStyle={item.isCardFrozen ? styles.badgeBordered : undefined} + isBadgeSuccess={isInSetupState ? true : undefined} wrapperStyle={[styles.paymentMethod, listItemStyle]} iconRight={isInSetupState ? undefined : item.iconRight} shouldShowRightIcon={!showThreeDotsMenu && item.shouldShowRightIcon} diff --git a/src/pages/workspace/WorkspacesListRow.tsx b/src/pages/workspace/WorkspacesListRow.tsx index f6fa05b971e5..ac6f8bea28df 100644 --- a/src/pages/workspace/WorkspacesListRow.tsx +++ b/src/pages/workspace/WorkspacesListRow.tsx @@ -184,7 +184,7 @@ function WorkspacesListRow({ @@ -199,7 +199,7 @@ function WorkspacesListRow({ diff --git a/src/pages/workspace/accounting/PolicyAccountingPage.tsx b/src/pages/workspace/accounting/PolicyAccountingPage.tsx index e7d9cffe6aa7..0019d65c3730 100644 --- a/src/pages/workspace/accounting/PolicyAccountingPage.tsx +++ b/src/pages/workspace/accounting/PolicyAccountingPage.tsx @@ -319,7 +319,7 @@ function PolicyAccountingPage({policy}: PolicyAccountingPageProps) { } : undefined, badgeStyle: styles.mr3, - badgeSuccess: isXero, + isBadgeSuccess: isXero, shouldShowBadgeBelow: shouldUseNarrowLayout, rightComponent: (