// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. import React, {useCallback, useEffect, useRef} from 'react'; import {useIntl} from 'react-intl'; import {addFilesToDraft, removeDraft} from '@actions/local/draft'; import {useServerUrl} from '@context/server'; import useFileUploadError from '@hooks/file_upload_error'; import DraftEditPostUploadManager from '@managers/draft_upload_manager'; import {fileMaxWarning, fileSizeWarning, uploadDisabledWarning} from '@utils/file'; import SendHandler from '../send_handler'; import type {ErrorHandlers} from '@typings/components/upload_error_handlers'; import type {AvailableScreens} from '@typings/screens/navigation'; type Props = { testID?: string; channelId: string; cursorPosition: number; rootId?: string; canShowPostPriority?: boolean; files?: FileInfo[]; maxFileCount: number; maxFileSize: number; canUploadFiles: boolean; updateCursorPosition: React.Dispatch>; updatePostInputTop: (top: number) => void; updateValue: React.Dispatch>; value: string; setIsFocused: (isFocused: boolean) => void; onPostCreated?: (postId: string) => void; location?: AvailableScreens; } const emptyFileList: FileInfo[] = []; export default function DraftHandler(props: Props) { const { testID, channelId, cursorPosition, rootId = '', canShowPostPriority, files, maxFileCount, maxFileSize, canUploadFiles, updateCursorPosition, updatePostInputTop, updateValue, value, setIsFocused, onPostCreated, location, } = props; const serverUrl = useServerUrl(); const intl = useIntl(); const uploadErrorHandlers = useRef({}); const {uploadError, newUploadError} = useFileUploadError(); const clearDraft = useCallback(() => { removeDraft(serverUrl, channelId, rootId); updateValue(''); }, [serverUrl, channelId, rootId, updateValue]); const addFiles = useCallback((newFiles: FileInfo[]) => { if (!newFiles.length) { return; } if (!canUploadFiles) { newUploadError(uploadDisabledWarning(intl)); return; } const currentFileCount = files?.length || 0; const availableCount = maxFileCount - currentFileCount; if (newFiles.length > availableCount) { newUploadError(fileMaxWarning(intl, maxFileCount)); return; } const largeFile = newFiles.find((file) => file.size > maxFileSize); if (largeFile) { newUploadError(fileSizeWarning(intl, maxFileSize)); return; } addFilesToDraft(serverUrl, channelId, rootId, newFiles); for (const file of newFiles) { DraftEditPostUploadManager.prepareUpload(serverUrl, file, channelId, rootId); uploadErrorHandlers.current[file.clientId!] = DraftEditPostUploadManager.registerErrorHandler(file.clientId!, newUploadError); } newUploadError(null); }, [intl, newUploadError, maxFileSize, serverUrl, files?.length, channelId, rootId, canUploadFiles, maxFileCount]); // This effect mainly handles keeping clean the uploadErrorHandlers, and // reinstantiate them on component mount and file retry. useEffect(() => { let loadingFiles: FileInfo[] = []; if (files) { loadingFiles = files.filter((v) => v.clientId && DraftEditPostUploadManager.isUploading(v.clientId)); } for (const key of Object.keys(uploadErrorHandlers.current)) { if (!loadingFiles.find((v) => v.clientId === key)) { uploadErrorHandlers.current[key]?.(); delete (uploadErrorHandlers.current[key]); } } for (const file of loadingFiles) { if (!uploadErrorHandlers.current[file.clientId!]) { uploadErrorHandlers.current[file.clientId!] = DraftEditPostUploadManager.registerErrorHandler(file.clientId!, newUploadError); } } }, [files, newUploadError]); return ( ); }