Persist mute state across video scrolling

This commit is contained in:
Khoa.vo 2025-12-19 19:16:54 +07:00
parent 2d622c259f
commit 929df62342
2 changed files with 30 additions and 8 deletions

View file

@ -73,6 +73,9 @@ export const Feed: React.FC = () => {
const [searchResults, setSearchResults] = useState<Video[]>([]); const [searchResults, setSearchResults] = useState<Video[]>([]);
const [isSearching, setIsSearching] = useState(false); const [isSearching, setIsSearching] = useState(false);
// Global mute state - persists across video scrolling
const [isMuted, setIsMuted] = useState(true);
// ========== SWIPE LOGIC ========== // ========== SWIPE LOGIC ==========
const touchStart = useRef<number | null>(null); const touchStart = useRef<number | null>(null);
const touchEnd = useRef<number | null>(null); const touchEnd = useRef<number | null>(null);
@ -826,6 +829,8 @@ export const Feed: React.FC = () => {
isFollowing={following.includes(video.author)} isFollowing={following.includes(video.author)}
onFollow={handleFollow} onFollow={handleFollow}
onAuthorClick={(author) => searchByUsername(author)} onAuthorClick={(author) => searchByUsername(author)}
isMuted={isMuted}
onMuteToggle={() => setIsMuted(prev => !prev)}
/> />
) : ( ) : (
/* Lightweight Placeholder */ /* Lightweight Placeholder */

View file

@ -15,6 +15,8 @@ interface VideoPlayerProps {
isFollowing?: boolean; isFollowing?: boolean;
onFollow?: (author: string) => void; onFollow?: (author: string) => void;
onAuthorClick?: (author: string) => void; // In-app navigation to creator 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<VideoPlayerProps> = ({ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
@ -22,7 +24,9 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
isActive, isActive,
isFollowing = false, isFollowing = false,
onFollow, onFollow,
onAuthorClick onAuthorClick,
isMuted: externalMuted,
onMuteToggle
}) => { }) => {
const videoRef = useRef<HTMLVideoElement>(null); const videoRef = useRef<HTMLVideoElement>(null);
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
@ -34,7 +38,9 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
const [duration, setDuration] = useState(0); const [duration, setDuration] = useState(0);
const [isSeeking, setIsSeeking] = useState(false); const [isSeeking, setIsSeeking] = useState(false);
const [useFallback, setUseFallback] = useState(false); // Fallback to full proxy 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<HeartParticle[]>([]); const [hearts, setHearts] = useState<HeartParticle[]>([]);
const lastTapRef = useRef<number>(0); const lastTapRef = useRef<number>(0);
@ -52,7 +58,7 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
// Auto-play when becoming active // Auto-play when becoming active
if (videoRef.current.paused) { if (videoRef.current.paused) {
videoRef.current.currentTime = 0; 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) => { videoRef.current.play().catch((err) => {
// If autoplay fails even muted, show paused state // If autoplay fails even muted, show paused state
console.log('Autoplay blocked:', err.message); console.log('Autoplay blocked:', err.message);
@ -65,6 +71,13 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
} }
}, [isActive]); // Only trigger on isActive change }, [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 // Spacebar to pause/play when this video is active
useEffect(() => { useEffect(() => {
if (!isActive) return; if (!isActive) return;
@ -148,9 +161,13 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
const toggleMute = (e: React.MouseEvent | React.TouchEvent) => { const toggleMute = (e: React.MouseEvent | React.TouchEvent) => {
e.stopPropagation(); // Prevent video tap e.stopPropagation(); // Prevent video tap
if (!videoRef.current) return; if (!videoRef.current) return;
const newMuted = !videoRef.current.muted;
videoRef.current.muted = newMuted; // Use external toggle if provided, otherwise use local state
setIsMuted(newMuted); if (onMuteToggle) {
onMuteToggle();
} else {
setLocalMuted(prev => !prev);
}
}; };
// Handle tap - double tap shows heart, single tap toggles play // Handle tap - double tap shows heart, single tap toggles play