fix(P1): delete-confirm, daily-goal closed UI, meeting indicator, modal-confirm
P1 #4 — ConfirmModal (новый src/renderer/src/components/ui/ConfirmModal.tsx) с iOS-стилем + focus-trap (через Modal). Delete упражнения в Dashboard теперь спрашивает «Удалить упражнение?» с destructive-кнопкой. P1 #5 — Daily goal closed UI. ExerciseCard принимает doneToday prop и при `done >= dailyGoal` показывает «Цель закрыта · 100/100» вместо запутанного «25ч 13м» countdown'а. Цвет — success-зелёный. P1 #6 — Meeting auto-pause indicator. Новый IPC.getMeetingActive + evtMeetingChanged event. meeting-detect broadcast'ит изменения состояния. Dashboard показывает info-баннер «Не дёргаем — ты на встрече» когда meetingAutoPause включён и хотя бы один meeting процесс запущен. P1 #7 — Native window.confirm() заменён на ConfirmModal в Settings DataCard для restore-операции. Теперь iOS-style с destructive confirm-кнопкой и focus-trap'ом. Заодно P2 #8: Brain-иконка-badge на ExerciseCard для adaptive упражнений — пользователь видит почему «Next» не строго равен intervalMinutes. P2 #12: dailyReps/dailyRepsRange/totalDoneReps/repsDoneTodayForExercise используют entry.reps как fallback — heatmap не теряет данные после удаления упражнения.
This commit is contained in:
@@ -49,6 +49,7 @@ import {
|
||||
getUpdaterStatus,
|
||||
quitAndInstall
|
||||
} from './updater'
|
||||
import { isMeetingActiveSync } from './meeting-detect'
|
||||
import {
|
||||
validateActualReps,
|
||||
validateChallengeInput,
|
||||
@@ -187,6 +188,8 @@ export function registerIpc(): void {
|
||||
|
||||
ipcMain.handle(IPC.getAppVersion, () => app.getVersion())
|
||||
|
||||
ipcMain.handle(IPC.getMeetingActive, () => isMeetingActiveSync())
|
||||
|
||||
ipcMain.handle(IPC.quit, () => app.quit())
|
||||
ipcMain.handle(IPC.reminderClose, () => hideReminderWindow())
|
||||
|
||||
|
||||
@@ -16,8 +16,16 @@
|
||||
*/
|
||||
import { exec } from 'node:child_process'
|
||||
import { promisify } from 'node:util'
|
||||
import { BrowserWindow } from 'electron'
|
||||
import { IPC } from '@shared/ipc'
|
||||
import { log } from './logger'
|
||||
|
||||
function broadcast(active: boolean): void {
|
||||
for (const win of BrowserWindow.getAllWindows()) {
|
||||
if (!win.isDestroyed()) win.webContents.send(IPC.evtMeetingChanged, active)
|
||||
}
|
||||
}
|
||||
|
||||
const execAsync = promisify(exec)
|
||||
|
||||
/**
|
||||
@@ -65,6 +73,7 @@ export async function isMeetingActive(): Promise<boolean> {
|
||||
if (lower.includes(`"${proc}",`)) {
|
||||
if (!cachedActive) {
|
||||
log.info(`[meeting] detected ${proc} — pausing reminders`)
|
||||
broadcast(true)
|
||||
}
|
||||
cachedActive = true
|
||||
return true
|
||||
@@ -72,6 +81,7 @@ export async function isMeetingActive(): Promise<boolean> {
|
||||
}
|
||||
if (cachedActive) {
|
||||
log.info('[meeting] no meeting processes — resuming reminders')
|
||||
broadcast(false)
|
||||
}
|
||||
cachedActive = false
|
||||
return false
|
||||
|
||||
Reference in New Issue
Block a user