Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import com.wire.kalium.logic.feature.connection.BlockUserUseCase
import com.wire.kalium.logic.feature.connection.UnblockUserResult
import com.wire.kalium.logic.feature.connection.UnblockUserUseCase
import com.wire.kalium.logic.feature.conversation.AdminlessConversationFailure
import com.wire.kalium.logic.feature.conversation.ArchiveStatusUpdateResult
import com.wire.kalium.logic.feature.conversation.CheckConversationLeaveConditionsUseCase
import com.wire.kalium.logic.feature.conversation.ClearConversationContentUseCase
Expand Down Expand Up @@ -79,6 +80,7 @@
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.concurrent.ConcurrentHashMap

@ViewModelScopedPreview
interface ConversationOptionsMenuViewModel : ActionsManager<ConversationOptionsMenuViewAction> {
val leaveGroupDialogState: VisibilityState<LeaveGroupDialogState> get() = VisibilityState()
Expand Down Expand Up @@ -280,14 +282,21 @@
?.conversation
?.canDeleteGroup() ?: false

override fun leaveGroup(conversationId: ConversationId, conversationName: String, shouldDelete: Boolean) {

Check failure on line 285 in app/src/main/kotlin/com/wire/android/ui/common/bottomsheet/conversation/ConversationOptionsMenuViewModel.kt

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this method to reduce its Cognitive Complexity from 16 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=wireapp_wire-android&issues=AZ8n4xf7hOILnUTCVdEb&open=AZ8n4xf7hOILnUTCVdEb&pullRequest=5025
viewModelScope.launch {
leaveGroupDialogState.update { it.copy(loading = true) }
withContext(nonCancellableIOContext) {
leaveConversation(conversationId)
}.let { result ->
when (result) {
is RemoveMemberFromConversationUseCase.Result.Failure -> onMessage(HomeSnackBarMessage.LeaveConversationError)
is RemoveMemberFromConversationUseCase.Result.Failure -> {
when (val cause = result.cause) {
is AdminlessConversationFailure ->
sendAction(ConversationOptionsMenuViewAction.PromoteAdmin(conversationId, cause.eligibleMembers))
else -> onMessage(HomeSnackBarMessage.LeaveConversationError)
}
}

is RemoveMemberFromConversationUseCase.Result.Success -> when {
shouldDelete -> when (markAsDeletedLocallyAndEnqueueWorkerToDeleteCompletely(conversationId)) {
false -> onMessage(HomeSnackBarMessage.LeaveConversationError)
Expand Down Expand Up @@ -434,6 +443,7 @@

sealed interface ConversationOptionsMenuViewAction {
data class Message(val message: SnackBarMessage) : ConversationOptionsMenuViewAction
data class PromoteAdmin(val conversationId: ConversationId, val eligibleMembers: List<UserId>) : ConversationOptionsMenuViewAction
data class Left(val conversationId: ConversationId, val conversationName: String) : ConversationOptionsMenuViewAction
data class Deleted(val conversationId: ConversationId, val conversationName: String) : ConversationOptionsMenuViewAction
data class DeletedLocally(val conversationId: ConversationId, val conversationName: String) : ConversationOptionsMenuViewAction
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import com.wire.android.ui.home.conversationslist.model.DeleteGroupDialogState
import com.wire.android.ui.theme.WireTheme
import com.wire.android.util.ui.PreviewMultipleThemes
import com.wire.kalium.logic.data.id.ConversationId
import com.wire.kalium.logic.data.user.UserId

@SuppressLint("ComposeModifierMissing")
@Composable
Expand All @@ -55,7 +56,7 @@ fun ConversationOptionsModalSheetLayout(
onLeftConversation: () -> Unit = {},
onDeletedConversation: () -> Unit = {},
onDeletedConversationLocally: () -> Unit = {},
onPromoteAdmin: (ConversationId) -> Unit = {},
onPromoteAdmin: (ConversationId, List<UserId>) -> Unit = { _, _ -> },
openConversationDebugMenu: (ConversationId) -> Unit = {},
viewModel: ConversationOptionsMenuViewModel =
conversationOptionsMenuViewModel()
Expand All @@ -70,7 +71,7 @@ fun ConversationOptionsModalSheetLayout(
dialogState = viewModel.leaveGroupOptionsDialogState,
onPromoteAdmin = { state ->
viewModel.leaveGroupOptionsDialogState.dismiss()
onPromoteAdmin(state.conversationId)
onPromoteAdmin(state.conversationId, emptyList())
},
onDeleteGroup = { state ->
viewModel.leaveGroupOptionsDialogState.dismiss()
Expand Down Expand Up @@ -106,6 +107,7 @@ fun ConversationOptionsModalSheetLayout(
is ConversationOptionsMenuViewAction.Deleted -> onDeletedConversation()
is ConversationOptionsMenuViewAction.DeletedLocally -> onDeletedConversationLocally()
is ConversationOptionsMenuViewAction.Left -> onLeftConversation()
is ConversationOptionsMenuViewAction.PromoteAdmin -> onPromoteAdmin(action.conversationId, action.eligibleMembers)
is ConversationOptionsMenuViewAction.Message -> sheetState.hide {
snackbarHostState.showSnackbar(action.message.uiText.asString(context.resources))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import com.wire.android.ui.home.conversations.search.messages.SearchConversation
import com.wire.android.ui.home.conversations.usecase.GetConversationMessagesFromSearchUseCase
import com.wire.android.util.dispatchers.DispatcherProvider
import com.wire.kalium.logic.feature.conversation.AddMemberToConversationUseCase
import com.wire.kalium.logic.feature.conversation.ObserveConversationMembersUseCase
import com.wire.kalium.logic.feature.conversation.ObserveEligibleMembersForConversationAdminRoleUseCase
import com.wire.kalium.logic.feature.conversation.PromoteAdminAndLeaveConversationUseCase
import com.wire.kalium.logic.feature.conversation.folder.CreateConversationFolderUseCase
Expand All @@ -46,6 +47,7 @@ class ConversationSearchFolderViewModelFactory @Inject constructor(
private val getConversationMessagesFromSearch: GetConversationMessagesFromSearchUseCase,
private val promoteAdminAndLeave: PromoteAdminAndLeaveConversationUseCase,
private val observeEligibleMembers: ObserveEligibleMembersForConversationAdminRoleUseCase,
private val observeConversationMembers: ObserveConversationMembersUseCase,
) {
fun conversationFoldersViewModel(args: ConversationFoldersStateArgs) = ConversationFoldersVMImpl(
args = args,
Expand Down Expand Up @@ -78,6 +80,7 @@ class ConversationSearchFolderViewModelFactory @Inject constructor(
fun promoteAdminViewModel(savedStateHandle: SavedStateHandle) = PromoteAdminViewModel(
promoteAdminAndLeave = promoteAdminAndLeave,
observeEligibleMembers = observeEligibleMembers,
observeConversationMembers = observeConversationMembers,
dispatchers = dispatchers,
savedStateHandle = savedStateHandle,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ import com.ramcosta.composedestinations.generated.app.destinations.UpdateAppsAcc
import com.ramcosta.composedestinations.generated.app.destinations.PromoteAdminScreenDestination
import com.wire.android.ui.home.conversations.details.editguestaccess.EditGuestAccessParams
import com.wire.android.ui.home.conversations.promoteadmin.PromoteAdminNavArgs
import com.wire.android.ui.home.conversations.promoteadmin.toPromoteAdminEligibleMemberArgs
import com.wire.android.ui.home.conversations.details.options.GroupConversationOptions
import com.wire.android.ui.home.conversations.details.options.GroupConversationOptionsState
import com.wire.android.ui.home.conversations.details.options.LoadingGroupConversation
Expand All @@ -131,6 +132,7 @@ import com.wire.android.util.ui.PreviewMultipleThemes
import com.wire.android.util.ui.UIText
import com.wire.kalium.logic.data.conversation.Conversation
import com.wire.kalium.logic.data.id.ConversationId
import com.wire.kalium.logic.data.user.UserId
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
Expand Down Expand Up @@ -304,8 +306,14 @@ fun GroupConversationDetailsScreen(
)
)
},
onPromoteAdmin = { conversationId ->
navigator.navigate(NavigationCommand(PromoteAdminScreenDestination(PromoteAdminNavArgs(conversationId))))
onPromoteAdmin = { conversationId, eligibleMembers ->
navigator.navigate(
NavigationCommand(
PromoteAdminScreenDestination(
PromoteAdminNavArgs(conversationId, eligibleMembers.toPromoteAdminEligibleMemberArgs())
)
)
)
},
openConversationDebugMenu = {
navigator.navigate(
Expand Down Expand Up @@ -384,7 +392,7 @@ private fun GroupConversationDetailsContent(
onMoveToFolder: (ConversationFoldersNavArgs) -> Unit = {},
onLeftConversation: () -> Unit = {},
onDeletedConversation: () -> Unit = {},
onPromoteAdmin: (ConversationId) -> Unit = {},
onPromoteAdmin: (ConversationId, List<UserId>) -> Unit = { _, _ -> },
openConversationDebugMenu: (ConversationId) -> Unit = {},
initialPageIndex: GroupConversationDetailsTabItem = GroupConversationDetailsTabItem.OPTIONS,
isScreenLoading: StateFlow<Boolean> = MutableStateFlow(false),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,19 @@ package com.wire.android.ui.home.conversations.promoteadmin
import android.os.Parcelable
import com.wire.android.ui.home.conversations.QualifiedIdParceler
import com.wire.kalium.logic.data.id.ConversationId
import com.wire.kalium.logic.data.id.QualifiedID
import com.wire.kalium.logic.data.user.UserId
import kotlinx.parcelize.Parcelize
import kotlinx.parcelize.TypeParceler

@Parcelize
@TypeParceler<ConversationId, QualifiedIdParceler>()
data class PromoteAdminNavArgs(val conversationId: ConversationId) : Parcelable
@TypeParceler<QualifiedID, QualifiedIdParceler>()
data class PromoteAdminNavArgs(
val conversationId: ConversationId,
val eligibleMembers: ArrayList<String> = arrayListOf(),
) : Parcelable

fun List<UserId>.toPromoteAdminEligibleMemberArgs(): ArrayList<String> =
mapTo(ArrayList()) { "${it.value}$USER_ID_DOMAIN_SEPARATOR${it.domain}" }

const val USER_ID_DOMAIN_SEPARATOR = "@"
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import com.wire.android.util.dispatchers.DispatcherProvider
import com.wire.kalium.logic.data.conversation.MemberDetails
import com.wire.kalium.logic.data.user.OtherUser
import com.wire.kalium.logic.data.user.UserId
import com.wire.kalium.logic.feature.conversation.ObserveConversationMembersUseCase
import com.wire.kalium.logic.feature.conversation.ObserveEligibleMembersForConversationAdminRoleUseCase
import com.wire.kalium.logic.feature.conversation.PromoteAdminAndLeaveConversationUseCase
import kotlinx.coroutines.NonCancellable
Expand All @@ -40,13 +41,16 @@ import kotlinx.coroutines.withContext
class PromoteAdminViewModel(
private val promoteAdminAndLeave: PromoteAdminAndLeaveConversationUseCase,
private val observeEligibleMembers: ObserveEligibleMembersForConversationAdminRoleUseCase,
private val observeConversationMembers: ObserveConversationMembersUseCase,
private val dispatchers: DispatcherProvider,
savedStateHandle: SavedStateHandle,
) : ActionsViewModel<PromoteAdminAction>() {

private val navArgs: PromoteAdminNavArgs = savedStateHandle.navArgs()

private val allMembers = MutableStateFlow<List<PromoteAdminMemberItem>>(emptyList())
private val clientEligibleMembers = MutableStateFlow<List<PromoteAdminMemberItem>>(emptyList())
private val allConversationMembers = MutableStateFlow<List<PromoteAdminMemberItem>>(emptyList())
private val eligibleMemberIds = MutableStateFlow(navArgs.eligibleMembers.map { it.toUserId() }.toSet())
private val searchQuery = MutableStateFlow("")
private val selectedUserId = MutableStateFlow<UserId?>(null)

Expand All @@ -55,7 +59,18 @@ class PromoteAdminViewModel(

init {
viewModelScope.launch {
combine(allMembers, searchQuery, selectedUserId) { members, query, selected ->
combine(
clientEligibleMembers,
allConversationMembers,
eligibleMemberIds,
searchQuery,
selectedUserId
) { clientEligibleMembers, allConversationMembers, eligibleIds, query, selected ->
val members = if (eligibleIds.isEmpty()) {
clientEligibleMembers
} else {
allConversationMembers.filterByEligibleIds(eligibleIds)
}
PromoteAdminState(
searchQuery = query,
filteredMembers = filter(members, query),
Expand All @@ -69,7 +84,13 @@ class PromoteAdminViewModel(

viewModelScope.launch {
observeEligibleMembers(navArgs.conversationId).collect { members ->
allMembers.value = members.map { it.toMemberItem() }
clientEligibleMembers.value = members.map { it.toMemberItem() }
}
}

viewModelScope.launch {
observeConversationMembers(navArgs.conversationId).collect { members ->
allConversationMembers.value = members.map { it.toMemberItem() }
}
}
}
Expand All @@ -92,9 +113,17 @@ class PromoteAdminViewModel(
}

val action = when (result) {
PromoteAdminAndLeaveConversationUseCase.Result.Success -> PromoteAdminAction.Success
PromoteAdminAndLeaveConversationUseCase.Result.FailedToPromoteUser -> PromoteAdminAction.FailedToPromoteUser
PromoteAdminAndLeaveConversationUseCase.Result.FailedToLeaveConversation -> PromoteAdminAction.FailedToLeaveConversation
is PromoteAdminAndLeaveConversationUseCase.Result.Success -> PromoteAdminAction.Success
is PromoteAdminAndLeaveConversationUseCase.Result.FailedToPromoteUser -> PromoteAdminAction.FailedToPromoteUser
is PromoteAdminAndLeaveConversationUseCase.Result.FailedToLeaveConversation -> {
if (result.eligibleMembers.isEmpty()) {
PromoteAdminAction.FailedToLeaveConversation
} else {
selectedUserId.value = null
eligibleMemberIds.value = result.eligibleMembers.toSet()
PromoteAdminAction.FailedToLeaveConversation
}
}
}

sendAction(action)
Expand All @@ -114,6 +143,14 @@ class PromoteAdminViewModel(
}
}

private fun List<PromoteAdminMemberItem>.filterByEligibleIds(eligibleIds: Set<UserId>): List<PromoteAdminMemberItem> =
if (eligibleIds.isEmpty()) this else filter { it.userId in eligibleIds }

private fun String.toUserId() = UserId(
value = substringBeforeLast(USER_ID_DOMAIN_SEPARATOR),
domain = substringAfterLast(USER_ID_DOMAIN_SEPARATOR),
)

private fun MemberDetails.toMemberItem() = PromoteAdminMemberItem(
userId = user.id,
name = user.name.orEmpty(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ import com.wire.android.ui.common.visbility.rememberVisibilityState
import com.wire.android.ui.debug.conversation.DebugConversationScreenNavArgs
import com.wire.android.ui.home.conversations.PermissionPermanentlyDeniedDialogState
import com.wire.android.ui.home.conversations.promoteadmin.PromoteAdminNavArgs
import com.wire.android.ui.home.conversations.promoteadmin.toPromoteAdminEligibleMemberArgs
import com.wire.android.ui.home.conversationListViewModel
import com.wire.android.ui.home.conversationslist.common.ConversationList
import com.wire.android.ui.home.conversationslist.model.ConversationItem
Expand Down Expand Up @@ -276,7 +277,15 @@ fun ConversationsScreenContent(
ConversationOptionsModalSheetLayout(
sheetState = sheetState,
openConversationFolders = { navigator.navigate(NavigationCommand(ConversationFoldersScreenDestination(it))) },
onPromoteAdmin = { navigator.navigate(NavigationCommand(PromoteAdminScreenDestination(PromoteAdminNavArgs(it)))) },
onPromoteAdmin = { conversationId, eligibleMembers ->
navigator.navigate(
NavigationCommand(
PromoteAdminScreenDestination(
PromoteAdminNavArgs(conversationId, eligibleMembers.toPromoteAdminEligibleMemberArgs())
)
)
)
},
openConversationDebugMenu = { conversationId ->
navigator.navigate(
NavigationCommand(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,8 @@ class OtherUserProfileScreenViewModel @Inject constructor(
)
}
when (response) {
is RemoveMemberFromConversationUseCase.Result.Failure -> onMessage(RemoveConversationMemberError)
is RemoveMemberFromConversationUseCase.Result.Success -> onMessage(RemoveConversationMemberSuccess(state.userName))
is RemoveMemberFromConversationUseCase.Result.Failure -> onMessage(RemoveConversationMemberError)
}
removeConversationMemberDialogState.dismiss()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import com.wire.kalium.logic.feature.connection.BlockUserResult
import com.wire.kalium.logic.feature.connection.BlockUserUseCase
import com.wire.kalium.logic.feature.connection.UnblockUserResult
import com.wire.kalium.logic.feature.connection.UnblockUserUseCase
import com.wire.kalium.logic.feature.conversation.AdminlessConversationFailure
import com.wire.kalium.logic.feature.conversation.ArchiveStatusUpdateResult
import com.wire.kalium.logic.feature.conversation.ClearConversationContentUseCase
import com.wire.kalium.logic.feature.conversation.ConversationUpdateStatusResult
Expand Down Expand Up @@ -372,6 +373,46 @@ class ConversationOptionsMenuViewModelTest {
}
}

@Test
fun `given adminless failure with eligible members, when leaving group, then navigate to promote admin`() =
runTest(dispatcherProvider.main()) {
val eligibleMembers = listOf(userId)
val (arrangement, viewModel) = Arrangement()
.withLeaveConversation(
RemoveMemberFromConversationUseCase.Result.Failure(AdminlessConversationFailure(eligibleMembers))
)
.arrange()

viewModel.actions.test {
viewModel.leaveGroup(conversationId, "name", false)

coVerify(exactly = 1) { arrangement.leaveConversation(conversationId) }
assertIs<ConversationOptionsMenuViewAction.PromoteAdmin>(awaitItem()).also {
assertEquals(conversationId, it.conversationId)
assertEquals(eligibleMembers, it.eligibleMembers)
}
cancelAndIgnoreRemainingEvents()
}
}

@Test
fun `given adminless failure without eligible members, when leaving group, then show leave error`() =
runTest(dispatcherProvider.main()) {
val (arrangement, viewModel) = Arrangement()
.withLeaveConversation(RemoveMemberFromConversationUseCase.Result.Failure(CoreFailure.Unknown(null)))
.arrange()

viewModel.actions.test {
viewModel.leaveGroup(conversationId, "name", false)

coVerify(exactly = 1) { arrangement.leaveConversation(conversationId) }
assertIs<ConversationOptionsMenuViewAction.Message>(awaitItem()).also {
assertIs<HomeSnackBarMessage.LeaveConversationError>(it.message)
}
cancelAndIgnoreRemainingEvents()
}
}

@Test
fun `given success, when leaving group with deleting, then call proper action`() = runTest(dispatcherProvider.main()) {
val (arrangement, viewModel) = Arrangement()
Expand Down
Loading
Loading