diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 880582c7..6cb2ddce 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -8,6 +8,13 @@ module.exports = { ], rules: { '@typescript-eslint/explicit-function-return-type': 'off', - '@typescript-eslint/no-explicit-any': 'off' + '@typescript-eslint/no-explicit-any': 'off', + 'sort-imports': [ + 'error', + { + ignoreCase: true, + ignoreDeclarationSort: true + } + ] } } diff --git a/.vscode/settings.json b/.vscode/settings.json index 4f6177a3..684fc502 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,8 @@ { "editor.formatOnSave": true, "editor.codeActionsOnSave": { - "source.fixAll.eslint": "explicit" + "source.fixAll.eslint": "explicit", + "source.organizeImports": "explicit" }, "search.exclude": { "**/dist/**": true diff --git a/electron.vite.config.ts b/electron.vite.config.ts index 5b54e205..c454dadf 100644 --- a/electron.vite.config.ts +++ b/electron.vite.config.ts @@ -1,6 +1,6 @@ -import { resolve } from 'path' import { defineConfig, externalizeDepsPlugin } from 'electron-vite' import react from '@vitejs/plugin-react' +import { resolve } from 'path' export default defineConfig({ main: { diff --git a/package.json b/package.json index fcf9c3d2..abb74f5a 100644 --- a/package.json +++ b/package.json @@ -26,11 +26,14 @@ "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", "@fontsource/inter": "^5.0.18", + "@reduxjs/toolkit": "^2.2.5", "electron-updater": "^6.1.7", "electron-window-state": "^5.0.3", "localforage": "^1.10.0", + "react-redux": "^9.1.2", "react-router": "6", "react-router-dom": "6", + "redux-persist": "^6.0.0", "styled-components": "^6.1.11" }, "devDependencies": { diff --git a/src/main/index.ts b/src/main/index.ts index edd72467..bd30411d 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -1,9 +1,9 @@ -import { app, shell, BrowserWindow, ipcMain } from 'electron' -import { join } from 'path' -import { electronApp, optimizer, is } from '@electron-toolkit/utils' -import icon from '../../resources/icon.png?asset' +import { app, BrowserWindow, ipcMain, shell } from 'electron' +import { electronApp, is, optimizer } from '@electron-toolkit/utils' import windowStateKeeper from 'electron-window-state' +import { join } from 'path' import { initStore } from './store' +import icon from '../../resources/icon.png?asset' function createWindow(): void { // Load the previous state with fallback to defaults diff --git a/src/main/store.ts b/src/main/store.ts deleted file mode 100644 index 35a39a2d..00000000 --- a/src/main/store.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { app, ipcMain } from 'electron' -import path from 'path' - -const defaultPath = path.join(app.getPath('home'), '.cherry-ai') - -export function initStore() { - ipcMain.on('storage.set', (_, args) => {}) - ipcMain.on('storage.get', (_, args) => {}) - ipcMain.on('storage.delete', (_, args) => {}) - ipcMain.on('storage.clear', (_, args) => {}) -} diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx index 3d97e20b..38c58fc3 100644 --- a/src/renderer/src/App.tsx +++ b/src/renderer/src/App.tsx @@ -1,22 +1,26 @@ +import '@fontsource/inter' +import store from '@renderer/store' +import { Provider } from 'react-redux' +import { BrowserRouter, Route, Routes } from 'react-router-dom' import Sidebar from './components/app/Sidebar' import Statusbar from './components/app/Statusbar' -import HomePage from './pages/home/HomePage' -import { BrowserRouter, Routes, Route } from 'react-router-dom' import AppsPage from './pages/apps/AppsPage' +import HomePage from './pages/home/HomePage' import SettingsPage from './pages/settings/SettingsPage' -import '@fontsource/inter' function App(): JSX.Element { return ( - - - - } /> - } /> - } /> - - - + + + + + } /> + } /> + } /> + + + + ) } diff --git a/src/renderer/src/store/index.ts b/src/renderer/src/store/index.ts new file mode 100644 index 00000000..25cf17af --- /dev/null +++ b/src/renderer/src/store/index.ts @@ -0,0 +1,19 @@ +import { configureStore } from '@reduxjs/toolkit' + +import { combineReducers } from '@reduxjs/toolkit' +import { useDispatch } from 'react-redux' +import threads from './threads' + +const rootReducer = combineReducers({ + threads +}) + +const store = configureStore({ + reducer: rootReducer +}) + +export type RootState = ReturnType +export type AppDispatch = typeof store.dispatch +export const useAppDispatch = useDispatch.withTypes() + +export default store diff --git a/src/renderer/src/store/threads.ts b/src/renderer/src/store/threads.ts new file mode 100644 index 00000000..2a3003a3 --- /dev/null +++ b/src/renderer/src/store/threads.ts @@ -0,0 +1,30 @@ +import { createSlice, PayloadAction } from '@reduxjs/toolkit' +import { Thread } from '@renderer/types' + +interface State { + threads: Thread[] +} + +const initialState: State = { + threads: [] +} + +const threadsSlice = createSlice({ + name: 'threads', + initialState, + reducers: { + addThread: (state, action: PayloadAction) => { + state.threads.push(action.payload) + }, + removeThread: (state, action: PayloadAction<{ id: string }>) => { + state.threads = state.threads.filter((c) => c.id !== action.payload.id) + }, + updateThread: (state, action: PayloadAction) => { + state.threads = state.threads.map((c) => (c.id === action.payload.id ? action.payload : c)) + } + } +}) + +export const { addThread, removeThread, updateThread } = threadsSlice.actions + +export default threadsSlice diff --git a/src/renderer/src/types/index.ts b/src/renderer/src/types/index.ts new file mode 100644 index 00000000..c3f6021e --- /dev/null +++ b/src/renderer/src/types/index.ts @@ -0,0 +1,7 @@ +export type Thread = { + id: string + name: string + avatar: string + lastMessage: string + lastMessageAt: string +} diff --git a/yarn.lock b/yarn.lock index 6f966c0b..d2225dd0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -854,6 +854,16 @@ resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31" integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA== +"@reduxjs/toolkit@^2.2.5": + version "2.2.5" + resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-2.2.5.tgz#c0d2d8482ef80722bebe015ff05b06c34bfb6e0d" + integrity sha512-aeFA/s5NCG7NoJe/MhmwREJxRkDs0ZaSqt0MxhWUrwCf1UQXpwR87RROJEql0uAkLI6U7snBOYOcKw83ew3FPg== + dependencies: + immer "^10.0.3" + redux "^5.0.1" + redux-thunk "^3.1.0" + reselect "^5.1.0" + "@remix-run/router@1.16.1": version "1.16.1" resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.16.1.tgz#73db3c48b975eeb06d0006481bde4f5f2d17d1cd" @@ -1104,6 +1114,11 @@ resolved "https://registry.yarnpkg.com/@types/stylis/-/stylis-4.2.5.tgz#1daa6456f40959d06157698a653a9ab0a70281df" integrity sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw== +"@types/use-sync-external-store@^0.0.3": + version "0.0.3" + resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz#b6725d5f4af24ace33b36fafd295136e75509f43" + integrity sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA== + "@types/verror@^1.10.3": version "1.10.10" resolved "https://registry.yarnpkg.com/@types/verror/-/verror-1.10.10.tgz#d5a4b56abac169bfbc8b23d291363a682e6fa087" @@ -2894,6 +2909,11 @@ immediate@~3.0.5: resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ== +immer@^10.0.3: + version "10.1.1" + resolved "https://registry.yarnpkg.com/immer/-/immer-10.1.1.tgz#206f344ea372d8ea176891545ee53ccc062db7bc" + integrity sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw== + immutable@^4.0.0: version "4.3.6" resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.6.tgz#6a05f7858213238e587fb83586ffa3b4b27f0447" @@ -3780,6 +3800,14 @@ react-is@^16.13.1, react-is@^16.7.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== +react-redux@^9.1.2: + version "9.1.2" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-9.1.2.tgz#deba38c64c3403e9abd0c3fbeab69ffd9d8a7e4b" + integrity sha512-0OA4dhM1W48l3uzmv6B7TXPCGmokUU4p1M44DGN2/D9a1FjVPukVjER1PcPX97jIg6aUeLq1XJo1IpfbgULn0w== + dependencies: + "@types/use-sync-external-store" "^0.0.3" + use-sync-external-store "^1.0.0" + react-refresh@^0.14.0: version "0.14.2" resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.2.tgz#3833da01ce32da470f1f936b9d477da5c7028bf9" @@ -3826,6 +3854,21 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" +redux-persist@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/redux-persist/-/redux-persist-6.0.0.tgz#b4d2972f9859597c130d40d4b146fecdab51b3a8" + integrity sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ== + +redux-thunk@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-3.1.0.tgz#94aa6e04977c30e14e892eae84978c1af6058ff3" + integrity sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw== + +redux@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/redux/-/redux-5.0.1.tgz#97fa26881ce5746500125585d5642c77b6e9447b" + integrity sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w== + reflect.getprototypeof@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz#3ab04c32a8390b770712b7a8633972702d278859" @@ -3859,6 +3902,11 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== +reselect@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/reselect/-/reselect-5.1.0.tgz#c479139ab9dd91be4d9c764a7f3868210ef8cd21" + integrity sha512-aw7jcGLDpSgNDyWBQLv2cedml85qd95/iszJjN988zX1t7AVRJi19d9kto5+W7oCfQ94gyo40dVbT6g2k4/kXg== + resolve-alpn@^1.0.0: version "1.2.1" resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" @@ -4461,6 +4509,11 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" +use-sync-external-store@^1.0.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz#c3b6390f3a30eba13200d2302dcdf1e7b57b2ef9" + integrity sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw== + utf8-byte-length@^1.0.1: version "1.0.5" resolved "https://registry.yarnpkg.com/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz#f9f63910d15536ee2b2d5dd4665389715eac5c1e"