diff --git a/src/navigation/AppNavigator.tsx b/src/navigation/AppNavigator.tsx
index 1c1cec2..86d41b1 100644
--- a/src/navigation/AppNavigator.tsx
+++ b/src/navigation/AppNavigator.tsx
@@ -780,8 +780,10 @@ const AppNavigator = () => {
component={StreamsScreen as any}
options={{
headerShown: false,
- animation: Platform.OS === 'ios' ? 'slide_from_bottom' : 'fade_from_bottom',
- animationDuration: Platform.OS === 'android' ? 200 : 300,
+ animation: Platform.OS === 'ios' ? 'slide_from_bottom' : 'none',
+ animationDuration: Platform.OS === 'android' ? 0 : 300,
+ gestureEnabled: true,
+ gestureDirection: Platform.OS === 'ios' ? 'vertical' : 'horizontal',
...(Platform.OS === 'ios' && { presentation: 'modal' }),
contentStyle: {
backgroundColor: currentTheme.colors.darkBackground,
@@ -825,8 +827,30 @@ const AppNavigator = () => {
name="Search"
component={SearchScreen as any}
options={{
- animation: 'fade',
- animationDuration: Platform.OS === 'android' ? 300 : 350,
+ animation: Platform.OS === 'android' ? 'slide_from_right' : 'fade',
+ animationDuration: Platform.OS === 'android' ? 250 : 350,
+ gestureEnabled: true,
+ gestureDirection: 'horizontal',
+ ...(Platform.OS === 'android' && {
+ cardStyleInterpolator: ({ current, layouts }: any) => {
+ return {
+ cardStyle: {
+ transform: [
+ {
+ translateX: current.progress.interpolate({
+ inputRange: [0, 1],
+ outputRange: [layouts.screen.width, 0],
+ }),
+ },
+ ],
+ opacity: current.progress.interpolate({
+ inputRange: [0, 0.3, 1],
+ outputRange: [0, 0.85, 1],
+ }),
+ },
+ };
+ },
+ }),
contentStyle: {
backgroundColor: currentTheme.colors.darkBackground,
},
diff --git a/src/screens/SearchScreen.tsx b/src/screens/SearchScreen.tsx
index 2d57e65..7ba205b 100644
--- a/src/screens/SearchScreen.tsx
+++ b/src/screens/SearchScreen.tsx
@@ -287,7 +287,14 @@ const SearchScreen = () => {
setShowRecent(true);
loadRecentSearches();
} else {
- navigation.goBack();
+ // Add a small delay to allow keyboard to dismiss smoothly before navigation
+ if (Platform.OS === 'android') {
+ setTimeout(() => {
+ navigation.goBack();
+ }, 100);
+ } else {
+ navigation.goBack();
+ }
}
};
@@ -497,7 +504,14 @@ const SearchScreen = () => {
const headerHeight = headerBaseHeight + topSpacing + 60;
return (
-
+
{
)}
-
+
);
};
diff --git a/src/screens/StreamsScreen.tsx b/src/screens/StreamsScreen.tsx
index d7f2f59..c3750c5 100644
--- a/src/screens/StreamsScreen.tsx
+++ b/src/screens/StreamsScreen.tsx
@@ -33,6 +33,7 @@ import { useSettings } from '../hooks/useSettings';
import QualityBadge from '../components/metadata/QualityBadge';
import Animated, {
FadeIn,
+ FadeOut,
FadeInDown,
SlideInDown,
withSpring,
@@ -55,13 +56,14 @@ const DOLBY_ICON = 'https://upload.wikimedia.org/wikipedia/en/thumb/3/3f/Dolby_V
const { width, height } = Dimensions.get('window');
// Extracted Components
-const StreamCard = ({ stream, onPress, index, isLoading, statusMessage, theme }: {
+const StreamCard = ({ stream, onPress, index, isLoading, statusMessage, theme, isExiting }: {
stream: Stream;
onPress: () => void;
index: number;
isLoading?: boolean;
statusMessage?: string;
theme: any;
+ isExiting?: boolean;
}) => {
const styles = React.useMemo(() => createStyles(theme.colors), [theme.colors]);
@@ -78,13 +80,92 @@ const StreamCard = ({ stream, onPress, index, isLoading, statusMessage, theme }:
const displayTitle = isHDRezka ? `HDRezka ${stream.title}` : (stream.name || stream.title || 'Unnamed Stream');
const displayAddonName = isHDRezka ? '' : (stream.title || '');
- // Animation delay based on index - stagger effect
- const enterDelay = 100 + (index * 50);
+ // Animation delay based on index - stagger effect (only if not exiting)
+ const enterDelay = isExiting ? 0 : 100 + (index * 30);
+
+ // Use simple View when exiting to prevent animation conflicts
+ if (isExiting) {
+ return (
+
+
+
+
+
+
+ {displayTitle}
+
+ {displayAddonName && displayAddonName !== displayTitle && (
+
+ {displayAddonName}
+
+ )}
+
+
+ {/* Show loading indicator if stream is loading */}
+ {isLoading && (
+
+
+
+ {statusMessage || "Loading..."}
+
+
+ )}
+
+
+
+ {quality && quality >= "720" && (
+
+ )}
+
+ {isDolby && (
+
+ )}
+
+ {size && (
+
+ {size}
+
+ )}
+
+ {isDebrid && (
+
+ DEBRID
+
+ )}
+
+ {/* Special badge for HDRezka streams */}
+ {isHDRezka && (
+
+ HDREZKA
+
+ )}
+
+
+
+
+
+
+
+
+ );
+ }
return (
{
// Add state for handling orientation transition
const [isTransitioning, setIsTransitioning] = useState(false);
+
+ // Add state to prevent animation conflicts during exit
+ const [isExiting, setIsExiting] = useState(false);
// Add timing logs
const [loadStartTime, setLoadStartTime] = useState(0);
@@ -400,20 +484,25 @@ export const StreamsScreen = () => {
// Memoize handlers
const handleBack = useCallback(() => {
+ // Set exit state to prevent animation conflicts and hide content immediately
+ setIsExiting(true);
+
const cleanup = () => {
- headerOpacity.value = withTiming(0, { duration: 200 });
- heroScale.value = withTiming(0.95, { duration: 200 });
- filterOpacity.value = withTiming(0, { duration: 200 });
+ headerOpacity.value = withTiming(0, { duration: 100 });
+ heroScale.value = withTiming(0.95, { duration: 100 });
+ filterOpacity.value = withTiming(0, { duration: 100 });
};
cleanup();
// For series episodes, always replace current screen with metadata screen
if (type === 'series') {
+ // Immediate navigation for series
navigation.replace('Metadata', {
id: id,
type: type
});
} else {
+ // Immediate navigation for movies
navigation.goBack();
}
}, [navigation, headerOpacity, heroScale, filterOpacity, type, id]);
@@ -954,9 +1043,10 @@ export const StreamsScreen = () => {
isLoading={isLoading}
statusMessage={undefined}
theme={currentTheme}
+ isExiting={isExiting}
/>
);
- }, [handleStreamPress, currentTheme]);
+ }, [handleStreamPress, currentTheme, isExiting]);
const renderSectionHeader = useCallback(({ section }: { section: { title: string; addonId: string } }) => {
const isProviderLoading = loadingProviders[section.addonId];
@@ -1035,6 +1125,11 @@ export const StreamsScreen = () => {
barStyle="light-content"
/>
+ {/* Instant overlay when exiting to prevent glitches */}
+ {isExiting && (
+
+ )}
+
{/* Transition overlay to mask orientation changes */}
{isTransitioning && (