From 2e5c6db6287a1d282942d935fc1502c3b010964c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric?= Date: Wed, 25 Oct 2023 13:56:44 +0200 Subject: [PATCH 1/4] build(deps): Added Fuse.js --- chat/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/chat/package.json b/chat/package.json index facc36e..f972681 100644 --- a/chat/package.json +++ b/chat/package.json @@ -11,6 +11,7 @@ }, "dependencies": { "classnames": "^2.3.2", + "fuse.js": "^6.6.2", "react": "^18.2.0", "react-dom": "^18.2.0" }, From 5ed6512612589443ded87d71c1fdcff6a60333e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric?= Date: Wed, 25 Oct 2023 13:57:47 +0200 Subject: [PATCH 2/4] feat: Added SearchDialog Component --- .../Components/SearchDialog/SearchDialog.tsx | 120 ++++++++++++++++++ chat/src/Components/SearchDialog/index.ts | 1 + 2 files changed, 121 insertions(+) create mode 100644 chat/src/Components/SearchDialog/SearchDialog.tsx create mode 100644 chat/src/Components/SearchDialog/index.ts diff --git a/chat/src/Components/SearchDialog/SearchDialog.tsx b/chat/src/Components/SearchDialog/SearchDialog.tsx new file mode 100644 index 0000000..dc79e0a --- /dev/null +++ b/chat/src/Components/SearchDialog/SearchDialog.tsx @@ -0,0 +1,120 @@ +import React, { useState, useEffect, useRef } from "react"; + +type SearchDialogProps = { + onSearchChange: (newSearchTerm: string) => void; + searchTerm: string; +}; + +export const SearchDialog: React.FC = ({ + onSearchChange, + searchTerm, +}) => { + const [inputValue, setInputValue] = useState(searchTerm); + const inputRef = useRef(null); + const [isSearchDialogOpen, setIsSearchDialogOpen] = useState(false); + + const handleInputChange = (event: React.ChangeEvent) => { + const newSearchTerm = event.target.value; + setInputValue(newSearchTerm); + onSearchChange(newSearchTerm); + }; + + const clearInput = () => { + setInputValue(""); + onSearchChange(""); + inputRef.current?.focus(); + }; + + const handleKeyDown = (event: KeyboardEvent) => { + if ((event.metaKey || event.ctrlKey) && event.key === "f") { + event.preventDefault(); + setIsSearchDialogOpen((prev) => !prev); + } + }; + + useEffect(() => { + window.addEventListener("keydown", handleKeyDown); + return () => { + window.removeEventListener("keydown", handleKeyDown); + }; + }, []); + + const handleBackdropClick = (e: React.MouseEvent) => { + if (e.target === e.currentTarget) { + setIsSearchDialogOpen(false); + } + }; + + useEffect(() => { + inputRef.current?.focus(); + }, [isSearchDialogOpen]); + + return ( + isSearchDialogOpen && ( +
+
e.stopPropagation()} + > +
+
+
+
+ +
+ +
+ +
+
+
+
+
+
+ ) + ); +}; diff --git a/chat/src/Components/SearchDialog/index.ts b/chat/src/Components/SearchDialog/index.ts new file mode 100644 index 0000000..32ea5df --- /dev/null +++ b/chat/src/Components/SearchDialog/index.ts @@ -0,0 +1 @@ +export * from "./SearchDialog"; From e082f495b5cdaeb2ff9039da5337eb19d22a1f47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric?= Date: Wed, 25 Oct 2023 13:58:26 +0200 Subject: [PATCH 3/4] feat: Implement SearchDialog to the Chat component --- chat/src/Components/Chat/Chat.tsx | 54 ++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/chat/src/Components/Chat/Chat.tsx b/chat/src/Components/Chat/Chat.tsx index ddcd48d..06ef75e 100644 --- a/chat/src/Components/Chat/Chat.tsx +++ b/chat/src/Components/Chat/Chat.tsx @@ -3,6 +3,8 @@ import { IMessage } from "../../types"; import { Message } from "../Message"; import classNames from "classnames"; import { ReloadIcon } from "../../assets/Icons/Reload"; +import { SearchDialog } from "../SearchDialog"; +import Fuse from "fuse.js"; export const Chat: FC<{ messages: IMessage[]; @@ -13,6 +15,28 @@ export const Chat: FC<{ }> = ({ messages, focusedMessage, setFocusedMessage, onAction, filter }) => { const messagesEndRef = useRef(null); const [autoScroll, setAutoScroll] = useState(true); + const [searchTerm, setSearchTerm] = useState(""); + const [filteredMessages, setFilteredMessages] = + useState(messages); + const messagesToRender = searchTerm.trim() ? filteredMessages : messages; + + // This function is called whenever the search term changes in the SearchDialog + const handleSearchChange = (newSearchTerm: string) => { + setSearchTerm(newSearchTerm); + + // Fuse.js options; adjust as needed + const options = { + keys: ["content", "author", "platform"], + threshold: 0.4, + ignoreLocation: true, + }; + + const fuse = new Fuse(messages, options); + const result = fuse.search(newSearchTerm); + + const matches = result.map(({ item }) => item); // Extract the matched items + setFilteredMessages(matches); // Set filtered messages + }; const handleFocusMessage = (message: IMessage) => { setFocusedMessage(message.id === focusedMessage?.id ? null : message); @@ -41,18 +65,20 @@ export const Chat: FC<{ focusedMessage, })} > - {messages.map((message: IMessage, index: number) => { - return ( - - ); - })} + {messagesToRender.map( + (message: IMessage, index: number) => { + return ( + + ); + } + )}
@@ -69,6 +95,10 @@ export const Chat: FC<{ /> )} + ); }; From 353b39a5b06c8aa433e3fc3f4a8e83eeaa9a6fa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric?= Date: Wed, 25 Oct 2023 20:10:06 +0200 Subject: [PATCH 4/4] feat: Added a cute "no results" response --- chat/src/Components/Chat/Chat.tsx | 18 ++- .../src/assets/Illustrations/NoChatResult.tsx | 122 ++++++++++++++++++ 2 files changed, 135 insertions(+), 5 deletions(-) create mode 100644 chat/src/assets/Illustrations/NoChatResult.tsx diff --git a/chat/src/Components/Chat/Chat.tsx b/chat/src/Components/Chat/Chat.tsx index 06ef75e..29be326 100644 --- a/chat/src/Components/Chat/Chat.tsx +++ b/chat/src/Components/Chat/Chat.tsx @@ -5,6 +5,7 @@ import classNames from "classnames"; import { ReloadIcon } from "../../assets/Icons/Reload"; import { SearchDialog } from "../SearchDialog"; import Fuse from "fuse.js"; +import { NoChatResultIllustration } from "../../assets/Illustrations/NoChatResult"; export const Chat: FC<{ messages: IMessage[]; @@ -65,9 +66,16 @@ export const Chat: FC<{ focusedMessage, })} > - {messagesToRender.map( - (message: IMessage, index: number) => { - return ( + {messagesToRender.length === 0 ? ( +
+ It's quiet... too quiet. Try another search? +
+ +
+
+ ) : ( + messagesToRender.map( + (message: IMessage, index: number) => ( - ); - } + ) + ) )}
diff --git a/chat/src/assets/Illustrations/NoChatResult.tsx b/chat/src/assets/Illustrations/NoChatResult.tsx new file mode 100644 index 0000000..a14b6d6 --- /dev/null +++ b/chat/src/assets/Illustrations/NoChatResult.tsx @@ -0,0 +1,122 @@ +export function NoChatResultIllustration() { + const containerStyles = { + width: "50vw", + height: "50vh", + overflow: "hidden", + }; + + const svgStyles = { + width: "100%", + height: "100%", + }; + + return ( +
+ + + + + + + + + + + + + + + + + + + +
+ ); +}