@exode-team/react-recorder 1.1.9 → 1.1.10

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.
@@ -1 +1 @@
1
- {"version":3,"file":"VoiceMessagePlayer.d.ts","sourceRoot":"","sources":["../../../src/components/VoiceMessagePlayer/VoiceMessagePlayer.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AA0BH,MAAM,WAAW,qBAAqB;IAClC,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAeD,QAAA,MAAM,kBAAkB,KAuNtB,CAAC;AAGH,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
1
+ {"version":3,"file":"VoiceMessagePlayer.d.ts","sourceRoot":"","sources":["../../../src/components/VoiceMessagePlayer/VoiceMessagePlayer.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AA0BH,MAAM,WAAW,qBAAqB;IAClC,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAeD,QAAA,MAAM,kBAAkB,KAqOtB,CAAC;AAGH,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"useAudioBlobLoader.d.ts","sourceRoot":"","sources":["../../src/hooks/useAudioBlobLoader.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAYrD,UAAU,yBAAyB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,aAAa,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAC5B,iBAAiB,EAAE,iBAAiB,CAAC;CACxC;AAED,UAAU,wBAAwB;IAC9B,QAAQ,EAAE,KAAK,CAAC,gBAAgB,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IAC1D,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,iBAAiB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1C;AAED,QAAA,MAAM,kBAAkB,GAAI,SAAS,yBAAyB,KAAG,wBAoIhE,CAAC;AAGF,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
1
+ {"version":3,"file":"useAudioBlobLoader.d.ts","sourceRoot":"","sources":["../../src/hooks/useAudioBlobLoader.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAYrD,UAAU,yBAAyB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,aAAa,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAC5B,iBAAiB,EAAE,iBAAiB,CAAC;CACxC;AAED,UAAU,wBAAwB;IAC9B,QAAQ,EAAE,KAAK,CAAC,gBAAgB,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IAC1D,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,iBAAiB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1C;AAED,QAAA,MAAM,kBAAkB,GAAI,SAAS,yBAAyB,KAAG,wBA2IhE,CAAC;AAGF,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"useAudioPlayback.d.ts","sourceRoot":"","sources":["../../src/hooks/useAudioPlayback.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,UAAU,sBAAsB;IAC5B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,CAAC;IAClC,cAAc,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,KAAK,IAAI,CAAC;IAC7D,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,mBAAmB,EAAE,MAAM,IAAI,CAAC;CACnC;AAED,QAAA,MAAM,gBAAgB,QAAO,sBA2H5B,CAAC;AAGF,OAAO,EAAE,gBAAgB,EAAE,CAAC"}
1
+ {"version":3,"file":"useAudioPlayback.d.ts","sourceRoot":"","sources":["../../src/hooks/useAudioPlayback.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,UAAU,sBAAsB;IAC5B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,CAAC;IAClC,cAAc,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,KAAK,IAAI,CAAC;IAC7D,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,mBAAmB,EAAE,MAAM,IAAI,CAAC;CACnC;AAED,QAAA,MAAM,gBAAgB,QAAO,sBA+H5B,CAAC;AAGF,OAAO,EAAE,gBAAgB,EAAE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"useVoiceMessagePlayer.d.ts","sourceRoot":"","sources":["../../src/hooks/useVoiceMessagePlayer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AASH,OAAO,KAAK,EAAE,4BAA4B,EAAE,2BAA2B,EAAE,MAAM,UAAU,CAAC;AAG1F,QAAA,MAAM,qBAAqB,GAAI,SAAS,4BAA4B,KAAG,2BAqMtE,CAAC;AAGF,OAAO,EAAE,qBAAqB,EAAE,CAAC"}
1
+ {"version":3,"file":"useVoiceMessagePlayer.d.ts","sourceRoot":"","sources":["../../src/hooks/useVoiceMessagePlayer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AASH,OAAO,KAAK,EAAE,4BAA4B,EAAE,2BAA2B,EAAE,MAAM,UAAU,CAAC;AAG1F,QAAA,MAAM,qBAAqB,GAAI,SAAS,4BAA4B,KAAG,2BAmNtE,CAAC;AAGF,OAAO,EAAE,qBAAqB,EAAE,CAAC"}
package/dist/index.cjs CHANGED
@@ -78,6 +78,61 @@ const stopStream = (stream) => {
78
78
  *
79
79
  * @author: exode <hello@exode.ru>
80
80
  */
81
+ const SEEK_OFFSET = 5;
82
+ const setupMediaSession = (audio, callbacks) => {
83
+ if (!('mediaSession' in navigator)) {
84
+ return;
85
+ }
86
+ navigator.mediaSession.setActionHandler('play', () => {
87
+ audio.play().catch(() => { });
88
+ });
89
+ navigator.mediaSession.setActionHandler('pause', () => {
90
+ audio.pause();
91
+ });
92
+ navigator.mediaSession.setActionHandler('stop', () => {
93
+ var _a;
94
+ audio.pause();
95
+ audio.currentTime = 0;
96
+ (_a = callbacks.onStop) === null || _a === void 0 ? void 0 : _a.call(callbacks);
97
+ });
98
+ navigator.mediaSession.setActionHandler('seekbackward', () => {
99
+ var _a;
100
+ audio.currentTime = Math.max(0, audio.currentTime - SEEK_OFFSET);
101
+ (_a = callbacks.onSeekBackward) === null || _a === void 0 ? void 0 : _a.call(callbacks);
102
+ });
103
+ navigator.mediaSession.setActionHandler('seekforward', () => {
104
+ var _a;
105
+ audio.currentTime = Math.min(audio.duration, audio.currentTime + SEEK_OFFSET);
106
+ (_a = callbacks.onSeekForward) === null || _a === void 0 ? void 0 : _a.call(callbacks);
107
+ });
108
+ navigator.mediaSession.setActionHandler('seekto', (details) => {
109
+ if (details.seekTime != null && isFinite(audio.duration)) {
110
+ audio.currentTime = details.seekTime;
111
+ }
112
+ });
113
+ navigator.mediaSession.setActionHandler('previoustrack', () => {
114
+ var _a;
115
+ (_a = callbacks.onPreviousTrack) === null || _a === void 0 ? void 0 : _a.call(callbacks);
116
+ });
117
+ navigator.mediaSession.setActionHandler('nexttrack', () => {
118
+ var _a;
119
+ (_a = callbacks.onNextTrack) === null || _a === void 0 ? void 0 : _a.call(callbacks);
120
+ });
121
+ };
122
+ const clearMediaSession = () => {
123
+ if (!('mediaSession' in navigator)) {
124
+ return;
125
+ }
126
+ const actions = [
127
+ 'play', 'pause', 'stop',
128
+ 'seekbackward', 'seekforward', 'seekto',
129
+ 'previoustrack', 'nexttrack',
130
+ ];
131
+ actions.forEach((action) => {
132
+ navigator.mediaSession.setActionHandler(action, null);
133
+ });
134
+ navigator.mediaSession.playbackState = 'none';
135
+ };
81
136
  const createAudioPlayback = (url, callbacks) => {
82
137
  const audio = new Audio(url);
83
138
  audio.onloadedmetadata = () => {
@@ -89,6 +144,21 @@ const createAudioPlayback = (url, callbacks) => {
89
144
  audio.onended = () => {
90
145
  callbacks.onEnded();
91
146
  };
147
+ audio.onplay = () => {
148
+ var _a;
149
+ setupMediaSession(audio, callbacks);
150
+ if ('mediaSession' in navigator) {
151
+ navigator.mediaSession.playbackState = 'playing';
152
+ }
153
+ (_a = callbacks.onPlay) === null || _a === void 0 ? void 0 : _a.call(callbacks);
154
+ };
155
+ audio.onpause = () => {
156
+ var _a;
157
+ if ('mediaSession' in navigator) {
158
+ navigator.mediaSession.playbackState = 'paused';
159
+ }
160
+ (_a = callbacks.onPause) === null || _a === void 0 ? void 0 : _a.call(callbacks);
161
+ };
92
162
  return { audio, url };
93
163
  };
94
164
  const cleanupAudio = (audio) => {
@@ -97,6 +167,9 @@ const cleanupAudio = (audio) => {
97
167
  audio.onended = null;
98
168
  audio.ontimeupdate = null;
99
169
  audio.onloadedmetadata = null;
170
+ audio.onplay = null;
171
+ audio.onpause = null;
172
+ clearMediaSession();
100
173
  };
101
174
  const createObjectURL = (blob) => {
102
175
  return URL.createObjectURL(blob);
@@ -366,6 +439,8 @@ const useAudioPlayback = () => {
366
439
  setIsPlaying(false);
367
440
  setPlaybackTime(0);
368
441
  },
442
+ onPlay: () => setIsPlaying(true),
443
+ onPause: () => setIsPlaying(false),
369
444
  });
370
445
  audioRef.current = audio;
371
446
  setPlaybackTime(0);
@@ -385,6 +460,8 @@ const useAudioPlayback = () => {
385
460
  setIsPlaying(false);
386
461
  setPlaybackTime(0);
387
462
  },
463
+ onPlay: () => setIsPlaying(true),
464
+ onPause: () => setIsPlaying(false),
388
465
  });
389
466
  audioRef.current = audio;
390
467
  }
@@ -825,6 +902,13 @@ const useAudioBlobLoader = (options) => {
825
902
  onLoadedMetadata: (dur) => callbacksRef.current.onLoadedMetadata(dur),
826
903
  onTimeUpdate: (time) => callbacksRef.current.onTimeUpdate(time),
827
904
  onEnded: () => callbacksRef.current.onEnded(),
905
+ onPlay: () => { var _a, _b; return (_b = (_a = callbacksRef.current).onPlay) === null || _b === void 0 ? void 0 : _b.call(_a); },
906
+ onPause: () => { var _a, _b; return (_b = (_a = callbacksRef.current).onPause) === null || _b === void 0 ? void 0 : _b.call(_a); },
907
+ onStop: () => { var _a, _b; return (_b = (_a = callbacksRef.current).onStop) === null || _b === void 0 ? void 0 : _b.call(_a); },
908
+ onSeekBackward: () => { var _a, _b; return (_b = (_a = callbacksRef.current).onSeekBackward) === null || _b === void 0 ? void 0 : _b.call(_a); },
909
+ onSeekForward: () => { var _a, _b; return (_b = (_a = callbacksRef.current).onSeekForward) === null || _b === void 0 ? void 0 : _b.call(_a); },
910
+ onPreviousTrack: () => { var _a, _b; return (_b = (_a = callbacksRef.current).onPreviousTrack) === null || _b === void 0 ? void 0 : _b.call(_a); },
911
+ onNextTrack: () => { var _a, _b; return (_b = (_a = callbacksRef.current).onNextTrack) === null || _b === void 0 ? void 0 : _b.call(_a); },
828
912
  });
829
913
  audioRef.current = audio;
830
914
  setIsLoaded(true);
@@ -955,9 +1039,13 @@ const audioPlaybackManager = {
955
1039
  * @author: exode <hello@exode.ru>
956
1040
  */
957
1041
  const useVoiceMessagePlayer = (options) => {
958
- const { src, onEnded, waveformData, preloadedBlob, duration: externalDuration, } = options;
1042
+ const { src, onEnded, onPreviousTrack, onNextTrack, waveformData, preloadedBlob, duration: externalDuration, } = options;
959
1043
  const onEndedRef = React.useRef(onEnded);
1044
+ const onPreviousTrackRef = React.useRef(onPreviousTrack);
1045
+ const onNextTrackRef = React.useRef(onNextTrack);
960
1046
  onEndedRef.current = onEnded;
1047
+ onPreviousTrackRef.current = onPreviousTrack;
1048
+ onNextTrackRef.current = onNextTrack;
961
1049
  const [isPlaying, setIsPlaying] = React.useState(false);
962
1050
  const [currentTime, setCurrentTime] = React.useState(0);
963
1051
  const [duration, setDuration] = React.useState(externalDuration || 0);
@@ -991,6 +1079,14 @@ const useVoiceMessagePlayer = (options) => {
991
1079
  setCurrentTime(0);
992
1080
  (_a = onEndedRef.current) === null || _a === void 0 ? void 0 : _a.call(onEndedRef);
993
1081
  },
1082
+ onPlay: () => setIsPlaying(true),
1083
+ onPause: () => setIsPlaying(false),
1084
+ onStop: () => {
1085
+ setIsPlaying(false);
1086
+ setCurrentTime(0);
1087
+ },
1088
+ onPreviousTrack: () => { var _a; return (_a = onPreviousTrackRef.current) === null || _a === void 0 ? void 0 : _a.call(onPreviousTrackRef); },
1089
+ onNextTrack: () => { var _a; return (_a = onNextTrackRef.current) === null || _a === void 0 ? void 0 : _a.call(onNextTrackRef); },
994
1090
  },
995
1091
  });
996
1092
  /** Pre-fetch audio without playing (for viewport preloading) */
@@ -2138,6 +2234,13 @@ class VoicePlaybackChainClass {
2138
2234
  this.entries[idx + 1].play();
2139
2235
  }
2140
2236
  };
2237
+ /** Play the previous voice message before the given media ID */
2238
+ this.playPrevious = (currentId) => {
2239
+ const idx = this.entries.findIndex((e) => e.id === currentId);
2240
+ if (idx > 0) {
2241
+ this.entries[idx - 1].play();
2242
+ }
2243
+ };
2141
2244
  }
2142
2245
  }
2143
2246
  const VoicePlaybackChain = new VoicePlaybackChainClass();
@@ -2245,12 +2348,24 @@ const VoiceMessagePlayer = React.forwardRef((props, ref) => {
2245
2348
  VoicePlaybackChain.playNext(chainId);
2246
2349
  }
2247
2350
  }, [onEnded, chainId]);
2351
+ const handleNextTrack = React.useCallback(() => {
2352
+ if (chainId != null) {
2353
+ VoicePlaybackChain.playNext(chainId);
2354
+ }
2355
+ }, [chainId]);
2356
+ const handlePreviousTrack = React.useCallback(() => {
2357
+ if (chainId != null) {
2358
+ VoicePlaybackChain.playPrevious(chainId);
2359
+ }
2360
+ }, [chainId]);
2248
2361
  const { play, progress, isLoaded, isPlaying, isLoading, currentTime, waveformLevels, seekTo, seekToTime, prefetch, preload, formatTime, togglePlayback, duration: resolvedDuration, } = useVoiceMessagePlayer({
2249
2362
  src,
2250
2363
  duration,
2251
2364
  waveformData,
2252
2365
  preloadedBlob: props.preloadedBlob,
2253
2366
  onEnded: handleEnded,
2367
+ onNextTrack: handleNextTrack,
2368
+ onPreviousTrack: handlePreviousTrack,
2254
2369
  });
2255
2370
  React.useImperativeHandle(ref, () => ({ play, prefetch, seekToTime }), [
2256
2371
  play,