import { OrderedSet as ImmutableOrderedSet } from 'immutable'; import React, { useRef, useState } from 'react'; import { useIntl, defineMessages } from 'react-intl'; import { sendChatMessage, markChatRead, } from 'soapbox/actions/chats'; import { uploadMedia } from 'soapbox/actions/media'; import IconButton from 'soapbox/components/icon_button'; import UploadProgress from 'soapbox/components/upload-progress'; import UploadButton from 'soapbox/features/compose/components/upload_button'; import { useAppSelector, useAppDispatch } from 'soapbox/hooks'; import { truncateFilename } from 'soapbox/utils/media'; import ChatMessageList from './chat_message_list'; const messages = defineMessages({ placeholder: { id: 'chat_box.input.placeholder', defaultMessage: 'Send a messageā€¦' }, send: { id: 'chat_box.actions.send', defaultMessage: 'Send' }, }); const fileKeyGen = (): number => Math.floor((Math.random() * 0x10000)); interface IChatBox { chatId: string, onSetInputRef: (el: HTMLTextAreaElement) => void, } /** * Chat UI with just the messages and textarea. * Reused between floating desktop chats and fullscreen/mobile chats. */ const ChatBox: React.FC = ({ chatId, onSetInputRef }) => { const intl = useIntl(); const dispatch = useAppDispatch(); const chatMessageIds = useAppSelector(state => state.chat_message_lists.get(chatId, ImmutableOrderedSet())); const [content, setContent] = useState(''); const [attachment, setAttachment] = useState(undefined); const [isUploading, setIsUploading] = useState(false); const [uploadProgress, setUploadProgress] = useState(0); const [resetFileKey, setResetFileKey] = useState(fileKeyGen()); const inputElem = useRef(null); const clearState = () => { setContent(''); setAttachment(undefined); setIsUploading(false); setUploadProgress(0); setResetFileKey(fileKeyGen()); }; const getParams = () => { return { content, media_id: attachment && attachment.id, }; }; const canSubmit = () => { const conds = [ content.length > 0, attachment, ]; return conds.some(c => c); }; const sendMessage = () => { if (canSubmit() && !isUploading) { const params = getParams(); dispatch(sendChatMessage(chatId, params)); clearState(); } }; const insertLine = () => { setContent(content + '\n'); }; const handleKeyDown: React.KeyboardEventHandler = (e) => { markRead(); if (e.key === 'Enter' && e.shiftKey) { insertLine(); e.preventDefault(); } else if (e.key === 'Enter') { sendMessage(); e.preventDefault(); } }; const handleContentChange: React.ChangeEventHandler = (e) => { setContent(e.target.value); }; const markRead = () => { dispatch(markChatRead(chatId)); }; const handleHover = () => { markRead(); }; const setInputRef = (el: HTMLTextAreaElement) => { inputElem.current = el; onSetInputRef(el); }; const handleRemoveFile = () => { setAttachment(undefined); setResetFileKey(fileKeyGen()); }; const onUploadProgress = (e: ProgressEvent) => { const { loaded, total } = e; setUploadProgress(loaded / total); }; const handleFiles = (files: FileList) => { setIsUploading(true); const data = new FormData(); data.append('file', files[0]); dispatch(uploadMedia(data, onUploadProgress)).then((response: any) => { setAttachment(response.data); setIsUploading(false); }).catch(() => { setIsUploading(false); }); }; const renderAttachment = () => { if (!attachment) return null; return (
{truncateFilename(attachment.preview_url, 20)}
); }; const renderActionButton = () => { return canSubmit() ? ( ) : ( ); }; if (!chatMessageIds) return null; return (
{renderAttachment()} {isUploading && ( )}
{renderActionButton()}