✨ (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 { useState } from "react";
|
||||||
import ShortUniqueId from "short-unique-id";
|
|
||||||
import IconComponent from "../../../../../components/genericIconComponent";
|
|
||||||
import { Textarea } from "../../../../../components/ui/textarea";
|
|
||||||
import {
|
import {
|
||||||
CHAT_INPUT_PLACEHOLDER,
|
CHAT_INPUT_PLACEHOLDER,
|
||||||
CHAT_INPUT_PLACEHOLDER_SEND,
|
CHAT_INPUT_PLACEHOLDER_SEND,
|
||||||
} from "../../../../../constants/constants";
|
} from "../../../../../constants/constants";
|
||||||
import { uploadFile } from "../../../../../controllers/API";
|
import { uploadFile } from "../../../../../controllers/API";
|
||||||
import { Case } from "../../../../../shared/components/caseComponent";
|
|
||||||
import useFlowsManagerStore from "../../../../../stores/flowsManagerStore";
|
import useFlowsManagerStore from "../../../../../stores/flowsManagerStore";
|
||||||
import {
|
import {
|
||||||
FilePreviewType,
|
FilePreviewType,
|
||||||
chatInputType,
|
chatInputType,
|
||||||
} from "../../../../../types/components";
|
} from "../../../../../types/components";
|
||||||
import { classNames } from "../../../../../utils/utils";
|
|
||||||
import FilePreview from "../filePreviewChat";
|
import FilePreview from "../filePreviewChat";
|
||||||
|
import ButtonSendWrapper from "./components/buttonSendWrapper";
|
||||||
|
import TextAreaWrapper from "./components/textAreaWrapper";
|
||||||
import useAutoResizeTextArea from "./hooks/use-auto-resize-text-area";
|
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 useFocusOnUnlock from "./hooks/use-focus-unlock";
|
||||||
import useUpload from "./hooks/use-upload";
|
import useUpload from "./hooks/use-upload";
|
||||||
export default function ChatInput({
|
export default function ChatInput({
|
||||||
|
|
@ -29,12 +27,18 @@ export default function ChatInput({
|
||||||
const [repeat, setRepeat] = useState(1);
|
const [repeat, setRepeat] = useState(1);
|
||||||
const saveLoading = useFlowsManagerStore((state) => state.saveLoading);
|
const saveLoading = useFlowsManagerStore((state) => state.saveLoading);
|
||||||
const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId);
|
const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId);
|
||||||
const uid = new ShortUniqueId({ length: 3 });
|
|
||||||
const [files, setFiles] = useState<FilePreviewType[]>([]);
|
const [files, setFiles] = useState<FilePreviewType[]>([]);
|
||||||
|
const [isDragging, setIsDragging] = useState(false);
|
||||||
|
|
||||||
useFocusOnUnlock(lockChat, inputRef);
|
useFocusOnUnlock(lockChat, inputRef);
|
||||||
useAutoResizeTextArea(chatValue, inputRef);
|
useAutoResizeTextArea(chatValue, inputRef);
|
||||||
useUpload(uploadFile, currentFlowId, setFiles, uid);
|
useUpload(uploadFile, currentFlowId, setFiles);
|
||||||
|
|
||||||
|
const { dragOver, dragEnter, dragLeave, onDrop } = useDragAndDrop(
|
||||||
|
setIsDragging,
|
||||||
|
setFiles,
|
||||||
|
currentFlowId,
|
||||||
|
);
|
||||||
|
|
||||||
const send = () => {
|
const send = () => {
|
||||||
sendMessage({
|
sendMessage({
|
||||||
|
|
@ -44,90 +48,44 @@ export default function ChatInput({
|
||||||
setFiles([]);
|
setFiles([]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const checkSendingOk = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
||||||
|
return (
|
||||||
|
event.key === "Enter" &&
|
||||||
|
!lockChat &&
|
||||||
|
!saveLoading &&
|
||||||
|
!event.shiftKey &&
|
||||||
|
!event.nativeEvent.isComposing
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex w-full flex-col-reverse">
|
<div className="flex w-full flex-col-reverse">
|
||||||
<div className="relative w-full">
|
<div className="relative w-full">
|
||||||
<Textarea
|
<TextAreaWrapper
|
||||||
onKeyDown={(event) => {
|
dragOver={dragOver}
|
||||||
if (
|
dragEnter={dragEnter}
|
||||||
event.key === "Enter" &&
|
dragLeave={dragLeave}
|
||||||
!lockChat &&
|
onDrop={onDrop}
|
||||||
!saveLoading &&
|
checkSendingOk={checkSendingOk}
|
||||||
!event.shiftKey &&
|
send={send}
|
||||||
!event.nativeEvent.isComposing
|
lockChat={lockChat}
|
||||||
) {
|
noInput={noInput}
|
||||||
send();
|
saveLoading={saveLoading}
|
||||||
}
|
chatValue={chatValue}
|
||||||
}}
|
setChatValue={setChatValue}
|
||||||
rows={1}
|
CHAT_INPUT_PLACEHOLDER={CHAT_INPUT_PLACEHOLDER}
|
||||||
ref={inputRef}
|
CHAT_INPUT_PLACEHOLDER_SEND={CHAT_INPUT_PLACEHOLDER_SEND}
|
||||||
disabled={lockChat || noInput || saveLoading}
|
inputRef={inputRef}
|
||||||
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
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="form-modal-send-icon-position">
|
<div className="form-modal-send-icon-position">
|
||||||
<button
|
<ButtonSendWrapper
|
||||||
className={classNames(
|
send={send}
|
||||||
"form-modal-send-button",
|
lockChat={lockChat}
|
||||||
noInput
|
noInput={noInput}
|
||||||
? "bg-high-indigo text-background"
|
saveLoading={saveLoading}
|
||||||
: chatValue === ""
|
chatValue={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>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-full gap-2 pb-2">
|
<div className="flex w-full gap-2 pb-2">
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,7 @@ import useAlertStore from "../../../../stores/alertStore";
|
||||||
import useFlowStore from "../../../../stores/flowStore";
|
import useFlowStore from "../../../../stores/flowStore";
|
||||||
import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
|
import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
|
||||||
import { VertexBuildTypeAPI, sendAllProps } from "../../../../types/api";
|
import { VertexBuildTypeAPI, sendAllProps } from "../../../../types/api";
|
||||||
import {
|
import { ChatMessageType, ChatOutputType } from "../../../../types/chat";
|
||||||
ChatMessageType,
|
|
||||||
ChatOutputType,
|
|
||||||
FlowPoolObjectType,
|
|
||||||
} from "../../../../types/chat";
|
|
||||||
import { chatViewProps } from "../../../../types/components";
|
import { chatViewProps } from "../../../../types/components";
|
||||||
import { classNames } from "../../../../utils/utils";
|
import { classNames } from "../../../../utils/utils";
|
||||||
import ChatInput from "./chatInput";
|
import ChatInput from "./chatInput";
|
||||||
|
|
@ -64,11 +60,13 @@ export default function ChatView({
|
||||||
const chatMessages: ChatMessageType[] = chatOutputResponses
|
const chatMessages: ChatMessageType[] = chatOutputResponses
|
||||||
.sort((a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp))
|
.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) => {
|
.map((output, index) => {
|
||||||
try {
|
try {
|
||||||
const { sender, message, sender_name, stream_url,files } = output.data
|
const { sender, message, sender_name, stream_url, files } = output
|
||||||
.messages[0] as ChatOutputType;
|
.data.messages[0] as ChatOutputType;
|
||||||
|
|
||||||
const is_ai = sender === "Machine" || sender === null;
|
const is_ai = sender === "Machine" || sender === null;
|
||||||
return {
|
return {
|
||||||
|
|
@ -77,8 +75,7 @@ export default function ChatView({
|
||||||
sender_name,
|
sender_name,
|
||||||
componentId: output.id,
|
componentId: output.id,
|
||||||
stream_url: stream_url,
|
stream_url: stream_url,
|
||||||
files
|
files,
|
||||||
|
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
|
|
@ -123,7 +120,7 @@ export default function ChatView({
|
||||||
function updateChat(
|
function updateChat(
|
||||||
chat: ChatMessageType,
|
chat: ChatMessageType,
|
||||||
message: string,
|
message: string,
|
||||||
stream_url?: string
|
stream_url?: string,
|
||||||
) {
|
) {
|
||||||
// if (message === "") return;
|
// if (message === "") return;
|
||||||
chat.message = message;
|
chat.message = message;
|
||||||
|
|
@ -158,7 +155,7 @@ export default function ChatView({
|
||||||
"h-5 w-5",
|
"h-5 w-5",
|
||||||
lockChat
|
lockChat
|
||||||
? "animate-pulse text-primary"
|
? "animate-pulse text-primary"
|
||||||
: "text-primary hover:text-gray-600"
|
: "text-primary hover:text-gray-600",
|
||||||
)}
|
)}
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue