Bluesky app fork with some witchin' additions 馃挮 witchsky.app
bluesky fork client
119
fork

Configure Feed

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

at a876aae44ea07494ebea9727350aa060b81f317b 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/core/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}