react-native-audio-browser 0.2.0-next.10 → 0.2.0-next.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/android/build.gradle +6 -5
- package/android/gradle.properties +1 -1
- package/android/src/main/java/com/audiobrowser/AudioBrowser.kt +110 -25
- package/android/src/main/java/com/audiobrowser/browser/BrowserManager.kt +254 -188
- package/android/src/main/java/com/audiobrowser/browser/BrowserUrlResolution.kt +23 -3
- package/android/src/main/java/com/audiobrowser/browser/JsonModels.kt +77 -58
- package/android/src/main/java/com/audiobrowser/browser/SectionScope.kt +68 -26
- package/android/src/main/java/com/audiobrowser/browser/StyleResolver.kt +49 -0
- package/android/src/main/java/com/audiobrowser/extension/ResolvedTrackExt.kt +9 -7
- package/android/src/main/java/com/audiobrowser/extension/TappedTrack.kt +20 -0
- package/android/src/main/java/com/audiobrowser/extension/TrackIdentity.kt +15 -0
- package/android/src/main/java/com/audiobrowser/player/MediaSessionCallback.kt +120 -67
- package/android/src/main/java/com/audiobrowser/player/NowPlayingUpdater.kt +21 -24
- package/android/src/main/java/com/audiobrowser/player/PlaybackStateStore.kt +93 -73
- package/android/src/main/java/com/audiobrowser/player/Player.kt +48 -30
- package/android/src/main/java/com/audiobrowser/player/PlayerCapabilitiesExt.kt +2 -15
- package/android/src/main/java/com/audiobrowser/tls/AiaCertChaser.kt +46 -5
- package/android/src/main/java/com/audiobrowser/tls/AiaChasingExtendedTrustManager.kt +86 -27
- package/android/src/main/java/com/audiobrowser/tls/AiaChasingTrustManager.kt +58 -14
- package/android/src/main/java/com/audiobrowser/tls/AiaTls.kt +20 -6
- package/android/src/main/java/com/audiobrowser/tls/CachingCertificateFetcher.kt +249 -86
- package/android/src/main/java/com/audiobrowser/util/BrowserPathHelper.kt +44 -40
- package/android/src/main/java/com/audiobrowser/util/MediaExtrasBuilder.kt +28 -23
- package/android/src/main/java/com/audiobrowser/util/StyleWire.kt +35 -0
- package/android/src/main/java/com/audiobrowser/util/TrackFactory.kt +45 -53
- package/android/src/test/java/com/audiobrowser/TestFixtures.kt +69 -30
- package/android/src/test/java/com/audiobrowser/browser/BrowserManagerFavoritesTest.kt +153 -0
- package/android/src/test/java/com/audiobrowser/browser/BrowserManagerLayerResolutionTest.kt +6 -0
- package/android/src/test/java/com/audiobrowser/browser/BrowserManagerTest.kt +345 -8
- package/android/src/test/java/com/audiobrowser/browser/BrowserUrlResolutionTest.kt +38 -0
- package/android/src/test/java/com/audiobrowser/browser/JsonArtworkTest.kt +1 -1
- package/android/src/test/java/com/audiobrowser/browser/JsonStyleTest.kt +91 -0
- package/android/src/test/java/com/audiobrowser/browser/SectionScopeTest.kt +128 -53
- package/android/src/test/java/com/audiobrowser/browser/StyleResolverTest.kt +87 -0
- package/android/src/test/java/com/audiobrowser/extension/TappedTrackTest.kt +43 -0
- package/android/src/test/java/com/audiobrowser/extension/TrackIdentityTest.kt +41 -0
- package/android/src/test/java/com/audiobrowser/player/CapabilityControlsTest.kt +1 -2
- package/android/src/test/java/com/audiobrowser/player/NowPlayingUpdaterTest.kt +42 -2
- package/android/src/test/java/com/audiobrowser/player/PlaybackStateStoreTrackJsonTest.kt +55 -0
- package/android/src/test/java/com/audiobrowser/tls/AiaCertChaserTest.kt +152 -8
- package/android/src/test/java/com/audiobrowser/tls/AiaChasingTrustManagerTest.kt +218 -10
- package/android/src/test/java/com/audiobrowser/tls/CachingCertificateFetcherCallSiteTest.kt +322 -0
- package/android/src/test/java/com/audiobrowser/util/BrowserPathHelperTest.kt +41 -16
- package/android/src/test/java/com/audiobrowser/util/MediaExtrasBuilderTest.kt +161 -0
- package/android/src/test/java/com/audiobrowser/util/TrackFactoryMediaIdTest.kt +7 -7
- package/ios/Browser/BrowserConfig.swift +102 -5
- package/ios/Browser/BrowserManager+URLResolution.swift +18 -14
- package/ios/Browser/BrowserManager.swift +131 -143
- package/ios/Browser/BrowserPathHelper.swift +44 -44
- package/ios/Browser/JsonModels.swift +187 -106
- package/ios/Browser/SearchDrillIn.swift +6 -6
- package/ios/Browser/SectionPresentation.swift +34 -0
- package/ios/Browser/SectionScope.swift +58 -24
- package/ios/Browser/StyleResolver.swift +47 -0
- package/ios/Browser/TabBarEntries.swift +2 -2
- package/ios/Browser/TrackIdTemplate.swift +37 -0
- package/ios/CarPlay/CarPlayArtworkResolver.swift +8 -7
- package/ios/CarPlay/CarPlayController.swift +105 -67
- package/ios/CarPlay/CarPlayImageLoader.swift +38 -18
- package/ios/CarPlay/CarPlayListItemFactory.swift +217 -84
- package/ios/CarPlay/CarPlayNowPlayingManager.swift +15 -12
- package/ios/Extension/Capability+RemoteCommand.swift +1 -1
- package/ios/Extension/PlayerCapabilities+Favorite.swift +1 -15
- package/ios/Extension/ResolvedTrack+Copying.swift +11 -15
- package/ios/Extension/Section+Copying.swift +31 -0
- package/ios/Extension/Track+Copying.swift +6 -12
- package/ios/Extension/Track+Identity.swift +18 -0
- package/ios/HybridAudioBrowser.swift +113 -35
- package/ios/Model/NitroTypeStubs.swift +64 -23
- package/ios/Model/PlayerUpdateOptions.swift +7 -0
- package/ios/Player/NowPlayingUpdater.swift +1 -1
- package/ios/Player/PlaybackCoordinator.swift +2 -2
- package/ios/Player/RetryManager.swift +4 -3
- package/ios/Tests/BrowserPathHelperTests.swift +50 -20
- package/ios/Tests/CarPlayArtworkResolverTests.swift +24 -41
- package/ios/Tests/JsonModelsTests.swift +75 -1
- package/ios/Tests/PlaybackStateStoreTests.swift +4 -3
- package/ios/Tests/QueueManagerTests.swift +4 -4
- package/ios/Tests/ResolvedTrackCopyingTests.swift +37 -35
- package/ios/Tests/SearchDrillInTests.swift +16 -16
- package/ios/Tests/SectionPresentationTests.swift +61 -0
- package/ios/Tests/SectionScopeTests.swift +87 -55
- package/ios/Tests/StyleResolverTests.swift +67 -0
- package/ios/Tests/TabBarEntriesTests.swift +18 -18
- package/ios/Tests/TrackIdTemplateTests.swift +67 -0
- package/ios/Tests/TrackIdentityTests.swift +20 -0
- package/ios/Tests/TrackSelectorTests.swift +168 -33
- package/ios/TrackSelector.swift +45 -30
- package/ios/Util/ArtworkImageFetcher.swift +16 -1
- package/lib/commonjs/AudioBrowser.js +11 -0
- package/lib/commonjs/AudioBrowser.js.map +1 -1
- package/lib/commonjs/features/browser-config.js +11 -8
- package/lib/commonjs/features/browser-config.js.map +1 -1
- package/lib/commonjs/features/browser.js +2 -2
- package/lib/commonjs/features/favorites.js +13 -10
- package/lib/commonjs/features/favorites.js.map +1 -1
- package/lib/commonjs/features/playback/controls.js +4 -2
- package/lib/commonjs/features/playback/controls.js.map +1 -1
- package/lib/commonjs/features/playback/state.js +2 -3
- package/lib/commonjs/features/playback/state.js.map +1 -1
- package/lib/commonjs/features/player/options.js.map +1 -1
- package/lib/commonjs/features/player/setup.js +3 -1
- package/lib/commonjs/features/player/setup.js.map +1 -1
- package/lib/commonjs/features/player/validateOptions.js +2 -0
- package/lib/commonjs/features/player/validateOptions.js.map +1 -1
- package/lib/commonjs/features/queue/activeTrack.js +14 -3
- package/lib/commonjs/features/queue/activeTrack.js.map +1 -1
- package/lib/commonjs/features/queue/queue.js +11 -4
- package/lib/commonjs/features/queue/queue.js.map +1 -1
- package/lib/commonjs/utils/LazyNativeEmitter.js +21 -10
- package/lib/commonjs/utils/LazyNativeEmitter.js.map +1 -1
- package/lib/commonjs/utils/NativeUpdatedValue.js +31 -13
- package/lib/commonjs/utils/NativeUpdatedValue.js.map +1 -1
- package/lib/commonjs/utils/getTrackIdentity.js +24 -0
- package/lib/commonjs/utils/getTrackIdentity.js.map +1 -0
- package/lib/commonjs/utils/useNativeValueRefreshedBy.js +33 -0
- package/lib/commonjs/utils/useNativeValueRefreshedBy.js.map +1 -0
- package/lib/commonjs/web/NativeAudioBrowser.js +32 -24
- package/lib/commonjs/web/NativeAudioBrowser.js.map +1 -1
- package/lib/commonjs/web/browser/BrowserManager.js +144 -87
- package/lib/commonjs/web/browser/BrowserManager.js.map +1 -1
- package/lib/commonjs/web/browser/FavoriteManager.js +30 -21
- package/lib/commonjs/web/browser/FavoriteManager.js.map +1 -1
- package/lib/commonjs/web/browser/assertBrowsePageShape.js +2 -2
- package/lib/commonjs/web/browser/assertBrowsePageShape.js.map +1 -1
- package/lib/commonjs/web/browser/classifyTrackNavigation.js +9 -9
- package/lib/commonjs/web/browser/classifyTrackNavigation.js.map +1 -1
- package/lib/commonjs/web/browser/sections.js +89 -0
- package/lib/commonjs/web/browser/sections.js.map +1 -0
- package/lib/commonjs/web/player/NowPlayingManager.js +4 -3
- package/lib/commonjs/web/player/NowPlayingManager.js.map +1 -1
- package/lib/commonjs/web/util/BrowserPathHelper.js +56 -11
- package/lib/commonjs/web/util/BrowserPathHelper.js.map +1 -1
- package/lib/module/AudioBrowser.js +1 -0
- package/lib/module/AudioBrowser.js.map +1 -1
- package/lib/module/features/browser-config.js +11 -8
- package/lib/module/features/browser-config.js.map +1 -1
- package/lib/module/features/browser.js +2 -2
- package/lib/module/features/favorites.js +13 -10
- package/lib/module/features/favorites.js.map +1 -1
- package/lib/module/features/playback/controls.js +4 -2
- package/lib/module/features/playback/controls.js.map +1 -1
- package/lib/module/features/playback/state.js +2 -3
- package/lib/module/features/playback/state.js.map +1 -1
- package/lib/module/features/player/options.js.map +1 -1
- package/lib/module/features/player/setup.js +3 -1
- package/lib/module/features/player/setup.js.map +1 -1
- package/lib/module/features/player/validateOptions.js +2 -0
- package/lib/module/features/player/validateOptions.js.map +1 -1
- package/lib/module/features/queue/activeTrack.js +14 -3
- package/lib/module/features/queue/activeTrack.js.map +1 -1
- package/lib/module/features/queue/queue.js +11 -4
- package/lib/module/features/queue/queue.js.map +1 -1
- package/lib/module/utils/LazyNativeEmitter.js +21 -10
- package/lib/module/utils/LazyNativeEmitter.js.map +1 -1
- package/lib/module/utils/NativeUpdatedValue.js +31 -13
- package/lib/module/utils/NativeUpdatedValue.js.map +1 -1
- package/lib/module/utils/getTrackIdentity.js +20 -0
- package/lib/module/utils/getTrackIdentity.js.map +1 -0
- package/lib/module/utils/useNativeValueRefreshedBy.js +29 -0
- package/lib/module/utils/useNativeValueRefreshedBy.js.map +1 -0
- package/lib/module/web/NativeAudioBrowser.js +32 -24
- package/lib/module/web/NativeAudioBrowser.js.map +1 -1
- package/lib/module/web/browser/BrowserManager.js +144 -87
- package/lib/module/web/browser/BrowserManager.js.map +1 -1
- package/lib/module/web/browser/FavoriteManager.js +31 -21
- package/lib/module/web/browser/FavoriteManager.js.map +1 -1
- package/lib/module/web/browser/assertBrowsePageShape.js +2 -2
- package/lib/module/web/browser/assertBrowsePageShape.js.map +1 -1
- package/lib/module/web/browser/classifyTrackNavigation.js +9 -9
- package/lib/module/web/browser/classifyTrackNavigation.js.map +1 -1
- package/lib/module/web/browser/sections.js +83 -0
- package/lib/module/web/browser/sections.js.map +1 -0
- package/lib/module/web/player/NowPlayingManager.js +4 -3
- package/lib/module/web/player/NowPlayingManager.js.map +1 -1
- package/lib/module/web/util/BrowserPathHelper.js +56 -11
- package/lib/module/web/util/BrowserPathHelper.js.map +1 -1
- package/lib/typescript/src/AudioBrowser.d.ts +1 -0
- package/lib/typescript/src/AudioBrowser.d.ts.map +1 -1
- package/lib/typescript/src/features/browser-config.d.ts.map +1 -1
- package/lib/typescript/src/features/browser.d.ts +2 -2
- package/lib/typescript/src/features/favorites.d.ts +13 -10
- package/lib/typescript/src/features/favorites.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/controls.d.ts +4 -2
- package/lib/typescript/src/features/playback/controls.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/state.d.ts +2 -3
- package/lib/typescript/src/features/playback/state.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts +1 -1
- package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts.map +1 -1
- package/lib/typescript/src/features/player/options.d.ts +21 -19
- package/lib/typescript/src/features/player/options.d.ts.map +1 -1
- package/lib/typescript/src/features/player/setup.d.ts.map +1 -1
- package/lib/typescript/src/features/player/validateOptions.d.ts +1 -8
- package/lib/typescript/src/features/player/validateOptions.d.ts.map +1 -1
- package/lib/typescript/src/features/queue/activeTrack.d.ts +8 -1
- package/lib/typescript/src/features/queue/activeTrack.d.ts.map +1 -1
- package/lib/typescript/src/features/queue/queue.d.ts +5 -2
- package/lib/typescript/src/features/queue/queue.d.ts.map +1 -1
- package/lib/typescript/src/types/browser-native.d.ts +2 -2
- package/lib/typescript/src/types/browser-native.d.ts.map +1 -1
- package/lib/typescript/src/types/browser-nodes.d.ts +260 -127
- package/lib/typescript/src/types/browser-nodes.d.ts.map +1 -1
- package/lib/typescript/src/types/browser.d.ts +33 -53
- package/lib/typescript/src/types/browser.d.ts.map +1 -1
- package/lib/typescript/src/utils/LazyNativeEmitter.d.ts +16 -17
- package/lib/typescript/src/utils/LazyNativeEmitter.d.ts.map +1 -1
- package/lib/typescript/src/utils/NativeUpdatedValue.d.ts +24 -19
- package/lib/typescript/src/utils/NativeUpdatedValue.d.ts.map +1 -1
- package/lib/typescript/src/utils/getTrackIdentity.d.ts +18 -0
- package/lib/typescript/src/utils/getTrackIdentity.d.ts.map +1 -0
- package/lib/typescript/src/utils/useNativeValueRefreshedBy.d.ts +21 -0
- package/lib/typescript/src/utils/useNativeValueRefreshedBy.d.ts.map +1 -0
- package/nitrogen/generated/android/AudioBrowser+autolinking.cmake +0 -1
- package/nitrogen/generated/android/c++/{JFavoritesMatchMode.hpp → JArtworkRendering.hpp} +15 -15
- package/nitrogen/generated/android/c++/JArtworkRequestConfig.hpp +4 -3
- package/nitrogen/generated/android/c++/JBrowseResult.hpp +10 -4
- package/nitrogen/generated/android/c++/JCarPlayPlayingIndicatorLocation.hpp +58 -0
- package/nitrogen/generated/android/c++/JFavoriteChangedEvent.hpp +4 -3
- package/nitrogen/generated/android/c++/JFormatNowPlayingParams.hpp +4 -3
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___Track.hpp +4 -3
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.hpp +4 -3
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_std__string____Track.hpp +4 -3
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____Track.hpp +4 -3
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_Track______SearchParams.hpp +4 -2
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_void_____TrackLoadEvent.hpp +4 -2
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError__std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError_______BrowserSourceCallbackParam.hpp +10 -4
- package/nitrogen/generated/android/c++/JFunc_void_FavoriteChangedEvent.hpp +4 -3
- package/nitrogen/generated/android/c++/JFunc_void_Options.hpp +2 -5
- package/nitrogen/generated/android/c++/JFunc_void_PlaybackActiveTrackChangedEvent.hpp +4 -3
- package/nitrogen/generated/android/c++/JFunc_void_std__optional_ResolvedTrack_.hpp +9 -3
- package/nitrogen/generated/android/c++/JFunc_void_std__vector_Track_.hpp +4 -2
- package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.cpp +21 -14
- package/nitrogen/generated/android/c++/JIOSOptions.hpp +9 -3
- package/nitrogen/generated/android/c++/JMediaRequestConfig.hpp +4 -3
- package/nitrogen/generated/android/c++/JNativeBrowserConfiguration.hpp +16 -10
- package/nitrogen/generated/android/c++/JNativeRouteEntry.hpp +8 -2
- package/nitrogen/generated/android/c++/JNativeSetupPlayerOptions.hpp +6 -7
- package/nitrogen/generated/android/c++/JNativeUpdateOptions.hpp +2 -5
- package/nitrogen/generated/android/c++/JNitroIOSUpdateOptions.hpp +9 -3
- package/nitrogen/generated/android/c++/JOptions.hpp +2 -5
- package/nitrogen/generated/android/c++/JPlaybackActiveTrackChangedEvent.hpp +4 -3
- package/nitrogen/generated/android/c++/JPlayerCapabilities.hpp +5 -11
- package/nitrogen/generated/android/c++/JResolvedTrack.hpp +49 -51
- package/nitrogen/generated/android/c++/JSection.hpp +114 -0
- package/nitrogen/generated/android/c++/JSectionStyle.hpp +69 -0
- package/nitrogen/generated/android/c++/JStyleDisplay.hpp +58 -0
- package/nitrogen/generated/android/c++/JTrack.hpp +19 -48
- package/nitrogen/generated/android/c++/JTrackLoadEvent.hpp +4 -2
- package/nitrogen/generated/android/c++/JTrackStyle.hpp +25 -18
- package/nitrogen/generated/android/c++/JVariant_ResolvedTrack_BrowseError_Promise_BrowseResult_.hpp +10 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{FavoritesMatchMode.kt → ArtworkRendering.kt} +5 -5
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/CarPlayPlayingIndicatorLocation.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/IOSOptions.kt +6 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeBrowserConfiguration.kt +5 -5
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroIOSUpdateOptions.kt +6 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlayerCapabilities.kt +2 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ResolvedTrack.kt +12 -18
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Section.kt +50 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{FavoriteConfig.kt → SectionStyle.kt} +12 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/StyleDisplay.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Track.kt +6 -15
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TrackStyle.kt +23 -5
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.hpp +104 -82
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Umbrella.hpp +16 -10
- package/nitrogen/generated/ios/c++/HybridAudioBrowserSpecSwift.hpp +16 -10
- package/nitrogen/generated/ios/swift/ArtworkRendering.swift +40 -0
- package/nitrogen/generated/ios/swift/CarPlayPlayingIndicatorLocation.swift +40 -0
- package/nitrogen/generated/ios/swift/IOSOptions.swift +7 -2
- package/nitrogen/generated/ios/swift/NativeBrowserConfiguration.swift +5 -5
- package/nitrogen/generated/ios/swift/NitroIOSUpdateOptions.swift +12 -1
- package/nitrogen/generated/ios/swift/PlayerCapabilities.swift +8 -27
- package/nitrogen/generated/ios/swift/ResolvedTrack.swift +54 -83
- package/nitrogen/generated/ios/swift/Section.swift +100 -0
- package/nitrogen/generated/ios/swift/SectionStyle.swift +64 -0
- package/nitrogen/generated/ios/swift/StyleDisplay.swift +40 -0
- package/nitrogen/generated/ios/swift/Track.swift +20 -73
- package/nitrogen/generated/ios/swift/TrackStyle.swift +28 -22
- package/nitrogen/generated/shared/c++/{FavoritesMatchMode.hpp → ArtworkRendering.hpp} +17 -17
- package/nitrogen/generated/shared/c++/CarPlayPlayingIndicatorLocation.hpp +76 -0
- package/nitrogen/generated/shared/c++/IOSOptions.hpp +9 -2
- package/nitrogen/generated/shared/c++/NativeBrowserConfiguration.hpp +6 -6
- package/nitrogen/generated/shared/c++/NitroIOSUpdateOptions.hpp +9 -2
- package/nitrogen/generated/shared/c++/PlayerCapabilities.hpp +6 -9
- package/nitrogen/generated/shared/c++/ResolvedTrack.hpp +30 -38
- package/nitrogen/generated/shared/c++/Section.hpp +106 -0
- package/nitrogen/generated/shared/c++/SectionStyle.hpp +96 -0
- package/nitrogen/generated/shared/c++/StyleDisplay.hpp +76 -0
- package/nitrogen/generated/shared/c++/Track.hpp +15 -31
- package/nitrogen/generated/shared/c++/TrackStyle.hpp +50 -34
- package/package.json +3 -2
- package/src/AudioBrowser.ts +1 -0
- package/src/features/browser-config.test.ts +21 -6
- package/src/features/browser-config.ts +11 -8
- package/src/features/browser.test.ts +1 -1
- package/src/features/browser.ts +2 -2
- package/src/features/favorites.ts +13 -10
- package/src/features/hook-wiring.test.ts +63 -15
- package/src/features/playback/controls.ts +4 -2
- package/src/features/playback/state.ts +2 -3
- package/src/features/playback/trackPlaybackTime.ts +1 -1
- package/src/features/player/options.ts +27 -19
- package/src/features/player/setup.test.ts +12 -3
- package/src/features/player/setup.ts +3 -1
- package/src/features/player/validateOptions.ts +2 -0
- package/src/features/queue/activeTrack.ts +14 -3
- package/src/features/queue/queue.ts +11 -4
- package/src/nitro-stub-drift.test.ts +277 -0
- package/src/public-surface.test.ts +40 -0
- package/src/specs/audio-browser.nitro.ts +4 -3
- package/src/style-resolution.test.ts +139 -0
- package/src/types/browser-native.ts +2 -2
- package/src/types/browser-nodes.ts +268 -136
- package/src/types/browser.ts +34 -55
- package/src/utils/LazyNativeEmitter.ts +20 -10
- package/src/utils/NativeUpdatedValue.ts +29 -13
- package/src/utils/getTrackIdentity.ts +20 -0
- package/src/utils/useNativeValueRefreshedBy.ts +38 -0
- package/src/web/NativeAudioBrowser.ts +37 -22
- package/src/web/browser/BrowserManager.ts +176 -95
- package/src/web/browser/FavoriteManager.test.ts +84 -0
- package/src/web/browser/FavoriteManager.ts +28 -23
- package/src/web/browser/assertBrowsePageShape.test.ts +1 -1
- package/src/web/browser/assertBrowsePageShape.ts +5 -3
- package/src/web/browser/classifyTrackNavigation.test.ts +10 -8
- package/src/web/browser/classifyTrackNavigation.ts +9 -9
- package/src/web/browser/parseSearchResponse.test.ts +8 -2
- package/src/web/browser/sections.test.ts +91 -0
- package/src/web/browser/sections.ts +72 -0
- package/src/web/player/NowPlayingManager.ts +4 -3
- package/src/web/util/BrowserPathHelper.test.ts +52 -0
- package/src/web/util/BrowserPathHelper.ts +61 -13
- package/ios/CarPlay/ImageRowItem+CarPlay.swift +0 -19
- package/nitrogen/generated/android/c++/JFavoriteConfig.hpp +0 -58
- package/nitrogen/generated/android/c++/JImageRowItem.hpp +0 -105
- package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.cpp +0 -26
- package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.hpp +0 -71
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageRowItem.kt +0 -68
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_Boolean_FavoriteConfig.kt +0 -53
- package/nitrogen/generated/ios/swift/FavoriteConfig.swift +0 -29
- package/nitrogen/generated/ios/swift/FavoritesMatchMode.swift +0 -40
- package/nitrogen/generated/ios/swift/ImageRowItem.swift +0 -195
- package/nitrogen/generated/ios/swift/Variant_Bool_FavoriteConfig.swift +0 -18
- package/nitrogen/generated/shared/c++/FavoriteConfig.hpp +0 -84
- package/nitrogen/generated/shared/c++/ImageRowItem.hpp +0 -129
|
@@ -7,11 +7,6 @@
|
|
|
7
7
|
|
|
8
8
|
#pragma once
|
|
9
9
|
|
|
10
|
-
#if __has_include(<NitroModules/NitroHash.hpp>)
|
|
11
|
-
#include <NitroModules/NitroHash.hpp>
|
|
12
|
-
#else
|
|
13
|
-
#error NitroModules cannot be found! Are you sure you installed NitroModules properly?
|
|
14
|
-
#endif
|
|
15
10
|
#if __has_include(<NitroModules/JSIConverter.hpp>)
|
|
16
11
|
#include <NitroModules/JSIConverter.hpp>
|
|
17
12
|
#else
|
|
@@ -22,54 +17,75 @@
|
|
|
22
17
|
#else
|
|
23
18
|
#error NitroModules cannot be found! Are you sure you installed NitroModules properly?
|
|
24
19
|
#endif
|
|
20
|
+
#if __has_include(<NitroModules/JSIHelpers.hpp>)
|
|
21
|
+
#include <NitroModules/JSIHelpers.hpp>
|
|
22
|
+
#else
|
|
23
|
+
#error NitroModules cannot be found! Are you sure you installed NitroModules properly?
|
|
24
|
+
#endif
|
|
25
|
+
#if __has_include(<NitroModules/PropNameIDCache.hpp>)
|
|
26
|
+
#include <NitroModules/PropNameIDCache.hpp>
|
|
27
|
+
#else
|
|
28
|
+
#error NitroModules cannot be found! Are you sure you installed NitroModules properly?
|
|
29
|
+
#endif
|
|
30
|
+
|
|
31
|
+
// Forward declaration of `StyleDisplay` to properly resolve imports.
|
|
32
|
+
namespace margelo::nitro::audiobrowser { enum class StyleDisplay; }
|
|
33
|
+
// Forward declaration of `ArtworkRendering` to properly resolve imports.
|
|
34
|
+
namespace margelo::nitro::audiobrowser { enum class ArtworkRendering; }
|
|
35
|
+
|
|
36
|
+
#include "StyleDisplay.hpp"
|
|
37
|
+
#include <optional>
|
|
38
|
+
#include "ArtworkRendering.hpp"
|
|
25
39
|
|
|
26
40
|
namespace margelo::nitro::audiobrowser {
|
|
27
41
|
|
|
28
42
|
/**
|
|
29
|
-
*
|
|
43
|
+
* A struct which can be represented as a JavaScript object (TrackStyle).
|
|
30
44
|
*/
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
45
|
+
struct TrackStyle final {
|
|
46
|
+
public:
|
|
47
|
+
std::optional<StyleDisplay> display SWIFT_PRIVATE;
|
|
48
|
+
std::optional<ArtworkRendering> artworkRendering SWIFT_PRIVATE;
|
|
49
|
+
|
|
50
|
+
public:
|
|
51
|
+
TrackStyle() = default;
|
|
52
|
+
explicit TrackStyle(std::optional<StyleDisplay> display, std::optional<ArtworkRendering> artworkRendering): display(display), artworkRendering(artworkRendering) {}
|
|
53
|
+
|
|
54
|
+
public:
|
|
55
|
+
friend bool operator==(const TrackStyle& lhs, const TrackStyle& rhs) = default;
|
|
56
|
+
};
|
|
35
57
|
|
|
36
58
|
} // namespace margelo::nitro::audiobrowser
|
|
37
59
|
|
|
38
60
|
namespace margelo::nitro {
|
|
39
61
|
|
|
40
|
-
// C++ TrackStyle <> JS TrackStyle (
|
|
62
|
+
// C++ TrackStyle <> JS TrackStyle (object)
|
|
41
63
|
template <>
|
|
42
64
|
struct JSIConverter<margelo::nitro::audiobrowser::TrackStyle> final {
|
|
43
65
|
static inline margelo::nitro::audiobrowser::TrackStyle fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
throw std::invalid_argument("Cannot convert \"" + unionValue + "\" to enum TrackStyle - invalid value!");
|
|
50
|
-
}
|
|
66
|
+
jsi::Object obj = arg.asObject(runtime);
|
|
67
|
+
return margelo::nitro::audiobrowser::TrackStyle(
|
|
68
|
+
JSIConverter<std::optional<margelo::nitro::audiobrowser::StyleDisplay>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "display"))),
|
|
69
|
+
JSIConverter<std::optional<margelo::nitro::audiobrowser::ArtworkRendering>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "artworkRendering")))
|
|
70
|
+
);
|
|
51
71
|
}
|
|
52
|
-
static inline jsi::Value toJSI(jsi::Runtime& runtime, margelo::nitro::audiobrowser::TrackStyle arg) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
throw std::invalid_argument("Cannot convert TrackStyle to JS - invalid value: "
|
|
58
|
-
+ std::to_string(static_cast<int>(arg)) + "!");
|
|
59
|
-
}
|
|
72
|
+
static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::audiobrowser::TrackStyle& arg) {
|
|
73
|
+
jsi::Object obj(runtime);
|
|
74
|
+
obj.setProperty(runtime, PropNameIDCache::get(runtime, "display"), JSIConverter<std::optional<margelo::nitro::audiobrowser::StyleDisplay>>::toJSI(runtime, arg.display));
|
|
75
|
+
obj.setProperty(runtime, PropNameIDCache::get(runtime, "artworkRendering"), JSIConverter<std::optional<margelo::nitro::audiobrowser::ArtworkRendering>>::toJSI(runtime, arg.artworkRendering));
|
|
76
|
+
return obj;
|
|
60
77
|
}
|
|
61
78
|
static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
|
|
62
|
-
if (!value.
|
|
79
|
+
if (!value.isObject()) {
|
|
63
80
|
return false;
|
|
64
81
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
case hashString("grid"):
|
|
69
|
-
return true;
|
|
70
|
-
default:
|
|
71
|
-
return false;
|
|
82
|
+
jsi::Object obj = value.getObject(runtime);
|
|
83
|
+
if (!nitro::isPlainObject(runtime, obj)) {
|
|
84
|
+
return false;
|
|
72
85
|
}
|
|
86
|
+
if (!JSIConverter<std::optional<margelo::nitro::audiobrowser::StyleDisplay>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "display")))) return false;
|
|
87
|
+
if (!JSIConverter<std::optional<margelo::nitro::audiobrowser::ArtworkRendering>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "artworkRendering")))) return false;
|
|
88
|
+
return true;
|
|
73
89
|
}
|
|
74
90
|
};
|
|
75
91
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-audio-browser",
|
|
3
|
-
"version": "0.2.0-next.
|
|
3
|
+
"version": "0.2.0-next.12",
|
|
4
4
|
"description": "react-native-audio-browser is a react native package built with Nitro",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react-native",
|
|
@@ -52,7 +52,8 @@
|
|
|
52
52
|
"build": "yarn prepare",
|
|
53
53
|
"clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib",
|
|
54
54
|
"codegen": "node codegen/index.js --logLevel=\"debug\" && node post-script.cjs && node codegen/strip-struct-equality.cjs && yarn build",
|
|
55
|
-
"prepare": "bob build",
|
|
55
|
+
"prepare": "bob build && yarn hooks:install",
|
|
56
|
+
"hooks:install": "git config core.hooksPath .githooks || exit 0",
|
|
56
57
|
"example": "yarn workspace example-native",
|
|
57
58
|
"ci:format": "oxfmt --check --ignore-path .gitignore --ignore-path .oxfmtignore .",
|
|
58
59
|
"ci:format:ios": "swiftformat --lint ios",
|
package/src/AudioBrowser.ts
CHANGED
|
@@ -18,7 +18,7 @@ describe('toNativeConfig route classification', () => {
|
|
|
18
18
|
|
|
19
19
|
it('keeps a static page with a string artwork out of the RouteConfig branch', () => {
|
|
20
20
|
const page = {
|
|
21
|
-
|
|
21
|
+
path: '/x',
|
|
22
22
|
title: 'X',
|
|
23
23
|
artwork: 'https://img.example.com/x.png',
|
|
24
24
|
children: []
|
|
@@ -28,6 +28,21 @@ describe('toNativeConfig route classification', () => {
|
|
|
28
28
|
expect(entry?.artwork).toBeUndefined()
|
|
29
29
|
})
|
|
30
30
|
|
|
31
|
+
it('classifies a static page as static despite its top-level `path` key', () => {
|
|
32
|
+
// `path` is also a RequestConfig field; the page's required `title` must
|
|
33
|
+
// win the classification (a config with a `path` alone is not a page).
|
|
34
|
+
const page = { path: '/x', title: 'X', children: [] }
|
|
35
|
+
const entry = entryFor({ '/x': page }, '/x')
|
|
36
|
+
expect(entry?.browseStatic).toEqual(page)
|
|
37
|
+
expect(entry?.browseConfig).toBeUndefined()
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('classifies a path-only request config as a request config', () => {
|
|
41
|
+
const entry = entryFor({ '/x': { path: '/remapped' } }, '/x')
|
|
42
|
+
expect(entry?.browseConfig).toEqual({ path: '/remapped' })
|
|
43
|
+
expect(entry?.browseStatic).toBeUndefined()
|
|
44
|
+
})
|
|
45
|
+
|
|
31
46
|
it('classifies a method-only request config as a request config', () => {
|
|
32
47
|
const entry = entryFor({ '/x': { method: 'POST' } }, '/x')
|
|
33
48
|
expect(entry?.browseConfig).toEqual({ method: 'POST' })
|
|
@@ -66,13 +81,13 @@ describe('validateBrowserConfiguration', () => {
|
|
|
66
81
|
it('is silent for a valid configuration', () => {
|
|
67
82
|
validateBrowserConfiguration({
|
|
68
83
|
routes: {
|
|
69
|
-
'/favorites': async () => ({
|
|
84
|
+
'/favorites': async () => ({ path: '/favorites', title: 'Favorites' }),
|
|
70
85
|
'/albums/{id}': { baseUrl: 'https://api.example.com' },
|
|
71
86
|
'*': { baseUrl: 'https://api.example.com' }
|
|
72
87
|
},
|
|
73
88
|
tabs: [
|
|
74
|
-
{ title: 'Home',
|
|
75
|
-
{ title: 'Search',
|
|
89
|
+
{ title: 'Home', path: '/' },
|
|
90
|
+
{ title: 'Search', path: '/search' }
|
|
76
91
|
]
|
|
77
92
|
})
|
|
78
93
|
expect(warnings).toEqual([])
|
|
@@ -96,14 +111,14 @@ describe('validateBrowserConfiguration', () => {
|
|
|
96
111
|
|
|
97
112
|
it('does not flag a valid static page as unclassifiable', () => {
|
|
98
113
|
validateBrowserConfiguration({
|
|
99
|
-
routes: { '/x': {
|
|
114
|
+
routes: { '/x': { path: '/x', title: 'X', children: [] } }
|
|
100
115
|
})
|
|
101
116
|
expect(warnings).toEqual([])
|
|
102
117
|
})
|
|
103
118
|
|
|
104
119
|
it('warns on more than 4 static tabs', () => {
|
|
105
120
|
validateBrowserConfiguration({
|
|
106
|
-
tabs: [1, 2, 3, 4, 5].map((n) => ({ title: `Tab ${n}`,
|
|
121
|
+
tabs: [1, 2, 3, 4, 5].map((n) => ({ title: `Tab ${n}`, path: `/${n}` }))
|
|
107
122
|
})
|
|
108
123
|
expect(warnings.some((w) => w.includes('at most 4'))).toBe(true)
|
|
109
124
|
})
|
|
@@ -38,9 +38,12 @@ function isTransformableRequestConfig(
|
|
|
38
38
|
source: unknown
|
|
39
39
|
): source is TransformableRequestConfig {
|
|
40
40
|
if (!isObjectValue(source)) return false
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
//
|
|
41
|
+
// A static ResolvedTrack page also carries a top-level `path`, so `path`
|
|
42
|
+
// alone cannot mark a request config. A page is recognised by its required
|
|
43
|
+
// `title` — no RequestConfig field is named `title`.
|
|
44
|
+
if ('title' in source) return false
|
|
45
|
+
// Any RequestConfig key marks a request config. (A static ResolvedTrack's
|
|
46
|
+
// per-track request override is nested under `request`, not top-level.)
|
|
44
47
|
return (
|
|
45
48
|
'baseUrl' in source ||
|
|
46
49
|
'path' in source ||
|
|
@@ -120,7 +123,7 @@ function searchSourceToRouteEntry(source: SearchSource): NativeRouteEntry {
|
|
|
120
123
|
*/
|
|
121
124
|
function wrapTracksAsResolvedTrack(tracks: Track[]): ResolvedTrack {
|
|
122
125
|
return {
|
|
123
|
-
|
|
126
|
+
path: TABS_ROUTE_PATH,
|
|
124
127
|
title: 'Tabs',
|
|
125
128
|
children: tracks
|
|
126
129
|
}
|
|
@@ -245,14 +248,14 @@ export function validateBrowserConfiguration(
|
|
|
245
248
|
!isRouteConfig(source) &&
|
|
246
249
|
!isTransformableRequestConfig(source)
|
|
247
250
|
) {
|
|
248
|
-
// Falls through to the static-page branch; a page needs
|
|
251
|
+
// Falls through to the static-page branch; a page needs path + title.
|
|
249
252
|
const page = source as Partial<ResolvedTrack>
|
|
250
|
-
if (typeof page.
|
|
253
|
+
if (typeof page.path !== 'string' || typeof page.title !== 'string') {
|
|
251
254
|
warn(
|
|
252
255
|
`route "${path}" matched no source shape — expected a callback, ` +
|
|
253
256
|
`a request config ({ baseUrl, path, headers, ... }), a ` +
|
|
254
257
|
`RouteConfig ({ browse, media, artwork }), or a static page ` +
|
|
255
|
-
`({
|
|
258
|
+
`({ path, title, children? }). It will be served as static ` +
|
|
256
259
|
`content and likely render a blank screen.`
|
|
257
260
|
)
|
|
258
261
|
}
|
|
@@ -287,7 +290,7 @@ export function toNativeConfig(
|
|
|
287
290
|
handleTrackLoad: config.handleTrackLoad,
|
|
288
291
|
androidControllerOfflineError: config.androidControllerOfflineError,
|
|
289
292
|
carPlayLoadingTitle: config.carPlayLoadingTitle,
|
|
290
|
-
|
|
293
|
+
resolveAlbumPath: config.resolveAlbumPath,
|
|
291
294
|
formatNavigationError: config.formatNavigationError
|
|
292
295
|
}
|
|
293
296
|
}
|
|
@@ -12,7 +12,7 @@ describe('getBrowserConfiguration', () => {
|
|
|
12
12
|
})
|
|
13
13
|
|
|
14
14
|
it('returns the last configuration in its public shape', () => {
|
|
15
|
-
const config = { tabs: [{ title: 'Home',
|
|
15
|
+
const config = { tabs: [{ title: 'Home', path: '/' }] }
|
|
16
16
|
configureBrowser(config)
|
|
17
17
|
expect(getBrowserConfiguration()).toBe(config)
|
|
18
18
|
})
|
package/src/features/browser.ts
CHANGED
|
@@ -43,8 +43,8 @@ let currentConfiguration: BrowserConfiguration | undefined
|
|
|
43
43
|
* }
|
|
44
44
|
* },
|
|
45
45
|
* tabs: [
|
|
46
|
-
* { title: 'Home',
|
|
47
|
-
* { title: 'Search',
|
|
46
|
+
* { title: 'Home', path: '/' },
|
|
47
|
+
* { title: 'Search', path: '/search' }
|
|
48
48
|
* ]
|
|
49
49
|
* })
|
|
50
50
|
* ```
|
|
@@ -37,26 +37,29 @@ export function toggleActiveTrackFavorited(): void {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
|
-
* Sets the list of favorited track
|
|
40
|
+
* Sets the list of favorited track identifiers.
|
|
41
41
|
*
|
|
42
42
|
* This syncs your app's favorites with the native favorites cache, enabling
|
|
43
43
|
* the heart button in media controllers (notification, Android Auto, CarPlay)
|
|
44
44
|
* to show the correct state.
|
|
45
45
|
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
46
|
+
* Each identifier is matched **exactly** against a track's *identity* — its
|
|
47
|
+
* `id` when set, falling back to `src`. Store favorites as the same stable
|
|
48
|
+
* identifier you put in `Track.id` (or as the full `src` for id-less tracks).
|
|
49
49
|
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
50
|
+
* A track whose response already carries `favorited` keeps that value on
|
|
51
|
+
* display surfaces — a caller-set flag wins over cache hydration. The cache
|
|
52
|
+
* itself is written only by this call and by heart toggles
|
|
53
|
+
* (`setActiveTrackFavorited()` / media-controller taps), so you only need to
|
|
54
|
+
* call this on app launch — and again whenever the collection changes outside
|
|
55
|
+
* the player (a sync from another device, say).
|
|
53
56
|
*
|
|
54
|
-
* @param favorites - Array of favorited track `src`
|
|
57
|
+
* @param favorites - Array of favorited track identities (`id`, or `src` for id-less tracks)
|
|
55
58
|
*
|
|
56
59
|
* @example
|
|
57
60
|
* ```ts
|
|
58
|
-
* const
|
|
59
|
-
* setFavorites(
|
|
61
|
+
* const favoriteIds = await loadFavoritesFromStorage()
|
|
62
|
+
* setFavorites(favoriteIds)
|
|
60
63
|
* ```
|
|
61
64
|
*/
|
|
62
65
|
export function setFavorites(favorites: string[]): void {
|
|
@@ -4,10 +4,12 @@ import { renderHook, act, cleanup } from '@testing-library/react'
|
|
|
4
4
|
import { describe, it, expect, afterEach, vi } from 'vitest'
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* The
|
|
7
|
+
* The hooks that are a single delegation to `useNativeUpdatedValue`. Their
|
|
8
8
|
* behaviour is covered once in `utils/useNativeUpdatedValue.test.ts`; what is
|
|
9
9
|
* per-hook and untestable there is the wiring — which native callback slot the
|
|
10
10
|
* emitter claims, and which `eventKey` is pulled out of the payload.
|
|
11
|
+
* (`useActiveTrack` and `useQueue` are getter-refreshed instead — they update
|
|
12
|
+
* on transitions AND favorite toggles — and are covered in their own block.)
|
|
11
13
|
*
|
|
12
14
|
* Both are easy to get wrong and silent when wrong: the hook simply never
|
|
13
15
|
* updates, or yields the whole event where a field was meant. Five slots
|
|
@@ -159,13 +161,6 @@ const wirings: Wiring[] = [
|
|
|
159
161
|
event: true,
|
|
160
162
|
expected: true
|
|
161
163
|
},
|
|
162
|
-
{
|
|
163
|
-
name: 'useQueue',
|
|
164
|
-
hook: useQueue,
|
|
165
|
-
slot: 'onPlaybackQueueChanged',
|
|
166
|
-
event: [{ src: 'a' }],
|
|
167
|
-
expected: [{ src: 'a' }]
|
|
168
|
-
},
|
|
169
164
|
{
|
|
170
165
|
name: 'useFormattedNavigationError',
|
|
171
166
|
hook: useFormattedNavigationError,
|
|
@@ -210,13 +205,6 @@ const wirings: Wiring[] = [
|
|
|
210
205
|
event: { repeatMode: 'queue' },
|
|
211
206
|
expected: 'queue'
|
|
212
207
|
},
|
|
213
|
-
{
|
|
214
|
-
name: 'useActiveTrack',
|
|
215
|
-
hook: useActiveTrack,
|
|
216
|
-
slot: 'onPlaybackActiveTrackChanged',
|
|
217
|
-
event: { track: { src: 'a' } },
|
|
218
|
-
expected: { src: 'a' }
|
|
219
|
-
},
|
|
220
208
|
{
|
|
221
209
|
name: 'useBatteryWarningPending',
|
|
222
210
|
hook: useBatteryWarningPending,
|
|
@@ -252,3 +240,63 @@ describe('hook wiring table', () => {
|
|
|
252
240
|
expect(new Set(wirings.map((w) => w.slot)).size).toBe(wirings.length)
|
|
253
241
|
})
|
|
254
242
|
})
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* `useActiveTrack` and `useQueue` are getter-refreshed: a favorite toggle
|
|
246
|
+
* mutates the active track in place WITHOUT a transition event —
|
|
247
|
+
* transitions and mutations are separate events — so
|
|
248
|
+
* these hooks re-read their native getter on the transition slot AND on
|
|
249
|
+
* `onFavoriteChanged`.
|
|
250
|
+
*/
|
|
251
|
+
const { nativeBrowser } = await import('../native')
|
|
252
|
+
|
|
253
|
+
describe.each([
|
|
254
|
+
{
|
|
255
|
+
name: 'useActiveTrack',
|
|
256
|
+
hook: useActiveTrack,
|
|
257
|
+
getter: 'getActiveTrack',
|
|
258
|
+
transitionSlot: 'onPlaybackActiveTrackChanged',
|
|
259
|
+
initial: { src: 'a', favorited: false },
|
|
260
|
+
mutated: { src: 'a', favorited: true }
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
name: 'useQueue',
|
|
264
|
+
hook: useQueue,
|
|
265
|
+
getter: 'getQueue',
|
|
266
|
+
transitionSlot: 'onPlaybackQueueChanged',
|
|
267
|
+
initial: [{ src: 'a', favorited: false }],
|
|
268
|
+
mutated: [{ src: 'a', favorited: true }]
|
|
269
|
+
}
|
|
270
|
+
])(
|
|
271
|
+
'$name (getter-refreshed)',
|
|
272
|
+
({ hook, getter, transitionSlot, initial, mutated }) => {
|
|
273
|
+
it(`re-reads ${getter} on ${transitionSlot} and onFavoriteChanged`, () => {
|
|
274
|
+
expect(slots.has(transitionSlot)).toBe(true)
|
|
275
|
+
expect(slots.has('onFavoriteChanged')).toBe(true)
|
|
276
|
+
|
|
277
|
+
const getterMock = (
|
|
278
|
+
nativeBrowser as unknown as Record<string, ReturnType<typeof vi.fn>>
|
|
279
|
+
)[getter]!
|
|
280
|
+
|
|
281
|
+
getterMock.mockReturnValue(initial)
|
|
282
|
+
const { result } = renderHook(() => hook())
|
|
283
|
+
expect(result.current).toEqual(initial)
|
|
284
|
+
|
|
285
|
+
// A transition re-reads the getter (the event payload is not consumed).
|
|
286
|
+
getterMock.mockReturnValue(mutated)
|
|
287
|
+
act(() => slots.get(transitionSlot)!({}))
|
|
288
|
+
expect(result.current).toEqual(mutated)
|
|
289
|
+
|
|
290
|
+
// A favorite toggle mutates in place — no transition event fires, but
|
|
291
|
+
// the hook must still refresh.
|
|
292
|
+
getterMock.mockReturnValue(initial)
|
|
293
|
+
act(() =>
|
|
294
|
+
slots.get('onFavoriteChanged')!({
|
|
295
|
+
track: { src: 'a' },
|
|
296
|
+
favorited: false
|
|
297
|
+
})
|
|
298
|
+
)
|
|
299
|
+
expect(result.current).toEqual(initial)
|
|
300
|
+
})
|
|
301
|
+
}
|
|
302
|
+
)
|
|
@@ -58,8 +58,10 @@ export function seekTo(position: number): void {
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
/**
|
|
61
|
-
* Jump to the live edge of the current track
|
|
62
|
-
* live
|
|
61
|
+
* Jump to the live edge of the current track; no-op unless the track
|
|
62
|
+
* declares `live: true`. A stream with a seekable live window (HLS) seeks
|
|
63
|
+
* to the window's edge; a non-seekable stream (e.g. ICY radio) reconnects
|
|
64
|
+
* to rejoin live — an expired URL re-resolves through `media.resolve`.
|
|
63
65
|
*/
|
|
64
66
|
export function seekToLiveEdge(): void {
|
|
65
67
|
nativeBrowser.seekToLiveEdge()
|
|
@@ -18,9 +18,8 @@ import { useNativeUpdatedValue } from '../../utils/useNativeUpdatedValue'
|
|
|
18
18
|
* - `'buffering'`: Indicates that the player is currently loading more data
|
|
19
19
|
* before it can continue playing or is ready to start playing.
|
|
20
20
|
* - `'error'`: Indicates that playback of the current item failed. Call
|
|
21
|
-
* `
|
|
22
|
-
*
|
|
23
|
-
* the item again.
|
|
21
|
+
* `getPlaybackError()` (or subscribe via `usePlaybackError()`) for the
|
|
22
|
+
* error details. Call `retry()` or `play()` to try to play the item again.
|
|
24
23
|
* - `'ended'`: Indicates that playback stopped due to the end of the queue
|
|
25
24
|
* being reached.
|
|
26
25
|
*/
|
|
@@ -9,7 +9,7 @@ export interface PlaybackElapsed {
|
|
|
9
9
|
total: number
|
|
10
10
|
/** Seconds played since the previous callback (≈ `period`). */
|
|
11
11
|
sinceLast: number
|
|
12
|
-
/** The currently playing track (id / src /
|
|
12
|
+
/** The currently playing track (id / src / path / title …), if any. */
|
|
13
13
|
track?: Track
|
|
14
14
|
}
|
|
15
15
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
1
|
+
import type {
|
|
2
|
+
CarPlayNowPlayingButton,
|
|
3
|
+
CarPlayPlayingIndicatorLocation
|
|
4
|
+
} from '../../types/browser'
|
|
3
5
|
import { nativeBrowser } from '../../native'
|
|
4
6
|
import { NativeUpdatedValue } from '../../utils/NativeUpdatedValue'
|
|
5
7
|
import { useNativeUpdatedValue } from '../../utils/useNativeUpdatedValue'
|
|
@@ -75,31 +77,22 @@ export interface PlayerCapabilities {
|
|
|
75
77
|
* Android Auto and the Android 13+ system media controls — and an
|
|
76
78
|
* (empty or filled) heart on playable browse rows.
|
|
77
79
|
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
* - `false` / omitted: favoriting off everywhere.
|
|
83
|
-
* - `true`: on, with `'exact'` id matching.
|
|
84
|
-
* - `{ match }`: on, with the given match mode.
|
|
80
|
+
* The ids passed to `setFavorites` are matched exactly against a track's
|
|
81
|
+
* *identity* — its `id` when set, falling back to `src`. Store favorites as
|
|
82
|
+
* the same stable identifier you put in `Track.id` (or as the full `src`
|
|
83
|
+
* for tracks without an id).
|
|
85
84
|
*
|
|
86
85
|
* @example
|
|
87
86
|
* ```ts
|
|
88
|
-
*
|
|
89
|
-
* favorite: { match: 'exact' }
|
|
90
|
-
* setFavorites(['https://cdn.example.com/audio/track-42.mp3'])
|
|
91
|
-
* // → favorited when src === 'https://cdn.example.com/audio/track-42.mp3'
|
|
92
|
-
*
|
|
93
|
-
* // 'partial': a favorite id matches when it is a full path segment of src.
|
|
94
|
-
* favorite: { match: 'partial' }
|
|
87
|
+
* favorite: true
|
|
95
88
|
* setFavorites(['track-42'])
|
|
96
|
-
* // → favorited when
|
|
97
|
-
* //
|
|
89
|
+
* // → favorited when track.id === 'track-42'
|
|
90
|
+
* // (or, for id-less tracks, when track.src === 'track-42')
|
|
98
91
|
* ```
|
|
99
92
|
*
|
|
100
93
|
* @default false
|
|
101
94
|
*/
|
|
102
|
-
favorite?: boolean
|
|
95
|
+
favorite?: boolean
|
|
103
96
|
/**
|
|
104
97
|
* Enable shuffle mode toggle.
|
|
105
98
|
* @default true
|
|
@@ -295,6 +288,13 @@ export interface IOSOptions {
|
|
|
295
288
|
* @default []
|
|
296
289
|
*/
|
|
297
290
|
carPlayNowPlayingButtons: CarPlayNowPlayingButton[]
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Where the now-playing indicator draws on CarPlay list rows.
|
|
294
|
+
* See {@link CarPlayPlayingIndicatorLocation}.
|
|
295
|
+
* @default 'leading'
|
|
296
|
+
*/
|
|
297
|
+
carPlayPlayingIndicatorLocation: CarPlayPlayingIndicatorLocation
|
|
298
298
|
}
|
|
299
299
|
|
|
300
300
|
export interface AndroidOptions {
|
|
@@ -391,6 +391,13 @@ export interface IOSUpdateOptions {
|
|
|
391
391
|
* @default []
|
|
392
392
|
*/
|
|
393
393
|
carPlayNowPlayingButtons?: CarPlayNowPlayingButton[]
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Where the now-playing indicator draws on CarPlay list rows.
|
|
397
|
+
* See {@link CarPlayPlayingIndicatorLocation}.
|
|
398
|
+
* @default 'leading'
|
|
399
|
+
*/
|
|
400
|
+
carPlayPlayingIndicatorLocation?: CarPlayPlayingIndicatorLocation
|
|
394
401
|
}
|
|
395
402
|
|
|
396
403
|
/**
|
|
@@ -401,6 +408,7 @@ export interface NitroIOSUpdateOptions {
|
|
|
401
408
|
playbackRates?: number[]
|
|
402
409
|
carPlayUpNextButton?: boolean
|
|
403
410
|
carPlayNowPlayingButtons?: CarPlayNowPlayingButton[]
|
|
411
|
+
carPlayPlayingIndicatorLocation?: CarPlayPlayingIndicatorLocation
|
|
404
412
|
}
|
|
405
413
|
|
|
406
414
|
/**
|
|
@@ -74,14 +74,23 @@ describe('setupPlayer wire regrouping', () => {
|
|
|
74
74
|
})
|
|
75
75
|
})
|
|
76
76
|
|
|
77
|
-
it('moves ios
|
|
77
|
+
it('moves ios runtime fields into the runtime options', async () => {
|
|
78
78
|
await setupPlayer({
|
|
79
|
-
ios: {
|
|
79
|
+
ios: {
|
|
80
|
+
category: 'playback',
|
|
81
|
+
playbackRates: [0.5, 1, 2],
|
|
82
|
+
carPlayPlayingIndicatorLocation: 'trailing'
|
|
83
|
+
}
|
|
80
84
|
})
|
|
81
85
|
|
|
82
86
|
const sent = payload()
|
|
83
87
|
expect(sent.ios).toEqual({ category: 'playback' })
|
|
84
|
-
expect(sent.options).toEqual({
|
|
88
|
+
expect(sent.options).toEqual({
|
|
89
|
+
ios: {
|
|
90
|
+
playbackRates: [0.5, 1, 2],
|
|
91
|
+
carPlayPlayingIndicatorLocation: 'trailing'
|
|
92
|
+
}
|
|
93
|
+
})
|
|
85
94
|
})
|
|
86
95
|
|
|
87
96
|
it('omits a platform bag that only carried runtime fields', async () => {
|
|
@@ -741,12 +741,14 @@ export async function setupPlayer(
|
|
|
741
741
|
playbackRates,
|
|
742
742
|
carPlayUpNextButton,
|
|
743
743
|
carPlayNowPlayingButtons,
|
|
744
|
+
carPlayPlayingIndicatorLocation,
|
|
744
745
|
...iosSetup
|
|
745
746
|
} = ios
|
|
746
747
|
const iosUpdate = definedFields({
|
|
747
748
|
playbackRates,
|
|
748
749
|
carPlayUpNextButton,
|
|
749
|
-
carPlayNowPlayingButtons
|
|
750
|
+
carPlayNowPlayingButtons,
|
|
751
|
+
carPlayPlayingIndicatorLocation
|
|
750
752
|
})
|
|
751
753
|
validateIOSUpdateOptions(iosUpdate)
|
|
752
754
|
|
|
@@ -7,6 +7,8 @@ const MAX_CARPLAY_NOW_PLAYING_BUTTONS = 5
|
|
|
7
7
|
* Shared by `updateOptions` and `setupPlayer` (both can carry `ios` options).
|
|
8
8
|
* Kept out of `options.ts`, which the barrel re-exports — see
|
|
9
9
|
* public-surface.test.ts.
|
|
10
|
+
*
|
|
11
|
+
* @internal
|
|
10
12
|
*/
|
|
11
13
|
export function validateIOSUpdateOptions(ios?: IOSUpdateOptions): void {
|
|
12
14
|
const buttons = ios?.carPlayNowPlayingButtons
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { Track } from '../../types'
|
|
2
2
|
import { nativeBrowser } from '../../native'
|
|
3
3
|
import { NativeUpdatedValue } from '../../utils/NativeUpdatedValue'
|
|
4
|
-
import {
|
|
4
|
+
import { useNativeValueRefreshedBy } from '../../utils/useNativeValueRefreshedBy'
|
|
5
|
+
import { onFavoriteChanged } from '../favorites'
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Event data for when the active track changes.
|
|
@@ -40,6 +41,12 @@ export function getActiveTrackIndex(): number | undefined {
|
|
|
40
41
|
|
|
41
42
|
/**
|
|
42
43
|
* Subscribes to active track change events.
|
|
44
|
+
*
|
|
45
|
+
* Fires on **transitions only** — selection, queue advance, skip. In-place
|
|
46
|
+
* metadata mutations of the active track (a favorite toggle) emit
|
|
47
|
+
* {@link onFavoriteChanged} instead; {@link useActiveTrack} subscribes to
|
|
48
|
+
* both, so UI bound through the hook still re-renders on a heart toggle.
|
|
49
|
+
*
|
|
43
50
|
* @param callback - Called when the active track changes
|
|
44
51
|
* @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
|
|
45
52
|
*/
|
|
@@ -50,10 +57,14 @@ export const onActiveTrackChanged =
|
|
|
50
57
|
|
|
51
58
|
// MARK: - Hooks
|
|
52
59
|
|
|
60
|
+
// Module-level so the hook's subscription identity is stable across renders.
|
|
61
|
+
const activeTrackInvalidators = [onActiveTrackChanged, onFavoriteChanged]
|
|
62
|
+
|
|
53
63
|
/**
|
|
54
|
-
* Hook that returns the current active track
|
|
64
|
+
* Hook that returns the current active track, updating on transitions AND on
|
|
65
|
+
* in-place mutations of the active track (favorite toggles).
|
|
55
66
|
* @returns The current active track or undefined
|
|
56
67
|
*/
|
|
57
68
|
export function useActiveTrack(): Track | undefined {
|
|
58
|
-
return
|
|
69
|
+
return useNativeValueRefreshedBy(getActiveTrack, activeTrackInvalidators)
|
|
59
70
|
}
|