diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/AccountConcordActions.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/AccountConcordActions.kt index a52dbc5e38..153877b2da 100644 --- a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/AccountConcordActions.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/AccountConcordActions.kt @@ -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 { diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/ConcordInviteResult.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/ConcordInviteResult.kt index bb33934c49..b0580cc23b 100644 --- a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/ConcordInviteResult.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/ConcordInviteResult.kt @@ -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 } diff --git a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/screen/loggedIn/chats/publicChannels/concord/ConcordDirectInvites.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/screen/loggedIn/chats/publicChannels/concord/ConcordDirectInvites.kt index 48ad973e20..5d54539918 100644 --- a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/screen/loggedIn/chats/publicChannels/concord/ConcordDirectInvites.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/screen/loggedIn/chats/publicChannels/concord/ConcordDirectInvites.kt @@ -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) diff --git a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/screen/loggedIn/chats/publicChannels/concord/ConcordInviteScreen.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/screen/loggedIn/chats/publicChannels/concord/ConcordInviteScreen.kt index 7086c9ef1d..7a402b798f 100644 --- a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/screen/loggedIn/chats/publicChannels/concord/ConcordInviteScreen.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/screen/loggedIn/chats/publicChannels/concord/ConcordInviteScreen.kt @@ -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) } } }