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" }, diff --git a/chat/src/Components/Chat/Chat.tsx b/chat/src/Components/Chat/Chat.tsx index ddcd48d..29be326 100644 --- a/chat/src/Components/Chat/Chat.tsx +++ b/chat/src/Components/Chat/Chat.tsx @@ -3,6 +3,9 @@ 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"; +import { NoChatResultIllustration } from "../../assets/Illustrations/NoChatResult"; export const Chat: FC<{ messages: IMessage[]; @@ -13,6 +16,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 +66,27 @@ export const Chat: FC<{ focusedMessage, })} > - {messages.map((message: IMessage, index: number) => { - return ( - - ); - })} + {messagesToRender.length === 0 ? ( +
+ It's quiet... too quiet. Try another search? +
+ +
+
+ ) : ( + messagesToRender.map( + (message: IMessage, index: number) => ( + + ) + ) + )}
@@ -69,6 +103,10 @@ export const Chat: FC<{ /> )} + ); }; 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"; 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 ( +
+ + + + + + + + + + + + + + + + + + + +
+ ); +}