multiplatform: improve info views actions buttons design (#4622)

* multiplatform: improve info views actions buttons design

* spaces

* rework

* fix width
This commit is contained in:
spaced4ndy
2024-08-08 16:10:13 +04:00
committed by GitHub
parent 41576f80e7
commit 0782408682
3 changed files with 175 additions and 108 deletions
@@ -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)
)
}
}
@@ -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()
@@ -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,