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:
@@ -39,45 +39,77 @@ extension DataController {
|
||||
return (try? modelContext.fetch(descriptor)) ?? []
|
||||
}
|
||||
|
||||
/// Calculate the current mood streak efficiently using a single batch query.
|
||||
/// Returns a tuple of (streak count, last logged mood if today has entry).
|
||||
func calculateStreak(from votingDate: Date) -> (streak: Int, todaysMood: Mood?) {
|
||||
let calendar = Calendar.current
|
||||
let dayStart = calendar.startOfDay(for: votingDate)
|
||||
|
||||
// Fetch last 365 days of entries in a single query
|
||||
guard let yearAgo = calendar.date(byAdding: .day, value: -365, to: dayStart) else {
|
||||
return (0, nil)
|
||||
}
|
||||
|
||||
let entries = getData(startDate: yearAgo, endDate: votingDate, includedDays: [])
|
||||
.filter { $0.mood != .missing && $0.mood != .placeholder }
|
||||
|
||||
guard !entries.isEmpty else { return (0, nil) }
|
||||
|
||||
// Build a Set of dates that have valid entries for O(1) lookup
|
||||
let datesWithEntries = Set(entries.map { calendar.startOfDay(for: $0.forDate) })
|
||||
|
||||
// Check for today's entry
|
||||
let todaysEntry = entries.first { calendar.isDate($0.forDate, inSameDayAs: votingDate) }
|
||||
let todaysMood = todaysEntry?.mood
|
||||
|
||||
// Calculate streak walking backwards
|
||||
var streak = 0
|
||||
var checkDate = votingDate
|
||||
|
||||
// If no entry for current voting date, start counting from previous day
|
||||
if !datesWithEntries.contains(dayStart) {
|
||||
checkDate = calendar.date(byAdding: .day, value: -1, to: checkDate) ?? checkDate
|
||||
}
|
||||
|
||||
while true {
|
||||
let checkDayStart = calendar.startOfDay(for: checkDate)
|
||||
if datesWithEntries.contains(checkDayStart) {
|
||||
streak += 1
|
||||
checkDate = calendar.date(byAdding: .day, value: -1, to: checkDate) ?? checkDate
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return (streak, todaysMood)
|
||||
}
|
||||
|
||||
func splitIntoYearMonth(includedDays: [Int]) -> [Int: [Int: [MoodEntryModel]]] {
|
||||
// Single query to fetch all data - avoid N*12 queries
|
||||
let data = getData(
|
||||
startDate: Date(timeIntervalSince1970: 0),
|
||||
endDate: Date(),
|
||||
includedDays: includedDays
|
||||
).sorted { $0.forDate < $1.forDate }
|
||||
)
|
||||
|
||||
guard let earliest = data.first,
|
||||
let latest = data.last else { return [:] }
|
||||
guard !data.isEmpty else { return [:] }
|
||||
|
||||
let calendar = Calendar.current
|
||||
let earliestYear = calendar.component(.year, from: earliest.forDate)
|
||||
let latestYear = calendar.component(.year, from: latest.forDate)
|
||||
|
||||
// Group entries by year and month in memory (single pass)
|
||||
var result = [Int: [Int: [MoodEntryModel]]]()
|
||||
|
||||
for year in earliestYear...latestYear {
|
||||
var monthData = [Int: [MoodEntryModel]]()
|
||||
for entry in data {
|
||||
let year = calendar.component(.year, from: entry.forDate)
|
||||
let month = calendar.component(.month, from: entry.forDate)
|
||||
|
||||
for month in 1...12 {
|
||||
var components = DateComponents()
|
||||
components.year = year
|
||||
components.month = month
|
||||
components.day = 1
|
||||
|
||||
guard let startOfMonth = calendar.date(from: components) else { continue }
|
||||
|
||||
let items = getData(
|
||||
startDate: startOfMonth,
|
||||
endDate: startOfMonth.endOfMonth,
|
||||
includedDays: [1, 2, 3, 4, 5, 6, 7]
|
||||
)
|
||||
|
||||
if !items.isEmpty {
|
||||
monthData[month] = items
|
||||
}
|
||||
if result[year] == nil {
|
||||
result[year] = [:]
|
||||
}
|
||||
|
||||
result[year] = monthData
|
||||
if result[year]![month] == nil {
|
||||
result[year]![month] = []
|
||||
}
|
||||
result[year]![month]!.append(entry)
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
Reference in New Issue
Block a user