6 Commits

Author SHA1 Message Date
Yusuf Suleman
a5c95c2e5f 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>
2026-04-04 11:45:08 -05:00
Yusuf Suleman
7cfe3eeed5 feat: tap widget → opens app to food search
All checks were successful
Security Checks / secret-scanning (push) Successful in 3s
Security Checks / dockerfile-lint (push) Successful in 4s
Security Checks / dependency-audit (push) Successful in 13s
Widget: .widgetURL(platform://add-food) on all widget sizes
App: .onOpenURL handles platform://add-food → switches to Fitness
tab and opens the food assistant sheet

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 11:41:21 -05:00
Yusuf Suleman
5d51ac6833 harden: widget edge cases — expired session, account switch, cache
All checks were successful
Security Checks / dependency-audit (push) Successful in 14s
Security Checks / secret-scanning (push) Successful in 3s
Security Checks / dockerfile-lint (push) Successful in 4s
1. Session expires: widget gets 401 → clears stale cookie from
   App Group → stops retrying with bad auth → shows cached data
   until user opens app and re-authenticates

2. Account switch: login() now calls clearWidgetAuth() BEFORE
   syncCookieToWidget() — clears previous user's cached calories
   before writing new user's cookie. No brief display of wrong data.

3. Logout: already correct — clearWidgetAuth removes cookie +
   cached data, widget shows 0/2000

4. Minimum data: only session cookie + 2 cached numbers + timestamp
   in App Group. No passwords, no user IDs, no PII.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 11:31:44 -05:00
Yusuf Suleman
e21a26db18 feat: widget fetches calories from API independently + shared auth
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:
- Fetches /api/fitness/entries/totals and /api/fitness/goals/for-date
  directly from gateway using shared session cookie
- Falls back to cached data in App Group UserDefaults if network fails
- Refreshes every 15 minutes via WidgetKit timeline
- Each phone shows the logged-in user's own data

Auth sharing:
- AuthManager.syncCookieToWidget() copies the session cookie to
  App Group UserDefaults on login and auth check
- Widget reads cookie and makes authenticated API calls
- Logout clears widget auth + cached data

Data in App Group (group.com.quadjourney.platform):
- widget_sessionCookie: auth token for API calls
- widget_totalCalories: cached fallback
- widget_calorieGoal: cached fallback
- widget_lastUpdate: cache timestamp

HomeViewModel also writes cache on each loadTodayData() as fallback.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 11:29:52 -05:00
Yusuf Suleman
9965b1d634 feat: calorie ring widget — home screen + lock screen
All checks were successful
Security Checks / dockerfile-lint (push) Successful in 4s
Security Checks / dependency-audit (push) Successful in 13s
Security Checks / secret-scanning (push) Successful in 4s
Widget displays:
- systemSmall: calorie ring + "X left" text
- systemMedium: ring + "Calories" / "X of Y" / "X remaining"
- accessoryCircular: gauge ring for lock screen
- accessoryInline: "🔥 845 / 2000 cal" text for lock screen
- accessoryRectangular: linear gauge + calorie count

Data flow: main app writes totalCalories + calorieGoal to
UserDefaults on each loadTodayData(), then calls
WidgetCenter.shared.reloadAllTimelines(). Widget reads on
15-minute refresh cycle.

Note: currently uses standard UserDefaults (same app container).
For production, migrate to App Group UserDefaults so widget
process can read the data. Requires Xcode App Group setup.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 11:21:40 -05:00
a4ebe77973 feat: add PlatformWidget extension target
All checks were successful
Security Checks / dependency-audit (push) Successful in 13s
Security Checks / secret-scanning (push) Successful in 4s
Security Checks / dockerfile-lint (push) Successful in 3s
2026-04-04 11:19:18 -05:00