diff options
| author | ivar <i@oiee.no> | 2026-03-23 23:49:18 +0100 |
|---|---|---|
| committer | ivar <i@oiee.no> | 2026-03-23 23:49:18 +0100 |
| commit | 110fd5dcf10e75dc636d84573ab271447cc0405f (patch) | |
| tree | 47727c1b62c8f0d52f9a2cb14fdee824feb947bd /docs/superpowers/specs/2026-03-23-sunrise-sunset-widget-design.md | |
| parent | 2a6eeb6686f5c1922da2bce5964c2bf7172951ea (diff) | |
| download | solverv-110fd5dcf10e75dc636d84573ab271447cc0405f.tar.xz solverv-110fd5dcf10e75dc636d84573ab271447cc0405f.zip | |
docs: clarify widget-app group communication, constructor, timezone handling in sunrise/sunset spec
Diffstat (limited to 'docs/superpowers/specs/2026-03-23-sunrise-sunset-widget-design.md')
| -rw-r--r-- | docs/superpowers/specs/2026-03-23-sunrise-sunset-widget-design.md | 85 |
1 files changed, 57 insertions, 28 deletions
diff --git a/docs/superpowers/specs/2026-03-23-sunrise-sunset-widget-design.md b/docs/superpowers/specs/2026-03-23-sunrise-sunset-widget-design.md index 27f7dbd..1e4ed54 100644 --- a/docs/superpowers/specs/2026-03-23-sunrise-sunset-widget-design.md +++ b/docs/superpowers/specs/2026-03-23-sunrise-sunset-widget-design.md @@ -25,27 +25,36 @@ Extend `SolvervDef` to include sunrise/sunset times: SolvervDef ├── date: Date ├── bg: String -├── sunriseTime: Date? (optional, time only) -└── sunsetTime: Date? (optional, time only) +├── sunriseTime: Date? (optional) +├── sunsetTime: Date? (optional) +├── sunriseFormatted: String (computed) +└── sunsetFormatted: String (computed) ``` -Extend `SolvervEntry` to propagate the times: -``` -SolvervEntry -├── date: Date -├── def: SolvervDef (contains sunrise/sunset) +**Constructor signature:** +```swift +init(date: Date, sunriseTime: Date? = nil, sunsetTime: Date? = nil) { + self.date = date + self.sunriseTime = sunriseTime + self.sunsetTime = sunsetTime + self.bg = "smallbg" +} ``` +The formatted properties extract HH:mm in device local time. + ### Timeline Provider Logic Update `Provider.getTimeline()`: -1. Get user's cached location (latitude, longitude) -2. If location unavailable → create entry with nil sunrise/sunset -3. Initialize `SunTimes(latitude, longitude, date: currentDate)` -4. Extract sunrise and sunset times -5. Create entry with times -6. Refresh at next midnight +1. Get user's cached location from `AppGroupManager` (widget must use App Group storage, not LocationManager) +2. Check if location is recent (less than 24 hours old); if stale/unavailable → create entry with nil sunrise/sunset +3. Initialize local `SunTimes(latitude, longitude, date: currentDate)` calculator +4. Extract sunrise and sunset times (already converted to device local time) +5. Create entry with times: `SolvervEntry(def: SolvervDef(date: currentDate, sunriseTime: sr, sunsetTime: ss))` +6. Return timeline refreshing at next midnight + +**Note:** Widget runs in separate process and cannot access main app's LocationManager. Location MUST come from AppGroupManager storage, populated by the main app. ### Data Calculation Flow @@ -71,34 +80,54 @@ Widget Display | Scenario | Behavior | |----------|----------| | Location permission denied | No sunrise/sunset displayed | -| Location unavailable/stale | No sunrise/sunset displayed | -| SunTimes returns nil (polar) | No sunrise/sunset displayed | -| Date parsing fails | Widget shows current time's calculation | +| Location unavailable in AppGroupManager | No sunrise/sunset displayed | +| Location older than 24 hours | Treat as unavailable; no sunrise/sunset displayed | +| SunTimes returns nil (polar regions, 24h sun/darkness) | No sunrise/sunset displayed | + +### Timezone Handling + +- `SunTimes` internally converts to device's local timezone (uses `TimeZone.current`) +- Sunrise/sunset `Date` objects are in device local time +- Formatted display uses device local time (HH:mm) +- **Edge case:** If user changes timezone between midnight and next refresh, sun times remain valid for the previous timezone. They will update correctly at next midnight refresh (when new location may have different timezone). This is acceptable since widget only refreshes daily. ## Implementation Details ### Changes to SolvervDef -Add properties and computed properties for formatted times: -- `sunriseTime: Date?` — raw sunrise time -- `sunsetTime: Date?` — raw sunset time -- `sunriseFormatted: String` — "HH:mm" or empty -- `sunsetFormatted: String` — "HH:mm" or empty +Add properties: +- `sunriseTime: Date?` — raw sunrise time in device local timezone +- `sunsetTime: Date?` — raw sunset time in device local timezone + +Add computed properties for formatted times: +- `sunriseFormatted: String` — returns "HH:mm" format or empty string if nil +- `sunsetFormatted: String` — returns "HH:mm" format or empty string if nil + +Computed properties use device locale and 24-hour format. ### Changes to Provider Modify `getTimeline()` to: -1. Fetch location from cache (use existing location service) -2. If available, calculate sun times via `SunTimes` -3. Pass times to `SolvervDef` constructor -4. Return timeline refreshing at next midnight +1. Fetch location from `AppGroupManager.getUserLocation()` — widget runs in separate process and must use App Group storage +2. Check timestamp: if location is older than 24 hours, treat as unavailable +3. If available, instantiate sunrise/sunset calculator: `SunTimes(latitude: lat, longitude: lon, date: currentDate)` from `/Solverv/Utilities/SunTimes.swift` +4. Call `calculator.sunrise()` and `calculator.sunset()` to get Date objects +5. Pass times to `SolvervDef(date: currentDate, sunriseTime: sr, sunsetTime: ss)` +6. Create entry and return timeline refreshing at next midnight (unchanged) ### Widget View Updates -**SmallWidgetView:** Add sunrise/sunset display (layout TBD) -**MediumWidgetView:** Add sunrise/sunset display (layout TBD) +**SmallWidgetView:** +- If sunrise/sunset available: display as "↑ HH:mm ↓ HH:mm" or similar compact format +- If unavailable: display nothing (no placeholder or empty space) + +**MediumWidgetView:** +- If sunrise/sunset available: display sunrise and sunset times in HH:mm format (exact layout TBD based on available space) +- If unavailable: display nothing (no placeholder or empty space) -Display format: sunrise time and sunset time as HH:mm when available. +Both views: +- Access times via `entry.def.sunriseFormatted` and `entry.def.sunsetFormatted` +- Only render if both are non-empty strings ## Timeline & Refresh |
