UNPKG

stream-chat-react

Version:

React components to create chat conversations or livestream style chat

97 lines (96 loc) 4.47 kB
import React, { useCallback, useContext, useEffect, useMemo, useRef } from 'react'; import { useDropzone } from 'react-dropzone'; import clsx from 'clsx'; import { useMessageInputContext, useTranslationContext } from '../../context'; import { useAttachmentManagerState, useMessageComposer } from './hooks'; import { useStateStore } from '../../store'; const DragAndDropUploadContext = React.createContext({ subscribeToDrop: null, }); export const useDragAndDropUploadContext = () => useContext(DragAndDropUploadContext); /** * @private This hook should be used only once directly in the `MessageInputProvider` to * register `uploadNewFiles` functions of the rendered `MessageInputs`. Each `MessageInput` * will then be notified when the drop event occurs from within the `WithDragAndDropUpload` * component. */ export const useRegisterDropHandlers = () => { const { subscribeToDrop } = useDragAndDropUploadContext(); const messageComposer = useMessageComposer(); useEffect(() => { const unsubscribe = subscribeToDrop?.(messageComposer.attachmentManager.uploadFiles); return unsubscribe; }, [subscribeToDrop, messageComposer]); }; const attachmentManagerConfigStateSelector = (state) => ({ acceptedFiles: state.attachments.acceptedFiles, multipleUploads: state.attachments.maxNumberOfFilesPerMessage > 1, }); /** * Wrapper to replace now deprecated `Channel.dragAndDropWindow` option. * * @example * ```tsx * <Channel> * <WithDragAndDropUpload component="section" className="message-list-dnd-wrapper"> * <Window> * <MessageList /> * <MessageInput /> * </Window> * </WithDragAndDropUpload> * <Thread /> * <Channel> * ``` */ export const WithDragAndDropUpload = ({ children, className, component: Component = 'div', style, }) => { const dropHandlersRef = useRef(new Set()); const { t } = useTranslationContext(); const messageInputContext = useMessageInputContext(); const dragAndDropUploadContext = useDragAndDropUploadContext(); const messageComposer = useMessageComposer(); const { isUploadEnabled } = useAttachmentManagerState(); const { acceptedFiles, multipleUploads } = useStateStore(messageComposer.configState, attachmentManagerConfigStateSelector); // if message input context is available, there's no need to use the queue const isWithinMessageInputContext = Object.keys(messageInputContext).length > 0; const accept = useMemo(() => acceptedFiles.reduce((mediaTypeMap, mediaType) => { mediaTypeMap[mediaType] ?? (mediaTypeMap[mediaType] = []); return mediaTypeMap; }, {}), [acceptedFiles]); const subscribeToDrop = useCallback((fn) => { dropHandlersRef.current.add(fn); return () => { dropHandlersRef.current.delete(fn); }; }, []); const handleDrop = useCallback((files) => { dropHandlersRef.current.forEach((fn) => fn(files)); }, []); const { getRootProps, isDragActive, isDragReject } = useDropzone({ accept, // apply `disabled` rules if available, otherwise allow anything and // let the `uploadNewFiles` handle the limitations internally disabled: isWithinMessageInputContext ? !isUploadEnabled || (messageInputContext.cooldownRemaining ?? 0) > 0 : false, multiple: multipleUploads, noClick: true, onDrop: isWithinMessageInputContext ? messageComposer.attachmentManager.uploadFiles : handleDrop, }); // nested WithDragAndDropUpload components render wrappers without functionality // (MessageInputFlat has a default WithDragAndDropUpload) if (dragAndDropUploadContext.subscribeToDrop !== null) { return React.createElement(Component, { className: className }, children); } return (React.createElement(DragAndDropUploadContext.Provider, { value: { subscribeToDrop, } }, React.createElement(Component, { ...getRootProps({ className, style }) }, isDragActive && (React.createElement("div", { className: clsx('str-chat__dropzone-container', { 'str-chat__dropzone-container--not-accepted': isDragReject, }) }, !isDragReject && React.createElement("p", null, t('Drag your files here')), isDragReject && React.createElement("p", null, t('Some of the files will not be accepted')))), children))); };