Add Retry on Join room node.
This commit is contained in:
parent
e1564e5a2b
commit
0e8e6050dd
5 changed files with 116 additions and 36 deletions
|
|
@ -17,6 +17,7 @@
|
||||||
package io.element.android.features.joinroom.impl
|
package io.element.android.features.joinroom.impl
|
||||||
|
|
||||||
sealed interface JoinRoomEvents {
|
sealed interface JoinRoomEvents {
|
||||||
|
data object Retry : JoinRoomEvents
|
||||||
data object JoinRoom : JoinRoomEvents
|
data object JoinRoom : JoinRoomEvents
|
||||||
data object AcceptInvite : JoinRoomEvents
|
data object AcceptInvite : JoinRoomEvents
|
||||||
data object DeclineInvite : JoinRoomEvents
|
data object DeclineInvite : JoinRoomEvents
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,11 @@ import androidx.annotation.VisibleForTesting
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
import androidx.compose.runtime.produceState
|
import androidx.compose.runtime.produceState
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
import io.element.android.features.invite.api.response.AcceptDeclineInviteEvents
|
import io.element.android.features.invite.api.response.AcceptDeclineInviteEvents
|
||||||
|
|
@ -32,6 +35,7 @@ import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
|
import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
|
||||||
|
import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias
|
||||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||||
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
|
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
|
||||||
import io.element.android.libraries.matrix.api.room.preview.RoomPreview
|
import io.element.android.libraries.matrix.api.room.preview.RoomPreview
|
||||||
|
|
@ -56,8 +60,13 @@ class JoinRoomPresenter @AssistedInject constructor(
|
||||||
@Composable
|
@Composable
|
||||||
override fun present(): JoinRoomState {
|
override fun present(): JoinRoomState {
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
var retryCount by remember { mutableIntStateOf(0) }
|
||||||
val roomInfo by matrixClient.getRoomInfoFlow(roomId).collectAsState(initial = Optional.empty())
|
val roomInfo by matrixClient.getRoomInfoFlow(roomId).collectAsState(initial = Optional.empty())
|
||||||
val contentState by produceState<ContentState>(initialValue = ContentState.Loading(roomIdOrAlias), key1 = roomInfo) {
|
val contentState by produceState<ContentState>(
|
||||||
|
initialValue = ContentState.Loading(roomIdOrAlias),
|
||||||
|
key1 = roomInfo,
|
||||||
|
key2 = retryCount,
|
||||||
|
) {
|
||||||
value = when {
|
value = when {
|
||||||
roomInfo.isPresent -> {
|
roomInfo.isPresent -> {
|
||||||
roomInfo.get().toContentState()
|
roomInfo.get().toContentState()
|
||||||
|
|
@ -67,10 +76,17 @@ class JoinRoomPresenter @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
val result = matrixClient.getRoomPreview(roomIdOrAlias)
|
val result = matrixClient.getRoomPreview(roomId.toRoomIdOrAlias())
|
||||||
value = result.getOrNull()
|
value = result.fold(
|
||||||
?.toContentState()
|
onSuccess = { it.toContentState() },
|
||||||
?: ContentState.UnknownRoom(roomIdOrAlias)
|
onFailure = { throwable ->
|
||||||
|
if (throwable.message?.contains("403") == true) {
|
||||||
|
ContentState.UnknownRoom(roomIdOrAlias)
|
||||||
|
} else {
|
||||||
|
ContentState.Failure(roomIdOrAlias, throwable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
ContentState.Loading(roomIdOrAlias)
|
ContentState.Loading(roomIdOrAlias)
|
||||||
}
|
}
|
||||||
|
|
@ -93,6 +109,9 @@ class JoinRoomPresenter @AssistedInject constructor(
|
||||||
AcceptDeclineInviteEvents.DeclineInvite(inviteData)
|
AcceptDeclineInviteEvents.DeclineInvite(inviteData)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
JoinRoomEvents.Retry -> {
|
||||||
|
retryCount++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ data class JoinRoomState(
|
||||||
|
|
||||||
sealed interface ContentState {
|
sealed interface ContentState {
|
||||||
data class Loading(val roomIdOrAlias: RoomIdOrAlias) : ContentState
|
data class Loading(val roomIdOrAlias: RoomIdOrAlias) : ContentState
|
||||||
|
data class Failure(val roomIdOrAlias: RoomIdOrAlias, val error: Throwable) : ContentState
|
||||||
data class UnknownRoom(val roomIdOrAlias: RoomIdOrAlias) : ContentState
|
data class UnknownRoom(val roomIdOrAlias: RoomIdOrAlias) : ContentState
|
||||||
data class Loaded(
|
data class Loaded(
|
||||||
val roomId: RoomId,
|
val roomId: RoomId,
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import io.element.android.features.invite.api.response.AcceptDeclineInviteState
|
||||||
import io.element.android.features.invite.api.response.anAcceptDeclineInviteState
|
import io.element.android.features.invite.api.response.anAcceptDeclineInviteState
|
||||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
|
||||||
import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias
|
import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias
|
||||||
|
|
||||||
open class JoinRoomStateProvider : PreviewParameterProvider<JoinRoomState> {
|
open class JoinRoomStateProvider : PreviewParameterProvider<JoinRoomState> {
|
||||||
|
|
@ -41,9 +42,24 @@ open class JoinRoomStateProvider : PreviewParameterProvider<JoinRoomState> {
|
||||||
aJoinRoomState(
|
aJoinRoomState(
|
||||||
contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.IsInvited)
|
contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.IsInvited)
|
||||||
),
|
),
|
||||||
|
aJoinRoomState(
|
||||||
|
contentState = aFailureContentState()
|
||||||
|
),
|
||||||
|
aJoinRoomState(
|
||||||
|
contentState = aFailureContentState(roomIdOrAlias = A_ROOM_ALIAS.toRoomIdOrAlias())
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun aFailureContentState(
|
||||||
|
roomIdOrAlias: RoomIdOrAlias = A_ROOM_ID.toRoomIdOrAlias()
|
||||||
|
): ContentState {
|
||||||
|
return ContentState.Failure(
|
||||||
|
roomIdOrAlias = roomIdOrAlias,
|
||||||
|
error = Exception("Error"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fun anUnknownContentState(roomId: RoomId = A_ROOM_ID) = ContentState.UnknownRoom(roomId.toRoomIdOrAlias())
|
fun anUnknownContentState(roomId: RoomId = A_ROOM_ID) = ContentState.UnknownRoom(roomId.toRoomIdOrAlias())
|
||||||
|
|
||||||
fun aLoadingContentState(roomId: RoomId = A_ROOM_ID) = ContentState.Loading(roomId.toRoomIdOrAlias())
|
fun aLoadingContentState(roomId: RoomId = A_ROOM_ID) = ContentState.Loading(roomId.toRoomIdOrAlias())
|
||||||
|
|
@ -79,3 +95,4 @@ fun aJoinRoomState(
|
||||||
)
|
)
|
||||||
|
|
||||||
private val A_ROOM_ID = RoomId("!exa:matrix.org")
|
private val A_ROOM_ID = RoomId("!exa:matrix.org")
|
||||||
|
private val A_ROOM_ALIAS = RoomAlias("#exa:matrix.org")
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.widthIn
|
import androidx.compose.foundation.layout.widthIn
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
|
@ -52,6 +53,7 @@ import io.element.android.libraries.designsystem.theme.components.Icon
|
||||||
import io.element.android.libraries.designsystem.theme.components.OutlinedButton
|
import io.element.android.libraries.designsystem.theme.components.OutlinedButton
|
||||||
import io.element.android.libraries.designsystem.theme.components.Text
|
import io.element.android.libraries.designsystem.theme.components.Text
|
||||||
import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
||||||
|
import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -71,7 +73,7 @@ fun JoinRoomView(
|
||||||
},
|
},
|
||||||
footer = {
|
footer = {
|
||||||
JoinRoomFooter(
|
JoinRoomFooter(
|
||||||
joinAuthorisationStatus = state.joinAuthorisationStatus,
|
state = state,
|
||||||
onAcceptInvite = {
|
onAcceptInvite = {
|
||||||
state.eventSink(JoinRoomEvents.AcceptInvite)
|
state.eventSink(JoinRoomEvents.AcceptInvite)
|
||||||
},
|
},
|
||||||
|
|
@ -81,6 +83,9 @@ fun JoinRoomView(
|
||||||
onJoinRoom = {
|
onJoinRoom = {
|
||||||
state.eventSink(JoinRoomEvents.JoinRoom)
|
state.eventSink(JoinRoomEvents.JoinRoom)
|
||||||
},
|
},
|
||||||
|
onRetry = {
|
||||||
|
state.eventSink(JoinRoomEvents.Retry)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -88,46 +93,57 @@ fun JoinRoomView(
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun JoinRoomFooter(
|
private fun JoinRoomFooter(
|
||||||
joinAuthorisationStatus: JoinAuthorisationStatus,
|
state: JoinRoomState,
|
||||||
onAcceptInvite: () -> Unit,
|
onAcceptInvite: () -> Unit,
|
||||||
onDeclineInvite: () -> Unit,
|
onDeclineInvite: () -> Unit,
|
||||||
onJoinRoom: () -> Unit,
|
onJoinRoom: () -> Unit,
|
||||||
|
onRetry: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
when (joinAuthorisationStatus) {
|
if (state.contentState is ContentState.Failure) {
|
||||||
JoinAuthorisationStatus.IsInvited -> {
|
Button(
|
||||||
ButtonRowMolecule(modifier = modifier, horizontalArrangement = Arrangement.spacedBy(20.dp)) {
|
text = stringResource(CommonStrings.action_retry),
|
||||||
OutlinedButton(
|
onClick = onRetry,
|
||||||
text = stringResource(CommonStrings.action_decline),
|
modifier = modifier.fillMaxWidth(),
|
||||||
onClick = onDeclineInvite,
|
size = ButtonSize.Medium,
|
||||||
modifier = Modifier.weight(1f),
|
)
|
||||||
size = ButtonSize.Medium,
|
} else {
|
||||||
)
|
val joinAuthorisationStatus = state.joinAuthorisationStatus
|
||||||
|
when (joinAuthorisationStatus) {
|
||||||
|
JoinAuthorisationStatus.IsInvited -> {
|
||||||
|
ButtonRowMolecule(modifier = modifier, horizontalArrangement = Arrangement.spacedBy(20.dp)) {
|
||||||
|
OutlinedButton(
|
||||||
|
text = stringResource(CommonStrings.action_decline),
|
||||||
|
onClick = onDeclineInvite,
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
size = ButtonSize.Medium,
|
||||||
|
)
|
||||||
|
Button(
|
||||||
|
text = stringResource(CommonStrings.action_accept),
|
||||||
|
onClick = onAcceptInvite,
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
size = ButtonSize.Medium,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JoinAuthorisationStatus.CanJoin -> {
|
||||||
Button(
|
Button(
|
||||||
text = stringResource(CommonStrings.action_accept),
|
text = stringResource(R.string.screen_join_room_join_action),
|
||||||
onClick = onAcceptInvite,
|
onClick = onJoinRoom,
|
||||||
modifier = Modifier.weight(1f),
|
modifier = modifier.fillMaxWidth(),
|
||||||
size = ButtonSize.Medium,
|
size = ButtonSize.Medium,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
JoinAuthorisationStatus.CanKnock -> {
|
||||||
|
Button(
|
||||||
|
text = stringResource(R.string.screen_join_room_knock_action),
|
||||||
|
onClick = onJoinRoom,
|
||||||
|
modifier = modifier.fillMaxWidth(),
|
||||||
|
size = ButtonSize.Medium,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
JoinAuthorisationStatus.Unknown -> Unit
|
||||||
}
|
}
|
||||||
JoinAuthorisationStatus.CanJoin -> {
|
|
||||||
Button(
|
|
||||||
text = stringResource(R.string.screen_join_room_join_action),
|
|
||||||
onClick = onJoinRoom,
|
|
||||||
modifier = modifier.fillMaxWidth(),
|
|
||||||
size = ButtonSize.Medium,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
JoinAuthorisationStatus.CanKnock -> {
|
|
||||||
Button(
|
|
||||||
text = stringResource(R.string.screen_join_room_knock_action),
|
|
||||||
onClick = onJoinRoom,
|
|
||||||
modifier = modifier.fillMaxWidth(),
|
|
||||||
size = ButtonSize.Medium,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
JoinAuthorisationStatus.Unknown -> Unit
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -187,6 +203,32 @@ private fun JoinRoomContent(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
is ContentState.Failure -> {
|
||||||
|
ContentScaffold(
|
||||||
|
modifier = modifier,
|
||||||
|
avatar = {
|
||||||
|
PlaceholderAtom(width = AvatarSize.RoomHeader.dp, height = AvatarSize.RoomHeader.dp)
|
||||||
|
},
|
||||||
|
title = {
|
||||||
|
when (contentState.roomIdOrAlias) {
|
||||||
|
is RoomIdOrAlias.Alias -> {
|
||||||
|
Title(contentState.roomIdOrAlias.identifier)
|
||||||
|
}
|
||||||
|
is RoomIdOrAlias.Id -> {
|
||||||
|
PlaceholderAtom(width = 200.dp, height = 22.dp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
subtitle = {
|
||||||
|
Text(
|
||||||
|
text = "Failed to get information about the room",
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
color = MaterialTheme.colorScheme.error,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue