Bluesky app fork with some witchin' additions 馃挮
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

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