homescreenc changes
This commit is contained in:
parent
11030c5601
commit
99dc34cb65
11 changed files with 253 additions and 544 deletions
1
nuvio-providers
Submodule
1
nuvio-providers
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 96be1f53604182cb53f027160db9fc969ed3bdcc
|
||||
347
package-lock.json
generated
347
package-lock.json
generated
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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<NavigationProp<RootStackParamList>>();
|
||||
const { currentTheme } = useTheme();
|
||||
|
||||
|
|
@ -75,6 +77,7 @@ const CatalogSection = ({ catalog, onPosterPress }: CatalogSectionProps) => {
|
|||
<ContentItem
|
||||
item={item}
|
||||
onPress={handleContentPress}
|
||||
onFocusItem={onPosterFocus}
|
||||
/>
|
||||
</Animated.View>
|
||||
);
|
||||
|
|
@ -105,7 +108,7 @@ const CatalogSection = ({ catalog, onPosterPress }: CatalogSectionProps) => {
|
|||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
<FlatList
|
||||
<FlashList
|
||||
data={catalog.items}
|
||||
renderItem={renderContentItem}
|
||||
keyExtractor={(item) => `${item.id}-${item.type}`}
|
||||
|
|
@ -116,19 +119,7 @@ const CatalogSection = ({ catalog, onPosterPress }: CatalogSectionProps) => {
|
|||
decelerationRate="fast"
|
||||
snapToAlignment="start"
|
||||
ItemSeparatorComponent={() => <View style={{ width: 8 }} />}
|
||||
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 && {
|
||||
|
|
|
|||
|
|
@ -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(() => {
|
||||
|
|
|
|||
|
|
@ -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<ContinueWatchingRef>((props, re
|
|||
</View>
|
||||
</View>
|
||||
|
||||
<FlatList
|
||||
<FlashList
|
||||
data={continueWatchingItems}
|
||||
renderItem={({ item }) => (
|
||||
<ContinueWatchingItem
|
||||
|
|
@ -756,6 +756,7 @@ const ContinueWatchingSection = React.forwardRef<ContinueWatchingRef>((props, re
|
|||
decelerationRate="fast"
|
||||
snapToAlignment="start"
|
||||
ItemSeparatorComponent={() => <View style={{ width: 16 }} />}
|
||||
estimatedItemSize={280 + 16}
|
||||
// TV-specific focus navigation properties
|
||||
{...(Platform.isTV && {
|
||||
directionalLockEnabled: true,
|
||||
|
|
|
|||
|
|
@ -252,29 +252,7 @@ const FeaturedContent = ({ featuredContent }: FeaturedContentProps) => {
|
|||
)}
|
||||
</View>
|
||||
|
||||
{/* Action Buttons */}
|
||||
<View style={styles.actionButtonsContainer}>
|
||||
<TouchableOpacity
|
||||
style={styles.playButton}
|
||||
onPress={() => 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}
|
||||
>
|
||||
<MaterialIcons name="play-arrow" size={Platform.isTV ? 28 : 24} color="#000" />
|
||||
<Text style={styles.playButtonText}>Play</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
|
||||
</View>
|
||||
{/* Action buttons removed per design */}
|
||||
|
||||
|
||||
</View>
|
||||
|
|
@ -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
|
||||
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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(() => {
|
|||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
<FlatList
|
||||
<FlashList
|
||||
data={thisWeekEpisodes}
|
||||
keyExtractor={(item) => item.id}
|
||||
renderItem={renderEpisodeItem}
|
||||
|
|
@ -202,6 +202,7 @@ export const ThisWeekSection = React.memo(() => {
|
|||
decelerationRate="fast"
|
||||
snapToAlignment="start"
|
||||
ItemSeparatorComponent={() => <View style={{ width: 16 }} />}
|
||||
estimatedItemSize={ITEM_WIDTH + 16}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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 */}
|
||||
<View style={{
|
||||
height: Platform.OS === 'android' ? 80 : 60,
|
||||
height: Platform.OS === 'android' ? 56 : 56,
|
||||
width: '100%',
|
||||
backgroundColor: colors.darkBackground,
|
||||
backgroundColor: 'transparent',
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
|
|
@ -403,136 +402,168 @@ const MainTabs = () => {
|
|||
|
||||
const renderTabBar = (props: BottomTabBarProps) => {
|
||||
return (
|
||||
<View style={{
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
height: 85,
|
||||
backgroundColor: 'transparent',
|
||||
overflow: 'hidden',
|
||||
}}>
|
||||
<View
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 8,
|
||||
left: 0,
|
||||
right: 0,
|
||||
height: 40,
|
||||
backgroundColor: 'transparent',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-start',
|
||||
overflow: 'visible',
|
||||
zIndex: 100,
|
||||
}}
|
||||
>
|
||||
{Platform.OS === 'ios' ? (
|
||||
<BlurView
|
||||
tint="dark"
|
||||
intensity={75}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
borderTopColor: currentTheme.colors.border,
|
||||
borderTopWidth: 0.5,
|
||||
shadowColor: currentTheme.colors.black,
|
||||
shadowOffset: { width: 0, height: -2 },
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 3,
|
||||
borderRadius: 20,
|
||||
overflow: 'hidden',
|
||||
paddingVertical: 6,
|
||||
paddingHorizontal: 8,
|
||||
minWidth: 200,
|
||||
maxWidth: '90%',
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<LinearGradient
|
||||
colors={[
|
||||
'rgba(0, 0, 0, 0)',
|
||||
'rgba(0, 0, 0, 0.65)',
|
||||
'rgba(0, 0, 0, 0.85)',
|
||||
'rgba(0, 0, 0, 0.98)',
|
||||
]}
|
||||
locations={[0, 0.2, 0.4, 0.8]}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<View
|
||||
style={{
|
||||
height: '100%',
|
||||
paddingBottom: 20,
|
||||
paddingTop: 12,
|
||||
backgroundColor: 'transparent',
|
||||
}}
|
||||
>
|
||||
<View style={{ flexDirection: 'row', paddingTop: 4 }}>
|
||||
{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;
|
||||
>
|
||||
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
|
||||
{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 (
|
||||
<TouchableOpacity
|
||||
key={route.key}
|
||||
activeOpacity={0.7}
|
||||
onPress={onPress}
|
||||
hasTVPreferredFocus={index === 0 && Platform.isTV}
|
||||
tvParallaxProperties={Platform.isTV ? {
|
||||
enabled: true,
|
||||
shiftDistanceX: 2.0,
|
||||
shiftDistanceY: 2.0,
|
||||
tiltAngle: 0.05,
|
||||
magnification: 1.1,
|
||||
} : undefined}
|
||||
style={{
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: 'transparent',
|
||||
}}
|
||||
>
|
||||
<TabIcon
|
||||
focused={isFocused}
|
||||
color={isFocused ? currentTheme.colors.primary : currentTheme.colors.white}
|
||||
iconName={iconName}
|
||||
/>
|
||||
<Text
|
||||
return (
|
||||
<TouchableOpacity
|
||||
key={route.key}
|
||||
activeOpacity={0.8}
|
||||
onPress={onPress}
|
||||
hasTVPreferredFocus={index === 0 && Platform.isTV}
|
||||
tvParallaxProperties={Platform.isTV ? {
|
||||
enabled: true,
|
||||
shiftDistanceX: 2.0,
|
||||
shiftDistanceY: 2.0,
|
||||
tiltAngle: 0.05,
|
||||
magnification: 1.05,
|
||||
} : undefined}
|
||||
style={{
|
||||
fontSize: 12,
|
||||
fontWeight: '600',
|
||||
marginTop: 4,
|
||||
color: isFocused ? currentTheme.colors.primary : currentTheme.colors.white,
|
||||
opacity: isFocused ? 1 : 0.7,
|
||||
paddingVertical: 4,
|
||||
paddingHorizontal: 12,
|
||||
marginHorizontal: 2,
|
||||
borderRadius: 14,
|
||||
backgroundColor: 'transparent',
|
||||
}}
|
||||
>
|
||||
{typeof label === 'string' ? label : ''}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
})}
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 12,
|
||||
fontWeight: '600',
|
||||
color: isFocused ? currentTheme.colors.primary : currentTheme.colors.white,
|
||||
opacity: isFocused ? 1 : 0.75,
|
||||
}}
|
||||
>
|
||||
{typeof label === 'string' ? label : ''}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
</BlurView>
|
||||
) : (
|
||||
<View
|
||||
style={{
|
||||
borderRadius: 20,
|
||||
overflow: 'hidden',
|
||||
paddingVertical: 6,
|
||||
paddingHorizontal: 8,
|
||||
minWidth: 200,
|
||||
maxWidth: '90%',
|
||||
backgroundColor: 'rgba(0,0,0,0.6)',
|
||||
borderWidth: 0.5,
|
||||
borderColor: currentTheme.colors.border,
|
||||
}}
|
||||
>
|
||||
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
|
||||
{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 (
|
||||
<TouchableOpacity
|
||||
key={route.key}
|
||||
activeOpacity={0.8}
|
||||
onPress={onPress}
|
||||
hasTVPreferredFocus={index === 0 && Platform.isTV}
|
||||
tvParallaxProperties={Platform.isTV ? {
|
||||
enabled: true,
|
||||
shiftDistanceX: 2.0,
|
||||
shiftDistanceY: 2.0,
|
||||
tiltAngle: 0.05,
|
||||
magnification: 1.05,
|
||||
} : undefined}
|
||||
style={{
|
||||
paddingVertical: 4,
|
||||
paddingHorizontal: 12,
|
||||
marginHorizontal: 2,
|
||||
borderRadius: 14,
|
||||
backgroundColor: 'transparent',
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 12,
|
||||
fontWeight: '600',
|
||||
color: isFocused ? currentTheme.colors.primary : currentTheme.colors.white,
|
||||
opacity: isFocused ? 1 : 0.8,
|
||||
}}
|
||||
>
|
||||
{typeof label === 'string' ? label : ''}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
|
@ -578,8 +609,7 @@ const MainTabs = () => {
|
|||
],
|
||||
},
|
||||
}),
|
||||
header: () => (route.name === 'Home' ? <NuvioHeader /> : null),
|
||||
headerShown: route.name === 'Home',
|
||||
headerShown: false,
|
||||
tabBarShowLabel: false,
|
||||
tabBarStyle: {
|
||||
position: 'absolute',
|
||||
|
|
|
|||
|
|
@ -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<StreamingContent | null>(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 (
|
||||
<FeaturedContent
|
||||
key={`featured-${showHeroSection}-${featuredContentSource}`}
|
||||
featuredContent={featuredContent}
|
||||
isSaved={isSaved}
|
||||
handleSaveToLibrary={handleSaveToLibrary}
|
||||
/>
|
||||
);
|
||||
return null;
|
||||
case 'thisWeek':
|
||||
return <Animated.View entering={FadeIn.duration(300).delay(100)}><ThisWeekSection /></Animated.View>;
|
||||
case 'continueWatching':
|
||||
|
|
@ -624,7 +621,11 @@ const HomeScreen = () => {
|
|||
case 'catalog':
|
||||
return (
|
||||
<Animated.View entering={FadeIn.duration(300)}>
|
||||
<CatalogSection catalog={item.catalog} onPosterPress={handlePosterPress} />
|
||||
<CatalogSection
|
||||
catalog={item.catalog}
|
||||
onPosterPress={handlePosterPress}
|
||||
onPosterFocus={(content) => setSelectedContent(content)}
|
||||
/>
|
||||
</Animated.View>
|
||||
);
|
||||
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<StreamingContent | null>(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
|
||||
/>
|
||||
<FlatList
|
||||
{/* Locked hero section using selectedContent */}
|
||||
{showHeroSection && (
|
||||
<View>
|
||||
<FeaturedContent
|
||||
featuredContent={selectedContent}
|
||||
isSaved={isSaved}
|
||||
handleSaveToLibrary={handleSaveToLibrary}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
<FlashList
|
||||
data={listData}
|
||||
renderItem={renderListItem}
|
||||
keyExtractor={item => 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,
|
||||
})}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
|
|
@ -741,7 +734,11 @@ const HomeScreen = () => {
|
|||
currentTheme.colors,
|
||||
listData,
|
||||
renderListItem,
|
||||
ListFooterComponent
|
||||
ListFooterComponent,
|
||||
showHeroSection,
|
||||
selectedContent,
|
||||
isSaved,
|
||||
handleSaveToLibrary
|
||||
]);
|
||||
|
||||
return isLoading ? renderLoadingScreen : renderMainContent;
|
||||
|
|
|
|||
BIN
src/utils/.logger.ts.swp
Normal file
BIN
src/utils/.logger.ts.swp
Normal file
Binary file not shown.
Loading…
Reference in a new issue