1- import React , { useCallback , useContext , useEffect , useMemo , useState } from 'react' ;
1+ import React , { useCallback , useContext , useEffect , useMemo , useRef } from 'react' ;
22import { useDropzone } from 'react-dropzone' ;
33import clsx from 'clsx' ;
44import type { CSSProperties , ElementType , PropsWithChildren } from 'react' ;
@@ -13,34 +13,29 @@ import { useAttachmentManagerState, useMessageComposer } from './hooks';
1313import { useStateStore } from '../../store' ;
1414
1515const DragAndDropUploadContext = React . createContext < {
16- fileQueue : File [ ] ;
17- addFilesToQueue : ( ( files : File [ ] ) => void ) | null ;
18- removeFilesFromQueue : ( ( files : File [ ] ) => void ) | null ;
16+ subscribeToDrop : ( ( fn : ( files : File [ ] ) => void ) => ( ) => void ) | null ;
1917} > ( {
20- addFilesToQueue : null ,
21- fileQueue : [ ] ,
22- removeFilesFromQueue : null ,
18+ subscribeToDrop : null ,
2319} ) ;
2420
2521export const useDragAndDropUploadContext = ( ) => useContext ( DragAndDropUploadContext ) ;
2622
2723/**
28- * @private To maintain top -> bottom data flow, the drag-and-drop functionality allows dragging any files to the queue - the closest
29- * `MessageInputProvider` will be notified through `DragAndDropUploadContext.fileQueue` and starts the upload with `uploadNewAttachments`,
30- * forwarded files are removed from the queue immediately after.
24+ * @private This hook should be used only once directly in the `MessageInputProvider` to
25+ * register `uploadNewFiles` functions of the rendered `MessageInputs`. Each `MessageInput`
26+ * will then be notified when the drop event occurs from within the `WithDragAndDropUpload`
27+ * component.
3128 */
32- export const useHandleDragAndDropQueuedFiles = ( ) => {
33- const { fileQueue , removeFilesFromQueue } = useDragAndDropUploadContext ( ) ;
29+ export const useRegisterDropHandlers = ( ) => {
30+ const { subscribeToDrop } = useDragAndDropUploadContext ( ) ;
3431
3532 const messageComposer = useMessageComposer ( ) ;
3633
3734 useEffect ( ( ) => {
38- if ( ! removeFilesFromQueue ) return ;
35+ const unsubscribe = subscribeToDrop ?. ( messageComposer . attachmentManager . uploadFiles ) ;
3936
40- messageComposer . attachmentManager . uploadFiles ( fileQueue ) ;
41-
42- removeFilesFromQueue ( fileQueue ) ;
43- } , [ fileQueue , removeFilesFromQueue , messageComposer ] ) ;
37+ return unsubscribe ;
38+ } , [ subscribeToDrop , messageComposer ] ) ;
4439} ;
4540
4641const attachmentManagerConfigStateSelector = ( state : MessageComposerConfig ) => ( {
@@ -77,7 +72,7 @@ export const WithDragAndDropUpload = ({
7772 className ?: string ;
7873 style ?: CSSProperties ;
7974} > ) => {
80- const [ files , setFiles ] = useState < File [ ] > ( [ ] ) ;
75+ const dropHandlersRef = useRef < Set < ( f : File [ ] ) => void > > ( new Set ( ) ) ;
8176 const { acceptedFiles = [ ] } = useChannelStateContext ( ) ;
8277 const { t } = useTranslationContext ( ) ;
8378
@@ -97,13 +92,16 @@ export const WithDragAndDropUpload = ({
9792 [ acceptedFiles ] ,
9893 ) ;
9994
100- const addFilesToQueue = useCallback ( ( files : File [ ] ) => {
101- setFiles ( ( cv ) => cv . concat ( files ) ) ;
95+ const subscribeToDrop = useCallback ( ( fn : ( files : File [ ] ) => void ) => {
96+ dropHandlersRef . current . add ( fn ) ;
97+
98+ return ( ) => {
99+ dropHandlersRef . current . delete ( fn ) ;
100+ } ;
102101 } , [ ] ) ;
103102
104- const removeFilesFromQueue = useCallback ( ( files : File [ ] ) => {
105- if ( ! files . length ) return ;
106- setFiles ( ( cv ) => cv . filter ( ( f ) => files . indexOf ( f ) === - 1 ) ) ;
103+ const handleDrop = useCallback ( ( files : File [ ] ) => {
104+ dropHandlersRef . current . forEach ( ( fn ) => fn ( files ) ) ;
107105 } , [ ] ) ;
108106
109107 const { isUploadEnabled } = useAttachmentManagerState ( ) ;
@@ -123,18 +121,20 @@ export const WithDragAndDropUpload = ({
123121 noClick : true ,
124122 onDrop : isWithinMessageInputContext
125123 ? messageComposer . attachmentManager . uploadFiles
126- : addFilesToQueue ,
124+ : handleDrop ,
127125 } ) ;
128126
129127 // nested WithDragAndDropUpload components render wrappers without functionality
130128 // (MessageInputFlat has a default WithDragAndDropUpload)
131- if ( dragAndDropUploadContext . removeFilesFromQueue !== null ) {
129+ if ( dragAndDropUploadContext . subscribeToDrop !== null ) {
132130 return < Component className = { className } > { children } </ Component > ;
133131 }
134132
135133 return (
136134 < DragAndDropUploadContext . Provider
137- value = { { addFilesToQueue, fileQueue : files , removeFilesFromQueue } }
135+ value = { {
136+ subscribeToDrop,
137+ } }
138138 >
139139 < Component { ...getRootProps ( { className, style } ) } >
140140 { /* TODO: could be a replaceable component */ }
0 commit comments