### Install Dependencies and Start Expo Project Source: https://github.com/muxinc/slop-social/blob/main/README.md This section provides the command-line instructions to set up and run the Slop Social project using Bun and Expo. It covers installing all necessary dependencies, starting the development server, and running the application on iOS and Android simulators or devices. ```bash # Install dependencies bun install # Start the dev server npx expo start # Run on iOS npx expo run:ios # Run on Android npx expo run:android ``` -------------------------------- ### Implement Smart Video Preloading Strategy Source: https://github.com/muxinc/slop-social/blob/main/README.md This code illustrates a smart preloading strategy for video content within a feed. It preloads upcoming videos to minimize buffering and keeps one video ready in the background for quick back-scrolling. Videos outside the preload window have their source set to null to free up memory. ```typescript const MAX_PRELOAD_DISTANCE = 5; // Preload 5 videos ahead in scroll direction const shouldPreloadAhead = isAhead && Math.abs(distanceFromActive) <= MAX_PRELOAD_DISTANCE; // Keep 1 video behind ready for quick back-scroll const shouldPreloadBehind = !isAhead && Math.abs(distanceFromActive) === 1; ``` -------------------------------- ### Use Videos Hook for Infinite Scroll Data Fetching (TypeScript) Source: https://context7.com/muxinc/slop-social/llms.txt The `useVideos` hook is designed for data fetching in an infinite scroll scenario. It loads video metadata from a JSON source and provides loading states, error handling, and a refetch capability. The hook cycles the fetched videos to simulate a larger dataset for testing purposes. Video objects include playback IDs, titles, descriptions, view counts, and user profile information. ```tsx import { useVideos } from "@/hooks/use-videos"; function MyComponent() { const { videos, loading, error, refetch } = useVideos(); if (loading) { return ; } if (error) { return Error: {error}; } // videos is an array of Video objects cycled 25 times // Each video has: id, mux_playback_id, title, description, view_count, profile console.log(`Loaded ${videos.length} videos`); // 25 * original count return ; } // Video data structure: interface Video { id: string; user_id: string; mux_playback_id: string; // Used to construct HLS URL title: string | null; description: string | null; thumbnail_url: string | null; duration: number | null; view_count: number; created_at: string; profile?: { username: string | null; avatar_url: string | null; }; } ``` -------------------------------- ### Use Likes Hook for Local Like State Management (TypeScript) Source: https://context7.com/muxinc/slop-social/llms.txt The `useLikes` hook provides local state management for video likes, utilizing in-memory storage to persist like status and counts. It tracks whether a video is liked and its current like count, persisting this state across component remounts. The `toggleLike` function updates the liked status and increments or decrements the like count, providing a responsive and persistent liking mechanism. ```tsx import { useLikes } from "@/hooks/use-likes"; function LikeButton({ videoId }: { videoId: string }) { const { liked, likeCount, isLoading, toggleLike } = useLikes(videoId); return ( {likeCount} ); // Behavior: // - First render: Generates random like count (0-1000) // - toggleLike(): Increments if unliked, decrements if liked // - State persists across component unmount/remount via likesStore // - Each videoId maintains independent like state } ``` -------------------------------- ### Expo Video Player Configuration with Mux HLS Source: https://context7.com/muxinc/slop-social/llms.txt Configures the expo-video player for HLS streaming from Mux, enabling looping and conditional source loading for optimized memory usage. It includes logic to manage playback state based on component activity and preload status. ```tsx import { useVideoPlayer, VideoView } from "expo-video"; const videoUrl = `https://stream.mux.com/${video.mux_playback_id}.m3u8`; const player = useVideoPlayer( shouldPreload || isActive ? videoUrl : null, // Conditional loading (p) => { p.loop = true; // Infinite loop p.muted = true; // Start muted (unmuted only when active) } ); // Player control in useEffect: useEffect(() => { if (!player) return; if (isActive && !paused) { player.muted = false; player.play(); } else if (isActive && paused) { player.muted = true; player.pause(); } else if (shouldPreload) { player.muted = true; player.pause(); player.currentTime = 0; // Ready at start } else { player.muted = true; player.pause(); } }, [isActive, paused, player, shouldPreload]); // HLS prefetch: Setting source to URL triggers manifest/segment downloads // even when paused, reducing buffer time when video becomes active ``` -------------------------------- ### Video Item Component for Mux HLS Playback (TypeScript) Source: https://context7.com/muxinc/slop-social/llms.txt The `VideoItem` component renders individual videos, managing Mux HLS playback. It supports gesture detection for single and double taps, and its loading behavior is controlled by a `shouldPreload` prop. The component dynamically adjusts playback (mute/unmute, play/pause) based on the `isActive` prop, and can prefetch HLS sources or clean up memory by setting the source to null. The HLS playback URL is constructed using the Mux playback ID. ```tsx import { VideoItem } from "@/components/video-feed/video-item"; // Inside FlashList renderItem: // Behavior: // - isActive=true, paused=false: Unmutes and plays video // - isActive=true, paused=true: Mutes and pauses (user tapped) // - isActive=false, shouldPreload=true: Loads source paused at 0:00 (prefetch) // - isActive=false, shouldPreload=false: Sets source to null (memory cleanup) // Video URL construction: // https://stream.mux.com/{mux_playback_id}.m3u8 ``` -------------------------------- ### FlashList Configuration for Performance - React Native Source: https://context7.com/muxinc/slop-social/llms.txt This configuration optimizes FlashList for a smooth vertical video feed. It uses `overrideItemLayout` to pre-calculate item dimensions and offsets, `drawDistance` to control rendering scope, and `pagingEnabled` for snapping behavior. `onViewableItemsChanged` is crucial for tracking visible items. ```tsx import { FlashList } from "@shopify/flash-list"; import { Dimensions } from "react-native"; const { height: SCREEN_HEIGHT } = Dimensions.get("window"); // Inside VideoFeed component render: item.id} // Performance optimizations overrideItemLayout={(layout, _item, index) => { layout.size = SCREEN_HEIGHT; // Each item is exactly screen height layout.offset = SCREEN_HEIGHT * index; // Calculate offset = height * position }} drawDistance={SCREEN_HEIGHT * 3} // Render items within 3 screens getItemType={() => "video"} // Single type = optimal recycling pool // Paging and snapping pagingEnabled snapToInterval={SCREEN_HEIGHT} snapToAlignment="start" decelerationRate="fast" // Viewability tracking onViewableItemsChanged={handleViewableVideoChange} viewabilityConfig={{ itemVisiblePercentThreshold: 40 }} bounces={false} overScrollMode="never" /> ``` -------------------------------- ### Use Video Playback Hook for Lifecycle Management (TypeScript) Source: https://context7.com/muxinc/slop-social/llms.txt The `useVideoPlayback` hook manages the playback state of videos based on their visibility and the application's lifecycle. It automatically pauses videos when they scroll out of view (`isActive` becomes false) or when the application is backgrounded. The hook provides a `paused` state and a `togglePause` function to control playback, ensuring a seamless user experience by syncing video state with user context. ```tsx import { useVideoPlayback } from "@/hooks/use-video-playback"; function VideoPlayer({ isActive }: { isActive: boolean }) { const { paused, togglePause } = useVideoPlayback({ isActive }); // paused is automatically: // - false when video becomes active (scrolled into view) // - true when video leaves view (isActive=false) // - true when app goes to background (AppState change) return ( {paused ? "Play" : "Pause"} ); } // AppState listener automatically pauses all videos when app backgrounds ``` -------------------------------- ### VideoOverlay Component for React Native Source: https://context7.com/muxinc/slop-social/llms.txt A UI component for displaying video information like username, description, view counts, and engagement buttons. It uses gradient backgrounds for better readability over video content and supports number formatting for engagement stats. ```tsx import { VideoOverlay } from "@/components/video-feed/video-overlay"; import { LinearGradient } from "expo-linear-gradient"; toggleLike()} /> // Renders: // - Top gradient (status bar area fade) // - Bottom gradient (content area fade) // - Left side: @username, description, view count with eye icon // - Right side: Like button (heart), comment button, share button // - Number formatting: 1234 -> "1.2K", 1234567 -> "1.2M" // Layout: // - Positioned absolutely over video // - Bottom padding accounts for tab bar (80px) // - Gradient ensures text readable over any video content ``` -------------------------------- ### Gesture Interactions for Video Controls (TypeScript) Source: https://context7.com/muxinc/slop-social/llms.txt This snippet demonstrates the integration of `react-native-gesture-handler` for managing user interactions within the `VideoItem` component. It utilizes `Gesture.Tap` for single taps (to toggle play/pause) and double taps (to like), with `scheduleOnRN` from `react-native-worklets` ensuring these actions are processed smoothly on the React Native thread. The `Gesture.Exclusive` helper ensures that only one gesture handler is activated at a time. ```tsx import { Gesture, GestureDetector } from "react-native-gesture-handler"; import { scheduleOnRN } from "react-native-worklets"; // Inside VideoItem component: const singleTap = Gesture.Tap() .numberOfTaps(1) .onEnd(() => { scheduleOnRN(togglePause); // Schedule on React Native thread }); const doubleTap = Gesture.Tap() .numberOfTaps(2) .onEnd(() => { scheduleOnRN(handleLike); }); const taps = Gesture.Exclusive(doubleTap, singleTap); // Wrap video view: // User interactions: // Single tap: Toggles play/pause state // Double tap: Triggers like animation and increments count ``` -------------------------------- ### Optimize Video Feed Scrolling with FlashList Source: https://github.com/muxinc/slop-social/blob/main/README.md This snippet demonstrates how to configure Shopify's FlashList for a high-performance vertical video feed. It includes optimizations like pre-calculating item layouts, setting a draw distance for smooth scrolling, defining a single item type for efficient recycling, and enabling full-screen snapping for a TikTok-like experience. ```tsx { layout.size = SCREEN_HEIGHT; layout.offset = SCREEN_HEIGHT * index; }} // Render items within 3 screens for smooth scroll drawDistance={SCREEN_HEIGHT * 3} // Single item type = optimal recycling pool getItemType={() => "video"} // Full-screen snapping pagingEnabled snapToInterval={SCREEN_HEIGHT} decelerationRate="fast" /> ``` -------------------------------- ### Number Formatting Utility for View Counts Source: https://context7.com/muxinc/slop-social/llms.txt A utility function to format large numbers into a human-readable shorthand, using 'K' for thousands and 'M' for millions, with one decimal place. This is commonly used for displaying engagement metrics like view counts. ```tsx const formatNumber = (num: number): string => { if (num >= 1000000) return `${(num / 1000000).toFixed(1)}M`; if (num >= 1000) return `${(num / 1000).toFixed(1)}K`; return num.toString(); }; // Usage in VideoOverlay: {formatNumber(viewCount)} views // Examples: // 456 -> "456" // 1234 -> "1.2K" // 12543 -> "12.5K" // 1234567 -> "1.2M" ``` -------------------------------- ### Directional Video Preloading Strategy - React Native Source: https://context7.com/muxinc/slop-social/llms.txt Implements a smart video preloading strategy to ensure smooth playback in a vertical feed. It preloads a defined number of videos ahead in the scroll direction and keeps one video ready for back-scrolling. Videos not meeting preload criteria have their source set to null to conserve memory. ```tsx const MAX_PRELOAD_DISTANCE = 5; const renderItem = ({ item, index }: { item: Video; index: number }) => { const isActive = index === currentIndex; // Calculate distance and direction from currently active video const distanceFromActive = index - currentIndex; const isAhead = scrollDirection === 'down' ? distanceFromActive > 0 : distanceFromActive < 0; // Preload 5 videos ahead in scroll direction const shouldPreloadAhead = isAhead && Math.abs(distanceFromActive) <= MAX_PRELOAD_DISTANCE; // Keep 1 video behind ready for quick back-scroll const shouldPreloadBehind = !isAhead && Math.abs(distanceFromActive) === 1; const shouldPreload = shouldPreloadAhead || shouldPreloadBehind; return ( ); }; // Result: Only 7 videos (1 active + 5 ahead + 1 behind) load at once // Non-preloaded videos have source=null, freeing memory ``` -------------------------------- ### VideoFeed Component - React Native Source: https://context7.com/muxinc/slop-social/llms.txt The core VideoFeed component renders a scrollable list of videos using FlashList. It manages video visibility, directional preloading, and scroll state changes to optimize playback and memory usage. This component is a self-contained unit for displaying the video feed. ```tsx import { VideoFeed } from "@/components/video-feed/video-feed"; import { View, StyleSheet } from "react-native"; export default function HomeScreen() { return ( ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: "#000", }, }); // The VideoFeed component automatically: // - Loads videos from the data source // - Tracks current video index // - Preloads 5 videos ahead in scroll direction // - Keeps 1 video behind ready for back-scroll // - Recycles video player cells for optimal memory usage // - Snaps to full-screen intervals ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.