### 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.