mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-07-03 12:27:41 +08:00
refactor(redux-service): stub main-process bridge, keep interface
ReduxService.ts becomes a stub: the class, singleton export and four method signatures are preserved, but every method now logs an error and returns an empty value (undefined / void) of the declared return type. Consumers' existing null-safe branches absorb the empty return, so no call site needs to change, and the per-call error log keeps remaining call sites visible in production logs for staged migration to the v2 data layer (Preference / DataApi / direct IPC). The ReduxStoreReady IPC handshake is removed on both sides: the stub no longer reads from the renderer, so the channel has no producer or consumer. Dropped from IpcChannel enum; renderer-side invoke is commented out with the existing // [v2] Removed: convention.
This commit is contained in:
@@ -357,8 +357,6 @@ export enum IpcChannel {
|
||||
|
||||
FullscreenStatusChanged = 'fullscreen-status-changed',
|
||||
|
||||
ReduxStoreReady = 'redux-store-ready',
|
||||
|
||||
// Search Window
|
||||
SearchWindow_Open = 'search-window:open',
|
||||
SearchWindow_Close = 'search-window:close',
|
||||
|
||||
@@ -1,133 +1,54 @@
|
||||
/**
|
||||
* @deprecated Scheduled for removal in v2.0.0
|
||||
* @deprecated Stubbed in v2 — all methods log an error and return empty values.
|
||||
* --------------------------------------------------------------------------
|
||||
* ⚠️ NOTICE: V2 DATA&UI REFACTORING (by 0xfullex)
|
||||
* --------------------------------------------------------------------------
|
||||
* STOP: Feature PRs affecting this file are currently BLOCKED.
|
||||
* Only critical bug fixes are accepted during this migration phase.
|
||||
* The Redux store bridge has been neutralized in v2. The class and its public
|
||||
* interface are preserved so existing call sites still compile and run, but
|
||||
* every method now:
|
||||
* - logs `logger.error(...)` with the selector / action name so remaining
|
||||
* call sites are visible in production logs
|
||||
* - returns an empty value (`undefined` / `void`) matching the declared
|
||||
* return type; consumers' existing null-safe branches (`x?.y`, `x || []`,
|
||||
* `x || null`, etc.) will naturally degrade to a "no data" path.
|
||||
*
|
||||
* This file is being refactored to v2 standards.
|
||||
* Any non-critical changes will conflict with the ongoing work.
|
||||
* The former `ipcMain.handle(IpcChannel.ReduxStoreReady, ...)` handshake has
|
||||
* been removed on both sides: the stub does not read from the renderer, so
|
||||
* there is no readiness state to track, and the renderer's invoke of the
|
||||
* channel (renderer/src/store/index.ts) has been commented out with the
|
||||
* existing `// [v2] Removed:` convention.
|
||||
*
|
||||
* 🔗 Context & Status:
|
||||
* - Contribution Hold: https://github.com/CherryHQ/cherry-studio/issues/10954
|
||||
* - v2 Refactor PR : https://github.com/CherryHQ/cherry-studio/pull/10162
|
||||
* Migrate each caller to the v2 data layer (Preference / DataApi / direct
|
||||
* IPC) at its own pace. Once no callers remain, delete this file.
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
import { application } from '@application'
|
||||
import { loggerService } from '@logger'
|
||||
import { IpcChannel } from '@shared/IpcChannel'
|
||||
import { ipcMain } from 'electron'
|
||||
|
||||
type StoreValue = any
|
||||
|
||||
const logger = loggerService.withContext('ReduxService')
|
||||
const STORE_READY_TIMEOUT = 10000
|
||||
|
||||
const STUB_ERROR_MESSAGE =
|
||||
'ReduxService is stubbed in v2 — the Redux store bridge no longer works. This call site must be migrated to the new data layer.'
|
||||
|
||||
export class ReduxService {
|
||||
private isReady = false
|
||||
private resolveReady!: () => void
|
||||
private readyPromise = new Promise<void>((r) => (this.resolveReady = r))
|
||||
|
||||
constructor() {
|
||||
ipcMain.handle(IpcChannel.ReduxStoreReady, () => {
|
||||
this.isReady = true
|
||||
this.resolveReady()
|
||||
})
|
||||
}
|
||||
|
||||
private async waitForStoreReady(): Promise<void> {
|
||||
if (this.isReady) return
|
||||
|
||||
let timer: ReturnType<typeof setTimeout>
|
||||
const timeout = new Promise<never>((_, reject) => {
|
||||
timer = setTimeout(() => reject(new Error('Timeout waiting for Redux store to be ready')), STORE_READY_TIMEOUT)
|
||||
})
|
||||
|
||||
await Promise.race([this.readyPromise, timeout]).finally(() => clearTimeout(timer))
|
||||
}
|
||||
|
||||
private async getWebContents(): Promise<Electron.WebContents> {
|
||||
await this.waitForStoreReady()
|
||||
|
||||
const mainWindow = application.get('MainWindowService').getMainWindow()
|
||||
|
||||
if (!mainWindow) {
|
||||
throw new Error('Main window is not available')
|
||||
}
|
||||
|
||||
return mainWindow.webContents
|
||||
}
|
||||
|
||||
// Select state from renderer process
|
||||
async select<T = StoreValue>(selector: string): Promise<T> {
|
||||
try {
|
||||
const webContents = await this.getWebContents()
|
||||
return await webContents.executeJavaScript(`
|
||||
(() => {
|
||||
const state = window.store.getState();
|
||||
return ${selector};
|
||||
})()
|
||||
`)
|
||||
} catch (error) {
|
||||
logger.error('Failed to select store value:', error as Error)
|
||||
throw error
|
||||
}
|
||||
logger.error(`${STUB_ERROR_MESSAGE} select('${selector}')`)
|
||||
return undefined as unknown as T
|
||||
}
|
||||
|
||||
// Dispatch action
|
||||
async dispatch(action: any): Promise<void> {
|
||||
try {
|
||||
const webContents = await this.getWebContents()
|
||||
await webContents.executeJavaScript(`window.store.dispatch(${JSON.stringify(action)})`)
|
||||
} catch (error) {
|
||||
logger.error('Failed to dispatch action:', error as Error)
|
||||
throw error
|
||||
}
|
||||
logger.error(`${STUB_ERROR_MESSAGE} dispatch(type=${action?.type ?? 'unknown'})`)
|
||||
}
|
||||
|
||||
// Get entire state tree
|
||||
async getState(): Promise<any> {
|
||||
try {
|
||||
const webContents = await this.getWebContents()
|
||||
return await webContents.executeJavaScript(`window.store.getState()`)
|
||||
} catch (error) {
|
||||
logger.error('Failed to get state:', error as Error)
|
||||
throw error
|
||||
}
|
||||
logger.error(`${STUB_ERROR_MESSAGE} getState()`)
|
||||
return undefined
|
||||
}
|
||||
|
||||
// Batch dispatch actions
|
||||
async batch(actions: any[]): Promise<void> {
|
||||
for (const action of actions) {
|
||||
await this.dispatch(action)
|
||||
}
|
||||
logger.error(`${STUB_ERROR_MESSAGE} batch(${actions?.length ?? 0} actions)`)
|
||||
}
|
||||
}
|
||||
|
||||
export const reduxService = new ReduxService()
|
||||
|
||||
/**
|
||||
* @example
|
||||
* async function example() {
|
||||
* try {
|
||||
* // Select state
|
||||
* const settings = await reduxService.select('state.settings')
|
||||
* logger.log('settings', settings)
|
||||
*
|
||||
* // Dispatch action
|
||||
* await reduxService.dispatch({
|
||||
* type: 'settings/updateApiKey',
|
||||
* payload: 'new-api-key'
|
||||
* })
|
||||
*
|
||||
* // Batch dispatch actions
|
||||
* await reduxService.batch([
|
||||
* { type: 'action1', payload: 'data1' },
|
||||
* { type: 'action2', payload: 'data2' }
|
||||
* ])
|
||||
* } catch (error) {
|
||||
* logger.error('Error:', error)
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
*/
|
||||
import { loggerService } from '@logger'
|
||||
import { combineReducers, configureStore } from '@reduxjs/toolkit'
|
||||
import { IpcChannel } from '@shared/IpcChannel'
|
||||
// [v2] Removed: IpcChannel only referenced by the ReduxStoreReady signal below, which is now commented out.
|
||||
// import { IpcChannel } from '@shared/IpcChannel'
|
||||
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'
|
||||
@@ -124,9 +125,9 @@ export const persistor = persistStore(store, undefined, () => {
|
||||
}, 0)
|
||||
}
|
||||
|
||||
// Notify main process that Redux store is ready
|
||||
void window.electron?.ipcRenderer?.invoke(IpcChannel.ReduxStoreReady)
|
||||
logger.info('Redux store ready, notified main process')
|
||||
// [v2] Removed: ReduxService is stubbed in v2 and no longer registers a handler for this channel.
|
||||
// void window.electron?.ipcRenderer?.invoke(IpcChannel.ReduxStoreReady)
|
||||
// logger.info('Redux store ready, notified main process')
|
||||
})
|
||||
|
||||
export const useAppDispatch = useDispatch.withTypes<AppDispatch>()
|
||||
|
||||
Reference in New Issue
Block a user