172 lines
5.1 KiB
TypeScript
172 lines
5.1 KiB
TypeScript
|
|
"use client";
|
||
|
|
|
||
|
|
import { SendHorizontal, SmilePlusIcon, ThumbsUp } from "lucide-react";
|
||
|
|
import {
|
||
|
|
useCallback,
|
||
|
|
useEffect,
|
||
|
|
useRef,
|
||
|
|
useState,
|
||
|
|
type ChangeEvent,
|
||
|
|
} from "react";
|
||
|
|
import { Button } from "~/components/ui/button";
|
||
|
|
import {
|
||
|
|
AutosizeTextarea,
|
||
|
|
type AutosizeTextAreaRef,
|
||
|
|
} from "~/components/custom_ui/autoResizeTextArea";
|
||
|
|
import { api } from "~/utils/api";
|
||
|
|
import { ChatAttachments } from "~/components/chat/chat-attachments";
|
||
|
|
import { useMediaQuery } from "~/hooks/use-media-query";
|
||
|
|
import { useThrottledIsTypingMutation } from "~/hooks/chatHooks";
|
||
|
|
import type { Session } from "~/server/api/trpc";
|
||
|
|
import type { ChatsChatid } from "~/schemas/public/Chats";
|
||
|
|
import type { ChatUserInfo } from "~/server/services/chat.service";
|
||
|
|
import {
|
||
|
|
Popover,
|
||
|
|
PopoverContent,
|
||
|
|
PopoverTrigger,
|
||
|
|
} from "~/components/ui/popover";
|
||
|
|
import {
|
||
|
|
EmojiPickerContent,
|
||
|
|
EmojiPickerSearch,
|
||
|
|
EmojiPicker,
|
||
|
|
} from "~/components/custom_ui/emoji-picker";
|
||
|
|
import { useTranslation } from "~/providers/I18nProvider";
|
||
|
|
|
||
|
|
type ChatBottombarProps = {
|
||
|
|
chatId: ChatsChatid;
|
||
|
|
userData: Session;
|
||
|
|
chatUserData: ChatUserInfo;
|
||
|
|
};
|
||
|
|
|
||
|
|
export default function ChatBottombar({
|
||
|
|
chatId,
|
||
|
|
userData,
|
||
|
|
chatUserData,
|
||
|
|
}: ChatBottombarProps) {
|
||
|
|
const { locale } = useTranslation();
|
||
|
|
const [message, setMessage] = useState("");
|
||
|
|
const [isFocused, setIsFocused] = useState(false);
|
||
|
|
const inputRef = useRef<AutosizeTextAreaRef>(null);
|
||
|
|
|
||
|
|
const { mutate: addMutation } = api.messagesSSE.add.useMutation({});
|
||
|
|
|
||
|
|
const handleThumbsUp = useCallback(() => {
|
||
|
|
addMutation({
|
||
|
|
chatId,
|
||
|
|
message: "👍",
|
||
|
|
sender: userData.id,
|
||
|
|
});
|
||
|
|
setMessage("");
|
||
|
|
}, [chatId, addMutation, userData]);
|
||
|
|
|
||
|
|
const handleSend = () => {
|
||
|
|
if (message.length !== 0) {
|
||
|
|
addMutation({
|
||
|
|
chatId,
|
||
|
|
message: message.trim(),
|
||
|
|
sender: userData.id,
|
||
|
|
});
|
||
|
|
|
||
|
|
setMessage("");
|
||
|
|
|
||
|
|
if (inputRef.current) {
|
||
|
|
inputRef.current.textArea.focus();
|
||
|
|
inputRef.current.textArea.style.height = "auto";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
};
|
||
|
|
const isTypingMutation = useThrottledIsTypingMutation({
|
||
|
|
chatId,
|
||
|
|
username: userData.username,
|
||
|
|
});
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
// update isTyping state
|
||
|
|
isTypingMutation(isFocused && message.trim().length > 0);
|
||
|
|
}, [isFocused, message, isTypingMutation]);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
if (inputRef.current) {
|
||
|
|
inputRef.current.textArea.focus();
|
||
|
|
}
|
||
|
|
}, []);
|
||
|
|
const [openEmoji, setOpenEmoji] = useState(false);
|
||
|
|
|
||
|
|
const isMobile = useMediaQuery("(min-width: 426px)");
|
||
|
|
return (
|
||
|
|
<div className="flex min-h-[74px] w-full items-center justify-between gap-2 p-2">
|
||
|
|
<div className="flex">
|
||
|
|
<ChatAttachments userData={userData} chatUserData={chatUserData} />
|
||
|
|
</div>
|
||
|
|
<div className="relative w-full">
|
||
|
|
<AutosizeTextarea
|
||
|
|
autoComplete="off"
|
||
|
|
value={message}
|
||
|
|
minHeight={56}
|
||
|
|
ref={inputRef}
|
||
|
|
onKeyDown={(e) => {
|
||
|
|
if (e.key === "Enter") {
|
||
|
|
e.preventDefault();
|
||
|
|
if (e.shiftKey) {
|
||
|
|
setMessage((prev) => `${prev}\n`);
|
||
|
|
} else {
|
||
|
|
handleSend();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}}
|
||
|
|
onFocus={() => setIsFocused(true)}
|
||
|
|
onBlur={() => setIsFocused(false)}
|
||
|
|
onChange={(e: ChangeEvent<HTMLTextAreaElement>) => {
|
||
|
|
setMessage(e.target.value);
|
||
|
|
}}
|
||
|
|
name="message"
|
||
|
|
placeholder="Aa"
|
||
|
|
className="bg-background flex h-14 min-h-14 w-full resize-none items-center overflow-hidden rounded-xl border pr-8 ring-0 outline-hidden hover:border-neutral-400 focus:border-neutral-600 focus:ring-0 focus-visible:ring-0 focus-visible:ring-offset-0"
|
||
|
|
/>
|
||
|
|
{isMobile && (
|
||
|
|
<div className="absolute top-0 right-2 flex h-full items-center">
|
||
|
|
<Popover open={openEmoji} onOpenChange={setOpenEmoji}>
|
||
|
|
<PopoverTrigger asChild>
|
||
|
|
<Button variant="ghost">
|
||
|
|
<SmilePlusIcon className="stroke-muted-foreground" />
|
||
|
|
</Button>
|
||
|
|
</PopoverTrigger>
|
||
|
|
<PopoverContent align="end" className="w-fit p-0">
|
||
|
|
<EmojiPicker
|
||
|
|
locale={locale as "it" | "en"}
|
||
|
|
className="h-84"
|
||
|
|
onEmojiSelect={({ emoji }) => {
|
||
|
|
setMessage(message + emoji);
|
||
|
|
// if (inputRef.current) {
|
||
|
|
// inputRef.current.textArea.focus();
|
||
|
|
// }
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
<EmojiPickerSearch
|
||
|
|
placeholder={locale == "en" ? "Search..." : "Cerca..."}
|
||
|
|
/>
|
||
|
|
<EmojiPickerContent />
|
||
|
|
</EmojiPicker>
|
||
|
|
</PopoverContent>
|
||
|
|
</Popover>
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
<Button
|
||
|
|
aria-label="Send Message"
|
||
|
|
variant="ghost"
|
||
|
|
className="shrink-0"
|
||
|
|
onClick={
|
||
|
|
message.length != 0 ? () => handleSend() : () => handleThumbsUp()
|
||
|
|
}
|
||
|
|
>
|
||
|
|
{message.length != 0 ? (
|
||
|
|
<SendHorizontal size={20} className="text-muted-foreground" />
|
||
|
|
) : (
|
||
|
|
<ThumbsUp size={20} className="text-muted-foreground" />
|
||
|
|
)}
|
||
|
|
</Button>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|