diff --git a/ios/Platform/.stfolder/syncthing-folder-marker b/ios/Platform/.stfolder/syncthing-folder-marker new file mode 100644 index 0000000..e69de29 diff --git a/ios/Platform/Platform/Features/Assistant/AssistantChatView.swift b/ios/Platform/Platform/Features/Assistant/AssistantChatView.swift index 9071bf3..e9051f9 100644 --- a/ios/Platform/Platform/Features/Assistant/AssistantChatView.swift +++ b/ios/Platform/Platform/Features/Assistant/AssistantChatView.swift @@ -88,6 +88,7 @@ struct AssistantChatView: View { .padding(.vertical, 10) .background(Color.surfaceCard) } + .background(Color.canvas) .onChange(of: vm.selectedPhoto) { Task { await vm.handlePhotoSelection() } } diff --git a/ios/Platform/Platform/Features/Home/HomeView.swift b/ios/Platform/Platform/Features/Home/HomeView.swift index 33fe268..da52a45 100644 --- a/ios/Platform/Platform/Features/Home/HomeView.swift +++ b/ios/Platform/Platform/Features/Home/HomeView.swift @@ -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 - calorieWidget - .padding(.horizontal) + // 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) + VStack(spacing: 12) { + Text("Calories") + .font(.caption.weight(.semibold)) + .foregroundStyle(vm.hasBackground ? .white.opacity(0.7) : Color.textTertiary) + .textCase(.uppercase) + .tracking(0.5) - // Text info - VStack(alignment: .leading, spacing: 6) { - Text("Calories") - .font(.headline) - .foregroundStyle(vm.hasBackground ? .white : Color.textPrimary) + // Animated ring + ZStack { + Circle() + .stroke(Color.emerald.opacity(0.15), lineWidth: 9) - Text("\(Int(vm.totalCalories)) / \(Int(vm.calorieGoal))") - .font(.subheadline.weight(.medium)) - .foregroundStyle(vm.hasBackground ? .white.opacity(0.8) : Color.textSecondary) + 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) - let remaining = max(vm.calorieGoal - vm.totalCalories, 0) - Text("\(Int(remaining)) remaining") - .font(.caption) - .foregroundStyle(vm.hasBackground ? .white.opacity(0.6) : Color.textTertiary) + VStack(spacing: 1) { + Text("\(Int(vm.totalCalories))") + .font(.system(size: 22, weight: .bold, design: .rounded)) + .foregroundStyle(vm.hasBackground ? .white : Color.textPrimary) + Text("/ \(Int(vm.calorieGoal))") + .font(.system(size: 10, weight: .medium, design: .rounded)) + .foregroundStyle(vm.hasBackground ? .white.opacity(0.6) : Color.textTertiary) + } } + .frame(width: 80, height: 80) - Spacer() + 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(20) + .padding(16) + .frame(maxWidth: .infinity) + .aspectRatio(1, contentMode: .fit) .background { if vm.hasBackground { RoundedRectangle(cornerRadius: 16) diff --git a/ios/Platform/Platform/Shared/Extensions/Color+Extensions.swift b/ios/Platform/Platform/Shared/Extensions/Color+Extensions.swift index af47b18..56b3eba 100644 --- a/ios/Platform/Platform/Shared/Extensions/Color+Extensions.swift +++ b/ios/Platform/Platform/Shared/Extensions/Color+Extensions.swift @@ -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