stream-chat-react-native 9.0.0-beta.10 → 9.0.0-beta.11
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/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stream-chat-react-native",
|
|
3
3
|
"description": "The official React Native SDK for Stream Chat, a service for building chat applications",
|
|
4
|
-
"version": "9.0.0-beta.
|
|
4
|
+
"version": "9.0.0-beta.11",
|
|
5
5
|
"homepage": "https://www.npmjs.com/package/stream-chat-react-native",
|
|
6
6
|
"author": {
|
|
7
7
|
"company": "Stream.io Inc",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"es6-symbol": "^3.1.3",
|
|
31
31
|
"mime": "^4.0.7",
|
|
32
|
-
"stream-chat-react-native-core": "9.0.0-beta.
|
|
32
|
+
"stream-chat-react-native-core": "9.0.0-beta.11"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|
|
35
35
|
"@react-native-camera-roll/camera-roll": ">=7.8.0",
|
|
@@ -136,7 +136,7 @@ class _Audio {
|
|
|
136
136
|
};
|
|
137
137
|
startPlayer = async (uri, _, onPlaybackStatusUpdate) => {
|
|
138
138
|
try {
|
|
139
|
-
|
|
139
|
+
await audioRecorderPlayer.startPlayer(uri);
|
|
140
140
|
audioRecorderPlayer.addPlayBackListener((status) => {
|
|
141
141
|
onPlaybackStatusUpdate(status);
|
|
142
142
|
});
|
|
@@ -1,47 +1,353 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type { PlaybackStatus, SoundReturnType } from 'stream-chat-react-native-core';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
type InitialPlaybackStatus = {
|
|
4
|
+
positionMillis?: number;
|
|
5
|
+
progressUpdateIntervalMillis?: number;
|
|
6
|
+
rate?: number;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
type LegacyAudioRecorderPlayerConstructor = new () => NativePlaybackInstance;
|
|
10
|
+
|
|
11
|
+
let LegacyAudioRecorderPlayer: LegacyAudioRecorderPlayerConstructor | undefined;
|
|
12
|
+
let createNitroSound: (() => NativePlaybackInstance) | undefined;
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
({ createSound: createNitroSound } = require('react-native-nitro-sound'));
|
|
16
|
+
} catch (e) {
|
|
17
|
+
// do nothing
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
LegacyAudioRecorderPlayer = require('react-native-audio-recorder-player').default;
|
|
22
|
+
} catch (e) {
|
|
23
|
+
// do nothing
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const PROGRESS_UPDATE_INTERVAL_MILLIS = 100;
|
|
27
|
+
|
|
28
|
+
type NativePlaybackMeta = {
|
|
29
|
+
currentPosition?: number;
|
|
30
|
+
duration?: number;
|
|
31
|
+
isFinished?: boolean;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
type NativePlaybackEndMeta = {
|
|
35
|
+
currentPosition: number;
|
|
36
|
+
duration: number;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
type NativePlaybackInstance = {
|
|
40
|
+
addPlayBackListener: (callback: (meta: NativePlaybackMeta) => void) => void;
|
|
41
|
+
pausePlayer: () => Promise<string>;
|
|
42
|
+
removePlayBackListener: () => void;
|
|
43
|
+
resumePlayer: () => Promise<string>;
|
|
44
|
+
seekToPlayer: (time: number) => Promise<string>;
|
|
45
|
+
setPlaybackSpeed?: (playbackSpeed: number) => Promise<string>;
|
|
46
|
+
setSubscriptionDuration?: (seconds: number) => void | Promise<string>;
|
|
47
|
+
startPlayer: (uri?: string, httpHeaders?: Record<string, string>) => Promise<string>;
|
|
48
|
+
stopPlayer: () => Promise<string>;
|
|
49
|
+
addPlaybackEndListener?: (callback: (meta: NativePlaybackEndMeta) => void) => void;
|
|
50
|
+
dispose?: () => void;
|
|
51
|
+
removePlaybackEndListener?: () => void;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const createPlaybackInstance = (): NativePlaybackInstance | null => {
|
|
55
|
+
if (createNitroSound) {
|
|
56
|
+
return createNitroSound();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (LegacyAudioRecorderPlayer) {
|
|
60
|
+
return new LegacyAudioRecorderPlayer();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return null;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const createPlaybackStatus = ({
|
|
67
|
+
didJustFinish = false,
|
|
68
|
+
durationMillis,
|
|
69
|
+
error = '',
|
|
70
|
+
isLoaded,
|
|
71
|
+
isPlaying,
|
|
72
|
+
positionMillis,
|
|
73
|
+
}: {
|
|
74
|
+
didJustFinish?: boolean;
|
|
75
|
+
durationMillis: number;
|
|
76
|
+
error?: string;
|
|
77
|
+
isLoaded: boolean;
|
|
78
|
+
isPlaying: boolean;
|
|
79
|
+
positionMillis: number;
|
|
80
|
+
}): PlaybackStatus => ({
|
|
81
|
+
currentPosition: positionMillis,
|
|
82
|
+
didJustFinish,
|
|
83
|
+
duration: durationMillis,
|
|
84
|
+
durationMillis,
|
|
85
|
+
error,
|
|
86
|
+
isBuffering: false,
|
|
87
|
+
isLoaded,
|
|
88
|
+
isLooping: false,
|
|
89
|
+
isMuted: false,
|
|
90
|
+
isPlaying,
|
|
91
|
+
isSeeking: false,
|
|
92
|
+
positionMillis,
|
|
93
|
+
shouldPlay: isPlaying,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
class NativeAudioSoundAdapter implements SoundReturnType {
|
|
97
|
+
testID = 'native-audio-sound';
|
|
98
|
+
onPlaybackStatusUpdate?: (playbackStatus: PlaybackStatus) => void;
|
|
99
|
+
private playbackInstance: NativePlaybackInstance | null;
|
|
100
|
+
private sourceUri?: string;
|
|
101
|
+
private isDisposed = false;
|
|
102
|
+
private isLoaded = false;
|
|
103
|
+
private playing = false;
|
|
104
|
+
private durationMillis = 0;
|
|
105
|
+
private positionMillis = 0;
|
|
106
|
+
private playbackRate = 1;
|
|
107
|
+
private hasProgressListener = false;
|
|
108
|
+
private hasPlaybackEndListener = false;
|
|
109
|
+
|
|
110
|
+
constructor({
|
|
111
|
+
source,
|
|
112
|
+
initialStatus,
|
|
113
|
+
onPlaybackStatusUpdate,
|
|
114
|
+
}: {
|
|
115
|
+
source?: { uri: string };
|
|
116
|
+
initialStatus?: InitialPlaybackStatus;
|
|
117
|
+
onPlaybackStatusUpdate?: (playbackStatus: PlaybackStatus) => void;
|
|
118
|
+
}) {
|
|
119
|
+
this.playbackInstance = createPlaybackInstance();
|
|
120
|
+
this.sourceUri = source?.uri;
|
|
121
|
+
this.onPlaybackStatusUpdate = onPlaybackStatusUpdate;
|
|
122
|
+
this.playbackRate = initialStatus?.rate ?? 1;
|
|
123
|
+
this.positionMillis = initialStatus?.positionMillis ?? 0;
|
|
124
|
+
const progressUpdateIntervalMillis =
|
|
125
|
+
initialStatus?.progressUpdateIntervalMillis ?? PROGRESS_UPDATE_INTERVAL_MILLIS;
|
|
126
|
+
|
|
127
|
+
this.playbackInstance?.setSubscriptionDuration?.(progressUpdateIntervalMillis / 1000);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private emitPlaybackStatus({
|
|
131
|
+
didJustFinish = false,
|
|
132
|
+
error = '',
|
|
133
|
+
}: {
|
|
134
|
+
didJustFinish?: boolean;
|
|
135
|
+
error?: string;
|
|
136
|
+
} = {}) {
|
|
137
|
+
this.onPlaybackStatusUpdate?.(
|
|
138
|
+
createPlaybackStatus({
|
|
139
|
+
didJustFinish,
|
|
140
|
+
durationMillis: this.durationMillis,
|
|
141
|
+
error,
|
|
142
|
+
isLoaded: this.isLoaded,
|
|
143
|
+
isPlaying: this.playing,
|
|
144
|
+
positionMillis: this.positionMillis,
|
|
145
|
+
}),
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
private attachListeners() {
|
|
150
|
+
if (!this.playbackInstance || this.hasProgressListener) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
this.playbackInstance.addPlayBackListener(this.handlePlaybackProgress);
|
|
155
|
+
this.hasProgressListener = true;
|
|
156
|
+
|
|
157
|
+
if (this.playbackInstance.addPlaybackEndListener) {
|
|
158
|
+
this.playbackInstance.addPlaybackEndListener(this.handlePlaybackEnd);
|
|
159
|
+
this.hasPlaybackEndListener = true;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
private detachListeners() {
|
|
164
|
+
if (!this.playbackInstance) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (this.hasProgressListener) {
|
|
169
|
+
this.playbackInstance.removePlayBackListener();
|
|
170
|
+
this.hasProgressListener = false;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (this.hasPlaybackEndListener && this.playbackInstance.removePlaybackEndListener) {
|
|
174
|
+
this.playbackInstance.removePlaybackEndListener();
|
|
175
|
+
this.hasPlaybackEndListener = false;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
private handlePlaybackProgress = ({
|
|
180
|
+
currentPosition,
|
|
181
|
+
duration,
|
|
182
|
+
isFinished,
|
|
183
|
+
}: NativePlaybackMeta) => {
|
|
184
|
+
this.positionMillis = currentPosition ?? this.positionMillis;
|
|
185
|
+
this.durationMillis = duration ?? this.durationMillis;
|
|
186
|
+
|
|
187
|
+
const didJustFinish =
|
|
188
|
+
isFinished === true && this.durationMillis > 0 && this.positionMillis >= this.durationMillis;
|
|
189
|
+
|
|
190
|
+
if (didJustFinish) {
|
|
191
|
+
this.playing = false;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
this.emitPlaybackStatus({ didJustFinish });
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
private handlePlaybackEnd = ({ currentPosition, duration }: NativePlaybackEndMeta) => {
|
|
198
|
+
this.positionMillis = currentPosition ?? this.positionMillis;
|
|
199
|
+
this.durationMillis = duration ?? this.durationMillis;
|
|
200
|
+
this.playing = false;
|
|
201
|
+
this.emitPlaybackStatus({ didJustFinish: true });
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
private async ensureLoaded({ shouldPlay }: { shouldPlay: boolean }) {
|
|
205
|
+
if (!this.playbackInstance || this.isDisposed || !this.sourceUri) {
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (!this.isLoaded) {
|
|
210
|
+
this.attachListeners();
|
|
211
|
+
await this.playbackInstance.startPlayer(this.sourceUri);
|
|
212
|
+
this.isLoaded = true;
|
|
213
|
+
|
|
214
|
+
if (this.playbackRate !== 1 && this.playbackInstance.setPlaybackSpeed) {
|
|
215
|
+
await this.playbackInstance.setPlaybackSpeed(this.playbackRate);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (this.positionMillis > 0) {
|
|
219
|
+
await this.playbackInstance.seekToPlayer(this.positionMillis);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (!shouldPlay) {
|
|
223
|
+
await this.playbackInstance.pausePlayer();
|
|
224
|
+
}
|
|
225
|
+
} else if (shouldPlay) {
|
|
226
|
+
await this.playbackInstance.resumePlayer();
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
playAsync: SoundReturnType['playAsync'] = async () => {
|
|
233
|
+
const loaded = await this.ensureLoaded({ shouldPlay: true });
|
|
234
|
+
if (!loaded) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
this.playing = true;
|
|
239
|
+
this.emitPlaybackStatus();
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
resume: SoundReturnType['resume'] = () => {
|
|
243
|
+
void this.playAsync?.();
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
pauseAsync: SoundReturnType['pauseAsync'] = async () => {
|
|
247
|
+
if (!this.playbackInstance || !this.isLoaded || this.isDisposed) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
await this.playbackInstance.pausePlayer();
|
|
252
|
+
this.playing = false;
|
|
253
|
+
this.emitPlaybackStatus();
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
pause: SoundReturnType['pause'] = () => {
|
|
257
|
+
void this.pauseAsync?.();
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
seek: SoundReturnType['seek'] = async (progress) => {
|
|
261
|
+
const loaded = await this.ensureLoaded({ shouldPlay: false });
|
|
262
|
+
if (!loaded || !this.playbackInstance) {
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
this.positionMillis = progress * 1000;
|
|
267
|
+
await this.playbackInstance.seekToPlayer(this.positionMillis);
|
|
268
|
+
this.emitPlaybackStatus();
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
setPositionAsync: SoundReturnType['setPositionAsync'] = async (millis) => {
|
|
272
|
+
await this.seek?.(millis / 1000);
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
setRateAsync: SoundReturnType['setRateAsync'] = async (rate) => {
|
|
276
|
+
this.playbackRate = rate;
|
|
277
|
+
|
|
278
|
+
if (this.playbackInstance?.setPlaybackSpeed && this.isLoaded) {
|
|
279
|
+
await this.playbackInstance.setPlaybackSpeed(rate);
|
|
280
|
+
|
|
281
|
+
// Some Android backends resume playback as a side effect of changing speed.
|
|
282
|
+
// Preserve the previous paused state explicitly so rate changes stay silent.
|
|
283
|
+
if (!this.playing) {
|
|
284
|
+
await this.playbackInstance.pausePlayer();
|
|
285
|
+
this.emitPlaybackStatus();
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
replayAsync: SoundReturnType['replayAsync'] = async () => {
|
|
291
|
+
await this.stopAsync?.();
|
|
292
|
+
this.positionMillis = 0;
|
|
293
|
+
await this.playAsync?.();
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
stopAsync: SoundReturnType['stopAsync'] = async () => {
|
|
297
|
+
if (!this.playbackInstance || !this.isLoaded || this.isDisposed) {
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
await this.playbackInstance.stopPlayer();
|
|
302
|
+
this.isLoaded = false;
|
|
303
|
+
this.playing = false;
|
|
304
|
+
this.positionMillis = 0;
|
|
305
|
+
this.emitPlaybackStatus();
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
unloadAsync: SoundReturnType['unloadAsync'] = async () => {
|
|
309
|
+
if (this.isDisposed) {
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
try {
|
|
314
|
+
if (this.isLoaded && this.playbackInstance) {
|
|
315
|
+
await this.playbackInstance.stopPlayer();
|
|
316
|
+
}
|
|
317
|
+
} catch {
|
|
318
|
+
// Best effort cleanup.
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
this.detachListeners();
|
|
322
|
+
this.playbackInstance?.dispose?.();
|
|
323
|
+
this.isLoaded = false;
|
|
324
|
+
this.playing = false;
|
|
325
|
+
this.isDisposed = true;
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const initializeSound =
|
|
330
|
+
createNitroSound || LegacyAudioRecorderPlayer
|
|
331
|
+
? (
|
|
332
|
+
source?: { uri: string },
|
|
333
|
+
initialStatus?: InitialPlaybackStatus,
|
|
334
|
+
onPlaybackStatusUpdate?: (playbackStatus: PlaybackStatus) => void,
|
|
335
|
+
) => {
|
|
336
|
+
if (!source?.uri) {
|
|
337
|
+
return null;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const sound = new NativeAudioSoundAdapter({
|
|
341
|
+
initialStatus,
|
|
342
|
+
onPlaybackStatusUpdate,
|
|
343
|
+
source,
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
return sound;
|
|
347
|
+
}
|
|
348
|
+
: null;
|
|
4
349
|
|
|
5
350
|
export const Sound = {
|
|
6
|
-
initializeSound
|
|
7
|
-
|
|
8
|
-
Player: AudioVideoPlayer
|
|
9
|
-
? ({
|
|
10
|
-
onBuffer,
|
|
11
|
-
onEnd,
|
|
12
|
-
onLoad,
|
|
13
|
-
onLoadStart,
|
|
14
|
-
onPlaybackStateChanged,
|
|
15
|
-
onProgress,
|
|
16
|
-
onSeek,
|
|
17
|
-
paused,
|
|
18
|
-
rate,
|
|
19
|
-
soundRef,
|
|
20
|
-
style,
|
|
21
|
-
uri,
|
|
22
|
-
}) => (
|
|
23
|
-
<AudioVideoPlayer
|
|
24
|
-
audioOnly={true}
|
|
25
|
-
ignoreSilentSwitch={'ignore'}
|
|
26
|
-
onBuffer={onBuffer}
|
|
27
|
-
onEnd={onEnd}
|
|
28
|
-
onError={(error: Error) => {
|
|
29
|
-
console.log(error);
|
|
30
|
-
}}
|
|
31
|
-
onLoad={onLoad}
|
|
32
|
-
onLoadStart={onLoadStart}
|
|
33
|
-
onPlaybackStateChanged={onPlaybackStateChanged}
|
|
34
|
-
onProgress={onProgress}
|
|
35
|
-
onSeek={onSeek}
|
|
36
|
-
paused={paused}
|
|
37
|
-
rate={rate}
|
|
38
|
-
ref={soundRef}
|
|
39
|
-
progressUpdateInterval={100}
|
|
40
|
-
source={{
|
|
41
|
-
uri,
|
|
42
|
-
}}
|
|
43
|
-
style={style}
|
|
44
|
-
/>
|
|
45
|
-
)
|
|
46
|
-
: null,
|
|
351
|
+
initializeSound,
|
|
352
|
+
Player: null,
|
|
47
353
|
};
|