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

@@ -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)

View File

@@ -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()
}