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 e43fe44782..94bcd16f53 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 @@ -126,6 +126,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChangedBy import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn @@ -949,8 +950,20 @@ class AccountConcordActions( visibleConcordDirectInvites(), acceptingConcordDirectInvites, ) { visible, accepting -> if (accepting.isEmpty()) visible else visible.filterNot { it.wrapId in accepting } } + // `visible()` re-runs on every session revision and mints new (identity-equal only) views, + // and each emission clears and rebuilds every Notifications feed. Emit only when something + // a row shows or ranks by actually moved. + .distinctUntilChangedBy { list -> list.map { InviteRowKey(it.wrapId, it.catchUp, it.expired, it.followedSender, it.newChannelIds) } } .stateIn(account.scope, SharingStarted.Eagerly, emptyList()) + private data class InviteRowKey( + val wrapId: HexKey, + val catchUp: Boolean, + val expired: Boolean, + val followedSender: Boolean, + val newChannelIds: List, + ) + private fun visibleConcordDirectInvites() = combine( directInviteInbox.pending,