mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-05 11:18:24 +00:00
fix(concord): accept a Direct Invite only once
Hiding the card while an accept runs isn't a lock: two taps (or the hub and Notifications at once) ran two joins and announced two Guestbook JOINs. The accepting set is now claimed with an atomic check-and-set. A second accept returns the new InProgress result, which the card ignores because the accept already running reports the outcome. Verified: a double tap on the emulator left exactly one JOIN on the guestbook plane. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
a3ac36dab8
commit
10408bbd4d
+8
-1
@@ -1169,7 +1169,14 @@ class AccountConcordActions(
|
||||
*/
|
||||
suspend fun acceptConcordDirectInvite(wrapId: HexKey): ConcordInviteResult {
|
||||
if (!account.isWriteable()) return ConcordInviteResult.InvalidLink
|
||||
acceptingConcordDirectInvites.update { it + wrapId }
|
||||
// Check-and-set in one step: two accepts of one invite would run two joins and announce two
|
||||
// Guestbook JOINs.
|
||||
var claimed = false
|
||||
acceptingConcordDirectInvites.update { current ->
|
||||
claimed = wrapId !in current
|
||||
if (claimed) current + wrapId else current
|
||||
}
|
||||
if (!claimed) return ConcordInviteResult.InProgress
|
||||
try {
|
||||
return acceptConcordDirectInviteNow(wrapId)
|
||||
} finally {
|
||||
|
||||
+6
@@ -76,4 +76,10 @@ sealed interface ConcordInviteResult {
|
||||
* missing (CORD-02 §8). Nothing was announced. Retrying can help once the List loads.
|
||||
*/
|
||||
data object NotSaved : ConcordInviteResult
|
||||
|
||||
/**
|
||||
* This invite is already being accepted (a second tap, or the hub and Notifications at once).
|
||||
* Nothing was done; the accept already running reports the outcome.
|
||||
*/
|
||||
data object InProgress : ConcordInviteResult
|
||||
}
|
||||
|
||||
+2
@@ -334,6 +334,8 @@ fun ConcordDirectInviteCard(
|
||||
try {
|
||||
when (val result = accountViewModel.account.concord.acceptConcordDirectInvite(invite.wrapId)) {
|
||||
is ConcordInviteResult.Joined -> nav.nav(Route.ConcordServer(result.communityId))
|
||||
// The accept already running reports; this tap did nothing.
|
||||
is ConcordInviteResult.InProgress -> Unit
|
||||
is ConcordInviteResult.Expired -> accountViewModel.toastManager.toast(Res.string.concord_direct_invites_title, Res.string.concord_invite_failed_expired)
|
||||
is ConcordInviteResult.Banned -> accountViewModel.toastManager.toast(Res.string.concord_direct_invites_title, Res.string.concord_invite_failed_banned)
|
||||
is ConcordInviteResult.InvalidLink -> accountViewModel.toastManager.toast(Res.string.concord_direct_invites_title, Res.string.concord_invite_failed_invalid)
|
||||
|
||||
+3
@@ -147,6 +147,9 @@ fun ConcordInviteScreen(
|
||||
RedeemState.Failed(Res.string.concord_invite_failed, canRetry = true)
|
||||
is ConcordInviteResult.NotSaved ->
|
||||
RedeemState.Failed(Res.string.concord_invite_failed_not_saved, canRetry = true)
|
||||
// Only a Direct Invite accept reports this; a link redeem never does.
|
||||
is ConcordInviteResult.InProgress ->
|
||||
RedeemState.Failed(Res.string.concord_invite_failed, canRetry = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user