mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-04-20 16:22:04 +00:00
Refactor MetadataScreen to integrate haptic feedback for user interactions; update play button and library toggle functionality to enhance user experience. Replace BlurView with ExpoBlurView and CommunityBlurView for improved visual effects across platforms.
This commit is contained in:
parent
f9514a51a6
commit
94c49a2767
1 changed files with 155 additions and 85 deletions
|
|
@ -20,7 +20,9 @@ import { useRoute, useNavigation, useFocusEffect } from '@react-navigation/nativ
|
||||||
import { MaterialIcons } from '@expo/vector-icons';
|
import { MaterialIcons } from '@expo/vector-icons';
|
||||||
import { LinearGradient } from 'expo-linear-gradient';
|
import { LinearGradient } from 'expo-linear-gradient';
|
||||||
import { Image } from 'expo-image';
|
import { Image } from 'expo-image';
|
||||||
import { BlurView } from 'expo-blur';
|
import { BlurView as ExpoBlurView } from 'expo-blur';
|
||||||
|
import { BlurView as CommunityBlurView } from '@react-native-community/blur';
|
||||||
|
import * as Haptics from 'expo-haptics';
|
||||||
import { colors } from '../styles/colors';
|
import { colors } from '../styles/colors';
|
||||||
import { useMetadata } from '../hooks/useMetadata';
|
import { useMetadata } from '../hooks/useMetadata';
|
||||||
import { CastSection as OriginalCastSection } from '../components/metadata/CastSection';
|
import { CastSection as OriginalCastSection } from '../components/metadata/CastSection';
|
||||||
|
|
@ -103,54 +105,62 @@ const ActionButtons = React.memo(({
|
||||||
navigation: NavigationProp<RootStackParamList>;
|
navigation: NavigationProp<RootStackParamList>;
|
||||||
playButtonText: string;
|
playButtonText: string;
|
||||||
animatedStyle: any;
|
animatedStyle: any;
|
||||||
}) => (
|
}) => {
|
||||||
<Animated.View style={[styles.actionButtons, animatedStyle]}>
|
// Add wrapper for play button with haptic feedback
|
||||||
<TouchableOpacity
|
const handlePlay = () => {
|
||||||
style={[styles.actionButton, styles.playButton]}
|
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);
|
||||||
onPress={handleShowStreams}
|
handleShowStreams();
|
||||||
>
|
};
|
||||||
<MaterialIcons
|
|
||||||
name={playButtonText === 'Resume' ? "play-circle-outline" : "play-arrow"}
|
|
||||||
size={24}
|
|
||||||
color="#000"
|
|
||||||
/>
|
|
||||||
<Text style={styles.playButtonText}>
|
|
||||||
{playButtonText}
|
|
||||||
</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
|
|
||||||
<TouchableOpacity
|
return (
|
||||||
style={[styles.actionButton, styles.infoButton]}
|
<Animated.View style={[styles.actionButtons, animatedStyle]}>
|
||||||
onPress={toggleLibrary}
|
|
||||||
>
|
|
||||||
<MaterialIcons
|
|
||||||
name={inLibrary ? 'bookmark' : 'bookmark-border'}
|
|
||||||
size={24}
|
|
||||||
color="#fff"
|
|
||||||
/>
|
|
||||||
<Text style={styles.infoButtonText}>
|
|
||||||
{inLibrary ? 'Saved' : 'Save'}
|
|
||||||
</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
|
|
||||||
{type === 'series' && (
|
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={[styles.iconButton]}
|
style={[styles.actionButton, styles.playButton]}
|
||||||
onPress={async () => {
|
onPress={handlePlay}
|
||||||
const tmdb = TMDBService.getInstance();
|
|
||||||
const tmdbId = await tmdb.extractTMDBIdFromStremioId(id);
|
|
||||||
if (tmdbId) {
|
|
||||||
navigation.navigate('ShowRatings', { showId: tmdbId });
|
|
||||||
} else {
|
|
||||||
logger.error('Could not find TMDB ID for show');
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<MaterialIcons name="assessment" size={24} color="#fff" />
|
<MaterialIcons
|
||||||
|
name={playButtonText === 'Resume' ? "play-circle-outline" : "play-arrow"}
|
||||||
|
size={24}
|
||||||
|
color="#000"
|
||||||
|
/>
|
||||||
|
<Text style={styles.playButtonText}>
|
||||||
|
{playButtonText}
|
||||||
|
</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
)}
|
|
||||||
</Animated.View>
|
<TouchableOpacity
|
||||||
));
|
style={[styles.actionButton, styles.infoButton]}
|
||||||
|
onPress={toggleLibrary}
|
||||||
|
>
|
||||||
|
<MaterialIcons
|
||||||
|
name={inLibrary ? 'bookmark' : 'bookmark-border'}
|
||||||
|
size={24}
|
||||||
|
color="#fff"
|
||||||
|
/>
|
||||||
|
<Text style={styles.infoButtonText}>
|
||||||
|
{inLibrary ? 'Saved' : 'Save'}
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
|
||||||
|
{type === 'series' && (
|
||||||
|
<TouchableOpacity
|
||||||
|
style={[styles.iconButton]}
|
||||||
|
onPress={async () => {
|
||||||
|
const tmdb = TMDBService.getInstance();
|
||||||
|
const tmdbId = await tmdb.extractTMDBIdFromStremioId(id);
|
||||||
|
if (tmdbId) {
|
||||||
|
navigation.navigate('ShowRatings', { showId: tmdbId });
|
||||||
|
} else {
|
||||||
|
logger.error('Could not find TMDB ID for show');
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MaterialIcons name="assessment" size={24} color="#fff" />
|
||||||
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
|
</Animated.View>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
// Memoized WatchProgress Component
|
// Memoized WatchProgress Component
|
||||||
const WatchProgressDisplay = React.memo(({
|
const WatchProgressDisplay = React.memo(({
|
||||||
|
|
@ -254,6 +264,21 @@ const MetadataScreen = () => {
|
||||||
episodeId?: string;
|
episodeId?: string;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
|
|
||||||
|
// Add wrapper for toggleLibrary that includes haptic feedback
|
||||||
|
const handleToggleLibrary = useCallback(() => {
|
||||||
|
// Trigger appropriate haptic feedback based on action
|
||||||
|
if (inLibrary) {
|
||||||
|
// Removed from library - light impact
|
||||||
|
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
||||||
|
} else {
|
||||||
|
// Added to library - success feedback
|
||||||
|
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the original toggleLibrary function
|
||||||
|
toggleLibrary();
|
||||||
|
}, [inLibrary, toggleLibrary]);
|
||||||
|
|
||||||
// Add new animated value for watch progress
|
// Add new animated value for watch progress
|
||||||
const watchProgressOpacity = useSharedValue(0);
|
const watchProgressOpacity = useSharedValue(0);
|
||||||
const watchProgressScaleY = useSharedValue(0);
|
const watchProgressScaleY = useSharedValue(0);
|
||||||
|
|
@ -1023,46 +1048,91 @@ const MetadataScreen = () => {
|
||||||
<Animated.View style={containerAnimatedStyle}>
|
<Animated.View style={containerAnimatedStyle}>
|
||||||
{/* Floating Header */}
|
{/* Floating Header */}
|
||||||
<Animated.View style={[styles.floatingHeader, headerAnimatedStyle]}>
|
<Animated.View style={[styles.floatingHeader, headerAnimatedStyle]}>
|
||||||
<BlurView
|
{Platform.OS === 'ios' ? (
|
||||||
intensity={Platform.OS === 'ios' ? 50 : 80}
|
<ExpoBlurView
|
||||||
tint="dark"
|
intensity={50}
|
||||||
style={[styles.blurContainer, { paddingTop: Math.max(safeAreaTop * 0.8, safeAreaTop - 6) }]}
|
tint="dark"
|
||||||
>
|
style={[styles.blurContainer, { paddingTop: Math.max(safeAreaTop * 0.8, safeAreaTop - 6) }]}
|
||||||
<Animated.View style={[styles.floatingHeaderContent, headerElementsStyle]}>
|
>
|
||||||
<TouchableOpacity
|
<Animated.View style={[styles.floatingHeaderContent, headerElementsStyle]}>
|
||||||
style={styles.backButton}
|
<TouchableOpacity
|
||||||
onPress={handleBack}
|
style={styles.backButton}
|
||||||
hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
|
onPress={handleBack}
|
||||||
>
|
hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
|
||||||
<MaterialIcons name="arrow-back" size={24} color={colors.highEmphasis} />
|
>
|
||||||
</TouchableOpacity>
|
<MaterialIcons name="arrow-back" size={24} color={colors.highEmphasis} />
|
||||||
|
</TouchableOpacity>
|
||||||
<View style={styles.headerTitleContainer}>
|
|
||||||
{metadata.logo ? (
|
<View style={styles.headerTitleContainer}>
|
||||||
<Image
|
{metadata.logo ? (
|
||||||
source={{ uri: metadata.logo }}
|
<Image
|
||||||
style={styles.floatingHeaderLogo}
|
source={{ uri: metadata.logo }}
|
||||||
contentFit="contain"
|
style={styles.floatingHeaderLogo}
|
||||||
transition={150}
|
contentFit="contain"
|
||||||
|
transition={150}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Text style={styles.floatingHeaderTitle} numberOfLines={1}>{metadata.name}</Text>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.headerActionButton}
|
||||||
|
onPress={handleToggleLibrary}
|
||||||
|
hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
|
||||||
|
>
|
||||||
|
<MaterialIcons
|
||||||
|
name={inLibrary ? 'bookmark' : 'bookmark-border'}
|
||||||
|
size={22}
|
||||||
|
color={colors.highEmphasis}
|
||||||
/>
|
/>
|
||||||
) : (
|
</TouchableOpacity>
|
||||||
<Text style={styles.floatingHeaderTitle} numberOfLines={1}>{metadata.name}</Text>
|
</Animated.View>
|
||||||
)}
|
</ExpoBlurView>
|
||||||
</View>
|
) : (
|
||||||
|
<View style={[styles.blurContainer, { paddingTop: Math.max(safeAreaTop * 0.8, safeAreaTop - 6) }]}>
|
||||||
<TouchableOpacity
|
<CommunityBlurView
|
||||||
style={styles.headerActionButton}
|
style={styles.absoluteFill}
|
||||||
onPress={toggleLibrary}
|
blurType="dark"
|
||||||
hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
|
blurAmount={15}
|
||||||
>
|
reducedTransparencyFallbackColor="rgba(20, 20, 20, 0.9)"
|
||||||
<MaterialIcons
|
/>
|
||||||
name={inLibrary ? 'bookmark' : 'bookmark-border'}
|
<Animated.View style={[styles.floatingHeaderContent, headerElementsStyle]}>
|
||||||
size={22}
|
<TouchableOpacity
|
||||||
color={colors.highEmphasis}
|
style={styles.backButton}
|
||||||
/>
|
onPress={handleBack}
|
||||||
</TouchableOpacity>
|
hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
|
||||||
</Animated.View>
|
>
|
||||||
</BlurView>
|
<MaterialIcons name="arrow-back" size={24} color={colors.highEmphasis} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
|
||||||
|
<View style={styles.headerTitleContainer}>
|
||||||
|
{metadata.logo ? (
|
||||||
|
<Image
|
||||||
|
source={{ uri: metadata.logo }}
|
||||||
|
style={styles.floatingHeaderLogo}
|
||||||
|
contentFit="contain"
|
||||||
|
transition={150}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Text style={styles.floatingHeaderTitle} numberOfLines={1}>{metadata.name}</Text>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.headerActionButton}
|
||||||
|
onPress={handleToggleLibrary}
|
||||||
|
hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
|
||||||
|
>
|
||||||
|
<MaterialIcons
|
||||||
|
name={inLibrary ? 'bookmark' : 'bookmark-border'}
|
||||||
|
size={22}
|
||||||
|
color={colors.highEmphasis}
|
||||||
|
/>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</Animated.View>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
{Platform.OS === 'ios' && <View style={styles.headerBottomBorder} />}
|
{Platform.OS === 'ios' && <View style={styles.headerBottomBorder} />}
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
|
|
||||||
|
|
@ -1129,7 +1199,7 @@ const MetadataScreen = () => {
|
||||||
{/* Action Buttons */}
|
{/* Action Buttons */}
|
||||||
<ActionButtons
|
<ActionButtons
|
||||||
handleShowStreams={handleShowStreams}
|
handleShowStreams={handleShowStreams}
|
||||||
toggleLibrary={toggleLibrary}
|
toggleLibrary={handleToggleLibrary}
|
||||||
inLibrary={inLibrary}
|
inLibrary={inLibrary}
|
||||||
type={type as 'movie' | 'series'}
|
type={type as 'movie' | 'series'}
|
||||||
id={id}
|
id={id}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue