ios widget showing what is available at chucks
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: add siri intents

+199
+199
wasup-chucks/SiriIntents.swift
··· 1 + // 2 + // SiriIntents.swift 3 + // wasup-chucks 4 + // 5 + // Created by Kieran Klukas on 2/2/26. 6 + // 7 + 8 + import AppIntents 9 + 10 + // MARK: - Chuck's Status Intent 11 + 12 + struct ChucksStatusIntent: AppIntent { 13 + static var title: LocalizedStringResource = "Check Chuck's Status" 14 + static var description = IntentDescription("Check if Chuck's dining hall is currently open or closed.") 15 + static var openAppWhenRun: Bool = false 16 + 17 + @MainActor 18 + func perform() async throws -> some IntentResult & ProvidesDialog { 19 + let status = ChucksStatus.calculate() 20 + 21 + if status.isOpen { 22 + if let remaining = status.timeRemaining { 23 + let timeStr = remaining.compactCountdown 24 + return .result(dialog: "Chuck's is open for \(status.currentPhase.rawValue). It closes in \(timeStr).") 25 + } else { 26 + return .result(dialog: "Chuck's is open for \(status.currentPhase.rawValue).") 27 + } 28 + } else { 29 + if let next = status.nextPhase, next != .closed, let remaining = status.timeRemaining { 30 + let timeStr = remaining.compactCountdown 31 + return .result(dialog: "Chuck's is closed. \(next.rawValue) starts in \(timeStr).") 32 + } else { 33 + return .result(dialog: "Chuck's is closed for the day.") 34 + } 35 + } 36 + } 37 + } 38 + 39 + // MARK: - Chuck's Menu Intent 40 + 41 + struct ChucksMenuIntent: AppIntent { 42 + static var title: LocalizedStringResource = "Get Chuck's Menu" 43 + static var description = IntentDescription("Find out what's being served at Chuck's.") 44 + static var openAppWhenRun: Bool = false 45 + 46 + @Parameter(title: "Meal", default: .current) 47 + var meal: MealParameter 48 + 49 + @MainActor 50 + func perform() async throws -> some IntentResult & ProvidesDialog { 51 + let status = ChucksStatus.calculate() 52 + let phase: MealPhase 53 + 54 + switch meal { 55 + case .current: 56 + phase = status.isOpen ? status.currentPhase : (status.nextPhase ?? .lunch) 57 + case .breakfast: 58 + phase = .breakfast 59 + case .lunch: 60 + phase = .lunch 61 + case .dinner: 62 + phase = .dinner 63 + } 64 + 65 + if phase == .closed { 66 + return .result(dialog: "Chuck's is closed for the day. Check back tomorrow!") 67 + } 68 + 69 + do { 70 + let items = try await ChucksService.shared.getSpecials(for: Date(), phase: phase) 71 + if items.isEmpty { 72 + return .result(dialog: "I couldn't find the menu for \(phase.rawValue) right now.") 73 + } 74 + 75 + let itemNames = items.prefix(5).map { $0.name }.joined(separator: ", ") 76 + return .result(dialog: "For \(phase.rawValue) at Home Cooking: \(itemNames).") 77 + } catch { 78 + return .result(dialog: "I couldn't load the menu right now. Try again later.") 79 + } 80 + } 81 + } 82 + 83 + // MARK: - Meal Time Intent 84 + 85 + struct ChucksMealTimeIntent: AppIntent { 86 + static var title: LocalizedStringResource = "Get Meal Time" 87 + static var description = IntentDescription("Find out what time a meal is served at Chuck's.") 88 + static var openAppWhenRun: Bool = false 89 + 90 + @Parameter(title: "Meal") 91 + var meal: MealTimeParameter 92 + 93 + @MainActor 94 + func perform() async throws -> some IntentResult & ProvidesDialog { 95 + let calendar = CedarvilleTime.calendar 96 + let weekday = calendar.component(.weekday, from: Date()) 97 + let schedule = MealSchedule.schedule(for: weekday) 98 + 99 + let phase: MealPhase 100 + switch meal { 101 + case .breakfast: 102 + phase = .breakfast 103 + case .lunch: 104 + phase = .lunch 105 + case .dinner: 106 + phase = .dinner 107 + } 108 + 109 + guard let mealSchedule = schedule.first(where: { $0.phase == phase }) else { 110 + return .result(dialog: "\(phase.rawValue) isn't served today.") 111 + } 112 + 113 + let startTime = formatTime(mealSchedule.startHour, mealSchedule.startMinute) 114 + let endTime = formatTime(mealSchedule.endHour, mealSchedule.endMinute) 115 + 116 + return .result(dialog: "\(phase.rawValue) is served from \(startTime) to \(endTime) today.") 117 + } 118 + 119 + private func formatTime(_ hour: Int, _ minute: Int) -> String { 120 + let period = hour >= 12 ? "PM" : "AM" 121 + let displayHour = hour > 12 ? hour - 12 : (hour == 0 ? 12 : hour) 122 + if minute == 0 { 123 + return "\(displayHour) \(period)" 124 + } 125 + return "\(displayHour):\(String(format: "%02d", minute)) \(period)" 126 + } 127 + } 128 + 129 + // MARK: - Meal Parameters 130 + 131 + enum MealTimeParameter: String, AppEnum { 132 + case breakfast 133 + case lunch 134 + case dinner 135 + 136 + static var typeDisplayRepresentation: TypeDisplayRepresentation = "Meal" 137 + 138 + static var caseDisplayRepresentations: [MealTimeParameter: DisplayRepresentation] = [ 139 + .breakfast: "Breakfast", 140 + .lunch: "Lunch", 141 + .dinner: "Dinner" 142 + ] 143 + } 144 + 145 + enum MealParameter: String, AppEnum { 146 + case current 147 + case breakfast 148 + case lunch 149 + case dinner 150 + 151 + static var typeDisplayRepresentation: TypeDisplayRepresentation = "Meal" 152 + 153 + static var caseDisplayRepresentations: [MealParameter: DisplayRepresentation] = [ 154 + .current: "Current Meal", 155 + .breakfast: "Breakfast", 156 + .lunch: "Lunch", 157 + .dinner: "Dinner" 158 + ] 159 + } 160 + 161 + // MARK: - App Shortcuts 162 + 163 + struct ChucksShortcuts: AppShortcutsProvider { 164 + static var appShortcuts: [AppShortcut] { 165 + AppShortcut( 166 + intent: ChucksStatusIntent(), 167 + phrases: [ 168 + "Is \(.applicationName) open", 169 + "Check \(.applicationName) status", 170 + "When does \(.applicationName) open", 171 + "When does \(.applicationName) close" 172 + ], 173 + shortTitle: "Chuck's Status", 174 + systemImageName: "fork.knife.circle" 175 + ) 176 + 177 + AppShortcut( 178 + intent: ChucksMenuIntent(), 179 + phrases: [ 180 + "What's for \(\.$meal) at \(.applicationName)", 181 + "\(.applicationName) menu", 182 + "What's being served at \(.applicationName)" 183 + ], 184 + shortTitle: "Chuck's Menu", 185 + systemImageName: "menucard" 186 + ) 187 + 188 + AppShortcut( 189 + intent: ChucksMealTimeIntent(), 190 + phrases: [ 191 + "What time is \(\.$meal) at \(.applicationName)", 192 + "When is \(\.$meal) at \(.applicationName)", 193 + "\(.applicationName) \(\.$meal) hours" 194 + ], 195 + shortTitle: "Meal Times", 196 + systemImageName: "clock" 197 + ) 198 + } 199 + }