mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-01-15 23:59:08 +00:00
Compare commits
3 Commits
fix/github
...
chore/upda
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d89449b1bf | ||
|
|
d6ccd6f756 | ||
|
|
05d4154cd6 |
24
.github/workflows/build-apps.yml
vendored
24
.github/workflows/build-apps.yml
vendored
@@ -156,7 +156,7 @@ jobs:
|
|||||||
|
|
||||||
build-ios-phone:
|
build-ios-phone:
|
||||||
if: (!contains(github.event.head_commit.message, '[skip ci]') && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'streamyfin/streamyfin'))
|
if: (!contains(github.event.head_commit.message, '[skip ci]') && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'streamyfin/streamyfin'))
|
||||||
runs-on: macos-26
|
runs-on: macos-15
|
||||||
name: 🍎 Build iOS IPA (Phone)
|
name: 🍎 Build iOS IPA (Phone)
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
@@ -170,11 +170,6 @@ jobs:
|
|||||||
submodules: recursive
|
submodules: recursive
|
||||||
show-progress: false
|
show-progress: false
|
||||||
|
|
||||||
- name: 🟢 Setup Node.js
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: "24"
|
|
||||||
|
|
||||||
- name: 🍞 Setup Bun
|
- name: 🍞 Setup Bun
|
||||||
uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76 # v2.0.2
|
uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76 # v2.0.2
|
||||||
with:
|
with:
|
||||||
@@ -196,11 +191,6 @@ jobs:
|
|||||||
- name: 🛠️ Generate project files
|
- name: 🛠️ Generate project files
|
||||||
run: bun run prebuild
|
run: bun run prebuild
|
||||||
|
|
||||||
- name: 🔧 Setup Xcode
|
|
||||||
uses: maxim-lobanov/setup-xcode@v1
|
|
||||||
with:
|
|
||||||
xcode-version: "26.0.1"
|
|
||||||
|
|
||||||
- name: 🏗️ Setup EAS
|
- name: 🏗️ Setup EAS
|
||||||
uses: expo/expo-github-action@main
|
uses: expo/expo-github-action@main
|
||||||
with:
|
with:
|
||||||
@@ -229,7 +219,7 @@ jobs:
|
|||||||
# Disabled for now - uncomment when ready to build iOS TV
|
# Disabled for now - uncomment when ready to build iOS TV
|
||||||
# build-ios-tv:
|
# build-ios-tv:
|
||||||
# if: (!contains(github.event.head_commit.message, '[skip ci]') && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'streamyfin/streamyfin'))
|
# if: (!contains(github.event.head_commit.message, '[skip ci]') && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'streamyfin/streamyfin'))
|
||||||
# runs-on: macos-26
|
# runs-on: macos-15
|
||||||
# name: 🍎 Build iOS IPA (TV)
|
# name: 🍎 Build iOS IPA (TV)
|
||||||
# permissions:
|
# permissions:
|
||||||
# contents: read
|
# contents: read
|
||||||
@@ -243,11 +233,6 @@ jobs:
|
|||||||
# submodules: recursive
|
# submodules: recursive
|
||||||
# show-progress: false
|
# show-progress: false
|
||||||
#
|
#
|
||||||
# - name: 🟢 Setup Node.js
|
|
||||||
# uses: actions/setup-node@v4
|
|
||||||
# with:
|
|
||||||
# node-version: '20'
|
|
||||||
#
|
|
||||||
# - name: 🍞 Setup Bun
|
# - name: 🍞 Setup Bun
|
||||||
# uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76 # v2.0.2
|
# uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76 # v2.0.2
|
||||||
# with:
|
# with:
|
||||||
@@ -269,11 +254,6 @@ jobs:
|
|||||||
# - name: 🛠️ Generate project files
|
# - name: 🛠️ Generate project files
|
||||||
# run: bun run prebuild:tv
|
# run: bun run prebuild:tv
|
||||||
#
|
#
|
||||||
# - name: 🔧 Setup Xcode
|
|
||||||
# uses: maxim-lobanov/setup-xcode@v1
|
|
||||||
# with:
|
|
||||||
# xcode-version: '26.0.1'
|
|
||||||
#
|
|
||||||
# - name: 🏗️ Setup EAS
|
# - name: 🏗️ Setup EAS
|
||||||
# uses: expo/expo-github-action@main
|
# uses: expo/expo-github-action@main
|
||||||
# with:
|
# with:
|
||||||
|
|||||||
177
.vscode/settings.json
vendored
177
.vscode/settings.json
vendored
@@ -1,25 +1,178 @@
|
|||||||
{
|
{
|
||||||
|
// ==========================================
|
||||||
|
// FORMATTING & LINTING
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
|
// Biome as default formatter
|
||||||
"editor.defaultFormatter": "biomejs.biome",
|
"editor.defaultFormatter": "biomejs.biome",
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
"editor.codeActionsOnSave": {
|
"editor.formatOnPaste": true,
|
||||||
"source.fixAll.biome": "explicit"
|
"editor.formatOnType": false,
|
||||||
|
|
||||||
|
// Language-specific formatters
|
||||||
|
"[javascript]": {
|
||||||
|
"editor.defaultFormatter": "biomejs.biome",
|
||||||
|
"editor.formatOnSave": true
|
||||||
},
|
},
|
||||||
"[typescript]": {
|
"[typescript]": {
|
||||||
"editor.defaultFormatter": "biomejs.biome"
|
"editor.defaultFormatter": "biomejs.biome",
|
||||||
|
"editor.formatOnSave": true
|
||||||
},
|
},
|
||||||
"[typescriptreact]": {
|
"[typescriptreact]": {
|
||||||
"editor.defaultFormatter": "biomejs.biome"
|
"editor.defaultFormatter": "biomejs.biome",
|
||||||
},
|
"editor.formatOnSave": true
|
||||||
"[javascript]": {
|
|
||||||
"editor.defaultFormatter": "biomejs.biome"
|
|
||||||
},
|
},
|
||||||
"[javascriptreact]": {
|
"[javascriptreact]": {
|
||||||
"editor.defaultFormatter": "biomejs.biome"
|
"editor.defaultFormatter": "biomejs.biome",
|
||||||
|
"editor.formatOnSave": true
|
||||||
},
|
},
|
||||||
"[json]": {
|
"[json]": {
|
||||||
"editor.defaultFormatter": "biomejs.biome"
|
"editor.defaultFormatter": "biomejs.biome",
|
||||||
|
"editor.formatOnSave": true
|
||||||
},
|
},
|
||||||
"typescript.tsdk": "node_modules/typescript/lib",
|
"[jsonc]": {
|
||||||
"typescript.enablePromptUseWorkspaceTsdk": true,
|
"editor.defaultFormatter": "biomejs.biome",
|
||||||
"editor.formatOnSaveMode": "file"
|
"editor.formatOnSave": true
|
||||||
|
},
|
||||||
|
"[swift]": {
|
||||||
|
"editor.insertSpaces": true,
|
||||||
|
"editor.tabSize": 2
|
||||||
|
},
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// TYPESCRIPT & JAVASCRIPT
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
|
// TypeScript performance optimizations
|
||||||
|
"typescript.preferences.includePackageJsonAutoImports": "auto",
|
||||||
|
"typescript.suggest.autoImports": true,
|
||||||
|
"typescript.updateImportsOnFileMove.enabled": "always",
|
||||||
|
"typescript.preferences.preferTypeOnlyAutoImports": true,
|
||||||
|
"typescript.preferences.importModuleSpecifier": "relative",
|
||||||
|
"typescript.preferences.includeCompletionsForImportStatements": true,
|
||||||
|
"typescript.preferences.includeCompletionsWithSnippetText": true,
|
||||||
|
|
||||||
|
// JavaScript settings
|
||||||
|
"javascript.preferences.importModuleSpecifier": "relative",
|
||||||
|
"javascript.suggest.autoImports": true,
|
||||||
|
"javascript.updateImportsOnFileMove.enabled": "always",
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// REACT NATIVE & EXPO
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
|
// File associations for React Native
|
||||||
|
"files.associations": {
|
||||||
|
"*.expo.ts": "typescript",
|
||||||
|
"*.expo.tsx": "typescriptreact",
|
||||||
|
"*.expo.js": "javascript",
|
||||||
|
"*.expo.jsx": "javascriptreact",
|
||||||
|
"metro.config.js": "javascript",
|
||||||
|
"babel.config.js": "javascript",
|
||||||
|
"app.config.js": "javascript",
|
||||||
|
"eas.json": "jsonc"
|
||||||
|
},
|
||||||
|
|
||||||
|
// React Native specific settings
|
||||||
|
"emmet.includeLanguages": {
|
||||||
|
"typescriptreact": "html",
|
||||||
|
"javascriptreact": "html"
|
||||||
|
},
|
||||||
|
"emmet.triggerExpansionOnTab": true,
|
||||||
|
|
||||||
|
// Exclude build directories from search
|
||||||
|
"search.exclude": {
|
||||||
|
"**/node_modules": true
|
||||||
|
},
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// EDITOR PERFORMANCE & UX
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
|
// Performance optimizations
|
||||||
|
"editor.largeFileOptimizations": true,
|
||||||
|
"files.watcherExclude": {
|
||||||
|
"**/.git/objects/**": true,
|
||||||
|
"**/.git/subtree-cache/**": true,
|
||||||
|
"**/node_modules/**": true,
|
||||||
|
"**/.expo/**": true,
|
||||||
|
"**/ios/**": true,
|
||||||
|
"**/android/**": true,
|
||||||
|
"**/build/**": true,
|
||||||
|
"**/dist/**": true
|
||||||
|
},
|
||||||
|
|
||||||
|
// Better editor behavior
|
||||||
|
"editor.suggestSelection": "first",
|
||||||
|
"editor.quickSuggestions": {
|
||||||
|
"strings": true,
|
||||||
|
"comments": true,
|
||||||
|
"other": true
|
||||||
|
},
|
||||||
|
"editor.snippetSuggestions": "top",
|
||||||
|
"editor.tabCompletion": "on",
|
||||||
|
"editor.wordBasedSuggestions": "off",
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// TERMINAL & DEVELOPMENT
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
|
// Terminal settings for Bun (Windows-specific)
|
||||||
|
"terminal.integrated.profiles.windows": {
|
||||||
|
"Command Prompt": {
|
||||||
|
"path": "C:\\Windows\\System32\\cmd.exe",
|
||||||
|
"env": {
|
||||||
|
"PATH": "${env:PATH};./node_modules/.bin"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// WORKSPACE & NAVIGATION
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
|
// Better workspace navigation
|
||||||
|
"explorer.fileNesting.enabled": true,
|
||||||
|
"explorer.fileNesting.expand": false,
|
||||||
|
"explorer.fileNesting.patterns": {
|
||||||
|
"*.ts": "${capture}.js",
|
||||||
|
"*.tsx": "${capture}.js",
|
||||||
|
"*.js": "${capture}.js,${capture}.js.map,${capture}.min.js,${capture}.d.ts",
|
||||||
|
"*.jsx": "${capture}.js",
|
||||||
|
"package.json": "package-lock.json,yarn.lock,bun.lock,bun.lockb,.yarnrc,.yarnrc.yml",
|
||||||
|
"tsconfig.json": "tsconfig.*.json",
|
||||||
|
".env": ".env.*",
|
||||||
|
"app.json": "app.config.js,eas.json,expo-env.d.ts",
|
||||||
|
"README.md": "LICENSE.txt,SECURITY.md,CODE_OF_CONDUCT.md,CONTRIBUTING.md"
|
||||||
|
},
|
||||||
|
|
||||||
|
// Better breadcrumbs and navigation
|
||||||
|
"breadcrumbs.enabled": true,
|
||||||
|
"outline.showVariables": true,
|
||||||
|
"outline.showConstants": true,
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// GIT & VERSION CONTROL
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
|
// Git integration
|
||||||
|
"git.autofetch": true,
|
||||||
|
"git.enableSmartCommit": true,
|
||||||
|
"git.confirmSync": false,
|
||||||
|
"git.ignoreLimitWarning": true,
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// CODE QUALITY & ERRORS
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
|
// Better error detection
|
||||||
|
"typescript.validate.enable": true,
|
||||||
|
"javascript.validate.enable": true,
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.fixAll": "explicit",
|
||||||
|
"source.organizeImports": "explicit"
|
||||||
|
},
|
||||||
|
|
||||||
|
// Problem matcher for better error display
|
||||||
|
"typescript.tsc.autoDetect": "on"
|
||||||
}
|
}
|
||||||
|
|||||||
104
PR_DESCRIPTION.md
Normal file
104
PR_DESCRIPTION.md
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
# 📦 chore(deps): upgrade deps and fix iOS build
|
||||||
|
|
||||||
|
## 🔖 Summary
|
||||||
|
|
||||||
|
Upgrade runtime dependencies (react-i18next, react-native-worklets), improve TypeScript type safety for screen orientation handling, and fix iOS build error from develop branch.
|
||||||
|
|
||||||
|
## 🏷️ Ticket / Issue
|
||||||
|
|
||||||
|
None
|
||||||
|
|
||||||
|
## 🛠️ What's Changed
|
||||||
|
|
||||||
|
- **Type**: chore
|
||||||
|
- **Scope**: deps, refactor
|
||||||
|
- **Summary**: Upgraded react-i18next and react-native-worklets to latest versions, improved type safety for screen orientation APIs, and fixed iOS build error
|
||||||
|
|
||||||
|
## 📋 Details
|
||||||
|
|
||||||
|
This PR upgrades key runtime dependencies, improves type safety across orientation-related code, and fixes an iOS build error present in the develop branch.
|
||||||
|
|
||||||
|
### Changes Made
|
||||||
|
|
||||||
|
**iOS Build Fix:**
|
||||||
|
|
||||||
|
- Removed root-level `icon` field from `app.json` to resolve Xcode build failure
|
||||||
|
- The iOS-specific liquid glass icon format was causing asset catalog compilation errors
|
||||||
|
- Build error: "None of the input catalogs contained a matching stickers icon set or app icon set named 'icon-ios-liquid-glass'"
|
||||||
|
- Removing the root icon allows the iOS-specific icon to be processed correctly
|
||||||
|
|
||||||
|
**Runtime Dependencies Updated:**
|
||||||
|
|
||||||
|
- `react-i18next`: 15.4.0 → 16.3.3 (latest internationalization features and improvements)
|
||||||
|
- `react-native-worklets`: 0.5.1 → 0.6.1 (performance optimizations)
|
||||||
|
- `typescript`: ^5.9.3 → ~5.9.3 (stricter version constraint for stability)
|
||||||
|
|
||||||
|
**TypeScript Type Safety Improvements:**
|
||||||
|
|
||||||
|
- Added explicit type annotations to orientation event handlers in `useOrientation` hook
|
||||||
|
- Fixed `OrientationLock` type references to use proper enum types from `.tv.ts` module
|
||||||
|
- Improved type consistency by using `Record<number, string>` for orientation enum mappings
|
||||||
|
- Reorganized imports to follow consistent pattern (third-party → internal)
|
||||||
|
|
||||||
|
### ⚠️ Breaking Changes
|
||||||
|
|
||||||
|
None
|
||||||
|
|
||||||
|
### 🔐 Security & Privacy Impact
|
||||||
|
|
||||||
|
None
|
||||||
|
|
||||||
|
### ⚡ Performance Impact
|
||||||
|
|
||||||
|
- react-native-worklets 0.6.1 includes performance optimizations for worklet execution
|
||||||
|
- No runtime behavior changes - orientation handling works exactly as before
|
||||||
|
|
||||||
|
## ✅ Checklist
|
||||||
|
|
||||||
|
- [x] I've read the [contribution guidelines](CONTRIBUTING.md)
|
||||||
|
- [x] Code follows project style and passes lint/format (bun scripts)
|
||||||
|
- [x] Type checks pass (tsc/biome/etc.)
|
||||||
|
- [x] Docs updated (README/ADR/usage/API) - N/A for dependency updates
|
||||||
|
- [x] No secrets/credentials included; env vars documented
|
||||||
|
- [x] Release notes/CHANGELOG entry added (if applicable) - N/A for internal tooling
|
||||||
|
- [x] Verified locally that changes behave as expected
|
||||||
|
|
||||||
|
## 🔍 Testing Instructions
|
||||||
|
|
||||||
|
1. Checkout branch: `git fetch origin && git checkout chore/update-dev-dependencies`
|
||||||
|
2. Install dependencies: `bun install`
|
||||||
|
3. Run type checks: `bun run typecheck`
|
||||||
|
- [x] Verify no TypeScript errors
|
||||||
|
4. Run linter: `bun run lint`
|
||||||
|
5. Test orientation features:
|
||||||
|
- [x] Screen rotation works correctly on mobile
|
||||||
|
- [x] Orientation lock/unlock functions work
|
||||||
|
- [x] TV platform continues to use landscape orientation
|
||||||
|
6. Test iOS build:
|
||||||
|
- [x] iOS build completes successfully without icon-related errors
|
||||||
|
- [x] App icons display correctly on iOS devices
|
||||||
|
7. Test i18n functionality:
|
||||||
|
- [x] Language switching works
|
||||||
|
- [x] Translations load correctly
|
||||||
|
8. Verification steps:
|
||||||
|
- [x] All commands complete successfully
|
||||||
|
- [x] No runtime errors
|
||||||
|
- [x] Orientation behavior unchanged
|
||||||
|
|
||||||
|
## ⚙️ Deployment Notes
|
||||||
|
|
||||||
|
- No deployment impact - standard dependency updates
|
||||||
|
- Team members will need to run `bun install` to update dependencies
|
||||||
|
- No configuration changes required
|
||||||
|
|
||||||
|
## 📝 Additional Notes
|
||||||
|
|
||||||
|
**Why these updates?**
|
||||||
|
|
||||||
|
- **react-i18next 16.3.3**: Latest stable version with improved TypeScript support and performance
|
||||||
|
- **react-native-worklets 0.6.1**: Bug fixes and performance improvements for animation worklets
|
||||||
|
- **TypeScript constraint change**: Using `~5.9.3` instead of `^5.9.3` to avoid unexpected minor version updates
|
||||||
|
|
||||||
|
**Type safety improvements:**
|
||||||
|
|
||||||
|
The TypeScript changes fix incorrect usage of `OrientationLock` as a type when it was actually a runtime value. Now we properly import the enum type from the `.tv.ts` module for type checking while keeping runtime behavior identical.
|
||||||
1
app.json
1
app.json
@@ -4,7 +4,6 @@
|
|||||||
"slug": "streamyfin",
|
"slug": "streamyfin",
|
||||||
"version": "0.47.1",
|
"version": "0.47.1",
|
||||||
"orientation": "default",
|
"orientation": "default",
|
||||||
"icon": "./assets/images/icon.png",
|
|
||||||
"scheme": "streamyfin",
|
"scheme": "streamyfin",
|
||||||
"userInterfaceStyle": "dark",
|
"userInterfaceStyle": "dark",
|
||||||
"jsEngine": "hermes",
|
"jsEngine": "hermes",
|
||||||
|
|||||||
10
bun.lock
10
bun.lock
@@ -50,7 +50,7 @@
|
|||||||
"patch-package": "^8.0.0",
|
"patch-package": "^8.0.0",
|
||||||
"react": "19.1.0",
|
"react": "19.1.0",
|
||||||
"react-dom": "19.1.0",
|
"react-dom": "19.1.0",
|
||||||
"react-i18next": "^15.4.0",
|
"react-i18next": "16.3.3",
|
||||||
"react-native": "npm:react-native-tvos@0.81.5-1",
|
"react-native": "npm:react-native-tvos@0.81.5-1",
|
||||||
"react-native-awesome-slider": "^2.9.0",
|
"react-native-awesome-slider": "^2.9.0",
|
||||||
"react-native-bottom-tabs": "^1.0.2",
|
"react-native-bottom-tabs": "^1.0.2",
|
||||||
@@ -78,7 +78,7 @@
|
|||||||
"react-native-video": "6.16.1",
|
"react-native-video": "6.16.1",
|
||||||
"react-native-volume-manager": "^2.0.8",
|
"react-native-volume-manager": "^2.0.8",
|
||||||
"react-native-web": "^0.21.0",
|
"react-native-web": "^0.21.0",
|
||||||
"react-native-worklets": "0.5.1",
|
"react-native-worklets": "0.6.1",
|
||||||
"sonner-native": "^0.21.0",
|
"sonner-native": "^0.21.0",
|
||||||
"tailwindcss": "3.3.2",
|
"tailwindcss": "3.3.2",
|
||||||
"use-debounce": "^10.0.4",
|
"use-debounce": "^10.0.4",
|
||||||
@@ -98,7 +98,7 @@
|
|||||||
"husky": "^9.1.7",
|
"husky": "^9.1.7",
|
||||||
"lint-staged": "^16.2.6",
|
"lint-staged": "^16.2.6",
|
||||||
"react-test-renderer": "19.1.1",
|
"react-test-renderer": "19.1.1",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "~5.9.3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -1609,7 +1609,7 @@
|
|||||||
|
|
||||||
"react-freeze": ["react-freeze@1.0.4", "", { "peerDependencies": { "react": ">=17.0.0" } }, "sha512-r4F0Sec0BLxWicc7HEyo2x3/2icUTrRmDjaaRyzzn+7aDyFZliszMDOgLVwSnQnYENOlL1o569Ze2HZefk8clA=="],
|
"react-freeze": ["react-freeze@1.0.4", "", { "peerDependencies": { "react": ">=17.0.0" } }, "sha512-r4F0Sec0BLxWicc7HEyo2x3/2icUTrRmDjaaRyzzn+7aDyFZliszMDOgLVwSnQnYENOlL1o569Ze2HZefk8clA=="],
|
||||||
|
|
||||||
"react-i18next": ["react-i18next@15.7.4", "", { "dependencies": { "@babel/runtime": "^7.27.6", "html-parse-stringify": "^3.0.1" }, "peerDependencies": { "i18next": ">= 23.4.0", "react": ">= 16.8.0", "typescript": "^5" }, "optionalPeers": ["typescript"] }, "sha512-nyU8iKNrI5uDJch0z9+Y5XEr34b0wkyYj3Rp+tfbahxtlswxSCjcUL9H0nqXo9IR3/t5Y5PKIA3fx3MfUyR9Xw=="],
|
"react-i18next": ["react-i18next@16.3.3", "", { "dependencies": { "@babel/runtime": "^7.27.6", "html-parse-stringify": "^3.0.1", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "i18next": ">= 25.6.2", "react": ">= 16.8.0", "typescript": "^5" }, "optionalPeers": ["typescript"] }, "sha512-IaY2W+ueVd/fe7H6Wj2S4bTuLNChnajFUlZFfCTrTHWzGcOrUHlVzW55oXRSl+J51U8Onn6EvIhQ+Bar9FUcjw=="],
|
||||||
|
|
||||||
"react-is": ["react-is@19.2.0", "", {}, "sha512-x3Ax3kNSMIIkyVYhWPyO09bu0uttcAIoecO/um/rKGQ4EltYWVYtyiGkS/3xMynrbVQdS69Jhlv8FXUEZehlzA=="],
|
"react-is": ["react-is@19.2.0", "", {}, "sha512-x3Ax3kNSMIIkyVYhWPyO09bu0uttcAIoecO/um/rKGQ4EltYWVYtyiGkS/3xMynrbVQdS69Jhlv8FXUEZehlzA=="],
|
||||||
|
|
||||||
@@ -1671,7 +1671,7 @@
|
|||||||
|
|
||||||
"react-native-web": ["react-native-web@0.21.2", "", { "dependencies": { "@babel/runtime": "^7.18.6", "@react-native/normalize-colors": "^0.74.1", "fbjs": "^3.0.4", "inline-style-prefixer": "^7.0.1", "memoize-one": "^6.0.0", "nullthrows": "^1.1.1", "postcss-value-parser": "^4.2.0", "styleq": "^0.1.3" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-SO2t9/17zM4iEnFvlu2DA9jqNbzNhoUP+AItkoCOyFmDMOhUnBBznBDCYN92fGdfAkfQlWzPoez6+zLxFNsZEg=="],
|
"react-native-web": ["react-native-web@0.21.2", "", { "dependencies": { "@babel/runtime": "^7.18.6", "@react-native/normalize-colors": "^0.74.1", "fbjs": "^3.0.4", "inline-style-prefixer": "^7.0.1", "memoize-one": "^6.0.0", "nullthrows": "^1.1.1", "postcss-value-parser": "^4.2.0", "styleq": "^0.1.3" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-SO2t9/17zM4iEnFvlu2DA9jqNbzNhoUP+AItkoCOyFmDMOhUnBBznBDCYN92fGdfAkfQlWzPoez6+zLxFNsZEg=="],
|
||||||
|
|
||||||
"react-native-worklets": ["react-native-worklets@0.5.1", "", { "dependencies": { "@babel/plugin-transform-arrow-functions": "^7.0.0-0", "@babel/plugin-transform-class-properties": "^7.0.0-0", "@babel/plugin-transform-classes": "^7.0.0-0", "@babel/plugin-transform-nullish-coalescing-operator": "^7.0.0-0", "@babel/plugin-transform-optional-chaining": "^7.0.0-0", "@babel/plugin-transform-shorthand-properties": "^7.0.0-0", "@babel/plugin-transform-template-literals": "^7.0.0-0", "@babel/plugin-transform-unicode-regex": "^7.0.0-0", "@babel/preset-typescript": "^7.16.7", "convert-source-map": "^2.0.0", "semver": "7.7.2" }, "peerDependencies": { "@babel/core": "^7.0.0-0", "react": "*", "react-native": "*" } }, "sha512-lJG6Uk9YuojjEX/tQrCbcbmpdLCSFxDK1rJlkDhgqkVi1KZzG7cdcBFQRqyNOOzR9Y0CXNuldmtWTGOyM0k0+w=="],
|
"react-native-worklets": ["react-native-worklets@0.6.1", "", { "dependencies": { "@babel/plugin-transform-arrow-functions": "^7.0.0-0", "@babel/plugin-transform-class-properties": "^7.0.0-0", "@babel/plugin-transform-classes": "^7.0.0-0", "@babel/plugin-transform-nullish-coalescing-operator": "^7.0.0-0", "@babel/plugin-transform-optional-chaining": "^7.0.0-0", "@babel/plugin-transform-shorthand-properties": "^7.0.0-0", "@babel/plugin-transform-template-literals": "^7.0.0-0", "@babel/plugin-transform-unicode-regex": "^7.0.0-0", "@babel/preset-typescript": "^7.16.7", "convert-source-map": "^2.0.0", "semver": "7.7.2" }, "peerDependencies": { "@babel/core": "^7.0.0-0", "react": "*", "react-native": "*" } }, "sha512-URca8l7c7Uog7gv4mcg9KILdJlnbvwdS5yfXQYf5TDkD2W1VY1sduEKrD+sA3lUPXH/TG1vmXAvNxCNwPMYgGg=="],
|
||||||
|
|
||||||
"react-refresh": ["react-refresh@0.14.2", "", {}, "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA=="],
|
"react-refresh": ["react-refresh@0.14.2", "", {}, "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA=="],
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,14 @@
|
|||||||
import type { OrientationChangeEvent } from "expo-screen-orientation";
|
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Platform } from "react-native";
|
import { Platform } from "react-native";
|
||||||
|
import * as ScreenOrientation from "@/packages/expo-screen-orientation";
|
||||||
import {
|
import {
|
||||||
addOrientationChangeListener,
|
Orientation,
|
||||||
getOrientationAsync,
|
|
||||||
lockAsync,
|
|
||||||
Orientation as OrientationEnum,
|
|
||||||
OrientationLock,
|
OrientationLock,
|
||||||
unlockAsync,
|
} from "../packages/expo-screen-orientation.tv";
|
||||||
} from "@/packages/expo-screen-orientation";
|
|
||||||
import { Orientation } from "../packages/expo-screen-orientation.tv";
|
|
||||||
|
|
||||||
const orientationToOrientationLock = (
|
const orientationToOrientationLock = (
|
||||||
orientation: (typeof OrientationEnum)[keyof typeof OrientationEnum],
|
orientation: Orientation,
|
||||||
): (typeof OrientationLock)[keyof typeof OrientationLock] => {
|
): OrientationLock => {
|
||||||
switch (orientation) {
|
switch (orientation) {
|
||||||
case Orientation.LANDSCAPE_LEFT:
|
case Orientation.LANDSCAPE_LEFT:
|
||||||
return OrientationLock.LANDSCAPE_LEFT;
|
return OrientationLock.LANDSCAPE_LEFT;
|
||||||
@@ -28,52 +23,46 @@ const orientationToOrientationLock = (
|
|||||||
|
|
||||||
export const useOrientation = () => {
|
export const useOrientation = () => {
|
||||||
const [orientation, setOrientation] = useState(
|
const [orientation, setOrientation] = useState(
|
||||||
Platform.isTV ? OrientationLock.LANDSCAPE : OrientationLock.UNKNOWN,
|
Platform.isTV
|
||||||
|
? ScreenOrientation.OrientationLock.LANDSCAPE
|
||||||
|
: ScreenOrientation.OrientationLock.UNKNOWN,
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (Platform.isTV) return;
|
if (Platform.isTV) return;
|
||||||
|
|
||||||
const orientationSubscription = addOrientationChangeListener(
|
const orientationSubscription =
|
||||||
(event: OrientationChangeEvent) => {
|
ScreenOrientation.addOrientationChangeListener(
|
||||||
setOrientation(
|
(event: { orientationInfo: { orientation: Orientation } }) => {
|
||||||
orientationToOrientationLock(event.orientationInfo.orientation),
|
setOrientation(
|
||||||
);
|
orientationToOrientationLock(event.orientationInfo.orientation),
|
||||||
},
|
);
|
||||||
);
|
},
|
||||||
|
);
|
||||||
|
|
||||||
getOrientationAsync().then(
|
ScreenOrientation.getOrientationAsync().then((orientation: Orientation) => {
|
||||||
(orientation: (typeof OrientationEnum)[keyof typeof OrientationEnum]) => {
|
setOrientation(orientationToOrientationLock(orientation));
|
||||||
setOrientation(orientationToOrientationLock(orientation));
|
});
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
orientationSubscription.remove();
|
orientationSubscription.remove();
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const lockOrientation = async (
|
const lockOrientation = async (lock: OrientationLock) => {
|
||||||
lock: (typeof OrientationLock)[keyof typeof OrientationLock],
|
|
||||||
) => {
|
|
||||||
if (Platform.isTV) return;
|
if (Platform.isTV) return;
|
||||||
|
|
||||||
if (lock === OrientationLock.DEFAULT) {
|
if (lock === ScreenOrientation.OrientationLock.DEFAULT) {
|
||||||
await unlockAsync();
|
await ScreenOrientation.unlockAsync();
|
||||||
} else {
|
} else {
|
||||||
await lockAsync(lock);
|
await ScreenOrientation.lockAsync(lock);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const unlockOrientationFn = async () => {
|
const unlockOrientation = async () => {
|
||||||
if (Platform.isTV) return;
|
if (Platform.isTV) return;
|
||||||
await unlockAsync();
|
await ScreenOrientation.unlockAsync();
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return { orientation, setOrientation, lockOrientation, unlockOrientation };
|
||||||
orientation,
|
|
||||||
setOrientation,
|
|
||||||
lockOrientation,
|
|
||||||
unlockOrientation: unlockOrientationFn,
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -68,7 +68,7 @@
|
|||||||
"patch-package": "^8.0.0",
|
"patch-package": "^8.0.0",
|
||||||
"react": "19.1.0",
|
"react": "19.1.0",
|
||||||
"react-dom": "19.1.0",
|
"react-dom": "19.1.0",
|
||||||
"react-i18next": "^15.4.0",
|
"react-i18next": "16.3.3",
|
||||||
"react-native": "npm:react-native-tvos@0.81.5-1",
|
"react-native": "npm:react-native-tvos@0.81.5-1",
|
||||||
"react-native-awesome-slider": "^2.9.0",
|
"react-native-awesome-slider": "^2.9.0",
|
||||||
"react-native-bottom-tabs": "^1.0.2",
|
"react-native-bottom-tabs": "^1.0.2",
|
||||||
@@ -96,7 +96,7 @@
|
|||||||
"react-native-video": "6.16.1",
|
"react-native-video": "6.16.1",
|
||||||
"react-native-volume-manager": "^2.0.8",
|
"react-native-volume-manager": "^2.0.8",
|
||||||
"react-native-web": "^0.21.0",
|
"react-native-web": "^0.21.0",
|
||||||
"react-native-worklets": "0.5.1",
|
"react-native-worklets": "0.6.1",
|
||||||
"sonner-native": "^0.21.0",
|
"sonner-native": "^0.21.0",
|
||||||
"tailwindcss": "3.3.2",
|
"tailwindcss": "3.3.2",
|
||||||
"use-debounce": "^10.0.4",
|
"use-debounce": "^10.0.4",
|
||||||
|
|||||||
@@ -1,26 +1,24 @@
|
|||||||
import { Platform } from "react-native";
|
import { Platform } from "react-native";
|
||||||
|
|
||||||
// Dummy exports for TV
|
// Dummy exports for TV
|
||||||
enum DummyOrientationLock {
|
const DummyOrientationLock = {
|
||||||
DEFAULT = 0,
|
DEFAULT: 0,
|
||||||
ALL = 1,
|
ALL: 1,
|
||||||
PORTRAIT = 2,
|
PORTRAIT: 2,
|
||||||
PORTRAIT_UP = 3,
|
PORTRAIT_UP: 3,
|
||||||
PORTRAIT_DOWN = 4,
|
PORTRAIT_DOWN: 4,
|
||||||
LANDSCAPE = 5,
|
LANDSCAPE: 5,
|
||||||
LANDSCAPE_LEFT = 6,
|
LANDSCAPE_LEFT: 6,
|
||||||
LANDSCAPE_RIGHT = 7,
|
LANDSCAPE_RIGHT: 7,
|
||||||
OTHER = 8,
|
};
|
||||||
UNKNOWN = 9,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum DummyOrientation {
|
const DummyOrientation = {
|
||||||
UNKNOWN = 0,
|
UNKNOWN: 0,
|
||||||
PORTRAIT_UP = 1,
|
PORTRAIT_UP: 1,
|
||||||
PORTRAIT_DOWN = 2,
|
PORTRAIT_DOWN: 2,
|
||||||
LANDSCAPE_LEFT = 3,
|
LANDSCAPE_LEFT: 3,
|
||||||
LANDSCAPE_RIGHT = 4,
|
LANDSCAPE_RIGHT: 4,
|
||||||
}
|
};
|
||||||
|
|
||||||
const dummyLockAsync = async () => {};
|
const dummyLockAsync = async () => {};
|
||||||
const dummyUnlockAsync = async () => {};
|
const dummyUnlockAsync = async () => {};
|
||||||
@@ -40,10 +38,6 @@ export const OrientationLock = Platform.isTV
|
|||||||
export const Orientation = Platform.isTV
|
export const Orientation = Platform.isTV
|
||||||
? DummyOrientation
|
? DummyOrientation
|
||||||
: ScreenOrientation?.Orientation;
|
: ScreenOrientation?.Orientation;
|
||||||
|
|
||||||
// Export types
|
|
||||||
export type OrientationLockType = typeof OrientationLock;
|
|
||||||
export type OrientationType = typeof Orientation;
|
|
||||||
export const lockAsync = Platform.isTV
|
export const lockAsync = Platform.isTV
|
||||||
? dummyLockAsync
|
? dummyLockAsync
|
||||||
: ScreenOrientation?.lockAsync;
|
: ScreenOrientation?.lockAsync;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { useCallback, useEffect, useMemo } from "react";
|
|||||||
import { Platform } from "react-native";
|
import { Platform } from "react-native";
|
||||||
import { BITRATES, type Bitrate } from "@/components/BitrateSelector";
|
import { BITRATES, type Bitrate } from "@/components/BitrateSelector";
|
||||||
import * as ScreenOrientation from "@/packages/expo-screen-orientation";
|
import * as ScreenOrientation from "@/packages/expo-screen-orientation";
|
||||||
|
import { OrientationLock } from "@/packages/expo-screen-orientation.tv";
|
||||||
import { apiAtom } from "@/providers/JellyfinProvider";
|
import { apiAtom } from "@/providers/JellyfinProvider";
|
||||||
import { writeInfoLog } from "@/utils/log";
|
import { writeInfoLog } from "@/utils/log";
|
||||||
import { storage } from "../mmkv";
|
import { storage } from "../mmkv";
|
||||||
@@ -25,10 +26,7 @@ export type DownloadOption = {
|
|||||||
value: DownloadQuality;
|
value: DownloadQuality;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ScreenOrientationEnum: Record<
|
export const ScreenOrientationEnum: Record<number, string> = {
|
||||||
(typeof ScreenOrientation.OrientationLock)[keyof typeof ScreenOrientation.OrientationLock],
|
|
||||||
string
|
|
||||||
> = {
|
|
||||||
[ScreenOrientation.OrientationLock.DEFAULT]:
|
[ScreenOrientation.OrientationLock.DEFAULT]:
|
||||||
"home.settings.other.orientations.DEFAULT",
|
"home.settings.other.orientations.DEFAULT",
|
||||||
[ScreenOrientation.OrientationLock.ALL]:
|
[ScreenOrientation.OrientationLock.ALL]:
|
||||||
@@ -154,7 +152,7 @@ export type Settings = {
|
|||||||
subtitleMode: SubtitlePlaybackMode;
|
subtitleMode: SubtitlePlaybackMode;
|
||||||
rememberSubtitleSelections: boolean;
|
rememberSubtitleSelections: boolean;
|
||||||
showHomeTitles: boolean;
|
showHomeTitles: boolean;
|
||||||
defaultVideoOrientation: (typeof ScreenOrientation.OrientationLock)[keyof typeof ScreenOrientation.OrientationLock];
|
defaultVideoOrientation: OrientationLock;
|
||||||
forwardSkipTime: number;
|
forwardSkipTime: number;
|
||||||
rewindSkipTime: number;
|
rewindSkipTime: number;
|
||||||
showCustomMenuLinks: boolean;
|
showCustomMenuLinks: boolean;
|
||||||
@@ -218,7 +216,7 @@ export const defaultValues: Settings = {
|
|||||||
subtitleMode: SubtitlePlaybackMode.Default,
|
subtitleMode: SubtitlePlaybackMode.Default,
|
||||||
rememberSubtitleSelections: true,
|
rememberSubtitleSelections: true,
|
||||||
showHomeTitles: true,
|
showHomeTitles: true,
|
||||||
defaultVideoOrientation: ScreenOrientation.OrientationLock.DEFAULT,
|
defaultVideoOrientation: OrientationLock.DEFAULT,
|
||||||
forwardSkipTime: 30,
|
forwardSkipTime: 30,
|
||||||
rewindSkipTime: 10,
|
rewindSkipTime: 10,
|
||||||
showCustomMenuLinks: false,
|
showCustomMenuLinks: false,
|
||||||
|
|||||||
Reference in New Issue
Block a user