fix: goals save API (void response), user greeting, goal field labels
- Goals PUT returns partial JSON, use putVoid + reload - Home shows 'Hi, Yusuf' instead of 'Home' - EmptyResponse type for void-like endpoints Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,13 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
// Accepts any JSON response (for void-like endpoints that return partial data)
|
||||||
|
private struct EmptyResponse: Decodable {
|
||||||
|
init(from decoder: Decoder) throws {
|
||||||
|
// Accept anything
|
||||||
|
_ = try? decoder.singleValueContainer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum APIError: Error, LocalizedError {
|
enum APIError: Error, LocalizedError {
|
||||||
case invalidURL
|
case invalidURL
|
||||||
case httpError(Int, String)
|
case httpError(Int, String)
|
||||||
@@ -138,6 +146,10 @@ final class APIClient {
|
|||||||
try await request("PUT", path: path, body: body)
|
try await request("PUT", path: path, body: body)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func putVoid(_ path: String, body: any Encodable) async throws {
|
||||||
|
let _: EmptyResponse = try await request("PUT", path: path, body: body)
|
||||||
|
}
|
||||||
|
|
||||||
func delete<T: Decodable>(_ path: String) async throws -> T {
|
func delete<T: Decodable>(_ path: String) async throws -> T {
|
||||||
try await request("DELETE", path: path)
|
try await request("DELETE", path: path)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,8 +30,8 @@ struct FitnessAPI {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateGoals(_ request: UpdateGoalsRequest) async throws -> DailyGoal {
|
func updateGoals(_ request: UpdateGoalsRequest) async throws {
|
||||||
try await api.put("\(basePath)/goals", body: request)
|
try await api.putVoid("\(basePath)/goals", body: request)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateEntry(id: String, body: UpdateEntryRequest) async throws -> FoodEntry {
|
func updateEntry(id: String, body: UpdateEntryRequest) async throws -> FoodEntry {
|
||||||
|
|||||||
@@ -43,9 +43,10 @@ final class GoalsViewModel {
|
|||||||
sugar: Double(editSugar) ?? 0,
|
sugar: Double(editSugar) ?? 0,
|
||||||
fiber: Double(editFiber) ?? 0
|
fiber: Double(editFiber) ?? 0
|
||||||
)
|
)
|
||||||
let updated = try await api.updateGoals(request)
|
try await api.updateGoals(request)
|
||||||
goal = updated
|
// Reload goals to get the full updated object
|
||||||
populateFields(from: updated)
|
goal = try await api.getGoals(date: Date().apiDateString)
|
||||||
|
populateFields(from: goal)
|
||||||
saveSuccess = true
|
saveSuccess = true
|
||||||
} catch {
|
} catch {
|
||||||
self.error = error.localizedDescription
|
self.error = error.localizedDescription
|
||||||
|
|||||||
@@ -23,9 +23,17 @@ struct HomeView: View {
|
|||||||
VStack(spacing: 16) {
|
VStack(spacing: 16) {
|
||||||
// Top bar
|
// Top bar
|
||||||
HStack {
|
HStack {
|
||||||
|
VStack(alignment: .leading, spacing: 2) {
|
||||||
|
if let name = auth.currentUser?.displayName {
|
||||||
|
Text("Hi, \(name)")
|
||||||
|
.font(.title.weight(.bold))
|
||||||
|
.foregroundStyle(vm.hasBackground ? .white : Color.textPrimary)
|
||||||
|
} else {
|
||||||
Text("Home")
|
Text("Home")
|
||||||
.font(.title.weight(.bold))
|
.font(.title.weight(.bold))
|
||||||
.foregroundStyle(vm.hasBackground ? .white : Color.textPrimary)
|
.foregroundStyle(vm.hasBackground ? .white : Color.textPrimary)
|
||||||
|
}
|
||||||
|
}
|
||||||
Spacer()
|
Spacer()
|
||||||
Menu {
|
Menu {
|
||||||
PhotosPicker(
|
PhotosPicker(
|
||||||
|
|||||||
Reference in New Issue
Block a user