forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
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}