Skip to content

Commit c002f7d

Browse files
Add WithDragAndDropUpload component
1 parent 5fa6b0f commit c002f7d

File tree

6 files changed

+225
-106
lines changed

6 files changed

+225
-106
lines changed

src/components/Channel/Channel.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,10 @@ export type ChannelProps<
212212
updatedMessage: UpdatedMessage<StreamChatGenerics>,
213213
options?: UpdateMessageOptions,
214214
) => ReturnType<StreamChat<StreamChatGenerics>['updateMessage']>;
215-
/** If true, chat users will be able to drag and drop file uploads to the entire channel window */
215+
/**
216+
* @deprecated Use `WithDragAndDrop` instead (replace default `Input` component, with `MessageInputFlat` wrapped in `WithDragAndDrop`).
217+
* @description If true, chat users will be able to drag and drop file uploads to the entire channel window
218+
*/
216219
dragAndDropWindow?: boolean;
217220
/** Custom UI component to be shown if no active channel is set, defaults to null and skips rendering the Channel component */
218221
EmptyPlaceholder?: React.ReactElement;
@@ -246,7 +249,10 @@ export type ChannelProps<
246249
onMentionsClick?: OnMentionAction<StreamChatGenerics>;
247250
/** Custom action handler function to run on hover of an @mention in a message */
248251
onMentionsHover?: OnMentionAction<StreamChatGenerics>;
249-
/** If `dragAndDropWindow` prop is true, the props to pass to the MessageInput component (overrides props placed directly on MessageInput) */
252+
/**
253+
* @deprecated Use `WithDragAndDrop` instead (replace default `Input` component, with `MessageInputFlat` wrapped in `WithDragAndDrop`).
254+
* @description If `dragAndDropWindow` prop is `true`, the props to pass to the `MessageInput` component (overrides props placed directly on `MessageInput`)
255+
*/
250256
optionalMessageInputProps?: MessageInputProps<StreamChatGenerics, V>;
251257
/** You can turn on/off thumbnail generation for video attachments */
252258
shouldGenerateVideoThumbnail?: boolean;

src/components/MessageInput/MessageInput.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import type {
2626
} from '../../types/types';
2727
import type { URLEnrichmentConfig } from './hooks/useLinkPreviews';
2828
import type { CustomAudioRecordingConfig } from '../MediaRecorder';
29+
import { useHandleDragAndDropQueuedFiles } from './WithDragAndDropUpload';
2930

3031
export type EmojiSearchIndexResult = {
3132
id: string;
@@ -151,6 +152,9 @@ const MessageInputProvider = <
151152
emojiSearchIndex: props.emojiSearchIndex ?? emojiSearchIndex,
152153
});
153154

155+
// @ts-expect-error generics to be removed
156+
useHandleDragAndDropQueuedFiles(messageInputContextValue);
157+
154158
return (
155159
<MessageInputContextProvider<StreamChatGenerics, V> value={messageInputContextValue}>
156160
{props.children}
Lines changed: 62 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import React, { useCallback, useEffect, useMemo, useState } from 'react';
22
import type { Event } from 'stream-chat';
3-
import clsx from 'clsx';
4-
import { useDropzone } from 'react-dropzone';
53
import {
64
AttachmentSelector as DefaultAttachmentSelector,
75
SimpleAttachmentSelector,
@@ -28,17 +26,16 @@ import { RecordingAttachmentType } from '../MediaRecorder/classes';
2826
import { useChatContext } from '../../context/ChatContext';
2927
import { useChannelActionContext } from '../../context/ChannelActionContext';
3028
import { useChannelStateContext } from '../../context/ChannelStateContext';
31-
import { useTranslationContext } from '../../context/TranslationContext';
3229
import { useMessageInputContext } from '../../context/MessageInputContext';
3330
import { useComponentContext } from '../../context/ComponentContext';
3431

3532
import type { DefaultStreamChatGenerics } from '../../types/types';
3633
import { AIStates, useAIState } from '../AIStateIndicator';
34+
import { WithDragAndDropUpload } from './WithDragAndDropUpload';
3735

3836
export const MessageInputFlat = <
3937
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
4038
>() => {
41-
const { t } = useTranslationContext('MessageInputFlat');
4239
const {
4340
asyncMessagesMultiSendEnabled,
4441
attachments,
@@ -48,14 +45,12 @@ export const MessageInputFlat = <
4845
hideSendButton,
4946
isUploadEnabled,
5047
linkPreviews,
51-
maxFilesLeft,
5248
message,
5349
numberOfUploads,
5450
parent,
5551
recordingController,
5652
setCooldownRemaining,
5753
text,
58-
uploadNewFiles,
5954
} = useMessageInputContext<StreamChatGenerics>('MessageInputFlat');
6055

6156
const {
@@ -72,8 +67,6 @@ export const MessageInputFlat = <
7267
StopAIGenerationButton: StopAIGenerationButtonOverride,
7368
} = useComponentContext<StreamChatGenerics>('MessageInputFlat');
7469
const {
75-
acceptedFiles = [],
76-
multipleUploads,
7770
quotedMessage,
7871
} = useChannelStateContext<StreamChatGenerics>('MessageInputFlat');
7972
const { setQuotedMessage } = useChannelActionContext('MessageInputFlat');
@@ -96,23 +89,6 @@ export const MessageInputFlat = <
9689
[attachments],
9790
);
9891

99-
const accept = useMemo(
100-
() =>
101-
acceptedFiles.reduce<Record<string, Array<string>>>((mediaTypeMap, mediaType) => {
102-
mediaTypeMap[mediaType] ??= [];
103-
return mediaTypeMap;
104-
}, {}),
105-
[acceptedFiles],
106-
);
107-
108-
const { getRootProps, isDragActive, isDragReject } = useDropzone({
109-
accept,
110-
disabled: !isUploadEnabled || maxFilesLeft === 0,
111-
multiple: multipleUploads,
112-
noClick: true,
113-
onDrop: uploadNewFiles,
114-
});
115-
11692
useEffect(() => {
11793
const handleQuotedMessageUpdate = (e: Event<StreamChatGenerics>) => {
11894
if (e.message?.id !== quotedMessage?.id) return;
@@ -156,90 +132,76 @@ export const MessageInputFlat = <
156132
!!StopAIGenerationButton;
157133

158134
return (
159-
<>
160-
<div {...getRootProps({ className: 'str-chat__message-input' })}>
161-
{recordingEnabled &&
162-
recordingController.permissionState === 'denied' &&
163-
showRecordingPermissionDeniedNotification && (
164-
<RecordingPermissionDeniedNotification
165-
onClose={closePermissionDeniedNotification}
166-
permissionName={RecordingPermission.MIC}
167-
/>
168-
)}
169-
{findAndEnqueueURLsToEnrich && (
170-
<LinkPreviewList linkPreviews={Array.from(linkPreviews.values())} />
135+
<WithDragAndDropUpload as='div' className='str-chat__message-input'>
136+
{recordingEnabled &&
137+
recordingController.permissionState === 'denied' &&
138+
showRecordingPermissionDeniedNotification && (
139+
<RecordingPermissionDeniedNotification
140+
onClose={closePermissionDeniedNotification}
141+
permissionName={RecordingPermission.MIC}
142+
/>
171143
)}
172-
{isDragActive && (
173-
<div
174-
className={clsx('str-chat__dropzone-container', {
175-
'str-chat__dropzone-container--not-accepted': isDragReject,
176-
})}
177-
>
178-
{!isDragReject && <p>{t<string>('Drag your files here')}</p>}
179-
{isDragReject && <p>{t<string>('Some of the files will not be accepted')}</p>}
180-
</div>
181-
)}
182-
{displayQuotedMessage && <QuotedMessagePreviewHeader />}
183-
184-
<div className='str-chat__message-input-inner'>
185-
<AttachmentSelector />
186-
<div className='str-chat__message-textarea-container'>
187-
{displayQuotedMessage && (
188-
<QuotedMessagePreview quotedMessage={quotedMessage} />
144+
{findAndEnqueueURLsToEnrich && (
145+
<LinkPreviewList linkPreviews={Array.from(linkPreviews.values())} />
146+
)}
147+
{displayQuotedMessage && <QuotedMessagePreviewHeader />}
148+
149+
<div className='str-chat__message-input-inner'>
150+
<AttachmentSelector />
151+
<div className='str-chat__message-textarea-container'>
152+
{displayQuotedMessage && <QuotedMessagePreview quotedMessage={quotedMessage} />}
153+
{isUploadEnabled &&
154+
!!(numberOfUploads + failedUploadsCount || attachments.length > 0) && (
155+
<AttachmentPreviewList />
189156
)}
190-
{isUploadEnabled &&
191-
!!(numberOfUploads + failedUploadsCount || attachments.length > 0) && (
192-
<AttachmentPreviewList />
193-
)}
194157

195-
<div className='str-chat__message-textarea-with-emoji-picker'>
196-
<ChatAutoComplete />
158+
<div className='str-chat__message-textarea-with-emoji-picker'>
159+
<ChatAutoComplete />
197160

198-
{EmojiPicker && <EmojiPicker />}
199-
</div>
161+
{EmojiPicker && <EmojiPicker />}
200162
</div>
201-
{shouldDisplayStopAIGeneration ? (
202-
<StopAIGenerationButton onClick={stopGenerating} />
203-
) : (
204-
!hideSendButton && (
205-
<>
206-
{cooldownRemaining ? (
207-
<CooldownTimer
208-
cooldownInterval={cooldownRemaining}
209-
setCooldownRemaining={setCooldownRemaining}
163+
</div>
164+
{shouldDisplayStopAIGeneration ? (
165+
<StopAIGenerationButton onClick={stopGenerating} />
166+
) : (
167+
!hideSendButton && (
168+
<>
169+
{cooldownRemaining ? (
170+
<CooldownTimer
171+
cooldownInterval={cooldownRemaining}
172+
setCooldownRemaining={setCooldownRemaining}
173+
/>
174+
) : (
175+
<>
176+
<SendButton
177+
disabled={
178+
!numberOfUploads &&
179+
!text.length &&
180+
attachments.length - failedUploadsCount === 0
181+
}
182+
sendMessage={handleSubmit}
210183
/>
211-
) : (
212-
<>
213-
<SendButton
184+
{recordingEnabled && (
185+
<StartRecordingAudioButton
214186
disabled={
215-
!numberOfUploads &&
216-
!text.length &&
217-
attachments.length - failedUploadsCount === 0
187+
isRecording ||
188+
(!asyncMessagesMultiSendEnabled &&
189+
attachments.some(
190+
(a) => a.type === RecordingAttachmentType.VOICE_RECORDING,
191+
))
218192
}
219-
sendMessage={handleSubmit}
193+
onClick={() => {
194+
recordingController.recorder?.start();
195+
setShowRecordingPermissionDeniedNotification(true);
196+
}}
220197
/>
221-
{recordingEnabled && (
222-
<StartRecordingAudioButton
223-
disabled={
224-
isRecording ||
225-
(!asyncMessagesMultiSendEnabled &&
226-
attachments.some(
227-
(a) => a.type === RecordingAttachmentType.VOICE_RECORDING,
228-
))
229-
}
230-
onClick={() => {
231-
recordingController.recorder?.start();
232-
setShowRecordingPermissionDeniedNotification(true);
233-
}}
234-
/>
235-
)}
236-
</>
237-
)}
238-
</>
239-
)
240-
)}
241-
</div>
198+
)}
199+
</>
200+
)}
201+
</>
202+
)
203+
)}
242204
</div>
243-
</>
205+
</WithDragAndDropUpload>
244206
);
245207
};

0 commit comments

Comments
 (0)