mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-03-11 17:45:38 +00:00
feat: implement intro submission feature and update to beta.14
This commit is contained in:
parent
28216b475f
commit
8d9fed3f7f
15 changed files with 828 additions and 121 deletions
10
ALPHA_BUILD_2_ANNOUNCEMENT.md
Normal file
10
ALPHA_BUILD_2_ANNOUNCEMENT.md
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
# Nuvio Alpha Build 2
|
||||||
|
|
||||||
|
This is the second alpha release of Nuvio!
|
||||||
|
|
||||||
|
## What's New
|
||||||
|
- **Intro Submission:** You can now submit intro timestamps directly to IntroDB!
|
||||||
|
- **Bug Fixes:** Various improvements and stability fixes.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
Download the attached APK and install it on your Android device.
|
||||||
132
package-lock.json
generated
132
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "nuvio",
|
"name": "nuvio",
|
||||||
"version": "0.6.0-beta.6",
|
"version": "0.6.0-beta.16",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "nuvio",
|
"name": "nuvio",
|
||||||
"version": "0.6.0-beta.6",
|
"version": "0.6.0-beta.16",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@adrianso/react-native-device-brightness": "^1.2.7",
|
"@adrianso/react-native-device-brightness": "^1.2.7",
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
"@gorhom/bottom-sheet": "^5.2.6",
|
"@gorhom/bottom-sheet": "^5.2.6",
|
||||||
"@kesha-antonov/react-native-background-downloader": "^4.4.5",
|
"@kesha-antonov/react-native-background-downloader": "^4.4.5",
|
||||||
"@legendapp/list": "^2.0.13",
|
"@legendapp/list": "^2.0.13",
|
||||||
"@lottiefiles/dotlottie-react": "^0.17.7",
|
"@lottiefiles/dotlottie-react": "^0.13.5",
|
||||||
"@react-native-community/blur": "^4.4.1",
|
"@react-native-community/blur": "^4.4.1",
|
||||||
"@react-native-community/netinfo": "^11.4.1",
|
"@react-native-community/netinfo": "^11.4.1",
|
||||||
"@react-native-community/slider": "^5.1.1",
|
"@react-native-community/slider": "^5.1.1",
|
||||||
|
|
@ -1540,6 +1540,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz",
|
||||||
"integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==",
|
"integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
|
|
@ -1798,6 +1799,15 @@
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@expo/cli/node_modules/undici": {
|
||||||
|
"version": "6.23.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici/-/undici-6.23.0.tgz",
|
||||||
|
"integrity": "sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.17"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@expo/code-signing-certificates": {
|
"node_modules/@expo/code-signing-certificates": {
|
||||||
"version": "0.0.5",
|
"version": "0.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@expo/code-signing-certificates/-/code-signing-certificates-0.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@expo/code-signing-certificates/-/code-signing-certificates-0.0.5.tgz",
|
||||||
|
|
@ -2088,6 +2098,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/@expo/metro-runtime/-/metro-runtime-6.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@expo/metro-runtime/-/metro-runtime-6.1.2.tgz",
|
||||||
"integrity": "sha512-nvM+Qv45QH7pmYvP8JB1G8JpScrWND3KrMA6ZKe62cwwNiX/BjHU28Ear0v/4bQWXlOY0mv6B8CDIm8JxXde9g==",
|
"integrity": "sha512-nvM+Qv45QH7pmYvP8JB1G8JpScrWND3KrMA6ZKe62cwwNiX/BjHU28Ear0v/4bQWXlOY0mv6B8CDIm8JxXde9g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"anser": "^1.4.9",
|
"anser": "^1.4.9",
|
||||||
"pretty-format": "^29.7.0",
|
"pretty-format": "^29.7.0",
|
||||||
|
|
@ -2576,6 +2587,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/@jimp/custom/-/custom-0.22.12.tgz",
|
"resolved": "https://registry.npmjs.org/@jimp/custom/-/custom-0.22.12.tgz",
|
||||||
"integrity": "sha512-xcmww1O/JFP2MrlGUMd3Q78S3Qu6W3mYTXYuIqFq33EorgYHV/HqymHfXy9GjiCJ7OI+7lWx6nYFOzU7M4rd1Q==",
|
"integrity": "sha512-xcmww1O/JFP2MrlGUMd3Q78S3Qu6W3mYTXYuIqFq33EorgYHV/HqymHfXy9GjiCJ7OI+7lWx6nYFOzU7M4rd1Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jimp/core": "^0.22.12"
|
"@jimp/core": "^0.22.12"
|
||||||
}
|
}
|
||||||
|
|
@ -2757,21 +2769,22 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@lottiefiles/dotlottie-react": {
|
"node_modules/@lottiefiles/dotlottie-react": {
|
||||||
"version": "0.17.10",
|
"version": "0.13.5",
|
||||||
"resolved": "https://registry.npmjs.org/@lottiefiles/dotlottie-react/-/dotlottie-react-0.17.10.tgz",
|
"resolved": "https://registry.npmjs.org/@lottiefiles/dotlottie-react/-/dotlottie-react-0.13.5.tgz",
|
||||||
"integrity": "sha512-ikrN05/q0/KjqIU+n48uNwmE7DeZIC9y3Nd19httcKqe273zoOeNYycEaQzLSdcpEGnWLmHaZpgtoo07aQZAXg==",
|
"integrity": "sha512-4U5okwjRqDPkjB572hfZtLXJ/LGfCo6vDwUB2KIPEUoSgqbIlw+UrbnaqVp3GS+dRvhMD27F2JObpHpYRlpF0Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lottiefiles/dotlottie-web": "0.58.1"
|
"@lottiefiles/dotlottie-web": "0.44.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "^17 || ^18 || ^19"
|
"react": "^17 || ^18 || ^19"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@lottiefiles/dotlottie-web": {
|
"node_modules/@lottiefiles/dotlottie-web": {
|
||||||
"version": "0.58.1",
|
"version": "0.44.0",
|
||||||
"resolved": "https://registry.npmjs.org/@lottiefiles/dotlottie-web/-/dotlottie-web-0.58.1.tgz",
|
"resolved": "https://registry.npmjs.org/@lottiefiles/dotlottie-web/-/dotlottie-web-0.44.0.tgz",
|
||||||
"integrity": "sha512-YC4pmScrV0R3rd11gU5xHrjeNczlCic69zlnMH/buDIzYxIbpR88oPUhGtKgu5ln7EJchoLpeRJbA3uLCzSeTA==",
|
"integrity": "sha512-IUWKVciDJI/BMWDWnh7j0Ngd0N8q9ySRAwm84aDqIE07qpmdZ7x1rkIpBaU1yHSNqNYHeh1Rxsl+LC3CY4f0KA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@posthog/core": {
|
"node_modules/@posthog/core": {
|
||||||
|
|
@ -3116,7 +3129,7 @@
|
||||||
"version": "0.72.8",
|
"version": "0.72.8",
|
||||||
"resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.72.8.tgz",
|
"resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.72.8.tgz",
|
||||||
"integrity": "sha512-J3Q4Bkuo99k7mu+jPS9gSUSgq+lLRSI/+ahXNwV92XgJ/8UgOTxu2LPwhJnBk/sQKxq7E8WkZBnBiozukQMqrw==",
|
"integrity": "sha512-J3Q4Bkuo99k7mu+jPS9gSUSgq+lLRSI/+ahXNwV92XgJ/8UgOTxu2LPwhJnBk/sQKxq7E8WkZBnBiozukQMqrw==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"invariant": "^2.2.4",
|
"invariant": "^2.2.4",
|
||||||
|
|
@ -3237,6 +3250,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-7.1.25.tgz",
|
"resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-7.1.25.tgz",
|
||||||
"integrity": "sha512-zQeWK9txDePWbYfqTs0C6jeRdJTm/7VhQtW/1IbJNDi9/rFIRzZule8bdQPAnf8QWUsNujRmi1J9OG/hhfbalg==",
|
"integrity": "sha512-zQeWK9txDePWbYfqTs0C6jeRdJTm/7VhQtW/1IbJNDi9/rFIRzZule8bdQPAnf8QWUsNujRmi1J9OG/hhfbalg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@react-navigation/core": "^7.13.6",
|
"@react-navigation/core": "^7.13.6",
|
||||||
"escape-string-regexp": "^4.0.0",
|
"escape-string-regexp": "^4.0.0",
|
||||||
|
|
@ -3878,6 +3892,7 @@
|
||||||
"integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==",
|
"integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/core": "^7.21.3",
|
"@babel/core": "^7.21.3",
|
||||||
"@svgr/babel-preset": "8.1.0",
|
"@svgr/babel-preset": "8.1.0",
|
||||||
|
|
@ -4098,6 +4113,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz",
|
||||||
"integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==",
|
"integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/prop-types": "*",
|
"@types/prop-types": "*",
|
||||||
"csstype": "^3.2.2"
|
"csstype": "^3.2.2"
|
||||||
|
|
@ -4107,8 +4123,9 @@
|
||||||
"version": "0.72.8",
|
"version": "0.72.8",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.72.8.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.72.8.tgz",
|
||||||
"integrity": "sha512-St6xA7+EoHN5mEYfdWnfYt0e8u6k2FR0P9s2arYgakQGFgU1f9FlPrIEcj0X24pLCF5c5i3WVuLCUdiCYHmOoA==",
|
"integrity": "sha512-St6xA7+EoHN5mEYfdWnfYt0e8u6k2FR0P9s2arYgakQGFgU1f9FlPrIEcj0X24pLCF5c5i3WVuLCUdiCYHmOoA==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@react-native/virtualized-lists": "^0.72.4",
|
"@react-native/virtualized-lists": "^0.72.4",
|
||||||
"@types/react": "*"
|
"@types/react": "*"
|
||||||
|
|
@ -4644,6 +4661,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz",
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz",
|
||||||
"integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==",
|
"integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"follow-redirects": "^1.15.6",
|
"follow-redirects": "^1.15.6",
|
||||||
"form-data": "^4.0.4",
|
"form-data": "^4.0.4",
|
||||||
|
|
@ -5047,6 +5065,7 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"baseline-browser-mapping": "^2.9.0",
|
"baseline-browser-mapping": "^2.9.0",
|
||||||
"caniuse-lite": "^1.0.30001759",
|
"caniuse-lite": "^1.0.30001759",
|
||||||
|
|
@ -6276,6 +6295,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/expo/-/expo-54.0.29.tgz",
|
"resolved": "https://registry.npmjs.org/expo/-/expo-54.0.29.tgz",
|
||||||
"integrity": "sha512-9C90gyOzV83y2S3XzCbRDCuKYNaiyCzuP9ketv46acHCEZn+QTamPK/DobdghoSiofCmlfoaiD6/SzfxDiHMnw==",
|
"integrity": "sha512-9C90gyOzV83y2S3XzCbRDCuKYNaiyCzuP9ketv46acHCEZn+QTamPK/DobdghoSiofCmlfoaiD6/SzfxDiHMnw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.20.0",
|
"@babel/runtime": "^7.20.0",
|
||||||
"@expo/cli": "54.0.19",
|
"@expo/cli": "54.0.19",
|
||||||
|
|
@ -6479,6 +6499,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/expo-device/-/expo-device-8.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/expo-device/-/expo-device-8.0.10.tgz",
|
||||||
"integrity": "sha512-jd5BxjaF7382JkDMaC+P04aXXknB2UhWaVx5WiQKA05ugm/8GH5uaz9P9ckWdMKZGQVVEOC8MHaUADoT26KmFA==",
|
"integrity": "sha512-jd5BxjaF7382JkDMaC+P04aXXknB2UhWaVx5WiQKA05ugm/8GH5uaz9P9ckWdMKZGQVVEOC8MHaUADoT26KmFA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ua-parser-js": "^0.7.33"
|
"ua-parser-js": "^0.7.33"
|
||||||
},
|
},
|
||||||
|
|
@ -6506,6 +6527,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-19.0.21.tgz",
|
"resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-19.0.21.tgz",
|
||||||
"integrity": "sha512-s3DlrDdiscBHtab/6W1osrjGL+C2bvoInPJD7sOwmxfJ5Woynv2oc+Fz1/xVXaE/V7HE/+xrHC/H45tu6lZzzg==",
|
"integrity": "sha512-s3DlrDdiscBHtab/6W1osrjGL+C2bvoInPJD7sOwmxfJ5Woynv2oc+Fz1/xVXaE/V7HE/+xrHC/H45tu6lZzzg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"expo": "*",
|
"expo": "*",
|
||||||
"react-native": "*"
|
"react-native": "*"
|
||||||
|
|
@ -6516,6 +6538,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/expo-font/-/expo-font-14.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/expo-font/-/expo-font-14.0.10.tgz",
|
||||||
"integrity": "sha512-UqyNaaLKRpj4pKAP4HZSLnuDQqueaO5tB1c/NWu5vh1/LF9ulItyyg2kF/IpeOp0DeOLk0GY0HrIXaKUMrwB+Q==",
|
"integrity": "sha512-UqyNaaLKRpj4pKAP4HZSLnuDQqueaO5tB1c/NWu5vh1/LF9ulItyyg2kF/IpeOp0DeOLk0GY0HrIXaKUMrwB+Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fontfaceobserver": "^2.1.0"
|
"fontfaceobserver": "^2.1.0"
|
||||||
},
|
},
|
||||||
|
|
@ -6611,6 +6634,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/expo-localization/-/expo-localization-17.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/expo-localization/-/expo-localization-17.0.8.tgz",
|
||||||
"integrity": "sha512-UrdwklZBDJ+t+ZszMMiE0SXZ2eJxcquCuQcl6EvGHM9K+e6YqKVRQ+w8qE+iIB3H75v2RJy6MHAaLK+Mqeo04g==",
|
"integrity": "sha512-UrdwklZBDJ+t+ZszMMiE0SXZ2eJxcquCuQcl6EvGHM9K+e6YqKVRQ+w8qE+iIB3H75v2RJy6MHAaLK+Mqeo04g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"rtl-detect": "^1.0.2"
|
"rtl-detect": "^1.0.2"
|
||||||
},
|
},
|
||||||
|
|
@ -7670,6 +7694,7 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.28.4"
|
"@babel/runtime": "^7.28.4"
|
||||||
},
|
},
|
||||||
|
|
@ -8286,6 +8311,20 @@
|
||||||
"xml-name-validator": ">= 2.0.1 < 3.0.0"
|
"xml-name-validator": ">= 2.0.1 < 3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jsdom/node_modules/tough-cookie": {
|
||||||
|
"version": "2.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
|
||||||
|
"integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"psl": "^1.1.28",
|
||||||
|
"punycode": "^2.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/jsesc": {
|
"node_modules/jsesc": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
|
||||||
|
|
@ -10562,6 +10601,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
|
||||||
"integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==",
|
"integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
|
|
@ -10602,6 +10642,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
|
||||||
"integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
|
"integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"scheduler": "^0.26.0"
|
"scheduler": "^0.26.0"
|
||||||
},
|
},
|
||||||
|
|
@ -10659,6 +10700,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/react-native/-/react-native-0.81.4.tgz",
|
"resolved": "https://registry.npmjs.org/react-native/-/react-native-0.81.4.tgz",
|
||||||
"integrity": "sha512-bt5bz3A/+Cv46KcjV0VQa+fo7MKxs17RCcpzjftINlen4ZDUl0I6Ut+brQ2FToa5oD0IB0xvQHfmsg2EDqsZdQ==",
|
"integrity": "sha512-bt5bz3A/+Cv46KcjV0VQa+fo7MKxs17RCcpzjftINlen4ZDUl0I6Ut+brQ2FToa5oD0IB0xvQHfmsg2EDqsZdQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jest/create-cache-key-function": "^29.7.0",
|
"@jest/create-cache-key-function": "^29.7.0",
|
||||||
"@react-native/assets-registry": "0.81.4",
|
"@react-native/assets-registry": "0.81.4",
|
||||||
|
|
@ -10747,6 +10789,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/react-native-bottom-tabs/-/react-native-bottom-tabs-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-native-bottom-tabs/-/react-native-bottom-tabs-1.1.0.tgz",
|
||||||
"integrity": "sha512-Uu1gvM3i1Hb4DjVvR/38J1QVQEs0RkPc7K6yon99HgvRWWOyLs7kjPDhUswtb8ije4pKW712skIXWJ0lgKzbyQ==",
|
"integrity": "sha512-Uu1gvM3i1Hb4DjVvR/38J1QVQEs0RkPc7K6yon99HgvRWWOyLs7kjPDhUswtb8ije4pKW712skIXWJ0lgKzbyQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"react-freeze": "^1.0.0",
|
"react-freeze": "^1.0.0",
|
||||||
"sf-symbols-typescript": "^2.0.0",
|
"sf-symbols-typescript": "^2.0.0",
|
||||||
|
|
@ -10777,6 +10820,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.29.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.29.1.tgz",
|
||||||
"integrity": "sha512-du3qmv0e3Sm7qsd9SfmHps+AggLiylcBBQ8ztz7WUtd8ZjKs5V3kekAbi9R2W9bRLSg47Ntp4GGMYZOhikQdZA==",
|
"integrity": "sha512-du3qmv0e3Sm7qsd9SfmHps+AggLiylcBBQ8ztz7WUtd8ZjKs5V3kekAbi9R2W9bRLSg47Ntp4GGMYZOhikQdZA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@egjs/hammerjs": "^2.0.17",
|
"@egjs/hammerjs": "^2.0.17",
|
||||||
"hoist-non-react-statics": "^3.3.0",
|
"hoist-non-react-statics": "^3.3.0",
|
||||||
|
|
@ -10875,6 +10919,7 @@
|
||||||
"integrity": "sha512-hcvjTu9YJE9fMmnAUvhG8CxvYLpOuMQ/2eyi/S6GyrecezF6Rmk/uRQEL6v09BRFWA/xRVZNQVulQPS+2HS3mQ==",
|
"integrity": "sha512-hcvjTu9YJE9fMmnAUvhG8CxvYLpOuMQ/2eyi/S6GyrecezF6Rmk/uRQEL6v09BRFWA/xRVZNQVulQPS+2HS3mQ==",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "*",
|
"react": "*",
|
||||||
"react-native": "*"
|
"react-native": "*"
|
||||||
|
|
@ -10940,6 +10985,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-4.2.0.tgz",
|
||||||
"integrity": "sha512-frhu5b8/m/VvaMWz48V8RxcsXnE3hrlErQ5chr21MzAeDCpY4X14sQjvm+jvu3aOI+7Cz2atdRpyhhIuqxVaXg==",
|
"integrity": "sha512-frhu5b8/m/VvaMWz48V8RxcsXnE3hrlErQ5chr21MzAeDCpY4X14sQjvm+jvu3aOI+7Cz2atdRpyhhIuqxVaXg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"react-native-is-edge-to-edge": "1.2.1",
|
"react-native-is-edge-to-edge": "1.2.1",
|
||||||
"semver": "7.7.3"
|
"semver": "7.7.3"
|
||||||
|
|
@ -10979,6 +11025,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-5.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-5.6.2.tgz",
|
||||||
"integrity": "sha512-4XGqMNj5qjUTYywJqpdWZ9IG8jgkS3h06sfVjfw5yZQZfWnRFXczi0GnYyFyCc2EBps/qFmoCH8fez//WumdVg==",
|
"integrity": "sha512-4XGqMNj5qjUTYywJqpdWZ9IG8jgkS3h06sfVjfw5yZQZfWnRFXczi0GnYyFyCc2EBps/qFmoCH8fez//WumdVg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "*",
|
"react": "*",
|
||||||
"react-native": "*"
|
"react-native": "*"
|
||||||
|
|
@ -10989,6 +11036,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.18.0.tgz",
|
||||||
"integrity": "sha512-mRTLWL7Uc1p/RFNveEIIrhP22oxHduC2ZnLr/2iHwBeYpGXR0rJZ7Bgc0ktxQSHRjWTPT70qc/7yd4r9960PBQ==",
|
"integrity": "sha512-mRTLWL7Uc1p/RFNveEIIrhP22oxHduC2ZnLr/2iHwBeYpGXR0rJZ7Bgc0ktxQSHRjWTPT70qc/7yd4r9960PBQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"react-freeze": "^1.0.0",
|
"react-freeze": "^1.0.0",
|
||||||
"warn-once": "^0.1.0"
|
"warn-once": "^0.1.0"
|
||||||
|
|
@ -11003,6 +11051,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-15.15.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-15.15.1.tgz",
|
||||||
"integrity": "sha512-ZUD1xwc3Hwo4cOmOLumjJVoc7lEf9oQFlHnLmgccLC19fNm6LVEdtB+Cnip6gEi0PG3wfvVzskViEtrySQP8Fw==",
|
"integrity": "sha512-ZUD1xwc3Hwo4cOmOLumjJVoc7lEf9oQFlHnLmgccLC19fNm6LVEdtB+Cnip6gEi0PG3wfvVzskViEtrySQP8Fw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"css-select": "^5.1.0",
|
"css-select": "^5.1.0",
|
||||||
"css-tree": "^1.1.3",
|
"css-tree": "^1.1.3",
|
||||||
|
|
@ -11231,6 +11280,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.21.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.21.2.tgz",
|
||||||
"integrity": "sha512-SO2t9/17zM4iEnFvlu2DA9jqNbzNhoUP+AItkoCOyFmDMOhUnBBznBDCYN92fGdfAkfQlWzPoez6+zLxFNsZEg==",
|
"integrity": "sha512-SO2t9/17zM4iEnFvlu2DA9jqNbzNhoUP+AItkoCOyFmDMOhUnBBznBDCYN92fGdfAkfQlWzPoez6+zLxFNsZEg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.18.6",
|
"@babel/runtime": "^7.18.6",
|
||||||
"@react-native/normalize-colors": "^0.74.1",
|
"@react-native/normalize-colors": "^0.74.1",
|
||||||
|
|
@ -11487,6 +11537,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz",
|
||||||
"integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==",
|
"integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
|
|
@ -11690,6 +11741,20 @@
|
||||||
"node": ">= 0.12"
|
"node": ">= 0.12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/request/node_modules/tough-cookie": {
|
||||||
|
"version": "2.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
|
||||||
|
"integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"psl": "^1.1.28",
|
||||||
|
"punycode": "^2.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/require-directory": {
|
"node_modules/require-directory": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||||
|
|
@ -12939,6 +13004,24 @@
|
||||||
"url": "https://github.com/sponsors/jonschlinkert"
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tldts": {
|
||||||
|
"version": "7.0.19",
|
||||||
|
"resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.19.tgz",
|
||||||
|
"integrity": "sha512-8PWx8tvC4jDB39BQw1m4x8y5MH1BcQ5xHeL2n7UVFulMPH/3Q0uiamahFJ3lXA0zO2SUyRXuVVbWSDmstlt9YA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tldts-core": "^7.0.19"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"tldts": "bin/cli.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tldts-core": {
|
||||||
|
"version": "7.0.19",
|
||||||
|
"resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.19.tgz",
|
||||||
|
"integrity": "sha512-lJX2dEWx0SGH4O6p+7FPwYmJ/bu1JbcGJ8RLaG9b7liIgZ85itUVEPbMtWRVrde/0fnDPEPHW10ZsKW3kVsE9A==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/tmp": {
|
"node_modules/tmp": {
|
||||||
"version": "0.2.5",
|
"version": "0.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz",
|
||||||
|
|
@ -12994,17 +13077,16 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tough-cookie": {
|
"node_modules/tough-cookie": {
|
||||||
"version": "2.5.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.0.tgz",
|
||||||
"integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
|
"integrity": "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==",
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"optional": true,
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"psl": "^1.1.28",
|
"tldts": "^7.0.5"
|
||||||
"punycode": "^2.1.1"
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.8"
|
"node": ">=16"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tr46": {
|
"node_modules/tr46": {
|
||||||
|
|
@ -13081,8 +13163,9 @@
|
||||||
"version": "5.9.3",
|
"version": "5.9.3",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
|
|
@ -13123,15 +13206,6 @@
|
||||||
"integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==",
|
"integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/undici": {
|
|
||||||
"version": "6.22.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/undici/-/undici-6.22.0.tgz",
|
|
||||||
"integrity": "sha512-hU/10obOIu62MGYjdskASR3CUAiYaFTtC9Pa6vHyf//mAipSvSQg6od2CnJswq7fvzNS3zJhxoRkgNVaHurWKw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18.17"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/undici-types": {
|
"node_modules/undici-types": {
|
||||||
"version": "7.16.0",
|
"version": "7.16.0",
|
||||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "nuvio",
|
"name": "nuvio",
|
||||||
"version": "0.6.0-beta.6",
|
"version": "0.6.0-beta.16",
|
||||||
"main": "index.ts",
|
"main": "index.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "expo start",
|
"start": "expo start",
|
||||||
"android": "expo run:android",
|
"android": "expo run:android",
|
||||||
"ios": "expo run:ios",
|
"ios": "expo run:ios",
|
||||||
|
"build": "export NODE_ENV=production && cd android && ./gradlew assembleRelease",
|
||||||
"postinstall": "patch-package"
|
"postinstall": "patch-package"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
@ -19,7 +20,7 @@
|
||||||
"@gorhom/bottom-sheet": "^5.2.6",
|
"@gorhom/bottom-sheet": "^5.2.6",
|
||||||
"@kesha-antonov/react-native-background-downloader": "^4.4.5",
|
"@kesha-antonov/react-native-background-downloader": "^4.4.5",
|
||||||
"@legendapp/list": "^2.0.13",
|
"@legendapp/list": "^2.0.13",
|
||||||
"@lottiefiles/dotlottie-react": "^0.17.7",
|
"@lottiefiles/dotlottie-react": "^0.13.5",
|
||||||
"@react-native-community/blur": "^4.4.1",
|
"@react-native-community/blur": "^4.4.1",
|
||||||
"@react-native-community/netinfo": "^11.4.1",
|
"@react-native-community/netinfo": "^11.4.1",
|
||||||
"@react-native-community/slider": "^5.1.1",
|
"@react-native-community/slider": "^5.1.1",
|
||||||
|
|
@ -110,5 +111,8 @@
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"xcode": "^3.0.1"
|
"xcode": "^3.0.1"
|
||||||
},
|
},
|
||||||
|
"overrides": {
|
||||||
|
"@types/react": "~18.3.12"
|
||||||
|
},
|
||||||
"private": true
|
"private": true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
38
patches/react-native-bottom-tabs+1.1.0.patch
Normal file
38
patches/react-native-bottom-tabs+1.1.0.patch
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
diff --git a/node_modules/react-native-bottom-tabs/app.plugin.js b/node_modules/react-native-bottom-tabs/app.plugin.js
|
||||||
|
index 87edf84..711d0c0 100644
|
||||||
|
--- a/node_modules/react-native-bottom-tabs/app.plugin.js
|
||||||
|
+++ b/node_modules/react-native-bottom-tabs/app.plugin.js
|
||||||
|
@@ -1 +1,31 @@
|
||||||
|
-module.exports = require('./lib/module/expo');
|
||||||
|
+"use strict";
|
||||||
|
+
|
||||||
|
+const { createRunOncePlugin, withAndroidStyles } = require("@expo/config-plugins");
|
||||||
|
+
|
||||||
|
+const MATERIAL3_THEME_DYANMIC = "Theme.Material3.DynamicColors.DayNight.NoActionBar";
|
||||||
|
+const MATERIAL3_THEME = "Theme.Material3.DayNight.NoActionBar";
|
||||||
|
+const MATERIAL2_THEME = "Theme.MaterialComponents.DayNight.NoActionBar";
|
||||||
|
+const MATERIAL3_EXPRESSIVE_THEME = "Theme.Material3Expressive.DayNight.NoActionBar";
|
||||||
|
+
|
||||||
|
+const withMaterial3Theme = (config, options) => {
|
||||||
|
+ const theme = options?.theme;
|
||||||
|
+ return withAndroidStyles(config, stylesConfig => {
|
||||||
|
+ stylesConfig.modResults.resources.style = stylesConfig.modResults.resources.style?.map(style => {
|
||||||
|
+ if (style.$.name === "AppTheme") {
|
||||||
|
+ if (theme === "material3-dynamic") {
|
||||||
|
+ style.$.parent = MATERIAL3_THEME_DYANMIC;
|
||||||
|
+ } else if (theme === "material2") {
|
||||||
|
+ style.$.parent = MATERIAL2_THEME;
|
||||||
|
+ } else if (theme === "material3-expressive") {
|
||||||
|
+ style.$.parent = MATERIAL3_EXPRESSIVE_THEME;
|
||||||
|
+ } else {
|
||||||
|
+ style.$.parent = MATERIAL3_THEME;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ return style;
|
||||||
|
+ });
|
||||||
|
+ return stylesConfig;
|
||||||
|
+ });
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+module.exports = createRunOncePlugin(withMaterial3Theme, "react-native-bottom-tabs");
|
||||||
|
\ No newline at end of file
|
||||||
|
|
@ -35,6 +35,7 @@ import { AudioTrackModal } from './modals/AudioTrackModal';
|
||||||
import { SubtitleModals } from './modals/SubtitleModals';
|
import { SubtitleModals } from './modals/SubtitleModals';
|
||||||
import { SubtitleSyncModal } from './modals/SubtitleSyncModal';
|
import { SubtitleSyncModal } from './modals/SubtitleSyncModal';
|
||||||
import SpeedModal from './modals/SpeedModal';
|
import SpeedModal from './modals/SpeedModal';
|
||||||
|
import { SubmitIntroModal } from './modals/SubmitIntroModal';
|
||||||
import { SourcesModal } from './modals/SourcesModal';
|
import { SourcesModal } from './modals/SourcesModal';
|
||||||
import { EpisodesModal } from './modals/EpisodesModal';
|
import { EpisodesModal } from './modals/EpisodesModal';
|
||||||
import { EpisodeStreamsModal } from './modals/EpisodeStreamsModal';
|
import { EpisodeStreamsModal } from './modals/EpisodeStreamsModal';
|
||||||
|
|
@ -917,6 +918,7 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
setShowAudioModal={modals.setShowAudioModal}
|
setShowAudioModal={modals.setShowAudioModal}
|
||||||
setShowSubtitleModal={modals.setShowSubtitleModal}
|
setShowSubtitleModal={modals.setShowSubtitleModal}
|
||||||
setShowSpeedModal={modals.setShowSpeedModal}
|
setShowSpeedModal={modals.setShowSpeedModal}
|
||||||
|
setShowSubmitIntroModal={modals.setShowSubmitIntroModal}
|
||||||
isSubtitleModalOpen={modals.showSubtitleModal}
|
isSubtitleModalOpen={modals.showSubtitleModal}
|
||||||
setShowSourcesModal={modals.setShowSourcesModal}
|
setShowSourcesModal={modals.setShowSourcesModal}
|
||||||
setShowEpisodesModal={type === 'series' ? modals.setShowEpisodesModal : undefined}
|
setShowEpisodesModal={type === 'series' ? modals.setShowEpisodesModal : undefined}
|
||||||
|
|
@ -932,6 +934,7 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
onSwitchToMPV={handleManualSwitchToMPV}
|
onSwitchToMPV={handleManualSwitchToMPV}
|
||||||
useExoPlayer={useExoPlayer}
|
useExoPlayer={useExoPlayer}
|
||||||
isBuffering={playerState.isBuffering}
|
isBuffering={playerState.isBuffering}
|
||||||
|
imdbId={imdbId}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SpeedActivatedOverlay
|
<SpeedActivatedOverlay
|
||||||
|
|
@ -1106,6 +1109,15 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
setHoldToSpeedValue={speedControl.setHoldToSpeedValue}
|
setHoldToSpeedValue={speedControl.setHoldToSpeedValue}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<SubmitIntroModal
|
||||||
|
visible={modals.showSubmitIntroModal}
|
||||||
|
onClose={() => modals.setShowSubmitIntroModal(false)}
|
||||||
|
currentTime={playerState.currentTime}
|
||||||
|
imdbId={imdbId}
|
||||||
|
season={season}
|
||||||
|
episode={episode}
|
||||||
|
/>
|
||||||
|
|
||||||
<EpisodesModal
|
<EpisodesModal
|
||||||
showEpisodesModal={modals.showEpisodesModal}
|
showEpisodesModal={modals.showEpisodesModal}
|
||||||
setShowEpisodesModal={modals.setShowEpisodesModal}
|
setShowEpisodesModal={modals.setShowEpisodesModal}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import UpNextButton from './common/UpNextButton';
|
||||||
import { PlayerControls } from './controls/PlayerControls';
|
import { PlayerControls } from './controls/PlayerControls';
|
||||||
import AudioTrackModal from './modals/AudioTrackModal';
|
import AudioTrackModal from './modals/AudioTrackModal';
|
||||||
import SpeedModal from './modals/SpeedModal';
|
import SpeedModal from './modals/SpeedModal';
|
||||||
|
import { SubmitIntroModal } from './modals/SubmitIntroModal';
|
||||||
import SubtitleModals from './modals/SubtitleModals';
|
import SubtitleModals from './modals/SubtitleModals';
|
||||||
import { SubtitleSyncModal } from './modals/SubtitleSyncModal';
|
import { SubtitleSyncModal } from './modals/SubtitleSyncModal';
|
||||||
import SourcesModal from './modals/SourcesModal';
|
import SourcesModal from './modals/SourcesModal';
|
||||||
|
|
@ -868,6 +869,7 @@ const KSPlayerCore: React.FC = () => {
|
||||||
setShowAudioModal={modals.setShowAudioModal}
|
setShowAudioModal={modals.setShowAudioModal}
|
||||||
setShowSubtitleModal={modals.setShowSubtitleModal}
|
setShowSubtitleModal={modals.setShowSubtitleModal}
|
||||||
setShowSpeedModal={modals.setShowSpeedModal}
|
setShowSpeedModal={modals.setShowSpeedModal}
|
||||||
|
setShowSubmitIntroModal={modals.setShowSubmitIntroModal}
|
||||||
isSubtitleModalOpen={modals.showSubtitleModal}
|
isSubtitleModalOpen={modals.showSubtitleModal}
|
||||||
setShowSourcesModal={modals.setShowSourcesModal}
|
setShowSourcesModal={modals.setShowSourcesModal}
|
||||||
setShowEpisodesModal={type === 'series' ? modals.setShowEpisodesModal : undefined}
|
setShowEpisodesModal={type === 'series' ? modals.setShowEpisodesModal : undefined}
|
||||||
|
|
@ -881,6 +883,7 @@ const KSPlayerCore: React.FC = () => {
|
||||||
allowsAirPlay={allowsAirPlay}
|
allowsAirPlay={allowsAirPlay}
|
||||||
onAirPlayPress={() => ksPlayerRef.current?.showAirPlayPicker()}
|
onAirPlayPress={() => ksPlayerRef.current?.showAirPlayPicker()}
|
||||||
isBuffering={isBuffering}
|
isBuffering={isBuffering}
|
||||||
|
imdbId={imdbId}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
@ -998,6 +1001,15 @@ const KSPlayerCore: React.FC = () => {
|
||||||
setHoldToSpeedValue={speedControl.setHoldToSpeedValue}
|
setHoldToSpeedValue={speedControl.setHoldToSpeedValue}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<SubmitIntroModal
|
||||||
|
visible={modals.showSubmitIntroModal}
|
||||||
|
onClose={() => modals.setShowSubmitIntroModal(false)}
|
||||||
|
currentTime={currentTime}
|
||||||
|
imdbId={imdbId}
|
||||||
|
season={season}
|
||||||
|
episode={episode}
|
||||||
|
/>
|
||||||
|
|
||||||
<SubtitleModals
|
<SubtitleModals
|
||||||
showSubtitleModal={modals.showSubtitleModal}
|
showSubtitleModal={modals.showSubtitleModal}
|
||||||
setShowSubtitleModal={modals.setShowSubtitleModal}
|
setShowSubtitleModal={modals.setShowSubtitleModal}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,10 @@ import { useTranslation } from 'react-i18next';
|
||||||
import { styles } from '../utils/playerStyles'; // Updated styles
|
import { styles } from '../utils/playerStyles'; // Updated styles
|
||||||
import { getTrackDisplayName } from '../utils/playerUtils';
|
import { getTrackDisplayName } from '../utils/playerUtils';
|
||||||
import { useTheme } from '../../../contexts/ThemeContext';
|
import { useTheme } from '../../../contexts/ThemeContext';
|
||||||
|
import { useSettings } from '../../../hooks/useSettings';
|
||||||
|
|
||||||
|
import { introService } from '../../../services/introService';
|
||||||
|
import { toastService } from '../../../services/toastService';
|
||||||
|
|
||||||
interface PlayerControlsProps {
|
interface PlayerControlsProps {
|
||||||
showControls: boolean;
|
showControls: boolean;
|
||||||
|
|
@ -37,6 +41,7 @@ interface PlayerControlsProps {
|
||||||
setShowAudioModal: (show: boolean) => void;
|
setShowAudioModal: (show: boolean) => void;
|
||||||
setShowSubtitleModal: (show: boolean) => void;
|
setShowSubtitleModal: (show: boolean) => void;
|
||||||
setShowSpeedModal: (show: boolean) => void;
|
setShowSpeedModal: (show: boolean) => void;
|
||||||
|
setShowSubmitIntroModal: (show: boolean) => void;
|
||||||
isSubtitleModalOpen?: boolean;
|
isSubtitleModalOpen?: boolean;
|
||||||
setShowSourcesModal?: (show: boolean) => void;
|
setShowSourcesModal?: (show: boolean) => void;
|
||||||
setShowEpisodesModal?: (show: boolean) => void;
|
setShowEpisodesModal?: (show: boolean) => void;
|
||||||
|
|
@ -55,6 +60,7 @@ interface PlayerControlsProps {
|
||||||
onSwitchToMPV?: () => void;
|
onSwitchToMPV?: () => void;
|
||||||
useExoPlayer?: boolean;
|
useExoPlayer?: boolean;
|
||||||
isBuffering?: boolean;
|
isBuffering?: boolean;
|
||||||
|
imdbId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PlayerControls: React.FC<PlayerControlsProps> = ({
|
export const PlayerControls: React.FC<PlayerControlsProps> = ({
|
||||||
|
|
@ -85,6 +91,7 @@ export const PlayerControls: React.FC<PlayerControlsProps> = ({
|
||||||
setShowAudioModal,
|
setShowAudioModal,
|
||||||
setShowSubtitleModal,
|
setShowSubtitleModal,
|
||||||
setShowSpeedModal,
|
setShowSpeedModal,
|
||||||
|
setShowSubmitIntroModal,
|
||||||
isSubtitleModalOpen,
|
isSubtitleModalOpen,
|
||||||
setShowSourcesModal,
|
setShowSourcesModal,
|
||||||
setShowEpisodesModal,
|
setShowEpisodesModal,
|
||||||
|
|
@ -100,10 +107,17 @@ export const PlayerControls: React.FC<PlayerControlsProps> = ({
|
||||||
onSwitchToMPV,
|
onSwitchToMPV,
|
||||||
useExoPlayer,
|
useExoPlayer,
|
||||||
isBuffering = false,
|
isBuffering = false,
|
||||||
|
imdbId,
|
||||||
}) => {
|
}) => {
|
||||||
const { currentTheme } = useTheme();
|
const { currentTheme } = useTheme();
|
||||||
|
const { settings } = useSettings();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
// --- Intro Submission Logic ---
|
||||||
|
const handleIntroPress = () => {
|
||||||
|
setShowSubmitIntroModal(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/* Responsive Spacing */
|
/* Responsive Spacing */
|
||||||
const screenWidth = Dimensions.get('window').width;
|
const screenWidth = Dimensions.get('window').width;
|
||||||
|
|
@ -619,6 +633,20 @@ export const PlayerControls: React.FC<PlayerControlsProps> = ({
|
||||||
/>
|
/>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|
||||||
|
{/* Submit Intro Button */}
|
||||||
|
{season !== undefined && episode !== undefined && settings.introSubmitEnabled && settings.introDbApiKey && (
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.iconButton}
|
||||||
|
onPress={handleIntroPress}
|
||||||
|
>
|
||||||
|
<Ionicons
|
||||||
|
name="flag-outline"
|
||||||
|
size={24}
|
||||||
|
color="white"
|
||||||
|
/>
|
||||||
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Right Side: Episodes Button */}
|
{/* Right Side: Episodes Button */}
|
||||||
{setShowEpisodesModal && (
|
{setShowEpisodesModal && (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ export const usePlayerModals = () => {
|
||||||
const [showErrorModal, setShowErrorModal] = useState(false);
|
const [showErrorModal, setShowErrorModal] = useState(false);
|
||||||
const [showSubtitleLanguageModal, setShowSubtitleLanguageModal] = useState(false);
|
const [showSubtitleLanguageModal, setShowSubtitleLanguageModal] = useState(false);
|
||||||
const [showCastDetails, setShowCastDetails] = useState(false);
|
const [showCastDetails, setShowCastDetails] = useState(false);
|
||||||
|
const [showSubmitIntroModal, setShowSubmitIntroModal] = useState(false);
|
||||||
|
|
||||||
// Some modals have associated data
|
// Some modals have associated data
|
||||||
const [selectedEpisodeForStreams, setSelectedEpisodeForStreams] = useState<Episode | null>(null);
|
const [selectedEpisodeForStreams, setSelectedEpisodeForStreams] = useState<Episode | null>(null);
|
||||||
|
|
@ -31,6 +32,7 @@ export const usePlayerModals = () => {
|
||||||
showErrorModal, setShowErrorModal,
|
showErrorModal, setShowErrorModal,
|
||||||
showSubtitleLanguageModal, setShowSubtitleLanguageModal,
|
showSubtitleLanguageModal, setShowSubtitleLanguageModal,
|
||||||
showCastDetails, setShowCastDetails,
|
showCastDetails, setShowCastDetails,
|
||||||
|
showSubmitIntroModal, setShowSubmitIntroModal,
|
||||||
selectedEpisodeForStreams, setSelectedEpisodeForStreams,
|
selectedEpisodeForStreams, setSelectedEpisodeForStreams,
|
||||||
errorDetails, setErrorDetails,
|
errorDetails, setErrorDetails,
|
||||||
selectedCastMember, setSelectedCastMember
|
selectedCastMember, setSelectedCastMember
|
||||||
|
|
|
||||||
347
src/components/player/modals/SubmitIntroModal.tsx
Normal file
347
src/components/player/modals/SubmitIntroModal.tsx
Normal file
|
|
@ -0,0 +1,347 @@
|
||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { View, Text, TouchableOpacity, useWindowDimensions, StyleSheet, TextInput, ActivityIndicator } from 'react-native';
|
||||||
|
import { Ionicons, MaterialIcons } from '@expo/vector-icons';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import Animated, {
|
||||||
|
FadeIn,
|
||||||
|
FadeOut,
|
||||||
|
SlideInDown,
|
||||||
|
SlideOutDown,
|
||||||
|
} from 'react-native-reanimated';
|
||||||
|
import { useSettings } from '../../../hooks/useSettings';
|
||||||
|
import { introService } from '../../../services/introService';
|
||||||
|
import { toastService } from '../../../services/toastService';
|
||||||
|
|
||||||
|
interface SubmitIntroModalProps {
|
||||||
|
visible: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
currentTime: number;
|
||||||
|
imdbId?: string;
|
||||||
|
season?: number;
|
||||||
|
episode?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses time string (MM:SS or SS) to seconds
|
||||||
|
*/
|
||||||
|
const parseTimeToSeconds = (input: string): number | null => {
|
||||||
|
if (!input) return null;
|
||||||
|
|
||||||
|
// Format: MM:SS
|
||||||
|
if (input.includes(':')) {
|
||||||
|
const parts = input.split(':');
|
||||||
|
if (parts.length !== 2) return null;
|
||||||
|
const mins = parseInt(parts[0], 10);
|
||||||
|
const secs = parseInt(parts[1], 10);
|
||||||
|
if (isNaN(mins) || isParseSecs(secs)) return null;
|
||||||
|
return mins * 60 + secs;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format: Seconds only
|
||||||
|
const secs = parseInt(input, 10);
|
||||||
|
return isNaN(secs) ? null : secs;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isParseSecs = (secs: number) => isNaN(secs) || secs < 0 || secs >= 60;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats seconds to MM:SS
|
||||||
|
*/
|
||||||
|
const formatSecondsToMMSS = (seconds: number): string => {
|
||||||
|
const mins = Math.floor(seconds / 60);
|
||||||
|
const secs = Math.floor(seconds % 60);
|
||||||
|
return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SubmitIntroModal: React.FC<SubmitIntroModalProps> = ({
|
||||||
|
visible,
|
||||||
|
onClose,
|
||||||
|
currentTime,
|
||||||
|
imdbId,
|
||||||
|
season,
|
||||||
|
episode,
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { width } = useWindowDimensions();
|
||||||
|
const { settings } = useSettings();
|
||||||
|
|
||||||
|
const [startTimeStr, setStartTimeStr] = useState('00:00');
|
||||||
|
const [endTimeStr, setEndTimeStr] = useState(formatSecondsToMMSS(currentTime));
|
||||||
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (visible) {
|
||||||
|
setEndTimeStr(formatSecondsToMMSS(currentTime));
|
||||||
|
}
|
||||||
|
}, [visible, currentTime]);
|
||||||
|
|
||||||
|
if (!visible) return null;
|
||||||
|
|
||||||
|
const handleCaptureStart = () => setStartTimeStr(formatSecondsToMMSS(currentTime));
|
||||||
|
const handleCaptureEnd = () => setEndTimeStr(formatSecondsToMMSS(currentTime));
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
const startSec = parseTimeToSeconds(startTimeStr);
|
||||||
|
const endSec = parseTimeToSeconds(endTimeStr);
|
||||||
|
|
||||||
|
if (startSec === null || endSec === null) {
|
||||||
|
toastService.error('Invalid format', 'Please use MM:SS format');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endSec <= startSec) {
|
||||||
|
toastService.warning('Invalid duration', 'End time must be after start time');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!imdbId || season === undefined || episode === undefined) {
|
||||||
|
toastService.error('Missing metadata', 'Could not identify this episode');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsSubmitting(true);
|
||||||
|
try {
|
||||||
|
const success = await introService.submitIntro(
|
||||||
|
settings.introDbApiKey,
|
||||||
|
imdbId,
|
||||||
|
season,
|
||||||
|
episode,
|
||||||
|
startSec,
|
||||||
|
endSec
|
||||||
|
);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
toastService.success(t('player_ui.intro_submitted', { defaultValue: 'Intro submitted successfully' }));
|
||||||
|
onClose();
|
||||||
|
} else {
|
||||||
|
toastService.error(t('player_ui.intro_submit_failed', { defaultValue: 'Failed to submit intro' }));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
toastService.error('Error', 'An unexpected error occurred');
|
||||||
|
} finally {
|
||||||
|
setIsSubmitting(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const startVal = parseTimeToSeconds(startTimeStr);
|
||||||
|
const endVal = parseTimeToSeconds(endTimeStr);
|
||||||
|
const durationSec = (startVal !== null && endVal !== null) ? endVal - startVal : 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={[StyleSheet.absoluteFill, { zIndex: 10000 }]}>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={StyleSheet.absoluteFill}
|
||||||
|
activeOpacity={1}
|
||||||
|
onPress={onClose}
|
||||||
|
>
|
||||||
|
<Animated.View entering={FadeIn} exiting={FadeOut} style={{ flex: 1, backgroundColor: 'rgba(0,0,0,0.4)' }} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
|
||||||
|
<View pointerEvents="box-none" style={localStyles.centeredView}>
|
||||||
|
<Animated.View
|
||||||
|
entering={SlideInDown.duration(300)}
|
||||||
|
exiting={SlideOutDown.duration(250)}
|
||||||
|
style={[localStyles.modalContainer, { width: Math.min(width * 0.85, 380) }]}
|
||||||
|
>
|
||||||
|
<View style={localStyles.header}>
|
||||||
|
<Text style={localStyles.title}>Submit Intro Timestamp</Text>
|
||||||
|
<TouchableOpacity onPress={onClose} style={localStyles.closeButton}>
|
||||||
|
<Ionicons name="close" size={24} color="rgba(255,255,255,0.5)" />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={localStyles.content}>
|
||||||
|
{/* Start Time Input */}
|
||||||
|
<View style={localStyles.inputRow}>
|
||||||
|
<View style={{ flex: 1 }}>
|
||||||
|
<Text style={localStyles.label}>Start Time (MM:SS)</Text>
|
||||||
|
<TextInput
|
||||||
|
style={localStyles.input}
|
||||||
|
value={startTimeStr}
|
||||||
|
onChangeText={setStartTimeStr}
|
||||||
|
placeholder="00:00"
|
||||||
|
placeholderTextColor="rgba(255,255,255,0.3)"
|
||||||
|
keyboardType="numbers-and-punctuation"
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<TouchableOpacity onPress={handleCaptureStart} style={localStyles.captureBtn}>
|
||||||
|
<MaterialIcons name="my-location" size={20} color="white" />
|
||||||
|
<Text style={localStyles.captureText}>Capture</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* End Time Input */}
|
||||||
|
<View style={localStyles.inputRow}>
|
||||||
|
<View style={{ flex: 1 }}>
|
||||||
|
<Text style={localStyles.label}>End Time (MM:SS)</Text>
|
||||||
|
<TextInput
|
||||||
|
style={localStyles.input}
|
||||||
|
value={endTimeStr}
|
||||||
|
onChangeText={setEndTimeStr}
|
||||||
|
placeholder="00:00"
|
||||||
|
placeholderTextColor="rgba(255,255,255,0.3)"
|
||||||
|
keyboardType="numbers-and-punctuation"
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<TouchableOpacity onPress={handleCaptureEnd} style={localStyles.captureBtn}>
|
||||||
|
<MaterialIcons name="my-location" size={20} color="white" />
|
||||||
|
<Text style={localStyles.captureText}>Capture</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Action Buttons */}
|
||||||
|
<View style={localStyles.buttonRow}>
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={onClose}
|
||||||
|
disabled={isSubmitting}
|
||||||
|
style={[localStyles.cancelBtn, isSubmitting && { opacity: 0.5 }]}
|
||||||
|
>
|
||||||
|
<Text style={localStyles.cancelBtnText}>Cancel</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={handleSubmit}
|
||||||
|
disabled={isSubmitting}
|
||||||
|
style={[localStyles.submitBtn, isSubmitting && { opacity: 0.7 }]}
|
||||||
|
>
|
||||||
|
{isSubmitting ? (
|
||||||
|
<ActivityIndicator color="black" />
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<MaterialIcons name="send" size={18} color="black" />
|
||||||
|
<Text style={localStyles.submitBtnText}>Submit</Text>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</Animated.View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const localStyles = StyleSheet.create({
|
||||||
|
centeredView: {
|
||||||
|
...StyleSheet.absoluteFillObject,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
paddingBottom: 40,
|
||||||
|
},
|
||||||
|
modalContainer: {
|
||||||
|
backgroundColor: 'rgba(20, 20, 20, 0.98)',
|
||||||
|
borderRadius: 28,
|
||||||
|
padding: 24,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: 'rgba(255,255,255,0.1)',
|
||||||
|
shadowColor: "#000",
|
||||||
|
shadowOffset: { width: 0, height: 10 },
|
||||||
|
shadowOpacity: 0.5,
|
||||||
|
shadowRadius: 15,
|
||||||
|
elevation: 20,
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginBottom: 24,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
color: 'white',
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: '700',
|
||||||
|
},
|
||||||
|
closeButton: {
|
||||||
|
padding: 4,
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
gap: 20,
|
||||||
|
},
|
||||||
|
inputRow: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'flex-end',
|
||||||
|
gap: 12,
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
color: 'rgba(255,255,255,0.6)',
|
||||||
|
fontSize: 12,
|
||||||
|
marginBottom: 8,
|
||||||
|
fontWeight: '600',
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
letterSpacing: 0.5,
|
||||||
|
},
|
||||||
|
input: {
|
||||||
|
backgroundColor: 'rgba(255,255,255,0.05)',
|
||||||
|
borderRadius: 12,
|
||||||
|
paddingHorizontal: 16,
|
||||||
|
paddingVertical: 12,
|
||||||
|
color: 'white',
|
||||||
|
fontSize: 16,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: 'rgba(255,255,255,0.1)',
|
||||||
|
},
|
||||||
|
captureBtn: {
|
||||||
|
backgroundColor: 'rgba(255,255,255,0.1)',
|
||||||
|
height: 48,
|
||||||
|
paddingHorizontal: 12,
|
||||||
|
borderRadius: 12,
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
gap: 6,
|
||||||
|
},
|
||||||
|
captureText: {
|
||||||
|
color: 'white',
|
||||||
|
fontSize: 13,
|
||||||
|
fontWeight: '600',
|
||||||
|
},
|
||||||
|
summaryBox: {
|
||||||
|
backgroundColor: 'rgba(255,255,255,0.03)',
|
||||||
|
borderRadius: 16,
|
||||||
|
padding: 16,
|
||||||
|
marginTop: 8,
|
||||||
|
},
|
||||||
|
summaryText: {
|
||||||
|
color: 'rgba(255,255,255,0.5)',
|
||||||
|
fontSize: 14,
|
||||||
|
marginBottom: 4,
|
||||||
|
},
|
||||||
|
hintText: {
|
||||||
|
color: 'rgba(255,255,255,0.3)',
|
||||||
|
fontSize: 12,
|
||||||
|
fontStyle: 'italic',
|
||||||
|
},
|
||||||
|
buttonRow: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
gap: 12,
|
||||||
|
marginTop: 12,
|
||||||
|
},
|
||||||
|
cancelBtn: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: 'rgba(255,255,255,0.1)',
|
||||||
|
borderRadius: 16,
|
||||||
|
height: 56,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
cancelBtnText: {
|
||||||
|
color: 'white',
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: '600',
|
||||||
|
},
|
||||||
|
submitBtn: {
|
||||||
|
flex: 2,
|
||||||
|
backgroundColor: 'white',
|
||||||
|
borderRadius: 16,
|
||||||
|
height: 56,
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
gap: 10,
|
||||||
|
},
|
||||||
|
submitBtnText: {
|
||||||
|
color: 'black',
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: '700',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
@ -59,6 +59,8 @@ export interface AppSettings {
|
||||||
// Playback behavior
|
// Playback behavior
|
||||||
alwaysResume: boolean; // If true, resume automatically without prompt when progress < 85%
|
alwaysResume: boolean; // If true, resume automatically without prompt when progress < 85%
|
||||||
skipIntroEnabled: boolean; // Enable/disable Skip Intro overlay (IntroDB)
|
skipIntroEnabled: boolean; // Enable/disable Skip Intro overlay (IntroDB)
|
||||||
|
introSubmitEnabled: boolean; // Enable/disable Intro Submission
|
||||||
|
introDbApiKey: string; // API Key for IntroDB submission
|
||||||
// Downloads
|
// Downloads
|
||||||
enableDownloads: boolean; // Show Downloads tab and enable saving streams
|
enableDownloads: boolean; // Show Downloads tab and enable saving streams
|
||||||
// Theme settings
|
// Theme settings
|
||||||
|
|
@ -147,6 +149,8 @@ export const DEFAULT_SETTINGS: AppSettings = {
|
||||||
// Playback behavior defaults
|
// Playback behavior defaults
|
||||||
alwaysResume: true,
|
alwaysResume: true,
|
||||||
skipIntroEnabled: true,
|
skipIntroEnabled: true,
|
||||||
|
introSubmitEnabled: false,
|
||||||
|
introDbApiKey: '',
|
||||||
// Downloads
|
// Downloads
|
||||||
enableDownloads: false,
|
enableDownloads: false,
|
||||||
useExternalPlayerForDownloads: false,
|
useExternalPlayerForDownloads: false,
|
||||||
|
|
|
||||||
|
|
@ -417,7 +417,12 @@
|
||||||
"timing_offset": "Timing Offset (s)",
|
"timing_offset": "Timing Offset (s)",
|
||||||
"visual_sync": "Visual Sync",
|
"visual_sync": "Visual Sync",
|
||||||
"timing_hint": "Nudge subtitles earlier (-) or later (+) to sync if needed.",
|
"timing_hint": "Nudge subtitles earlier (-) or later (+) to sync if needed.",
|
||||||
"reset_defaults": "Reset to defaults"
|
"reset_defaults": "Reset to defaults",
|
||||||
|
"mark_intro_start": "Mark Intro Start",
|
||||||
|
"mark_intro_end": "Mark Intro End",
|
||||||
|
"intro_start_marked": "Intro start marked",
|
||||||
|
"intro_submitted": "Intro submitted successfully",
|
||||||
|
"intro_submit_failed": "Failed to submit intro"
|
||||||
},
|
},
|
||||||
"downloads": {
|
"downloads": {
|
||||||
"title": "Downloads",
|
"title": "Downloads",
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useState, useCallback, useMemo, useRef, useEffect } from 'react';
|
import React, { useState, useCallback, useMemo, useRef, useEffect } from 'react';
|
||||||
import { View, StyleSheet, ScrollView, StatusBar, Platform, Text, TouchableOpacity, Dimensions } from 'react-native';
|
import { View, StyleSheet, ScrollView, StatusBar, Platform, Text, TouchableOpacity, Dimensions, TextInput } from 'react-native';
|
||||||
import { useNavigation, useFocusEffect } from '@react-navigation/native';
|
import { useNavigation, useFocusEffect } from '@react-navigation/native';
|
||||||
import { NavigationProp } from '@react-navigation/native';
|
import { NavigationProp } from '@react-navigation/native';
|
||||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
|
|
@ -13,6 +13,7 @@ import { MaterialIcons } from '@expo/vector-icons';
|
||||||
import { BottomSheetModal, BottomSheetScrollView, BottomSheetBackdrop } from '@gorhom/bottom-sheet';
|
import { BottomSheetModal, BottomSheetScrollView, BottomSheetBackdrop } from '@gorhom/bottom-sheet';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { SvgXml } from 'react-native-svg';
|
import { SvgXml } from 'react-native-svg';
|
||||||
|
import { toastService } from '../../services/toastService';
|
||||||
|
|
||||||
const { width } = Dimensions.get('window');
|
const { width } = Dimensions.get('window');
|
||||||
|
|
||||||
|
|
@ -77,6 +78,16 @@ export const PlaybackSettingsContent: React.FC<PlaybackSettingsContentProps> = (
|
||||||
const config = useRealtimeConfig();
|
const config = useRealtimeConfig();
|
||||||
|
|
||||||
const [introDbLogoXml, setIntroDbLogoXml] = useState<string | null>(null);
|
const [introDbLogoXml, setIntroDbLogoXml] = useState<string | null>(null);
|
||||||
|
const [apiKeyInput, setApiKeyInput] = useState(settings?.introDbApiKey || '');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setApiKeyInput(settings?.introDbApiKey || '');
|
||||||
|
}, [settings?.introDbApiKey]);
|
||||||
|
|
||||||
|
const handleApiKeySubmit = () => {
|
||||||
|
updateSetting('introDbApiKey', apiKeyInput);
|
||||||
|
toastService.success(t('settings.items.api_key_saved', { defaultValue: 'API Key Saved' }));
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
|
|
@ -225,6 +236,49 @@ export const PlaybackSettingsContent: React.FC<PlaybackSettingsContentProps> = (
|
||||||
/>
|
/>
|
||||||
</SettingsCard>
|
</SettingsCard>
|
||||||
|
|
||||||
|
{/* IntroDB Contribution Section */}
|
||||||
|
<SettingsCard title={t('settings.sections.introdb_contribution', { defaultValue: 'IntroDB Contribution' })} isTablet={isTablet}>
|
||||||
|
<SettingItem
|
||||||
|
title={t('settings.items.enable_intro_submission', { defaultValue: 'Enable Intro Submission' })}
|
||||||
|
description={t('settings.items.enable_intro_submission_desc', { defaultValue: 'Contribute timestamps to the community' })}
|
||||||
|
icon="flag"
|
||||||
|
renderControl={() => (
|
||||||
|
<CustomSwitch
|
||||||
|
value={settings?.introSubmitEnabled ?? false}
|
||||||
|
onValueChange={(value) => updateSetting('introSubmitEnabled', value)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
isLast={!settings?.introSubmitEnabled}
|
||||||
|
isTablet={isTablet}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{settings?.introSubmitEnabled && (
|
||||||
|
<View style={styles.inputContainer}>
|
||||||
|
<Text style={styles.inputLabel}>
|
||||||
|
{t('settings.items.introdb_api_key', { defaultValue: 'INTRODB API KEY' })}
|
||||||
|
</Text>
|
||||||
|
<View style={styles.apiKeyRow}>
|
||||||
|
<TextInput
|
||||||
|
style={[styles.input, { flex: 1, marginRight: 10, color: currentTheme.colors.highEmphasis }]}
|
||||||
|
value={apiKeyInput}
|
||||||
|
onChangeText={setApiKeyInput}
|
||||||
|
placeholder="Enter your API key"
|
||||||
|
placeholderTextColor={currentTheme.colors.mediumEmphasis}
|
||||||
|
autoCapitalize="none"
|
||||||
|
autoCorrect={false}
|
||||||
|
secureTextEntry
|
||||||
|
/>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.confirmButton}
|
||||||
|
onPress={handleApiKeySubmit}
|
||||||
|
>
|
||||||
|
<MaterialIcons name="check" size={24} color="black" />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</SettingsCard>
|
||||||
|
|
||||||
{/* Audio & Subtitle Preferences */}
|
{/* Audio & Subtitle Preferences */}
|
||||||
<SettingsCard title={t('settings.sections.audio_subtitles')} isTablet={isTablet}>
|
<SettingsCard title={t('settings.sections.audio_subtitles')} isTablet={isTablet}>
|
||||||
<SettingItem
|
<SettingItem
|
||||||
|
|
@ -542,6 +596,39 @@ const styles = StyleSheet.create({
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
color: 'rgba(255,255,255,0.5)',
|
color: 'rgba(255,255,255,0.5)',
|
||||||
},
|
},
|
||||||
|
inputContainer: {
|
||||||
|
paddingHorizontal: 16,
|
||||||
|
paddingBottom: 16,
|
||||||
|
paddingTop: 8,
|
||||||
|
},
|
||||||
|
inputLabel: {
|
||||||
|
fontSize: 12,
|
||||||
|
color: 'rgba(255,255,255,0.5)',
|
||||||
|
marginBottom: 8,
|
||||||
|
marginLeft: 4,
|
||||||
|
},
|
||||||
|
input: {
|
||||||
|
backgroundColor: 'rgba(255,255,255,0.08)',
|
||||||
|
borderRadius: 12,
|
||||||
|
paddingHorizontal: 16,
|
||||||
|
paddingVertical: 12,
|
||||||
|
color: 'white',
|
||||||
|
fontSize: 14,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: 'rgba(255,255,255,0.1)',
|
||||||
|
},
|
||||||
|
apiKeyRow: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
confirmButton: {
|
||||||
|
backgroundColor: 'white',
|
||||||
|
borderRadius: 12,
|
||||||
|
width: 48,
|
||||||
|
height: 48,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default PlaybackSettingsScreen;
|
export default PlaybackSettingsScreen;
|
||||||
|
|
|
||||||
|
|
@ -187,6 +187,44 @@ async function fetchFromIntroDb(imdbId: string, season: number, episode: number)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submits an intro timestamp to IntroDB
|
||||||
|
*/
|
||||||
|
export async function submitIntro(
|
||||||
|
apiKey: string,
|
||||||
|
imdbId: string,
|
||||||
|
season: number,
|
||||||
|
episode: number,
|
||||||
|
startTime: number, // in seconds
|
||||||
|
endTime: number // in seconds
|
||||||
|
): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
if (!apiKey) {
|
||||||
|
logger.warn('[IntroService] Missing API key for submission');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await axios.post(`${INTRODB_API_URL}/submit`, {
|
||||||
|
imdb_id: imdbId,
|
||||||
|
season,
|
||||||
|
episode,
|
||||||
|
start_ms: Math.round(startTime * 1000),
|
||||||
|
end_ms: Math.round(endTime * 1000),
|
||||||
|
}, {
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${apiKey}`,
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
timeout: 10000,
|
||||||
|
});
|
||||||
|
|
||||||
|
return response.status === 200 || response.status === 201;
|
||||||
|
} catch (error: any) {
|
||||||
|
logger.error('[IntroService] Error submitting intro:', error?.response?.data || error?.message || error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches skip intervals (intro, outro, recap) from available providers
|
* Fetches skip intervals (intro, outro, recap) from available providers
|
||||||
*/
|
*/
|
||||||
|
|
@ -266,7 +304,8 @@ export async function getIntroTimestamps(
|
||||||
|
|
||||||
export const introService = {
|
export const introService = {
|
||||||
getIntroTimestamps,
|
getIntroTimestamps,
|
||||||
getSkipTimes
|
getSkipTimes,
|
||||||
|
submitIntro
|
||||||
};
|
};
|
||||||
|
|
||||||
export default introService;
|
export default introService;
|
||||||
|
|
|
||||||
45
src/types/react-native-background-downloader.d.ts
vendored
Normal file
45
src/types/react-native-background-downloader.d.ts
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
declare module '@kesha-antonov/react-native-background-downloader' {
|
||||||
|
export interface DownloadTask {
|
||||||
|
id: string;
|
||||||
|
percent: number;
|
||||||
|
bytesWritten: number;
|
||||||
|
totalBytes: number;
|
||||||
|
state: 'DOWNLOADING' | 'PAUSED' | 'DONE' | 'STOPPED' | 'FAILED';
|
||||||
|
pause(): void;
|
||||||
|
resume(): void;
|
||||||
|
stop(): void;
|
||||||
|
start(): void;
|
||||||
|
begin(handler: (expectedBytes: number) => void): DownloadTask;
|
||||||
|
progress(handler: (percent: number, bytesWritten: number, totalBytes: number) => void): DownloadTask;
|
||||||
|
done(handler: () => void): DownloadTask;
|
||||||
|
error(handler: (error: any) => void): DownloadTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const directories: {
|
||||||
|
documents: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function download(options: { id: string; url: string; destination: string; headers?: Record<string, string>; metadata?: any }): DownloadTask;
|
||||||
|
|
||||||
|
export function checkForExistingDownloads(): Promise<DownloadTask[]>;
|
||||||
|
|
||||||
|
// Legacy exports to match library behavior
|
||||||
|
export function completeHandler(id: string): void;
|
||||||
|
|
||||||
|
export function createDownloadTask(options: { id: string; url: string; destination: string; headers?: Record<string, string>; metadata?: any }): DownloadTask;
|
||||||
|
|
||||||
|
export function getExistingDownloadTasks(): Promise<DownloadTask[]>;
|
||||||
|
|
||||||
|
const RNBackgroundDownloader: {
|
||||||
|
download(options: { id: string; url: string; destination: string; headers?: Record<string, string>; metadata?: any }): DownloadTask;
|
||||||
|
checkForExistingDownloads(): Promise<DownloadTask[]>;
|
||||||
|
directories: {
|
||||||
|
documents: string;
|
||||||
|
};
|
||||||
|
completeHandler(id: string): void;
|
||||||
|
createDownloadTask(options: { id: string; url: string; destination: string; headers?: Record<string, string>; metadata?: any }): DownloadTask;
|
||||||
|
getExistingDownloadTasks(): Promise<DownloadTask[]>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RNBackgroundDownloader;
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue