diff --git a/app/components/chat-list.tsx b/app/components/chat-list.tsx index 7ef6e7b83..98506f68c 100644 --- a/app/components/chat-list.tsx +++ b/app/components/chat-list.tsx @@ -9,15 +9,15 @@ import { OnDragEndResponder, } from "@hello-pangea/dnd"; -import { useChatStore } from "../store"; +import { ChatSession, useChatStore } from "../store"; import Locale from "../locales"; import { Link, useLocation, useNavigate } from "react-router-dom"; import { Path } from "../constant"; import { MaskAvatar } from "./mask"; import { Mask } from "../store/mask"; -import { useRef, useEffect } from "react"; -import { showConfirm } from "./ui-lib"; +import { useRef, useEffect, useState } from "react"; +import { showConfirm, SearchInput } from "./ui-lib"; import { useMobileScreen } from "../utils"; export function ChatItem(props: { @@ -102,7 +102,7 @@ export function ChatItem(props: { ); } -export function ChatList(props: { narrow?: boolean }) { +export function ChatList(props: { narrow?: boolean; search: string }) { const [sessions, selectedIndex, selectSession, moveSession] = useChatStore( (state) => [ state.sessions, @@ -131,6 +131,24 @@ export function ChatList(props: { narrow?: boolean }) { moveSession(source.index, destination.index); }; + function haveSearchKeyword(item: ChatSession): boolean { + if (props.search.length === 0) { + return true; + } + + let foundKeyword = false; + + item.messages.forEach((message) => { + // console.log(chatListSearch, message.content, message.content.includes(chatListSearch)) + if (message.content.includes(props.search)) { + foundKeyword = true; + return; + } + }); + + return foundKeyword; + } + return ( @@ -140,31 +158,34 @@ export function ChatList(props: { narrow?: boolean }) { ref={provided.innerRef} {...provided.droppableProps} > - {sessions.map((item, i) => ( - { - navigate(Path.Chat); - selectSession(i); - }} - onDelete={async () => { - if ( - (!props.narrow && !isMobileScreen) || - (await showConfirm(Locale.Home.DeleteChat)) - ) { - chatStore.deleteSession(i); - } - }} - narrow={props.narrow} - mask={item.mask} - /> - ))} + {sessions.map( + (item, i) => + haveSearchKeyword(item) && ( + { + navigate(Path.Chat); + selectSession(i); + }} + onDelete={async () => { + if ( + (!props.narrow && !isMobileScreen) || + (await showConfirm(Locale.Home.DeleteChat)) + ) { + chatStore.deleteSession(i); + } + }} + narrow={props.narrow} + mask={item.mask} + /> + ), + )} {provided.placeholder} )} diff --git a/app/components/home.module.scss b/app/components/home.module.scss index b836d2bec..b0884d248 100644 --- a/app/components/home.module.scss +++ b/app/components/home.module.scss @@ -230,6 +230,11 @@ white-space: nowrap; } +.chat-list-search { + padding: 10px 0px; + margin-bottom: 10px; +} + .narrow-sidebar { .sidebar-title, .sidebar-sub-title { diff --git a/app/components/sidebar.tsx b/app/components/sidebar.tsx index 69b2e71f8..0aa5d0a17 100644 --- a/app/components/sidebar.tsx +++ b/app/components/sidebar.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, useMemo } from "react"; +import { useEffect, useRef, useMemo, useState } from "react"; import styles from "./home.module.scss"; @@ -29,7 +29,7 @@ import { import { Link, useNavigate } from "react-router-dom"; import { isIOS, useMobileScreen } from "../utils"; import dynamic from "next/dynamic"; -import { showConfirm, showToast } from "./ui-lib"; +import { SearchInput, showConfirm, showToast } from "./ui-lib"; const ChatList = dynamic(async () => (await import("./chat-list")).ChatList, { loading: () => null, @@ -141,6 +141,8 @@ export function SideBar(props: { className?: string }) { [isMobileScreen], ); + const [chatListSearch, setChatListSearch] = useState(""); + useHotKey(); return ( @@ -188,6 +190,16 @@ export function SideBar(props: { className?: string }) { /> +
+ { + setChatListSearch(e.currentTarget.value); + }} + placeholder={Locale.Home.Search} + > +
+
{ @@ -196,7 +208,7 @@ export function SideBar(props: { className?: string }) { } }} > - +
diff --git a/app/components/ui-lib.tsx b/app/components/ui-lib.tsx index da700c0fb..ce5311043 100644 --- a/app/components/ui-lib.tsx +++ b/app/components/ui-lib.tsx @@ -275,6 +275,20 @@ export function PasswordInput(props: HTMLProps) { ); } +export function SearchInput(props: HTMLProps) { + const [visible, setVisible] = useState(false); + + function changeVisibility() { + setVisible(!visible); + } + + return ( +
+ +
+ ); +} + export function Select( props: React.DetailedHTMLProps< React.SelectHTMLAttributes, diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 43ea67633..57e725458 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -124,6 +124,7 @@ const cn = { DeleteChat: "确认删除选中的对话?", DeleteToast: "已删除会话", Revert: "撤销", + Search: "输入筛选的关键词", }, Settings: { Title: "设置", diff --git a/app/locales/en.ts b/app/locales/en.ts index 94c737550..d6daf2fc5 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -127,6 +127,7 @@ const en: LocaleType = { DeleteChat: "Confirm to delete the selected conversation?", DeleteToast: "Chat Deleted", Revert: "Revert", + Search: "Enter filter keywords", }, Settings: { Title: "Settings", diff --git a/app/styles/globals.scss b/app/styles/globals.scss index 3c59f2d44..17f61596a 100644 --- a/app/styles/globals.scss +++ b/app/styles/globals.scss @@ -344,6 +344,16 @@ pre { } } +.search-input-container { + display: flex; + justify-content: center; + + .search-input { + min-width: 99%; + text-align: left; + } +} + .user-avatar { height: 30px; min-height: 30px;