stream-chat-react
Version:
React components to create chat conversations or livestream style chat
71 lines (70 loc) • 6.29 kB
JavaScript
import React, { useCallback, useState } from 'react';
import { AttachmentSelector as DefaultAttachmentSelector, SimpleAttachmentSelector, } from './AttachmentSelector';
import { AttachmentPreviewList as DefaultAttachmentPreviewList } from './AttachmentPreviewList';
import { CooldownTimer as DefaultCooldownTimer } from './CooldownTimer';
import { SendButton as DefaultSendButton } from './SendButton';
import { StopAIGenerationButton as DefaultStopAIGenerationButton } from './StopAIGenerationButton';
import { AudioRecorder as DefaultAudioRecorder, RecordingPermissionDeniedNotification as DefaultRecordingPermissionDeniedNotification, StartRecordingAudioButton as DefaultStartRecordingAudioButton, RecordingPermission, } from '../MediaRecorder';
import { QuotedMessagePreview as DefaultQuotedMessagePreview, QuotedMessagePreviewHeader, } from './QuotedMessagePreview';
import { LinkPreviewList as DefaultLinkPreviewList } from './LinkPreviewList';
import { SendToChannelCheckbox as DefaultSendToChannelCheckbox } from './SendToChannelCheckbox';
import { TextareaComposer as DefaultTextareaComposer } from '../TextareaComposer';
import { AIStates, useAIState } from '../AIStateIndicator';
import { RecordingAttachmentType } from '../MediaRecorder/classes';
import { useChatContext } from '../../context/ChatContext';
import { useMessageInputContext } from '../../context/MessageInputContext';
import { useComponentContext } from '../../context/ComponentContext';
import { useAttachmentManagerState } from './hooks/useAttachmentManagerState';
import { useMessageContext } from '../../context';
import { WithDragAndDropUpload } from './WithDragAndDropUpload';
export const MessageInputFlat = () => {
const { message } = useMessageContext();
const { asyncMessagesMultiSendEnabled, cooldownRemaining, handleSubmit, hideSendButton, recordingController, setCooldownRemaining, } = useMessageInputContext('MessageInputFlat');
const { AttachmentPreviewList = DefaultAttachmentPreviewList, AttachmentSelector = message ? SimpleAttachmentSelector : DefaultAttachmentSelector, AudioRecorder = DefaultAudioRecorder, CooldownTimer = DefaultCooldownTimer, EmojiPicker, LinkPreviewList = DefaultLinkPreviewList, QuotedMessagePreview = DefaultQuotedMessagePreview, RecordingPermissionDeniedNotification = DefaultRecordingPermissionDeniedNotification, SendButton = DefaultSendButton, SendToChannelCheckbox = DefaultSendToChannelCheckbox, StartRecordingAudioButton = DefaultStartRecordingAudioButton, StopAIGenerationButton: StopAIGenerationButtonOverride, TextareaComposer = DefaultTextareaComposer, } = useComponentContext();
const { channel } = useChatContext('MessageInputFlat');
const { aiState } = useAIState(channel);
const stopGenerating = useCallback(() => channel?.stopAIResponse(), [channel]);
const [showRecordingPermissionDeniedNotification, setShowRecordingPermissionDeniedNotification,] = useState(false);
const closePermissionDeniedNotification = useCallback(() => {
setShowRecordingPermissionDeniedNotification(false);
}, []);
const { attachments } = useAttachmentManagerState();
if (recordingController.recordingState)
return React.createElement(AudioRecorder, null);
const recordingEnabled = !!(recordingController.recorder && navigator.mediaDevices); // account for requirement on iOS as per this bug report: https://bugs.webkit.org/show_bug.cgi?id=252303
const isRecording = !!recordingController.recordingState;
/**
* This bit here is needed to make sure that we can get rid of the default behaviour
* if need be. Essentially, this allows us to pass StopAIGenerationButton={null} and
* completely circumvent the default logic if it's not what we want. We need it as a
* prop because there is no other trivial way to override the SendMessage button otherwise.
*/
const StopAIGenerationButton = StopAIGenerationButtonOverride === undefined
? DefaultStopAIGenerationButton
: StopAIGenerationButtonOverride;
const shouldDisplayStopAIGeneration = [AIStates.Thinking, AIStates.Generating].includes(aiState) &&
!!StopAIGenerationButton;
return (React.createElement(WithDragAndDropUpload, { className: 'str-chat__message-input', component: 'div' },
recordingEnabled &&
recordingController.permissionState === 'denied' &&
showRecordingPermissionDeniedNotification && (React.createElement(RecordingPermissionDeniedNotification, { onClose: closePermissionDeniedNotification, permissionName: RecordingPermission.MIC })),
React.createElement(LinkPreviewList, null),
React.createElement(QuotedMessagePreviewHeader, null),
React.createElement("div", { className: 'str-chat__message-input-inner' },
React.createElement(AttachmentSelector, null),
React.createElement("div", { className: 'str-chat__message-textarea-container' },
React.createElement(QuotedMessagePreview, null),
React.createElement(AttachmentPreviewList, null),
React.createElement("div", { className: 'str-chat__message-textarea-with-emoji-picker' },
React.createElement(TextareaComposer, null),
EmojiPicker && React.createElement(EmojiPicker, null))),
shouldDisplayStopAIGeneration ? (React.createElement(StopAIGenerationButton, { onClick: stopGenerating })) : (!hideSendButton && (React.createElement(React.Fragment, null, cooldownRemaining ? (React.createElement(CooldownTimer, { cooldownInterval: cooldownRemaining, setCooldownRemaining: setCooldownRemaining })) : (React.createElement(React.Fragment, null,
React.createElement(SendButton, { sendMessage: handleSubmit }),
recordingEnabled && (React.createElement(StartRecordingAudioButton, { disabled: isRecording ||
(!asyncMessagesMultiSendEnabled &&
attachments.some((a) => a.type === RecordingAttachmentType.VOICE_RECORDING)), onClick: () => {
recordingController.recorder?.start();
setShowRecordingPermissionDeniedNotification(true);
} })))))))),
React.createElement(SendToChannelCheckbox, null)));
};