From 07824086824c40d067dcb2752e941fd012052330 Mon Sep 17 00:00:00 2001 From: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> Date: Thu, 8 Aug 2024 16:10:13 +0400 Subject: [PATCH] multiplatform: improve info views actions buttons design (#4622) * multiplatform: improve info views actions buttons design * spaces * rework * fix width --- .../simplex/common/views/chat/ChatInfoView.kt | 143 +++++++++++------- .../views/chat/group/GroupChatInfoView.kt | 56 ++++--- .../views/chat/group/GroupMemberInfoView.kt | 84 +++++----- 3 files changed, 175 insertions(+), 108 deletions(-) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatInfoView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatInfoView.kt index 7c694e00b1..5d9b33d416 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatInfoView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatInfoView.kt @@ -31,8 +31,7 @@ import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.intl.Locale import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp +import androidx.compose.ui.unit.* import chat.simplex.common.model.* import chat.simplex.common.model.ChatController.appPrefs import chat.simplex.common.model.ChatModel.controller @@ -545,20 +544,22 @@ fun ChatInfoLayout( SectionSpacer() - Row( - Modifier - .fillMaxWidth() - .padding(horizontal = DEFAULT_PADDING), - horizontalArrangement = Arrangement.Center, - verticalAlignment = Alignment.CenterVertically + Box( + Modifier.fillMaxWidth(), + contentAlignment = Alignment.Center ) { - SearchButton(chat, contact, close, onSearchClicked) - Spacer(Modifier.weight(1f)) - AudioCallButton(chat, contact) - Spacer(Modifier.weight(1f)) - VideoButton(chat, contact) - Spacer(Modifier.weight(1f)) - MuteButton(chat, contact) + Row( + Modifier + .widthIn(max = 460.dp) + .padding(horizontal = DEFAULT_PADDING), + horizontalArrangement = Arrangement.SpaceEvenly, + verticalAlignment = Alignment.CenterVertically + ) { + SearchButton(modifier = Modifier.fillMaxWidth(0.25f), chat, contact, close, onSearchClicked) + AudioCallButton(modifier = Modifier.fillMaxWidth(0.33f), chat, contact) + VideoButton(modifier = Modifier.fillMaxWidth(0.5f), chat, contact) + MuteButton(modifier = Modifier.fillMaxWidth(1f), chat, contact) + } } SectionSpacer() @@ -779,9 +780,16 @@ fun LocalAliasEditor( } @Composable -fun SearchButton(chat: Chat, contact: Contact, close: () -> Unit, onSearchClicked: () -> Unit) { +fun SearchButton( + modifier: Modifier, + chat: Chat, + contact: Contact, + close: () -> Unit, + onSearchClicked: () -> Unit +) { val disabled = !contact.ready || chat.chatItems.isEmpty() InfoViewActionButton( + modifier = modifier, icon = painterResource(MR.images.ic_search), title = generalGetString(MR.strings.info_view_search_button), disabled = disabled, @@ -796,11 +804,16 @@ fun SearchButton(chat: Chat, contact: Contact, close: () -> Unit, onSearchClicke } @Composable -fun MuteButton(chat: Chat, contact: Contact) { +fun MuteButton( + modifier: Modifier, + chat: Chat, + contact: Contact +) { val ntfsEnabled = remember { mutableStateOf(chat.chatInfo.ntfsEnabled) } val disabled = !contact.ready || !contact.active InfoViewActionButton( + modifier = modifier, icon = if (ntfsEnabled.value) painterResource(MR.images.ic_notifications_off) else painterResource(MR.images.ic_notifications), title = if (ntfsEnabled.value) stringResource(MR.strings.mute_chat) else stringResource(MR.strings.unmute_chat), disabled = disabled, @@ -812,8 +825,13 @@ fun MuteButton(chat: Chat, contact: Contact) { } @Composable -fun AudioCallButton(chat: Chat, contact: Contact) { +fun AudioCallButton( + modifier: Modifier, + chat: Chat, + contact: Contact +) { CallButton( + modifier = modifier, chat, contact, icon = painterResource(MR.images.ic_call), @@ -823,8 +841,13 @@ fun AudioCallButton(chat: Chat, contact: Contact) { } @Composable -fun VideoButton(chat: Chat, contact: Contact) { +fun VideoButton( + modifier: Modifier, + chat: Chat, + contact: Contact +) { CallButton( + modifier = modifier, chat, contact, icon = painterResource(MR.images.ic_videocam), @@ -834,7 +857,14 @@ fun VideoButton(chat: Chat, contact: Contact) { } @Composable -fun CallButton(chat: Chat, contact: Contact, icon: Painter, title: String, mediaType: CallMediaType) { +fun CallButton( + modifier: Modifier, + chat: Chat, + contact: Contact, + icon: Painter, + title: String, + mediaType: CallMediaType +) { val canCall = contact.ready && contact.active && contact.mergedPreferences.calls.enabled.forUser && chatModel.activeCall.value == null val needToAllowCallsToContact = remember(chat.chatInfo) { chat.chatInfo is ChatInfo.Direct && with(chat.chatInfo.contact.mergedPreferences.calls) { @@ -845,6 +875,7 @@ fun CallButton(chat: Chat, contact: Contact, icon: Painter, title: String, media val allowedCallsByPrefs = remember(chat.chatInfo) { chat.chatInfo.featureEnabled(ChatFeature.Calls) } InfoViewActionButton( + modifier = modifier, icon = icon, title = title, disabled = chatModel.activeCall.value != null, @@ -907,42 +938,52 @@ private fun showCallsProhibitedAlert() { ) } -// for ChatInfoView (it has most buttons - 4) we use Spacer(Modifier.weight(1f)) to fit, -// for GroupChat And GroupMemberInfoViews (2 to 3 buttons) we use this as approximately equal to spacing in ChatInfoView -val INFO_VIEW_BUTTONS_PADDING = 36.dp - @Composable -fun InfoViewActionButton(icon: Painter, title: String, disabled: Boolean, disabledLook: Boolean, onClick: () -> Unit) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center - ) { - IconButton( - onClick = onClick, - enabled = !disabled +fun InfoViewActionButton( + modifier: Modifier, + icon: Painter, + title: String, + disabled: Boolean, + disabledLook: Boolean, + onClick: () -> Unit +) { + Box(modifier) { + Column( + Modifier + .fillMaxWidth() + .padding(8.dp), + horizontalAlignment = Alignment.CenterHorizontally ) { - Box( - modifier = Modifier - .background( - if (disabledLook) MaterialTheme.colors.secondaryVariant else MaterialTheme.colors.primary, - shape = CircleShape - ) - .padding(16.dp) + IconButton( + onClick = onClick, + enabled = !disabled ) { - Icon( - icon, - contentDescription = null, - Modifier.size(24.dp * fontSizeSqrtMultiplier), - tint = if (disabledLook) MaterialTheme.colors.secondary else MaterialTheme.colors.onPrimary - ) + Box( + Modifier + .size(56.dp) + .background( + if (disabledLook) MaterialTheme.colors.secondaryVariant else MaterialTheme.colors.primary, + shape = CircleShape + ), + contentAlignment = Alignment.Center + ) { + Icon( + icon, + contentDescription = null, + Modifier.size(24.dp * fontSizeSqrtMultiplier), + tint = if (disabledLook) MaterialTheme.colors.secondary else MaterialTheme.colors.onPrimary + ) + } } + Text( + title.capitalize(Locale.current), + Modifier.padding(top = DEFAULT_SPACE_AFTER_ICON), + style = MaterialTheme.typography.subtitle2.copy(fontWeight = FontWeight.Normal, fontSize = 12.sp), + color = MaterialTheme.colors.secondary, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) } - Text( - title.capitalize(Locale.current), - style = MaterialTheme.typography.subtitle2.copy(fontWeight = FontWeight.Normal), - color = MaterialTheme.colors.secondary, - modifier = Modifier.padding(top = DEFAULT_SPACE_AFTER_ICON) - ) } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupChatInfoView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupChatInfoView.kt index 5d69972886..97b6cb91a7 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupChatInfoView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupChatInfoView.kt @@ -23,8 +23,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp +import androidx.compose.ui.unit.* import chat.simplex.common.model.* import chat.simplex.common.model.ChatModel.withChats import chat.simplex.common.ui.theme.* @@ -184,10 +183,17 @@ private fun removeMemberAlert(rhId: Long?, groupInfo: GroupInfo, mem: GroupMembe } @Composable -fun SearchButton(chat: Chat, group: GroupInfo, close: () -> Unit, onSearchClicked: () -> Unit) { +fun SearchButton( + modifier: Modifier, + chat: Chat, + group: GroupInfo, + close: () -> Unit, + onSearchClicked: () -> Unit +) { val disabled = !group.ready || chat.chatItems.isEmpty() InfoViewActionButton( + modifier = modifier, icon = painterResource(MR.images.ic_search), title = generalGetString(MR.strings.info_view_search_button), disabled = disabled, @@ -202,10 +208,15 @@ fun SearchButton(chat: Chat, group: GroupInfo, close: () -> Unit, onSearchClicke } @Composable -fun MuteButton(chat: Chat, groupInfo: GroupInfo) { +fun MuteButton( + modifier: Modifier, + chat: Chat, + groupInfo: GroupInfo +) { val ntfsEnabled = remember { mutableStateOf(chat.chatInfo.ntfsEnabled) } InfoViewActionButton( + modifier = modifier, icon = if (ntfsEnabled.value) painterResource(MR.images.ic_notifications_off) else painterResource(MR.images.ic_notifications), title = if (ntfsEnabled.value) stringResource(MR.strings.mute_chat) else stringResource(MR.strings.unmute_chat), disabled = !groupInfo.ready, @@ -217,8 +228,13 @@ fun MuteButton(chat: Chat, groupInfo: GroupInfo) { } @Composable -fun AddGroupMembersButton(chat: Chat, groupInfo: GroupInfo) { +fun AddGroupMembersButton( + modifier: Modifier, + chat: Chat, + groupInfo: GroupInfo +) { InfoViewActionButton( + modifier = modifier, icon = if (groupInfo.incognito) painterResource(MR.images.ic_add_link) else painterResource(MR.images.ic_person_add_500), title = stringResource(MR.strings.action_button_add_members), disabled = !groupInfo.ready, @@ -278,20 +294,26 @@ fun GroupChatInfoLayout( } SectionSpacer() - Row( - Modifier - .fillMaxWidth() - .padding(horizontal = DEFAULT_PADDING), - horizontalArrangement = Arrangement.Center, - verticalAlignment = Alignment.CenterVertically + Box( + Modifier.fillMaxWidth(), + contentAlignment = Alignment.Center ) { - SearchButton(chat, groupInfo, close, onSearchClicked) - if (groupInfo.canAddMembers) { - Spacer(Modifier.width(INFO_VIEW_BUTTONS_PADDING)) - AddGroupMembersButton(chat, groupInfo) + Row( + Modifier + .widthIn(max = if (groupInfo.canAddMembers) 320.dp else 230.dp) + .padding(horizontal = DEFAULT_PADDING), + horizontalArrangement = Arrangement.SpaceEvenly, + verticalAlignment = Alignment.CenterVertically + ) { + if (groupInfo.canAddMembers) { + SearchButton(modifier = Modifier.fillMaxWidth(0.33f), chat, groupInfo, close, onSearchClicked) + AddGroupMembersButton(modifier = Modifier.fillMaxWidth(0.5f), chat, groupInfo) + MuteButton(modifier = Modifier.fillMaxWidth(1f), chat, groupInfo) + } else { + SearchButton(modifier = Modifier.fillMaxWidth(0.5f), chat, groupInfo, close, onSearchClicked) + MuteButton(modifier = Modifier.fillMaxWidth(1f), chat, groupInfo) + } } - Spacer(Modifier.width(INFO_VIEW_BUTTONS_PADDING)) - MuteButton(chat, groupInfo) } SectionSpacer() diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupMemberInfoView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupMemberInfoView.kt index 4e84b1b651..f814fb2eb8 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupMemberInfoView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupMemberInfoView.kt @@ -327,49 +327,49 @@ fun GroupMemberInfoLayout( val contactId = member.memberContactId - Row( - Modifier - .fillMaxWidth() - .padding(horizontal = DEFAULT_PADDING), - horizontalArrangement = Arrangement.Center, - verticalAlignment = Alignment.CenterVertically + Box( + Modifier.fillMaxWidth(), + contentAlignment = Alignment.Center ) { - val knownChat = if (contactId != null) knownDirectChat(contactId) else null - if (knownChat != null) { - val (chat, contact) = knownChat - OpenChatButton(onClick = { openDirectChat(contact.contactId) }) - Spacer(Modifier.width(INFO_VIEW_BUTTONS_PADDING)) - AudioCallButton(chat, contact) - Spacer(Modifier.width(INFO_VIEW_BUTTONS_PADDING)) - VideoButton(chat, contact) - } else if (groupInfo.fullGroupPreferences.directMessages.on(groupInfo.membership)) { - if (contactId != null) { - OpenChatButton(onClick = { openDirectChat(contactId) }) // legacy - only relevant for direct contacts created when joining group - } else { - OpenChatButton(onClick = { createMemberContact() }) + Row( + Modifier + .widthIn(max = 320.dp) + .padding(horizontal = DEFAULT_PADDING), + horizontalArrangement = Arrangement.SpaceEvenly, + verticalAlignment = Alignment.CenterVertically + ) { + val knownChat = if (contactId != null) knownDirectChat(contactId) else null + if (knownChat != null) { + val (chat, contact) = knownChat + OpenChatButton(modifier = Modifier.fillMaxWidth(0.33f), onClick = { openDirectChat(contact.contactId) }) + AudioCallButton(modifier = Modifier.fillMaxWidth(0.5f), chat, contact) + VideoButton(modifier = Modifier.fillMaxWidth(1f), chat, contact) + } else if (groupInfo.fullGroupPreferences.directMessages.on(groupInfo.membership)) { + if (contactId != null) { + OpenChatButton(modifier = Modifier.fillMaxWidth(0.33f), onClick = { openDirectChat(contactId) }) // legacy - only relevant for direct contacts created when joining group + } else { + OpenChatButton(modifier = Modifier.fillMaxWidth(0.33f), onClick = { createMemberContact() }) + } + InfoViewActionButton(modifier = Modifier.fillMaxWidth(0.5f), painterResource(MR.images.ic_call), generalGetString(MR.strings.info_view_call_button), disabled = false, disabledLook = true, onClick = { + showSendMessageToEnableCallsAlert() + }) + InfoViewActionButton(modifier = Modifier.fillMaxWidth(1f), painterResource(MR.images.ic_videocam), generalGetString(MR.strings.info_view_video_button), disabled = false, disabledLook = true, onClick = { + showSendMessageToEnableCallsAlert() + }) + } else { // no known contact chat && directMessages are off + InfoViewActionButton(modifier = Modifier.fillMaxWidth(0.33f), painterResource(MR.images.ic_chat_bubble), generalGetString(MR.strings.info_view_message_button), disabled = false, disabledLook = true, onClick = { + showDirectMessagesProhibitedAlert(generalGetString(MR.strings.cant_send_message_to_member_alert_title)) + }) + InfoViewActionButton(modifier = Modifier.fillMaxWidth(0.5f), painterResource(MR.images.ic_call), generalGetString(MR.strings.info_view_call_button), disabled = false, disabledLook = true, onClick = { + showDirectMessagesProhibitedAlert(generalGetString(MR.strings.cant_call_member_alert_title)) + }) + InfoViewActionButton(modifier = Modifier.fillMaxWidth(1f), painterResource(MR.images.ic_videocam), generalGetString(MR.strings.info_view_video_button), disabled = false, disabledLook = true, onClick = { + showDirectMessagesProhibitedAlert(generalGetString(MR.strings.cant_call_member_alert_title)) + }) } - Spacer(Modifier.width(INFO_VIEW_BUTTONS_PADDING)) - InfoViewActionButton(painterResource(MR.images.ic_call), generalGetString(MR.strings.info_view_call_button), disabled = false, disabledLook = true, onClick = { - showSendMessageToEnableCallsAlert() - }) - Spacer(Modifier.width(INFO_VIEW_BUTTONS_PADDING)) - InfoViewActionButton(painterResource(MR.images.ic_videocam), generalGetString(MR.strings.info_view_video_button), disabled = false, disabledLook = true, onClick = { - showSendMessageToEnableCallsAlert() - }) - } else { // no known contact chat && directMessages are off - InfoViewActionButton(painterResource(MR.images.ic_chat_bubble), generalGetString(MR.strings.info_view_message_button), disabled = false, disabledLook = true, onClick = { - showDirectMessagesProhibitedAlert(generalGetString(MR.strings.cant_send_message_to_member_alert_title)) - }) - Spacer(Modifier.width(INFO_VIEW_BUTTONS_PADDING)) - InfoViewActionButton(painterResource(MR.images.ic_call), generalGetString(MR.strings.info_view_call_button), disabled = false, disabledLook = true, onClick = { - showDirectMessagesProhibitedAlert(generalGetString(MR.strings.cant_call_member_alert_title)) - }) - Spacer(Modifier.width(INFO_VIEW_BUTTONS_PADDING)) - InfoViewActionButton(painterResource(MR.images.ic_videocam), generalGetString(MR.strings.info_view_video_button), disabled = false, disabledLook = true, onClick = { - showDirectMessagesProhibitedAlert(generalGetString(MR.strings.cant_call_member_alert_title)) - }) } } + SectionSpacer() if (member.memberActive) { @@ -580,8 +580,12 @@ fun RemoveMemberButton(onClick: () -> Unit) { } @Composable -fun OpenChatButton(onClick: () -> Unit) { +fun OpenChatButton( + modifier: Modifier, + onClick: () -> Unit +) { InfoViewActionButton( + modifier = modifier, icon = painterResource(MR.images.ic_chat_bubble), title = generalGetString(MR.strings.info_view_message_button), disabled = false,