diff --git a/frontend/src/components/Feed.tsx b/frontend/src/components/Feed.tsx index 0d780b2..d4e3188 100644 --- a/frontend/src/components/Feed.tsx +++ b/frontend/src/components/Feed.tsx @@ -73,6 +73,9 @@ export const Feed: React.FC = () => { const [searchResults, setSearchResults] = useState([]); const [isSearching, setIsSearching] = useState(false); + // Global mute state - persists across video scrolling + const [isMuted, setIsMuted] = useState(true); + // ========== SWIPE LOGIC ========== const touchStart = useRef(null); const touchEnd = useRef(null); @@ -655,8 +658,8 @@ export const Feed: React.FC = () => { onClick={handleJsonLogin} disabled={!jsonInput.trim()} className={`w-full py-4 text-white font-semibold rounded-2xl transition-all transform active:scale-[0.98] shadow-lg text-base ${jsonInput.trim() - ? 'bg-gradient-to-r from-cyan-500 to-pink-500 hover:from-cyan-400 hover:to-pink-400 shadow-pink-500/20' - : 'bg-gray-700 cursor-not-allowed' + ? 'bg-gradient-to-r from-cyan-500 to-pink-500 hover:from-cyan-400 hover:to-pink-400 shadow-pink-500/20' + : 'bg-gray-700 cursor-not-allowed' }`} > Connect to TikTok @@ -826,6 +829,8 @@ export const Feed: React.FC = () => { isFollowing={following.includes(video.author)} onFollow={handleFollow} onAuthorClick={(author) => searchByUsername(author)} + isMuted={isMuted} + onMuteToggle={() => setIsMuted(prev => !prev)} /> ) : ( /* Lightweight Placeholder */ diff --git a/frontend/src/components/VideoPlayer.tsx b/frontend/src/components/VideoPlayer.tsx index 42b6bfb..46b706d 100644 --- a/frontend/src/components/VideoPlayer.tsx +++ b/frontend/src/components/VideoPlayer.tsx @@ -15,6 +15,8 @@ interface VideoPlayerProps { isFollowing?: boolean; onFollow?: (author: string) => void; onAuthorClick?: (author: string) => void; // In-app navigation to creator + isMuted?: boolean; // Global mute state from parent + onMuteToggle?: () => void; // Callback to toggle parent mute state } export const VideoPlayer: React.FC = ({ @@ -22,7 +24,9 @@ export const VideoPlayer: React.FC = ({ isActive, isFollowing = false, onFollow, - onAuthorClick + onAuthorClick, + isMuted: externalMuted, + onMuteToggle }) => { const videoRef = useRef(null); const containerRef = useRef(null); @@ -34,7 +38,9 @@ export const VideoPlayer: React.FC = ({ const [duration, setDuration] = useState(0); const [isSeeking, setIsSeeking] = useState(false); const [useFallback, setUseFallback] = useState(false); // Fallback to full proxy - const [isMuted, setIsMuted] = useState(true); // Start muted for autoplay policy + // Use external mute state if provided, otherwise use local state for backward compatibility + const [localMuted, setLocalMuted] = useState(true); + const isMuted = externalMuted !== undefined ? externalMuted : localMuted; const [hearts, setHearts] = useState([]); const lastTapRef = useRef(0); @@ -52,7 +58,7 @@ export const VideoPlayer: React.FC = ({ // Auto-play when becoming active if (videoRef.current.paused) { videoRef.current.currentTime = 0; - videoRef.current.muted = true; // Always start muted for autoplay policy + videoRef.current.muted = isMuted; // Use current mute state videoRef.current.play().catch((err) => { // If autoplay fails even muted, show paused state console.log('Autoplay blocked:', err.message); @@ -65,6 +71,13 @@ export const VideoPlayer: React.FC = ({ } }, [isActive]); // Only trigger on isActive change + // Sync video muted property when isMuted state changes + useEffect(() => { + if (videoRef.current) { + videoRef.current.muted = isMuted; + } + }, [isMuted]); + // Spacebar to pause/play when this video is active useEffect(() => { if (!isActive) return; @@ -148,9 +161,13 @@ export const VideoPlayer: React.FC = ({ const toggleMute = (e: React.MouseEvent | React.TouchEvent) => { e.stopPropagation(); // Prevent video tap if (!videoRef.current) return; - const newMuted = !videoRef.current.muted; - videoRef.current.muted = newMuted; - setIsMuted(newMuted); + + // Use external toggle if provided, otherwise use local state + if (onMuteToggle) { + onMuteToggle(); + } else { + setLocalMuted(prev => !prev); + } }; // Handle tap - double tap shows heart, single tap toggles play