diff --git a/android/app/src/main/res/xml/automotive_app_desc.xml b/android/app/src/main/res/xml/automotive_app_desc.xml new file mode 100644 index 0000000..bb31111 --- /dev/null +++ b/android/app/src/main/res/xml/automotive_app_desc.xml @@ -0,0 +1,4 @@ + + + + diff --git a/index.js b/index.js index 7325fbc..44c8859 100644 --- a/index.js +++ b/index.js @@ -7,9 +7,11 @@ import PlaybackService from './src/utility/PlaybackService'; import { setupSentry } from '@/utility/Sentry'; import { enableScreens } from 'react-native-screens'; import { patchTrackPlayer } from '@/utility/AddedTrackEvents'; +import { registerAutoPlay } from './src/screens/carplay/register'; setupSentry(); enableScreens(); patchTrackPlayer(); AppRegistry.registerComponent(appName, () => App); -TrackPlayer.registerPlaybackService(() => PlaybackService); \ No newline at end of file +TrackPlayer.registerPlaybackService(() => PlaybackService); +registerAutoPlay(); \ No newline at end of file diff --git a/ios/AppDelegate.swift b/ios/AppDelegate.swift deleted file mode 100644 index ebc3e8f..0000000 --- a/ios/AppDelegate.swift +++ /dev/null @@ -1,48 +0,0 @@ -import UIKit -import React -import React_RCTAppDelegate -import ReactAppDependencyProvider - -@main -class AppDelegate: UIResponder, UIApplicationDelegate { - var window: UIWindow? - - var reactNativeDelegate: ReactNativeDelegate? - var reactNativeFactory: RCTReactNativeFactory? - - func application( - _ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil - ) -> Bool { - let delegate = ReactNativeDelegate() - let factory = RCTReactNativeFactory(delegate: delegate) - delegate.dependencyProvider = RCTAppDependencyProvider() - - reactNativeDelegate = delegate - reactNativeFactory = factory - - window = UIWindow(frame: UIScreen.main.bounds) - - factory.startReactNative( - withModuleName: "Fintunes", - in: window, - launchOptions: launchOptions - ) - - return true - } -} - -class ReactNativeDelegate: RCTDefaultReactNativeFactoryDelegate { - override func sourceURL(for bridge: RCTBridge) -> URL? { - self.bundleURL() - } - - override func bundleURL() -> URL? { -#if DEBUG - RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index") -#else - Bundle.main.url(forResource: "main", withExtension: "jsbundle") -#endif - } -} diff --git a/ios/Fintunes-Bridging-Header.h b/ios/Fintunes-Bridging-Header.h new file mode 100644 index 0000000..1b2cb5d --- /dev/null +++ b/ios/Fintunes-Bridging-Header.h @@ -0,0 +1,4 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// + diff --git a/ios/Fintunes.xcodeproj/project.pbxproj b/ios/Fintunes.xcodeproj/project.pbxproj index a68490d..1e5c173 100644 --- a/ios/Fintunes.xcodeproj/project.pbxproj +++ b/ios/Fintunes.xcodeproj/project.pbxproj @@ -13,10 +13,21 @@ 4C04FC6E055249ABB204D3BC /* Inter-VariableFont_slnt,wght.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 4B4A0465FF364579B28CF5D7 /* Inter-VariableFont_slnt,wght.ttf */; }; AB393FCA2857CC8400773469 /* SnapshotHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB393FC92857CC8400773469 /* SnapshotHelper.swift */; }; AB4A8DFE2857C8DA005A1ED0 /* FintunesUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB4A8DFD2857C8DA005A1ED0 /* FintunesUITests.swift */; }; - AB7AA5F92DC8E5D600578CAC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB7AA5F82DC8E5D600578CAC /* AppDelegate.swift */; }; + ABD245FC2F3605DE00135530 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABD245FB2F3605DE00135530 /* AppDelegate.swift */; }; + ABD245FE2F3605F500135530 /* PhoneSceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABD245FD2F3605F500135530 /* PhoneSceneDelegate.swift */; }; FA01635F2599C28FC19F2EC3 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 3896494129CBC30258D9BB1C /* PrivacyInfo.xcprivacy */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + AB87931D2F2CE1530093DF3D /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 13B07F861A680F5B00A75B9A; + remoteInfo = Fintunes; + }; +/* End PBXContainerItemProxy section */ + /* Begin PBXFileReference section */ 13B07F961A680F5B00A75B9A /* Fintunes.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Fintunes.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; @@ -30,7 +41,10 @@ AB393FC92857CC8400773469 /* SnapshotHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SnapshotHelper.swift; sourceTree = ""; }; AB4A8DFB2857C8DA005A1ED0 /* FintunesUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FintunesUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; AB4A8DFD2857C8DA005A1ED0 /* FintunesUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FintunesUITests.swift; sourceTree = ""; }; - AB7AA5F82DC8E5D600578CAC /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + AB78871B2F35FDA400C6CF3D /* Fintunes-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Fintunes-Bridging-Header.h"; sourceTree = ""; }; + AB8793222F2D399F0093DF3D /* Fintunes.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = Fintunes.entitlements; path = Fintunes/Fintunes.entitlements; sourceTree = ""; }; + ABD245FB2F3605DE00135530 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = Fintunes/AppDelegate.swift; sourceTree = ""; }; + ABD245FD2F3605F500135530 /* PhoneSceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhoneSceneDelegate.swift; sourceTree = ""; }; E22EC545298DA9F9017776C0 /* libPods-Fintunes.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Fintunes.a"; sourceTree = BUILT_PRODUCTS_DIR; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; ED2971642150620600B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS12.0.sdk/System/Library/Frameworks/JavaScriptCore.framework; sourceTree = DEVELOPER_DIR; }; @@ -58,11 +72,14 @@ 13B07FAE1A68108700A75B9A /* Fintunes */ = { isa = PBXGroup; children = ( - AB7AA5F82DC8E5D600578CAC /* AppDelegate.swift */, + ABD245FD2F3605F500135530 /* PhoneSceneDelegate.swift */, + ABD245FB2F3605DE00135530 /* AppDelegate.swift */, + AB8793222F2D399F0093DF3D /* Fintunes.entitlements */, 13B07FB51A68108700A75B9A /* Images.xcassets */, 13B07FB61A68108700A75B9A /* Info.plist */, 13B07FB11A68108700A75B9A /* LaunchScreen.xib */, 3896494129CBC30258D9BB1C /* PrivacyInfo.xcprivacy */, + AB78871B2F35FDA400C6CF3D /* Fintunes-Bridging-Header.h */, ); name = Fintunes; sourceTree = ""; @@ -189,7 +206,7 @@ TargetAttributes = { 13B07F861A680F5B00A75B9A = { DevelopmentTeam = HD2D35G9Y4; - LastSwiftMigration = 1210; + LastSwiftMigration = 2620; ProvisioningStyle = Manual; }; AB4A8DFA2857C8DA005A1ED0 = { @@ -309,6 +326,7 @@ "${PODS_CONFIGURATION_BUILD_DIR}/RNSVG/RNSVGFilters.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/React-Core_privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/React-cxxreact/React-cxxreact_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactNativeAutoPlay/ReactNativeAutoPlay.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/Sentry/Sentry.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/boost/boost_privacy.bundle", @@ -321,6 +339,7 @@ "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNSVGFilters.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-Core_privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-cxxreact_privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ReactNativeAutoPlay.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SDWebImage.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Sentry.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/boost_privacy.bundle", @@ -338,7 +357,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - AB7AA5F92DC8E5D600578CAC /* AppDelegate.swift in Sources */, + ABD245FE2F3605F500135530 /* PhoneSceneDelegate.swift in Sources */, + ABD245FC2F3605DE00135530 /* AppDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -357,6 +377,7 @@ AB4A8E022857C8DA005A1ED0 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 13B07F861A680F5B00A75B9A /* Fintunes */; + targetProxy = AB87931D2F2CE1530093DF3D /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ @@ -379,6 +400,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Fintunes/Fintunes.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; @@ -405,7 +427,8 @@ PRODUCT_BUNDLE_IDENTIFIER = nl.moeilijkedingen.jellyfinaudioplayer; PRODUCT_NAME = Fintunes; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "nl.moeilijkedingen.jellyfinaudioplayer AppStore 1754345870"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "nl.moeilijkedingen.jellyfinaudioplayer AppStore 17"; + SWIFT_OBJC_BRIDGING_HEADER = "Fintunes-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; @@ -418,6 +441,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Fintunes/Fintunes.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; @@ -440,7 +464,8 @@ PRODUCT_NAME = Fintunes; PROVISIONING_PROFILE = "915c5213-22f6-4f9d-8065-2a06300f9bfb"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "nl.moeilijkedingen.jellyfinaudioplayer AppStore 1754345870"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "nl.moeilijkedingen.jellyfinaudioplayer AppStore 17"; + SWIFT_OBJC_BRIDGING_HEADER = "Fintunes-Bridging-Header.h"; SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; }; diff --git a/ios/Fintunes/AppDelegate.swift b/ios/Fintunes/AppDelegate.swift index ebc3e8f..afb0853 100644 --- a/ios/Fintunes/AppDelegate.swift +++ b/ios/Fintunes/AppDelegate.swift @@ -31,6 +31,36 @@ class AppDelegate: UIResponder, UIApplicationDelegate { return true } + + @objc func getRootViewForAutoplay( + moduleName: String, + initialProperties: [String: Any]? + ) -> UIView? { + if RCTIsNewArchEnabled() { + if let factory = reactNativeFactory?.rootViewFactory as? RCTRootViewFactory { + return factory.view( + withModuleName: moduleName, + initialProperties: initialProperties, + launchOptions: nil + ) + } + + return reactNativeFactory?.rootViewFactory.view( + withModuleName: moduleName, + initialProperties: initialProperties + ) + } + + if let rootView = window?.rootViewController?.view as? RCTRootView { + return RCTRootView( + bridge: rootView.bridge, + moduleName: moduleName, + initialProperties: initialProperties + ) + } + + return nil + } } class ReactNativeDelegate: RCTDefaultReactNativeFactoryDelegate { diff --git a/ios/Fintunes/Fintunes.entitlements b/ios/Fintunes/Fintunes.entitlements new file mode 100644 index 0000000..cd9ef49 --- /dev/null +++ b/ios/Fintunes/Fintunes.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.developer.carplay-audio + + + diff --git a/ios/Fintunes/Info.plist b/ios/Fintunes/Info.plist index b8204c8..8be0293 100644 --- a/ios/Fintunes/Info.plist +++ b/ios/Fintunes/Info.plist @@ -64,5 +64,35 @@ UIViewControllerBasedStatusBarAppearance + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + CPTemplateApplicationSceneSessionRoleApplication + + + UISceneClassName + CPTemplateApplicationScene + UISceneConfigurationName + CarPlayHeadUnit + UISceneDelegateClassName + HeadUnitSceneDelegate + + + UIWindowSceneSessionRoleApplication + + + UISceneClassName + UIWindowScene + UISceneConfigurationName + Phone + UISceneDelegateClassName + Fintunes.PhoneSceneDelegate + + + + diff --git a/ios/Fintunes/PhoneSceneDelegate.swift b/ios/Fintunes/PhoneSceneDelegate.swift new file mode 100644 index 0000000..2d655f8 --- /dev/null +++ b/ios/Fintunes/PhoneSceneDelegate.swift @@ -0,0 +1,32 @@ +import UIKit +import React_RCTAppDelegate + +class PhoneSceneDelegate: UIResponder, UIWindowSceneDelegate { + var window: UIWindow? + + func scene( + _ scene: UIScene, + willConnectTo session: UISceneSession, + options connectionOptions: UIScene.ConnectionOptions + ) { + guard let windowScene = scene as? UIWindowScene else { return } + + // Get the AppDelegate to access the React Native setup + guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return } + + // Create the window for this scene + let window = UIWindow(windowScene: windowScene) + + // Start React Native if not already started + if let factory = appDelegate.reactNativeFactory { + factory.startReactNative( + withModuleName: "Fintunes", + in: window, + launchOptions: nil + ) + } + + self.window = window + window.makeKeyAndVisible() + } +} diff --git a/ios/PhoneSceneDelegate.swift b/ios/PhoneSceneDelegate.swift new file mode 100644 index 0000000..2d655f8 --- /dev/null +++ b/ios/PhoneSceneDelegate.swift @@ -0,0 +1,32 @@ +import UIKit +import React_RCTAppDelegate + +class PhoneSceneDelegate: UIResponder, UIWindowSceneDelegate { + var window: UIWindow? + + func scene( + _ scene: UIScene, + willConnectTo session: UISceneSession, + options connectionOptions: UIScene.ConnectionOptions + ) { + guard let windowScene = scene as? UIWindowScene else { return } + + // Get the AppDelegate to access the React Native setup + guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return } + + // Create the window for this scene + let window = UIWindow(windowScene: windowScene) + + // Start React Native if not already started + if let factory = appDelegate.reactNativeFactory { + factory.startReactNative( + withModuleName: "Fintunes", + in: window, + launchOptions: nil + ) + } + + self.window = window + window.makeKeyAndVisible() + } +} diff --git a/ios/Podfile.lock b/ios/Podfile.lock index d6bc3cd..d2da48a 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -25,6 +25,35 @@ PODS: - libwebp/sharpyuv (1.3.2) - libwebp/webp (1.3.2): - libwebp/sharpyuv + - NitroModules (0.33.7): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-callinvoker + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - SocketRocket + - Yoga - RCT-Folly (2024.11.18.00): - boost - DoubleConversion @@ -1939,8 +1968,34 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - react-native-netinfo (11.4.1): + - react-native-netinfo (11.5.2): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - SocketRocket + - Yoga - react-native-safe-area-context (5.6.2): - boost - DoubleConversion @@ -2646,6 +2701,36 @@ PODS: - React-perflogger (= 0.83.1) - React-utils (= 0.83.1) - SocketRocket + - ReactNativeAutoPlay (0.2.2): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - NitroModules + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-callinvoker + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - SocketRocket + - Yoga - ReactNativeBlur (4.5.3): - boost - DoubleConversion @@ -2914,7 +2999,7 @@ PODS: - RNWorklets - SocketRocket - Yoga - - RNScreens (4.20.0): + - RNScreens (4.22.0): - boost - DoubleConversion - fast_float @@ -2941,10 +3026,10 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNScreens/common (= 4.20.0) + - RNScreens/common (= 4.22.0) - SocketRocket - Yoga - - RNScreens/common (4.20.0): + - RNScreens/common (4.22.0): - boost - DoubleConversion - fast_float @@ -3003,7 +3088,7 @@ PODS: - Sentry/HybridSDK (= 8.58.0) - SocketRocket - Yoga - - RNSVG (15.15.1): + - RNSVG (15.15.2): - boost - DoubleConversion - fast_float @@ -3029,10 +3114,10 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNSVG/common (= 15.15.1) + - RNSVG/common (= 15.15.2) - SocketRocket - Yoga - - RNSVG/common (15.15.1): + - RNSVG/common (15.15.2): - boost - DoubleConversion - fast_float @@ -3174,6 +3259,7 @@ DEPENDENCIES: - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) - hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`) - libwebp (= 1.3.2) + - NitroModules (from `../node_modules/react-native-nitro-modules`) - RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) - RCTDeprecation (from `../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation`) - RCTRequired (from `../node_modules/react-native/Libraries/Required`) @@ -3251,6 +3337,7 @@ DEPENDENCIES: - ReactAppDependencyProvider (from `build/generated/ios/ReactAppDependencyProvider`) - ReactCodegen (from `build/generated/ios/ReactCodegen`) - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) + - "ReactNativeAutoPlay (from `../node_modules/@iternio/react-native-auto-play`)" - "ReactNativeBlur (from `../node_modules/@sbaiahmed1/react-native-blur`)" - "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)" - "RNDateTimePicker (from `../node_modules/@react-native-community/datetimepicker`)" @@ -3295,6 +3382,8 @@ EXTERNAL SOURCES: hermes-engine: :podspec: "../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec" :tag: hermes-v0.14.0 + NitroModules: + :path: "../node_modules/react-native-nitro-modules" RCT-Folly: :podspec: "../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec" RCTDeprecation: @@ -3447,6 +3536,8 @@ EXTERNAL SOURCES: :path: build/generated/ios/ReactCodegen ReactCommon: :path: "../node_modules/react-native/ReactCommon" + ReactNativeAutoPlay: + :path: "../node_modules/@iternio/react-native-auto-play" ReactNativeBlur: :path: "../node_modules/@sbaiahmed1/react-native-blur" RNCAsyncStorage: @@ -3485,6 +3576,7 @@ SPEC CHECKSUMS: libavif: 84bbb62fb232c3018d6f1bab79beea87e35de7b7 libdav1d: 23581a4d8ec811ff171ed5e2e05cd27bad64c39f libwebp: 1786c9f4ff8a279e4dac1e8f385004d5fc253009 + NitroModules: e8ec707a245a85cf1eb4289f91a9372f9590a897 RCT-Folly: 846fda9475e61ec7bcbf8a3fe81edfcaeb090669 RCTDeprecation: a41bbdd9af30bf2e5715796b313e44ec43eefff1 RCTRequired: 7be34aabb0b77c3cefe644528df0fa0afad4e4d0 @@ -3523,7 +3615,7 @@ SPEC CHECKSUMS: React-Mapbuffer: 7b72a669e94662359dad4f42b5af005eb24b4e83 React-microtasksnativemodule: cdc02da075f2857803ed63f24f5f72fc40e094c0 react-native-accessibility-settings: 8803c0bd7e1724269bf0ac5064be51773c05df9f - react-native-netinfo: cec9c4e86083cb5b6aba0e0711f563e2fbbff187 + react-native-netinfo: 57447b5a45c98808f8eae292cf641f3d91d13830 react-native-safe-area-context: c00143b4823773bba23f2f19f85663ae89ceb460 react-native-skia: b60d0b9d9aea330baef956b7f015d0af2b42076d react-native-track-player: dd56f9948e03756a217c75e0a61aa04aab65b5a1 @@ -3561,6 +3653,7 @@ SPEC CHECKSUMS: ReactAppDependencyProvider: 0eb286cc274abb059ee601b862ebddac2e681d01 ReactCodegen: 3d48510bcef445f6403c0004047d4d9cbb915435 ReactCommon: ac934cb340aee91282ecd6f273a26d24d4c55cae + ReactNativeAutoPlay: c7130c09e5c07962d26fcee8dfa53ebc78a40eb4 ReactNativeBlur: 500e90ffcc66e89a3a27a6f3a50a7b765ac1f145 RNCAsyncStorage: 29f0230e1a25f36c20b05f65e2eb8958d6526e82 RNDateTimePicker: 97a86d7c8b51f2d51f694a9dddfe5996dfd9a407 @@ -3569,9 +3662,9 @@ SPEC CHECKSUMS: RNGestureHandler: cd4be101cfa17ea6bbd438710caa02e286a84381 RNLocalize: 23e6689ec46c6cba49eb8cde959e30e49160a35b RNReanimated: 292cd58688552a22b3fc1cefcfbc49b336dfed68 - RNScreens: 714e10b6b554f7dc7ad9f78dcf36dc8e3fc73415 + RNScreens: c77cc7a5d1fbc23f0725b54e96eec81118453382 RNSentry: a4c81a5c5bcd58d765126726432348e60cd7183d - RNSVG: ba3c3bc3cf413f32d1a7012274fe503c6fc1eadd + RNSVG: a4ea97e14b7a2221d85091a6b3128f807445a7ec RNWorklets: 01efdd402d236a13651ea5ea5437ca85a44e7afa SDWebImage: e9c98383c7572d713c1a0d7dd2783b10599b9838 SDWebImageAVIFCoder: afe194a084e851f70228e4be35ef651df0fc5c57 diff --git a/package.json b/package.json index a88a524..b45841c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "fintunes", "version": "2.5.0", - "main": "src/index.js", + "main": "index.js", "private": true, "scripts": { "android": "react-native run-android", @@ -13,6 +13,7 @@ }, "dependencies": { "@d11/react-native-fast-image": "8.13.0", + "@iternio/react-native-auto-play": "0.2.2", "@react-native-async-storage/async-storage": "^2.2.0", "@react-native-community/datetimepicker": "^8.6.0", "@react-native-community/netinfo": "^11.4.1", @@ -41,6 +42,7 @@ "react-native-gesture-handler": "^2.30.0", "react-native-localize": "3.6.1", "react-native-modal-datetime-picker": "18.0.0", + "react-native-nitro-modules": "^0.33.2", "react-native-reanimated": "^4.2.1", "react-native-safe-area-context": "^5.6.2", "react-native-screens": "^4.20.0", diff --git a/patches/@iternio__react-native-auto-play.patch b/patches/@iternio__react-native-auto-play.patch new file mode 100644 index 0000000..2f11930 --- /dev/null +++ b/patches/@iternio__react-native-auto-play.patch @@ -0,0 +1,114 @@ +diff --git a/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/AndroidAutoScreen.kt b/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/AndroidAutoScreen.kt +index 19eb2de81576c4c9df9f0353f9dc7aef3f5fd1b5..8b9db9044e40c405fb8d166829eebe8391357150 100644 +--- a/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/AndroidAutoScreen.kt ++++ b/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/AndroidAutoScreen.kt +@@ -124,7 +124,14 @@ class AndroidAutoScreen( + } + + fun getScreenManager(): ScreenManager? { +- return screens[AndroidAutoSession.ROOT_SESSION]?.screenManager ++ // First try to get the ROOT_SESSION screen ++ val rootScreen = screens[AndroidAutoSession.ROOT_SESSION] ++ if (rootScreen != null) { ++ return rootScreen.screenManager ++ } ++ // If ROOT_SESSION doesn't exist, get any screen's screenManager ++ // (after setRootTemplate, the root might be under a different key) ++ return screens.values.firstOrNull()?.screenManager + } + + fun invalidateScreens() { +diff --git a/ios/extensions/CarPlayTemplateExtensions.swift b/ios/extensions/CarPlayTemplateExtensions.swift +index e15d37c34acc692ead9a39e962242e46e3aea525..73ac36aeee867dca7e8c8a6bbe9f79cc254a5e30 100644 +--- a/ios/extensions/CarPlayTemplateExtensions.swift ++++ b/ios/extensions/CarPlayTemplateExtensions.swift +@@ -18,8 +18,8 @@ extension CPTemplate { + self.init() + initTemplate(template: self, id: id) + } +- @objc var id: String { +- return (self.userInfo as? [String: Any])?["id"] as! String ++ @objc var id: String? { ++ return (self.userInfo as? [String: Any])?["id"] as? String + } + } + +diff --git a/ios/scenes/AutoPlayInterfaceController.swift b/ios/scenes/AutoPlayInterfaceController.swift +index 5008b1ed3790e2d2798317aaeb4df3e65378dbce..927288c8a13bcdbc370cb48c3868c7aa57d89e33 100644 +--- a/ios/scenes/AutoPlayInterfaceController.swift ++++ b/ios/scenes/AutoPlayInterfaceController.swift +@@ -90,7 +90,9 @@ class AutoPlayInterfaceController: NSObject, CPInterfaceControllerDelegate { + var templateIds: [String] = [] + + templates.forEach { template in +- let templateId = template.id ++ guard let templateId = template.id else { ++ return ++ } + + if templateId == rootTemplateId { + return +@@ -117,12 +119,12 @@ class AutoPlayInterfaceController: NSObject, CPInterfaceControllerDelegate { + { + guard + let template = interfaceController.templates.first( +- where: { +- templateId == $0.id ++ where: { template in ++ template.id == templateId + }) + else { return [] } + +- var templateIds: [String] = interfaceController.templates.map { ++ var templateIds: [String] = interfaceController.templates.compactMap { + template in template.id + } + +@@ -173,7 +175,10 @@ class AutoPlayInterfaceController: NSObject, CPInterfaceControllerDelegate { + _ aTemplate: CPTemplate, + animated: Bool + ) { +- let templateId = aTemplate.id ++ guard let templateId = aTemplate.id else { ++ // System template without ID (e.g., CPNowPlayingTemplate) ++ return ++ } + + try? RootModule.withAutoPlayTemplate(templateId: templateId) { + (template: AutoPlayTemplate) in +@@ -187,7 +192,10 @@ class AutoPlayInterfaceController: NSObject, CPInterfaceControllerDelegate { + _ aTemplate: CPTemplate, + animated: Bool + ) { +- let templateId = aTemplate.id ++ guard let templateId = aTemplate.id else { ++ // System template without ID (e.g., CPNowPlayingTemplate) ++ return ++ } + + if rootTemplateId == templateId { + // this makes sure we purge outdated CPSearchTemplate since that one can be popped on with a CarPlay native button we can not intercept +@@ -210,7 +218,10 @@ class AutoPlayInterfaceController: NSObject, CPInterfaceControllerDelegate { + _ aTemplate: CPTemplate, + animated: Bool + ) { +- let templateId = aTemplate.id ++ guard let templateId = aTemplate.id else { ++ // System template without ID (e.g., CPNowPlayingTemplate) ++ return ++ } + + try? RootModule.withAutoPlayTemplate( + templateId: templateId, +@@ -228,7 +239,10 @@ class AutoPlayInterfaceController: NSObject, CPInterfaceControllerDelegate { + _ aTemplate: CPTemplate, + animated: Bool + ) { +- let templateId = aTemplate.id ++ guard let templateId = aTemplate.id else { ++ // System template without ID (e.g., CPNowPlayingTemplate) ++ return ++ } + + try? RootModule.withAutoPlayTemplate( + templateId: templateId, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 995c8ea..58e668d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,6 +5,9 @@ settings: excludeLinksFromLockfile: false patchedDependencies: + '@iternio/react-native-auto-play': + hash: 2ab8be0081e20edc9203f48ede10d78ffcdb6eb30798e0d0b92d8d29b19777fc + path: patches/@iternio__react-native-auto-play.patch react-native-track-player: hash: e7b3a1d9dfe94a2a6a4067d693dff8c4d67147057264361b27db20fd497ef184 path: patches/react-native-track-player.patch @@ -19,6 +22,9 @@ importers: '@d11/react-native-fast-image': specifier: 8.13.0 version: 8.13.0(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) + '@iternio/react-native-auto-play': + specifier: 0.2.2 + version: 0.2.2(patch_hash=2ab8be0081e20edc9203f48ede10d78ffcdb6eb30798e0d0b92d8d29b19777fc)(react-native-nitro-modules@0.33.7(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) '@react-native-async-storage/async-storage': specifier: ^2.2.0 version: 2.2.0(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3)) @@ -27,10 +33,10 @@ importers: version: 8.6.0(expo@53.0.7(@babel/core@7.28.6)(react-native-webview@13.16.0(patch_hash=2bf2ed0feba988e3f2d2ebc497218220b351ab5157dc5fedf7085f8525cacb65)(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) '@react-native-community/netinfo': specifier: ^11.4.1 - version: 11.4.1(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3)) + version: 11.5.2(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) '@react-navigation/bottom-tabs': specifier: 7.10.1 - version: 7.10.1(@react-navigation/native@7.1.28(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native-safe-area-context@5.6.2(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native-screens@4.20.0(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) + version: 7.10.1(@react-navigation/native@7.1.28(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native-safe-area-context@5.6.2(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native-screens@4.22.0(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) '@react-navigation/elements': specifier: 2.9.5 version: 2.9.5(@react-navigation/native@7.1.28(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native-safe-area-context@5.6.2(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) @@ -39,10 +45,10 @@ importers: version: 7.1.28(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) '@react-navigation/native-stack': specifier: 7.10.1 - version: 7.10.1(@react-navigation/native@7.1.28(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native-safe-area-context@5.6.2(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native-screens@4.20.0(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) + version: 7.10.1(@react-navigation/native@7.1.28(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native-safe-area-context@5.6.2(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native-screens@4.22.0(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) '@react-navigation/stack': specifier: ^7.6.16 - version: 7.6.16(ec029809ebb85d37416ce527f5a2921e) + version: 7.7.1(3a89e20a6c76724265963005993bf895) '@reduxjs/toolkit': specifier: 2.11.2 version: 2.11.2(react-redux@9.2.0(@types/react@19.2.9)(react@19.2.3)(redux@5.0.1))(react@19.2.3) @@ -103,6 +109,9 @@ importers: react-native-modal-datetime-picker: specifier: 18.0.0 version: 18.0.0(@react-native-community/datetimepicker@8.6.0(expo@53.0.7(@babel/core@7.28.6)(react-native-webview@13.16.0(patch_hash=2bf2ed0feba988e3f2d2ebc497218220b351ab5157dc5fedf7085f8525cacb65)(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3)) + react-native-nitro-modules: + specifier: ^0.33.2 + version: 0.33.7(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) react-native-reanimated: specifier: ^4.2.1 version: 4.2.1(react-native-worklets@0.7.2(@babel/core@7.28.6)(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) @@ -111,13 +120,13 @@ importers: version: 5.6.2(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) react-native-screens: specifier: ^4.20.0 - version: 4.20.0(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) + version: 4.22.0(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) react-native-shadow-2: specifier: 7.1.2 - version: 7.1.2(react-native-svg@15.15.1(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3)) + version: 7.1.2(react-native-svg@15.15.2(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3)) react-native-svg: specifier: ^15.15.1 - version: 15.15.1(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) + version: 15.15.2(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) react-native-track-player: specifier: 4.1.2 version: 4.1.2(patch_hash=e7b3a1d9dfe94a2a6a4067d693dff8c4d67147057264361b27db20fd497ef184)(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) @@ -145,7 +154,7 @@ importers: version: 7.28.6 '@babel/preset-env': specifier: ^7.28.6 - version: 7.28.6(@babel/core@7.28.6) + version: 7.29.0(@babel/core@7.28.6) '@babel/runtime': specifier: 7.28.6 version: 7.28.6 @@ -157,7 +166,7 @@ importers: version: 0.83.1(@babel/core@7.28.6) '@react-native/eslint-config': specifier: ^0.83.1 - version: 0.83.1(eslint@8.57.1)(jest@29.7.0(@types/node@25.1.0))(prettier@3.8.1)(typescript@5.9.3) + version: 0.83.1(eslint@8.57.1)(jest@29.7.0(@types/node@25.2.1))(prettier@3.8.1)(typescript@5.9.3) '@react-native/metro-config': specifier: ^0.83.1 version: 0.83.1(@babel/core@7.28.6) @@ -181,7 +190,7 @@ importers: version: 4.17.23 '@types/node': specifier: ^25.1.0 - version: 25.1.0 + version: 25.2.1 '@types/react': specifier: 19.2.9 version: 19.2.9 @@ -205,13 +214,13 @@ importers: version: 7.0.1(eslint@8.57.1) jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@25.1.0) + version: 29.7.0(@types/node@25.2.1) metro-react-native-babel-transformer: specifier: 0.77.0 version: 0.77.0(@babel/core@7.28.6) react-native-svg-transformer: specifier: 1.5.2 - version: 1.5.2(react-native-svg@15.15.1(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(typescript@5.9.3) + version: 1.5.2(react-native-svg@15.15.2(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(typescript@5.9.3) tslib: specifier: 2.8.1 version: 2.8.1 @@ -236,10 +245,18 @@ packages: resolution: {integrity: sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==} engines: {node: '>=6.9.0'} + '@babel/code-frame@7.29.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + engines: {node: '>=6.9.0'} + '@babel/compat-data@7.28.6': resolution: {integrity: sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==} engines: {node: '>=6.9.0'} + '@babel/compat-data@7.29.0': + resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==} + engines: {node: '>=6.9.0'} + '@babel/core@7.28.6': resolution: {integrity: sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==} engines: {node: '>=6.9.0'} @@ -255,6 +272,10 @@ packages: resolution: {integrity: sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==} engines: {node: '>=6.9.0'} + '@babel/generator@7.29.1': + resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} + engines: {node: '>=6.9.0'} + '@babel/helper-annotate-as-pure@7.27.3': resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} engines: {node: '>=6.9.0'} @@ -355,6 +376,11 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.29.0': + resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5': resolution: {integrity: sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==} engines: {node: '>=6.9.0'} @@ -399,8 +425,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-proposal-decorators@7.28.6': - resolution: {integrity: sha512-RVdFPPyY9fCRAX68haPmOk2iyKW8PKJFthmm8NeSI3paNxKWGZIn99+VbIf0FrtCpFnPgnpF/L48tadi617ULg==} + '@babel/plugin-proposal-decorators@7.29.0': + resolution: {integrity: sha512-CVBVv3VY/XRMxRYq5dwr2DS7/MvqPm23cOCjbwNnVrfOqcWlnefua1uUs0sjdKOGjvPUG633o07uWzJq4oI6dA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -590,6 +616,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-async-generator-functions@7.29.0': + resolution: {integrity: sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-async-to-generator@7.28.6': resolution: {integrity: sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==} engines: {node: '>=6.9.0'} @@ -662,8 +694,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.28.6': - resolution: {integrity: sha512-5suVoXjC14lUN6ZL9OLKIHCNVWCrqGqlmEp/ixdXjvgnEl/kauLvvMO/Xw9NyMc95Joj1AeLVPVMvibBgSoFlA==} + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.0': + resolution: {integrity: sha512-zBPcW2lFGxdiD8PUnPwJjag2J9otbcLQzvbiOzDxpYXyCuYX9agOwMPGn1prVH0a4qzhCKu24rlH4c1f7yA8rw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -746,8 +778,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-modules-systemjs@7.28.5': - resolution: {integrity: sha512-vn5Jma98LCOeBy/KpeQhXcV2WZgaRUtjwQmjoBuLNlOmkg0fB5pdvYVeWRYI69wWKwK2cD1QbMiUQnoujWvrew==} + '@babel/plugin-transform-modules-systemjs@7.29.0': + resolution: {integrity: sha512-PrujnVFbOdUpw4UHiVwKvKRLMMic8+eC0CuNlxjsyZUiBjhFdPsewdXCkveh2KqBA9/waD0W1b4hXSOBQJezpQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -764,6 +796,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0 + '@babel/plugin-transform-named-capturing-groups-regex@7.29.0': + resolution: {integrity: sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@babel/plugin-transform-new-target@7.27.1': resolution: {integrity: sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==} engines: {node: '>=6.9.0'} @@ -884,6 +922,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-regenerator@7.29.0': + resolution: {integrity: sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-regexp-modifiers@7.28.6': resolution: {integrity: sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg==} engines: {node: '>=6.9.0'} @@ -902,6 +946,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-runtime@7.29.0': + resolution: {integrity: sha512-jlaRT5dJtMaMCV6fAuLbsQMSwz/QkvaHOHOSXRitGGwSpR1blCY4KUKoyP2tYO8vJcqYe8cEj96cqSztv3uF9w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-shorthand-properties@7.27.1': resolution: {integrity: sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==} engines: {node: '>=6.9.0'} @@ -962,8 +1012,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/preset-env@7.28.6': - resolution: {integrity: sha512-GaTI4nXDrs7l0qaJ6Rg06dtOXTBCG6TMDB44zbqofCIC4PqC7SEvmFFtpxzCDw9W5aJ7RKVshgXTLvLdBFV/qw==} + '@babel/preset-env@7.29.0': + resolution: {integrity: sha512-fNEdfc0yi16lt6IZo2Qxk3knHVdfMYX33czNb4v8yWhemoBhibCpQK/uYHtSKIiO+p/zd3+8fYVXhQdOVV608w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1003,10 +1053,18 @@ packages: resolution: {integrity: sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==} engines: {node: '>=6.9.0'} + '@babel/traverse@7.29.0': + resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + engines: {node: '>=6.9.0'} + '@babel/types@7.28.6': resolution: {integrity: sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==} engines: {node: '>=6.9.0'} + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + engines: {node: '>=6.9.0'} + '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} @@ -1164,6 +1222,13 @@ packages: resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} engines: {node: '>=8'} + '@iternio/react-native-auto-play@0.2.2': + resolution: {integrity: sha512-Jhf5lvrfASTPaLG4CLW0F97ZwjR51CHU4NJVQNuv47eCkyjnhCEsJLgtMUI3rdceQfx9cqpeNhs7T6jzzKN4SA==} + peerDependencies: + react: '*' + react-native: '*' + react-native-nitro-modules: '*' + '@jest/console@29.7.0': resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1352,9 +1417,10 @@ packages: react-native-windows: optional: true - '@react-native-community/netinfo@11.4.1': - resolution: {integrity: sha512-B0BYAkghz3Q2V09BF88RA601XursIEA111tnc2JOaN7axJWmNefmfjZqw/KdSxKZp7CZUuPpjBmz/WCR9uaHYg==} + '@react-native-community/netinfo@11.5.2': + resolution: {integrity: sha512-/g0m65BtX9HU+bPiCH2517bOHpEIUsGrWFXDzi1a5nNKn5KujQgm04WhL7/OSXWKHyrT8VVtUoJA0XKRxueBpQ==} peerDependencies: + react: '*' react-native: '>=0.59' '@react-native/assets-registry@0.83.1': @@ -1518,8 +1584,8 @@ packages: '@react-navigation/routers@7.5.3': resolution: {integrity: sha512-1tJHg4KKRJuQ1/EvJxatrMef3NZXEPzwUIUZ3n1yJ2t7Q97siwRtbynRpQG9/69ebbtiZ8W3ScOZF/OmhvM4Rg==} - '@react-navigation/stack@7.6.16': - resolution: {integrity: sha512-kYmogwU2jpxmjIrGO2P9PJCwgBHiju7OdItRkhFEHHppwU4jJQx/ViJtJ9ib3G4pqpurFdmqn1YsVB4bf7Zmxw==} + '@react-navigation/stack@7.7.1': + resolution: {integrity: sha512-aVxwWkl7tMgfmFW3iiklcTvu6Bt/j5lcUzkyzn16Q+g83Ng/mNx8LoBbjpASrXTnT4tgzINLdEtxdZ7EI1+Sdw==} peerDependencies: '@react-navigation/native': ^7.1.28 react: '>= 18.2.0' @@ -1859,8 +1925,8 @@ packages: '@types/lodash@4.17.23': resolution: {integrity: sha512-RDvF6wTulMPjrNdCoYRC8gNR880JNGT8uB+REUpC2Ns4pRqQJhGz90wh7rgdXDPpCczF3VGktDuFGVnz8zP7HA==} - '@types/node@25.1.0': - resolution: {integrity: sha512-t7frlewr6+cbx+9Ohpl0NOTKXZNV9xHRmNOvql47BFJKcEG1CxtxlPEEe+gR9uhVWM4DwhnvTF110mIL4yP9RA==} + '@types/node@25.2.1': + resolution: {integrity: sha512-CPrnr8voK8vC6eEtyRzvMpgp3VyVRhgclonE7qYi6P9sXwYb59ucfrnmFBTaP0yUi8Gk4yZg/LlTJULGxvTNsg==} '@types/react@19.2.9': resolution: {integrity: sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA==} @@ -2156,6 +2222,11 @@ packages: peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + babel-plugin-polyfill-corejs3@0.14.0: + resolution: {integrity: sha512-AvDcMxJ34W4Wgy4KBIIePQTAOP1Ie2WFwkQp3dB7FQ/f0lI5+nM96zUnYEOE1P9sEg0es5VCP0HxiWu5fUHZAQ==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + babel-plugin-polyfill-regenerator@0.6.6: resolution: {integrity: sha512-hYm+XLYRMvupxiQzrvXUj7YyvFFVfv5gI0R71AJzudg1g2AI2vyCPPIFEBjk162/wFzti3inBHo7isWFuEVS/A==} peerDependencies: @@ -3131,15 +3202,17 @@ packages: glob@10.5.0: resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me glob@9.3.5: resolution: {integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==} engines: {node: '>=16 || 14 >=14.17'} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me globals@13.24.0: resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} @@ -4459,6 +4532,12 @@ packages: '@react-native-community/datetimepicker': '>=6.7.0' react-native: '>=0.65.0' + react-native-nitro-modules@0.33.7: + resolution: {integrity: sha512-WepMobWe4j1Ae5GQ5RxYGBdBpJBwzP6zaOxJ7r6nhbY5iyl01DL3Gsh4gk8edzNFRuAh1rvXDAHIipq8SahxeQ==} + peerDependencies: + react: '*' + react-native: '*' + react-native-reanimated@4.2.1: resolution: {integrity: sha512-/NcHnZMyOvsD/wYXug/YqSKw90P9edN0kEPL5lP4PFf1aQ4F1V7MKe/E0tvfkXKIajy3Qocp5EiEnlcrK/+BZg==} peerDependencies: @@ -4472,8 +4551,8 @@ packages: react: '*' react-native: '*' - react-native-screens@4.20.0: - resolution: {integrity: sha512-wg3ILSd8yHM2YMsWqDjr1+Rxj1qn9CrzZ8qAqDXYd+jf6p3GIMwi+NugFUbRBRZMXs3MNEXCS1vAkvc2ZwpaAA==} + react-native-screens@4.22.0: + resolution: {integrity: sha512-f1AtzxmgZlKeoioWzdNdQ/4SuqZ/w370KCBnA666pN8UZ33X4PkVJ/9v9y2y5sj0cXKEcgeN3Br9lmeDRQ735A==} peerDependencies: react: '*' react-native: '*' @@ -4490,8 +4569,8 @@ packages: react-native: '>=0.59.0' react-native-svg: '>=12.0.0' - react-native-svg@15.15.1: - resolution: {integrity: sha512-ZUD1xwc3Hwo4cOmOLumjJVoc7lEf9oQFlHnLmgccLC19fNm6LVEdtB+Cnip6gEi0PG3wfvVzskViEtrySQP8Fw==} + react-native-svg@15.15.2: + resolution: {integrity: sha512-lpaSwA2i+eLvcEdDZyGgMEInQW99K06zjJqfMFblE0yxI0SCN5E4x6in46f0IYi6i3w2t2aaq3oOnyYBe+bo4w==} peerDependencies: react: '*' react-native: '*' @@ -4509,6 +4588,10 @@ packages: shaka-player: optional: true + react-native-uuid@2.0.3: + resolution: {integrity: sha512-f/YfIS2f5UB+gut7t/9BKGSCYbRA9/74A5R1MDp+FLYsuS+OSWoiM/D8Jko6OJB6Jcu3v6ONuddvZKHdIGpeiw==} + engines: {node: '>=10.0.0', npm: '>=6.0.0'} + react-native-webview@13.16.0: resolution: {integrity: sha512-Nh13xKZWW35C0dbOskD7OX01nQQavOzHbCw9XoZmar4eXCo7AvrYJ0jlUfRVVIJzqINxHlpECYLdmAdFsl9xDA==} peerDependencies: @@ -4725,6 +4808,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.7.4: + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + engines: {node: '>=10'} + hasBin: true + send@0.19.2: resolution: {integrity: sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==} engines: {node: '>= 0.8.0'} @@ -5384,8 +5472,16 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 + '@babel/code-frame@7.29.0': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + '@babel/compat-data@7.28.6': {} + '@babel/compat-data@7.29.0': {} + '@babel/core@7.28.6': dependencies: '@babel/code-frame': 7.28.6 @@ -5422,6 +5518,14 @@ snapshots: '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 + '@babel/generator@7.29.1': + dependencies: + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + '@babel/helper-annotate-as-pure@7.27.3': dependencies: '@babel/types': 7.28.6 @@ -5467,7 +5571,7 @@ snapshots: '@babel/helper-environment-visitor@7.24.7': dependencies: - '@babel/types': 7.28.6 + '@babel/types': 7.29.0 '@babel/helper-globals@7.28.0': {} @@ -5514,7 +5618,7 @@ snapshots: '@babel/core': 7.28.6 '@babel/helper-member-expression-to-functions': 7.28.5 '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color @@ -5556,6 +5660,10 @@ snapshots: dependencies: '@babel/types': 7.28.6 + '@babel/parser@7.29.0': + dependencies: + '@babel/types': 7.29.0 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5(@babel/core@7.28.6)': dependencies: '@babel/core': 7.28.6 @@ -5609,7 +5717,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/plugin-proposal-decorators@7.28.6(@babel/core@7.28.6)': + '@babel/plugin-proposal-decorators@7.29.0(@babel/core@7.28.6)': dependencies: '@babel/core': 7.28.6 '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.28.6) @@ -5638,7 +5746,7 @@ snapshots: '@babel/plugin-proposal-object-rest-spread@7.20.7(@babel/core@7.28.6)': dependencies: - '@babel/compat-data': 7.28.6 + '@babel/compat-data': 7.29.0 '@babel/core': 7.28.6 '@babel/helper-compilation-targets': 7.28.6 '@babel/helper-plugin-utils': 7.28.6 @@ -5795,6 +5903,15 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-async-generator-functions@7.29.0(@babel/core@7.28.6)': + dependencies: + '@babel/core': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.6) + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + '@babel/plugin-transform-async-to-generator@7.28.6(@babel/core@7.28.6)': dependencies: '@babel/core': 7.28.6 @@ -5887,7 +6004,7 @@ snapshots: '@babel/core': 7.28.6 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.28.6(@babel/core@7.28.6)': + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.0(@babel/core@7.28.6)': dependencies: '@babel/core': 7.28.6 '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.6) @@ -5975,13 +6092,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-systemjs@7.28.5(@babel/core@7.28.6)': + '@babel/plugin-transform-modules-systemjs@7.29.0(@babel/core@7.28.6)': dependencies: '@babel/core': 7.28.6 '@babel/helper-module-transforms': 7.28.6(@babel/core@7.28.6) '@babel/helper-plugin-utils': 7.28.6 '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color @@ -5999,6 +6116,12 @@ snapshots: '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.6) '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-named-capturing-groups-regex@7.29.0(@babel/core@7.28.6)': + dependencies: + '@babel/core': 7.28.6 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.6) + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.28.6)': dependencies: '@babel/core': 7.28.6 @@ -6132,6 +6255,11 @@ snapshots: '@babel/core': 7.28.6 '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-regenerator@7.29.0(@babel/core@7.28.6)': + dependencies: + '@babel/core': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-regexp-modifiers@7.28.6(@babel/core@7.28.6)': dependencies: '@babel/core': 7.28.6 @@ -6155,6 +6283,19 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-runtime@7.29.0(@babel/core@7.28.6)': + dependencies: + '@babel/core': 7.28.6 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + babel-plugin-polyfill-corejs2: 0.4.15(@babel/core@7.28.6) + babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.28.6) + babel-plugin-polyfill-regenerator: 0.6.6(@babel/core@7.28.6) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + optional: true + '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.28.6)': dependencies: '@babel/core': 7.28.6 @@ -6217,9 +6358,9 @@ snapshots: '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.6) '@babel/helper-plugin-utils': 7.28.6 - '@babel/preset-env@7.28.6(@babel/core@7.28.6)': + '@babel/preset-env@7.29.0(@babel/core@7.28.6)': dependencies: - '@babel/compat-data': 7.28.6 + '@babel/compat-data': 7.29.0 '@babel/core': 7.28.6 '@babel/helper-compilation-targets': 7.28.6 '@babel/helper-plugin-utils': 7.28.6 @@ -6234,7 +6375,7 @@ snapshots: '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.28.6) '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.28.6) '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.28.6) - '@babel/plugin-transform-async-generator-functions': 7.28.6(@babel/core@7.28.6) + '@babel/plugin-transform-async-generator-functions': 7.29.0(@babel/core@7.28.6) '@babel/plugin-transform-async-to-generator': 7.28.6(@babel/core@7.28.6) '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.28.6) '@babel/plugin-transform-block-scoping': 7.28.6(@babel/core@7.28.6) @@ -6245,7 +6386,7 @@ snapshots: '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.28.6) '@babel/plugin-transform-dotall-regex': 7.28.6(@babel/core@7.28.6) '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.28.6) - '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.28.6(@babel/core@7.28.6) + '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.29.0(@babel/core@7.28.6) '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.28.6) '@babel/plugin-transform-explicit-resource-management': 7.28.6(@babel/core@7.28.6) '@babel/plugin-transform-exponentiation-operator': 7.28.6(@babel/core@7.28.6) @@ -6258,9 +6399,9 @@ snapshots: '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.28.6) '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.28.6) '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-transform-modules-systemjs': 7.28.5(@babel/core@7.28.6) + '@babel/plugin-transform-modules-systemjs': 7.29.0(@babel/core@7.28.6) '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.28.6) - '@babel/plugin-transform-named-capturing-groups-regex': 7.27.1(@babel/core@7.28.6) + '@babel/plugin-transform-named-capturing-groups-regex': 7.29.0(@babel/core@7.28.6) '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.28.6) '@babel/plugin-transform-nullish-coalescing-operator': 7.28.6(@babel/core@7.28.6) '@babel/plugin-transform-numeric-separator': 7.28.6(@babel/core@7.28.6) @@ -6272,7 +6413,7 @@ snapshots: '@babel/plugin-transform-private-methods': 7.28.6(@babel/core@7.28.6) '@babel/plugin-transform-private-property-in-object': 7.28.6(@babel/core@7.28.6) '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.28.6) - '@babel/plugin-transform-regenerator': 7.28.6(@babel/core@7.28.6) + '@babel/plugin-transform-regenerator': 7.29.0(@babel/core@7.28.6) '@babel/plugin-transform-regexp-modifiers': 7.28.6(@babel/core@7.28.6) '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.28.6) '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.28.6) @@ -6286,7 +6427,7 @@ snapshots: '@babel/plugin-transform-unicode-sets-regex': 7.28.6(@babel/core@7.28.6) '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.28.6) babel-plugin-polyfill-corejs2: 0.4.15(@babel/core@7.28.6) - babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.28.6) + babel-plugin-polyfill-corejs3: 0.14.0(@babel/core@7.28.6) babel-plugin-polyfill-regenerator: 0.6.6(@babel/core@7.28.6) core-js-compat: 3.48.0 semver: 6.3.1 @@ -6356,11 +6497,28 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/traverse@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.29.0 + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + '@babel/types@7.28.6': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 + '@babel/types@7.29.0': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + '@bcoe/v8-coverage@0.2.3': {} '@d11/react-native-fast-image@8.13.0(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3)': @@ -6455,7 +6613,7 @@ snapshots: resolve: 1.22.11 resolve-from: 5.0.0 resolve.exports: 2.0.3 - semver: 7.7.3 + semver: 7.7.4 send: 0.19.2 slugify: 1.6.6 source-map-support: 0.5.21 @@ -6490,7 +6648,7 @@ snapshots: getenv: 2.0.0 glob: 10.5.0 resolve-from: 5.0.0 - semver: 7.7.3 + semver: 7.7.4 slash: 3.0.0 slugify: 1.6.6 xcode: 3.0.1 @@ -6510,7 +6668,7 @@ snapshots: getenv: 2.0.0 glob: 10.5.0 resolve-from: 5.0.0 - semver: 7.7.3 + semver: 7.7.4 slash: 3.0.0 slugify: 1.6.6 xcode: 3.0.1 @@ -6534,7 +6692,7 @@ snapshots: require-from-string: 2.0.2 resolve-from: 5.0.0 resolve-workspace-root: 2.0.1 - semver: 7.7.3 + semver: 7.7.4 slugify: 1.6.6 sucrase: 3.35.0 transitivePeerDependencies: @@ -6571,7 +6729,7 @@ snapshots: minimatch: 9.0.5 p-limit: 3.1.0 resolve-from: 5.0.0 - semver: 7.7.3 + semver: 7.7.4 transitivePeerDependencies: - supports-color optional: true @@ -6584,7 +6742,7 @@ snapshots: jimp-compact: 0.16.1 parse-png: 2.1.0 resolve-from: 5.0.0 - semver: 7.7.3 + semver: 7.7.4 temp-dir: 2.0.0 unique-string: 2.0.0 optional: true @@ -6604,9 +6762,9 @@ snapshots: '@expo/metro-config@0.20.13': dependencies: '@babel/core': 7.28.6 - '@babel/generator': 7.28.6 - '@babel/parser': 7.28.6 - '@babel/types': 7.28.6 + '@babel/generator': 7.29.1 + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 '@expo/config': 11.0.13 '@expo/env': 1.0.7 '@expo/json-file': 9.1.5 @@ -6659,7 +6817,7 @@ snapshots: '@react-native/normalize-colors': 0.79.6 debug: 4.4.3 resolve-from: 5.0.0 - semver: 7.7.3 + semver: 7.7.4 xml2js: 0.6.0 transitivePeerDependencies: - supports-color @@ -6688,7 +6846,7 @@ snapshots: '@expo/xcpretty@4.4.0': dependencies: - '@babel/code-frame': 7.28.6 + '@babel/code-frame': 7.29.0 chalk: 4.1.2 js-yaml: 4.1.1 optional: true @@ -6738,10 +6896,17 @@ snapshots: '@istanbuljs/schema@0.1.3': {} + '@iternio/react-native-auto-play@0.2.2(patch_hash=2ab8be0081e20edc9203f48ede10d78ffcdb6eb30798e0d0b92d8d29b19777fc)(react-native-nitro-modules@0.33.7(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3)': + dependencies: + react: 19.2.3 + react-native: 0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3) + react-native-nitro-modules: 0.33.7(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) + react-native-uuid: 2.0.3 + '@jest/console@29.7.0': dependencies: '@jest/types': 29.6.3 - '@types/node': 25.1.0 + '@types/node': 25.2.1 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 @@ -6754,14 +6919,14 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 25.1.0 + '@types/node': 25.2.1 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@25.1.0) + jest-config: 29.7.0(@types/node@25.2.1) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -6792,7 +6957,7 @@ snapshots: dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 25.1.0 + '@types/node': 25.2.1 jest-mock: 29.7.0 '@jest/expect-utils@29.7.0': @@ -6814,7 +6979,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 25.1.0 + '@types/node': 25.2.1 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -6832,7 +6997,7 @@ snapshots: '@jest/pattern@30.0.1': dependencies: - '@types/node': 25.1.0 + '@types/node': 25.2.1 jest-regex-util: 30.0.1 '@jest/reporters@29.7.0': @@ -6843,7 +7008,7 @@ snapshots: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.31 - '@types/node': 25.1.0 + '@types/node': 25.2.1 chalk: 4.1.2 collect-v8-coverage: 1.0.3 exit: 0.1.2 @@ -6917,7 +7082,7 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 25.1.0 + '@types/node': 25.2.1 '@types/yargs': 17.0.35 chalk: 4.1.2 @@ -6927,7 +7092,7 @@ snapshots: '@jest/schemas': 30.0.5 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 25.1.0 + '@types/node': 25.2.1 '@types/yargs': 17.0.35 chalk: 4.1.2 @@ -7117,15 +7282,16 @@ snapshots: optionalDependencies: expo: 53.0.7(@babel/core@7.28.6)(react-native-webview@13.16.0(patch_hash=2bf2ed0feba988e3f2d2ebc497218220b351ab5157dc5fedf7085f8525cacb65)(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) - '@react-native-community/netinfo@11.4.1(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))': + '@react-native-community/netinfo@11.5.2(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3)': dependencies: + react: 19.2.3 react-native: 0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3) '@react-native/assets-registry@0.83.1': {} '@react-native/babel-plugin-codegen@0.79.2(@babel/core@7.28.6)': dependencies: - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.29.0 '@react-native/codegen': 0.79.2(@babel/core@7.28.6) transitivePeerDependencies: - '@babel/core' @@ -7149,7 +7315,7 @@ snapshots: '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.28.6) '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.28.6) '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.28.6) - '@babel/plugin-transform-async-generator-functions': 7.28.6(@babel/core@7.28.6) + '@babel/plugin-transform-async-generator-functions': 7.29.0(@babel/core@7.28.6) '@babel/plugin-transform-async-to-generator': 7.28.6(@babel/core@7.28.6) '@babel/plugin-transform-block-scoping': 7.28.6(@babel/core@7.28.6) '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.28.6) @@ -7162,7 +7328,7 @@ snapshots: '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.28.6) '@babel/plugin-transform-logical-assignment-operators': 7.28.6(@babel/core@7.28.6) '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-transform-named-capturing-groups-regex': 7.27.1(@babel/core@7.28.6) + '@babel/plugin-transform-named-capturing-groups-regex': 7.29.0(@babel/core@7.28.6) '@babel/plugin-transform-nullish-coalescing-operator': 7.28.6(@babel/core@7.28.6) '@babel/plugin-transform-numeric-separator': 7.28.6(@babel/core@7.28.6) '@babel/plugin-transform-object-rest-spread': 7.28.6(@babel/core@7.28.6) @@ -7175,8 +7341,8 @@ snapshots: '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@7.28.6) '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.6) '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.6) - '@babel/plugin-transform-regenerator': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-transform-runtime': 7.28.5(@babel/core@7.28.6) + '@babel/plugin-transform-regenerator': 7.29.0(@babel/core@7.28.6) + '@babel/plugin-transform-runtime': 7.29.0(@babel/core@7.28.6) '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.28.6) '@babel/plugin-transform-spread': 7.28.6(@babel/core@7.28.6) '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.28.6) @@ -7326,7 +7492,7 @@ snapshots: - supports-color - utf-8-validate - '@react-native/eslint-config@0.83.1(eslint@8.57.1)(jest@29.7.0(@types/node@25.1.0))(prettier@3.8.1)(typescript@5.9.3)': + '@react-native/eslint-config@0.83.1(eslint@8.57.1)(jest@29.7.0(@types/node@25.2.1))(prettier@3.8.1)(typescript@5.9.3)': dependencies: '@babel/core': 7.28.6 '@babel/eslint-parser': 7.28.6(@babel/core@7.28.6)(eslint@8.57.1) @@ -7337,7 +7503,7 @@ snapshots: eslint-config-prettier: 8.10.2(eslint@8.57.1) eslint-plugin-eslint-comments: 3.2.0(eslint@8.57.1) eslint-plugin-ft-flow: 2.0.3(@babel/eslint-parser@7.28.6(@babel/core@7.28.6)(eslint@8.57.1))(eslint@8.57.1) - eslint-plugin-jest: 29.12.1(@typescript-eslint/eslint-plugin@8.53.1(@typescript-eslint/parser@8.53.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(jest@29.7.0(@types/node@25.1.0))(typescript@5.9.3) + eslint-plugin-jest: 29.12.1(@typescript-eslint/eslint-plugin@8.53.1(@typescript-eslint/parser@8.53.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(jest@29.7.0(@types/node@25.2.1))(typescript@5.9.3) eslint-plugin-react: 7.37.5(eslint@8.57.1) eslint-plugin-react-hooks: 7.0.1(eslint@8.57.1) eslint-plugin-react-native: 4.1.0(eslint@8.57.1) @@ -7390,7 +7556,7 @@ snapshots: optionalDependencies: '@types/react': 19.2.9 - '@react-navigation/bottom-tabs@7.10.1(@react-navigation/native@7.1.28(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native-safe-area-context@5.6.2(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native-screens@4.20.0(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3)': + '@react-navigation/bottom-tabs@7.10.1(@react-navigation/native@7.1.28(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native-safe-area-context@5.6.2(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native-screens@4.22.0(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3)': dependencies: '@react-navigation/elements': 2.9.5(@react-navigation/native@7.1.28(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native-safe-area-context@5.6.2(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) '@react-navigation/native': 7.1.28(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) @@ -7398,7 +7564,7 @@ snapshots: react: 19.2.3 react-native: 0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3) react-native-safe-area-context: 5.6.2(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) - react-native-screens: 4.20.0(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) + react-native-screens: 4.22.0(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) sf-symbols-typescript: 2.2.0 transitivePeerDependencies: - '@react-native-masked-view/masked-view' @@ -7425,7 +7591,7 @@ snapshots: use-latest-callback: 0.2.6(react@19.2.3) use-sync-external-store: 1.6.0(react@19.2.3) - '@react-navigation/native-stack@7.10.1(@react-navigation/native@7.1.28(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native-safe-area-context@5.6.2(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native-screens@4.20.0(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3)': + '@react-navigation/native-stack@7.10.1(@react-navigation/native@7.1.28(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native-safe-area-context@5.6.2(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native-screens@4.22.0(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3)': dependencies: '@react-navigation/elements': 2.9.5(@react-navigation/native@7.1.28(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native-safe-area-context@5.6.2(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) '@react-navigation/native': 7.1.28(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) @@ -7433,7 +7599,7 @@ snapshots: react: 19.2.3 react-native: 0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3) react-native-safe-area-context: 5.6.2(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) - react-native-screens: 4.20.0(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) + react-native-screens: 4.22.0(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) sf-symbols-typescript: 2.2.0 warn-once: 0.1.1 transitivePeerDependencies: @@ -7453,7 +7619,7 @@ snapshots: dependencies: nanoid: 3.3.11 - '@react-navigation/stack@7.6.16(ec029809ebb85d37416ce527f5a2921e)': + '@react-navigation/stack@7.7.1(3a89e20a6c76724265963005993bf895)': dependencies: '@react-navigation/elements': 2.9.5(@react-navigation/native@7.1.28(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native-safe-area-context@5.6.2(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) '@react-navigation/native': 7.1.28(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) @@ -7462,7 +7628,7 @@ snapshots: react-native: 0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3) react-native-gesture-handler: 2.30.0(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) react-native-safe-area-context: 5.6.2(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) - react-native-screens: 4.20.0(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) + react-native-screens: 4.22.0(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) use-latest-callback: 0.2.6(react@19.2.3) transitivePeerDependencies: - '@react-native-masked-view/masked-view' @@ -7769,7 +7935,7 @@ snapshots: '@types/graceful-fs@4.1.9': dependencies: - '@types/node': 25.1.0 + '@types/node': 25.2.1 '@types/hammerjs@2.0.46': {} @@ -7790,7 +7956,7 @@ snapshots: '@types/lodash@4.17.23': {} - '@types/node@25.1.0': + '@types/node@25.2.1': dependencies: undici-types: 7.16.0 @@ -8151,7 +8317,7 @@ snapshots: babel-plugin-jest-hoist@29.6.3: dependencies: '@babel/template': 7.28.6 - '@babel/types': 7.28.6 + '@babel/types': 7.29.0 '@types/babel__core': 7.20.5 '@types/babel__traverse': 7.28.0 @@ -8165,7 +8331,7 @@ snapshots: babel-plugin-polyfill-corejs2@0.4.15(@babel/core@7.28.6): dependencies: - '@babel/compat-data': 7.28.6 + '@babel/compat-data': 7.29.0 '@babel/core': 7.28.6 '@babel/helper-define-polyfill-provider': 0.6.6(@babel/core@7.28.6) semver: 6.3.1 @@ -8180,6 +8346,14 @@ snapshots: transitivePeerDependencies: - supports-color + babel-plugin-polyfill-corejs3@0.14.0(@babel/core@7.28.6): + dependencies: + '@babel/core': 7.28.6 + '@babel/helper-define-polyfill-provider': 0.6.6(@babel/core@7.28.6) + core-js-compat: 3.48.0 + transitivePeerDependencies: + - supports-color + babel-plugin-polyfill-regenerator@0.6.6(@babel/core@7.28.6): dependencies: '@babel/core': 7.28.6 @@ -8229,7 +8403,7 @@ snapshots: babel-preset-expo@13.1.11(@babel/core@7.28.6): dependencies: '@babel/helper-module-imports': 7.28.6 - '@babel/plugin-proposal-decorators': 7.28.6(@babel/core@7.28.6) + '@babel/plugin-proposal-decorators': 7.29.0(@babel/core@7.28.6) '@babel/plugin-proposal-export-default-from': 7.27.1(@babel/core@7.28.6) '@babel/plugin-syntax-export-default-from': 7.28.6(@babel/core@7.28.6) '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.28.6) @@ -8239,7 +8413,7 @@ snapshots: '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.6) '@babel/plugin-transform-private-methods': 7.28.6(@babel/core@7.28.6) '@babel/plugin-transform-private-property-in-object': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-transform-runtime': 7.28.5(@babel/core@7.28.6) + '@babel/plugin-transform-runtime': 7.29.0(@babel/core@7.28.6) '@babel/preset-react': 7.28.5(@babel/core@7.28.6) '@babel/preset-typescript': 7.28.5(@babel/core@7.28.6) '@react-native/babel-preset': 0.79.2(@babel/core@7.28.6) @@ -8435,7 +8609,7 @@ snapshots: chrome-launcher@0.15.2: dependencies: - '@types/node': 25.1.0 + '@types/node': 25.2.1 escape-string-regexp: 4.0.0 is-wsl: 2.2.0 lighthouse-logger: 1.4.2 @@ -8444,7 +8618,7 @@ snapshots: chromium-edge-launcher@0.2.0: dependencies: - '@types/node': 25.1.0 + '@types/node': 25.2.1 escape-string-regexp: 4.0.0 is-wsl: 2.2.0 lighthouse-logger: 1.4.2 @@ -8582,13 +8756,13 @@ snapshots: optionalDependencies: typescript: 5.9.3 - create-jest@29.7.0(@types/node@25.1.0): + create-jest@29.7.0(@types/node@25.2.1): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@25.1.0) + jest-config: 29.7.0(@types/node@25.2.1) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -8938,13 +9112,13 @@ snapshots: lodash: 4.17.23 string-natural-compare: 3.0.1 - eslint-plugin-jest@29.12.1(@typescript-eslint/eslint-plugin@8.53.1(@typescript-eslint/parser@8.53.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(jest@29.7.0(@types/node@25.1.0))(typescript@5.9.3): + eslint-plugin-jest@29.12.1(@typescript-eslint/eslint-plugin@8.53.1(@typescript-eslint/parser@8.53.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(jest@29.7.0(@types/node@25.2.1))(typescript@5.9.3): dependencies: '@typescript-eslint/utils': 8.54.0(eslint@8.57.1)(typescript@5.9.3) eslint: 8.57.1 optionalDependencies: '@typescript-eslint/eslint-plugin': 8.53.1(@typescript-eslint/parser@8.53.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) - jest: 29.7.0(@types/node@25.1.0) + jest: 29.7.0(@types/node@25.2.1) transitivePeerDependencies: - supports-color - typescript @@ -9694,7 +9868,7 @@ snapshots: istanbul-lib-instrument@5.2.1: dependencies: '@babel/core': 7.28.6 - '@babel/parser': 7.28.6 + '@babel/parser': 7.29.0 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 semver: 6.3.1 @@ -9704,7 +9878,7 @@ snapshots: istanbul-lib-instrument@6.0.3: dependencies: '@babel/core': 7.28.6 - '@babel/parser': 7.28.6 + '@babel/parser': 7.29.0 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 semver: 7.7.3 @@ -9758,7 +9932,7 @@ snapshots: '@jest/expect': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 25.1.0 + '@types/node': 25.2.1 chalk: 4.1.2 co: 4.6.0 dedent: 1.7.1 @@ -9778,16 +9952,16 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@29.7.0(@types/node@25.1.0): + jest-cli@29.7.0(@types/node@25.2.1): dependencies: '@jest/core': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@25.1.0) + create-jest: 29.7.0(@types/node@25.2.1) exit: 0.1.2 import-local: 3.2.0 - jest-config: 29.7.0(@types/node@25.1.0) + jest-config: 29.7.0(@types/node@25.2.1) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -9797,7 +9971,7 @@ snapshots: - supports-color - ts-node - jest-config@29.7.0(@types/node@25.1.0): + jest-config@29.7.0(@types/node@25.2.1): dependencies: '@babel/core': 7.28.6 '@jest/test-sequencer': 29.7.0 @@ -9822,7 +9996,7 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 25.1.0 + '@types/node': 25.2.1 transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -9858,7 +10032,7 @@ snapshots: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 25.1.0 + '@types/node': 25.2.1 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -9868,7 +10042,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.9 - '@types/node': 25.1.0 + '@types/node': 25.2.1 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -9926,13 +10100,13 @@ snapshots: jest-mock@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 25.1.0 + '@types/node': 25.2.1 jest-util: 29.7.0 jest-mock@30.2.0: dependencies: '@jest/types': 30.2.0 - '@types/node': 25.1.0 + '@types/node': 25.2.1 jest-util: 30.2.0 jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): @@ -9969,7 +10143,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 25.1.0 + '@types/node': 25.2.1 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -9997,7 +10171,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 25.1.0 + '@types/node': 25.2.1 chalk: 4.1.2 cjs-module-lexer: 1.4.3 collect-v8-coverage: 1.0.3 @@ -10043,7 +10217,7 @@ snapshots: jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 25.1.0 + '@types/node': 25.2.1 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -10052,7 +10226,7 @@ snapshots: jest-util@30.2.0: dependencies: '@jest/types': 30.2.0 - '@types/node': 25.1.0 + '@types/node': 25.2.1 chalk: 4.1.2 ci-info: 4.3.1 graceful-fs: 4.2.11 @@ -10071,7 +10245,7 @@ snapshots: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 25.1.0 + '@types/node': 25.2.1 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -10080,17 +10254,17 @@ snapshots: jest-worker@29.7.0: dependencies: - '@types/node': 25.1.0 + '@types/node': 25.2.1 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 - jest@29.7.0(@types/node@25.1.0): + jest@29.7.0(@types/node@25.2.1): dependencies: '@jest/core': 29.7.0 '@jest/types': 29.6.3 import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@25.1.0) + jest-cli: 29.7.0(@types/node@25.2.1) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -10391,7 +10565,7 @@ snapshots: '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.28.6) '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.28.6) '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-transform-named-capturing-groups-regex': 7.27.1(@babel/core@7.28.6) + '@babel/plugin-transform-named-capturing-groups-regex': 7.29.0(@babel/core@7.28.6) '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.6) '@babel/plugin-transform-react-display-name': 7.28.0(@babel/core@7.28.6) '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@7.28.6) @@ -10431,7 +10605,7 @@ snapshots: metro-source-map@0.83.3: dependencies: '@babel/traverse': 7.28.6 - '@babel/traverse--for-generate-function-map': '@babel/traverse@7.28.6' + '@babel/traverse--for-generate-function-map': '@babel/traverse@7.29.0' '@babel/types': 7.28.6 flow-enums-runtime: 0.0.6 invariant: 2.2.4 @@ -10459,7 +10633,7 @@ snapshots: '@babel/core': 7.28.6 '@babel/generator': 7.28.6 '@babel/template': 7.28.6 - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.29.0 flow-enums-runtime: 0.0.6 nullthrows: 1.1.1 transitivePeerDependencies: @@ -10492,7 +10666,7 @@ snapshots: '@babel/generator': 7.28.6 '@babel/parser': 7.28.6 '@babel/template': 7.28.6 - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.29.0 '@babel/types': 7.28.6 accepts: 1.3.8 chalk: 4.1.2 @@ -10630,7 +10804,7 @@ snapshots: dependencies: hosted-git-info: 7.0.2 proc-log: 4.2.0 - semver: 7.7.3 + semver: 7.7.4 validate-npm-package-name: 5.0.1 optional: true @@ -11025,6 +11199,11 @@ snapshots: prop-types: 15.8.1 react-native: 0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3) + react-native-nitro-modules@0.33.7(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3): + dependencies: + react: 19.2.3 + react-native: 0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3) + react-native-reanimated@4.2.1(react-native-worklets@0.7.2(@babel/core@7.28.6)(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3): dependencies: react: 19.2.3 @@ -11038,32 +11217,32 @@ snapshots: react: 19.2.3 react-native: 0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3) - react-native-screens@4.20.0(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3): + react-native-screens@4.22.0(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3): dependencies: react: 19.2.3 react-freeze: 1.0.4(react@19.2.3) react-native: 0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3) warn-once: 0.1.1 - react-native-shadow-2@7.1.2(react-native-svg@15.15.1(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3)): + react-native-shadow-2@7.1.2(react-native-svg@15.15.2(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3)): dependencies: colord: 2.9.2 react-native: 0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3) - react-native-svg: 15.15.1(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) + react-native-svg: 15.15.2(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) - react-native-svg-transformer@1.5.2(react-native-svg@15.15.1(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(typescript@5.9.3): + react-native-svg-transformer@1.5.2(react-native-svg@15.15.2(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3))(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(typescript@5.9.3): dependencies: '@svgr/core': 8.1.0(typescript@5.9.3) '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.9.3)) '@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0(typescript@5.9.3))(typescript@5.9.3) path-dirname: 1.0.2 react-native: 0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3) - react-native-svg: 15.15.1(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) + react-native-svg: 15.15.2(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3) transitivePeerDependencies: - supports-color - typescript - react-native-svg@15.15.1(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3): + react-native-svg@15.15.2(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3): dependencies: css-select: 5.2.2 css-tree: 1.1.3 @@ -11076,6 +11255,8 @@ snapshots: react: 19.2.3 react-native: 0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3) + react-native-uuid@2.0.3: {} + react-native-webview@13.16.0(patch_hash=2bf2ed0feba988e3f2d2ebc497218220b351ab5157dc5fedf7085f8525cacb65)(react-native@0.83.1(@babel/core@7.28.6)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.83.1(@babel/core@7.28.6))(@types/react@19.2.9)(react@19.2.3))(react@19.2.3): dependencies: escape-string-regexp: 4.0.0 @@ -11337,6 +11518,9 @@ snapshots: semver@7.7.3: {} + semver@7.7.4: + optional: true + send@0.19.2: dependencies: debug: 2.6.9 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index c429381..fc2c9c9 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -4,3 +4,4 @@ onlyBuiltDependencies: patchedDependencies: react-native-webview: patches/react-native-webview.patch react-native-track-player: patches/react-native-track-player.patch + '@iternio/react-native-auto-play': patches/@iternio__react-native-auto-play.patch diff --git a/src/localisation/lang/bg/locale.json b/src/localisation/lang/bg/locale.json index 3768693..bbae273 100644 --- a/src/localisation/lang/bg/locale.json +++ b/src/localisation/lang/bg/locale.json @@ -83,5 +83,11 @@ "kbps": "kbps", "transcoded": "Транскодиран", "khz": "kHz", - "direct-play": "Direct play" + "direct-play": "Direct play", + "browse": "Преглед", + "play": "Възпроизвеждане", + "shuffle": "Разбъркай", + "tracks": "песни", + "unknown-artist": "Неизвестен изпълнител", + "unknown-track": "Неизвестна песен" } diff --git a/src/localisation/lang/ca/locale.json b/src/localisation/lang/ca/locale.json index 54ffb98..98116db 100644 --- a/src/localisation/lang/ca/locale.json +++ b/src/localisation/lang/ca/locale.json @@ -83,5 +83,11 @@ "khz": "kHz", "direct-play": "Reproducció directa", "transcoded": "Transcodificació", - "kbps": "kbps" + "kbps": "kbps", + "browse": "Explorar", + "play": "Reproduir", + "shuffle": "Aleatori", + "tracks": "pistes", + "unknown-artist": "Artista desconegut", + "unknown-track": "Pista desconeguda" } diff --git a/src/localisation/lang/cs/locale.json b/src/localisation/lang/cs/locale.json index fd8e958..bb96df9 100644 --- a/src/localisation/lang/cs/locale.json +++ b/src/localisation/lang/cs/locale.json @@ -83,5 +83,11 @@ "direct-play": "Přímé přehrávání", "transcoded": "Překódované", "khz": "kHz", - "kbps": "kbps" + "kbps": "kbps", + "browse": "Procházet", + "play": "Přehrát", + "shuffle": "Zamíchat", + "tracks": "skladeb", + "unknown-artist": "Neznámý interpret", + "unknown-track": "Neznámá skladba" } diff --git a/src/localisation/lang/da/locale.json b/src/localisation/lang/da/locale.json index 491f93c..792bb8f 100644 --- a/src/localisation/lang/da/locale.json +++ b/src/localisation/lang/da/locale.json @@ -83,5 +83,11 @@ "direct-play": "Direkte spil", "transcoded": "Transcode", "kbps": "kbps", - "khz": "kHz" + "khz": "kHz", + "browse": "Gennemse", + "play": "Afspil", + "shuffle": "Bland", + "tracks": "numre", + "unknown-artist": "Ukendt kunstner", + "unknown-track": "Ukendt nummer" } diff --git a/src/localisation/lang/de/locale.json b/src/localisation/lang/de/locale.json index 5c49852..78aa431 100644 --- a/src/localisation/lang/de/locale.json +++ b/src/localisation/lang/de/locale.json @@ -83,5 +83,11 @@ "direct-play": "Direkte Wiedergabe", "transcoded": "Transkodiert", "khz": "kHz", - "kbps": "kbps" + "kbps": "kbps", + "browse": "Durchsuchen", + "play": "Abspielen", + "shuffle": "Zufällig", + "tracks": "Titel", + "unknown-artist": "Unbekannter Künstler", + "unknown-track": "Unbekannter Titel" } diff --git a/src/localisation/lang/en/locale.json b/src/localisation/lang/en/locale.json index 98cb7cb..da11b76 100644 --- a/src/localisation/lang/en/locale.json +++ b/src/localisation/lang/en/locale.json @@ -83,5 +83,11 @@ "direct-play": "Direct play", "transcoded": "Transcoded", "khz": "kHz", - "kbps": "kbps" + "kbps": "kbps", + "browse": "Browse", + "play": "Play", + "shuffle": "Shuffle", + "tracks": "tracks", + "unknown-artist": "Unknown Artist", + "unknown-track": "Unknown Track" } diff --git a/src/localisation/lang/es/locale.json b/src/localisation/lang/es/locale.json index 36f7308..a88f47d 100644 --- a/src/localisation/lang/es/locale.json +++ b/src/localisation/lang/es/locale.json @@ -83,5 +83,11 @@ "direct-play": "Reproducción directa", "transcoded": "Transcodificar", "khz": "kHz", - "kbps": "kbps" + "kbps": "kbps", + "browse": "Explorar", + "play": "Reproducir", + "shuffle": "Aleatorio", + "tracks": "pistas", + "unknown-artist": "Artista desconocido", + "unknown-track": "Pista desconocida" } diff --git a/src/localisation/lang/fi/locale.json b/src/localisation/lang/fi/locale.json index 580751e..7dccb9a 100644 --- a/src/localisation/lang/fi/locale.json +++ b/src/localisation/lang/fi/locale.json @@ -83,5 +83,11 @@ "similar-albums": "Samanlaisia albumeja", "playback-reporting": "Toiston raportointi", "playback-reporting-description": "Toiston raportoinnin avulla kaikki toistotapahtumat palautetaan palvelimellesi. Näin voit seurata eniten kuunneltuja kappaleita, esimerkiksi pluginien, kuten ListenBrainz tai LastFM, avulla.", - "color-scheme-description": "Fintunes noudattaa oletuksena käyttöjärjestelmän ulkoasua. Voit kuitenkin ohittaa tämän varmistaaksesi, että Fintunes on aina tummassa tai vaaleassa tilassa." + "color-scheme-description": "Fintunes noudattaa oletuksena käyttöjärjestelmän ulkoasua. Voit kuitenkin ohittaa tämän varmistaaksesi, että Fintunes on aina tummassa tai vaaleassa tilassa.", + "browse": "Selaa", + "play": "Toista", + "shuffle": "Sekoita", + "tracks": "kappaletta", + "unknown-artist": "Tuntematon artisti", + "unknown-track": "Tuntematon kappale" } diff --git a/src/localisation/lang/fr/locale.json b/src/localisation/lang/fr/locale.json index 8617935..4fd41d2 100644 --- a/src/localisation/lang/fr/locale.json +++ b/src/localisation/lang/fr/locale.json @@ -83,5 +83,11 @@ "direct-play": "Lecture directe", "transcoded": "Converti", "khz": "kHz", - "kbps": "kbps" + "kbps": "kbps", + "browse": "Parcourir", + "play": "Lire", + "shuffle": "Aléatoire", + "tracks": "pistes", + "unknown-artist": "Artiste inconnu", + "unknown-track": "Piste inconnue" } diff --git a/src/localisation/lang/hu/locale.json b/src/localisation/lang/hu/locale.json index d32c6ed..b01c05e 100644 --- a/src/localisation/lang/hu/locale.json +++ b/src/localisation/lang/hu/locale.json @@ -80,5 +80,11 @@ "where-is-data-stored": "Hol van tárolva a gyűjtött adat?", "where-is-data-stored-description": "A Sentry rendszert saját szervereken futtatjuk, a mi saját infrastruktúránkon. A szerverekhez, adatbázisokhoz és naplófájlokhoz csak mi férünk hozzá, legkevésbé a Sentry munkatársai. Az infrastruktúra az Európai Unióban van futtatva.", "enable-error-reporting": "Engedélyezni szeretnéd a hibajelentést?", - "enable-error-reporting-description": "Segít nekünk fejleszteni az alkalmazásélményt a hibák és összeomlások jelentésével." + "enable-error-reporting-description": "Segít nekünk fejleszteni az alkalmazásélményt a hibák és összeomlások jelentésével.", + "browse": "Böngészés", + "play": "Lejátszás", + "shuffle": "Keverés", + "tracks": "számok", + "unknown-artist": "Ismeretlen előadó", + "unknown-track": "Ismeretlen szám" } diff --git a/src/localisation/lang/id/locale.json b/src/localisation/lang/id/locale.json index 7e017e9..069b1dc 100644 --- a/src/localisation/lang/id/locale.json +++ b/src/localisation/lang/id/locale.json @@ -83,5 +83,11 @@ "privacy-policy": "Kebijakan Privasi", "sleep-timer": "Pengatur waktu tidur", "delete": "Menghapus", - "cancel": "Membatalkan" + "cancel": "Membatalkan", + "browse": "Jelajahi", + "play": "Mainkan", + "shuffle": "Acak", + "tracks": "lagu", + "unknown-artist": "Artis Tidak Dikenal", + "unknown-track": "Lagu Tidak Dikenal" } diff --git a/src/localisation/lang/it/locale.json b/src/localisation/lang/it/locale.json index 03e298b..ac7931d 100644 --- a/src/localisation/lang/it/locale.json +++ b/src/localisation/lang/it/locale.json @@ -83,5 +83,11 @@ "direct-play": "Gioco diretto", "transcoded": "Transcodifica", "khz": "kHz", - "kbps": "kbps" + "kbps": "kbps", + "browse": "Sfoglia", + "play": "Riproduci", + "shuffle": "Casuale", + "tracks": "tracce", + "unknown-artist": "Artista sconosciuto", + "unknown-track": "Traccia sconosciuta" } diff --git a/src/localisation/lang/ja/locale.json b/src/localisation/lang/ja/locale.json index 3510e73..a27acb8 100644 --- a/src/localisation/lang/ja/locale.json +++ b/src/localisation/lang/ja/locale.json @@ -83,5 +83,11 @@ "direct-play": "ダイレクトプレイ", "transcoded": "トランスコード", "khz": "kHz", - "kbps": "kbps" + "kbps": "kbps", + "browse": "ブラウズ", + "play": "再生", + "shuffle": "シャッフル", + "tracks": "トラック", + "unknown-artist": "不明なアーティスト", + "unknown-track": "不明なトラック" } diff --git a/src/localisation/lang/nb_NO/locale.json b/src/localisation/lang/nb_NO/locale.json index 43a38f6..bb95818 100644 --- a/src/localisation/lang/nb_NO/locale.json +++ b/src/localisation/lang/nb_NO/locale.json @@ -82,5 +82,11 @@ "lyrics": "Sangtekster", "khz": "kHz", "kbps": "kbps", - "transcoded": "Transkodet" + "transcoded": "Transkodet", + "browse": "Bla gjennom", + "play": "Spill", + "shuffle": "Stokk", + "tracks": "spor", + "unknown-artist": "Ukjent artist", + "unknown-track": "Ukjent spor" } diff --git a/src/localisation/lang/nl/locale.json b/src/localisation/lang/nl/locale.json index 86a4d27..febd68c 100644 --- a/src/localisation/lang/nl/locale.json +++ b/src/localisation/lang/nl/locale.json @@ -83,5 +83,11 @@ "transcoded": "Getranscodeerd", "khz": "kHz", "kbps": "kbps", - "lyrics": "Songtekst" + "lyrics": "Songtekst", + "browse": "Bladeren", + "play": "Afspelen", + "shuffle": "Willekeurig", + "tracks": "nummers", + "unknown-artist": "Onbekende artiest", + "unknown-track": "Onbekend nummer" } diff --git a/src/localisation/lang/pl/locale.json b/src/localisation/lang/pl/locale.json index 858bb49..75ebd0e 100644 --- a/src/localisation/lang/pl/locale.json +++ b/src/localisation/lang/pl/locale.json @@ -83,5 +83,11 @@ "direct-play": "Dyrektywa", "transcoded": "Transcode", "khz": "kHz", - "kbps": "kbps" + "kbps": "kbps", + "browse": "Przeglądaj", + "play": "Odtwórz", + "shuffle": "Losowo", + "tracks": "utworów", + "unknown-artist": "Nieznany artysta", + "unknown-track": "Nieznany utwór" } diff --git a/src/localisation/lang/pt/locale.json b/src/localisation/lang/pt/locale.json index 161097c..4fbcf6a 100644 --- a/src/localisation/lang/pt/locale.json +++ b/src/localisation/lang/pt/locale.json @@ -83,5 +83,11 @@ "direct-play": "Reprodução Direta", "transcoded": "Transcodificado", "khz": "kHz", - "retry-failed-downloads": "Tentar Transferir Novamente" + "retry-failed-downloads": "Tentar Transferir Novamente", + "browse": "Explorar", + "play": "Reproduzir", + "shuffle": "Aleatório", + "tracks": "faixas", + "unknown-artist": "Artista Desconhecido", + "unknown-track": "Faixa Desconhecida" } diff --git a/src/localisation/lang/pt_BR/locale.json b/src/localisation/lang/pt_BR/locale.json index a396172..62e3064 100644 --- a/src/localisation/lang/pt_BR/locale.json +++ b/src/localisation/lang/pt_BR/locale.json @@ -83,5 +83,11 @@ "direct-play": "Reprodução direta", "transcoded": "Transcodificado", "khz": "kHz", - "kbps": "kbps" + "kbps": "kbps", + "browse": "Navegar", + "play": "Reproduzir", + "shuffle": "Aleatório", + "tracks": "faixas", + "unknown-artist": "Artista Desconhecido", + "unknown-track": "Faixa Desconhecida" } diff --git a/src/localisation/lang/ru/locale.json b/src/localisation/lang/ru/locale.json index 4a071cd..1fab0c0 100644 --- a/src/localisation/lang/ru/locale.json +++ b/src/localisation/lang/ru/locale.json @@ -83,5 +83,11 @@ "direct-play": "Прямая игра", "transcoded": "Транскод", "khz": "кГц", - "kbps": "кбит/с" + "kbps": "кбит/с", + "browse": "Обзор", + "play": "Воспроизвести", + "shuffle": "Перемешать", + "tracks": "треков", + "unknown-artist": "Неизвестный исполнитель", + "unknown-track": "Неизвестный трек" } diff --git a/src/localisation/lang/sl/locale.json b/src/localisation/lang/sl/locale.json index 96dd92d..341bae4 100644 --- a/src/localisation/lang/sl/locale.json +++ b/src/localisation/lang/sl/locale.json @@ -81,5 +81,11 @@ "disc": "Disk", "lyrics": "Besedilo", "khz": "kHz", - "transcoded": "Transcode" + "transcoded": "Transcode", + "browse": "Prebrskaj", + "play": "Predvajaj", + "shuffle": "Naključno", + "tracks": "skladbe", + "unknown-artist": "Neznan izvajalec", + "unknown-track": "Neznana skladba" } diff --git a/src/localisation/lang/sv/locale.json b/src/localisation/lang/sv/locale.json index 59e4e13..f33b015 100644 --- a/src/localisation/lang/sv/locale.json +++ b/src/localisation/lang/sv/locale.json @@ -83,5 +83,11 @@ "direct-play": "Direkt spel", "transcoded": "Transkodning", "khz": "kHz", - "kbps": "kb/s" + "kbps": "kb/s", + "browse": "Bläddra", + "play": "Spela", + "shuffle": "Blanda", + "tracks": "spår", + "unknown-artist": "Okänd artist", + "unknown-track": "Okänt spår" } diff --git a/src/localisation/lang/ta/locale.json b/src/localisation/lang/ta/locale.json index 478a433..b3d8e00 100644 --- a/src/localisation/lang/ta/locale.json +++ b/src/localisation/lang/ta/locale.json @@ -83,5 +83,11 @@ "color-scheme-description": "இயல்பாக, உங்கள் இயக்க முறைமையின் வண்ணத் திட்டத்தை FINTUNES பின்பற்றும். இருப்பினும் நீங்கள் இதை மேலெழுத தேர்வு செய்யலாம் fintunes எப்போதும் இருண்ட பயன்முறையில் அல்லது ஒளி பயன்முறையில் இருப்பதை உறுதிப்படுத்தலாம்.", "direct-play": "நேரடி நாடகம்", "transcoded": "டிரான்ச்கோடட்", - "kbps": "கே.பி.பி.எச்" + "kbps": "கே.பி.பி.எச்", + "browse": "உலாவு", + "play": "இயக்கு", + "shuffle": "சீரற்றது", + "tracks": "பாடல்கள்", + "unknown-artist": "தெரியாத கலைஞர்", + "unknown-track": "தெரியாத பாடல்" } diff --git a/src/localisation/lang/uk/locale.json b/src/localisation/lang/uk/locale.json index 4c79277..a567a5b 100644 --- a/src/localisation/lang/uk/locale.json +++ b/src/localisation/lang/uk/locale.json @@ -83,5 +83,11 @@ "direct-play": "Пряме відтворення", "transcoded": "Перекодований", "khz": "кГц", - "kbps": "кбіт/с" + "kbps": "кбіт/с", + "browse": "Огляд", + "play": "Відтворити", + "shuffle": "Перемішати", + "tracks": "треків", + "unknown-artist": "Невідомий виконавець", + "unknown-track": "Невідомий трек" } diff --git a/src/localisation/lang/zh/locale.json b/src/localisation/lang/zh/locale.json index ff5ee7b..cc4fd7a 100644 --- a/src/localisation/lang/zh/locale.json +++ b/src/localisation/lang/zh/locale.json @@ -83,5 +83,11 @@ "direct-play": "直接播放", "transcoded": "转码", "khz": "kHz", - "kbps": "kbps" + "kbps": "kbps", + "browse": "浏览", + "play": "播放", + "shuffle": "随机播放", + "tracks": "曲目", + "unknown-artist": "未知艺术家", + "unknown-track": "未知曲目" } diff --git a/src/localisation/lang/zh_Hant/locale.json b/src/localisation/lang/zh_Hant/locale.json index e1cf38a..d0c470d 100644 --- a/src/localisation/lang/zh_Hant/locale.json +++ b/src/localisation/lang/zh_Hant/locale.json @@ -83,5 +83,11 @@ "why-use-tracking": "為什麼要使用跟蹤?", "why-use-tracking-description": "跟蹤透過報告奇怪的邊緣案例和疏忽來幫助加快此應用程式的開發。 這有助於使應用程式更加穩定和強大,從而改善每個人的應用程式體驗。", "what-data-is-gathered": "收集了哪些資料?", - "enable-error-reporting-description": "這透過向我們傳送崩潰和錯誤報告來幫助改善應用程式體驗。" + "enable-error-reporting-description": "這透過向我們傳送崩潰和錯誤報告來幫助改善應用程式體驗。", + "browse": "瀏覽", + "play": "播放", + "shuffle": "隨機播放", + "tracks": "曲目", + "unknown-artist": "未知藝術家", + "unknown-track": "未知曲目" } diff --git a/src/localisation/types.ts b/src/localisation/types.ts index 39593a0..be880ee 100644 --- a/src/localisation/types.ts +++ b/src/localisation/types.ts @@ -83,3 +83,9 @@ export type LocaleKeys = 'play-next' | 'transcoded' | 'khz' | 'kbps' +| 'browse' +| 'play' +| 'shuffle' +| 'tracks' +| 'unknown-artist' +| 'unknown-track' diff --git a/src/screens/carplay/register.ts b/src/screens/carplay/register.ts new file mode 100644 index 0000000..1e14a59 --- /dev/null +++ b/src/screens/carplay/register.ts @@ -0,0 +1,28 @@ +import { HybridAutoPlay } from '@iternio/react-native-auto-play'; +import { createBrowseMenu } from './templates/BrowseMenu'; + +/** + * Registers CarPlay AutoPlay listeners and sets up the root browse menu + * template when the device is connected to a CarPlay-enabled vehicle. + */ +export function registerAutoPlay(): void { + // Listen for CarPlay connection events + HybridAutoPlay.addListener('didConnect', async () => { + console.log('[AutoPlay] Connected'); + + try { + // Set up and display the browse menu as the root template + const browseTemplate = createBrowseMenu(); + await browseTemplate.setRootTemplate(); + + console.log('[AutoPlay] Root template set successfully'); + } catch (error) { + console.error('[AutoPlay] Error setting up templates:', error); + } + }); + + // Listen for CarPlay disconnection events + HybridAutoPlay.addListener('didDisconnect', () => { + console.log('[AutoPlay] Disconnected'); + }); +} diff --git a/src/screens/carplay/templates/AlbumsList.ts b/src/screens/carplay/templates/AlbumsList.ts new file mode 100644 index 0000000..22ca2a7 --- /dev/null +++ b/src/screens/carplay/templates/AlbumsList.ts @@ -0,0 +1,271 @@ +import { ListTemplate, HybridAutoPlay, type ImageButton } from '@iternio/react-native-auto-play'; +import type { Album } from '@/store/music/types'; +import { t } from '@/localisation'; +import { fetchTracksByAlbum } from '@/store/music/actions'; +import { ALPHABET_LETTERS } from '@/CONSTANTS'; +import { playTracks } from '@/utility/usePlayTracks'; +import store from '@/store'; + +/** + * Creates a list template showing the 24 most recently added albums, + * sorted by creation date in descending order. + */ +export function createRecentAlbumsTemplate(): ListTemplate { + console.log('[AlbumsList] Creating Recent Albums template...'); + + const state = store.getState(); + + const allAlbums = state.music.albums.ids + .map(id => state.music.albums.entities[id]) + .filter((album): album is Album => album !== undefined); + + const albumsWithDates = allAlbums.filter(album => album.DateCreated !== undefined); + const albums = albumsWithDates + .sort((a, b) => { + const dateA = new Date(a.DateCreated!).getTime(); + const dateB = new Date(b.DateCreated!).getTime(); + return dateB - dateA; + }) + .slice(0, 24); + + console.log('[AlbumsList] Recent albums count:', albums.length); + + const items = albums.map(album => ({ + type: 'default' as const, + title: { text: album.Name }, + detailedText: { text: album.AlbumArtist || t('unknown-artist') }, + onPress: async () => { + console.log('[AlbumsList] Album selected:', album.Name); + try { + const detailTemplate = await createAlbumDetailTemplate(album); + await detailTemplate.push(); + console.log('[AlbumsList] Album detail pushed'); + } catch (error) { + console.error('[AlbumsList] Error pushing album detail:', error); + } + }, + })); + + return new ListTemplate({ + title: { text: t('recent-albums') }, + sections: items.length > 0 ? { type: 'default', items } : undefined, + headerActions: { + android: { + startHeaderAction: { + type: 'back', + onPress: () => HybridAutoPlay.popTemplate(), + }, + }, + ios: { + backButton: { + type: 'back', + onPress: () => HybridAutoPlay.popTemplate(), + }, + }, + }, + }); +} + +/** + * Creates a list template showing all albums grouped by alphabetical + * sections, sorted by artist name then album name. + */ +export function createAllAlbumsTemplate(): ListTemplate { + console.log('[AlbumsList] Creating All Albums template...'); + + const state = store.getState(); + const albums = state.music.albums.ids + .map(id => state.music.albums.entities[id]) + .filter((album): album is Album => album !== undefined) + .sort((a, b) => { + const artistA = (a.AlbumArtist || a.Artists?.[0] || t('unknown-artist')).toUpperCase(); + const artistB = (b.AlbumArtist || b.Artists?.[0] || t('unknown-artist')).toUpperCase(); + if (artistA < artistB) return -1; + if (artistA > artistB) return 1; + const nameA = a.Name.toUpperCase(); + const nameB = b.Name.toUpperCase(); + return nameA < nameB ? -1 : nameA > nameB ? 1 : 0; + }); + + console.log('[AlbumsList] Total albums:', albums.length); + + // Group into alphabetical sections + const sectionMap = new Map(); + + albums.forEach(album => { + const firstLetter = (album.AlbumArtist || album.Artists?.[0] || '#')[0].toUpperCase(); + const section = ALPHABET_LETTERS.includes(firstLetter) ? firstLetter : '#'; + + if (!sectionMap.has(section)) { + sectionMap.set(section, []); + } + sectionMap.get(section)!.push(album); + }); + + const sections = Array.from(sectionMap.entries()) + .filter(([_, sectionAlbums]) => sectionAlbums.length > 0) + .sort(([a], [b]) => { + if (a === '#') return 1; + if (b === '#') return -1; + return a.localeCompare(b); + }) + .map(([letter, sectionAlbums]) => ({ + type: 'default' as const, + title: letter, + items: sectionAlbums.map(album => ({ + type: 'default' as const, + title: { text: album.Name }, + detailedText: { text: album.AlbumArtist || t('unknown-artist') }, + onPress: async () => { + console.log('[AlbumsList] Album selected:', album.Name); + try { + const detailTemplate = await createAlbumDetailTemplate(album); + await detailTemplate.push(); + console.log('[AlbumsList] Album detail pushed'); + } catch (error) { + console.error('[AlbumsList] Error pushing album detail:', error); + } + }, + })), + })); + + return new ListTemplate({ + title: { text: t('all-albums') }, + sections: sections.length > 0 ? sections : undefined, + headerActions: { + android: { + startHeaderAction: { + type: 'back', + onPress: () => HybridAutoPlay.popTemplate(), + }, + }, + ios: { + backButton: { + type: 'back', + onPress: () => HybridAutoPlay.popTemplate(), + }, + }, + }, + }); +} + +/** + * Creates a detail template for a specific album showing its tracks + * with play and shuffle actions. Fetches tracks from the API if they're + * not already in the store. + */ +export async function createAlbumDetailTemplate(album: Album): Promise { + let state = store.getState(); + let trackIds = state.music.tracks.byAlbum[album.Id] || []; + + // Only fetch if tracks are missing + if (trackIds.length === 0) { + console.log('[AlbumsList] Tracks missing, fetching for album:', album.Name); + await store.dispatch(fetchTracksByAlbum(album.Id)); + state = store.getState(); + trackIds = state.music.tracks.byAlbum[album.Id] || []; + } + + const tracks = trackIds + .map(id => state.music.tracks.entities[id]) + .filter((track): track is NonNullable => track !== undefined); + + console.log('[AlbumsList] Album tracks:', tracks.length); + + const items = tracks.map((track, index) => ({ + type: 'default' as const, + title: { text: track.Name }, + detailedText: { text: track.Artists?.join(', ') || t('unknown-artist') }, + onPress: async () => { + try { + await playTracks( + trackIds, + state.music.tracks.entities, + state.downloads.entities, + { playIndex: index } + ); + } catch (error) { + console.error('[AlbumsList] Error playing track:', error); + } + }, + })); + + const playAction: ImageButton = { + type: 'image', + image: { type: 'glyph', name: 'play_arrow', fontScale: 0.8 }, + onPress: async () => { + console.log('[AlbumsList] Play album:', album.Name); + try { + await playTracks( + trackIds, + state.music.tracks.entities, + state.downloads.entities + ); + } catch (error) { + console.error('[AlbumsList] Error playing album:', error); + } + }, + }; + + const shuffleAction: ImageButton = { + type: 'image', + image: { type: 'glyph', name: 'shuffle', fontScale: 0.8 }, + onPress: async () => { + console.log('[AlbumsList] Shuffle album:', album.Name); + try { + await playTracks( + trackIds, + state.music.tracks.entities, + state.downloads.entities, + { shuffle: true } + ); + } catch (error) { + console.error('[AlbumsList] Error shuffling album:', error); + } + }, + }; + + if (items.length === 0) { + return new ListTemplate({ + title: { text: album.Name }, + headerActions: { + android: { + startHeaderAction: { + type: 'back', + onPress: () => HybridAutoPlay.popTemplate(), + }, + }, + ios: { + backButton: { + type: 'back', + onPress: () => HybridAutoPlay.popTemplate(), + }, + }, + }, + }); + } + + return new ListTemplate({ + title: { text: album.Name }, + sections: { + type: 'default', + items, + }, + headerActions: { + android: { + startHeaderAction: { + type: 'back', + onPress: () => HybridAutoPlay.popTemplate(), + }, + endHeaderActions: [playAction, shuffleAction], + }, + ios: { + backButton: { + type: 'back', + onPress: () => HybridAutoPlay.popTemplate(), + }, + trailingNavigationBarButtons: [playAction, shuffleAction], + }, + }, + }); +} diff --git a/src/screens/carplay/templates/ArtistsList.ts b/src/screens/carplay/templates/ArtistsList.ts new file mode 100644 index 0000000..316b76a --- /dev/null +++ b/src/screens/carplay/templates/ArtistsList.ts @@ -0,0 +1,153 @@ +import { ListTemplate, HybridAutoPlay } from '@iternio/react-native-auto-play'; +import type { MusicArtist } from '@/store/music/types'; +import { t } from '@/localisation'; +import { createAlbumDetailTemplate } from './AlbumsList'; +import { ALPHABET_LETTERS } from '@/CONSTANTS'; +import store from '@/store'; + +/** + * Creates a list template showing all artists grouped by alphabetical + * sections, sorted alphabetically by name. + */ +export function createArtistsTemplate(): ListTemplate { + console.log('[ArtistsList] Creating Artists template...'); + + const state = store.getState(); + const artists = state.music.artists.ids + .map(id => state.music.artists.entities[id]) + .filter((artist): artist is MusicArtist => artist !== undefined) + .sort((a, b) => a.Name.toUpperCase().localeCompare(b.Name.toUpperCase())); + + console.log('[ArtistsList] Total artists:', artists.length); + + // Group into alphabetical sections + const sectionMap = new Map(); + + artists.forEach(artist => { + const firstLetter = artist.Name[0].toUpperCase(); + const section = ALPHABET_LETTERS.includes(firstLetter) ? firstLetter : '#'; + + if (!sectionMap.has(section)) { + sectionMap.set(section, []); + } + sectionMap.get(section)!.push(artist); + }); + + const sections = Array.from(sectionMap.entries()) + .filter(([_, sectionArtists]) => sectionArtists.length > 0) + .sort(([a], [b]) => { + if (a === '#') return 1; + if (b === '#') return -1; + return a.localeCompare(b); + }) + .map(([letter, sectionArtists]) => ({ + type: 'default' as const, + title: letter, + items: sectionArtists.map(artist => ({ + type: 'default' as const, + title: { text: artist.Name }, + onPress: async () => { + console.log('[ArtistsList] Artist selected:', artist.Name); + try { + const detailTemplate = createArtistDetailTemplate(artist); + await detailTemplate.push(); + console.log('[ArtistsList] Artist detail pushed'); + } catch (error) { + console.error('[ArtistsList] Error pushing artist detail:', error); + } + }, + })), + })); + + return new ListTemplate({ + title: { text: t('artists') }, + sections: sections.length > 0 ? sections : undefined, + headerActions: { + android: { + startHeaderAction: { + type: 'back', + onPress: () => HybridAutoPlay.popTemplate(), + }, + }, + ios: { + backButton: { + type: 'back', + onPress: () => HybridAutoPlay.popTemplate(), + }, + }, + }, + }); +} + +/** + * Creates a detail template for a specific artist showing all their + * albums. + */ +function createArtistDetailTemplate(artist: MusicArtist): ListTemplate { + const state = store.getState(); + + // Find albums by this artist + const albums = state.music.albums.ids + .map(id => state.music.albums.entities[id]) + .filter(album => album?.ArtistItems?.find(item => item.Id === artist.Id)) + .filter((album): album is NonNullable => album !== undefined); + + console.log('[ArtistsList] Artist albums:', albums.length); + + const items = albums.map(album => ({ + type: 'default' as const, + title: { text: album.Name }, + detailedText: { text: album.ProductionYear?.toString() || t('album') }, + onPress: async () => { + console.log('[ArtistsList] Album selected:', album.Name); + try { + const detailTemplate = await createAlbumDetailTemplate(album); + await detailTemplate.push(); + console.log('[ArtistsList] Album detail pushed'); + } catch (error) { + console.error('[ArtistsList] Error opening album:', error); + } + }, + })); + + // Play/shuffle actions removed - artist view shows albums, not tracks + + if (items.length === 0) { + return new ListTemplate({ + title: { text: artist.Name }, + headerActions: { + android: { + startHeaderAction: { + type: 'back', + onPress: () => HybridAutoPlay.popTemplate(), + }, + }, + ios: { + backButton: { + type: 'back', + onPress: () => HybridAutoPlay.popTemplate(), + }, + }, + }, + }); + } + + return new ListTemplate({ + title: { text: artist.Name }, + sections: { type: 'default', items }, + headerActions: { + android: { + startHeaderAction: { + type: 'back', + onPress: () => HybridAutoPlay.popTemplate(), + }, + }, + ios: { + backButton: { + type: 'back', + onPress: () => HybridAutoPlay.popTemplate(), + }, + }, + }, + }); +} diff --git a/src/screens/carplay/templates/BrowseMenu.ts b/src/screens/carplay/templates/BrowseMenu.ts new file mode 100644 index 0000000..20097c3 --- /dev/null +++ b/src/screens/carplay/templates/BrowseMenu.ts @@ -0,0 +1,83 @@ +import { ListTemplate } from '@iternio/react-native-auto-play'; +import { t } from '@/localisation'; +import { + createRecentAlbumsTemplate, + createAllAlbumsTemplate, +} from './AlbumsList'; +import { createPlaylistsTemplate } from './PlaylistsList'; +import { createArtistsTemplate } from './ArtistsList'; + +/** + * Creates the main CarPlay browse menu with navigation to recent albums, + * all albums, playlists, and artists. + */ +export function createBrowseMenu(): ListTemplate { + return new ListTemplate({ + title: { text: t('browse') }, + sections: { + type: 'default', + items: [ + { + type: 'default', + title: { text: t('recent-albums') }, + image: { type: 'glyph', name: 'album', fontScale: 0.6 }, + onPress: async () => { + console.log('[BrowseMenu] Recent Albums selected'); + try { + const template = createRecentAlbumsTemplate(); + await template.push(); + console.log('[BrowseMenu] Recent Albums template pushed'); + } catch (error) { + console.error('[BrowseMenu] Error pushing Recent Albums:', error); + } + }, + }, + { + type: 'default', + title: { text: t('all-albums') }, + image: { type: 'glyph', name: 'album', fontScale: 0.6 }, + onPress: async () => { + console.log('[BrowseMenu] All Albums selected'); + try { + const template = createAllAlbumsTemplate(); + await template.push(); + console.log('[BrowseMenu] All Albums template pushed'); + } catch (error) { + console.error('[BrowseMenu] Error pushing All Albums:', error); + } + }, + }, + { + type: 'default', + title: { text: t('playlists') }, + image: { type: 'glyph', name: 'queue', fontScale: 0.6 }, + onPress: async () => { + console.log('[BrowseMenu] Playlists selected'); + try { + const template = createPlaylistsTemplate(); + await template.push(); + console.log('[BrowseMenu] Playlists template pushed'); + } catch (error) { + console.error('[BrowseMenu] Error pushing Playlists:', error); + } + }, + }, + { + type: 'default', + title: { text: t('artists') }, + image: { type: 'glyph', name: 'artist', fontScale: 0.6 }, + onPress: async () => { + console.log('[BrowseMenu] Artists selected'); + try { + const template = createArtistsTemplate(); + await template.push(); + console.log('[BrowseMenu] Artists template pushed'); + } catch (error) { + console.error('[BrowseMenu] Error pushing Artists:', error); + } + }, + }, + ], + }, + }); +} diff --git a/src/screens/carplay/templates/PlaylistsList.ts b/src/screens/carplay/templates/PlaylistsList.ts new file mode 100644 index 0000000..8992e59 --- /dev/null +++ b/src/screens/carplay/templates/PlaylistsList.ts @@ -0,0 +1,180 @@ +import { ListTemplate, HybridAutoPlay, type ImageButton } from '@iternio/react-native-auto-play'; +import type { Playlist } from '@/store/music/types'; +import { t } from '@/localisation'; +import { fetchTracksByPlaylist } from '@/store/music/actions'; +import { playTracks } from '@/utility/usePlayTracks'; +import store from '@/store'; + +/** + * Creates a list template showing all user playlists with track counts. + */ +export function createPlaylistsTemplate(): ListTemplate { + console.log('[PlaylistsList] Creating Playlists template...'); + + const state = store.getState(); + const playlists = state.music.playlists.ids + .map(id => state.music.playlists.entities[id]) + .filter((playlist): playlist is Playlist => playlist !== undefined); + + console.log('[PlaylistsList] Total playlists:', playlists.length); + + const items = playlists.map(playlist => { + const trackCount = playlist.ChildCount || + state.music.tracks.byPlaylist[playlist.Id]?.length || + playlist.Tracks?.length || + 0; + + return { + type: 'default' as const, + title: { text: playlist.Name }, + detailedText: { text: `${trackCount} ${trackCount !== 1 ? t('tracks') : t('track')}` }, + onPress: async () => { + console.log('[PlaylistsList] Playlist selected:', playlist.Name); + try { + const detailTemplate = await createPlaylistDetailTemplate(playlist); + await detailTemplate.push(); + console.log('[PlaylistsList] Playlist detail pushed'); + } catch (error) { + console.error('[PlaylistsList] Error pushing playlist detail:', error); + } + }, + }; + }); + + return new ListTemplate({ + title: { text: t('playlists') }, + sections: items.length > 0 ? { type: 'default', items } : undefined, + headerActions: { + android: { + startHeaderAction: { + type: 'back', + onPress: () => HybridAutoPlay.popTemplate(), + }, + }, + ios: { + backButton: { + type: 'back', + onPress: () => HybridAutoPlay.popTemplate(), + }, + }, + }, + }); +} + +/** + * Creates a detail template for a specific playlist showing its tracks + * with play and shuffle actions. Fetches tracks from the API if they're + * not already in the store. + */ +async function createPlaylistDetailTemplate(playlist: Playlist): Promise { + let state = store.getState(); + let trackIds = state.music.tracks.byPlaylist[playlist.Id] || []; + + // Only fetch if tracks are missing + if (trackIds.length === 0) { + console.log('[PlaylistsList] Tracks missing, fetching for playlist:', playlist.Name); + await store.dispatch(fetchTracksByPlaylist(playlist.Id)); + state = store.getState(); + trackIds = state.music.tracks.byPlaylist[playlist.Id] || []; + } + + const tracks = trackIds + .map(id => state.music.tracks.entities[id]) + .filter((track): track is NonNullable => track !== undefined); + + console.log('[PlaylistsList] Playlist tracks:', tracks.length); + + const items = tracks.map((track, index) => ({ + type: 'default' as const, + title: { text: track.Name }, + detailedText: { text: track.Artists?.join(', ') || t('unknown-artist') }, + onPress: async () => { + try { + await playTracks( + trackIds, + state.music.tracks.entities, + state.downloads.entities, + { playIndex: index } + ); + } catch (error) { + console.error('[PlaylistsList] Error playing track:', error); + } + }, + })); + + const playAction: ImageButton = { + type: 'image', + image: { type: 'glyph', name: 'play_arrow', fontScale: 0.8 }, + onPress: async () => { + console.log('[PlaylistsList] Play playlist:', playlist.Name); + try { + await playTracks( + trackIds, + state.music.tracks.entities, + state.downloads.entities + ); + } catch (error) { + console.error('[PlaylistsList] Error playing playlist:', error); + } + }, + }; + + const shuffleAction: ImageButton = { + type: 'image', + image: { type: 'glyph', name: 'shuffle', fontScale: 0.8 }, + onPress: async () => { + console.log('[PlaylistsList] Shuffle playlist:', playlist.Name); + try { + await playTracks( + trackIds, + state.music.tracks.entities, + state.downloads.entities, + { shuffle: true } + ); + } catch (error) { + console.error('[PlaylistsList] Error shuffling playlist:', error); + } + }, + }; + + if (items.length === 0) { + return new ListTemplate({ + title: { text: playlist.Name }, + headerActions: { + android: { + startHeaderAction: { + type: 'back', + onPress: () => HybridAutoPlay.popTemplate(), + }, + }, + ios: { + backButton: { + type: 'back', + onPress: () => HybridAutoPlay.popTemplate(), + }, + }, + }, + }); + } + + return new ListTemplate({ + title: { text: playlist.Name }, + sections: { type: 'default', items }, + headerActions: { + android: { + startHeaderAction: { + type: 'back', + onPress: () => HybridAutoPlay.popTemplate(), + }, + endHeaderActions: [playAction, shuffleAction], + }, + ios: { + backButton: { + type: 'back', + onPress: () => HybridAutoPlay.popTemplate(), + }, + trailingNavigationBarButtons: [playAction, shuffleAction], + }, + }, + }); +} diff --git a/src/store/music/types.ts b/src/store/music/types.ts index 33f1b4c..f2f4440 100644 --- a/src/store/music/types.ts +++ b/src/store/music/types.ts @@ -166,6 +166,7 @@ export interface Playlist { BackdropImageTags: any[]; LocationType: string; MediaType: string; + ChildCount?: number; Tracks?: string[]; lastRefreshed?: number; } diff --git a/src/utility/JellyfinApi/lib.ts b/src/utility/JellyfinApi/lib.ts index 8ee6139..09d222f 100644 --- a/src/utility/JellyfinApi/lib.ts +++ b/src/utility/JellyfinApi/lib.ts @@ -19,7 +19,7 @@ const deviceMap: Record = { * from the Redux store to a HTTP Header that authenticates the user against the * Jellyfin server. */ -function generateConfig(credentials: Credentials): RequestInit { +export function generateConfig(credentials: Credentials): RequestInit { switch (credentials?.type) { case 'jellyfin': return { diff --git a/src/utility/JellyfinApi/playlist.ts b/src/utility/JellyfinApi/playlist.ts index 02a71d1..55853ff 100644 --- a/src/utility/JellyfinApi/playlist.ts +++ b/src/utility/JellyfinApi/playlist.ts @@ -6,7 +6,7 @@ const playlistOptions = { SortOrder: 'Ascending', IncludeItemTypes: 'Playlist', Recursive: 'true', - Fields: 'PrimaryImageAspectRatio,SortName,BasicSyncInfo,DateCreated', + Fields: 'PrimaryImageAspectRatio,SortName,BasicSyncInfo,DateCreated,ChildCount', ImageTypeLimit: '1', EnableImageTypes: 'Primary,Backdrop,Banner,Thumb' }; diff --git a/src/utility/JellyfinApi/track.ts b/src/utility/JellyfinApi/track.ts index e06c9a2..f885630 100644 --- a/src/utility/JellyfinApi/track.ts +++ b/src/utility/JellyfinApi/track.ts @@ -1,7 +1,7 @@ import { AlbumTrack, CodecMetadata } from '@/store/music/types'; import { Platform } from 'react-native'; import { Track } from 'react-native-track-player'; -import { asyncFetchStore, fetchApi, getImage } from './lib'; +import { asyncFetchStore, fetchApi, getImage, generateConfig } from './lib'; const trackOptionsOsOverrides: Record> = { ios: { @@ -49,6 +49,11 @@ export function generateTrackUrl(trackId: string) { export async function generateTrack(track: AlbumTrack): Promise { // Also construct the URL for the stream const url = generateTrackUrl(track.Id); + + // Get credentials and generate authentication headers + const credentials = asyncFetchStore().getState().settings.credentials; + const config = generateConfig(credentials); + const headers = config.headers; return { url, @@ -58,6 +63,7 @@ export async function generateTrack(track: AlbumTrack): Promise { album: track.Album, duration: track.RunTimeTicks, artwork: getImage(track), + headers, bitRate: baseTrackOptions.audioBitRate, }; } diff --git a/src/utility/usePlayTracks.ts b/src/utility/usePlayTracks.ts index af72904..54bb7a4 100644 --- a/src/utility/usePlayTracks.ts +++ b/src/utility/usePlayTracks.ts @@ -3,6 +3,8 @@ import { useCallback } from 'react'; import TrackPlayer, { Track } from 'react-native-track-player'; import { shuffle as shuffleArray } from 'lodash'; import { generateTrack } from './JellyfinApi/track'; +import type { AlbumTrack } from '@/store/music/types'; +import type { DownloadEntity } from '@/store/downloads/types'; interface PlayOptions { play: boolean; @@ -24,137 +26,151 @@ const defaults: PlayOptions = { method: 'replace', }; +/** + * Core playback logic that can be used outside of React components + * Used by both usePlayTracks hook and CarPlay templates + */ +export async function playTracks( + trackIds: string[] | undefined, + tracks: Record, + downloads: Record, + options: Partial = {}, +): Promise { + if (!trackIds) { + return; + } + + // GUARD: Check options and queue + const { + play, + shuffle, + method, + } = Object.assign({}, defaults, options); + + // Convert all trackIds to the relevant format for react-native-track-player + const generatedTracks = (await Promise.all(trackIds.map(async (trackId) => { + const track = tracks[trackId]; + + // GUARD: Check that the track actually exists in Redux + if (!trackId || !track) { + return; + } + + // Retrieve the generated track from Jellyfin + const generatedTrack = await generateTrack(track); + + // Check if a downloaded version exists, and if so rewrite the URL + const download = downloads[trackId]; + if (download?.location) { + generatedTrack.url = 'file://' + download.location; + } + if (download?.image) { + generatedTrack.artwork = 'file://' + download.image; + } + + return generatedTrack; + }))).filter((t): t is Track => typeof t !== 'undefined'); + + // Potentially shuffle all tracks + const newTracks = shuffle ? shuffleArray(generatedTracks) : generatedTracks; + + console.log('[playTracks] Generated tracks:', newTracks.length, 'tracks'); + console.log('[playTracks] First track URL:', newTracks[0]?.url); + + // Then, we'll need to check where to add the track + switch(method) { + case 'add-to-end': { + await TrackPlayer.add(newTracks); + console.log('[playTracks] Added tracks to end, queue size:', (await TrackPlayer.getQueue()).length); + + // Then we'll skip to it and play it + if (play) { + await TrackPlayer.skip((await TrackPlayer.getQueue()).length - newTracks.length); + await TrackPlayer.play(); + console.log('[playTracks] Playback started'); + } + + break; + } + case 'add-after-currently-playing': { + // Try and locate the current track + const currentTrackIndex = await TrackPlayer.getActiveTrackIndex(); + + if (currentTrackIndex === undefined) { + break; + } + + // TrackPlayer.add wants the index of the track to insert in front of, + // or just one more + const targetTrack = currentTrackIndex + 1; + + // Depending on whether this track exists, we either add it there, + // or at the end of the queue. + await TrackPlayer.add(newTracks, targetTrack); + console.log('[playTracks] Added tracks after current'); + + if (play) { + await TrackPlayer.skip(currentTrackIndex + 1); + await TrackPlayer.play(); + console.log('[playTracks] Playback started'); + } + + break; + } + case 'replace': { + // Reset the queue first + await TrackPlayer.reset(); + console.log('[playTracks] Queue reset'); + + // GUARD: Check if we need to skip to a particular index + if (options.playIndex !== undefined) { + // If so, we'll split the tracks into tracks before the + // index that should be played, and the queue of tracks that + // will start playing + const before = newTracks.slice(0, options.playIndex); + const current = newTracks.slice(options.playIndex); + + // First, we'll add the current queue and (optionally) force + // it to start playing. + await TrackPlayer.add(current); + console.log('[playTracks] Added current tracks, starting at index:', options.playIndex); + if (play) { + await TrackPlayer.play(); + console.log('[playTracks] Playback started'); + } + + // Then, we'll insert the "previous" tracks after the queue + // has started playing. This ensures that these tracks won't + // trigger any events on the track player. + await TrackPlayer.add(before, 0); + } else { + await TrackPlayer.add(newTracks); + console.log('[playTracks] Added all tracks, queue size:', (await TrackPlayer.getQueue()).length); + if (play) { + await TrackPlayer.play(); + const state = await TrackPlayer.getPlaybackState(); + console.log('[playTracks] Playback started, state:', state); + } + } + + break; + } + } + + return newTracks; +} + /** * Generate a callback function that starts playing a full album given its * supplied id. */ export default function usePlayTracks() { - const tracks = useTypedSelector(state => state.music.tracks.entities); - const downloads = useTypedSelector(state => state.downloads.entities); + const tracksEntities = useTypedSelector(state => state.music.tracks.entities); + const downloadsEntities = useTypedSelector(state => state.downloads.entities); - return useCallback(async function playTracks( - trackIds: string[] | undefined, - options: Partial = {}, - ): Promise { - if (!trackIds) { - return; - } - - // Retrieve options and queue - const { - play, - shuffle, - method, - } = Object.assign({}, defaults, options); - - // Convert all trackIds to the relevant format for react-native-track-player - const generatedTracks = (await Promise.all(trackIds.map(async (trackId) => { - const track = tracks[trackId]; - - // GUARD: Check that the track actually exists in Redux - if (!trackId || !track) { - return; - } - - // Retrieve the generated track from Jellyfin - const generatedTrack = await generateTrack(track); - - // Check if a downloaded version exists, and if so rewrite the URL - const download = downloads[trackId]; - if (download?.location) { - generatedTrack.url = 'file://' + download.location; - } - if (download?.image) { - generatedTrack.artwork = 'file://' + download.image; - } - - return generatedTrack; - }))).filter((t): t is Track => typeof t !== 'undefined'); - - // Potentially shuffle all tracks - const newTracks = shuffle ? shuffleArray(generatedTracks) : generatedTracks; - - console.log('[usePlayTracks] Generated tracks:', newTracks.length, 'tracks'); - console.log('[usePlayTracks] First track URL:', newTracks[0]?.url); - - // Then, we'll need to check where to add the track - switch(method) { - case 'add-to-end': { - await TrackPlayer.add(newTracks); - console.log('[usePlayTracks] Added tracks to end, queue size:', (await TrackPlayer.getQueue()).length); - - // Then we'll skip to it and play it - if (play) { - await TrackPlayer.skip((await TrackPlayer.getQueue()).length - newTracks.length); - await TrackPlayer.play(); - console.log('[usePlayTracks] Playback started'); - } - - break; - } - case 'add-after-currently-playing': { - // Try and locate the current track - const currentTrackIndex = await TrackPlayer.getActiveTrackIndex(); - - if (currentTrackIndex === undefined) { - break; - } - - // TrackPlayer.add wants the index of the track to insert in front of, - // or just one more - const targetTrack = currentTrackIndex + 1; - - // Depending on whether this track exists, we either add it there, - // or at the end of the queue. - await TrackPlayer.add(newTracks, targetTrack); - console.log('[usePlayTracks] Added tracks after current'); - - if (play) { - await TrackPlayer.skip(currentTrackIndex + 1); - await TrackPlayer.play(); - console.log('[usePlayTracks] Playback started'); - } - - break; - } - case 'replace': { - // Reset the queue first - await TrackPlayer.reset(); - console.log('[usePlayTracks] Queue reset'); - - // GUARD: Check if we need to skip to a particular index - if (options.playIndex) { - // If so, we'll split the tracks into tracks before the - // index that should be played, and the queue of tracks that - // will start playing - const before = newTracks.slice(0, options.playIndex); - const current = newTracks.slice(options.playIndex); - - // First, we'll add the current queue and (optionally) force - // it to start playing. - await TrackPlayer.add(current); - console.log('[usePlayTracks] Added current tracks, starting at index:', options.playIndex); - if (play) { - await TrackPlayer.play(); - console.log('[usePlayTracks] Playback started'); - } - - // Then, we'll insert the "previous" tracks after the queue - // has started playing. This ensures that these tracks won't - // trigger any events on the track player. - await TrackPlayer.add(before, 0); - } else { - await TrackPlayer.add(newTracks); - console.log('[usePlayTracks] Added all tracks, queue size:', (await TrackPlayer.getQueue()).length); - if (play) { - await TrackPlayer.play(); - const state = await TrackPlayer.getPlaybackState(); - console.log('[usePlayTracks] Playback started, state:', state); - } - } - - break; - } - } - }, [downloads, tracks]); + return useCallback( + (trackIds: string[] | undefined, options: Partial = {}) => + playTracks(trackIds, tracksEntities, downloadsEntities, options), + [downloadsEntities, tracksEntities] + ); } \ No newline at end of file