Bluesky app fork with some witchin' additions 馃挮
at main 233 lines 6.7 kB view raw
1import {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 {useFocusEffect} from '@react-navigation/native' 8import {useQueryClient} from '@tanstack/react-query' 9 10import {VIDEO_FEED_URI} from '#/lib/constants' 11import {makeCustomFeedLink} from '#/lib/routes/links' 12import {useEnableSquareButtons} from '#/state/preferences/enable-square-buttons' 13import {RQKEY, usePostFeedQuery} from '#/state/queries/post-feed' 14import {BlockDrawerGesture} from '#/view/shell/BlockDrawerGesture' 15import {atoms as a, tokens, useGutters, useTheme} from '#/alf' 16import {ButtonIcon} from '#/components/Button' 17import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '#/components/icons/Chevron' 18import {Link} from '#/components/Link' 19import {Text} from '#/components/Typography' 20import { 21 CompactVideoPostCard, 22 CompactVideoPostCardPlaceholder, 23} from '#/components/VideoPostCard' 24import {useAnalytics} from '#/analytics' 25 26const CARD_WIDTH = 100 27 28const FEED_DESC = `feedgen|${VIDEO_FEED_URI}` 29const FEED_PARAMS: { 30 feedCacheKey: 'explore' 31} = { 32 feedCacheKey: 'explore', 33} 34 35export function ExploreTrendingVideos() { 36 const gutters = useGutters([0, 'base']) 37 const {data, isLoading, error} = usePostFeedQuery(FEED_DESC, FEED_PARAMS) 38 39 // Refetch on tab change if nothing else is using this query. 40 const queryClient = useQueryClient() 41 useFocusEffect(() => { 42 return () => { 43 const query = queryClient 44 .getQueryCache() 45 .find({queryKey: RQKEY(FEED_DESC, FEED_PARAMS)}) 46 if (query && query.getObserversCount() <= 1) { 47 query.fetch() 48 } 49 } 50 }) 51 52 // const {data: saved} = useSavedFeeds() 53 // const isSavedAlready = useMemo(() => { 54 // return !!saved?.feeds?.some(info => info.config.value === VIDEO_FEED_URI) 55 // }, [saved]) 56 57 // const {mutateAsync: addSavedFeeds, isPending: isPinPending} = 58 // useAddSavedFeedsMutation() 59 // const pinFeed = useCallback( 60 // (e: any) => { 61 // e.preventDefault() 62 63 // addSavedFeeds([ 64 // { 65 // type: 'feed', 66 // value: VIDEO_FEED_URI, 67 // pinned: true, 68 // }, 69 // ]) 70 71 // // prevent navigation 72 // return false 73 // }, 74 // [addSavedFeeds], 75 // ) 76 77 if (error) { 78 return null 79 } 80 81 return ( 82 <View style={[a.pb_xl]}> 83 <BlockDrawerGesture> 84 <ScrollView 85 horizontal 86 showsHorizontalScrollIndicator={false} 87 decelerationRate="fast" 88 snapToInterval={CARD_WIDTH + tokens.space.sm}> 89 <View 90 style={[ 91 a.pt_lg, 92 a.flex_row, 93 a.gap_sm, 94 { 95 paddingLeft: gutters.paddingLeft, 96 paddingRight: gutters.paddingRight, 97 }, 98 ]}> 99 {isLoading ? ( 100 Array(10) 101 .fill(0) 102 .map((_, i) => ( 103 <View key={i} style={[{width: CARD_WIDTH}]}> 104 <CompactVideoPostCardPlaceholder /> 105 </View> 106 )) 107 ) : error || !data ? ( 108 <Text> 109 <Trans>Whoops! Trending videos failed to load.</Trans> 110 </Text> 111 ) : ( 112 <VideoCards data={data} /> 113 )} 114 </View> 115 </ScrollView> 116 </BlockDrawerGesture> 117 118 {/* {!isSavedAlready && ( 119 <View 120 style={[ 121 gutters, 122 a.pt_lg, 123 a.flex_row, 124 a.align_center, 125 a.justify_between, 126 a.gap_xl, 127 ]}> 128 <Text style={[a.flex_1, a.text_sm, a.leading_snug]}> 129 <Trans> 130 Pin the trending videos feed to your home screen for easy access 131 </Trans> 132 </Text> 133 <Button 134 disabled={isPinPending} 135 label={_(msg`Pin`)} 136 size="small" 137 variant="outline" 138 color="secondary" 139 onPress={pinFeed}> 140 <ButtonText>{_(msg`Pin`)}</ButtonText> 141 <ButtonIcon icon={Pin} position="right" /> 142 </Button> 143 </View> 144 )} */} 145 </View> 146 ) 147} 148 149function VideoCards({ 150 data, 151}: { 152 data: Exclude<ReturnType<typeof usePostFeedQuery>['data'], undefined> 153}) { 154 const t = useTheme() 155 const {_} = useLingui() 156 const ax = useAnalytics() 157 const enableSquareButtons = useEnableSquareButtons() 158 const items = useMemo(() => { 159 return data.pages 160 .flatMap(page => page.slices) 161 .map(slice => slice.items[0]) 162 .filter(Boolean) 163 .filter(item => AppBskyEmbedVideo.isView(item.post.embed)) 164 .slice(0, 8) 165 }, [data]) 166 const href = useMemo(() => { 167 const urip = new AtUri(VIDEO_FEED_URI) 168 return makeCustomFeedLink(urip.host, urip.rkey, undefined, 'explore') 169 }, []) 170 171 return ( 172 <> 173 {items.map(item => ( 174 <View key={item.post.uri} style={[{width: CARD_WIDTH}]}> 175 <CompactVideoPostCard 176 post={item.post} 177 moderation={item.moderation} 178 sourceContext={{ 179 type: 'feedgen', 180 uri: VIDEO_FEED_URI, 181 sourceInterstitial: 'explore', 182 }} 183 onInteract={() => { 184 ax.metric('videoCard:click', {context: 'interstitial:explore'}) 185 }} 186 /> 187 </View> 188 ))} 189 190 <View style={[{width: CARD_WIDTH * 2}]}> 191 <Link 192 to={href} 193 label={_(msg`View more`)} 194 style={[ 195 a.justify_center, 196 a.align_center, 197 a.flex_1, 198 a.rounded_md, 199 t.atoms.bg_contrast_25, 200 ]}> 201 {({pressed}) => ( 202 <View 203 style={[ 204 a.flex_row, 205 a.align_center, 206 a.gap_md, 207 { 208 opacity: pressed ? 0.6 : 1, 209 }, 210 ]}> 211 <Text style={[a.text_md]}> 212 <Trans>View more</Trans> 213 </Text> 214 <View 215 style={[ 216 a.align_center, 217 a.justify_center, 218 enableSquareButtons ? a.rounded_sm : a.rounded_full, 219 { 220 width: 34, 221 height: 34, 222 backgroundColor: t.palette.primary_500, 223 }, 224 ]}> 225 <ButtonIcon icon={ChevronRight} /> 226 </View> 227 </View> 228 )} 229 </Link> 230 </View> 231 </> 232 ) 233}