mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-01-11 20:10:25 +00:00
changes CP
This commit is contained in:
parent
303c4c909e
commit
f605dd3d49
34 changed files with 191 additions and 238 deletions
1
android/app/src/main/assets/modules.json
Normal file
1
android/app/src/main/assets/modules.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
{}
|
||||
|
|
@ -189,7 +189,6 @@
|
|||
TargetAttributes = {
|
||||
13B07F861A680F5B00A75B9A = {
|
||||
LastSwiftMigration = 1250;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -502,6 +501,7 @@
|
|||
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.nuvio.app;
|
||||
PRODUCT_NAME = "Nuvio";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Nuvio/Nuvio-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
|
|
|
|||
|
|
@ -1,10 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>aps-environment</key>
|
||||
<string>development</string>
|
||||
<key>com.apple.developer.associated-domains</key>
|
||||
<array/>
|
||||
</dict>
|
||||
</plist>
|
||||
<dict/>
|
||||
</plist>
|
||||
|
|
|
|||
|
|
@ -240,15 +240,9 @@ PODS:
|
|||
- ExpoModulesCore
|
||||
- ExpoHaptics (14.0.1):
|
||||
- ExpoModulesCore
|
||||
- ExpoImage (2.0.7):
|
||||
- ExpoModulesCore
|
||||
- libavif/libdav1d
|
||||
- SDWebImage (~> 5.19.1)
|
||||
- SDWebImageAVIFCoder (~> 0.11.0)
|
||||
- SDWebImageSVGCoder (~> 1.7.0)
|
||||
- ExpoKeepAwake (14.0.3):
|
||||
- ExpoModulesCore
|
||||
- ExpoLibVlcPlayer (2.1.7):
|
||||
- ExpoLibVlcPlayer (2.2.1):
|
||||
- ExpoModulesCore
|
||||
- MobileVLCKit (= 3.6.1b1)
|
||||
- ExpoLinearGradient (14.0.2):
|
||||
|
|
@ -2519,11 +2513,9 @@ PODS:
|
|||
- SDWebImage (5.19.7):
|
||||
- SDWebImage/Core (= 5.19.7)
|
||||
- SDWebImage/Core (5.19.7)
|
||||
- SDWebImageAVIFCoder (0.11.0):
|
||||
- SDWebImageAVIFCoder (0.11.1):
|
||||
- libavif/core (>= 0.11.0)
|
||||
- SDWebImage (~> 5.10)
|
||||
- SDWebImageSVGCoder (1.7.0):
|
||||
- SDWebImage/Core (~> 5.6)
|
||||
- SDWebImageWebPCoder (0.14.6):
|
||||
- libwebp (~> 1.0)
|
||||
- SDWebImage/Core (~> 5.17)
|
||||
|
|
@ -2555,7 +2547,6 @@ DEPENDENCIES:
|
|||
- ExpoFileSystem (from `../node_modules/expo-file-system/ios`)
|
||||
- ExpoFont (from `../node_modules/expo-font/ios`)
|
||||
- ExpoHaptics (from `../node_modules/expo-haptics/ios`)
|
||||
- ExpoImage (from `../node_modules/expo-image/ios`)
|
||||
- ExpoKeepAwake (from `../node_modules/expo-keep-awake/ios`)
|
||||
- ExpoLibVlcPlayer (from `../node_modules/expo-libvlc-player/ios`)
|
||||
- ExpoLinearGradient (from `../node_modules/expo-linear-gradient/ios`)
|
||||
|
|
@ -2666,7 +2657,6 @@ SPEC REPOS:
|
|||
- ReachabilitySwift
|
||||
- SDWebImage
|
||||
- SDWebImageAVIFCoder
|
||||
- SDWebImageSVGCoder
|
||||
- SDWebImageWebPCoder
|
||||
- Sentry
|
||||
- SocketRocket
|
||||
|
|
@ -2719,8 +2709,6 @@ EXTERNAL SOURCES:
|
|||
:path: "../node_modules/expo-font/ios"
|
||||
ExpoHaptics:
|
||||
:path: "../node_modules/expo-haptics/ios"
|
||||
ExpoImage:
|
||||
:path: "../node_modules/expo-image/ios"
|
||||
ExpoKeepAwake:
|
||||
:path: "../node_modules/expo-keep-awake/ios"
|
||||
ExpoLibVlcPlayer:
|
||||
|
|
@ -2958,9 +2946,8 @@ SPEC CHECKSUMS:
|
|||
ExpoFileSystem: 42d363d3b96f9afab980dcef60d5657a4443c655
|
||||
ExpoFont: f354e926f8feae5e831ec8087f36652b44a0b188
|
||||
ExpoHaptics: 8d199b2f33245ea85289ff6c954c7ee7c00a5b5d
|
||||
ExpoImage: d840b256050f4428d2942bc2b6e9251f9e0d7021
|
||||
ExpoKeepAwake: b0171a73665bfcefcfcc311742a72a956e6aa680
|
||||
ExpoLibVlcPlayer: 027c16c178364a133f6ee10fc7a7d8636f92dbe8
|
||||
ExpoLibVlcPlayer: dce3d0b5847838cd5f8c5f3c3aa1bc55c92e911d
|
||||
ExpoLinearGradient: 35ebd83b16f80b3add053a2fd68cc328ed927f60
|
||||
ExpoLinking: 8d12bee174ba0cdf31239706578e29e74a417402
|
||||
ExpoLocalization: 7776ea3bdb112125390745bbaf919b734b2ad1c7
|
||||
|
|
@ -3062,8 +3049,7 @@ SPEC CHECKSUMS:
|
|||
RNSVG: b889dc9c1948eeea0576a16cc405c91c37a12c19
|
||||
RNVectorIcons: c95fdae217b0ed388f2b4d7ed7a4edc457c1df47
|
||||
SDWebImage: 8a6b7b160b4d710e2a22b6900e25301075c34cb3
|
||||
SDWebImageAVIFCoder: 00310d246aab3232ce77f1d8f0076f8c4b021d90
|
||||
SDWebImageSVGCoder: 15a300a97ec1c8ac958f009c02220ac0402e936c
|
||||
SDWebImageAVIFCoder: afe194a084e851f70228e4be35ef651df0fc5c57
|
||||
SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380
|
||||
Sentry: 1ca8405451040482877dcd344dfa3ef80b646631
|
||||
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
|
||||
|
|
|
|||
20
package-lock.json
generated
20
package-lock.json
generated
|
|
@ -47,9 +47,8 @@
|
|||
"expo-document-picker": "~13.0.3",
|
||||
"expo-file-system": "~18.0.12",
|
||||
"expo-haptics": "~14.0.1",
|
||||
"expo-image": "~2.0.7",
|
||||
"expo-intent-launcher": "~12.0.2",
|
||||
"expo-libvlc-player": "^2.2.1",
|
||||
"expo-libvlc-player": "^2.1.7",
|
||||
"expo-linear-gradient": "~14.0.2",
|
||||
"expo-localization": "~16.0.1",
|
||||
"expo-notifications": "~0.29.14",
|
||||
|
|
@ -7986,23 +7985,6 @@
|
|||
"expo": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/expo-image": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/expo-image/-/expo-image-2.0.7.tgz",
|
||||
"integrity": "sha512-kv40OIJOkItwznhdqFmKxTMC5O8GkpyTf8ng7Py4Hy6IBiH59dkeP6vUZQhzPhJOm5v1kZK4XldbskBosqzOug==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"expo": "*",
|
||||
"react": "*",
|
||||
"react-native": "*",
|
||||
"react-native-web": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react-native-web": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/expo-intent-launcher": {
|
||||
"version": "12.0.2",
|
||||
"resolved": "https://registry.npmjs.org/expo-intent-launcher/-/expo-intent-launcher-12.0.2.tgz",
|
||||
|
|
|
|||
|
|
@ -47,7 +47,6 @@
|
|||
"expo-document-picker": "~13.0.3",
|
||||
"expo-file-system": "~18.0.12",
|
||||
"expo-haptics": "~14.0.1",
|
||||
"expo-image": "~2.0.7",
|
||||
"expo-intent-launcher": "~12.0.2",
|
||||
"expo-libvlc-player": "^2.1.7",
|
||||
"expo-linear-gradient": "~14.0.2",
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ const OptimizedImage: React.FC<OptimizedImageProps> = ({
|
|||
if (!optimizedUrl || !isVisible) return;
|
||||
|
||||
try {
|
||||
await FastImage.preload([{ uri: optimizedUrl }]);
|
||||
FastImage.preload([{ uri: optimizedUrl }]);
|
||||
if (!mountedRef.current) return;
|
||||
setIsLoaded(true);
|
||||
onLoad?.();
|
||||
|
|
|
|||
|
|
@ -232,10 +232,9 @@ const FeaturedContent = ({ featuredContent, isSaved, handleSaveToLibrary, loadin
|
|||
}, 1500);
|
||||
});
|
||||
|
||||
await Promise.race([
|
||||
FastImage.preload([{ uri: url }]),
|
||||
timeout,
|
||||
]);
|
||||
// FastImage.preload doesn't return a promise, so we just call it and use timeout
|
||||
FastImage.preload([{ uri: url }]);
|
||||
await timeout;
|
||||
imageCache[url] = true;
|
||||
logger.debug('[FeaturedContent] preloadImage:success', { url, duration: since(t0) });
|
||||
return true;
|
||||
|
|
@ -569,7 +568,7 @@ const FeaturedContent = ({ featuredContent, isSaved, handleSaveToLibrary, loadin
|
|||
priority: FastImage.priority.high,
|
||||
cache: FastImage.cacheControl.immutable
|
||||
}}
|
||||
style={styles.tabletLogo as ImageStyle}
|
||||
style={styles.tabletLogo as any}
|
||||
resizeMode={FastImage.resizeMode.contain}
|
||||
onError={onLogoLoadError}
|
||||
/>
|
||||
|
|
@ -703,7 +702,7 @@ const FeaturedContent = ({ featuredContent, isSaved, handleSaveToLibrary, loadin
|
|||
priority: FastImage.priority.high,
|
||||
cache: FastImage.cacheControl.immutable
|
||||
}}
|
||||
style={styles.featuredLogo as ImageStyle}
|
||||
style={styles.featuredLogo as any}
|
||||
resizeMode={FastImage.resizeMode.contain}
|
||||
onError={onLogoLoadError}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -166,9 +166,8 @@ const HeroCarousel: React.FC<HeroCarouselProps> = ({ items, loading = false }) =
|
|||
priority: FastImage.priority.low,
|
||||
cache: FastImage.cacheControl.immutable
|
||||
}}
|
||||
style={styles.backgroundImage as ImageStyle}
|
||||
style={styles.backgroundImage as any}
|
||||
resizeMode={FastImage.resizeMode.cover}
|
||||
blurRadius={Platform.OS === 'android' ? 8 : 12}
|
||||
/>
|
||||
<LinearGradient
|
||||
colors={["rgba(0,0,0,0.45)", "rgba(0,0,0,0.75)"]}
|
||||
|
|
@ -291,7 +290,7 @@ const CarouselCard: React.FC<CarouselCardProps> = memo(({ item, colors, logoFail
|
|||
priority: FastImage.priority.normal,
|
||||
cache: FastImage.cacheControl.immutable
|
||||
}}
|
||||
style={styles.banner as ImageStyle}
|
||||
style={styles.banner as any}
|
||||
resizeMode={FastImage.resizeMode.cover}
|
||||
/>
|
||||
<LinearGradient
|
||||
|
|
@ -308,7 +307,7 @@ const CarouselCard: React.FC<CarouselCardProps> = memo(({ item, colors, logoFail
|
|||
priority: FastImage.priority.high,
|
||||
cache: FastImage.cacheControl.immutable
|
||||
}}
|
||||
style={styles.logo as ImageStyle}
|
||||
style={styles.logo as any}
|
||||
resizeMode={FastImage.resizeMode.contain}
|
||||
onError={onLogoError}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import {
|
|||
} from 'react-native';
|
||||
import { MaterialIcons } from '@expo/vector-icons';
|
||||
import { BlurView } from 'expo-blur';
|
||||
import { Image } from 'expo-image';
|
||||
import FastImage from '@d11/react-native-fast-image';
|
||||
import Animated, {
|
||||
FadeIn,
|
||||
FadeOut,
|
||||
|
|
@ -228,12 +228,12 @@ export const CastDetailsModal: React.FC<CastDetailsModalProps> = ({
|
|||
backgroundColor: 'rgba(255, 255, 255, 0.08)',
|
||||
}}>
|
||||
{castMember?.profile_path ? (
|
||||
<Image
|
||||
<FastImage
|
||||
source={{
|
||||
uri: `https://image.tmdb.org/t/p/w185${castMember.profile_path}`,
|
||||
}}
|
||||
style={{ width: '100%', height: '100%' }}
|
||||
contentFit="cover"
|
||||
resizeMode={FastImage.resizeMode.cover}
|
||||
/>
|
||||
) : (
|
||||
<View style={{
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
TouchableOpacity,
|
||||
ActivityIndicator,
|
||||
} from 'react-native';
|
||||
import { Image } from 'expo-image';
|
||||
import FastImage from '@d11/react-native-fast-image';
|
||||
import Animated, {
|
||||
FadeIn,
|
||||
} from 'react-native-reanimated';
|
||||
|
|
@ -65,13 +65,12 @@ export const CastSection: React.FC<CastSectionProps> = ({
|
|||
>
|
||||
<View style={styles.castImageContainer}>
|
||||
{item.profile_path ? (
|
||||
<Image
|
||||
<FastImage
|
||||
source={{
|
||||
uri: `https://image.tmdb.org/t/p/w185${item.profile_path}`,
|
||||
}}
|
||||
style={styles.castImage}
|
||||
contentFit="cover"
|
||||
transition={200}
|
||||
resizeMode={FastImage.resizeMode.cover}
|
||||
/>
|
||||
) : (
|
||||
<View style={[styles.castImagePlaceholder, { backgroundColor: currentTheme.colors.darkBackground }]}>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import {
|
|||
} from 'react-native';
|
||||
import { BlurView as ExpoBlurView } from 'expo-blur';
|
||||
import { MaterialIcons } from '@expo/vector-icons';
|
||||
import { Image } from 'expo-image';
|
||||
import FastImage from '@d11/react-native-fast-image';
|
||||
import Animated, {
|
||||
useAnimatedStyle,
|
||||
interpolate,
|
||||
|
|
@ -96,11 +96,10 @@ const FloatingHeader: React.FC<FloatingHeaderProps> = ({
|
|||
|
||||
<View style={styles.headerTitleContainer}>
|
||||
{metadata.logo && !logoLoadError ? (
|
||||
<Image
|
||||
<FastImage
|
||||
source={{ uri: metadata.logo }}
|
||||
style={styles.floatingHeaderLogo}
|
||||
contentFit="contain"
|
||||
transition={150}
|
||||
resizeMode={FastImage.resizeMode.contain}
|
||||
onError={() => {
|
||||
logger.warn(`[FloatingHeader] Logo failed to load: ${metadata.logo}`);
|
||||
setLogoLoadError(true);
|
||||
|
|
@ -146,11 +145,10 @@ const FloatingHeader: React.FC<FloatingHeaderProps> = ({
|
|||
|
||||
<View style={styles.headerTitleContainer}>
|
||||
{metadata.logo && !logoLoadError ? (
|
||||
<Image
|
||||
<FastImage
|
||||
source={{ uri: metadata.logo }}
|
||||
style={styles.floatingHeaderLogo}
|
||||
contentFit="contain"
|
||||
transition={150}
|
||||
resizeMode={FastImage.resizeMode.contain}
|
||||
onError={() => {
|
||||
logger.warn(`[FloatingHeader] Logo failed to load: ${metadata.logo}`);
|
||||
setLogoLoadError(true);
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import { useFocusEffect, useIsFocused } from '@react-navigation/native';
|
|||
|
||||
import { MaterialIcons } from '@expo/vector-icons';
|
||||
import { LinearGradient } from 'expo-linear-gradient';
|
||||
import { Image } from 'expo-image';
|
||||
import FastImage from '@d11/react-native-fast-image';
|
||||
import { BlurView as ExpoBlurView } from 'expo-blur';
|
||||
import { BlurView as CommunityBlurView } from '@react-native-community/blur';
|
||||
import Constants, { ExecutionEnvironment } from 'expo-constants';
|
||||
|
|
@ -1489,11 +1489,10 @@ const HeroSection: React.FC<HeroSectionProps> = memo(({
|
|||
<Animated.View style={[styles.logoContainer, titleCardAnimatedStyle]}>
|
||||
<Animated.View style={[styles.titleLogoContainer, logoAnimatedStyle]}>
|
||||
{shouldLoadSecondaryData && metadata.logo && !logoLoadError ? (
|
||||
<Image
|
||||
<FastImage
|
||||
source={{ uri: metadata.logo }}
|
||||
style={isTablet ? styles.tabletTitleLogo : styles.titleLogo}
|
||||
contentFit="contain"
|
||||
transition={150}
|
||||
resizeMode={FastImage.resizeMode.contain}
|
||||
onError={() => {
|
||||
runOnJS(setLogoLoadError)(true);
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
ActivityIndicator,
|
||||
} from 'react-native';
|
||||
import { MaterialIcons } from '@expo/vector-icons';
|
||||
import { Image } from 'expo-image';
|
||||
import FastImage from '@d11/react-native-fast-image';
|
||||
import Animated, {
|
||||
FadeIn,
|
||||
useAnimatedStyle,
|
||||
|
|
@ -162,10 +162,10 @@ function formatRuntime(runtime: string): string {
|
|||
)}
|
||||
{metadata.imdbRating && !isMDBEnabled && (
|
||||
<View style={styles.ratingContainer}>
|
||||
<Image
|
||||
<FastImage
|
||||
source={{ uri: 'https://upload.wikimedia.org/wikipedia/commons/thumb/6/69/IMDB_Logo_2016.svg/575px-IMDB_Logo_2016.svg.png' }}
|
||||
style={styles.imdbLogo}
|
||||
contentFit="contain"
|
||||
resizeMode={FastImage.resizeMode.contain}
|
||||
/>
|
||||
<Text style={[styles.ratingText, { color: currentTheme.colors.text }]}>{metadata.imdbRating}</Text>
|
||||
</View>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import {
|
|||
ActivityIndicator,
|
||||
Dimensions,
|
||||
} from 'react-native';
|
||||
import { Image } from 'expo-image';
|
||||
import FastImage from '@d11/react-native-fast-image';
|
||||
import { useNavigation, StackActions } from '@react-navigation/native';
|
||||
import { NavigationProp } from '@react-navigation/native';
|
||||
import { RootStackParamList } from '../../navigation/AppNavigator';
|
||||
|
|
@ -97,11 +97,10 @@ export const MoreLikeThisSection: React.FC<MoreLikeThisSectionProps> = ({
|
|||
style={styles.itemContainer}
|
||||
onPress={() => handleItemPress(item)}
|
||||
>
|
||||
<Image
|
||||
<FastImage
|
||||
source={{ uri: item.poster }}
|
||||
style={[styles.poster, { backgroundColor: currentTheme.colors.elevation1 }]}
|
||||
contentFit="cover"
|
||||
transition={200}
|
||||
resizeMode={FastImage.resizeMode.cover}
|
||||
/>
|
||||
<Text style={[styles.title, { color: currentTheme.colors.mediumEmphasis }]} numberOfLines={2}>
|
||||
{item.name}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useEffect, useState, useRef } from 'react';
|
||||
import { View, Text, StyleSheet, ScrollView, TouchableOpacity, ActivityIndicator, Dimensions, useWindowDimensions, useColorScheme, FlatList } from 'react-native';
|
||||
import { Image } from 'expo-image';
|
||||
import FastImage from '@d11/react-native-fast-image';
|
||||
import { MaterialIcons } from '@expo/vector-icons';
|
||||
import { LinearGradient } from 'expo-linear-gradient';
|
||||
import { FlashList, FlashListRef } from '@shopify/flash-list';
|
||||
|
|
@ -454,10 +454,10 @@ export const SeriesContent: React.FC<SeriesContentProps> = ({
|
|||
onPress={() => onSeasonChange(season)}
|
||||
>
|
||||
<View style={[styles.seasonPosterContainer, isTablet && styles.seasonPosterContainerTablet]}>
|
||||
<Image
|
||||
<FastImage
|
||||
source={{ uri: seasonPoster }}
|
||||
style={styles.seasonPoster}
|
||||
contentFit="cover"
|
||||
resizeMode={FastImage.resizeMode.cover}
|
||||
/>
|
||||
{selectedSeason === season && (
|
||||
<View style={[
|
||||
|
|
@ -559,10 +559,10 @@ export const SeriesContent: React.FC<SeriesContentProps> = ({
|
|||
styles.episodeImageContainer,
|
||||
isTablet && styles.episodeImageContainerTablet
|
||||
]}>
|
||||
<Image
|
||||
<FastImage
|
||||
source={{ uri: episodeImage }}
|
||||
style={styles.episodeImage}
|
||||
contentFit="cover"
|
||||
resizeMode={FastImage.resizeMode.cover}
|
||||
/>
|
||||
<View style={styles.episodeNumberBadge}>
|
||||
<Text style={styles.episodeNumberText}>{episodeString}</Text>
|
||||
|
|
@ -605,10 +605,10 @@ export const SeriesContent: React.FC<SeriesContentProps> = ({
|
|||
]}>
|
||||
{effectiveVote > 0 && (
|
||||
<View style={styles.ratingContainer}>
|
||||
<Image
|
||||
<FastImage
|
||||
source={{ uri: TMDB_LOGO }}
|
||||
style={styles.tmdbLogo}
|
||||
contentFit="contain"
|
||||
resizeMode={FastImage.resizeMode.contain}
|
||||
/>
|
||||
<Text style={[styles.ratingText, { color: currentTheme.colors.textMuted }]}>
|
||||
{effectiveVote.toFixed(1)}
|
||||
|
|
@ -727,10 +727,10 @@ export const SeriesContent: React.FC<SeriesContentProps> = ({
|
|||
</View>
|
||||
|
||||
{/* Background Image */}
|
||||
<Image
|
||||
<FastImage
|
||||
source={{ uri: episodeImage }}
|
||||
style={styles.episodeBackgroundImage}
|
||||
contentFit="cover"
|
||||
resizeMode={FastImage.resizeMode.cover}
|
||||
/>
|
||||
|
||||
{/* Standard Gradient Overlay */}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import React, { useState, useRef, useEffect, useMemo, useCallback } from 'react';
|
||||
import { View, TouchableOpacity, TouchableWithoutFeedback, Dimensions, Animated, ActivityIndicator, Platform, NativeModules, StatusBar, Text, Image, StyleSheet, Modal, AppState } from 'react-native';
|
||||
import { View, TouchableOpacity, TouchableWithoutFeedback, Dimensions, Animated, ActivityIndicator, Platform, NativeModules, StatusBar, Text, StyleSheet, Modal, AppState } from 'react-native';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import Video, { VideoRef, SelectedTrack, SelectedTrackType, BufferingStrategyType, ViewType } from 'react-native-video';
|
||||
import FastImage from '@d11/react-native-fast-image';
|
||||
import { useNavigation, useRoute, RouteProp, useFocusEffect } from '@react-navigation/native';
|
||||
import { RootStackParamList } from '../../navigation/AppNavigator';
|
||||
import { PinchGestureHandler, PanGestureHandler, TapGestureHandler, State, PinchGestureHandlerGestureEvent, PanGestureHandlerGestureEvent, TapGestureHandlerGestureEvent } from 'react-native-gesture-handler';
|
||||
|
|
@ -586,22 +587,21 @@ const AndroidVideoPlayer: React.FC = () => {
|
|||
backdropImageOpacityAnim.setValue(0);
|
||||
|
||||
// Prefetch the image
|
||||
Image.prefetch(backdrop)
|
||||
.then(() => {
|
||||
// Image loaded successfully, fade it in smoothly
|
||||
setIsBackdropLoaded(true);
|
||||
Animated.timing(backdropImageOpacityAnim, {
|
||||
toValue: 1,
|
||||
duration: 400,
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
})
|
||||
.catch((error) => {
|
||||
// If prefetch fails, still show the image but without animation
|
||||
if (__DEV__) logger.warn('[AndroidVideoPlayer] Backdrop prefetch failed, showing anyway:', error);
|
||||
setIsBackdropLoaded(true);
|
||||
backdropImageOpacityAnim.setValue(1);
|
||||
});
|
||||
try {
|
||||
FastImage.preload([{ uri: backdrop }]);
|
||||
// Image prefetch initiated, fade it in smoothly
|
||||
setIsBackdropLoaded(true);
|
||||
Animated.timing(backdropImageOpacityAnim, {
|
||||
toValue: 1,
|
||||
duration: 400,
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
} catch (error) {
|
||||
// If prefetch fails, still show the image but without animation
|
||||
if (__DEV__) logger.warn('[AndroidVideoPlayer] Backdrop prefetch failed, showing anyway:', error);
|
||||
setIsBackdropLoaded(true);
|
||||
backdropImageOpacityAnim.setValue(1);
|
||||
}
|
||||
} else {
|
||||
// No backdrop provided, consider it "loaded"
|
||||
setIsBackdropLoaded(true);
|
||||
|
|
@ -612,7 +612,11 @@ const AndroidVideoPlayer: React.FC = () => {
|
|||
useEffect(() => {
|
||||
const logoUrl = (metadata && (metadata as any).logo) as string | undefined;
|
||||
if (logoUrl && typeof logoUrl === 'string') {
|
||||
Image.prefetch(logoUrl).catch(() => {});
|
||||
try {
|
||||
FastImage.preload([{ uri: logoUrl }]);
|
||||
} catch (error) {
|
||||
// Silently ignore logo prefetch errors
|
||||
}
|
||||
}
|
||||
}, [metadata]);
|
||||
|
||||
|
|
@ -3112,19 +3116,20 @@ const AndroidVideoPlayer: React.FC = () => {
|
|||
pointerEvents={isOpeningAnimationComplete ? 'none' : 'auto'}
|
||||
>
|
||||
{backdrop && (
|
||||
<Animated.Image
|
||||
source={{ uri: backdrop }}
|
||||
style={[
|
||||
<Animated.View style={[
|
||||
StyleSheet.absoluteFill,
|
||||
{
|
||||
width: screenDimensions.width,
|
||||
height: screenDimensions.height,
|
||||
opacity: backdropImageOpacityAnim
|
||||
}
|
||||
]}
|
||||
resizeMode="cover"
|
||||
blurRadius={0}
|
||||
/>
|
||||
]}>
|
||||
<FastImage
|
||||
source={{ uri: backdrop }}
|
||||
style={StyleSheet.absoluteFillObject}
|
||||
resizeMode={FastImage.resizeMode.cover}
|
||||
/>
|
||||
</Animated.View>
|
||||
)}
|
||||
<LinearGradient
|
||||
colors={[
|
||||
|
|
@ -3155,13 +3160,13 @@ const AndroidVideoPlayer: React.FC = () => {
|
|||
opacity: logoOpacityAnim,
|
||||
alignItems: 'center',
|
||||
}}>
|
||||
<Image
|
||||
<FastImage
|
||||
source={{ uri: metadata.logo }}
|
||||
style={{
|
||||
width: 300,
|
||||
height: 180,
|
||||
resizeMode: 'contain',
|
||||
}}
|
||||
resizeMode={FastImage.resizeMode.contain}
|
||||
/>
|
||||
</Animated.View>
|
||||
</>
|
||||
|
|
@ -3599,7 +3604,7 @@ const AndroidVideoPlayer: React.FC = () => {
|
|||
shadowRadius: 8,
|
||||
elevation: 5,
|
||||
}}>
|
||||
<Image
|
||||
<FastImage
|
||||
source={{ uri: `https://image.tmdb.org/t/p/w300${selectedCastMember.profile_path}` }}
|
||||
style={{
|
||||
width: Math.min(120, screenDimensions.width * 0.18),
|
||||
|
|
@ -3607,7 +3612,7 @@ const AndroidVideoPlayer: React.FC = () => {
|
|||
borderRadius: 12,
|
||||
backgroundColor: 'rgba(255,255,255,0.1)'
|
||||
}}
|
||||
resizeMode="cover"
|
||||
resizeMode={FastImage.resizeMode.cover}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import React, { useState, useRef, useEffect, useMemo, useCallback } from 'react';
|
||||
import { View, TouchableOpacity, Dimensions, Animated, ActivityIndicator, Platform, NativeModules, StatusBar, Text, Image, StyleSheet, Modal, AppState } from 'react-native';
|
||||
import { View, TouchableOpacity, Dimensions, Animated, ActivityIndicator, Platform, NativeModules, StatusBar, Text, StyleSheet, Modal, AppState } from 'react-native';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { useNavigation, useRoute, RouteProp, useFocusEffect } from '@react-navigation/native';
|
||||
import FastImage from '@d11/react-native-fast-image';
|
||||
import { RootStackParamList, RootStackNavigationProp } from '../../navigation/AppNavigator';
|
||||
import { PinchGestureHandler, PanGestureHandler, TapGestureHandler, State, PinchGestureHandlerGestureEvent, PanGestureHandlerGestureEvent, TapGestureHandlerGestureEvent } from 'react-native-gesture-handler';
|
||||
import RNImmersiveMode from 'react-native-immersive-mode';
|
||||
|
|
@ -317,22 +318,21 @@ const KSPlayerCore: React.FC = () => {
|
|||
backdropImageOpacityAnim.setValue(0);
|
||||
|
||||
// Prefetch the image
|
||||
Image.prefetch(backdrop)
|
||||
.then(() => {
|
||||
// Image loaded successfully, fade it in smoothly
|
||||
setIsBackdropLoaded(true);
|
||||
Animated.timing(backdropImageOpacityAnim, {
|
||||
toValue: 1,
|
||||
duration: 400,
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
})
|
||||
.catch((error) => {
|
||||
// If prefetch fails, still show the image but without animation
|
||||
if (__DEV__) logger.warn('[VideoPlayer] Backdrop prefetch failed, showing anyway:', error);
|
||||
setIsBackdropLoaded(true);
|
||||
backdropImageOpacityAnim.setValue(1);
|
||||
});
|
||||
try {
|
||||
FastImage.preload([{ uri: backdrop }]);
|
||||
// Image prefetch initiated, fade it in smoothly
|
||||
setIsBackdropLoaded(true);
|
||||
Animated.timing(backdropImageOpacityAnim, {
|
||||
toValue: 1,
|
||||
duration: 400,
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
} catch (error) {
|
||||
// If prefetch fails, still show the image but without animation
|
||||
if (__DEV__) logger.warn('[VideoPlayer] Backdrop prefetch failed, showing anyway:', error);
|
||||
setIsBackdropLoaded(true);
|
||||
backdropImageOpacityAnim.setValue(1);
|
||||
}
|
||||
} else {
|
||||
// No backdrop provided, consider it "loaded"
|
||||
setIsBackdropLoaded(true);
|
||||
|
|
@ -343,7 +343,11 @@ const KSPlayerCore: React.FC = () => {
|
|||
useEffect(() => {
|
||||
const logoUrl = (metadata && (metadata as any).logo) as string | undefined;
|
||||
if (logoUrl && typeof logoUrl === 'string') {
|
||||
Image.prefetch(logoUrl).catch(() => {});
|
||||
try {
|
||||
FastImage.preload([{ uri: logoUrl }]);
|
||||
} catch (error) {
|
||||
// Silently ignore logo prefetch errors
|
||||
}
|
||||
}
|
||||
}, [metadata]);
|
||||
// Resolve current episode description for series
|
||||
|
|
@ -2431,19 +2435,20 @@ const KSPlayerCore: React.FC = () => {
|
|||
pointerEvents={shouldHideOpeningOverlay ? 'none' : 'auto'}
|
||||
>
|
||||
{backdrop && (
|
||||
<Animated.Image
|
||||
source={{ uri: backdrop }}
|
||||
style={[
|
||||
<Animated.View style={[
|
||||
StyleSheet.absoluteFill,
|
||||
{
|
||||
width: screenDimensions.width,
|
||||
height: screenDimensions.height,
|
||||
opacity: backdropImageOpacityAnim
|
||||
}
|
||||
]}
|
||||
resizeMode="cover"
|
||||
blurRadius={0}
|
||||
/>
|
||||
]}>
|
||||
<FastImage
|
||||
source={{ uri: backdrop }}
|
||||
style={StyleSheet.absoluteFillObject}
|
||||
resizeMode={FastImage.resizeMode.cover}
|
||||
/>
|
||||
</Animated.View>
|
||||
)}
|
||||
<LinearGradient
|
||||
colors={[
|
||||
|
|
@ -2474,13 +2479,13 @@ const KSPlayerCore: React.FC = () => {
|
|||
opacity: logoOpacityAnim,
|
||||
alignItems: 'center',
|
||||
}}>
|
||||
<Image
|
||||
<FastImage
|
||||
source={{ uri: metadata.logo }}
|
||||
style={{
|
||||
width: 300,
|
||||
height: 180,
|
||||
resizeMode: 'contain',
|
||||
}}
|
||||
resizeMode={FastImage.resizeMode.contain}
|
||||
/>
|
||||
</Animated.View>
|
||||
</>
|
||||
|
|
@ -2817,7 +2822,7 @@ const KSPlayerCore: React.FC = () => {
|
|||
shadowRadius: 8,
|
||||
elevation: 5,
|
||||
}}>
|
||||
<Image
|
||||
<FastImage
|
||||
source={{ uri: `https://image.tmdb.org/t/p/w300${selectedCastMember.profile_path}` }}
|
||||
style={{
|
||||
width: Math.min(120, screenDimensions.width * 0.18),
|
||||
|
|
@ -2825,7 +2830,7 @@ const KSPlayerCore: React.FC = () => {
|
|||
borderRadius: 12,
|
||||
backgroundColor: 'rgba(255,255,255,0.1)'
|
||||
}}
|
||||
resizeMode="cover"
|
||||
resizeMode={FastImage.resizeMode.cover}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { useState, useEffect, useRef, useCallback } from 'react';
|
|||
import { logger } from '../utils/logger';
|
||||
import { TMDBService } from '../services/tmdbService';
|
||||
import { isTmdbUrl } from '../utils/logoUtils';
|
||||
import { Image } from 'expo-image';
|
||||
import FastImage from '@d11/react-native-fast-image';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
|
||||
// Cache for image availability checks
|
||||
|
|
@ -176,7 +176,7 @@ export const useMetadataAssets = (
|
|||
|
||||
if (logoUrl) {
|
||||
// Preload the image
|
||||
await Image.prefetch(logoUrl);
|
||||
FastImage.preload([{ uri: logoUrl }]);
|
||||
|
||||
setMetadata((prevMetadata: any) => ({ ...prevMetadata!, logo: logoUrl }));
|
||||
}
|
||||
|
|
@ -275,7 +275,7 @@ export const useMetadataAssets = (
|
|||
|
||||
// Preload the image
|
||||
if (finalBanner) {
|
||||
await Image.prefetch(finalBanner);
|
||||
FastImage.preload([{ uri: finalBanner }]);
|
||||
}
|
||||
}
|
||||
else if (details?.poster_path) {
|
||||
|
|
@ -284,7 +284,7 @@ export const useMetadataAssets = (
|
|||
|
||||
// Preload the image
|
||||
if (finalBanner) {
|
||||
await Image.prefetch(finalBanner);
|
||||
FastImage.preload([{ uri: finalBanner }]);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import CustomAlert from '../components/CustomAlert';
|
|||
import { useRoute, useNavigation, RouteProp, useFocusEffect } from '@react-navigation/native';
|
||||
import { MaterialIcons } from '@expo/vector-icons';
|
||||
import { useTheme } from '../contexts/ThemeContext';
|
||||
import { Image } from 'expo-image';
|
||||
import FastImage from '@d11/react-native-fast-image';
|
||||
import { BlurView as ExpoBlurView } from 'expo-blur';
|
||||
// Lazy-safe community blur import (avoid bundling issues on web)
|
||||
let AndroidBlurView: any = null;
|
||||
|
|
@ -642,11 +642,10 @@ const AIChatScreen: React.FC = () => {
|
|||
<SafeAreaView edges={['top','bottom']} style={[styles.container, { backgroundColor: currentTheme.colors.darkBackground }]}>
|
||||
{backdropUrl && (
|
||||
<View style={StyleSheet.absoluteFill} pointerEvents="none">
|
||||
<Image
|
||||
<FastImage
|
||||
source={{ uri: backdropUrl }}
|
||||
style={StyleSheet.absoluteFill}
|
||||
contentFit="cover"
|
||||
recyclingKey={backdropUrl || undefined}
|
||||
resizeMode={FastImage.resizeMode.cover}
|
||||
/>
|
||||
{Platform.OS === 'android' && AndroidBlurView
|
||||
? <AndroidBlurView blurAmount={12} blurRadius={6} style={StyleSheet.absoluteFill} />
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { View, Text, StyleSheet, TouchableOpacity, StatusBar, Platform, Animated, Easing, TextInput, ActivityIndicator } from 'react-native';
|
||||
import { Image } from 'expo-image';
|
||||
import FastImage from '@d11/react-native-fast-image';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { MaterialIcons } from '@expo/vector-icons';
|
||||
|
|
@ -110,10 +110,10 @@ const AccountManageScreen: React.FC = () => {
|
|||
<View style={styles.profileContainer}>
|
||||
{avatarUrl && !avatarError ? (
|
||||
<View style={[styles.avatar, { overflow: 'hidden' }]}>
|
||||
<Image
|
||||
<FastImage
|
||||
source={{ uri: avatarUrl }}
|
||||
style={styles.avatarImage}
|
||||
contentFit="cover"
|
||||
resizeMode={FastImage.resizeMode.cover}
|
||||
onError={() => setAvatarError(true)}
|
||||
/>
|
||||
</View>
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import {
|
|||
} from 'react-native';
|
||||
import { stremioService, Manifest } from '../services/stremioService';
|
||||
import { MaterialIcons } from '@expo/vector-icons';
|
||||
import { Image as ExpoImage } from 'expo-image';
|
||||
import FastImage from '@d11/react-native-fast-image';
|
||||
import { LinearGradient } from 'expo-linear-gradient';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { NavigationProp } from '@react-navigation/native';
|
||||
|
|
@ -972,10 +972,10 @@ const AddonsScreen = () => {
|
|||
|
||||
<View style={styles.addonHeader}>
|
||||
{logo ? (
|
||||
<ExpoImage
|
||||
<FastImage
|
||||
source={{ uri: logo }}
|
||||
style={styles.addonIcon}
|
||||
contentFit="contain"
|
||||
style={styles.addonIcon}
|
||||
resizeMode={FastImage.resizeMode.contain}
|
||||
/>
|
||||
) : (
|
||||
<View style={styles.addonIconPlaceholder}>
|
||||
|
|
@ -1048,10 +1048,10 @@ const AddonsScreen = () => {
|
|||
return (
|
||||
<View style={styles.communityAddonItem}>
|
||||
{logo ? (
|
||||
<ExpoImage
|
||||
<FastImage
|
||||
source={{ uri: logo }}
|
||||
style={styles.communityAddonIcon}
|
||||
contentFit="contain"
|
||||
resizeMode={FastImage.resizeMode.contain}
|
||||
/>
|
||||
) : (
|
||||
<View style={styles.communityAddonIconPlaceholder}>
|
||||
|
|
@ -1240,10 +1240,10 @@ const AddonsScreen = () => {
|
|||
<View style={styles.addonItem}>
|
||||
<View style={styles.addonHeader}>
|
||||
{promoAddon.logo ? (
|
||||
<ExpoImage
|
||||
<FastImage
|
||||
source={{ uri: promoAddon.logo }}
|
||||
style={styles.addonIcon}
|
||||
contentFit="contain"
|
||||
style={styles.addonIcon}
|
||||
resizeMode={FastImage.resizeMode.contain}
|
||||
/>
|
||||
) : (
|
||||
<View style={styles.addonIconPlaceholder}>
|
||||
|
|
@ -1318,10 +1318,10 @@ const AddonsScreen = () => {
|
|||
<View style={styles.addonItem}>
|
||||
<View style={styles.addonHeader}>
|
||||
{item.manifest.logo ? (
|
||||
<ExpoImage
|
||||
<FastImage
|
||||
source={{ uri: item.manifest.logo }}
|
||||
style={styles.addonIcon}
|
||||
contentFit="contain"
|
||||
style={styles.addonIcon}
|
||||
resizeMode={FastImage.resizeMode.contain}
|
||||
/>
|
||||
) : (
|
||||
<View style={styles.addonIconPlaceholder}>
|
||||
|
|
@ -1419,10 +1419,10 @@ const AddonsScreen = () => {
|
|||
<View style={styles.addonDetailHeader}>
|
||||
{/* @ts-ignore */}
|
||||
{addonDetails.logo ? (
|
||||
<ExpoImage
|
||||
<FastImage
|
||||
source={{ uri: addonDetails.logo }}
|
||||
style={styles.addonLogo}
|
||||
contentFit="contain"
|
||||
resizeMode={FastImage.resizeMode.contain}
|
||||
/>
|
||||
) : (
|
||||
<View style={styles.addonLogoPlaceholder}>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import {
|
|||
import { InteractionManager } from 'react-native';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { NavigationProp } from '@react-navigation/native';
|
||||
import { Image } from 'expo-image';
|
||||
import FastImage from '@d11/react-native-fast-image';
|
||||
import { MaterialIcons } from '@expo/vector-icons';
|
||||
import { LinearGradient } from 'expo-linear-gradient';
|
||||
import { useTheme } from '../contexts/ThemeContext';
|
||||
|
|
@ -138,11 +138,10 @@ const CalendarScreen = () => {
|
|||
onPress={() => handleSeriesPress(item.seriesId, item)}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<Image
|
||||
source={{ uri: imageUrl }}
|
||||
<FastImage
|
||||
source={{ uri: imageUrl || '' }}
|
||||
style={styles.poster}
|
||||
contentFit="cover"
|
||||
transition={300}
|
||||
resizeMode={FastImage.resizeMode.cover}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import {
|
|||
FlatList,
|
||||
} from 'react-native';
|
||||
import { MaterialIcons } from '@expo/vector-icons';
|
||||
import { Image } from 'expo-image';
|
||||
import FastImage from '@d11/react-native-fast-image';
|
||||
import Animated, {
|
||||
FadeIn,
|
||||
FadeOut,
|
||||
|
|
@ -379,12 +379,12 @@ const CastMoviesScreen: React.FC = () => {
|
|||
backgroundColor: 'rgba(255, 255, 255, 0.05)',
|
||||
}}>
|
||||
{item.poster_path ? (
|
||||
<Image
|
||||
<FastImage
|
||||
source={{
|
||||
uri: `https://image.tmdb.org/t/p/w500${item.poster_path}`,
|
||||
}}
|
||||
style={{ width: '100%', height: '100%' }}
|
||||
contentFit="cover"
|
||||
resizeMode={FastImage.resizeMode.cover}
|
||||
/>
|
||||
) : (
|
||||
<View style={{
|
||||
|
|
@ -589,12 +589,12 @@ const CastMoviesScreen: React.FC = () => {
|
|||
backgroundColor: 'rgba(255, 255, 255, 0.08)',
|
||||
}}>
|
||||
{castMember?.profile_path ? (
|
||||
<Image
|
||||
<FastImage
|
||||
source={{
|
||||
uri: `https://image.tmdb.org/t/p/w185${castMember.profile_path}`,
|
||||
}}
|
||||
style={{ width: '100%', height: '100%' }}
|
||||
contentFit="cover"
|
||||
resizeMode={FastImage.resizeMode.cover}
|
||||
/>
|
||||
) : (
|
||||
<View style={{
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import { StackNavigationProp } from '@react-navigation/stack';
|
|||
import { RootStackParamList } from '../navigation/AppNavigator';
|
||||
import { Meta, stremioService } from '../services/stremioService';
|
||||
import { useTheme } from '../contexts/ThemeContext';
|
||||
import { Image } from 'expo-image';
|
||||
import FastImage from '@d11/react-native-fast-image';
|
||||
import { BlurView } from 'expo-blur';
|
||||
import { MaterialIcons } from '@expo/vector-icons';
|
||||
import { logger } from '../utils/logger';
|
||||
|
|
@ -620,15 +620,10 @@ const CatalogScreen: React.FC<CatalogScreenProps> = ({ route, navigation }) => {
|
|||
onPress={() => navigation.navigate('Metadata', { id: item.id, type: item.type, addonId })}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<Image
|
||||
<FastImage
|
||||
source={{ uri: optimizePosterUrl(item.poster) }}
|
||||
style={styles.poster}
|
||||
contentFit="cover"
|
||||
cachePolicy={Platform.OS === 'android' ? 'memory-disk' : 'memory-disk'}
|
||||
transition={100}
|
||||
allowDownscaling
|
||||
priority="normal"
|
||||
recyclingKey={`${item.id}-${item.type}`}
|
||||
resizeMode={FastImage.resizeMode.cover}
|
||||
/>
|
||||
|
||||
{type === 'movie' && nowPlayingMovies.has(item.id) && (
|
||||
|
|
|
|||
|
|
@ -463,7 +463,7 @@ const HomeScreen = () => {
|
|||
}));
|
||||
|
||||
// Preload all images at once - FastImage handles batching internally
|
||||
await FastImage.preload(sources);
|
||||
FastImage.preload(sources);
|
||||
} catch (error) {
|
||||
// Silently handle preload errors
|
||||
if (__DEV__) console.warn('Image preload error:', error);
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import { FlashList } from '@shopify/flash-list';
|
|||
import { useNavigation } from '@react-navigation/native';
|
||||
import { NavigationProp } from '@react-navigation/native';
|
||||
import { MaterialIcons } from '@expo/vector-icons';
|
||||
import { Image } from 'expo-image';
|
||||
import FastImage from '@d11/react-native-fast-image';
|
||||
import Animated, { FadeIn, FadeOut } from 'react-native-reanimated';
|
||||
import { LinearGradient } from 'expo-linear-gradient';
|
||||
import { catalogService } from '../services/catalogService';
|
||||
|
|
@ -119,13 +119,10 @@ const TraktItem = React.memo(({ item, width, navigation, currentTheme }: { item:
|
|||
<View>
|
||||
<View style={[styles.posterContainer, { shadowColor: currentTheme.colors.black }]}>
|
||||
{posterUrl ? (
|
||||
<Image
|
||||
<FastImage
|
||||
source={{ uri: posterUrl }}
|
||||
style={styles.poster}
|
||||
contentFit="cover"
|
||||
cachePolicy="disk"
|
||||
transition={0}
|
||||
allowDownscaling
|
||||
resizeMode={FastImage.resizeMode.cover}
|
||||
/>
|
||||
) : (
|
||||
<View style={[styles.poster, { backgroundColor: currentTheme.colors.elevation1, justifyContent: 'center', alignItems: 'center' }]}>
|
||||
|
|
@ -403,12 +400,10 @@ const LibraryScreen = () => {
|
|||
>
|
||||
<View>
|
||||
<View style={[styles.posterContainer, { shadowColor: currentTheme.colors.black }]}>
|
||||
<Image
|
||||
<FastImage
|
||||
source={{ uri: item.poster || 'https://via.placeholder.com/300x450' }}
|
||||
style={styles.poster}
|
||||
contentFit="cover"
|
||||
cachePolicy="memory"
|
||||
transition={300}
|
||||
resizeMode={FastImage.resizeMode.cover}
|
||||
/>
|
||||
{item.watched && (
|
||||
<View style={styles.watchedIndicator}>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import {
|
|||
Animated,
|
||||
} from 'react-native';
|
||||
import CustomAlert from '../components/CustomAlert';
|
||||
import { Image } from 'expo-image';
|
||||
import FastImage from '@d11/react-native-fast-image';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
|
|
@ -1598,10 +1598,10 @@ const PluginsScreen: React.FC = () => {
|
|||
<View key={scraper.id} style={styles.scraperCard}>
|
||||
<View style={styles.scraperCardHeader}>
|
||||
{scraper.logo ? (
|
||||
<Image
|
||||
<FastImage
|
||||
source={{ uri: scraper.logo }}
|
||||
style={styles.scraperLogo}
|
||||
contentFit="contain"
|
||||
resizeMode={FastImage.resizeMode.contain}
|
||||
/>
|
||||
) : (
|
||||
<View style={styles.scraperLogo} />
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import { useNavigation } from '@react-navigation/native';
|
|||
import { NavigationProp } from '@react-navigation/native';
|
||||
import { MaterialIcons } from '@expo/vector-icons';
|
||||
import { catalogService, StreamingContent, GroupedSearchResults, AddonSearchResults } from '../services/catalogService';
|
||||
import { Image } from 'expo-image';
|
||||
import FastImage from '@d11/react-native-fast-image';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { DropUpMenu } from '../components/home/DropUpMenu';
|
||||
import { DeviceEventEmitter, Share } from 'react-native';
|
||||
|
|
@ -571,11 +571,10 @@ const SearchScreen = () => {
|
|||
backgroundColor: currentTheme.colors.darkBackground,
|
||||
borderColor: 'rgba(255,255,255,0.05)'
|
||||
}]}>
|
||||
<Image
|
||||
<FastImage
|
||||
source={{ uri: item.poster || PLACEHOLDER_POSTER }}
|
||||
style={styles.horizontalItemPoster}
|
||||
contentFit="cover"
|
||||
transition={300}
|
||||
resizeMode={FastImage.resizeMode.cover}
|
||||
/>
|
||||
{/* Bookmark and watched icons top right, bookmark to the left of watched */}
|
||||
{inLibrary && (
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import {
|
|||
StatusBar,
|
||||
Platform,
|
||||
Dimensions,
|
||||
Image,
|
||||
Button,
|
||||
Linking,
|
||||
Clipboard
|
||||
|
|
@ -18,6 +17,7 @@ import {
|
|||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { NavigationProp } from '@react-navigation/native';
|
||||
import FastImage from '@d11/react-native-fast-image';
|
||||
import { MaterialIcons } from '@expo/vector-icons';
|
||||
import { Picker } from '@react-native-picker/picker';
|
||||
import { useSettings, DEFAULT_SETTINGS } from '../hooks/useSettings';
|
||||
|
|
@ -805,10 +805,10 @@ const SettingsScreen: React.FC = () => {
|
|||
activeOpacity={0.7}
|
||||
>
|
||||
<View style={styles.discordButtonContent}>
|
||||
<Image
|
||||
<FastImage
|
||||
source={{ uri: 'https://pngimg.com/uploads/discord/discord_PNG3.png' }}
|
||||
style={styles.discordLogo}
|
||||
resizeMode="contain"
|
||||
resizeMode={FastImage.resizeMode.contain}
|
||||
/>
|
||||
<Text style={[styles.discordButtonText, { color: currentTheme.colors.highEmphasis }]}>
|
||||
Join Discord
|
||||
|
|
@ -821,10 +821,10 @@ const SettingsScreen: React.FC = () => {
|
|||
onPress={() => Linking.openURL('https://ko-fi.com/tapframe')}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<Image
|
||||
<FastImage
|
||||
source={require('../../assets/support_me_on_kofi_red.png')}
|
||||
style={styles.kofiImage}
|
||||
resizeMode="contain"
|
||||
resizeMode={FastImage.resizeMode.contain}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
|
@ -892,10 +892,10 @@ const SettingsScreen: React.FC = () => {
|
|||
activeOpacity={0.7}
|
||||
>
|
||||
<View style={styles.discordButtonContent}>
|
||||
<Image
|
||||
<FastImage
|
||||
source={{ uri: 'https://pngimg.com/uploads/discord/discord_PNG3.png' }}
|
||||
style={styles.discordLogo}
|
||||
resizeMode="contain"
|
||||
resizeMode={FastImage.resizeMode.contain}
|
||||
/>
|
||||
<Text style={[styles.discordButtonText, { color: currentTheme.colors.highEmphasis }]}>
|
||||
Join Discord
|
||||
|
|
@ -908,10 +908,10 @@ const SettingsScreen: React.FC = () => {
|
|||
onPress={() => Linking.openURL('https://ko-fi.com/tapframe')}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<Image
|
||||
<FastImage
|
||||
source={require('../../assets/support_me_on_kofi_red.png')}
|
||||
style={styles.kofiImage}
|
||||
resizeMode="contain"
|
||||
resizeMode={FastImage.resizeMode.contain}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import {
|
|||
Platform,
|
||||
StatusBar,
|
||||
} from 'react-native';
|
||||
import { Image } from 'expo-image';
|
||||
import FastImage from '@d11/react-native-fast-image';
|
||||
import { BlurView } from 'expo-blur';
|
||||
import { useTheme } from '../contexts/ThemeContext';
|
||||
import { TMDBService, TMDBShow as Show, TMDBSeason, TMDBEpisode } from '../services/tmdbService';
|
||||
|
|
@ -175,11 +175,10 @@ const RatingSourceToggle = memo(({ ratingSource, setRatingSource, theme }: {
|
|||
|
||||
const ShowInfo = memo(({ show, theme }: { show: Show | null, theme: any }) => (
|
||||
<View style={styles.showInfo}>
|
||||
<Image
|
||||
<FastImage
|
||||
source={{ uri: `https://image.tmdb.org/t/p/w500${show?.poster_path}` }}
|
||||
style={styles.poster}
|
||||
contentFit="cover"
|
||||
transition={200}
|
||||
resizeMode={FastImage.resizeMode.cover}
|
||||
/>
|
||||
<View style={styles.showDetails}>
|
||||
<Text style={[styles.showTitle, { color: theme.colors.white }]}>{show?.name}</Text>
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ import { RouteProp } from '@react-navigation/native';
|
|||
import { NavigationProp } from '@react-navigation/native';
|
||||
import { MaterialIcons } from '@expo/vector-icons';
|
||||
import { LinearGradient } from 'expo-linear-gradient';
|
||||
import { Image } from 'expo-image';
|
||||
import FastImage from '@d11/react-native-fast-image';
|
||||
import { RootStackParamList, RootStackNavigationProp } from '../navigation/AppNavigator';
|
||||
import { useMetadata } from '../hooks/useMetadata';
|
||||
import { useMetadataAssets } from '../hooks/useMetadataAssets';
|
||||
|
|
@ -116,10 +116,10 @@ const AnimatedImage = memo(({
|
|||
|
||||
return (
|
||||
<Animated.View style={[style, animatedStyle]}>
|
||||
<Image
|
||||
<FastImage
|
||||
source={source}
|
||||
style={StyleSheet.absoluteFillObject}
|
||||
contentFit={contentFit}
|
||||
resizeMode={FastImage.resizeMode.cover}
|
||||
onLoad={onLoad}
|
||||
/>
|
||||
</Animated.View>
|
||||
|
|
@ -336,10 +336,10 @@ const StreamCard = memo(({ stream, onPress, index, isLoading, statusMessage, the
|
|||
{/* Scraper Logo */}
|
||||
{showLogos && scraperLogo && (
|
||||
<View style={styles.scraperLogoContainer}>
|
||||
<Image
|
||||
<FastImage
|
||||
source={{ uri: scraperLogo }}
|
||||
style={styles.scraperLogo}
|
||||
resizeMode="contain"
|
||||
resizeMode={FastImage.resizeMode.contain}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
|
|
@ -1968,10 +1968,10 @@ export const StreamsScreen = () => {
|
|||
<View style={[styles.movieTitleContainer]}>
|
||||
<View style={styles.movieTitleContent}>
|
||||
{metadata.logo && !movieLogoError ? (
|
||||
<Image
|
||||
<FastImage
|
||||
source={{ uri: metadata.logo }}
|
||||
style={styles.movieLogo}
|
||||
contentFit="contain"
|
||||
resizeMode={FastImage.resizeMode.contain}
|
||||
onError={() => setMovieLogoError(true)}
|
||||
/>
|
||||
) : (
|
||||
|
|
@ -2021,7 +2021,7 @@ export const StreamsScreen = () => {
|
|||
</Text>
|
||||
{effectiveEpisodeVote > 0 && (
|
||||
<View style={styles.streamsHeroRating}>
|
||||
<Image source={{ uri: TMDB_LOGO }} style={styles.tmdbLogo} contentFit="contain" />
|
||||
<FastImage source={{ uri: TMDB_LOGO }} style={styles.tmdbLogo} resizeMode={FastImage.resizeMode.contain} />
|
||||
<Text style={styles.streamsHeroRatingText}>
|
||||
{effectiveEpisodeVote.toFixed(1)}
|
||||
</Text>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import {
|
|||
Keyboard,
|
||||
Clipboard,
|
||||
Switch,
|
||||
Image,
|
||||
KeyboardAvoidingView,
|
||||
TouchableWithoutFeedback,
|
||||
Modal,
|
||||
|
|
@ -22,6 +21,7 @@ import {
|
|||
import { useNavigation } from '@react-navigation/native';
|
||||
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import FastImage from '@d11/react-native-fast-image';
|
||||
import { tmdbService } from '../services/tmdbService';
|
||||
import { useSettings } from '../hooks/useSettings';
|
||||
import { logger } from '../utils/logger';
|
||||
|
|
@ -387,17 +387,17 @@ const TMDBSettingsScreen = () => {
|
|||
|
||||
return (
|
||||
<View style={styles.bannerContainer}>
|
||||
<Image
|
||||
<FastImage
|
||||
source={{ uri: banner || undefined }}
|
||||
style={styles.bannerImage}
|
||||
resizeMode="cover"
|
||||
resizeMode={FastImage.resizeMode.cover}
|
||||
/>
|
||||
<View style={styles.bannerOverlay} />
|
||||
{logo && (
|
||||
<Image
|
||||
<FastImage
|
||||
source={{ uri: logo }}
|
||||
style={styles.logoOverBanner}
|
||||
resizeMode="contain"
|
||||
resizeMode={FastImage.resizeMode.contain}
|
||||
/>
|
||||
)}
|
||||
{!logo && (
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import {
|
|||
StyleSheet,
|
||||
TouchableOpacity,
|
||||
ActivityIndicator,
|
||||
Image,
|
||||
SafeAreaView,
|
||||
ScrollView,
|
||||
StatusBar,
|
||||
|
|
@ -16,6 +15,7 @@ import {
|
|||
import { useNavigation } from '@react-navigation/native';
|
||||
import { makeRedirectUri, useAuthRequest, ResponseType, Prompt, CodeChallengeMethod } from 'expo-auth-session';
|
||||
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
|
||||
import FastImage from '@d11/react-native-fast-image';
|
||||
import { traktService, TraktUser } from '../services/traktService';
|
||||
import { useSettings } from '../hooks/useSettings';
|
||||
import { logger } from '../utils/logger';
|
||||
|
|
@ -259,9 +259,10 @@ const TraktSettingsScreen: React.FC = () => {
|
|||
<View style={styles.profileContainer}>
|
||||
<View style={styles.profileHeader}>
|
||||
{userProfile.avatar ? (
|
||||
<Image
|
||||
<FastImage
|
||||
source={{ uri: userProfile.avatar }}
|
||||
style={styles.avatar}
|
||||
resizeMode={FastImage.resizeMode.cover}
|
||||
/>
|
||||
) : (
|
||||
<View style={[styles.avatarPlaceholder, { backgroundColor: currentTheme.colors.primary }]}>
|
||||
|
|
|
|||
Loading…
Reference in a new issue