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, IconMoodSmile, IconPaperclip, IconSend } from "@tabler/icons-react"; import { useEffect, useRef, useState } from "react"; import { useBlacklist } from "@/app/providers/BlacklistProvider/useBlacklist"; import { base64ImageToBlurhash, filePrapareForNetworkTransfer, generateRandomKey, imagePrepareForNetworkTransfer } from "@/app/utils/utils"; import { Attachment, AttachmentType } from "@/app/providers/ProtocolProvider/protocol/packets/packet.message"; import { DialogAttachment } from "../DialogAttachment/DialogAttachment"; import { PacketTyping } from "@/app/providers/ProtocolProvider/protocol/packets/packet.typeing"; import { usePublicKey } from "@/app/providers/AccountProvider/usePublicKey"; import { usePrivateKeyHash } from "@/app/providers/AccountProvider/usePrivateKeyHash"; import { useSender } from "@/app/providers/ProtocolProvider/useSender"; import { useReplyMessages } from "@/app/providers/DialogProvider/useReplyMessages"; import { useFileDialog, useHotkeys } from "@mantine/hooks"; import { ATTACHMENTS_NOT_ALLOWED_TO_REPLY, MAX_ATTACHMENTS_IN_MESSAGE, MAX_UPLOAD_FILESIZE_MB } from "@/app/constants"; import { useSystemAccounts } from "@/app/providers/SystemAccountsProvider/useSystemAccounts"; import { Dropzone } from '@mantine/dropzone'; import { RichTextInput } from "../RichTextInput/RichTextInput"; import EmojiPicker, { EmojiClickData, Theme } from 'emoji-picker-react'; import { useAvatars } from "@/app/providers/AvatarProvider/useAvatars"; import { useGroups } from "@/app/providers/DialogProvider/useGroups"; import { useGroupMembers } from "@/app/providers/InformationProvider/useGroupMembers"; 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"; export function DialogInput() { const colors = useRosettaColors(); const {sendMessage, dialog} = useDialog(); const {members, loading} = useGroupMembers(dialog); const {hasGroup, leaveGroup} = useGroups(); const [message, setMessage] = useState(""); const [blocked] = useBlacklist(dialog); const [attachments, setAttachments] = useState([]); const publicKey = usePublicKey(); const isAdmin = hasGroup(dialog) && members[0] == publicKey; const isBannedGroup = hasGroup(dialog) && members.length == 0 && !loading; const privateKey = usePrivateKeyHash(); const sendPacket = useSender(); const typingTimeoutRef = useRef(null); const {replyMessages, deselectAllMessages} = useReplyMessages(); const computedTheme = useComputedColorScheme(); const systemAccounts = useSystemAccounts(); const [mentionList, setMentionList] = useState([]); const mentionHandling = useRef(""); const {getDraft, saveDraft} = useDrafts(dialog); const avatars = useAvatars( hasGroup(dialog) ? dialog : publicKey , false); const editableDivRef = useRef(null); const getUserFromCache = useUserCacheFunc(); const regexp = new RegExp(/@([\w\d_]{3,})$/); useHotkeys([ ['Esc', () => { setAttachments([]); }] ], [], true); const fileDialog = useFileDialog({ multiple: false, //naccept: '*', onChange: async (files) => { if(!files){ return; } if(files.length == 0){ return; } if(attachments.length >= MAX_ATTACHMENTS_IN_MESSAGE){ return; } if(attachments.find(a => ATTACHMENTS_NOT_ALLOWED_TO_REPLY.includes(a.type))){ return; } const file = files[0]; if(((file.size / 1024) / 1024) > MAX_UPLOAD_FILESIZE_MB){ return; } const fileContent = await filePrapareForNetworkTransfer(file); setAttachments([...attachments, { blob: fileContent, id: generateRandomKey(8), type: AttachmentType.FILE, preview: files[0].size + "::" + files[0].name }]); } }); useEffect(() => { const draftMessage = getDraft(); console.info("GET DRAFT", draftMessage); if(draftMessage == "" || !editableDivRef){ return; } setMessage(draftMessage); editableDivRef.current.insertHTMLInCurrentCarretPosition(draftMessage); }, [dialog, editableDivRef]); useEffect(() => { if(replyMessages.inDialogInput && replyMessages.inDialogInput == dialog){ setAttachments([{ type: AttachmentType.MESSAGES, id: generateRandomKey(8), blob: JSON.stringify([...replyMessages.messages]), preview: "" }]); editableDivRef.current.focus(); } }, [dialog, replyMessages]); useEffect(() => { saveDraft(message); if(regexp.test(message) && hasGroup(dialog)){ const username = regexp.exec(message); if(!username){ return; } if(username[1].length > 2){ handleMention(username[1]); } }else{ setMentionList([]); } }, [message]); if(systemAccounts.find((acc) => acc.publicKey == dialog)){ return <>; } const handleMention = async (username: string) => { const regexpToFindAllMentionedUsernames = new RegExp(`@([\\w\\d_]{2,})`, 'g'); const mentionedUsernamesInMessage = message.match(regexpToFindAllMentionedUsernames); const mentionsList : Mention[] = []; if(!isAdmin && username.startsWith('adm') && (mentionedUsernamesInMessage && !mentionedUsernamesInMessage.includes('@admin'))){ mentionsList.push({ username: 'admin', title: 'Administrator', publicKey: '' }); } for(let i = 0; i < members.length; i++){ const userInfo = await getUserFromCache(members[i]); if(!userInfo){ continue; } if(!userInfo.username.startsWith(username)){ continue; } if(mentionedUsernamesInMessage && mentionedUsernamesInMessage.includes(`@${userInfo.username}`)){ continue; } mentionsList.push({ username: userInfo.username, title: userInfo.title, publicKey: userInfo.publicKey }); } setMentionList(mentionsList); mentionHandling.current = username; } const send = () => { if(blocked || (message.trim() == "" && attachments.length <= 0)) { return; } sendMessage(message, attachments); editableDivRef.current.clear(); setAttachments([]); deselectAllMessages(); } const handleKeyDown = (event: React.KeyboardEvent) => { if (event.key === 'Enter' && !event.shiftKey) { event.preventDefault(); if(mentionList.length <= 0){ send(); } } if (!event.shiftKey && event.key.length === 1 && !blocked) { if (!typingTimeoutRef.current) { sendTypeingPacket(); typingTimeoutRef.current = setTimeout(() => { typingTimeoutRef.current = null; }, 3000); } } }; const onRemoveAttachment = (attachment: Attachment) => { setAttachments(attachments.filter(a => a.id != attachment.id)); editableDivRef.current.focus(); } const onClickPaperclip = () => { fileDialog.open(); } const onClickCamera = async () => { if(avatars.length == 0){ return; } setAttachments([{ blob: avatars[0].avatar, id: generateRandomKey(8), type: AttachmentType.AVATAR, preview: await base64ImageToBlurhash(avatars[0].avatar) }]); editableDivRef.current.focus(); } const sendTypeingPacket = () => { let packet = new PacketTyping(); packet.setToPublicKey(dialog); packet.setFromPublicKey(publicKey); packet.setPrivateKey(privateKey); sendPacket(packet); } const onPaste = async (event: React.ClipboardEvent) => { if(attachments.length >= MAX_ATTACHMENTS_IN_MESSAGE){ return; } if(attachments.find(a => ATTACHMENTS_NOT_ALLOWED_TO_REPLY.includes(a.type))){ return; } const items = event.clipboardData.items; for (let i = 0; i < items.length; i++) { const item = items[i]; if (item.type.startsWith("image/")) { const file = item.getAsFile(); if (file) { const base64Image = await imagePrepareForNetworkTransfer(file); setAttachments([...attachments, { blob: base64Image, id: generateRandomKey(8), type: AttachmentType.IMAGE, preview: await base64ImageToBlurhash(base64Image) }]); } editableDivRef.current.focus(); break; } } } const onDragAndDrop = async (files : File[]) => { if(!files){ return; } if(files.length == 0){ return; } if(attachments.length >= MAX_ATTACHMENTS_IN_MESSAGE){ return; } if(files.length > 1){ return; } const file = files[0]; if(((file.size / 1024) / 1024) > MAX_UPLOAD_FILESIZE_MB){ return; } let fileContent = await filePrapareForNetworkTransfer(file); setAttachments([...attachments, { blob: fileContent, id: generateRandomKey(8), type: AttachmentType.FILE, preview: files[0].size + "::" + files[0].name }]); } const onEmojiClick = (emojiData : EmojiClickData) => { editableDivRef.current.insertHTML( `:emoji_${emojiData.unified}:` ); } const onSelectMention = (mention : Mention) => { editableDivRef.current.insertHTMLInCurrentCarretPosition(mention.username.substring(mentionHandling.current.length)); mentionHandling.current = ""; } return ( <> 0}> {(styles) => ( )} {attachments.length > 0 && {attachments.map((m) => ( ))} } Drop files here to attach without compression {!isBannedGroup && ( {!blocked && Attach } onClick={onClickPaperclip}>File {((avatars.length > 0 && !hasGroup(dialog)) || (avatars.length > 0 && hasGroup(dialog) && isAdmin)) && } onClick={onClickCamera}>Avatar {hasGroup(dialog) && 'group'}} } {blocked && You need unblock user for send messages. } )} {isBannedGroup && ( { leaveGroup(dialog) }} animated={[ '#ff5656', '#e03131' ]} color="red" animationDurationMs={1000} w={'80%'} size={'sm'} radius={'xl'} leftSection={ } mb={'md'}>Leave )} ) }