Refactor LivePlayer component to improve loading state handling and error messaging

This commit is contained in:
zimplexing
2025-07-10 17:26:12 +08:00
parent 2ab64a683c
commit eaa783824d
2 changed files with 86 additions and 23 deletions

View File

@@ -16,7 +16,6 @@ export default function LiveScreen() {
const [currentChannelIndex, setCurrentChannelIndex] = useState(0);
const [isLoading, setIsLoading] = useState(false);
const [isPlayerLoading, setIsPlayerLoading] = useState(true);
const [isChannelListVisible, setIsChannelListVisible] = useState(false);
const [channelTitle, setChannelTitle] = useState<string | null>(null);
const titleTimer = useRef<NodeJS.Timeout | null>(null);
@@ -51,14 +50,6 @@ export default function LiveScreen() {
loadChannels();
}, []);
const handlePlaybackStatusUpdate = (status: AVPlaybackStatus) => {
if (status.isLoaded) {
setIsPlayerLoading(!status.isPlaying && !status.isBuffering);
} else {
setIsPlayerLoading(true);
}
};
const showChannelTitle = (title: string) => {
setChannelTitle(title);
if (titleTimer.current) clearTimeout(titleTimer.current);
@@ -98,16 +89,7 @@ export default function LiveScreen() {
return (
<ThemedView style={styles.container}>
<LivePlayer
streamUrl={selectedChannelUrl}
channelTitle={channelTitle}
onPlaybackStatusUpdate={handlePlaybackStatusUpdate}
/>
{isPlayerLoading && (
<View style={styles.loadingOverlay}>
<ActivityIndicator size="large" color="#fff" />
</View>
)}
<LivePlayer streamUrl={selectedChannelUrl} channelTitle={channelTitle} onPlaybackStatusUpdate={() => {}} />
<Modal
animationType="slide"
transparent={true}

View File

@@ -1,4 +1,4 @@
import React, { useRef } from "react";
import React, { useRef, useState, useEffect } from "react";
import { View, StyleSheet, Text, ActivityIndicator } from "react-native";
import { Video, ResizeMode, AVPlaybackStatus } from "expo-av";
@@ -8,13 +8,73 @@ interface LivePlayerProps {
onPlaybackStatusUpdate: (status: AVPlaybackStatus) => void;
}
const PLAYBACK_TIMEOUT = 15000; // 15 seconds
export default function LivePlayer({ streamUrl, channelTitle, onPlaybackStatusUpdate }: LivePlayerProps) {
const video = useRef<Video>(null);
const [isLoading, setIsLoading] = useState(false);
const [isTimeout, setIsTimeout] = useState(false);
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
useEffect(() => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
if (streamUrl) {
setIsLoading(true);
setIsTimeout(false);
timeoutRef.current = setTimeout(() => {
setIsTimeout(true);
setIsLoading(false);
}, PLAYBACK_TIMEOUT);
} else {
setIsLoading(false);
setIsTimeout(false);
}
return () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
};
}, [streamUrl]);
const handlePlaybackStatusUpdate = (status: AVPlaybackStatus) => {
if (status.isLoaded) {
if (status.isPlaying) {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
setIsLoading(false);
setIsTimeout(false);
} else if (status.isBuffering) {
setIsLoading(true);
}
} else {
if (status.error) {
setIsLoading(false);
setIsTimeout(true);
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
}
}
onPlaybackStatusUpdate(status);
};
if (!streamUrl) {
return (
<View style={styles.container}>
<Text>Select a channel to play.</Text>
<Text style={styles.messageText}>Select a channel to play.</Text>
</View>
);
}
if (isTimeout) {
return (
<View style={styles.container}>
<Text style={styles.messageText}>Failed to load stream. It might be offline or unavailable.</Text>
</View>
);
}
@@ -29,9 +89,19 @@ export default function LivePlayer({ streamUrl, channelTitle, onPlaybackStatusUp
}}
resizeMode={ResizeMode.CONTAIN}
shouldPlay
onPlaybackStatusUpdate={onPlaybackStatusUpdate}
onPlaybackStatusUpdate={handlePlaybackStatusUpdate}
onError={(e) => {
setIsTimeout(true);
setIsLoading(false);
}}
/>
{channelTitle && (
{isLoading && (
<View style={styles.loadingOverlay}>
<ActivityIndicator size="large" color="#fff" />
<Text style={styles.messageText}>Loading...</Text>
</View>
)}
{channelTitle && !isLoading && !isTimeout && (
<View style={styles.overlay}>
<Text style={styles.title}>{channelTitle}</Text>
</View>
@@ -63,4 +133,15 @@ const styles = StyleSheet.create({
color: "#fff",
fontSize: 18,
},
messageText: {
color: "#fff",
fontSize: 16,
marginTop: 10,
},
loadingOverlay: {
...StyleSheet.absoluteFillObject,
justifyContent: "center",
alignItems: "center",
backgroundColor: "rgba(0, 0, 0, 0.5)",
},
});