diff --git a/.vscode/settings.json b/.vscode/settings.json
index 684fc502..4f6177a3 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,8 +1,7 @@
{
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
- "source.fixAll.eslint": "explicit",
- "source.organizeImports": "explicit"
+ "source.fixAll.eslint": "explicit"
},
"search.exclude": {
"**/dist/**": true
diff --git a/src/main/index.ts b/src/main/index.ts
index bd30411d..87886877 100644
--- a/src/main/index.ts
+++ b/src/main/index.ts
@@ -1,8 +1,7 @@
-import { app, BrowserWindow, ipcMain, shell } from 'electron'
import { electronApp, is, optimizer } from '@electron-toolkit/utils'
+import { app, BrowserWindow, ipcMain, shell } from 'electron'
import windowStateKeeper from 'electron-window-state'
import { join } from 'path'
-import { initStore } from './store'
import icon from '../../resources/icon.png?asset'
function createWindow(): void {
@@ -68,8 +67,6 @@ app.whenReady().then(() => {
// IPC test
ipcMain.on('ping', () => console.log('pong'))
- initStore()
-
createWindow()
app.on('activate', function () {
diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx
index 38c58fc3..7d128cd8 100644
--- a/src/renderer/src/App.tsx
+++ b/src/renderer/src/App.tsx
@@ -1,7 +1,8 @@
import '@fontsource/inter'
-import store from '@renderer/store'
+import store, { persistor } from '@renderer/store'
import { Provider } from 'react-redux'
import { BrowserRouter, Route, Routes } from 'react-router-dom'
+import { PersistGate } from 'redux-persist/integration/react'
import Sidebar from './components/app/Sidebar'
import Statusbar from './components/app/Statusbar'
import AppsPage from './pages/apps/AppsPage'
@@ -11,15 +12,17 @@ import SettingsPage from './pages/settings/SettingsPage'
function App(): JSX.Element {
return (
-
-
-
- } />
- } />
- } />
-
-
-
+
+
+
+
+ } />
+ } />
+ } />
+
+
+
+
)
}
diff --git a/src/renderer/src/hooks/useConversactions.ts b/src/renderer/src/hooks/useConversactions.ts
deleted file mode 100644
index 2269a3e0..00000000
--- a/src/renderer/src/hooks/useConversactions.ts
+++ /dev/null
@@ -1,51 +0,0 @@
-import { runAsyncFunction } from '@renderer/utils'
-import localforage from 'localforage'
-import { useEffect, useState } from 'react'
-
-export type Conversation = {
- id: string
- name: string
- avatar: string
- lastMessage: string
- lastMessageAt: string
-}
-
-export default function useConversations() {
- const [conversations, setConversations] = useState([])
- const [activeConversation, setActiveConversation] = useState()
-
- // Use localforage to initialize conversations
- useEffect(() => {
- runAsyncFunction(async () => {
- const conversations = await localforage.getItem('conversations')
- conversations && setConversations(conversations)
- })
- }, [])
-
- // Update localforage
- useEffect(() => {
- localforage.setItem('conversations', conversations)
- }, [conversations])
-
- const addConversation = (conversation) => {
- setConversations([...conversations, conversation])
- }
-
- const removeConversation = (conversationId) => {
- setConversations(conversations.filter((c) => c.id !== conversationId))
- }
-
- const updateConversation = (conversation) => {
- setConversations(conversations.map((c) => (c.id === conversation.id ? conversation : c)))
- }
-
- return {
- conversations,
- activeConversation,
- setConversations,
- addConversation,
- removeConversation,
- updateConversation,
- setActiveConversation
- }
-}
diff --git a/src/renderer/src/hooks/useThreads.ts b/src/renderer/src/hooks/useThreads.ts
new file mode 100644
index 00000000..e0b26bc1
--- /dev/null
+++ b/src/renderer/src/hooks/useThreads.ts
@@ -0,0 +1,17 @@
+import { useAppDispatch, useAppSelector } from '@renderer/store'
+import { addThread, removeThread, setActiveThread, updateThread } from '@renderer/store/threads'
+import { Thread } from '@renderer/types'
+
+export default function useThreads() {
+ const { threads, activeThread } = useAppSelector((state) => state.threads)
+ const dispatch = useAppDispatch()
+
+ return {
+ threads,
+ activeThread,
+ setActiveThread: (thread: Thread) => dispatch(setActiveThread(thread)),
+ addThread: (thread: Thread) => dispatch(addThread(thread)),
+ removeThread: (id: string) => dispatch(removeThread({ id })),
+ updateThread: (thread: Thread) => dispatch(updateThread(thread))
+ }
+}
diff --git a/src/renderer/src/pages/home/HomePage.tsx b/src/renderer/src/pages/home/HomePage.tsx
index ca2be215..63cec426 100644
--- a/src/renderer/src/pages/home/HomePage.tsx
+++ b/src/renderer/src/pages/home/HomePage.tsx
@@ -1,21 +1,21 @@
import { Navbar, NavbarCenter, NavbarLeft, NavbarRight } from '@renderer/components/app/Navbar'
-import useConversations from '@renderer/hooks/useConversactions'
+import useThreads from '@renderer/hooks/useThreads'
import { FC, useEffect } from 'react'
import styled from 'styled-components'
-import Conversations from './components/Conversations'
import Chat from './components/Chat'
+import Threads from './components/Threads'
const HomePage: FC = () => {
- const { conversations, activeConversation, setActiveConversation, addConversation } = useConversations()
+ const { threads, activeThread, setActiveThread, addThread } = useThreads()
useEffect(() => {
- if (!activeConversation) {
- setActiveConversation(conversations[0])
+ if (!activeThread) {
+ setActiveThread(threads[0])
}
- }, [activeConversation, conversations])
+ }, [activeThread, threads])
const onCreateConversation = () => {
- const _conversation = {
+ const _thread = {
// ID auto increment
id: Math.random().toString(),
name: 'New conversation',
@@ -24,8 +24,8 @@ const HomePage: FC = () => {
lastMessage: 'message',
lastMessageAt: 'now'
}
- addConversation(_conversation)
- setActiveConversation(_conversation)
+ addThread(_thread)
+ setActiveThread(_thread)
}
return (
@@ -40,12 +40,8 @@ const HomePage: FC = () => {
-
-
+
+
diff --git a/src/renderer/src/pages/home/components/Chat.tsx b/src/renderer/src/pages/home/components/Chat.tsx
index 0628d633..dd0b30a6 100644
--- a/src/renderer/src/pages/home/components/Chat.tsx
+++ b/src/renderer/src/pages/home/components/Chat.tsx
@@ -1,13 +1,13 @@
-import { Conversation } from '@renderer/hooks/useConversactions'
+import { Thread } from '@renderer/types'
import { FC } from 'react'
import styled from 'styled-components'
interface Props {
- activeConversation?: Conversation
+ activeThread?: Thread
}
-const Chat: FC = ({ activeConversation }) => {
- return {activeConversation?.lastMessage}
+const Chat: FC = ({ activeThread }) => {
+ return {activeThread?.lastMessage}
}
const Container = styled.div`
@@ -15,6 +15,7 @@ const Container = styled.div`
height: 100%;
flex: 1;
border-right: 1px solid #ffffff20;
+ padding: 15px;
`
export default Chat
diff --git a/src/renderer/src/pages/home/components/Conversations.tsx b/src/renderer/src/pages/home/components/Threads.tsx
similarity index 50%
rename from src/renderer/src/pages/home/components/Conversations.tsx
rename to src/renderer/src/pages/home/components/Threads.tsx
index bf0c1613..e4bfc302 100644
--- a/src/renderer/src/pages/home/components/Conversations.tsx
+++ b/src/renderer/src/pages/home/components/Threads.tsx
@@ -1,25 +1,25 @@
-import type { Conversation } from '@renderer/hooks/useConversactions'
+import type { Thread } from '@renderer/types'
import { FC } from 'react'
import styled from 'styled-components'
interface Props {
- conversations: Conversation[]
- activeConversation?: Conversation
- onSelectConversation: (conversation: Conversation) => void
+ threads: Thread[]
+ activeThread?: Thread
+ onSelectThread: (conversation: Thread) => void
}
-const Conversations: FC = ({ conversations, activeConversation, onSelectConversation }) => {
+const Conversations: FC = ({ threads, activeThread, onSelectThread }) => {
return (
- {conversations.map((conversation) => (
- onSelectConversation(conversation)}
- className={conversation.id === activeConversation?.id ? 'active' : ''}>
- {conversation.lastMessageAt}
- {conversation.name}
- {conversation.lastMessage}
-
+ {threads.map((thread) => (
+ onSelectThread(thread)}
+ className={thread.id === activeThread?.id ? 'active' : ''}>
+ {thread.lastMessageAt}
+ {thread.name}
+ {thread.lastMessage}
+
))}
)
@@ -38,7 +38,7 @@ const Container = styled.div`
}
`
-const Conversation = styled.div`
+const ThreadItem = styled.div`
display: flex;
flex-direction: column;
padding: 10px;
@@ -54,18 +54,18 @@ const Conversation = styled.div`
margin-bottom: 10px;
`
-const ConversationTime = styled.div`
+const ThreadTime = styled.div`
font-size: 12px;
color: var(--color-text-2);
`
-const ConversationName = styled.div`
+const ThreadName = styled.div`
font-size: 14px;
color: var(--color-text-1);
font-weight: bold;
`
-const ConversationLastMessage = styled.div`
+const ThreadLastMessage = styled.div`
font-size: 12px;
color: var(--color-text-2);
`
diff --git a/src/renderer/src/store/index.ts b/src/renderer/src/store/index.ts
index 25cf17af..b447e7c5 100644
--- a/src/renderer/src/store/index.ts
+++ b/src/renderer/src/store/index.ts
@@ -1,19 +1,35 @@
-import { configureStore } from '@reduxjs/toolkit'
-
-import { combineReducers } from '@reduxjs/toolkit'
-import { useDispatch } from 'react-redux'
+import { combineReducers, configureStore } from '@reduxjs/toolkit'
+import { useDispatch, useSelector, useStore } from 'react-redux'
+import { FLUSH, PAUSE, PERSIST, persistReducer, persistStore, PURGE, REGISTER, REHYDRATE } from 'redux-persist'
+import storage from 'redux-persist/lib/storage'
import threads from './threads'
-const rootReducer = combineReducers({
- threads
-})
-
const store = configureStore({
- reducer: rootReducer
+ reducer: persistReducer(
+ {
+ key: 'cherry-ai',
+ storage,
+ version: 1
+ },
+ combineReducers({
+ threads
+ })
+ ),
+ middleware: (getDefaultMiddleware) =>
+ getDefaultMiddleware({
+ serializableCheck: {
+ ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER]
+ }
+ })
})
-export type RootState = ReturnType
+export type RootState = ReturnType
export type AppDispatch = typeof store.dispatch
+
+export const persistor = persistStore(store)
export const useAppDispatch = useDispatch.withTypes()
+export const useAppSelector = useSelector.withTypes()
+export const useAppStore = useStore.withTypes()
+// export const dispatch: AppDispatch = useDispatch()
export default store
diff --git a/src/renderer/src/store/threads.ts b/src/renderer/src/store/threads.ts
index 2a3003a3..f9364224 100644
--- a/src/renderer/src/store/threads.ts
+++ b/src/renderer/src/store/threads.ts
@@ -1,12 +1,14 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
import { Thread } from '@renderer/types'
-interface State {
+export interface ThreadsState {
threads: Thread[]
+ activeThread?: Thread
}
-const initialState: State = {
- threads: []
+const initialState: ThreadsState = {
+ threads: [],
+ activeThread: undefined
}
const threadsSlice = createSlice({
@@ -21,10 +23,13 @@ const threadsSlice = createSlice({
},
updateThread: (state, action: PayloadAction) => {
state.threads = state.threads.map((c) => (c.id === action.payload.id ? action.payload : c))
+ },
+ setActiveThread: (state, action: PayloadAction) => {
+ state.activeThread = action.payload
}
}
})
-export const { addThread, removeThread, updateThread } = threadsSlice.actions
+export const { addThread, removeThread, updateThread, setActiveThread } = threadsSlice.actions
-export default threadsSlice
+export default threadsSlice.reducer