Fix residence auto-update, widget theming, and document patterns

- Fix residence detail not updating after edit:
  - DataManager.updateResidence() now updates both _residences and _myResidences
  - ResidenceViewModel auto-updates selectedResidence when data changes
  - No pull-to-refresh needed after editing

- Add widget theme support:
  - Widgets now use user's selected theme via App Group UserDefaults
  - ThemeManager has simplified version for widget extension context
  - Added WIDGET_EXTENSION compiler flag to CaseraExtension target

- Redesign widget views with organic aesthetic:
  - Updated FreeWidgetView, SmallWidgetView, MediumWidgetView, LargeWidgetView
  - Created OrganicTaskRowView, OrganicStatsView, OrganicStatPillWidget

- Document patterns in CLAUDE.md:
  - Added Mutation & Auto-Update Pattern section
  - Added iOS Shared Components documentation
  - Documented reusable buttons, forms, empty states, cards, modifiers

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Trey t
2025-12-17 22:58:55 -06:00
parent 7d76393e40
commit b39d37a6e8
8 changed files with 719 additions and 254 deletions
+32 -3
View File
@@ -1,4 +1,7 @@
import SwiftUI
#if !WIDGET_EXTENSION
import Combine
#endif
// MARK: - Theme ID Enum
enum ThemeID: String, CaseIterable, Codable {
@@ -56,7 +59,31 @@ enum ThemeID: String, CaseIterable, Codable {
}
}
// MARK: - Shared App Group UserDefaults
private let appGroupID = "group.com.tt.casera.CaseraDev"
private let sharedDefaults = UserDefaults(suiteName: appGroupID) ?? UserDefaults.standard
// MARK: - Theme Manager
#if WIDGET_EXTENSION
// Simplified ThemeManager for widget extensions (no ObservableObject needed)
class ThemeManager {
static let shared = ThemeManager()
var currentTheme: ThemeID {
// Load saved theme from shared App Group defaults
if let savedThemeRawValue = sharedDefaults.string(forKey: themeKey),
let savedTheme = ThemeID(rawValue: savedThemeRawValue) {
return savedTheme
}
return .bright
}
private let themeKey = "selectedTheme"
private init() {}
}
#else
// Full ThemeManager for main app with ObservableObject support
class ThemeManager: ObservableObject {
static let shared = ThemeManager()
@@ -69,8 +96,8 @@ class ThemeManager: ObservableObject {
private let themeKey = "selectedTheme"
private init() {
// Load saved theme or default to .bright
if let savedThemeRawValue = UserDefaults.standard.string(forKey: themeKey),
// Load saved theme from shared App Group defaults
if let savedThemeRawValue = sharedDefaults.string(forKey: themeKey),
let savedTheme = ThemeID(rawValue: savedThemeRawValue) {
self.currentTheme = savedTheme
} else {
@@ -79,7 +106,8 @@ class ThemeManager: ObservableObject {
}
private func saveTheme() {
UserDefaults.standard.set(currentTheme.rawValue, forKey: themeKey)
// Save to shared App Group defaults so widgets can access it
sharedDefaults.set(currentTheme.rawValue, forKey: themeKey)
}
func setTheme(_ theme: ThemeID) {
@@ -88,3 +116,4 @@ class ThemeManager: ObservableObject {
}
}
}
#endif