✨ (chatInput): add ButtonSendWrapper component for better modularity
✨ (chatInput): add TextAreaWrapper component for better modularity ♻️ (chatInput): refactor chat input logic to use new wrapper components ♻️ (chatView): clean up imports and improve code readability
This commit is contained in:
parent
5638e2909c
commit
7473eea263
3 changed files with 105 additions and 98 deletions
|
|
@ -0,0 +1,52 @@
|
|||
import IconComponent from "../../../../../../../components/genericIconComponent";
|
||||
import { Case } from "../../../../../../../shared/components/caseComponent";
|
||||
import { classNames } from "../../../../../../../utils/utils";
|
||||
|
||||
const ButtonSendWrapper = ({
|
||||
send,
|
||||
lockChat,
|
||||
noInput,
|
||||
saveLoading,
|
||||
chatValue,
|
||||
}) => {
|
||||
return (
|
||||
<button
|
||||
className={classNames(
|
||||
"form-modal-send-button",
|
||||
noInput
|
||||
? "bg-high-indigo text-background"
|
||||
: chatValue === ""
|
||||
? "text-primary"
|
||||
: "bg-chat-send text-background",
|
||||
)}
|
||||
disabled={lockChat || saveLoading}
|
||||
onClick={(): void => send()}
|
||||
>
|
||||
<Case condition={lockChat || saveLoading}>
|
||||
<IconComponent
|
||||
name="Lock"
|
||||
className="form-modal-lock-icon"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</Case>
|
||||
|
||||
<Case condition={noInput}>
|
||||
<IconComponent
|
||||
name="Zap"
|
||||
className="form-modal-play-icon"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</Case>
|
||||
|
||||
<Case condition={!(lockChat || saveLoading) && !noInput}>
|
||||
<IconComponent
|
||||
name="LucideSend"
|
||||
className="form-modal-send-icon "
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</Case>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export default ButtonSendWrapper;
|
||||
|
|
@ -1,21 +1,19 @@
|
|||
import { useState } from "react";
|
||||
import ShortUniqueId from "short-unique-id";
|
||||
import IconComponent from "../../../../../components/genericIconComponent";
|
||||
import { Textarea } from "../../../../../components/ui/textarea";
|
||||
import {
|
||||
CHAT_INPUT_PLACEHOLDER,
|
||||
CHAT_INPUT_PLACEHOLDER_SEND,
|
||||
} from "../../../../../constants/constants";
|
||||
import { uploadFile } from "../../../../../controllers/API";
|
||||
import { Case } from "../../../../../shared/components/caseComponent";
|
||||
import useFlowsManagerStore from "../../../../../stores/flowsManagerStore";
|
||||
import {
|
||||
FilePreviewType,
|
||||
chatInputType,
|
||||
} from "../../../../../types/components";
|
||||
import { classNames } from "../../../../../utils/utils";
|
||||
import FilePreview from "../filePreviewChat";
|
||||
import ButtonSendWrapper from "./components/buttonSendWrapper";
|
||||
import TextAreaWrapper from "./components/textAreaWrapper";
|
||||
import useAutoResizeTextArea from "./hooks/use-auto-resize-text-area";
|
||||
import useDragAndDrop from "./hooks/use-drag-and-drop";
|
||||
import useFocusOnUnlock from "./hooks/use-focus-unlock";
|
||||
import useUpload from "./hooks/use-upload";
|
||||
export default function ChatInput({
|
||||
|
|
@ -29,12 +27,18 @@ export default function ChatInput({
|
|||
const [repeat, setRepeat] = useState(1);
|
||||
const saveLoading = useFlowsManagerStore((state) => state.saveLoading);
|
||||
const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId);
|
||||
const uid = new ShortUniqueId({ length: 3 });
|
||||
const [files, setFiles] = useState<FilePreviewType[]>([]);
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
|
||||
useFocusOnUnlock(lockChat, inputRef);
|
||||
useAutoResizeTextArea(chatValue, inputRef);
|
||||
useUpload(uploadFile, currentFlowId, setFiles, uid);
|
||||
useUpload(uploadFile, currentFlowId, setFiles);
|
||||
|
||||
const { dragOver, dragEnter, dragLeave, onDrop } = useDragAndDrop(
|
||||
setIsDragging,
|
||||
setFiles,
|
||||
currentFlowId,
|
||||
);
|
||||
|
||||
const send = () => {
|
||||
sendMessage({
|
||||
|
|
@ -44,90 +48,44 @@ export default function ChatInput({
|
|||
setFiles([]);
|
||||
};
|
||||
|
||||
const checkSendingOk = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
||||
return (
|
||||
event.key === "Enter" &&
|
||||
!lockChat &&
|
||||
!saveLoading &&
|
||||
!event.shiftKey &&
|
||||
!event.nativeEvent.isComposing
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex w-full flex-col-reverse">
|
||||
<div className="relative w-full">
|
||||
<Textarea
|
||||
onKeyDown={(event) => {
|
||||
if (
|
||||
event.key === "Enter" &&
|
||||
!lockChat &&
|
||||
!saveLoading &&
|
||||
!event.shiftKey &&
|
||||
!event.nativeEvent.isComposing
|
||||
) {
|
||||
send();
|
||||
}
|
||||
}}
|
||||
rows={1}
|
||||
ref={inputRef}
|
||||
disabled={lockChat || noInput || saveLoading}
|
||||
style={{
|
||||
resize: "none",
|
||||
bottom: `${inputRef?.current?.scrollHeight}px`,
|
||||
maxHeight: "150px",
|
||||
overflow: `${
|
||||
inputRef.current && inputRef.current.scrollHeight > 150
|
||||
? "auto"
|
||||
: "hidden"
|
||||
}`,
|
||||
}}
|
||||
value={
|
||||
lockChat ? "Thinking..." : saveLoading ? "Saving..." : chatValue
|
||||
}
|
||||
onChange={(event): void => {
|
||||
setChatValue(event.target.value);
|
||||
}}
|
||||
className={classNames(
|
||||
lockChat || saveLoading
|
||||
? " form-modal-lock-true bg-input"
|
||||
: noInput
|
||||
? "form-modal-no-input bg-input"
|
||||
: " form-modal-lock-false bg-background",
|
||||
|
||||
"form-modal-lockchat",
|
||||
)}
|
||||
placeholder={
|
||||
noInput ? CHAT_INPUT_PLACEHOLDER : CHAT_INPUT_PLACEHOLDER_SEND
|
||||
}
|
||||
<TextAreaWrapper
|
||||
dragOver={dragOver}
|
||||
dragEnter={dragEnter}
|
||||
dragLeave={dragLeave}
|
||||
onDrop={onDrop}
|
||||
checkSendingOk={checkSendingOk}
|
||||
send={send}
|
||||
lockChat={lockChat}
|
||||
noInput={noInput}
|
||||
saveLoading={saveLoading}
|
||||
chatValue={chatValue}
|
||||
setChatValue={setChatValue}
|
||||
CHAT_INPUT_PLACEHOLDER={CHAT_INPUT_PLACEHOLDER}
|
||||
CHAT_INPUT_PLACEHOLDER_SEND={CHAT_INPUT_PLACEHOLDER_SEND}
|
||||
inputRef={inputRef}
|
||||
/>
|
||||
|
||||
<div className="form-modal-send-icon-position">
|
||||
<button
|
||||
className={classNames(
|
||||
"form-modal-send-button",
|
||||
noInput
|
||||
? "bg-high-indigo text-background"
|
||||
: chatValue === ""
|
||||
? "text-primary"
|
||||
: "bg-chat-send text-background",
|
||||
)}
|
||||
disabled={lockChat || saveLoading}
|
||||
onClick={(): void => send()}
|
||||
>
|
||||
<Case condition={lockChat || saveLoading}>
|
||||
<IconComponent
|
||||
name="Lock"
|
||||
className="form-modal-lock-icon"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</Case>
|
||||
|
||||
<Case condition={noInput}>
|
||||
<IconComponent
|
||||
name="Zap"
|
||||
className="form-modal-play-icon"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</Case>
|
||||
|
||||
<Case condition={!(lockChat || saveLoading) && !noInput}>
|
||||
<IconComponent
|
||||
name="LucideSend"
|
||||
className="form-modal-send-icon "
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</Case>
|
||||
</button>
|
||||
<ButtonSendWrapper
|
||||
send={send}
|
||||
lockChat={lockChat}
|
||||
noInput={noInput}
|
||||
saveLoading={saveLoading}
|
||||
chatValue={chatValue}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex w-full gap-2 pb-2">
|
||||
|
|
|
|||
|
|
@ -9,11 +9,7 @@ import useAlertStore from "../../../../stores/alertStore";
|
|||
import useFlowStore from "../../../../stores/flowStore";
|
||||
import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
|
||||
import { VertexBuildTypeAPI, sendAllProps } from "../../../../types/api";
|
||||
import {
|
||||
ChatMessageType,
|
||||
ChatOutputType,
|
||||
FlowPoolObjectType,
|
||||
} from "../../../../types/chat";
|
||||
import { ChatMessageType, ChatOutputType } from "../../../../types/chat";
|
||||
import { chatViewProps } from "../../../../types/components";
|
||||
import { classNames } from "../../../../utils/utils";
|
||||
import ChatInput from "./chatInput";
|
||||
|
|
@ -64,11 +60,13 @@ export default function ChatView({
|
|||
const chatMessages: ChatMessageType[] = chatOutputResponses
|
||||
.sort((a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp))
|
||||
//
|
||||
.filter((output) => output.data.messages && output.data.messages.length > 0)
|
||||
.filter(
|
||||
(output) => output.data.messages && output.data.messages.length > 0,
|
||||
)
|
||||
.map((output, index) => {
|
||||
try {
|
||||
const { sender, message, sender_name, stream_url,files } = output.data
|
||||
.messages[0] as ChatOutputType;
|
||||
const { sender, message, sender_name, stream_url, files } = output
|
||||
.data.messages[0] as ChatOutputType;
|
||||
|
||||
const is_ai = sender === "Machine" || sender === null;
|
||||
return {
|
||||
|
|
@ -77,8 +75,7 @@ export default function ChatView({
|
|||
sender_name,
|
||||
componentId: output.id,
|
||||
stream_url: stream_url,
|
||||
files
|
||||
|
||||
files,
|
||||
};
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
|
|
@ -123,7 +120,7 @@ export default function ChatView({
|
|||
function updateChat(
|
||||
chat: ChatMessageType,
|
||||
message: string,
|
||||
stream_url?: string
|
||||
stream_url?: string,
|
||||
) {
|
||||
// if (message === "") return;
|
||||
chat.message = message;
|
||||
|
|
@ -158,7 +155,7 @@ export default function ChatView({
|
|||
"h-5 w-5",
|
||||
lockChat
|
||||
? "animate-pulse text-primary"
|
||||
: "text-primary hover:text-gray-600"
|
||||
: "text-primary hover:text-gray-600",
|
||||
)}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue