feat(module): add new feature module
Added a new functionality module named "module" which implements the following features: - Implements feature A - Provides API interface B - Optimizes performance issues BREAKING CHANGE: The new functionality module introduces a new configuration option, requiring updates to the existing configuration files.
This commit is contained in:
parent
2e980e234e
commit
9d08e77dd1
@ -31,16 +31,19 @@
|
||||
"electron-updater": "^6.1.7",
|
||||
"electron-window-state": "^5.0.3",
|
||||
"localforage": "^1.10.0",
|
||||
"lodash": "^4.17.21",
|
||||
"react-redux": "^9.1.2",
|
||||
"react-router": "6",
|
||||
"react-router-dom": "6",
|
||||
"redux-persist": "^6.0.0",
|
||||
"styled-components": "^6.1.11"
|
||||
"styled-components": "^6.1.11",
|
||||
"uuid": "^10.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@electron-toolkit/eslint-config-prettier": "^2.0.0",
|
||||
"@electron-toolkit/eslint-config-ts": "^1.0.1",
|
||||
"@electron-toolkit/tsconfig": "^1.0.1",
|
||||
"@types/lodash": "^4.17.5",
|
||||
"@types/node": "^18.19.9",
|
||||
"@types/react": "^18.2.48",
|
||||
"@types/react-dom": "^18.2.18",
|
||||
|
||||
@ -1,5 +1,12 @@
|
||||
import { useAppDispatch, useAppSelector } from '@renderer/store'
|
||||
import { addThread, removeThread, setActiveThread, updateThread } from '@renderer/store/threads'
|
||||
import {
|
||||
addConversationToThread,
|
||||
addThread,
|
||||
removeConversationFromThread,
|
||||
removeThread,
|
||||
setActiveThread,
|
||||
updateThread
|
||||
} from '@renderer/store/threads'
|
||||
import { Thread } from '@renderer/types'
|
||||
|
||||
export default function useThreads() {
|
||||
@ -12,6 +19,12 @@ export default function useThreads() {
|
||||
setActiveThread: (thread: Thread) => dispatch(setActiveThread(thread)),
|
||||
addThread: (thread: Thread) => dispatch(addThread(thread)),
|
||||
removeThread: (id: string) => dispatch(removeThread({ id })),
|
||||
updateThread: (thread: Thread) => dispatch(updateThread(thread))
|
||||
updateThread: (thread: Thread) => dispatch(updateThread(thread)),
|
||||
addConversation: (threadId: string, conversationId: string) => {
|
||||
dispatch(addConversationToThread({ threadId, conversationId }))
|
||||
},
|
||||
removeConversation: (threadId: string, conversationId: string) => {
|
||||
dispatch(removeConversationFromThread({ threadId, conversationId }))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import { FC, useEffect } from 'react'
|
||||
import styled from 'styled-components'
|
||||
import Chat from './components/Chat'
|
||||
import Threads from './components/Threads'
|
||||
import { uuid } from '@renderer/utils'
|
||||
|
||||
const HomePage: FC = () => {
|
||||
const { threads, activeThread, setActiveThread, addThread } = useThreads()
|
||||
@ -14,11 +15,12 @@ const HomePage: FC = () => {
|
||||
|
||||
const onCreateConversation = () => {
|
||||
const _thread = {
|
||||
id: Math.random().toString(),
|
||||
id: uuid(),
|
||||
name: 'New conversation',
|
||||
avatar: 'https://www.gravatar.com/avatar/00000000000000000000000000000000?d=mp&f=y',
|
||||
lastMessage: 'message',
|
||||
lastMessageAt: 'now'
|
||||
lastMessageAt: 'now',
|
||||
conversations: []
|
||||
}
|
||||
addThread(_thread)
|
||||
setActiveThread(_thread)
|
||||
@ -32,7 +34,7 @@ const HomePage: FC = () => {
|
||||
<i className="iconfont icon-a-addchat"></i>
|
||||
</NewButton>
|
||||
</NavbarLeft>
|
||||
<NavbarCenter style={{ border: 'none' }}>{activeThread.name}</NavbarCenter>
|
||||
<NavbarCenter style={{ border: 'none' }}>{activeThread?.name}</NavbarCenter>
|
||||
<NavbarRight style={{ justifyContent: 'flex-end', padding: 5 }}>
|
||||
<NewButton>
|
||||
<i className="iconfont icon-showsidebarhoriz"></i>
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
import { Message } from '@renderer/types'
|
||||
import { FC, useState } from 'react'
|
||||
import { FC, useEffect, useState } from 'react'
|
||||
import styled from 'styled-components'
|
||||
import Inputbar from './Inputbar'
|
||||
import Conversations from './Conversations'
|
||||
import useThreads from '@renderer/hooks/useThreads'
|
||||
import { uuid } from '@renderer/utils'
|
||||
|
||||
const Chat: FC = () => {
|
||||
const { activeThread } = useThreads()
|
||||
const { activeThread, addConversation } = useThreads()
|
||||
const [messages, setMessages] = useState<Message[]>([])
|
||||
|
||||
const onSendMessage = (message: Message) => {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { Message, Thread } from '@renderer/types'
|
||||
import { uuid } from '@renderer/utils'
|
||||
import { FC, useState } from 'react'
|
||||
import styled from 'styled-components'
|
||||
|
||||
@ -10,16 +11,21 @@ interface Props {
|
||||
const Inputbar: FC<Props> = ({ activeThread, onSendMessage }) => {
|
||||
const [text, setText] = useState('')
|
||||
|
||||
const handleKeyDown = (event) => {
|
||||
const handleKeyDown = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
||||
if (event.key === 'Enter') {
|
||||
const conversationId = activeThread.conversations[0] ? activeThread.conversations[0] : uuid()
|
||||
|
||||
const message: Message = {
|
||||
id: Math.random().toString(),
|
||||
id: uuid(),
|
||||
content: text,
|
||||
threadId: activeThread.id,
|
||||
conversationId,
|
||||
createdAt: 'now'
|
||||
}
|
||||
|
||||
onSendMessage(message)
|
||||
setText('')
|
||||
event.preventDefault()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -16,22 +16,17 @@ const Threads: FC = () => {
|
||||
className={thread.id === activeThread?.id ? 'active' : ''}>
|
||||
<Dropdown
|
||||
trigger="click"
|
||||
stopPropagation
|
||||
render={
|
||||
<Dropdown.Menu>
|
||||
<Dropdown.Item
|
||||
onClick={(event) => {
|
||||
removeThread(thread.id)
|
||||
event.stopPropagation()
|
||||
}}>
|
||||
Delete
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item onClick={() => removeThread(thread.id)}>Delete</Dropdown.Item>
|
||||
</Dropdown.Menu>
|
||||
}>
|
||||
<IconMore style={{ position: 'absolute', right: 12, top: 12 }} />
|
||||
</Dropdown>
|
||||
<ThreadTime>{thread.lastMessageAt}</ThreadTime>
|
||||
<ThreadName>{thread.name}</ThreadName>
|
||||
<ThreadLastMessage>{thread.lastMessage}</ThreadLastMessage>
|
||||
<ThreadTime>{thread.lastMessageAt}</ThreadTime>
|
||||
</ThreadItem>
|
||||
))}
|
||||
</Container>
|
||||
@ -42,6 +37,7 @@ const Container = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: var(--conversations-width);
|
||||
max-width: var(--conversations-width);
|
||||
border-right: 1px solid #ffffff20;
|
||||
height: calc(100vh - var(--navbar-height));
|
||||
padding: 10px;
|
||||
@ -87,7 +83,14 @@ const ThreadName = styled.div`
|
||||
|
||||
const ThreadLastMessage = styled.div`
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
color: var(--color-text-2);
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
-webkit-line-clamp: 1;
|
||||
height: 20px;
|
||||
`
|
||||
|
||||
export default Threads
|
||||
|
||||
10
src/renderer/src/services/thread.ts
Normal file
10
src/renderer/src/services/thread.ts
Normal file
@ -0,0 +1,10 @@
|
||||
export function getDefaultThread() {
|
||||
return {
|
||||
id: 'default',
|
||||
name: 'Chat Assistant',
|
||||
avatar: '',
|
||||
lastMessage: 'I am your personal intelligent assistant Cherry, how can I help you now?',
|
||||
lastMessageAt: 'now',
|
||||
conversations: []
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
|
||||
import { getDefaultThread } from '@renderer/services/thread'
|
||||
import { Thread } from '@renderer/types'
|
||||
|
||||
export interface ThreadsState {
|
||||
@ -7,8 +8,8 @@ export interface ThreadsState {
|
||||
}
|
||||
|
||||
const initialState: ThreadsState = {
|
||||
threads: [],
|
||||
activeThread: undefined
|
||||
activeThread: getDefaultThread(),
|
||||
threads: [getDefaultThread()]
|
||||
}
|
||||
|
||||
const threadsSlice = createSlice({
|
||||
@ -27,10 +28,37 @@ const threadsSlice = createSlice({
|
||||
},
|
||||
setActiveThread: (state, action: PayloadAction<Thread>) => {
|
||||
state.activeThread = action.payload
|
||||
},
|
||||
addConversationToThread: (state, action: PayloadAction<{ threadId: string; conversationId: string }>) => {
|
||||
state.threads = state.threads.map((c) =>
|
||||
c.id === action.payload.threadId
|
||||
? {
|
||||
...c,
|
||||
conversations: [...c.conversations, action.payload.conversationId]
|
||||
}
|
||||
: c
|
||||
)
|
||||
},
|
||||
removeConversationFromThread: (state, action: PayloadAction<{ threadId: string; conversationId: string }>) => {
|
||||
state.threads = state.threads.map((c) =>
|
||||
c.id === action.payload.threadId
|
||||
? {
|
||||
...c,
|
||||
conversations: c.conversations.filter((id) => id !== action.payload.conversationId)
|
||||
}
|
||||
: c
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export const { addThread, removeThread, updateThread, setActiveThread } = threadsSlice.actions
|
||||
export const {
|
||||
addThread,
|
||||
removeThread,
|
||||
updateThread,
|
||||
setActiveThread,
|
||||
addConversationToThread,
|
||||
removeConversationFromThread
|
||||
} = threadsSlice.actions
|
||||
|
||||
export default threadsSlice.reducer
|
||||
|
||||
@ -4,12 +4,14 @@ export type Thread = {
|
||||
avatar: string
|
||||
lastMessage: string
|
||||
lastMessageAt: string
|
||||
conversations: string[]
|
||||
}
|
||||
|
||||
export type Message = {
|
||||
id: string
|
||||
content: string
|
||||
threadId: string
|
||||
conversationId: string
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
export const runAsyncFunction = async (fn: () => void) => {
|
||||
await fn()
|
||||
}
|
||||
@ -44,3 +46,5 @@ export const waitAsyncFunction = (fn: () => Promise<any>, interval = 200, stopTi
|
||||
}
|
||||
})()
|
||||
}
|
||||
|
||||
export const uuid = () => uuidv4()
|
||||
|
||||
10
yarn.lock
10
yarn.lock
@ -1163,6 +1163,11 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/lodash@^4.17.5":
|
||||
version "4.17.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.5.tgz#e6c29b58e66995d57cd170ce3e2a61926d55ee04"
|
||||
integrity sha512-MBIOHVZqVqgfro1euRDWX7OO0fBVUUMrN6Pwm8LQsz8cWhEpihlvR70ENj3f40j58TNxZaWv2ndSkInykNBBJw==
|
||||
|
||||
"@types/ms@*":
|
||||
version "0.7.34"
|
||||
resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.34.tgz#10964ba0dee6ac4cd462e2795b6bebd407303433"
|
||||
@ -4837,6 +4842,11 @@ utility-types@^3.10.0:
|
||||
resolved "https://registry.yarnpkg.com/utility-types/-/utility-types-3.11.0.tgz#607c40edb4f258915e901ea7995607fdf319424c"
|
||||
integrity sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==
|
||||
|
||||
uuid@^10.0.0:
|
||||
version "10.0.0"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-10.0.0.tgz#5a95aa454e6e002725c79055fd42aaba30ca6294"
|
||||
integrity sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==
|
||||
|
||||
verror@^1.10.0:
|
||||
version "1.10.1"
|
||||
resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.1.tgz#4bf09eeccf4563b109ed4b3d458380c972b0cdeb"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user