fix+test: автономные правки после ревью v0.5.7

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.
This commit is contained in:
AnRil
2026-05-22 23:22:34 +07:00
parent c742417e82
commit 0c813c3ac8
10 changed files with 371 additions and 11 deletions

View File

@@ -68,6 +68,15 @@ export const IPC = {
evtUpdaterStatus: 'evt:updaterStatus',
evtMaximizeChanged: 'evt:maximizeChanged',
evtMeetingChanged: 'evt:meetingChanged',
/**
* Шлётся когда история мутирует (markDone / snooze / skip /
* markChallengeDone / clearHistory / import). Renderer'у достаточно
* перезапросить getHistory. Раньше Dashboard переключал history по
* `exercises` ref'у — но markDone мутирует Exercise in place, ref не
* меняется, и heatmap стояла. Этот event — единый сигнал что надо
* перетянуть.
*/
evtHistoryChanged: 'evt:historyChanged',
getMeetingActive: 'system:meetingActive'
} as const