dmed-voice-assistant 1.0.0
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/README.md +2 -0
- package/package.json +70 -0
- package/src/VoiceAssistant/index.js +45 -0
- package/src/VoiceAssistant/recognition.js +952 -0
- package/src/VoiceAssistant/recorder.js +575 -0
@@ -0,0 +1,575 @@
|
|
1
|
+
import { useEffect, useState, useRef } from "react";
|
2
|
+
import { Box, Typography, MenuItem } from "@mui/material";
|
3
|
+
import Grid from '@mui/material/Grid2';
|
4
|
+
import { MicIcon, StartIcon, SmallStartIcon, SettingIcon, StopIcon, RedStopIcon, LanguageIcon, BoxIcon, SmallPauseIcon } from "../shared/svgs";
|
5
|
+
import StyledSelect from "../shared/StyledSelect";
|
6
|
+
import RecordListItem from "../shared/RecordListItem";
|
7
|
+
import Recorder from "recorder-js";
|
8
|
+
|
9
|
+
const apiUrl = 'https://api.origintechx.dev/qa/v1/diagnose/voice';
|
10
|
+
|
11
|
+
const getVoiceFileName = (date) => {
|
12
|
+
const year = date.getFullYear(); // Get the full year (YYYY)
|
13
|
+
const month = String(date.getMonth() + 1).padStart(2, '0'); // Get the month (MM), pad with leading zero if necessary
|
14
|
+
const day = String(date.getDate()).padStart(2, '0'); // Get the day (DD), pad with leading zero if necessary
|
15
|
+
|
16
|
+
return `Voice${year}${month}${day}.wav`;
|
17
|
+
}
|
18
|
+
|
19
|
+
const getTimeValues = (totalSeconds) => {
|
20
|
+
const hours = Math.floor(totalSeconds / 3600); // Get hours
|
21
|
+
let minutes = Math.floor((totalSeconds % 3600) / 60); // Get minutes
|
22
|
+
let seconds = totalSeconds % 60; // Get seconds
|
23
|
+
|
24
|
+
minutes = minutes < 10 ? `0${minutes}` : minutes;
|
25
|
+
seconds = seconds < 10 ? `0${seconds}` : seconds;
|
26
|
+
|
27
|
+
return { hours, minutes, seconds }; // Return the values separately
|
28
|
+
};
|
29
|
+
|
30
|
+
const getAudioDuration = async (blob) => {
|
31
|
+
const audioContext = new AudioContext();
|
32
|
+
const arrayBuffer = await blob.arrayBuffer();
|
33
|
+
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
|
34
|
+
const duration = audioBuffer.duration;
|
35
|
+
return parseInt(duration);
|
36
|
+
}
|
37
|
+
|
38
|
+
const RecorderBox = ({
|
39
|
+
mode = 'recorder',
|
40
|
+
recordHistoryList,
|
41
|
+
setMode,
|
42
|
+
onNewRecordEvent,
|
43
|
+
onRecordDataChange
|
44
|
+
}) => {
|
45
|
+
const [isVoiceMode, setIsVoiceMode] = useState(false);
|
46
|
+
const [isStartedRecord, setIsStartedRecord] = useState(false);
|
47
|
+
const [selectedVoice, setSelectedVoice] = useState("");
|
48
|
+
const [voiceList, setVoiceList] = useState([]);
|
49
|
+
const languageList = ['Auto-Detect', 'English', 'Chinese (Simplified)'];
|
50
|
+
const [selectedLanguage, setSelectedLanguage] = useState("");
|
51
|
+
const [recordList, setRecordList] = useState(recordHistoryList);
|
52
|
+
const [newRecordFileName, setNewRecordFileName] = useState("");
|
53
|
+
const [newRecordTime, setNewRecordTime] = useState(0);
|
54
|
+
const [isRunning, setIsRunning] = useState(false);
|
55
|
+
const [intervalId, setIntervalId] = useState(null);
|
56
|
+
|
57
|
+
const [audioBlob, setAudioBlob] = useState(null);
|
58
|
+
const [audioUrl, setAudioUrl] = useState('');
|
59
|
+
const [audioSize, setAudioSize] = useState(0);
|
60
|
+
const mediaRecorderRef = useRef(null);
|
61
|
+
|
62
|
+
const handleVoiceChange = (event) => {
|
63
|
+
setSelectedVoice(event.target.value);
|
64
|
+
};
|
65
|
+
|
66
|
+
const handleLanguageChange = (event) => {
|
67
|
+
setSelectedLanguage(event.target.value);
|
68
|
+
};
|
69
|
+
|
70
|
+
const initRecorder = async () => {
|
71
|
+
try {
|
72
|
+
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
73
|
+
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
74
|
+
const newRecorder = new Recorder(audioContext);
|
75
|
+
await newRecorder.init(stream);
|
76
|
+
mediaRecorderRef.current = newRecorder;
|
77
|
+
} catch (error) {
|
78
|
+
console.error("Unable to access microphone", error);
|
79
|
+
}
|
80
|
+
};
|
81
|
+
|
82
|
+
const startRecording = async () => {
|
83
|
+
if (!mediaRecorderRef.current) {
|
84
|
+
await initRecorder();
|
85
|
+
}
|
86
|
+
if (mediaRecorderRef.current) {
|
87
|
+
setAudioBlob(null);
|
88
|
+
mediaRecorderRef.current.start();
|
89
|
+
setIsStartedRecord(true);
|
90
|
+
|
91
|
+
setNewRecordFileName(getVoiceFileName(new Date()));
|
92
|
+
startCounting();
|
93
|
+
}
|
94
|
+
};
|
95
|
+
|
96
|
+
const stopRecording = () => {
|
97
|
+
if (mediaRecorderRef.current) {
|
98
|
+
mediaRecorderRef.current.stop().then(async ({ blob }) => {
|
99
|
+
setAudioBlob(blob);
|
100
|
+
setAudioUrl(URL.createObjectURL(blob));
|
101
|
+
console.log(blob)
|
102
|
+
let temp = [...recordList];
|
103
|
+
const newVoice = {
|
104
|
+
audioURL: URL.createObjectURL(blob),
|
105
|
+
name: newRecordFileName,
|
106
|
+
date: new Date(),
|
107
|
+
size: blob.size,
|
108
|
+
time: await getAudioDuration(blob)
|
109
|
+
};
|
110
|
+
|
111
|
+
temp.push(newVoice);
|
112
|
+
setRecordList(temp);
|
113
|
+
|
114
|
+
if(onNewRecordEvent) {
|
115
|
+
onNewRecordEvent(newVoice);
|
116
|
+
}
|
117
|
+
if(onRecordDataChange) {
|
118
|
+
onRecordDataChange(temp);
|
119
|
+
}
|
120
|
+
});
|
121
|
+
setIsStartedRecord(false);
|
122
|
+
|
123
|
+
stopCounting();
|
124
|
+
setNewRecordTime(0);
|
125
|
+
setAudioSize(0);
|
126
|
+
}
|
127
|
+
};
|
128
|
+
|
129
|
+
const handleStartRecord = async () => {
|
130
|
+
if(isVoiceMode) return;
|
131
|
+
|
132
|
+
if(!isStartedRecord) {
|
133
|
+
startRecording();
|
134
|
+
} else {
|
135
|
+
stopRecording();
|
136
|
+
}
|
137
|
+
};
|
138
|
+
|
139
|
+
const handleModeChange = () => {
|
140
|
+
if(mode === "recorder") {
|
141
|
+
setMode("recognition");
|
142
|
+
} else if(mode === "recognition") {
|
143
|
+
setMode("recorder");
|
144
|
+
}
|
145
|
+
};
|
146
|
+
|
147
|
+
const handleSteupChange = () => {
|
148
|
+
if(isStartedRecord) return;
|
149
|
+
|
150
|
+
setIsVoiceMode(!isVoiceMode);
|
151
|
+
};
|
152
|
+
|
153
|
+
const handleLabelChange = (id, newLabel) => {
|
154
|
+
setRecordList((prevRecords) =>
|
155
|
+
prevRecords.map((record, index) =>
|
156
|
+
index === id ? { ...record, label: newLabel } : record
|
157
|
+
)
|
158
|
+
);
|
159
|
+
};
|
160
|
+
|
161
|
+
const handleDelete = (id) => {
|
162
|
+
setRecordList((prevRecords) => prevRecords.filter((record, index) => index !== id));
|
163
|
+
};
|
164
|
+
|
165
|
+
const startCounting = () => {
|
166
|
+
if (isRunning) return; // Prevent starting a new interval if already running
|
167
|
+
const id = setInterval(async () => {
|
168
|
+
setNewRecordTime((prevCount) => prevCount + 1); // Increment count every 1000ms
|
169
|
+
await mediaRecorderRef.current.audioRecorder.getBuffer((blob) => {
|
170
|
+
setAudioSize((blob[0].byteLength / 1024 / 1024).toFixed(2));
|
171
|
+
})
|
172
|
+
}, 1000);
|
173
|
+
setIntervalId(id); // Store the interval ID
|
174
|
+
setIsRunning(true); // Set the counter as running
|
175
|
+
};
|
176
|
+
|
177
|
+
const stopCounting = () => {
|
178
|
+
clearInterval(intervalId); // Stop the interval using the interval ID
|
179
|
+
setIsRunning(false); // Set the counter as not running
|
180
|
+
};
|
181
|
+
|
182
|
+
useEffect(() => {
|
183
|
+
const fetchAudioInputDevices = async () => {
|
184
|
+
try {
|
185
|
+
// Request permission to access media devices
|
186
|
+
await navigator.mediaDevices.getUserMedia({ audio: true });
|
187
|
+
|
188
|
+
// Enumerate all media devices
|
189
|
+
const devices = await navigator.mediaDevices.enumerateDevices();
|
190
|
+
|
191
|
+
// Filter only audio input devices
|
192
|
+
const audioInputs = devices.filter(device => device.kind === "audioinput");
|
193
|
+
let temp = ['Auto-Detect'];
|
194
|
+
audioInputs.forEach(device => {
|
195
|
+
temp.push(device.label);
|
196
|
+
});
|
197
|
+
setVoiceList(temp);
|
198
|
+
} catch (error) {
|
199
|
+
console.error("Error accessing audio devices:", error);
|
200
|
+
}
|
201
|
+
};
|
202
|
+
|
203
|
+
fetchAudioInputDevices();
|
204
|
+
}, []);
|
205
|
+
|
206
|
+
useEffect(() => {
|
207
|
+
// uploadRecording();
|
208
|
+
}, [audioBlob]);
|
209
|
+
|
210
|
+
return (
|
211
|
+
<Box
|
212
|
+
className="bg-[#0B0B0B] rounded-[5px] border p-[20px] w-[850px]"
|
213
|
+
>
|
214
|
+
<Grid container spacing={2}>
|
215
|
+
<Grid size={6}>
|
216
|
+
<Box className="flex items-center justify-between">
|
217
|
+
<Typography className="!text-[24px] !font-[600]" color="#EAE5DC">
|
218
|
+
Voice assistant
|
219
|
+
</Typography>
|
220
|
+
<Box
|
221
|
+
className="flex items-center px-[10px] py-[4px] bg-[#006FFF4D] rounded-[89.1px] cursor-pointer h-[28px]"
|
222
|
+
sx={{
|
223
|
+
border: '0.9px solid #006FFFB2',
|
224
|
+
}}
|
225
|
+
onClick={handleModeChange}
|
226
|
+
>
|
227
|
+
<Box>
|
228
|
+
<MicIcon />
|
229
|
+
</Box>
|
230
|
+
<Typography className="!font-600 !text-[14px]" color="#EAE5DC">
|
231
|
+
Recorder mode
|
232
|
+
</Typography>
|
233
|
+
</Box>
|
234
|
+
</Box>
|
235
|
+
|
236
|
+
<Box className="flex items-center justify-between mt-4">
|
237
|
+
{
|
238
|
+
!isStartedRecord ?
|
239
|
+
<Box
|
240
|
+
className="flex items-center space-x-1 cursor-pointer px-[9px] py-[6px] rounded-[5px] bg-[#0B0B0B]"
|
241
|
+
sx={{
|
242
|
+
'&:hover': {
|
243
|
+
boxShadow: '0px 0px 5px 1px #44C63380',
|
244
|
+
cursor: isVoiceMode && 'not-allowed'
|
245
|
+
}
|
246
|
+
}}
|
247
|
+
onClick={handleStartRecord}
|
248
|
+
>
|
249
|
+
<Box>
|
250
|
+
<StartIcon />
|
251
|
+
</Box>
|
252
|
+
<Typography
|
253
|
+
className="!font-400 !text-[16px]"
|
254
|
+
color="#EAE5DC"
|
255
|
+
>
|
256
|
+
Start
|
257
|
+
</Typography>
|
258
|
+
</Box>
|
259
|
+
:
|
260
|
+
<Box
|
261
|
+
className="flex items-center space-x-1 cursor-pointer px-[9px] py-[6px] rounded-[5px] bg-[#0B0B0B]"
|
262
|
+
sx={{
|
263
|
+
border: '0.9px solid #F3151580',
|
264
|
+
'&:hover': {
|
265
|
+
boxShadow: '0px 0px 5px 1px #F3151580'
|
266
|
+
}
|
267
|
+
}}
|
268
|
+
onClick={handleStartRecord}
|
269
|
+
>
|
270
|
+
<Box>
|
271
|
+
<SmallPauseIcon />
|
272
|
+
</Box>
|
273
|
+
<Typography
|
274
|
+
className="!font-400 !text-[14px]"
|
275
|
+
color="#EAE5DC"
|
276
|
+
>
|
277
|
+
Stop
|
278
|
+
</Typography>
|
279
|
+
</Box>
|
280
|
+
}
|
281
|
+
<Box
|
282
|
+
className="flex items-center space-x-1 cursor-pointer px-[9px] py-[6px] rounded-[5px] bg-[#0B0B0B]"
|
283
|
+
sx={{
|
284
|
+
'&:hover': {
|
285
|
+
boxShadow: '0px 0px 5px 2px #58585880',
|
286
|
+
cursor: isStartedRecord && 'not-allowed'
|
287
|
+
}
|
288
|
+
}}
|
289
|
+
onClick={handleSteupChange}
|
290
|
+
>
|
291
|
+
<Box>
|
292
|
+
<SettingIcon />
|
293
|
+
</Box>
|
294
|
+
<Typography className="!font-400 !text-[14px]" color="#EAE5DC">
|
295
|
+
Setup
|
296
|
+
</Typography>
|
297
|
+
</Box>
|
298
|
+
</Box>
|
299
|
+
|
300
|
+
<Box className="rounded-[5px] bg-[#A3DBFE] mt-2">
|
301
|
+
<Box className="flex items-center justify-between p-[4.5px]">
|
302
|
+
<Box
|
303
|
+
className="flex items-center space-x-1 rounded-[5px] px-[5px] py-[2px] bg-[#0B0B0B]"
|
304
|
+
sx={{
|
305
|
+
boxShadow: '0px 0px 1.8px 0px #00000040'
|
306
|
+
}}
|
307
|
+
>
|
308
|
+
<Box>
|
309
|
+
{ !isStartedRecord ? <StopIcon /> : <RedStopIcon /> }
|
310
|
+
</Box>
|
311
|
+
<Typography className="!font-[600] !text-[14px]" color="#A3DBFE">
|
312
|
+
{ !isStartedRecord ? 'STOP' : 'REC' }
|
313
|
+
</Typography>
|
314
|
+
</Box>
|
315
|
+
<Typography className="!font-400 !text-[20px] px-[9px]" color="#1A2123"
|
316
|
+
sx={{
|
317
|
+
fontFamily: "Space Grotesk !important"
|
318
|
+
}}
|
319
|
+
>
|
320
|
+
01.08.2024
|
321
|
+
</Typography>
|
322
|
+
</Box>
|
323
|
+
|
324
|
+
<Box className="flex justify-between p-[9px]">
|
325
|
+
<Typography className="!font-400 !text-[36px]" color="#1A2123"
|
326
|
+
sx={{
|
327
|
+
fontFamily: "Space Grotesk !important"
|
328
|
+
}}
|
329
|
+
>
|
330
|
+
{getTimeValues(newRecordTime).hours}<span style={{ color: '#494A48', fontWeight: "300", fontSize: "16px"}}>h</span> {getTimeValues(newRecordTime).minutes}<span style={{ color: '#494A48', fontWeight: "300", fontSize: "16px"}}>m</span> {getTimeValues(newRecordTime).seconds}<span style={{ color: '#494A48', fontWeight: "300", fontSize: "16px"}}>s</span>
|
331
|
+
</Typography>
|
332
|
+
<Box className="flex flex-col space-y-3 text-right">
|
333
|
+
<Typography className="!font-400 !text-[16px]" color="#494A48"
|
334
|
+
sx={{
|
335
|
+
fontFamily: "Space Grotesk !important"
|
336
|
+
}}
|
337
|
+
>
|
338
|
+
{audioSize} <span style={{ fontSize: "14px", fontFamily: "Space Grotesk !important" }}>MB</span>
|
339
|
+
</Typography>
|
340
|
+
{
|
341
|
+
isStartedRecord &&
|
342
|
+
<Typography
|
343
|
+
className="!font-bold !text-[16px]" color="#494A48"
|
344
|
+
sx={{ fontFamily: "Space Grotesk !important" }}
|
345
|
+
>
|
346
|
+
{newRecordFileName}
|
347
|
+
</Typography>
|
348
|
+
}
|
349
|
+
</Box>
|
350
|
+
</Box>
|
351
|
+
</Box>
|
352
|
+
</Grid>
|
353
|
+
|
354
|
+
<Grid size={6} className={`w-full ${isVoiceMode ? 'pr-[10px]' : ''}`}>
|
355
|
+
{
|
356
|
+
isVoiceMode &&
|
357
|
+
<>
|
358
|
+
<Box className="flex space-x-1 w-full">
|
359
|
+
<Box>
|
360
|
+
<MicIcon />
|
361
|
+
</Box>
|
362
|
+
<Box className="flex-1">
|
363
|
+
<Typography className="!font-[600] !text-[16px]" color="#EAE5DC">
|
364
|
+
Voice
|
365
|
+
</Typography>
|
366
|
+
<Typography className="!font-400 !text-[14px] pt-1" color="#EAE5DC">
|
367
|
+
Input device
|
368
|
+
</Typography>
|
369
|
+
<StyledSelect
|
370
|
+
className="mt-1"
|
371
|
+
fullWidth
|
372
|
+
displayEmpty
|
373
|
+
value={selectedVoice}
|
374
|
+
onChange={handleVoiceChange}
|
375
|
+
renderValue={(selected) => {
|
376
|
+
if (selected === "") {
|
377
|
+
return <span style={{
|
378
|
+
fontSize: '12.6px',
|
379
|
+
fontWeight: '400',
|
380
|
+
lineHeight: '25.2px',
|
381
|
+
color: '#EAE5DC99' }}>Auto-Detect</span>;
|
382
|
+
}
|
383
|
+
return <span style={{
|
384
|
+
fontSize: '12.6px',
|
385
|
+
fontWeight: '400',
|
386
|
+
lineHeight: '25.2px',
|
387
|
+
color: '#EAE5DC99' }}>{voiceList[selected]}</span>;
|
388
|
+
}}
|
389
|
+
MenuProps={{
|
390
|
+
PaperProps: {
|
391
|
+
sx: {
|
392
|
+
marginTop: "5px",
|
393
|
+
padding: '5px',
|
394
|
+
background: '#0B0B0B',
|
395
|
+
border: '0.9px solid #565656',
|
396
|
+
'& .MuiList-root': {
|
397
|
+
padding: "unset"
|
398
|
+
},
|
399
|
+
'& .MuiMenuItem-root': {
|
400
|
+
padding: "7.2px 9px",
|
401
|
+
color: '#EAE5DC99',
|
402
|
+
fontFamily: 'Reddit Sans',
|
403
|
+
fontSize: '12.6px',
|
404
|
+
fontWeight: '400',
|
405
|
+
lineHeight: '25.2px'
|
406
|
+
},
|
407
|
+
'& .MuiMenuItem-root:hover': {
|
408
|
+
background: "#A3DBFE99",
|
409
|
+
color: '#1A2123',
|
410
|
+
},
|
411
|
+
'& .MuiMenuItem-root.Mui-selected': {
|
412
|
+
background: "#A3DBFE",
|
413
|
+
color: '#1A2123',
|
414
|
+
},
|
415
|
+
'& .MuiMenuItem-root.Mui-selected:hover': {
|
416
|
+
background: "#A3DBFE99",
|
417
|
+
color: '#1A2123',
|
418
|
+
}
|
419
|
+
},
|
420
|
+
},
|
421
|
+
}}
|
422
|
+
>
|
423
|
+
{
|
424
|
+
voiceList.map((device, index) => {
|
425
|
+
return (
|
426
|
+
<MenuItem value={index} key={index}>{device}</MenuItem>
|
427
|
+
)
|
428
|
+
})
|
429
|
+
}
|
430
|
+
</StyledSelect>
|
431
|
+
</Box>
|
432
|
+
</Box>
|
433
|
+
|
434
|
+
<Box className="flex space-x-1 w-full mt-2">
|
435
|
+
<Box>
|
436
|
+
<LanguageIcon />
|
437
|
+
</Box>
|
438
|
+
<Box className="flex-1">
|
439
|
+
<Typography className="!font-[600] !text-[16px]" color="#EAE5DC">
|
440
|
+
Language
|
441
|
+
</Typography>
|
442
|
+
<Typography className="!font-400 !text-[14px] pt-1" color="#EAE5DC">
|
443
|
+
Prefer language
|
444
|
+
</Typography>
|
445
|
+
<StyledSelect
|
446
|
+
className="mt-1"
|
447
|
+
fullWidth
|
448
|
+
displayEmpty
|
449
|
+
value={selectedLanguage}
|
450
|
+
onChange={handleLanguageChange}
|
451
|
+
renderValue={(selected) => {
|
452
|
+
if (selected === "") {
|
453
|
+
return <span style={{
|
454
|
+
fontSize: '12.6px',
|
455
|
+
fontWeight: '400',
|
456
|
+
lineHeight: '25.2px',
|
457
|
+
color: '#EAE5DC99' }}>Auto-Detect</span>;
|
458
|
+
}
|
459
|
+
return <span style={{
|
460
|
+
fontSize: '12.6px',
|
461
|
+
fontWeight: '400',
|
462
|
+
lineHeight: '25.2px',
|
463
|
+
color: '#EAE5DC99' }}>{languageList[selected]}</span>;
|
464
|
+
}}
|
465
|
+
MenuProps={{
|
466
|
+
PaperProps: {
|
467
|
+
sx: {
|
468
|
+
marginTop: "5px",
|
469
|
+
padding: '5px',
|
470
|
+
background: '#0B0B0B',
|
471
|
+
border: '0.9px solid #565656',
|
472
|
+
'& .MuiList-root': {
|
473
|
+
padding: "unset"
|
474
|
+
},
|
475
|
+
'& .MuiMenuItem-root': {
|
476
|
+
padding: "7.2px 9px",
|
477
|
+
color: '#EAE5DC99',
|
478
|
+
fontFamily: 'Reddit Sans',
|
479
|
+
fontSize: '12.6px',
|
480
|
+
fontWeight: '400',
|
481
|
+
lineHeight: '25.2px'
|
482
|
+
},
|
483
|
+
'& .MuiMenuItem-root:hover': {
|
484
|
+
background: "#A3DBFE99",
|
485
|
+
color: '#1A2123',
|
486
|
+
},
|
487
|
+
'& .MuiMenuItem-root.Mui-selected': {
|
488
|
+
background: "#A3DBFE",
|
489
|
+
color: '#1A2123',
|
490
|
+
},
|
491
|
+
'& .MuiMenuItem-root.Mui-selected:hover': {
|
492
|
+
background: "#A3DBFE99",
|
493
|
+
color: '#1A2123',
|
494
|
+
}
|
495
|
+
},
|
496
|
+
},
|
497
|
+
}}
|
498
|
+
>
|
499
|
+
<MenuItem value={0}>Auto-Detect</MenuItem>
|
500
|
+
<MenuItem value={1}>English</MenuItem>
|
501
|
+
<MenuItem value={2}>Chinese (Simplified)</MenuItem>
|
502
|
+
</StyledSelect>
|
503
|
+
</Box>
|
504
|
+
</Box>
|
505
|
+
</>
|
506
|
+
}
|
507
|
+
|
508
|
+
{
|
509
|
+
!isVoiceMode &&
|
510
|
+
<>
|
511
|
+
{
|
512
|
+
recordList.length === 0 &&
|
513
|
+
<>
|
514
|
+
<Box className="flex flex-col items-center justify-center h-full">
|
515
|
+
<Box>
|
516
|
+
<BoxIcon />
|
517
|
+
</Box>
|
518
|
+
<Typography className="!font-[600] !text-[16px] pt-2" color="#EAE5DC"
|
519
|
+
sx={{ fontFamily: "Afacad !important" }}
|
520
|
+
>
|
521
|
+
Record Empty
|
522
|
+
</Typography>
|
523
|
+
<Box className="flex items-center space-x-1 mt-1">
|
524
|
+
<Typography className="!font-400 !text-[14px]" color="#EAE5DC"
|
525
|
+
sx={{ fontFamily: "Afacad !important" }}
|
526
|
+
>
|
527
|
+
Push
|
528
|
+
</Typography>
|
529
|
+
<Box>
|
530
|
+
<SmallStartIcon />
|
531
|
+
</Box>
|
532
|
+
<Typography className="!font-400 !text-[16px]" color="#EAE5DC"
|
533
|
+
sx={{ fontFamily: "Afacad !important" }}
|
534
|
+
>
|
535
|
+
to start
|
536
|
+
</Typography>
|
537
|
+
</Box>
|
538
|
+
</Box>
|
539
|
+
</>
|
540
|
+
}
|
541
|
+
|
542
|
+
{
|
543
|
+
recordList.length > 0 &&
|
544
|
+
<Box className="flex flex-col space-y-2 p-[10px] scrollableBox"
|
545
|
+
sx={{
|
546
|
+
maxHeight: "225px",
|
547
|
+
}}
|
548
|
+
>
|
549
|
+
{
|
550
|
+
recordList.map((record, index) => {
|
551
|
+
return (
|
552
|
+
<RecordListItem
|
553
|
+
audioURL={record.audioURL}
|
554
|
+
label={record.name}
|
555
|
+
capacity={record.size}
|
556
|
+
time={record.time}
|
557
|
+
createdDate={record.date}
|
558
|
+
key={index}
|
559
|
+
onLabelChange={(newLabel) => handleLabelChange(index, newLabel)}
|
560
|
+
onDelete={() => handleDelete(index)}
|
561
|
+
/>
|
562
|
+
)
|
563
|
+
})
|
564
|
+
}
|
565
|
+
</Box>
|
566
|
+
}
|
567
|
+
</>
|
568
|
+
}
|
569
|
+
</Grid>
|
570
|
+
</Grid>
|
571
|
+
</Box>
|
572
|
+
)
|
573
|
+
};
|
574
|
+
|
575
|
+
export default RecorderBox;
|