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

Configure Feed

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

at main 239 lines 6.8 kB view raw
1import {useCallback, useEffect, useMemo} from 'react' 2import {ScrollView, View} from 'react-native' 3import {AppBskyEmbedVideo, AtUri} from '@atproto/api' 4import {msg} from '@lingui/core/macro' 5import {useLingui} from '@lingui/react' 6import {Trans} from '@lingui/react/macro' 7import {useQueryClient} from '@tanstack/react-query' 8 9import {VIDEO_FEED_URI} from '#/lib/constants' 10import {makeCustomFeedLink} from '#/lib/routes/links' 11import {useEnableSquareButtons} from '#/state/preferences/enable-square-buttons' 12import {useTrendingSettingsApi} from '#/state/preferences/trending' 13import {RQKEY, usePostFeedQuery} from '#/state/queries/post-feed' 14import {BlockDrawerGesture} from '#/view/shell/BlockDrawerGesture' 15import {atoms as a, useGutters, useTheme} from '#/alf' 16import {Button, ButtonIcon} from '#/components/Button' 17import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '#/components/icons/Chevron' 18import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times' 19import {Link} from '#/components/Link' 20import * as Prompt from '#/components/Prompt' 21import {Text} from '#/components/Typography' 22import { 23 CompactVideoPostCard, 24 CompactVideoPostCardPlaceholder, 25} from '#/components/VideoPostCard' 26import {useAnalytics} from '#/analytics' 27 28const CARD_WIDTH = 108 29 30const FEED_DESC = `feedgen|${VIDEO_FEED_URI}` 31const FEED_PARAMS: { 32 feedCacheKey: 'discover' 33} = { 34 feedCacheKey: 'discover', 35} 36 37export function TrendingVideos() { 38 const t = useTheme() 39 const {_} = useLingui() 40 const ax = useAnalytics() 41 const gutters = useGutters([0, 'base']) 42 const {data, isLoading, error} = usePostFeedQuery(FEED_DESC, FEED_PARAMS) 43 44 // Refetch on unmount if nothing else is using this query. 45 const queryClient = useQueryClient() 46 useEffect(() => { 47 return () => { 48 const query = queryClient 49 .getQueryCache() 50 .find({queryKey: RQKEY(FEED_DESC, FEED_PARAMS)}) 51 if (query && query.getObserversCount() <= 1) { 52 query.fetch() 53 } 54 } 55 }, [queryClient]) 56 57 const {setTrendingVideoDisabled} = useTrendingSettingsApi() 58 const trendingPrompt = Prompt.usePromptControl() 59 60 const onConfirmHide = useCallback(() => { 61 setTrendingVideoDisabled(true) 62 ax.metric('trendingVideos:hide', {context: 'interstitial:discover'}) 63 }, [ax, setTrendingVideoDisabled]) 64 65 if (error) { 66 return null 67 } 68 69 return ( 70 <View 71 style={[ 72 a.pt_sm, 73 a.pb_lg, 74 a.border_t, 75 a.overflow_hidden, 76 t.atoms.border_contrast_low, 77 t.atoms.bg_contrast_25, 78 ]}> 79 <View 80 style={[ 81 gutters, 82 a.pb_sm, 83 a.flex_row, 84 a.align_center, 85 a.justify_between, 86 ]}> 87 <Text style={[a.text_sm, a.font_semi_bold, a.leading_snug]}> 88 <Trans>Trending Videos</Trans> 89 </Text> 90 <Button 91 label={_(msg`Dismiss this section`)} 92 size="tiny" 93 variant="solid" 94 color="secondary" 95 shape="square" 96 onPress={() => trendingPrompt.open()}> 97 <ButtonIcon icon={X} size="sm" /> 98 </Button> 99 </View> 100 101 <BlockDrawerGesture> 102 <ScrollView 103 horizontal 104 showsHorizontalScrollIndicator={false} 105 decelerationRate="fast" 106 snapToInterval={CARD_WIDTH + a.gap_md.gap} 107 style={[a.overflow_visible]}> 108 <View 109 style={[ 110 a.flex_row, 111 a.gap_md, 112 { 113 paddingLeft: gutters.paddingLeft, 114 paddingRight: gutters.paddingRight, 115 }, 116 ]}> 117 {isLoading ? ( 118 Array(10) 119 .fill(0) 120 .map((_, i) => ( 121 <View key={i} style={[{width: CARD_WIDTH}]}> 122 <CompactVideoPostCardPlaceholder /> 123 </View> 124 )) 125 ) : error || !data ? ( 126 <Text> 127 <Trans>Whoops! Trending videos failed to load.</Trans> 128 </Text> 129 ) : ( 130 <VideoCards data={data} /> 131 )} 132 </View> 133 </ScrollView> 134 </BlockDrawerGesture> 135 136 <Prompt.Basic 137 control={trendingPrompt} 138 title={_(msg`Hide trending videos?`)} 139 description={_(msg`You can update this later from your settings.`)} 140 confirmButtonCta={_(msg`Hide`)} 141 onConfirm={onConfirmHide} 142 /> 143 </View> 144 ) 145} 146 147function VideoCards({ 148 data, 149}: { 150 data: Exclude<ReturnType<typeof usePostFeedQuery>['data'], undefined> 151}) { 152 const ax = useAnalytics() 153 const items = useMemo(() => { 154 return data.pages 155 .flatMap(page => page.slices) 156 .map(slice => slice.items[0]) 157 .filter(Boolean) 158 .filter(item => AppBskyEmbedVideo.isView(item.post.embed)) 159 .slice(0, 8) 160 }, [data]) 161 162 return ( 163 <> 164 {items.map(item => ( 165 <View key={item.post.uri} style={[{width: CARD_WIDTH}]}> 166 <CompactVideoPostCard 167 post={item.post} 168 moderation={item.moderation} 169 sourceContext={{ 170 type: 'feedgen', 171 uri: VIDEO_FEED_URI, 172 sourceInterstitial: 'discover', 173 }} 174 onInteract={() => { 175 ax.metric('videoCard:click', { 176 context: 'interstitial:discover', 177 }) 178 }} 179 /> 180 </View> 181 ))} 182 183 <ViewMoreCard /> 184 </> 185 ) 186} 187 188function ViewMoreCard() { 189 const t = useTheme() 190 const {_} = useLingui() 191 192 const href = useMemo(() => { 193 const urip = new AtUri(VIDEO_FEED_URI) 194 return makeCustomFeedLink(urip.host, urip.rkey, undefined, 'discover') 195 }, []) 196 197 const enableSquareButtons = useEnableSquareButtons() 198 199 return ( 200 <View style={[{width: CARD_WIDTH * 2}]}> 201 <Link 202 to={href} 203 label={_(msg`View more`)} 204 style={[ 205 a.justify_center, 206 a.align_center, 207 a.flex_1, 208 a.rounded_lg, 209 a.border, 210 t.atoms.border_contrast_low, 211 t.atoms.bg, 212 t.atoms.shadow_sm, 213 ]}> 214 {({pressed}) => ( 215 <View 216 style={[ 217 a.flex_row, 218 a.align_center, 219 a.gap_md, 220 { 221 opacity: pressed ? 0.6 : 1, 222 }, 223 ]}> 224 <Text style={[a.text_md]}> 225 <Trans>View more</Trans> 226 </Text> 227 <Button 228 color="primary" 229 size="small" 230 shape={enableSquareButtons ? 'square' : 'round'} 231 label={_(msg`View more trending videos`)}> 232 <ButtonIcon icon={ChevronRight} /> 233 </Button> 234 </View> 235 )} 236 </Link> 237 </View> 238 ) 239}