Files
cozsweet-frontend-nextjs/src/app/chat/components/chat-input-bar.tsx
T

91 lines
2.6 KiB
TypeScript

"use client";
import { useRef, useState } from "react";
import { useChatDispatch } from "@/stores/chat/chat-context";
import { useKeyboardHeight } from "@/hooks";
import { Logger } from "@/utils";
import { ChatInputTextField } from "./chat-input-text-field";
import { ChatSendButton } from "./chat-send-button";
import styles from "./chat-input-bar.module.css";
const log = new Logger("AppChatComponentsChatInputBar");
const POINTER_SEND_CLICK_DEDUPE_MS = 500;
export interface ChatInputBarProps {
disabled?: boolean;
}
export function ChatInputBar({ disabled = false }: ChatInputBarProps) {
const dispatch = useChatDispatch();
const [input, setInput] = useState("");
const [isFocused, setIsFocused] = useState(false);
const textareaRef = useRef<HTMLTextAreaElement>(null);
const lastPointerSendAtRef = useRef(0);
const hasContent = input.trim().length > 0;
useKeyboardHeight({
targetRef: textareaRef,
active: isFocused,
});
const handleInputChange = (value: string) => {
setInput(value);
};
const handleSend = (source: "click" | "keyboard" | "pointerdown") => {
if (source === "click") {
const elapsed = Date.now() - lastPointerSendAtRef.current;
if (elapsed >= 0 && elapsed < POINTER_SEND_CLICK_DEDUPE_MS) {
log.debug("[chat-input-bar] suppress duplicate click", {
elapsed,
contentLength: input.length,
hasContent,
disabled,
isFocused,
});
return;
}
}
if (disabled || !hasContent) return;
log.debug("[chat-input-bar] handleSend", {
source,
contentLength: input.length,
contentPreview: input.slice(0, 50),
hasContent,
disabled,
isFocused,
});
dispatch({ type: "ChatSendMessage", content: input });
setInput("");
textareaRef.current?.focus();
};
const handlePointerDownSend = () => {
lastPointerSendAtRef.current = Date.now();
handleSend("pointerdown");
};
return (
<div className={`${styles.bar} ${isFocused ? styles.barFocused : ""}`}>
<div className={`${styles.row} ${isFocused ? styles.rowFocused : ""}`}>
<ChatInputTextField
ref={textareaRef}
value={input}
onChange={handleInputChange}
onSubmit={() => handleSend("keyboard")}
onFocusChange={setIsFocused}
disabled={disabled}
/>
<ChatSendButton
disabled={disabled}
hasContent={hasContent}
onClick={() => handleSend("click")}
onPointerDownSend={handlePointerDownSend}
/>
</div>
</div>
);
}