Bluesky app fork with some witchin' additions 馃挮
at feat/tealfm 133 lines 3.9 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} 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, utils} from '#/alf' 11import {Live_Stroke2_Corner0_Rounded as LiveIcon} from '#/components/icons/Live' 12import {Link} from '#/components/Link' 13import {Text} from '#/components/Typography' 14import {useAnalytics} from '#/analytics' 15import { 16 type LiveEventFeed, 17 type LiveEventFeedMetricContext, 18} from '#/features/liveEvents/types' 19 20const roundedStyles = [a.rounded_md, a.curve_continuous] 21 22export function LiveEventFeedCardCompact({ 23 feed, 24 metricContext, 25}: { 26 feed: LiveEventFeed 27 metricContext: LiveEventFeedMetricContext 28}) { 29 const {_} = useLingui() 30 const ax = useAnalytics() 31 32 const layout = feed.layouts.compact 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={[a.w_full, a.align_start, a.overflow_hidden, roundedStyles]}> 65 <Image 66 accessibilityIgnoresInvertColors 67 source={{uri: layout.image}} 68 placeholder={{blurhash: layout.blurhash}} 69 style={[a.absolute, a.inset_0, a.w_full, a.h_full]} 70 contentFit="cover" 71 placeholderContentFit="cover" 72 /> 73 74 <LinearGradient 75 colors={[overlayColor, utils.alpha(overlayColor, 0)]} 76 locations={[0, 1]} 77 start={{x: 0, y: 0}} 78 end={{x: 1, y: 0}} 79 style={[ 80 a.absolute, 81 a.inset_0, 82 a.transition_opacity, 83 { 84 transitionDuration: '200ms', 85 opacity: hovered || pressed ? 0.6 : 0, 86 }, 87 ]} 88 /> 89 90 <View style={[a.w_full, a.justify_end]}> 91 <LinearGradient 92 colors={[ 93 overlayColor, 94 utils.alpha(overlayColor, 0.7), 95 utils.alpha(overlayColor, 0), 96 ]} 97 locations={[0, 0.8, 1]} 98 start={{x: 0, y: 0}} 99 end={{x: 1, y: 0}} 100 style={[a.absolute, a.inset_0]} 101 /> 102 103 <View 104 style={[ 105 a.flex_1, 106 a.flex_row, 107 a.align_center, 108 a.gap_xs, 109 a.z_10, 110 a.px_lg, 111 a.py_md, 112 ]}> 113 <LiveIcon size="md" fill={textColor} /> 114 <Text 115 numberOfLines={1} 116 style={[ 117 a.flex_1, 118 a.leading_snug, 119 a.font_bold, 120 a.text_lg, 121 a.pr_xl, 122 {color: textColor}, 123 ]}> 124 {layout.title} 125 </Text> 126 </View> 127 </View> 128 </View> 129 </View> 130 )} 131 </Link> 132 ) 133}