mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-04-27 19:33:02 +00:00
Add close button to VideoPlayer and improve loading indicators in StreamsScreen
This update introduces a close button in the VideoPlayer component for better user control during video loading. Additionally, the StreamsScreen has been enhanced to show loading indicators for individual stream providers, improving the user experience by providing visual feedback during data fetching.
This commit is contained in:
parent
988a746a5b
commit
6c44c0ec59
3 changed files with 67 additions and 18 deletions
|
|
@ -9,6 +9,7 @@ import * as ScreenOrientation from 'expo-screen-orientation';
|
||||||
import { storageService } from '../../services/storageService';
|
import { storageService } from '../../services/storageService';
|
||||||
import { logger } from '../../utils/logger';
|
import { logger } from '../../utils/logger';
|
||||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||||
|
import { MaterialIcons } from '@expo/vector-icons';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DEFAULT_SUBTITLE_SIZE,
|
DEFAULT_SUBTITLE_SIZE,
|
||||||
|
|
@ -747,6 +748,14 @@ const VideoPlayer: React.FC = () => {
|
||||||
]}
|
]}
|
||||||
pointerEvents={isOpeningAnimationComplete ? 'none' : 'auto'}
|
pointerEvents={isOpeningAnimationComplete ? 'none' : 'auto'}
|
||||||
>
|
>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.loadingCloseButton}
|
||||||
|
onPress={handleClose}
|
||||||
|
activeOpacity={0.7}
|
||||||
|
>
|
||||||
|
<MaterialIcons name="close" size={24} color="#ffffff" />
|
||||||
|
</TouchableOpacity>
|
||||||
|
|
||||||
<View style={styles.openingContent}>
|
<View style={styles.openingContent}>
|
||||||
<ActivityIndicator size="large" color="#E50914" />
|
<ActivityIndicator size="large" color="#E50914" />
|
||||||
<Text style={styles.openingText}>Loading video...</Text>
|
<Text style={styles.openingText}>Loading video...</Text>
|
||||||
|
|
|
||||||
|
|
@ -752,4 +752,16 @@ export const styles = StyleSheet.create({
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
marginTop: 2,
|
marginTop: 2,
|
||||||
},
|
},
|
||||||
|
loadingCloseButton: {
|
||||||
|
position: 'absolute',
|
||||||
|
top: 40,
|
||||||
|
right: 20,
|
||||||
|
width: 44,
|
||||||
|
height: 44,
|
||||||
|
backgroundColor: 'rgba(0, 0, 0, 0.6)',
|
||||||
|
borderRadius: 22,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
zIndex: 9999,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
@ -856,11 +856,8 @@ export const StreamsScreen = () => {
|
||||||
|
|
||||||
const renderItem = useCallback(({ item, index, section }: { item: Stream; index: number; section: any }) => {
|
const renderItem = useCallback(({ item, index, section }: { item: Stream; index: number; section: any }) => {
|
||||||
const stream = item;
|
const stream = item;
|
||||||
const isLoading = loadingProviders[section.addonId];
|
// Don't show loading for individual streams that are already available and displayed
|
||||||
|
const isLoading = false; // If streams are being rendered, they're available and shouldn't be loading
|
||||||
// Special handling for HDRezka streams
|
|
||||||
const quality = stream.title?.match(/(\d+)p/)?.[1] || null;
|
|
||||||
const isHDRezka = section.addonId === 'hdrezka';
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StreamCard
|
<StreamCard
|
||||||
|
|
@ -869,20 +866,35 @@ export const StreamsScreen = () => {
|
||||||
onPress={() => handleStreamPress(stream)}
|
onPress={() => handleStreamPress(stream)}
|
||||||
index={index}
|
index={index}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
statusMessage={providerStatus[section.addonId]?.message}
|
statusMessage={undefined}
|
||||||
theme={currentTheme}
|
theme={currentTheme}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}, [handleStreamPress, loadingProviders, providerStatus, currentTheme]);
|
}, [handleStreamPress, currentTheme]);
|
||||||
|
|
||||||
const renderSectionHeader = useCallback(({ section }: { section: { title: string; addonId: string } }) => (
|
const renderSectionHeader = useCallback(({ section }: { section: { title: string; addonId: string } }) => {
|
||||||
<Animated.View
|
const isProviderLoading = loadingProviders[section.addonId];
|
||||||
entering={FadeIn.duration(400)}
|
|
||||||
layout={Layout.springify()}
|
return (
|
||||||
>
|
<Animated.View
|
||||||
<Text style={styles.streamGroupTitle}>{section.title}</Text>
|
entering={FadeIn.duration(400)}
|
||||||
</Animated.View>
|
layout={Layout.springify()}
|
||||||
), [styles.streamGroupTitle]);
|
style={styles.sectionHeaderContainer}
|
||||||
|
>
|
||||||
|
<View style={styles.sectionHeaderContent}>
|
||||||
|
<Text style={styles.streamGroupTitle}>{section.title}</Text>
|
||||||
|
{isProviderLoading && (
|
||||||
|
<View style={styles.sectionLoadingIndicator}>
|
||||||
|
<ActivityIndicator size="small" color={colors.primary} />
|
||||||
|
<Text style={[styles.sectionLoadingText, { color: colors.primary }]}>
|
||||||
|
Loading...
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</Animated.View>
|
||||||
|
);
|
||||||
|
}, [styles.streamGroupTitle, styles.sectionHeaderContainer, styles.sectionHeaderContent, styles.sectionLoadingIndicator, styles.sectionLoadingText, loadingProviders, colors.primary]);
|
||||||
|
|
||||||
// Cleanup on unmount
|
// Cleanup on unmount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -1088,7 +1100,8 @@ export const StreamsScreen = () => {
|
||||||
)}
|
)}
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
|
|
||||||
{isLoading || (Object.keys(streams).length === 0 && (loadingStreams || loadingEpisodeStreams)) ? (
|
{/* Show streams immediately as they become available, with loading indicators for pending providers */}
|
||||||
|
{Object.keys(streams).length === 0 && (loadingStreams || loadingEpisodeStreams) ? (
|
||||||
<Animated.View
|
<Animated.View
|
||||||
entering={FadeIn.duration(300)}
|
entering={FadeIn.duration(300)}
|
||||||
style={styles.loadingContainer}
|
style={styles.loadingContainer}
|
||||||
|
|
@ -1096,7 +1109,7 @@ export const StreamsScreen = () => {
|
||||||
<ActivityIndicator size="large" color={colors.primary} />
|
<ActivityIndicator size="large" color={colors.primary} />
|
||||||
<Text style={styles.loadingText}>Finding available streams...</Text>
|
<Text style={styles.loadingText}>Finding available streams...</Text>
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
) : Object.keys(streams).length === 0 ? (
|
) : Object.keys(streams).length === 0 && !loadingStreams && !loadingEpisodeStreams ? (
|
||||||
<Animated.View
|
<Animated.View
|
||||||
entering={FadeIn.duration(300)}
|
entering={FadeIn.duration(300)}
|
||||||
style={styles.noStreams}
|
style={styles.noStreams}
|
||||||
|
|
@ -1122,7 +1135,7 @@ export const StreamsScreen = () => {
|
||||||
bounces={true}
|
bounces={true}
|
||||||
overScrollMode="never"
|
overScrollMode="never"
|
||||||
ListFooterComponent={
|
ListFooterComponent={
|
||||||
isLoading ? (
|
(loadingStreams || loadingEpisodeStreams) ? (
|
||||||
<View style={styles.footerLoading}>
|
<View style={styles.footerLoading}>
|
||||||
<ActivityIndicator size="small" color={colors.primary} />
|
<ActivityIndicator size="small" color={colors.primary} />
|
||||||
<Text style={styles.footerLoadingText}>Loading more sources...</Text>
|
<Text style={styles.footerLoadingText}>Loading more sources...</Text>
|
||||||
|
|
@ -1540,6 +1553,21 @@ const createStyles = (colors: any) => StyleSheet.create({
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
zIndex: 9999,
|
zIndex: 9999,
|
||||||
},
|
},
|
||||||
|
sectionHeaderContainer: {
|
||||||
|
padding: 16,
|
||||||
|
},
|
||||||
|
sectionHeaderContent: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
},
|
||||||
|
sectionLoadingIndicator: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
sectionLoadingText: {
|
||||||
|
marginLeft: 8,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default memo(StreamsScreen);
|
export default memo(StreamsScreen);
|
||||||
Loading…
Reference in a new issue