Open user profile and room with event from permalink

This commit is contained in:
Benoit Marty 2024-05-01 10:15:54 +02:00 committed by Benoit Marty
parent 682fd45e00
commit 0476bd95c8
93 changed files with 1499 additions and 370 deletions

View file

@ -34,9 +34,10 @@ import io.element.android.features.roomdetails.impl.edit.RoomDetailsEditNode
import io.element.android.features.roomdetails.impl.invite.RoomInviteMembersNode
import io.element.android.features.roomdetails.impl.members.RoomMemberListNode
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsNode
import io.element.android.features.roomdetails.impl.members.details.avatar.AvatarPreviewNode
import io.element.android.features.roomdetails.impl.notificationsettings.RoomNotificationSettingsNode
import io.element.android.features.roomdetails.impl.rolesandpermissions.RolesAndPermissionsFlowNode
import io.element.android.features.userprofile.shared.UserProfileNodeHelper
import io.element.android.features.userprofile.shared.avatar.AvatarPreviewNode
import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.BaseFlowNode
import io.element.android.libraries.architecture.createNode
@ -78,7 +79,7 @@ class RoomDetailsFlowNode @AssistedInject constructor(
@Parcelize
data class RoomNotificationSettings(
/**
* When presented from outsite the context of the room, the rooms settings UI is different.
* When presented from outside the context of the room, the rooms settings UI is different.
* Figma designs: https://www.figma.com/file/0MMNu7cTOzLOlWb7ctTkv3/Element-X?type=design&node-id=5199-198932&mode=design&t=fTTvpuxYFjewYQOe-0
*/
val showUserDefinedSettingStyle: Boolean
@ -164,7 +165,7 @@ class RoomDetailsFlowNode @AssistedInject constructor(
}
is NavTarget.RoomMemberDetails -> {
val callback = object : RoomMemberDetailsNode.Callback {
val callback = object : UserProfileNodeHelper.Callback {
override fun openAvatarPreview(username: String, avatarUrl: String) {
backstack.push(NavTarget.AvatarPreview(username, avatarUrl))
}

View file

@ -17,7 +17,7 @@
package io.element.android.features.roomdetails.impl
import io.element.android.features.leaveroom.api.LeaveRoomState
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsState
import io.element.android.features.userprofile.shared.UserProfileState
import io.element.android.libraries.matrix.api.core.RoomAlias
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.room.RoomMember
@ -32,7 +32,7 @@ data class RoomDetailsState(
val memberCount: Long,
val isEncrypted: Boolean,
val roomType: RoomDetailsType,
val roomMemberDetailsState: RoomMemberDetailsState?,
val roomMemberDetailsState: UserProfileState?,
val canEdit: Boolean,
val canInvite: Boolean,
val canShowNotificationSettings: Boolean,

View file

@ -19,8 +19,8 @@ package io.element.android.features.roomdetails.impl
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.features.leaveroom.api.LeaveRoomState
import io.element.android.features.leaveroom.api.aLeaveRoomState
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsState
import io.element.android.features.roomdetails.impl.members.details.aRoomMemberDetailsState
import io.element.android.features.userprofile.shared.UserProfileState
import io.element.android.features.userprofile.shared.aUserProfileState
import io.element.android.libraries.matrix.api.core.RoomAlias
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.UserId
@ -90,7 +90,7 @@ fun aRoomDetailsState(
canEdit: Boolean = false,
canShowNotificationSettings: Boolean = true,
roomType: RoomDetailsType = RoomDetailsType.Room,
roomMemberDetailsState: RoomMemberDetailsState? = null,
roomMemberDetailsState: UserProfileState? = null,
leaveRoomState: LeaveRoomState = aLeaveRoomState(),
roomNotificationSettings: RoomNotificationSettings = aRoomNotificationSettings(),
isFavorite: Boolean = false,
@ -130,5 +130,5 @@ fun aDmRoomDetailsState(
) = aRoomDetailsState(
roomName = roomName,
roomType = RoomDetailsType.Dm(aDmRoomMember(isIgnored = isDmMemberIgnored)),
roomMemberDetailsState = aRoomMemberDetailsState()
roomMemberDetailsState = aUserProfileState()
)

View file

@ -49,10 +49,10 @@ 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.leaveroom.api.LeaveRoomView
import io.element.android.features.roomdetails.impl.blockuser.BlockUserDialogs
import io.element.android.features.roomdetails.impl.blockuser.BlockUserSection
import io.element.android.features.roomdetails.impl.members.details.RoomMemberHeaderSection
import io.element.android.features.roomdetails.impl.members.details.RoomMemberMainActionsSection
import io.element.android.features.userprofile.shared.UserProfileHeaderSection
import io.element.android.features.userprofile.shared.UserProfileMainActionsSection
import io.element.android.features.userprofile.shared.blockuser.BlockUserDialogs
import io.element.android.features.userprofile.shared.blockuser.BlockUserSection
import io.element.android.libraries.architecture.coverage.ExcludeFromCoverage
import io.element.android.libraries.designsystem.components.ClickableLinkText
import io.element.android.libraries.designsystem.components.avatar.Avatar
@ -143,15 +143,15 @@ fun RoomDetailsView(
is RoomDetailsType.Dm -> {
val member = state.roomType.roomMember
RoomMemberHeaderSection(
UserProfileHeaderSection(
avatarUrl = state.roomAvatarUrl ?: member.avatarUrl,
userId = member.userId.value,
userId = member.userId,
userName = state.roomName,
openAvatarPreview = { avatarUrl ->
openAvatarPreview(member.getBestName(), avatarUrl)
},
)
RoomMemberMainActionsSection(onShareUser = ::onShareMember)
UserProfileMainActionsSection(onShareUser = ::onShareMember)
}
}
Spacer(Modifier.height(18.dp))

View file

@ -1,83 +0,0 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.roomdetails.impl.blockuser
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import io.element.android.features.roomdetails.impl.R
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsEvents
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsState
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
@Composable
fun BlockUserDialogs(state: RoomMemberDetailsState) {
when (state.displayConfirmationDialog) {
null -> Unit
RoomMemberDetailsState.ConfirmationDialog.Block -> {
BlockConfirmationDialog(
onBlockAction = {
state.eventSink(
RoomMemberDetailsEvents.BlockUser(
needsConfirmation = false
)
)
},
onDismiss = { state.eventSink(RoomMemberDetailsEvents.ClearConfirmationDialog) }
)
}
RoomMemberDetailsState.ConfirmationDialog.Unblock -> {
UnblockConfirmationDialog(
onUnblockAction = {
state.eventSink(
RoomMemberDetailsEvents.UnblockUser(
needsConfirmation = false
)
)
},
onDismiss = { state.eventSink(RoomMemberDetailsEvents.ClearConfirmationDialog) }
)
}
}
}
@Composable
private fun BlockConfirmationDialog(
onBlockAction: () -> Unit,
onDismiss: () -> Unit,
) {
ConfirmationDialog(
title = stringResource(R.string.screen_dm_details_block_user),
content = stringResource(R.string.screen_dm_details_block_alert_description),
submitText = stringResource(R.string.screen_dm_details_block_alert_action),
onSubmitClicked = onBlockAction,
onDismiss = onDismiss
)
}
@Composable
private fun UnblockConfirmationDialog(
onUnblockAction: () -> Unit,
onDismiss: () -> Unit,
) {
ConfirmationDialog(
title = stringResource(R.string.screen_dm_details_unblock_user),
content = stringResource(R.string.screen_dm_details_unblock_alert_description),
submitText = stringResource(R.string.screen_dm_details_unblock_alert_action),
onSubmitClicked = onUnblockAction,
onDismiss = onDismiss
)
}

View file

@ -1,99 +0,0 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.roomdetails.impl.blockuser
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.progressSemantics
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import io.element.android.compound.tokens.generated.CompoundIcons
import io.element.android.features.roomdetails.impl.R
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsEvents
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsState
import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.core.bool.orFalse
import io.element.android.libraries.designsystem.components.dialogs.RetryDialog
import io.element.android.libraries.designsystem.components.list.ListItemContent
import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
import io.element.android.libraries.designsystem.theme.components.IconSource
import io.element.android.libraries.designsystem.theme.components.ListItem
import io.element.android.libraries.designsystem.theme.components.ListItemStyle
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.ui.strings.CommonStrings
@Composable
internal fun BlockUserSection(state: RoomMemberDetailsState) {
PreferenceCategory(showDivider = false) {
when (state.isBlocked) {
is AsyncData.Failure -> PreferenceBlockUser(isBlocked = state.isBlocked.prevData, isLoading = false, eventSink = state.eventSink)
is AsyncData.Loading -> PreferenceBlockUser(isBlocked = state.isBlocked.prevData, isLoading = true, eventSink = state.eventSink)
is AsyncData.Success -> PreferenceBlockUser(isBlocked = state.isBlocked.data, isLoading = false, eventSink = state.eventSink)
AsyncData.Uninitialized -> PreferenceBlockUser(isBlocked = null, isLoading = true, eventSink = state.eventSink)
}
}
if (state.isBlocked is AsyncData.Failure) {
RetryDialog(
content = stringResource(CommonStrings.error_unknown),
onDismiss = { state.eventSink(RoomMemberDetailsEvents.ClearBlockUserError) },
onRetry = {
val event = when (state.isBlocked.prevData) {
true -> RoomMemberDetailsEvents.UnblockUser(needsConfirmation = false)
false -> RoomMemberDetailsEvents.BlockUser(needsConfirmation = false)
// null case Should not happen
null -> RoomMemberDetailsEvents.ClearBlockUserError
}
state.eventSink(event)
},
)
}
}
@Composable
private fun PreferenceBlockUser(
isBlocked: Boolean?,
isLoading: Boolean,
eventSink: (RoomMemberDetailsEvents) -> Unit,
) {
val loadingCurrentValue = @Composable {
CircularProgressIndicator(
modifier = Modifier
.progressSemantics()
.size(20.dp),
strokeWidth = 2.dp
)
}
if (isBlocked.orFalse()) {
ListItem(
headlineContent = { Text(stringResource(R.string.screen_dm_details_unblock_user)) },
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Block())),
onClick = { if (!isLoading) eventSink(RoomMemberDetailsEvents.UnblockUser(needsConfirmation = true)) },
trailingContent = if (isLoading) ListItemContent.Custom(loadingCurrentValue) else null,
style = ListItemStyle.Primary,
)
} else {
ListItem(
headlineContent = { Text(stringResource(R.string.screen_dm_details_block_user)) },
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Block())),
style = ListItemStyle.Destructive,
onClick = { if (!isLoading) eventSink(RoomMemberDetailsEvents.BlockUser(needsConfirmation = true)) },
trailingContent = if (isLoading) ListItemContent.Custom(loadingCurrentValue) else null,
)
}
}

View file

@ -1,26 +0,0 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.roomdetails.impl.members.details
sealed interface RoomMemberDetailsEvents {
data object StartDM : RoomMemberDetailsEvents
data object ClearStartDMState : RoomMemberDetailsEvents
data class BlockUser(val needsConfirmation: Boolean = false) : RoomMemberDetailsEvents
data class UnblockUser(val needsConfirmation: Boolean = false) : RoomMemberDetailsEvents
data object ClearBlockUserError : RoomMemberDetailsEvents
data object ClearConfirmationDialog : RoomMemberDetailsEvents
}

View file

@ -28,8 +28,8 @@ import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import im.vector.app.features.analytics.plan.MobileScreen
import io.element.android.anvilannotations.ContributesNode
import io.element.android.features.roomdetails.impl.R
import io.element.android.libraries.androidutils.system.startSharePlainTextIntent
import io.element.android.features.userprofile.shared.UserProfileNodeHelper
import io.element.android.features.userprofile.shared.UserProfileView
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.architecture.NodeInputs
import io.element.android.libraries.architecture.inputs
@ -38,8 +38,6 @@ import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.permalink.PermalinkBuilder
import io.element.android.services.analytics.api.AnalyticsService
import timber.log.Timber
import io.element.android.libraries.androidutils.R as AndroidUtilsR
@ContributesNode(RoomScope::class)
class RoomMemberDetailsNode @AssistedInject constructor(
@ -49,18 +47,14 @@ class RoomMemberDetailsNode @AssistedInject constructor(
private val permalinkBuilder: PermalinkBuilder,
presenterFactory: RoomMemberDetailsPresenter.Factory,
) : Node(buildContext, plugins = plugins) {
interface Callback : NodeInputs {
fun openAvatarPreview(username: String, avatarUrl: String)
fun onStartDM(roomId: RoomId)
}
data class RoomMemberDetailsInput(
val roomMemberId: UserId
) : NodeInputs
private val inputs = inputs<RoomMemberDetailsInput>()
private val callback = inputs<Callback>()
private val callback = inputs<UserProfileNodeHelper.Callback>()
private val presenter = presenterFactory.create(inputs.roomMemberId)
private val userProfileNodeHelper = UserProfileNodeHelper(inputs.roomMemberId)
init {
lifecycle.subscribe(
@ -75,17 +69,7 @@ class RoomMemberDetailsNode @AssistedInject constructor(
val context = LocalContext.current
fun onShareUser() {
val permalinkResult = permalinkBuilder.permalinkForUser(inputs.roomMemberId)
permalinkResult.onSuccess { permalink ->
context.startSharePlainTextIntent(
activityResultLauncher = null,
chooserTitle = context.getString(R.string.screen_room_details_share_room_title),
text = permalink,
noActivityFoundMessage = context.getString(AndroidUtilsR.string.error_no_compatible_app_found)
)
}.onFailure {
Timber.e(it)
}
userProfileNodeHelper.onShareUser(context, permalinkBuilder)
}
fun onStartDM(roomId: RoomId) {
@ -95,11 +79,12 @@ class RoomMemberDetailsNode @AssistedInject constructor(
val state = presenter.present()
LaunchedEffect(state.startDmActionState) {
if (state.startDmActionState is AsyncAction.Success) {
onStartDM(state.startDmActionState.data)
val result = state.startDmActionState
if (result is AsyncAction.Success) {
onStartDM(result.data)
}
}
RoomMemberDetailsView(
UserProfileView(
state = state,
modifier = modifier,
goBack = this::navigateUp,

View file

@ -28,7 +28,10 @@ import androidx.compose.runtime.setValue
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import io.element.android.features.createroom.api.StartDMAction
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsState.ConfirmationDialog
import io.element.android.features.userprofile.shared.UserProfileEvents
import io.element.android.features.userprofile.shared.UserProfilePresenterHelper
import io.element.android.features.userprofile.shared.UserProfileState
import io.element.android.features.userprofile.shared.UserProfileState.ConfirmationDialog
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.architecture.Presenter
@ -39,7 +42,6 @@ import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.ui.room.getRoomMemberAsState
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
@ -51,13 +53,18 @@ class RoomMemberDetailsPresenter @AssistedInject constructor(
private val client: MatrixClient,
private val room: MatrixRoom,
private val startDMAction: StartDMAction,
) : Presenter<RoomMemberDetailsState> {
) : Presenter<UserProfileState> {
interface Factory {
fun create(roomMemberId: UserId): RoomMemberDetailsPresenter
}
private val userProfilePresenterHelper = UserProfilePresenterHelper(
userId = roomMemberId,
client = client,
)
@Composable
override fun present(): RoomMemberDetailsState {
override fun present(): UserProfileState {
val coroutineScope = rememberCoroutineScope()
var confirmationDialog by remember { mutableStateOf<ConfirmationDialog?>(null) }
val roomMember by room.getRoomMemberAsState(roomMemberId)
@ -81,34 +88,34 @@ class RoomMemberDetailsPresenter @AssistedInject constructor(
}
}
fun handleEvents(event: RoomMemberDetailsEvents) {
fun handleEvents(event: UserProfileEvents) {
when (event) {
is RoomMemberDetailsEvents.BlockUser -> {
is UserProfileEvents.BlockUser -> {
if (event.needsConfirmation) {
confirmationDialog = ConfirmationDialog.Block
} else {
confirmationDialog = null
coroutineScope.blockUser(roomMemberId, isBlocked)
userProfilePresenterHelper.blockUser(coroutineScope, isBlocked)
}
}
is RoomMemberDetailsEvents.UnblockUser -> {
is UserProfileEvents.UnblockUser -> {
if (event.needsConfirmation) {
confirmationDialog = ConfirmationDialog.Unblock
} else {
confirmationDialog = null
coroutineScope.unblockUser(roomMemberId, isBlocked)
userProfilePresenterHelper.unblockUser(coroutineScope, isBlocked)
}
}
RoomMemberDetailsEvents.ClearConfirmationDialog -> confirmationDialog = null
RoomMemberDetailsEvents.ClearBlockUserError -> {
UserProfileEvents.ClearConfirmationDialog -> confirmationDialog = null
UserProfileEvents.ClearBlockUserError -> {
isBlocked.value = AsyncData.Success(isBlocked.value.dataOrNull().orFalse())
}
RoomMemberDetailsEvents.StartDM -> {
UserProfileEvents.StartDM -> {
coroutineScope.launch {
startDMAction.execute(roomMemberId, startDmActionState)
}
}
RoomMemberDetailsEvents.ClearStartDMState -> {
UserProfileEvents.ClearStartDMState -> {
startDmActionState.value = AsyncAction.Uninitialized
}
}
@ -144,8 +151,8 @@ class RoomMemberDetailsPresenter @AssistedInject constructor(
)
}
return RoomMemberDetailsState(
userId = roomMemberId.value,
return UserProfileState(
userId = roomMemberId,
userName = userName,
avatarUrl = userAvatar,
isBlocked = isBlocked.value,
@ -155,22 +162,4 @@ class RoomMemberDetailsPresenter @AssistedInject constructor(
eventSink = ::handleEvents
)
}
private fun CoroutineScope.blockUser(userId: UserId, isBlockedState: MutableState<AsyncData<Boolean>>) = launch {
isBlockedState.value = AsyncData.Loading(false)
client.ignoreUser(userId)
.onFailure {
isBlockedState.value = AsyncData.Failure(it, false)
}
// Note: on success, ignoredUserList will be updated.
}
private fun CoroutineScope.unblockUser(userId: UserId, isBlockedState: MutableState<AsyncData<Boolean>>) = launch {
isBlockedState.value = AsyncData.Loading(true)
client.unignoreUser(userId)
.onFailure {
isBlockedState.value = AsyncData.Failure(it, true)
}
// Note: on success, ignoredUserList will be updated.
}
}

View file

@ -1,37 +0,0 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.roomdetails.impl.members.details
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.matrix.api.core.RoomId
data class RoomMemberDetailsState(
val userId: String,
val userName: String?,
val avatarUrl: String?,
val isBlocked: AsyncData<Boolean>,
val startDmActionState: AsyncAction<RoomId>,
val displayConfirmationDialog: ConfirmationDialog?,
val isCurrentUser: Boolean,
val eventSink: (RoomMemberDetailsEvents) -> Unit
) {
enum class ConfirmationDialog {
Block,
Unblock
}
}

View file

@ -1,56 +0,0 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.roomdetails.impl.members.details
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.matrix.api.core.RoomId
open class RoomMemberDetailsStateProvider : PreviewParameterProvider<RoomMemberDetailsState> {
override val values: Sequence<RoomMemberDetailsState>
get() = sequenceOf(
aRoomMemberDetailsState(),
aRoomMemberDetailsState(userName = null),
aRoomMemberDetailsState(isBlocked = AsyncData.Success(true)),
aRoomMemberDetailsState(displayConfirmationDialog = RoomMemberDetailsState.ConfirmationDialog.Block),
aRoomMemberDetailsState(displayConfirmationDialog = RoomMemberDetailsState.ConfirmationDialog.Unblock),
aRoomMemberDetailsState(isBlocked = AsyncData.Loading(true)),
aRoomMemberDetailsState(startDmActionState = AsyncAction.Loading),
// Add other states here
)
}
fun aRoomMemberDetailsState(
userId: String = "@daniel:domain.com",
userName: String? = "Daniel",
avatarUrl: String? = null,
isBlocked: AsyncData<Boolean> = AsyncData.Success(false),
startDmActionState: AsyncAction<RoomId> = AsyncAction.Uninitialized,
displayConfirmationDialog: RoomMemberDetailsState.ConfirmationDialog? = null,
isCurrentUser: Boolean = false,
eventSink: (RoomMemberDetailsEvents) -> Unit = {},
) = RoomMemberDetailsState(
userId = userId,
userName = userName,
avatarUrl = avatarUrl,
isBlocked = isBlocked,
startDmActionState = startDmActionState,
displayConfirmationDialog = displayConfirmationDialog,
isCurrentUser = isCurrentUser,
eventSink = eventSink,
)

View file

@ -1,141 +0,0 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.roomdetails.impl.members.details
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import io.element.android.compound.tokens.generated.CompoundIcons
import io.element.android.features.roomdetails.impl.R
import io.element.android.features.roomdetails.impl.blockuser.BlockUserDialogs
import io.element.android.features.roomdetails.impl.blockuser.BlockUserSection
import io.element.android.libraries.architecture.coverage.ExcludeFromCoverage
import io.element.android.libraries.designsystem.components.async.AsyncActionView
import io.element.android.libraries.designsystem.components.async.AsyncActionViewDefaults
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.list.ListItemContent
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.preview.PreviewWithLargeHeight
import io.element.android.libraries.designsystem.theme.components.IconSource
import io.element.android.libraries.designsystem.theme.components.ListItem
import io.element.android.libraries.designsystem.theme.components.ListItemStyle
import io.element.android.libraries.designsystem.theme.components.Scaffold
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.RoomId
import io.element.android.libraries.ui.strings.CommonStrings
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun RoomMemberDetailsView(
state: RoomMemberDetailsState,
onShareUser: () -> Unit,
onDMStarted: (RoomId) -> Unit,
goBack: () -> Unit,
openAvatarPreview: (username: String, url: String) -> Unit,
modifier: Modifier = Modifier,
) {
Scaffold(
modifier = modifier,
topBar = {
TopAppBar(title = { }, navigationIcon = { BackButton(onClick = goBack) })
},
) { padding ->
Column(
modifier = Modifier
.padding(padding)
.consumeWindowInsets(padding)
.verticalScroll(rememberScrollState())
) {
RoomMemberHeaderSection(
avatarUrl = state.avatarUrl,
userId = state.userId,
userName = state.userName,
openAvatarPreview = { avatarUrl ->
openAvatarPreview(state.userName ?: state.userId, avatarUrl)
},
)
RoomMemberMainActionsSection(onShareUser = onShareUser)
Spacer(modifier = Modifier.height(26.dp))
if (!state.isCurrentUser) {
StartDMSection(onStartDMClicked = { state.eventSink(RoomMemberDetailsEvents.StartDM) })
BlockUserSection(state)
BlockUserDialogs(state)
}
AsyncActionView(
async = state.startDmActionState,
progressDialog = {
AsyncActionViewDefaults.ProgressDialog(
progressText = stringResource(CommonStrings.common_starting_chat),
)
},
onSuccess = onDMStarted,
errorMessage = { stringResource(R.string.screen_start_chat_error_starting_chat) },
onRetry = { state.eventSink(RoomMemberDetailsEvents.StartDM) },
onErrorDismiss = { state.eventSink(RoomMemberDetailsEvents.ClearStartDMState) },
)
}
}
}
@Composable
private fun StartDMSection(
onStartDMClicked: () -> Unit,
) {
ListItem(
headlineContent = { Text(stringResource(CommonStrings.common_direct_chat)) },
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Chat())),
style = ListItemStyle.Primary,
onClick = onStartDMClicked,
)
}
@PreviewWithLargeHeight
@Composable
internal fun RoomMemberDetailsViewLightPreview(@PreviewParameter(RoomMemberDetailsStateProvider::class) state: RoomMemberDetailsState) =
ElementPreviewLight { ContentToPreview(state) }
@PreviewWithLargeHeight
@Composable
internal fun RoomMemberDetailsViewDarkPreview(@PreviewParameter(RoomMemberDetailsStateProvider::class) state: RoomMemberDetailsState) =
ElementPreviewDark { ContentToPreview(state) }
@ExcludeFromCoverage
@Composable
private fun ContentToPreview(state: RoomMemberDetailsState) {
RoomMemberDetailsView(
state = state,
onShareUser = {},
goBack = {},
onDMStarted = {},
openAvatarPreview = { _, _ -> }
)
}

View file

@ -1,81 +0,0 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.roomdetails.impl.members.details
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
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.size
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.clipToBounds
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme
import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.testtags.TestTags
import io.element.android.libraries.testtags.testTag
@Composable
fun RoomMemberHeaderSection(
avatarUrl: String?,
userId: String,
userName: String?,
openAvatarPreview: (url: String) -> Unit,
modifier: Modifier = Modifier
) {
Column(modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
Box(modifier = Modifier.size(70.dp)) {
Avatar(
avatarData = AvatarData(userId, userName, avatarUrl, AvatarSize.UserHeader),
modifier = Modifier
.clickable(enabled = avatarUrl != null) { openAvatarPreview(avatarUrl!!) }
.fillMaxSize()
.testTag(TestTags.memberDetailAvatar)
)
}
Spacer(modifier = Modifier.height(24.dp))
if (userName != null) {
Text(
modifier = Modifier.clipToBounds(),
text = userName,
style = ElementTheme.typography.fontHeadingLgBold,
)
Spacer(modifier = Modifier.height(6.dp))
}
Text(
text = userId,
style = ElementTheme.typography.fontBodyLgRegular,
color = MaterialTheme.colorScheme.secondary,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
textAlign = TextAlign.Center,
)
Spacer(Modifier.height(40.dp))
}
}

View file

@ -1,38 +0,0 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.roomdetails.impl.members.details
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import io.element.android.compound.tokens.generated.CompoundIcons
import io.element.android.libraries.designsystem.components.button.MainActionButton
import io.element.android.libraries.ui.strings.CommonStrings
@Composable
fun RoomMemberMainActionsSection(onShareUser: () -> Unit, modifier: Modifier = Modifier) {
Row(modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
MainActionButton(
title = stringResource(CommonStrings.action_share),
imageVector = CompoundIcons.ShareAndroid(),
onClick = onShareUser
)
}
}

View file

@ -1,33 +0,0 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.roomdetails.impl.members.details.avatar
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.plugin.Plugin
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import io.element.android.anvilannotations.ContributesNode
import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.mediaviewer.api.viewer.MediaViewerNode
import io.element.android.libraries.mediaviewer.api.viewer.MediaViewerPresenter
@ContributesNode(RoomScope::class)
class AvatarPreviewNode @AssistedInject constructor(
@Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>,
presenterFactory: MediaViewerPresenter.Factory,
) : MediaViewerNode(buildContext, plugins, presenterFactory)

View file

@ -1,11 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_dm_details_block_alert_action">"Заблакіраваць"</string>
<string name="screen_dm_details_block_alert_description">"Заблакіраваныя карыстальнікі не змогуць адпраўляць вам паведамленні, і ўсе іх паведамленні будуць схаваны. Вы можаце разблакіраваць іх у любы час."</string>
<string name="screen_dm_details_block_user">"Заблакіраваць карыстальніка"</string>
<string name="screen_dm_details_unblock_alert_action">"Разблакіраваць"</string>
<string name="screen_dm_details_unblock_alert_description">"Вы зноў зможаце ўбачыць усе паведамленні."</string>
<string name="screen_dm_details_unblock_user">"Разблакіраваць карыстальніка"</string>
<string name="screen_notification_settings_edit_failed_updating_default_mode">"Пры абнаўленні налад апавяшчэнняў адбылася памылка."</string>
<string name="screen_notification_settings_mentions_only_disclaimer">"Ваш хатні сервер не падтрымлівае гэтую опцыю ў зашыфраваных пакоях, вы можаце не атрымаць апавяшчэнне ў некаторых пакоях."</string>
<string name="screen_polls_history_title">"Апытанні"</string>
@ -117,5 +111,4 @@
<string name="screen_room_roles_and_permissions_roles_header">"Ролі"</string>
<string name="screen_room_roles_and_permissions_room_details">"Дэталі пакоя"</string>
<string name="screen_room_roles_and_permissions_title">"Ролі і дазволы"</string>
<string name="screen_start_chat_error_starting_chat">"Пры спробе пачаць чат адбылася памылка"</string>
</resources>

View file

@ -1,9 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_dm_details_block_alert_action">"Блокиране"</string>
<string name="screen_dm_details_block_user">"Блокиране на потребителя"</string>
<string name="screen_dm_details_unblock_alert_action">"Отблокиране"</string>
<string name="screen_dm_details_unblock_user">"Отблокиране на потребителя"</string>
<string name="screen_polls_history_title">"Анкети"</string>
<string name="screen_room_change_role_section_users">"Членове"</string>
<string name="screen_room_details_add_topic_title">"Добавяне на тема"</string>

View file

@ -1,11 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_dm_details_block_alert_action">"Zablokovat"</string>
<string name="screen_dm_details_block_alert_description">"Blokovaní uživatelé vám nebudou moci posílat zprávy a všechny jejich zprávy budou skryty. Můžete je kdykoli odblokovat."</string>
<string name="screen_dm_details_block_user">"Zablokovat uživatele"</string>
<string name="screen_dm_details_unblock_alert_action">"Odblokovat"</string>
<string name="screen_dm_details_unblock_alert_description">"Znovu uvidíte všechny zprávy od nich."</string>
<string name="screen_dm_details_unblock_user">"Odblokovat uživatele"</string>
<string name="screen_notification_settings_edit_failed_updating_default_mode">"Při aktualizaci nastavení oznámení došlo k chybě."</string>
<string name="screen_notification_settings_mentions_only_disclaimer">"Váš domovský server tuto možnost v zašifrovaných místnostech nepodporuje, v některých místnostech nemusíte být upozorněni."</string>
<string name="screen_polls_history_title">"Hlasování"</string>
@ -117,5 +111,4 @@
<string name="screen_room_roles_and_permissions_roles_header">"Role"</string>
<string name="screen_room_roles_and_permissions_room_details">"Podrobnosti místnosti"</string>
<string name="screen_room_roles_and_permissions_title">"Role a oprávnění"</string>
<string name="screen_start_chat_error_starting_chat">"Při pokusu o zahájení chatu došlo k chybě"</string>
</resources>

View file

@ -1,11 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_dm_details_block_alert_action">"Blockieren"</string>
<string name="screen_dm_details_block_alert_description">"Blockierte Benutzer können Dir keine Nachrichten senden und alle ihre alten Nachrichten werden ausgeblendet. Die Blockierung kann jederzeit aufgehoben werden."</string>
<string name="screen_dm_details_block_user">"Benutzer blockieren"</string>
<string name="screen_dm_details_unblock_alert_action">"Blockierung aufheben"</string>
<string name="screen_dm_details_unblock_alert_description">"Der Nutzer kann dir wieder Nachrichten senden &amp; alle Nachrichten des Nutzers werden wieder angezeigt."</string>
<string name="screen_dm_details_unblock_user">"Blockierung aufheben"</string>
<string name="screen_notification_settings_edit_failed_updating_default_mode">"Beim Aktualisieren der Benachrichtigungseinstellungen ist ein Fehler aufgetreten."</string>
<string name="screen_notification_settings_mentions_only_disclaimer">"Dein Homeserver unterstützt diese Option in verschlüsselten Chat nicht. In einigen Chats wirst du möglicherweise nicht benachrichtigt."</string>
<string name="screen_polls_history_title">"Umfragen"</string>
@ -20,15 +14,15 @@
<string name="screen_room_change_permissions_remove_people">"Personen entfernen"</string>
<string name="screen_room_change_permissions_room_avatar">"Avatar ändern"</string>
<string name="screen_room_change_permissions_room_details">"Raum-Details anpassen"</string>
<string name="screen_room_change_permissions_room_name">"Raum-Name ändern"</string>
<string name="screen_room_change_permissions_room_topic">"Raum-Thema ändern"</string>
<string name="screen_room_change_permissions_room_name">"Raumname ändern"</string>
<string name="screen_room_change_permissions_room_topic">"Raumthema ändern"</string>
<string name="screen_room_change_permissions_send_messages">"Nachrichten senden"</string>
<string name="screen_room_change_role_administrators_title">"Admins bearbeiten"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Du kannst diese Aktion nicht mehr rückgängig machen. Du vergibst dieselbe Rolle, wie auch Du sie hast."</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Du kannst diese Aktion nicht mehr rückgängig machen. Du vergibst dieselbe Rolle, die du auch hast."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Als Administrator hinzufügen?"</string>
<string name="screen_room_change_role_confirm_demote_self_action">"Zurückstufen"</string>
<string name="screen_room_change_role_confirm_demote_self_description">"Du stufst dich selbst herab. Diese Änderung kann nicht rückgängig gemacht werden. Wenn du der letzte Benutzer mit dieser Rolle bist, ist es nicht möglich, diese Rolle wiederzuerlangen."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Möchtest Du Dich selbst herabstufen?"</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Möchtest du dich selbst herabstufen?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (Ausstehend)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(Ausstehend)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Administratoren haben automatisch Moderatorenrechte"</string>
@ -56,7 +50,7 @@
<string name="screen_room_details_notification_mode_default">"Standard"</string>
<string name="screen_room_details_notification_title">"Benachrichtigungen"</string>
<string name="screen_room_details_roles_and_permissions">"Rollen und Berechtigungen"</string>
<string name="screen_room_details_room_name_label">"Raum-Name"</string>
<string name="screen_room_details_room_name_label">"Raumname"</string>
<string name="screen_room_details_security_title">"Sicherheit"</string>
<string name="screen_room_details_share_room_title">"Teilen"</string>
<string name="screen_room_details_title">"Informationen"</string>
@ -116,5 +110,4 @@
<string name="screen_room_roles_and_permissions_roles_header">"Rollen"</string>
<string name="screen_room_roles_and_permissions_room_details">"Raum-Details anpassen"</string>
<string name="screen_room_roles_and_permissions_title">"Rollen und Berechtigungen"</string>
<string name="screen_start_chat_error_starting_chat">"Beim Versuch, einen Chat zu starten, ist ein Fehler aufgetreten"</string>
</resources>

View file

@ -1,11 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_dm_details_block_alert_action">"Bloquear"</string>
<string name="screen_dm_details_block_alert_description">"Los usuarios bloqueados no podrán enviarte mensajes y todos sus mensajes se ocultarán. Puedes desbloquearlos cuando quieras."</string>
<string name="screen_dm_details_block_user">"Bloquear usuario"</string>
<string name="screen_dm_details_unblock_alert_action">"Desbloquear"</string>
<string name="screen_dm_details_unblock_alert_description">"Podrás ver todos sus mensajes de nuevo."</string>
<string name="screen_dm_details_unblock_user">"Desbloquear usuario"</string>
<string name="screen_notification_settings_edit_failed_updating_default_mode">"Se ha producido un error al actualizar la configuración de notificaciones."</string>
<string name="screen_notification_settings_mentions_only_disclaimer">"Tu servidor principal no admite esta opción en salas cifradas, puede que no recibas notificaciones en algunas salas."</string>
<string name="screen_polls_history_title">"Encuestas"</string>
@ -51,5 +45,4 @@
<string name="screen_room_notification_settings_mode_all_messages">"Todos los mensajes"</string>
<string name="screen_room_notification_settings_mode_mentions_and_keywords">"Únicamente Menciones y Palabras clave"</string>
<string name="screen_room_notification_settings_room_custom_settings_title">"En esta sala, notificarme por"</string>
<string name="screen_start_chat_error_starting_chat">"Se ha producido un error al intentar iniciar un chat"</string>
</resources>

View file

@ -1,11 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_dm_details_block_alert_action">"Bloquer"</string>
<string name="screen_dm_details_block_alert_description">"Les utilisateurs bloqués ne pourront pas vous envoyer de messages et tous leurs messages seront masqués. Vous pouvez les débloquer à tout moment."</string>
<string name="screen_dm_details_block_user">"Bloquer lutilisateur"</string>
<string name="screen_dm_details_unblock_alert_action">"Débloquer"</string>
<string name="screen_dm_details_unblock_alert_description">"Vous pourrez à nouveau voir tous ses messages."</string>
<string name="screen_dm_details_unblock_user">"Débloquer lutilisateur"</string>
<string name="screen_notification_settings_edit_failed_updating_default_mode">"Une erreur sest produite lors de la mise à jour du paramètre de notification."</string>
<string name="screen_notification_settings_mentions_only_disclaimer">"Votre serveur daccueil ne supporte pas cette option pour les salons chiffrés, vous pourriez ne pas être notifié(e) dans certains salons."</string>
<string name="screen_polls_history_title">"Sondages"</string>
@ -116,5 +110,4 @@
<string name="screen_room_roles_and_permissions_roles_header">"Rôles"</string>
<string name="screen_room_roles_and_permissions_room_details">"Détails du salon"</string>
<string name="screen_room_roles_and_permissions_title">"Rôles et autorisations"</string>
<string name="screen_start_chat_error_starting_chat">"Une erreur sest produite lors de la tentative de création de la discussion"</string>
</resources>

View file

@ -1,11 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_dm_details_block_alert_action">"Letiltás"</string>
<string name="screen_dm_details_block_alert_description">"A letiltott felhasználók nem fognak tudni üzeneteket küldeni, és az összes üzenetük rejtve lesz. Bármikor feloldhatja a letiltásukat."</string>
<string name="screen_dm_details_block_user">"Felhasználó letiltása"</string>
<string name="screen_dm_details_unblock_alert_action">"Letiltás feloldása"</string>
<string name="screen_dm_details_unblock_alert_description">"Újra láthatja az összes üzenetét."</string>
<string name="screen_dm_details_unblock_user">"Felhasználó kitiltásának feloldása"</string>
<string name="screen_notification_settings_edit_failed_updating_default_mode">"Hiba történt az értesítési beállítás frissítésekor."</string>
<string name="screen_notification_settings_mentions_only_disclaimer">"A Matrix-kiszolgálója nem támogatja ezt a beállítást a titkosított szobákban, előfordulhat, hogy egyes szobákban nem kap értesítést."</string>
<string name="screen_polls_history_title">"Szavazások"</string>
@ -116,5 +110,4 @@
<string name="screen_room_roles_and_permissions_roles_header">"Szerepkörök"</string>
<string name="screen_room_roles_and_permissions_room_details">"Szoba részletei"</string>
<string name="screen_room_roles_and_permissions_title">"Szerepkörök és jogosultságok"</string>
<string name="screen_start_chat_error_starting_chat">"Hiba történt a csevegés indításakor"</string>
</resources>

View file

@ -1,11 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_dm_details_block_alert_action">"Blokir"</string>
<string name="screen_dm_details_block_alert_description">"Pengguna yang diblokir tidak akan dapat mengirim Anda pesan dan semua pesan mereka akan disembunyikan. Anda dapat membuka blokirnya kapan saja."</string>
<string name="screen_dm_details_block_user">"Blokir pengguna"</string>
<string name="screen_dm_details_unblock_alert_action">"Buka blokir"</string>
<string name="screen_dm_details_unblock_alert_description">"Anda akan dapat melihat semua pesan dari mereka lagi."</string>
<string name="screen_dm_details_unblock_user">"Buka blokir pengguna"</string>
<string name="screen_notification_settings_edit_failed_updating_default_mode">"Terjadi kesalahan saat memperbarui pengaturan pemberitahuan."</string>
<string name="screen_notification_settings_mentions_only_disclaimer">"Homeserver Anda tidak mendukung opsi ini dalam ruangan terenkripsi, Anda mungkin tidak diberi tahu dalam beberapa ruangan."</string>
<string name="screen_polls_history_title">"Pemungutan suara"</string>
@ -115,5 +109,4 @@
<string name="screen_room_roles_and_permissions_roles_header">"Peran"</string>
<string name="screen_room_roles_and_permissions_room_details">"Detail ruangan"</string>
<string name="screen_room_roles_and_permissions_title">"Peran dan perizinan"</string>
<string name="screen_start_chat_error_starting_chat">"Terjadi kesalahan saat mencoba memulai obrolan"</string>
</resources>

View file

@ -1,11 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_dm_details_block_alert_action">"Blocca"</string>
<string name="screen_dm_details_block_alert_description">"Gli utenti bloccati non saranno in grado di inviarti messaggi e tutti quelli già ricevuti saranno nascosti. Puoi sbloccarli in qualsiasi momento."</string>
<string name="screen_dm_details_block_user">"Blocca utente"</string>
<string name="screen_dm_details_unblock_alert_action">"Sblocca"</string>
<string name="screen_dm_details_unblock_alert_description">"Potrai vedere di nuovo tutti i suoi messaggi."</string>
<string name="screen_dm_details_unblock_user">"Sblocca utente"</string>
<string name="screen_notification_settings_edit_failed_updating_default_mode">"Si è verificato un errore durante l\'aggiornamento delle impostazioni di notifica."</string>
<string name="screen_notification_settings_mentions_only_disclaimer">"Il tuo homeserver non supporta questa opzione nelle stanze crifrate, quindi potresti non ricevere notifiche in alcune stanze."</string>
<string name="screen_polls_history_title">"Sondaggi"</string>
@ -113,5 +107,4 @@
<string name="screen_room_roles_and_permissions_roles_header">"Ruoli"</string>
<string name="screen_room_roles_and_permissions_room_details">"Dettagli della stanza"</string>
<string name="screen_room_roles_and_permissions_title">"Ruoli e autorizzazioni"</string>
<string name="screen_start_chat_error_starting_chat">"Si è verificato un errore durante il tentativo di avviare una chat"</string>
</resources>

View file

@ -1,11 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_dm_details_block_alert_action">"Blocați"</string>
<string name="screen_dm_details_block_alert_description">"Utilizatorii blocați nu vă vor putea trimite mesaje și toate mesajele lor vor fi ascunse. Puteți anula această acțiune oricând."</string>
<string name="screen_dm_details_block_user">"Blocați utilizatorul"</string>
<string name="screen_dm_details_unblock_alert_action">"Deblocați"</string>
<string name="screen_dm_details_unblock_alert_description">"La deblocarea utilizatorului, veți putea vedea din nou toate mesajele de la acesta."</string>
<string name="screen_dm_details_unblock_user">"Deblocați utilizatorul"</string>
<string name="screen_notification_settings_edit_failed_updating_default_mode">"A apărut o eroare în timpul actualizării setărilor pentru notificari."</string>
<string name="screen_notification_settings_mentions_only_disclaimer">"Serverul dumneavoastră nu acceptă această opțiune în camerele criptate, este posibil să nu primiți notificări în unele camere."</string>
<string name="screen_polls_history_title">"Sondaje"</string>
@ -63,5 +57,4 @@
<string name="screen_room_notification_settings_mode_all_messages">"Toate mesajele"</string>
<string name="screen_room_notification_settings_mode_mentions_and_keywords">"Numai mențiuni și cuvinte cheie"</string>
<string name="screen_room_notification_settings_room_custom_settings_title">"În această cameră, anunțați-mă pentru"</string>
<string name="screen_start_chat_error_starting_chat">"A apărut o eroare la încercarea începerii conversației"</string>
</resources>

View file

@ -1,11 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_dm_details_block_alert_action">"Заблокировать"</string>
<string name="screen_dm_details_block_alert_description">"Заблокированные пользователи не смогут отправлять вам сообщения, а все их сообщения будут скрыты. Вы можете разблокировать их в любое время."</string>
<string name="screen_dm_details_block_user">"Заблокировать пользователя"</string>
<string name="screen_dm_details_unblock_alert_action">"Разблокировать"</string>
<string name="screen_dm_details_unblock_alert_description">"Вы снова сможете увидеть все сообщения."</string>
<string name="screen_dm_details_unblock_user">"Разблокировать пользователя"</string>
<string name="screen_notification_settings_edit_failed_updating_default_mode">"При обновлении настроек уведомления произошла ошибка."</string>
<string name="screen_notification_settings_mentions_only_disclaimer">"Ваш домашний сервер не поддерживает эту опцию в зашифрованных комнатах, в некоторых комнатах вы можете не получать уведомления."</string>
<string name="screen_polls_history_title">"Опросы"</string>
@ -117,5 +111,4 @@
<string name="screen_room_roles_and_permissions_roles_header">"Роли"</string>
<string name="screen_room_roles_and_permissions_room_details">"Информация о комнате"</string>
<string name="screen_room_roles_and_permissions_title">"Роли и разрешения"</string>
<string name="screen_start_chat_error_starting_chat">"Произошла ошибка при попытке открытия комнаты"</string>
</resources>

View file

@ -1,11 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_dm_details_block_alert_action">"Zablokovať"</string>
<string name="screen_dm_details_block_alert_description">"Blokovaní používatelia vám nebudú môcť posielať správy a všetky ich správy budú skryté. Môžete ich kedykoľvek odblokovať."</string>
<string name="screen_dm_details_block_user">"Zablokovať používateľa"</string>
<string name="screen_dm_details_unblock_alert_action">"Odblokovať"</string>
<string name="screen_dm_details_unblock_alert_description">"Všetky správy od nich budete môcť opäť vidieť."</string>
<string name="screen_dm_details_unblock_user">"Odblokovať používateľa"</string>
<string name="screen_notification_settings_edit_failed_updating_default_mode">"Pri aktualizácii nastavenia oznámenia došlo k chybe."</string>
<string name="screen_notification_settings_mentions_only_disclaimer">"Váš domovský server nepodporuje túto možnosť v šifrovaných miestnostiach, v niektorých miestnostiach nemusíte dostať upozornenie."</string>
<string name="screen_polls_history_title">"Ankety"</string>
@ -117,5 +111,4 @@
<string name="screen_room_roles_and_permissions_roles_header">"Roly"</string>
<string name="screen_room_roles_and_permissions_room_details">"Podrobnosti o miestnosti"</string>
<string name="screen_room_roles_and_permissions_title">"Roly a povolenia"</string>
<string name="screen_start_chat_error_starting_chat">"Pri pokuse o spustenie konverzácie sa vyskytla chyba"</string>
</resources>

View file

@ -1,11 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_dm_details_block_alert_action">"Blockera"</string>
<string name="screen_dm_details_block_alert_description">"Blockerade användare kommer inte att kunna skicka meddelanden till dig och alla deras meddelanden kommer att döljas. Du kan avblockera dem när som helst."</string>
<string name="screen_dm_details_block_user">"Blockera användare"</string>
<string name="screen_dm_details_unblock_alert_action">"Avblockera"</string>
<string name="screen_dm_details_unblock_alert_description">"Du kommer att kunna se alla meddelanden från dem igen."</string>
<string name="screen_dm_details_unblock_user">"Avblockera användare"</string>
<string name="screen_notification_settings_edit_failed_updating_default_mode">"Ett fel uppstod vid uppdatering av aviseringsinställningen."</string>
<string name="screen_room_change_permissions_everyone">"Alla"</string>
<string name="screen_room_details_add_topic_title">"Lägg till ämne"</string>
@ -48,5 +42,4 @@
<string name="screen_room_notification_settings_mode_all_messages">"Alla meddelanden"</string>
<string name="screen_room_notification_settings_mode_mentions_and_keywords">"Endast omnämnanden och nyckelord"</string>
<string name="screen_room_notification_settings_room_custom_settings_title">"I det här rummet, meddela mig för"</string>
<string name="screen_start_chat_error_starting_chat">"Ett fel uppstod när du försökte starta en chatt"</string>
</resources>

View file

@ -1,11 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_dm_details_block_alert_action">"Заблокувати"</string>
<string name="screen_dm_details_block_alert_description">"Заблоковані користувачі не зможуть надсилати Вам повідомлення, і всі їхні повідомлення будуть приховані. Ви можете розблокувати їх у будь-який час."</string>
<string name="screen_dm_details_block_user">"Заблокувати користувача"</string>
<string name="screen_dm_details_unblock_alert_action">"Розблокувати"</string>
<string name="screen_dm_details_unblock_alert_description">"Ви знову зможете бачити всі повідомлення від них."</string>
<string name="screen_dm_details_unblock_user">"Розблокувати користувача"</string>
<string name="screen_notification_settings_edit_failed_updating_default_mode">"Під час оновлення налаштувань сповіщень сталася помилка."</string>
<string name="screen_notification_settings_mentions_only_disclaimer">"Ваш домашній сервер не підтримує цю опцію в зашифрованих кімнатах, ви можете не отримати сповіщення в деяких кімнатах."</string>
<string name="screen_polls_history_title">"Опитування"</string>
@ -113,5 +107,4 @@
<string name="screen_room_roles_and_permissions_roles_header">"Ролі"</string>
<string name="screen_room_roles_and_permissions_room_details">"Деталі кімнати"</string>
<string name="screen_room_roles_and_permissions_title">"Ролі та дозволи"</string>
<string name="screen_start_chat_error_starting_chat">"Під час спроби почати чат сталася помилка"</string>
</resources>

View file

@ -1,9 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_dm_details_block_alert_action">"封鎖"</string>
<string name="screen_dm_details_block_user">"封鎖使用者"</string>
<string name="screen_dm_details_unblock_alert_action">"解除封鎖"</string>
<string name="screen_dm_details_unblock_user">"解除封鎖使用者"</string>
<string name="screen_notification_settings_edit_failed_updating_default_mode">"更新通知設定時發生錯誤。"</string>
<string name="screen_polls_history_title">"所有投票"</string>
<string name="screen_room_change_permissions_everyone">"所有人"</string>

View file

@ -1,11 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_dm_details_block_alert_action">"Block"</string>
<string name="screen_dm_details_block_alert_description">"Blocked users won\'t be able to send you messages and all their messages will be hidden. You can unblock them anytime."</string>
<string name="screen_dm_details_block_user">"Block user"</string>
<string name="screen_dm_details_unblock_alert_action">"Unblock"</string>
<string name="screen_dm_details_unblock_alert_description">"You\'ll be able to see all messages from them again."</string>
<string name="screen_dm_details_unblock_user">"Unblock user"</string>
<string name="screen_notification_settings_edit_failed_updating_default_mode">"An error occurred while updating the notification setting."</string>
<string name="screen_notification_settings_mentions_only_disclaimer">"Your homeserver does not support this option in encrypted rooms, you may not get notified in some rooms."</string>
<string name="screen_polls_history_title">"Polls"</string>
@ -116,5 +110,4 @@
<string name="screen_room_roles_and_permissions_roles_header">"Roles"</string>
<string name="screen_room_roles_and_permissions_room_details">"Room details"</string>
<string name="screen_room_roles_and_permissions_title">"Roles and permissions"</string>
<string name="screen_start_chat_error_starting_chat">"An error occurred when trying to start a chat"</string>
</resources>

View file

@ -1,96 +0,0 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.roomdetails.impl.blockuser
import androidx.activity.ComponentActivity
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import io.element.android.features.roomdetails.impl.R
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsEvents
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsState
import io.element.android.features.roomdetails.impl.members.details.aRoomMemberDetailsState
import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.tests.testutils.EventsRecorder
import io.element.android.tests.testutils.clickOn
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class BlockUserDialogsTest {
@get:Rule val rule = createAndroidComposeRule<ComponentActivity>()
@Test
fun `confirm block user emit expected Event`() {
val eventsRecorder = EventsRecorder<RoomMemberDetailsEvents>()
rule.setContent {
BlockUserDialogs(
state = aRoomMemberDetailsState(
displayConfirmationDialog = RoomMemberDetailsState.ConfirmationDialog.Block,
eventSink = eventsRecorder,
)
)
}
rule.clickOn(R.string.screen_dm_details_block_alert_action)
eventsRecorder.assertSingle(RoomMemberDetailsEvents.BlockUser(false))
}
@Test
fun `cancel block user emit expected Event`() {
val eventsRecorder = EventsRecorder<RoomMemberDetailsEvents>()
rule.setContent {
BlockUserDialogs(
state = aRoomMemberDetailsState(
displayConfirmationDialog = RoomMemberDetailsState.ConfirmationDialog.Block,
eventSink = eventsRecorder,
)
)
}
rule.clickOn(CommonStrings.action_cancel)
eventsRecorder.assertSingle(RoomMemberDetailsEvents.ClearConfirmationDialog)
}
@Test
fun `confirm unblock user emit expected Event`() {
val eventsRecorder = EventsRecorder<RoomMemberDetailsEvents>()
rule.setContent {
BlockUserDialogs(
state = aRoomMemberDetailsState(
displayConfirmationDialog = RoomMemberDetailsState.ConfirmationDialog.Unblock,
eventSink = eventsRecorder,
)
)
}
rule.clickOn(R.string.screen_dm_details_unblock_alert_action)
eventsRecorder.assertSingle(RoomMemberDetailsEvents.UnblockUser(false))
}
@Test
fun `cancel unblock user emit expected Event`() {
val eventsRecorder = EventsRecorder<RoomMemberDetailsEvents>()
rule.setContent {
BlockUserDialogs(
state = aRoomMemberDetailsState(
displayConfirmationDialog = RoomMemberDetailsState.ConfirmationDialog.Unblock,
eventSink = eventsRecorder,
)
)
}
rule.clickOn(CommonStrings.action_cancel)
eventsRecorder.assertSingle(RoomMemberDetailsEvents.ClearConfirmationDialog)
}
}

View file

@ -25,9 +25,9 @@ import io.element.android.features.createroom.api.StartDMAction
import io.element.android.features.createroom.test.FakeStartDMAction
import io.element.android.features.roomdetails.aMatrixRoom
import io.element.android.features.roomdetails.impl.members.aRoomMember
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsEvents
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsPresenter
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsState
import io.element.android.features.userprofile.shared.UserProfileEvents
import io.element.android.features.userprofile.shared.UserProfileState
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.matrix.api.MatrixClient
@ -66,7 +66,7 @@ class RoomMemberDetailsPresenterTests {
presenter.present()
}.test {
val initialState = awaitFirstItem()
assertThat(initialState.userId).isEqualTo(roomMember.userId.value)
assertThat(initialState.userId).isEqualTo(roomMember.userId)
assertThat(initialState.userName).isEqualTo(roomMember.displayName)
assertThat(initialState.avatarUrl).isEqualTo(roomMember.avatarUrl)
assertThat(initialState.isBlocked).isEqualTo(AsyncData.Success(roomMember.isIgnored))
@ -157,12 +157,12 @@ class RoomMemberDetailsPresenterTests {
presenter.present()
}.test {
val initialState = awaitFirstItem()
initialState.eventSink(RoomMemberDetailsEvents.BlockUser(needsConfirmation = true))
initialState.eventSink(UserProfileEvents.BlockUser(needsConfirmation = true))
val dialogState = awaitItem()
assertThat(dialogState.displayConfirmationDialog).isEqualTo(RoomMemberDetailsState.ConfirmationDialog.Block)
assertThat(dialogState.displayConfirmationDialog).isEqualTo(UserProfileState.ConfirmationDialog.Block)
dialogState.eventSink(RoomMemberDetailsEvents.ClearConfirmationDialog)
dialogState.eventSink(UserProfileEvents.ClearConfirmationDialog)
assertThat(awaitItem().displayConfirmationDialog).isNull()
ensureAllEventsConsumed()
@ -181,12 +181,12 @@ class RoomMemberDetailsPresenterTests {
presenter.present()
}.test {
val initialState = awaitFirstItem()
initialState.eventSink(RoomMemberDetailsEvents.BlockUser(needsConfirmation = false))
initialState.eventSink(UserProfileEvents.BlockUser(needsConfirmation = false))
assertThat(awaitItem().isBlocked.isLoading()).isTrue()
client.emitIgnoreUserList(listOf(roomMember.userId))
assertThat(awaitItem().isBlocked.dataOrNull()).isTrue()
initialState.eventSink(RoomMemberDetailsEvents.UnblockUser(needsConfirmation = false))
initialState.eventSink(UserProfileEvents.UnblockUser(needsConfirmation = false))
assertThat(awaitItem().isBlocked.isLoading()).isTrue()
client.emitIgnoreUserList(listOf())
assertThat(awaitItem().isBlocked.dataOrNull()).isFalse()
@ -202,12 +202,12 @@ class RoomMemberDetailsPresenterTests {
presenter.present()
}.test {
val initialState = awaitFirstItem()
initialState.eventSink(RoomMemberDetailsEvents.BlockUser(needsConfirmation = false))
initialState.eventSink(UserProfileEvents.BlockUser(needsConfirmation = false))
assertThat(awaitItem().isBlocked.isLoading()).isTrue()
val errorState = awaitItem()
assertThat(errorState.isBlocked.errorOrNull()).isEqualTo(A_THROWABLE)
// Clear error
initialState.eventSink(RoomMemberDetailsEvents.ClearBlockUserError)
initialState.eventSink(UserProfileEvents.ClearBlockUserError)
assertThat(awaitItem().isBlocked).isEqualTo(AsyncData.Success(false))
}
}
@ -221,12 +221,12 @@ class RoomMemberDetailsPresenterTests {
presenter.present()
}.test {
val initialState = awaitFirstItem()
initialState.eventSink(RoomMemberDetailsEvents.UnblockUser(needsConfirmation = false))
initialState.eventSink(UserProfileEvents.UnblockUser(needsConfirmation = false))
assertThat(awaitItem().isBlocked.isLoading()).isTrue()
val errorState = awaitItem()
assertThat(errorState.isBlocked.errorOrNull()).isEqualTo(A_THROWABLE)
// Clear error
initialState.eventSink(RoomMemberDetailsEvents.ClearBlockUserError)
initialState.eventSink(UserProfileEvents.ClearBlockUserError)
assertThat(awaitItem().isBlocked).isEqualTo(AsyncData.Success(true))
}
}
@ -238,12 +238,12 @@ class RoomMemberDetailsPresenterTests {
presenter.present()
}.test {
val initialState = awaitFirstItem()
initialState.eventSink(RoomMemberDetailsEvents.UnblockUser(needsConfirmation = true))
initialState.eventSink(UserProfileEvents.UnblockUser(needsConfirmation = true))
val dialogState = awaitItem()
assertThat(dialogState.displayConfirmationDialog).isEqualTo(RoomMemberDetailsState.ConfirmationDialog.Unblock)
assertThat(dialogState.displayConfirmationDialog).isEqualTo(UserProfileState.ConfirmationDialog.Unblock)
dialogState.eventSink(RoomMemberDetailsEvents.ClearConfirmationDialog)
dialogState.eventSink(UserProfileEvents.ClearConfirmationDialog)
assertThat(awaitItem().displayConfirmationDialog).isNull()
ensureAllEventsConsumed()
@ -264,18 +264,18 @@ class RoomMemberDetailsPresenterTests {
// Failure
startDMAction.givenExecuteResult(startDMFailureResult)
initialState.eventSink(RoomMemberDetailsEvents.StartDM)
initialState.eventSink(UserProfileEvents.StartDM)
assertThat(awaitItem().startDmActionState).isInstanceOf(AsyncAction.Loading::class.java)
awaitItem().also { state ->
assertThat(state.startDmActionState).isEqualTo(startDMFailureResult)
state.eventSink(RoomMemberDetailsEvents.ClearStartDMState)
state.eventSink(UserProfileEvents.ClearStartDMState)
}
// Success
startDMAction.givenExecuteResult(startDMSuccessResult)
awaitItem().also { state ->
assertThat(state.startDmActionState).isEqualTo(AsyncAction.Uninitialized)
state.eventSink(RoomMemberDetailsEvents.StartDM)
state.eventSink(UserProfileEvents.StartDM)
}
assertThat(awaitItem().startDmActionState).isInstanceOf(AsyncAction.Loading::class.java)
awaitItem().also { state ->