feat: widget has two tap targets — fitness + add food
All checks were successful
Security Checks / dependency-audit (push) Successful in 13s
Security Checks / secret-scanning (push) Successful in 3s
Security Checks / dockerfile-lint (push) Successful in 4s

Widget background tap → platform://fitness → opens Fitness tab
Widget + button tap → platform://add-food → opens Fitness + food assistant

Small widget: + button in bottom-right corner (emerald green)
Medium widget: + button in bottom-right corner
Lock screen widgets: single tap → fitness (no room for + button)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Yusuf Suleman
2026-04-04 11:45:08 -05:00
parent 7cfe3eeed5
commit a5c95c2e5f
2 changed files with 45 additions and 22 deletions

View File

@@ -121,9 +121,15 @@ struct MainTabView: View {
AssistantSheetView(onFoodAdded: foodAdded) AssistantSheetView(onFoodAdded: foodAdded)
} }
.onOpenURL { url in .onOpenURL { url in
if url.scheme == "platform" && url.host == "add-food" { guard url.scheme == "platform" else { return }
selectedTab = 1 // Fitness tab switch url.host {
case "fitness":
selectedTab = 1
case "add-food":
selectedTab = 1
showAssistant = true showAssistant = true
default:
break
} }
} }
.task { .task {

View File

@@ -184,14 +184,23 @@ struct SmallWidgetView: View {
let entry: CalorieEntry let entry: CalorieEntry
var body: some View { var body: some View {
VStack(spacing: 8) { ZStack(alignment: .bottomTrailing) {
CalorieRingView(entry: entry, size: 90, lineWidth: 8) VStack(spacing: 8) {
CalorieRingView(entry: entry, size: 80, lineWidth: 7)
Text("\(Int(entry.remaining)) left") Text("\(Int(entry.remaining)) left")
.font(.caption2.weight(.medium)) .font(.caption2.weight(.medium))
.foregroundStyle(.secondary) .foregroundStyle(.secondary)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
// + button separate tap target opens food assistant
Link(destination: URL(string: "platform://add-food")!) {
Image(systemName: "plus.circle.fill")
.font(.title2)
.foregroundStyle(Color(red: 0.020, green: 0.588, blue: 0.412))
}
} }
.frame(maxWidth: .infinity, maxHeight: .infinity)
} }
} }
@@ -199,24 +208,32 @@ struct MediumWidgetView: View {
let entry: CalorieEntry let entry: CalorieEntry
var body: some View { var body: some View {
HStack(spacing: 20) { ZStack(alignment: .bottomTrailing) {
CalorieRingView(entry: entry, size: 100, lineWidth: 9) HStack(spacing: 20) {
CalorieRingView(entry: entry, size: 100, lineWidth: 9)
VStack(alignment: .leading, spacing: 6) { VStack(alignment: .leading, spacing: 6) {
Text("Calories") Text("Calories")
.font(.headline) .font(.headline)
.foregroundStyle(.primary) .foregroundStyle(.primary)
Text("\(Int(entry.totalCalories)) of \(Int(entry.calorieGoal))") Text("\(Int(entry.totalCalories)) of \(Int(entry.calorieGoal))")
.font(.subheadline) .font(.subheadline)
.foregroundStyle(.secondary) .foregroundStyle(.secondary)
Text("\(Int(entry.remaining)) remaining") Text("\(Int(entry.remaining)) remaining")
.font(.caption) .font(.caption)
.foregroundStyle(.tertiary) .foregroundStyle(.tertiary)
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
Link(destination: URL(string: "platform://add-food")!) {
Image(systemName: "plus.circle.fill")
.font(.title2)
.foregroundStyle(Color(red: 0.020, green: 0.588, blue: 0.412))
} }
} }
.frame(maxWidth: .infinity, maxHeight: .infinity)
} }
} }
@@ -290,7 +307,7 @@ struct PlatformWidget: Widget {
var body: some WidgetConfiguration { var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: CalorieProvider()) { entry in StaticConfiguration(kind: kind, provider: CalorieProvider()) { entry in
PlatformWidgetEntryView(entry: entry) PlatformWidgetEntryView(entry: entry)
.widgetURL(URL(string: "platform://add-food")) .widgetURL(URL(string: "platform://fitness"))
.containerBackground(.fill.tertiary, for: .widget) .containerBackground(.fill.tertiary, for: .widget)
} }
.configurationDisplayName("Calories") .configurationDisplayName("Calories")