Skip to content

Commit

Permalink
feat: add topics history
Browse files Browse the repository at this point in the history
  • Loading branch information
kangfenmao committed Oct 5, 2024
1 parent 2da3a3f commit 4cc140e
Show file tree
Hide file tree
Showing 24 changed files with 735 additions and 121 deletions.
2 changes: 2 additions & 0 deletions src/renderer/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ 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 SettingsPage from './pages/settings/SettingsPage'
import TranslatePage from './pages/translate/TranslatePage'
Expand All @@ -31,6 +32,7 @@ function App(): JSX.Element {
<Route path="/agents" element={<AgentsPage />} />
<Route path="/translate" element={<TranslatePage />} />
<Route path="/apps" element={<AppsPage />} />
<Route path="/messages/*" element={<HistoryPage />} />
<Route path="/settings/*" element={<SettingsPage />} />
</Routes>
</HashRouter>
Expand Down
8 changes: 7 additions & 1 deletion src/renderer/src/components/app/Sidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FolderOutlined, TranslationOutlined } from '@ant-design/icons'
import { FileSearchOutlined, FolderOutlined, TranslationOutlined } from '@ant-design/icons'
import { isMac } from '@renderer/config/constant'
import { isLocalAi, UserAvatar } from '@renderer/config/env'
import useAvatar from '@renderer/hooks/useAvatar'
Expand All @@ -23,6 +23,7 @@ const Sidebar: FC = () => {
const { windowStyle } = useSettings()

const isRoute = (path: string): string => (pathname === path ? 'active' : '')
const isRoutes = (path: string): string => (pathname.startsWith(path) ? 'active' : '')

const onEditUser = () => UserPopup.show()

Expand Down Expand Up @@ -72,6 +73,11 @@ const Sidebar: FC = () => {
<FolderOutlined />
</Icon>
</StyledLink>
<StyledLink onClick={() => to('/messages')}>
<Icon className={isRoutes('/messages')}>
<FileSearchOutlined />
</Icon>
</StyledLink>
</Menus>
</MainMenus>
<Menus onClick={MinApp.onClose}>
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/src/hooks/useAssistant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export function useDefaultAssistant() {
return {
defaultAssistant: {
...defaultAssistant,
topics: [getDefaultTopic()]
topics: [getDefaultTopic(defaultAssistant.id)]
},
updateDefaultAssistant: (assistant: Assistant) => dispatch(updateDefaultAssistant({ assistant }))
}
Expand Down
13 changes: 11 additions & 2 deletions src/renderer/src/hooks/useTopic.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import db from '@renderer/databases'
import { deleteMessageFiles } from '@renderer/services/messages'
import store from '@renderer/store'
import { Assistant, Topic } from '@renderer/types'
import { find } from 'lodash'
import { useEffect, useState } from 'react'
Expand All @@ -8,9 +9,9 @@ import { useAssistant } from './useAssistant'

let _activeTopic: Topic

export function useActiveTopic(_assistant: Assistant) {
export function useActiveTopic(_assistant: Assistant, topic?: Topic) {
const { assistant } = useAssistant(_assistant.id)
const [activeTopic, setActiveTopic] = useState(_activeTopic || assistant?.topics[0])
const [activeTopic, setActiveTopic] = useState(topic || _activeTopic || assistant?.topics[0])

_activeTopic = activeTopic

Expand All @@ -28,6 +29,14 @@ export function getTopic(assistant: Assistant, topicId: string) {
return assistant?.topics.find((topic) => topic.id === topicId)
}

export async function getTopicById(topicId: string) {
const assistants = store.getState().assistants.assistants
const topics = assistants.map((assistant) => assistant.topics).flat()
const topic = topics.find((topic) => topic.id === topicId)
const messages = await TopicManager.getTopicMessages(topicId)
return { ...topic, messages } as Topic
}

export class TopicManager {
static async getTopic(id: string) {
return await db.topics.get(id)
Expand Down
11 changes: 8 additions & 3 deletions src/renderer/src/i18n/en-us.json
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,14 @@
"tag.system": "System",
"tag.user": "Mine"
},
"minapp": {
"title": "MinApp"
},
"history": {
"title": "Topics Search",
"search.placeholder": "Search topics or messages...",
"continue_chat": "Continue Chatting"
},
"provider": {
"nvidia": "Nvidia",
"zhinao": "360AI",
Expand Down Expand Up @@ -290,9 +298,6 @@
"keep_alive_time.placeholder": "Minutes",
"keep_alive_time.description": "The time in minutes to keep the connection alive, default is 5 minutes."
},
"minapp": {
"title": "MinApp"
},
"error": {
"chat.response": "Something went wrong. Please check if you have set your API key in the Settings > Providers",
"backup.file_format": "Backup file format error"
Expand Down
11 changes: 8 additions & 3 deletions src/renderer/src/i18n/zh-cn.json
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,14 @@
"tag.system": "系统",
"tag.user": "我的"
},
"minapp": {
"title": "小程序"
},
"history": {
"title": "话题搜索",
"search.placeholder": "搜索话题或消息...",
"continue_chat": "继续聊天"
},
"provider": {
"nvidia": "英伟达",
"zhinao": "360智脑",
Expand Down Expand Up @@ -290,9 +298,6 @@
"keep_alive_time.placeholder": "分钟",
"keep_alive_time.description": "对话后模型在内存中保持的时间(默认:5分钟)"
},
"minapp": {
"title": "小程序"
},
"error": {
"chat.response": "出错了,如果没有配置 API 密钥,请前往设置 > 模型提供商中配置密钥",
"backup.file_format": "备份文件格式错误"
Expand Down
11 changes: 8 additions & 3 deletions src/renderer/src/i18n/zh-tw.json
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,14 @@
"tag.system": "系統",
"tag.user": "我的"
},
"minapp": {
"title": "小程序"
},
"history": {
"title": "搜尋話題",
"search.placeholder": "搜尋話題或訊息...",
"continue_chat": "繼續聊天"
},
"provider": {
"nvidia": "輝達",
"zhinao": "360智腦",
Expand Down Expand Up @@ -290,9 +298,6 @@
"keep_alive_time.placeholder": "分鐘",
"keep_alive_time.description": "對話後模型在記憶體中保持的時間(預設為 5 分鐘)。"
},
"minapp": {
"title": "小程序"
},
"error": {
"chat.response": "出現錯誤。如果尚未配置 API 密鑰,請前往設定 > 模型提供者中配置密鑰",
"backup.file_format": "備份文件格式錯誤"
Expand Down
157 changes: 157 additions & 0 deletions src/renderer/src/pages/history/HistoryPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
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 { last } from 'lodash'
import { FC, useState } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'

import SearchMessage from './components/SearchMessage'
import SearchResults from './components/SearchResults'
import TopicMessages from './components/TopicMessages'
import TopicsHistory from './components/TopicsHistory'

type Route = 'topics' | 'topic' | 'search' | 'message'

let _search = ''
let _stack: Route[] = ['topics']
let _topic: Topic | undefined
let _message: Message | undefined

const TopicsPage: FC = () => {
const { t } = useTranslation()
const [search, setSearch] = useState(_search)
const [stack, setStack] = useState<Route[]>(_stack)
const [topic, setTopic] = useState<Topic | undefined>(_topic)
const [message, setMessage] = useState<Message | undefined>(_message)

_search = search
_stack = stack
_topic = topic
_message = message

const goBack = () => {
const _stack = [...stack]
const route = _stack.pop()
setStack(_stack)
route === 'search' && setSearch('')
route === 'topic' && setTopic(undefined)
route === 'message' && setMessage(undefined)
}

const onSearch = () => {
setStack(['topics', 'search'])
setTopic(undefined)
}

const onTopicClick = (topic: Topic) => {
setStack((prev) => [...prev, 'topic'])
setTopic(topic)
}

const onMessageClick = (message: Message) => {
setStack(['topics', 'search', 'message'])
setMessage(message)
}

const isShow = (route: Route) => (last(stack) === route ? 'flex' : 'none')

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} style={{ display: isShow('topics') }} />
<TopicMessages topic={topic} style={{ display: isShow('topic') }} />
<SearchResults
keywords={search}
onMessageClick={onMessageClick}
onTopicClick={onTopicClick}
style={{ display: isShow('search') }}
/>
<SearchMessage message={message} style={{ display: isShow('message') }} />
</ContentContainer>
</Container>
)
}

const Container = styled.div`
display: flex;
flex: 1;
flex-direction: column;
height: 100%;
`

const ContentContainer = styled.div`
display: flex;
flex: 1;
flex-direction: column;
align-items: center;
height: 100%;
overflow: hidden;
`

const Header = styled.div`
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
padding: 8px 20px;
width: 100%;
position: relative;
`

const HeaderLeft = styled.div`
display: flex;
flex-direction: row;
align-items: center;
position: absolute;
top: 8px;
left: 15px;
`

const MenuIcon = styled.div`
cursor: pointer;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
width: 36px;
height: 36px;
border-radius: 50%;
&:hover {
background-color: var(--color-background-mute);
.anticon {
color: var(--color-text-1);
}
}
`

const SearchInput = styled(Input)`
border-radius: 30px;
width: 800px;
height: 36px;
`

export default TopicsPage
38 changes: 38 additions & 0 deletions src/renderer/src/pages/history/components/SearchMessage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { default as MessageItem } from '@renderer/pages/home/Messages/Message'
import { Message } from '@renderer/types'
import { FC } from 'react'
import styled from 'styled-components'

interface Props extends React.HTMLAttributes<HTMLDivElement> {
message?: Message
}

const SearchMessage: FC<Props> = ({ message, ...props }) => {
if (!message) {
return null
}

return (
<MessagesContainer {...props}>
<ContainerWrapper style={{ paddingTop: 30, paddingBottom: 30 }}>
<MessageItem message={message} showMenu={false} />
</ContainerWrapper>
</MessagesContainer>
)
}

const MessagesContainer = styled.div`
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
overflow-y: scroll;
`

const ContainerWrapper = styled.div`
width: 800px;
display: flex;
flex-direction: column;
`

export default SearchMessage
Loading

0 comments on commit 4cc140e

Please sign in to comment.