Initial commit

This commit is contained in:
AnRil
2026-05-16 13:43:29 +07:00
commit 688a86b611
208 changed files with 44350 additions and 0 deletions

204
src/main/ipc.ts Normal file
View File

@@ -0,0 +1,204 @@
import { ipcMain, nativeTheme, systemPreferences, BrowserWindow, app, shell } from 'electron'
import { IPC } from '@shared/ipc'
import type { Challenge, Exercise, GameId, Settings } from '@shared/types'
import {
addChallenge,
addExercise,
deleteChallenge,
deleteExercise,
getState,
markDone,
setGameEnabled,
skip,
snooze,
updateChallenge,
updateExercise,
updateSettings
} from './store'
import { broadcastState } from './state-actions'
import { setAutostart, isAutostartEnabled } from './autostart'
import { setPaused, forceCheck } from './scheduler'
import { hideReminderWindow, getMainWindow } from './windows'
import {
broadcastGames,
installGame,
listGamesStatus,
simulateMatchEnd,
toggleGame,
uninstallGame
} from './games/registry'
export function registerIpc(): void {
ipcMain.handle(IPC.getState, () => {
const state = getState()
state.settings.startWithWindows = isAutostartEnabled()
return state
})
ipcMain.handle(
IPC.addExercise,
(_e, input: Omit<Exercise, 'id' | 'nextFireAt' | 'lastDoneAt'>) => {
const ex = addExercise(input)
broadcastState()
return ex
}
)
ipcMain.handle(IPC.updateExercise, (_e, id: string, patch: Partial<Exercise>) => {
const ex = updateExercise(id, patch)
broadcastState()
return ex
})
ipcMain.handle(IPC.deleteExercise, (_e, id: string) => {
const ok = deleteExercise(id)
broadcastState()
return ok
})
ipcMain.handle(IPC.toggleExercise, (_e, id: string, enabled: boolean) => {
const patch: Partial<Exercise> = { enabled }
if (enabled) {
const ex = getState().exercises.find((e) => e.id === id)
if (ex) patch.nextFireAt = Date.now() + ex.intervalMinutes * 60_000
}
const ex = updateExercise(id, patch)
broadcastState()
return ex
})
ipcMain.handle(IPC.markDone, (_e, id: string) => {
const ex = markDone(id)
broadcastState()
return ex
})
ipcMain.handle(IPC.snooze, (_e, id: string, minutes: number) => {
const ex = snooze(id, minutes)
broadcastState()
return ex
})
ipcMain.handle(IPC.skip, (_e, id: string) => {
const ex = skip(id)
broadcastState()
return ex
})
ipcMain.handle(IPC.updateSettings, (_e, patch: Partial<Settings>) => {
if (patch.startWithWindows !== undefined) {
setAutostart(patch.startWithWindows)
}
const merged: Partial<Settings> = { ...patch }
if (patch.startWithWindows !== undefined) {
merged.startWithWindows = isAutostartEnabled()
}
const settings = updateSettings(merged)
broadcastState()
return settings
})
ipcMain.handle(IPC.getAccentColor, () => {
try {
return '#' + systemPreferences.getAccentColor()
} catch {
return '#5B8DEF'
}
})
ipcMain.handle(IPC.getOsTheme, () =>
nativeTheme.shouldUseDarkColors ? 'dark' : 'light'
)
ipcMain.handle(IPC.pauseAll, () => setPaused(true))
ipcMain.handle(IPC.resumeAll, () => {
setPaused(false)
forceCheck()
})
ipcMain.handle(IPC.quit, () => app.quit())
ipcMain.handle(IPC.reminderClose, () => hideReminderWindow())
ipcMain.on(IPC.minimizeMain, (event) => {
BrowserWindow.fromWebContents(event.sender)?.minimize()
})
ipcMain.on(IPC.closeMain, () => {
const main = getMainWindow()
if (!main) return
if (getState().settings.minimizeToTray) main.hide()
else main.close()
})
ipcMain.on(IPC.hideMain, () => getMainWindow()?.hide())
// Games
ipcMain.handle(IPC.gamesList, async () => listGamesStatus())
ipcMain.handle(IPC.gameInstall, async (_e, id: GameId) => {
const status = await installGame(id)
setGameEnabled(id, true)
await toggleGame(id, true)
const all = await listGamesStatus()
broadcastGames(all)
broadcastState()
return status
})
ipcMain.handle(IPC.gameUninstall, async (_e, id: GameId) => {
const status = await uninstallGame(id)
setGameEnabled(id, false)
const all = await listGamesStatus()
broadcastGames(all)
broadcastState()
return status
})
ipcMain.handle(IPC.gameToggle, async (_e, id: GameId, enabled: boolean) => {
setGameEnabled(id, enabled)
await toggleGame(id, enabled)
const all = await listGamesStatus()
broadcastGames(all)
broadcastState()
})
ipcMain.handle(IPC.gameOpenLaunchOptions, (_e, _id: GameId) => {
// Opens Steam's library; user manually adds launch options.
shell.openExternal('steam://nav/games/details/570')
})
// Challenges
ipcMain.handle(IPC.addChallenge, (_e, input: Omit<Challenge, 'id'>) => {
const c = addChallenge(input)
broadcastState()
return c
})
ipcMain.handle(
IPC.updateChallenge,
(_e, id: string, patch: Partial<Challenge>) => {
const c = updateChallenge(id, patch)
broadcastState()
return c
}
)
ipcMain.handle(IPC.deleteChallenge, (_e, id: string) => {
const ok = deleteChallenge(id)
broadcastState()
return ok
})
ipcMain.handle(IPC.toggleChallenge, (_e, id: string, enabled: boolean) => {
const c = updateChallenge(id, { enabled })
broadcastState()
return c
})
ipcMain.handle(IPC.closeMatchSummary, () => hideReminderWindow())
// Dev helper: simulate a match end with given stats.
ipcMain.handle(
'dev:simulateMatchEnd',
(_e, id: GameId, stats: Record<string, number>) => {
simulateMatchEnd(id, stats)
}
)
}