Базовая версия голосовых сообщений и аудиоплеер. Кодирование OPUS
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { useDialog } from "@/app/providers/DialogProvider/useDialog";
|
||||
import { useRosettaColors } from "@/app/hooks/useRosettaColors";
|
||||
import { Box, Divider, Flex, Menu, Popover, Text, Transition, useComputedColorScheme } from "@mantine/core";
|
||||
import { IconBarrierBlock, IconCamera, IconDoorExit, IconFile, IconMicrophone, IconMoodSmile, IconPaperclip, IconSend } from "@tabler/icons-react";
|
||||
import { IconBarrierBlock, IconCamera, IconDoorExit, IconFile, IconMicrophone, IconMoodSmile, IconPaperclip, IconSend, IconTrash } from "@tabler/icons-react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useBlacklist } from "@/app/providers/BlacklistProvider/useBlacklist";
|
||||
import { filePrapareForNetworkTransfer, generateRandomKey, imagePrepareForNetworkTransfer } from "@/app/utils/utils";
|
||||
@@ -25,7 +25,8 @@ import { AnimatedButton } from "../AnimatedButton/AnimatedButton";
|
||||
import { useUserCacheFunc } from "@/app/providers/InformationProvider/useUserCacheFunc";
|
||||
import { MentionList, Mention } from "../MentionList/MentionList";
|
||||
import { useDrafts } from "@/app/providers/DialogProvider/useDrafts";
|
||||
|
||||
import { useVoiceMessage } from "./useVoiceMessage";
|
||||
import { VoiceRecorder } from "../VoiceRecorder/VoiceRecorder";
|
||||
|
||||
export function DialogInput() {
|
||||
const colors = useRosettaColors();
|
||||
@@ -47,6 +48,7 @@ export function DialogInput() {
|
||||
const [mentionList, setMentionList] = useState<Mention[]>([]);
|
||||
const mentionHandling = useRef<string>("");
|
||||
const {getDraft, saveDraft} = useDrafts(dialog);
|
||||
const {start, stop, isRecording, duration, waves, getAudioBlob, interpolateCompressWaves} = useVoiceMessage();
|
||||
|
||||
|
||||
const avatars = useAvatars(
|
||||
@@ -65,10 +67,12 @@ export function DialogInput() {
|
||||
], [], true);
|
||||
|
||||
const hasText = message.trim().length > 0;
|
||||
const showSendIcon = hasText || attachments.length > 0;
|
||||
const showSendIcon = hasText || attachments.length > 0 || isRecording;
|
||||
|
||||
const onMicClick = () => {
|
||||
console.info("Start voice record");
|
||||
const onMicroClick = () => {
|
||||
if(!isRecording) {
|
||||
start();
|
||||
}
|
||||
};
|
||||
|
||||
const fileDialog = useFileDialog({
|
||||
@@ -195,8 +199,28 @@ export function DialogInput() {
|
||||
mentionHandling.current = username;
|
||||
}
|
||||
|
||||
const send = () => {
|
||||
if(blocked || (message.trim() == "" && attachments.length <= 0)) {
|
||||
const send = async () => {
|
||||
if(blocked || (message.trim() == "" && attachments.length <= 0 && !isRecording)){
|
||||
return;
|
||||
}
|
||||
if(isRecording){
|
||||
const audioBlob = getAudioBlob();
|
||||
stop();
|
||||
if(!audioBlob){
|
||||
return;
|
||||
}
|
||||
sendMessage("", [
|
||||
{
|
||||
blob: Buffer.from(await audioBlob.arrayBuffer()).toString('binary'),
|
||||
id: generateRandomKey(8),
|
||||
type: AttachmentType.VOICE,
|
||||
preview: duration + "::" + interpolateCompressWaves(35).join(","),
|
||||
transport: {
|
||||
transport_server: "",
|
||||
transport_tag: ""
|
||||
}
|
||||
}
|
||||
]);
|
||||
return;
|
||||
}
|
||||
sendMessage(message, attachments);
|
||||
@@ -372,77 +396,84 @@ export function DialogInput() {
|
||||
{!blocked &&
|
||||
<Flex h={'100%'} p={'xs'} direction={'row'} bg={colors.boxColor}>
|
||||
<Flex w={25} mt={10} justify={'center'}>
|
||||
<Menu width={150} withArrow>
|
||||
<Menu.Target>
|
||||
<IconPaperclip stroke={1.5} style={{
|
||||
cursor: 'pointer'
|
||||
}} size={25} color={colors.chevrons.active}></IconPaperclip>
|
||||
</Menu.Target>
|
||||
<Menu.Dropdown style={{
|
||||
userSelect: 'none'
|
||||
}}>
|
||||
<Menu.Label fz={'xs'} fw={500} c={'dimmed'}>Attach</Menu.Label>
|
||||
<Menu.Item fz={'xs'} fw={500} leftSection={
|
||||
<IconFile size={14}></IconFile>
|
||||
} onClick={onClickPaperclip}>File</Menu.Item>
|
||||
{((avatars.length > 0 && !hasGroup(dialog))
|
||||
|| (avatars.length > 0 && hasGroup(dialog) && isAdmin))
|
||||
&& <Menu.Item fz={'xs'} fw={500} leftSection={
|
||||
<IconCamera size={14}></IconCamera>
|
||||
} onClick={onClickCamera}>Avatar {hasGroup(dialog) && 'group'}</Menu.Item>}
|
||||
</Menu.Dropdown>
|
||||
</Menu>
|
||||
{isRecording && (
|
||||
<IconTrash onClick={stop} style={{
|
||||
cursor: 'pointer'
|
||||
}} color={colors.error} stroke={1.5} size={25}></IconTrash>
|
||||
)}
|
||||
{!isRecording && (
|
||||
<Menu width={150} withArrow>
|
||||
<Menu.Target>
|
||||
<IconPaperclip stroke={1.5} style={{
|
||||
cursor: 'pointer'
|
||||
}} size={25} color={colors.chevrons.active}></IconPaperclip>
|
||||
</Menu.Target>
|
||||
<Menu.Dropdown style={{
|
||||
userSelect: 'none'
|
||||
}}>
|
||||
<Menu.Label fz={'xs'} fw={500} c={'dimmed'}>Attach</Menu.Label>
|
||||
<Menu.Item fz={'xs'} fw={500} leftSection={
|
||||
<IconFile size={14}></IconFile>
|
||||
} onClick={onClickPaperclip}>File</Menu.Item>
|
||||
{((avatars.length > 0 && !hasGroup(dialog))
|
||||
|| (avatars.length > 0 && hasGroup(dialog) && isAdmin))
|
||||
&& <Menu.Item fz={'xs'} fw={500} leftSection={
|
||||
<IconCamera size={14}></IconCamera>
|
||||
} onClick={onClickCamera}>Avatar {hasGroup(dialog) && 'group'}</Menu.Item>}
|
||||
</Menu.Dropdown>
|
||||
</Menu>
|
||||
)}
|
||||
</Flex>
|
||||
<Flex
|
||||
w={'calc(100% - (25px + 50px + var(--mantine-spacing-xs)))'}
|
||||
maw={'calc(100% - (25px + 50px + var(--mantine-spacing-xs)))'}
|
||||
align={'center'}
|
||||
>
|
||||
<RichTextInput
|
||||
ref={editableDivRef}
|
||||
style={{
|
||||
border: 0,
|
||||
minHeight: 45,
|
||||
fontSize: 14,
|
||||
background: 'transparent',
|
||||
width: '100%',
|
||||
paddingLeft: 10,
|
||||
paddingRight: 10,
|
||||
outline: 'none',
|
||||
paddingTop: 10,
|
||||
paddingBottom: 8
|
||||
}}
|
||||
placeholder="Type message..."
|
||||
autoFocus
|
||||
//ref={textareaRef}
|
||||
//onPaste={onPaste}
|
||||
//maxLength={2500}
|
||||
//w={'100%'}
|
||||
//h={'100%'}
|
||||
onKeyDown={handleKeyDown}
|
||||
onChange={setMessage}
|
||||
onPaste={onPaste}
|
||||
|
||||
//dangerouslySetInnerHTML={{__html: message}}
|
||||
></RichTextInput>
|
||||
{!isRecording && <>
|
||||
<RichTextInput
|
||||
ref={editableDivRef}
|
||||
style={{
|
||||
border: 0,
|
||||
minHeight: 45,
|
||||
fontSize: 14,
|
||||
background: 'transparent',
|
||||
width: '100%',
|
||||
paddingLeft: 10,
|
||||
paddingRight: 10,
|
||||
outline: 'none',
|
||||
paddingTop: 10,
|
||||
paddingBottom: 8
|
||||
}}
|
||||
placeholder="Type message..."
|
||||
autoFocus
|
||||
onKeyDown={handleKeyDown}
|
||||
onChange={setMessage}
|
||||
onPaste={onPaste}
|
||||
></RichTextInput>
|
||||
</>}
|
||||
{isRecording && <>
|
||||
<VoiceRecorder duration={duration} waves={waves}></VoiceRecorder>
|
||||
</>}
|
||||
</Flex>
|
||||
<Flex mt={10} w={'calc(50px + var(--mantine-spacing-xs))'} gap={'xs'}>
|
||||
<Popover withArrow>
|
||||
<Popover.Target>
|
||||
<IconMoodSmile color={colors.chevrons.active} stroke={1.5} size={25} style={{
|
||||
cursor: 'pointer'
|
||||
}}></IconMoodSmile>
|
||||
</Popover.Target>
|
||||
<Popover.Dropdown p={0}>
|
||||
<EmojiPicker
|
||||
onEmojiClick={onEmojiClick}
|
||||
searchDisabled
|
||||
skinTonesDisabled
|
||||
theme={computedTheme == 'dark' ? Theme.DARK : Theme.LIGHT}
|
||||
/>
|
||||
</Popover.Dropdown>
|
||||
</Popover>
|
||||
<Box pos="relative" w={25} h={25}>
|
||||
{!isRecording && <>
|
||||
<Popover withArrow>
|
||||
<Popover.Target>
|
||||
<IconMoodSmile color={colors.chevrons.active} stroke={1.5} size={25} style={{
|
||||
cursor: 'pointer'
|
||||
}}></IconMoodSmile>
|
||||
</Popover.Target>
|
||||
<Popover.Dropdown p={0}>
|
||||
<EmojiPicker
|
||||
onEmojiClick={onEmojiClick}
|
||||
searchDisabled
|
||||
skinTonesDisabled
|
||||
theme={computedTheme == 'dark' ? Theme.DARK : Theme.LIGHT}
|
||||
/>
|
||||
</Popover.Dropdown>
|
||||
</Popover>
|
||||
</>}
|
||||
<Box pos="relative" ml={isRecording ? 35 : 0} w={25} h={25}>
|
||||
<Transition mounted={showSendIcon} transition="pop" duration={180} timingFunction="ease">
|
||||
{(styles) => (
|
||||
<IconSend
|
||||
@@ -465,7 +496,7 @@ export function DialogInput() {
|
||||
<IconMicrophone
|
||||
stroke={1.5}
|
||||
color={colors.chevrons.active}
|
||||
onClick={onMicClick}
|
||||
onClick={onMicroClick}
|
||||
style={{
|
||||
...styles,
|
||||
position: 'absolute',
|
||||
|
||||
Reference in New Issue
Block a user