From 878a1a5cb8987f88b58234d83e8ef4c42a97027d Mon Sep 17 00:00:00 2001 From: mamadoudicko Date: Thu, 17 Aug 2023 17:19:28 +0200 Subject: [PATCH 01/10] feat(chat): add brain selection through mention input --- .../components/ActionsBar/ActionsBar.tsx | 2 +- .../ChatInput/__tests__/ChatInput.test.tsx | 0 .../ChatInput/components/ChatBar/ChatBar.tsx | 11 + .../ChatBar/components/BrainMentionItem.tsx} | 8 +- .../components/MentionInput/MentionInput.tsx | 84 +++ .../components/AddNewBrainButton.tsx | 9 + .../components/BrainSuggestion.tsx | 27 + .../MentionInput/components/index.ts | 1 + .../hooks/helpers/MentionPlugin.tsx | 34 + .../hooks/helpers/MentionState.ts | 96 +++ .../hooks/helpers/MentionUtils.ts | 74 ++ .../MentionInput/hooks/useMentionInput.tsx | 122 ++++ .../ChatBar/components/MentionInput/index.ts | 1 + .../utils/mapMinimalBrainToMentionData.ts | 11 + .../components/ChatBar/components/index.ts | 1 + .../components/ChatBar/hooks/useChatBar.ts | 10 + .../ChatInput/components/ChatBar/index.ts | 1 + .../ChatInput/components/ChatBar/types.ts | 5 + .../components/ConfigModal/ConfigModal.tsx | 0 .../ConfigModal/hooks/useConfigModal.ts | 0 .../ChatInput/components/ConfigModal/index.ts | 0 .../components/MicButton/MicButton.tsx | 0 .../components/MicButton/hooks/useSpeech.ts | 0 .../components/ChatInput/components/index.ts | 1 + .../ChatInput/hooks/useChatInput.ts | 9 +- .../ActionsBar/components/ChatInput/index.tsx | 47 ++ .../ActionsBar/components/MentionsInput.tsx | 21 - .../components/ActionsBar/components/index.ts | 3 +- .../ActionsBar/hooks/useActionsBar.tsx | 15 - .../[chatId]/components/ChatInput/index.tsx | 73 -- .../app/chat/[chatId]/components/index.ts | 1 - .../components/BrainUser/BrainUser.tsx | 6 +- .../components/BrainActions/BrainActions.tsx | 3 +- .../BrainActions/components/index.ts | 1 - .../components/BrainActions/types.ts | 1 + .../components => }/ShareBrain/ShareBrain.tsx | 0 .../ShareBrain/__tests__/ShareBrain.test.tsx | 0 .../components => }/ShareBrain/index.ts | 0 .../components => }/ShareBrain/types/index.ts | 2 +- .../utils/generateBrainAssignation.ts | 2 +- frontend/lib/components/UserToInvite.tsx | 6 +- frontend/lib/hooks/useShareBrain.ts | 10 +- frontend/package.json | 4 + frontend/yarn.lock | 644 ++++++++++++------ 44 files changed, 988 insertions(+), 358 deletions(-) rename frontend/app/chat/[chatId]/components/{ => ActionsBar/components}/ChatInput/__tests__/ChatInput.test.tsx (100%) create mode 100644 frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/ChatBar.tsx rename frontend/app/chat/[chatId]/components/ActionsBar/components/{MentionItem.tsx => ChatInput/components/ChatBar/components/BrainMentionItem.tsx} (74%) create mode 100644 frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/MentionInput/MentionInput.tsx create mode 100644 frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/MentionInput/components/AddNewBrainButton.tsx create mode 100644 frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/MentionInput/components/BrainSuggestion.tsx create mode 100644 frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/MentionInput/components/index.ts create mode 100644 frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/MentionInput/hooks/helpers/MentionPlugin.tsx create mode 100644 frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/MentionInput/hooks/helpers/MentionState.ts create mode 100644 frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/MentionInput/hooks/helpers/MentionUtils.ts create mode 100644 frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/MentionInput/hooks/useMentionInput.tsx create mode 100644 frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/MentionInput/index.ts create mode 100644 frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/MentionInput/utils/mapMinimalBrainToMentionData.ts create mode 100644 frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/index.ts create mode 100644 frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/hooks/useChatBar.ts create mode 100644 frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/index.ts create mode 100644 frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/types.ts rename frontend/app/chat/[chatId]/components/{ => ActionsBar/components}/ChatInput/components/ConfigModal/ConfigModal.tsx (100%) rename frontend/app/chat/[chatId]/components/{ => ActionsBar/components}/ChatInput/components/ConfigModal/hooks/useConfigModal.ts (100%) rename frontend/app/chat/[chatId]/components/{ => ActionsBar/components}/ChatInput/components/ConfigModal/index.ts (100%) rename frontend/app/chat/[chatId]/components/{ => ActionsBar/components}/ChatInput/components/MicButton/MicButton.tsx (100%) rename frontend/app/chat/[chatId]/components/{ => ActionsBar/components}/ChatInput/components/MicButton/hooks/useSpeech.ts (100%) create mode 100644 frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/index.ts rename frontend/app/chat/[chatId]/components/{ => ActionsBar/components}/ChatInput/hooks/useChatInput.ts (61%) create mode 100644 frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/index.tsx delete mode 100644 frontend/app/chat/[chatId]/components/ActionsBar/components/MentionsInput.tsx delete mode 100644 frontend/app/chat/[chatId]/components/ActionsBar/hooks/useActionsBar.tsx delete mode 100644 frontend/app/chat/[chatId]/components/ChatInput/index.tsx rename frontend/lib/components/{NavBar/components/NavItems/components/BrainsDropDown/components/BrainActions/components => }/ShareBrain/ShareBrain.tsx (100%) rename frontend/lib/components/{NavBar/components/NavItems/components/BrainsDropDown/components/BrainActions/components => }/ShareBrain/__tests__/ShareBrain.test.tsx (100%) rename frontend/lib/components/{NavBar/components/NavItems/components/BrainsDropDown/components/BrainActions/components => }/ShareBrain/index.ts (100%) rename frontend/lib/components/{NavBar/components/NavItems/components/BrainsDropDown/components/BrainActions/components => }/ShareBrain/types/index.ts (83%) rename frontend/lib/components/{NavBar/components/NavItems/components/BrainsDropDown/components/BrainActions/components => }/ShareBrain/utils/generateBrainAssignation.ts (55%) diff --git a/frontend/app/chat/[chatId]/components/ActionsBar/ActionsBar.tsx b/frontend/app/chat/[chatId]/components/ActionsBar/ActionsBar.tsx index 28b903981825..1063dcabce97 100644 --- a/frontend/app/chat/[chatId]/components/ActionsBar/ActionsBar.tsx +++ b/frontend/app/chat/[chatId]/components/ActionsBar/ActionsBar.tsx @@ -1,4 +1,4 @@ -import { ChatInput } from "../ChatInput"; +import { ChatInput } from "./components"; export const ActionsBar = (): JSX.Element => { return ( diff --git a/frontend/app/chat/[chatId]/components/ChatInput/__tests__/ChatInput.test.tsx b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/__tests__/ChatInput.test.tsx similarity index 100% rename from frontend/app/chat/[chatId]/components/ChatInput/__tests__/ChatInput.test.tsx rename to frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/__tests__/ChatInput.test.tsx diff --git a/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/ChatBar.tsx b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/ChatBar.tsx new file mode 100644 index 000000000000..725984145686 --- /dev/null +++ b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/ChatBar.tsx @@ -0,0 +1,11 @@ +"use client"; + +import { MentionInput } from "./components"; + +type ChatBarProps = { + onSubmit: (text?: string) => void; +}; + +export const ChatBar = ({ onSubmit }: ChatBarProps): JSX.Element => { + return ; +}; diff --git a/frontend/app/chat/[chatId]/components/ActionsBar/components/MentionItem.tsx b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/BrainMentionItem.tsx similarity index 74% rename from frontend/app/chat/[chatId]/components/ActionsBar/components/MentionItem.tsx rename to frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/BrainMentionItem.tsx index 7e671a289de5..b2b4089d79aa 100644 --- a/frontend/app/chat/[chatId]/components/ActionsBar/components/MentionItem.tsx +++ b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/BrainMentionItem.tsx @@ -3,18 +3,16 @@ import { MdRemoveCircleOutline } from "react-icons/md"; type MentionItemProps = { text: string; onRemove: () => void; - prefix?: string; }; -export const MentionItem = ({ +export const BrainMentionItem = ({ text, - prefix = "", onRemove, }: MentionItemProps): JSX.Element => { return ( -
+
- {`${prefix}${text}`} + {text} void; +}; +export const MentionInput = ({ onSubmit }: MentionInputProps): ReactElement => { + const { + mentionInputRef, + MentionSuggestions, + editorState, + onOpenChange, + onSearchChange, + open, + plugins, + setEditorState, + suggestions, + onAddMention, + } = useMentionInput(); + + const keyBindingFn = (e: React.KeyboardEvent) => { + if (e.key === "Enter" && !e.shiftKey) { + const contentState = editorState.getCurrentContent(); + const rawText = contentState.getPlainText(); + onSubmit(rawText); + // empty the editor content by keep + setEditorState(EditorState.createEmpty()); + + return "submit"; + } + + return getDefaultKeyBinding(e); + }; + + const { t } = useTranslation(["chat"]); + + return ( +
{ + mentionInputRef.current?.focus(); + }} + > + + ( + +
+ {children} + +
+
+ )} + onAddMention={onAddMention} + entryComponent={({ mention, ...otherProps }) => ( +
+ +
+ )} + /> +
+ ); +}; diff --git a/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/MentionInput/components/AddNewBrainButton.tsx b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/MentionInput/components/AddNewBrainButton.tsx new file mode 100644 index 000000000000..ececd1b775c4 --- /dev/null +++ b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/MentionInput/components/AddNewBrainButton.tsx @@ -0,0 +1,9 @@ +import Link from "next/link"; + +import Button from "@/lib/components/ui/Button"; + +export const AddNewBrainButton = (): JSX.Element => ( + + + +); diff --git a/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/MentionInput/components/BrainSuggestion.tsx b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/MentionInput/components/BrainSuggestion.tsx new file mode 100644 index 000000000000..3a905fd6018b --- /dev/null +++ b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/MentionInput/components/BrainSuggestion.tsx @@ -0,0 +1,27 @@ +import { UUID } from "crypto"; + +import { ShareBrain } from "@/lib/components/ShareBrain"; + +type BrainSuggestionProps = { + content: string; + id: string; +}; +export const BrainSuggestion = ({ + content, + id, +}: BrainSuggestionProps): JSX.Element => { + return ( +
+
+ {content} +
+
+ +
+
+ ); +}; diff --git a/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/MentionInput/components/index.ts b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/MentionInput/components/index.ts new file mode 100644 index 000000000000..44cffc618d2a --- /dev/null +++ b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/MentionInput/components/index.ts @@ -0,0 +1 @@ +export * from "./AddNewBrainButton"; diff --git a/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/MentionInput/hooks/helpers/MentionPlugin.tsx b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/MentionInput/hooks/helpers/MentionPlugin.tsx new file mode 100644 index 000000000000..b3d75eaae533 --- /dev/null +++ b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/MentionInput/hooks/helpers/MentionPlugin.tsx @@ -0,0 +1,34 @@ +import createMentionPlugin from "@draft-js-plugins/mention"; +import { useMemo } from "react"; + +import { BrainMentionItem } from "../../../BrainMentionItem"; + +interface MentionPluginProps { + removeMention: (entityKeyToRemove: string) => void; +} + +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types +export const useMentionPlugin = (props: MentionPluginProps) => { + const { removeMention } = props; + + const { MentionSuggestions, plugins } = useMemo(() => { + const mentionPlugin = createMentionPlugin({ + mentionComponent: ({ entityKey, mention: { name } }) => ( + removeMention(entityKey)} + /> + ), + + popperOptions: { + placement: "top-end", + }, + }); + const { MentionSuggestions: coreMentionSuggestions } = mentionPlugin; + const corePlugins = [mentionPlugin]; + + return { plugins: corePlugins, MentionSuggestions: coreMentionSuggestions }; + }, []); + + return { MentionSuggestions, plugins }; +}; diff --git a/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/MentionInput/hooks/helpers/MentionState.ts b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/MentionInput/hooks/helpers/MentionState.ts new file mode 100644 index 000000000000..cc1ff3275696 --- /dev/null +++ b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/MentionInput/hooks/helpers/MentionState.ts @@ -0,0 +1,96 @@ +import { EditorState, Modifier } from "draft-js"; +import { useEffect, useState } from "react"; + +import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext"; + +import { MentionInputMentionsType } from "../../../../types"; +import { mapMinimalBrainToMentionData } from "../../utils/mapMinimalBrainToMentionData"; + +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types +export const useMentionState = () => { + const { allBrains, currentBrainId } = useBrainContext(); + const [editorState, setEditorState] = useState(() => + EditorState.createEmpty() + ); + + const [mentionItems, setMentionItems] = useState({ + "@": allBrains.map((brain) => ({ ...brain, value: brain.name })), + }); + + const [suggestions, setSuggestions] = useState( + mapMinimalBrainToMentionData(mentionItems["@"]) + ); + + useEffect(() => { + setMentionItems({ + ...mentionItems, + "@": [ + ...allBrains.map((brain) => ({ + ...brain, + value: brain.name, + })), + ], + }); + }, [allBrains]); + + useEffect(() => { + if (currentBrainId === null || mentionItems["@"].length === 0) { + return; + } + + const mention = mentionItems["@"].find( + (item) => item.id === currentBrainId + ); + + if (mention !== undefined) { + const mentionText = `@${mention.name}`; + const mentionWithSpace = `${mentionText} `; + + const contentState = editorState.getCurrentContent(); + const plainText = contentState.getPlainText(); + + if (plainText.includes(mentionWithSpace)) { + return; + } + + const stateWithEntity = contentState.createEntity( + "mention", + "IMMUTABLE", + { + mention, + } + ); + const entityKey = stateWithEntity.getLastCreatedEntityKey(); + + const selectionState = editorState.getSelection(); + const updatedContentState = Modifier.insertText( + contentState, + selectionState, + mentionWithSpace, + undefined, + entityKey + ); + + const newSelection = selectionState.merge({ + anchorOffset: selectionState.getStartOffset() + mentionWithSpace.length, + focusOffset: selectionState.getStartOffset() + mentionWithSpace.length, + }); + + const newEditorState = EditorState.forceSelection( + EditorState.push(editorState, updatedContentState, "insert-characters"), + newSelection + ); + + setEditorState(newEditorState); + } + }, [currentBrainId, mentionItems]); + + return { + editorState, + setEditorState, + mentionItems, + setSuggestions, + setMentionItems, + suggestions, + }; +}; diff --git a/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/MentionInput/hooks/helpers/MentionUtils.ts b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/MentionInput/hooks/helpers/MentionUtils.ts new file mode 100644 index 000000000000..12e2fffcb990 --- /dev/null +++ b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/MentionInput/hooks/helpers/MentionUtils.ts @@ -0,0 +1,74 @@ +import { MentionData } from "@draft-js-plugins/mention"; +import { EditorState, Modifier } from "draft-js"; + +import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext"; + +type MentionUtilsProps = { + editorState: EditorState; + setEditorState: React.Dispatch>; +}; + +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types +export const useMentionUtils = (props: MentionUtilsProps) => { + const { editorState, setEditorState } = props; + const { setCurrentBrainId } = useBrainContext(); + + const removeMention = (entityKeyToRemove: string): void => { + const contentState = editorState.getCurrentContent(); + const entity = contentState.getEntity(entityKeyToRemove); + + if (entity.getType() === "mention") { + const newContentState = contentState.replaceEntityData( + entityKeyToRemove, + {} + ); + + const newEditorState = EditorState.push( + editorState, + newContentState, + "apply-entity" + ); + + setEditorState(newEditorState); + setCurrentBrainId(null); + } + }; + + const insertMention = ( + mention: MentionData, + mentionWithSpace = " " + ): EditorState => { + const contentState = editorState.getCurrentContent(); + const selectionState = editorState.getSelection(); + + const stateWithEntity = contentState.createEntity("mention", "IMMUTABLE", { + mention, + }); + const entityKey = stateWithEntity.getLastCreatedEntityKey(); + + const newContentState = Modifier.insertText( + contentState, + selectionState, + mentionWithSpace, + undefined, + entityKey + ); + + const newSelection = selectionState.merge({ + anchorOffset: selectionState.getStartOffset() + mentionWithSpace.length, + focusOffset: selectionState.getStartOffset() + mentionWithSpace.length, + }); + + const newEditorState = EditorState.forceSelection( + EditorState.push(editorState, newContentState, "insert-characters"), + newSelection + ); + + return newEditorState; + }; + + return { + removeMention, + insertMention, + }; +}; diff --git a/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/MentionInput/hooks/useMentionInput.tsx b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/MentionInput/hooks/useMentionInput.tsx new file mode 100644 index 000000000000..0270e3b7a2f1 --- /dev/null +++ b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/MentionInput/hooks/useMentionInput.tsx @@ -0,0 +1,122 @@ +/* eslint-disable max-lines */ +import Editor from "@draft-js-plugins/editor"; +import { + defaultSuggestionsFilter, + MentionData, +} from "@draft-js-plugins/mention"; +import { UUID } from "crypto"; +import { useCallback, useEffect, useRef, useState } from "react"; + +import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext"; + +import { useMentionPlugin } from "./helpers/MentionPlugin"; +import { useMentionState } from "./helpers/MentionState"; +import { useMentionUtils } from "./helpers/MentionUtils"; +import { mapMinimalBrainToMentionData } from "../utils/mapMinimalBrainToMentionData"; + +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types +export const useMentionInput = () => { + const { allBrains, currentBrainId, setCurrentBrainId } = useBrainContext(); + + const { + editorState, + setEditorState, + setMentionItems, + mentionItems, + setSuggestions, + suggestions, + } = useMentionState(); + + const { removeMention, insertMention } = useMentionUtils({ + editorState, + setEditorState, + }); + + const { MentionSuggestions, plugins } = useMentionPlugin({ + removeMention, + }); + + const mentionInputRef = useRef(null); + + const [selectedBrainAddedOnload, setSelectedBrainAddedOnload] = + useState(false); + + const [open, setOpen] = useState(false); + + const onOpenChange = useCallback((_open: boolean) => { + setOpen(_open); + }, []); + + const onAddMention = (mention: MentionData) => { + setCurrentBrainId(mention.id as UUID); + }; + + const onSearchChange = useCallback( + ({ trigger, value }: { trigger: string; value: string }) => { + setSuggestions( + defaultSuggestionsFilter( + value, + currentBrainId !== null + ? [] + : mapMinimalBrainToMentionData(mentionItems["@"]), + trigger + ) + ); + }, + [mentionItems, currentBrainId] + ); + + useEffect(() => { + setSuggestions(mapMinimalBrainToMentionData(mentionItems["@"])); + }, [mentionItems]); + + useEffect(() => { + setMentionItems({ + ...mentionItems, + "@": [ + ...allBrains.map((brain) => ({ + ...brain, + value: brain.name, + })), + ], + }); + }, [allBrains]); + + useEffect(() => { + if (selectedBrainAddedOnload) { + return; + } + + if (currentBrainId === null || mentionItems["@"].length === 0) { + return; + } + + const mention = mentionItems["@"].find( + (item) => item.id === currentBrainId + ); + + if (mention === undefined) { + return; + } + + insertMention({ + id: currentBrainId, + name: mention.name, + }); + + setSelectedBrainAddedOnload(true); + }, [currentBrainId, mentionItems]); + + return { + mentionInputRef, + plugins, + MentionSuggestions, + onOpenChange, + onSearchChange, + open, + suggestions, + onAddMention, + setEditorState, + editorState, + }; +}; diff --git a/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/MentionInput/index.ts b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/MentionInput/index.ts new file mode 100644 index 000000000000..bc5bdfe89212 --- /dev/null +++ b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/MentionInput/index.ts @@ -0,0 +1 @@ +export * from "./MentionInput"; diff --git a/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/MentionInput/utils/mapMinimalBrainToMentionData.ts b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/MentionInput/utils/mapMinimalBrainToMentionData.ts new file mode 100644 index 000000000000..0eb02e66730e --- /dev/null +++ b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/MentionInput/utils/mapMinimalBrainToMentionData.ts @@ -0,0 +1,11 @@ +import { MentionData } from "@draft-js-plugins/mention"; + +import { MinimalBrainForUser } from "@/lib/context/BrainProvider/types"; + +export const mapMinimalBrainToMentionData = ( + brains: MinimalBrainForUser[] +): MentionData[] => + brains.map((brain) => ({ + name: brain.name, + id: brain.id as string, + })); diff --git a/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/index.ts b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/index.ts new file mode 100644 index 000000000000..bc5bdfe89212 --- /dev/null +++ b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/components/index.ts @@ -0,0 +1 @@ +export * from "./MentionInput"; diff --git a/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/hooks/useChatBar.ts b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/hooks/useChatBar.ts new file mode 100644 index 000000000000..7e83e5aeb94c --- /dev/null +++ b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/hooks/useChatBar.ts @@ -0,0 +1,10 @@ +import { useFeature } from "@growthbook/growthbook-react"; + +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types +export const useChatBar = () => { + const shouldUseNewUX = useFeature("new-ux").on; + + return { + shouldUseNewUX, + }; +}; diff --git a/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/index.ts b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/index.ts new file mode 100644 index 000000000000..85c1ff984f5a --- /dev/null +++ b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/index.ts @@ -0,0 +1 @@ +export * from "./ChatBar"; diff --git a/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/types.ts b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/types.ts new file mode 100644 index 000000000000..27da9948901c --- /dev/null +++ b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ChatBar/types.ts @@ -0,0 +1,5 @@ +import { MinimalBrainForUser } from "@/lib/context/BrainProvider/types"; + +export type MentionInputMentionsType = { + "@": MinimalBrainForUser[]; +}; diff --git a/frontend/app/chat/[chatId]/components/ChatInput/components/ConfigModal/ConfigModal.tsx b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ConfigModal/ConfigModal.tsx similarity index 100% rename from frontend/app/chat/[chatId]/components/ChatInput/components/ConfigModal/ConfigModal.tsx rename to frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ConfigModal/ConfigModal.tsx diff --git a/frontend/app/chat/[chatId]/components/ChatInput/components/ConfigModal/hooks/useConfigModal.ts b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ConfigModal/hooks/useConfigModal.ts similarity index 100% rename from frontend/app/chat/[chatId]/components/ChatInput/components/ConfigModal/hooks/useConfigModal.ts rename to frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ConfigModal/hooks/useConfigModal.ts diff --git a/frontend/app/chat/[chatId]/components/ChatInput/components/ConfigModal/index.ts b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ConfigModal/index.ts similarity index 100% rename from frontend/app/chat/[chatId]/components/ChatInput/components/ConfigModal/index.ts rename to frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/ConfigModal/index.ts diff --git a/frontend/app/chat/[chatId]/components/ChatInput/components/MicButton/MicButton.tsx b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/MicButton/MicButton.tsx similarity index 100% rename from frontend/app/chat/[chatId]/components/ChatInput/components/MicButton/MicButton.tsx rename to frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/MicButton/MicButton.tsx diff --git a/frontend/app/chat/[chatId]/components/ChatInput/components/MicButton/hooks/useSpeech.ts b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/MicButton/hooks/useSpeech.ts similarity index 100% rename from frontend/app/chat/[chatId]/components/ChatInput/components/MicButton/hooks/useSpeech.ts rename to frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/MicButton/hooks/useSpeech.ts diff --git a/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/index.ts b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/index.ts new file mode 100644 index 000000000000..85c1ff984f5a --- /dev/null +++ b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/components/index.ts @@ -0,0 +1 @@ +export * from "./ChatBar"; diff --git a/frontend/app/chat/[chatId]/components/ChatInput/hooks/useChatInput.ts b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/hooks/useChatInput.ts similarity index 61% rename from frontend/app/chat/[chatId]/components/ChatInput/hooks/useChatInput.ts rename to frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/hooks/useChatInput.ts index fca7c036db37..7592ce51a129 100644 --- a/frontend/app/chat/[chatId]/components/ChatInput/hooks/useChatInput.ts +++ b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/hooks/useChatInput.ts @@ -1,18 +1,19 @@ import { useState } from "react"; -import { useChat } from "../../../hooks/useChat"; +import { useChat } from "@/app/chat/[chatId]/hooks/useChat"; // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types export const useChatInput = () => { const [message, setMessage] = useState(""); const { addQuestion, generatingAnswer, chatId } = useChat(); - const submitQuestion = () => { - if (message.length === 0) { + const submitQuestion = (currentMessage?: string) => { + const messageToSubmit = currentMessage ?? message; + if (messageToSubmit.length === 0) { return; } if (!generatingAnswer) { - void addQuestion(message, () => setMessage("")); + void addQuestion(messageToSubmit, () => setMessage("")); } }; diff --git a/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/index.tsx b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/index.tsx new file mode 100644 index 000000000000..151faae45ea6 --- /dev/null +++ b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/index.tsx @@ -0,0 +1,47 @@ +"use client"; +import { useTranslation } from "react-i18next"; + +import Button from "@/lib/components/ui/Button"; + +import { ChatBar } from "./components/ChatBar/ChatBar"; +import { ConfigModal } from "./components/ConfigModal"; +import { MicButton } from "./components/MicButton/MicButton"; +import { useChatInput } from "./hooks/useChatInput"; + +export const ChatInput = (): JSX.Element => { + const { setMessage, submitQuestion, chatId, generatingAnswer } = + useChatInput(); + const { t } = useTranslation(["chat"]); + + return ( +
{ + e.preventDefault(); + submitQuestion(); + }} + className="sticky flex items-star bottom-0 bg-white dark:bg-black w-full flex justify-center gap-2 z-20" + > +
+ +
+ +
+ +
+ + +
+
+
+ ); +}; diff --git a/frontend/app/chat/[chatId]/components/ActionsBar/components/MentionsInput.tsx b/frontend/app/chat/[chatId]/components/ActionsBar/components/MentionsInput.tsx deleted file mode 100644 index 07226dc424b4..000000000000 --- a/frontend/app/chat/[chatId]/components/ActionsBar/components/MentionsInput.tsx +++ /dev/null @@ -1,21 +0,0 @@ -type StyleMentionsInputProps = { - value: string; - onChange: (value: string) => void; - placeholder: string; -}; - -export const MentionsInput = ({ - onChange, - placeholder, - value, -}: StyleMentionsInputProps): JSX.Element => { - return ( - onChange(event.target.value)} - value={value} - className="focus:outline-none focus:border-none" - /> - ); -}; diff --git a/frontend/app/chat/[chatId]/components/ActionsBar/components/index.ts b/frontend/app/chat/[chatId]/components/ActionsBar/components/index.ts index 80fc14b5019b..924a01f255d8 100644 --- a/frontend/app/chat/[chatId]/components/ActionsBar/components/index.ts +++ b/frontend/app/chat/[chatId]/components/ActionsBar/components/index.ts @@ -1,2 +1 @@ -export * from "./MentionItem"; -export * from "./MentionsInput"; +export * from "./ChatInput"; diff --git a/frontend/app/chat/[chatId]/components/ActionsBar/hooks/useActionsBar.tsx b/frontend/app/chat/[chatId]/components/ActionsBar/hooks/useActionsBar.tsx deleted file mode 100644 index 1203de50b48e..000000000000 --- a/frontend/app/chat/[chatId]/components/ActionsBar/hooks/useActionsBar.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { useState } from "react"; - -// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types -export const useActionsBar = () => { - const [value, setValue] = useState(""); - - const handleChange = (newPlainTextValue: string) => { - setValue(newPlainTextValue); - }; - - return { - handleChange, - value, - }; -}; diff --git a/frontend/app/chat/[chatId]/components/ChatInput/index.tsx b/frontend/app/chat/[chatId]/components/ChatInput/index.tsx deleted file mode 100644 index 80b008e49c04..000000000000 --- a/frontend/app/chat/[chatId]/components/ChatInput/index.tsx +++ /dev/null @@ -1,73 +0,0 @@ -"use client"; -import { useFeature } from "@growthbook/growthbook-react"; -import { useTranslation } from "react-i18next"; - -import Button from "@/lib/components/ui/Button"; -import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext"; - -import { ConfigModal } from "./components/ConfigModal"; -import { MicButton } from "./components/MicButton/MicButton"; -import { useChatInput } from "./hooks/useChatInput"; -import { MentionItem } from "../ActionsBar/components"; - -export const ChatInput = (): JSX.Element => { - const { message, setMessage, submitQuestion, chatId, generatingAnswer } = - useChatInput(); - const { t } = useTranslation(["chat"]); - const { currentBrain, setCurrentBrainId } = useBrainContext(); - const shouldUseNewUX = useFeature("new-ux").on; - - return ( -
{ - e.preventDefault(); - submitQuestion(); - }} - className="sticky flex items-star bottom-0 bg-white dark:bg-black w-full flex justify-center gap-2 z-20" - > - {currentBrain !== undefined && ( - setCurrentBrainId(null)} - prefix="@" - /> - )} - -