From cea3aad0d430903bdf2c00be51afc5e757dbda98 Mon Sep 17 00:00:00 2001 From: Diogo Cunha Date: Tue, 16 Jul 2024 23:33:14 +0100 Subject: [PATCH] android, desktop: deleted contacts and requests --- .../views/contacts/ContactListNavView.kt | 29 ++++++ .../views/contacts/ContactPreviewView.kt | 36 ++++++-- .../common/views/contacts/ContactsView.kt | 90 ++++++++++++++----- .../common/views/newchat/NewChatSheet.kt | 3 +- .../commonMain/resources/MR/base/strings.xml | 1 + 5 files changed, 129 insertions(+), 30 deletions(-) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/contacts/ContactListNavView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/contacts/ContactListNavView.kt index b03a679ce8..1b69047d2e 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/contacts/ContactListNavView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/contacts/ContactListNavView.kt @@ -76,6 +76,27 @@ fun ContactListNavLinkView(chat: Chat, nextChatSelected: State, oneHand oneHandUI ) } + is ChatInfo.ContactConnection -> { + ChatListNavLinkLayout( + chatLinkPreview = { + tryOrShowError("${chat.id}ContactListNavLink", error = { ErrorChatListItem() }) { + ContactPreviewView(chat, disabled) + } + }, + click = { + contactRequestAlertDialog( + rhId = chat.remoteHostId, + connection = chat.chatInfo.contactConnection + ) + }, + dropdownMenuItems = {}, + showMenu, + disabled, + selectedChat, + nextChatSelected, + oneHandUI + ) + } else -> {} } } @@ -111,4 +132,12 @@ fun DeleteContactAction(chat: Chat, chatModel: ChatModel, showMenu: MutableState }, color = Color.Red ) +} + +fun contactRequestAlertDialog(rhId: Long?, connection: PendingContactConnection) { + AlertManager.shared.showAlertMsg( + title = "Connection not yet accepted", + text = "${connection.displayName} didn't accept your connection request yet.", + hostDevice = hostDevice(rhId), + ) } \ No newline at end of file diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/contacts/ContactPreviewView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/contacts/ContactPreviewView.kt index f2ad077063..bd48c0d9e5 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/contacts/ContactPreviewView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/contacts/ContactPreviewView.kt @@ -9,6 +9,7 @@ import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.painter.Painter import dev.icerock.moko.resources.compose.painterResource import dev.icerock.moko.resources.compose.stringResource import androidx.compose.ui.text.style.TextOverflow @@ -27,15 +28,31 @@ fun ContactPreviewView( val cInfo = chat.chatInfo @Composable - fun inactiveIcon() { + fun OverlayIcon(painter: Painter, description: String) { Icon( - painterResource(MR.images.ic_cancel_filled), - stringResource(MR.strings.icon_descr_group_inactive), + painter, + description, Modifier.size(18.dp).background(MaterialTheme.colors.background, CircleShape), tint = MaterialTheme.colors.secondary ) } + @Composable + fun pendingIcon() { + OverlayIcon( + painterResource(MR.images.ic_timer), + stringResource(MR.strings.icon_descr_pending_contact_connection) + ) + } + + @Composable + fun inactiveIcon() { + OverlayIcon( + painterResource(MR.images.ic_cancel_filled), + stringResource(MR.strings.icon_descr_group_inactive) + ) + } + @Composable fun chatPreviewImageOverlayIcon() { when (cInfo) { @@ -43,7 +60,7 @@ fun ContactPreviewView( if (!cInfo.contact.active) { inactiveIcon() } - + is ChatInfo.ContactConnection -> pendingIcon() else -> {} } } @@ -78,6 +95,15 @@ fun ContactPreviewView( color = Color.Unspecified ) } + is ChatInfo.ContactConnection -> + Row(verticalAlignment = Alignment.CenterVertically) { + Text( + cInfo.chatViewName, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + color = MaterialTheme.colors.secondary + ) + } else -> {} } } @@ -100,7 +126,7 @@ fun ContactPreviewView( Spacer(Modifier.fillMaxWidth().weight(1f)) - if (chat.chatInfo is ChatInfo.ContactRequest) { + if (chat.chatInfo is ChatInfo.ContactRequest || chat.chatInfo is ChatInfo.ContactConnection) { Text( text = generalGetString(MR.strings.contact_type_new).uppercase(), color = MaterialTheme.colors.onPrimary, diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/contacts/ContactsView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/contacts/ContactsView.kt index caea80df44..88561cc238 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/contacts/ContactsView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/contacts/ContactsView.kt @@ -51,6 +51,7 @@ import chat.simplex.common.model.ChatModel import chat.simplex.common.model.ChatType import chat.simplex.common.model.ContactStatus import chat.simplex.common.model.Format +import chat.simplex.common.model.RemoteHostInfo import chat.simplex.common.model.SharedPreference import chat.simplex.common.platform.BackHandler import chat.simplex.common.platform.ColumnWithScrollBar @@ -65,6 +66,10 @@ import chat.simplex.common.platform.hideKeyboard import chat.simplex.common.ui.theme.* import chat.simplex.common.views.chatlist.DetailedSMPStatsView import chat.simplex.common.views.helpers.* +import chat.simplex.common.views.newchat.AddGroupView +import chat.simplex.common.views.newchat.NewChatOption +import chat.simplex.common.views.newchat.NewChatOptions +import chat.simplex.common.views.newchat.NewChatView import chat.simplex.common.views.newchat.strHasSingleSimplexLink import chat.simplex.res.MR import dev.icerock.moko.resources.compose.painterResource @@ -73,9 +78,8 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.launch import java.util.HashSet - enum class ContactType { - CARD, REQUEST, RECENT, REMOVED, UNKNOWN + CARD, REQUEST_RECEIVED, REQUEST_SENT, RECENT, REMOVED, UNKNOWN } private fun contactChats(c: List, contactTypes: List): List { @@ -84,16 +88,19 @@ private fun contactChats(c: List, contactTypes: List): List ContactType.REQUEST + is ChatInfo.ContactRequest -> ContactType.REQUEST_RECEIVED is ChatInfo.Direct -> { val contact = cInfo.contact; when { contact.activeConn == null && contact.profile.contactLink != null -> ContactType.CARD contact.contactStatus == ContactStatus.DeletedByUser || contact.contactStatus == ContactStatus.Deleted -> ContactType.REMOVED + contact.chatDeleted -> ContactType.REMOVED else -> ContactType.RECENT } } + // TODO: Redo this. + is ChatInfo.ContactConnection -> ContactType.REQUEST_SENT else -> ContactType.UNKNOWN } } @@ -111,7 +118,32 @@ val chatsByTypeComparator = Comparator { chat1, chat2 -> } @Composable -fun ContactActionsSection(contactActions: @Composable () -> Unit) { +fun ModalData.DeletedContactsView(rh: RemoteHostInfo?, close: () -> Unit) { + ModalView( + close = close + ) { + Column( + Modifier.fillMaxSize(), + ) { + Box(contentAlignment = Alignment.Center) { + val bottomPadding = DEFAULT_PADDING + AppBarTitle( + stringResource(MR.strings.contact_type_deleted), + hostDevice(rh?.remoteHostId), + bottomPadding = bottomPadding + ) + } + + ContactsLayout( + contactActions = {}, + contactTypes = listOf(ContactType.REMOVED), + ) + } + } +} + +@Composable +private fun ContactActionsSection(contactActions: @Composable () -> Unit, rh: RemoteHostInfo?) { contactActions() Spacer(Modifier.height(DEFAULT_PADDING)) @@ -121,7 +153,10 @@ fun ContactActionsSection(contactActions: @Composable () -> Unit) { SectionView { SectionItemView( click = { - // + ModalManager.start.showCustomModal { close -> DeletedContactsView( + rh = rh, + close = close) + } } ) { Icon( @@ -137,7 +172,10 @@ fun ContactActionsSection(contactActions: @Composable () -> Unit) { } @Composable -fun ContactsView(contactActions: @Composable () -> Unit) { +private fun ContactsLayout( + contactActions: @Composable () -> Unit, + contactTypes: List, + contactListTitle: String? = null) { val listState = rememberLazyListState(lazyListState.first, lazyListState.second) var searchFocused by remember { mutableStateOf(false) } val searchText = rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf( @@ -158,20 +196,28 @@ fun ContactsView(contactActions: @Composable () -> Unit) { } if (!searchFocused) { - ContactActionsSection(contactActions) + contactActions() } Spacer(Modifier.height(DEFAULT_PADDING)) - SectionView(title = stringResource(MR.strings.contact_list_header_title).uppercase(), padding = PaddingValues(DEFAULT_PADDING)) { - ContactsList(listState = listState, chatModel = chatModel, searchText = searchText, contactType = ContactType.RECENT) - } - - if (searchFocused) { - ContactActionsSection(contactActions) + SectionView(title = contactListTitle, padding = PaddingValues(DEFAULT_PADDING)) { + ContactsList(listState = listState, chatModel = chatModel, searchText = searchText, contactTypes = contactTypes) + // TODO: Empty list states. } } +@Composable +fun ContactsView( + contactActions: @Composable () -> Unit, + rh: RemoteHostInfo? +) { + ContactsLayout( + contactActions = { ContactActionsSection(contactActions, rh) }, + contactTypes = listOf(ContactType.CARD, ContactType.RECENT, ContactType.REQUEST_RECEIVED, ContactType.REQUEST_SENT), + contactListTitle = stringResource(MR.strings.contact_list_header_title).uppercase() + ) +} @Composable fun ContactsSearchBar(listState: LazyListState, searchText: MutableState, focused: Boolean, onFocusChanged: (hasFocus: Boolean) -> Unit) { @@ -245,9 +291,8 @@ fun ToggleFilterButton() { private var lazyListState = 0 to 0 - @Composable -fun ContactsList(listState: LazyListState, chatModel: ChatModel, searchText: MutableState, contactType: ContactType) { +fun ContactsList(listState: LazyListState, chatModel: ChatModel, searchText: MutableState, contactTypes: List) { val oneHandUI = remember { chatModel.controller.appPrefs.oneHandUI } DisposableEffect(Unit) { onDispose { @@ -257,7 +302,10 @@ fun ContactsList(listState: LazyListState, chatModel: ChatModel, searchText: Mut } val showUnreadAndFavorites = remember { ChatController.appPrefs.showUnreadAndFavorites.state }.value - val allChats = remember { contactChats(chatModel.chats, listOf(ContactType.CARD, ContactType.RECENT, ContactType.REQUEST)) } + val allChats = remember { contactChats( + chatModel.chats, + contactTypes) + } val filteredContactChats = filteredContactChats( showUnreadAndFavorites = showUnreadAndFavorites, @@ -275,7 +323,7 @@ fun ContactsList(listState: LazyListState, chatModel: ChatModel, searchText: Mut } } } else { - LazyColumnWithScrollBar( + LazyColumn( Modifier.fillMaxWidth(), listState ) { @@ -314,18 +362,12 @@ private fun filteredContactChats( searchText: String, contactChats: List ): List { + // TODO: Refresh list after delete. return contactChats .filter { chat -> filterChat( chat = chat, searchText = searchText, showUnreadAndFavorites = showUnreadAndFavorites) } - .distinctBy { chat -> - when (val cInfo = chat.chatInfo) { - is ChatInfo.ContactRequest -> cInfo.contactRequest.contactRequestId - is ChatInfo.Direct -> cInfo.contact.contactId - else -> cInfo.id - } - } .sortedWith(chatsByTypeComparator) } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt index 98419a2b18..e94721d07f 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt @@ -70,7 +70,8 @@ fun ModalData.NewChatView(rh: RemoteHostInfo?) { ModalManager.center.showCustomModal { close -> AddGroupView(chatModel, chatModel.currentRemoteHost.value, close) } } ) - } + }, + rh = rh ) } } diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml index 68b62ade0d..ec1be7b5f8 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml @@ -2221,4 +2221,5 @@ Recent No filtered contacts Your contacts + Pending contact connection \ No newline at end of file