Update Neon colors and show color circles in theme picker

- Update NeonMoodTint to use synthwave colors matching Neon voting style
  (cyan, lime, yellow, orange, magenta)
- Replace text label with 5 color circles in theme preview Colors row
- Remove unused textColor customization code and picker views
- Add .id(moodTint) to Month/Year views for color refresh
- Clean up various unused color-related code

🤖 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-30 00:08:01 -06:00
parent 51c5777c03
commit bea2d3bbc9
58 changed files with 1142 additions and 967 deletions

View File

@@ -175,76 +175,80 @@ class LiveActivityScheduler: ObservableObject {
return calendar.date(byAdding: .hour, value: 5, to: ratingDateTime)
}
/// Check if user has rated today
func hasRatedToday() -> Bool {
/// Cached streak data to avoid redundant calculations
private var cachedStreakData: (streak: Int, todaysMood: Mood?, votingDate: Date)?
/// Get streak data using efficient batch query (cached per voting date)
private func getStreakData() -> (streak: Int, todaysMood: Mood?) {
let votingDate = ShowBasedOnVoteLogics.getCurrentVotingDate(onboardingData: UserDefaultsStore.getOnboarding())
let dayStart = Calendar.current.startOfDay(for: votingDate)
let dayEnd = Calendar.current.date(bySettingHour: 23, minute: 59, second: 59, of: dayStart)!
// Return cached data if still valid for current voting date
if let cached = cachedStreakData,
Calendar.current.isDate(cached.votingDate, inSameDayAs: votingDate) {
return (cached.streak, cached.todaysMood)
}
// Calculate and cache
#if WIDGET_EXTENSION
let entry = WidgetDataProvider.shared.getData(startDate: dayStart, endDate: dayEnd, includedDays: []).first
#else
let entry = DataController.shared.getData(startDate: dayStart, endDate: dayEnd, includedDays: []).first
#endif
return entry != nil && entry?.mood != .missing && entry?.mood != .placeholder
}
// Widget extension uses its own data provider
let calendar = Calendar.current
let dayStart = calendar.startOfDay(for: votingDate)
guard let yearAgo = calendar.date(byAdding: .day, value: -365, to: dayStart) else {
return (0, nil)
}
let entries = WidgetDataProvider.shared.getData(startDate: yearAgo, endDate: votingDate, includedDays: [])
.filter { $0.mood != .missing && $0.mood != .placeholder }
guard !entries.isEmpty else { return (0, nil) }
let datesWithEntries = Set(entries.map { calendar.startOfDay(for: $0.forDate) })
let todaysEntry = entries.first { calendar.isDate($0.forDate, inSameDayAs: votingDate) }
let todaysMood = todaysEntry?.mood
/// Calculate current streak
func calculateStreak() -> Int {
var streak = 0
var checkDate = ShowBasedOnVoteLogics.getCurrentVotingDate(onboardingData: UserDefaultsStore.getOnboarding())
// Check if current voting date has an entry
let currentDayStart = Calendar.current.startOfDay(for: checkDate)
let currentDayEnd = Calendar.current.date(bySettingHour: 23, minute: 59, second: 59, of: currentDayStart)!
#if WIDGET_EXTENSION
let currentEntry = WidgetDataProvider.shared.getData(startDate: currentDayStart, endDate: currentDayEnd, includedDays: []).first
#else
let currentEntry = DataController.shared.getData(startDate: currentDayStart, endDate: currentDayEnd, includedDays: []).first
#endif
// If no entry for current voting date, start counting from previous day
// This ensures the streak shows correctly even if user hasn't rated today yet
if currentEntry == nil || currentEntry?.mood == .missing || currentEntry?.mood == .placeholder {
checkDate = Calendar.current.date(byAdding: .day, value: -1, to: checkDate)!
var checkDate = votingDate
if !datesWithEntries.contains(dayStart) {
checkDate = calendar.date(byAdding: .day, value: -1, to: checkDate) ?? checkDate
}
while true {
let dayStart = Calendar.current.startOfDay(for: checkDate)
let dayEnd = Calendar.current.date(bySettingHour: 23, minute: 59, second: 59, of: dayStart)!
#if WIDGET_EXTENSION
let entry = WidgetDataProvider.shared.getData(startDate: dayStart, endDate: dayEnd, includedDays: []).first
#else
let entry = DataController.shared.getData(startDate: dayStart, endDate: dayEnd, includedDays: []).first
#endif
if let entry = entry, entry.mood != .missing && entry.mood != .placeholder {
let checkDayStart = calendar.startOfDay(for: checkDate)
if datesWithEntries.contains(checkDayStart) {
streak += 1
checkDate = Calendar.current.date(byAdding: .day, value: -1, to: checkDate)!
checkDate = calendar.date(byAdding: .day, value: -1, to: checkDate) ?? checkDate
} else {
break
}
}
return streak
cachedStreakData = (streak, todaysMood, votingDate)
return (streak, todaysMood)
#else
let result = DataController.shared.calculateStreak(from: votingDate)
cachedStreakData = (result.streak, result.todaysMood, votingDate)
return result
#endif
}
/// Check if user has rated today
func hasRatedToday() -> Bool {
return getStreakData().todaysMood != nil
}
/// Calculate current streak
func calculateStreak() -> Int {
return getStreakData().streak
}
/// Get today's mood if logged
func getTodaysMood() -> Mood? {
let votingDate = ShowBasedOnVoteLogics.getCurrentVotingDate(onboardingData: UserDefaultsStore.getOnboarding())
let dayStart = Calendar.current.startOfDay(for: votingDate)
let dayEnd = Calendar.current.date(bySettingHour: 23, minute: 59, second: 59, of: dayStart)!
return getStreakData().todaysMood
}
#if WIDGET_EXTENSION
let entry = WidgetDataProvider.shared.getData(startDate: dayStart, endDate: dayEnd, includedDays: []).first
#else
let entry = DataController.shared.getData(startDate: dayStart, endDate: dayEnd, includedDays: []).first
#endif
if let entry = entry, entry.mood != .missing && entry.mood != .placeholder {
return entry.mood
}
return nil
/// Invalidate cached streak data (call when mood is logged)
func invalidateCache() {
cachedStreakData = nil
}
/// Schedule Live Activity based on current time and rating time