diff --git a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenter.kt b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenter.kt index 082120c6f3..6c8b4d533c 100644 --- a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenter.kt +++ b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenter.kt @@ -94,8 +94,8 @@ class AcceptDeclineInvitePresenter @Inject constructor( private fun CoroutineScope.declineInvite(roomId: RoomId, declinedAction: MutableState>) = launch { suspend { - client.getInvitedRoom(roomId)?.use { - it.declineInvite().getOrThrow() + client.getPendingRoom(roomId)?.use { + it.leave().getOrThrow() notificationCleaner.clearMembershipNotificationForRoom(client.sessionId, roomId) } roomId diff --git a/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenterTest.kt b/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenterTest.kt index 6c1057638f..620f899997 100644 --- a/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenterTest.kt +++ b/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenterTest.kt @@ -22,7 +22,7 @@ import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_ROOM_NAME import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.FakeMatrixClient -import io.element.android.libraries.matrix.test.room.FakeInvitedRoom +import io.element.android.libraries.matrix.test.room.FakePendingRoom import io.element.android.libraries.matrix.test.room.join.FakeJoinRoom import io.element.android.libraries.push.api.notifications.NotificationCleaner import io.element.android.libraries.push.test.notifications.FakeNotificationCleaner @@ -78,7 +78,7 @@ class AcceptDeclineInvitePresenterTest { Result.failure(RuntimeException("Failed to leave room")) } val client = FakeMatrixClient().apply { - getInvitedRoomResults[A_ROOM_ID] = FakeInvitedRoom(declineInviteResult = declineInviteFailure) + getPendingRoomResults[A_ROOM_ID] = FakePendingRoom(declineInviteResult = declineInviteFailure) } val presenter = createAcceptDeclineInvitePresenter(client = client) presenter.test { @@ -121,7 +121,7 @@ class AcceptDeclineInvitePresenterTest { Result.success(Unit) } val client = FakeMatrixClient().apply { - getInvitedRoomResults[A_ROOM_ID] = FakeInvitedRoom(declineInviteResult = declineInviteSuccess) + getPendingRoomResults[A_ROOM_ID] = FakePendingRoom(declineInviteResult = declineInviteSuccess) } val presenter = createAcceptDeclineInvitePresenter( client = client, diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomEvents.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomEvents.kt index 33bbe68c63..eddfa79717 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomEvents.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomEvents.kt @@ -11,7 +11,9 @@ sealed interface JoinRoomEvents { data object RetryFetchingContent : JoinRoomEvents data object JoinRoom : JoinRoomEvents data object KnockRoom : JoinRoomEvents - data object ClearError : JoinRoomEvents + data class CancelKnock(val requiresConfirmation: Boolean) : JoinRoomEvents + data class UpdateKnockMessage(val message: String) : JoinRoomEvents + data object ClearActionStates : JoinRoomEvents data object AcceptInvite : JoinRoomEvents data object DeclineInvite : JoinRoomEvents } diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomNode.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomNode.kt index c0835bfca2..44f4d8def0 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomNode.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomNode.kt @@ -43,7 +43,8 @@ class JoinRoomNode @AssistedInject constructor( state = state, onBackClick = ::navigateUp, onJoinSuccess = ::navigateUp, - onKnockSuccess = ::navigateUp, + onCancelKnockSuccess = ::navigateUp, + onKnockSuccess = { }, modifier = modifier ) acceptDeclineInviteView.Render( diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenter.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenter.kt index 8e0fa9193e..0b3e828275 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenter.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenter.kt @@ -17,6 +17,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.produceState import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import dagger.assisted.Assisted import dagger.assisted.AssistedInject @@ -24,6 +25,7 @@ import im.vector.app.features.analytics.plan.JoinedRoom import io.element.android.features.invite.api.response.AcceptDeclineInviteEvents import io.element.android.features.invite.api.response.AcceptDeclineInviteState import io.element.android.features.invite.api.response.InviteData +import io.element.android.features.joinroom.impl.di.CancelKnockRoom import io.element.android.features.joinroom.impl.di.KnockRoom import io.element.android.features.roomdirectory.api.RoomDescription import io.element.android.libraries.architecture.AsyncAction @@ -46,6 +48,8 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import java.util.Optional +private const val MAX_KNOCK_MESSAGE_LENGTH = 500 + class JoinRoomPresenter @AssistedInject constructor( @Assisted private val roomId: RoomId, @Assisted private val roomIdOrAlias: RoomIdOrAlias, @@ -55,6 +59,7 @@ class JoinRoomPresenter @AssistedInject constructor( private val matrixClient: MatrixClient, private val joinRoom: JoinRoom, private val knockRoom: KnockRoom, + private val cancelKnockRoom: CancelKnockRoom, private val acceptDeclineInvitePresenter: Presenter, private val buildMeta: BuildMeta, ) : Presenter { @@ -75,6 +80,8 @@ class JoinRoomPresenter @AssistedInject constructor( val roomInfo by matrixClient.getRoomInfoFlow(roomId.toRoomIdOrAlias()).collectAsState(initial = Optional.empty()) val joinAction: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } val knockAction: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } + val cancelKnockAction: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } + var knockMessage by rememberSaveable { mutableStateOf("") } val contentState by produceState( initialValue = ContentState.Loading(roomIdOrAlias), key1 = roomInfo, @@ -110,7 +117,7 @@ class JoinRoomPresenter @AssistedInject constructor( fun handleEvents(event: JoinRoomEvents) { when (event) { JoinRoomEvents.JoinRoom -> coroutineScope.joinRoom(joinAction) - JoinRoomEvents.KnockRoom -> coroutineScope.knockRoom(knockAction) + is JoinRoomEvents.KnockRoom -> coroutineScope.knockRoom(knockAction, knockMessage) JoinRoomEvents.AcceptInvite -> { val inviteData = contentState.toInviteData() ?: return acceptDeclineInviteState.eventSink( @@ -123,12 +130,17 @@ class JoinRoomPresenter @AssistedInject constructor( AcceptDeclineInviteEvents.DeclineInvite(inviteData) ) } + is JoinRoomEvents.CancelKnock -> coroutineScope.cancelKnockRoom(event.requiresConfirmation, cancelKnockAction) JoinRoomEvents.RetryFetchingContent -> { retryCount++ } - JoinRoomEvents.ClearError -> { + JoinRoomEvents.ClearActionStates -> { knockAction.value = AsyncAction.Uninitialized joinAction.value = AsyncAction.Uninitialized + cancelKnockAction.value = AsyncAction.Uninitialized + } + is JoinRoomEvents.UpdateKnockMessage -> { + knockMessage = event.message.take(MAX_KNOCK_MESSAGE_LENGTH) } } } @@ -138,7 +150,9 @@ class JoinRoomPresenter @AssistedInject constructor( acceptDeclineInviteState = acceptDeclineInviteState, joinAction = joinAction.value, knockAction = knockAction.value, + cancelKnockAction = cancelKnockAction.value, applicationName = buildMeta.applicationName, + knockMessage = knockMessage, eventSink = ::handleEvents ) } @@ -153,9 +167,19 @@ class JoinRoomPresenter @AssistedInject constructor( } } - private fun CoroutineScope.knockRoom(knockAction: MutableState>) = launch { + private fun CoroutineScope.knockRoom(knockAction: MutableState>, message: String) = launch { knockAction.runUpdatingState { - knockRoom(roomId) + knockRoom(roomIdOrAlias, message, serverNames) + } + } + + private fun CoroutineScope.cancelKnockRoom(requiresConfirmation: Boolean, cancelKnockAction: MutableState>) = launch { + if (requiresConfirmation) { + cancelKnockAction.value = AsyncAction.ConfirmingNoParams + } else { + cancelKnockAction.runUpdatingState { + cancelKnockRoom(roomId) + } } } } @@ -206,7 +230,7 @@ internal fun MatrixRoomInfo.toContentState(): ContentState { name = name, topic = topic, alias = canonicalAlias, - numberOfMembers = activeMembersCount.toLong(), + numberOfMembers = activeMembersCount, isDm = isDm, roomType = if (isSpace) RoomType.Space else RoomType.Room, roomAvatarUrl = avatarUrl, @@ -214,6 +238,7 @@ internal fun MatrixRoomInfo.toContentState(): ContentState { currentUserMembership == CurrentUserMembership.INVITED -> JoinAuthorisationStatus.IsInvited( inviteSender = inviter?.toInviteSender() ) + currentUserMembership == CurrentUserMembership.KNOCKED -> JoinAuthorisationStatus.IsKnocked isPublic -> JoinAuthorisationStatus.CanJoin else -> JoinAuthorisationStatus.Unknown } diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomState.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomState.kt index c37867fc46..6049e6cd5a 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomState.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomState.kt @@ -24,7 +24,9 @@ data class JoinRoomState( val acceptDeclineInviteState: AcceptDeclineInviteState, val joinAction: AsyncAction, val knockAction: AsyncAction, + val cancelKnockAction: AsyncAction, val applicationName: String, + val knockMessage: String, val eventSink: (JoinRoomEvents) -> Unit ) { val joinAuthorisationStatus = when (contentState) { @@ -68,6 +70,7 @@ sealed interface ContentState { sealed interface JoinAuthorisationStatus { data class IsInvited(val inviteSender: InviteSender?) : JoinAuthorisationStatus + data object IsKnocked : JoinAuthorisationStatus data object CanKnock : JoinAuthorisationStatus data object CanJoin : JoinAuthorisationStatus data object Unknown : JoinAuthorisationStatus diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomStateProvider.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomStateProvider.kt index a560b5fea8..33dcf786e5 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomStateProvider.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomStateProvider.kt @@ -81,6 +81,12 @@ open class JoinRoomStateProvider : PreviewParameterProvider { isDm = true, ) ), + aJoinRoomState( + contentState = aLoadedContentState( + name = "A knocked Room", + joinAuthorisationStatus = JoinAuthorisationStatus.IsKnocked + ) + ) ) } @@ -124,13 +130,17 @@ fun aJoinRoomState( acceptDeclineInviteState: AcceptDeclineInviteState = anAcceptDeclineInviteState(), joinAction: AsyncAction = AsyncAction.Uninitialized, knockAction: AsyncAction = AsyncAction.Uninitialized, + cancelKnockAction: AsyncAction = AsyncAction.Uninitialized, + knockMessage: String = "", eventSink: (JoinRoomEvents) -> Unit = {} ) = JoinRoomState( contentState = contentState, acceptDeclineInviteState = acceptDeclineInviteState, joinAction = joinAction, knockAction = knockAction, + cancelKnockAction = cancelKnockAction, applicationName = "AppName", + knockMessage = knockMessage, eventSink = eventSink ) diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt index 6d86227e07..b73742c05b 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt @@ -9,21 +9,31 @@ package io.element.android.features.joinroom.impl import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.sizeIn +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme @@ -32,20 +42,25 @@ import io.element.android.libraries.designsystem.atomic.atoms.RoomPreviewDescrip import io.element.android.libraries.designsystem.atomic.atoms.RoomPreviewSubtitleAtom import io.element.android.libraries.designsystem.atomic.atoms.RoomPreviewTitleAtom import io.element.android.libraries.designsystem.atomic.molecules.ButtonRowMolecule +import io.element.android.libraries.designsystem.atomic.molecules.IconTitlePlaceholdersRowMolecule +import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule import io.element.android.libraries.designsystem.atomic.molecules.RoomPreviewMembersCountMolecule import io.element.android.libraries.designsystem.atomic.organisms.RoomPreviewOrganism import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage import io.element.android.libraries.designsystem.background.LightGradientBackground +import io.element.android.libraries.designsystem.components.BigIcon import io.element.android.libraries.designsystem.components.async.AsyncActionView import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.button.SuperButton +import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.ButtonSize import io.element.android.libraries.designsystem.theme.components.OutlinedButton +import io.element.android.libraries.designsystem.theme.components.OutlinedTextField import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.matrix.api.core.RoomIdOrAlias @@ -59,6 +74,7 @@ fun JoinRoomView( onBackClick: () -> Unit, onJoinSuccess: () -> Unit, onKnockSuccess: () -> Unit, + onCancelKnockSuccess: () -> Unit, modifier: Modifier = Modifier, ) { Box( @@ -69,12 +85,14 @@ fun JoinRoomView( containerColor = Color.Transparent, paddingValues = PaddingValues(16.dp), topBar = { - JoinRoomTopBar(onBackClick = onBackClick) + JoinRoomTopBar(contentState = state.contentState, onBackClick = onBackClick) }, content = { JoinRoomContent( contentState = state.contentState, applicationName = state.applicationName, + knockMessage = state.knockMessage, + onKnockMessageUpdate = { state.eventSink(JoinRoomEvents.UpdateKnockMessage(it)) }, ) }, footer = { @@ -92,6 +110,9 @@ fun JoinRoomView( onKnockRoom = { state.eventSink(JoinRoomEvents.KnockRoom) }, + onCancelKnock = { + state.eventSink(JoinRoomEvents.CancelKnock(requiresConfirmation = true)) + }, onRetry = { state.eventSink(JoinRoomEvents.RetryFetchingContent) }, @@ -103,12 +124,30 @@ fun JoinRoomView( AsyncActionView( async = state.joinAction, onSuccess = { onJoinSuccess() }, - onErrorDismiss = { state.eventSink(JoinRoomEvents.ClearError) }, + onErrorDismiss = { state.eventSink(JoinRoomEvents.ClearActionStates) }, ) AsyncActionView( async = state.knockAction, onSuccess = { onKnockSuccess() }, - onErrorDismiss = { state.eventSink(JoinRoomEvents.ClearError) }, + onErrorDismiss = { state.eventSink(JoinRoomEvents.ClearActionStates) }, + ) + AsyncActionView( + async = state.cancelKnockAction, + onSuccess = { onCancelKnockSuccess() }, + onErrorDismiss = { state.eventSink(JoinRoomEvents.ClearActionStates) }, + errorMessage = { + stringResource(CommonStrings.error_unknown) + }, + confirmationDialog = { + ConfirmationDialog( + content = stringResource(R.string.screen_join_room_cancel_knock_alert_description), + title = stringResource(R.string.screen_join_room_cancel_knock_alert_title), + submitText = stringResource(R.string.screen_join_room_cancel_knock_alert_confirmation), + cancelText = stringResource(CommonStrings.action_no), + onSubmitClick = { state.eventSink(JoinRoomEvents.CancelKnock(requiresConfirmation = false)) }, + onDismiss = { state.eventSink(JoinRoomEvents.ClearActionStates) }, + ) + }, ) } @@ -119,63 +158,81 @@ private fun JoinRoomFooter( onDeclineInvite: () -> Unit, onJoinRoom: () -> Unit, onKnockRoom: () -> Unit, + onCancelKnock: () -> Unit, onRetry: () -> Unit, onGoBack: () -> Unit, modifier: Modifier = Modifier, ) { - if (state.contentState is ContentState.Failure) { - Button( - text = stringResource(CommonStrings.action_retry), - onClick = onRetry, - modifier = modifier.fillMaxWidth(), - size = ButtonSize.Large, - ) - } else if (state.contentState is ContentState.Loaded && state.contentState.roomType == RoomType.Space) { - Button( - text = stringResource(CommonStrings.action_go_back), - onClick = onGoBack, - modifier = modifier.fillMaxWidth(), - size = ButtonSize.Large, - ) - } else { - val joinAuthorisationStatus = state.joinAuthorisationStatus - when (joinAuthorisationStatus) { - is JoinAuthorisationStatus.IsInvited -> { - ButtonRowMolecule(modifier = modifier, horizontalArrangement = Arrangement.spacedBy(20.dp)) { - OutlinedButton( - text = stringResource(CommonStrings.action_decline), - onClick = onDeclineInvite, - modifier = Modifier.weight(1f), - size = ButtonSize.LargeLowPadding, - ) - Button( - text = stringResource(CommonStrings.action_accept), - onClick = onAcceptInvite, - modifier = Modifier.weight(1f), - size = ButtonSize.LargeLowPadding, - ) + Box( + modifier = modifier + .fillMaxWidth() + .padding(top = 8.dp) + ) { + if (state.contentState is ContentState.Failure) { + Button( + text = stringResource(CommonStrings.action_retry), + onClick = onRetry, + modifier = Modifier.fillMaxWidth(), + size = ButtonSize.Large, + ) + } else if (state.contentState is ContentState.Loaded && state.contentState.roomType == RoomType.Space) { + Button( + text = stringResource(CommonStrings.action_go_back), + onClick = onGoBack, + modifier = Modifier.fillMaxWidth(), + size = ButtonSize.Large, + ) + } else { + val joinAuthorisationStatus = state.joinAuthorisationStatus + when (joinAuthorisationStatus) { + is JoinAuthorisationStatus.IsInvited -> { + ButtonRowMolecule(horizontalArrangement = Arrangement.spacedBy(20.dp)) { + OutlinedButton( + text = stringResource(CommonStrings.action_decline), + onClick = onDeclineInvite, + modifier = Modifier.weight(1f), + size = ButtonSize.LargeLowPadding, + ) + Button( + text = stringResource(CommonStrings.action_accept), + onClick = onAcceptInvite, + modifier = Modifier.weight(1f), + size = ButtonSize.LargeLowPadding, + ) + } } - } - JoinAuthorisationStatus.CanJoin -> { - SuperButton( - onClick = onJoinRoom, - modifier = modifier.fillMaxWidth(), - buttonSize = ButtonSize.Large, - ) { - Text( - text = stringResource(R.string.screen_join_room_join_action), + JoinAuthorisationStatus.CanJoin -> { + SuperButton( + onClick = onJoinRoom, + modifier = Modifier.fillMaxWidth(), + buttonSize = ButtonSize.Large, + ) { + Text( + text = stringResource(R.string.screen_join_room_join_action), + ) + } + } + JoinAuthorisationStatus.CanKnock -> { + SuperButton( + onClick = onKnockRoom, + modifier = Modifier.fillMaxWidth(), + buttonSize = ButtonSize.Large, + ) { + Text( + text = stringResource(R.string.screen_join_room_knock_action), + ) + } + } + JoinAuthorisationStatus.IsKnocked -> { + OutlinedButton( + text = stringResource(R.string.screen_join_room_cancel_knock_action), + onClick = onCancelKnock, + modifier = Modifier.fillMaxWidth(), + size = ButtonSize.Large, ) } + JoinAuthorisationStatus.Unknown -> Unit } - JoinAuthorisationStatus.CanKnock -> { - Button( - text = stringResource(R.string.screen_join_room_knock_action), - onClick = onKnockRoom, - modifier = modifier.fillMaxWidth(), - size = ButtonSize.Large, - ) - } - JoinAuthorisationStatus.Unknown -> Unit } } } @@ -184,132 +241,217 @@ private fun JoinRoomFooter( private fun JoinRoomContent( contentState: ContentState, applicationName: String, + knockMessage: String, + onKnockMessageUpdate: (String) -> Unit, modifier: Modifier = Modifier, ) { - when (contentState) { - is ContentState.Loaded -> { - RoomPreviewOrganism( - modifier = modifier, - avatar = { - Avatar(contentState.avatarData(AvatarSize.RoomHeader)) - }, - title = { - if (contentState.name != null) { - RoomPreviewTitleAtom( - title = contentState.name, - ) - } else { - RoomPreviewTitleAtom( - title = stringResource(id = CommonStrings.common_no_room_name), - fontStyle = FontStyle.Italic - ) - } - }, - subtitle = { - if (contentState.alias != null) { - RoomPreviewSubtitleAtom(contentState.alias.value) + Box(modifier = modifier) { + when (contentState) { + is ContentState.Loaded -> { + when (contentState.joinAuthorisationStatus) { + is JoinAuthorisationStatus.IsKnocked -> { + IsKnockedLoadedContent() } - }, - description = { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(8.dp), - ) { - val inviteSender = (contentState.joinAuthorisationStatus as? JoinAuthorisationStatus.IsInvited)?.inviteSender - if (inviteSender != null) { - InviteSenderView(inviteSender = inviteSender) - } - RoomPreviewDescriptionAtom(contentState.topic ?: "") - if (contentState.roomType == RoomType.Space) { - Spacer(modifier = Modifier.height(24.dp)) - Text( - text = stringResource(R.string.screen_join_room_space_not_supported_title), - textAlign = TextAlign.Center, - style = ElementTheme.typography.fontBodyLgMedium, - color = MaterialTheme.colorScheme.primary, - ) - Text( - text = stringResource(R.string.screen_join_room_space_not_supported_description, applicationName), - textAlign = TextAlign.Center, - style = ElementTheme.typography.fontBodyMdRegular, - color = MaterialTheme.colorScheme.secondary, - ) - } - } - }, - memberCount = { - if (contentState.showMemberCount) { - RoomPreviewMembersCountMolecule(memberCount = contentState.numberOfMembers ?: 0) + else -> { + DefaultLoadedContent( + modifier = Modifier.verticalScroll(rememberScrollState()), + contentState = contentState, + applicationName = applicationName, + knockMessage = knockMessage, + onKnockMessageUpdate = onKnockMessageUpdate + ) } } - ) - } - is ContentState.UnknownRoom -> { - RoomPreviewOrganism( - modifier = modifier, - avatar = { - PlaceholderAtom(width = AvatarSize.RoomHeader.dp, height = AvatarSize.RoomHeader.dp) - }, - title = { - RoomPreviewTitleAtom(stringResource(R.string.screen_join_room_title_no_preview)) - }, - subtitle = { - RoomPreviewSubtitleAtom(stringResource(R.string.screen_join_room_subtitle_no_preview)) - }, - ) - } - is ContentState.Loading -> { - RoomPreviewOrganism( - modifier = modifier, - avatar = { - PlaceholderAtom(width = AvatarSize.RoomHeader.dp, height = AvatarSize.RoomHeader.dp) - }, - title = { - PlaceholderAtom(width = 200.dp, height = 22.dp) - }, - subtitle = { - PlaceholderAtom(width = 140.dp, height = 20.dp) - }, - ) - } - is ContentState.Failure -> { - RoomPreviewOrganism( - modifier = modifier, - avatar = { - PlaceholderAtom(width = AvatarSize.RoomHeader.dp, height = AvatarSize.RoomHeader.dp) - }, - title = { - when (contentState.roomIdOrAlias) { - is RoomIdOrAlias.Alias -> { - RoomPreviewTitleAtom(contentState.roomIdOrAlias.identifier) - } - is RoomIdOrAlias.Id -> { - PlaceholderAtom(width = 200.dp, height = 22.dp) + } + is ContentState.UnknownRoom -> { + RoomPreviewOrganism( + avatar = { + PlaceholderAtom(width = AvatarSize.RoomHeader.dp, height = AvatarSize.RoomHeader.dp) + }, + title = { + RoomPreviewTitleAtom(stringResource(R.string.screen_join_room_title_no_preview)) + }, + subtitle = { + RoomPreviewSubtitleAtom(stringResource(R.string.screen_join_room_subtitle_no_preview)) + }, + ) + } + is ContentState.Loading -> { + RoomPreviewOrganism( + avatar = { + PlaceholderAtom(width = AvatarSize.RoomHeader.dp, height = AvatarSize.RoomHeader.dp) + }, + title = { + PlaceholderAtom(width = 200.dp, height = 22.dp) + }, + subtitle = { + PlaceholderAtom(width = 140.dp, height = 20.dp) + }, + ) + } + is ContentState.Failure -> { + RoomPreviewOrganism( + avatar = { + PlaceholderAtom(width = AvatarSize.RoomHeader.dp, height = AvatarSize.RoomHeader.dp) + }, + title = { + when (contentState.roomIdOrAlias) { + is RoomIdOrAlias.Alias -> { + RoomPreviewTitleAtom(contentState.roomIdOrAlias.identifier) + } + is RoomIdOrAlias.Id -> { + PlaceholderAtom(width = 200.dp, height = 22.dp) + } } - } - }, - subtitle = { + }, + subtitle = { + Text( + text = stringResource(id = CommonStrings.error_unknown), + textAlign = TextAlign.Center, + color = MaterialTheme.colorScheme.error, + ) + }, + ) + } + } + } +} + +@Composable +private fun IsKnockedLoadedContent(modifier: Modifier = Modifier) { + BoxWithConstraints( + modifier = modifier + .fillMaxHeight() + .padding(horizontal = 16.dp), + contentAlignment = Alignment.Center, + ) { + IconTitleSubtitleMolecule( + modifier = Modifier.sizeIn(minHeight = maxHeight * 0.7f), + iconStyle = BigIcon.Style.SuccessSolid, + title = stringResource(R.string.screen_join_room_knock_sent_title), + subTitle = stringResource(R.string.screen_join_room_knock_sent_description), + ) + } +} + +@Composable +private fun DefaultLoadedContent( + contentState: ContentState.Loaded, + applicationName: String, + knockMessage: String, + onKnockMessageUpdate: (String) -> Unit, + modifier: Modifier = Modifier, +) { + RoomPreviewOrganism( + modifier = modifier, + avatar = { + Avatar(contentState.avatarData(AvatarSize.RoomHeader)) + }, + title = { + if (contentState.name != null) { + RoomPreviewTitleAtom( + title = contentState.name, + ) + } else { + RoomPreviewTitleAtom( + title = stringResource(id = CommonStrings.common_no_room_name), + fontStyle = FontStyle.Italic + ) + } + }, + subtitle = { + if (contentState.alias != null) { + RoomPreviewSubtitleAtom(contentState.alias.value) + } + }, + description = { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + val inviteSender = (contentState.joinAuthorisationStatus as? JoinAuthorisationStatus.IsInvited)?.inviteSender + if (inviteSender != null) { + InviteSenderView(inviteSender = inviteSender) + } + RoomPreviewDescriptionAtom(contentState.topic ?: "") + if (contentState.roomType == RoomType.Space) { + Spacer(modifier = Modifier.height(24.dp)) Text( - text = stringResource(id = CommonStrings.error_unknown), + text = stringResource(R.string.screen_join_room_space_not_supported_title), textAlign = TextAlign.Center, - color = MaterialTheme.colorScheme.error, + style = ElementTheme.typography.fontBodyLgMedium, + color = MaterialTheme.colorScheme.primary, ) - }, - ) + Text( + text = stringResource(R.string.screen_join_room_space_not_supported_description, applicationName), + textAlign = TextAlign.Center, + style = ElementTheme.typography.fontBodyMdRegular, + color = MaterialTheme.colorScheme.secondary, + ) + } else if (contentState.joinAuthorisationStatus is JoinAuthorisationStatus.CanKnock) { + Spacer(modifier = Modifier.height(24.dp)) + OutlinedTextField( + value = knockMessage, + onValueChange = onKnockMessageUpdate, + maxLines = 3, + minLines = 3, + modifier = Modifier.fillMaxWidth() + ) + Text( + text = stringResource(R.string.screen_join_room_knock_message_description), + style = ElementTheme.typography.fontBodySmRegular, + color = ElementTheme.colors.textPlaceholder, + textAlign = TextAlign.Start, + modifier = Modifier.fillMaxWidth() + ) + } + } + }, + memberCount = { + if (contentState.showMemberCount) { + RoomPreviewMembersCountMolecule(memberCount = contentState.numberOfMembers ?: 0) + } } - } + ) } @OptIn(ExperimentalMaterial3Api::class) @Composable private fun JoinRoomTopBar( + contentState: ContentState, onBackClick: () -> Unit, ) { TopAppBar( navigationIcon = { BackButton(onClick = onBackClick) }, - title = {}, + title = { + if (contentState is ContentState.Loaded && contentState.joinAuthorisationStatus is JoinAuthorisationStatus.IsKnocked) { + val roundedCornerShape = RoundedCornerShape(8.dp) + val titleModifier = Modifier + .clip(roundedCornerShape) + if (contentState.name != null) { + Row( + modifier = titleModifier, + verticalAlignment = Alignment.CenterVertically + ) { + Avatar(avatarData = contentState.avatarData(AvatarSize.TimelineRoom)) + Text( + modifier = Modifier.padding(horizontal = 8.dp), + text = contentState.name, + style = ElementTheme.typography.fontBodyLgMedium, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } + } else { + IconTitlePlaceholdersRowMolecule( + iconSize = AvatarSize.TimelineRoom.dp, + modifier = titleModifier + ) + } + } + }, ) } @@ -321,5 +463,6 @@ internal fun JoinRoomViewPreview(@PreviewParameter(JoinRoomStateProvider::class) onBackClick = { }, onJoinSuccess = { }, onKnockSuccess = { }, + onCancelKnockSuccess = { }, ) } diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/di/CancelKnockRoom.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/di/CancelKnockRoom.kt new file mode 100644 index 0000000000..cf928ea30a --- /dev/null +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/di/CancelKnockRoom.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.joinroom.impl.di + +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.libraries.di.SessionScope +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.core.RoomId +import javax.inject.Inject + +interface CancelKnockRoom { + suspend operator fun invoke(roomId: RoomId): Result +} + +@ContributesBinding(SessionScope::class) +class DefaultCancelKnockRoom @Inject constructor(private val client: MatrixClient) : CancelKnockRoom { + override suspend fun invoke(roomId: RoomId): Result { + return client + .getPendingRoom(roomId) + ?.leave() + ?: Result.failure(IllegalStateException("No pending room found")) + } +} diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/di/JoinRoomModule.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/di/JoinRoomModule.kt index db710b66ad..e981d6c46b 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/di/JoinRoomModule.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/di/JoinRoomModule.kt @@ -31,6 +31,7 @@ object JoinRoomModule { client: MatrixClient, joinRoom: JoinRoom, knockRoom: KnockRoom, + cancelKnockRoom: CancelKnockRoom, acceptDeclineInvitePresenter: Presenter, buildMeta: BuildMeta, ): JoinRoomPresenter.Factory { @@ -51,6 +52,7 @@ object JoinRoomModule { matrixClient = client, joinRoom = joinRoom, knockRoom = knockRoom, + cancelKnockRoom = cancelKnockRoom, acceptDeclineInvitePresenter = acceptDeclineInvitePresenter, buildMeta = buildMeta, ) diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/di/KnockRoom.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/di/KnockRoom.kt index b2fc94cdd6..9b82aa43e2 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/di/KnockRoom.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/di/KnockRoom.kt @@ -10,14 +10,26 @@ package io.element.android.features.joinroom.impl.di import com.squareup.anvil.annotations.ContributesBinding import io.element.android.libraries.di.SessionScope 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.RoomIdOrAlias import javax.inject.Inject interface KnockRoom { - suspend operator fun invoke(roomId: RoomId): Result + suspend operator fun invoke( + roomIdOrAlias: RoomIdOrAlias, + message: String, + serverNames: List, + ): Result } @ContributesBinding(SessionScope::class) class DefaultKnockRoom @Inject constructor(private val client: MatrixClient) : KnockRoom { - override suspend fun invoke(roomId: RoomId) = client.knockRoom(roomId) + override suspend fun invoke( + roomIdOrAlias: RoomIdOrAlias, + message: String, + serverNames: List + ): Result { + return client + .knockRoom(roomIdOrAlias, message, serverNames) + .map { } + } } diff --git a/features/joinroom/impl/src/main/res/values/localazy.xml b/features/joinroom/impl/src/main/res/values/localazy.xml index 86ed3c0cc3..d744f0b31f 100644 --- a/features/joinroom/impl/src/main/res/values/localazy.xml +++ b/features/joinroom/impl/src/main/res/values/localazy.xml @@ -1,7 +1,14 @@ + "Cancel request" + "Yes, cancel" + "Are you sure that you want to cancel your request to join this room?" + "Cancel request to join" "Join room" "Send request to join" + "Message (optional)" + "You will receive an invite to join the room if your request is accepted." + "Request to join sent" "%1$s does not support spaces yet. You can access spaces on web." "Spaces are not supported yet" "Click the button below and a room administrator will be notified. You’ll be able to join the conversation once approved." diff --git a/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/FakeCancelKnockRoom.kt b/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/FakeCancelKnockRoom.kt new file mode 100644 index 0000000000..baf7f10307 --- /dev/null +++ b/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/FakeCancelKnockRoom.kt @@ -0,0 +1,20 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.joinroom.impl + +import io.element.android.features.joinroom.impl.di.CancelKnockRoom +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.tests.testutils.simulateLongTask + +class FakeCancelKnockRoom( + var lambda: (RoomId) -> Result = { Result.success(Unit) } +) : CancelKnockRoom { + override suspend fun invoke(roomId: RoomId) = simulateLongTask { + lambda(roomId) + } +} diff --git a/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/FakeKnockRoom.kt b/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/FakeKnockRoom.kt index e60707d165..991a35554e 100644 --- a/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/FakeKnockRoom.kt +++ b/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/FakeKnockRoom.kt @@ -8,13 +8,13 @@ package io.element.android.features.joinroom.impl import io.element.android.features.joinroom.impl.di.KnockRoom -import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.core.RoomIdOrAlias import io.element.android.tests.testutils.simulateLongTask class FakeKnockRoom( - var lambda: (RoomId) -> Result = { Result.success(Unit) } + var lambda: (RoomIdOrAlias, String, List) -> Result = { _, _, _ -> Result.success(Unit) } ) : KnockRoom { - override suspend fun invoke(roomId: RoomId) = simulateLongTask { - lambda(roomId) + override suspend fun invoke(roomIdOrAlias: RoomIdOrAlias, message: String, serverNames: List): Result = simulateLongTask { + lambda(roomIdOrAlias, message, serverNames) } } diff --git a/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenterTest.kt b/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenterTest.kt index 0a323122ac..48bf792447 100644 --- a/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenterTest.kt +++ b/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenterTest.kt @@ -12,6 +12,7 @@ import im.vector.app.features.analytics.plan.JoinedRoom import io.element.android.features.invite.api.response.AcceptDeclineInviteEvents import io.element.android.features.invite.api.response.AcceptDeclineInviteState import io.element.android.features.invite.api.response.anAcceptDeclineInviteState +import io.element.android.features.joinroom.impl.di.CancelKnockRoom import io.element.android.features.joinroom.impl.di.KnockRoom import io.element.android.features.roomdirectory.api.RoomDescription import io.element.android.libraries.architecture.AsyncAction @@ -37,6 +38,7 @@ import io.element.android.libraries.matrix.test.room.aRoomSummary import io.element.android.libraries.matrix.test.room.join.FakeJoinRoom import io.element.android.libraries.matrix.ui.model.toInviteSender import io.element.android.tests.testutils.WarmUpRule +import io.element.android.tests.testutils.lambda.any import io.element.android.tests.testutils.lambda.assert import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.value @@ -59,6 +61,8 @@ class JoinRoomPresenterTest { assertThat(state.contentState).isEqualTo(ContentState.Loading(A_ROOM_ID.toRoomIdOrAlias())) assertThat(state.joinAuthorisationStatus).isEqualTo(JoinAuthorisationStatus.Unknown) assertThat(state.acceptDeclineInviteState).isEqualTo(anAcceptDeclineInviteState()) + assertThat(state.cancelKnockAction).isEqualTo(AsyncAction.Uninitialized) + assertThat(state.knockAction).isEqualTo(AsyncAction.Uninitialized) assertThat(state.applicationName).isEqualTo("AppName") cancelAndIgnoreRemainingEvents() } @@ -214,7 +218,7 @@ class JoinRoomPresenterTest { } awaitItem().also { state -> assertThat(state.joinAction).isEqualTo(AsyncAction.Failure(AN_EXCEPTION)) - state.eventSink(JoinRoomEvents.ClearError) + state.eventSink(JoinRoomEvents.ClearActionStates) } awaitItem().also { state -> assertThat(state.joinAction).isEqualTo(AsyncAction.Uninitialized) @@ -325,16 +329,20 @@ class JoinRoomPresenterTest { @Test fun `present - emit knock room event`() = runTest { - val knockRoomSuccess = lambdaRecorder { _: RoomId -> + val knockMessage = "Knock message" + val knockRoomSuccess = lambdaRecorder { _: RoomIdOrAlias, _: String, _: List -> Result.success(Unit) } - val knockRoomFailure = lambdaRecorder { roomId: RoomId -> - Result.failure(RuntimeException("Failed to knock room $roomId")) + val knockRoomFailure = lambdaRecorder { roomIdOrAlias: RoomIdOrAlias, _: String, _: List -> + Result.failure(RuntimeException("Failed to knock room $roomIdOrAlias")) } val fakeKnockRoom = FakeKnockRoom(knockRoomSuccess) val presenter = createJoinRoomPresenter(knockRoom = fakeKnockRoom) presenter.test { skipItems(1) + awaitItem().also { state -> + state.eventSink(JoinRoomEvents.UpdateKnockMessage(knockMessage)) + } awaitItem().also { state -> state.eventSink(JoinRoomEvents.KnockRoom) } @@ -353,8 +361,46 @@ class JoinRoomPresenterTest { } assert(knockRoomSuccess) .isCalledOnce() - .with(value(A_ROOM_ID)) + .with(value(A_ROOM_ID.toRoomIdOrAlias()), value(knockMessage), any()) assert(knockRoomFailure) + .isCalledOnce() + .with(value(A_ROOM_ID.toRoomIdOrAlias()), value(knockMessage), any()) + } + + @Test + fun `present - emit cancel knock room event`() = runTest { + val cancelKnockRoomSuccess = lambdaRecorder { _: RoomId -> + Result.success(Unit) + } + val cancelKnockRoomFailure = lambdaRecorder { roomId: RoomId -> + Result.failure(RuntimeException("Failed to knock room $roomId")) + } + val cancelKnockRoom = FakeCancelKnockRoom(cancelKnockRoomSuccess) + val presenter = createJoinRoomPresenter(cancelKnockRoom = cancelKnockRoom) + presenter.test { + skipItems(1) + awaitItem().also { state -> + state.eventSink(JoinRoomEvents.CancelKnock(true)) + } + awaitItem().also { state -> + assertThat(state.cancelKnockAction).isEqualTo(AsyncAction.ConfirmingNoParams) + state.eventSink(JoinRoomEvents.CancelKnock(false)) + } + assertThat(awaitItem().cancelKnockAction).isEqualTo(AsyncAction.Loading) + awaitItem().also { state -> + assertThat(state.cancelKnockAction).isEqualTo(AsyncAction.Success(Unit)) + cancelKnockRoom.lambda = cancelKnockRoomFailure + state.eventSink(JoinRoomEvents.CancelKnock(false)) + } + assertThat(awaitItem().cancelKnockAction).isEqualTo(AsyncAction.Loading) + awaitItem().also { state -> + assertThat(state.cancelKnockAction).isInstanceOf(AsyncAction.Failure::class.java) + } + } + assert(cancelKnockRoomFailure) + .isCalledOnce() + .with(value(A_ROOM_ID)) + assert(cancelKnockRoomSuccess) .isCalledOnce() .with(value(A_ROOM_ID)) } @@ -474,6 +520,7 @@ class JoinRoomPresenterTest { Result.success(Unit) }, knockRoom: KnockRoom = FakeKnockRoom(), + cancelKnockRoom: CancelKnockRoom = FakeCancelKnockRoom(), buildMeta: BuildMeta = aBuildMeta(applicationName = "AppName"), acceptDeclineInvitePresenter: Presenter = Presenter { anAcceptDeclineInviteState() } ): JoinRoomPresenter { @@ -486,6 +533,7 @@ class JoinRoomPresenterTest { matrixClient = matrixClient, joinRoom = FakeJoinRoom(joinRoomLambda), knockRoom = knockRoom, + cancelKnockRoom = cancelKnockRoom, buildMeta = buildMeta, acceptDeclineInvitePresenter = acceptDeclineInvitePresenter ) diff --git a/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomViewTest.kt b/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomViewTest.kt index a120b74e9e..70857c52fc 100644 --- a/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomViewTest.kt +++ b/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomViewTest.kt @@ -61,6 +61,7 @@ class JoinRoomViewTest { rule.setJoinRoomView( aJoinRoomState( contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.CanKnock), + knockMessage = "Knock knock", eventSink = eventsRecorder, ), ) @@ -79,7 +80,34 @@ class JoinRoomViewTest { ), ) rule.clickOn(CommonStrings.action_ok) - eventsRecorder.assertSingle(JoinRoomEvents.ClearError) + eventsRecorder.assertSingle(JoinRoomEvents.ClearActionStates) + } + + @Test + fun `clicking on cancel knock request emit the expected Event`() { + val eventsRecorder = EventsRecorder() + rule.setJoinRoomView( + aJoinRoomState( + contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.IsKnocked), + eventSink = eventsRecorder, + ), + ) + rule.clickOn(R.string.screen_join_room_cancel_knock_action) + eventsRecorder.assertSingle(JoinRoomEvents.CancelKnock(true)) + } + + @Test + fun `clicking on closing Cancel Knock error emits the expected Event`() { + val eventsRecorder = EventsRecorder() + rule.setJoinRoomView( + aJoinRoomState( + contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.IsKnocked), + cancelKnockAction = AsyncAction.Failure(Exception("Error")), + eventSink = eventsRecorder, + ), + ) + rule.clickOn(CommonStrings.action_ok) + eventsRecorder.assertSingle(JoinRoomEvents.ClearActionStates) } @Test @@ -93,7 +121,7 @@ class JoinRoomViewTest { ), ) rule.clickOn(CommonStrings.action_ok) - eventsRecorder.assertSingle(JoinRoomEvents.ClearError) + eventsRecorder.assertSingle(JoinRoomEvents.ClearActionStates) } @Test @@ -170,6 +198,7 @@ private fun AndroidComposeTestRule.setJoinR onBackClick: () -> Unit = EnsureNeverCalled(), onJoinSuccess: () -> Unit = EnsureNeverCalled(), onKnockSuccess: () -> Unit = EnsureNeverCalled(), + onCancelKnockSuccess: () -> Unit = EnsureNeverCalled(), ) { setContent { JoinRoomView( @@ -177,6 +206,7 @@ private fun AndroidComposeTestRule.setJoinR onBackClick = onBackClick, onJoinSuccess = onJoinSuccess, onKnockSuccess = onKnockSuccess, + onCancelKnockSuccess = onCancelKnockSuccess ) } } diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt index 460871c62b..dce7acf2f0 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt @@ -12,6 +12,7 @@ import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement.Absolute.spacedBy +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.IntrinsicSize @@ -38,6 +39,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons +import io.element.android.features.roomlist.impl.R import io.element.android.features.roomlist.impl.RoomListEvents import io.element.android.features.roomlist.impl.model.RoomListRoomSummary import io.element.android.features.roomlist.impl.model.RoomListRoomSummaryProvider @@ -72,54 +74,86 @@ internal fun RoomSummaryRow( eventSink: (RoomListEvents) -> Unit, modifier: Modifier = Modifier, ) { - when (room.displayType) { - RoomSummaryDisplayType.PLACEHOLDER -> { - RoomSummaryPlaceholderRow(modifier = modifier) - } - RoomSummaryDisplayType.INVITE -> { - RoomSummaryScaffoldRow( - room = room, - onClick = onClick, - onLongClick = { - Timber.d("Long click on invite room") - }, - modifier = modifier - ) { - InviteNameAndIndicatorRow(name = room.name) - InviteSubtitle(isDm = room.isDm, inviteSender = room.inviteSender, canonicalAlias = room.canonicalAlias) - if (!room.isDm && room.inviteSender != null) { - Spacer(modifier = Modifier.height(4.dp)) - InviteSenderView( - modifier = Modifier.fillMaxWidth(), - inviteSender = room.inviteSender, + Box(modifier = modifier) { + when (room.displayType) { + RoomSummaryDisplayType.PLACEHOLDER -> { + RoomSummaryPlaceholderRow() + } + RoomSummaryDisplayType.INVITE -> { + RoomSummaryScaffoldRow( + room = room, + onClick = onClick, + onLongClick = { + Timber.d("Long click on invite room") + }, + ) { + InviteNameAndIndicatorRow(name = room.name) + InviteSubtitle(isDm = room.isDm, inviteSender = room.inviteSender, canonicalAlias = room.canonicalAlias) + if (!room.isDm && room.inviteSender != null) { + Spacer(modifier = Modifier.height(4.dp)) + InviteSenderView( + modifier = Modifier.fillMaxWidth(), + inviteSender = room.inviteSender, + ) + } + Spacer(modifier = Modifier.height(12.dp)) + InviteButtonsRow( + onAcceptClick = { + eventSink(RoomListEvents.AcceptInvite(room)) + }, + onDeclineClick = { + eventSink(RoomListEvents.DeclineInvite(room)) + } ) } - Spacer(modifier = Modifier.height(12.dp)) - InviteButtonsRow( - onAcceptClick = { - eventSink(RoomListEvents.AcceptInvite(room)) + } + RoomSummaryDisplayType.ROOM -> { + RoomSummaryScaffoldRow( + room = room, + onClick = onClick, + onLongClick = { + eventSink(RoomListEvents.ShowContextMenu(room)) }, - onDeclineClick = { - eventSink(RoomListEvents.DeclineInvite(room)) - } - ) + ) { + NameAndTimestampRow( + name = room.name, + timestamp = room.timestamp, + isHighlighted = room.isHighlighted + ) + LastMessageAndIndicatorRow(room = room) + } } - } - RoomSummaryDisplayType.ROOM -> { - RoomSummaryScaffoldRow( - room = room, - onClick = onClick, - onLongClick = { - eventSink(RoomListEvents.ShowContextMenu(room)) - }, - modifier = modifier - ) { - NameAndTimestampRow( - name = room.name, - timestamp = room.timestamp, - isHighlighted = room.isHighlighted - ) - LastMessageAndIndicatorRow(room = room) + RoomSummaryDisplayType.KNOCKED -> { + RoomSummaryScaffoldRow( + room = room, + onClick = onClick, + onLongClick = { + Timber.d("Long click on knocked room") + }, + ) { + NameAndTimestampRow( + name = room.name, + timestamp = null, + isHighlighted = room.isHighlighted + ) + if (room.canonicalAlias != null) { + Text( + text = room.canonicalAlias.value, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + style = ElementTheme.typography.fontBodyMdRegular, + color = ElementTheme.colors.textSecondary, + ) + Spacer(modifier = Modifier.height(4.dp)) + } + Text( + text = stringResource(id = R.string.screen_join_room_knock_sent_title), + maxLines = 1, + overflow = TextOverflow.Ellipsis, + style = ElementTheme.typography.fontBodyMdRegular, + color = ElementTheme.colors.textSecondary, + ) + } } } } diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListRoomSummaryFactory.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListRoomSummaryFactory.kt index 004ce9174b..534de3c4d4 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListRoomSummaryFactory.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListRoomSummaryFactory.kt @@ -48,10 +48,16 @@ class RoomListRoomSummaryFactory @Inject constructor( inviteSender = roomInfo.inviter?.toInviteSender(), isDm = roomInfo.isDm, canonicalAlias = roomInfo.canonicalAlias, - displayType = if (roomInfo.currentUserMembership == CurrentUserMembership.INVITED) { - RoomSummaryDisplayType.INVITE - } else { - RoomSummaryDisplayType.ROOM + displayType = when (roomInfo.currentUserMembership) { + CurrentUserMembership.INVITED -> { + RoomSummaryDisplayType.INVITE + } + CurrentUserMembership.KNOCKED -> { + RoomSummaryDisplayType.KNOCKED + } + else -> { + RoomSummaryDisplayType.ROOM + } }, heroes = roomInfo.heroes.map { user -> user.getAvatarData(size = AvatarSize.RoomListItem) diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryProvider.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryProvider.kt index 894bc46377..55cd89d0ce 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryProvider.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryProvider.kt @@ -102,6 +102,15 @@ open class RoomListRoomSummaryProvider : PreviewParameterProvider"Decline chat" "No Invites" "%1$s (%2$s) invited you" + "Request to join sent" "This is a one time process, thanks for waiting." "Setting up your account." "Create a new conversation or room" diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt index ea26e29719..6af1763e83 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt @@ -21,9 +21,9 @@ import io.element.android.libraries.matrix.api.notification.NotificationService import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService import io.element.android.libraries.matrix.api.oidc.AccountManagementAction import io.element.android.libraries.matrix.api.pusher.PushersService -import io.element.android.libraries.matrix.api.room.InvitedRoom import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoomInfo +import io.element.android.libraries.matrix.api.room.PendingRoom import io.element.android.libraries.matrix.api.room.RoomMembershipObserver import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias import io.element.android.libraries.matrix.api.room.preview.RoomPreview @@ -52,7 +52,7 @@ interface MatrixClient : Closeable { val sessionCoroutineScope: CoroutineScope val ignoredUsersFlow: StateFlow> suspend fun getRoom(roomId: RoomId): MatrixRoom? - suspend fun getInvitedRoom(roomId: RoomId): InvitedRoom? + suspend fun getPendingRoom(roomId: RoomId): PendingRoom? suspend fun findDM(userId: UserId): RoomId? suspend fun ignoreUser(userId: UserId): Result suspend fun unignoreUser(userId: UserId): Result @@ -65,7 +65,7 @@ interface MatrixClient : Closeable { suspend fun removeAvatar(): Result suspend fun joinRoom(roomId: RoomId): Result suspend fun joinRoomByIdOrAlias(roomIdOrAlias: RoomIdOrAlias, serverNames: List): Result - suspend fun knockRoom(roomId: RoomId): Result + suspend fun knockRoom(roomIdOrAlias: RoomIdOrAlias, message: String, serverNames: List): Result fun syncService(): SyncService fun sessionVerificationService(): SessionVerificationService fun pushersService(): PushersService diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/InvitedRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/PendingRoom.kt similarity index 59% rename from libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/InvitedRoom.kt rename to libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/PendingRoom.kt index 7e1dd5d10d..273e038313 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/InvitedRoom.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/PendingRoom.kt @@ -10,11 +10,11 @@ package io.element.android.libraries.matrix.api.room import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId -/** A reference to a room the current user has been invited to, with the ability to decline the invite. */ -interface InvitedRoom : AutoCloseable { +/** A reference to a room the current user has knocked to or has been invited to, with the ability to leave the room. */ +interface PendingRoom : AutoCloseable { val sessionId: SessionId val roomId: RoomId - /** Decline the invite to this room. */ - suspend fun declineInvite(): Result + /** Leave the room ie.decline invite or cancel knock. */ + suspend fun leave(): Result } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index 1abe47a362..b7cd2d2061 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -30,8 +30,8 @@ import io.element.android.libraries.matrix.api.notificationsettings.Notification import io.element.android.libraries.matrix.api.oidc.AccountManagementAction import io.element.android.libraries.matrix.api.pusher.PushersService import io.element.android.libraries.matrix.api.room.CurrentUserMembership -import io.element.android.libraries.matrix.api.room.InvitedRoom import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.api.room.PendingRoom import io.element.android.libraries.matrix.api.room.RoomMembershipObserver import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias import io.element.android.libraries.matrix.api.room.preview.RoomPreview @@ -251,24 +251,26 @@ class RustMatrixClient( return roomFactory.create(roomId) } - override suspend fun getInvitedRoom(roomId: RoomId): InvitedRoom? { - return roomFactory.createInvitedRoom(roomId) + override suspend fun getPendingRoom(roomId: RoomId): PendingRoom? { + return roomFactory.createPendingRoom(roomId) } /** - * Wait for the room to be available in the room list, with a membership for the current user of [CurrentUserMembership.JOINED]. + * Wait for the room to be available in the room list with the correct membership for the current user. * @param roomIdOrAlias the room id or alias to wait for * @param timeout the timeout to wait for the room to be available + * @param currentUserMembership the membership to wait for * @throws TimeoutCancellationException if the room is not available after the timeout */ - private suspend fun awaitJoinedRoom( + private suspend fun awaitRoom( roomIdOrAlias: RoomIdOrAlias, - timeout: Duration + timeout: Duration, + currentUserMembership: CurrentUserMembership, ): RoomSummary { return withTimeout(timeout) { getRoomSummaryFlow(roomIdOrAlias) .mapNotNull { optionalRoomSummary -> optionalRoomSummary.getOrNull() } - .filter { roomSummary -> roomSummary.info.currentUserMembership == CurrentUserMembership.JOINED } + .filter { roomSummary -> roomSummary.info.currentUserMembership == currentUserMembership } .first() // Ensure that the room is ready .also { client.awaitRoomRemoteEcho(it.roomId.value) } @@ -314,7 +316,7 @@ class RustMatrixClient( val roomId = RoomId(client.createRoom(rustParams)) // Wait to receive the room back from the sync but do not returns failure if it fails. try { - awaitJoinedRoom(roomId.toRoomIdOrAlias(), 30.seconds) + awaitRoom(roomId.toRoomIdOrAlias(), 30.seconds, CurrentUserMembership.JOINED) } catch (e: Exception) { Timber.e(e, "Timeout waiting for the room to be available in the room list") } @@ -369,7 +371,7 @@ class RustMatrixClient( runCatching { client.joinRoomById(roomId.value).destroy() try { - awaitJoinedRoom(roomId.toRoomIdOrAlias(), 10.seconds) + awaitRoom(roomId.toRoomIdOrAlias(), 10.seconds, CurrentUserMembership.JOINED) } catch (e: Exception) { Timber.e(e, "Timeout waiting for the room to be available in the room list") null @@ -384,7 +386,7 @@ class RustMatrixClient( serverNames = serverNames, ).destroy() try { - awaitJoinedRoom(roomIdOrAlias, 10.seconds) + awaitRoom(roomIdOrAlias, 10.seconds, CurrentUserMembership.JOINED) } catch (e: Exception) { Timber.e(e, "Timeout waiting for the room to be available in the room list") null @@ -392,8 +394,18 @@ class RustMatrixClient( } } - override suspend fun knockRoom(roomId: RoomId): Result { - return Result.failure(NotImplementedError("Not yet implemented")) + override suspend fun knockRoom(roomIdOrAlias: RoomIdOrAlias, message: String, serverNames: List): Result = withContext( + sessionDispatcher + ) { + runCatching { + client.knock(roomIdOrAlias.identifier).destroy() + try { + awaitRoom(roomIdOrAlias, 10.seconds, CurrentUserMembership.KNOCKED) + } catch (e: Exception) { + Timber.e(e, "Timeout waiting for the room to be available in the room list") + null + } + } } override suspend fun trackRecentlyVisitedRoom(roomId: RoomId): Result = withContext(sessionDispatcher) { diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustInvitedRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustPendingRoom.kt similarity index 58% rename from libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustInvitedRoom.kt rename to libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustPendingRoom.kt index 67e9e5e7a5..46df9bed80 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustInvitedRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustPendingRoom.kt @@ -9,20 +9,20 @@ package io.element.android.libraries.matrix.impl.room import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId -import io.element.android.libraries.matrix.api.room.InvitedRoom +import io.element.android.libraries.matrix.api.room.PendingRoom import org.matrix.rustcomponents.sdk.Room -class RustInvitedRoom( +class RustPendingRoom( override val sessionId: SessionId, - private val invitedRoom: Room, -) : InvitedRoom { - override val roomId = RoomId(invitedRoom.id()) + private val inner: Room, +) : PendingRoom { + override val roomId = RoomId(inner.id()) - override suspend fun declineInvite(): Result = runCatching { - invitedRoom.leave() + override suspend fun leave(): Result = runCatching { + inner.leave() } override fun close() { - invitedRoom.destroy() + inner.destroy() } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomFactory.kt index 2e41e36183..e06424e723 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomFactory.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomFactory.kt @@ -14,8 +14,8 @@ import io.element.android.libraries.matrix.api.core.DeviceId import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService -import io.element.android.libraries.matrix.api.room.InvitedRoom import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.api.room.PendingRoom import io.element.android.libraries.matrix.api.roomlist.RoomListService import io.element.android.libraries.matrix.api.roomlist.awaitLoaded import io.element.android.libraries.matrix.impl.roomlist.fullRoomWithTimeline @@ -35,6 +35,7 @@ import timber.log.Timber import org.matrix.rustcomponents.sdk.RoomListService as InnerRoomListService private const val CACHE_SIZE = 16 +private val PENDING_MEMBERSHIPS = setOf(Membership.INVITED, Membership.KNOCKED) class RustRoomFactory( private val sessionId: SessionId, @@ -120,7 +121,7 @@ class RustRoomFactory( } } - suspend fun createInvitedRoom(roomId: RoomId): InvitedRoom? = withContext(dispatcher) { + suspend fun createPendingRoom(roomId: RoomId): PendingRoom? = withContext(dispatcher) { if (isDestroyed) { Timber.d("Room factory is destroyed, returning null for $roomId") return@withContext null @@ -130,20 +131,20 @@ class RustRoomFactory( Timber.d("Room not found for $roomId") return@withContext null } - if (roomListItem.membership() != Membership.INVITED) { - Timber.d("Room $roomId is not in invited state") + if (roomListItem.membership() !in PENDING_MEMBERSHIPS) { + Timber.d("Room $roomId is not in pending state") return@withContext null } - val invitedRoom = try { + val innerRoom = try { + // TODO use new method when available, for now it'll fail for knocked rooms roomListItem.invitedRoom() } catch (e: RoomListException) { - Timber.e(e, "Failed to get invited room for $roomId") + Timber.e(e, "Failed to get pending room for $roomId") return@withContext null } - - RustInvitedRoom( + RustPendingRoom( sessionId = sessionId, - invitedRoom = invitedRoom, + inner = innerRoom, ) } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt index d784bba502..2c69048c39 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt @@ -22,8 +22,8 @@ import io.element.android.libraries.matrix.api.notification.NotificationService import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService import io.element.android.libraries.matrix.api.oidc.AccountManagementAction import io.element.android.libraries.matrix.api.pusher.PushersService -import io.element.android.libraries.matrix.api.room.InvitedRoom import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.api.room.PendingRoom import io.element.android.libraries.matrix.api.room.RoomMembershipObserver import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias import io.element.android.libraries.matrix.api.room.preview.RoomPreview @@ -101,7 +101,7 @@ class FakeMatrixClient( private var createDmResult: Result = Result.success(A_ROOM_ID) private var findDmResult: RoomId? = A_ROOM_ID private val getRoomResults = mutableMapOf() - val getInvitedRoomResults = mutableMapOf() + val getPendingRoomResults = mutableMapOf() private val searchUserResults = mutableMapOf>() private val getProfileResults = mutableMapOf>() private var uploadMediaResult: Result = Result.success(AN_AVATAR_URL) @@ -114,8 +114,8 @@ class FakeMatrixClient( var joinRoomByIdOrAliasLambda: (RoomIdOrAlias, List) -> Result = { _, _ -> Result.success(null) } - var knockRoomLambda: (RoomId) -> Result = { - Result.success(Unit) + var knockRoomLambda: (RoomIdOrAlias, String, List) -> Result = { _, _, _ -> + Result.success(null) } var getRoomSummaryFlowLambda = { _: RoomIdOrAlias -> flowOf>(Optional.empty()) @@ -128,8 +128,8 @@ class FakeMatrixClient( return getRoomResults[roomId] } - override suspend fun getInvitedRoom(roomId: RoomId): InvitedRoom? { - return getInvitedRoomResults[roomId] + override suspend fun getPendingRoom(roomId: RoomId): PendingRoom? { + return getPendingRoomResults[roomId] } override suspend fun findDM(userId: UserId): RoomId? { @@ -223,7 +223,9 @@ class FakeMatrixClient( return joinRoomByIdOrAliasLambda(roomIdOrAlias, serverNames) } - override suspend fun knockRoom(roomId: RoomId): Result = knockRoomLambda(roomId) + override suspend fun knockRoom(roomIdOrAlias: RoomIdOrAlias, message: String, serverNames: List): Result { + return knockRoomLambda(roomIdOrAlias, message, serverNames) + } override fun sessionVerificationService(): SessionVerificationService = sessionVerificationService diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeInvitedRoom.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakePendingRoom.kt similarity index 81% rename from libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeInvitedRoom.kt rename to libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakePendingRoom.kt index 06416e2c80..c8523b31a6 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeInvitedRoom.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakePendingRoom.kt @@ -9,18 +9,18 @@ package io.element.android.libraries.matrix.test.room import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId -import io.element.android.libraries.matrix.api.room.InvitedRoom +import io.element.android.libraries.matrix.api.room.PendingRoom import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.simulateLongTask -class FakeInvitedRoom( +class FakePendingRoom( override val sessionId: SessionId = A_SESSION_ID, override val roomId: RoomId = A_ROOM_ID, private val declineInviteResult: () -> Result = { lambdaError() } -) : InvitedRoom { - override suspend fun declineInvite(): Result = simulateLongTask { +) : PendingRoom { + override suspend fun leave(): Result = simulateLongTask { declineInviteResult() } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/InviteSenderView.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/InviteSenderView.kt index b78e731aff..62f57a36d4 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/InviteSenderView.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/InviteSenderView.kt @@ -8,10 +8,11 @@ package io.element.android.libraries.matrix.ui.components import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme @@ -30,11 +31,12 @@ fun InviteSenderView( modifier: Modifier = Modifier ) { Row( - horizontalArrangement = Arrangement.spacedBy(4.dp), - verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = modifier, ) { + Box(modifier = Modifier.padding(vertical = 2.dp)) { Avatar(avatarData = inviteSender.avatarData) + } Text( text = inviteSender.annotatedString(), style = ElementTheme.typography.fontBodyMdRegular, diff --git a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_11_en.png b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_11_en.png new file mode 100644 index 0000000000..15fef4a084 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_11_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a6eee1185065c82f90cf53d7b9fd62e6de72d907606099b0536b6305ea9537f2 +size 117250 diff --git a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_4_en.png index de60f4f118..a8b4207c5a 100644 --- a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:68aa9bd1630bfe084c3cb54449946c95fdfc4fec4190453a648ffbb738c50c02 -size 118338 +oid sha256:74f12ea2c5114363b809fcf4d897487cb87ecfab361952471c05d22898d0048f +size 130010 diff --git a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_6_en.png index ed54a87320..da1fd14667 100644 --- a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a89fd43e3e373474df355a970ea2dd4d525f554f509c4d34b098151fab9ea6a0 -size 118678 +oid sha256:70c33e148a040ec24287f9ca48353a76e8167015f24d8249bff405e9cc9f16ff +size 118640 diff --git a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_11_en.png b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_11_en.png new file mode 100644 index 0000000000..29dcd629aa --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_11_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:07458d183bae7ecfb7ee61bb6f2e0abb9a4399dd373068002fec3722f575e754 +size 102438 diff --git a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_4_en.png index 0b77bfc7a4..1dc6bb1e2e 100644 --- a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:159cdb25a248a86f348ec72a1af680a14d6fdf16d7d268649ef324f8296b0356 -size 104708 +oid sha256:6950f5e0824c964936077ecda4ff7edb8c8c6796b5a4ae304e6027e57a83cb55 +size 116382 diff --git a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_6_en.png index adf5f78145..e0062f755b 100644 --- a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:69bef3b0f9f8cdb315e4bc1a18b82d12c8927fcb389ebf5975db8c254a040a9b -size 104737 +oid sha256:c3d75eca5904d605b91becebca60199f7e60e6f2bec6b9a945ce8d130cfd7e47 +size 104802 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomListContentView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomListContentView_Day_0_en.png index db9812abde..6158359001 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomListContentView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomListContentView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:77536b81116242dbedd516972cb4945711dee01832b2e9133051d33ede8a8e1a -size 40882 +oid sha256:1da21d7c1ca79691ccb4e0cb7a2e076874dd3894102b7764f874d42a1be83fcf +size 40960 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomListContentView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomListContentView_Day_4_en.png index e9c46d9fe5..8952f01b7f 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomListContentView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomListContentView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8a4e60263d9fb57f115abb852eb5a080bfcdaa5cc1c786b2b0716c8313a94955 -size 72153 +oid sha256:a78abedec8a3aad14bf6368bf73d46621feaf8e6fd6e019d381077ef05856259 +size 72236 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomListContentView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomListContentView_Night_0_en.png index c42500388a..2976deaf4a 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomListContentView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomListContentView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:83ce6ad6b6d5940d95a817b9f4da70d094407f1571ed541c84457cbf59281329 -size 40819 +oid sha256:fbdced976e93e3059dc1b12c4c6ea18410748b9ba20c07f545ec81d7d1dc8451 +size 40791 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomListContentView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomListContentView_Night_4_en.png index 9cd7e9ca2d..1e6d6d626d 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomListContentView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomListContentView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:df340436aa5aab1438bd27a917273d7aa48096efbb373b28308e7a89817241ea -size 70845 +oid sha256:f60d07c4be6d75142e753dec5071f7a6d5dd15519d87a2eb491b708fa7aeea4b +size 70917 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomSummaryRow_Day_29_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomSummaryRow_Day_29_en.png index 640f5909f3..fd79b53c8d 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomSummaryRow_Day_29_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomSummaryRow_Day_29_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c530aabdc8bd8e85a60c7daf7b88c56736bc2c9e10b251e5d1312da66c79586d -size 22812 +oid sha256:7cb57e2567310265a2866565a0f1f9a70ac656d6d93b6df076ba875add699c2b +size 22782 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomSummaryRow_Day_31_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomSummaryRow_Day_31_en.png index e904aca6f6..aa4da0e60f 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomSummaryRow_Day_31_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomSummaryRow_Day_31_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e6afb5e2e6fb3766ed18412f624a98c436c45de6956b996e7d8c947f0523e5e6 -size 21107 +oid sha256:89e6c33ba736594d5a69a9cade77eed957f6c59a23fbd7ce7a8af4962ef7fe66 +size 21162 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomSummaryRow_Day_32_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomSummaryRow_Day_32_en.png new file mode 100644 index 0000000000..dd7c6a9bbd --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomSummaryRow_Day_32_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1943e0cca177b76535bcfff5572e519e70eb45d9a93b48369f174b66813b2b8c +size 11545 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomSummaryRow_Day_33_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomSummaryRow_Day_33_en.png new file mode 100644 index 0000000000..a3ec1b0131 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomSummaryRow_Day_33_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4b5435505e95bb4ea71dbc96faccc4c3e52c35eac6d15ca8f05011a65deb2361 +size 16913 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomSummaryRow_Night_29_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomSummaryRow_Night_29_en.png index ebc79973ec..6f925c3337 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomSummaryRow_Night_29_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomSummaryRow_Night_29_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:427773f5702b8830f4aadf70bcd19ebcf253085de027200afd21fd33ff8a8a3f -size 22646 +oid sha256:af6a921903f5b827650ff59b7a07a53a0ee6fff8bf05b9c9caa7e40b2ff65bcc +size 22726 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomSummaryRow_Night_31_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomSummaryRow_Night_31_en.png index dde7fa7c2c..1616e5308c 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomSummaryRow_Night_31_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomSummaryRow_Night_31_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:35c0c73bc2a40f787f97d8b1d94442d379e7dc2cff708bd16d259d1d9594271d -size 20911 +oid sha256:d938c23b881918f2fc73cf52f85fb103fede4aecbe83cc5725e50ca9f271f711 +size 20925 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomSummaryRow_Night_32_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomSummaryRow_Night_32_en.png new file mode 100644 index 0000000000..84e1a9d135 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomSummaryRow_Night_32_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c92ce38a3829ce48152f956999593b90955830371aa3230ea598f9ba80560163 +size 11903 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomSummaryRow_Night_33_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomSummaryRow_Night_33_en.png new file mode 100644 index 0000000000..2f335c9b81 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_RoomSummaryRow_Night_33_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9a4e420c18dc7fec6afecee9a02c23d2ca031f57e51a7937a1217f7663b3a225 +size 17196 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.search_RoomListSearchContent_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.search_RoomListSearchContent_Day_2_en.png index 125e11836d..334d724fb7 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.search_RoomListSearchContent_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.search_RoomListSearchContent_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7fb36e51fe4e047d3b7299622ed41acf3fee6b717589972449af57b2163a25a3 -size 43528 +oid sha256:57dc005cc7cceaa1f387e3a80c7676313a616c23ca4af06a5867fd438d8ed2a2 +size 43596 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.search_RoomListSearchContent_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.search_RoomListSearchContent_Night_2_en.png index f6336ebafb..772d9bcbe7 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.search_RoomListSearchContent_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.search_RoomListSearchContent_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:736fd7a2ad72251ce6f9cea37ff907639b5234a66f9de542041df85917b8cdb1 -size 43253 +oid sha256:b46afbcc4a208a4ceeb15932569fe0569898d394452e6af6ab41d15fc2c372db +size 43226 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_0_en.png index 06672574c0..8c838d0407 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7c5897f6945dbf3abe154f26388987c8223f65748d90fa6b049d306c64341ea0 -size 78588 +oid sha256:f12d9f5c181579e054fde00a32ce72cf032326b3c63f2424e5df696db0138368 +size 78667 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_10_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_10_en.png index 441bda873c..1e0fce8252 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:61d92b59ab35acebcbecab11064734141bf543bd4c3fc5cddda7f7fdf4631ae5 -size 99173 +oid sha256:3081dd73e3a33e786266b49de1267c401e55ac3c5b37d1b9c61749b0f7d55c29 +size 99240 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_1_en.png index 06672574c0..8c838d0407 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7c5897f6945dbf3abe154f26388987c8223f65748d90fa6b049d306c64341ea0 -size 78588 +oid sha256:f12d9f5c181579e054fde00a32ce72cf032326b3c63f2424e5df696db0138368 +size 78667 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_2_en.png index a4f67e7d75..07f4f2564a 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5709694ba5ba479bb1652cea0218db591ba9aac83213b8a05d155cf73707a913 -size 79045 +oid sha256:4f2e8f7d0a8d384f7958165b113f67f637efd7ab4d4e2f3db321edfdf83e8e82 +size 79079 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_6_en.png index 2dbd00ccca..d072cb92dd 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4bb464339f602dbf8f0a3046ebaaefbe64808351289ba70450eef0565b23ee40 -size 98084 +oid sha256:3c27b870ca1639a58efcd42dfc31bbb12a935e685ca1b87e891ec88abaf8033b +size 98148 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_0_en.png index 0707e57659..20de12e5be 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7fe52dfaeac33dc77da9e741102a6248a8b142fb4706bea34b4e1a9c9f0062c7 -size 86083 +oid sha256:eaf6ef4d088e706e5e37a33ae7897af0ffbfda7afac486f9de2cabd7e24ac0be +size 86150 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_10_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_10_en.png index cccaf41942..7cbf6930a2 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dfeb3170f15fc62836bed2961fb190af7ee1fea59c9f7ee1c393c7398c4dabaf -size 106113 +oid sha256:7eb4d87dd2844f81bf745f53cbe83253fe8b48471692a00dbe0f385de75c8c3a +size 106079 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_1_en.png index 0707e57659..20de12e5be 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7fe52dfaeac33dc77da9e741102a6248a8b142fb4706bea34b4e1a9c9f0062c7 -size 86083 +oid sha256:eaf6ef4d088e706e5e37a33ae7897af0ffbfda7afac486f9de2cabd7e24ac0be +size 86150 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_2_en.png index fa94719a0f..f4fd4c8c85 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1a4e557d8e3683fb3b30b7d3264a6953f713684edd2c07493d9cb3a04da4ef45 -size 87017 +oid sha256:c1014ee5cdde8ea23b6655ee805761e824bcae40d353d4208af41c8b7801998a +size 86938 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_6_en.png index e6a839f4a9..5f3b07d888 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:00492d7154e1ea7468b544a2fcb29fbc23c8b54260420451fb7f912b5226eb69 -size 104915 +oid sha256:0550ab262835b885364b429000e0a887a6bfe594ba031fdbbb04fb4ec63ea475 +size 104884 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_InviteSenderView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_InviteSenderView_Day_0_en.png index 8243405428..6fe1374597 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_InviteSenderView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_InviteSenderView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:70b2f162bfc0c397eb9701922ade58665558ea0acff25784dbef3cdd5e840d83 -size 10561 +oid sha256:8257b4c0465151c099b82747c54b6d42881c4315114fc00840890fc3b6796d1b +size 10522 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_InviteSenderView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_InviteSenderView_Night_0_en.png index 56f3063546..30ee52cf20 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_InviteSenderView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_InviteSenderView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:20fea6755d84bd8668ba045929fb93248c8039711fcf6bec3897c93b89542e72 -size 10423 +oid sha256:4716815888abcb74ecc017722b5e4a7955ad94f90d9a4fcfe2847c21a7cd5777 +size 10373 diff --git a/tools/localazy/config.json b/tools/localazy/config.json index f670f08987..fb05345687 100644 --- a/tools/localazy/config.json +++ b/tools/localazy/config.json @@ -156,7 +156,8 @@ "banner\\.migrate_to_native_sliding_sync\\..*", "full_screen_intent_banner_.*", "screen_migration_.*", - "screen_invites_.*" + "screen_invites_.*", + "screen\\.join_room\\.knock_sent_title" ] }, { @@ -281,7 +282,8 @@ { "name" : ":features:joinroom:impl", "includeRegex" : [ - "screen_join_room_.*" + "screen_join_room_.*", + "screen\\.join_room\\..*" ] } ]