168 lines
5.7 KiB
Swift
168 lines
5.7 KiB
Swift
// ── Siri Shortcuts (App Intents) ──────────────────────────────
|
|
// "Set a timer for 25 minutes", "Start Pomodoro", etc.
|
|
// iOS 17+ App Intents framework
|
|
|
|
import AppIntents
|
|
import Foundation
|
|
|
|
// MARK: - Set Timer Intent
|
|
|
|
struct SetTimerIntent: AppIntent {
|
|
static var title: LocalizedStringResource = "Set a Timer"
|
|
static var description: IntentDescription = "Creates a countdown timer for the specified duration"
|
|
static var openAppWhenRun: Bool = false
|
|
|
|
@Parameter(title: "Duration (minutes)", default: 25)
|
|
var minutes: Int
|
|
|
|
@Parameter(title: "Label", default: "Timer")
|
|
var label: String
|
|
|
|
static var parameterSummary: some ParameterSummary {
|
|
Summary("Set a \(\.$minutes) minute timer called \(\.$label)")
|
|
}
|
|
|
|
func perform() async throws -> some IntentResult & ProvidesDialog {
|
|
let now = Date()
|
|
let targetTime = now.addingTimeInterval(TimeInterval(minutes * 60))
|
|
let intervals = CascadePreset.minimal.defaultIntervals
|
|
|
|
let snapshot = TimerSnapshot(
|
|
id: UUID().uuidString,
|
|
label: label,
|
|
type: .countdown,
|
|
urgency: .standard,
|
|
state: .active,
|
|
targetTime: targetTime,
|
|
duration: TimeInterval(minutes * 60),
|
|
startedAt: now,
|
|
elapsedBeforePause: 0,
|
|
snoozeCount: 0,
|
|
category: nil,
|
|
pomodoroCurrentRound: nil,
|
|
pomodoroTotalRounds: nil,
|
|
pomodoroIsBreak: nil,
|
|
nextWarningTime: intervals.first.map { targetTime.addingTimeInterval(-Double($0) * 60) },
|
|
totalWarnings: intervals.count,
|
|
firedWarnings: 0
|
|
)
|
|
|
|
// Write to shared data
|
|
let manager = SharedTimerDataManager.shared
|
|
var timers = manager.readSnapshots()
|
|
timers.append(snapshot)
|
|
manager.writeSnapshots(timers)
|
|
manager.writeActiveTimer(nil) // Let iOS app recalculate
|
|
|
|
return .result(dialog: "Timer set for \(minutes) minutes: \(label)")
|
|
}
|
|
}
|
|
|
|
// MARK: - Start Pomodoro Intent
|
|
|
|
struct StartPomodoroIntent: AppIntent {
|
|
static var title: LocalizedStringResource = "Start Pomodoro"
|
|
static var description: IntentDescription = "Starts a 25-minute Pomodoro focus session"
|
|
static var openAppWhenRun: Bool = true
|
|
|
|
@Parameter(title: "Work Minutes", default: 25)
|
|
var workMinutes: Int
|
|
|
|
static var parameterSummary: some ParameterSummary {
|
|
Summary("Start a \(\.$workMinutes) minute Pomodoro session")
|
|
}
|
|
|
|
func perform() async throws -> some IntentResult & ProvidesDialog {
|
|
let now = Date()
|
|
let durationSeconds = TimeInterval(workMinutes * 60)
|
|
let targetTime = now.addingTimeInterval(durationSeconds)
|
|
|
|
let snapshot = TimerSnapshot(
|
|
id: UUID().uuidString,
|
|
label: "Pomodoro Focus",
|
|
type: .pomodoro,
|
|
urgency: .standard,
|
|
state: .active,
|
|
targetTime: targetTime,
|
|
duration: durationSeconds,
|
|
startedAt: now,
|
|
elapsedBeforePause: 0,
|
|
snoozeCount: 0,
|
|
category: nil,
|
|
pomodoroCurrentRound: 1,
|
|
pomodoroTotalRounds: 4,
|
|
pomodoroIsBreak: false,
|
|
nextWarningTime: targetTime.addingTimeInterval(-60),
|
|
totalWarnings: 1,
|
|
firedWarnings: 0
|
|
)
|
|
|
|
let manager = SharedTimerDataManager.shared
|
|
var timers = manager.readSnapshots()
|
|
timers.append(snapshot)
|
|
manager.writeSnapshots(timers)
|
|
|
|
return .result(dialog: "Pomodoro started: \(workMinutes) minutes of focus time!")
|
|
}
|
|
}
|
|
|
|
// MARK: - What's My Next Timer Intent
|
|
|
|
struct NextTimerInfoIntent: AppIntent {
|
|
static var title: LocalizedStringResource = "What's My Next Timer?"
|
|
static var description: IntentDescription = "Tells you about your next upcoming timer"
|
|
static var openAppWhenRun: Bool = false
|
|
|
|
func perform() async throws -> some IntentResult & ProvidesDialog {
|
|
let manager = SharedTimerDataManager.shared
|
|
guard let next = manager.readNextFiringTimer() else {
|
|
return .result(dialog: "You don't have any active timers right now.")
|
|
}
|
|
|
|
let remaining = next.remainingSeconds()
|
|
let compact = formatDurationCompact(remaining)
|
|
let timeStr = formatTime(next.targetTime)
|
|
|
|
return .result(dialog: "\(next.label) fires in \(compact), at \(timeStr).")
|
|
}
|
|
}
|
|
|
|
// MARK: - Shortcuts Provider
|
|
|
|
struct ChronoMindShortcutsProvider: AppShortcutsProvider {
|
|
static var appShortcuts: [AppShortcut] {
|
|
AppShortcut(
|
|
intent: SetTimerIntent(),
|
|
phrases: [
|
|
"Set a timer in \(.applicationName)",
|
|
"Set a \(\.$minutes) minute timer in \(.applicationName)",
|
|
"Start a \(\.$minutes) minute timer called \(\.$label) in \(.applicationName)",
|
|
],
|
|
shortTitle: "Set Timer",
|
|
systemImageName: "timer"
|
|
)
|
|
|
|
AppShortcut(
|
|
intent: StartPomodoroIntent(),
|
|
phrases: [
|
|
"Start Pomodoro in \(.applicationName)",
|
|
"Start a focus session in \(.applicationName)",
|
|
"Start a \(\.$workMinutes) minute Pomodoro in \(.applicationName)",
|
|
],
|
|
shortTitle: "Start Pomodoro",
|
|
systemImageName: "target"
|
|
)
|
|
|
|
AppShortcut(
|
|
intent: NextTimerInfoIntent(),
|
|
phrases: [
|
|
"What's my next timer in \(.applicationName)",
|
|
"When's my next alarm in \(.applicationName)",
|
|
"Next timer in \(.applicationName)",
|
|
],
|
|
shortTitle: "Next Timer",
|
|
systemImageName: "clock"
|
|
)
|
|
}
|
|
}
|