Skip to content

Give conflict resolution a busy state and a saveable list key - #115

Merged
in-jun merged 1 commit into
mainfrom
fix/conflictsscreen
Jul 18, 2026
Merged

Give conflict resolution a busy state and a saveable list key#115
in-jun merged 1 commit into
mainfrom
fix/conflictsscreen

Conversation

@in-jun

@in-jun in-jun commented Jul 18, 2026

Copy link
Copy Markdown
Owner

The Conflicts screen keyed its list on a raw Pair and gave the resolution buttons no in-progress feedback. Both are addressed here, sharing a single stable identity via a new ConflictItem.key.

  • Replace the LazyColumn item key with a saveable String ("pairId:conflictCopyPath") instead of a kotlin.Pair, which can't be written to a Bundle and would throw once any per-item rememberSaveable or scroll restoration keyed on it is added.
  • Track in-flight resolutions in the ViewModel and expose them as a resolving set; while a conflict's resolution and follow-up sync run, its card's buttons are disabled and a progress indicator is shown, so a re-tap can't land on the already-resolved no-op. This mirrors the syncing state HomeScreen's card already uses.

Fixes #102
Fixes #72

@in-jun in-jun left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both fixes look correct and are cleanly unified behind the new ConflictItem.key.

#102 — saveable list key. Swapping the kotlin.Pair for "$pairId:$conflictCopyPath" satisfies the Bundle-saveable contract, and uniqueness is preserved: a Long never contains a colon, so the mapping from (pairId, conflictCopyPath) to the string is injective. Consistent with HomeScreen's scalar key.

#72 — in-progress state. The resolving set mirrors the established _syncing pattern well — the re-tap guard and set insertion happen synchronously on the main thread before launch (same as syncPair), so a rapid double-tap can't slip through, and the try/finally re-enables the card on the StaleConflictException path so a retry is possible. Disabling the buttons plus the spinner reads clearly.

A couple of things I checked that hold up: key being a computed val keeps it out of equals/hashCode, so matching against the string set survives the list refresh creating new ConflictItem instances; and the fire-and-forget final refreshConflicts() leaves only a brief window where a resolved card is still tappable, which remains a safe no-op via the storage.probe(...) == null check — same as before this change.

Tightly scoped to the two issues. LGTM."

@in-jun
in-jun merged commit ef0fb3f into main Jul 18, 2026
1 check passed
@in-jun
in-jun deleted the fix/conflictsscreen branch July 18, 2026 15:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Conflicts list uses a non-saveable Pair as the LazyColumn item key Conflict resolution buttons show no in-progress state, inviting duplicate taps

1 participant