@exode-team/react-recorder 1.1.14 → 1.1.15

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 (81) hide show
  1. package/dist/components/AudioRecorder.js +50 -0
  2. package/dist/components/AudioRecorder.js.map +1 -0
  3. package/dist/components/AudioRecorder.messages.js +47 -0
  4. package/dist/components/AudioRecorder.messages.js.map +1 -0
  5. package/dist/components/AudioRecorder.styled.js +16 -0
  6. package/dist/components/AudioRecorder.styled.js.map +1 -0
  7. package/dist/components/IdleControl/IdleControl.js +12 -0
  8. package/dist/components/IdleControl/IdleControl.js.map +1 -0
  9. package/dist/components/IdleControl/IdleControl.styled.js +68 -0
  10. package/dist/components/IdleControl/IdleControl.styled.js.map +1 -0
  11. package/dist/components/RecordingShell/RecordingShell.js +80 -0
  12. package/dist/components/RecordingShell/RecordingShell.js.map +1 -0
  13. package/dist/components/RecordingShell/RecordingShell.styled.js +159 -0
  14. package/dist/components/RecordingShell/RecordingShell.styled.js.map +1 -0
  15. package/dist/components/VoiceMessagePlayer/VoiceMessagePlayer.js +151 -0
  16. package/dist/components/VoiceMessagePlayer/VoiceMessagePlayer.js.map +1 -0
  17. package/dist/components/VoiceMessagePlayer/VoiceMessagePlayer.styled.js +92 -0
  18. package/dist/components/VoiceMessagePlayer/VoiceMessagePlayer.styled.js.map +1 -0
  19. package/dist/components/VoiceRecorderInput/PreviewBars.js +52 -0
  20. package/dist/components/VoiceRecorderInput/PreviewBars.js.map +1 -0
  21. package/dist/components/VoiceRecorderInput/TimelineBars.js +31 -0
  22. package/dist/components/VoiceRecorderInput/TimelineBars.js.map +1 -0
  23. package/dist/components/VoiceRecorderInput/VoiceRecorderInput.js +51 -0
  24. package/dist/components/VoiceRecorderInput/VoiceRecorderInput.js.map +1 -0
  25. package/dist/components/VoiceRecorderInput/VoiceRecorderInput.styled.js +175 -0
  26. package/dist/components/VoiceRecorderInput/VoiceRecorderInput.styled.js.map +1 -0
  27. package/dist/components/WaveformBars/WaveformBars.js +29 -0
  28. package/dist/components/WaveformBars/WaveformBars.js.map +1 -0
  29. package/dist/components/WaveformBars/WaveformBars.styled.js +20 -0
  30. package/dist/components/WaveformBars/WaveformBars.styled.js.map +1 -0
  31. package/dist/constants.js +17 -0
  32. package/dist/constants.js.map +1 -0
  33. package/dist/hooks/useAudioBlobLoader.js +175 -0
  34. package/dist/hooks/useAudioBlobLoader.js.map +1 -0
  35. package/dist/hooks/useAudioPlayback.js +119 -0
  36. package/dist/hooks/useAudioPlayback.js.map +1 -0
  37. package/dist/hooks/useAudioRecorderHandlers.js +137 -0
  38. package/dist/hooks/useAudioRecorderHandlers.js.map +1 -0
  39. package/dist/hooks/useMediaRecorder.js +127 -0
  40. package/dist/hooks/useMediaRecorder.js.map +1 -0
  41. package/dist/hooks/useRecorderVisualization.js +94 -0
  42. package/dist/hooks/useRecorderVisualization.js.map +1 -0
  43. package/dist/hooks/useRecordingSender.js +53 -0
  44. package/dist/hooks/useRecordingSender.js.map +1 -0
  45. package/dist/hooks/useRecordingTiming.js +93 -0
  46. package/dist/hooks/useRecordingTiming.js.map +1 -0
  47. package/dist/hooks/useVoiceMessagePlayer.js +177 -0
  48. package/dist/hooks/useVoiceMessagePlayer.js.map +1 -0
  49. package/dist/hooks/useVoiceRecorder.js +153 -0
  50. package/dist/hooks/useVoiceRecorder.js.map +1 -0
  51. package/dist/hooks/useVoiceRecorderInput.js +163 -0
  52. package/dist/hooks/useVoiceRecorderInput.js.map +1 -0
  53. package/dist/index.js +13 -2659
  54. package/dist/index.js.map +1 -1
  55. package/dist/platform/animation-frame-adapter.js +14 -0
  56. package/dist/platform/animation-frame-adapter.js.map +1 -0
  57. package/dist/platform/audio-analyzer-adapter.js +74 -0
  58. package/dist/platform/audio-analyzer-adapter.js.map +1 -0
  59. package/dist/platform/audio-blob-fetcher.js +19 -0
  60. package/dist/platform/audio-blob-fetcher.js.map +1 -0
  61. package/dist/platform/audio-playback-adapter.js +114 -0
  62. package/dist/platform/audio-playback-adapter.js.map +1 -0
  63. package/dist/platform/audio-playback-manager.js +26 -0
  64. package/dist/platform/audio-playback-manager.js.map +1 -0
  65. package/dist/platform/media-recorder-adapter.js +23 -0
  66. package/dist/platform/media-recorder-adapter.js.map +1 -0
  67. package/dist/platform/microphone.js +24 -0
  68. package/dist/platform/microphone.js.map +1 -0
  69. package/dist/platform/mpeg-frame.js +60 -0
  70. package/dist/platform/mpeg-frame.js.map +1 -0
  71. package/dist/utils/format.js +13 -0
  72. package/dist/utils/format.js.map +1 -0
  73. package/dist/utils/math.js +16 -0
  74. package/dist/utils/math.js.map +1 -0
  75. package/dist/utils/pseudo-waveform.js +40 -0
  76. package/dist/utils/pseudo-waveform.js.map +1 -0
  77. package/dist/utils/voice-blob-cache.js +35 -0
  78. package/dist/utils/voice-blob-cache.js.map +1 -0
  79. package/dist/utils/voice-playback-chain.js +42 -0
  80. package/dist/utils/voice-playback-chain.js.map +1 -0
  81. package/package.json +2 -1
@@ -0,0 +1,153 @@
1
+ import { useState, useCallback, useMemo, useEffect } from 'react';
2
+ import { formatTime } from '../utils/format.js';
3
+ import { useMediaRecorder } from './useMediaRecorder.js';
4
+ import { useAudioPlayback } from './useAudioPlayback.js';
5
+ import { useRecordingTiming } from './useRecordingTiming.js';
6
+ import { useRecorderVisualization } from './useRecorderVisualization.js';
7
+ import { useRecordingSender } from './useRecordingSender.js';
8
+
9
+ /**
10
+ * useVoiceRecorder
11
+ *
12
+ * @author: exode <hello@exode.ru>
13
+ */
14
+ function useVoiceRecorder(props) {
15
+ const { uploadId, uploader, onUploaded, onRecordingStop, onRecordingStart, onRecordingError, onRecordingDelete, onPermissionDenied, fileName = 'record.webm', mimeType = 'audio/webm', autoResetAfterUpload = true, } = props;
16
+ const [audioBlob, setAudioBlob] = useState(null);
17
+ const { audioURL, isPlaying, mountBlob, playbackTime, stopPlayback, resetPlayback, togglePlayback: _togglePlayback, playbackDuration, } = useAudioPlayback();
18
+ const { audioLevels, previewLevels, resetAudioLevels, stopVisualization, resetPreviewLevels, resumeVisualization, cleanupVisualization, generatePreviewLevels, initializeVisualization, } = useRecorderVisualization();
19
+ const { lastDuration, getElapsedMs, recordingTime, pauseSession, resetSession, resumeSession, startNewSession, } = useRecordingTiming();
20
+ const resetCurrentRecording = useCallback((opts) => {
21
+ var _a;
22
+ setAudioBlob(null);
23
+ resetPlayback();
24
+ if ((_a = opts === null || opts === void 0 ? void 0 : opts.resetPreview) !== null && _a !== void 0 ? _a : true) {
25
+ resetPreviewLevels();
26
+ }
27
+ if (opts === null || opts === void 0 ? void 0 : opts.resetLevels) {
28
+ resetAudioLevels();
29
+ }
30
+ if (opts === null || opts === void 0 ? void 0 : opts.notifyDelete) {
31
+ onRecordingDelete === null || onRecordingDelete === void 0 ? void 0 : onRecordingDelete();
32
+ }
33
+ }, [onRecordingDelete, resetAudioLevels, resetPlayback, resetPreviewLevels]);
34
+ const deleteRecording = useCallback(() => {
35
+ resetCurrentRecording({
36
+ resetLevels: true,
37
+ notifyDelete: true,
38
+ });
39
+ resetSession();
40
+ }, [resetCurrentRecording, resetSession]);
41
+ const { sendRecording } = useRecordingSender({
42
+ audioBlob,
43
+ fileName,
44
+ mimeType,
45
+ uploadId,
46
+ uploader,
47
+ onUploaded,
48
+ deleteRecording,
49
+ onRecordingError,
50
+ autoResetAfterUpload,
51
+ getDurationMs: getElapsedMs,
52
+ });
53
+ const mountRecordingBlob = useCallback((blob) => {
54
+ setAudioBlob(blob);
55
+ mountBlob(blob);
56
+ }, [mountBlob]);
57
+ const recorderCallbacks = useMemo(() => ({
58
+ onDataChunk: () => { },
59
+ onNeedsPreview: (chunks) => {
60
+ const snapshot = new Blob(chunks, { type: mimeType });
61
+ mountRecordingBlob(snapshot);
62
+ void generatePreviewLevels(snapshot);
63
+ },
64
+ onStopped: (chunks, discarded) => {
65
+ pauseSession();
66
+ stopVisualization({ resetLevels: false });
67
+ if (discarded) {
68
+ resetCurrentRecording({ notifyDelete: true });
69
+ resetSession();
70
+ return;
71
+ }
72
+ const blob = new Blob(chunks, { type: mimeType });
73
+ onRecordingStop === null || onRecordingStop === void 0 ? void 0 : onRecordingStop(blob);
74
+ mountRecordingBlob(blob);
75
+ void generatePreviewLevels(blob);
76
+ resetSession();
77
+ },
78
+ onPaused: () => {
79
+ pauseSession();
80
+ stopVisualization({ resetLevels: false, closeContext: false });
81
+ },
82
+ onResumed: () => {
83
+ resumeSession();
84
+ resumeVisualization();
85
+ },
86
+ onStreamAcquired: (stream) => {
87
+ initializeVisualization(stream);
88
+ },
89
+ onPermissionDenied: (error) => {
90
+ onPermissionDenied === null || onPermissionDenied === void 0 ? void 0 : onPermissionDenied(error);
91
+ },
92
+ onError: (error) => {
93
+ onRecordingError === null || onRecordingError === void 0 ? void 0 : onRecordingError(error);
94
+ },
95
+ }), [
96
+ generatePreviewLevels,
97
+ initializeVisualization,
98
+ mimeType,
99
+ mountRecordingBlob,
100
+ onPermissionDenied,
101
+ onRecordingError,
102
+ onRecordingStop,
103
+ pauseSession,
104
+ resetCurrentRecording,
105
+ resetSession,
106
+ resumeSession,
107
+ resumeVisualization,
108
+ stopVisualization,
109
+ ]);
110
+ const { isPaused, isRecording, togglePause, cleanupStream, stopRecording, cancelRecording, startRecording: _startRecording, } = useMediaRecorder(recorderCallbacks);
111
+ const startRecording = useCallback(async () => {
112
+ resetCurrentRecording({ resetLevels: true });
113
+ await _startRecording(mimeType);
114
+ onRecordingStart === null || onRecordingStart === void 0 ? void 0 : onRecordingStart();
115
+ startNewSession();
116
+ }, [_startRecording, mimeType, onRecordingStart, resetCurrentRecording, startNewSession]);
117
+ useEffect(() => {
118
+ return () => {
119
+ cleanupVisualization();
120
+ cleanupStream();
121
+ resetPlayback();
122
+ resetSession();
123
+ };
124
+ }, [cleanupVisualization, cleanupStream, resetPlayback, resetSession]);
125
+ const togglePlayback = useCallback(() => {
126
+ _togglePlayback(onRecordingError);
127
+ }, [_togglePlayback, onRecordingError]);
128
+ return {
129
+ audioURL,
130
+ isPaused,
131
+ isPlaying,
132
+ isRecording,
133
+ audioLevels,
134
+ lastDuration,
135
+ playbackTime,
136
+ recordingTime,
137
+ previewLevels,
138
+ playbackDuration,
139
+ formatTime,
140
+ stopPlayback,
141
+ sendRecording,
142
+ stopRecording,
143
+ togglePlayback,
144
+ startRecording,
145
+ cancelRecording,
146
+ deleteRecording,
147
+ toggleRecordingPause: togglePause,
148
+ hasRecording: Boolean(audioURL),
149
+ };
150
+ }
151
+
152
+ export { useVoiceRecorder };
153
+ //# sourceMappingURL=useVoiceRecorder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useVoiceRecorder.js","sources":["../../src/hooks/useVoiceRecorder.ts"],"sourcesContent":["/**\n * useVoiceRecorder\n *\n * @author: exode <hello@exode.ru>\n */\n\nimport { useCallback, useEffect, useMemo, useState } from 'react';\n\nimport { formatTime } from '../utils';\nimport { useMediaRecorder } from './useMediaRecorder';\nimport { useAudioPlayback } from './useAudioPlayback';\nimport { useRecordingTiming } from './useRecordingTiming';\nimport { useRecorderVisualization } from './useRecorderVisualization';\nimport { useRecordingSender } from './useRecordingSender';\n\nimport type { UseVoiceRecorderOptions, UseVoiceRecorderResult } from '../types';\n\n\nfunction useVoiceRecorder<TFile = unknown>(props: UseVoiceRecorderOptions<TFile>): UseVoiceRecorderResult {\n\n const {\n uploadId,\n uploader,\n onUploaded,\n onRecordingStop,\n onRecordingStart,\n onRecordingError,\n onRecordingDelete,\n onPermissionDenied,\n fileName = 'record.webm',\n mimeType = 'audio/webm',\n autoResetAfterUpload = true,\n } = props;\n\n const [ audioBlob, setAudioBlob ] = useState<Blob | null>(null);\n\n const {\n audioURL,\n isPlaying,\n mountBlob,\n playbackTime,\n stopPlayback,\n resetPlayback,\n togglePlayback: _togglePlayback,\n playbackDuration,\n } = useAudioPlayback();\n\n const {\n audioLevels,\n previewLevels,\n resetAudioLevels,\n stopVisualization,\n resetPreviewLevels,\n resumeVisualization,\n cleanupVisualization,\n generatePreviewLevels,\n initializeVisualization,\n } = useRecorderVisualization();\n\n const {\n lastDuration,\n getElapsedMs,\n recordingTime,\n pauseSession,\n resetSession,\n resumeSession,\n startNewSession,\n } = useRecordingTiming();\n\n const resetCurrentRecording = useCallback(\n (opts?: { resetPreview?: boolean; resetLevels?: boolean; notifyDelete?: boolean }) => {\n setAudioBlob(null);\n resetPlayback();\n if (opts?.resetPreview ?? true) {\n resetPreviewLevels();\n }\n if (opts?.resetLevels) {\n resetAudioLevels();\n }\n if (opts?.notifyDelete) {\n onRecordingDelete?.();\n }\n },\n [ onRecordingDelete, resetAudioLevels, resetPlayback, resetPreviewLevels ],\n );\n\n const deleteRecording = useCallback(() => {\n resetCurrentRecording({\n resetLevels: true,\n notifyDelete: true,\n });\n\n resetSession();\n }, [ resetCurrentRecording, resetSession ]);\n\n const { sendRecording } = useRecordingSender<TFile>({\n audioBlob,\n fileName,\n mimeType,\n uploadId,\n uploader,\n onUploaded,\n deleteRecording,\n onRecordingError,\n autoResetAfterUpload,\n getDurationMs: getElapsedMs,\n });\n\n const mountRecordingBlob = useCallback((blob: Blob) => {\n setAudioBlob(blob);\n mountBlob(blob);\n }, [ mountBlob ]);\n\n const recorderCallbacks = useMemo(() => ({\n onDataChunk: () => {},\n onNeedsPreview: (chunks: Blob[]) => {\n const snapshot = new Blob(chunks, { type: mimeType });\n mountRecordingBlob(snapshot);\n void generatePreviewLevels(snapshot);\n },\n onStopped: (chunks: Blob[], discarded: boolean) => {\n pauseSession();\n stopVisualization({ resetLevels: false });\n\n if (discarded) {\n resetCurrentRecording({ notifyDelete: true });\n resetSession();\n return;\n }\n\n const blob = new Blob(chunks, { type: mimeType });\n onRecordingStop?.(blob);\n mountRecordingBlob(blob);\n void generatePreviewLevels(blob);\n resetSession();\n },\n onPaused: () => {\n pauseSession();\n stopVisualization({ resetLevels: false, closeContext: false });\n },\n onResumed: () => {\n resumeSession();\n resumeVisualization();\n },\n onStreamAcquired: (stream: MediaStream) => {\n initializeVisualization(stream);\n },\n onPermissionDenied: (error: unknown) => {\n onPermissionDenied?.(error);\n },\n onError: (error: unknown) => {\n onRecordingError?.(error);\n },\n }), [\n generatePreviewLevels,\n initializeVisualization,\n mimeType,\n mountRecordingBlob,\n onPermissionDenied,\n onRecordingError,\n onRecordingStop,\n pauseSession,\n resetCurrentRecording,\n resetSession,\n resumeSession,\n resumeVisualization,\n stopVisualization,\n ]);\n\n const {\n isPaused,\n isRecording,\n togglePause,\n cleanupStream,\n stopRecording,\n cancelRecording,\n startRecording: _startRecording,\n } = useMediaRecorder(recorderCallbacks);\n\n const startRecording = useCallback(async () => {\n resetCurrentRecording({ resetLevels: true });\n await _startRecording(mimeType);\n onRecordingStart?.();\n startNewSession();\n }, [ _startRecording, mimeType, onRecordingStart, resetCurrentRecording, startNewSession ]);\n\n useEffect(() => {\n return () => {\n cleanupVisualization();\n cleanupStream();\n resetPlayback();\n resetSession();\n };\n }, [ cleanupVisualization, cleanupStream, resetPlayback, resetSession ]);\n\n const togglePlayback = useCallback(() => {\n _togglePlayback(onRecordingError);\n }, [ _togglePlayback, onRecordingError ]);\n\n return {\n audioURL,\n isPaused,\n isPlaying,\n isRecording,\n audioLevels,\n lastDuration,\n playbackTime,\n recordingTime,\n previewLevels,\n playbackDuration,\n formatTime,\n stopPlayback,\n sendRecording,\n stopRecording,\n togglePlayback,\n startRecording,\n cancelRecording,\n deleteRecording,\n toggleRecordingPause: togglePause,\n hasRecording: Boolean(audioURL),\n };\n}\n\n\nexport { useVoiceRecorder };\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,163 @@
1
+ import { useState, useRef, useCallback, useEffect } from 'react';
2
+ import { useVoiceRecorder } from './useVoiceRecorder.js';
3
+ import { TIMELINE_SAMPLE_INTERVAL } from '../constants.js';
4
+
5
+ /**
6
+ * useVoiceRecorderInput — orchestrates recording, sending and timeline for VoiceRecorderInput
7
+ *
8
+ * @author: exode <hello@exode.ru>
9
+ */
10
+ const useVoiceRecorderInput = (options) => {
11
+ const { onSend, onError, onCancel, uploader, onUploaded, onPermissionDenied, autoStart = true, mimeType = 'audio/webm', fileName = 'voice-message.webm', } = options;
12
+ const [isSending, setIsSending] = useState(false);
13
+ const [timelineLevels, setTimelineLevels] = useState([]);
14
+ const audioBlobRef = useRef(null);
15
+ const audioLevelsRef = useRef([]);
16
+ const lastDurationRef = useRef(0);
17
+ const shouldSendAfterStopRef = useRef(false);
18
+ const doSendRef = useRef();
19
+ const autoStartedRef = useRef(false);
20
+ const [uploadId] = useState(() => typeof crypto !== 'undefined' && 'randomUUID' in crypto
21
+ ? crypto.randomUUID()
22
+ : `upload-${Date.now()}`);
23
+ const handleRecordingStop = useCallback((blob) => {
24
+ var _a;
25
+ audioBlobRef.current = blob;
26
+ if (shouldSendAfterStopRef.current) {
27
+ shouldSendAfterStopRef.current = false;
28
+ void ((_a = doSendRef.current) === null || _a === void 0 ? void 0 : _a.call(doSendRef));
29
+ }
30
+ }, []);
31
+ const recorder = useVoiceRecorder({
32
+ fileName,
33
+ mimeType,
34
+ uploadId,
35
+ onPermissionDenied,
36
+ onRecordingStop: handleRecordingStop,
37
+ onRecordingError: onError,
38
+ uploader: onSend ? undefined : uploader,
39
+ onUploaded: onSend ? (() => { }) : (onUploaded !== null && onUploaded !== void 0 ? onUploaded : (() => { })),
40
+ });
41
+ const { audioLevels, isPaused, isRecording, recordingTime, hasRecording, stopPlayback, sendRecording, stopRecording, startRecording, cancelRecording, deleteRecording, toggleRecordingPause, } = recorder;
42
+ audioLevelsRef.current = audioLevels;
43
+ useEffect(() => {
44
+ if (recordingTime > 0) {
45
+ lastDurationRef.current = recordingTime;
46
+ }
47
+ }, [recordingTime]);
48
+ const isPreview = isPaused || (!isRecording && hasRecording);
49
+ const isActiveRecording = isRecording && !isPaused;
50
+ useEffect(() => {
51
+ if (!isActiveRecording) {
52
+ return;
53
+ }
54
+ const interval = setInterval(() => {
55
+ const levels = audioLevelsRef.current;
56
+ const peak = levels.length > 0 ? Math.max(...levels) : 0;
57
+ setTimelineLevels((prev) => [...prev, peak]);
58
+ }, TIMELINE_SAMPLE_INTERVAL);
59
+ return () => clearInterval(interval);
60
+ }, [isActiveRecording]);
61
+ useEffect(() => {
62
+ if (isRecording && !isPaused) {
63
+ setTimelineLevels([]);
64
+ }
65
+ }, [isRecording]);
66
+ const handleResume = useCallback(() => {
67
+ stopPlayback();
68
+ toggleRecordingPause();
69
+ }, [
70
+ stopPlayback,
71
+ toggleRecordingPause,
72
+ ]);
73
+ const handlePause = toggleRecordingPause;
74
+ const handleCancel = useCallback(() => {
75
+ shouldSendAfterStopRef.current = false;
76
+ if (isRecording) {
77
+ cancelRecording();
78
+ }
79
+ else if (hasRecording) {
80
+ stopPlayback();
81
+ deleteRecording();
82
+ }
83
+ onCancel === null || onCancel === void 0 ? void 0 : onCancel();
84
+ }, [
85
+ onCancel,
86
+ isRecording,
87
+ hasRecording,
88
+ stopPlayback,
89
+ cancelRecording,
90
+ deleteRecording,
91
+ ]);
92
+ const doSend = useCallback(async () => {
93
+ setIsSending(true);
94
+ try {
95
+ stopPlayback();
96
+ if (onSend) {
97
+ if (!audioBlobRef.current) {
98
+ return;
99
+ }
100
+ const file = new File([audioBlobRef.current], fileName, { type: audioBlobRef.current.type || mimeType });
101
+ await onSend(file, recordingTime || lastDurationRef.current || 0);
102
+ deleteRecording();
103
+ }
104
+ else {
105
+ await sendRecording();
106
+ }
107
+ }
108
+ catch (error) {
109
+ onError === null || onError === void 0 ? void 0 : onError(error);
110
+ }
111
+ finally {
112
+ setIsSending(false);
113
+ }
114
+ }, [
115
+ onSend,
116
+ onError,
117
+ fileName,
118
+ mimeType,
119
+ recordingTime,
120
+ sendRecording,
121
+ stopPlayback,
122
+ deleteRecording,
123
+ ]);
124
+ doSendRef.current = doSend;
125
+ const handleSendRequest = useCallback(async () => {
126
+ if (isSending) {
127
+ return;
128
+ }
129
+ if (isRecording) {
130
+ shouldSendAfterStopRef.current = true;
131
+ stopRecording();
132
+ return;
133
+ }
134
+ await doSend();
135
+ }, [
136
+ doSend,
137
+ isRecording,
138
+ isSending,
139
+ stopRecording,
140
+ ]);
141
+ useEffect(() => {
142
+ if (autoStart && !autoStartedRef.current) {
143
+ autoStartedRef.current = true;
144
+ void startRecording().catch((error) => {
145
+ onPermissionDenied === null || onPermissionDenied === void 0 ? void 0 : onPermissionDenied(error);
146
+ });
147
+ }
148
+ }, [autoStart]);
149
+ return {
150
+ recorder,
151
+ isSending,
152
+ isPreview,
153
+ timelineLevels,
154
+ lastDurationRef,
155
+ handleResume,
156
+ handlePause,
157
+ handleCancel,
158
+ handleSendRequest,
159
+ };
160
+ };
161
+
162
+ export { useVoiceRecorderInput };
163
+ //# sourceMappingURL=useVoiceRecorderInput.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useVoiceRecorderInput.js","sources":["../../src/hooks/useVoiceRecorderInput.ts"],"sourcesContent":["/**\n * useVoiceRecorderInput — orchestrates recording, sending and timeline for VoiceRecorderInput\n *\n * @author: exode <hello@exode.ru>\n */\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\n\nimport { useVoiceRecorder } from './useVoiceRecorder';\nimport { TIMELINE_SAMPLE_INTERVAL } from '../constants';\nimport type { UploadStrategy } from '../types';\n\n\ninterface UseVoiceRecorderInputOptions {\n uploader?: UploadStrategy<unknown>;\n onUploaded?: (file: unknown, duration: number) => void | Promise<void>;\n onSend?: (file: File, duration: number) => void | Promise<void>;\n onError?: (error: unknown) => void;\n onCancel?: () => void;\n onPermissionDenied?: (error: unknown) => void;\n autoStart?: boolean;\n mimeType?: string;\n fileName?: string;\n}\n\n\nconst useVoiceRecorderInput = (options: UseVoiceRecorderInputOptions) => {\n\n const {\n onSend,\n onError,\n onCancel,\n uploader,\n onUploaded,\n onPermissionDenied,\n autoStart = true,\n mimeType = 'audio/webm',\n fileName = 'voice-message.webm',\n } = options;\n\n const [ isSending, setIsSending ] = useState(false);\n const [ timelineLevels, setTimelineLevels ] = useState<number[]>([]);\n\n const audioBlobRef = useRef<Blob | null>(null);\n const audioLevelsRef = useRef<number[]>([]);\n const lastDurationRef = useRef(0);\n const shouldSendAfterStopRef = useRef(false);\n const doSendRef = useRef<() => Promise<void>>();\n const autoStartedRef = useRef(false);\n\n const [ uploadId ] = useState(\n () => typeof crypto !== 'undefined' && 'randomUUID' in crypto\n ? crypto.randomUUID()\n : `upload-${Date.now()}`,\n );\n\n const handleRecordingStop = useCallback((blob: Blob) => {\n audioBlobRef.current = blob;\n if (shouldSendAfterStopRef.current) {\n shouldSendAfterStopRef.current = false;\n void doSendRef.current?.();\n }\n }, []);\n\n const recorder = useVoiceRecorder({\n fileName,\n mimeType,\n uploadId,\n onPermissionDenied,\n onRecordingStop: handleRecordingStop,\n onRecordingError: onError,\n uploader: onSend ? undefined : uploader,\n onUploaded: onSend ? (() => {}) : (onUploaded ?? (() => {})),\n });\n\n const {\n audioLevels,\n isPaused,\n isRecording,\n recordingTime,\n hasRecording,\n stopPlayback,\n sendRecording,\n stopRecording,\n startRecording,\n cancelRecording,\n deleteRecording,\n toggleRecordingPause,\n } = recorder;\n\n audioLevelsRef.current = audioLevels;\n\n useEffect(() => {\n if (recordingTime > 0) {\n lastDurationRef.current = recordingTime;\n }\n }, [ recordingTime ]);\n\n const isPreview = isPaused || (!isRecording && hasRecording);\n const isActiveRecording = isRecording && !isPaused;\n\n useEffect(() => {\n if (!isActiveRecording) {\n return;\n }\n const interval = setInterval(() => {\n const levels = audioLevelsRef.current;\n const peak = levels.length > 0 ? Math.max(...levels) : 0;\n setTimelineLevels((prev) => [ ...prev, peak ]);\n }, TIMELINE_SAMPLE_INTERVAL);\n return () => clearInterval(interval);\n }, [ isActiveRecording ]);\n\n useEffect(() => {\n if (isRecording && !isPaused) {\n setTimelineLevels([]);\n }\n }, [ isRecording ]);\n\n const handleResume = useCallback(() => {\n stopPlayback();\n toggleRecordingPause();\n }, [\n stopPlayback,\n toggleRecordingPause,\n ]);\n\n const handlePause = toggleRecordingPause;\n\n const handleCancel = useCallback(() => {\n shouldSendAfterStopRef.current = false;\n\n if (isRecording) {\n cancelRecording();\n } else if (hasRecording) {\n stopPlayback();\n deleteRecording();\n }\n onCancel?.();\n }, [\n onCancel,\n isRecording,\n hasRecording,\n stopPlayback,\n cancelRecording,\n deleteRecording,\n ]);\n\n const doSend = useCallback(async () => {\n setIsSending(true);\n try {\n stopPlayback();\n if (onSend) {\n if (!audioBlobRef.current) {\n return;\n }\n\n const file = new File(\n [ audioBlobRef.current ],\n fileName,\n { type: audioBlobRef.current.type || mimeType },\n );\n\n await onSend(\n file,\n recordingTime || lastDurationRef.current || 0,\n );\n\n deleteRecording();\n } else {\n await sendRecording();\n }\n } catch (error) {\n onError?.(error);\n } finally {\n setIsSending(false);\n }\n }, [\n onSend,\n onError,\n fileName,\n mimeType,\n recordingTime,\n sendRecording,\n stopPlayback,\n deleteRecording,\n ]);\n\n doSendRef.current = doSend;\n\n const handleSendRequest = useCallback(async () => {\n if (isSending) {\n return;\n }\n\n if (isRecording) {\n shouldSendAfterStopRef.current = true;\n\n stopRecording();\n\n return;\n }\n\n await doSend();\n }, [\n doSend,\n isRecording,\n isSending,\n stopRecording,\n ]);\n\n useEffect(() => {\n if (autoStart && !autoStartedRef.current) {\n autoStartedRef.current = true;\n void startRecording().catch((error) => {\n onPermissionDenied?.(error);\n });\n }\n }, [ autoStart ]);\n\n return {\n recorder,\n isSending,\n isPreview,\n timelineLevels,\n lastDurationRef,\n handleResume,\n handlePause,\n handleCancel,\n handleSendRequest,\n };\n};\n\n\nexport { useVoiceRecorderInput };\n"],"names":[],"mappings":"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"}