mirror of
https://github.com/vndangkhoa/purestream.git
synced 2026-04-05 17:37:59 +07:00
Persist mute state across video scrolling
This commit is contained in:
parent
2d622c259f
commit
929df62342
2 changed files with 30 additions and 8 deletions
|
|
@ -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);
|
||||||
|
|
@ -655,8 +658,8 @@ export const Feed: React.FC = () => {
|
||||||
onClick={handleJsonLogin}
|
onClick={handleJsonLogin}
|
||||||
disabled={!jsonInput.trim()}
|
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()
|
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-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-gray-700 cursor-not-allowed'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
Connect to TikTok
|
Connect to TikTok
|
||||||
|
|
@ -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 */
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue