Merge branch 'chat-multi-upload' into 'develop'

Chats: allow uploading multiple attachments at once (if the backend supports it)

See merge request soapbox-pub/soapbox!2313
This commit is contained in:
Alex Gleason 2023-03-06 18:46:01 +00:00
commit ca9e59b56b
4 changed files with 27 additions and 20 deletions

View File

@ -47,7 +47,7 @@ interface IChatComposer extends Pick<React.TextareaHTMLAttributes<HTMLTextAreaEl
resetContentKey: number | null resetContentKey: number | null
attachments?: Attachment[] attachments?: Attachment[]
onDeleteAttachment?: (i: number) => void onDeleteAttachment?: (i: number) => void
isUploading?: boolean uploadCount?: number
uploadProgress?: number uploadProgress?: number
} }
@ -65,7 +65,7 @@ const ChatComposer = React.forwardRef<HTMLTextAreaElement | null, IChatComposer>
onPaste, onPaste,
attachments = [], attachments = [],
onDeleteAttachment, onDeleteAttachment,
isUploading, uploadCount = 0,
uploadProgress, uploadProgress,
}, ref) => { }, ref) => {
const intl = useIntl(); const intl = useIntl();
@ -82,6 +82,7 @@ const ChatComposer = React.forwardRef<HTMLTextAreaElement | null, IChatComposer>
const [suggestions, setSuggestions] = useState<Suggestion>(initialSuggestionState); const [suggestions, setSuggestions] = useState<Suggestion>(initialSuggestionState);
const isSuggestionsAvailable = suggestions.list.length > 0; const isSuggestionsAvailable = suggestions.list.length > 0;
const isUploading = uploadCount > 0;
const hasAttachment = attachments.length > 0; const hasAttachment = attachments.length > 0;
const isOverCharacterLimit = maxCharacterCount && value?.length > maxCharacterCount; const isOverCharacterLimit = maxCharacterCount && value?.length > maxCharacterCount;
const isSubmitDisabled = disabled || isUploading || isOverCharacterLimit || (value.length === 0 && !hasAttachment); const isSubmitDisabled = disabled || isUploading || isOverCharacterLimit || (value.length === 0 && !hasAttachment);
@ -200,7 +201,7 @@ const ChatComposer = React.forwardRef<HTMLTextAreaElement | null, IChatComposer>
disabled={disabled} disabled={disabled}
attachments={attachments} attachments={attachments}
onDeleteAttachment={onDeleteAttachment} onDeleteAttachment={onDeleteAttachment}
isUploading={isUploading} uploadCount={uploadCount}
uploadProgress={uploadProgress} uploadProgress={uploadProgress}
/> />
{isSuggestionsAvailable ? ( {isSuggestionsAvailable ? (

View File

@ -9,7 +9,7 @@ import ChatUpload from './chat-upload';
interface IChatTextarea extends React.ComponentProps<typeof Textarea> { interface IChatTextarea extends React.ComponentProps<typeof Textarea> {
attachments?: Attachment[] attachments?: Attachment[]
onDeleteAttachment?: (i: number) => void onDeleteAttachment?: (i: number) => void
isUploading?: boolean uploadCount?: number
uploadProgress?: number uploadProgress?: number
} }
@ -17,10 +17,12 @@ interface IChatTextarea extends React.ComponentProps<typeof Textarea> {
const ChatTextarea: React.FC<IChatTextarea> = ({ const ChatTextarea: React.FC<IChatTextarea> = ({
attachments, attachments,
onDeleteAttachment, onDeleteAttachment,
isUploading = false, uploadCount = 0,
uploadProgress = 0, uploadProgress = 0,
...rest ...rest
}) => { }) => {
const isUploading = uploadCount > 0;
const handleDeleteAttachment = (i: number) => { const handleDeleteAttachment = (i: number) => {
return () => { return () => {
if (onDeleteAttachment) { if (onDeleteAttachment) {
@ -54,11 +56,11 @@ const ChatTextarea: React.FC<IChatTextarea> = ({
</div> </div>
))} ))}
{isUploading && ( {Array.from(Array(uploadCount)).map(() => (
<div className='ml-2 mt-2 flex'> <div className='ml-2 mt-2 flex'>
<ChatPendingUpload progress={uploadProgress} /> <ChatPendingUpload progress={uploadProgress} />
</div> </div>
)} ))}
</HStack> </HStack>
)} )}

View File

@ -57,7 +57,7 @@ const Chat: React.FC<ChatInterface> = ({ chat, inputRef, className }) => {
const [content, setContent] = useState<string>(''); const [content, setContent] = useState<string>('');
const [attachments, setAttachments] = useState<Attachment[]>([]); const [attachments, setAttachments] = useState<Attachment[]>([]);
const [isUploading, setIsUploading] = useState(false); const [uploadCount, setUploadCount] = useState(0);
const [uploadProgress, setUploadProgress] = useState(0); const [uploadProgress, setUploadProgress] = useState(0);
const [resetContentKey, setResetContentKey] = useState<number>(fileKeyGen()); const [resetContentKey, setResetContentKey] = useState<number>(fileKeyGen());
const [resetFileKey, setResetFileKey] = useState<number>(fileKeyGen()); const [resetFileKey, setResetFileKey] = useState<number>(fileKeyGen());
@ -86,7 +86,7 @@ const Chat: React.FC<ChatInterface> = ({ chat, inputRef, className }) => {
} }
setContent(''); setContent('');
setAttachments([]); setAttachments([]);
setIsUploading(false); setUploadCount(0);
setUploadProgress(0); setUploadProgress(0);
setResetFileKey(fileKeyGen()); setResetFileKey(fileKeyGen());
setResetContentKey(fileKeyGen()); setResetContentKey(fileKeyGen());
@ -151,17 +151,21 @@ const Chat: React.FC<ChatInterface> = ({ chat, inputRef, className }) => {
return; return;
} }
setIsUploading(true); setUploadCount(files.length);
const promises = Array.from(files).map(async(file) => {
const data = new FormData(); const data = new FormData();
data.append('file', files[0]); data.append('file', file);
const response = await dispatch(uploadMedia(data, onUploadProgress));
dispatch(uploadMedia(data, onUploadProgress)).then((response: any) => { return normalizeAttachment(response.data);
setAttachments([...attachments, normalizeAttachment(response.data)]);
setIsUploading(false);
}).catch(() => {
setIsUploading(false);
}); });
return Promise.all(promises)
.then((newAttachments) => {
setAttachments([...attachments, ...newAttachments]);
setUploadCount(0);
})
.catch(() => setUploadCount(0));
}; };
useEffect(() => { useEffect(() => {
@ -189,7 +193,7 @@ const Chat: React.FC<ChatInterface> = ({ chat, inputRef, className }) => {
onPaste={handlePaste} onPaste={handlePaste}
attachments={attachments} attachments={attachments}
onDeleteAttachment={handleRemoveFile} onDeleteAttachment={handleRemoveFile}
isUploading={isUploading} uploadCount={uploadCount}
uploadProgress={uploadProgress} uploadProgress={uploadProgress}
/> />
</Stack> </Stack>

View File

@ -284,7 +284,7 @@ const getInstanceFeatures = (instance: Instance) => {
* Whether chat messages can accept a `media_id` attachment. * Whether chat messages can accept a `media_id` attachment.
* @see POST /api/v1/pleroma/chats/:id/messages * @see POST /api/v1/pleroma/chats/:id/messages
*/ */
chatsMedia: v.software !== TRUTHSOCIAL, chatsMedia: v.software !== TRUTHSOCIAL || v.build === UNRELEASED,
/** /**
* Whether chat messages have read receipts. * Whether chat messages have read receipts.