Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Delete group conversation locally [#WPB-11556] #3774

Merged
merged 7 commits into from
Dec 31, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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 @@ -31,7 +31,6 @@ import com.wire.android.ui.calling.model.UICallParticipant
import com.wire.android.ui.calling.ongoing.fullscreen.SelectedParticipant
import com.wire.kalium.logic.data.call.Call
import com.wire.kalium.logic.data.call.CallClient
import com.wire.kalium.logic.data.call.CallQuality
import com.wire.kalium.logic.data.call.VideoState
import com.wire.kalium.logic.data.id.ConversationId
import com.wire.kalium.logic.data.user.UserId
Expand Down Expand Up @@ -130,8 +129,7 @@ class OngoingCallViewModel @AssistedInject constructor(
val clients: List<CallClient> = it.map { uiParticipant ->
CallClient(
userId = uiParticipant.id.toString(),
clientId = uiParticipant.clientId,
quality = mapQualityStream(uiParticipant)
clientId = uiParticipant.clientId
)
}
requestVideoStreams(conversationId, clients)
Expand All @@ -140,14 +138,6 @@ class OngoingCallViewModel @AssistedInject constructor(
}
}

private fun mapQualityStream(uiParticipant: UICallParticipant): CallQuality {
m-zagorski marked this conversation as resolved.
Show resolved Hide resolved
return if (uiParticipant.clientId == selectedParticipant.clientId) {
CallQuality.HIGH
} else {
CallQuality.LOW
}
}

private fun startDoubleTapToastDisplayCountDown() {
doubleTapIndicatorCountDownTimer?.cancel()
doubleTapIndicatorCountDownTimer =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ fun ConversationSheetContent(
unblockUser: (UnblockUserDialogState) -> Unit,
leaveGroup: (GroupDialogState) -> Unit,
deleteGroup: (GroupDialogState) -> Unit,
deleteGroupLocally: (GroupDialogState) -> Unit,
isBottomSheetVisible: () -> Boolean = { true }
) {
// it may be null as initial state
Expand All @@ -63,6 +64,7 @@ fun ConversationSheetContent(
unblockUserClick = unblockUser,
leaveGroup = leaveGroup,
deleteGroup = deleteGroup,
deleteGroupLocally = deleteGroupLocally,
navigateToNotification = conversationSheetState::toMutingNotificationOption
)
}
Expand Down Expand Up @@ -125,7 +127,8 @@ data class ConversationSheetContent(
val mlsVerificationStatus: Conversation.VerificationStatus,
val proteusVerificationStatus: Conversation.VerificationStatus,
val isUnderLegalHold: Boolean,
val isFavorite: Boolean?
val isFavorite: Boolean?,
val isDeletingConversationLocallyRunning: Boolean
) {

private val isSelfUserMember: Boolean get() = selfRole != null
Expand All @@ -144,6 +147,8 @@ data class ConversationSheetContent(

fun canLeaveTheGroup(): Boolean = conversationTypeDetail is ConversationTypeDetail.Group && isSelfUserMember

fun canDeleteGroupLocally(): Boolean = !isSelfUserMember && !isDeletingConversationLocallyRunning

fun canBlockUser(): Boolean {
return conversationTypeDetail is ConversationTypeDetail.Private
&& conversationTypeDetail.blockingState == BlockingState.NOT_BLOCKED
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ class ConversationSheetState(
@Composable
fun rememberConversationSheetState(
conversationItem: ConversationItem,
conversationOptionNavigation: ConversationOptionNavigation
conversationOptionNavigation: ConversationOptionNavigation,
isConversationDeletionLocallyRunning: Boolean
): ConversationSheetState {
val conversationSheetContent: ConversationSheetContent = when (conversationItem) {
is ConversationItem.GroupConversation -> {
Expand All @@ -79,7 +80,8 @@ fun rememberConversationSheetState(
mlsVerificationStatus = Conversation.VerificationStatus.VERIFIED,
proteusVerificationStatus = Conversation.VerificationStatus.VERIFIED,
isUnderLegalHold = showLegalHoldIndicator,
isFavorite = isFavorite
isFavorite = isFavorite,
isDeletingConversationLocallyRunning = isConversationDeletionLocallyRunning
)
}
}
Expand All @@ -105,7 +107,8 @@ fun rememberConversationSheetState(
mlsVerificationStatus = Conversation.VerificationStatus.VERIFIED,
proteusVerificationStatus = Conversation.VerificationStatus.VERIFIED,
isUnderLegalHold = showLegalHoldIndicator,
isFavorite = isFavorite
isFavorite = isFavorite,
isDeletingConversationLocallyRunning = false
)
}
}
Expand All @@ -126,13 +129,14 @@ fun rememberConversationSheetState(
mlsVerificationStatus = Conversation.VerificationStatus.VERIFIED,
proteusVerificationStatus = Conversation.VerificationStatus.VERIFIED,
isUnderLegalHold = showLegalHoldIndicator,
isFavorite = null
isFavorite = null,
isDeletingConversationLocallyRunning = false
)
}
}
}

return remember(conversationItem, conversationOptionNavigation) {
return remember(conversationItem, conversationOptionNavigation, isConversationDeletionLocallyRunning) {
ConversationSheetState(
conversationSheetContent = conversationSheetContent,
conversationOptionNavigation = conversationOptionNavigation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ internal fun ConversationMainSheetContent(
unblockUserClick: (UnblockUserDialogState) -> Unit,
leaveGroup: (GroupDialogState) -> Unit,
deleteGroup: (GroupDialogState) -> Unit,
deleteGroupLocally: (GroupDialogState) -> Unit,
navigateToNotification: () -> Unit
) {
WireMenuModalSheetContent(
Expand Down Expand Up @@ -270,6 +271,28 @@ internal fun ConversationMainSheetContent(
)
}
}
if (conversationSheetContent.canDeleteGroupLocally()) {
add {
MenuBottomSheetItem(
leading = {
MenuItemIcon(
id = R.drawable.ic_close,
contentDescription = null
)
},
title = stringResource(R.string.label_delete_group_locally),
itemProvidedColor = MaterialTheme.colorScheme.error,
onItemClick = {
deleteGroupLocally(
GroupDialogState(
conversationSheetContent.conversationId,
conversationSheetContent.title
)
)
}
)
}
}
if (conversationSheetContent.canDeleteGroup()) {
add {
MenuBottomSheetItem(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ sealed class HomeSnackBarMessage(override val uiText: UIText) : SnackBarMessage
groupName
)
)
data class DeleteConversationGroupLocallySuccess(val groupName: String) : HomeSnackBarMessage(
UIText.StringResource(
R.string.conversation_group_removed_locally_success,
groupName
)
)

data object DeleteConversationGroupError : HomeSnackBarMessage(UIText.StringResource(R.string.delete_group_conversation_error))
data object LeftConversationSuccess : HomeSnackBarMessage(UIText.StringResource(R.string.left_conversation_group_success))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,8 @@ private fun GroupConversationDetailsContent(
blockUser = {},
unblockUser = {},
leaveGroup = leaveGroupDialogState::show,
deleteGroup = deleteGroupDialogState::show
deleteGroup = deleteGroupDialogState::show,
deleteGroupLocally = {}
)
}
)
Expand Down Expand Up @@ -609,7 +610,8 @@ fun PreviewGroupConversationDetails() {
mlsVerificationStatus = Conversation.VerificationStatus.VERIFIED,
isUnderLegalHold = false,
proteusVerificationStatus = Conversation.VerificationStatus.VERIFIED,
isFavorite = false
isFavorite = false,
isDeletingConversationLocallyRunning = false
),
bottomSheetEventsHandler = GroupConversationDetailsBottomSheetEventsHandler.PREVIEW,
onBackPressed = {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,8 @@ class GroupConversationDetailsViewModel @Inject constructor(
mlsVerificationStatus = groupDetails.conversation.mlsVerificationStatus,
proteusVerificationStatus = groupDetails.conversation.proteusVerificationStatus,
isUnderLegalHold = groupDetails.conversation.legalHoldStatus.showLegalHoldIndicator(),
isFavorite = groupDetails.isFavorite
isFavorite = groupDetails.isFavorite,
isDeletingConversationLocallyRunning = false
)

updateState(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* 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.home.conversations.details.menu

import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import com.wire.android.R
import com.wire.android.ui.common.VisibilityState
import com.wire.android.ui.common.WireDialog
import com.wire.android.ui.common.WireDialogButtonProperties
import com.wire.android.ui.common.WireDialogButtonType
import com.wire.android.ui.common.button.WireButtonState
import com.wire.android.ui.common.visbility.VisibilityState
import com.wire.android.ui.home.conversationslist.model.GroupDialogState

@Composable
internal fun DeleteConversationGroupLocallyDialog(
dialogState: VisibilityState<GroupDialogState>,
isLoading: Boolean,
onDeleteGroupLocally: (GroupDialogState) -> Unit,
) {
VisibilityState(dialogState) {
WireDialog(
title = stringResource(id = R.string.delete_group_locally_conversation_dialog_title, it.conversationName),
text = stringResource(id = R.string.delete_group_locally_conversation_dialog_description),
buttonsHorizontalAlignment = true,
onDismiss = dialogState::dismiss,
dismissButtonProperties = WireDialogButtonProperties(
onClick = dialogState::dismiss,
text = stringResource(id = R.string.label_cancel),
state = WireButtonState.Default
),
optionButton1Properties = WireDialogButtonProperties(
onClick = { onDeleteGroupLocally(it) },
text = stringResource(id = R.string.delete_group_locally_delete_for_me_label),
type = WireDialogButtonType.Primary,
state = if (isLoading) {
WireButtonState.Disabled
} else {
WireButtonState.Error
},
loading = isLoading
)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import androidx.paging.PagingData
import androidx.paging.cachedIn
import androidx.paging.insertSeparators
import androidx.paging.map
import androidx.work.WorkManager
import com.wire.android.BuildConfig
import com.wire.android.appLogger
import com.wire.android.di.CurrentAccount
Expand All @@ -47,6 +48,9 @@ import com.wire.android.ui.home.conversationslist.model.ConversationsSource
import com.wire.android.ui.home.conversationslist.model.DialogState
import com.wire.android.ui.home.conversationslist.model.GroupDialogState
import com.wire.android.util.dispatchers.DispatcherProvider
import com.wire.android.workmanager.worker.ConversationDeletionLocallyStatus
import com.wire.android.workmanager.worker.enqueueConversationDeletionLocally
import com.wire.android.workmanager.worker.observeConversationDeletionStatusLocally
import com.wire.kalium.logic.data.conversation.Conversation
import com.wire.kalium.logic.data.conversation.ConversationFilter
import com.wire.kalium.logic.data.conversation.MutedConversationStatus
Expand Down Expand Up @@ -88,6 +92,7 @@ import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
Expand All @@ -109,6 +114,8 @@ interface ConversationListViewModel {
fun blockUser(blockUserState: BlockUserDialogState) {}
fun unblockUser(userId: UserId) {}
fun deleteGroup(groupDialogState: GroupDialogState) {}
fun deleteGroupLocally(groupDialogState: GroupDialogState) {}
fun observeIsDeletingConversationLocally(conversationId: ConversationId): Flow<Boolean>
fun leaveGroup(leaveGroupState: GroupDialogState) {}
fun clearConversationContent(dialogState: DialogState) {}
fun muteConversation(conversationId: ConversationId?, mutedConversationStatus: MutedConversationStatus) {}
Expand All @@ -120,6 +127,7 @@ class ConversationListViewModelPreview(
foldersWithConversations: Flow<PagingData<ConversationFolderItem>> = previewConversationFoldersFlow(),
) : ConversationListViewModel {
override val conversationListState = ConversationListState.Paginated(foldersWithConversations)
override fun observeIsDeletingConversationLocally(conversationId: ConversationId): Flow<Boolean> = flowOf(false)
}

@Suppress("MagicNumber", "TooManyFunctions", "LongParameterList")
Expand All @@ -142,7 +150,8 @@ class ConversationListViewModelImpl @AssistedInject constructor(
private val observeLegalHoldStateForSelfUser: ObserveLegalHoldStateForSelfUserUseCase,
@CurrentAccount val currentAccount: UserId,
private val userTypeMapper: UserTypeMapper,
private val observeSelfUser: GetSelfUserUseCase
private val observeSelfUser: GetSelfUserUseCase,
private val workManager: WorkManager
) : ConversationListViewModel, ViewModel() {

@AssistedFactory
Expand Down Expand Up @@ -367,6 +376,35 @@ class ConversationListViewModelImpl @AssistedInject constructor(
}
}

override fun deleteGroupLocally(groupDialogState: GroupDialogState) {
viewModelScope.launch {
closeBottomSheet.emit(Unit)
workManager.enqueueConversationDeletionLocally(groupDialogState.conversationId)
.collect { status ->
when (status) {
ConversationDeletionLocallyStatus.SUCCEEDED -> {
_infoMessage.emit(HomeSnackBarMessage.DeleteConversationGroupLocallySuccess(groupDialogState.conversationName))
}

ConversationDeletionLocallyStatus.FAILED -> {
_infoMessage.emit(HomeSnackBarMessage.DeleteConversationGroupError)
}

ConversationDeletionLocallyStatus.RUNNING,
ConversationDeletionLocallyStatus.IDLE -> {
// nop
}
}
}
}
}

override fun observeIsDeletingConversationLocally(conversationId: ConversationId): Flow<Boolean> {
return workManager.observeConversationDeletionStatusLocally(conversationId)
.map { status -> status == ConversationDeletionLocallyStatus.RUNNING }
.distinctUntilChanged()
}

// TODO: needs to be implemented
@Suppress("EmptyFunctionBlock")
override fun moveConversationToFolder() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import com.wire.android.ui.home.conversationslist.model.GroupDialogState
class ConversationsDialogsState(
val leaveGroupDialogState: VisibilityState<GroupDialogState>,
val deleteGroupDialogState: VisibilityState<GroupDialogState>,
val deleteGroupLocallyDialogState: VisibilityState<GroupDialogState>,
val blockUserDialogState: VisibilityState<BlockUserDialogState>,
val unblockUserDialogState: VisibilityState<UnblockUserDialogState>,
val clearContentDialogState: VisibilityState<DialogState>,
Expand All @@ -48,6 +49,7 @@ fun rememberConversationsDialogsState(requestInProgress: Boolean): Conversations

val leaveGroupDialogState = rememberVisibilityState<GroupDialogState>()
val deleteGroupDialogState = rememberVisibilityState<GroupDialogState>()
val deleteGroupLocallyDialogState = rememberVisibilityState<GroupDialogState>()
val blockUserDialogState = rememberVisibilityState<BlockUserDialogState>()
val unblockUserDialogState = rememberVisibilityState<UnblockUserDialogState>()
val clearContentDialogState = rememberVisibilityState<DialogState>()
Expand All @@ -57,6 +59,7 @@ fun rememberConversationsDialogsState(requestInProgress: Boolean): Conversations
ConversationsDialogsState(
leaveGroupDialogState,
deleteGroupDialogState,
deleteGroupLocallyDialogState,
blockUserDialogState,
unblockUserDialogState,
clearContentDialogState,
Expand Down
Loading
Loading