react-native-audio-browser 0.2.0-next.11 → 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/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/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 +26 -39
- package/android/src/test/java/com/audiobrowser/browser/BrowserManagerFavoritesTest.kt +153 -0
- package/android/src/test/java/com/audiobrowser/browser/BrowserManagerTest.kt +228 -50
- 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/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 +46 -21
- 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/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 +1 -1
- 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 +40 -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
|
@@ -62,41 +62,125 @@ extension JsonArtwork {
|
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
///
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
65
|
+
/// A style declaration block on the wire (ADR 0011):
|
|
66
|
+
/// `{ display?, gridWrap?, artworkRendering? }`.
|
|
67
|
+
///
|
|
68
|
+
/// One JSON model serves both the `TrackStyle` and `SectionStyle` positions —
|
|
69
|
+
/// keys that don't exist at the narrower position simply don't map.
|
|
70
|
+
///
|
|
71
|
+
/// Decoding is tolerant by design: a stale payload — the retired string
|
|
72
|
+
/// styles, a wrong-typed field, an unknown enum value — must degrade to "no
|
|
73
|
+
/// declaration", never fail the page or the persisted playback state (the
|
|
74
|
+
/// decoders above this are strict, so a throw here would kill the whole
|
|
75
|
+
/// payload). `init(from:)` therefore never throws, and unknown enum values
|
|
76
|
+
/// map to nil at `toNitro` time.
|
|
77
|
+
struct JsonStyle: Codable, Equatable {
|
|
78
|
+
let display: String?
|
|
79
|
+
let gridWrap: Bool?
|
|
80
|
+
let artworkRendering: String?
|
|
77
81
|
|
|
78
82
|
init(
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
83
|
+
display: String? = nil,
|
|
84
|
+
gridWrap: Bool? = nil,
|
|
85
|
+
artworkRendering: String? = nil,
|
|
86
|
+
) {
|
|
87
|
+
self.display = display
|
|
88
|
+
self.gridWrap = gridWrap
|
|
89
|
+
self.artworkRendering = artworkRendering
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
init(from decoder: Decoder) throws {
|
|
93
|
+
// A non-object value (the retired `style: 'grid'` string) decodes as an
|
|
94
|
+
// empty block rather than failing the parent.
|
|
95
|
+
guard let container = try? decoder.container(keyedBy: CodingKeys.self) else {
|
|
96
|
+
display = nil
|
|
97
|
+
gridWrap = nil
|
|
98
|
+
artworkRendering = nil
|
|
99
|
+
return
|
|
100
|
+
}
|
|
101
|
+
display = (try? container.decodeIfPresent(String.self, forKey: .display)) ?? nil
|
|
102
|
+
gridWrap = (try? container.decodeIfPresent(Bool.self, forKey: .gridWrap)) ?? nil
|
|
103
|
+
artworkRendering = (try? container.decodeIfPresent(String.self, forKey: .artworkRendering)) ?? nil
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
extension JsonStyle {
|
|
108
|
+
/// The wire strings map to Nitro enums here, tolerantly: an unknown value
|
|
109
|
+
/// (a future `display` mode, say) is no declaration, not an error. A block
|
|
110
|
+
/// that resolves to no declarations at all — `{}`, the tolerant decoder's
|
|
111
|
+
/// empty result for the retired string vocabulary, or all-unknown values —
|
|
112
|
+
/// maps to nil, so "no declaration" has one shape on every platform
|
|
113
|
+
/// (Android's lenient mapper makes the same collapse).
|
|
114
|
+
func toTrackStyle() -> TrackStyle? {
|
|
115
|
+
let display = display.flatMap { StyleDisplay(fromString: $0.lowercased()) }
|
|
116
|
+
let artworkRendering = artworkRendering.flatMap { ArtworkRendering(fromString: $0.lowercased()) }
|
|
117
|
+
guard display != nil || artworkRendering != nil else { return nil }
|
|
118
|
+
return TrackStyle(display: display, artworkRendering: artworkRendering)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
func toSectionStyle() -> SectionStyle? {
|
|
122
|
+
let display = display.flatMap { StyleDisplay(fromString: $0.lowercased()) }
|
|
123
|
+
let artworkRendering = artworkRendering.flatMap { ArtworkRendering(fromString: $0.lowercased()) }
|
|
124
|
+
guard display != nil || artworkRendering != nil || gridWrap != nil else { return nil }
|
|
125
|
+
// Argument order is the generated init's (own properties before
|
|
126
|
+
// inherited ones — Nitro flattens `extends` own-props-first).
|
|
127
|
+
return SectionStyle(
|
|
128
|
+
gridWrap: gridWrap,
|
|
129
|
+
display: display,
|
|
130
|
+
artworkRendering: artworkRendering,
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/// Snapshot a live track block back to its JSON model (for persistence).
|
|
135
|
+
/// An all-nil block snapshots as no block — the decoders collapse it to
|
|
136
|
+
/// "no declaration" anyway, so never persist the distinction.
|
|
137
|
+
init?(_ style: TrackStyle?) {
|
|
138
|
+
guard let style, style.display != nil || style.artworkRendering != nil else { return nil }
|
|
139
|
+
self.init(
|
|
140
|
+
display: Self.string(style.display),
|
|
141
|
+
artworkRendering: Self.string(style.artworkRendering),
|
|
142
|
+
)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private static func string(_ display: StyleDisplay?) -> String? {
|
|
146
|
+
switch display {
|
|
147
|
+
case .list: "list"
|
|
148
|
+
case .grid: "grid"
|
|
149
|
+
case nil: nil
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private static func string(_ rendering: ArtworkRendering?) -> String? {
|
|
154
|
+
switch rendering {
|
|
155
|
+
case .original: "original"
|
|
156
|
+
case .stencil: "stencil"
|
|
157
|
+
case nil: nil
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/// JSON model for a page section (ADR 0010). Legacy `groupTitle`/`imageRow`
|
|
163
|
+
/// keys in payloads are simply unknown to these models and decode as
|
|
164
|
+
/// ignored dead weight.
|
|
165
|
+
struct JsonSection: Codable {
|
|
166
|
+
let title: String?
|
|
167
|
+
let subtitle: String?
|
|
168
|
+
let style: JsonStyle?
|
|
169
|
+
let path: String?
|
|
170
|
+
let children: [JsonTrack]
|
|
171
|
+
|
|
172
|
+
init(
|
|
173
|
+
title: String? = nil,
|
|
174
|
+
subtitle: String? = nil,
|
|
175
|
+
style: JsonStyle? = nil,
|
|
176
|
+
path: String? = nil,
|
|
177
|
+
children: [JsonTrack],
|
|
89
178
|
) {
|
|
90
|
-
self.id = id
|
|
91
|
-
self.url = url
|
|
92
|
-
self.src = src
|
|
93
|
-
self.artwork = artwork
|
|
94
179
|
self.title = title
|
|
95
|
-
self.
|
|
96
|
-
self.
|
|
97
|
-
self.
|
|
98
|
-
self.
|
|
99
|
-
self.request = request
|
|
180
|
+
self.subtitle = subtitle
|
|
181
|
+
self.style = style
|
|
182
|
+
self.path = path
|
|
183
|
+
self.children = children
|
|
100
184
|
}
|
|
101
185
|
}
|
|
102
186
|
|
|
@@ -116,59 +200,59 @@ struct JsonTrackRequest: Codable {
|
|
|
116
200
|
/// JSON model for resolved track (container with children).
|
|
117
201
|
struct JsonResolvedTrack: Codable {
|
|
118
202
|
let id: String?
|
|
119
|
-
let
|
|
203
|
+
let path: String
|
|
120
204
|
let title: String
|
|
121
205
|
let subtitle: String?
|
|
122
206
|
let artwork: JsonArtwork?
|
|
123
207
|
let artist: String?
|
|
124
|
-
let
|
|
208
|
+
let albumPath: String?
|
|
125
209
|
let album: String?
|
|
126
210
|
let description: String?
|
|
127
211
|
let genre: String?
|
|
128
212
|
let duration: Double?
|
|
213
|
+
let sections: [JsonSection]?
|
|
129
214
|
let children: [JsonTrack]?
|
|
130
215
|
let src: String?
|
|
131
|
-
let style:
|
|
132
|
-
let
|
|
133
|
-
let groupTitle: String?
|
|
216
|
+
let style: JsonStyle?
|
|
217
|
+
let disabled: Bool?
|
|
134
218
|
let live: Bool?
|
|
135
219
|
let carPlaySiriListButton: String?
|
|
136
220
|
|
|
137
221
|
init(
|
|
138
222
|
id: String? = nil,
|
|
139
|
-
|
|
223
|
+
path: String,
|
|
140
224
|
title: String,
|
|
141
225
|
subtitle: String? = nil,
|
|
142
226
|
artwork: JsonArtwork? = nil,
|
|
143
227
|
artist: String? = nil,
|
|
144
|
-
|
|
228
|
+
albumPath: String? = nil,
|
|
145
229
|
album: String? = nil,
|
|
146
230
|
description: String? = nil,
|
|
147
231
|
genre: String? = nil,
|
|
148
232
|
duration: Double? = nil,
|
|
233
|
+
sections: [JsonSection]? = nil,
|
|
149
234
|
children: [JsonTrack]? = nil,
|
|
150
235
|
src: String? = nil,
|
|
151
|
-
style:
|
|
152
|
-
|
|
153
|
-
groupTitle: String? = nil,
|
|
236
|
+
style: JsonStyle? = nil,
|
|
237
|
+
disabled: Bool? = nil,
|
|
154
238
|
live: Bool? = nil,
|
|
155
239
|
carPlaySiriListButton: String? = nil,
|
|
156
240
|
) {
|
|
157
|
-
self.
|
|
241
|
+
self.path = path
|
|
158
242
|
self.title = title
|
|
159
243
|
self.subtitle = subtitle
|
|
160
244
|
self.artwork = artwork
|
|
161
245
|
self.artist = artist
|
|
162
|
-
self.
|
|
246
|
+
self.albumPath = albumPath
|
|
163
247
|
self.album = album
|
|
164
248
|
self.description = description
|
|
165
249
|
self.genre = genre
|
|
166
250
|
self.duration = duration
|
|
251
|
+
self.sections = sections
|
|
167
252
|
self.children = children
|
|
168
253
|
self.src = src
|
|
169
254
|
self.style = style
|
|
170
|
-
self.
|
|
171
|
-
self.groupTitle = groupTitle
|
|
255
|
+
self.disabled = disabled
|
|
172
256
|
self.live = live
|
|
173
257
|
self.carPlaySiriListButton = carPlaySiriListButton
|
|
174
258
|
self.id = id
|
|
@@ -178,50 +262,46 @@ struct JsonResolvedTrack: Codable {
|
|
|
178
262
|
/// JSON model for individual tracks.
|
|
179
263
|
struct JsonTrack: Codable {
|
|
180
264
|
let id: String?
|
|
181
|
-
let
|
|
265
|
+
let path: String?
|
|
182
266
|
let title: String
|
|
183
267
|
let subtitle: String?
|
|
184
268
|
let artwork: JsonArtwork?
|
|
185
269
|
let artist: String?
|
|
186
|
-
let
|
|
270
|
+
let albumPath: String?
|
|
187
271
|
let album: String?
|
|
188
272
|
let description: String?
|
|
189
273
|
let genre: String?
|
|
190
274
|
let duration: Double?
|
|
191
275
|
let src: String?
|
|
192
276
|
let request: JsonTrackRequest?
|
|
193
|
-
let style:
|
|
194
|
-
let
|
|
195
|
-
let groupTitle: String?
|
|
277
|
+
let style: JsonStyle?
|
|
278
|
+
let disabled: Bool?
|
|
196
279
|
let live: Bool?
|
|
197
|
-
let imageRow: [JsonImageRowItem]?
|
|
198
280
|
|
|
199
281
|
init(
|
|
200
282
|
id: String? = nil,
|
|
201
|
-
|
|
283
|
+
path: String? = nil,
|
|
202
284
|
title: String,
|
|
203
285
|
subtitle: String? = nil,
|
|
204
286
|
artwork: JsonArtwork? = nil,
|
|
205
287
|
artist: String? = nil,
|
|
206
|
-
|
|
288
|
+
albumPath: String? = nil,
|
|
207
289
|
album: String? = nil,
|
|
208
290
|
description: String? = nil,
|
|
209
291
|
genre: String? = nil,
|
|
210
292
|
duration: Double? = nil,
|
|
211
293
|
src: String? = nil,
|
|
212
294
|
request: JsonTrackRequest? = nil,
|
|
213
|
-
style:
|
|
214
|
-
|
|
215
|
-
groupTitle: String? = nil,
|
|
295
|
+
style: JsonStyle? = nil,
|
|
296
|
+
disabled: Bool? = nil,
|
|
216
297
|
live: Bool? = nil,
|
|
217
|
-
imageRow: [JsonImageRowItem]? = nil,
|
|
218
298
|
) {
|
|
219
|
-
self.
|
|
299
|
+
self.path = path
|
|
220
300
|
self.title = title
|
|
221
301
|
self.subtitle = subtitle
|
|
222
302
|
self.artwork = artwork
|
|
223
303
|
self.artist = artist
|
|
224
|
-
self.
|
|
304
|
+
self.albumPath = albumPath
|
|
225
305
|
self.album = album
|
|
226
306
|
self.description = description
|
|
227
307
|
self.genre = genre
|
|
@@ -229,10 +309,8 @@ struct JsonTrack: Codable {
|
|
|
229
309
|
self.src = src
|
|
230
310
|
self.request = request
|
|
231
311
|
self.style = style
|
|
232
|
-
self.
|
|
233
|
-
self.groupTitle = groupTitle
|
|
312
|
+
self.disabled = disabled
|
|
234
313
|
self.live = live
|
|
235
|
-
self.imageRow = imageRow
|
|
236
314
|
self.id = id
|
|
237
315
|
}
|
|
238
316
|
}
|
|
@@ -250,26 +328,27 @@ struct JsonTrack: Codable {
|
|
|
250
328
|
|
|
251
329
|
extension JsonTrack {
|
|
252
330
|
/// Snapshot the persistable subset of a live Track (inverse of `toNitro()`).
|
|
331
|
+
/// The track's own declared style block and `disabled` persist (ADR 0011);
|
|
332
|
+
/// section/page-inherited values are never stamped onto tracks and
|
|
333
|
+
/// re-resolve at render, exactly as they do live.
|
|
253
334
|
init(from track: Track) {
|
|
254
335
|
self.init(
|
|
255
336
|
id: track.id,
|
|
256
|
-
|
|
337
|
+
path: track.path,
|
|
257
338
|
title: track.title,
|
|
258
339
|
subtitle: track.subtitle,
|
|
259
340
|
artwork: JsonArtwork(track.artwork),
|
|
260
341
|
artist: track.artist,
|
|
261
|
-
|
|
342
|
+
albumPath: track.albumPath,
|
|
262
343
|
album: track.album,
|
|
263
344
|
description: track.description,
|
|
264
345
|
genre: track.genre,
|
|
265
346
|
duration: track.duration,
|
|
266
347
|
src: track.src,
|
|
267
348
|
request: track.request.map(JsonTrackRequest.init(from:)),
|
|
268
|
-
style:
|
|
269
|
-
|
|
270
|
-
groupTitle: track.groupTitle,
|
|
349
|
+
style: JsonStyle(track.style),
|
|
350
|
+
disabled: track.disabled,
|
|
271
351
|
live: track.live,
|
|
272
|
-
imageRow: nil,
|
|
273
352
|
)
|
|
274
353
|
}
|
|
275
354
|
}
|
|
@@ -289,14 +368,16 @@ struct JsonTrack: Codable {
|
|
|
289
368
|
init(from track: Track) {
|
|
290
369
|
self.init(
|
|
291
370
|
id: track.id,
|
|
292
|
-
|
|
371
|
+
path: track.path,
|
|
293
372
|
title: track.title,
|
|
294
373
|
artwork: JsonArtwork(track.artwork),
|
|
295
374
|
artist: track.artist,
|
|
296
|
-
|
|
375
|
+
albumPath: track.albumPath,
|
|
297
376
|
album: track.album,
|
|
298
377
|
src: track.src,
|
|
299
378
|
request: track.request.map(JsonTrackRequest.init(from:)),
|
|
379
|
+
style: JsonStyle(track.style),
|
|
380
|
+
disabled: track.disabled,
|
|
300
381
|
live: track.live,
|
|
301
382
|
)
|
|
302
383
|
}
|
|
@@ -308,26 +389,14 @@ struct JsonTrack: Codable {
|
|
|
308
389
|
|
|
309
390
|
#if canImport(NitroModules)
|
|
310
391
|
|
|
311
|
-
|
|
312
|
-
func
|
|
313
|
-
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
extension JsonImageRowItem {
|
|
318
|
-
func toNitro() -> ImageRowItem {
|
|
319
|
-
ImageRowItem(
|
|
320
|
-
id: id,
|
|
321
|
-
url: url,
|
|
322
|
-
src: src,
|
|
323
|
-
artwork: artwork,
|
|
324
|
-
artworkSource: nil,
|
|
392
|
+
extension JsonSection {
|
|
393
|
+
func toNitro() -> Section {
|
|
394
|
+
Section(
|
|
325
395
|
title: title,
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
request: request?.toNitro(),
|
|
396
|
+
subtitle: subtitle,
|
|
397
|
+
style: style?.toSectionStyle(),
|
|
398
|
+
path: path,
|
|
399
|
+
children: children.map { $0.toNitro() },
|
|
331
400
|
)
|
|
332
401
|
}
|
|
333
402
|
}
|
|
@@ -335,28 +404,26 @@ struct JsonTrack: Codable {
|
|
|
335
404
|
extension JsonResolvedTrack {
|
|
336
405
|
func toNitro() -> ResolvedTrack {
|
|
337
406
|
ResolvedTrack(
|
|
338
|
-
|
|
407
|
+
path: path,
|
|
408
|
+
style: style?.toSectionStyle(),
|
|
409
|
+
sections: sections?.map { $0.toNitro() },
|
|
339
410
|
children: children?.map { $0.toNitro() },
|
|
340
411
|
carPlaySiriListButton: carPlaySiriListButton.flatMap { CarPlaySiriListButtonPosition(fromString: $0) },
|
|
341
412
|
id: id,
|
|
342
413
|
src: src,
|
|
343
414
|
artwork: artwork?.toNitro(),
|
|
344
415
|
artworkSource: nil, request: nil,
|
|
345
|
-
artworkCarPlayTinted: nil,
|
|
346
416
|
title: title,
|
|
347
417
|
subtitle: subtitle,
|
|
348
418
|
artist: artist,
|
|
349
|
-
|
|
419
|
+
albumPath: albumPath,
|
|
350
420
|
album: album,
|
|
351
421
|
description: description,
|
|
352
422
|
genre: genre,
|
|
353
423
|
duration: duration,
|
|
354
|
-
|
|
355
|
-
childrenStyle: childrenStyle?.toTrackStyle(),
|
|
424
|
+
disabled: disabled,
|
|
356
425
|
favorited: nil,
|
|
357
|
-
groupTitle: groupTitle,
|
|
358
426
|
live: live,
|
|
359
|
-
imageRow: nil,
|
|
360
427
|
)
|
|
361
428
|
}
|
|
362
429
|
}
|
|
@@ -365,26 +432,23 @@ struct JsonTrack: Codable {
|
|
|
365
432
|
func toNitro() -> Track {
|
|
366
433
|
Track(
|
|
367
434
|
id: id,
|
|
368
|
-
|
|
435
|
+
path: path,
|
|
369
436
|
src: src,
|
|
370
437
|
artwork: artwork?.toNitro(),
|
|
371
438
|
artworkSource: nil,
|
|
372
439
|
request: request?.toNitro(),
|
|
373
|
-
artworkCarPlayTinted: nil,
|
|
374
440
|
title: title,
|
|
375
441
|
subtitle: subtitle,
|
|
376
442
|
artist: artist,
|
|
377
|
-
|
|
443
|
+
albumPath: albumPath,
|
|
378
444
|
album: album,
|
|
379
445
|
description: description,
|
|
380
446
|
genre: genre,
|
|
381
447
|
duration: duration,
|
|
382
448
|
style: style?.toTrackStyle(),
|
|
383
|
-
|
|
449
|
+
disabled: disabled,
|
|
384
450
|
favorited: nil,
|
|
385
|
-
groupTitle: groupTitle,
|
|
386
451
|
live: live,
|
|
387
|
-
imageRow: imageRow?.map { $0.toNitro() },
|
|
388
452
|
)
|
|
389
453
|
}
|
|
390
454
|
}
|
|
@@ -396,15 +460,29 @@ struct JsonTrack: Codable {
|
|
|
396
460
|
func toNitro() -> Track {
|
|
397
461
|
Track(
|
|
398
462
|
id: id ?? "",
|
|
399
|
-
|
|
463
|
+
path: path,
|
|
400
464
|
src: src,
|
|
465
|
+
artwork: artwork?.toNitro(),
|
|
401
466
|
request: request.map { TrackRequest(userAgent: $0.userAgent, headers: $0.headers, query: $0.query) },
|
|
402
467
|
title: title,
|
|
403
468
|
artist: artist,
|
|
404
|
-
|
|
469
|
+
albumPath: albumPath,
|
|
405
470
|
album: album,
|
|
471
|
+
style: style?.toTrackStyle(),
|
|
472
|
+
disabled: disabled,
|
|
406
473
|
live: live,
|
|
407
|
-
|
|
474
|
+
)
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
extension JsonSection {
|
|
479
|
+
func toNitro() -> Section {
|
|
480
|
+
Section(
|
|
481
|
+
title: title,
|
|
482
|
+
subtitle: subtitle,
|
|
483
|
+
style: style?.toSectionStyle(),
|
|
484
|
+
path: path,
|
|
485
|
+
children: children.map { $0.toNitro() },
|
|
408
486
|
)
|
|
409
487
|
}
|
|
410
488
|
}
|
|
@@ -412,10 +490,13 @@ struct JsonTrack: Codable {
|
|
|
412
490
|
extension JsonResolvedTrack {
|
|
413
491
|
func toNitro() -> ResolvedTrack {
|
|
414
492
|
ResolvedTrack(
|
|
415
|
-
|
|
493
|
+
path: path,
|
|
494
|
+
style: style?.toSectionStyle(),
|
|
495
|
+
sections: sections?.map { $0.toNitro() },
|
|
416
496
|
children: children?.map { $0.toNitro() },
|
|
417
497
|
artwork: artwork?.toNitro(),
|
|
418
498
|
title: title,
|
|
499
|
+
disabled: disabled,
|
|
419
500
|
)
|
|
420
501
|
}
|
|
421
502
|
}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import Foundation
|
|
2
2
|
|
|
3
3
|
/// A search result that may or may not be directly playable. `src` is the
|
|
4
|
-
/// playable stream; `
|
|
4
|
+
/// playable stream; `path` is a browsable container path. Abstracting these two
|
|
5
5
|
/// fields keeps the drill-in decision free of the Nitro `Track` type so it can
|
|
6
6
|
/// be unit-tested.
|
|
7
7
|
protocol PlayableSearchItem {
|
|
8
8
|
var src: String? { get }
|
|
9
|
-
var
|
|
9
|
+
var path: String? { get }
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
enum SearchDrillIn {
|
|
13
13
|
/// Picks the playable items for a "play «X»" voice search.
|
|
14
14
|
///
|
|
15
|
-
/// If the first result is browsable-only (a `
|
|
15
|
+
/// If the first result is browsable-only (a `path` container with no `src` — a
|
|
16
16
|
/// place/genre page), drills into it via `resolveChildren` and uses its
|
|
17
17
|
/// playable children, so we play the first station *inside* the page rather
|
|
18
18
|
/// than skipping the page for a later inline station. Falls back to the flat
|
|
@@ -25,8 +25,8 @@ enum SearchDrillIn {
|
|
|
25
25
|
guard !children.isEmpty else { return nil }
|
|
26
26
|
|
|
27
27
|
let candidates: [Item]
|
|
28
|
-
if let first = children.first, first.src == nil, let
|
|
29
|
-
let drilled = try await resolveChildren(
|
|
28
|
+
if let first = children.first, first.src == nil, let path = first.path {
|
|
29
|
+
let drilled = try await resolveChildren(path).filter { $0.src != nil }
|
|
30
30
|
candidates = drilled.isEmpty ? children : drilled
|
|
31
31
|
} else {
|
|
32
32
|
candidates = children
|
|
@@ -37,7 +37,7 @@ enum SearchDrillIn {
|
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
// The real Nitro `Track` already carries `src`/`
|
|
40
|
+
// The real Nitro `Track` already carries `src`/`path`; conform it for the app
|
|
41
41
|
// build. (The testable target builds without NitroModules and uses a stub item.)
|
|
42
42
|
#if canImport(NitroModules)
|
|
43
43
|
extension Track: PlayableSearchItem {}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#if canImport(NitroModules)
|
|
2
|
+
import NitroModules
|
|
3
|
+
#endif
|
|
4
|
+
|
|
5
|
+
/// The rendered form of a section — the single place where (resolved style,
|
|
6
|
+
/// platform capability) maps to what CarPlay draws. Degradation drops
|
|
7
|
+
/// decorations before layout (ADR 0011): a *wrapping* grid can only reach the
|
|
8
|
+
/// tile path where the platform has a wrapping tile container (CarPlay:
|
|
9
|
+
/// iOS 26+'s element API); without one it becomes a list, where every item
|
|
10
|
+
/// stays reachable instead of truncating at an unknowable width. A
|
|
11
|
+
/// single-line grid (`gridWrap: false`) renders everywhere — the legacy image
|
|
12
|
+
/// row IS the single-line presentation.
|
|
13
|
+
enum SectionPresentation: Equatable {
|
|
14
|
+
case list
|
|
15
|
+
case singleLineRow
|
|
16
|
+
case wrappingGrid
|
|
17
|
+
|
|
18
|
+
/// - Parameter supportsWrappingGrid: whether the platform has a wrapping
|
|
19
|
+
/// tile container. Injected (rather than read via `#available` here) so
|
|
20
|
+
/// the mapping is testable off-device.
|
|
21
|
+
init(for style: SectionStyle?, supportsWrappingGrid: Bool) {
|
|
22
|
+
guard style?.display == .grid else {
|
|
23
|
+
self = .list
|
|
24
|
+
return
|
|
25
|
+
}
|
|
26
|
+
if style?.gridWrap == false {
|
|
27
|
+
self = .singleLineRow
|
|
28
|
+
} else if supportsWrappingGrid {
|
|
29
|
+
self = .wrappingGrid
|
|
30
|
+
} else {
|
|
31
|
+
self = .list
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -1,35 +1,69 @@
|
|
|
1
1
|
// Queue scope is the tapped section, not the whole page (ADR 0006): the
|
|
2
2
|
// playback context a listener expects is the list they tapped in — a page
|
|
3
3
|
// aggregating several sections must not leak next/previous across them.
|
|
4
|
+
// Sections are structural (ADR 0010), so scoping is a lookup, not a
|
|
5
|
+
// groupTitle-run derivation.
|
|
4
6
|
enum SectionScope {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
/// The section containing the tapped identity, plus the tapped child's
|
|
8
|
+
/// offset within it — non-nil only when the stamped flat index pinned the
|
|
9
|
+
/// exact copy.
|
|
10
|
+
struct Scoped {
|
|
11
|
+
let section: Section
|
|
12
|
+
let tappedOffset: Int?
|
|
11
13
|
}
|
|
12
14
|
|
|
13
|
-
/// The section of `
|
|
14
|
-
/// when
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
/// The section of `sections` containing the playable `trackId` (a track
|
|
16
|
+
/// identity: id when non-blank, else src), or nil when not found.
|
|
17
|
+
///
|
|
18
|
+
/// `tappedIndex` — the flat page position stamped into the contextual URL
|
|
19
|
+
/// (children concatenated in section order) — is a tie-breaker, never an
|
|
20
|
+
/// identifier: when the child at that position still carries the tapped
|
|
21
|
+
/// identity, it pins which section (and which copy) was tapped; when it
|
|
22
|
+
/// doesn't (the list shifted), resolution falls back to the first section
|
|
23
|
+
/// containing the identity. A stale index can therefore never select a
|
|
24
|
+
/// different track — at worst a different copy of the same one.
|
|
25
|
+
static func scoped(
|
|
26
|
+
in sections: [Section],
|
|
27
|
+
containing trackId: String,
|
|
28
|
+
tappedIndex: Int? = nil,
|
|
29
|
+
) -> Scoped? {
|
|
30
|
+
if let tappedIndex, tappedIndex >= 0 {
|
|
31
|
+
var start = 0
|
|
32
|
+
for section in sections {
|
|
33
|
+
let offset = tappedIndex - start
|
|
34
|
+
if offset < section.children.count {
|
|
35
|
+
if section.children[offset].identity == trackId {
|
|
36
|
+
return Scoped(section: section, tappedOffset: offset)
|
|
37
|
+
}
|
|
38
|
+
break
|
|
39
|
+
}
|
|
40
|
+
start += section.children.count
|
|
19
41
|
}
|
|
20
42
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
var start = index
|
|
26
|
-
while start > 0, children[start - 1].groupTitle == group {
|
|
27
|
-
start -= 1
|
|
28
|
-
}
|
|
29
|
-
var end = index
|
|
30
|
-
while end + 1 < children.count, children[end + 1].groupTitle == group {
|
|
31
|
-
end += 1
|
|
43
|
+
for section in sections {
|
|
44
|
+
if section.children.contains(where: { $0.identity == trackId }) {
|
|
45
|
+
return Scoped(section: section, tappedOffset: nil)
|
|
46
|
+
}
|
|
32
47
|
}
|
|
33
|
-
return
|
|
48
|
+
return nil
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// MARK: - Page shape helpers (ADR 0010)
|
|
53
|
+
|
|
54
|
+
extension ResolvedTrack {
|
|
55
|
+
/// The canonical sectioned shape: `sections` wins when present; plain
|
|
56
|
+
/// `children` is authoring sugar for one untitled section.
|
|
57
|
+
var normalizedSections: [Section]? {
|
|
58
|
+
if let sections { return sections }
|
|
59
|
+
guard let children else { return nil }
|
|
60
|
+
return [.untitled(children)]
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/// The page's children concatenated in section order — the flattening
|
|
64
|
+
/// that defines contextual `__index` positions and the flat views (tabs,
|
|
65
|
+
/// search) of a sectioned page.
|
|
66
|
+
var flattenedChildren: [Track]? {
|
|
67
|
+
normalizedSections.map { $0.flatMap(\.children) }
|
|
34
68
|
}
|
|
35
69
|
}
|