Bluesky app fork with some witchin' additions 馃挮
at feat/tealfm 140 lines 4.1 kB view raw
1import {useMemo} from 'react' 2import {View} from 'react-native' 3import {Image} from 'expo-image' 4import {LinearGradient} from 'expo-linear-gradient' 5import {msg, Trans} from '@lingui/macro' 6import {useLingui} from '@lingui/react' 7 8import {useCallOnce} from '#/lib/once' 9import {isBskyCustomFeedUrl} from '#/lib/strings/url-helpers' 10import {atoms as a, useBreakpoints, utils} from '#/alf' 11import {Link} from '#/components/Link' 12import {Text} from '#/components/Typography' 13import {useAnalytics} from '#/analytics' 14import { 15 type LiveEventFeed, 16 type LiveEventFeedMetricContext, 17} from '#/features/liveEvents/types' 18 19const roundedStyles = [a.rounded_lg, a.curve_continuous] 20 21export function LiveEventFeedCardWide({ 22 feed, 23 metricContext, 24}: { 25 feed: LiveEventFeed 26 metricContext: LiveEventFeedMetricContext 27}) { 28 const ax = useAnalytics() 29 const {_} = useLingui() 30 const {gtPhone} = useBreakpoints() 31 32 const layout = feed.layouts.wide 33 const overlayColor = layout.overlayColor 34 const textColor = layout.textColor 35 const url = useMemo(() => { 36 // Validated in multiple places on the backend 37 if (isBskyCustomFeedUrl(feed.url)) { 38 return new URL(feed.url).pathname 39 } 40 return '/' 41 }, [feed.url]) 42 43 useCallOnce(() => { 44 ax.metric('liveEvents:feedBanner:seen', { 45 feed: feed.url, 46 context: metricContext, 47 }) 48 })() 49 50 return ( 51 <Link 52 to={url} 53 label={_(msg`Live event happening now: ${feed.title}`)} 54 style={[a.w_full]} 55 onPress={() => { 56 ax.metric('liveEvents:feedBanner:click', { 57 feed: feed.url, 58 context: metricContext, 59 }) 60 }}> 61 {({hovered, pressed}) => ( 62 <View style={[roundedStyles, a.shadow_md, a.w_full]}> 63 <View 64 style={[ 65 a.align_start, 66 roundedStyles, 67 a.overflow_hidden, 68 { 69 aspectRatio: gtPhone ? 576 / 144 : 369 / 100, 70 }, 71 ]}> 72 <Image 73 accessibilityIgnoresInvertColors 74 source={{uri: layout.image}} 75 placeholder={{blurhash: layout.blurhash}} 76 style={[a.absolute, a.inset_0, a.w_full, a.h_full]} 77 contentFit="cover" 78 placeholderContentFit="cover" 79 /> 80 81 <LinearGradient 82 colors={[overlayColor, utils.alpha(overlayColor, 0)]} 83 locations={[0, 1]} 84 start={{x: 0, y: 0}} 85 end={{x: 1, y: 0}} 86 style={[ 87 a.absolute, 88 a.inset_0, 89 a.transition_opacity, 90 { 91 transitionDuration: '200ms', 92 opacity: hovered || pressed ? 0.6 : 0, 93 }, 94 ]} 95 /> 96 97 <View style={[a.flex_1, a.justify_end]}> 98 <LinearGradient 99 colors={[overlayColor, utils.alpha(overlayColor, 0)]} 100 locations={[0, 1]} 101 start={{x: 0, y: 0}} 102 end={{x: 1, y: 0}} 103 style={[a.absolute, a.inset_0]} 104 /> 105 106 <View 107 style={[ 108 a.z_10, 109 gtPhone ? [a.pl_xl, a.pb_lg] : [a.pl_lg, a.pb_md], 110 {paddingRight: 64}, 111 ]}> 112 <Text 113 style={[ 114 a.leading_snug, 115 gtPhone ? a.text_xs : a.text_2xs, 116 {color: textColor, opacity: 0.8}, 117 ]}> 118 {feed.preview ? ( 119 <Trans>Preview</Trans> 120 ) : ( 121 <Trans>Happening now</Trans> 122 )} 123 </Text> 124 <Text 125 style={[ 126 a.leading_snug, 127 a.font_bold, 128 gtPhone ? a.text_3xl : a.text_lg, 129 {color: textColor}, 130 ]}> 131 {layout.title} 132 </Text> 133 </View> 134 </View> 135 </View> 136 </View> 137 )} 138 </Link> 139 ) 140}