Conversation
Greptile SummaryThis PR fixes macOS goal editing by plumbing a full goal-update flow: a new Key observations:
Confidence Score: 4/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant User
participant GoalEditSheet
participant GoalsWidget
participant DashboardPage
participant DashboardViewModel
participant APIClient
participant GoalStorage
User->>GoalEditSheet: Tap "Save"
GoalEditSheet->>GoalsWidget: onSave(title, current, target)
GoalEditSheet->>GoalEditSheet: onDismiss() — sheet dismissed immediately
GoalsWidget->>DashboardPage: onUpdateGoal(goal, title, current, target)
DashboardPage->>DashboardViewModel: Task { await updateGoal(...) }
DashboardViewModel->>APIClient: updateGoal(goalId, title, currentValue, targetValue)
APIClient->>APIClient: PATCH v1/goals/{goalId} (JSON body)
APIClient-->>DashboardViewModel: updated: Goal
APIClient->>APIClient: goalsCache = nil
DashboardViewModel->>GoalStorage: syncServerGoal(updated) [try?]
GoalStorage-->>DashboardViewModel: (may silently fail)
DashboardViewModel->>GoalStorage: getLocalGoals()
GoalStorage-->>DashboardViewModel: [Goal]
DashboardViewModel->>DashboardPage: goals updated → UI refreshed
Last reviewed commit: 19ca2a6 |
| func updateGoal(_ goal: Goal, title: String, currentValue: Double, targetValue: Double) async { | ||
| log("Goals: Updating goal '\(goal.title)' -> title='\(title)', current=\(currentValue), target=\(targetValue)") | ||
|
|
||
| do { | ||
| let updated = try await APIClient.shared.updateGoal( | ||
| goalId: goal.id, | ||
| title: title, | ||
| currentValue: currentValue, | ||
| targetValue: targetValue | ||
| ) | ||
|
|
||
| _ = try? await GoalStorage.shared.syncServerGoal(updated) | ||
| goals = try await GoalStorage.shared.getLocalGoals() | ||
| log("Goals: Updated goal '\(updated.title)' confirmed by API") | ||
| } catch { | ||
| logError("Failed to update goal", error: error) | ||
| goals = (try? await GoalStorage.shared.getLocalGoals()) ?? goals | ||
| } | ||
| } |
There was a problem hiding this comment.
No optimistic update before API call
updateGoalProgress immediately updates both goals[index].currentValue and local SQLite before making the API call, giving instant UI feedback. The new updateGoal waits for the full API round-trip before refreshing the UI, which can cause a noticeable lag when editing a goal's title or target.
Consider applying a similar optimistic strategy — update the in-memory goals array immediately, then reconcile with the API response:
func updateGoal(_ goal: Goal, title: String, currentValue: Double, targetValue: Double) async {
log("Goals: Updating goal '\(goal.title)' -> title='\(title)', current=\(currentValue), target=\(targetValue)")
// Optimistic update
if let index = goals.firstIndex(where: { $0.id == goal.id }) {
goals[index].title = title
goals[index].currentValue = currentValue
goals[index].targetValue = targetValue
}
do {
let updated = try await APIClient.shared.updateGoal(
goalId: goal.id,
title: title,
currentValue: currentValue,
targetValue: targetValue
)
_ = try? await GoalStorage.shared.syncServerGoal(updated)
goals = try await GoalStorage.shared.getLocalGoals()
log("Goals: Updated goal '\(updated.title)' confirmed by API")
} catch {
logError("Failed to update goal", error: error)
goals = (try? await GoalStorage.shared.getLocalGoals()) ?? goals
}
}Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| _ = try? await GoalStorage.shared.syncServerGoal(updated) | ||
| goals = try await GoalStorage.shared.getLocalGoals() |
There was a problem hiding this comment.
Stale local data when
syncServerGoal fails silently
syncServerGoal is called with try?, so if the local SQLite write fails, the subsequent getLocalGoals() will return the old values — making the UI appear unchanged even though the API update succeeded. The user sees no error and has to re-open the goals list to observe the change is gone.
The same pattern exists in createGoal. If a silent local-sync failure is acceptable here, consider at least applying the server response directly to the in-memory array as a fallback:
_ = try? await GoalStorage.shared.syncServerGoal(updated)
// Refresh from local; fall back to injecting the API response directly
if let refreshed = try? await GoalStorage.shared.getLocalGoals(), !refreshed.isEmpty {
goals = refreshed
} else if let index = goals.firstIndex(where: { $0.id == updated.id }) {
goals[index] = updated
}## Summary\n- add a desktop API client method for full goal updates\n- wire the goal edit sheet to save title, current value, and target value\n- refresh cached/local goals after a successful update\n\n## Testing\n- xcrun swift build -c debug --package-path Desktop\n- launched Omi Dev.app and verified the desktop app started successfully\n
Summary\n- add a desktop API client method for full goal updates\n- wire the goal edit sheet to save title, current value, and target value\n- refresh cached/local goals after a successful update\n\n## Testing\n- xcrun swift build -c debug --package-path Desktop\n- launched Omi Dev.app and verified the desktop app started successfully\n