110 lines
3.0 KiB
TypeScript
110 lines
3.0 KiB
TypeScript
"use client";
|
|
import { useRef, useState } from "react";
|
|
|
|
import { useChatDispatch } from "@/stores/chat/chat-context";
|
|
import { useUserState } from "@/stores/user/user-context";
|
|
import { useKeyboardHeight } from "@/hooks";
|
|
import { Logger } from "@/utils";
|
|
|
|
import { ChatInputActionPanel } from "./chat-input-action-panel";
|
|
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");
|
|
|
|
export interface ChatInputBarProps {
|
|
disabled?: boolean;
|
|
}
|
|
|
|
export function ChatInputBar({ disabled = false }: ChatInputBarProps) {
|
|
const dispatch = useChatDispatch();
|
|
const { currentUser } = useUserState();
|
|
const [input, setInput] = useState("");
|
|
const [isFocused, setIsFocused] = useState(false);
|
|
const [isActionPanelOpen, setIsActionPanelOpen] = useState(false);
|
|
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
|
|
|
const hasContent = input.trim().length > 0;
|
|
const isActive = isFocused || isActionPanelOpen;
|
|
const voiceMinutesRemaining = currentUser?.voiceMinutesRemaining ?? 0;
|
|
|
|
useKeyboardHeight({
|
|
targetRef: textareaRef,
|
|
active: isFocused,
|
|
});
|
|
|
|
const handleInputChange = (value: string) => {
|
|
setInput(value);
|
|
if (value.trim().length > 0) {
|
|
setIsActionPanelOpen(false);
|
|
}
|
|
};
|
|
|
|
const handleSend = () => {
|
|
if (!hasContent) return;
|
|
log.debug("[chat-input-bar] handleSend", {
|
|
contentLength: input.length,
|
|
contentPreview: input.slice(0, 50),
|
|
hasContent,
|
|
disabled,
|
|
isFocused,
|
|
});
|
|
dispatch({ type: "ChatSendMessage", content: input });
|
|
setInput("");
|
|
textareaRef.current?.focus();
|
|
};
|
|
|
|
const handleActionButtonClick = () => {
|
|
if (hasContent) {
|
|
handleSend();
|
|
return;
|
|
}
|
|
|
|
textareaRef.current?.blur();
|
|
setIsFocused(false);
|
|
setIsActionPanelOpen((open) => !open);
|
|
};
|
|
|
|
const handleVoiceMessageClick = () => {
|
|
log.debug("[chat-input-bar] voice message action clicked", {
|
|
voiceMinutesRemaining,
|
|
});
|
|
};
|
|
|
|
return (
|
|
<div
|
|
className={`${styles.bar} ${isActive ? styles.barFocused : ""} ${
|
|
isActionPanelOpen ? styles.barExpanded : ""
|
|
}`}
|
|
>
|
|
<div
|
|
className={`${styles.row} ${isActive ? styles.rowFocused : ""} ${
|
|
isActionPanelOpen ? styles.rowExpanded : ""
|
|
}`}
|
|
>
|
|
<ChatInputTextField
|
|
ref={textareaRef}
|
|
value={input}
|
|
onChange={handleInputChange}
|
|
onSubmit={handleSend}
|
|
onFocusChange={setIsFocused}
|
|
disabled={disabled}
|
|
/>
|
|
<ChatSendButton
|
|
disabled={disabled}
|
|
hasContent={hasContent}
|
|
isExpanded={isActionPanelOpen}
|
|
onClick={handleActionButtonClick}
|
|
/>
|
|
</div>
|
|
{isActionPanelOpen ? (
|
|
<ChatInputActionPanel
|
|
voiceMinutesRemaining={voiceMinutesRemaining}
|
|
onVoiceMessageClick={handleVoiceMessageClick}
|
|
/>
|
|
) : null}
|
|
</div>
|
|
);
|
|
}
|