From 25b5579bb3eb8c62e16acb3c2d3b46f63bdb2192 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Tue, 14 Jan 2025 19:37:34 +0000 Subject: [PATCH] yolo mode --- bskyweb/cmd/bskyweb/server.go | 3 + src/Navigation.tsx | 6 + src/components/FeedInterstitials.tsx | 2 +- src/components/Layout/Header/index.tsx | 6 +- src/lib/routes/types.ts | 1 + src/routes.ts | 2 + src/screens/Feeds/YoloScreen.tsx | 216 +++++++++++++++++++++++++ src/view/com/util/List.tsx | 6 +- 8 files changed, 237 insertions(+), 5 deletions(-) create mode 100644 src/screens/Feeds/YoloScreen.tsx diff --git a/bskyweb/cmd/bskyweb/server.go b/bskyweb/cmd/bskyweb/server.go index 3ef19fc462..5febbc9d05 100644 --- a/bskyweb/cmd/bskyweb/server.go +++ b/bskyweb/cmd/bskyweb/server.go @@ -272,6 +272,9 @@ func serve(cctx *cli.Context) error { e.GET("/messages", server.WebGeneric) e.GET("/messages/:conversation", server.WebGeneric) + // temp + e.GET("/temp-yolo", server.WebGeneric) + // profile endpoints; only first populates info e.GET("/profile/:handleOrDID", server.WebProfile) e.GET("/profile/:handleOrDID/follows", server.WebGeneric) diff --git a/src/Navigation.tsx b/src/Navigation.tsx index 18705c5ffb..494f0148f2 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -89,6 +89,7 @@ import {Wizard} from '#/screens/StarterPack/Wizard' import {useTheme} from '#/alf' import {router} from '#/routes' import {Referrer} from '../modules/expo-bluesky-swiss-army' +import {YoloScreen} from './screens/Feeds/YoloScreen' import {AboutSettingsScreen} from './screens/Settings/AboutSettings' import {AccessibilitySettingsScreen} from './screens/Settings/AccessibilitySettings' import {AccountSettingsScreen} from './screens/Settings/AccountSettings' @@ -422,6 +423,11 @@ function commonScreens(Stack: typeof HomeTab, unreadCountLabel?: string) { getComponent={() => Wizard} options={{title: title(msg`Edit your starter pack`), requireAuth: true}} /> + YoloScreen} + options={{title: title(msg`Yolo mode`), requireAuth: true}} + /> ) } diff --git a/src/components/FeedInterstitials.tsx b/src/components/FeedInterstitials.tsx index 3c6c4f53f4..77d1986947 100644 --- a/src/components/FeedInterstitials.tsx +++ b/src/components/FeedInterstitials.tsx @@ -546,7 +546,7 @@ export function VideoModeEntranceInterstitial() { ) { shape="square" onPress={onPressBack} hitSlop={HITSLOP_30} - style={[{marginLeft: -BUTTON_VISUAL_ALIGNMENT_OFFSET}, style]} + style={[ + {marginLeft: -BUTTON_VISUAL_ALIGNMENT_OFFSET}, + a.bg_transparent, + style, + ]} {...props}> diff --git a/src/lib/routes/types.ts b/src/lib/routes/types.ts index d720886e9f..2037feee25 100644 --- a/src/lib/routes/types.ts +++ b/src/lib/routes/types.ts @@ -57,6 +57,7 @@ export type CommonNavigatorParams = { StarterPackShort: {code: string} StarterPackWizard: undefined StarterPackEdit: {rkey?: string} + TempYolo: undefined } export type BottomTabNavigatorParams = CommonNavigatorParams & { diff --git a/src/routes.ts b/src/routes.ts index 7cd7c0880d..ab05bcb9b2 100644 --- a/src/routes.ts +++ b/src/routes.ts @@ -64,4 +64,6 @@ export const router = new Router({ StarterPack: '/starter-pack/:name/:rkey', StarterPackShort: '/starter-pack-short/:code', StarterPackWizard: '/starter-pack/create', + // temp + TempYolo: '/temp-yolo', }) diff --git a/src/screens/Feeds/YoloScreen.tsx b/src/screens/Feeds/YoloScreen.tsx new file mode 100644 index 0000000000..4e68724d1c --- /dev/null +++ b/src/screens/Feeds/YoloScreen.tsx @@ -0,0 +1,216 @@ +import {useCallback, useEffect, useState} from 'react' +import { + ListRenderItem, + SafeAreaView, + useWindowDimensions, + View, + ViewToken, +} from 'react-native' +import {useSafeAreaInsets} from 'react-native-safe-area-context' +import {useEvent} from 'expo' +import {BlurView} from 'expo-blur' +import {useVideoPlayer, VideoPlayer, VideoView} from 'expo-video' +import {AppBskyEmbedVideo, AppBskyFeedDefs} from '@atproto/api' +import {Trans} from '@lingui/macro' +import {useFocusEffect} from '@react-navigation/native' +import {NativeStackScreenProps} from '@react-navigation/native-stack' + +import {THEVIDS_FEED_URI} from '#/lib/constants' +import {CommonNavigatorParams} from '#/lib/routes/types' +import {FeedPostSliceItem, usePostFeedQuery} from '#/state/queries/post-feed' +import {useSetMinimalShellMode} from '#/state/shell' +import {List} from '#/view/com/util/List' +import {atoms as a, useTheme} from '#/alf' +import * as Layout from '#/components/Layout' +import {ListFooter} from '#/components/Lists' + +type Props = NativeStackScreenProps +export function YoloScreen({}: Props) { + const {top} = useSafeAreaInsets() + const t = useTheme() + const [headerHeight, setHeaderHeight] = useState(0) + + const setMinShellMode = useSetMinimalShellMode() + useFocusEffect( + useCallback(() => { + setMinShellMode(true) + return () => { + setMinShellMode(false) + } + }, [setMinShellMode]), + ) + + return ( + + + setHeaderHeight(nativeEvent.layout.height) + }> + + + + + Yolo mode + + + + + + + + ) +} + +function YoloFeed({headerHeight}: {headerHeight: number}) { + const { + data, + isFetching, + refetch, + hasNextPage, + isFetchingNextPage, + fetchNextPage, + } = usePostFeedQuery(`feedgen|${THEVIDS_FEED_URI}`) + + const player1 = useVideoPlayer('', p => { + p.loop = true + }) + const player2 = useVideoPlayer('', p => { + p.loop = true + }) + const player3 = useVideoPlayer('', p => { + p.loop = true + }) + + const videos = data?.pages.flatMap(page => + page.slices.flatMap(slice => slice.items), + ) + + const [currentIndex, setCurrentIndex] = useState(0) + + const renderItem: ListRenderItem = useCallback( + ({item, index}) => { + const {post} = item + if (!post.embed || !AppBskyEmbedVideo.isView(post.embed)) { + return null + } + + const player = [player1, player2, player3][index % 3] + + return ( + + ) + }, + [player1, player2, player3, currentIndex, headerHeight], + ) + + const onViewableItemsChanged = useCallback( + ({viewableItems}: {viewableItems: ViewToken[]; changed: ViewToken[]}) => { + if (viewableItems[0] && viewableItems[0].index !== null) { + setCurrentIndex(viewableItems[0].index) + } + }, + [], + ) + + return ( + + } + onEndReached={() => { + if (hasNextPage && !isFetchingNextPage) { + fetchNextPage() + } + }} + showsVerticalScrollIndicator={false} + onViewableItemsChanged={onViewableItemsChanged} + viewabilityConfig={{itemVisiblePercentThreshold: 75}} + /> + ) +} + +function keyExtractor(item: FeedPostSliceItem) { + return item._reactKey +} + +function VideoScreen({ + player, + embed, + active, + loaded, + headerHeight, +}: { + player: VideoPlayer + post: AppBskyFeedDefs.PostView + embed: AppBskyEmbedVideo.View + active: boolean + loaded: boolean + headerHeight: number +}) { + const {height, width} = useWindowDimensions() + const source = embed.playlist + const sourceChangeEvent = useEvent(player, 'sourceChange') as { + // incorrect types + source: {uri?: string} + oldSource: {uri?: string} + } + + useEffect(() => { + if (loaded && sourceChangeEvent?.source?.uri !== source) { + player.replace(source) + } + }, [sourceChangeEvent?.source?.uri, loaded, source, player]) + + useEffect(() => { + if (active) { + player.play() + } + return () => { + player.pause() + } + }, [active, player]) + + return ( + + + {active && ( + + )} + + + ) +} diff --git a/src/view/com/util/List.tsx b/src/view/com/util/List.tsx index a4e1a0947b..41ca5b5725 100644 --- a/src/view/com/util/List.tsx +++ b/src/view/com/util/List.tsx @@ -152,6 +152,9 @@ let List = React.forwardRef( return ( ( onScroll={scrollHandler} scrollsToTop={!activeLightbox} scrollEventThrottle={1} - onViewableItemsChanged={onViewableItemsChanged} - viewabilityConfig={viewabilityConfig} - showsVerticalScrollIndicator={!isAndroid} style={style} // @ts-expect-error FlatList_INTERNAL ref type is wrong -sfn ref={ref}