Skip to content

Commit e0bc484

Browse files
authored
Fix leaving the room not always dismissing the room screen (#5089)
* Fix leaving the room not always dismissing the room screen Use the existing `RoomInfo` membership check to dismiss the room instead of using `RoomMembershipObserver`. * Restore `membershipObserver`, check Maestro still works * Improve the logic for the local membership change check * Remove redundant room id check
1 parent f10582c commit e0bc484

File tree

2 files changed

+44
-18
lines changed
  • appnav/src/main/kotlin/io/element/android/appnav/room
  • libraries/core/src/main/kotlin/io/element/android/libraries/core/coroutine

2 files changed

+44
-18
lines changed

appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import io.element.android.libraries.architecture.NodeInputs
3737
import io.element.android.libraries.architecture.createNode
3838
import io.element.android.libraries.architecture.inputs
3939
import io.element.android.libraries.core.bool.orFalse
40+
import io.element.android.libraries.core.coroutine.withPreviousValue
4041
import io.element.android.libraries.di.SessionScope
4142
import io.element.android.libraries.matrix.api.MatrixClient
4243
import io.element.android.libraries.matrix.api.core.RoomAlias
@@ -47,11 +48,14 @@ import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
4748
import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias
4849
import io.element.android.libraries.matrix.api.sync.SyncService
4950
import io.element.android.libraries.matrix.ui.room.LoadingRoomState
51+
import kotlinx.coroutines.flow.SharingStarted
5052
import kotlinx.coroutines.flow.combine
5153
import kotlinx.coroutines.flow.distinctUntilChanged
54+
import kotlinx.coroutines.flow.filter
5255
import kotlinx.coroutines.flow.first
5356
import kotlinx.coroutines.flow.launchIn
5457
import kotlinx.coroutines.flow.map
58+
import kotlinx.coroutines.flow.shareIn
5559
import kotlinx.coroutines.launch
5660
import kotlinx.parcelize.Parcelize
5761
import timber.log.Timber
@@ -124,8 +128,19 @@ class RoomFlowNode @AssistedInject constructor(
124128
private fun subscribeToRoomInfoFlow(roomId: RoomId, serverNames: List<String>) {
125129
val roomInfoFlow = client.getRoomInfoFlow(roomId)
126130
val isSpaceFlow = roomInfoFlow.map { it.getOrNull()?.isSpace.orFalse() }.distinctUntilChanged()
127-
val currentMembershipFlow = roomInfoFlow.map { it.getOrNull()?.currentUserMembership }.distinctUntilChanged()
128-
combine(currentMembershipFlow, isSpaceFlow) { membership, isSpace ->
131+
132+
// This observes the local membership changes for the room
133+
val membershipUpdateFlow = membershipObserver.updates
134+
.filter { it.roomId == roomId }
135+
.distinctUntilChanged()
136+
// We add a replay so we can check the last local membership update
137+
.shareIn(lifecycleScope, started = SharingStarted.Eagerly, replay = 1)
138+
139+
val currentMembershipFlow = roomInfoFlow
140+
.map { it.getOrNull()?.currentUserMembership }
141+
.distinctUntilChanged()
142+
.withPreviousValue()
143+
combine(currentMembershipFlow, isSpaceFlow) { (previousMembership, membership), isSpace ->
129144
Timber.d("Room membership: $membership")
130145
when (membership) {
131146
CurrentUserMembership.JOINED -> {
@@ -146,26 +161,24 @@ class RoomFlowNode @AssistedInject constructor(
146161
}
147162
}
148163
else -> {
149-
// Was invited or the room is not known, display the join room screen
150-
backstack.newRoot(
151-
NavTarget.JoinRoom(
152-
roomId = roomId,
153-
serverNames = serverNames,
154-
trigger = inputs.trigger.getOrNull() ?: JoinedRoom.Trigger.Invite,
164+
if (membership == CurrentUserMembership.LEFT && previousMembership == CurrentUserMembership.JOINED) {
165+
// The user left the room in this device, remove the room from the backstack
166+
if (!membershipUpdateFlow.first().isUserInRoom) {
167+
navigateUp()
168+
}
169+
} else {
170+
// Was invited or the room is not known, display the join room screen
171+
backstack.newRoot(
172+
NavTarget.JoinRoom(
173+
roomId = roomId,
174+
serverNames = serverNames,
175+
trigger = inputs.trigger.getOrNull() ?: JoinedRoom.Trigger.Invite,
176+
)
155177
)
156-
)
178+
}
157179
}
158180
}
159181
}.launchIn(lifecycleScope)
160-
161-
// If the user leaves the room from this client, close the room flow.
162-
lifecycleScope.launch {
163-
membershipObserver.updates
164-
.first { it.roomId == roomId && !it.isUserInRoom }
165-
.run {
166-
navigateUp()
167-
}
168-
}
169182
}
170183

171184
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {

libraries/core/src/main/kotlin/io/element/android/libraries/core/coroutine/Flow.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,24 @@
88
package io.element.android.libraries.core.coroutine
99

1010
import kotlinx.coroutines.flow.Flow
11+
import kotlinx.coroutines.flow.filterNotNull
1112
import kotlinx.coroutines.flow.first
13+
import kotlinx.coroutines.flow.runningFold
1214

1315
/**
1416
* Returns the first element of the flow that is an instance of [T], waiting for it if necessary.
1517
*/
1618
suspend inline fun <reified T> Flow<*>.firstInstanceOf(): T {
1719
return first { it is T } as T
1820
}
21+
22+
/**
23+
* Returns a flow that emits pairs of the previous and current values.
24+
* The first emission will be a pair of `null` and the first value emitted by the source flow.
25+
*/
26+
fun <T> Flow<T>.withPreviousValue(): Flow<Pair<T?, T>> {
27+
return runningFold(null) { prev: Pair<T?, T>?, current ->
28+
prev?.second to current
29+
}
30+
.filterNotNull()
31+
}

0 commit comments

Comments
 (0)