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' ;
@@ -11,34 +11,27 @@ import {
1111import type { MessageInputContextValue } from '../../context' ;
1212
1313const DragAndDropUploadContext = React . createContext < {
14- fileQueue : File [ ] ;
15- addFilesToQueue : ( ( files : File [ ] ) => void ) | null ;
16- removeFilesFromQueue : ( ( files : File [ ] ) => void ) | null ;
14+ subscribeToDrop : ( ( fn : ( files : File [ ] ) => void ) => ( ) => void ) | null ;
1715} > ( {
18- addFilesToQueue : null ,
19- fileQueue : [ ] ,
20- removeFilesFromQueue : null ,
16+ subscribeToDrop : null ,
2117} ) ;
2218
2319export const useDragAndDropUploadContext = ( ) => useContext ( DragAndDropUploadContext ) ;
2420
2521/**
26- * @private To maintain top -> bottom data flow, the drag-and-drop functionality allows dragging any files to the queue - the closest
27- * `MessageInputProvider` will be notified through `DragAndDropUploadContext.fileQueue` and starts the upload with `uploadNewAttachments`,
28- * forwarded files are removed from the queue immediately after.
22+ * @private This hook should be used only once directly in the `MessageInputProvider` to
23+ * register `uploadNewFiles` functions of the rendered `MessageInputs`. Each `MessageInput`
24+ * will then be notified when the drop event occurs from within the `WithDragAndDropUpload`
25+ * component.
2926 */
30- export const useHandleDragAndDropQueuedFiles = ( {
31- uploadNewFiles,
32- } : MessageInputContextValue ) => {
33- const { fileQueue, removeFilesFromQueue } = useDragAndDropUploadContext ( ) ;
27+ export const useRegisterDropHandlers = ( { uploadNewFiles } : MessageInputContextValue ) => {
28+ const { subscribeToDrop } = useDragAndDropUploadContext ( ) ;
3429
3530 useEffect ( ( ) => {
36- if ( ! removeFilesFromQueue ) return ;
31+ const unsubscribe = subscribeToDrop ?. ( uploadNewFiles ) ;
3732
38- uploadNewFiles ( fileQueue ) ;
39-
40- removeFilesFromQueue ( fileQueue ) ;
41- } , [ fileQueue , removeFilesFromQueue , uploadNewFiles ] ) ;
33+ return unsubscribe ;
34+ } , [ subscribeToDrop , uploadNewFiles ] ) ;
4235} ;
4336
4437/**
@@ -71,7 +64,7 @@ export const WithDragAndDropUpload = ({
7164 className ?: string ;
7265 style ?: CSSProperties ;
7366} > ) => {
74- const [ files , setFiles ] = useState < File [ ] > ( [ ] ) ;
67+ const dropHandlersRef = useRef < Set < ( f : File [ ] ) => void > > ( new Set ( ) ) ;
7568 const { acceptedFiles = [ ] , multipleUploads } = useChannelStateContext ( ) ;
7669 const { t } = useTranslationContext ( ) ;
7770
@@ -91,13 +84,16 @@ export const WithDragAndDropUpload = ({
9184 [ acceptedFiles ] ,
9285 ) ;
9386
94- const addFilesToQueue = useCallback ( ( files : File [ ] ) => {
95- setFiles ( ( cv ) => cv . concat ( files ) ) ;
87+ const subscribeToDrop = useCallback ( ( fn : ( files : File [ ] ) => void ) => {
88+ dropHandlersRef . current . add ( fn ) ;
89+
90+ return ( ) => {
91+ dropHandlersRef . current . delete ( fn ) ;
92+ } ;
9693 } , [ ] ) ;
9794
98- const removeFilesFromQueue = useCallback ( ( files : File [ ] ) => {
99- if ( ! files . length ) return ;
100- setFiles ( ( cv ) => cv . filter ( ( f ) => files . indexOf ( f ) === - 1 ) ) ;
95+ const handleDrop = useCallback ( ( files : File [ ] ) => {
96+ dropHandlersRef . current . forEach ( ( fn ) => fn ( files ) ) ;
10197 } , [ ] ) ;
10298
10399 const { getRootProps, isDragActive, isDragReject } = useDropzone ( {
@@ -109,20 +105,20 @@ export const WithDragAndDropUpload = ({
109105 : false ,
110106 multiple : multipleUploads ,
111107 noClick : true ,
112- onDrop : isWithinMessageInputContext
113- ? messageInputContext . uploadNewFiles
114- : addFilesToQueue ,
108+ onDrop : isWithinMessageInputContext ? messageInputContext . uploadNewFiles : handleDrop ,
115109 } ) ;
116110
117111 // nested WithDragAndDropUpload components render wrappers without functionality
118112 // (MessageInputFlat has a default WithDragAndDropUpload)
119- if ( dragAndDropUploadContext . removeFilesFromQueue !== null ) {
113+ if ( dragAndDropUploadContext . subscribeToDrop !== null ) {
120114 return < Component className = { className } > { children } </ Component > ;
121115 }
122116
123117 return (
124118 < DragAndDropUploadContext . Provider
125- value = { { addFilesToQueue, fileQueue : files , removeFilesFromQueue } }
119+ value = { {
120+ subscribeToDrop,
121+ } }
126122 >
127123 < Component { ...getRootProps ( { className, style } ) } >
128124 { /* TODO: could be a replaceable component */ }
0 commit comments