Bug — Heatmap/streak/achievements не обновлялись после markDone/
markChallengeDone. Регресс из Sprint C (история выделена из
state-broadcast). Корень: store мутирует Exercise.lastDoneAt
in-place → state.exercises ref не меняется → useEffect([exercises])
не fires → Dashboard не перетягивает history.
Фикс: новый event IPC.evtHistoryChanged + broadcastHistoryChanged().
Триггерится после markDone/snooze/skip/markChallengeDone/
clearHistory/import. Dashboard.useEffect подписывается через
onHistoryChanged.
Settings → AboutCard теперь показывает текущую версию приложения
(раньше была только кнопка «Что нового»). Версия через
IPC.getAppVersion.
Tests:
+6 для repsDoneTodayForExercise — match-challenges, snapshot,
deleted-exercise fallback, ignore skip/snooze.
+2 для dailyReps с новыми snapshot-полями (match-challenges
и deleted exercises).
+6 для unseenVersions + RELEASE_NOTES контракт.
+7 для adjustNextFireAt (адаптивный шедулер): малая история,
плохой/хороший час, MAX_SHIFT_HOURS, фильтр по упражнению,
30-day window.
Итого 135 → 159 (+24).
Грепнул src/ на стейл-references к removed setPaused/isPaused/
`let paused` — чисто. Sprint C-D refactor завершён без residue.
30 lines
1.1 KiB
TypeScript
30 lines
1.1 KiB
TypeScript
import { BrowserWindow } from 'electron'
|
||
import { IPC } from '@shared/ipc'
|
||
import { getExercises, getStateForRenderer, updateExercise } from './store'
|
||
|
||
export function broadcastState(): void {
|
||
// Используем 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)
|
||
}
|
||
}
|
||
|
||
/** Сигнализирует renderer'у что историю надо перетянуть. */
|
||
export function broadcastHistoryChanged(): void {
|
||
for (const win of BrowserWindow.getAllWindows()) {
|
||
if (!win.isDestroyed()) win.webContents.send(IPC.evtHistoryChanged)
|
||
}
|
||
}
|
||
|
||
export function snoozeAll(minutes: number): void {
|
||
const now = Date.now()
|
||
for (const ex of getExercises()) {
|
||
if (!ex.enabled) continue
|
||
updateExercise(ex.id, { nextFireAt: now + minutes * 60_000 })
|
||
}
|
||
broadcastState()
|
||
}
|