fix: half-width animated calorie widget, remove duplicate +, warm colors
- Calorie widget: half width with animated ring (spring animation) - Removed floating + from dashboard (only on fitness tab) - surfaceCard changed from white to warm white (#FFFCF8) - AssistantChatView gets canvas background - Future widget placeholder on right side of grid Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
0
ios/Platform/.stfolder/syncthing-folder-marker
Normal file
0
ios/Platform/.stfolder/syncthing-folder-marker
Normal file
@@ -88,6 +88,7 @@ struct AssistantChatView: View {
|
||||
.padding(.vertical, 10)
|
||||
.background(Color.surfaceCard)
|
||||
}
|
||||
.background(Color.canvas)
|
||||
.onChange(of: vm.selectedPhoto) {
|
||||
Task { await vm.handlePhotoSelection() }
|
||||
}
|
||||
|
||||
@@ -5,9 +5,10 @@ struct HomeView: View {
|
||||
@Environment(AuthManager.self) private var auth
|
||||
@State private var vm = HomeViewModel()
|
||||
@State private var showAssistant = false
|
||||
@State private var ringAnimated = false
|
||||
|
||||
var body: some View {
|
||||
ZStack(alignment: .bottomTrailing) {
|
||||
ZStack {
|
||||
// Background
|
||||
if let bg = vm.backgroundImage {
|
||||
Image(uiImage: bg)
|
||||
@@ -55,38 +56,24 @@ struct HomeView: View {
|
||||
.padding(.horizontal)
|
||||
.padding(.top, 60)
|
||||
|
||||
// Calorie widget — full width card
|
||||
// Widget grid — half width
|
||||
HStack(spacing: 12) {
|
||||
calorieWidget
|
||||
// Future widget placeholder — invisible spacer
|
||||
Color.clear
|
||||
}
|
||||
.padding(.horizontal)
|
||||
|
||||
Spacer(minLength: 100)
|
||||
}
|
||||
}
|
||||
|
||||
// Floating + button
|
||||
Button {
|
||||
showAssistant = true
|
||||
} label: {
|
||||
Image(systemName: "plus")
|
||||
.font(.title2.weight(.semibold))
|
||||
.foregroundStyle(.white)
|
||||
.frame(width: 56, height: 56)
|
||||
.background(Color.accentWarm)
|
||||
.clipShape(Circle())
|
||||
.shadow(color: Color.accentWarm.opacity(0.3), radius: 8, y: 4)
|
||||
}
|
||||
.padding(.trailing, 20)
|
||||
.padding(.bottom, 100)
|
||||
}
|
||||
.navigationBarHidden(true)
|
||||
.sheet(isPresented: $showAssistant) {
|
||||
FoodSearchView(
|
||||
date: Date().apiDateString,
|
||||
onFoodAdded: { Task { await vm.loadTodayData() } }
|
||||
)
|
||||
}
|
||||
.task {
|
||||
await vm.loadTodayData()
|
||||
withAnimation(.spring(response: 0.8, dampingFraction: 0.7)) {
|
||||
ringAnimated = true
|
||||
}
|
||||
}
|
||||
.onChange(of: vm.selectedPhoto) {
|
||||
Task { await vm.handlePhotoSelection() }
|
||||
@@ -94,37 +81,43 @@ struct HomeView: View {
|
||||
}
|
||||
|
||||
private var calorieWidget: some View {
|
||||
HStack(spacing: 20) {
|
||||
// Ring
|
||||
MacroRingWithLabel(
|
||||
consumed: vm.totalCalories,
|
||||
goal: vm.calorieGoal,
|
||||
label: "kcal",
|
||||
color: .emerald,
|
||||
size: 90,
|
||||
lineWidth: 9
|
||||
)
|
||||
.frame(width: 90, height: 90)
|
||||
|
||||
// Text info
|
||||
VStack(alignment: .leading, spacing: 6) {
|
||||
VStack(spacing: 12) {
|
||||
Text("Calories")
|
||||
.font(.headline)
|
||||
.font(.caption.weight(.semibold))
|
||||
.foregroundStyle(vm.hasBackground ? .white.opacity(0.7) : Color.textTertiary)
|
||||
.textCase(.uppercase)
|
||||
.tracking(0.5)
|
||||
|
||||
// Animated ring
|
||||
ZStack {
|
||||
Circle()
|
||||
.stroke(Color.emerald.opacity(0.15), lineWidth: 9)
|
||||
|
||||
Circle()
|
||||
.trim(from: 0, to: ringAnimated ? min(max(vm.calorieGoal > 0 ? vm.totalCalories / vm.calorieGoal : 0, 0), 1) : 0)
|
||||
.stroke(Color.emerald, style: StrokeStyle(lineWidth: 9, lineCap: .round))
|
||||
.rotationEffect(.degrees(-90))
|
||||
.animation(.spring(response: 0.8, dampingFraction: 0.7), value: ringAnimated)
|
||||
|
||||
VStack(spacing: 1) {
|
||||
Text("\(Int(vm.totalCalories))")
|
||||
.font(.system(size: 22, weight: .bold, design: .rounded))
|
||||
.foregroundStyle(vm.hasBackground ? .white : Color.textPrimary)
|
||||
|
||||
Text("\(Int(vm.totalCalories)) / \(Int(vm.calorieGoal))")
|
||||
.font(.subheadline.weight(.medium))
|
||||
.foregroundStyle(vm.hasBackground ? .white.opacity(0.8) : Color.textSecondary)
|
||||
|
||||
let remaining = max(vm.calorieGoal - vm.totalCalories, 0)
|
||||
Text("\(Int(remaining)) remaining")
|
||||
.font(.caption)
|
||||
Text("/ \(Int(vm.calorieGoal))")
|
||||
.font(.system(size: 10, weight: .medium, design: .rounded))
|
||||
.foregroundStyle(vm.hasBackground ? .white.opacity(0.6) : Color.textTertiary)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.padding(20)
|
||||
.frame(width: 80, height: 80)
|
||||
|
||||
let remaining = max(vm.calorieGoal - vm.totalCalories, 0)
|
||||
Text("\(Int(remaining)) left")
|
||||
.font(.caption2.weight(.medium))
|
||||
.foregroundStyle(vm.hasBackground ? .white.opacity(0.6) : Color.textTertiary)
|
||||
}
|
||||
.padding(16)
|
||||
.frame(maxWidth: .infinity)
|
||||
.aspectRatio(1, contentMode: .fit)
|
||||
.background {
|
||||
if vm.hasBackground {
|
||||
RoundedRectangle(cornerRadius: 16)
|
||||
|
||||
@@ -9,7 +9,7 @@ extension Color {
|
||||
static let emerald = Color(red: 0.020, green: 0.588, blue: 0.412) // #059669
|
||||
|
||||
// MARK: - Surfaces
|
||||
static let surfaceCard = Color.white
|
||||
static let surfaceCard = Color(red: 255/255, green: 252/255, blue: 248/255) // warm white
|
||||
static let surfaceSheet = Color(red: 0.98, green: 0.97, blue: 0.95)
|
||||
|
||||
// MARK: - Text
|
||||
|
||||
Reference in New Issue
Block a user