added file preview
This commit is contained in:
18
client/assets/group.svg
Normal file
18
client/assets/group.svg
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" ?>
|
||||
<svg viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<style>.cls-1{fill:#101820;}</style>
|
||||
</defs>
|
||||
<title/>
|
||||
<g data-name="Layer 20" id="Layer_20">
|
||||
<path class="cls-1" d="M16,22a6,6,0,1,1,6-6A6,6,0,0,1,16,22Zm0-10a4,4,0,1,0,4,4A4,4,0,0,0,16,12Z"/>
|
||||
<path class="cls-1"
|
||||
d="M21,31H11a4,4,0,0,1-4-4V24.45a1,1,0,0,1,.63-.92l3.64-1.46A1,1,0,1,1,12,23.93l-3,1.2V27a2,2,0,0,0,2,2H21a2,2,0,0,0,2-2V25.13l-3-1.2a1,1,0,0,1,.74-1.86l3.64,1.46a1,1,0,0,1,.63.92V27A4,4,0,0,1,21,31Z"/>
|
||||
<path class="cls-1" d="M9,11a5,5,0,1,1,5-5A5,5,0,0,1,9,11ZM9,3a3,3,0,1,0,3,3A3,3,0,0,0,9,3Z"/>
|
||||
<path class="cls-1"
|
||||
d="M8,19.39H5a4,4,0,0,1-4-4V13.64a1,1,0,0,1,.63-.93l3.19-1.25A1,1,0,0,1,6.11,12a1,1,0,0,1-.56,1.3L3,14.32v1.07a2,2,0,0,0,2,2H8a1,1,0,0,1,0,2Z"/>
|
||||
<path class="cls-1" d="M23,11a5,5,0,1,1,5-5A5,5,0,0,1,23,11Zm0-8a3,3,0,1,0,3,3A3,3,0,0,0,23,3Z"/>
|
||||
<path class="cls-1"
|
||||
d="M27,19.39H24a1,1,0,0,1,0-2h3a2,2,0,0,0,2-2V14.32l-2.55-1a1,1,0,0,1-.56-1.3,1,1,0,0,1,1.29-.57l3.19,1.25a1,1,0,0,1,.63.93v1.75A4,4,0,0,1,27,19.39Z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
15
client/assets/group2.svg
Normal file
15
client/assets/group2.svg
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" ?>
|
||||
<svg fill="none" height="27" viewBox="0 0 35 27" width="35" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.60001 12.6C7.1464 12.6 8.39999 11.3464 8.39999 9.8C8.39999 8.25361 7.1464 7 5.60001 7C4.05361 7 2.8 8.25361 2.8 9.8C2.8 11.3464 4.05361 12.6 5.60001 12.6Z"
|
||||
stroke="#4F4F4F" stroke-miterlimit="10" stroke-width="2"/>
|
||||
<path d="M8.3 23.6H2.60001C1.70001 23.6 1.10001 22.9 1.10001 22V19.2C1.10001 17 2.8 15.3 5 15.3H8.5"
|
||||
stroke="#4F4F4F" stroke-miterlimit="10" stroke-width="2"/>
|
||||
<path d="M17.6 10C20.0301 10 22 8.03007 22 5.60001C22 3.16996 20.0301 1.20001 17.6 1.20001C15.17 1.20001 13.2 3.16996 13.2 5.60001C13.2 8.03007 15.17 10 17.6 10Z"
|
||||
stroke="#4F4F4F" stroke-miterlimit="10" stroke-width="2"/>
|
||||
<path d="M24.1 25.2H10.7C9.3 25.2 8.2 24.1 8.2 22.7V18.4C8.2 15 11.1 12.2 14.6 12.2H20.3C23.8 12.2 26.7 15 26.7 18.4V22.7C26.6 24.1 25.5 25.2 24.1 25.2Z"
|
||||
stroke="#4F4F4F" stroke-miterlimit="10" stroke-width="2"/>
|
||||
<path d="M29 12.6C30.5464 12.6 31.8 11.3464 31.8 9.8C31.8 8.25361 30.5464 7 29 7C27.4536 7 26.2 8.25361 26.2 9.8C26.2 11.3464 27.4536 12.6 29 12.6Z"
|
||||
stroke="#4F4F4F" stroke-miterlimit="10" stroke-width="2"/>
|
||||
<path d="M26.3 23.6H32C32.9 23.6 33.5 22.9 33.5 22V19.2C33.5 17 31.8 15.3 29.6 15.3H26.1" stroke="#4F4F4F"
|
||||
stroke-miterlimit="10" stroke-width="2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -6,7 +6,7 @@ import { customAlphabet } from 'nanoid';
|
||||
import { useOutletContext } from 'react-router-dom';
|
||||
import { ChatMessages, ContactsProps } from '../../pages/Chat.tsx';
|
||||
import { axiosClient } from '../../App.tsx';
|
||||
import { File, Paperclip } from 'lucide-react';
|
||||
import { File, Paperclip, X } from 'lucide-react';
|
||||
|
||||
const nanoid = customAlphabet('1234567890', 5);
|
||||
|
||||
@@ -48,6 +48,14 @@ const MessageForm = ({ contact, setMessages }: MessageFormProps) => {
|
||||
adjustHeight();
|
||||
}, [message, adjustHeight]);
|
||||
|
||||
const handleClearFile = () => {
|
||||
if (fileInputRef.current) {
|
||||
fileInputRef.current.value = '';
|
||||
}
|
||||
setFile(null);
|
||||
setValue('attachment', null);
|
||||
};
|
||||
|
||||
if (!socket) {
|
||||
console.error('Socket not initialized');
|
||||
return;
|
||||
@@ -96,13 +104,13 @@ const MessageForm = ({ contact, setMessages }: MessageFormProps) => {
|
||||
}
|
||||
}
|
||||
|
||||
const tempId: string = nanoid(); // Temporary ID for unsent messages
|
||||
const tempId: string = nanoid();
|
||||
|
||||
const tempMessage: ChatMessages = {
|
||||
sender: username,
|
||||
message: data.message.trim(),
|
||||
recipient: contact.conversation_id,
|
||||
message_id: 0, // Set to 0 because of key={msg.message_id || msg.tempId} in messages list
|
||||
message_id: 0,
|
||||
pending: true,
|
||||
tempId: tempId,
|
||||
attachment_url: attachmentUrl || null,
|
||||
@@ -110,7 +118,7 @@ const MessageForm = ({ contact, setMessages }: MessageFormProps) => {
|
||||
conversation_id: 0,
|
||||
};
|
||||
|
||||
setMessages((prevMessages) => [...prevMessages, tempMessage]); // Display as gray
|
||||
setMessages((prevMessages) => [...prevMessages, tempMessage]);
|
||||
|
||||
socket.emit(
|
||||
'chat message',
|
||||
@@ -141,8 +149,7 @@ const MessageForm = ({ contact, setMessages }: MessageFormProps) => {
|
||||
);
|
||||
|
||||
reset({ message: '' });
|
||||
setValue('attachment', null);
|
||||
setFile(null);
|
||||
handleClearFile();
|
||||
}
|
||||
console.log(
|
||||
`status: ${response.status}, tempId: ${response.tempId}, message: ${response.message}`,
|
||||
@@ -158,7 +165,6 @@ const MessageForm = ({ contact, setMessages }: MessageFormProps) => {
|
||||
});
|
||||
};
|
||||
|
||||
// Handle Enter and Ctrl+Enter in textarea
|
||||
const handleKeyPress: KeyboardEventHandler<HTMLTextAreaElement> = (e) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
@@ -187,63 +193,85 @@ const MessageForm = ({ contact, setMessages }: MessageFormProps) => {
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(submitMessage)} className="w-full">
|
||||
<div className="flex items-end gap-2 w-full text-center">
|
||||
<div className="flex-1 inline-block">
|
||||
<div className="relative flex justify-center">
|
||||
<textarea
|
||||
{...messageRegister}
|
||||
ref={(e) => {
|
||||
ref(e);
|
||||
textareaRef.current = e;
|
||||
}}
|
||||
className={`w-full overflow-y-hidden resize-none bg-green-50 text-black rounded-md p-2 min-h-[40px] max-h-96
|
||||
${isOverLimit ? 'border-2 border-red-500' : isNearLimit ? 'border-2 border-yellow-500' : ''} mx-auto`}
|
||||
autoFocus={!!contact}
|
||||
disabled={!contact}
|
||||
placeholder="Enter message"
|
||||
onKeyDown={handleKeyPress}
|
||||
rows={1}
|
||||
/>
|
||||
<span
|
||||
className={`absolute right-2 bottom-1 text-sm ${
|
||||
isOverLimit
|
||||
? 'text-red-500 font-bold'
|
||||
: isNearLimit
|
||||
? 'text-yellow-600'
|
||||
: 'text-gray-500'
|
||||
}`}
|
||||
<div className="flex flex-col gap-2 w-full">
|
||||
{file && (
|
||||
<div className="flex items-center justify-between p-2 bg-green-50 rounded-md mx-2">
|
||||
<div className="flex items-center">
|
||||
<File className="w-4 h-4 mr-2" />
|
||||
<span className="text-black text-sm truncate">{file.name}</span>
|
||||
<span className="text-xs text-gray-500 ml-2">
|
||||
({Math.round(file.size / 1024)} KB)
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleClearFile}
|
||||
className="p-1 hover:bg-green-100 rounded-full"
|
||||
aria-label="Clear file"
|
||||
>
|
||||
{charCount}/2000
|
||||
</span>
|
||||
<X className="w-4 h-4 text-gray-500" />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex items-end gap-2 w-full text-center">
|
||||
<div className="flex-1 inline-block">
|
||||
<div className="relative flex justify-center">
|
||||
<textarea
|
||||
{...messageRegister}
|
||||
ref={(e) => {
|
||||
ref(e);
|
||||
textareaRef.current = e;
|
||||
}}
|
||||
className={`w-full overflow-y-hidden resize-none bg-green-50 text-black rounded-md p-2 min-h-[40px] max-h-96
|
||||
${isOverLimit ? 'border-2 border-red-500' : isNearLimit ? 'border-2 border-yellow-500' : ''} mx-auto`}
|
||||
autoFocus={!!contact}
|
||||
disabled={!contact}
|
||||
placeholder="Enter message"
|
||||
onKeyDown={handleKeyPress}
|
||||
rows={1}
|
||||
/>
|
||||
<span
|
||||
className={`absolute right-2 bottom-1 text-sm ${
|
||||
isOverLimit
|
||||
? 'text-red-500 font-bold'
|
||||
: isNearLimit
|
||||
? 'text-yellow-600'
|
||||
: 'text-gray-500'
|
||||
}`}
|
||||
>
|
||||
{charCount}/2000
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<label
|
||||
htmlFor="attachment"
|
||||
className="flex items-center justify-center hover:cursor-pointer p-1 rounded-full hover:bg-gray-800"
|
||||
>
|
||||
<Paperclip />
|
||||
</label>
|
||||
<input
|
||||
name="attachment"
|
||||
id="attachment"
|
||||
type="file"
|
||||
accept="*/*"
|
||||
onChange={handleFileChange}
|
||||
disabled={isUploading}
|
||||
ref={fileInputRef}
|
||||
className="hidden"
|
||||
/>
|
||||
{isUploading && (
|
||||
<span className="text-gray-500 text-sm">Uploading...</span>
|
||||
)}
|
||||
<button
|
||||
className="text-black bg-green-500 hover:bg-green-400 font-bold py-2 px-4 rounded w-24 shrink-0 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
type="submit"
|
||||
disabled={isOverLimit || isUploading}
|
||||
>
|
||||
Send
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<label
|
||||
htmlFor="attachment"
|
||||
className="flex items-center justify-center hover:cursor-pointer p-1 rounded-full hover:bg-gray-800"
|
||||
>
|
||||
<Paperclip size={24} />
|
||||
</label>
|
||||
<input
|
||||
name="attachment"
|
||||
id="attachment"
|
||||
type="file"
|
||||
accept="*/*"
|
||||
onChange={handleFileChange}
|
||||
disabled={isUploading}
|
||||
ref={fileInputRef}
|
||||
className="hidden"
|
||||
/>
|
||||
{isUploading && (
|
||||
<span className="text-gray-500 text-sm">Uploading...</span>
|
||||
)}
|
||||
<button
|
||||
className="text-black bg-green-500 hover:bg-green-400 font-bold py-2 px-4 rounded w-24 shrink-0 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
type="submit"
|
||||
disabled={isOverLimit || isUploading}
|
||||
>
|
||||
Send
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
Reference in New Issue
Block a user