fix: flush redux persist data when app quit and update (#8741)

* feat(database): enable strict transaction durability for CherryStudio database

- Updated the Dexie database initialization to include `chromeTransactionDurability: 'strict'`, enhancing data integrity during transactions.

* feat(app): enhance application shutdown process and data flushing

- Added functionality to flush storage data and cookies before quitting the application, ensuring data integrity.
- Introduced a new `handleBeforeQuit` function to centralize cleanup logic for both manual and update-triggered quits.
- Updated logging to provide better insights during the shutdown process.
- Modified ProxyManager to use debug level for unchanged proxy configurations.
- Added `persistor` to the global window object and implemented `handleSaveData` to flush Redux state before quitting.

* format code

* feat(ipc): add App_SaveData channel and implement data saving on window close

- Introduced a new IPC channel `App_SaveData` for saving application data.
- Updated `WindowService` to send a save data message when the main window is closed.
- Enhanced `useAppInit` hook to handle the `App_SaveData` event and trigger data saving logic.

* refactor(env): remove persistor from global window object

- Removed the `persistor` property from the global `window` object in both `env.d.ts` and `index.ts` files, streamlining the application state management.
This commit is contained in:
beyondkmp 2025-08-01 14:47:11 +08:00 committed by GitHub
parent b7394c98a4
commit 488a01d7d7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 28 additions and 2 deletions

View File

@ -34,6 +34,7 @@ export enum IpcChannel {
App_InstallUvBinary = 'app:install-uv-binary',
App_InstallBunBinary = 'app:install-bun-binary',
App_LogToMain = 'app:log-to-main',
App_SaveData = 'app:save-data',
App_MacIsProcessTrusted = 'app:mac-is-process-trusted',
App_MacRequestProcessTrust = 'app:mac-request-process-trust',

View File

@ -66,7 +66,7 @@ export class ProxyManager {
try {
if (config?.mode === this.config?.mode && config?.proxyRules === this.config?.proxyRules) {
logger.info('proxy config is the same, skip configure')
logger.debug('proxy config is the same, skip configure')
return
}

View File

@ -319,6 +319,13 @@ export class WindowService {
private setupWindowLifecycleEvents(mainWindow: BrowserWindow) {
mainWindow.on('close', (event) => {
// save data before when close window
try {
mainWindow.webContents.send(IpcChannel.App_SaveData)
} catch (error) {
logger.error('Failed to save data:', error as Error)
}
// 如果已经触发退出,直接退出
if (app.isQuitting) {
return app.quit()

View File

@ -8,10 +8,12 @@ import KnowledgeQueue from '@renderer/queue/KnowledgeQueue'
import MemoryService from '@renderer/services/MemoryService'
import { useAppDispatch } from '@renderer/store'
import { useAppSelector } from '@renderer/store'
import { handleSaveData } from '@renderer/store'
import { selectMemoryConfig } from '@renderer/store/memory'
import { setAvatar, setFilesPath, setResourcesPath, setUpdateState } from '@renderer/store/runtime'
import { delay, runAsyncFunction } from '@renderer/utils'
import { defaultLanguage } from '@shared/config/constant'
import { IpcChannel } from '@shared/IpcChannel'
import { useLiveQuery } from 'dexie-react-hooks'
import { useEffect } from 'react'
@ -49,6 +51,12 @@ export function useAppInit() {
})
}, [])
useEffect(() => {
window.electron.ipcRenderer.on(IpcChannel.App_SaveData, async () => {
await handleSaveData()
})
}, [])
useUpdateHandler()
useFullScreenNotice()

View File

@ -7,7 +7,7 @@ import { useMinappPopup } from '@renderer/hooks/useMinappPopup'
import { useRuntime } from '@renderer/hooks/useRuntime'
import { useSettings } from '@renderer/hooks/useSettings'
import i18n from '@renderer/i18n'
import { useAppDispatch } from '@renderer/store'
import { handleSaveData, useAppDispatch } from '@renderer/store'
import { setUpdateState } from '@renderer/store/runtime'
import { ThemeMode } from '@renderer/types'
import { runAsyncFunction } from '@renderer/utils'
@ -41,6 +41,7 @@ const AboutSettings: FC = () => {
}
if (update.downloaded) {
await handleSaveData()
window.api.showUpdateDialog()
return
}

View File

@ -1,4 +1,5 @@
import { combineReducers, configureStore } from '@reduxjs/toolkit'
import { loggerService } from '@renderer/services/LoggerService'
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'
@ -28,6 +29,8 @@ import tabs from './tabs'
import translate from './translate'
import websearch from './websearch'
const logger = loggerService.withContext('Store')
const rootReducer = combineReducers({
assistants,
agents,
@ -101,4 +104,10 @@ export const useAppSelector = useSelector.withTypes<RootState>()
export const useAppStore = useStore.withTypes<typeof store>()
window.store = store
export async function handleSaveData() {
logger.info('Flushing redux persistor data')
await persistor.flush()
logger.info('Flushed redux persistor data')
}
export default store