Chats: support multiple attachments

environments/review-multiple-a-zwess7/deployments/2611
Alex Gleason 2 years ago
parent dbf2e53b93
commit ef5001d38b
No known key found for this signature in database
GPG Key ID: 7211D1F99744FBB7

@ -73,6 +73,7 @@ const ChatComposer = React.forwardRef<HTMLTextAreaElement | null, IChatComposer>
const isBlocked = useAppSelector((state) => state.getIn(['relationships', chat?.account?.id, 'blocked_by']));
const isBlocking = useAppSelector((state) => state.getIn(['relationships', chat?.account?.id, 'blocking']));
const maxCharacterCount = useAppSelector((state) => state.instance.getIn(['configuration', 'chats', 'max_characters']) as number);
const attachmentLimit = useAppSelector(state => state.instance.configuration.getIn(['chats', 'max_media_attachments']) as number);
const [suggestions, setSuggestions] = useState<Suggestion>(initialSuggestionState);
const isSuggestionsAvailable = suggestions.list.length > 0;
@ -172,6 +173,7 @@ const ChatComposer = React.forwardRef<HTMLTextAreaElement | null, IChatComposer>
resetFileKey={resetFileKey}
iconClassName='w-5 h-5'
className='text-primary-500'
disabled={attachments.length >= attachmentLimit}
/>
</Stack>
)}

@ -5,17 +5,21 @@ import { defineMessages, useIntl } from 'react-intl';
import { uploadMedia } from 'soapbox/actions/media';
import { Stack } from 'soapbox/components/ui';
import { useAppDispatch } from 'soapbox/hooks';
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
import { normalizeAttachment } from 'soapbox/normalizers';
import { IChat, useChatActions } from 'soapbox/queries/chats';
import toast from 'soapbox/toast';
import ChatComposer from './chat-composer';
import ChatMessageList from './chat-message-list';
import type { Attachment } from 'soapbox/types/entities';
const fileKeyGen = (): number => Math.floor((Math.random() * 0x10000));
const messages = defineMessages({
failedToSend: { id: 'chat.failed_to_send', defaultMessage: 'Message failed to send.' },
uploadErrorLimit: { id: 'upload_error.limit', defaultMessage: 'File upload limit exceeded.' },
});
interface ChatInterface {
@ -49,18 +53,19 @@ const Chat: React.FC<ChatInterface> = ({ chat, inputRef, className }) => {
const dispatch = useAppDispatch();
const { createChatMessage, acceptChat } = useChatActions(chat.id);
const attachmentLimit = useAppSelector(state => state.instance.configuration.getIn(['chats', 'max_media_attachments']) as number);
const [content, setContent] = useState<string>('');
const [attachment, setAttachment] = useState<any>(undefined);
const [attachments, setAttachments] = useState<Attachment[]>([]);
const [isUploading, setIsUploading] = useState(false);
const [uploadProgress, setUploadProgress] = useState(0);
const [resetFileKey, setResetFileKey] = useState<number>(fileKeyGen());
const [errorMessage, setErrorMessage] = useState<string>();
const isSubmitDisabled = content.length === 0 && !attachment;
const isSubmitDisabled = content.length === 0 && attachments.length === 0;
const submitMessage = () => {
createChatMessage.mutate({ chatId: chat.id, content, mediaId: attachment?.id }, {
createChatMessage.mutate({ chatId: chat.id, content, mediaIds: attachments.map(a => a.id) }, {
onSuccess: () => {
setErrorMessage(undefined);
},
@ -79,7 +84,7 @@ const Chat: React.FC<ChatInterface> = ({ chat, inputRef, className }) => {
clearNativeInputValue(inputRef.current);
}
setContent('');
setAttachment(undefined);
setAttachments([]);
setIsUploading(false);
setUploadProgress(0);
setResetFileKey(fileKeyGen());
@ -127,7 +132,7 @@ const Chat: React.FC<ChatInterface> = ({ chat, inputRef, className }) => {
const handleMouseOver = () => markRead();
const handleRemoveFile = () => {
setAttachment(undefined);
setAttachments([]);
setResetFileKey(fileKeyGen());
};
@ -137,13 +142,18 @@ const Chat: React.FC<ChatInterface> = ({ chat, inputRef, className }) => {
};
const handleFiles = (files: FileList) => {
if (files.length + attachments.length > attachmentLimit) {
toast.error(messages.uploadErrorLimit);
return;
}
setIsUploading(true);
const data = new FormData();
data.append('file', files[0]);
dispatch(uploadMedia(data, onUploadProgress)).then((response: any) => {
setAttachment(normalizeAttachment(response.data));
setAttachments([...attachments, normalizeAttachment(response.data)]);
setIsUploading(false);
}).catch(() => {
setIsUploading(false);
@ -172,7 +182,7 @@ const Chat: React.FC<ChatInterface> = ({ chat, inputRef, className }) => {
onSelectFile={handleFiles}
resetFileKey={resetFileKey}
onPaste={handlePaste}
attachments={attachment ? [attachment] : []}
attachments={attachments}
onDeleteAttachment={handleRemoveFile}
isUploading={isUploading}
uploadProgress={uploadProgress}

@ -22,7 +22,8 @@ export const InstanceRecord = ImmutableRecord({
configuration: ImmutableMap<string, any>({
media_attachments: ImmutableMap<string, any>(),
chats: ImmutableMap<string, number>({
max_characters: 500,
max_characters: 5000,
max_media_attachments: 1,
}),
polls: ImmutableMap<string, number>({
max_options: 4,

@ -233,9 +233,13 @@ const useChatActions = (chatId: string) => {
};
const createChatMessage = useMutation(
(
{ chatId, content, mediaId }: { chatId: string, content: string, mediaId?: string },
) => api.post<IChatMessage>(`/api/v1/pleroma/chats/${chatId}/messages`, { content, media_id: mediaId, media_ids: [mediaId] }),
({ chatId, content, mediaIds }: { chatId: string, content: string, mediaIds?: string[] }) => {
return api.post<IChatMessage>(`/api/v1/pleroma/chats/${chatId}/messages`, {
content,
media_id: (mediaIds && mediaIds.length === 1) ? mediaIds[0] : undefined, // Pleroma backwards-compat
media_ids: mediaIds,
});
},
{
retry: false,
onMutate: async (variables) => {

Loading…
Cancel
Save