Skip to content

Commit e69dd05

Browse files
committed
Improve LeaveSpacePresenter and add a retry mechanism if loading the rooms fails.
1 parent 71f9660 commit e69dd05

File tree

4 files changed

+57
-35
lines changed

4 files changed

+57
-35
lines changed

features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceEvents.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ package io.element.android.features.space.impl.leave
1010
import io.element.android.libraries.matrix.api.core.RoomId
1111

1212
sealed interface LeaveSpaceEvents {
13+
data object Retry : LeaveSpaceEvents
1314
data object SelectAllRooms : LeaveSpaceEvents
1415
data object DeselectAllRooms : LeaveSpaceEvents
1516
data class ToggleRoomSelection(val roomId: RoomId) : LeaveSpaceEvents

features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpacePresenter.kt

Lines changed: 45 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@
88
package io.element.android.features.space.impl.leave
99

1010
import androidx.compose.runtime.Composable
11+
import androidx.compose.runtime.LaunchedEffect
1112
import androidx.compose.runtime.MutableState
1213
import androidx.compose.runtime.getValue
14+
import androidx.compose.runtime.mutableIntStateOf
1315
import androidx.compose.runtime.mutableStateOf
14-
import androidx.compose.runtime.produceState
1516
import androidx.compose.runtime.remember
1617
import androidx.compose.runtime.rememberCoroutineScope
1718
import androidx.compose.runtime.setValue
@@ -26,10 +27,9 @@ import io.element.android.libraries.architecture.runUpdatingState
2627
import io.element.android.libraries.matrix.api.core.RoomId
2728
import io.element.android.libraries.matrix.api.spaces.LeaveSpaceHandle
2829
import io.element.android.libraries.matrix.api.spaces.LeaveSpaceRoom
29-
import kotlinx.collections.immutable.ImmutableSet
30+
import kotlinx.collections.immutable.ImmutableList
3031
import kotlinx.collections.immutable.persistentSetOf
3132
import kotlinx.collections.immutable.toImmutableList
32-
import kotlinx.collections.immutable.toPersistentSet
3333
import kotlinx.coroutines.CoroutineScope
3434
import kotlinx.coroutines.launch
3535

@@ -42,72 +42,85 @@ class LeaveSpacePresenter(
4242
fun create(leaveSpaceHandle: LeaveSpaceHandle): LeaveSpacePresenter
4343
}
4444

45+
data class LeaveSpaceRooms(
46+
val current: LeaveSpaceRoom?,
47+
val others: List<LeaveSpaceRoom>,
48+
)
49+
4550
@Composable
4651
override fun present(): LeaveSpaceState {
4752
val coroutineScope = rememberCoroutineScope()
48-
var currentSpace: LeaveSpaceRoom? by remember { mutableStateOf(null) }
53+
var retryCount by remember { mutableIntStateOf(0) }
4954
val leaveSpaceAction = remember {
5055
mutableStateOf<AsyncAction<Unit>>(AsyncAction.Uninitialized)
5156
}
52-
val selectedRoomIds = remember {
53-
mutableStateOf<ImmutableSet<RoomId>>(persistentSetOf())
57+
var selectedRoomIds by remember {
58+
mutableStateOf<Collection<RoomId>>(setOf())
59+
}
60+
var leaveSpaceRooms by remember {
61+
mutableStateOf<AsyncData<LeaveSpaceRooms>>(AsyncData.Loading())
5462
}
55-
val leaveSpaceRooms by produceState(AsyncData.Loading()) {
63+
LaunchedEffect(retryCount) {
5664
val rooms = leaveSpaceHandle.rooms()
5765
val (currentRoom, otherRooms) = rooms.getOrNull()
5866
.orEmpty()
5967
.partition { it.spaceRoom.roomId == leaveSpaceHandle.id }
60-
currentSpace = currentRoom.firstOrNull()
6168
// By default select all rooms that can be left
62-
selectedRoomIds.value = otherRooms
69+
selectedRoomIds = otherRooms
6370
.filter { it.isLastAdmin.not() }
6471
.map { it.spaceRoom.roomId }
65-
.toPersistentSet()
66-
value = rooms.fold(
67-
onSuccess = { AsyncData.Success(otherRooms) },
72+
leaveSpaceRooms = rooms.fold(
73+
onSuccess = {
74+
AsyncData.Success(
75+
LeaveSpaceRooms(
76+
current = currentRoom.firstOrNull(),
77+
others = otherRooms.toImmutableList(),
78+
)
79+
)
80+
},
6881
onFailure = { AsyncData.Failure(it) }
6982
)
7083
}
71-
val selectableSpaceRooms by produceState(
72-
initialValue = AsyncData.Loading(),
73-
key1 = leaveSpaceRooms,
74-
key2 = selectedRoomIds.value,
75-
) {
76-
value = leaveSpaceRooms.map { list ->
77-
list.orEmpty().map { room ->
84+
var selectableSpaceRooms by remember {
85+
mutableStateOf<AsyncData<ImmutableList<SelectableSpaceRoom>>>(AsyncData.Loading())
86+
}
87+
LaunchedEffect(selectedRoomIds, leaveSpaceRooms) {
88+
selectableSpaceRooms = leaveSpaceRooms.map {
89+
it?.others.orEmpty().map { room ->
7890
SelectableSpaceRoom(
7991
spaceRoom = room.spaceRoom,
8092
isLastAdmin = room.isLastAdmin,
81-
isSelected = selectedRoomIds.value.contains(room.spaceRoom.roomId),
93+
isSelected = selectedRoomIds.contains(room.spaceRoom.roomId),
8294
)
8395
}.toImmutableList()
8496
}
8597
}
8698

8799
fun handleEvents(event: LeaveSpaceEvents) {
88100
when (event) {
101+
LeaveSpaceEvents.Retry -> {
102+
leaveSpaceRooms = AsyncData.Loading()
103+
retryCount += 1
104+
}
89105
LeaveSpaceEvents.DeselectAllRooms -> {
90-
selectedRoomIds.value = persistentSetOf()
106+
selectedRoomIds = persistentSetOf()
91107
}
92108
LeaveSpaceEvents.SelectAllRooms -> {
93-
selectedRoomIds.value = selectableSpaceRooms.dataOrNull()
109+
selectedRoomIds = selectableSpaceRooms.dataOrNull()
94110
.orEmpty()
95111
.filter { it.isLastAdmin.not() }
96112
.map { it.spaceRoom.roomId }
97-
.toPersistentSet()
98113
}
99114
is LeaveSpaceEvents.ToggleRoomSelection -> {
100-
val currentSet = selectedRoomIds.value
101-
selectedRoomIds.value = if (currentSet.contains(event.roomId)) {
102-
currentSet - event.roomId
115+
selectedRoomIds = if (selectedRoomIds.contains(event.roomId)) {
116+
selectedRoomIds - event.roomId
103117
} else {
104-
currentSet + event.roomId
118+
selectedRoomIds + event.roomId
105119
}
106-
.toPersistentSet()
107120
}
108121
LeaveSpaceEvents.LeaveSpace -> coroutineScope.leaveSpace(
109122
leaveSpaceAction = leaveSpaceAction,
110-
selectedRoomIds = selectedRoomIds.value,
123+
selectedRoomIds = selectedRoomIds,
111124
)
112125
LeaveSpaceEvents.CloseError -> {
113126
leaveSpaceAction.value = AsyncAction.Uninitialized
@@ -116,8 +129,8 @@ class LeaveSpacePresenter(
116129
}
117130

118131
return LeaveSpaceState(
119-
spaceName = currentSpace?.spaceRoom?.name,
120-
isLastAdmin = currentSpace?.isLastAdmin == true,
132+
spaceName = leaveSpaceRooms.dataOrNull()?.current?.spaceRoom?.name,
133+
isLastAdmin = leaveSpaceRooms.dataOrNull()?.current?.isLastAdmin == true,
121134
selectableSpaceRooms = selectableSpaceRooms,
122135
leaveSpaceAction = leaveSpaceAction.value,
123136
eventSink = ::handleEvents,
@@ -126,7 +139,7 @@ class LeaveSpacePresenter(
126139

127140
private fun CoroutineScope.leaveSpace(
128141
leaveSpaceAction: MutableState<AsyncAction<Unit>>,
129-
selectedRoomIds: Set<RoomId>,
142+
selectedRoomIds: Collection<RoomId>,
130143
) = launch {
131144
runUpdatingState(leaveSpaceAction) {
132145
leaveSpaceHandle.leave(selectedRoomIds.toList())

features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceView.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,9 @@ fun LeaveSpaceView(
111111
is AsyncData.Failure -> item {
112112
AsyncFailure(
113113
throwable = state.selectableSpaceRooms.error,
114-
onRetry = null,
114+
onRetry = {
115+
state.eventSink(LeaveSpaceEvents.Retry)
116+
},
115117
)
116118
}
117119
is AsyncData.Loading,

features/space/impl/src/test/kotlin/io/element/android/features/space/impl/leave/LeaveSpacePresenterTest.kt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,15 @@ class LeaveSpacePresenterTest {
6060
val state = awaitItem()
6161
assertThat(state.selectableSpaceRooms.isLoading()).isTrue()
6262
assertThat(state.leaveSpaceAction).isEqualTo(AsyncAction.Uninitialized)
63-
skipItems(2)
63+
skipItems(3)
6464
val stateError = awaitItem()
6565
assertThat(stateError.selectableSpaceRooms.isFailure()).isTrue()
66+
// Retry
67+
stateError.eventSink(LeaveSpaceEvents.Retry)
68+
skipItems(1)
69+
val stateLoadingAgain = awaitItem()
70+
assertThat(stateLoadingAgain.selectableSpaceRooms.isLoading()).isTrue()
71+
cancelAndIgnoreRemainingEvents()
6672
}
6773
}
6874

@@ -166,7 +172,7 @@ class LeaveSpacePresenterTest {
166172
)
167173
)
168174
presenter.test {
169-
skipItems(3)
175+
skipItems(4)
170176
val state = awaitItem()
171177
state.eventSink(LeaveSpaceEvents.LeaveSpace)
172178
val stateLeaving = awaitItem()

0 commit comments

Comments
 (0)