react-native-audio-browser 0.2.0-next.7 → 0.2.0-next.9
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/android/build.gradle +7 -1
- package/android/src/main/java/com/audiobrowser/AudioBrowser.kt +33 -8
- package/android/src/main/java/com/audiobrowser/ServiceBinding.kt +46 -0
- package/android/src/main/java/com/audiobrowser/browser/BrowserManager.kt +1 -2
- package/android/src/main/java/com/audiobrowser/browser/BrowserUrlResolution.kt +1 -2
- package/android/src/main/java/com/audiobrowser/browser/SectionScope.kt +6 -7
- package/android/src/main/java/com/audiobrowser/player/NowPlayingUpdater.kt +0 -1
- package/android/src/main/java/com/audiobrowser/player/PlaybackStateStore.kt +2 -4
- package/android/src/main/java/com/audiobrowser/player/RetryLoadErrorHandlingPolicy.kt +2 -1
- package/android/src/main/java/com/audiobrowser/util/ArtworkVariant.kt +7 -9
- package/android/src/main/java/com/audiobrowser/util/TrackFactory.kt +8 -7
- package/android/src/test/java/com/audiobrowser/ServiceBindingTest.kt +68 -0
- package/android/src/test/java/com/audiobrowser/TestFixtures.kt +0 -1
- package/android/src/test/java/com/audiobrowser/browser/JsonArtworkTest.kt +2 -1
- package/android/src/test/java/com/audiobrowser/browser/SectionScopeTest.kt +2 -4
- package/android/src/test/java/com/audiobrowser/player/PlaybackErrorClassifierTest.kt +3 -4
- package/android/src/test/java/com/audiobrowser/player/RetryLoadErrorHandlingPolicyTest.kt +3 -3
- package/ios/Browser/BrowserManager.swift +2 -2
- package/ios/Browser/SearchDrillIn.swift +1 -1
- package/ios/CarPlay/RNABMediaAddHandler.swift +6 -6
- package/ios/CarPlay/RNABMediaAffinityHandler.swift +2 -2
- package/ios/CarPlay/RNABMediaIntentHandler.swift +16 -16
- package/ios/HybridAudioBrowser.swift +86 -55
- package/ios/Model/RemoteCommand.swift +18 -0
- package/ios/Model/TrackPlayerError.swift +2 -2
- package/ios/Option/SessionCategories.swift +3 -2
- package/ios/Player/MediaIntentCriteria.swift +4 -4
- package/ios/Player/PlaybackErrorHandler.swift +2 -2
- package/ios/Player/RetryManager.swift +2 -1
- package/ios/RemoteCommand/RemoteCommandCallbacks.swift +47 -0
- package/ios/RemoteCommand/RemoteCommandController.swift +9 -6
- package/ios/Tests/LoadSeekCoordinatorTests.swift +3 -1
- package/ios/Tests/MediaIntentCriteriaTests.swift +2 -2
- package/ios/Tests/PlaybackCoordinatorTests.swift +1 -1
- package/ios/Tests/RemoteCommandControllerTests.swift +156 -0
- package/ios/Tests/RemoteCommandTests.swift +67 -0
- package/ios/Tests/SectionScopeTests.swift +2 -2
- package/ios/Tests/TabBarEntriesTests.swift +7 -7
- package/ios/TrackPlayer.swift +1 -1
- package/ios/TrackPlayerCallbacks.swift +5 -38
- package/lib/commonjs/AudioBrowser.js +0 -11
- package/lib/commonjs/AudioBrowser.js.map +1 -1
- package/lib/commonjs/features/carConnection.js +1 -1
- package/lib/commonjs/features/equalizer.js +1 -1
- package/lib/commonjs/features/errors.js +3 -3
- package/lib/commonjs/features/favorites.js +1 -1
- package/lib/commonjs/features/gate.js +1 -1
- package/lib/commonjs/features/metadata.js +3 -3
- package/lib/commonjs/features/network.js +1 -1
- package/lib/commonjs/features/nowPlaying.js +1 -1
- package/lib/commonjs/features/output.js +1 -1
- package/lib/commonjs/features/playback/playWhenReady.js +1 -1
- package/lib/commonjs/features/playback/playing.js +1 -1
- package/lib/commonjs/features/playback/progress.js +1 -1
- package/lib/commonjs/features/playback/state.js +1 -1
- package/lib/commonjs/features/playback/trackPlaybackTime.js +0 -6
- package/lib/commonjs/features/playback/trackPlaybackTime.js.map +1 -1
- package/lib/commonjs/features/playback/volume.js +1 -1
- package/lib/commonjs/features/player/options.js +3 -19
- package/lib/commonjs/features/player/options.js.map +1 -1
- package/lib/commonjs/features/player/setup.js +2 -2
- package/lib/commonjs/features/player/setup.js.map +1 -1
- package/lib/commonjs/features/player/validateOptions.js +24 -0
- package/lib/commonjs/features/player/validateOptions.js.map +1 -0
- package/lib/commonjs/features/queue/activeTrack.js +1 -1
- package/lib/commonjs/features/queue/queue.js +2 -2
- package/lib/commonjs/features/queue/repeatMode.js +1 -1
- package/lib/commonjs/features/queue/shuffle.js +1 -1
- package/lib/commonjs/features/remoteControls.js +13 -92
- package/lib/commonjs/features/remoteControls.js.map +1 -1
- package/lib/commonjs/features/sleepTimer.js +1 -1
- package/lib/commonjs/utils/LazyNativeEmitter.js +8 -5
- package/lib/commonjs/utils/LazyNativeEmitter.js.map +1 -1
- package/lib/module/AudioBrowser.js +4 -1
- package/lib/module/AudioBrowser.js.map +1 -1
- package/lib/module/features/carConnection.js +1 -1
- package/lib/module/features/equalizer.js +1 -1
- package/lib/module/features/errors.js +3 -3
- package/lib/module/features/favorites.js +1 -1
- package/lib/module/features/gate.js +1 -1
- package/lib/module/features/metadata.js +3 -3
- package/lib/module/features/network.js +1 -1
- package/lib/module/features/nowPlaying.js +1 -1
- package/lib/module/features/output.js +1 -1
- package/lib/module/features/playback/playWhenReady.js +1 -1
- package/lib/module/features/playback/playing.js +1 -1
- package/lib/module/features/playback/progress.js +1 -1
- package/lib/module/features/playback/state.js +1 -1
- package/lib/module/features/playback/trackPlaybackTime.js +0 -5
- package/lib/module/features/playback/trackPlaybackTime.js.map +1 -1
- package/lib/module/features/playback/volume.js +1 -1
- package/lib/module/features/player/options.js +2 -17
- package/lib/module/features/player/options.js.map +1 -1
- package/lib/module/features/player/setup.js +1 -1
- package/lib/module/features/player/setup.js.map +1 -1
- package/lib/module/features/player/validateOptions.js +20 -0
- package/lib/module/features/player/validateOptions.js.map +1 -0
- package/lib/module/features/queue/activeTrack.js +1 -1
- package/lib/module/features/queue/queue.js +2 -2
- package/lib/module/features/queue/repeatMode.js +1 -1
- package/lib/module/features/queue/shuffle.js +1 -1
- package/lib/module/features/remoteControls.js +13 -92
- package/lib/module/features/remoteControls.js.map +1 -1
- package/lib/module/features/sleepTimer.js +1 -1
- package/lib/module/utils/LazyNativeEmitter.js +8 -5
- package/lib/module/utils/LazyNativeEmitter.js.map +1 -1
- package/lib/typescript/src/AudioBrowser.d.ts +0 -1
- package/lib/typescript/src/AudioBrowser.d.ts.map +1 -1
- package/lib/typescript/src/features/carConnection.d.ts +1 -1
- package/lib/typescript/src/features/equalizer.d.ts +1 -1
- package/lib/typescript/src/features/errors.d.ts +3 -3
- package/lib/typescript/src/features/favorites.d.ts +1 -1
- package/lib/typescript/src/features/gate.d.ts +3 -2
- package/lib/typescript/src/features/gate.d.ts.map +1 -1
- package/lib/typescript/src/features/metadata.d.ts +3 -3
- package/lib/typescript/src/features/network.d.ts +1 -1
- package/lib/typescript/src/features/nowPlaying.d.ts +1 -1
- package/lib/typescript/src/features/output.d.ts +1 -1
- package/lib/typescript/src/features/playback/playWhenReady.d.ts +1 -1
- package/lib/typescript/src/features/playback/playing.d.ts +1 -1
- package/lib/typescript/src/features/playback/progress.d.ts +1 -1
- package/lib/typescript/src/features/playback/state.d.ts +1 -1
- package/lib/typescript/src/features/playback/volume.d.ts +1 -1
- package/lib/typescript/src/features/player/options.d.ts +1 -1
- package/lib/typescript/src/features/player/options.d.ts.map +1 -1
- package/lib/typescript/src/features/player/validateOptions.d.ts +2 -0
- package/lib/typescript/src/features/player/validateOptions.d.ts.map +1 -0
- package/lib/typescript/src/features/queue/activeTrack.d.ts +1 -1
- package/lib/typescript/src/features/queue/queue.d.ts +2 -2
- package/lib/typescript/src/features/queue/repeatMode.d.ts +1 -1
- package/lib/typescript/src/features/queue/shuffle.d.ts +1 -1
- package/lib/typescript/src/features/remoteControls.d.ts +23 -22
- package/lib/typescript/src/features/remoteControls.d.ts.map +1 -1
- package/lib/typescript/src/features/sleepTimer.d.ts +1 -1
- package/lib/typescript/src/utils/LazyNativeEmitter.d.ts +8 -4
- package/lib/typescript/src/utils/LazyNativeEmitter.d.ts.map +1 -1
- package/package.json +3 -1
- package/src/AudioBrowser.ts +4 -1
- package/src/features/carConnection.ts +1 -1
- package/src/features/equalizer.ts +1 -1
- package/src/features/errors.ts +3 -3
- package/src/features/favorites.ts +1 -1
- package/src/features/gate.ts +1 -1
- package/src/features/metadata.ts +3 -3
- package/src/features/network.ts +1 -1
- package/src/features/nowPlaying.ts +1 -1
- package/src/features/output.ts +1 -1
- package/src/features/playback/playWhenReady.ts +1 -1
- package/src/features/playback/playing.ts +1 -1
- package/src/features/playback/progress.ts +1 -1
- package/src/features/playback/state.ts +1 -1
- package/src/features/playback/trackPlaybackTime.test.ts +20 -6
- package/src/features/playback/trackPlaybackTime.ts +0 -5
- package/src/features/playback/volume.ts +1 -1
- package/src/features/player/options.ts +2 -20
- package/src/features/player/setup.ts +1 -1
- package/src/features/player/validateOptions.ts +22 -0
- package/src/features/queue/activeTrack.ts +1 -1
- package/src/features/queue/queue.ts +2 -2
- package/src/features/queue/repeatMode.ts +1 -1
- package/src/features/queue/shuffle.ts +1 -1
- package/src/features/remoteControls.ts +13 -92
- package/src/features/sleepTimer.ts +1 -1
- package/src/public-surface.test.ts +123 -0
- package/src/utils/LazyNativeEmitter.ts +9 -6
|
@@ -11,7 +11,7 @@ import type {
|
|
|
11
11
|
UpdateOptions
|
|
12
12
|
} from './options'
|
|
13
13
|
import { nativeBrowser } from '../../native'
|
|
14
|
-
import { validateIOSUpdateOptions } from './
|
|
14
|
+
import { validateIOSUpdateOptions } from './validateOptions'
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Parameters passed to the {@link FormatNowPlayingCallback}.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { IOSUpdateOptions } from './options'
|
|
2
|
+
|
|
3
|
+
const MAX_CARPLAY_NOW_PLAYING_BUTTONS = 5
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Warns when more CarPlay now-playing buttons are configured than CarPlay renders.
|
|
7
|
+
* Shared by `updateOptions` and `setupPlayer` (both can carry `ios` options).
|
|
8
|
+
*
|
|
9
|
+
* Lives outside `options.ts` because `features/player/index.ts` re-exports that
|
|
10
|
+
* module wholesale: anything exported there joins the package's runtime surface,
|
|
11
|
+
* `@internal` or not. This file is imported directly by the two callers and by
|
|
12
|
+
* nothing in the barrel.
|
|
13
|
+
*/
|
|
14
|
+
export function validateIOSUpdateOptions(ios?: IOSUpdateOptions): void {
|
|
15
|
+
const buttons = ios?.carPlayNowPlayingButtons
|
|
16
|
+
if (buttons && buttons.length > MAX_CARPLAY_NOW_PLAYING_BUTTONS) {
|
|
17
|
+
console.warn(
|
|
18
|
+
`[react-native-audio-browser] ${buttons.length} CarPlay now-playing ` +
|
|
19
|
+
`buttons configured; CarPlay shows at most ${MAX_CARPLAY_NOW_PLAYING_BUTTONS}.`
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -41,7 +41,7 @@ export function getActiveTrackIndex(): number | undefined {
|
|
|
41
41
|
/**
|
|
42
42
|
* Subscribes to active track change events.
|
|
43
43
|
* @param callback - Called when the active track changes
|
|
44
|
-
* @returns
|
|
44
|
+
* @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
|
|
45
45
|
*/
|
|
46
46
|
export const onActiveTrackChanged =
|
|
47
47
|
NativeUpdatedValue.emitterize<PlaybackActiveTrackChangedEvent>(
|
|
@@ -156,7 +156,7 @@ export function getQueue(): Track[] {
|
|
|
156
156
|
/**
|
|
157
157
|
* Subscribes to playback queue ended events.
|
|
158
158
|
* @param callback - Called when playback has paused due to reaching the end of the queue
|
|
159
|
-
* @returns
|
|
159
|
+
* @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
|
|
160
160
|
*/
|
|
161
161
|
export const onQueueEnded =
|
|
162
162
|
NativeUpdatedValue.emitterize<PlaybackQueueEndedEvent>(
|
|
@@ -167,7 +167,7 @@ export const onQueueEnded =
|
|
|
167
167
|
* Subscribes to queue change events.
|
|
168
168
|
* Called when tracks are added, removed, reordered, or when track metadata changes.
|
|
169
169
|
* @param callback - Called with the updated queue
|
|
170
|
-
* @returns
|
|
170
|
+
* @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
|
|
171
171
|
*/
|
|
172
172
|
export const onQueueChanged = NativeUpdatedValue.emitterize<Track[]>(
|
|
173
173
|
(cb) => (nativeBrowser.onPlaybackQueueChanged = cb)
|
|
@@ -40,7 +40,7 @@ export interface RepeatModeChangedEvent {
|
|
|
40
40
|
/**
|
|
41
41
|
* Subscribes to repeat mode changes.
|
|
42
42
|
* @param callback - Called when repeat mode changes
|
|
43
|
-
* @returns
|
|
43
|
+
* @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
|
|
44
44
|
*/
|
|
45
45
|
export const onRepeatModeChanged =
|
|
46
46
|
NativeUpdatedValue.emitterize<RepeatModeChangedEvent>(
|
|
@@ -32,7 +32,7 @@ export function toggleShuffle(): void {
|
|
|
32
32
|
/**
|
|
33
33
|
* Subscribes to shuffle mode changes.
|
|
34
34
|
* @param callback - Called when shuffle mode changes
|
|
35
|
-
* @returns
|
|
35
|
+
* @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
|
|
36
36
|
*/
|
|
37
37
|
export const onShuffleChanged = NativeUpdatedValue.emitterize<boolean>(
|
|
38
38
|
(cb) => (nativeBrowser.onPlaybackShuffleModeChanged = cb)
|
|
@@ -53,90 +53,11 @@ export interface RemoteSkipEvent {
|
|
|
53
53
|
index: number
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
// MARK: - Default Handlers
|
|
57
|
-
|
|
58
|
-
// Install remote control handlers with default behavior immediately when module loads
|
|
59
|
-
// Custom handlers can override the defaults using handleRemote* functions
|
|
60
|
-
|
|
61
|
-
// Basic playback controls
|
|
62
|
-
// AudioBrowser.onRemotePlay(() => {
|
|
63
|
-
// const customHandler = customHandlers.get('play')
|
|
64
|
-
// if (customHandler) {
|
|
65
|
-
// customHandler()
|
|
66
|
-
// } else {
|
|
67
|
-
// play()
|
|
68
|
-
// }
|
|
69
|
-
// })
|
|
70
|
-
|
|
71
|
-
// AudioBrowser.onRemotePause(() => {
|
|
72
|
-
// const customHandler = customHandlers.get('pause')
|
|
73
|
-
// if (customHandler) {
|
|
74
|
-
// customHandler()
|
|
75
|
-
// } else {
|
|
76
|
-
// pause()
|
|
77
|
-
// }
|
|
78
|
-
// })
|
|
79
|
-
|
|
80
|
-
// AudioBrowser.onRemoteNext(() => {
|
|
81
|
-
// const customHandler = customHandlers.get('next')
|
|
82
|
-
// if (customHandler) {
|
|
83
|
-
// customHandler()
|
|
84
|
-
// } else {
|
|
85
|
-
// skipToNext()
|
|
86
|
-
// }
|
|
87
|
-
// })
|
|
88
|
-
|
|
89
|
-
// AudioBrowser.onRemotePrevious(() => {
|
|
90
|
-
// const customHandler = customHandlers.get('previous')
|
|
91
|
-
// if (customHandler) {
|
|
92
|
-
// customHandler()
|
|
93
|
-
// } else {
|
|
94
|
-
// skipToPrevious()
|
|
95
|
-
// }
|
|
96
|
-
// })
|
|
97
|
-
|
|
98
|
-
// AudioBrowser.onRemoteStop(() => {
|
|
99
|
-
// const customHandler = customHandlers.get('stop')
|
|
100
|
-
// if (customHandler) {
|
|
101
|
-
// customHandler()
|
|
102
|
-
// } else {
|
|
103
|
-
// stop()
|
|
104
|
-
// }
|
|
105
|
-
// })
|
|
106
|
-
|
|
107
|
-
// // Seek controls
|
|
108
|
-
// AudioBrowser.onRemoteSeek((event: any) => {
|
|
109
|
-
// const customHandler = customHandlers.get('seek')
|
|
110
|
-
// if (customHandler) {
|
|
111
|
-
// customHandler(event)
|
|
112
|
-
// } else {
|
|
113
|
-
// seekTo(event.position)
|
|
114
|
-
// }
|
|
115
|
-
// })
|
|
116
|
-
|
|
117
|
-
// AudioBrowser.onRemoteJumpForward((event: any) => {
|
|
118
|
-
// const customHandler = customHandlers.get('jumpForward')
|
|
119
|
-
// if (customHandler) {
|
|
120
|
-
// customHandler(event)
|
|
121
|
-
// } else {
|
|
122
|
-
// seekBy(event.interval)
|
|
123
|
-
// }
|
|
124
|
-
// })
|
|
125
|
-
|
|
126
|
-
// AudioBrowser.onRemoteJumpBackward((event: any) => {
|
|
127
|
-
// const customHandler = customHandlers.get('jumpBackward')
|
|
128
|
-
// if (customHandler) {
|
|
129
|
-
// customHandler(event)
|
|
130
|
-
// } else {
|
|
131
|
-
// seekBy(-event.interval)
|
|
132
|
-
// }
|
|
133
|
-
// })
|
|
134
|
-
|
|
135
56
|
// MARK: - Handler Override Functions
|
|
136
57
|
//
|
|
137
58
|
// Use these functions when you want to OVERRIDE the default remote control behavior.
|
|
138
59
|
// These will replace the default handlers with your custom logic.
|
|
139
|
-
// If you just want to listen to events for debugging/logging, use the onRemote*
|
|
60
|
+
// If you just want to listen to events for debugging/logging, use the onRemote* emitters below.
|
|
140
61
|
|
|
141
62
|
/**
|
|
142
63
|
* Sets a custom handler for remote play events, overriding the default behavior.
|
|
@@ -210,7 +131,7 @@ export function handleRemoteJumpBackward(
|
|
|
210
131
|
|
|
211
132
|
// MARK: - Event Callbacks (for listening/debugging only)
|
|
212
133
|
//
|
|
213
|
-
// Use these
|
|
134
|
+
// Use these emitters when you want to LISTEN to remote control events without overriding
|
|
214
135
|
// the default behavior. These are perfect for logging, analytics, or debugging.
|
|
215
136
|
// Multiple listeners can be registered for the same event.
|
|
216
137
|
// To override the default behavior, use the handleRemote* functions above.
|
|
@@ -218,7 +139,7 @@ export function handleRemoteJumpBackward(
|
|
|
218
139
|
/**
|
|
219
140
|
* Subscribes to remote jump backward events.
|
|
220
141
|
* @param callback - Called when the user presses the jump backward button
|
|
221
|
-
* @returns
|
|
142
|
+
* @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
|
|
222
143
|
*/
|
|
223
144
|
export const onRemoteJumpBackward =
|
|
224
145
|
LazyNativeEmitter.emitterize<RemoteJumpBackwardEvent>(
|
|
@@ -228,7 +149,7 @@ export const onRemoteJumpBackward =
|
|
|
228
149
|
/**
|
|
229
150
|
* Subscribes to remote jump forward events.
|
|
230
151
|
* @param callback - Called when the user presses the jump forward button
|
|
231
|
-
* @returns
|
|
152
|
+
* @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
|
|
232
153
|
*/
|
|
233
154
|
export const onRemoteJumpForward =
|
|
234
155
|
LazyNativeEmitter.emitterize<RemoteJumpForwardEvent>(
|
|
@@ -238,7 +159,7 @@ export const onRemoteJumpForward =
|
|
|
238
159
|
/**
|
|
239
160
|
* Subscribes to remote next events.
|
|
240
161
|
* @param callback - Called when the user presses the next track button
|
|
241
|
-
* @returns
|
|
162
|
+
* @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
|
|
242
163
|
*/
|
|
243
164
|
export const onRemoteNext = LazyNativeEmitter.emitterize<void>(
|
|
244
165
|
(cb) => (nativeBrowser.onRemoteNext = cb)
|
|
@@ -247,7 +168,7 @@ export const onRemoteNext = LazyNativeEmitter.emitterize<void>(
|
|
|
247
168
|
/**
|
|
248
169
|
* Subscribes to remote pause events.
|
|
249
170
|
* @param callback - Called when the user presses the pause button
|
|
250
|
-
* @returns
|
|
171
|
+
* @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
|
|
251
172
|
*/
|
|
252
173
|
export const onRemotePause = LazyNativeEmitter.emitterize<void>(
|
|
253
174
|
(cb) => (nativeBrowser.onRemotePause = cb)
|
|
@@ -256,7 +177,7 @@ export const onRemotePause = LazyNativeEmitter.emitterize<void>(
|
|
|
256
177
|
/**
|
|
257
178
|
* Subscribes to remote play events.
|
|
258
179
|
* @param callback - Called when the user presses the play button
|
|
259
|
-
* @returns
|
|
180
|
+
* @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
|
|
260
181
|
*/
|
|
261
182
|
export const onRemotePlay = LazyNativeEmitter.emitterize<void>(
|
|
262
183
|
(cb) => (nativeBrowser.onRemotePlay = cb)
|
|
@@ -265,7 +186,7 @@ export const onRemotePlay = LazyNativeEmitter.emitterize<void>(
|
|
|
265
186
|
/**
|
|
266
187
|
* Subscribes to remote play ID events (Android only).
|
|
267
188
|
* @param callback - Called when the user selects a track from an external device
|
|
268
|
-
* @returns
|
|
189
|
+
* @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
|
|
269
190
|
*/
|
|
270
191
|
export const onRemotePlayId = LazyNativeEmitter.emitterize<RemotePlayIdEvent>(
|
|
271
192
|
(cb) => (nativeBrowser.onRemotePlayId = cb)
|
|
@@ -274,7 +195,7 @@ export const onRemotePlayId = LazyNativeEmitter.emitterize<RemotePlayIdEvent>(
|
|
|
274
195
|
/**
|
|
275
196
|
* Subscribes to remote play search events (Android only).
|
|
276
197
|
* @param callback - Called when the user searches for a track (usually voice search)
|
|
277
|
-
* @returns
|
|
198
|
+
* @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
|
|
278
199
|
*/
|
|
279
200
|
export const onRemotePlaySearch =
|
|
280
201
|
LazyNativeEmitter.emitterize<RemotePlaySearchEvent>(
|
|
@@ -284,7 +205,7 @@ export const onRemotePlaySearch =
|
|
|
284
205
|
/**
|
|
285
206
|
* Subscribes to remote previous events.
|
|
286
207
|
* @param callback - Called when the user presses the previous track button
|
|
287
|
-
* @returns
|
|
208
|
+
* @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
|
|
288
209
|
*/
|
|
289
210
|
export const onRemotePrevious = LazyNativeEmitter.emitterize<void>(
|
|
290
211
|
(cb) => (nativeBrowser.onRemotePrevious = cb)
|
|
@@ -293,7 +214,7 @@ export const onRemotePrevious = LazyNativeEmitter.emitterize<void>(
|
|
|
293
214
|
/**
|
|
294
215
|
* Subscribes to remote seek events.
|
|
295
216
|
* @param callback - Called when the user changes the position of the timeline
|
|
296
|
-
* @returns
|
|
217
|
+
* @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
|
|
297
218
|
*/
|
|
298
219
|
export const onRemoteSeek = LazyNativeEmitter.emitterize<RemoteSeekEvent>(
|
|
299
220
|
(cb) => (nativeBrowser.onRemoteSeek = cb)
|
|
@@ -302,7 +223,7 @@ export const onRemoteSeek = LazyNativeEmitter.emitterize<RemoteSeekEvent>(
|
|
|
302
223
|
/**
|
|
303
224
|
* Subscribes to remote skip events (Android only).
|
|
304
225
|
* @param callback - Called when the user presses the skip button
|
|
305
|
-
* @returns
|
|
226
|
+
* @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
|
|
306
227
|
*/
|
|
307
228
|
export const onRemoteSkip = LazyNativeEmitter.emitterize<RemoteSkipEvent>(
|
|
308
229
|
(cb) => (nativeBrowser.onRemoteSkip = cb)
|
|
@@ -311,7 +232,7 @@ export const onRemoteSkip = LazyNativeEmitter.emitterize<RemoteSkipEvent>(
|
|
|
311
232
|
/**
|
|
312
233
|
* Subscribes to remote stop events.
|
|
313
234
|
* @param callback - Called when the user presses the stop button
|
|
314
|
-
* @returns
|
|
235
|
+
* @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
|
|
315
236
|
*/
|
|
316
237
|
export const onRemoteStop = LazyNativeEmitter.emitterize<void>(
|
|
317
238
|
(cb) => (nativeBrowser.onRemoteStop = cb)
|
|
@@ -86,7 +86,7 @@ export function clearSleepTimer(): boolean {
|
|
|
86
86
|
/**
|
|
87
87
|
* Subscribes to sleep timer changes.
|
|
88
88
|
* @param callback - Called when the sleep timer state changes
|
|
89
|
-
* @returns
|
|
89
|
+
* @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
|
|
90
90
|
*/
|
|
91
91
|
export const onSleepTimerChanged = NativeUpdatedValue.emitterize<SleepTimer>(
|
|
92
92
|
(cb) => (nativeBrowser.onSleepTimerChanged = cb)
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
2
|
+
import { dirname, join, relative, resolve } from 'node:path'
|
|
3
|
+
import { describe, it, expect } from 'vitest'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Guard on what `import { … } from 'react-native-audio-browser'` can reach.
|
|
7
|
+
*
|
|
8
|
+
* The package root is a chain of unfiltered `export *` barrels, so every
|
|
9
|
+
* `export` in every feature file joins the public surface by default — there is
|
|
10
|
+
* no gate to forget to pass, only one to forget to add.
|
|
11
|
+
*
|
|
12
|
+
* `@internal` alone does not keep a symbol private. It is honoured by
|
|
13
|
+
* `stripInternal` when generating `lib/typescript`, which is what a consumer's
|
|
14
|
+
* TypeScript sees — but React Native resolves `"react-native": "src/index"`, so
|
|
15
|
+
* Metro bundles the *source*, where nothing was stripped. An `@internal` value
|
|
16
|
+
* is therefore invisible to the type checker and fully reachable at runtime:
|
|
17
|
+
* `require('react-native-audio-browser').nativeBrowser` used to hand out the raw
|
|
18
|
+
* Nitro object, whose `on*` properties are the single callback slots the library's
|
|
19
|
+
* emitters own — assigning one unsubscribes every hook in the package.
|
|
20
|
+
*
|
|
21
|
+
* So: values marked `@internal` must not be reachable from the barrel at all.
|
|
22
|
+
* Move them to a module the barrel doesn't re-export (see
|
|
23
|
+
* `features/player/validateOptions.ts`) and import them directly. Types are
|
|
24
|
+
* exempt — they carry no runtime representation, and `stripInternal` genuinely
|
|
25
|
+
* removes them from the published `.d.ts`.
|
|
26
|
+
*/
|
|
27
|
+
const SRC = join(process.cwd(), 'src')
|
|
28
|
+
const ENTRY = join(SRC, 'index.ts')
|
|
29
|
+
|
|
30
|
+
const VALUE_KINDS = new Set([
|
|
31
|
+
'const',
|
|
32
|
+
'let',
|
|
33
|
+
'var',
|
|
34
|
+
'function',
|
|
35
|
+
'class',
|
|
36
|
+
'enum'
|
|
37
|
+
])
|
|
38
|
+
|
|
39
|
+
function resolveModule(fromFile: string, spec: string): string | undefined {
|
|
40
|
+
const base = resolve(dirname(fromFile), spec.replace(/\.tsx?$/, ''))
|
|
41
|
+
return [`${base}.ts`, `${base}.tsx`, join(base, 'index.ts')].find(existsSync)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface Export {
|
|
45
|
+
name: string
|
|
46
|
+
file: string
|
|
47
|
+
internal: boolean
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Walks the `export *` graph from `entry`, collecting every reachable value export. */
|
|
51
|
+
function collectValueExports(entry: string): Export[] {
|
|
52
|
+
const found: Export[] = []
|
|
53
|
+
const seen = new Set<string>()
|
|
54
|
+
|
|
55
|
+
const visit = (file: string) => {
|
|
56
|
+
if (seen.has(file)) return
|
|
57
|
+
seen.add(file)
|
|
58
|
+
const source = readFileSync(file, 'utf8')
|
|
59
|
+
|
|
60
|
+
for (const [, spec] of source.matchAll(/^export \* from ['"](.+?)['"]/gm)) {
|
|
61
|
+
const target = resolveModule(file, spec!)
|
|
62
|
+
if (target) visit(target)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const declaration =
|
|
66
|
+
/^export (?:declare )?(?:async )?(?:abstract )?(const|let|var|function|class|enum) (\w+)/gm
|
|
67
|
+
for (const match of source.matchAll(declaration)) {
|
|
68
|
+
if (!VALUE_KINDS.has(match[1]!)) continue
|
|
69
|
+
// The JSDoc block, if any, is whatever sits directly above the export.
|
|
70
|
+
const preceding = source.slice(0, match.index)
|
|
71
|
+
const doc = /\/\*\*(?:(?!\*\/)[\s\S])*\*\/\s*$/.exec(preceding)?.[0] ?? ''
|
|
72
|
+
found.push({
|
|
73
|
+
name: match[2]!,
|
|
74
|
+
file: relative(SRC, file),
|
|
75
|
+
internal: /@internal\b/.test(doc)
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
visit(entry)
|
|
81
|
+
return found
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
describe('public surface', () => {
|
|
85
|
+
const exports = collectValueExports(ENTRY)
|
|
86
|
+
|
|
87
|
+
it('reaches the feature modules at all (the walker still works)', () => {
|
|
88
|
+
// Cheap canary: if the barrel structure changes shape and the walker stops
|
|
89
|
+
// resolving, every other assertion here would pass vacuously.
|
|
90
|
+
expect(exports.map((e) => e.name)).toContain('trackPlaybackTime')
|
|
91
|
+
expect(exports.length).toBeGreaterThan(100)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('exposes no value marked @internal', () => {
|
|
95
|
+
const leaked = exports
|
|
96
|
+
.filter((e) => e.internal)
|
|
97
|
+
.map((e) => `${e.name} (${e.file})`)
|
|
98
|
+
expect(
|
|
99
|
+
leaked,
|
|
100
|
+
'These values are tagged @internal but are reachable from the package root ' +
|
|
101
|
+
'at runtime — stripInternal only hides them from the published types, and ' +
|
|
102
|
+
'Metro bundles src/. Move each to a module the barrels do not re-export ' +
|
|
103
|
+
'and import it directly:\n' +
|
|
104
|
+
leaked.join('\n')
|
|
105
|
+
).toEqual([])
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it('exposes no __-prefixed test hook', () => {
|
|
109
|
+
const hooks = exports
|
|
110
|
+
.filter((e) => e.name.startsWith('__'))
|
|
111
|
+
.map((e) => `${e.name} (${e.file})`)
|
|
112
|
+
expect(
|
|
113
|
+
hooks,
|
|
114
|
+
'Test-only exports ship to consumers. Drive the unit under test through ' +
|
|
115
|
+
'the seam it already has (a mocked native slot, say) instead:\n' +
|
|
116
|
+
hooks.join('\n')
|
|
117
|
+
).toEqual([])
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
it('does not re-export the raw Nitro object', () => {
|
|
121
|
+
expect(exports.map((e) => e.name)).not.toContain('nativeBrowser')
|
|
122
|
+
})
|
|
123
|
+
})
|
|
@@ -12,9 +12,11 @@ type Callback<T> = (arg: T) => void
|
|
|
12
12
|
*
|
|
13
13
|
* @example
|
|
14
14
|
* ```typescript
|
|
15
|
-
* export const onRemotePlay =
|
|
15
|
+
* export const onRemotePlay = LazyNativeEmitter.emitterize<void>(
|
|
16
16
|
* (cb) => (nativeBrowser.onRemotePlay = cb)
|
|
17
17
|
* )
|
|
18
|
+
*
|
|
19
|
+
* const unsubscribe = onRemotePlay.addListener(() => console.log('play'))
|
|
18
20
|
* ```
|
|
19
21
|
*/
|
|
20
22
|
export class LazyNativeEmitter<T> {
|
|
@@ -43,14 +45,15 @@ export class LazyNativeEmitter<T> {
|
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
/**
|
|
46
|
-
* Creates a lazy emitter
|
|
48
|
+
* Creates a lazy emitter for a native callback.
|
|
49
|
+
*
|
|
47
50
|
* @param setter - Function that sets the native callback property
|
|
48
|
-
* @returns
|
|
51
|
+
* @returns The emitter instance — subscribe with `addListener`, which returns
|
|
52
|
+
* an unsubscribe function
|
|
49
53
|
*/
|
|
50
54
|
static emitterize<T>(
|
|
51
55
|
setter: (callback: (data: T) => void) => void
|
|
52
|
-
):
|
|
53
|
-
|
|
54
|
-
return (callback: Callback<T>) => emitter.addListener(callback)
|
|
56
|
+
): LazyNativeEmitter<T> {
|
|
57
|
+
return new LazyNativeEmitter(setter)
|
|
55
58
|
}
|
|
56
59
|
}
|