quickblox-react-ui-kit 0.4.2-beta.6 → 0.4.2-beta.7
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/.env +1 -0
- package/dist/hooks/useQuickBloxUIKit.d.ts +2 -4
- package/dist/index-ui.js +3146 -2315
- package/dist/index-ui.js.map +1 -1
- package/media-recorder-js.d.ts +133 -0
- package/package.json +2 -1
- package/src/Data/DefaultConfigurations.ts +2 -2
- package/src/Presentation/Views/Dialog/MessageItem/MessageItem.tsx +2 -2
- package/src/Presentation/Views/Dialog/useDialogViewModel.ts +86 -64
- package/src/Presentation/Views/DialogList/useDialogListViewModel.ts +27 -28
- package/src/Presentation/layouts/Desktop/QuickBloxUIKitDesktopLayout.tsx +28 -26
- package/src/Presentation/ui-components/Message/Bubble/AudioBubble/AudioBubble.scss +3 -1
- package/src/QBconfig.ts +2 -2
- package/src/hooks/useQuickBloxUIKit.ts +128 -160
- package/tsconfig.json +2 -1
- package/webpack.config.js +14 -1
|
@@ -3,6 +3,7 @@ import '../Presentation/Views/Dialog/Dialog.scss';
|
|
|
3
3
|
import '../Presentation/Views/Dialog/DialogHeader/DialogInfoIcon/DialogInfoIcon.scss';
|
|
4
4
|
import { Tone } from 'qb-ai-rephrase/src/Tone';
|
|
5
5
|
import { toast } from 'react-toastify';
|
|
6
|
+
import QBMediaRecorder from 'media-recorder-js';
|
|
6
7
|
import useQbInitializedDataContext from '../Presentation/providers/QuickBloxUIKitProvider/useQbInitializedDataContext';
|
|
7
8
|
import { DialogEntity } from '../Domain/entity/DialogEntity';
|
|
8
9
|
import { DialogListViewModel } from '../Presentation/Views/DialogList/DialogListViewModel';
|
|
@@ -41,7 +42,6 @@ import { UsersListViewModel } from '../Presentation/Views/DialogInfo/UsersList/U
|
|
|
41
42
|
|
|
42
43
|
interface QuickBloxUIKitReturn {
|
|
43
44
|
constants: {
|
|
44
|
-
mimeType: string;
|
|
45
45
|
messagePerPage: number;
|
|
46
46
|
maxFileSize?: number;
|
|
47
47
|
maxWidthToResizing: string;
|
|
@@ -65,9 +65,9 @@ interface QuickBloxUIKitReturn {
|
|
|
65
65
|
showDialogInformation: boolean;
|
|
66
66
|
needDialogInformation: boolean;
|
|
67
67
|
isRecording: boolean;
|
|
68
|
-
stream?: MediaStream;
|
|
68
|
+
stream?: MediaStream | null;
|
|
69
69
|
permission: boolean;
|
|
70
|
-
resultAudioBlob?: Blob;
|
|
70
|
+
resultAudioBlob?: Blob | null;
|
|
71
71
|
audioChunks: Blob[];
|
|
72
72
|
fileToSend?: File | null;
|
|
73
73
|
messageText: string;
|
|
@@ -78,7 +78,6 @@ interface QuickBloxUIKitReturn {
|
|
|
78
78
|
defaultAITranslateWidget?: AIMessageWidget;
|
|
79
79
|
defaultAIAssistWidget?: AIMessageWidget;
|
|
80
80
|
rephraseTones: Tone[];
|
|
81
|
-
mimeType: string;
|
|
82
81
|
messagePerPage: number;
|
|
83
82
|
enableForwarding: boolean;
|
|
84
83
|
enableReplying: boolean;
|
|
@@ -146,7 +145,7 @@ export default function useQuickBloxUIKit({
|
|
|
146
145
|
uikitHeightOffset = '0px',
|
|
147
146
|
}: QuickBloxUIKitProps): QuickBloxUIKitReturn {
|
|
148
147
|
// 103
|
|
149
|
-
const mimeType = 'audio/webm;codecs=opus'; // audio/ogg audio/mpeg audio/webm audio/x-wav audio/mp4
|
|
148
|
+
// const mimeType = 'audio/webm;codecs=opus'; // audio/ogg audio/mpeg audio/webm audio/x-wav audio/mp4
|
|
150
149
|
const messagePerPage = 47;
|
|
151
150
|
|
|
152
151
|
const currentContext = useQbInitializedDataContext();
|
|
@@ -372,10 +371,14 @@ export default function useQuickBloxUIKit({
|
|
|
372
371
|
const [fileToSend, setFileToSend] = useState<File | null>(null);
|
|
373
372
|
const [isRecording, setIsRecording] = useState(false);
|
|
374
373
|
const [permission, setPermission] = useState(false);
|
|
375
|
-
|
|
376
|
-
const
|
|
377
|
-
const
|
|
378
|
-
const [
|
|
374
|
+
//
|
|
375
|
+
const [stream, setStream] = useState<MediaStream | null>(null);
|
|
376
|
+
const mediaRecorder = useRef<QBMediaRecorder | null>(null);
|
|
377
|
+
const [resultAudioBlob, setResultAudioBlob] = useState<Blob | null>(null);
|
|
378
|
+
const [audioChunks, setAudioChunks] = useState<Blob[]>([]);
|
|
379
|
+
const [peerConnection, setPeerConnection] =
|
|
380
|
+
useState<RTCPeerConnection | null>(null);
|
|
381
|
+
//
|
|
379
382
|
const newModal = useModal();
|
|
380
383
|
const [dialogToLeave, setDialogToLeave] = useState<DialogEntity>();
|
|
381
384
|
const [showDialogList, setShowDialogList] = useState<boolean>(true);
|
|
@@ -487,191 +490,158 @@ export default function useQuickBloxUIKit({
|
|
|
487
490
|
});
|
|
488
491
|
};
|
|
489
492
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
try {
|
|
493
|
-
const mediaStream = await navigator.mediaDevices.getUserMedia({
|
|
494
|
-
audio: true,
|
|
495
|
-
video: false,
|
|
496
|
-
});
|
|
497
|
-
|
|
498
|
-
setPermission(true);
|
|
499
|
-
setStream(mediaStream);
|
|
500
|
-
} catch (err) {
|
|
501
|
-
showErrorMessage(
|
|
502
|
-
`The MediaRecorder API throws exception ${stringifyError(err)} .`,
|
|
503
|
-
);
|
|
504
|
-
}
|
|
505
|
-
} else {
|
|
506
|
-
showErrorMessage(
|
|
507
|
-
'The MediaRecorder API is not supported in your browser.',
|
|
508
|
-
);
|
|
509
|
-
}
|
|
510
|
-
};
|
|
511
|
-
|
|
512
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars,@typescript-eslint/require-await
|
|
513
|
-
const startRecording = async () => {
|
|
514
|
-
if (!stream) return;
|
|
515
|
-
|
|
516
|
-
// Определение браузера
|
|
493
|
+
// Detect browser and set MIME type
|
|
494
|
+
const detectBrowserAndMimeType = () => {
|
|
517
495
|
const userAgent = navigator.userAgent.toLowerCase();
|
|
518
496
|
const isChrome =
|
|
519
497
|
/chrome/.test(userAgent) && !/edge|opr|brave/.test(userAgent);
|
|
520
498
|
const isSafari = /^((?!chrome|android).)*safari/.test(userAgent);
|
|
521
499
|
const isFirefox = /firefox/.test(userAgent);
|
|
522
|
-
const
|
|
500
|
+
const isIOS = /iphone|ipad|ipod/.test(userAgent);
|
|
523
501
|
|
|
524
|
-
// eslint-disable-next-line no-nested-ternary
|
|
525
502
|
console.log(
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
503
|
+
`Browser detected: ${
|
|
504
|
+
// eslint-disable-next-line no-nested-ternary
|
|
505
|
+
isChrome
|
|
506
|
+
? 'Chrome'
|
|
507
|
+
: // eslint-disable-next-line no-nested-ternary
|
|
508
|
+
isSafari
|
|
509
|
+
? 'Safari'
|
|
510
|
+
: // eslint-disable-next-line no-nested-ternary
|
|
511
|
+
isFirefox
|
|
512
|
+
? 'Firefox'
|
|
513
|
+
: isIOS
|
|
514
|
+
? 'iOS'
|
|
515
|
+
: 'Other'
|
|
516
|
+
}`,
|
|
536
517
|
);
|
|
537
518
|
|
|
538
519
|
const mimeTypes = {
|
|
539
520
|
chrome: ['audio/webm;codecs=opus', 'audio/webm'],
|
|
540
|
-
safari: ['audio/
|
|
521
|
+
safari: ['audio/mp4', 'audio/mp4;codecs=mp4a', 'audio/aac', 'audio/wav'],
|
|
522
|
+
ios: ['audio/mp4', 'audio/mp4;codecs=mp4a', 'audio/aac'],
|
|
541
523
|
firefox: ['audio/ogg', 'audio/webm'],
|
|
542
|
-
other: ['audio/
|
|
524
|
+
other: ['audio/webm', 'audio/mp4', 'audio/wav'],
|
|
543
525
|
};
|
|
544
526
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
}
|
|
527
|
+
// eslint-disable-next-line no-nested-ternary
|
|
528
|
+
const targetMimeTypes = isIOS
|
|
529
|
+
? mimeTypes.ios
|
|
530
|
+
: // eslint-disable-next-line no-nested-ternary
|
|
531
|
+
isSafari
|
|
532
|
+
? mimeTypes.safari
|
|
533
|
+
: // eslint-disable-next-line no-nested-ternary
|
|
534
|
+
isChrome
|
|
535
|
+
? mimeTypes.chrome
|
|
536
|
+
: isFirefox
|
|
537
|
+
? mimeTypes.firefox
|
|
538
|
+
: mimeTypes.other;
|
|
539
|
+
|
|
540
|
+
return (
|
|
541
|
+
targetMimeTypes.find((type) => QBMediaRecorder.isTypeSupported(type)) ||
|
|
542
|
+
'audio/wav'
|
|
543
|
+
);
|
|
544
|
+
};
|
|
564
545
|
|
|
565
|
-
|
|
546
|
+
// Request microphone access and setup WebRTC
|
|
547
|
+
const getMicrophonePermission = async () => {
|
|
548
|
+
if (!window) {
|
|
549
|
+
showErrorMessage(
|
|
550
|
+
'The MediaRecorder API is not supported in your browser.',
|
|
551
|
+
);
|
|
552
|
+
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
566
555
|
|
|
567
556
|
try {
|
|
568
|
-
const
|
|
557
|
+
const mediaStream = await navigator.mediaDevices.getUserMedia({
|
|
558
|
+
audio: true,
|
|
559
|
+
});
|
|
569
560
|
|
|
570
|
-
|
|
571
|
-
|
|
561
|
+
// Create WebRTC peer connection
|
|
562
|
+
const pc = new RTCPeerConnection();
|
|
572
563
|
|
|
573
|
-
|
|
564
|
+
mediaStream
|
|
565
|
+
.getTracks()
|
|
566
|
+
.forEach((track) => pc.addTrack(track, mediaStream));
|
|
574
567
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
localAudioChunks.push(event.data);
|
|
578
|
-
}
|
|
568
|
+
pc.ontrack = (event) => {
|
|
569
|
+
setStream(event.streams[0]);
|
|
579
570
|
};
|
|
580
571
|
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
572
|
+
setPeerConnection(pc);
|
|
573
|
+
setStream(mediaStream);
|
|
574
|
+
setPermission(true);
|
|
575
|
+
console.log('Microphone access granted, WebRTC connection established.');
|
|
576
|
+
} catch (err) {
|
|
577
|
+
showErrorMessage(
|
|
578
|
+
`The MediaRecorder API throws exception ${stringifyError(err)}.`,
|
|
579
|
+
);
|
|
584
580
|
}
|
|
585
581
|
};
|
|
586
|
-
// previous version - startRecording:
|
|
587
|
-
// const startRecording = async () => {
|
|
588
|
-
// if (!stream) return;
|
|
589
|
-
// const mimeTypes = [
|
|
590
|
-
// 'audio/aac',
|
|
591
|
-
// 'audio/mp4',
|
|
592
|
-
// 'audio/mpeg',
|
|
593
|
-
// 'audio/ogg',
|
|
594
|
-
// 'audio/wav',
|
|
595
|
-
// 'audio/webm',
|
|
596
|
-
// 'audio/3gpp',
|
|
597
|
-
// 'audio/flac',
|
|
598
|
-
// 'audio/x-aiff',
|
|
599
|
-
// 'audio/x-m4a',
|
|
600
|
-
// ];
|
|
601
|
-
//
|
|
602
|
-
// console.log('MIME TYPES: ');
|
|
603
|
-
// mimeTypes.forEach((mType) => {
|
|
604
|
-
// if (MediaRecorder.isTypeSupported(mimeType)) {
|
|
605
|
-
// console.log(`${mType} is supported`);
|
|
606
|
-
// } else {
|
|
607
|
-
// console.log(`${mType} is not supported`);
|
|
608
|
-
// }
|
|
609
|
-
// });
|
|
610
|
-
// // audio/mp4;codecs=mp4a audio/webm;codecs=opus audio/webm;codecs=vp9,opus
|
|
611
|
-
// const mimeContent = window.MediaRecorder.isTypeSupported('audio/mp4')
|
|
612
|
-
// ? 'audio/mp4;codecs=mp4a'
|
|
613
|
-
// : 'audio/webm;codecs=opus';
|
|
614
|
-
//
|
|
615
|
-
// const media = new MediaRecorder(stream, { mimeType: mimeContent });
|
|
616
|
-
//
|
|
617
|
-
// mediaRecorder.current = media;
|
|
618
|
-
// mediaRecorder.current.start();
|
|
619
|
-
//
|
|
620
|
-
// const localAudioChunks: any[] = [];
|
|
621
|
-
//
|
|
622
|
-
// mediaRecorder.current.ondataavailable = (event) => {
|
|
623
|
-
// if (typeof event.data === 'undefined') return;
|
|
624
|
-
// if (event.data.size === 0) return;
|
|
625
|
-
// localAudioChunks.push(event.data);
|
|
626
|
-
// };
|
|
627
|
-
//
|
|
628
|
-
// setAudioChunks(localAudioChunks);
|
|
629
|
-
// };
|
|
630
582
|
|
|
631
|
-
//
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
583
|
+
// Start recording using QBMediaRecorder
|
|
584
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
585
|
+
const startRecording = async () => {
|
|
586
|
+
if (!stream) return;
|
|
635
587
|
|
|
636
|
-
|
|
637
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
638
|
-
const mimeContent = window.MediaRecorder.isTypeSupported(
|
|
639
|
-
'audio/mp4;codecs=mp4a',
|
|
640
|
-
)
|
|
641
|
-
? 'audio/mp4;codecs=mp4a'
|
|
642
|
-
: 'audio/webm;codecs=opus';
|
|
643
|
-
// const audioBlob = new Blob(audioChunks, { type: mimeContent }); // mimeType
|
|
644
|
-
// const mp4Blob = new Blob(recordedChunks, { type: 'video/mp4' });
|
|
588
|
+
const mimeType = detectBrowserAndMimeType();
|
|
645
589
|
|
|
646
|
-
|
|
647
|
-
// const audioBlob = new Blob(audioChunks, { type: 'audio/mp4' }); // mimeType
|
|
648
|
-
const audioBlob = new Blob(audioChunks, { type: 'audio/mp4' });
|
|
590
|
+
console.log(`Selected MIME-type: ${mimeType}`);
|
|
649
591
|
|
|
650
|
-
|
|
592
|
+
const recorder = new QBMediaRecorder({
|
|
593
|
+
mimeType,
|
|
594
|
+
timeslice: 1000, // Chunks of 1 second
|
|
595
|
+
ignoreMutedMedia: true,
|
|
596
|
+
onstart: () => console.log('Recording started'),
|
|
597
|
+
onstop: (file) => {
|
|
598
|
+
console.log('Final audio file:', file);
|
|
599
|
+
setResultAudioBlob(file);
|
|
600
|
+
setAudioChunks([]); // Clear recorded chunks
|
|
601
|
+
},
|
|
602
|
+
ondataavailable: (event) => {
|
|
603
|
+
if (event.data.size > 0) {
|
|
604
|
+
setAudioChunks((prev) => [...prev, event.data]);
|
|
605
|
+
}
|
|
606
|
+
},
|
|
607
|
+
onerror: (error) => console.error('Recording error:', error),
|
|
608
|
+
});
|
|
651
609
|
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
stream?.getAudioTracks().forEach((track) => {
|
|
655
|
-
track.stop();
|
|
656
|
-
});
|
|
657
|
-
setPermission(false);
|
|
658
|
-
//
|
|
659
|
-
};
|
|
610
|
+
mediaRecorder.current = recorder;
|
|
611
|
+
recorder.start(stream);
|
|
660
612
|
};
|
|
661
613
|
|
|
662
|
-
|
|
663
|
-
|
|
614
|
+
// Stop recording
|
|
615
|
+
const stopRecording = () => {
|
|
616
|
+
if (!mediaRecorder.current) return;
|
|
664
617
|
|
|
665
|
-
|
|
666
|
-
b.lastModifiedDate = new Date();
|
|
667
|
-
b.name = fileName;
|
|
618
|
+
mediaRecorder.current.stop();
|
|
668
619
|
|
|
669
|
-
//
|
|
670
|
-
|
|
620
|
+
// Stop WebRTC stream
|
|
621
|
+
if (peerConnection) {
|
|
622
|
+
peerConnection.close();
|
|
623
|
+
setPeerConnection(null);
|
|
624
|
+
}
|
|
625
|
+
};
|
|
671
626
|
|
|
672
|
-
|
|
627
|
+
// Convert Blob to File
|
|
628
|
+
const blobToFile = (blob: Blob, fileName: string): File => {
|
|
629
|
+
return new File([blob], fileName, { type: blob.type });
|
|
673
630
|
};
|
|
674
631
|
|
|
632
|
+
// const blobToFile = (theBlob: Blob, fileName: string): File => {
|
|
633
|
+
// const b: any = theBlob;
|
|
634
|
+
//
|
|
635
|
+
// // A Blob() is almost a File() - it's just missing the two properties below which we will add
|
|
636
|
+
// b.lastModifiedDate = new Date();
|
|
637
|
+
// b.name = fileName;
|
|
638
|
+
//
|
|
639
|
+
// // Cast to a File() type
|
|
640
|
+
// const resultFile = theBlob as unknown as File;
|
|
641
|
+
//
|
|
642
|
+
// return resultFile;
|
|
643
|
+
// };
|
|
644
|
+
|
|
675
645
|
function sendTextMessageActions(textToSend: string) {
|
|
676
646
|
if (isOnline) {
|
|
677
647
|
// closeReplyMessageFlowHandler
|
|
@@ -1081,7 +1051,6 @@ export default function useQuickBloxUIKit({
|
|
|
1081
1051
|
// 972
|
|
1082
1052
|
return {
|
|
1083
1053
|
constants: {
|
|
1084
|
-
mimeType,
|
|
1085
1054
|
messagePerPage,
|
|
1086
1055
|
maxFileSize,
|
|
1087
1056
|
maxWidthToResizing,
|
|
@@ -1116,7 +1085,6 @@ export default function useQuickBloxUIKit({
|
|
|
1116
1085
|
defaultAIRephraseWidget,
|
|
1117
1086
|
defaultAITranslateWidget,
|
|
1118
1087
|
defaultAIAssistWidget,
|
|
1119
|
-
mimeType,
|
|
1120
1088
|
messagePerPage,
|
|
1121
1089
|
maxTokensForAIRephrase,
|
|
1122
1090
|
rephraseTones,
|
package/tsconfig.json
CHANGED
package/webpack.config.js
CHANGED
|
@@ -15,6 +15,10 @@ module.exports = {
|
|
|
15
15
|
},
|
|
16
16
|
resolve: {
|
|
17
17
|
extensions: ['.ts', '.tsx'],
|
|
18
|
+
alias: {
|
|
19
|
+
'./errors': path.resolve(__dirname, 'node_modules/media-recorder-js/src/errors.js'),
|
|
20
|
+
'./mimeTypes': path.resolve(__dirname, 'node_modules/media-recorder-js/src/mimeTypes.js'),
|
|
21
|
+
},
|
|
18
22
|
},
|
|
19
23
|
externals: {
|
|
20
24
|
"react": "react",
|
|
@@ -29,7 +33,16 @@ module.exports = {
|
|
|
29
33
|
// Creates style nodes from JS strings
|
|
30
34
|
'style-loader',
|
|
31
35
|
// Translates CSS into CommonJS
|
|
32
|
-
'css-loader'
|
|
36
|
+
'css-loader',
|
|
37
|
+
{
|
|
38
|
+
loader: 'sass-loader',
|
|
39
|
+
options: {
|
|
40
|
+
implementation: require('sass'),
|
|
41
|
+
sassOptions: {
|
|
42
|
+
quietDeps: true, // Отключает устаревшие предупреждения
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
},
|
|
33
46
|
],
|
|
34
47
|
},
|
|
35
48
|
{
|