stream-chat-react
stream-chat-react copied to clipboard
question: how do I add custom attachment objects to MessageInput state?
Describe the bug
When customizing the MessageInput component to handle file attachments, the message does not properly accept the attachment. Specifically, when trying to send a message with an attachment (image or file), the message fails to include the attachment.
To Reproduce
Steps to reproduce the behavior:
//home component code start
const removeFilePreview = (index) => {
const newFilePreviews = filePreviews.filter((_, i) => i !== index);
setFilePreviews(newFilePreviews);
};
const handleFileClick = () => {
const fileInput = document.createElement("input");
fileInput.type = "file";
fileInput.accept = "*/*";
fileInput.multiple = true;
fileInput.onchange = (e) => {
const files = e.target.files;
if (files.length > 0) {
const previews = Array.from(files).map((file) => ({
file,
preview: URL.createObjectURL(file),
}));
setFilePreviews((prev) => [...prev, ...previews]);
}
};
fileInput.click();
};
const isFileImage = (file) => {
const imageExtensions = ["jpg", "jpeg", "png", "gif", "bmp", "webp"];
const fileExtension = file.name.split(".").pop().toLowerCase();
return imageExtensions.includes(fileExtension);
};
const setMainPreview = (index) => {
const selectedPreview = filePreviews.splice(index, 1)[0];
setFilePreviews([selectedPreview, ...filePreviews]);
};
{filePreviews.length > 0 && (
<div className="absolute z-10 top-16 left-0 right-0 bottom-20 w-2/2 bg-[#E5E4E2] flex flex-col items-center justify-center p-0">
<div className="relative w-full h-[90%]">
<button
onClick={() => removeFilePreview(0)}
type="button"
className="absolute top-0 mt-[-30px] bottom-160 left-10 text-grey p-2"
>
<IoCloseCircleOutline size={30} />
</button>
{isFileImage(filePreviews[0].file) ? (
<img
src={filePreviews[0].preview}
alt="Preview"
className="object-contain h-[85%] w-full px-4"
/>
) : (
<div className="flex flex-col items-center justify-center h-[85%]">
<FaFile size={50} className="text-gray-400 mb-2" />
<p className="text-gray-400">No preview available</p>
</div>
)}
{filePreviews.length > 0 && (
<div className="flex items-center justify-center mb-4 px-4 pt-5">
{filePreviews.map((preview, index) => (
<div
key={index}
className={`relative mr-2 ${
index === 0 ? "border-2 border-blue-500" : ""
}`}
onClick={() => setMainPreview(index)}
>
<button
onClick={(e) => {
e.stopPropagation();
removeFilePreview(index);
}}
type="button"
className="absolute top-0 right-0 text-grey p-1 opacity-0 hover:opacity-100 transition-opacity duration-300 shadow-md"
>
<IoCloseCircleOutline color="grey" size={10} />
</button>
{isFileImage(preview.file) ? (
<img
src={preview.preview}
alt="Preview"
className="object-cover w-14 h-14 rounded"
/>
) : (
<div className="flex flex-col items-center justify-center w-14 h-14 bg-gray-200 rounded">
<FaFile size={24} className="text-gray-500 mb-1" />
<p className="text-[10px] text-gray-500">
No preview
</p>
</div>
)}
</div>
))}
<div
className="w-16 h-16 bg-gray-200 rounded flex items-center justify-center cursor-pointer"
onClick={() => handleFileClick()}
>
<IoAddCircleOutline size={24} />
</div>
</div>
)}
</div>
</div>
)}
<Chat client={streamClient} theme="messaging light">
<Channel channel={channel} >
<Window>
<div className="p-2 bg-[#315A9D]">
<CustomChannelHeader
onClick={toggleContactInfo}
isContactInfoOpen={isContactInfoOpen}
user={user}
/>
</div>
<MessageList
/>
<CustomMessageInput
overrideSubmitHandler={overrideSubmitHandler}
channel={channel}
setFilePreviews={setFilePreviews}
EmojiPicker={EmojiPicker}
filePreviews={filePreviews}
/>
</Window>
</Channel>
</Chat>
///home component
import React, { useState } from "react";
import {
MessageInput,
ChatAutoComplete,
useMessageInputContext,
useChannelActionContext,
Attachment,
} from "stream-chat-react";
import Picker from "@emoji-mart/react";
import { FaMapMarkerAlt, FaImage, FaRegFileAlt } from "react-icons/fa";
import { BsEmojiSmile } from "react-icons/bs";
import {
IoAddCircleOutline,
IoMicOutline,
IoCloseCircleOutline,
} from "react-icons/io5";
import "./style.css";
import { PiHeadphones } from "react-icons/pi";
import Modal from "react-modal";
import client from "../../streamClient";
const customStyles = {
content: {
top: "68%",
left: "28%",
bottom: "0%",
width: "170px",
height: "230px",
borderRadius: "20px",
},
overlay: {
backgroundColor: "transparent",
},
};
Modal.setAppElement("#root");
const CustomMessageInput = ({
isContactInfoOpen,
filePreviews,
setFilePreviews,
// overrideSubmitHandler,
...props
}) => {
const [isOpen, setIsOpen] = useState(false);
const [isEmojiPickerOpen, setIsEmojiPickerOpen] = useState(false);
const { handleSubmit, text, setText, uploadNewFiles } =
useMessageInputContext();
const { sendMessage } = useChannelActionContext();
console.log(filePreviews, "previews");
const toggleModal = () => {
setIsOpen(!isOpen);
};
const handleSmileClick = () => {
setIsEmojiPickerOpen(!isEmojiPickerOpen);
};
const handleEmojiSelect = (emoji) => {
setText((prevText) => prevText + emoji.native);
setIsEmojiPickerOpen(false);
};
const handleFileChange = (e) => {
const files = e.target.files;
if (files.length > 0) {
const previews = Array.from(files).map((file) => ({
file,
preview: URL.createObjectURL(file),
}));
setFilePreviews((prev) => [...prev, ...previews]);
}
toggleModal();
};
const handleFileClick = (acceptType) => {
const fileInput = document.createElement("input");
fileInput.type = "file";
fileInput.accept = acceptType;
fileInput.multiple = true;
fileInput.onchange = handleFileChange;
fileInput.click();
};
const handleLocationClick = () => {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
(position) => {
console.log("Latitude:", position.coords.latitude);
console.log("Longitude:", position.coords.longitude);
},
(error) => {
console.error("Error getting location:", error);
}
);
} else {
console.error("Geolocation is not supported by this browser.");
}
};
const handleSend = async (event) => {
event.preventDefault();
if (filePreviews.length > 0) {
await sendMessage(filePreviews.map((preview) => preview.file));
}
await handleSubmit(event);
setFilePreviews([]);
};
const overrideSubmitHandler = (message, filePreviews) => {
let updatedMessage = message;
console.log(message, 'message14')
if (message.attachments) {
message.attachments.forEach((attachment) => {
if (attachment.type === 'image') {
const updatedAttachment = {
...filePreviews,
};
updatedMessage = {
...message,
attachments: [updatedAttachment],
};
}
});
}
sendMessage(updatedMessage);
};
// const meg = channel.sendImage(filePreviews[0])
return (
<div className={`sticky bottom-0 bg-[#DEE3EF] text-gray-800 p-3`}>
<div className="flex items-center">
<div
className="cursor-pointer mr-2 p-2 rounded-full bg-[#315A9D] text-white flex items-center justify-center"
onClick={handleSmileClick}
>
<BsEmojiSmile size={20} />
</div>
<div
className="cursor-pointer mr-2 p-2 rounded-full bg-[#315A9D] text-white flex items-center justify-center"
onClick={toggleModal}
>
{isOpen ? (
<IoCloseCircleOutline size={20} />
) : (
<IoAddCircleOutline size={20} />
)}
</div>
<MessageInput
// doFileUploadRequest={(file, channel) => {
// return client.sendFile(filePreviews, file);
// }}
// doImageUploadRequest={(file, channel) => {
// return client.sendFile(filePreviews, file);
// doFileUploadRequest = {filePreviews}
// attachments={filePreviews}
{...props}
Input={(inputProps) => (
<div className="flex-1 relative">
<ChatAutoComplete
{...inputProps}
value={text}
// filePreviews={filePreviews}
// onChange={(e) => setText(e.target.value)}
className="bg-white text-gray-800 placeholder-gray-400 px-4 py-1 rounded-lg border border-gray-300"
placeholder="Type Message..."
name="message"
overrideSubmitHandler={overrideSubmitHandler}
// onKeyDown={(e) => {
// if (
// (e.key === "Enter" || e.key === "Return") &&
// !e.shiftKey
// ) {
// handleSend(e);
// }
// }}
/>
</div>
)}
/>
<div className="cursor-pointer ml-2 p-2 rounded-full bg-[#315A9D] text-white flex items-center justify-center">
<IoMicOutline size={20} />
</div>
</div>
{isEmojiPickerOpen && (
<div className="absolute bottom-12 left-2 z-10">
<Picker onEmojiSelect={handleEmojiSelect} />
</div>
)}
<Modal
isOpen={isOpen}
onRequestClose={toggleModal}
style={customStyles}
contentLabel="Example Modal"
>
<div className="flex flex-col space-y-4 h-auto">
<label className="flex items-center cursor-pointer">
<FaRegFileAlt color="#315A9D" className="mr-2" />
<span>Document</span>
<input
type="file"
style={{ display: "none" }}
accept=".pdf,.doc,.docx,.txt,.xls,.xlsx,.ppt,.pptx,.odt"
onChange={handleFileChange}
/>
</label>
<button
className="flex items-center"
onClick={() => handleFileClick("image/*")}
>
<FaImage color="#315A9D" className="mr-2" /> Gallery
</button>
<button
className="flex items-center"
onClick={() => handleFileClick("audio/*")}
>
<PiHeadphones color="#315A9D" className="mr-2" /> Audio
</button>
<button
className="flex items-center"
onClick={() => handleFileClick("video/*")}
>
<FaMapMarkerAlt color="#315A9D" className="mr-2" /> Video
</button>
<button className="flex items-center" onClick={handleLocationClick}>
<FaMapMarkerAlt color="#315A9D" className="mr-2" /> Location
</button>
</div>
</Modal>
</div>
);
};
export default CustomMessageInput;
-Implement the above CustomMessageInput component in your project. -Try to send a message with an attachment.
- Observe the issue where the message does not send with the attachment.
- Please let me know if you need any additional information or further assistance.
Expected behavior
A clear and concise description of what you expected to happen.
onClicking the document/image/audi and so on from modal I want to open that and pass that to message input .
Package version
- stream-chat-react:^11.18.1
- stream-chat-css:
- stream-chat-js: "react": "^18.3.1",
Desktop (please complete the following information):
- OS: [e.g. iOS] iOS
- Browser [e.g. chrome, safari] Chrome
- Version [e.g. 22] Version 125.0.6422.142
Additional context
Here's the corrected sentence:
This issue is blocking my development . I tried using overrideSubmitHandler, but this causes a delay in sending the message and not getting attachments.