@exode-team/react-recorder 1.1.7 → 1.1.8
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/dist/components/AudioRecorder.d.ts +1 -1
- package/dist/components/AudioRecorder.d.ts.map +1 -1
- package/dist/components/RecordingShell/RecordingShell.d.ts +1 -0
- package/dist/components/RecordingShell/RecordingShell.d.ts.map +1 -1
- package/dist/components/RecordingShell/RecordingShell.styled.d.ts +0 -2
- package/dist/components/RecordingShell/RecordingShell.styled.d.ts.map +1 -1
- package/dist/components/WaveformBars/WaveformBars.d.ts +4 -0
- package/dist/components/WaveformBars/WaveformBars.d.ts.map +1 -1
- package/dist/components/WaveformBars/WaveformBars.styled.d.ts +3 -0
- package/dist/components/WaveformBars/WaveformBars.styled.d.ts.map +1 -0
- package/dist/hooks/useAudioRecorderHandlers.d.ts.map +1 -1
- package/dist/hooks/useMediaRecorder.d.ts.map +1 -1
- package/dist/hooks/useRecordingSender.d.ts +2 -1
- package/dist/hooks/useRecordingSender.d.ts.map +1 -1
- package/dist/hooks/useRecordingTiming.d.ts +2 -0
- package/dist/hooks/useRecordingTiming.d.ts.map +1 -1
- package/dist/hooks/useVoiceRecorder.d.ts.map +1 -1
- package/dist/hooks/useVoiceRecorderInput.d.ts +1 -1
- package/dist/hooks/useVoiceRecorderInput.d.ts.map +1 -1
- package/dist/index.cjs +111 -50
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +111 -50
- package/dist/index.js.map +1 -1
- package/dist/types/recorder.d.ts +8 -1
- package/dist/types/recorder.d.ts.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -274,6 +274,9 @@ const useMediaRecorder = (callbacks) => {
|
|
|
274
274
|
if (!mediaRecorderRef.current || !isRecording) {
|
|
275
275
|
return;
|
|
276
276
|
}
|
|
277
|
+
if (mediaRecorderRef.current.state === 'paused') {
|
|
278
|
+
mediaRecorderRef.current.resume();
|
|
279
|
+
}
|
|
277
280
|
mediaRecorderRef.current.stop();
|
|
278
281
|
}, [isRecording]);
|
|
279
282
|
const cancelRecording = useCallback(() => {
|
|
@@ -432,6 +435,7 @@ const useAudioPlayback = () => {
|
|
|
432
435
|
* @author: exode <hello@exode.ru>
|
|
433
436
|
*/
|
|
434
437
|
const useRecordingTiming = () => {
|
|
438
|
+
const [lastDuration, setLastDuration] = useState(0);
|
|
435
439
|
const [recordingTime, setRecordingTime] = useState(0);
|
|
436
440
|
const timerRef = useRef(null);
|
|
437
441
|
const recordingStartRef = useRef(null);
|
|
@@ -459,6 +463,7 @@ const useRecordingTiming = () => {
|
|
|
459
463
|
const startNewSession = useCallback(() => {
|
|
460
464
|
recordingAccumulatedRef.current = 0;
|
|
461
465
|
setRecordingTime(0);
|
|
466
|
+
setLastDuration(0);
|
|
462
467
|
recordingStartRef.current = performance.now();
|
|
463
468
|
startTicker();
|
|
464
469
|
}, [startTicker]);
|
|
@@ -476,7 +481,21 @@ const useRecordingTiming = () => {
|
|
|
476
481
|
setRecordingTime(roundTo(recordingAccumulatedRef.current));
|
|
477
482
|
stopTicker();
|
|
478
483
|
}, [stopTicker]);
|
|
484
|
+
const getElapsedMs = useCallback(() => {
|
|
485
|
+
const accumulated = recordingAccumulatedRef.current;
|
|
486
|
+
const elapsed = recordingStartRef.current !== null
|
|
487
|
+
? (performance.now() - recordingStartRef.current) / 1000 + accumulated
|
|
488
|
+
: accumulated;
|
|
489
|
+
return Math.round(elapsed * 1000);
|
|
490
|
+
}, []);
|
|
479
491
|
const resetSession = useCallback(() => {
|
|
492
|
+
const accumulated = recordingAccumulatedRef.current;
|
|
493
|
+
const elapsed = recordingStartRef.current !== null
|
|
494
|
+
? (performance.now() - recordingStartRef.current) / 1000 + accumulated
|
|
495
|
+
: accumulated;
|
|
496
|
+
if (elapsed > 0) {
|
|
497
|
+
setLastDuration(roundTo(elapsed));
|
|
498
|
+
}
|
|
480
499
|
stopTicker();
|
|
481
500
|
recordingStartRef.current = null;
|
|
482
501
|
recordingAccumulatedRef.current = 0;
|
|
@@ -488,6 +507,8 @@ const useRecordingTiming = () => {
|
|
|
488
507
|
};
|
|
489
508
|
}, [stopTicker]);
|
|
490
509
|
return {
|
|
510
|
+
lastDuration,
|
|
511
|
+
getElapsedMs,
|
|
491
512
|
pauseSession,
|
|
492
513
|
resetSession,
|
|
493
514
|
recordingTime,
|
|
@@ -589,7 +610,7 @@ const useRecorderVisualization = () => {
|
|
|
589
610
|
* @author: exode <hello@exode.ru>
|
|
590
611
|
*/
|
|
591
612
|
const useRecordingSender = (options) => {
|
|
592
|
-
const { uploader, fileName, mimeType, uploadId, audioBlob, onUploaded, onRecordingError, deleteRecording, autoResetAfterUpload, } = options;
|
|
613
|
+
const { uploader, fileName, mimeType, uploadId, audioBlob, onUploaded, getDurationMs, onRecordingError, deleteRecording, autoResetAfterUpload, } = options;
|
|
593
614
|
const sendRecording = useCallback(async () => {
|
|
594
615
|
if (!audioBlob) {
|
|
595
616
|
const error = new Error('Voice recording is not available');
|
|
@@ -602,11 +623,13 @@ const useRecordingSender = (options) => {
|
|
|
602
623
|
throw error;
|
|
603
624
|
}
|
|
604
625
|
try {
|
|
626
|
+
const durationMs = getDurationMs();
|
|
627
|
+
const durationSec = durationMs / 1000;
|
|
605
628
|
const file = new File([audioBlob], fileName, {
|
|
606
629
|
type: audioBlob.type || mimeType,
|
|
607
630
|
});
|
|
608
631
|
const uploaded = await uploader(file, { uploadId });
|
|
609
|
-
await onUploaded(uploaded);
|
|
632
|
+
await onUploaded(uploaded, durationSec);
|
|
610
633
|
if (autoResetAfterUpload) {
|
|
611
634
|
deleteRecording();
|
|
612
635
|
}
|
|
@@ -622,8 +645,9 @@ const useRecordingSender = (options) => {
|
|
|
622
645
|
audioBlob,
|
|
623
646
|
mimeType,
|
|
624
647
|
onUploaded,
|
|
625
|
-
|
|
648
|
+
getDurationMs,
|
|
626
649
|
deleteRecording,
|
|
650
|
+
onRecordingError,
|
|
627
651
|
autoResetAfterUpload,
|
|
628
652
|
]);
|
|
629
653
|
return { sendRecording };
|
|
@@ -639,7 +663,7 @@ function useVoiceRecorder(props) {
|
|
|
639
663
|
const [audioBlob, setAudioBlob] = useState(null);
|
|
640
664
|
const { audioURL, isPlaying, mountBlob, playbackTime, stopPlayback, resetPlayback, togglePlayback: _togglePlayback, playbackDuration, } = useAudioPlayback();
|
|
641
665
|
const { audioLevels, previewLevels, resetAudioLevels, stopVisualization, resetPreviewLevels, resumeVisualization, cleanupVisualization, generatePreviewLevels, initializeVisualization, } = useRecorderVisualization();
|
|
642
|
-
const { recordingTime, pauseSession, resetSession, resumeSession, startNewSession, } = useRecordingTiming();
|
|
666
|
+
const { lastDuration, getElapsedMs, recordingTime, pauseSession, resetSession, resumeSession, startNewSession, } = useRecordingTiming();
|
|
643
667
|
const resetCurrentRecording = useCallback((opts) => {
|
|
644
668
|
var _a;
|
|
645
669
|
setAudioBlob(null);
|
|
@@ -667,10 +691,11 @@ function useVoiceRecorder(props) {
|
|
|
667
691
|
mimeType,
|
|
668
692
|
uploadId,
|
|
669
693
|
uploader,
|
|
670
|
-
autoResetAfterUpload,
|
|
671
694
|
onUploaded,
|
|
672
|
-
onRecordingError,
|
|
673
695
|
deleteRecording,
|
|
696
|
+
onRecordingError,
|
|
697
|
+
autoResetAfterUpload,
|
|
698
|
+
getDurationMs: getElapsedMs,
|
|
674
699
|
});
|
|
675
700
|
const mountRecordingBlob = useCallback((blob) => {
|
|
676
701
|
setAudioBlob(blob);
|
|
@@ -753,6 +778,7 @@ function useVoiceRecorder(props) {
|
|
|
753
778
|
isPlaying,
|
|
754
779
|
isRecording,
|
|
755
780
|
audioLevels,
|
|
781
|
+
lastDuration,
|
|
756
782
|
playbackTime,
|
|
757
783
|
recordingTime,
|
|
758
784
|
previewLevels,
|
|
@@ -1099,9 +1125,6 @@ const useAudioRecorderHandlers = (options) => {
|
|
|
1099
1125
|
toggleRecordingPause,
|
|
1100
1126
|
]);
|
|
1101
1127
|
const handleSend = useCallback(async () => {
|
|
1102
|
-
if (isSending) {
|
|
1103
|
-
return;
|
|
1104
|
-
}
|
|
1105
1128
|
shouldSendAfterStopRef.current = false;
|
|
1106
1129
|
setIsSending(true);
|
|
1107
1130
|
try {
|
|
@@ -1118,7 +1141,6 @@ const useAudioRecorderHandlers = (options) => {
|
|
|
1118
1141
|
setIsSending(false);
|
|
1119
1142
|
}
|
|
1120
1143
|
}, [
|
|
1121
|
-
isSending,
|
|
1122
1144
|
stopPlayback,
|
|
1123
1145
|
onUploadError,
|
|
1124
1146
|
sendRecording,
|
|
@@ -1129,6 +1151,7 @@ const useAudioRecorderHandlers = (options) => {
|
|
|
1129
1151
|
return;
|
|
1130
1152
|
}
|
|
1131
1153
|
if (isRecording) {
|
|
1154
|
+
setIsSending(true);
|
|
1132
1155
|
shouldSendAfterStopRef.current = true;
|
|
1133
1156
|
stopRecording();
|
|
1134
1157
|
return;
|
|
@@ -1266,6 +1289,44 @@ const IdleControl = (props) => {
|
|
|
1266
1289
|
return (jsxs(IdleWrapper, { children: [jsxs(IdleButton, { type: "button", onClick: onClick, "aria-label": startAriaLabel, children: [jsx(IdleButtonOutline, { "aria-hidden": true }), jsx(IdleButtonGlow, { "aria-hidden": true }), jsx(Icon32Microphone, { height: 48, width: 48 })] }), jsx(IdleInstruction, { children: instruction })] }));
|
|
1267
1290
|
};
|
|
1268
1291
|
|
|
1292
|
+
const Bars = styled.div `
|
|
1293
|
+
position: absolute;
|
|
1294
|
+
inset: 0;
|
|
1295
|
+
display: flex;
|
|
1296
|
+
align-items: flex-end;
|
|
1297
|
+
gap: 3px;
|
|
1298
|
+
`;
|
|
1299
|
+
const Bar = styled.div `
|
|
1300
|
+
flex: 1;
|
|
1301
|
+
min-width: 2px;
|
|
1302
|
+
border-radius: 9999px;
|
|
1303
|
+
background: var(--white, #ffffff);
|
|
1304
|
+
opacity: 0.6;
|
|
1305
|
+
transition: height 120ms ease, opacity 120ms ease;
|
|
1306
|
+
`;
|
|
1307
|
+
|
|
1308
|
+
const WaveformBars = (props) => {
|
|
1309
|
+
const { values, progress, styleInterpolator, scale = 40, minHeight = 6, divider = 100, } = props;
|
|
1310
|
+
return (jsx(Bars, { children: values.map((value, index) => {
|
|
1311
|
+
const normalized = clampValue(value);
|
|
1312
|
+
const height = Math.max(minHeight, (normalized / divider) * scale);
|
|
1313
|
+
const barProgress = values.length > 1 ? index / (values.length - 1) : 0;
|
|
1314
|
+
const isPlayed = (progress !== null && progress !== void 0 ? progress : 0) > 0 && barProgress <= (progress !== null && progress !== void 0 ? progress : 0);
|
|
1315
|
+
const interpolated = styleInterpolator === null || styleInterpolator === void 0 ? void 0 : styleInterpolator({
|
|
1316
|
+
normalized,
|
|
1317
|
+
height,
|
|
1318
|
+
index,
|
|
1319
|
+
progress: progress !== null && progress !== void 0 ? progress : 0,
|
|
1320
|
+
barProgress,
|
|
1321
|
+
isPlayed,
|
|
1322
|
+
});
|
|
1323
|
+
return (jsx(Bar, { style: {
|
|
1324
|
+
height: `${height}px`,
|
|
1325
|
+
...interpolated,
|
|
1326
|
+
} }, index));
|
|
1327
|
+
}) }));
|
|
1328
|
+
};
|
|
1329
|
+
|
|
1269
1330
|
/**
|
|
1270
1331
|
* RecordingShell styles
|
|
1271
1332
|
*
|
|
@@ -1360,21 +1421,6 @@ const Waveform = styled.div `
|
|
|
1360
1421
|
align-items: center;
|
|
1361
1422
|
overflow: hidden;
|
|
1362
1423
|
`;
|
|
1363
|
-
const Bars = styled.div `
|
|
1364
|
-
position: absolute;
|
|
1365
|
-
inset: 0;
|
|
1366
|
-
display: flex;
|
|
1367
|
-
align-items: flex-end;
|
|
1368
|
-
gap: 3px;
|
|
1369
|
-
`;
|
|
1370
|
-
const Bar = styled.div `
|
|
1371
|
-
flex: 1;
|
|
1372
|
-
min-width: 2px;
|
|
1373
|
-
border-radius: 9999px;
|
|
1374
|
-
background: var(--white, #ffffff);
|
|
1375
|
-
opacity: 0.6;
|
|
1376
|
-
transition: height 120ms ease, opacity 120ms ease;
|
|
1377
|
-
`;
|
|
1378
1424
|
const Timer$1 = styled.span `
|
|
1379
1425
|
text-align: right;
|
|
1380
1426
|
font-size: 13px;
|
|
@@ -1436,31 +1482,33 @@ const Spinner = styled.span `
|
|
|
1436
1482
|
}
|
|
1437
1483
|
`;
|
|
1438
1484
|
|
|
1439
|
-
const WaveformBars = (props) => {
|
|
1440
|
-
const { values, styleInterpolator, scale = 40, minHeight = 6, divider = 100, } = props;
|
|
1441
|
-
return (jsx(Bars, { children: values.map((value, index) => {
|
|
1442
|
-
const normalized = clampValue(value);
|
|
1443
|
-
const height = Math.max(minHeight, (normalized / divider) * scale);
|
|
1444
|
-
const interpolated = styleInterpolator === null || styleInterpolator === void 0 ? void 0 : styleInterpolator({ normalized, height, index });
|
|
1445
|
-
return (jsx(Bar, { style: {
|
|
1446
|
-
height: `${height}px`,
|
|
1447
|
-
...interpolated,
|
|
1448
|
-
} }, index));
|
|
1449
|
-
}) }));
|
|
1450
|
-
};
|
|
1451
|
-
|
|
1452
1485
|
const RecordingShell = (props) => {
|
|
1453
1486
|
const { isPaused, isPlaying, isSending, isRecording, audioLevels, hasRecording, playbackTime, previewLevels, recordingTime, stopAriaLabel, playAriaLabel, sendAriaLabel, startAriaLabel, cancelAriaLabel, playbackDuration, pausePlaybackAriaLabel, pauseRecordingAriaLabel, resumeRecordingAriaLabel, onSend, onCancel, formatTime, onRecordToggle, onTogglePlayback, } = props;
|
|
1487
|
+
const { lastDuration } = props;
|
|
1488
|
+
const isPreview = !isRecording || isPaused;
|
|
1454
1489
|
const waveformValues = isRecording ? audioLevels : (hasRecording ? previewLevels : audioLevels);
|
|
1490
|
+
const effectiveDuration = recordingTime
|
|
1491
|
+
|| playbackDuration
|
|
1492
|
+
|| lastDuration
|
|
1493
|
+
|| 1;
|
|
1494
|
+
const progress = (isPreview && isPlaying)
|
|
1495
|
+
? Math.min(1, playbackTime / effectiveDuration)
|
|
1496
|
+
: 0;
|
|
1455
1497
|
const displayTime = useMemo(() => {
|
|
1456
|
-
if (
|
|
1498
|
+
if (isPlaying) {
|
|
1499
|
+
return formatTime(playbackTime);
|
|
1500
|
+
}
|
|
1501
|
+
if (isRecording && !isPaused) {
|
|
1502
|
+
return formatTime(recordingTime);
|
|
1503
|
+
}
|
|
1504
|
+
if (isRecording && isPaused) {
|
|
1457
1505
|
return formatTime(recordingTime);
|
|
1458
1506
|
}
|
|
1507
|
+
if (lastDuration > 0) {
|
|
1508
|
+
return formatTime(lastDuration);
|
|
1509
|
+
}
|
|
1459
1510
|
if (hasRecording) {
|
|
1460
|
-
if (
|
|
1461
|
-
return formatTime(playbackTime);
|
|
1462
|
-
}
|
|
1463
|
-
if (playbackDuration > 0) {
|
|
1511
|
+
if (playbackDuration > 0.5) {
|
|
1464
1512
|
return formatTime(playbackDuration);
|
|
1465
1513
|
}
|
|
1466
1514
|
if (recordingTime > 0) {
|
|
@@ -1468,7 +1516,17 @@ const RecordingShell = (props) => {
|
|
|
1468
1516
|
}
|
|
1469
1517
|
}
|
|
1470
1518
|
return formatTime(0);
|
|
1471
|
-
}, [
|
|
1519
|
+
}, [
|
|
1520
|
+
isPaused,
|
|
1521
|
+
isPlaying,
|
|
1522
|
+
formatTime,
|
|
1523
|
+
isRecording,
|
|
1524
|
+
lastDuration,
|
|
1525
|
+
hasRecording,
|
|
1526
|
+
playbackTime,
|
|
1527
|
+
recordingTime,
|
|
1528
|
+
playbackDuration,
|
|
1529
|
+
]);
|
|
1472
1530
|
const recordButtonDisabled = isSending;
|
|
1473
1531
|
const recordButtonIcon = !isRecording || isPaused
|
|
1474
1532
|
? jsx(Icon16Microphone, { width: 24, height: 24 })
|
|
@@ -1486,10 +1544,13 @@ const RecordingShell = (props) => {
|
|
|
1486
1544
|
: jsx(Icon16Play, { width: 24, height: 24 });
|
|
1487
1545
|
const playbackAria = isPlaying ? pausePlaybackAriaLabel : playAriaLabel;
|
|
1488
1546
|
const sendDisabled = isSending || (!hasRecording && !isRecording);
|
|
1489
|
-
return (jsxs(Wrapper$1, { children: [jsx(CancelButton$1, { type: "button", onClick: onCancel, "aria-label": cancelAriaLabel, children: jsx(Icon28DeleteOutlineAndroid, { fill: "var(--exode_red)", width: 24, height: 24, style: { minWidth: 24 } }) }), jsxs(Track, { children: [jsx(CircleButton, { type: "button", "$variant": recordButtonVariant, onClick: onRecordToggle, "aria-label": recordButtonAria, disabled: recordButtonDisabled, children: recordButtonIcon }), !canShowPlayback ? null : (jsx(CircleButton, { type: "button", "$variant": "play", onClick: onTogglePlayback, "aria-label": playbackAria, disabled: playbackDisabled, children: playbackIcon })), jsx(Waveform, { "$expanded": canShowPlayback, children: jsx(WaveformBars, {
|
|
1547
|
+
return (jsxs(Wrapper$1, { children: [jsx(CancelButton$1, { type: "button", onClick: onCancel, "aria-label": cancelAriaLabel, children: jsx(Icon28DeleteOutlineAndroid, { fill: "var(--exode_red)", width: 24, height: 24, style: { minWidth: 24 } }) }), jsxs(Track, { children: [jsx(CircleButton, { type: "button", "$variant": recordButtonVariant, onClick: onRecordToggle, "aria-label": recordButtonAria, disabled: recordButtonDisabled, children: recordButtonIcon }), !canShowPlayback ? null : (jsx(CircleButton, { type: "button", "$variant": "play", onClick: onTogglePlayback, "aria-label": playbackAria, disabled: playbackDisabled, children: playbackIcon })), jsx(Waveform, { "$expanded": canShowPlayback, children: jsx(WaveformBars, { scale: 42, minHeight: 6, divider: 100, progress: progress, values: waveformValues, styleInterpolator: ({ normalized, isPlayed, progress: p }) => ({
|
|
1490
1548
|
height: `${12 + normalized / 2}px`,
|
|
1491
1549
|
maxHeight: '34px',
|
|
1492
|
-
opacity:
|
|
1550
|
+
opacity: p > 0
|
|
1551
|
+
? (isPlayed ? 0.80 : 0.22)
|
|
1552
|
+
: 0.38 + Math.min(0.5, normalized / 120),
|
|
1553
|
+
transition: 'opacity 80ms ease',
|
|
1493
1554
|
}) }) }), jsx(Timer$1, { children: displayTime })] }), jsx(SendButton$1, { type: "button", onClick: onSend, disabled: sendDisabled, "aria-label": sendAriaLabel, children: isSending ? (jsx(SpinnerSlot, { children: jsx(Spinner, {}) })) : (jsx(Icon28SendOutline, { width: 24, height: 24 })) })] }));
|
|
1494
1555
|
};
|
|
1495
1556
|
|
|
@@ -1572,9 +1633,9 @@ const AudioRecorder = (props) => {
|
|
|
1572
1633
|
messages,
|
|
1573
1634
|
onUploadError,
|
|
1574
1635
|
});
|
|
1575
|
-
const { isPaused, isPlaying, formatTime, audioLevels, isRecording, playbackTime, hasRecording, previewLevels, recordingTime, playbackDuration, } = recorder;
|
|
1576
|
-
const showRecorderBar = isRecording || hasRecording;
|
|
1577
|
-
return (jsx(Container, { className: className, children: showRecorderBar ? (jsx(RecordingShell, { isPaused: isPaused, isSending: isSending, isPlaying: isPlaying, formatTime: formatTime, onCancel: handleCancel, isRecording: isRecording, audioLevels: audioLevels, onSend: handleSendRequest, playbackTime: playbackTime, hasRecording: hasRecording, previewLevels: previewLevels, recordingTime: recordingTime, playbackDuration: playbackDuration, onRecordToggle: handleRecordToggle, sendAriaLabel: messages.buttons.send, onTogglePlayback: handlePlaybackToggle, cancelAriaLabel: messages.buttons.cancel, playAriaLabel: messages.aria.playRecording, stopAriaLabel: messages.aria.stopRecording, startAriaLabel: messages.aria.startRecording, pauseRecordingAriaLabel: messages.aria.pauseRecording, resumeRecordingAriaLabel: messages.aria.resumeRecording, pausePlaybackAriaLabel: messages.aria.pausePlayback })) : (jsx(IdleControl, { onClick: handleRecordToggle, startAriaLabel: messages.aria.startRecording, instruction: messages.instruction })) }));
|
|
1636
|
+
const { isPaused, isPlaying, formatTime, audioLevels, isRecording, lastDuration, playbackTime, hasRecording, previewLevels, recordingTime, playbackDuration, } = recorder;
|
|
1637
|
+
const showRecorderBar = isRecording || hasRecording || isSending;
|
|
1638
|
+
return (jsx(Container, { className: className, children: showRecorderBar ? (jsx(RecordingShell, { isPaused: isPaused, isSending: isSending, isPlaying: isPlaying, formatTime: formatTime, onCancel: handleCancel, isRecording: isRecording, audioLevels: audioLevels, onSend: handleSendRequest, playbackTime: playbackTime, lastDuration: lastDuration, hasRecording: hasRecording, previewLevels: previewLevels, recordingTime: recordingTime, playbackDuration: playbackDuration, onRecordToggle: handleRecordToggle, sendAriaLabel: messages.buttons.send, onTogglePlayback: handlePlaybackToggle, cancelAriaLabel: messages.buttons.cancel, playAriaLabel: messages.aria.playRecording, stopAriaLabel: messages.aria.stopRecording, startAriaLabel: messages.aria.startRecording, pauseRecordingAriaLabel: messages.aria.pauseRecording, resumeRecordingAriaLabel: messages.aria.resumeRecording, pausePlaybackAriaLabel: messages.aria.pausePlayback })) : (jsx(IdleControl, { onClick: handleRecordToggle, startAriaLabel: messages.aria.startRecording, instruction: messages.instruction })) }));
|
|
1578
1639
|
};
|
|
1579
1640
|
|
|
1580
1641
|
/**
|
|
@@ -2213,7 +2274,7 @@ const VoiceMessagePlayer = React.forwardRef((props, ref) => {
|
|
|
2213
2274
|
observer.disconnect();
|
|
2214
2275
|
};
|
|
2215
2276
|
}, []);
|
|
2216
|
-
const displayTime = isPlaying
|
|
2277
|
+
const displayTime = (isPlaying || currentTime > 0)
|
|
2217
2278
|
? formatTime(currentTime)
|
|
2218
2279
|
: formatTime(resolvedDuration);
|
|
2219
2280
|
const handleTrackClick = useCallback((e) => {
|