103 lines
4.1 KiB
TypeScript
103 lines
4.1 KiB
TypeScript
import { Plus, Send, Trash } from "lucide-react"
|
|
import React from "react"
|
|
import { sendMessage } from "~/lib/api/client/channel"
|
|
import { uploadFiles } from "~/lib/api/client/file"
|
|
import TextBox from "./custom-ui/text-box"
|
|
import { Button } from "./ui/button"
|
|
|
|
export interface MessageBoxProps {
|
|
channelId: string
|
|
}
|
|
|
|
export default function MessageBox(
|
|
{ channelId }: MessageBoxProps
|
|
) {
|
|
const [text, setText] = React.useState("")
|
|
const [attachments, setAttachments] = React.useState<File[]>([])
|
|
|
|
const fileInputRef = React.useRef<HTMLInputElement>(null)
|
|
|
|
const onSendMessage = async () => {
|
|
const content = text.trim()
|
|
if (!content && !attachments.length)
|
|
return
|
|
|
|
const uploadedAttachments = await uploadFiles(attachments)
|
|
|
|
await sendMessage(channelId, text, uploadedAttachments)
|
|
setText("")
|
|
setAttachments([])
|
|
}
|
|
|
|
const addAttachment = async () => {
|
|
if (!fileInputRef.current)
|
|
return
|
|
|
|
fileInputRef.current.click()
|
|
}
|
|
|
|
const onFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
const files = Array.from(e.target.files || [])
|
|
const newAttachments = [...attachments, ...files]
|
|
setAttachments(newAttachments.slice(0, 10))
|
|
|
|
if (!fileInputRef.current)
|
|
return
|
|
|
|
fileInputRef.current.value = ""
|
|
}
|
|
|
|
React.useEffect(() => {
|
|
setText("")
|
|
setAttachments([])
|
|
}, [channelId])
|
|
|
|
return (
|
|
<div className="p-2">
|
|
<div className="max-h-1/2 w-full max-w-full grid items-center gap-x-2 outline-none py-4 px-2 rounded-xl no-scrollbar bg-background border border-input focus-within:ring-2 focus-within:ring-ring focus-within:border-ring"
|
|
style={{ gridTemplateColumns: "auto 1fr auto", gridTemplateRows: "1fr auto" }}>
|
|
<div className="row-start-1 col-start-1 row-span-2 col-span-1 h-full">
|
|
<Button size="icon" variant="ghost" onClick={addAttachment} disabled={attachments.length >= 10}>
|
|
<Plus />
|
|
</Button>
|
|
<input type="file" multiple className="hidden" ref={fileInputRef} onChange={onFileChange} />
|
|
</div>
|
|
|
|
<div className="flex-1 row-start-1 col-start-2 row-span-1 col-span-1">
|
|
<TextBox value={text}
|
|
wrapperClassName="contain-inline-size"
|
|
onChange={setText}
|
|
placeholder="Type your message here..."
|
|
aria-label="Message input">
|
|
|
|
</TextBox>
|
|
</div>
|
|
<div className="flex-1 row-start-2 col-start-2 row-span-1 col-span-1 overflow-y-auto max-h-40">
|
|
<div className={`${attachments.length > 0 ? "pt-2" : "hidden"}`}>
|
|
<div className="flex flex-col gap-2">
|
|
{
|
|
attachments.map((file, i) => (
|
|
<div key={i} className="flex items-center gap-2 wrap-anywhere rounded-xl border border-input bg-background p-2">
|
|
<div className="flex-1">
|
|
{file.name}
|
|
</div>
|
|
<div>
|
|
<Button size="icon" variant="destructive" onClick={() => setAttachments(attachments.filter((_, j) => i !== j))}>
|
|
<Trash />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
))
|
|
}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="row-start-1 col-start-3 row-span-2 col-span-1 h-full">
|
|
<Button size="icon" onClick={onSendMessage}>
|
|
<Send />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
} |