From 2c2ab9810555f4cae15697ca7065ff9b083e3683 Mon Sep 17 00:00:00 2001 From: JRoberts <8711996+jr-simplex@users.noreply.github.com> Date: Thu, 19 May 2022 13:37:12 +0400 Subject: [PATCH] android: more long press interactions on ChatListNavLinkView (#667) --- .../java/chat/simplex/app/MainActivity.kt | 2 +- .../chat/simplex/app/views/chat/ChatView.kt | 2 +- .../simplex/app/views/chat/item/CIFileView.kt | 2 +- .../app/views/chatlist/ChatListNavLinkView.kt | 352 +++++++++++------- .../simplex/app/views/helpers/GetImageView.kt | 22 -- 5 files changed, 216 insertions(+), 164 deletions(-) diff --git a/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt b/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt index 3a5aed1779..11ef98ff29 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt @@ -113,7 +113,7 @@ fun processIntent(intent: Intent?, chatModel: ChatModel) { if (chatId != null) { val cInfo = chatModel.getChat(chatId)?.chatInfo chatModel.clearOverlays.value = true - if (cInfo != null) withApi { openChat(chatModel, cInfo) } + if (cInfo != null) withApi { openChat(cInfo, chatModel) } } } NtfManager.ShowChatsAction -> { diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatView.kt index 93b4ace721..dbaec141e9 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatView.kt @@ -87,7 +87,7 @@ fun ChatView(chatModel: ChatModel) { val c = chatModel.chats.firstOrNull { it.chatInfo is ChatInfo.Direct && it.chatInfo.contact.contactId == contactId } - if (c != null) withApi { openChat(chatModel, c.chatInfo) } + if (c != null) withApi { openChat(c.chatInfo, chatModel) } }, deleteMessage = { itemId, mode -> withApi { diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIFileView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIFileView.kt index 718d48f1f4..fdf25baba9 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIFileView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIFileView.kt @@ -203,7 +203,7 @@ class ChatItemProvider: PreviewParameterProvider { @Preview @Composable -fun PreviewTextItemViewSnd(@PreviewParameter(ChatItemProvider::class) chatItem: ChatItem) { +fun PreviewCIFileFramedItemView(@PreviewParameter(ChatItemProvider::class) chatItem: ChatItem) { val showMenu = remember { mutableStateOf(false) } SimpleXTheme { FramedItemView(User.sampleData, chatItem, showMenu = showMenu, receiveFile = {}) diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListNavLinkView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListNavLinkView.kt index 72724bf71b..2c19940e56 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListNavLinkView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListNavLinkView.kt @@ -5,14 +5,13 @@ import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.* import androidx.compose.material.* import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.Delete -import androidx.compose.material.icons.outlined.Restore +import androidx.compose.material.icons.outlined.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.* import androidx.compose.ui.unit.dp import chat.simplex.app.R import chat.simplex.app.model.* @@ -23,89 +22,184 @@ import kotlinx.datetime.Clock @Composable fun ChatListNavLinkView(chat: Chat, chatModel: ChatModel) { - ChatListNavLinkLayout( - chat = chat, - click = { - when (chat.chatInfo) { - is ChatInfo.ContactRequest -> contactRequestAlertDialog(chat.chatInfo, chatModel) - is ChatInfo.ContactConnection -> contactConnectionAlertDialog(chat.chatInfo.contactConnection, chatModel) - else -> - if (chat.chatInfo.ready) { - withApi { openChat(chatModel, chat.chatInfo) } - } else { - pendingContactAlertDialog(chat.chatInfo, chatModel) - } - } - }, - deleteContact = { - AlertManager.shared.showAlertMsg( - title = generalGetString(R.string.delete_contact__question), - text = generalGetString(R.string.delete_contact_all_messages_deleted_cannot_undo_warning), - confirmText = generalGetString(R.string.delete_verb), - onConfirm = { - val cInfo = chat.chatInfo - withApi { - val r = chatModel.controller.apiDeleteChat(cInfo.chatType, cInfo.apiId) - if (r) { - chatModel.removeChat(cInfo.id) - chatModel.chatId.value = null - } - } - } + val showMenu = remember { mutableStateOf(false) } + when (chat.chatInfo) { + is ChatInfo.Direct -> + ChatListNavLinkLayout( + chatLinkPreview = { ChatPreviewView(chat) }, + click = { openOrPendingChat(chat.chatInfo, chatModel) }, + dropdownMenuItems = { ContactMenuItems(chat.chatInfo, chatModel, showMenu) }, + showMenu ) - }, - clearChat = { - AlertManager.shared.showAlertMsg( - title = generalGetString(R.string.clear_chat_question), - text = generalGetString(R.string.clear_chat_warning), - confirmText = generalGetString(R.string.clear_verb), - onConfirm = { - val cInfo = chat.chatInfo - withApi { - val r = chatModel.controller.apiClearChat(cInfo.chatType, cInfo.apiId) - if (r) { - chatModel.clearChat(cInfo) - } - } - } + is ChatInfo.Group -> + ChatListNavLinkLayout( + chatLinkPreview = { ChatPreviewView(chat) }, + click = { openOrPendingChat(chat.chatInfo, chatModel) }, + dropdownMenuItems = { GroupMenuItems(chat.chatInfo, chatModel, showMenu) }, + showMenu ) - }, - ) + is ChatInfo.ContactRequest -> + ChatListNavLinkLayout( + chatLinkPreview = { ContactRequestView(chat.chatInfo) }, + click = { contactRequestAlertDialog(chat.chatInfo, chatModel) }, + dropdownMenuItems = { ContactRequestMenuItems(chat.chatInfo, chatModel, showMenu) }, + showMenu + ) + is ChatInfo.ContactConnection -> + ChatListNavLinkLayout( + chatLinkPreview = { ContactConnectionView(chat.chatInfo.contactConnection) }, + click = { contactConnectionAlertDialog(chat.chatInfo.contactConnection, chatModel) }, + dropdownMenuItems = { ContactConnectionMenuItems(chat.chatInfo, chatModel, showMenu) }, + showMenu + ) + } } -suspend fun openChat(chatModel: ChatModel, cInfo: ChatInfo) { - val chat = chatModel.controller.apiGetChat(cInfo.chatType, cInfo.apiId) +fun openOrPendingChat(chatInfo: ChatInfo, chatModel: ChatModel) { + if (chatInfo.ready) { + withApi { openChat(chatInfo, chatModel) } + } else { + pendingContactAlertDialog(chatInfo, chatModel) + } +} + +suspend fun openChat(chatInfo: ChatInfo, chatModel: ChatModel) { + val chat = chatModel.controller.apiGetChat(chatInfo.chatType, chatInfo.apiId) if (chat != null) { chatModel.chatItems.clear() chatModel.chatItems.addAll(chat.chatItems) - chatModel.chatId.value = cInfo.id + chatModel.chatId.value = chatInfo.id } } +@Composable +fun ContactMenuItems(chatInfo: ChatInfo.Direct, chatModel: ChatModel, showMenu: MutableState) { + ItemAction( + stringResource(R.string.clear_verb), + Icons.Outlined.Restore, + onClick = { + clearChatDialog(chatInfo, chatModel) + showMenu.value = false + } + ) + ItemAction( + stringResource(R.string.delete_verb), + Icons.Outlined.Delete, + onClick = { + deleteContactDialog(chatInfo, chatModel) + showMenu.value = false + }, + color = Color.Red + ) +} + +@Composable +fun GroupMenuItems(chatInfo: ChatInfo.Group, chatModel: ChatModel, showMenu: MutableState) { + ItemAction( + stringResource(R.string.clear_verb), + Icons.Outlined.Restore, + onClick = { + clearChatDialog(chatInfo, chatModel) + showMenu.value = false + } + ) +} + +@Composable +fun ContactRequestMenuItems(chatInfo: ChatInfo.ContactRequest, chatModel: ChatModel, showMenu: MutableState) { + ItemAction( + stringResource(R.string.accept_contact_button), + Icons.Outlined.Check, + onClick = { + acceptContactRequest(chatInfo, chatModel) + showMenu.value = false + } + ) + ItemAction( + stringResource(R.string.reject_contact_button), + Icons.Outlined.Close, + onClick = { + rejectContactRequest(chatInfo, chatModel) + showMenu.value = false + }, + color = Color.Red + ) +} + +@Composable +fun ContactConnectionMenuItems(chatInfo: ChatInfo.ContactConnection, chatModel: ChatModel, showMenu: MutableState) { + ItemAction( + stringResource(R.string.delete_verb), + Icons.Outlined.Delete, + onClick = { + deleteContactConnectionAlert(chatInfo.contactConnection, chatModel) + showMenu.value = false + }, + color = Color.Red + ) +} + +fun deleteContactDialog(contact: ChatInfo.Direct, chatModel: ChatModel) { + AlertManager.shared.showAlertMsg( + title = generalGetString(R.string.delete_contact__question), + text = generalGetString(R.string.delete_contact_all_messages_deleted_cannot_undo_warning), + confirmText = generalGetString(R.string.delete_verb), + onConfirm = { + withApi { + val r = chatModel.controller.apiDeleteChat(contact.chatType, contact.apiId) + if (r) { + chatModel.removeChat(contact.id) + chatModel.chatId.value = null + } + } + } + ) +} + +fun clearChatDialog(chatInfo: ChatInfo, chatModel: ChatModel) { + AlertManager.shared.showAlertMsg( + title = generalGetString(R.string.clear_chat_question), + text = generalGetString(R.string.clear_chat_warning), + confirmText = generalGetString(R.string.clear_verb), + onConfirm = { + withApi { + val r = chatModel.controller.apiClearChat(chatInfo.chatType, chatInfo.apiId) + if (r) { + chatModel.clearChat(chatInfo) + } + } + } + ) +} + fun contactRequestAlertDialog(contactRequest: ChatInfo.ContactRequest, chatModel: ChatModel) { AlertManager.shared.showAlertDialog( title = generalGetString(R.string.accept_connection_request__question), text = generalGetString(R.string.if_you_choose_to_reject_the_sender_will_not_be_notified), confirmText = generalGetString(R.string.accept_contact_button), - onConfirm = { - withApi { - val contact = chatModel.controller.apiAcceptContactRequest(contactRequest.apiId) - if (contact != null) { - val chat = Chat(ChatInfo.Direct(contact), listOf()) - chatModel.replaceChat(contactRequest.id, chat) - } - } - }, + onConfirm = { acceptContactRequest(contactRequest, chatModel) }, dismissText = generalGetString(R.string.reject_contact_button), - onDismiss = { - withApi { - chatModel.controller.apiRejectContactRequest(contactRequest.apiId) - chatModel.removeChat(contactRequest.id) - } - } + onDismiss = { rejectContactRequest(contactRequest, chatModel) } ) } +fun acceptContactRequest(contactRequest: ChatInfo.ContactRequest, chatModel: ChatModel) { + withApi { + val contact = chatModel.controller.apiAcceptContactRequest(contactRequest.apiId) + if (contact != null) { + val chat = Chat(ChatInfo.Direct(contact), listOf()) + chatModel.replaceChat(contactRequest.id, chat) + } + } +} + +fun rejectContactRequest(contactRequest: ChatInfo.ContactRequest, chatModel: ChatModel) { + withApi { + chatModel.controller.apiRejectContactRequest(contactRequest.apiId) + chatModel.removeChat(contactRequest.id) + } +} + fun contactConnectionAlertDialog(connection: PendingContactConnection, chatModel: ChatModel) { AlertManager.shared.showAlertDialogButtons( title = generalGetString( @@ -177,21 +271,17 @@ fun pendingContactAlertDialog(chatInfo: ChatInfo, chatModel: ChatModel) { @Composable fun ChatListNavLinkLayout( - chat: Chat, + chatLinkPreview: @Composable () -> Unit, click: () -> Unit, - deleteContact: () -> Unit, - clearChat: () -> Unit + dropdownMenuItems: (@Composable () -> Unit)?, + showMenu: MutableState ) { - val showMenu = remember { mutableStateOf(false) } Surface( modifier = Modifier .fillMaxWidth() .combinedClickable( onClick = click, - onLongClick = { - if (chat.chatInfo is ChatInfo.Direct || chat.chatInfo is ChatInfo.Group) - showMenu.value = true - } + onLongClick = { showMenu.value = true } ) .height(88.dp) ) { @@ -203,38 +293,16 @@ fun ChatListNavLinkLayout( .padding(end = 12.dp), verticalAlignment = Alignment.Top ) { - when (chat.chatInfo) { - is ChatInfo.Direct -> ChatPreviewView(chat) - is ChatInfo.Group -> ChatPreviewView(chat) - is ChatInfo.ContactRequest -> ContactRequestView(chat.chatInfo) - is ChatInfo.ContactConnection -> ContactConnectionView(chat.chatInfo.contactConnection) - } + chatLinkPreview() } - Box(Modifier.padding(horizontal = 16.dp)) { - DropdownMenu( - expanded = showMenu.value, - onDismissRequest = { showMenu.value = false }, - Modifier.width(220.dp) - ) { - if (chat.chatInfo is ChatInfo.Direct || chat.chatInfo is ChatInfo.Group) - ItemAction( - stringResource(R.string.clear_verb), - Icons.Outlined.Restore, - onClick = { - clearChat() - showMenu.value = false - } - ) - if (chat.chatInfo is ChatInfo.Direct) { - ItemAction( - stringResource(R.string.delete_verb), - Icons.Outlined.Delete, - onClick = { - deleteContact() - showMenu.value = false - }, - color = Color.Red - ) + if (dropdownMenuItems != null) { + Box(Modifier.padding(horizontal = 16.dp)) { + DropdownMenu( + expanded = showMenu.value, + onDismissRequest = { showMenu.value = false }, + Modifier.width(220.dp) + ) { + dropdownMenuItems() } } } @@ -252,21 +320,25 @@ fun ChatListNavLinkLayout( fun PreviewChatListNavLinkDirect() { SimpleXTheme { ChatListNavLinkLayout( - chat = Chat( - chatInfo = ChatInfo.Direct.sampleData, - chatItems = listOf( - ChatItem.getSampleData( - 1, - CIDirection.DirectSnd(), - Clock.System.now(), - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." + chatLinkPreview = { + ChatPreviewView( + chat = Chat( + chatInfo = ChatInfo.Direct.sampleData, + chatItems = listOf( + ChatItem.getSampleData( + 1, + CIDirection.DirectSnd(), + Clock.System.now(), + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." + ) + ), + chatStats = Chat.ChatStats() ) - ), - chatStats = Chat.ChatStats() - ), + ) + }, click = {}, - deleteContact = {}, - clearChat = {} + dropdownMenuItems = null, + showMenu = remember { mutableStateOf(false) } ) } } @@ -281,21 +353,25 @@ fun PreviewChatListNavLinkDirect() { fun PreviewChatListNavLinkGroup() { SimpleXTheme { ChatListNavLinkLayout( - chat = Chat( - chatInfo = ChatInfo.Group.sampleData, - chatItems = listOf( - ChatItem.getSampleData( - 1, - CIDirection.DirectSnd(), - Clock.System.now(), - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." + chatLinkPreview = { + ChatPreviewView( + Chat( + chatInfo = ChatInfo.Group.sampleData, + chatItems = listOf( + ChatItem.getSampleData( + 1, + CIDirection.DirectSnd(), + Clock.System.now(), + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." + ) + ), + chatStats = Chat.ChatStats() ) - ), - chatStats = Chat.ChatStats() - ), + ) + }, click = {}, - deleteContact = {}, - clearChat = {} + dropdownMenuItems = null, + showMenu = remember { mutableStateOf(false) } ) } } @@ -310,14 +386,12 @@ fun PreviewChatListNavLinkGroup() { fun PreviewChatListNavLinkContactRequest() { SimpleXTheme { ChatListNavLinkLayout( - chat = Chat( - chatInfo = ChatInfo.ContactRequest.sampleData, - chatItems = listOf(), - chatStats = Chat.ChatStats() - ), + chatLinkPreview = { + ContactRequestView(ChatInfo.ContactRequest.sampleData) + }, click = {}, - deleteContact = {}, - clearChat = {} + dropdownMenuItems = null, + showMenu = remember { mutableStateOf(false) } ) } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/GetImageView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/GetImageView.kt index 58079f4d3b..45b52ce276 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/GetImageView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/GetImageView.kt @@ -152,8 +152,6 @@ fun rememberGetContentLauncher(cb: (Uri?) -> Unit): ManagedActivityResultLaunche fun GetImageBottomSheet( imageBitmap: MutableState, onImageChange: (Bitmap) -> Unit, - fileUri: MutableState? = null, - onFileChange: ((Uri) -> Unit)? = null, hideBottomSheet: () -> Unit ) { val context = LocalContext.current @@ -179,20 +177,6 @@ fun GetImageBottomSheet( Toast.makeText(context, generalGetString(R.string.toast_permission_denied), Toast.LENGTH_SHORT).show() } } - val filesLauncher = rememberGetContentLauncher { uri: Uri? -> - if (uri != null && fileUri != null && onFileChange != null) { - val fileSize = getFileSize(context, uri) - if (fileSize != null && fileSize <= MAX_FILE_SIZE) { - fileUri.value = uri - onFileChange(uri) - } else { - AlertManager.shared.showAlertMsg( - generalGetString(R.string.large_file), - String.format(generalGetString(R.string.maximum_supported_file_size), formatBytes(MAX_FILE_SIZE)) - ) - } - } - } Box( modifier = Modifier @@ -223,12 +207,6 @@ fun GetImageBottomSheet( galleryLauncher.launch("image/*") hideBottomSheet() } - if (fileUri != null && onFileChange != null) { - ActionButton(null, stringResource(R.string.choose_file), icon = Icons.Outlined.InsertDriveFile) { - filesLauncher.launch("*/*") - hideBottomSheet() - } - } } } }