feat: search chats / folders

fixes #102
This commit is contained in:
Jing Hua 2023-04-01 22:45:57 +08:00
parent 467160fbb1
commit 55aa22a28b
6 changed files with 102 additions and 2 deletions

View file

@ -48,6 +48,7 @@
"i18next-browser-languagedetector": "^7.0.1",
"i18next-http-backend": "^2.1.1",
"jspdf": "^2.5.1",
"lodash": "^4.17.21",
"match-sorter": "^6.3.1",
"papaparse": "^5.4.1",
"react": "^18.2.0",
@ -64,6 +65,7 @@
},
"devDependencies": {
"@tailwindcss/typography": "^0.5.9",
"@types/lodash": "^4.14.192",
"@types/papaparse": "^5.3.7",
"@types/react": "^18.0.27",
"@types/react-dom": "^18.0.10",

View file

@ -5,6 +5,7 @@ import { shallow } from 'zustand/shallow';
import NewFolder from './NewFolder';
import ChatFolder from './ChatFolder';
import ChatHistory from './ChatHistory';
import ChatSearch from './ChatSearch';
import {
ChatHistoryInterface,
@ -23,10 +24,13 @@ const ChatHistoryList = () => {
const [isHover, setIsHover] = useState<boolean>(false);
const [folders, setFolders] = useState<ChatHistoryFolderInterface>({});
const [noFolders, setNoFolders] = useState<ChatHistoryInterface[]>([]);
const [filter, setFilter] = useState<string>('');
const chatsRef = useRef<ChatInterface[]>(useStore.getState().chats || []);
const foldersNameRef = useRef<string[]>(useStore.getState().foldersName);
const filterRef = useRef<string>(filter);
const updateFolders = () => {
const updateFolders = useRef(() => {
const _folders: ChatHistoryFolderInterface = {};
const _noFolders: ChatHistoryInterface[] = [];
const chats = useStore.getState().chats;
@ -36,6 +40,14 @@ const ChatHistoryList = () => {
if (chats) {
chats.forEach((chat, index) => {
const filterLowerCase = filterRef.current.toLowerCase();
if (
!chat.title.toLocaleLowerCase().includes(filterLowerCase) &&
!chat.folder?.toLowerCase().includes(filterLowerCase) &&
index !== currentChatIndex
)
return;
if (!chat.folder) {
_noFolders.push({ title: chat.title, index: index });
} else {
@ -47,7 +59,7 @@ const ChatHistoryList = () => {
setFolders(_folders);
setNoFolders(_noFolders);
};
}).current;
useEffect(() => {
updateFolders();
@ -92,6 +104,11 @@ const ChatHistoryList = () => {
}
}, [currentChatIndex, chatTitles]);
useEffect(() => {
filterRef.current = filter;
updateFolders();
}, [filter]);
const handleDrop = (e: React.DragEvent<HTMLDivElement>) => {
if (e.dataTransfer) {
e.stopPropagation();
@ -130,6 +147,7 @@ const ChatHistoryList = () => {
onDragEnd={handleDragEnd}
>
<NewFolder />
<ChatSearch filter={filter} setFilter={setFilter} />
<div className='flex flex-col gap-2 text-gray-100 text-sm'>
{Object.keys(folders).map((folderName, folderIndex) => (
<ChatFolder

View file

@ -0,0 +1,41 @@
import React, { useEffect, useRef, useState } from 'react';
import { debounce } from 'lodash';
import useStore from '@store/store';
import SearchBar from '@components/SearchBar';
const ChatSearch = ({
filter,
setFilter,
}: {
filter: string;
setFilter: React.Dispatch<React.SetStateAction<string>>;
}) => {
const [_filter, _setFilter] = useState<string>(filter);
const generating = useStore((state) => state.generating);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
_setFilter(e.target.value);
};
const debouncedUpdateFilter = useRef(
debounce((f) => {
setFilter(f);
}, 500)
).current;
useEffect(() => {
debouncedUpdateFilter(_filter);
}, [_filter]);
return (
<SearchBar
value={_filter}
handleChange={handleChange}
className='h-8'
disabled={generating}
/>
);
};
export default ChatSearch;

View file

@ -0,0 +1,33 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
const SearchBar = ({
value,
handleChange,
className,
disabled,
}: {
value: string;
handleChange: React.ChangeEventHandler<HTMLInputElement>;
className?: React.HTMLAttributes<HTMLDivElement>['className'];
disabled?: boolean;
}) => {
const { t } = useTranslation();
return (
<div className={className}>
<input
disabled={disabled}
type='text'
className='text-gray-800 dark:text-white p-3 text-sm bg-gray-700 disabled:opacity-40 disabled:cursor-not-allowed transition-opacity m-0 w-full h-full focus:outline-none rounded border-none'
placeholder={t('search') as string}
value={value}
onChange={(e) => {
handleChange(e);
}}
/>
</div>
);
};
export default SearchBar;

View file

@ -0,0 +1 @@
export { default } from './SearchBar';

View file

@ -582,6 +582,11 @@
dependencies:
"@types/node" "*"
"@types/lodash@^4.14.192":
version "4.14.192"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.192.tgz#5790406361a2852d332d41635d927f1600811285"
integrity sha512-km+Vyn3BYm5ytMO13k9KTp27O75rbQ0NFw+U//g+PX7VZyjCioXaRFisqSIJRECljcTv73G3i6BpglNGHgUQ5A==
"@types/mdast@^3.0.0":
version "3.0.10"
resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.10.tgz#4724244a82a4598884cbbe9bcfd73dff927ee8af"