react-native-audio-browser 0.2.0-next.11 → 0.2.0-next.13

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.
Files changed (416) hide show
  1. package/README.md +14 -4
  2. package/android/build.gradle +20 -1
  3. package/android/src/main/java/com/audiobrowser/AudioBrowser.kt +115 -26
  4. package/android/src/main/java/com/audiobrowser/browser/BrowserManager.kt +262 -188
  5. package/android/src/main/java/com/audiobrowser/browser/BrowserUrlResolution.kt +23 -3
  6. package/android/src/main/java/com/audiobrowser/browser/InertStyleDiagnostic.kt +267 -0
  7. package/android/src/main/java/com/audiobrowser/browser/JsonModels.kt +95 -58
  8. package/android/src/main/java/com/audiobrowser/browser/SectionScope.kt +68 -26
  9. package/android/src/main/java/com/audiobrowser/browser/StyleResolver.kt +60 -0
  10. package/android/src/main/java/com/audiobrowser/extension/ResolvedTrackExt.kt +19 -7
  11. package/android/src/main/java/com/audiobrowser/extension/TappedTrack.kt +20 -0
  12. package/android/src/main/java/com/audiobrowser/extension/TrackIdentity.kt +15 -0
  13. package/android/src/main/java/com/audiobrowser/model/PlayerUpdateOptions.kt +1 -1
  14. package/android/src/main/java/com/audiobrowser/player/MediaSessionCallback.kt +120 -67
  15. package/android/src/main/java/com/audiobrowser/player/NowPlayingUpdater.kt +21 -24
  16. package/android/src/main/java/com/audiobrowser/player/PlaybackStateStore.kt +109 -74
  17. package/android/src/main/java/com/audiobrowser/player/Player.kt +51 -30
  18. package/android/src/main/java/com/audiobrowser/player/PlayerCapabilitiesExt.kt +2 -15
  19. package/android/src/main/java/com/audiobrowser/player/ShuffleOrderLedBy.kt +30 -0
  20. package/android/src/main/java/com/audiobrowser/util/BrowserPathHelper.kt +44 -40
  21. package/android/src/main/java/com/audiobrowser/util/MediaExtrasBuilder.kt +28 -23
  22. package/android/src/main/java/com/audiobrowser/util/StyleWire.kt +108 -0
  23. package/android/src/main/java/com/audiobrowser/util/TrackFactory.kt +52 -53
  24. package/android/src/test/java/com/audiobrowser/TestFixtures.kt +74 -41
  25. package/android/src/test/java/com/audiobrowser/browser/BrowserManagerFavoritesTest.kt +153 -0
  26. package/android/src/test/java/com/audiobrowser/browser/BrowserManagerTest.kt +229 -50
  27. package/android/src/test/java/com/audiobrowser/browser/BrowserUrlResolutionTest.kt +38 -0
  28. package/android/src/test/java/com/audiobrowser/browser/InertStyleDiagnosticTest.kt +277 -0
  29. package/android/src/test/java/com/audiobrowser/browser/JsonArtworkTest.kt +1 -1
  30. package/android/src/test/java/com/audiobrowser/browser/JsonStyleTest.kt +147 -0
  31. package/android/src/test/java/com/audiobrowser/browser/SectionScopeTest.kt +128 -53
  32. package/android/src/test/java/com/audiobrowser/browser/StyleResolverTest.kt +154 -0
  33. package/android/src/test/java/com/audiobrowser/extension/TappedTrackTest.kt +43 -0
  34. package/android/src/test/java/com/audiobrowser/extension/TrackIdentityTest.kt +41 -0
  35. package/android/src/test/java/com/audiobrowser/player/CapabilityControlsTest.kt +1 -2
  36. package/android/src/test/java/com/audiobrowser/player/NowPlayingUpdaterTest.kt +42 -2
  37. package/android/src/test/java/com/audiobrowser/player/PlaybackStateStoreTrackJsonTest.kt +55 -0
  38. package/android/src/test/java/com/audiobrowser/player/ShuffleOrderLedByTest.kt +79 -0
  39. package/android/src/test/java/com/audiobrowser/util/BrowserPathHelperTest.kt +41 -16
  40. package/android/src/test/java/com/audiobrowser/util/MediaExtrasBuilderTest.kt +161 -0
  41. package/android/src/test/java/com/audiobrowser/util/TrackFactoryMediaIdTest.kt +7 -7
  42. package/ios/Browser/BrowserConfig.swift +102 -5
  43. package/ios/Browser/BrowserManager+URLResolution.swift +18 -14
  44. package/ios/Browser/BrowserManager.swift +229 -180
  45. package/ios/Browser/BrowserPathHelper.swift +44 -44
  46. package/ios/Browser/InertStyleDiagnostic.swift +279 -0
  47. package/ios/Browser/JsonModels.swift +258 -106
  48. package/ios/Browser/SearchDrillIn.swift +6 -6
  49. package/ios/Browser/SectionPresentation.swift +64 -0
  50. package/ios/Browser/SectionScope.swift +58 -24
  51. package/ios/Browser/StyleResolver.swift +58 -0
  52. package/ios/Browser/TabBarEntries.swift +9 -3
  53. package/ios/Browser/TrackIdTemplate.swift +37 -0
  54. package/ios/CarPlay/CarPlayArtworkResolver.swift +8 -7
  55. package/ios/CarPlay/CarPlayController.swift +227 -85
  56. package/ios/CarPlay/CarPlayImageLoader.swift +38 -18
  57. package/ios/CarPlay/CarPlayListItemFactory.swift +289 -84
  58. package/ios/CarPlay/CarPlayNowPlayingManager.swift +15 -12
  59. package/ios/Extension/Capability+RemoteCommand.swift +1 -1
  60. package/ios/Extension/PlayerCapabilities+Favorite.swift +1 -15
  61. package/ios/Extension/ResolvedTrack+Copying.swift +11 -15
  62. package/ios/Extension/Section+Copying.swift +31 -0
  63. package/ios/Extension/Track+Copying.swift +6 -12
  64. package/ios/Extension/Track+Identity.swift +18 -0
  65. package/ios/Extension/UIApplication+ActiveScene.swift +27 -0
  66. package/ios/Http/HttpClient.swift +8 -2
  67. package/ios/HybridAudioBrowser.swift +166 -45
  68. package/ios/Model/NitroTypeStubs.swift +106 -21
  69. package/ios/Model/PlayerUpdateOptions.swift +7 -0
  70. package/ios/Player/MediaIntentCriteria.swift +13 -5
  71. package/ios/Player/MediaLoader.swift +33 -9
  72. package/ios/Player/NowPlayingUpdater.swift +54 -6
  73. package/ios/Player/PlaybackCoordinator.swift +13 -6
  74. package/ios/Player/PlaybackTimer.swift +8 -2
  75. package/ios/Player/QueueManager.swift +36 -10
  76. package/ios/Player/RetryManager.swift +4 -3
  77. package/ios/Player/ShuffleOrder.swift +23 -0
  78. package/ios/Player/SleepTimerHandling.swift +13 -0
  79. package/ios/RemoteCommand/RemoteCommandController.swift +53 -18
  80. package/ios/Tests/BrowserPathHelperTests.swift +50 -20
  81. package/ios/Tests/CarPlayArtworkResolverTests.swift +24 -41
  82. package/ios/Tests/InertStyleDiagnosticTests.swift +200 -0
  83. package/ios/Tests/JsonModelsTests.swift +150 -1
  84. package/ios/Tests/PlaybackStateStoreTests.swift +4 -3
  85. package/ios/Tests/QueueManagerTests.swift +72 -6
  86. package/ios/Tests/RemoteCommandControllerTests.swift +16 -0
  87. package/ios/Tests/ResolvedTrackCopyingTests.swift +37 -35
  88. package/ios/Tests/SearchDrillInTests.swift +16 -16
  89. package/ios/Tests/SectionPresentationTests.swift +90 -0
  90. package/ios/Tests/SectionScopeTests.swift +87 -55
  91. package/ios/Tests/StyleResolverTests.swift +127 -0
  92. package/ios/Tests/Support/TestTypes.swift +7 -0
  93. package/ios/Tests/TabBarEntriesTests.swift +18 -18
  94. package/ios/Tests/TrackIdTemplateTests.swift +67 -0
  95. package/ios/Tests/TrackIdentityTests.swift +20 -0
  96. package/ios/Tests/TrackSelectorTests.swift +168 -33
  97. package/ios/TrackPlayer.swift +57 -34
  98. package/ios/TrackSelector.swift +45 -30
  99. package/ios/Util/ArtworkImageFetcher.swift +16 -1
  100. package/ios/Util/NetworkMonitor.swift +43 -26
  101. package/ios/Util/SFSymbolRenderer.swift +24 -2
  102. package/ios/Util/SVGProcessor.swift +9 -2
  103. package/lib/commonjs/AudioBrowser.js +11 -0
  104. package/lib/commonjs/AudioBrowser.js.map +1 -1
  105. package/lib/commonjs/features/browser-config.js +11 -8
  106. package/lib/commonjs/features/browser-config.js.map +1 -1
  107. package/lib/commonjs/features/browser.js +2 -2
  108. package/lib/commonjs/features/favorites.js +13 -10
  109. package/lib/commonjs/features/favorites.js.map +1 -1
  110. package/lib/commonjs/features/playback/controls.js +4 -2
  111. package/lib/commonjs/features/playback/controls.js.map +1 -1
  112. package/lib/commonjs/features/playback/progress.js +2 -1
  113. package/lib/commonjs/features/playback/progress.js.map +1 -1
  114. package/lib/commonjs/features/playback/rate.js +8 -1
  115. package/lib/commonjs/features/playback/rate.js.map +1 -1
  116. package/lib/commonjs/features/playback/state.js +2 -3
  117. package/lib/commonjs/features/playback/state.js.map +1 -1
  118. package/lib/commonjs/features/player/options.js.map +1 -1
  119. package/lib/commonjs/features/player/setup.js +3 -1
  120. package/lib/commonjs/features/player/setup.js.map +1 -1
  121. package/lib/commonjs/features/player/validateOptions.js +2 -0
  122. package/lib/commonjs/features/player/validateOptions.js.map +1 -1
  123. package/lib/commonjs/features/queue/activeTrack.js +14 -3
  124. package/lib/commonjs/features/queue/activeTrack.js.map +1 -1
  125. package/lib/commonjs/features/queue/queue.js +11 -4
  126. package/lib/commonjs/features/queue/queue.js.map +1 -1
  127. package/lib/commonjs/utils/LazyNativeEmitter.js +21 -10
  128. package/lib/commonjs/utils/LazyNativeEmitter.js.map +1 -1
  129. package/lib/commonjs/utils/NativeUpdatedValue.js +31 -13
  130. package/lib/commonjs/utils/NativeUpdatedValue.js.map +1 -1
  131. package/lib/commonjs/utils/getTrackIdentity.js +24 -0
  132. package/lib/commonjs/utils/getTrackIdentity.js.map +1 -0
  133. package/lib/commonjs/utils/useNativeValueRefreshedBy.js +33 -0
  134. package/lib/commonjs/utils/useNativeValueRefreshedBy.js.map +1 -0
  135. package/lib/commonjs/web/NativeAudioBrowser.js +32 -24
  136. package/lib/commonjs/web/NativeAudioBrowser.js.map +1 -1
  137. package/lib/commonjs/web/TrackPlayer/QueueManager.js +14 -0
  138. package/lib/commonjs/web/TrackPlayer/QueueManager.js.map +1 -1
  139. package/lib/commonjs/web/browser/BrowserManager.js +144 -87
  140. package/lib/commonjs/web/browser/BrowserManager.js.map +1 -1
  141. package/lib/commonjs/web/browser/FavoriteManager.js +30 -21
  142. package/lib/commonjs/web/browser/FavoriteManager.js.map +1 -1
  143. package/lib/commonjs/web/browser/assertBrowsePageShape.js +2 -2
  144. package/lib/commonjs/web/browser/assertBrowsePageShape.js.map +1 -1
  145. package/lib/commonjs/web/browser/classifyTrackNavigation.js +9 -9
  146. package/lib/commonjs/web/browser/classifyTrackNavigation.js.map +1 -1
  147. package/lib/commonjs/web/browser/sections.js +89 -0
  148. package/lib/commonjs/web/browser/sections.js.map +1 -0
  149. package/lib/commonjs/web/player/NowPlayingManager.js +4 -3
  150. package/lib/commonjs/web/player/NowPlayingManager.js.map +1 -1
  151. package/lib/commonjs/web/util/BrowserPathHelper.js +56 -11
  152. package/lib/commonjs/web/util/BrowserPathHelper.js.map +1 -1
  153. package/lib/module/AudioBrowser.js +1 -0
  154. package/lib/module/AudioBrowser.js.map +1 -1
  155. package/lib/module/features/browser-config.js +11 -8
  156. package/lib/module/features/browser-config.js.map +1 -1
  157. package/lib/module/features/browser.js +2 -2
  158. package/lib/module/features/favorites.js +13 -10
  159. package/lib/module/features/favorites.js.map +1 -1
  160. package/lib/module/features/playback/controls.js +4 -2
  161. package/lib/module/features/playback/controls.js.map +1 -1
  162. package/lib/module/features/playback/progress.js +2 -1
  163. package/lib/module/features/playback/progress.js.map +1 -1
  164. package/lib/module/features/playback/rate.js +8 -1
  165. package/lib/module/features/playback/rate.js.map +1 -1
  166. package/lib/module/features/playback/state.js +2 -3
  167. package/lib/module/features/playback/state.js.map +1 -1
  168. package/lib/module/features/player/options.js.map +1 -1
  169. package/lib/module/features/player/setup.js +3 -1
  170. package/lib/module/features/player/setup.js.map +1 -1
  171. package/lib/module/features/player/validateOptions.js +2 -0
  172. package/lib/module/features/player/validateOptions.js.map +1 -1
  173. package/lib/module/features/queue/activeTrack.js +14 -3
  174. package/lib/module/features/queue/activeTrack.js.map +1 -1
  175. package/lib/module/features/queue/queue.js +11 -4
  176. package/lib/module/features/queue/queue.js.map +1 -1
  177. package/lib/module/utils/LazyNativeEmitter.js +21 -10
  178. package/lib/module/utils/LazyNativeEmitter.js.map +1 -1
  179. package/lib/module/utils/NativeUpdatedValue.js +31 -13
  180. package/lib/module/utils/NativeUpdatedValue.js.map +1 -1
  181. package/lib/module/utils/getTrackIdentity.js +20 -0
  182. package/lib/module/utils/getTrackIdentity.js.map +1 -0
  183. package/lib/module/utils/useNativeValueRefreshedBy.js +29 -0
  184. package/lib/module/utils/useNativeValueRefreshedBy.js.map +1 -0
  185. package/lib/module/web/NativeAudioBrowser.js +32 -24
  186. package/lib/module/web/NativeAudioBrowser.js.map +1 -1
  187. package/lib/module/web/TrackPlayer/QueueManager.js +14 -0
  188. package/lib/module/web/TrackPlayer/QueueManager.js.map +1 -1
  189. package/lib/module/web/browser/BrowserManager.js +144 -87
  190. package/lib/module/web/browser/BrowserManager.js.map +1 -1
  191. package/lib/module/web/browser/FavoriteManager.js +31 -21
  192. package/lib/module/web/browser/FavoriteManager.js.map +1 -1
  193. package/lib/module/web/browser/assertBrowsePageShape.js +2 -2
  194. package/lib/module/web/browser/assertBrowsePageShape.js.map +1 -1
  195. package/lib/module/web/browser/classifyTrackNavigation.js +9 -9
  196. package/lib/module/web/browser/classifyTrackNavigation.js.map +1 -1
  197. package/lib/module/web/browser/sections.js +83 -0
  198. package/lib/module/web/browser/sections.js.map +1 -0
  199. package/lib/module/web/player/NowPlayingManager.js +4 -3
  200. package/lib/module/web/player/NowPlayingManager.js.map +1 -1
  201. package/lib/module/web/util/BrowserPathHelper.js +56 -11
  202. package/lib/module/web/util/BrowserPathHelper.js.map +1 -1
  203. package/lib/typescript/src/AudioBrowser.d.ts +1 -0
  204. package/lib/typescript/src/AudioBrowser.d.ts.map +1 -1
  205. package/lib/typescript/src/features/browser-config.d.ts.map +1 -1
  206. package/lib/typescript/src/features/browser.d.ts +2 -2
  207. package/lib/typescript/src/features/favorites.d.ts +13 -10
  208. package/lib/typescript/src/features/favorites.d.ts.map +1 -1
  209. package/lib/typescript/src/features/playback/controls.d.ts +4 -2
  210. package/lib/typescript/src/features/playback/controls.d.ts.map +1 -1
  211. package/lib/typescript/src/features/playback/progress.d.ts.map +1 -1
  212. package/lib/typescript/src/features/playback/rate.d.ts +8 -1
  213. package/lib/typescript/src/features/playback/rate.d.ts.map +1 -1
  214. package/lib/typescript/src/features/playback/state.d.ts +2 -3
  215. package/lib/typescript/src/features/playback/state.d.ts.map +1 -1
  216. package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts +1 -1
  217. package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts.map +1 -1
  218. package/lib/typescript/src/features/player/options.d.ts +21 -19
  219. package/lib/typescript/src/features/player/options.d.ts.map +1 -1
  220. package/lib/typescript/src/features/player/setup.d.ts.map +1 -1
  221. package/lib/typescript/src/features/player/validateOptions.d.ts +1 -8
  222. package/lib/typescript/src/features/player/validateOptions.d.ts.map +1 -1
  223. package/lib/typescript/src/features/queue/activeTrack.d.ts +8 -1
  224. package/lib/typescript/src/features/queue/activeTrack.d.ts.map +1 -1
  225. package/lib/typescript/src/features/queue/queue.d.ts +5 -2
  226. package/lib/typescript/src/features/queue/queue.d.ts.map +1 -1
  227. package/lib/typescript/src/types/browser-native.d.ts +2 -2
  228. package/lib/typescript/src/types/browser-native.d.ts.map +1 -1
  229. package/lib/typescript/src/types/browser-nodes.d.ts +361 -126
  230. package/lib/typescript/src/types/browser-nodes.d.ts.map +1 -1
  231. package/lib/typescript/src/types/browser.d.ts +33 -53
  232. package/lib/typescript/src/types/browser.d.ts.map +1 -1
  233. package/lib/typescript/src/utils/LazyNativeEmitter.d.ts +16 -17
  234. package/lib/typescript/src/utils/LazyNativeEmitter.d.ts.map +1 -1
  235. package/lib/typescript/src/utils/NativeUpdatedValue.d.ts +24 -19
  236. package/lib/typescript/src/utils/NativeUpdatedValue.d.ts.map +1 -1
  237. package/lib/typescript/src/utils/getTrackIdentity.d.ts +18 -0
  238. package/lib/typescript/src/utils/getTrackIdentity.d.ts.map +1 -0
  239. package/lib/typescript/src/utils/useNativeValueRefreshedBy.d.ts +21 -0
  240. package/lib/typescript/src/utils/useNativeValueRefreshedBy.d.ts.map +1 -0
  241. package/nitrogen/generated/android/AudioBrowser+autolinking.cmake +0 -1
  242. package/nitrogen/generated/android/c++/{JFavoritesMatchMode.hpp → JArtworkRendering.hpp} +15 -15
  243. package/nitrogen/generated/android/c++/JArtworkRequestConfig.hpp +8 -3
  244. package/nitrogen/generated/android/c++/JBrowseResult.hpp +16 -4
  245. package/nitrogen/generated/android/c++/JCarPlayPlayingIndicatorLocation.hpp +58 -0
  246. package/nitrogen/generated/android/c++/JCardImage.hpp +58 -0
  247. package/nitrogen/generated/android/c++/JFavoriteChangedEvent.hpp +8 -3
  248. package/nitrogen/generated/android/c++/JFormatNowPlayingParams.hpp +8 -3
  249. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___Track.hpp +8 -3
  250. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.hpp +8 -3
  251. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_std__string____Track.hpp +8 -3
  252. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____Track.hpp +8 -3
  253. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_Track______SearchParams.hpp +8 -2
  254. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_void_____TrackLoadEvent.hpp +8 -2
  255. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError__std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError_______BrowserSourceCallbackParam.hpp +16 -4
  256. package/nitrogen/generated/android/c++/JFunc_void_FavoriteChangedEvent.hpp +8 -3
  257. package/nitrogen/generated/android/c++/JFunc_void_Options.hpp +2 -5
  258. package/nitrogen/generated/android/c++/JFunc_void_PlaybackActiveTrackChangedEvent.hpp +8 -3
  259. package/nitrogen/generated/android/c++/JFunc_void_std__optional_ResolvedTrack_.hpp +15 -3
  260. package/nitrogen/generated/android/c++/JFunc_void_std__vector_Track_.hpp +8 -2
  261. package/nitrogen/generated/android/c++/JGridTile.hpp +61 -0
  262. package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.cpp +33 -14
  263. package/nitrogen/generated/android/c++/JIOSOptions.hpp +9 -3
  264. package/nitrogen/generated/android/c++/JImageShape.hpp +58 -0
  265. package/nitrogen/generated/android/c++/JMediaRequestConfig.hpp +8 -3
  266. package/nitrogen/generated/android/c++/JNativeBrowserConfiguration.hpp +22 -10
  267. package/nitrogen/generated/android/c++/JNativeRouteEntry.hpp +14 -2
  268. package/nitrogen/generated/android/c++/JNativeSetupPlayerOptions.hpp +10 -7
  269. package/nitrogen/generated/android/c++/JNativeUpdateOptions.hpp +2 -5
  270. package/nitrogen/generated/android/c++/JNitroIOSUpdateOptions.hpp +9 -3
  271. package/nitrogen/generated/android/c++/JOptions.hpp +2 -5
  272. package/nitrogen/generated/android/c++/JPlaybackActiveTrackChangedEvent.hpp +8 -3
  273. package/nitrogen/generated/android/c++/JPlayerCapabilities.hpp +5 -11
  274. package/nitrogen/generated/android/c++/JResolvedTrack.hpp +55 -51
  275. package/nitrogen/generated/android/c++/JSection.hpp +120 -0
  276. package/nitrogen/generated/android/c++/JSectionStyle.hpp +96 -0
  277. package/nitrogen/generated/android/c++/JStyleDisplay.hpp +58 -0
  278. package/nitrogen/generated/android/c++/JTrack.hpp +23 -48
  279. package/nitrogen/generated/android/c++/JTrackLoadEvent.hpp +8 -2
  280. package/nitrogen/generated/android/c++/JTrackStyle.hpp +46 -18
  281. package/nitrogen/generated/android/c++/JVariant_ResolvedTrack_BrowseError_Promise_BrowseResult_.hpp +16 -4
  282. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{FavoritesMatchMode.kt → ArtworkRendering.kt} +5 -5
  283. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/CarPlayPlayingIndicatorLocation.kt +23 -0
  284. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/CardImage.kt +23 -0
  285. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/GridTile.kt +24 -0
  286. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/IOSOptions.kt +6 -3
  287. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageShape.kt +23 -0
  288. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeBrowserConfiguration.kt +5 -5
  289. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroIOSUpdateOptions.kt +6 -3
  290. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlayerCapabilities.kt +2 -2
  291. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ResolvedTrack.kt +12 -18
  292. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{FavoriteConfig.kt → Section.kt} +18 -6
  293. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SectionStyle.kt +59 -0
  294. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/StyleDisplay.kt +23 -0
  295. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Track.kt +6 -15
  296. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TrackStyle.kt +35 -5
  297. package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.hpp +158 -82
  298. package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Umbrella.hpp +25 -10
  299. package/nitrogen/generated/ios/c++/HybridAudioBrowserSpecSwift.hpp +25 -10
  300. package/nitrogen/generated/ios/swift/ArtworkRendering.swift +40 -0
  301. package/nitrogen/generated/ios/swift/CarPlayPlayingIndicatorLocation.swift +40 -0
  302. package/nitrogen/generated/ios/swift/CardImage.swift +40 -0
  303. package/nitrogen/generated/ios/swift/GridTile.swift +44 -0
  304. package/nitrogen/generated/ios/swift/IOSOptions.swift +7 -2
  305. package/nitrogen/generated/ios/swift/ImageShape.swift +40 -0
  306. package/nitrogen/generated/ios/swift/NativeBrowserConfiguration.swift +5 -5
  307. package/nitrogen/generated/ios/swift/NitroIOSUpdateOptions.swift +12 -1
  308. package/nitrogen/generated/ios/swift/PlayerCapabilities.swift +8 -27
  309. package/nitrogen/generated/ios/swift/ResolvedTrack.swift +54 -83
  310. package/nitrogen/generated/ios/swift/Section.swift +100 -0
  311. package/nitrogen/generated/ios/swift/SectionStyle.swift +133 -0
  312. package/nitrogen/generated/ios/swift/StyleDisplay.swift +40 -0
  313. package/nitrogen/generated/ios/swift/Track.swift +20 -73
  314. package/nitrogen/generated/ios/swift/TrackStyle.swift +86 -22
  315. package/nitrogen/generated/shared/c++/{FavoritesMatchMode.hpp → ArtworkRendering.hpp} +17 -17
  316. package/nitrogen/generated/shared/c++/CarPlayPlayingIndicatorLocation.hpp +76 -0
  317. package/nitrogen/generated/shared/c++/CardImage.hpp +76 -0
  318. package/nitrogen/generated/shared/c++/GridTile.hpp +80 -0
  319. package/nitrogen/generated/shared/c++/IOSOptions.hpp +9 -2
  320. package/nitrogen/generated/shared/c++/ImageShape.hpp +76 -0
  321. package/nitrogen/generated/shared/c++/NativeBrowserConfiguration.hpp +6 -6
  322. package/nitrogen/generated/shared/c++/NitroIOSUpdateOptions.hpp +9 -2
  323. package/nitrogen/generated/shared/c++/PlayerCapabilities.hpp +6 -9
  324. package/nitrogen/generated/shared/c++/ResolvedTrack.hpp +30 -38
  325. package/nitrogen/generated/shared/c++/Section.hpp +106 -0
  326. package/nitrogen/generated/shared/c++/SectionStyle.hpp +126 -0
  327. package/nitrogen/generated/shared/c++/StyleDisplay.hpp +76 -0
  328. package/nitrogen/generated/shared/c++/Track.hpp +15 -31
  329. package/nitrogen/generated/shared/c++/TrackStyle.hpp +73 -34
  330. package/package.json +7 -2
  331. package/src/AudioBrowser.ts +1 -0
  332. package/src/features/browser-config.test.ts +21 -6
  333. package/src/features/browser-config.ts +11 -8
  334. package/src/features/browser.test.ts +1 -1
  335. package/src/features/browser.ts +2 -2
  336. package/src/features/favorites.ts +13 -10
  337. package/src/features/hook-wiring.test.ts +63 -15
  338. package/src/features/playback/controls.ts +4 -2
  339. package/src/features/playback/progress.ts +2 -1
  340. package/src/features/playback/rate.ts +8 -1
  341. package/src/features/playback/state.ts +2 -3
  342. package/src/features/playback/trackPlaybackTime.ts +1 -1
  343. package/src/features/player/options.ts +27 -19
  344. package/src/features/player/setup.test.ts +12 -3
  345. package/src/features/player/setup.ts +3 -1
  346. package/src/features/player/validateOptions.ts +2 -0
  347. package/src/features/queue/activeTrack.ts +14 -3
  348. package/src/features/queue/queue.ts +11 -4
  349. package/src/nitro-stub-drift.test.ts +40 -0
  350. package/src/public-surface.test.ts +40 -0
  351. package/src/specs/audio-browser.nitro.ts +4 -3
  352. package/src/style-resolution.test.ts +210 -0
  353. package/src/types/browser-native.ts +2 -2
  354. package/src/types/browser-nodes.ts +377 -135
  355. package/src/types/browser.ts +34 -55
  356. package/src/utils/LazyNativeEmitter.ts +20 -10
  357. package/src/utils/NativeUpdatedValue.ts +29 -13
  358. package/src/utils/getTrackIdentity.ts +20 -0
  359. package/src/utils/useNativeValueRefreshedBy.ts +38 -0
  360. package/src/web/NativeAudioBrowser.ts +37 -22
  361. package/src/web/TrackPlayer/QueueManager.test.ts +29 -0
  362. package/src/web/TrackPlayer/QueueManager.ts +14 -0
  363. package/src/web/browser/BrowserManager.ts +176 -95
  364. package/src/web/browser/FavoriteManager.test.ts +84 -0
  365. package/src/web/browser/FavoriteManager.ts +28 -23
  366. package/src/web/browser/assertBrowsePageShape.test.ts +1 -1
  367. package/src/web/browser/assertBrowsePageShape.ts +5 -3
  368. package/src/web/browser/classifyTrackNavigation.test.ts +10 -8
  369. package/src/web/browser/classifyTrackNavigation.ts +9 -9
  370. package/src/web/browser/parseSearchResponse.test.ts +8 -2
  371. package/src/web/browser/sections.test.ts +91 -0
  372. package/src/web/browser/sections.ts +72 -0
  373. package/src/web/player/NowPlayingManager.ts +4 -3
  374. package/src/web/util/BrowserPathHelper.test.ts +52 -0
  375. package/src/web/util/BrowserPathHelper.ts +61 -13
  376. package/website/guide/android-auto.md +114 -0
  377. package/website/guide/android-certificates.md +409 -0
  378. package/website/guide/artwork.md +255 -0
  379. package/website/guide/audio-output.md +135 -0
  380. package/website/guide/automotive.md +86 -0
  381. package/website/guide/basic-usage.md +151 -0
  382. package/website/guide/battery.md +115 -0
  383. package/website/guide/browser.md +581 -0
  384. package/website/guide/carplay.md +620 -0
  385. package/website/guide/configuration.md +173 -0
  386. package/website/guide/equalizer.md +231 -0
  387. package/website/guide/errors.md +310 -0
  388. package/website/guide/favorites.md +154 -0
  389. package/website/guide/gate.md +143 -0
  390. package/website/guide/getting-started.md +46 -0
  391. package/website/guide/hooks.md +238 -0
  392. package/website/guide/live-streams.md +125 -0
  393. package/website/guide/metadata.md +239 -0
  394. package/website/guide/migrating-from-track-player.md +244 -0
  395. package/website/guide/native-callback-fetch.md +127 -0
  396. package/website/guide/network.md +86 -0
  397. package/website/guide/now-playing.md +175 -0
  398. package/website/guide/playback.md +328 -0
  399. package/website/guide/queue.md +300 -0
  400. package/website/guide/remote-controls.md +256 -0
  401. package/website/guide/search.md +255 -0
  402. package/website/guide/sleep-timer.md +268 -0
  403. package/website/guide/track.md +246 -0
  404. package/ios/CarPlay/ImageRowItem+CarPlay.swift +0 -19
  405. package/nitrogen/generated/android/c++/JFavoriteConfig.hpp +0 -58
  406. package/nitrogen/generated/android/c++/JImageRowItem.hpp +0 -105
  407. package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.cpp +0 -26
  408. package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.hpp +0 -71
  409. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageRowItem.kt +0 -68
  410. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_Boolean_FavoriteConfig.kt +0 -53
  411. package/nitrogen/generated/ios/swift/FavoriteConfig.swift +0 -29
  412. package/nitrogen/generated/ios/swift/FavoritesMatchMode.swift +0 -40
  413. package/nitrogen/generated/ios/swift/ImageRowItem.swift +0 -195
  414. package/nitrogen/generated/ios/swift/Variant_Bool_FavoriteConfig.swift +0 -18
  415. package/nitrogen/generated/shared/c++/FavoriteConfig.hpp +0 -84
  416. package/nitrogen/generated/shared/c++/ImageRowItem.hpp +0 -129
@@ -35,9 +35,7 @@ namespace margelo::nitro::audiobrowser { struct ImageSource; }
35
35
  // Forward declaration of `TrackRequest` to properly resolve imports.
36
36
  namespace margelo::nitro::audiobrowser { struct TrackRequest; }
37
37
  // Forward declaration of `TrackStyle` to properly resolve imports.
38
- namespace margelo::nitro::audiobrowser { enum class TrackStyle; }
39
- // Forward declaration of `ImageRowItem` to properly resolve imports.
40
- namespace margelo::nitro::audiobrowser { struct ImageRowItem; }
38
+ namespace margelo::nitro::audiobrowser { struct TrackStyle; }
41
39
 
42
40
  #include <string>
43
41
  #include <optional>
@@ -46,8 +44,6 @@ namespace margelo::nitro::audiobrowser { struct ImageRowItem; }
46
44
  #include "ImageSource.hpp"
47
45
  #include "TrackRequest.hpp"
48
46
  #include "TrackStyle.hpp"
49
- #include "ImageRowItem.hpp"
50
- #include <vector>
51
47
 
52
48
  namespace margelo::nitro::audiobrowser {
53
49
 
@@ -57,30 +53,27 @@ namespace margelo::nitro::audiobrowser {
57
53
  struct Track final {
58
54
  public:
59
55
  std::optional<std::string> id SWIFT_PRIVATE;
60
- std::optional<std::string> url SWIFT_PRIVATE;
56
+ std::optional<std::string> path SWIFT_PRIVATE;
61
57
  std::optional<std::string> src SWIFT_PRIVATE;
62
58
  std::optional<std::variant<std::string, ArtworkVariants>> artwork SWIFT_PRIVATE;
63
59
  std::optional<ImageSource> artworkSource SWIFT_PRIVATE;
64
60
  std::optional<TrackRequest> request SWIFT_PRIVATE;
65
- std::optional<bool> artworkCarPlayTinted SWIFT_PRIVATE;
66
61
  std::string title SWIFT_PRIVATE;
67
62
  std::optional<std::string> subtitle SWIFT_PRIVATE;
68
63
  std::optional<std::string> artist SWIFT_PRIVATE;
69
- std::optional<std::string> albumUrl SWIFT_PRIVATE;
64
+ std::optional<std::string> albumPath SWIFT_PRIVATE;
70
65
  std::optional<std::string> album SWIFT_PRIVATE;
71
66
  std::optional<std::string> description SWIFT_PRIVATE;
72
67
  std::optional<std::string> genre SWIFT_PRIVATE;
73
68
  std::optional<double> duration SWIFT_PRIVATE;
74
69
  std::optional<TrackStyle> style SWIFT_PRIVATE;
75
- std::optional<TrackStyle> childrenStyle SWIFT_PRIVATE;
70
+ std::optional<bool> disabled SWIFT_PRIVATE;
76
71
  std::optional<bool> favorited SWIFT_PRIVATE;
77
- std::optional<std::string> groupTitle SWIFT_PRIVATE;
78
72
  std::optional<bool> live SWIFT_PRIVATE;
79
- std::optional<std::vector<ImageRowItem>> imageRow SWIFT_PRIVATE;
80
73
 
81
74
  public:
82
75
  Track() = default;
83
- explicit Track(std::optional<std::string> id, std::optional<std::string> url, std::optional<std::string> src, std::optional<std::variant<std::string, ArtworkVariants>> artwork, std::optional<ImageSource> artworkSource, std::optional<TrackRequest> request, std::optional<bool> artworkCarPlayTinted, std::string title, std::optional<std::string> subtitle, std::optional<std::string> artist, std::optional<std::string> albumUrl, std::optional<std::string> album, std::optional<std::string> description, std::optional<std::string> genre, std::optional<double> duration, std::optional<TrackStyle> style, std::optional<TrackStyle> childrenStyle, std::optional<bool> favorited, std::optional<std::string> groupTitle, std::optional<bool> live, std::optional<std::vector<ImageRowItem>> imageRow): id(id), url(url), src(src), artwork(artwork), artworkSource(artworkSource), request(request), artworkCarPlayTinted(artworkCarPlayTinted), title(title), subtitle(subtitle), artist(artist), albumUrl(albumUrl), album(album), description(description), genre(genre), duration(duration), style(style), childrenStyle(childrenStyle), favorited(favorited), groupTitle(groupTitle), live(live), imageRow(imageRow) {}
76
+ explicit Track(std::optional<std::string> id, std::optional<std::string> path, std::optional<std::string> src, std::optional<std::variant<std::string, ArtworkVariants>> artwork, std::optional<ImageSource> artworkSource, std::optional<TrackRequest> request, std::string title, std::optional<std::string> subtitle, std::optional<std::string> artist, std::optional<std::string> albumPath, std::optional<std::string> album, std::optional<std::string> description, std::optional<std::string> genre, std::optional<double> duration, std::optional<TrackStyle> style, std::optional<bool> disabled, std::optional<bool> favorited, std::optional<bool> live): id(id), path(path), src(src), artwork(artwork), artworkSource(artworkSource), request(request), title(title), subtitle(subtitle), artist(artist), albumPath(albumPath), album(album), description(description), genre(genre), duration(duration), style(style), disabled(disabled), favorited(favorited), live(live) {}
84
77
 
85
78
  public:
86
79
  friend bool operator==(const Track& lhs, const Track& rhs) = default;
@@ -97,51 +90,45 @@ namespace margelo::nitro {
97
90
  jsi::Object obj = arg.asObject(runtime);
98
91
  return margelo::nitro::audiobrowser::Track(
99
92
  JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "id"))),
100
- JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "url"))),
93
+ JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "path"))),
101
94
  JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "src"))),
102
95
  JSIConverter<std::optional<std::variant<std::string, margelo::nitro::audiobrowser::ArtworkVariants>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "artwork"))),
103
96
  JSIConverter<std::optional<margelo::nitro::audiobrowser::ImageSource>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "artworkSource"))),
104
97
  JSIConverter<std::optional<margelo::nitro::audiobrowser::TrackRequest>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "request"))),
105
- JSIConverter<std::optional<bool>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "artworkCarPlayTinted"))),
106
98
  JSIConverter<std::string>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "title"))),
107
99
  JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "subtitle"))),
108
100
  JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "artist"))),
109
- JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "albumUrl"))),
101
+ JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "albumPath"))),
110
102
  JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "album"))),
111
103
  JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "description"))),
112
104
  JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "genre"))),
113
105
  JSIConverter<std::optional<double>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "duration"))),
114
106
  JSIConverter<std::optional<margelo::nitro::audiobrowser::TrackStyle>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "style"))),
115
- JSIConverter<std::optional<margelo::nitro::audiobrowser::TrackStyle>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "childrenStyle"))),
107
+ JSIConverter<std::optional<bool>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "disabled"))),
116
108
  JSIConverter<std::optional<bool>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "favorited"))),
117
- JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "groupTitle"))),
118
- JSIConverter<std::optional<bool>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "live"))),
119
- JSIConverter<std::optional<std::vector<margelo::nitro::audiobrowser::ImageRowItem>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "imageRow")))
109
+ JSIConverter<std::optional<bool>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "live")))
120
110
  );
121
111
  }
122
112
  static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::audiobrowser::Track& arg) {
123
113
  jsi::Object obj(runtime);
124
114
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "id"), JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.id));
125
- obj.setProperty(runtime, PropNameIDCache::get(runtime, "url"), JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.url));
115
+ obj.setProperty(runtime, PropNameIDCache::get(runtime, "path"), JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.path));
126
116
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "src"), JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.src));
127
117
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "artwork"), JSIConverter<std::optional<std::variant<std::string, margelo::nitro::audiobrowser::ArtworkVariants>>>::toJSI(runtime, arg.artwork));
128
118
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "artworkSource"), JSIConverter<std::optional<margelo::nitro::audiobrowser::ImageSource>>::toJSI(runtime, arg.artworkSource));
129
119
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "request"), JSIConverter<std::optional<margelo::nitro::audiobrowser::TrackRequest>>::toJSI(runtime, arg.request));
130
- obj.setProperty(runtime, PropNameIDCache::get(runtime, "artworkCarPlayTinted"), JSIConverter<std::optional<bool>>::toJSI(runtime, arg.artworkCarPlayTinted));
131
120
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "title"), JSIConverter<std::string>::toJSI(runtime, arg.title));
132
121
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "subtitle"), JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.subtitle));
133
122
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "artist"), JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.artist));
134
- obj.setProperty(runtime, PropNameIDCache::get(runtime, "albumUrl"), JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.albumUrl));
123
+ obj.setProperty(runtime, PropNameIDCache::get(runtime, "albumPath"), JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.albumPath));
135
124
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "album"), JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.album));
136
125
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "description"), JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.description));
137
126
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "genre"), JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.genre));
138
127
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "duration"), JSIConverter<std::optional<double>>::toJSI(runtime, arg.duration));
139
128
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "style"), JSIConverter<std::optional<margelo::nitro::audiobrowser::TrackStyle>>::toJSI(runtime, arg.style));
140
- obj.setProperty(runtime, PropNameIDCache::get(runtime, "childrenStyle"), JSIConverter<std::optional<margelo::nitro::audiobrowser::TrackStyle>>::toJSI(runtime, arg.childrenStyle));
129
+ obj.setProperty(runtime, PropNameIDCache::get(runtime, "disabled"), JSIConverter<std::optional<bool>>::toJSI(runtime, arg.disabled));
141
130
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "favorited"), JSIConverter<std::optional<bool>>::toJSI(runtime, arg.favorited));
142
- obj.setProperty(runtime, PropNameIDCache::get(runtime, "groupTitle"), JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.groupTitle));
143
131
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "live"), JSIConverter<std::optional<bool>>::toJSI(runtime, arg.live));
144
- obj.setProperty(runtime, PropNameIDCache::get(runtime, "imageRow"), JSIConverter<std::optional<std::vector<margelo::nitro::audiobrowser::ImageRowItem>>>::toJSI(runtime, arg.imageRow));
145
132
  return obj;
146
133
  }
147
134
  static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
@@ -153,26 +140,23 @@ namespace margelo::nitro {
153
140
  return false;
154
141
  }
155
142
  if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "id")))) return false;
156
- if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "url")))) return false;
143
+ if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "path")))) return false;
157
144
  if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "src")))) return false;
158
145
  if (!JSIConverter<std::optional<std::variant<std::string, margelo::nitro::audiobrowser::ArtworkVariants>>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "artwork")))) return false;
159
146
  if (!JSIConverter<std::optional<margelo::nitro::audiobrowser::ImageSource>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "artworkSource")))) return false;
160
147
  if (!JSIConverter<std::optional<margelo::nitro::audiobrowser::TrackRequest>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "request")))) return false;
161
- if (!JSIConverter<std::optional<bool>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "artworkCarPlayTinted")))) return false;
162
148
  if (!JSIConverter<std::string>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "title")))) return false;
163
149
  if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "subtitle")))) return false;
164
150
  if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "artist")))) return false;
165
- if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "albumUrl")))) return false;
151
+ if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "albumPath")))) return false;
166
152
  if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "album")))) return false;
167
153
  if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "description")))) return false;
168
154
  if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "genre")))) return false;
169
155
  if (!JSIConverter<std::optional<double>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "duration")))) return false;
170
156
  if (!JSIConverter<std::optional<margelo::nitro::audiobrowser::TrackStyle>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "style")))) return false;
171
- if (!JSIConverter<std::optional<margelo::nitro::audiobrowser::TrackStyle>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "childrenStyle")))) return false;
157
+ if (!JSIConverter<std::optional<bool>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "disabled")))) return false;
172
158
  if (!JSIConverter<std::optional<bool>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "favorited")))) return false;
173
- if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "groupTitle")))) return false;
174
159
  if (!JSIConverter<std::optional<bool>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "live")))) return false;
175
- if (!JSIConverter<std::optional<std::vector<margelo::nitro::audiobrowser::ImageRowItem>>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "imageRow")))) return false;
176
160
  return true;
177
161
  }
178
162
  };
@@ -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,98 @@
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
+ // Forward declaration of `ImageShape` to properly resolve imports.
36
+ namespace margelo::nitro::audiobrowser { enum class ImageShape; }
37
+ // Forward declaration of `CardImage` to properly resolve imports.
38
+ namespace margelo::nitro::audiobrowser { enum class CardImage; }
39
+
40
+ #include "StyleDisplay.hpp"
41
+ #include <optional>
42
+ #include "ArtworkRendering.hpp"
43
+ #include "ImageShape.hpp"
44
+ #include <string>
45
+ #include "CardImage.hpp"
25
46
 
26
47
  namespace margelo::nitro::audiobrowser {
27
48
 
28
49
  /**
29
- * An enum which can be represented as a JavaScript union (TrackStyle).
50
+ * A struct which can be represented as a JavaScript object (TrackStyle).
30
51
  */
31
- enum class TrackStyle {
32
- LIST SWIFT_NAME(list) = 0,
33
- GRID SWIFT_NAME(grid) = 1,
34
- } CLOSED_ENUM;
52
+ struct TrackStyle final {
53
+ public:
54
+ std::optional<StyleDisplay> display SWIFT_PRIVATE;
55
+ std::optional<ArtworkRendering> artworkRendering SWIFT_PRIVATE;
56
+ std::optional<ImageShape> imageShape SWIFT_PRIVATE;
57
+ std::optional<std::string> accessorySymbol SWIFT_PRIVATE;
58
+ std::optional<std::string> cardTint SWIFT_PRIVATE;
59
+ std::optional<CardImage> cardImage SWIFT_PRIVATE;
60
+
61
+ public:
62
+ TrackStyle() = default;
63
+ explicit TrackStyle(std::optional<StyleDisplay> display, std::optional<ArtworkRendering> artworkRendering, std::optional<ImageShape> imageShape, std::optional<std::string> accessorySymbol, std::optional<std::string> cardTint, std::optional<CardImage> cardImage): display(display), artworkRendering(artworkRendering), imageShape(imageShape), accessorySymbol(accessorySymbol), cardTint(cardTint), cardImage(cardImage) {}
64
+
65
+ public:
66
+ friend bool operator==(const TrackStyle& lhs, const TrackStyle& rhs) = default;
67
+ };
35
68
 
36
69
  } // namespace margelo::nitro::audiobrowser
37
70
 
38
71
  namespace margelo::nitro {
39
72
 
40
- // C++ TrackStyle <> JS TrackStyle (union)
73
+ // C++ TrackStyle <> JS TrackStyle (object)
41
74
  template <>
42
75
  struct JSIConverter<margelo::nitro::audiobrowser::TrackStyle> final {
43
76
  static inline margelo::nitro::audiobrowser::TrackStyle fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
44
- std::string unionValue = JSIConverter<std::string>::fromJSI(runtime, arg);
45
- switch (hashString(unionValue.c_str(), unionValue.size())) {
46
- case hashString("list"): return margelo::nitro::audiobrowser::TrackStyle::LIST;
47
- case hashString("grid"): return margelo::nitro::audiobrowser::TrackStyle::GRID;
48
- default: [[unlikely]]
49
- throw std::invalid_argument("Cannot convert \"" + unionValue + "\" to enum TrackStyle - invalid value!");
50
- }
77
+ jsi::Object obj = arg.asObject(runtime);
78
+ return margelo::nitro::audiobrowser::TrackStyle(
79
+ JSIConverter<std::optional<margelo::nitro::audiobrowser::StyleDisplay>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "display"))),
80
+ JSIConverter<std::optional<margelo::nitro::audiobrowser::ArtworkRendering>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "artworkRendering"))),
81
+ JSIConverter<std::optional<margelo::nitro::audiobrowser::ImageShape>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "imageShape"))),
82
+ JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "accessorySymbol"))),
83
+ JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "cardTint"))),
84
+ JSIConverter<std::optional<margelo::nitro::audiobrowser::CardImage>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "cardImage")))
85
+ );
51
86
  }
52
- static inline jsi::Value toJSI(jsi::Runtime& runtime, margelo::nitro::audiobrowser::TrackStyle arg) {
53
- switch (arg) {
54
- case margelo::nitro::audiobrowser::TrackStyle::LIST: return JSIConverter<std::string>::toJSI(runtime, "list");
55
- case margelo::nitro::audiobrowser::TrackStyle::GRID: return JSIConverter<std::string>::toJSI(runtime, "grid");
56
- default: [[unlikely]]
57
- throw std::invalid_argument("Cannot convert TrackStyle to JS - invalid value: "
58
- + std::to_string(static_cast<int>(arg)) + "!");
59
- }
87
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::audiobrowser::TrackStyle& arg) {
88
+ jsi::Object obj(runtime);
89
+ obj.setProperty(runtime, PropNameIDCache::get(runtime, "display"), JSIConverter<std::optional<margelo::nitro::audiobrowser::StyleDisplay>>::toJSI(runtime, arg.display));
90
+ obj.setProperty(runtime, PropNameIDCache::get(runtime, "artworkRendering"), JSIConverter<std::optional<margelo::nitro::audiobrowser::ArtworkRendering>>::toJSI(runtime, arg.artworkRendering));
91
+ obj.setProperty(runtime, PropNameIDCache::get(runtime, "imageShape"), JSIConverter<std::optional<margelo::nitro::audiobrowser::ImageShape>>::toJSI(runtime, arg.imageShape));
92
+ obj.setProperty(runtime, PropNameIDCache::get(runtime, "accessorySymbol"), JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.accessorySymbol));
93
+ obj.setProperty(runtime, PropNameIDCache::get(runtime, "cardTint"), JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.cardTint));
94
+ obj.setProperty(runtime, PropNameIDCache::get(runtime, "cardImage"), JSIConverter<std::optional<margelo::nitro::audiobrowser::CardImage>>::toJSI(runtime, arg.cardImage));
95
+ return obj;
60
96
  }
61
97
  static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
62
- if (!value.isString()) {
98
+ if (!value.isObject()) {
63
99
  return false;
64
100
  }
65
- std::string unionValue = JSIConverter<std::string>::fromJSI(runtime, value);
66
- switch (hashString(unionValue.c_str(), unionValue.size())) {
67
- case hashString("list"):
68
- case hashString("grid"):
69
- return true;
70
- default:
71
- return false;
101
+ jsi::Object obj = value.getObject(runtime);
102
+ if (!nitro::isPlainObject(runtime, obj)) {
103
+ return false;
72
104
  }
105
+ if (!JSIConverter<std::optional<margelo::nitro::audiobrowser::StyleDisplay>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "display")))) return false;
106
+ if (!JSIConverter<std::optional<margelo::nitro::audiobrowser::ArtworkRendering>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "artworkRendering")))) return false;
107
+ if (!JSIConverter<std::optional<margelo::nitro::audiobrowser::ImageShape>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "imageShape")))) return false;
108
+ if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "accessorySymbol")))) return false;
109
+ if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "cardTint")))) return false;
110
+ if (!JSIConverter<std::optional<margelo::nitro::audiobrowser::CardImage>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "cardImage")))) return false;
111
+ return true;
73
112
  }
74
113
  };
75
114
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-audio-browser",
3
- "version": "0.2.0-next.11",
3
+ "version": "0.2.0-next.13",
4
4
  "description": "react-native-audio-browser is a react native package built with Nitro",
5
5
  "keywords": [
6
6
  "react-native",
@@ -31,6 +31,7 @@
31
31
  "ios/**/*.mm",
32
32
  "ios/**/*.cpp",
33
33
  "ios/**/*.swift",
34
+ "website/guide",
34
35
  "*.podspec",
35
36
  "README.md"
36
37
  ],
@@ -46,23 +47,27 @@
46
47
  "scripts": {
47
48
  "android:format": "yarn workspace example-native android:format",
48
49
  "android:format:check": "yarn workspace example-native android:format:check",
50
+ "android:gradle-stop": "yarn workspace example-native android:gradle-stop",
49
51
  "android:ide": "yarn workspace example-native android:ide",
50
52
  "android:logs": "react-native log-android",
51
53
  "android:test": "yarn workspace example-native android:test",
54
+ "android:types": "yarn workspace example-native android:types",
52
55
  "build": "yarn prepare",
53
56
  "clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib",
54
57
  "codegen": "node codegen/index.js --logLevel=\"debug\" && node post-script.cjs && node codegen/strip-struct-equality.cjs && yarn build",
55
58
  "prepare": "bob build && yarn hooks:install",
56
59
  "hooks:install": "git config core.hooksPath .githooks || exit 0",
57
60
  "example": "yarn workspace example-native",
61
+ "ci:android": "yarn android:format:check && yarn android:types && yarn android:test",
58
62
  "ci:format": "oxfmt --check --ignore-path .gitignore --ignore-path .oxfmtignore .",
59
63
  "ci:format:ios": "swiftformat --lint ios",
64
+ "ci:ios": "yarn ci:format:ios && yarn ios:test",
60
65
  "ci:lint": "oxlint --type-aware src",
61
66
  "format": "oxfmt --write --ignore-path .gitignore --ignore-path .oxfmtignore .",
62
67
  "ios:format": "swiftformat ios",
63
68
  "ios:ide": "yarn workspace example-native ios:ide",
64
69
  "ios:rebuild": "yarn workspace example-native ios:rebuild",
65
- "ios:test": "swift test",
70
+ "ios:test": "swift test --disable-sandbox",
66
71
  "lint": "oxlint --fix",
67
72
  "release": "semantic-release",
68
73
  "test": "vitest run",
@@ -1,4 +1,5 @@
1
1
  // `./native` is deliberately not re-exported — see public-surface.test.ts.
2
2
  export * from './features'
3
3
  export * from './types'
4
+ export * from './utils/getTrackIdentity'
4
5
  export * from './utils/useDebug'
@@ -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
- url: '/x',
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 () => ({ url: '/favorites', title: 'Favorites' }),
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', url: '/' },
75
- { title: 'Search', url: '/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': { url: '/x', title: 'X', children: [] } }
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}`, url: `/${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
- // Any RequestConfig key marks a request config. A static ResolvedTrack
42
- // shares none of these at the top level (its per-track request override is
43
- // nested under `request`).
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
- url: TABS_ROUTE_PATH,
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 url + title.
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.url !== 'string' || typeof page.title !== 'string') {
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
- `({ url, title, children? }). It will be served as static ` +
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
- resolveAlbumUrl: config.resolveAlbumUrl,
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', url: '/' }] }
15
+ const config = { tabs: [{ title: 'Home', path: '/' }] }
16
16
  configureBrowser(config)
17
17
  expect(getBrowserConfiguration()).toBe(config)
18
18
  })
@@ -43,8 +43,8 @@ let currentConfiguration: BrowserConfiguration | undefined
43
43
  * }
44
44
  * },
45
45
  * tabs: [
46
- * { title: 'Home', url: '/' },
47
- * { title: 'Search', url: '/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 source identifiers.
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
- * Note: This is only needed if the tracks provided to AudioBrowser do not
47
- * include the `favorited` field. If your API already includes favorite state in
48
- * track responses, the native favorites cache is populated automatically during browsing.
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
- * When the heart button is tapped in media controllers or when you call
51
- * `setActiveTrackFavorited()`, the native favorites cache is automatically
52
- * updated. You only need to call this on app launch to hydrate the cache.
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` values
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 favoriteSrcs = await loadFavoritesFromStorage()
59
- * setFavorites(favoriteSrcs)
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 22 hooks that are a single delegation to `useNativeUpdatedValue`. Their
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
+ )