diff --git a/app/src/main/kotlin/com/wire/android/di/accountScoped/ConversationModule.kt b/app/src/main/kotlin/com/wire/android/di/accountScoped/ConversationModule.kt index baf45e7b76b..f726d098dc4 100644 --- a/app/src/main/kotlin/com/wire/android/di/accountScoped/ConversationModule.kt +++ b/app/src/main/kotlin/com/wire/android/di/accountScoped/ConversationModule.kt @@ -327,4 +327,14 @@ class ConversationModule { @Provides fun provideGetFavoriteFolderUseCase(conversationScope: ConversationScope) = conversationScope.getFavoriteFolder + + @ViewModelScoped + @Provides + fun provideAddConversationToFavoritesUseCase(conversationScope: ConversationScope) = + conversationScope.addConversationToFavorites + + @ViewModelScoped + @Provides + fun provideRemoveConversationFromFavoritesUseCase(conversationScope: ConversationScope) = + conversationScope.removeConversationFromFavorites } diff --git a/app/src/main/kotlin/com/wire/android/mapper/ConversationMapper.kt b/app/src/main/kotlin/com/wire/android/mapper/ConversationMapper.kt index 951ca60ceed..72af626fba8 100644 --- a/app/src/main/kotlin/com/wire/android/mapper/ConversationMapper.kt +++ b/app/src/main/kotlin/com/wire/android/mapper/ConversationMapper.kt @@ -65,6 +65,7 @@ fun ConversationDetailsWithEvents.toConversationItem( proteusVerificationStatus = conversationDetails.conversation.proteusVerificationStatus, hasNewActivitiesToShow = hasNewActivitiesToShow, searchQuery = searchQuery, + isFavorite = conversationDetails.isFavorite ) } @@ -101,6 +102,7 @@ fun ConversationDetailsWithEvents.toConversationItem( proteusVerificationStatus = conversationDetails.conversation.proteusVerificationStatus, hasNewActivitiesToShow = hasNewActivitiesToShow, searchQuery = searchQuery, + isFavorite = conversationDetails.isFavorite ) } diff --git a/app/src/main/kotlin/com/wire/android/model/SnackBarMessage.kt b/app/src/main/kotlin/com/wire/android/model/SnackBarMessage.kt index 8a020d78792..a3fb5b97001 100644 --- a/app/src/main/kotlin/com/wire/android/model/SnackBarMessage.kt +++ b/app/src/main/kotlin/com/wire/android/model/SnackBarMessage.kt @@ -27,3 +27,10 @@ interface SnackBarMessage { val uiText: UIText val actionLabel: UIText? get() = null } + +data class DefaultSnackBarMessage( + override val uiText: UIText, + override val actionLabel: UIText? = null +) : SnackBarMessage + +fun UIText.asSnackBarMessage(actionLabel: UIText? = null): SnackBarMessage = DefaultSnackBarMessage(this, actionLabel) diff --git a/app/src/main/kotlin/com/wire/android/ui/common/bottomsheet/conversation/ConversationSheetContent.kt b/app/src/main/kotlin/com/wire/android/ui/common/bottomsheet/conversation/ConversationSheetContent.kt index 57007fae7aa..f7aeeec65a6 100644 --- a/app/src/main/kotlin/com/wire/android/ui/common/bottomsheet/conversation/ConversationSheetContent.kt +++ b/app/src/main/kotlin/com/wire/android/ui/common/bottomsheet/conversation/ConversationSheetContent.kt @@ -37,7 +37,7 @@ import com.wire.kalium.logic.data.user.UserId fun ConversationSheetContent( conversationSheetState: ConversationSheetState, onMutingConversationStatusChange: () -> Unit, - addConversationToFavourites: () -> Unit, + changeFavoriteState: (GroupDialogState, addToFavorite: Boolean) -> Unit, moveConversationToFolder: () -> Unit, updateConversationArchiveStatus: (DialogState) -> Unit, clearConversationContent: (DialogState) -> Unit, @@ -54,9 +54,8 @@ fun ConversationSheetContent( ConversationOptionNavigation.Home -> { ConversationMainSheetContent( conversationSheetContent = conversationSheetState.conversationSheetContent!!, + changeFavoriteState = changeFavoriteState, // TODO(profile): enable when implemented -// -// addConversationToFavourites = addConversationToFavourites, // moveConversationToFolder = moveConversationToFolder, updateConversationArchiveStatus = updateConversationArchiveStatus, clearConversationContent = clearConversationContent, @@ -125,6 +124,7 @@ data class ConversationSheetContent( val mlsVerificationStatus: Conversation.VerificationStatus, val proteusVerificationStatus: Conversation.VerificationStatus, val isUnderLegalHold: Boolean, + val isFavorite: Boolean? ) { private val isSelfUserMember: Boolean get() = selfRole != null @@ -147,9 +147,9 @@ data class ConversationSheetContent( fun canUnblockUser(): Boolean = conversationTypeDetail is ConversationTypeDetail.Private && conversationTypeDetail.blockingState == BlockingState.BLOCKED - fun canAddToFavourite(): Boolean = - (conversationTypeDetail is ConversationTypeDetail.Private && conversationTypeDetail.blockingState != BlockingState.BLOCKED) - || conversationTypeDetail is ConversationTypeDetail.Group + fun canAddToFavourite(): Boolean = isFavorite != null && + ((conversationTypeDetail is ConversationTypeDetail.Private && conversationTypeDetail.blockingState != BlockingState.BLOCKED) + || conversationTypeDetail is ConversationTypeDetail.Group) fun isAbandonedOneOnOneConversation(participantsCount: Int): Boolean = title.isEmpty() && participantsCount == 1 } diff --git a/app/src/main/kotlin/com/wire/android/ui/common/bottomsheet/conversation/ConversationSheetState.kt b/app/src/main/kotlin/com/wire/android/ui/common/bottomsheet/conversation/ConversationSheetState.kt index 0279e37367b..3455dd1315b 100644 --- a/app/src/main/kotlin/com/wire/android/ui/common/bottomsheet/conversation/ConversationSheetState.kt +++ b/app/src/main/kotlin/com/wire/android/ui/common/bottomsheet/conversation/ConversationSheetState.kt @@ -78,7 +78,8 @@ fun rememberConversationSheetState( protocol = Conversation.ProtocolInfo.Proteus, mlsVerificationStatus = Conversation.VerificationStatus.VERIFIED, proteusVerificationStatus = Conversation.VerificationStatus.VERIFIED, - isUnderLegalHold = showLegalHoldIndicator + isUnderLegalHold = showLegalHoldIndicator, + isFavorite = isFavorite ) } } @@ -102,7 +103,8 @@ fun rememberConversationSheetState( protocol = Conversation.ProtocolInfo.Proteus, mlsVerificationStatus = Conversation.VerificationStatus.VERIFIED, proteusVerificationStatus = Conversation.VerificationStatus.VERIFIED, - isUnderLegalHold = showLegalHoldIndicator + isUnderLegalHold = showLegalHoldIndicator, + isFavorite = isFavorite ) } } @@ -122,7 +124,8 @@ fun rememberConversationSheetState( protocol = Conversation.ProtocolInfo.Proteus, mlsVerificationStatus = Conversation.VerificationStatus.VERIFIED, proteusVerificationStatus = Conversation.VerificationStatus.VERIFIED, - isUnderLegalHold = showLegalHoldIndicator + isUnderLegalHold = showLegalHoldIndicator, + isFavorite = null ) } } diff --git a/app/src/main/kotlin/com/wire/android/ui/common/bottomsheet/conversation/HomeSheetContent.kt b/app/src/main/kotlin/com/wire/android/ui/common/bottomsheet/conversation/HomeSheetContent.kt index ab514914c85..c06603826aa 100644 --- a/app/src/main/kotlin/com/wire/android/ui/common/bottomsheet/conversation/HomeSheetContent.kt +++ b/app/src/main/kotlin/com/wire/android/ui/common/bottomsheet/conversation/HomeSheetContent.kt @@ -53,12 +53,14 @@ import com.wire.android.ui.theme.wireTypography import com.wire.kalium.logic.data.conversation.MutedConversationStatus import com.wire.kalium.logic.data.user.ConnectionState +// items cannot be simplified +@Suppress("CyclomaticComplexMethod") @Composable internal fun ConversationMainSheetContent( conversationSheetContent: ConversationSheetContent, -// TODO(profile): enable when implemented -// addConversationToFavourites: () -> Unit, -// moveConversationToFolder: () -> Unit, + changeFavoriteState: (dialogState: GroupDialogState, addToFavorite: Boolean) -> Unit, + // TODO(profile): enable when implemented + // moveConversationToFolder: () -> Unit, updateConversationArchiveStatus: (DialogState) -> Unit, clearConversationContent: (DialogState) -> Unit, blockUserClick: (BlockUserDialogState) -> Unit, @@ -108,21 +110,38 @@ internal fun ConversationMainSheetContent( ) } } + + if (conversationSheetContent.canAddToFavourite() && !conversationSheetContent.isArchived) { + conversationSheetContent.isFavorite?.let { isFavorite -> + add { + MenuBottomSheetItem( + title = stringResource( + if (isFavorite) { + R.string.label_remove_from_favourites + } else { + R.string.label_add_to_favourites + } + ), + leading = { + MenuItemIcon( + id = R.drawable.ic_favourite, + contentDescription = null + ) + }, + onItemClick = { + changeFavoriteState( + GroupDialogState( + conversationSheetContent.conversationId, + conversationSheetContent.title + ), + !isFavorite + ) + } + ) + } + } + } // TODO(profile): enable when implemented -// -// if (conversationSheetContent.canAddToFavourite()) -// add { -// MenuBottomSheetItem( -// title = stringResource(R.string.label_add_to_favourites), -// icon = { -// MenuItemIcon( -// id = R.drawable.ic_favourite, -// contentDescription = stringResource(R.string.content_description_add_to_favourite), -// ) -// }, -// onItemClick = addConversationToFavourites -// ) -// } // add { // MenuBottomSheetItem( // icon = { diff --git a/app/src/main/kotlin/com/wire/android/ui/common/bottomsheet/folder/ChangeConversationFavoriteStateArgs.kt b/app/src/main/kotlin/com/wire/android/ui/common/bottomsheet/folder/ChangeConversationFavoriteStateArgs.kt new file mode 100644 index 00000000000..a3dd9b6b5b7 --- /dev/null +++ b/app/src/main/kotlin/com/wire/android/ui/common/bottomsheet/folder/ChangeConversationFavoriteStateArgs.kt @@ -0,0 +1,26 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.wire.android.ui.common.bottomsheet.folder + +import com.wire.android.di.ScopedArgs +import kotlinx.serialization.Serializable + +@Serializable +object ChangeConversationFavoriteStateArgs : ScopedArgs { + override val key = "ConnectionActionButtonArgsKey" +} diff --git a/app/src/main/kotlin/com/wire/android/ui/common/bottomsheet/folder/ChangeConversationFavoriteVM.kt b/app/src/main/kotlin/com/wire/android/ui/common/bottomsheet/folder/ChangeConversationFavoriteVM.kt new file mode 100644 index 00000000000..6d2c0a14cd2 --- /dev/null +++ b/app/src/main/kotlin/com/wire/android/ui/common/bottomsheet/folder/ChangeConversationFavoriteVM.kt @@ -0,0 +1,71 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +package com.wire.android.ui.common.bottomsheet.folder + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.wire.android.R +import com.wire.android.di.ViewModelScopedPreview +import com.wire.android.model.SnackBarMessage +import com.wire.android.model.asSnackBarMessage +import com.wire.android.ui.home.conversationslist.model.GroupDialogState +import com.wire.android.util.ui.UIText +import com.wire.kalium.logic.feature.conversation.folder.AddConversationToFavoritesUseCase +import com.wire.kalium.logic.feature.conversation.folder.RemoveConversationFromFavoritesUseCase +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.launch +import javax.inject.Inject + +@ViewModelScopedPreview +interface ChangeConversationFavoriteVM { + val infoMessage: SharedFlow + get() = MutableSharedFlow() + + fun changeFavoriteState(dialogState: GroupDialogState, addToFavorite: Boolean) {} +} + +@HiltViewModel +class ChangeConversationFavoriteVMImpl @Inject constructor( + private val addConversationToFavorites: AddConversationToFavoritesUseCase, + private val removeConversationFromFavorites: RemoveConversationFromFavoritesUseCase, +) : ChangeConversationFavoriteVM, ViewModel() { + + private val _infoMessage = MutableSharedFlow() + override val infoMessage = _infoMessage.asSharedFlow() + + override fun changeFavoriteState(dialogState: GroupDialogState, addToFavorite: Boolean) { + viewModelScope.launch { + val messageResource = if (addToFavorite) { + when (addConversationToFavorites(dialogState.conversationId)) { + is AddConversationToFavoritesUseCase.Result.Failure -> R.string.error_adding_to_favorite + AddConversationToFavoritesUseCase.Result.Success -> R.string.success_adding_to_favorite + } + } else { + when (removeConversationFromFavorites(dialogState.conversationId)) { + is RemoveConversationFromFavoritesUseCase.Result.Failure -> R.string.error_removing_from_favorite + RemoveConversationFromFavoritesUseCase.Result.Success -> R.string.success_removing_from_favorite + } + } + _infoMessage.emit(UIText.StringResource(messageResource, dialogState.conversationName).asSnackBarMessage()) + } + } +} diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/GroupConversationDetailsScreen.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/GroupConversationDetailsScreen.kt index a6d19e62bc7..b067f167aeb 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/GroupConversationDetailsScreen.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/GroupConversationDetailsScreen.kt @@ -63,6 +63,7 @@ import com.ramcosta.composedestinations.result.ResultBackNavigator import com.ramcosta.composedestinations.result.ResultRecipient import com.wire.android.R import com.wire.android.appLogger +import com.wire.android.di.hiltViewModelScoped import com.wire.android.navigation.NavigationCommand import com.wire.android.navigation.Navigator import com.wire.android.navigation.WireDestination @@ -78,6 +79,9 @@ import com.wire.android.ui.common.bottomsheet.WireModalSheetLayout import com.wire.android.ui.common.bottomsheet.conversation.ConversationSheetContent import com.wire.android.ui.common.bottomsheet.conversation.ConversationTypeDetail import com.wire.android.ui.common.bottomsheet.conversation.rememberConversationSheetState +import com.wire.android.ui.common.bottomsheet.folder.ChangeConversationFavoriteStateArgs +import com.wire.android.ui.common.bottomsheet.folder.ChangeConversationFavoriteVM +import com.wire.android.ui.common.bottomsheet.folder.ChangeConversationFavoriteVMImpl import com.wire.android.ui.common.bottomsheet.rememberWireModalSheetState import com.wire.android.ui.common.bottomsheet.show import com.wire.android.ui.common.button.WirePrimaryButton @@ -285,7 +289,11 @@ private fun GroupConversationDetailsContent( isLoading: Boolean, isAbandonedOneOnOneConversation: Boolean, onSearchConversationMessagesClick: () -> Unit, - onConversationMediaClick: () -> Unit + onConversationMediaClick: () -> Unit, + changeConversationFavoriteStateViewModel: ChangeConversationFavoriteVM = + hiltViewModelScoped( + ChangeConversationFavoriteStateArgs + ), ) { val scope = rememberCoroutineScope() val resources = LocalContext.current.resources @@ -461,7 +469,7 @@ private fun GroupConversationDetailsContent( ) } }, - addConversationToFavourites = bottomSheetEventsHandler::onAddConversationToFavourites, + changeFavoriteState = changeConversationFavoriteStateViewModel::changeFavoriteState, moveConversationToFolder = bottomSheetEventsHandler::onMoveConversationToFolder, updateConversationArchiveStatus = { // Only show the confirmation dialog if the conversation is not archived @@ -597,7 +605,8 @@ fun PreviewGroupConversationDetails() { ), mlsVerificationStatus = Conversation.VerificationStatus.VERIFIED, isUnderLegalHold = false, - proteusVerificationStatus = Conversation.VerificationStatus.VERIFIED + proteusVerificationStatus = Conversation.VerificationStatus.VERIFIED, + isFavorite = false ), bottomSheetEventsHandler = GroupConversationDetailsBottomSheetEventsHandler.PREVIEW, onBackPressed = {}, diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/GroupConversationDetailsViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/GroupConversationDetailsViewModel.kt index 31063e6a052..e5704c32097 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/GroupConversationDetailsViewModel.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/GroupConversationDetailsViewModel.kt @@ -158,6 +158,7 @@ class GroupConversationDetailsViewModel @Inject constructor( mlsVerificationStatus = groupDetails.conversation.mlsVerificationStatus, proteusVerificationStatus = groupDetails.conversation.proteusVerificationStatus, isUnderLegalHold = groupDetails.conversation.legalHoldStatus.showLegalHoldIndicator(), + isFavorite = groupDetails.isFavorite ) updateState( @@ -374,10 +375,6 @@ class GroupConversationDetailsViewModel @Inject constructor( } } - @Suppress("EmptyFunctionBlock") - override fun onAddConversationToFavourites(conversationId: ConversationId?) { - } - @Suppress("EmptyFunctionBlock") override fun onMoveConversationToFolder(conversationId: ConversationId?) { } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/menu/GroupConversationDetailsBottomSheetEventsHandler.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/menu/GroupConversationDetailsBottomSheetEventsHandler.kt index 16a000ea627..6a0d8132b13 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/menu/GroupConversationDetailsBottomSheetEventsHandler.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/menu/GroupConversationDetailsBottomSheetEventsHandler.kt @@ -27,7 +27,6 @@ import com.wire.kalium.util.DateTimeUtil @Suppress("TooManyFunctions") interface GroupConversationDetailsBottomSheetEventsHandler { fun onMutingConversationStatusChange(conversationId: ConversationId?, status: MutedConversationStatus, onMessage: (UIText) -> Unit) - fun onAddConversationToFavourites(conversationId: ConversationId? = null) fun onMoveConversationToFolder(conversationId: ConversationId? = null) fun updateConversationArchiveStatus( dialogState: DialogState, @@ -47,7 +46,6 @@ interface GroupConversationDetailsBottomSheetEventsHandler { ) { } - override fun onAddConversationToFavourites(conversationId: ConversationId?) {} override fun onMoveConversationToFolder(conversationId: ConversationId?) {} override fun updateConversationArchiveStatus( dialogState: DialogState, diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/ConversationListViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/ConversationListViewModel.kt index 2b5c4b66748..a4a225b4c59 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/ConversationListViewModel.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/ConversationListViewModel.kt @@ -110,7 +110,6 @@ interface ConversationListViewModel { fun leaveGroup(leaveGroupState: GroupDialogState) {} fun clearConversationContent(dialogState: DialogState) {} fun muteConversation(conversationId: ConversationId?, mutedConversationStatus: MutedConversationStatus) {} - fun addConversationToFavourites() {} fun moveConversationToFolder() {} fun searchQueryChanged(searchQuery: String) {} } @@ -367,11 +366,6 @@ class ConversationListViewModelImpl @AssistedInject constructor( } } - // TODO: needs to be implemented - @Suppress("EmptyFunctionBlock") - override fun addConversationToFavourites() { - } - // TODO: needs to be implemented @Suppress("EmptyFunctionBlock") override fun moveConversationToFolder() { diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/ConversationsScreenContent.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/ConversationsScreenContent.kt index 4d29db15cb6..244fc837067 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/ConversationsScreenContent.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/ConversationsScreenContent.kt @@ -33,6 +33,7 @@ import androidx.paging.LoadState import androidx.paging.compose.collectAsLazyPagingItems import com.wire.android.R import com.wire.android.appLogger +import com.wire.android.di.hiltViewModelScoped import com.wire.android.feature.analytics.AnonymousAnalyticsManagerImpl import com.wire.android.feature.analytics.model.AnalyticsEvent import com.wire.android.navigation.NavigationCommand @@ -44,6 +45,9 @@ import com.wire.android.ui.common.bottomsheet.WireModalSheetLayout import com.wire.android.ui.common.bottomsheet.conversation.ConversationOptionNavigation import com.wire.android.ui.common.bottomsheet.conversation.ConversationSheetContent import com.wire.android.ui.common.bottomsheet.conversation.rememberConversationSheetState +import com.wire.android.ui.common.bottomsheet.folder.ChangeConversationFavoriteStateArgs +import com.wire.android.ui.common.bottomsheet.folder.ChangeConversationFavoriteVM +import com.wire.android.ui.common.bottomsheet.folder.ChangeConversationFavoriteVMImpl import com.wire.android.ui.common.bottomsheet.rememberWireModalSheetState import com.wire.android.ui.common.dialogs.ArchiveConversationDialog import com.wire.android.ui.common.dialogs.BlockUserDialogContent @@ -98,6 +102,10 @@ fun ConversationsScreenContent( LocalInspectionMode.current -> ConversationCallListViewModelPreview else -> hiltViewModel(key = "call_${conversationsSource.name}") }, + changeConversationFavoriteStateViewModel: ChangeConversationFavoriteVM = + hiltViewModelScoped( + ChangeConversationFavoriteStateArgs + ), ) { var currentConversationOptionNavigation by remember { mutableStateOf(ConversationOptionNavigation.Home) @@ -304,7 +312,7 @@ fun ConversationsScreenContent( mutedConversationStatus = conversationState.conversationSheetContent!!.mutingConversationState ) }, - addConversationToFavourites = conversationListViewModel::addConversationToFavourites, + changeFavoriteState = changeConversationFavoriteStateViewModel::changeFavoriteState, moveConversationToFolder = conversationListViewModel::moveConversationToFolder, updateConversationArchiveStatus = showConfirmationDialogOrUnarchive(), clearConversationContent = clearContentDialogState::show, @@ -318,6 +326,9 @@ fun ConversationsScreenContent( } SnackBarMessageHandler(infoMessages = conversationListViewModel.infoMessage) + SnackBarMessageHandler(infoMessages = changeConversationFavoriteStateViewModel.infoMessage, onEmitted = { + sheetState.hide() + }) } private const val TAG = "BaseConversationsScreen" diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/common/ConversationItemFactory.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/common/ConversationItemFactory.kt index 9a8f216b91b..180824ee0a9 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/common/ConversationItemFactory.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/common/ConversationItemFactory.kt @@ -311,7 +311,8 @@ fun PreviewGroupConversationItemWithUnreadCount() = WireTheme { teamId = null, isArchived = false, mlsVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED, - proteusVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED + proteusVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED, + isFavorite = false ), modifier = Modifier, isSelectableItem = false, @@ -336,7 +337,8 @@ fun PreviewGroupConversationItemWithNoBadges() = WireTheme { teamId = null, isArchived = false, mlsVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED, - proteusVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED + proteusVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED, + isFavorite = false ), modifier = Modifier, isSelectableItem = false, @@ -363,7 +365,8 @@ fun PreviewGroupConversationItemWithLastDeletedMessage() = WireTheme { teamId = null, isArchived = false, mlsVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED, - proteusVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED + proteusVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED, + isFavorite = false ), modifier = Modifier, isSelectableItem = false, @@ -388,7 +391,8 @@ fun PreviewGroupConversationItemWithMutedBadgeAndUnreadMentionBadge() = WireThem teamId = null, isArchived = false, mlsVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED, - proteusVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED + proteusVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED, + isFavorite = false ), modifier = Modifier, isSelectableItem = false, @@ -414,7 +418,8 @@ fun PreviewGroupConversationItemWithOngoingCall() = WireTheme { hasOnGoingCall = true, isArchived = false, mlsVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED, - proteusVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED + proteusVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED, + isFavorite = false ), modifier = Modifier, isSelectableItem = false, @@ -496,7 +501,8 @@ fun PreviewPrivateConversationItemWithBlockedBadge() = WireTheme { userId = UserId("value", "domain"), isArchived = false, mlsVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED, - proteusVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED + proteusVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED, + isFavorite = false ), modifier = Modifier, isSelectableItem = false, diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/common/ConversationList.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/common/ConversationList.kt index e57d257d11d..2fe1ea862ce 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/common/ConversationList.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/common/ConversationList.kt @@ -204,6 +204,7 @@ fun previewConversationList(count: Int, startIndex: Int = 0, unread: Boolean = f mlsVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED, proteusVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED, searchQuery = searchQuery, + isFavorite = false ) ) @@ -222,6 +223,7 @@ fun previewConversationList(count: Int, startIndex: Int = 0, unread: Boolean = f mlsVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED, proteusVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED, searchQuery = searchQuery, + isFavorite = false ) ) } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/model/ConversationItem.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/model/ConversationItem.kt index dbc5f36305c..46726d55209 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/model/ConversationItem.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/model/ConversationItem.kt @@ -38,6 +38,7 @@ sealed class ConversationItem : ConversationFolderItem { abstract val badgeEventType: BadgeEventType abstract val teamId: TeamId? abstract val isArchived: Boolean + abstract val isFavorite: Boolean abstract val mlsVerificationStatus: Conversation.VerificationStatus abstract val proteusVerificationStatus: Conversation.VerificationStatus abstract val hasNewActivitiesToShow: Boolean @@ -58,6 +59,7 @@ sealed class ConversationItem : ConversationFolderItem { override val badgeEventType: BadgeEventType, override val teamId: TeamId?, override val isArchived: Boolean, + override val isFavorite: Boolean, override val mlsVerificationStatus: Conversation.VerificationStatus, override val proteusVerificationStatus: Conversation.VerificationStatus, override val hasNewActivitiesToShow: Boolean = false, @@ -76,6 +78,7 @@ sealed class ConversationItem : ConversationFolderItem { override val badgeEventType: BadgeEventType, override val teamId: TeamId?, override val isArchived: Boolean, + override val isFavorite: Boolean, override val mlsVerificationStatus: Conversation.VerificationStatus, override val proteusVerificationStatus: Conversation.VerificationStatus, override val hasNewActivitiesToShow: Boolean = false, @@ -91,6 +94,7 @@ sealed class ConversationItem : ConversationFolderItem { override val lastMessageContent: UILastMessageContent?, override val badgeEventType: BadgeEventType, override val isArchived: Boolean = false, + override val isFavorite: Boolean = false, override val hasNewActivitiesToShow: Boolean = false, override val searchQuery: String = "", ) : ConversationItem() { diff --git a/app/src/main/kotlin/com/wire/android/ui/userprofile/other/OtherUserProfileEventsHandlers.kt b/app/src/main/kotlin/com/wire/android/ui/userprofile/other/OtherUserProfileEventsHandlers.kt index 42bad51159b..162b14cee3c 100644 --- a/app/src/main/kotlin/com/wire/android/ui/userprofile/other/OtherUserProfileEventsHandlers.kt +++ b/app/src/main/kotlin/com/wire/android/ui/userprofile/other/OtherUserProfileEventsHandlers.kt @@ -67,7 +67,6 @@ interface OtherUserProfileFooterEventsHandler { interface OtherUserProfileBottomSheetEventsHandler { fun onChangeMemberRole(role: Conversation.Member.Role) fun onMutingConversationStatusChange(conversationId: ConversationId?, status: MutedConversationStatus) - fun onAddConversationToFavourites(conversationId: ConversationId? = null) fun onMoveConversationToFolder(conversationId: ConversationId? = null) fun onMoveConversationToArchive(dialogState: DialogState) fun onClearConversationContent(dialogState: DialogState) @@ -77,7 +76,6 @@ interface OtherUserProfileBottomSheetEventsHandler { val PREVIEW = object : OtherUserProfileBottomSheetEventsHandler { override fun onChangeMemberRole(role: Conversation.Member.Role) {} override fun onMutingConversationStatusChange(conversationId: ConversationId?, status: MutedConversationStatus) {} - override fun onAddConversationToFavourites(conversationId: ConversationId?) {} override fun onMoveConversationToFolder(conversationId: ConversationId?) {} override fun onMoveConversationToArchive(dialogState: DialogState) {} override fun onClearConversationContent(dialogState: DialogState) {} diff --git a/app/src/main/kotlin/com/wire/android/ui/userprofile/other/OtherUserProfileScreen.kt b/app/src/main/kotlin/com/wire/android/ui/userprofile/other/OtherUserProfileScreen.kt index b71d1d77019..98d5a77978d 100644 --- a/app/src/main/kotlin/com/wire/android/ui/userprofile/other/OtherUserProfileScreen.kt +++ b/app/src/main/kotlin/com/wire/android/ui/userprofile/other/OtherUserProfileScreen.kt @@ -54,6 +54,7 @@ import androidx.hilt.navigation.compose.hiltViewModel import com.ramcosta.composedestinations.annotation.RootNavGraph import com.ramcosta.composedestinations.result.ResultBackNavigator import com.wire.android.R +import com.wire.android.di.hiltViewModelScoped import com.wire.android.navigation.BackStackMode import com.wire.android.navigation.NavigationCommand import com.wire.android.navigation.Navigator @@ -67,6 +68,9 @@ import com.wire.android.ui.common.VisibilityState import com.wire.android.ui.common.WireTabRow import com.wire.android.ui.common.bottomsheet.WireModalSheetLayout import com.wire.android.ui.common.bottomsheet.WireModalSheetState +import com.wire.android.ui.common.bottomsheet.folder.ChangeConversationFavoriteStateArgs +import com.wire.android.ui.common.bottomsheet.folder.ChangeConversationFavoriteVM +import com.wire.android.ui.common.bottomsheet.folder.ChangeConversationFavoriteVMImpl import com.wire.android.ui.common.bottomsheet.rememberWireModalSheetState import com.wire.android.ui.common.bottomsheet.show import com.wire.android.ui.common.button.WireButtonState @@ -104,6 +108,7 @@ import com.wire.android.ui.userprofile.group.RemoveConversationMemberState import com.wire.android.ui.userprofile.other.bottomsheet.OtherUserBottomSheetState import com.wire.android.ui.userprofile.other.bottomsheet.OtherUserProfileBottomSheetContent import com.wire.android.util.ui.PreviewMultipleThemes +import com.wire.android.util.ui.SnackBarMessageHandler import com.wire.android.util.ui.UIText import com.wire.kalium.logic.data.id.ConversationId import com.wire.kalium.logic.data.user.ConnectionState @@ -202,6 +207,7 @@ fun OtherUserProfileScreen( snackbarHostState.showSnackbar(it.asString(context.resources)) } } + LaunchedEffect(Unit) { viewModel.closeBottomSheet.collect { sheetState.hide() @@ -237,7 +243,11 @@ fun OtherProfileScreenContent( onOpenDeviceDetails: (Device) -> Unit = {}, onConversationMediaClick: () -> Unit = {}, navigateBack: () -> Unit = {}, - onLegalHoldLearnMoreClick: () -> Unit = {} + onLegalHoldLearnMoreClick: () -> Unit = {}, + changeConversationFavoriteViewModel: ChangeConversationFavoriteVM = + hiltViewModelScoped( + ChangeConversationFavoriteStateArgs + ) ) { val otherUserProfileScreenState = rememberOtherUserProfileScreenState() val blockUserDialogState = rememberVisibilityState() @@ -273,6 +283,8 @@ fun OtherProfileScreenContent( }) } + SnackBarMessageHandler(changeConversationFavoriteViewModel.infoMessage, onEmitted = closeBottomSheet) + val tabItems by remember(state) { derivedStateOf { listOfNotNull( @@ -358,6 +370,7 @@ fun OtherProfileScreenContent( unblockUser = unblockUserDialogState::show, clearContent = clearConversationDialogState::show, archivingStatusState = archivingConversationDialogState::show, + changeFavoriteState = changeConversationFavoriteViewModel::changeFavoriteState, closeBottomSheet = closeBottomSheet, ) } diff --git a/app/src/main/kotlin/com/wire/android/ui/userprofile/other/OtherUserProfileScreenViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/userprofile/other/OtherUserProfileScreenViewModel.kt index f11a08c7030..c57fa29f6d7 100644 --- a/app/src/main/kotlin/com/wire/android/ui/userprofile/other/OtherUserProfileScreenViewModel.kt +++ b/app/src/main/kotlin/com/wire/android/ui/userprofile/other/OtherUserProfileScreenViewModel.kt @@ -307,10 +307,6 @@ class OtherUserProfileScreenViewModel @Inject constructor( } } - @Suppress("EmptyFunctionBlock") - override fun onAddConversationToFavourites(conversationId: ConversationId?) { - } - @Suppress("EmptyFunctionBlock") override fun onMoveConversationToFolder(conversationId: ConversationId?) { } @@ -421,6 +417,7 @@ class OtherUserProfileScreenViewModel @Inject constructor( mlsVerificationStatus = conversation.mlsVerificationStatus, proteusVerificationStatus = conversation.proteusVerificationStatus, isUnderLegalHold = conversation.legalHoldStatus.showLegalHoldIndicator(), + isFavorite = null ) } ) diff --git a/app/src/main/kotlin/com/wire/android/ui/userprofile/other/bottomsheet/OtherUserProfileBottomSheet.kt b/app/src/main/kotlin/com/wire/android/ui/userprofile/other/bottomsheet/OtherUserProfileBottomSheet.kt index ae26a0e52a5..1757407daf3 100644 --- a/app/src/main/kotlin/com/wire/android/ui/userprofile/other/bottomsheet/OtherUserProfileBottomSheet.kt +++ b/app/src/main/kotlin/com/wire/android/ui/userprofile/other/bottomsheet/OtherUserProfileBottomSheet.kt @@ -24,6 +24,7 @@ import com.wire.android.ui.common.bottomsheet.conversation.rememberConversationS import com.wire.android.ui.common.dialogs.BlockUserDialogState import com.wire.android.ui.common.dialogs.UnblockUserDialogState import com.wire.android.ui.home.conversationslist.model.DialogState +import com.wire.android.ui.home.conversationslist.model.GroupDialogState import com.wire.android.ui.userprofile.other.OtherUserProfileBottomSheetEventsHandler @Composable @@ -34,6 +35,7 @@ fun OtherUserProfileBottomSheetContent( archivingStatusState: (DialogState) -> Unit, blockUser: (BlockUserDialogState) -> Unit, unblockUser: (UnblockUserDialogState) -> Unit, + changeFavoriteState: (GroupDialogState, addToFavorite: Boolean) -> Unit, closeBottomSheet: () -> Unit, getBottomSheetVisibility: () -> Boolean ) { @@ -50,7 +52,7 @@ fun OtherUserProfileBottomSheetContent( mutedConversationStatus ) }, - addConversationToFavourites = eventsHandler::onAddConversationToFavourites, + changeFavoriteState = changeFavoriteState, moveConversationToFolder = eventsHandler::onMoveConversationToFolder, updateConversationArchiveStatus = { if (!it.isArchived) { diff --git a/app/src/main/kotlin/com/wire/android/util/ui/SnackBarMessageHandler.kt b/app/src/main/kotlin/com/wire/android/util/ui/SnackBarMessageHandler.kt index 525edb9aba8..f523e0f2540 100644 --- a/app/src/main/kotlin/com/wire/android/util/ui/SnackBarMessageHandler.kt +++ b/app/src/main/kotlin/com/wire/android/util/ui/SnackBarMessageHandler.kt @@ -29,13 +29,15 @@ import kotlinx.coroutines.flow.SharedFlow @Composable fun SnackBarMessageHandler( infoMessages: SharedFlow, - onActionClicked: (SnackBarMessage) -> Unit = {} + onEmitted: () -> Unit = {}, + onActionClicked: (SnackBarMessage) -> Unit = {}, ) { val context = LocalContext.current val snackbarHostState = LocalSnackbarHostState.current LaunchedEffect(Unit) { infoMessages.collect { + onEmitted() snackbarHostState.showSnackbar( message = it.uiText.asString(context.resources), actionLabel = it.actionLabel?.asString(context.resources), diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 506a4495b54..5b8ddbbb05c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -636,6 +636,7 @@ Notifications Add to Favorites + Remove from Favorites Move to Folder Move to Archive Unarchive @@ -904,6 +905,11 @@ Archive conversation? This conversation moves into your archive. You still get new messages, files, and calls, but no notifications. You can unarchive the conversation at any time. Archive + + “%s” was added to Favorites + “%s” was removed from Favorites + “%s” could not be added to Favorites + “%s” could not be removed from Favorites MessageComposeInputState transition HorizontalBouncingWritingPen transition diff --git a/app/src/test/kotlin/com/wire/android/framework/TestConversationItem.kt b/app/src/test/kotlin/com/wire/android/framework/TestConversationItem.kt index d5561428db9..3ad1bcb7955 100644 --- a/app/src/test/kotlin/com/wire/android/framework/TestConversationItem.kt +++ b/app/src/test/kotlin/com/wire/android/framework/TestConversationItem.kt @@ -44,7 +44,8 @@ object TestConversationItem { userId = UserId("value", "domain"), isArchived = false, mlsVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED, - proteusVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED + proteusVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED, + isFavorite = false ) val GROUP = ConversationItem.GroupConversation( @@ -59,7 +60,8 @@ object TestConversationItem { teamId = null, isArchived = false, mlsVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED, - proteusVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED + proteusVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED, + isFavorite = false ) val CONNECTION = ConversationItem.ConnectionConversation( diff --git a/app/src/test/kotlin/com/wire/android/ui/common/bottomsheet/conversation/ConversationSheetContentTest.kt b/app/src/test/kotlin/com/wire/android/ui/common/bottomsheet/conversation/ConversationSheetContentTest.kt index 09f6b2160ab..9b9d8d5b273 100644 --- a/app/src/test/kotlin/com/wire/android/ui/common/bottomsheet/conversation/ConversationSheetContentTest.kt +++ b/app/src/test/kotlin/com/wire/android/ui/common/bottomsheet/conversation/ConversationSheetContentTest.kt @@ -41,7 +41,8 @@ class ConversationSheetContentTest { protocol = Conversation.ProtocolInfo.Proteus, mlsVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED, proteusVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED, - isUnderLegalHold = false + isUnderLegalHold = false, + isFavorite = false ) val givenParticipantsCount = 1 @@ -63,7 +64,8 @@ class ConversationSheetContentTest { protocol = Conversation.ProtocolInfo.Proteus, mlsVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED, proteusVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED, - isUnderLegalHold = false + isUnderLegalHold = false, + isFavorite = false ) val givenParticipantsCount = 3 @@ -85,7 +87,8 @@ class ConversationSheetContentTest { protocol = Conversation.ProtocolInfo.Proteus, mlsVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED, proteusVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED, - isUnderLegalHold = false + isUnderLegalHold = false, + isFavorite = false ) val givenParticipantsCount = 3 diff --git a/app/src/test/kotlin/com/wire/android/ui/common/bottomsheet/folder/ChangeConversationFavoriteVMTest.kt b/app/src/test/kotlin/com/wire/android/ui/common/bottomsheet/folder/ChangeConversationFavoriteVMTest.kt new file mode 100644 index 00000000000..330ef97d5c9 --- /dev/null +++ b/app/src/test/kotlin/com/wire/android/ui/common/bottomsheet/folder/ChangeConversationFavoriteVMTest.kt @@ -0,0 +1,151 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.wire.android.ui.common.bottomsheet.folder + +import app.cash.turbine.test +import com.wire.android.R +import com.wire.android.config.CoroutineTestExtension +import com.wire.android.framework.TestConversation +import com.wire.android.model.asSnackBarMessage +import com.wire.android.ui.home.conversationslist.model.GroupDialogState +import com.wire.android.util.ui.UIText +import com.wire.kalium.logic.CoreFailure +import com.wire.kalium.logic.feature.conversation.folder.AddConversationToFavoritesUseCase +import com.wire.kalium.logic.feature.conversation.folder.RemoveConversationFromFavoritesUseCase +import io.mockk.MockKAnnotations +import io.mockk.coEvery +import io.mockk.coVerify +import io.mockk.impl.annotations.MockK +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(CoroutineTestExtension::class) +class ChangeConversationFavoriteVMTest { + + @Test + fun `given conversation is added to favorites successfully, then infoMessage should emit success`() = runTest { + val (arrangement, viewModel) = Arrangement().arrange { + withAddToFavoritesResult(AddConversationToFavoritesUseCase.Result.Success) + } + + viewModel.infoMessage.test { + + viewModel.changeFavoriteState(dialogState, addToFavorite = true) + + assertEquals( + UIText.StringResource(R.string.success_adding_to_favorite, conversationName).asSnackBarMessage(), + awaitItem() + ) + coVerify(exactly = 1) { + arrangement.addConversationToFavorites(dialogState.conversationId) + } + } + } + + @Test + fun `given conversation fails to add to favorites, then infoMessage should emit error`() = runTest { + val (arrangement, viewModel) = Arrangement().arrange { + withAddToFavoritesResult(AddConversationToFavoritesUseCase.Result.Failure(CoreFailure.Unknown(null))) + } + viewModel.infoMessage.test { + viewModel.changeFavoriteState(dialogState, addToFavorite = true) + + assertEquals( + UIText.StringResource(R.string.error_adding_to_favorite, conversationName).asSnackBarMessage(), + awaitItem() + ) + coVerify(exactly = 1) { + arrangement.addConversationToFavorites(dialogState.conversationId) + } + } + } + + @Test + fun `given conversation is removed from favorites successfully, then infoMessage should emit success`() = runTest { + val (arrangement, viewModel) = Arrangement().arrange { + withRemoveFromFavoritesResult(RemoveConversationFromFavoritesUseCase.Result.Success) + } + viewModel.infoMessage.test { + viewModel.changeFavoriteState(dialogState, addToFavorite = false) + + assertEquals( + UIText.StringResource(R.string.success_removing_from_favorite, conversationName).asSnackBarMessage(), + awaitItem() + ) + coVerify(exactly = 1) { + arrangement.removeConversationFromFavorites(dialogState.conversationId) + } + } + } + + @Test + fun `given conversation fails to remove from favorites, then infoMessage should emit error`() = runTest { + val (arrangement, viewModel) = Arrangement().arrange { + withRemoveFromFavoritesResult(RemoveConversationFromFavoritesUseCase.Result.Failure(CoreFailure.Unknown(null))) + } + viewModel.infoMessage.test { + viewModel.changeFavoriteState(dialogState, addToFavorite = false) + + assertEquals( + UIText.StringResource(R.string.error_removing_from_favorite, conversationName).asSnackBarMessage(), + awaitItem() + ) + coVerify(exactly = 1) { + arrangement.removeConversationFromFavorites(dialogState.conversationId) + } + } + } + + companion object { + val dialogState = GroupDialogState(conversationId = TestConversation.ID, conversationName = "Test Conversation") + val conversationName = dialogState.conversationName + } + + private class Arrangement { + + @MockK + lateinit var addConversationToFavorites: AddConversationToFavoritesUseCase + + @MockK + lateinit var removeConversationFromFavorites: RemoveConversationFromFavoritesUseCase + + private lateinit var viewModel: ChangeConversationFavoriteVM + + init { + MockKAnnotations.init(this, relaxUnitFun = true) + } + + fun withAddToFavoritesResult(result: AddConversationToFavoritesUseCase.Result) = apply { + coEvery { addConversationToFavorites(any()) } returns result + } + + fun withRemoveFromFavoritesResult(result: RemoveConversationFromFavoritesUseCase.Result) = apply { + coEvery { removeConversationFromFavorites(any()) } returns result + } + + fun arrange(block: Arrangement.() -> Unit) = apply(block).let { + viewModel = ChangeConversationFavoriteVMImpl( + addConversationToFavorites, + removeConversationFromFavorites + ) + this to viewModel + } + } +} diff --git a/app/src/test/kotlin/com/wire/android/ui/home/conversations/details/GroupConversationDetailsViewModelTest.kt b/app/src/test/kotlin/com/wire/android/ui/home/conversations/details/GroupConversationDetailsViewModelTest.kt index 3ee92bb493e..6c7b203bdb3 100644 --- a/app/src/test/kotlin/com/wire/android/ui/home/conversations/details/GroupConversationDetailsViewModelTest.kt +++ b/app/src/test/kotlin/com/wire/android/ui/home/conversations/details/GroupConversationDetailsViewModelTest.kt @@ -452,6 +452,7 @@ class GroupConversationDetailsViewModelTest { mlsVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED, proteusVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED, isUnderLegalHold = true, + isFavorite = false ) // When - Then assertEquals(expected, viewModel.conversationSheetContent) diff --git a/kalium b/kalium index 08c0cffe74b..c7b96a2584d 160000 --- a/kalium +++ b/kalium @@ -1 +1 @@ -Subproject commit 08c0cffe74b9523c7464e3645eb79f2ca7d59d3f +Subproject commit c7b96a2584d6cba1619501b917a90b757785b7ac