perf: sprint C — отделить history от IPC state-broadcast

#9  AppState больше не содержит `history` (вынесено в PersistedState
    — internal store-shape). `broadcastState()` и IPC.getState шлют
    через IPC только exercises/settings/challenges/gamesEnabled.

    Раньше каждый markDone/snooze/toggle вызывал broadcastState() →
    весь state, включая до 10k history-записей (~500KB JSON), летел
    через IPC к каждому BrowserWindow и парсился в renderer'е. На
    долгом горизонте использования становилось заметным лагом UI.

    Renderer и раньше историю из state не читал (Dashboard вызывает
    IPC.getHistory отдельно), так что это чистый perf-win без
    behavioural change. Store-internal mutations продолжают работать
    с полным PersistedState через `getState()`; renderer-bound IPC
    использует новый `getStateForRenderer()`.

Не сделано из спринта C: zustand setState refactor (#8) — текущая
архитектура работает корректно (zustand bathes), `?? []` fallback'и
в селекторах безопасны. Реальный gain был от #9, который и закрыт.
This commit is contained in:
AnRil
2026-05-22 01:18:25 +07:00
parent 4745f5e091
commit e7ccca98e7
4 changed files with 54 additions and 17 deletions

View File

@@ -1,9 +1,12 @@
import { BrowserWindow } from 'electron'
import { IPC } from '@shared/ipc'
import { getExercises, getState, updateExercise } from './store'
import { getExercises, getStateForRenderer, updateExercise } from './store'
export function broadcastState(): void {
const state = getState()
// Используем variant без `history` — иначе при 10k записей через IPC
// на каждый markDone летит 500KB JSON × M подписчиков. Renderer
// запрашивает историю отдельно через IPC.getHistory.
const state = getStateForRenderer()
for (const win of BrowserWindow.getAllWindows()) {
if (!win.isDestroyed()) win.webContents.send(IPC.evtStateChanged, state)
}