From 99dc34cb65ec0b2c09b9e393b7dcbe37bebda08b Mon Sep 17 00:00:00 2001 From: tapframe Date: Tue, 12 Aug 2025 12:42:56 +0530 Subject: [PATCH] homescreenc changes --- nuvio-providers | 1 + package-lock.json | 347 +++--------------- package.json | 7 +- src/components/home/CatalogSection.tsx | 23 +- src/components/home/ContentItem.tsx | 6 +- .../home/ContinueWatchingSection.tsx | 5 +- src/components/home/FeaturedContent.tsx | 54 +-- src/components/home/ThisWeekSection.tsx | 5 +- src/navigation/AppNavigator.tsx | 274 ++++++++------ src/screens/HomeScreen.tsx | 75 ++-- src/utils/.logger.ts.swp | Bin 0 -> 12288 bytes 11 files changed, 253 insertions(+), 544 deletions(-) create mode 160000 nuvio-providers create mode 100644 src/utils/.logger.ts.swp diff --git a/nuvio-providers b/nuvio-providers new file mode 160000 index 0000000..96be1f5 --- /dev/null +++ b/nuvio-providers @@ -0,0 +1 @@ +Subproject commit 96be1f53604182cb53f027160db9fc969ed3bdcc diff --git a/package-lock.json b/package-lock.json index 8802c1f..6b53ff7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "@react-navigation/native-stack": "^7.3.10", "@react-navigation/stack": "^7.2.10", "@sentry/react-native": "^6.15.1", + "@shopify/flash-list": "^1.8.3", "@types/lodash": "^4.17.16", "@types/react-native-video": "^5.0.20", "axios": "^1.10.0", @@ -40,7 +41,7 @@ "react-native-gesture-handler": "~2.20.2", "react-native-immersive-mode": "^2.0.2", "react-native-paper": "^5.13.1", - "react-native-reanimated": "~3.6.0", + "react-native-reanimated": "3.6.0", "react-native-safe-area-context": "4.12.0", "react-native-screens": "~4.4.0", "react-native-svg": "^15.11.2", @@ -3046,128 +3047,6 @@ "node": ">=10" } }, - "node_modules/@sentry/cli/node_modules/@sentry/cli-linux-arm": { - "version": "2.50.2", - "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm/-/cli-linux-arm-2.50.2.tgz", - "integrity": "sha512-jzFwg9AeeuFAFtoCcyaDEPG05TU02uOy1nAX09c1g7FtsyQlPcbhI94JQGmnPzdRjjDmORtwIUiVZQrVTkDM7w==", - "cpu": [ - "arm" - ], - "license": "BSD-3-Clause", - "optional": true, - "os": [ - "linux", - "freebsd", - "android" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@sentry/cli/node_modules/@sentry/cli-linux-arm64": { - "version": "2.50.2", - "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.50.2.tgz", - "integrity": "sha512-03Cj215M3IdoHAwevCxm5oOm9WICFpuLR05DQnODFCeIUsGvE1pZsc+Gm0Ky/ZArq2PlShBJTpbHvXbCUka+0w==", - "cpu": [ - "arm64" - ], - "license": "BSD-3-Clause", - "optional": true, - "os": [ - "linux", - "freebsd", - "android" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@sentry/cli/node_modules/@sentry/cli-linux-i686": { - "version": "2.50.2", - "resolved": "https://registry.npmjs.org/@sentry/cli-linux-i686/-/cli-linux-i686-2.50.2.tgz", - "integrity": "sha512-J+POvB34uVyHbIYF++Bc/OCLw+gqKW0H/y/mY7rRZCiocgpk266M4NtsOBl6bEaurMx1D+BCIEjr4nc01I/rqA==", - "cpu": [ - "x86", - "ia32" - ], - "license": "BSD-3-Clause", - "optional": true, - "os": [ - "linux", - "freebsd", - "android" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@sentry/cli/node_modules/@sentry/cli-linux-x64": { - "version": "2.50.2", - "resolved": "https://registry.npmjs.org/@sentry/cli-linux-x64/-/cli-linux-x64-2.50.2.tgz", - "integrity": "sha512-81yQVRLj8rnuHoYcrM7QbOw8ubA3weiMdPtTxTim1s6WExmPgnPTKxLCr9xzxGJxFdYo3xIOhtf5JFpUX/3j4A==", - "cpu": [ - "x64" - ], - "license": "BSD-3-Clause", - "optional": true, - "os": [ - "linux", - "freebsd", - "android" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@sentry/cli/node_modules/@sentry/cli-win32-arm64": { - "version": "2.50.2", - "resolved": "https://registry.npmjs.org/@sentry/cli-win32-arm64/-/cli-win32-arm64-2.50.2.tgz", - "integrity": "sha512-QjentLGvpibgiZlmlV9ifZyxV73lnGH6pFZWU5wLeRiaYKxWtNrrHpVs+HiWlRhkwQ0mG1/S40PGNgJ20DJ3gA==", - "cpu": [ - "arm64" - ], - "license": "BSD-3-Clause", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@sentry/cli/node_modules/@sentry/cli-win32-i686": { - "version": "2.50.2", - "resolved": "https://registry.npmjs.org/@sentry/cli-win32-i686/-/cli-win32-i686-2.50.2.tgz", - "integrity": "sha512-UkBIIzkQkQ1UkjQX8kHm/+e7IxnEhK6CdgSjFyNlxkwALjDWHJjMztevqAPz3kv4LdM6q1MxpQ/mOqXICNhEGg==", - "cpu": [ - "x86", - "ia32" - ], - "license": "BSD-3-Clause", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@sentry/cli/node_modules/@sentry/cli-win32-x64": { - "version": "2.50.2", - "resolved": "https://registry.npmjs.org/@sentry/cli-win32-x64/-/cli-win32-x64-2.50.2.tgz", - "integrity": "sha512-tE27pu1sRRub1Jpmemykv3QHddBcyUk39Fsvv+n4NDpQyMgsyVPcboxBZyby44F0jkpI/q3bUH2tfCB1TYDNLg==", - "cpu": [ - "x64" - ], - "license": "BSD-3-Clause", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, "node_modules/@sentry/core": { "version": "8.55.0", "license": "MIT", @@ -3236,6 +3115,21 @@ "node": ">=14.18" } }, + "node_modules/@shopify/flash-list": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@shopify/flash-list/-/flash-list-1.8.3.tgz", + "integrity": "sha512-vXuj6JyuMjONVOXjEhWFeaONPuWN/53Cl2LeyeM8TZ0JzUcNU+BE6iyga1/yyJeDf0K7YPgAE/PcUX2+DM1LiA==", + "license": "MIT", + "dependencies": { + "recyclerlistview": "4.2.3", + "tslib": "2.8.1" + }, + "peerDependencies": { + "@babel/runtime": "*", + "react": "*", + "react-native": "*" + } + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "license": "MIT" @@ -7148,186 +7042,6 @@ "url": "https://opencollective.com/parcel" } }, - "node_modules/lightningcss/node_modules/lightningcss-darwin-arm64": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.27.0.tgz", - "integrity": "sha512-Gl/lqIXY+d+ySmMbgDf0pgaWSqrWYxVHoc88q+Vhf2YNzZ8DwoRzGt5NZDVqqIW5ScpSnmmjcgXP87Dn2ylSSQ==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss/node_modules/lightningcss-freebsd-x64": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.27.0.tgz", - "integrity": "sha512-n1sEf85fePoU2aDN2PzYjoI8gbBqnmLGEhKq7q0DKLj0UTVmOTwDC7PtLcy/zFxzASTSBlVQYJUhwIStQMIpRA==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss/node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.27.0.tgz", - "integrity": "sha512-MUMRmtdRkOkd5z3h986HOuNBD1c2lq2BSQA1Jg88d9I7bmPGx08bwGcnB75dvr17CwxjxD6XPi3Qh8ArmKFqCA==", - "cpu": [ - "arm" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss/node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.27.0.tgz", - "integrity": "sha512-cPsxo1QEWq2sfKkSq2Bq5feQDHdUEwgtA9KaB27J5AX22+l4l0ptgjMZZtYtUnteBofjee+0oW1wQ1guv04a7A==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss/node_modules/lightningcss-linux-arm64-musl": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.27.0.tgz", - "integrity": "sha512-rCGBm2ax7kQ9pBSeITfCW9XSVF69VX+fm5DIpvDZQl4NnQoMQyRwhZQm9pd59m8leZ1IesRqWk2v/DntMo26lg==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss/node_modules/lightningcss-linux-x64-gnu": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.27.0.tgz", - "integrity": "sha512-Dk/jovSI7qqhJDiUibvaikNKI2x6kWPN79AQiD/E/KeQWMjdGe9kw51RAgoWFDi0coP4jinaH14Nrt/J8z3U4A==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss/node_modules/lightningcss-linux-x64-musl": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.27.0.tgz", - "integrity": "sha512-QKjTxXm8A9s6v9Tg3Fk0gscCQA1t/HMoF7Woy1u68wCk5kS4fR+q3vXa1p3++REW784cRAtkYKrPy6JKibrEZA==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss/node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.27.0.tgz", - "integrity": "sha512-/wXegPS1hnhkeG4OXQKEMQeJd48RDC3qdh+OA8pCuOPCyvnm/yEayrJdJVqzBsqpy1aJklRCVxscpFur80o6iQ==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss/node_modules/lightningcss-win32-x64-msvc": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.27.0.tgz", - "integrity": "sha512-/OJLj94Zm/waZShL8nB5jsNj3CfNATLCTyFxZyouilfTmSoLDX7VlVAmhPHoZWVFp4vdmoiEbPEYC8HID3m6yw==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, "node_modules/lines-and-columns": { "version": "1.2.4", "license": "MIT" @@ -9149,9 +8863,9 @@ "license": "MIT" }, "node_modules/react-native-reanimated": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.6.3.tgz", - "integrity": "sha512-2KkkPozoIvDbJcHuf8qeyoLROXQxizSi+2CTCkuNVkVZOxxY4B0Omvgq61aOQhSZUh/649x1YHoAaTyGMGDJUw==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.6.0.tgz", + "integrity": "sha512-eDdhZTRYofrIqFB/Z5xLTWxcB7wDj4ifrNm+gZ2xHSZPjAQ747ukDdH9rglPyPmi+GcmDH7Wff411Xsw5fm45Q==", "license": "MIT", "dependencies": { "@babel/plugin-transform-object-assign": "^7.16.7", @@ -9521,6 +9235,21 @@ "node": ">= 4" } }, + "node_modules/recyclerlistview": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/recyclerlistview/-/recyclerlistview-4.2.3.tgz", + "integrity": "sha512-STR/wj/FyT8EMsBzzhZ1l2goYirMkIgfV3gYEPxI3Kf3lOnu6f7Dryhyw7/IkQrgX5xtTcDrZMqytvteH9rL3g==", + "license": "Apache-2.0", + "dependencies": { + "lodash.debounce": "4.0.8", + "prop-types": "15.8.1", + "ts-object-utils": "0.0.5" + }, + "peerDependencies": { + "react": ">= 15.2.1", + "react-native": ">= 0.30.0" + } + }, "node_modules/regenerate": { "version": "1.4.2", "license": "MIT" @@ -10834,6 +10563,12 @@ "version": "0.1.13", "license": "Apache-2.0" }, + "node_modules/ts-object-utils": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/ts-object-utils/-/ts-object-utils-0.0.5.tgz", + "integrity": "sha512-iV0GvHqOmilbIKJsfyfJY9/dNHCs969z3so90dQWsO1eMMozvTpnB1MEaUbb3FYtZTGjv5sIy/xmslEz0Rg2TA==", + "license": "ISC" + }, "node_modules/tslib": { "version": "2.8.1", "license": "0BSD" diff --git a/package.json b/package.json index a83f5fc..c6d197b 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "@react-navigation/native-stack": "^7.3.10", "@react-navigation/stack": "^7.2.10", "@sentry/react-native": "^6.15.1", + "@shopify/flash-list": "^1.8.3", "@types/lodash": "^4.17.16", "@types/react-native-video": "^5.0.20", "axios": "^1.10.0", @@ -31,25 +32,21 @@ "date-fns": "^4.1.0", "eventemitter3": "^5.0.1", "expo": "~52.0.43", - "expo-blur": "^14.0.3", "expo-haptics": "~14.0.1", "expo-image": "~2.0.7", "expo-intent-launcher": "~12.0.2", "expo-linear-gradient": "~14.0.2", - "expo-random": "^14.0.1", - "expo-status-bar": "~2.0.1", "expo-system-ui": "^4.0.9", - "lodash": "^4.17.21", "react": "18.3.1", "react-native": "npm:react-native-tvos@latest", "react-native-gesture-handler": "~2.20.2", "react-native-immersive-mode": "^2.0.2", "react-native-paper": "^5.13.1", - "react-native-reanimated": "@latest", + "react-native-reanimated": "3.6.0", "react-native-safe-area-context": "4.12.0", "react-native-screens": "~4.4.0", "react-native-svg": "^15.11.2", diff --git a/src/components/home/CatalogSection.tsx b/src/components/home/CatalogSection.tsx index c5a71f1..424ba73 100644 --- a/src/components/home/CatalogSection.tsx +++ b/src/components/home/CatalogSection.tsx @@ -1,5 +1,6 @@ import React from 'react'; -import { View, Text, StyleSheet, TouchableOpacity, FlatList, Platform, Dimensions } from 'react-native'; +import { View, Text, StyleSheet, TouchableOpacity, Platform, Dimensions } from 'react-native'; +import { FlashList } from '@shopify/flash-list'; import { NavigationProp, useNavigation } from '@react-navigation/native'; import { MaterialIcons } from '@expo/vector-icons'; import { LinearGradient } from 'expo-linear-gradient'; @@ -12,6 +13,7 @@ import { RootStackParamList } from '../../navigation/AppNavigator'; interface CatalogSectionProps { catalog: CatalogContent; onPosterPress?: (content: StreamingContent) => void; + onPosterFocus?: (content: StreamingContent) => void; } const { width } = Dimensions.get('window'); @@ -54,7 +56,7 @@ const calculatePosterLayout = (screenWidth: number) => { const posterLayout = calculatePosterLayout(width); const POSTER_WIDTH = posterLayout.posterWidth; -const CatalogSection = ({ catalog, onPosterPress }: CatalogSectionProps) => { +const CatalogSection = ({ catalog, onPosterPress, onPosterFocus }: CatalogSectionProps) => { const navigation = useNavigation>(); const { currentTheme } = useTheme(); @@ -75,6 +77,7 @@ const CatalogSection = ({ catalog, onPosterPress }: CatalogSectionProps) => { ); @@ -105,7 +108,7 @@ const CatalogSection = ({ catalog, onPosterPress }: CatalogSectionProps) => { - `${item.id}-${item.type}`} @@ -116,19 +119,7 @@ const CatalogSection = ({ catalog, onPosterPress }: CatalogSectionProps) => { decelerationRate="fast" snapToAlignment="start" ItemSeparatorComponent={() => } - initialNumToRender={3} - maxToRenderPerBatch={2} - windowSize={3} - removeClippedSubviews={Platform.OS === 'android'} - updateCellsBatchingPeriod={50} - getItemLayout={(data, index) => ({ - length: POSTER_WIDTH + 8, - offset: (POSTER_WIDTH + 8) * index, - index, - })} - maintainVisibleContentPosition={{ - minIndexForVisible: 0 - }} + estimatedItemSize={POSTER_WIDTH + 8} onEndReachedThreshold={1} // TV-specific focus navigation properties {...(Platform.isTV && { diff --git a/src/components/home/ContentItem.tsx b/src/components/home/ContentItem.tsx index d81edf2..1ae1eb9 100644 --- a/src/components/home/ContentItem.tsx +++ b/src/components/home/ContentItem.tsx @@ -9,6 +9,7 @@ import { DropUpMenu } from './DropUpMenu'; interface ContentItemProps { item: StreamingContent; onPress: (id: string, type: string) => void; + onFocusItem?: (item: StreamingContent) => void; } const { width } = Dimensions.get('window'); @@ -51,7 +52,7 @@ const calculatePosterLayout = (screenWidth: number) => { const posterLayout = calculatePosterLayout(width); const POSTER_WIDTH = posterLayout.posterWidth; -const ContentItem = React.memo(({ item, onPress }: ContentItemProps) => { +const ContentItem = React.memo(({ item, onPress, onFocusItem }: ContentItemProps) => { const [menuVisible, setMenuVisible] = useState(false); const [isWatched, setIsWatched] = useState(false); const [isFocused, setIsFocused] = useState(false); @@ -102,6 +103,9 @@ const ContentItem = React.memo(({ item, onPress }: ContentItemProps) => { friction: 6, }).start(); } + if (onFocusItem) { + onFocusItem(item); + } }, [scaleAnim]); const handleBlur = useCallback(() => { diff --git a/src/components/home/ContinueWatchingSection.tsx b/src/components/home/ContinueWatchingSection.tsx index 13a60ef..740f7ce 100644 --- a/src/components/home/ContinueWatchingSection.tsx +++ b/src/components/home/ContinueWatchingSection.tsx @@ -3,7 +3,6 @@ import { View, Text, StyleSheet, - FlatList, TouchableOpacity, Dimensions, AppState, @@ -13,6 +12,7 @@ import { Platform, Animated } from 'react-native'; +import { FlashList } from '@shopify/flash-list'; // Removed react-native-reanimated import import { useNavigation } from '@react-navigation/native'; import { NavigationProp } from '@react-navigation/native'; @@ -737,7 +737,7 @@ const ContinueWatchingSection = React.forwardRef((props, re - ( ((props, re decelerationRate="fast" snapToAlignment="start" ItemSeparatorComponent={() => } + estimatedItemSize={280 + 16} // TV-specific focus navigation properties {...(Platform.isTV && { directionalLockEnabled: true, diff --git a/src/components/home/FeaturedContent.tsx b/src/components/home/FeaturedContent.tsx index 36f9db4..0c6d896 100644 --- a/src/components/home/FeaturedContent.tsx +++ b/src/components/home/FeaturedContent.tsx @@ -252,29 +252,7 @@ const FeaturedContent = ({ featuredContent }: FeaturedContentProps) => { )} - {/* Action Buttons */} - - navigation.navigate('Metadata', { - id: featuredContent.id, - type: featuredContent.type - })} - hasTVPreferredFocus={Platform.isTV} - tvParallaxProperties={Platform.isTV ? { - enabled: true, - shiftDistanceX: 4.0, - shiftDistanceY: 4.0, - tiltAngle: 0.05, - magnification: 1.1, - } : undefined} - > - - Play - - - - + {/* Action buttons removed per design */} @@ -287,7 +265,7 @@ const FeaturedContent = ({ featuredContent }: FeaturedContentProps) => { const styles = StyleSheet.create({ featuredContainer: { width: '100%', - height: Platform.isTV ? height * 0.5 : height * 0.4, + height: Platform.isTV ? height * 0.65 : height * 0.5, marginTop: 0, marginBottom: Platform.isTV ? 16 : 8, position: 'relative', @@ -471,33 +449,7 @@ const styles = StyleSheet.create({ fontSize: Platform.isTV ? 18 : 16, fontWeight: '600', }, - actionButtonsContainer: { - flexDirection: 'row', - alignItems: 'center', - gap: Platform.isTV ? 20 : 16, - marginTop: Platform.isTV ? 24 : 16, - }, - playButton: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center', - backgroundColor: '#FFFFFF', - paddingHorizontal: Platform.isTV ? 48 : 32, - paddingVertical: Platform.isTV ? 16 : 12, - borderRadius: Platform.isTV ? 12 : 8, - shadowColor: '#000', - shadowOffset: { width: 0, height: 2 }, - shadowOpacity: 0.3, - shadowRadius: 4, - elevation: 4, - minWidth: Platform.isTV ? 200 : 140, - }, - playButtonText: { - color: '#000', - fontSize: Platform.isTV ? 18 : 16, - fontWeight: '700', - marginLeft: 8, - }, + // Action buttons removed }); diff --git a/src/components/home/ThisWeekSection.tsx b/src/components/home/ThisWeekSection.tsx index 741c359..0becdb8 100644 --- a/src/components/home/ThisWeekSection.tsx +++ b/src/components/home/ThisWeekSection.tsx @@ -3,11 +3,11 @@ import { View, Text, StyleSheet, - FlatList, TouchableOpacity, ActivityIndicator, Dimensions } from 'react-native'; +import { FlashList } from '@shopify/flash-list'; import { useNavigation } from '@react-navigation/native'; import { NavigationProp } from '@react-navigation/native'; import { Image } from 'expo-image'; @@ -191,7 +191,7 @@ export const ThisWeekSection = React.memo(() => { - item.id} renderItem={renderEpisodeItem} @@ -202,6 +202,7 @@ export const ThisWeekSection = React.memo(() => { decelerationRate="fast" snapToAlignment="start" ItemSeparatorComponent={() => } + estimatedItemSize={ITEM_WIDTH + 16} /> ); diff --git a/src/navigation/AppNavigator.tsx b/src/navigation/AppNavigator.tsx index 046f02c..61970b5 100644 --- a/src/navigation/AppNavigator.tsx +++ b/src/navigation/AppNavigator.tsx @@ -10,7 +10,6 @@ import { MaterialCommunityIcons } from '@expo/vector-icons'; import { LinearGradient } from 'expo-linear-gradient'; import { BlurView } from 'expo-blur'; import { colors } from '../styles/colors'; -import { NuvioHeader } from '../components/NuvioHeader'; import { Stream } from '../types/streams'; import { SafeAreaProvider } from 'react-native-safe-area-context'; import { useTheme } from '../contexts/ThemeContext'; @@ -372,11 +371,11 @@ const TabScreenWrapper: React.FC<{children: React.ReactNode}> = ({ children }) = position: 'relative', overflow: 'hidden' }}> - {/* Reserve consistent space for the header area on all screens */} + {/* Optional reserved space behind content if needed */} { const renderTabBar = (props: BottomTabBarProps) => { return ( - + {Platform.OS === 'ios' ? ( - ) : ( - - )} - - - {props.state.routes.map((route, index) => { - const { options } = props.descriptors[route.key]; - const label = - options.tabBarLabel !== undefined - ? options.tabBarLabel - : options.title !== undefined - ? options.title - : route.name; + > + + {props.state.routes.map((route, index) => { + const { options } = props.descriptors[route.key]; + const label = + options.tabBarLabel !== undefined + ? options.tabBarLabel + : options.title !== undefined + ? options.title + : route.name; - const isFocused = props.state.index === index; + const isFocused = props.state.index === index; - const onPress = () => { - const event = props.navigation.emit({ - type: 'tabPress', - target: route.key, - canPreventDefault: true, - }); + const onPress = () => { + const event = props.navigation.emit({ + type: 'tabPress', + target: route.key, + canPreventDefault: true, + }); - if (!isFocused && !event.defaultPrevented) { - props.navigation.navigate(route.name); - } - }; + if (!isFocused && !event.defaultPrevented) { + props.navigation.navigate(route.name); + } + }; - let iconName: IconNameType = 'home'; - switch (route.name) { - case 'Home': - iconName = 'home'; - break; - case 'Library': - iconName = 'play-box-multiple'; - break; - case 'Search': - iconName = 'feature-search'; - break; - case 'Settings': - iconName = 'cog'; - break; - } - - return ( - - - - {typeof label === 'string' ? label : ''} - - - ); - })} + + {typeof label === 'string' ? label : ''} + + + ); + })} + + + ) : ( + + + {props.state.routes.map((route, index) => { + const { options } = props.descriptors[route.key]; + const label = + options.tabBarLabel !== undefined + ? options.tabBarLabel + : options.title !== undefined + ? options.title + : route.name; + + const isFocused = props.state.index === index; + + const onPress = () => { + const event = props.navigation.emit({ + type: 'tabPress', + target: route.key, + canPreventDefault: true, + }); + + if (!isFocused && !event.defaultPrevented) { + props.navigation.navigate(route.name); + } + }; + + return ( + + + {typeof label === 'string' ? label : ''} + + + ); + })} + - + )} ); }; @@ -578,8 +609,7 @@ const MainTabs = () => { ], }, }), - header: () => (route.name === 'Home' ? : null), - headerShown: route.name === 'Home', + headerShown: false, tabBarShowLabel: false, tabBarStyle: { position: 'absolute', diff --git a/src/screens/HomeScreen.tsx b/src/screens/HomeScreen.tsx index 38188c7..541643c 100644 --- a/src/screens/HomeScreen.tsx +++ b/src/screens/HomeScreen.tsx @@ -3,7 +3,6 @@ import { View, Text, StyleSheet, - FlatList, TouchableOpacity, ActivityIndicator, SafeAreaView, @@ -18,6 +17,7 @@ import { Pressable, Alert } from 'react-native'; +import { FlashList } from '@shopify/flash-list'; import { useNavigation, useFocusEffect } from '@react-navigation/native'; import { NavigationProp } from '@react-navigation/native'; import { RootStackParamList } from '../navigation/AppNavigator'; @@ -241,6 +241,12 @@ const HomeScreen = () => { handleSaveToLibrary, refreshFeatured } = useFeaturedContent(); + const [selectedContent, setSelectedContent] = useState(null); + useEffect(() => { + if (!selectedContent && featuredContent) { + setSelectedContent(featuredContent); + } + }, [featuredContent, selectedContent]); // Progressive catalog loading function const loadCatalogsProgressively = useCallback(async () => { @@ -587,9 +593,7 @@ const HomeScreen = () => { } // Normal flow when addons are present - if (showHeroSection) { - data.push({ type: 'featured', key: 'featured' }); - } + // Hero section will be rendered locked at the top outside the list data.push({ type: 'thisWeek', key: 'thisWeek' }); data.push({ type: 'continueWatching', key: 'continueWatching' }); @@ -604,19 +608,12 @@ const HomeScreen = () => { }); return data; - }, [hasAddons, showHeroSection, catalogs]); + }, [hasAddons, catalogs]); const renderListItem = useCallback(({ item }: { item: HomeScreenListItem }) => { switch (item.type) { case 'featured': - return ( - - ); + return null; case 'thisWeek': return ; case 'continueWatching': @@ -624,7 +621,11 @@ const HomeScreen = () => { case 'catalog': return ( - + setSelectedContent(content)} + /> ); case 'placeholder': @@ -691,12 +692,10 @@ const HomeScreen = () => { ), [catalogsLoading, catalogs, loadedCatalogCount, totalCatalogsRef.current, navigation, currentTheme.colors]); - // Memoize the main content section - const [selectedContent, setSelectedContent] = useState(null); - + // Handle poster press from catalogs to navigate to Metadata const handlePosterPress = useCallback((content: StreamingContent) => { - setSelectedContent(content); - }, []); + navigation.navigate('Metadata', { id: content.id, type: content.type }); + }, [navigation]); const renderMainContent = useMemo(() => { if (isLoading) return null; @@ -708,31 +707,25 @@ const HomeScreen = () => { backgroundColor="transparent" translucent /> - + + + )} + item.key} - contentContainerStyle={[ - styles.scrollContent, - { paddingTop: 0 } - ]} + contentContainerStyle={StyleSheet.flatten([styles.scrollContent, { paddingTop: 0 }])} showsVerticalScrollIndicator={false} ListFooterComponent={ListFooterComponent} - initialNumToRender={5} - maxToRenderPerBatch={5} - windowSize={11} - removeClippedSubviews={Platform.OS === 'android'} + estimatedItemSize={280} onEndReachedThreshold={0.5} - updateCellsBatchingPeriod={50} - maintainVisibleContentPosition={{ - minIndexForVisible: 0, - autoscrollToTopThreshold: 10 - }} - getItemLayout={(data, index) => ({ - length: 280, - offset: index * 280, - index, - })} /> ); @@ -741,7 +734,11 @@ const HomeScreen = () => { currentTheme.colors, listData, renderListItem, - ListFooterComponent + ListFooterComponent, + showHeroSection, + selectedContent, + isSaved, + handleSaveToLibrary ]); return isLoading ? renderLoadingScreen : renderMainContent; diff --git a/src/utils/.logger.ts.swp b/src/utils/.logger.ts.swp new file mode 100644 index 0000000000000000000000000000000000000000..5424b575fba47069cff32bf8013176de7b34799f GIT binary patch literal 12288 zcmeI2ziSjh6vrp2ER2bYmCe&2+=1>UT8KF$O;QLN{23Gp$L!wRX5waNnVGvJh(G=S z{Rb=*K`ToItwbxa)z;3xz{bwtcQ1QzXM$|XgE#QGVQ=0%W?FU9<6A-v4nR<5gJ>3`#98RyPiwI+xnYdT)p-8&xk6NU@Q-SZ|75x=Hl zA1-ye+T8F~$#tD2p|f$=@j7*3YLZ0Rz`Nbq_!~CB2KHxQLYzB0J5%hLK6!$U9=Wx@ z?HtVp*Z><~18jf|umLu}2H3zz8}RvN>!^)_pve*J4Z6xy1P=WU}^8`zrzPoiSD}Kv4ziutrNa}*ggjm#P%PNj2Y IkD&$r0yrxJf&c&j literal 0 HcmV?d00001