import React, { useEffect, useState, useRef } from "react"; // MiniVideoPlatform.jsx // Single-file React component (Tailwind CSS assumed). // Features: // - Create posts with video or image and optional caption // - Feed of posts (videos autoplay muted on scroll into view) // - Comments per post // - Likes // - Local persistence via localStorage // - Simple responsive layout // How to use: // 1) Create a React app (Vite or CRA) with Tailwind CSS configured. // 2) Drop this file into src/components or src/ and import it into App.jsx. // 3) Run the dev server. Files are stored in-memory (object URLs) and metadata in localStorage. export default function MiniVideoPlatform() { const [posts, setPosts] = useState(() => { try { const raw = localStorage.getItem("mvp_posts_v1"); return raw ? JSON.parse(raw) : []; } catch (e) { return []; } }); const [file, setFile] = useState(null); const [caption, setCaption] = useState(""); const [uploading, setUploading] = useState(false); const [query, setQuery] = useState(""); useEffect(() => { localStorage.setItem("mvp_posts_v1", JSON.stringify(posts)); }, [posts]); // Helper to create an object URL and store minimal metadata function handleFileChange(e) { const f = e.target.files && e.target.files[0]; if (!f) return; // Basic validation const isVideo = f.type.startsWith("video/"); const isImage = f.type.startsWith("image/"); if (!isVideo && !isImage) { alert("Please select a video or image file."); return; } setFile({ file: f, preview: URL.createObjectURL(f), type: isVideo ? "video" : "image" }); } function createPost(e) { e.preventDefault(); if (!file) return alert("Choose an image or video first."); setUploading(true); // Simulate upload delay setTimeout(() => { const newPost = { id: Date.now().toString(), type: file.type, url: file.preview, caption: caption.trim(), likes: 0, comments: [], createdAt: new Date().toISOString(), }; setPosts(prev => [newPost, ...prev]); setFile(null); setCaption(""); setUploading(false); }, 600); } function addComment(postId, text) { if (!text || !text.trim()) return; setPosts(prev => prev.map(p => p.id === postId ? { ...p, comments: [...p.comments, { id: Date.now().toString(), text: text.trim(), createdAt: new Date().toISOString() }] } : p)); } function toggleLike(postId) { setPosts(prev => prev.map(p => p.id === postId ? { ...p, likes: p.likes + 1 } : p)); } function deletePost(postId) { // Revoke object URL to free memory const post = posts.find(p => p.id === postId); if (post && post.url && post.url.startsWith("blob:")) URL.revokeObjectURL(post.url); setPosts(prev => prev.filter(p => p.id !== postId)); } // Simple search/filter const visiblePosts = posts.filter(p => p.caption.toLowerCase().includes(query.toLowerCase())); return (

Mini Video Platform

A simple client-side demo of a short-video feed: upload videos/images, comment, like.

{/* Upload panel */}

Create post

{file && (
{file.type === "video" ? (
)}
Note: media is stored temporarily in your browser (localStorage + object URLs).
setQuery(e.target.value)} placeholder="Search captions..." className="w-full rounded-md border p-2 text-sm" />
{/* Feed */}
{visiblePosts.length === 0 ? (
No posts yet — be the first to upload!
) : ( visiblePosts.map(post => (
U
User
{new Date(post.createdAt).toLocaleString()}
{post.likes} likes
{post.type === "video" ? ( ) : ( post )}
{post.caption &&
{post.caption}
}
{post.comments.length > 0 && (
{post.comments.map(c => (
C
{c.text}
{new Date(c.createdAt).toLocaleString()}
))}
)}
)) )}
); } // Small helper components below: function CommentBox({ post, onAdd }) { const [text, setText] = useState(""); return (
{ e.preventDefault(); onAdd(post.id, text); setText(""); }}> setText(e.target.value)} placeholder="Add comment..." className="rounded-md border p-2 text-sm" />
); } function AutoPlayVideo({ src }) { const ref = useRef(null); useEffect(() => { const node = ref.current; if (!node) return; let observer; // Autoplay when visible if ("IntersectionObserver" in window) { observer = new IntersectionObserver(entries => { entries.forEach(entry => { if (entry.isIntersecting) { node.play().catch(() => {}); } else { node.pause(); } }); }, { threshold: 0.5 }); observer.observe(node); } return () => observer && observer.disconnect(); }, []); return