Calm 2.0 Fase 3: Home iPad columna única, Goals con anillos, Journal timeline, Sources polish (build 53)
Home iPad/Mac (feedback del usuario: 'me gusta más el iPhone'): - Fuera el grid de 2 columnas (acoplaba alturas de fila y dejaba huecos entre cards de tamaños distintos) → columna única centrada a 720pt, patrón Things/Day One. Eliminado el drag-drop del grid, columnSpan y el overlay full-screen (~120 líneas); el orden/visibilidad sigue en Customize. Goals: - ProgressRing nuevo (anillo estilo Fitness con porcentaje centrado y color semántico: verde=logrado, ámbar=retrasado, acento=en camino) en GoalRowView y en la card de Goals del Home. GoalProgressBar queda solo en la share card. Journal: - Momentum & Streaks vive ahora en la cabecera del Journal (la historia del hábito es narrativa, no dashboard). - Filas timeline: círculo de mood como ancla visual (tint semántico por mood), mes + estrellas + preview de nota. Sources: - Badge 'Needs update' como chip ámbar visible; valor en tipografía rounded. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WoScpmHdVj1aUf4rAp6hbe
This commit is contained in:
@@ -63,6 +63,17 @@ struct JournalView: View {
|
||||
AppBackground()
|
||||
|
||||
List {
|
||||
// Calm 2.0: the habit story (streaks, achievements) lives in
|
||||
// the Journal — this is the emotional home of the ritual.
|
||||
if !viewModel.monthlyNotes.isEmpty && searchText.isEmpty {
|
||||
Section {
|
||||
MomentumStreaksCard()
|
||||
}
|
||||
.listRowInsets(EdgeInsets())
|
||||
.listRowBackground(Color.clear)
|
||||
.listSectionSeparator(.hidden)
|
||||
}
|
||||
|
||||
Section("Monthly Check-ins") {
|
||||
if filteredMonthlyNotes.isEmpty {
|
||||
if searchText.isEmpty {
|
||||
@@ -183,36 +194,42 @@ struct JournalView: View {
|
||||
}
|
||||
}
|
||||
|
||||
/// Timeline-style row: the mood is the visual anchor of each month.
|
||||
private func monthlyNoteRow(_ entry: MonthlyNoteItem) -> some View {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
HStack {
|
||||
Text(entry.date.monthYearString)
|
||||
.font(.subheadline.weight(.semibold))
|
||||
Spacer()
|
||||
HStack(alignment: .top, spacing: 12) {
|
||||
ZStack {
|
||||
Circle()
|
||||
.fill((entry.mood?.tint ?? Color.secondary).opacity(0.14))
|
||||
.frame(width: 40, height: 40)
|
||||
Image(systemName: entry.mood?.iconName ?? "book.closed")
|
||||
.font(.system(size: 16))
|
||||
.foregroundColor(entry.mood?.tint ?? .secondary)
|
||||
}
|
||||
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
HStack {
|
||||
Text(entry.date.monthYearString)
|
||||
.font(.subheadline.weight(.semibold))
|
||||
Spacer()
|
||||
if entry.rating != nil {
|
||||
starRow(rating: entry.rating)
|
||||
.font(.caption2)
|
||||
}
|
||||
}
|
||||
|
||||
if let mood = entry.mood {
|
||||
moodPill(mood)
|
||||
} else {
|
||||
Text("Mood not set")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
Text(mood.title)
|
||||
.font(.caption.weight(.medium))
|
||||
.foregroundColor(mood.tint)
|
||||
}
|
||||
}
|
||||
|
||||
HStack(spacing: 6) {
|
||||
starRow(rating: entry.rating)
|
||||
if entry.rating == nil {
|
||||
Text("No rating")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
Text(entry.note.isEmpty ? String(localized: "journal_no_note") : entry.note)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
.lineLimit(2)
|
||||
}
|
||||
|
||||
Text(entry.note.isEmpty ? "No note yet." : entry.note)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
.lineLimit(2)
|
||||
}
|
||||
.padding(.vertical, 4)
|
||||
.padding(.vertical, 6)
|
||||
}
|
||||
|
||||
private func starRow(rating: Int?) -> some View {
|
||||
@@ -339,6 +356,19 @@ struct MonthlyNoteEditorView: View {
|
||||
}
|
||||
}
|
||||
|
||||
extension MonthlyCheckInMood {
|
||||
/// Semantic tint per mood — the timeline's color language.
|
||||
var tint: Color {
|
||||
switch self {
|
||||
case .energized: return .orange
|
||||
case .confident: return .positiveGreen
|
||||
case .balanced: return .teal
|
||||
case .cautious: return .yellow
|
||||
case .stressed: return .negativeRed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
NavigationStack {
|
||||
JournalView()
|
||||
|
||||
Reference in New Issue
Block a user