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:
@@ -20,13 +20,9 @@ struct DayView: View {
|
||||
|
||||
@AppStorage(UserDefaultsStore.Keys.moodImages.rawValue, store: GroupUserDefaults.groupDefaults) private var imagePack: MoodImages = .FontAwesome
|
||||
@AppStorage(UserDefaultsStore.Keys.moodTint.rawValue, store: GroupUserDefaults.groupDefaults) private var moodTint: MoodTints = .Default
|
||||
@AppStorage(UserDefaultsStore.Keys.textColor.rawValue, store: GroupUserDefaults.groupDefaults) private var textColor: Color = DefaultTextColor.textColor
|
||||
@AppStorage(UserDefaultsStore.Keys.dayViewStyle.rawValue, store: GroupUserDefaults.groupDefaults) private var dayViewStyle: DayViewStyle = .classic
|
||||
@Environment(\.colorScheme) private var colorScheme
|
||||
|
||||
// store a value that gets changed when user updates custom colors to update the view since the moodTint doesn't change
|
||||
@AppStorage(UserDefaultsStore.Keys.customMoodTintUpdateNumber.rawValue, store: GroupUserDefaults.groupDefaults) private var customMoodTintUpdateNumber: Int = 0
|
||||
|
||||
// MARK: edit row properties
|
||||
@State private var showingSheet = false
|
||||
@State private var selectedEntry: MoodEntryModel?
|
||||
@@ -42,9 +38,6 @@ struct DayView: View {
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
Text(String(customMoodTintUpdateNumber))
|
||||
.hidden()
|
||||
|
||||
mainView
|
||||
.onAppear(perform: {
|
||||
EventLogger.log(event: "show_home_view")
|
||||
@@ -65,6 +58,7 @@ struct DayView: View {
|
||||
}
|
||||
}
|
||||
.padding([.top])
|
||||
.preferredColorScheme(theme.preferredColorScheme)
|
||||
}
|
||||
|
||||
|
||||
@@ -83,7 +77,6 @@ struct DayView: View {
|
||||
}
|
||||
.padding([.leading, .trailing])
|
||||
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in
|
||||
DataController.shared.fillInMissingDates()
|
||||
viewModel.updateData()
|
||||
}
|
||||
.background(
|
||||
@@ -102,19 +95,20 @@ struct DayView: View {
|
||||
}
|
||||
}
|
||||
|
||||
/// Cached sorted year/month data to avoid sorting dictionaries in ForEach
|
||||
private var sortedGroupedData: [(year: Int, months: [(month: Int, entries: [MoodEntryModel])])] {
|
||||
viewModel.grouped
|
||||
.sorted { $0.key > $1.key }
|
||||
.map { (year: $0.key, months: $0.value.sorted { $0.key > $1.key }.map { (month: $0.key, entries: $0.value) }) }
|
||||
}
|
||||
|
||||
private var listView: some View {
|
||||
ScrollView {
|
||||
LazyVStack(spacing: 8, pinnedViews: [.sectionHeaders]) {
|
||||
ForEach(viewModel.grouped.sorted(by: {
|
||||
$0.key > $1.key
|
||||
}), id: \.key) { year, months in
|
||||
|
||||
// for reach month
|
||||
ForEach(months.sorted(by: {
|
||||
$0.key > $1.key
|
||||
}), id: \.key) { month, entries in
|
||||
Section(header: SectionHeaderView(month: month, year: year, entries: entries)) {
|
||||
monthListView(month: month, year: year, entries: entries)
|
||||
ForEach(sortedGroupedData, id: \.year) { yearData in
|
||||
ForEach(yearData.months, id: \.month) { monthData in
|
||||
Section(header: SectionHeaderView(month: monthData.month, year: yearData.year, entries: monthData.entries)) {
|
||||
monthListView(month: monthData.month, year: yearData.year, entries: monthData.entries)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -172,12 +166,12 @@ extension DayView {
|
||||
// Calendar icon
|
||||
Image(systemName: "calendar")
|
||||
.font(.body.weight(.semibold))
|
||||
.foregroundColor(textColor.opacity(0.6))
|
||||
.foregroundColor(theme.currentTheme.labelColor.opacity(0.6))
|
||||
.accessibilityHidden(true)
|
||||
|
||||
Text("\(Random.monthName(fromMonthInt: month)) \(String(year))")
|
||||
.font(.title3.weight(.bold))
|
||||
.foregroundColor(textColor)
|
||||
.foregroundColor(theme.currentTheme.labelColor)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
@@ -196,7 +190,7 @@ extension DayView {
|
||||
.font(.largeTitle.weight(.black))
|
||||
.foregroundStyle(
|
||||
LinearGradient(
|
||||
colors: [textColor, textColor.opacity(0.4)],
|
||||
colors: [theme.currentTheme.labelColor, theme.currentTheme.labelColor.opacity(0.4)],
|
||||
startPoint: .top,
|
||||
endPoint: .bottom
|
||||
)
|
||||
@@ -207,18 +201,18 @@ extension DayView {
|
||||
Text(Random.monthName(fromMonthInt: month).uppercased())
|
||||
.font(.subheadline.weight(.bold))
|
||||
.tracking(3)
|
||||
.foregroundColor(textColor)
|
||||
.foregroundColor(theme.currentTheme.labelColor)
|
||||
|
||||
Text(String(year))
|
||||
.font(.caption.weight(.medium))
|
||||
.foregroundColor(textColor.opacity(0.5))
|
||||
.foregroundColor(theme.currentTheme.labelColor.opacity(0.5))
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
// Decorative element
|
||||
Circle()
|
||||
.fill(textColor.opacity(0.1))
|
||||
.fill(theme.currentTheme.labelColor.opacity(0.1))
|
||||
.frame(width: 8, height: 8)
|
||||
}
|
||||
.padding(.horizontal, 20)
|
||||
@@ -232,7 +226,7 @@ extension DayView {
|
||||
// Subtle gradient accent
|
||||
LinearGradient(
|
||||
colors: [
|
||||
textColor.opacity(0.03),
|
||||
theme.currentTheme.labelColor.opacity(0.03),
|
||||
Color.clear
|
||||
],
|
||||
startPoint: .leading,
|
||||
@@ -246,34 +240,34 @@ extension DayView {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
// Thick editorial rule
|
||||
Rectangle()
|
||||
.fill(textColor)
|
||||
.fill(theme.currentTheme.labelColor)
|
||||
.frame(height: 4)
|
||||
|
||||
HStack(alignment: .firstTextBaseline, spacing: 12) {
|
||||
// Large serif month name
|
||||
Text(Random.monthName(fromMonthInt: month).uppercased())
|
||||
.font(.title.weight(.regular))
|
||||
.foregroundColor(textColor)
|
||||
.foregroundColor(theme.currentTheme.labelColor)
|
||||
|
||||
// Year in lighter weight
|
||||
Text(String(year))
|
||||
.font(.body.weight(.light))
|
||||
.italic()
|
||||
.foregroundColor(textColor.opacity(0.5))
|
||||
.foregroundColor(theme.currentTheme.labelColor.opacity(0.5))
|
||||
|
||||
Spacer()
|
||||
|
||||
// Decorative flourish
|
||||
Text("§")
|
||||
.font(.title3.weight(.regular))
|
||||
.foregroundColor(textColor.opacity(0.3))
|
||||
.foregroundColor(theme.currentTheme.labelColor.opacity(0.3))
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 16)
|
||||
|
||||
// Thin bottom rule
|
||||
Rectangle()
|
||||
.fill(textColor.opacity(0.2))
|
||||
.fill(theme.currentTheme.labelColor.opacity(0.2))
|
||||
.frame(height: 1)
|
||||
}
|
||||
.background(.ultraThinMaterial)
|
||||
@@ -306,13 +300,14 @@ extension DayView {
|
||||
ZStack {
|
||||
Color.black
|
||||
|
||||
// Scanlines
|
||||
VStack(spacing: 3) {
|
||||
ForEach(0..<8, id: \.self) { _ in
|
||||
Rectangle()
|
||||
.fill(Color.white.opacity(0.02))
|
||||
.frame(height: 1)
|
||||
Spacer().frame(height: 3)
|
||||
// Scanlines - simplified with Canvas for performance
|
||||
Canvas { context, size in
|
||||
let lineHeight: CGFloat = 4
|
||||
var y: CGFloat = 0
|
||||
while y < size.height {
|
||||
let rect = CGRect(x: 0, y: y, width: size.width, height: 1)
|
||||
context.fill(Path(rect), with: .color(.white.opacity(0.02)))
|
||||
y += lineHeight
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -323,18 +318,18 @@ extension DayView {
|
||||
HStack(alignment: .center, spacing: 16) {
|
||||
// Brush stroke accent
|
||||
Capsule()
|
||||
.fill(textColor.opacity(0.15))
|
||||
.fill(theme.currentTheme.labelColor.opacity(0.15))
|
||||
.frame(width: 40, height: 3)
|
||||
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(Random.monthName(fromMonthInt: month))
|
||||
.font(.headline.weight(.thin))
|
||||
.tracking(4)
|
||||
.foregroundColor(textColor)
|
||||
.foregroundColor(theme.currentTheme.labelColor)
|
||||
|
||||
Text(String(year))
|
||||
.font(.caption2.weight(.ultraLight))
|
||||
.foregroundColor(textColor.opacity(0.4))
|
||||
.foregroundColor(theme.currentTheme.labelColor.opacity(0.4))
|
||||
}
|
||||
|
||||
Spacer()
|
||||
@@ -342,7 +337,7 @@ extension DayView {
|
||||
// Zen circle ornament
|
||||
Circle()
|
||||
.trim(from: 0, to: 0.7)
|
||||
.stroke(textColor.opacity(0.2), style: StrokeStyle(lineWidth: 2, lineCap: .round))
|
||||
.stroke(theme.currentTheme.labelColor.opacity(0.2), style: StrokeStyle(lineWidth: 2, lineCap: .round))
|
||||
.frame(width: 20, height: 20)
|
||||
.rotationEffect(.degrees(-60))
|
||||
}
|
||||
@@ -371,15 +366,15 @@ extension DayView {
|
||||
HStack(spacing: 12) {
|
||||
Text(Random.monthName(fromMonthInt: month))
|
||||
.font(.title3.weight(.semibold))
|
||||
.foregroundColor(textColor)
|
||||
.foregroundColor(theme.currentTheme.labelColor)
|
||||
|
||||
Capsule()
|
||||
.fill(textColor.opacity(0.2))
|
||||
.fill(theme.currentTheme.labelColor.opacity(0.2))
|
||||
.frame(width: 4, height: 4)
|
||||
|
||||
Text(String(year))
|
||||
.font(.body.weight(.medium))
|
||||
.foregroundColor(textColor.opacity(0.6))
|
||||
.foregroundColor(theme.currentTheme.labelColor.opacity(0.6))
|
||||
|
||||
Spacer()
|
||||
}
|
||||
@@ -395,21 +390,21 @@ extension DayView {
|
||||
// Tape reel icon
|
||||
ZStack {
|
||||
Circle()
|
||||
.stroke(textColor.opacity(0.3), lineWidth: 2)
|
||||
.stroke(theme.currentTheme.labelColor.opacity(0.3), lineWidth: 2)
|
||||
.frame(width: 24, height: 24)
|
||||
Circle()
|
||||
.fill(textColor.opacity(0.2))
|
||||
.fill(theme.currentTheme.labelColor.opacity(0.2))
|
||||
.frame(width: 10, height: 10)
|
||||
}
|
||||
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text("SIDE A")
|
||||
.font(.caption2.weight(.bold).monospaced())
|
||||
.foregroundColor(textColor.opacity(0.4))
|
||||
.foregroundColor(theme.currentTheme.labelColor.opacity(0.4))
|
||||
|
||||
Text("\(Random.monthName(fromMonthInt: month).uppercased()) '\(String(year).suffix(2))")
|
||||
.font(.body.weight(.black))
|
||||
.foregroundColor(textColor)
|
||||
.foregroundColor(theme.currentTheme.labelColor)
|
||||
.tracking(1)
|
||||
}
|
||||
|
||||
@@ -418,7 +413,7 @@ extension DayView {
|
||||
// Track counter
|
||||
Text(String(format: "%02d", month))
|
||||
.font(.title3.weight(.bold).monospaced())
|
||||
.foregroundColor(textColor.opacity(0.3))
|
||||
.foregroundColor(theme.currentTheme.labelColor.opacity(0.3))
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 14)
|
||||
@@ -433,7 +428,7 @@ extension DayView {
|
||||
// Organic blob background
|
||||
HStack {
|
||||
Ellipse()
|
||||
.fill(textColor.opacity(0.08))
|
||||
.fill(theme.currentTheme.labelColor.opacity(0.08))
|
||||
.frame(width: 120, height: 60)
|
||||
.blur(radius: 15)
|
||||
.offset(x: -20)
|
||||
@@ -443,17 +438,17 @@ extension DayView {
|
||||
HStack(spacing: 16) {
|
||||
Text(Random.monthName(fromMonthInt: month))
|
||||
.font(.title2.weight(.light))
|
||||
.foregroundColor(textColor)
|
||||
.foregroundColor(theme.currentTheme.labelColor)
|
||||
|
||||
Text(String(year))
|
||||
.font(.subheadline.weight(.regular))
|
||||
.foregroundColor(textColor.opacity(0.4))
|
||||
.foregroundColor(theme.currentTheme.labelColor.opacity(0.4))
|
||||
|
||||
Spacer()
|
||||
|
||||
// Blob indicator
|
||||
Circle()
|
||||
.fill(textColor.opacity(0.15))
|
||||
.fill(theme.currentTheme.labelColor.opacity(0.15))
|
||||
.frame(width: 12, height: 12)
|
||||
.blur(radius: 2)
|
||||
}
|
||||
@@ -465,12 +460,13 @@ extension DayView {
|
||||
|
||||
private func stackSectionHeader(month: Int, year: Int) -> some View {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
// Torn edge
|
||||
HStack(spacing: 0) {
|
||||
ForEach(0..<30, id: \.self) { _ in
|
||||
Rectangle()
|
||||
.fill(textColor.opacity(0.2))
|
||||
.frame(height: CGFloat.random(in: 2...4))
|
||||
// Torn edge - simplified with Canvas for performance
|
||||
Canvas { context, size in
|
||||
let segmentWidth = size.width / 15
|
||||
for i in 0..<15 {
|
||||
let height = CGFloat(2 + (i * 5) % 3) // Deterministic pseudo-random heights
|
||||
let rect = CGRect(x: CGFloat(i) * segmentWidth, y: 0, width: segmentWidth, height: height)
|
||||
context.fill(Path(rect), with: .color(Color(uiColor: UIColor.label).opacity(0.2)))
|
||||
}
|
||||
}
|
||||
.frame(height: 4)
|
||||
@@ -483,12 +479,12 @@ extension DayView {
|
||||
|
||||
Text(Random.monthName(fromMonthInt: month))
|
||||
.font(.headline.weight(.regular))
|
||||
.foregroundColor(textColor)
|
||||
.foregroundColor(theme.currentTheme.labelColor)
|
||||
|
||||
Text(String(year))
|
||||
.font(.subheadline.weight(.light))
|
||||
.italic()
|
||||
.foregroundColor(textColor.opacity(0.5))
|
||||
.foregroundColor(theme.currentTheme.labelColor.opacity(0.5))
|
||||
|
||||
Spacer()
|
||||
}
|
||||
@@ -516,7 +512,7 @@ extension DayView {
|
||||
}
|
||||
}()
|
||||
|
||||
let barColor = hasData ? moodTint.color(forMood: moodForColor) : textColor.opacity(0.2)
|
||||
let barColor = hasData ? moodTint.color(forMood: moodForColor) : theme.currentTheme.labelColor.opacity(0.2)
|
||||
// Width percentage based on average (0=20%, 4=100%)
|
||||
let widthPercent = hasData ? 0.2 + (averageMood / 4.0) * 0.8 : 0.2
|
||||
|
||||
@@ -524,7 +520,7 @@ extension DayView {
|
||||
// Month number
|
||||
Text(String(format: "%02d", month))
|
||||
.font(.title.weight(.thin))
|
||||
.foregroundColor(hasData ? barColor.opacity(0.6) : textColor.opacity(0.3))
|
||||
.foregroundColor(hasData ? barColor.opacity(0.6) : theme.currentTheme.labelColor.opacity(0.3))
|
||||
.frame(width: 50)
|
||||
|
||||
// Gradient bar sized by average mood
|
||||
@@ -532,7 +528,7 @@ extension DayView {
|
||||
ZStack(alignment: .leading) {
|
||||
// Background track
|
||||
Capsule()
|
||||
.fill(textColor.opacity(0.1))
|
||||
.fill(theme.currentTheme.labelColor.opacity(0.1))
|
||||
.frame(height: 8)
|
||||
|
||||
// Colored bar based on average
|
||||
@@ -554,7 +550,7 @@ extension DayView {
|
||||
VStack(alignment: .trailing, spacing: 2) {
|
||||
Text(Random.monthName(fromMonthInt: month))
|
||||
.font(.subheadline.weight(.medium))
|
||||
.foregroundColor(textColor)
|
||||
.foregroundColor(theme.currentTheme.labelColor)
|
||||
|
||||
if hasData {
|
||||
Text(String(format: "%.1f avg", averageMood + 1)) // Display as 1-5
|
||||
@@ -563,7 +559,7 @@ extension DayView {
|
||||
} else {
|
||||
Text(String(year))
|
||||
.font(.caption2.weight(.regular))
|
||||
.foregroundColor(textColor.opacity(0.4))
|
||||
.foregroundColor(theme.currentTheme.labelColor.opacity(0.4))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -576,11 +572,11 @@ extension DayView {
|
||||
HStack(spacing: 10) {
|
||||
Image(systemName: "calendar")
|
||||
.font(.body.weight(.semibold))
|
||||
.foregroundColor(textColor.opacity(0.6))
|
||||
.foregroundColor(theme.currentTheme.labelColor.opacity(0.6))
|
||||
|
||||
Text("\(Random.monthName(fromMonthInt: month)) \(String(year))")
|
||||
.font(.title3.weight(.bold))
|
||||
.foregroundColor(textColor)
|
||||
.foregroundColor(theme.currentTheme.labelColor)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
@@ -726,17 +722,17 @@ extension DayView {
|
||||
|
||||
Text(String(format: "%02d", month))
|
||||
.font(.subheadline.weight(.semibold))
|
||||
.foregroundColor(textColor)
|
||||
.foregroundColor(theme.currentTheme.labelColor)
|
||||
}
|
||||
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(Random.monthName(fromMonthInt: month))
|
||||
.font(.headline.weight(.medium))
|
||||
.foregroundColor(textColor)
|
||||
.foregroundColor(theme.currentTheme.labelColor)
|
||||
|
||||
Text(String(year))
|
||||
.font(.caption.weight(.regular))
|
||||
.foregroundColor(textColor.opacity(0.5))
|
||||
.foregroundColor(theme.currentTheme.labelColor.opacity(0.5))
|
||||
}
|
||||
|
||||
Spacer()
|
||||
@@ -818,11 +814,11 @@ extension DayView {
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(Random.monthName(fromMonthInt: month))
|
||||
.font(.title3.weight(.semibold))
|
||||
.foregroundColor(textColor)
|
||||
.foregroundColor(theme.currentTheme.labelColor)
|
||||
|
||||
Text(String(year))
|
||||
.font(.caption.weight(.medium))
|
||||
.foregroundColor(textColor.opacity(0.5))
|
||||
.foregroundColor(theme.currentTheme.labelColor.opacity(0.5))
|
||||
}
|
||||
|
||||
Spacer()
|
||||
@@ -830,7 +826,7 @@ extension DayView {
|
||||
// Tilt indicator
|
||||
Image(systemName: "iphone.gen3.radiowaves.left.and.right")
|
||||
.font(.headline)
|
||||
.foregroundColor(textColor.opacity(0.3))
|
||||
.foregroundColor(theme.currentTheme.labelColor.opacity(0.3))
|
||||
}
|
||||
.padding(.horizontal, 18)
|
||||
}
|
||||
@@ -847,24 +843,24 @@ extension DayView {
|
||||
HStack(spacing: 8) {
|
||||
// Minimal colored bar
|
||||
RoundedRectangle(cornerRadius: 2)
|
||||
.fill(textColor.opacity(0.3))
|
||||
.fill(theme.currentTheme.labelColor.opacity(0.3))
|
||||
.frame(width: 3, height: 16)
|
||||
|
||||
Text("\(Random.monthName(fromMonthInt: month).prefix(3).uppercased())")
|
||||
.font(.caption2.weight(.bold).monospaced())
|
||||
.foregroundColor(textColor.opacity(0.6))
|
||||
.foregroundColor(theme.currentTheme.labelColor.opacity(0.6))
|
||||
|
||||
Text("•")
|
||||
.font(.caption2)
|
||||
.foregroundColor(textColor.opacity(0.3))
|
||||
.foregroundColor(theme.currentTheme.labelColor.opacity(0.3))
|
||||
|
||||
Text(String(year))
|
||||
.font(.caption2.weight(.medium).monospaced())
|
||||
.foregroundColor(textColor.opacity(0.4))
|
||||
.foregroundColor(theme.currentTheme.labelColor.opacity(0.4))
|
||||
|
||||
// Thin separator line
|
||||
Rectangle()
|
||||
.fill(textColor.opacity(0.1))
|
||||
.fill(theme.currentTheme.labelColor.opacity(0.1))
|
||||
.frame(height: 1)
|
||||
}
|
||||
.padding(.horizontal, 14)
|
||||
|
||||
@@ -37,9 +37,8 @@ class DayViewViewModel: ObservableObject {
|
||||
|
||||
DataController.shared.addNewDataListener { [weak self] in
|
||||
guard let self = self else { return }
|
||||
withAnimation {
|
||||
self.updateData()
|
||||
}
|
||||
// Avoid withAnimation for bulk data updates - it causes expensive view diffing
|
||||
self.updateData()
|
||||
}
|
||||
updateData()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user