chore(deps): migrate to Expo SDK 56 (Phase 1 - compat)

Compatibility migration from SDK 55 to SDK 56 (react-native-tvos 0.85.3-0,
React 19.2.3). Phase 1 = breaking changes needed to build; new-feature
adoption and TypeScript 6 are deferred to Phase 2.

- Deps aligned to SDK 56 via `expo install --fix` (all expo-* 56.x, screens
  4.25.2, reanimated 4.3.1, worklets 0.8.3, gesture-handler 2.31.x, svg 15.15.4)
- react-native -> react-native-tvos@0.85.3-0; react/react-dom 19.2.3
- expo-router forked React Navigation: ran the SDK 56 codemod
  (@react-navigation/* imports -> expo-router/*), removed the 3 now-unused
  direct @react-navigation/* dependencies, retyped NestedTabPageStack via
  expo-router Stack.Screen options
- StyleSheet.absoluteFillObject -> absoluteFill (removed from RN 0.85 types)
- app.json ios.deploymentTarget 15.6 -> 16.4 (SDK 56 minimum)
- CI: Xcode 26.2 -> 26.4; made xcode-version Renovate-managed via a
  customManager + xcodereleases customDatasource
- @babel/core 7.29.7; dropped version-locked screens/codegen bun-patches
  (no longer applicable on SDK 56)

Deferred to Phase 2: TypeScript 6 (toolchain: @types/node, jest globals,
UdpSocket typing), @expo/vector-icons -> @react-native-vector-icons codemod.

typecheck passes. expo-doctor: 2 known failures remain (react-native-track-player
New Arch fork; typescript major mismatch pending the deferred TS6 bump).
This commit is contained in:
Gauvain
2026-05-28 23:56:03 +02:00
parent 5db4a79e8a
commit 38d638cdeb
20 changed files with 361 additions and 682 deletions

19
.github/renovate.json vendored
View File

@@ -25,6 +25,25 @@
"osvVulnerabilityAlerts": true,
"configMigration": true,
"separateMinorPatch": true,
"customManagers": [
{
"customType": "regex",
"managerFilePatterns": ["/\\.ya?ml$/"],
"matchStrings": [
"# renovate: datasource=(?<datasource>\\S+) depName=(?<depName>\\S+)(?: versioning=(?<versioning>\\S+))?\\s+xcode-version:\\s*[\"']?(?<currentValue>[^\"'\\s]+)"
],
"versioningTemplate": "{{#if versioning}}{{{versioning}}}{{else}}loose{{/if}}"
}
],
"customDatasources": {
"xcode": {
"defaultRegistryUrlTemplate": "https://xcodereleases.com/data.json",
"format": "json",
"transformTemplates": [
"{ \"releases\": [$[version.release.release=true].{\"version\": version.number}] }"
]
}
},
"lockFileMaintenance": {
"vulnerabilityAlerts": {
"enabled": true,

View File

@@ -218,7 +218,8 @@ jobs:
- name: 🔧 Setup Xcode
uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90 # v1
with:
xcode-version: "26.2"
# renovate: datasource=custom.xcode depName=xcode versioning=loose
xcode-version: "26.4"
- name: 🏗️ Setup EAS
uses: expo/expo-github-action@b184ff86a3c926240f1b6db41764c83a01c02eef # main
@@ -282,7 +283,8 @@ jobs:
- name: 🔧 Setup Xcode
uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90 # v1
with:
xcode-version: "26.2"
# renovate: datasource=custom.xcode depName=xcode versioning=loose
xcode-version: "26.4"
- name: 🚀 Build iOS app
env:
@@ -341,7 +343,8 @@ jobs:
- name: 🔧 Setup Xcode
uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90 # v1
with:
xcode-version: "26.2"
# renovate: datasource=custom.xcode depName=xcode versioning=loose
xcode-version: "26.4"
- name: 🏗️ Setup EAS
uses: expo/expo-github-action@b184ff86a3c926240f1b6db41764c83a01c02eef # main
@@ -408,7 +411,8 @@ jobs:
- name: 🔧 Setup Xcode
uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90 # v1
with:
xcode-version: "26.2"
# renovate: datasource=custom.xcode depName=xcode versioning=loose
xcode-version: "26.4"
- name: 🚀 Build iOS app
env:

View File

@@ -78,7 +78,7 @@
"expo-build-properties",
{
"ios": {
"deploymentTarget": "15.6",
"deploymentTarget": "16.4",
"useFrameworks": "static"
},
"android": {

View File

@@ -1,13 +1,13 @@
import { Slot, Stack, withLayoutContext } from "expo-router";
import {
createMaterialTopTabNavigator,
MaterialTopTabNavigationEventMap,
MaterialTopTabNavigationOptions,
} from "@react-navigation/material-top-tabs";
} from "expo-router/js-top-tabs";
import type {
ParamListBase,
TabNavigationState,
} from "@react-navigation/native";
import { Slot, Stack, withLayoutContext } from "expo-router";
} from "expo-router/react-navigation";
import { Platform } from "react-native";
const { Navigator } = createMaterialTopTabNavigator();

View File

@@ -1,13 +1,13 @@
import { Stack, useLocalSearchParams, withLayoutContext } from "expo-router";
import {
createMaterialTopTabNavigator,
MaterialTopTabNavigationEventMap,
MaterialTopTabNavigationOptions,
} from "@react-navigation/material-top-tabs";
} from "expo-router/js-top-tabs";
import type {
ParamListBase,
TabNavigationState,
} from "@react-navigation/native";
import { Stack, useLocalSearchParams, withLayoutContext } from "expo-router";
} from "expo-router/react-navigation";
import { useTranslation } from "react-i18next";
const { Navigator } = createMaterialTopTabNavigator();

View File

@@ -1,8 +1,8 @@
import { getItemsApi } from "@jellyfin/sdk/lib/utils/api";
import { useRoute } from "@react-navigation/native";
import { FlashList } from "@shopify/flash-list";
import { useInfiniteQuery } from "@tanstack/react-query";
import { useLocalSearchParams } from "expo-router";
import { useRoute } from "expo-router/react-navigation";
import { useAtom } from "jotai";
import { useCallback, useMemo } from "react";
import { useTranslation } from "react-i18next";

View File

@@ -1,8 +1,8 @@
import { getArtistsApi } from "@jellyfin/sdk/lib/utils/api";
import { useRoute } from "@react-navigation/native";
import { FlashList } from "@shopify/flash-list";
import { useInfiniteQuery } from "@tanstack/react-query";
import { useLocalSearchParams } from "expo-router";
import { useRoute } from "expo-router/react-navigation";
import { useAtom } from "jotai";
import { useCallback, useMemo } from "react";
import { useTranslation } from "react-i18next";

View File

@@ -1,9 +1,9 @@
import { Ionicons } from "@expo/vector-icons";
import { getItemsApi } from "@jellyfin/sdk/lib/utils/api";
import { useNavigation, useRoute } from "@react-navigation/native";
import { FlashList } from "@shopify/flash-list";
import { useInfiniteQuery } from "@tanstack/react-query";
import { useLocalSearchParams } from "expo-router";
import { useNavigation, useRoute } from "expo-router/react-navigation";
import { useAtom } from "jotai";
import { useCallback, useLayoutEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";

View File

@@ -1,9 +1,9 @@
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
import { getItemsApi } from "@jellyfin/sdk/lib/utils/api";
import { useRoute } from "@react-navigation/native";
import { FlashList } from "@shopify/flash-list";
import { useQuery } from "@tanstack/react-query";
import { useLocalSearchParams } from "expo-router";
import { useRoute } from "expo-router/react-navigation";
import { useAtom } from "jotai";
import { useCallback, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";

View File

@@ -3,11 +3,11 @@ import {
type NativeBottomTabNavigationEventMap,
type NativeBottomTabNavigationOptions,
} from "@bottom-tabs/react-navigation";
import { withLayoutContext } from "expo-router";
import type {
ParamListBase,
TabNavigationState,
} from "@react-navigation/native";
import { withLayoutContext } from "expo-router";
} from "expo-router/react-navigation";
import { useTranslation } from "react-i18next";
import { Platform, View } from "react-native";
import { SystemBars } from "react-native-edge-to-edge";

View File

@@ -1248,7 +1248,7 @@ const styles = StyleSheet.create({
color: "#fff",
},
downloadingOverlay: {
...StyleSheet.absoluteFillObject,
...StyleSheet.absoluteFill,
backgroundColor: "rgba(0,0,0,0.5)",
borderRadius: scaleSize(14),
justifyContent: "center",

View File

@@ -2,12 +2,12 @@ import "@/augmentations";
import { ActionSheetProvider } from "@expo/react-native-action-sheet";
import { BottomSheetModalProvider } from "@gorhom/bottom-sheet";
import NetInfo from "@react-native-community/netinfo";
import { DarkTheme, ThemeProvider } from "@react-navigation/native";
import { createSyncStoragePersister } from "@tanstack/query-sync-storage-persister";
import { onlineManager, QueryClient } from "@tanstack/react-query";
import { PersistQueryClientProvider } from "@tanstack/react-query-persist-client";
import * as BackgroundTask from "expo-background-task";
import * as Device from "expo-device";
import { DarkTheme, ThemeProvider } from "expo-router/react-navigation";
import { Platform } from "react-native";
import { GlobalModal } from "@/components/GlobalModal";
import { enableTVMenuKeyInterception } from "@/hooks/useTVBackHandler";

View File

@@ -1,29 +0,0 @@
diff --git a/lib/generators/modules/GenerateModuleObjCpp/index.js b/lib/generators/modules/GenerateModuleObjCpp/index.js
index 927711514d2deaa3c795fb98e676e0a1f596eddc..0364d66204a76fccd3e06a0dc72bf801aa04a50d 100644
--- a/lib/generators/modules/GenerateModuleObjCpp/index.js
+++ b/lib/generators/modules/GenerateModuleObjCpp/index.js
@@ -67,9 +67,12 @@ const HeaderFileTemplate = ({
* must have a single output. More files => more genrule()s => slower builds.
*/
-#ifndef __cplusplus
-#error This file must be compiled as Obj-C++. If you are importing it, you must change your file extension to .mm.
-#endif
+// Patched: guard the Obj-C++ body with __cplusplus instead of hard-#error-ing.
+// With use_frameworks! :static + New Arch, plain Obj-C .m TUs can trigger a
+// Clang module build (via Swift-interop -Swift.h umbrellas) that pulls this
+// header in Obj-C mode. Skipping the body (instead of erroring) lets the module
+// build; .mm consumers still get the full Obj-C++ contents unchanged.
+#if defined(__cplusplus)
// Avoid multiple includes of ${headerFileNameWithNoExt} symbols
#ifndef ${headerFileNameWithNoExt}_H
@@ -93,7 +96,7 @@ const HeaderFileTemplate = ({
structInlineMethods +
(assumeNonnull ? '\nNS_ASSUME_NONNULL_END\n' : '\n') +
`#endif // ${headerFileNameWithNoExt}_H` +
- '\n'
+ '\n#endif // defined(__cplusplus)\n'
);
};
const SourceFileTemplate = ({headerFileName, moduleImplementations}) => `/**

View File

@@ -1,191 +0,0 @@
diff --git a/node_modules/react-native-screens/.bun-tag-10a3b0add1bd4de6 b/.bun-tag-10a3b0add1bd4de6
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/node_modules/react-native-screens/.bun-tag-6a8504b742d5cfff b/.bun-tag-6a8504b742d5cfff
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/node_modules/react-native-screens/.bun-tag-d28396854bc27a3d b/.bun-tag-d28396854bc27a3d
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/ios/RNSScreenStack.mm b/ios/RNSScreenStack.mm
index 51f021831aed26a4eed3c85014020423b7b3108b..2f621547932806b94ab1e75ecc73772facd209d0 100644
--- a/ios/RNSScreenStack.mm
+++ b/ios/RNSScreenStack.mm
@@ -34,6 +34,11 @@
#import "integrations/RNSDismissibleModalProtocol.h"
#import "utils/UINavigationBar+RNSUtility.h"
+#if TARGET_OS_TV
+#import <React/RCTTVNavigationEventNotification.h>
+#import <React/RCTTVRemoteHandler.h>
+#endif // TARGET_OS_TV
+
#ifdef RNS_GAMMA_ENABLED
#import "RNSFrameCorrectionProvider.h"
#import "Swift-Bridging.h"
@@ -43,6 +48,12 @@
namespace react = facebook::react;
#endif // RCT_NEW_ARCH_ENABLED
+#if TARGET_OS_TV
+@interface RNSNavigationController ()
+@property (nonatomic, strong) UITapGestureRecognizer *rnscreens_menuGestureRecognizer;
+@end
+#endif // TARGET_OS_TV
+
@interface RNSScreenStackView () <
UINavigationControllerDelegate,
UIAdaptivePresentationControllerDelegate,
@@ -62,6 +73,57 @@ namespace react = facebook::react;
@implementation RNSNavigationController
+#if TARGET_OS_TV
+- (void)viewDidLoad
+{
+ [super viewDidLoad];
+
+ self.rnscreens_menuGestureRecognizer =
+ [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(rnscreens_menuPressed:)];
+ self.rnscreens_menuGestureRecognizer.allowedPressTypes = @[ @(UIPressTypeMenu) ];
+
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(rnscreens_enableMenuGesture)
+ name:RCTTVEnableMenuKeyNotification
+ object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(rnscreens_disableMenuGesture)
+ name:RCTTVDisableMenuKeyNotification
+ object:nil];
+
+ if ([RCTTVRemoteHandler useMenuKey]) {
+ [self rnscreens_enableMenuGesture];
+ }
+}
+
+- (void)dealloc
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+}
+
+- (void)rnscreens_enableMenuGesture
+{
+ if (![self.view.gestureRecognizers containsObject:self.rnscreens_menuGestureRecognizer]) {
+ [self.view addGestureRecognizer:self.rnscreens_menuGestureRecognizer];
+ }
+}
+
+- (void)rnscreens_disableMenuGesture
+{
+ if ([self.view.gestureRecognizers containsObject:self.rnscreens_menuGestureRecognizer]) {
+ [self.view removeGestureRecognizer:self.rnscreens_menuGestureRecognizer];
+ }
+}
+
+- (void)rnscreens_menuPressed:(UIGestureRecognizer *)recognizer
+{
+ [[NSNotificationCenter defaultCenter] postNavigationPressEventWithType:RCTTVRemoteEventMenu
+ keyAction:recognizer.eventKeyAction
+ tag:nil
+ target:nil];
+}
+#endif // TARGET_OS_TV
+
#if !TARGET_OS_TV
- (UIViewController *)childViewControllerForStatusBarStyle
{
diff --git a/ios/gamma/split-view/RNSSplitViewAppearanceApplicator.swift b/ios/gamma/split-view/RNSSplitViewAppearanceApplicator.swift
index 95c76ccf3528d3a8828e90b272a1d79b0828a139..f29d4df21440d23523ae7a2f6fe71c32154e3928 100644
--- a/ios/gamma/split-view/RNSSplitViewAppearanceApplicator.swift
+++ b/ios/gamma/split-view/RNSSplitViewAppearanceApplicator.swift
@@ -79,11 +79,13 @@ class RNSSplitViewAppearanceApplicator {
maxWidth: splitView.maximumSupplementaryColumnWidth)
#if compiler(>=6.2)
+ #if !os(tvOS)
if #available(iOS 26.0, *) {
validateColumnConstraints(
minWidth: splitView.minimumInspectorColumnWidth,
maxWidth: splitView.maximumInspectorColumnWidth)
}
+ #endif
#endif
// Step 2.2 - applying updates to columns
@@ -126,6 +128,7 @@ class RNSSplitViewAppearanceApplicator {
}
#if compiler(>=6.2)
+ #if !os(tvOS)
if #available(iOS 26.0, *) {
if splitView.minimumSecondaryColumnWidth >= 0 {
splitViewController.minimumSecondaryColumnWidth = splitView.minimumSecondaryColumnWidth
@@ -159,6 +162,7 @@ class RNSSplitViewAppearanceApplicator {
splitView.preferredInspectorColumnWidthOrFraction
}
}
+ #endif
#endif
// Step 2.3 - manipulating with inspector column
diff --git a/ios/gamma/split-view/RNSSplitViewHostController.swift b/ios/gamma/split-view/RNSSplitViewHostController.swift
index 0421e3ea92fc7bcdf57417b5ee3a62348fce34f5..cd878ab638d3c78a661e2df4c4c1b21011dfcf48 100644
--- a/ios/gamma/split-view/RNSSplitViewHostController.swift
+++ b/ios/gamma/split-view/RNSSplitViewHostController.swift
@@ -386,7 +386,7 @@ extension RNSSplitViewHostController: RNSSplitViewNavigationControllerViewFrameO
/// @param inspectors An array of inspector-type RNSSplitViewScreenComponentView subviews.
///
func maybeSetupInspector(_ inspectors: [RNSSplitViewScreenComponentView]) {
-
+ #if !os(tvOS)
if #available(iOS 26.0, *) {
let inspector = inspectors.first
if inspector != nil {
@@ -395,6 +395,7 @@ extension RNSSplitViewHostController: RNSSplitViewNavigationControllerViewFrameO
setViewController(inspectorViewController, for: .inspector)
}
}
+ #endif
}
///
@@ -404,9 +405,11 @@ extension RNSSplitViewHostController: RNSSplitViewNavigationControllerViewFrameO
/// Uses the UISplitViewController's new API introduced in iOS 26 to show the inspector column.
///
func maybeShowInspector() {
+ #if !os(tvOS)
if #available(iOS 26.0, *) {
show(.inspector)
}
+ #endif
}
///
@@ -416,9 +419,11 @@ extension RNSSplitViewHostController: RNSSplitViewNavigationControllerViewFrameO
/// Uses the UISplitViewController's new API introduced in iOS 26 to hide the inspector column.
///
func maybeHideInspector() {
+ #if !os(tvOS)
if #available(iOS 26.0, *) {
hide(.inspector)
}
+ #endif
}
}
#endif
@@ -444,6 +449,7 @@ extension RNSSplitViewHostController: UISplitViewControllerDelegate {
public func splitViewController(
_ svc: UISplitViewController, didHide column: UISplitViewController.Column
) {
+ #if !os(tvOS)
if #available(iOS 26.0, *) {
// TODO: we may consider removing this logic, because it could be handled by onViewDidDisappear on the column level
// On the other hand, maybe dedicated event related to the inspector would be a better approach.
@@ -461,6 +467,7 @@ extension RNSSplitViewHostController: UISplitViewControllerDelegate {
}
}
}
+ #endif
}
#endif

649
bun.lock

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,9 @@
import type { ParamListBase, RouteProp } from "@react-navigation/native";
import type { NativeStackNavigationOptions } from "@react-navigation/native-stack";
import { Stack } from "expo-router";
import type { ComponentProps } from "react";
import { Platform } from "react-native";
import { HeaderBackButton } from "../common/HeaderBackButton";
type ICommonScreenOptions =
| NativeStackNavigationOptions
| ((prop: {
route: RouteProp<ParamListBase, string>;
navigation: any;
}) => NativeStackNavigationOptions);
type ICommonScreenOptions = ComponentProps<typeof Stack.Screen>["options"];
export const commonScreenOptions: ICommonScreenOptions = {
title: "",

View File

@@ -263,7 +263,7 @@ const createStyles = (typography: ReturnType<typeof useScaledTVTypography>) =>
color: "#fff",
},
downloadingOverlay: {
...StyleSheet.absoluteFillObject,
...StyleSheet.absoluteFill,
backgroundColor: "rgba(0,0,0,0.5)",
borderRadius: scaleSize(14),
justifyContent: "center",

View File

@@ -1408,14 +1408,14 @@ export const Controls: FC<Props> = ({
const styles = StyleSheet.create({
controlsContainer: {
...StyleSheet.absoluteFillObject,
...StyleSheet.absoluteFill,
},
darkOverlay: {
...StyleSheet.absoluteFillObject,
...StyleSheet.absoluteFill,
backgroundColor: "rgba(0, 0, 0, 0.4)",
},
focusStealingOverlay: {
...StyleSheet.absoluteFillObject,
...StyleSheet.absoluteFill,
zIndex: 1,
},
bottomContainer: {

View File

@@ -28,61 +28,58 @@
"dependencies": {
"@bottom-tabs/react-navigation": "1.2.0",
"@douglowder/expo-av-route-picker-view": "^0.0.5",
"@expo/metro-runtime": "~55.0.11",
"@expo/metro-runtime": "~56.0.13",
"@expo/react-native-action-sheet": "^4.1.1",
"@expo/ui": "~55.0.17",
"@expo/ui": "~56.0.14",
"@expo/vector-icons": "^15.0.3",
"@gorhom/bottom-sheet": "5.2.8",
"@jellyfin/sdk": "^0.13.0",
"@react-native-community/netinfo": "^12.0.0",
"@react-navigation/material-top-tabs": "7.4.9",
"@react-navigation/native": "^7.2.5",
"@react-navigation/native-stack": "~7.14.5",
"@shopify/flash-list": "2.0.2",
"@tanstack/query-sync-storage-persister": "^5.90.18",
"@tanstack/react-pacer": "^0.19.1",
"@tanstack/react-query": "5.90.20",
"@tanstack/react-query-persist-client": "^5.90.18",
"axios": "^1.7.9",
"expo": "~55.0.26",
"expo-application": "~55.0.15",
"expo-asset": "~55.0.17",
"expo-audio": "~55.0.0",
"expo-background-task": "~55.0.18",
"expo-blur": "~55.0.14",
"expo-brightness": "~55.0.13",
"expo-build-properties": "~55.0.14",
"expo-camera": "~55.0.19",
"expo-constants": "~55.0.16",
"expo-crypto": "~55.0.15",
"expo-dev-client": "~55.0.35",
"expo-device": "~55.0.17",
"expo-font": "~55.0.8",
"expo-haptics": "~55.0.14",
"expo-image": "~55.0.11",
"expo-linear-gradient": "~55.0.14",
"expo-linking": "~55.0.15",
"expo-localization": "~55.0.15",
"expo-location": "~55.1.10",
"expo-notifications": "~55.0.23",
"expo-router": "~55.0.16",
"expo-screen-orientation": "~55.0.16",
"expo-secure-store": "~55.0.14",
"expo-sharing": "~55.0.20",
"expo-splash-screen": "~55.0.21",
"expo-status-bar": "~55.0.6",
"expo-system-ui": "~55.0.18",
"expo-task-manager": "~55.0.16",
"expo-web-browser": "~55.0.16",
"expo": "~56.0.6",
"expo-application": "~56.0.3",
"expo-asset": "~56.0.15",
"expo-audio": "~56.0.11",
"expo-background-task": "~56.0.15",
"expo-blur": "~56.0.3",
"expo-brightness": "~56.0.5",
"expo-build-properties": "~56.0.15",
"expo-camera": "~56.0.7",
"expo-constants": "~56.0.16",
"expo-crypto": "~56.0.4",
"expo-dev-client": "~56.0.16",
"expo-device": "~56.0.4",
"expo-font": "~56.0.5",
"expo-haptics": "~56.0.3",
"expo-image": "~56.0.9",
"expo-linear-gradient": "~56.0.4",
"expo-linking": "~56.0.12",
"expo-localization": "~56.0.6",
"expo-location": "~56.0.14",
"expo-notifications": "~56.0.14",
"expo-router": "~56.2.7",
"expo-screen-orientation": "~56.0.5",
"expo-secure-store": "~56.0.4",
"expo-sharing": "~56.0.14",
"expo-splash-screen": "~56.0.10",
"expo-status-bar": "~56.0.4",
"expo-system-ui": "~56.0.5",
"expo-task-manager": "~56.0.15",
"expo-web-browser": "~56.0.5",
"i18next": "^25.0.0",
"jotai": "2.16.2",
"lodash": "4.17.23",
"nativewind": "^2.0.11",
"patch-package": "^8.0.0",
"react": "19.2.0",
"react-dom": "19.2.0",
"react": "19.2.3",
"react-dom": "19.2.3",
"react-i18next": "16.5.3",
"react-native": "npm:react-native-tvos@0.83.6-0",
"react-native": "npm:react-native-tvos@0.85.3-0",
"react-native-awesome-slider": "^2.9.0",
"react-native-bottom-tabs": "1.2.0",
"react-native-circular-progress": "^1.4.1",
@@ -91,7 +88,7 @@
"react-native-device-info": "^15.0.0",
"react-native-draggable-flatlist": "^4.0.3",
"react-native-edge-to-edge": "^1.7.0",
"react-native-gesture-handler": "~2.30.0",
"react-native-gesture-handler": "~2.31.1",
"react-native-glass-effect-view": "^1.0.0",
"react-native-google-cast": "^4.9.1",
"react-native-image-colors": "^2.4.0",
@@ -99,13 +96,13 @@
"react-native-ios-utilities": "5.2.0",
"react-native-mmkv": "4.1.1",
"react-native-nitro-modules": "0.33.1",
"react-native-pager-view": "8.0.0",
"react-native-pager-view": "8.0.1",
"react-native-qrcode-svg": "^6.3.21",
"react-native-reanimated": "4.2.1",
"react-native-reanimated": "4.3.1",
"react-native-reanimated-carousel": "4.0.3",
"react-native-safe-area-context": "~5.6.0",
"react-native-screens": "~4.18.0",
"react-native-svg": "15.15.3",
"react-native-safe-area-context": "~5.7.0",
"react-native-screens": "4.25.2",
"react-native-svg": "15.15.4",
"react-native-text-ticker": "^1.15.0",
"react-native-track-player": "github:lovegaoshi/react-native-track-player#APM",
"react-native-udp": "^4.1.7",
@@ -113,14 +110,14 @@
"react-native-uuid": "^2.0.3",
"react-native-volume-manager": "^2.0.8",
"react-native-web": "^0.21.0",
"react-native-worklets": "0.7.4",
"react-native-worklets": "0.8.3",
"sonner-native": "0.21.2",
"tailwindcss": "3.3.2",
"use-debounce": "^10.0.4",
"zod": "4.1.13"
},
"devDependencies": {
"@babel/core": "7.28.6",
"@babel/core": "7.29.7",
"@biomejs/biome": "2.3.11",
"@react-native-community/cli": "20.1.3",
"@react-native-tvos/config-tv": "0.1.6",
@@ -148,6 +145,7 @@
},
"install": {
"exclude": [
"react-native",
"react-native-screens"
]
}
@@ -165,9 +163,7 @@
"unrs-resolver"
],
"patchedDependencies": {
"react-native-screens@4.18.0": "bun-patches/react-native-screens@4.18.0.patch",
"react-native-udp@4.1.7": "bun-patches/react-native-udp@4.1.7.patch",
"@react-native/codegen@0.83.6": "bun-patches/@react-native%2Fcodegen@0.83.6.patch",
"react-native-bottom-tabs@1.2.0": "bun-patches/react-native-bottom-tabs@1.2.0.patch"
}
}

View File

@@ -1,10 +1,10 @@
import { useFocusEffect } from "@react-navigation/core";
import {
type QueryKey,
type UseQueryOptions,
type UseQueryResult,
useQuery,
} from "@tanstack/react-query";
import { useFocusEffect } from "expo-router/react-navigation";
import { useCallback } from "react";
export function useReactNavigationQuery<