fix(P0): match-history, tray/dashboard pause sync, whatsnew для upgraders

P0 #1 — Match-челленджи теперь пишутся в историю.
    HistoryEntry расширен полями `reps?`, `name?`, `source?` (snapshot
    planned-reps + name на момент записи + 'reminder'/'match').
    Новый store.markChallengeDone(challengeId, reps) пишет entry с
    exerciseId='challenge:<id>' и source='match'.
    Зарегистрирован IPC.markChallengeDone handler (раньше канал был в
    enum, но handler не подключен).
    ReminderApp.MatchSummaryView вызывает window.api.markChallengeDone
    при ✓-клике. Стрик, today_done, achievements теперь учитывают
    игровые тренировки.
    Заодно dailyReps/dailyRepsRange/totalDoneReps используют
    entry.reps как fallback — heatmap не теряет данные после удаления
    упражнения (закрывает P2 #12).

P0 #2 — Tray-пауза синхронизирована с Dashboard.
    Раньше tray держал scheduler-local `paused` boolean, который не
    отражался в settings.globalEnabled — Dashboard показывал «running»
    с тикающим таймером, хотя fires не приходили. Сейчас оба пути
    (tray и Dashboard-кнопка) меняют единственный source of truth —
    settings.globalEnabled. setPaused/isPaused/paused удалены, IPC
    pauseAll/resumeAll переписаны на updateSettings.

P0 #3 — Whats-new покажется существующим пользователям при апгрейде.
    Раньше для всех undefined lastSeenVersion (включая обновляющихся
    с v0.5.5) делали silent-save без модалки — никто бы не увидел
    v0.5.6 changelog. Сейчас: если есть Exercise с lastDoneAt → это
    обновляющийся пользователь, показываем заметки текущей версии;
    если нет — новичок, silent.
This commit is contained in:
AnRil
2026-05-22 14:49:29 +07:00
parent 77007636df
commit 17df87b3aa
10 changed files with 138 additions and 43 deletions

View File

@@ -111,13 +111,34 @@ export type PersistedState = AppState & {
export type HistoryAction = 'done' | 'skip' | 'snooze'
/**
* Источник записи: обычное напоминание (от scheduler'а) или матч (челлендж).
* Используется для UI («подтянулся в матче» vs «по таймеру») и аналитики.
*/
export type HistorySource = 'reminder' | 'match'
export type HistoryEntry = {
/** ms epoch */
ts: number
/**
* Для обычных напоминаний — Exercise.id. Для challenge'ей — `challenge:<id>`
* (синтетический ключ; renderer'у не нужно искать exercise по нему).
*/
exerciseId: string
action: HistoryAction
/** When user did less than planned. Only meaningful for `done`. */
actualReps?: number
/**
* Snapshot повторений на момент записи. Гарантирует, что после удаления
* упражнения история не теряет «сколько было сделано» (раньше lookup
* `byId.get(exerciseId).reps` возвращал undefined → heatmap показывал 0).
* Для match-челленджей — фактическое число повторов из match summary.
*/
reps?: number
/** Snapshot названия упражнения/челленджа — для будущего log-view. */
name?: string
/** undefined = reminder (для обратной совместимости со старыми entries). */
source?: HistorySource
}
export type Tick = {