|
| 1 | +import React, {useMemo} from 'react'; |
| 2 | +import type {StyleProp, ViewStyle} from 'react-native'; |
| 3 | +import {View} from 'react-native'; |
| 4 | +import {useMemoizedLazyExpensifyIcons, useMemoizedLazyIllustrations} from '@hooks/useLazyAsset'; |
| 5 | +import useLocalize from '@hooks/useLocalize'; |
| 6 | +import useResponsiveLayout from '@hooks/useResponsiveLayout'; |
| 7 | +import useThemeStyles from '@hooks/useThemeStyles'; |
| 8 | +import BillingBanner from '@pages/settings/Subscription/CardSection/BillingBanner/BillingBanner'; |
| 9 | +import Badge from './Badge'; |
| 10 | +import Button from './Button'; |
| 11 | +import Text from './Text'; |
| 12 | + |
| 13 | +type AgentPromotionalBannerProps = { |
| 14 | + /** Title shown next to the bot illustration. */ |
| 15 | + title: string; |
| 16 | + |
| 17 | + /** Supporting copy under the title. */ |
| 18 | + subtitle: string; |
| 19 | + |
| 20 | + /** Called when the dismiss (X) button is pressed. */ |
| 21 | + onDismiss: () => void; |
| 22 | + |
| 23 | + /** Sentry label for the dismiss button. */ |
| 24 | + dismissSentryLabel: string; |
| 25 | + |
| 26 | + /** Optional CTA text. When omitted, the CTA is not rendered. */ |
| 27 | + ctaText?: string; |
| 28 | + |
| 29 | + /** Called when the CTA is pressed. Required when `ctaText` is set. */ |
| 30 | + onCtaPress?: () => void; |
| 31 | + |
| 32 | + /** Sentry label for the CTA. Required when `ctaText` is set. */ |
| 33 | + ctaSentryLabel?: string; |
| 34 | + |
| 35 | + /** Extra styles applied to the outer container. */ |
| 36 | + style?: StyleProp<ViewStyle>; |
| 37 | +}; |
| 38 | + |
| 39 | +function AgentPromotionalBanner({title, subtitle, onDismiss, dismissSentryLabel, ctaText, onCtaPress, ctaSentryLabel, style}: AgentPromotionalBannerProps) { |
| 40 | + const styles = useThemeStyles(); |
| 41 | + const {translate} = useLocalize(); |
| 42 | + const {shouldUseNarrowLayout} = useResponsiveLayout(); |
| 43 | + const illustrations = useMemoizedLazyIllustrations(['AiBot']); |
| 44 | + const expensifyIcons = useMemoizedLazyExpensifyIcons(['Close']); |
| 45 | + |
| 46 | + const hasCta = !!ctaText && !!onCtaPress; |
| 47 | + |
| 48 | + const titleNode = useMemo( |
| 49 | + () => ( |
| 50 | + <View style={[styles.flexRow, styles.flexShrink1]}> |
| 51 | + <Text style={[styles.textStrong]}> |
| 52 | + {title} |
| 53 | + <Badge |
| 54 | + badgeStyles={styles.agentPromotionalBannerBadge} |
| 55 | + success |
| 56 | + isStrong |
| 57 | + isCondensed |
| 58 | + text={translate('common.new')} |
| 59 | + /> |
| 60 | + </Text> |
| 61 | + </View> |
| 62 | + ), |
| 63 | + [title, styles, translate], |
| 64 | + ); |
| 65 | + |
| 66 | + const rightComponent = useMemo(() => { |
| 67 | + if (!hasCta) { |
| 68 | + return null; |
| 69 | + } |
| 70 | + if (shouldUseNarrowLayout) { |
| 71 | + return ( |
| 72 | + <View style={[styles.flex0, styles.flexBasis100, styles.maxWidth100Percentage, styles.justifyContentCenter]}> |
| 73 | + <Button |
| 74 | + success |
| 75 | + medium |
| 76 | + text={ctaText} |
| 77 | + onPress={onCtaPress} |
| 78 | + sentryLabel={ctaSentryLabel} |
| 79 | + /> |
| 80 | + </View> |
| 81 | + ); |
| 82 | + } |
| 83 | + return ( |
| 84 | + <Button |
| 85 | + success |
| 86 | + small |
| 87 | + text={ctaText} |
| 88 | + onPress={onCtaPress} |
| 89 | + sentryLabel={ctaSentryLabel} |
| 90 | + /> |
| 91 | + ); |
| 92 | + }, [hasCta, shouldUseNarrowLayout, ctaText, onCtaPress, ctaSentryLabel, styles]); |
| 93 | + |
| 94 | + return ( |
| 95 | + <View style={style}> |
| 96 | + <BillingBanner |
| 97 | + icon={illustrations.AiBot} |
| 98 | + title={titleNode} |
| 99 | + subtitle={subtitle} |
| 100 | + subtitleStyle={[styles.mt1]} |
| 101 | + style={[styles.borderRadiusComponentLarge, styles.gap4]} |
| 102 | + rightIcon={expensifyIcons.Close} |
| 103 | + onRightIconPress={onDismiss} |
| 104 | + rightIconAccessibilityLabel={translate('common.dismiss')} |
| 105 | + rightIconSentryLabel={dismissSentryLabel} |
| 106 | + rightComponent={rightComponent} |
| 107 | + /> |
| 108 | + </View> |
| 109 | + ); |
| 110 | +} |
| 111 | + |
| 112 | +AgentPromotionalBanner.displayName = 'AgentPromotionalBanner'; |
| 113 | + |
| 114 | +export default AgentPromotionalBanner; |
0 commit comments