feat: add search popup

This commit is contained in:
kangfenmao 2024-11-19 14:17:38 +08:00
parent 9b7e2282fe
commit 2d46a4494e
13 changed files with 160 additions and 83 deletions

View File

@ -13,7 +13,6 @@ import { ThemeProvider } from './context/ThemeProvider'
import AgentsPage from './pages/agents/AgentsPage'
import AppsPage from './pages/apps/AppsPage'
import FilesPage from './pages/files/FilesPage'
import HistoryPage from './pages/history/HistoryPage'
import HomePage from './pages/home/HomePage'
import PaintingsPage from './pages/paintings/PaintingsPage'
import SettingsPage from './pages/settings/SettingsPage'
@ -36,7 +35,6 @@ function App(): JSX.Element {
<Route path="/paintings" element={<PaintingsPage />} />
<Route path="/translate" element={<TranslatePage />} />
<Route path="/apps" element={<AppsPage />} />
<Route path="/messages/*" element={<HistoryPage />} />
<Route path="/settings/*" element={<SettingsPage />} />
</Routes>
</HashRouter>

View File

@ -0,0 +1,65 @@
import HistoryPage from '@renderer/pages/history/HistoryPage'
import { Modal } from 'antd'
import { useState } from 'react'
import { TopView } from '../TopView'
interface Props {
resolve: (data: any) => void
}
const PopupContainer: React.FC<Props> = ({ resolve }) => {
const [open, setOpen] = useState(true)
const onOk = () => {
setOpen(false)
}
const onCancel = () => {
setOpen(false)
}
const onClose = () => {
resolve({})
}
SearchPopup.hide = onCancel
return (
<Modal
open={open}
onOk={onOk}
onCancel={onCancel}
afterClose={onClose}
title={null}
width="80vw"
transitionName="ant-move-down"
styles={{
content: { padding: 0, border: '1px solid var(--color-border)' },
body: { height: '80vh' }
}}
footer={null}>
<HistoryPage />
</Modal>
)
}
export default class SearchPopup {
static topviewId = 0
static hide() {
TopView.hide('SearchPopup')
}
static show() {
return new Promise<any>((resolve) => {
TopView.show(
<PopupContainer
resolve={(v) => {
resolve(v)
TopView.hide('SearchPopup')
}}
/>,
'SearchPopup'
)
})
}
}

View File

@ -27,17 +27,27 @@ const PopupContainer: React.FC<Props> = ({ title, resolve }) => {
resolve({})
}
TemplatePopup.hide = onCancel
return (
<Modal title={title} open={open} onOk={onOk} onCancel={onCancel} afterClose={onClose}>
<Modal
title={title}
open={open}
onOk={onOk}
onCancel={onCancel}
afterClose={onClose}
transitionName="ant-move-down">
<Box mb={8}>Name</Box>
</Modal>
)
}
const TopViewKey = 'TemplatePopup'
export default class TemplatePopup {
static topviewId = 0
static hide() {
TopView.hide('TemplatePopup')
TopView.hide(TopViewKey)
}
static show(props: ShowParams) {
return new Promise<any>((resolve) => {
@ -46,10 +56,10 @@ export default class TemplatePopup {
{...props}
resolve={(v) => {
resolve(v)
this.hide()
TopView.hide(TopViewKey)
}}
/>,
'TemplatePopup'
TopViewKey
)
})
}

View File

@ -1,4 +1,4 @@
import { FileSearchOutlined, FolderOutlined, PictureOutlined, TranslationOutlined } from '@ant-design/icons'
import { FolderOutlined, PictureOutlined, TranslationOutlined } from '@ant-design/icons'
import { isMac } from '@renderer/config/constant'
import { isLocalAi, UserAvatar } from '@renderer/config/env'
import { useTheme } from '@renderer/context/ThemeProvider'
@ -93,13 +93,6 @@ const Sidebar: FC = () => {
</Icon>
</StyledLink>
</Tooltip>
<Tooltip title={t('history.title')} mouseEnterDelay={0.5} placement="right">
<StyledLink onClick={() => to('/messages')}>
<Icon className={isRoutes('/messages')}>
<FileSearchOutlined />
</Icon>
</StyledLink>
</Tooltip>
</Menus>
</MainMenus>
<Menus onClick={MinApp.onClose}>

View File

@ -31,6 +31,9 @@ const AntdProvider: FC<PropsWithChildren> = ({ children }) => {
Menu: {
activeBarBorderWidth: 0,
darkItemBg: 'transparent'
},
Modal: {
colorBgMask: isDarkTheme ? 'rgba(0, 0, 0, 0.85)' : 'rgba(255, 255, 255, 0.9)'
}
},
token: {

View File

@ -1,7 +1,6 @@
import { ArrowLeftOutlined, EnterOutlined, SearchOutlined } from '@ant-design/icons'
import { Navbar, NavbarCenter } from '@renderer/components/app/Navbar'
import { Message, Topic } from '@renderer/types'
import { Divider, Input } from 'antd'
import { Input } from 'antd'
import { last } from 'lodash'
import { FC, useState } from 'react'
import { useTranslation } from 'react-i18next'
@ -59,44 +58,38 @@ const TopicsPage: FC = () => {
return (
<Container>
<Navbar>
<NavbarCenter style={{ borderRight: 'none', justifyContent: 'flex-start' }}>{t('history.title')} </NavbarCenter>
</Navbar>
<ContentContainer id="content-container">
<Header>
{stack.length > 1 && (
<HeaderLeft>
<MenuIcon onClick={goBack}>
<ArrowLeftOutlined />
</MenuIcon>
</HeaderLeft>
)}
<SearchInput
placeholder={t('history.search.placeholder')}
type="search"
value={search}
allowClear
onChange={(e) => setSearch(e.target.value.trimStart())}
suffix={search.length >= 2 ? <EnterOutlined /> : <SearchOutlined />}
onPressEnter={onSearch}
/>
</Header>
<Divider style={{ margin: 0 }} />
<TopicsHistory
keywords={search}
onClick={onTopicClick as any}
onSearch={onSearch}
style={{ display: isShow('topics') }}
<Header>
{stack.length > 1 && (
<HeaderLeft>
<MenuIcon onClick={goBack}>
<ArrowLeftOutlined />
</MenuIcon>
</HeaderLeft>
)}
<SearchInput
placeholder={t('history.search.placeholder')}
type="search"
value={search}
allowClear
onChange={(e) => setSearch(e.target.value.trimStart())}
suffix={search.length >= 2 ? <EnterOutlined /> : <SearchOutlined />}
onPressEnter={onSearch}
/>
<TopicMessages topic={topic} style={{ display: isShow('topic') }} />
<SearchResults
keywords={isShow('search') ? search : ''}
onMessageClick={onMessageClick}
onTopicClick={onTopicClick}
style={{ display: isShow('search') }}
/>
<SearchMessage message={message} style={{ display: isShow('message') }} />
</ContentContainer>
</Header>
<TopicsHistory
keywords={search}
onClick={onTopicClick as any}
onSearch={onSearch}
style={{ display: isShow('topics') }}
/>
<TopicMessages topic={topic} style={{ display: isShow('topic') }} />
<SearchResults
keywords={isShow('search') ? search : ''}
onMessageClick={onMessageClick}
onTopicClick={onTopicClick}
style={{ display: isShow('search') }}
/>
<SearchMessage message={message} style={{ display: isShow('message') }} />
</Container>
)
}
@ -108,24 +101,17 @@ const Container = styled.div`
height: 100%;
`
const ContentContainer = styled.div`
display: flex;
flex: 1;
flex-direction: column;
align-items: center;
height: 100%;
overflow-y: scroll;
`
const Header = styled.div`
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
padding: 8px 20px;
padding-top: 10px;
padding: 10px 0;
width: 100%;
position: relative;
background-color: var(--color-background-mute);
border-top-left-radius: 8px;
border-top-right-radius: 8px;
`
const HeaderLeft = styled.div`

View File

@ -2,11 +2,11 @@ import { ArrowRightOutlined } from '@ant-design/icons'
import { HStack } from '@renderer/components/Layout'
import { default as MessageItem } from '@renderer/pages/home/Messages/Message'
import { locateToMessage } from '@renderer/services/MessagesService'
import NavigationService from '@renderer/services/NavigationService'
import { Message } from '@renderer/types'
import { Button } from 'antd'
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import { useNavigate } from 'react-router'
import styled from 'styled-components'
interface Props extends React.HTMLAttributes<HTMLDivElement> {
@ -14,7 +14,7 @@ interface Props extends React.HTMLAttributes<HTMLDivElement> {
}
const SearchMessage: FC<Props> = ({ message, ...props }) => {
const navigate = useNavigate()
const navigate = NavigationService.navigate!
const { t } = useTranslation()
if (!message) {

View File

@ -1,15 +1,16 @@
import { ArrowRightOutlined, MessageOutlined } from '@ant-design/icons'
import { HStack } from '@renderer/components/Layout'
import SearchPopup from '@renderer/components/Popups/SearchPopup'
import useScrollPosition from '@renderer/hooks/useScrollPosition'
import { useSettings } from '@renderer/hooks/useSettings'
import { getAssistantById } from '@renderer/services/AssistantService'
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
import { locateToMessage } from '@renderer/services/MessagesService'
import NavigationService from '@renderer/services/NavigationService'
import { Topic } from '@renderer/types'
import { Button, Divider, Empty } from 'antd'
import { t } from 'i18next'
import { FC } from 'react'
import { useNavigate } from 'react-router'
import styled from 'styled-components'
import { default as MessageItem } from '../../home/Messages/Message'
@ -19,7 +20,7 @@ interface Props extends React.HTMLAttributes<HTMLDivElement> {
}
const TopicMessages: FC<Props> = ({ topic, ...props }) => {
const navigate = useNavigate()
const navigate = NavigationService.navigate!
const { handleScroll, containerRef } = useScrollPosition('TopicMessages')
const { messageStyle } = useSettings()
@ -30,6 +31,7 @@ const TopicMessages: FC<Props> = ({ topic, ...props }) => {
}
const onContinueChat = (topic: Topic) => {
SearchPopup.hide()
const assistant = getAssistantById(topic.assistantId)
navigate('/', { state: { assistant, topic } })
setTimeout(() => EventEmitter.emit(EVENT_NAMES.SHOW_TOPIC_SIDEBAR), 100)

View File

@ -1,9 +1,10 @@
import { useAssistants } from '@renderer/hooks/useAssistant'
import { useShowAssistants } from '@renderer/hooks/useStore'
import { useActiveTopic } from '@renderer/hooks/useTopic'
import NavigationService from '@renderer/services/NavigationService'
import { Assistant } from '@renderer/types'
import { FC, useState } from 'react'
import { useLocation } from 'react-router-dom'
import { FC, useEffect, useState } from 'react'
import { useLocation, useNavigate } from 'react-router-dom'
import styled from 'styled-components'
import Chat from './Chat'
@ -14,6 +15,7 @@ let _activeAssistant: Assistant
const HomePage: FC = () => {
const { assistants } = useAssistants()
const navigate = useNavigate()
const location = useLocation()
const state = location.state
@ -24,6 +26,15 @@ const HomePage: FC = () => {
_activeAssistant = activeAssistant
useEffect(() => {
NavigationService.setNavigate(navigate)
}, [navigate])
useEffect(() => {
state?.assistant && setActiveAssistant(state?.assistant)
state?.topic && setActiveTopic(state?.topic)
}, [state])
return (
<Container>
<Navbar activeAssistant={activeAssistant} activeTopic={activeTopic} setActiveTopic={setActiveTopic} />

View File

@ -1,12 +1,12 @@
import { FormOutlined } from '@ant-design/icons'
import { SearchOutlined } from '@ant-design/icons'
import { Navbar, NavbarLeft, NavbarRight } from '@renderer/components/app/Navbar'
import AssistantSettingsPopup from '@renderer/components/AssistantSettings'
import { HStack } from '@renderer/components/Layout'
import SearchPopup from '@renderer/components/Popups/SearchPopup'
import { isMac, isWindows } from '@renderer/config/constant'
import { useAssistant } from '@renderer/hooks/useAssistant'
import { useSettings } from '@renderer/hooks/useSettings'
import { useShowAssistants, useShowTopics } from '@renderer/hooks/useStore'
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
import { Assistant, Topic } from '@renderer/types'
import { FC } from 'react'
import styled from 'styled-components'
@ -25,8 +25,6 @@ const HeaderNavbar: FC<Props> = ({ activeAssistant }) => {
const { topicPosition } = useSettings()
const { showTopics, toggleShowTopics } = useShowTopics()
const addNewTopic = () => EventEmitter.emit(EVENT_NAMES.ADD_NEW_TOPIC)
return (
<Navbar>
{showAssistants && (
@ -34,8 +32,8 @@ const HeaderNavbar: FC<Props> = ({ activeAssistant }) => {
<NewButton onClick={toggleShowAssistants} style={{ marginLeft: isMac ? 8 : 0 }}>
<i className="iconfont icon-hide-sidebar" />
</NewButton>
<NewButton onClick={addNewTopic}>
<FormOutlined />
<NewButton onClick={() => SearchPopup.show()}>
<SearchOutlined />
</NewButton>
</NavbarLeft>
)}

View File

@ -1,7 +1,6 @@
import { CheckCircleFilled, CloseCircleFilled, LoadingOutlined } from '@ant-design/icons'
import Scrollbar from '@renderer/components/Scrollbar'
import { TopView } from '@renderer/components/TopView'
import { useTheme } from '@renderer/context/ThemeProvider'
import { checkApi } from '@renderer/services/ApiService'
import { Button, List, Modal, Space, Spin, Typography } from 'antd'
import { useState } from 'react'
@ -30,7 +29,6 @@ const PopupContainer: React.FC<Props> = ({ title, provider, apiKeys, resolve })
return Array.from(uniqueKeys).map((key) => ({ key }))
})
const { t } = useTranslation()
const { theme } = useTheme()
const [isChecking, setIsChecking] = useState(false)
const checkAllKeys = async () => {
@ -79,11 +77,6 @@ const PopupContainer: React.FC<Props> = ({ title, provider, apiKeys, resolve })
afterClose={onClose}
centered
maskClosable={false}
maskProps={{
style: {
backgroundColor: theme === 'dark' ? 'rgba(0, 0, 0, 0.9)' : 'rgba(255, 255, 255, 0.9)'
}
}}
footer={
<Space style={{ display: 'flex', justifyContent: 'space-between' }}>
<Space>

View File

@ -1,3 +1,4 @@
import SearchPopup from '@renderer/components/Popups/SearchPopup'
import { DEFAULT_CONTEXTCOUNT } from '@renderer/config/constant'
import { getTopicById } from '@renderer/hooks/useTopic'
import { Assistant, Message, Topic } from '@renderer/types'
@ -43,6 +44,7 @@ export function deleteMessageFiles(message: Message) {
}
export async function locateToMessage(navigate: NavigateFunction, message: Message) {
SearchPopup.hide()
const assistant = getAssistantById(message.assistantId)
const topic = await getTopicById(message.topicId)
navigate('/', { state: { assistant, topic } })

View File

@ -0,0 +1,16 @@
import { NavigateFunction } from 'react-router-dom'
interface INavigationService {
navigate: NavigateFunction | null
setNavigate: (navigateFunc: NavigateFunction) => void
}
const NavigationService: INavigationService = {
navigate: null,
setNavigate: (navigateFunc: NavigateFunction): void => {
NavigationService.navigate = navigateFunc
}
}
export default NavigationService