diff --git a/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Color.kt b/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Color.kt index 6ca2ad5d7d..29bedfab43 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Color.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Color.kt @@ -27,3 +27,4 @@ val WarningOrange = Color(255, 127, 0, 255) val WarningYellow = Color(255, 192, 0, 255) val FileLight = Color(183, 190, 199, 255) val FileDark = Color(101, 101, 106, 255) +val MenuTextColorDark = Color.White.copy(alpha = 0.8f) diff --git a/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Theme.kt b/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Theme.kt index dfe6f68989..dbd17b94f7 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Theme.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Theme.kt @@ -28,7 +28,7 @@ val DarkColorPalette = darkColors( // surface = Color.Black, // background = Color(0xFF121212), // surface = Color(0xFF121212), -// error = Color(0xFFCF6679), + error = Color.Red, onBackground = Color(0xFFFFFBFA), onSurface = Color(0xFFFFFBFA), // onError: Color = Color.Black, @@ -37,6 +37,7 @@ val LightColorPalette = lightColors( primary = SimplexBlue, // If this value changes also need to update #0088ff in string resource files primaryVariant = SimplexGreen, secondary = LightGray, + error = Color.Red, // background = Color.White, // surface = Color.White // onPrimary = Color.White, 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 ef60e219f6..841126366a 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 @@ -378,7 +378,7 @@ fun ChatInfoToolbar( onSearchValueChanged: (String) -> Unit, ) { val scope = rememberCoroutineScope() - var showMenu by rememberSaveable { mutableStateOf(false) } + val showMenu = rememberSaveable { mutableStateOf(false) } var showSearch by rememberSaveable { mutableStateOf(false) } val onBackClicked = { if (!showSearch) { @@ -393,7 +393,7 @@ fun ChatInfoToolbar( val menuItems = arrayListOf<@Composable () -> Unit>() menuItems.add { ItemAction(stringResource(android.R.string.search_go).capitalize(Locale.current), Icons.Outlined.Search, onClick = { - showMenu = false + showMenu.value = false showSearch = true }) } @@ -401,7 +401,7 @@ fun ChatInfoToolbar( if (chat.chatInfo is ChatInfo.Direct && chat.chatInfo.contact.allowsFeature(ChatFeature.Calls)) { barButtons.add { IconButton({ - showMenu = false + showMenu.value = false startCall(CallMediaType.Audio) }) { Icon(Icons.Outlined.Phone, stringResource(R.string.icon_descr_more_button), tint = MaterialTheme.colors.primary) @@ -409,14 +409,14 @@ fun ChatInfoToolbar( } menuItems.add { ItemAction(stringResource(R.string.icon_descr_video_call).capitalize(Locale.current), Icons.Outlined.Videocam, onClick = { - showMenu = false + showMenu.value = false startCall(CallMediaType.Video) }) } } else if (chat.chatInfo is ChatInfo.Group && chat.chatInfo.groupInfo.canAddMembers && !chat.chatInfo.incognito) { barButtons.add { IconButton({ - showMenu = false + showMenu.value = false addMembers(chat.chatInfo.groupInfo) }) { Icon(Icons.Outlined.PersonAdd, stringResource(R.string.icon_descr_add_members), tint = MaterialTheme.colors.primary) @@ -429,7 +429,7 @@ fun ChatInfoToolbar( if (ntfsEnabled.value) stringResource(R.string.mute_chat) else stringResource(R.string.unmute_chat), if (ntfsEnabled.value) Icons.Outlined.NotificationsOff else Icons.Outlined.Notifications, onClick = { - showMenu = false + showMenu.value = false // Just to make a delay before changing state of ntfsEnabled, otherwise it will redraw menu item with new value before closing the menu scope.launch { delay(200) @@ -440,7 +440,7 @@ fun ChatInfoToolbar( } barButtons.add { - IconButton({ showMenu = true }) { + IconButton({ showMenu.value = true }) { Icon(Icons.Default.MoreVert, stringResource(R.string.icon_descr_more_button), tint = MaterialTheme.colors.primary) } } @@ -457,11 +457,7 @@ fun ChatInfoToolbar( Divider(Modifier.padding(top = AppBarHeight)) Box(Modifier.fillMaxWidth().wrapContentSize(Alignment.TopEnd).offset(y = AppBarHeight)) { - DropdownMenu( - expanded = showMenu, - onDismissRequest = { showMenu = false }, - Modifier.widthIn(min = 220.dp) - ) { + DefaultDropdownMenu(showMenu) { menuItems.forEach { it() } } } @@ -788,36 +784,27 @@ fun BoxWithConstraintsScope.FloatingButtons( } val showButtonWithCounter = topUnreadCount > 0 val height = with(LocalDensity.current) { maxHeight.toPx() } - var showDropDown by remember { mutableStateOf(false) } + val showDropDown = remember { mutableStateOf(false) } TopEndFloatingButton( Modifier.padding(end = 16.dp, top = 24.dp).align(Alignment.TopEnd), topUnreadCount, showButtonWithCounter, onClick = { scope.launch { listState.animateScrollBy(height) } }, - onLongClick = { showDropDown = true } + onLongClick = { showDropDown.value = true } ) - DropdownMenu( - expanded = showDropDown, - onDismissRequest = { showDropDown = false }, - Modifier.width(220.dp), - offset = DpOffset(maxWidth - 16.dp, 24.dp + fabSize) - ) { - DropdownMenuItem( + DefaultDropdownMenu(showDropDown, offset = DpOffset(maxWidth - 16.dp, 24.dp + fabSize)) { + ItemAction( + generalGetString(R.string.mark_read), + Icons.Outlined.Check, onClick = { markRead( CC.ItemRange(minUnreadItemId, chatItems[chatItems.size - listState.layoutInfo.visibleItemsInfo.lastIndex - 1].id - 1), bottomUnreadCount ) - showDropDown = false - } - ) { - Text( - generalGetString(R.string.mark_read), - maxLines = 1, - ) - } + showDropDown.value = false + }) } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/SendMsgView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/SendMsgView.kt index 31c9c34211..24a1f614b2 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/SendMsgView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/SendMsgView.kt @@ -155,20 +155,18 @@ fun SendMsgView( cs.contextItem is ComposeContextItem.NoContextItem && sendLiveMessage != null && updateLiveMessage != null ) { - var showDropdown by rememberSaveable { mutableStateOf(false) } - SendMsgButton(icon, sendButtonSize, sendButtonAlpha, !disabled, sendMessage) { showDropdown = true } + val showDropdown = rememberSaveable { mutableStateOf(false) } + SendMsgButton(icon, sendButtonSize, sendButtonAlpha, !disabled, sendMessage) { showDropdown.value = true } - DropdownMenu( - expanded = showDropdown, - onDismissRequest = { showDropdown = false }, - Modifier.width(220.dp), + DefaultDropdownMenu( + showDropdown, ) { ItemAction( generalGetString(R.string.send_live_message), Icons.Filled.Bolt, onClick = { startLiveMessage(scope, sendLiveMessage, updateLiveMessage, sendButtonSize, sendButtonAlpha, composeState, liveMessageAlertShown) - showDropdown = false + showDropdown.value = false } ) } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/ChatItemView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/ChatItemView.kt index d6a969715a..d1b87aae6b 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/ChatItemView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/ChatItemView.kt @@ -22,8 +22,7 @@ import androidx.compose.ui.unit.dp import chat.simplex.app.* import chat.simplex.app.R import chat.simplex.app.model.* -import chat.simplex.app.ui.theme.HighOrLowlight -import chat.simplex.app.ui.theme.SimpleXTheme +import chat.simplex.app.ui.theme.* import chat.simplex.app.views.chat.ComposeContextItem import chat.simplex.app.views.chat.ComposeState import chat.simplex.app.views.helpers.* @@ -105,11 +104,7 @@ fun ChatItemView( @Composable fun MsgContentItemDropdownMenu() { - DropdownMenu( - expanded = showMenu.value, - onDismissRequest = { showMenu.value = false }, - Modifier.width(220.dp) - ) { + DefaultDropdownMenu(showMenu) { if (cItem.meta.itemDeleted == null && !live) { ItemAction(stringResource(R.string.reply_verb), Icons.Outlined.Reply, onClick = { if (composeState.value.editing) { @@ -183,11 +178,7 @@ fun ChatItemView( @Composable fun MarkedDeletedItemDropdownMenu() { - DropdownMenu( - expanded = showMenu.value, - onDismissRequest = { showMenu.value = false }, - Modifier.width(220.dp) - ) { + DefaultDropdownMenu(showMenu) { if (!cItem.isDeletedContent) { ItemAction( stringResource(R.string.reveal_verb), @@ -227,11 +218,7 @@ fun ChatItemView( @Composable fun DeletedItem() { DeletedItemView(cItem, cInfo.timedMessagesTTL, showMember = showMember) - DropdownMenu( - expanded = showMenu.value, - onDismissRequest = { showMenu.value = false }, - Modifier.width(220.dp) - ) { + DefaultDropdownMenu(showMenu) { DeleteItemAction(cItem, showMenu, questionText = deleteMessageQuestionText(), deleteMessage) } } @@ -329,18 +316,21 @@ fun ModerateItemAction( } @Composable -fun ItemAction(text: String, icon: ImageVector, onClick: () -> Unit, color: Color = MaterialTheme.colors.onBackground) { - DropdownMenuItem(onClick) { - Row { +fun ItemAction(text: String, icon: ImageVector, onClick: () -> Unit, color: Color = Color.Unspecified) { + val finalColor = if (color == Color.Unspecified) { + if (isInDarkTheme()) MenuTextColorDark else Color.Black + } else color + DropdownMenuItem(onClick, contentPadding = PaddingValues(horizontal = DEFAULT_PADDING * 1.5f)) { + Row(verticalAlignment = Alignment.CenterVertically) { Text( text, modifier = Modifier .fillMaxWidth() .weight(1F) .padding(end = 15.dp), - color = color + color = finalColor ) - Icon(icon, text, tint = color) + Icon(icon, text, tint = finalColor) } } } @@ -366,7 +356,7 @@ fun deleteMessageAlertDialog(chatItem: ChatItem, questionText: String, deleteMes Modifier .fillMaxWidth() .padding(horizontal = 8.dp, vertical = 2.dp), - horizontalArrangement = Arrangement.End, + horizontalArrangement = Arrangement.Center, ) { TextButton(onClick = { deleteMessage(chatItem.id, CIDeleteMode.cidmInternal) 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 af412de608..1e8ed4a62f 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 @@ -194,26 +194,14 @@ fun MarkReadChatAction(chat: Chat, chatModel: ChatModel, showMenu: MutableState< @Composable fun MarkUnreadChatAction(chat: Chat, chatModel: ChatModel, showMenu: MutableState) { - DropdownMenuItem({ - markChatUnread(chat, chatModel) - showMenu.value = false - }) { - Row { - Text( - stringResource(R.string.mark_unread), - modifier = Modifier - .fillMaxWidth() - .weight(1F) - .padding(end = 15.dp), - color = MaterialTheme.colors.onBackground - ) - Icon( - Icons.Outlined.MarkChatUnread, - stringResource(R.string.mark_unread), - tint = MaterialTheme.colors.onBackground - ) + ItemAction( + stringResource(R.string.mark_unread), + Icons.Outlined.MarkChatUnread, + onClick = { + markChatUnread(chat, chatModel) + showMenu.value = false } - } + ) } @Composable @@ -447,7 +435,7 @@ fun contactConnectionAlertDialog(connection: PendingContactConnection, chatModel Modifier .fillMaxWidth() .padding(horizontal = 8.dp, vertical = 2.dp), - horizontalArrangement = Arrangement.End, + horizontalArrangement = Arrangement.Center, ) { TextButton(onClick = { AlertManager.shared.hideAlert() @@ -589,15 +577,7 @@ fun ChatListNavLinkLayout( chatLinkPreview() } if (dropdownMenuItems != null) { - Box(Modifier.padding(horizontal = 16.dp)) { - DropdownMenu( - expanded = showMenu.value, - onDismissRequest = { showMenu.value = false }, - Modifier.width(220.dp) - ) { - dropdownMenuItems() - } - } + DefaultDropdownMenu(showMenu, dropdownMenuItems = dropdownMenuItems) } } Divider(Modifier.padding(horizontal = 8.dp)) diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/UserPicker.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/UserPicker.kt index 5d37695616..a2494fb81c 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/UserPicker.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/UserPicker.kt @@ -1,19 +1,20 @@ package chat.simplex.app.views.chatlist +import SectionItemView import SectionItemViewSpaceBetween import android.util.Log import androidx.compose.animation.core.* import androidx.compose.foundation.* import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.* import androidx.compose.material.* import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.* import androidx.compose.material.icons.outlined.* import androidx.compose.runtime.* import androidx.compose.ui.* -import androidx.compose.ui.draw.shadow +import androidx.compose.ui.draw.* import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.platform.LocalConfiguration @@ -105,15 +106,16 @@ fun UserPicker( ) { Column( Modifier - .widthIn(min = 220.dp) + .widthIn(min = 260.dp) .width(IntrinsicSize.Min) .height(IntrinsicSize.Min) - .shadow(8.dp, MaterialTheme.shapes.medium, clip = false) - .background(if (isInDarkTheme()) MaterialTheme.colors.background.darker(-0.7f) else MaterialTheme.colors.background, MaterialTheme.shapes.medium) + .shadow(8.dp, RoundedCornerShape(corner = CornerSize(25.dp)), clip = true) + .background(if (isInDarkTheme()) Color(0xff222222) else MaterialTheme.colors.background, RoundedCornerShape(corner = CornerSize(25.dp))) + .clip(RoundedCornerShape(corner = CornerSize(25.dp))) ) { Column(Modifier.weight(1f).verticalScroll(rememberScrollState())) { users.forEach { u -> - UserProfilePickerItem(u.user, u.unreadCount, openSettings = { + UserProfilePickerItem(u.user, u.unreadCount, PaddingValues(start = DEFAULT_PADDING, end = DEFAULT_PADDING * 2), openSettings = { settingsClicked() userPickerState.value = AnimatedViewState.GONE }) { @@ -151,7 +153,7 @@ fun UserPicker( } @Composable -fun UserProfilePickerItem(u: User, unreadCount: Int = 0, onLongClick: () -> Unit = {}, openSettings: () -> Unit = {}, onClick: () -> Unit) { +fun UserProfilePickerItem(u: User, unreadCount: Int = 0, padding: PaddingValues = PaddingValues(start = 8.dp, end = DEFAULT_PADDING), onLongClick: () -> Unit = {}, openSettings: () -> Unit = {}, onClick: () -> Unit) { Row( Modifier .fillMaxWidth() @@ -162,7 +164,7 @@ fun UserProfilePickerItem(u: User, unreadCount: Int = 0, onLongClick: () -> Unit interactionSource = remember { MutableInteractionSource() }, indication = if (!u.activeUser) LocalIndication.current else null ) - .padding(PaddingValues(start = 8.dp, end = DEFAULT_PADDING)), + .padding(padding), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { @@ -211,6 +213,7 @@ fun UserProfileRow(u: User) { u.displayName, modifier = Modifier .padding(start = 8.dp, end = 8.dp), + color = if (isInDarkTheme()) MenuTextColorDark else Color.Black, fontWeight = if (u.activeUser) FontWeight.Medium else FontWeight.Normal ) } @@ -218,24 +221,26 @@ fun UserProfileRow(u: User) { @Composable private fun SettingsPickerItem(onClick: () -> Unit) { - SectionItemViewSpaceBetween(onClick, minHeight = 68.dp) { + SectionItemView(onClick, padding = PaddingValues(start = DEFAULT_PADDING * 2.2f, end = DEFAULT_PADDING * 2), minHeight = 68.dp) { val text = generalGetString(R.string.settings_section_title_settings).lowercase().capitalize(Locale.current) + Icon(Icons.Outlined.Settings, text, Modifier.size(20.dp), tint = MaterialTheme.colors.onBackground) + Spacer(Modifier.width(DEFAULT_PADDING * 1.5f)) Text( text, - color = MaterialTheme.colors.onBackground, + color = if (isInDarkTheme()) MenuTextColorDark else Color.Black, ) - Icon(Icons.Outlined.Settings, text, Modifier.size(20.dp), tint = MaterialTheme.colors.onBackground) } } @Composable private fun CancelPickerItem(onClick: () -> Unit) { - SectionItemViewSpaceBetween(onClick, minHeight = 68.dp) { + SectionItemViewSpaceBetween(onClick, padding = PaddingValues(start = DEFAULT_PADDING * 2.2f, end = DEFAULT_PADDING * 2), minHeight = 68.dp) { val text = generalGetString(R.string.cancel_verb) + Icon(Icons.Outlined.Close, text, Modifier.size(20.dp), tint = MaterialTheme.colors.onBackground) + Spacer(Modifier.width(DEFAULT_PADDING * 1.5f)) Text( text, - color = MaterialTheme.colors.onBackground, + color = if (isInDarkTheme()) MenuTextColorDark else Color.Black, ) - Icon(Icons.Outlined.Close, text, Modifier.size(20.dp), tint = MaterialTheme.colors.onBackground) } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/AlertManager.kt b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/AlertManager.kt index 97c4bfa51a..7eb452a5e6 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/AlertManager.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/AlertManager.kt @@ -3,13 +3,14 @@ package chat.simplex.app.views.helpers import android.util.Log import androidx.compose.foundation.background import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CornerSize +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.AnnotatedString -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.* import androidx.compose.ui.window.Dialog @@ -34,13 +35,14 @@ class AlertManager { text: String? = null, buttons: @Composable () -> Unit, ) { - val alertText: (@Composable () -> Unit)? = if (text == null) null else { -> Text(text) } showAlert { AlertDialog( + backgroundColor = if (isInDarkTheme()) Color(0xff222222) else MaterialTheme.colors.background, onDismissRequest = this::hideAlert, - title = { Text(title) }, - text = alertText, - buttons = buttons + title = alertTitle(title), + text = alertText(text), + buttons = buttons, + shape = RoundedCornerShape(corner = CornerSize(25.dp)) ) } } @@ -52,22 +54,21 @@ class AlertManager { ) { showAlert { Dialog(onDismissRequest = this::hideAlert) { - Column(Modifier.background(MaterialTheme.colors.background, MaterialTheme.shapes.medium)) { - Text(title, - Modifier.padding(start = DEFAULT_PADDING, end = DEFAULT_PADDING, top = DEFAULT_PADDING, bottom = if (text == null) DEFAULT_PADDING else DEFAULT_PADDING_HALF), - fontSize = 15.sp, - fontWeight = FontWeight.SemiBold + Column( + Modifier + .background(if (isInDarkTheme()) Color(0xff222222) else MaterialTheme.colors.background, RoundedCornerShape(corner = CornerSize(25.dp))) + .padding(bottom = DEFAULT_PADDING) + ) { + Text( + title, + Modifier.fillMaxWidth().padding(vertical = DEFAULT_PADDING), + textAlign = TextAlign.Center, + fontSize = 20.sp ) - if (text != null) { - CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.high) { - Text( - text, - Modifier.padding(start = DEFAULT_PADDING, end = DEFAULT_PADDING, bottom = DEFAULT_PADDING), - fontSize = 14.sp, - ) - } - } CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.high) { + if (text != null) { + Text(text, Modifier.fillMaxWidth().padding(start = DEFAULT_PADDING, end = DEFAULT_PADDING, bottom = DEFAULT_PADDING * 1.5f), fontSize = 16.sp, textAlign = TextAlign.Center, color = HighOrLowlight) + } buttons() } } @@ -85,24 +86,28 @@ class AlertManager { onDismissRequest: (() -> Unit)? = null, destructive: Boolean = false ) { - val alertText: (@Composable () -> Unit)? = if (text == null) null else { -> Text(text) } showAlert { AlertDialog( onDismissRequest = { onDismissRequest?.invoke(); hideAlert() }, - title = { Text(title) }, - text = alertText, - confirmButton = { - TextButton(onClick = { - onConfirm?.invoke() - hideAlert() - }) { Text(confirmText, color = if (destructive) MaterialTheme.colors.error else Color.Unspecified) } + title = alertTitle(title), + text = alertText(text), + buttons = { + Row ( + Modifier.fillMaxWidth().padding(horizontal = DEFAULT_PADDING).padding(bottom = DEFAULT_PADDING_HALF), + horizontalArrangement = Arrangement.SpaceBetween + ) { + TextButton(onClick = { + onDismiss?.invoke() + hideAlert() + }) { Text(dismissText) } + TextButton(onClick = { + onConfirm?.invoke() + hideAlert() + }) { Text(confirmText, color = if (destructive) MaterialTheme.colors.error else Color.Unspecified) } + } }, - dismissButton = { - TextButton(onClick = { - onDismiss?.invoke() - hideAlert() - }) { Text(dismissText) } - } + backgroundColor = if (isInDarkTheme()) Color(0xff222222) else MaterialTheme.colors.background, + shape = RoundedCornerShape(corner = CornerSize(25.dp)) ) } } @@ -117,16 +122,15 @@ class AlertManager { onDismissRequest: (() -> Unit)? = null, destructive: Boolean = false ) { - val alertText: (@Composable () -> Unit)? = if (text == null) null else { -> Text(text) } showAlert { AlertDialog( onDismissRequest = { onDismissRequest?.invoke(); hideAlert() }, - title = { Text(title) }, - text = alertText, + title = alertTitle(title), + text = alertText(text), buttons = { Column( Modifier.fillMaxWidth().padding(horizontal = 8.dp).padding(top = 16.dp, bottom = 2.dp), - horizontalAlignment = Alignment.End + horizontalAlignment = Alignment.CenterHorizontally ) { TextButton(onClick = { onDismiss?.invoke() @@ -135,9 +139,11 @@ class AlertManager { TextButton(onClick = { onConfirm?.invoke() hideAlert() - }) { Text(confirmText, color = if (destructive) MaterialTheme.colors.error else Color.Unspecified, textAlign = TextAlign.End) } + }) { Text(confirmText, color = if (destructive) Color.Red else Color.Unspecified, textAlign = TextAlign.End) } } }, + backgroundColor = if (isInDarkTheme()) Color(0xff222222) else MaterialTheme.colors.background, + shape = RoundedCornerShape(corner = CornerSize(25.dp)) ) } } @@ -146,18 +152,24 @@ class AlertManager { title: String, text: String? = null, confirmText: String = generalGetString(R.string.ok), onConfirm: (() -> Unit)? = null ) { - val alertText: (@Composable () -> Unit)? = if (text == null) null else { -> Text(text) } showAlert { AlertDialog( onDismissRequest = this::hideAlert, - title = { Text(title) }, - text = alertText, - confirmButton = { - TextButton(onClick = { - onConfirm?.invoke() - hideAlert() - }) { Text(confirmText) } - } + title = alertTitle(title), + text = alertText(text), + buttons = { + Row( + Modifier.fillMaxWidth().padding(horizontal = DEFAULT_PADDING).padding(bottom = DEFAULT_PADDING_HALF), + horizontalArrangement = Arrangement.Center + ) { + TextButton(onClick = { + onConfirm?.invoke() + hideAlert() + }) { Text(confirmText, color = Color.Unspecified) } + } + }, + backgroundColor = if (isInDarkTheme()) Color(0xff222222) else MaterialTheme.colors.background, + shape = RoundedCornerShape(corner = CornerSize(25.dp)) ) } } @@ -178,3 +190,30 @@ class AlertManager { val shared = AlertManager() } } + +private fun alertTitle(title: String): (@Composable () -> Unit)? { + return { + Text( + title, + Modifier.fillMaxWidth(), + textAlign = TextAlign.Center, + fontSize = 20.sp + ) + } +} + +private fun alertText(text: String?): (@Composable () -> Unit)? { + return if (text == null) { + null + } else { + ({ + Text( + text, + Modifier.fillMaxWidth(), + textAlign = TextAlign.Center, + fontSize = 16.sp, + color = HighOrLowlight + ) + }) + } +} \ No newline at end of file diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/DefaultDropdownMenu.kt b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/DefaultDropdownMenu.kt new file mode 100644 index 0000000000..0cb501e7f4 --- /dev/null +++ b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/DefaultDropdownMenu.kt @@ -0,0 +1,58 @@ +package chat.simplex.app.views.helpers + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CornerSize +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.DpOffset +import androidx.compose.ui.unit.dp +import chat.simplex.app.ui.theme.* + +@Composable +fun DefaultDropdownMenu( + showMenu: MutableState, + offset: DpOffset = DpOffset(0.dp, 0.dp), + dropdownMenuItems: (@Composable () -> Unit)? +) { + MaterialTheme( + colors = MaterialTheme.colors.copy(surface = if (isInDarkTheme()) Color(0xFF080808) else MaterialTheme.colors.background), + shapes = MaterialTheme.shapes.copy(medium = RoundedCornerShape(corner = CornerSize(25.dp))) + ) { + DropdownMenu( + expanded = showMenu.value, + onDismissRequest = { showMenu.value = false }, + Modifier + .widthIn(min = 250.dp) + .padding(vertical = 4.dp), + offset = offset, + ) { + dropdownMenuItems?.invoke() + } + } +} + +@Composable +fun ExposedDropdownMenuBoxScope.DefaultExposedDropdownMenu( + expanded: MutableState, + modifier: Modifier = Modifier, + dropdownMenuItems: (@Composable () -> Unit)? +) { + MaterialTheme( + colors = MaterialTheme.colors.copy(surface = if (isInDarkTheme()) Color(0xFF080808) else MaterialTheme.colors.background), + shapes = MaterialTheme.shapes.copy(medium = RoundedCornerShape(corner = CornerSize(25.dp))) + ) { + ExposedDropdownMenu( + modifier = Modifier.widthIn(min = 200.dp).then(modifier), + expanded = expanded.value, + onDismissRequest = { + expanded.value = false + } + ) { + dropdownMenuItems?.invoke() + } + } +} \ No newline at end of file diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/ExposedDropDownSettingRow.kt b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/ExposedDropDownSettingRow.kt index 138c78e2ad..3ae959d463 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/ExposedDropDownSettingRow.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/ExposedDropDownSettingRow.kt @@ -15,8 +15,7 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import chat.simplex.app.R -import chat.simplex.app.ui.theme.DEFAULT_PADDING -import chat.simplex.app.ui.theme.HighOrLowlight +import chat.simplex.app.ui.theme.* @Composable fun ExposedDropDownSettingRow( @@ -33,7 +32,7 @@ fun ExposedDropDownSettingRow( Modifier.fillMaxWidth().padding(vertical = 10.dp), verticalAlignment = Alignment.CenterVertically, ) { - var expanded by remember { mutableStateOf(false) } + val expanded = remember { mutableStateOf(false) } if (icon != null) { Icon( @@ -46,9 +45,9 @@ fun ExposedDropDownSettingRow( Text(title, Modifier.weight(1f), color = if (enabled.value) Color.Unspecified else HighOrLowlight) ExposedDropdownMenuBox( - expanded = expanded, + expanded = expanded.value, onExpandedChange = { - expanded = !expanded && enabled.value + expanded.value = !expanded.value && enabled.value } ) { Row( @@ -66,29 +65,28 @@ fun ExposedDropDownSettingRow( ) Spacer(Modifier.size(12.dp)) Icon( - if (!expanded) Icons.Outlined.ExpandMore else Icons.Outlined.ExpandLess, + if (!expanded.value) Icons.Outlined.ExpandMore else Icons.Outlined.ExpandLess, generalGetString(R.string.icon_descr_more_button), tint = HighOrLowlight ) } - ExposedDropdownMenu( + DefaultExposedDropdownMenu( modifier = Modifier.widthIn(min = 200.dp), expanded = expanded, - onDismissRequest = { - expanded = false - } ) { values.forEach { selectionOption -> DropdownMenuItem( onClick = { onSelected(selectionOption.first) - expanded = false - } + expanded.value = false + }, + contentPadding = PaddingValues(horizontal = DEFAULT_PADDING * 1.5f) ) { Text( selectionOption.second + (if (label != null) " $label" else ""), maxLines = 1, overflow = TextOverflow.Ellipsis, + color = if (isInDarkTheme()) MenuTextColorDark else Color.Black, ) } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/AdvancedNetworkSettings.kt b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/AdvancedNetworkSettings.kt index f5c633c245..e133f04a58 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/AdvancedNetworkSettings.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/AdvancedNetworkSettings.kt @@ -12,6 +12,7 @@ import androidx.compose.material.* import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.* import androidx.compose.runtime.* +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -253,14 +254,14 @@ fun IntSettingRow(title: String, selection: MutableState, values: List verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween ) { - var expanded by remember { mutableStateOf(false) } + val expanded = rememberSaveable { mutableStateOf(false) } Text(title) ExposedDropdownMenuBox( - expanded = expanded, + expanded = expanded.value, onExpandedChange = { - expanded = !expanded + expanded.value = !expanded.value } ) { Row( @@ -276,24 +277,22 @@ fun IntSettingRow(title: String, selection: MutableState, values: List ) Spacer(Modifier.size(4.dp)) Icon( - if (!expanded) Icons.Outlined.ExpandMore else Icons.Outlined.ExpandLess, + if (!expanded.value) Icons.Outlined.ExpandMore else Icons.Outlined.ExpandLess, generalGetString(R.string.invite_to_group_button), modifier = Modifier.padding(start = 8.dp), tint = HighOrLowlight ) } - ExposedDropdownMenu( + DefaultExposedDropdownMenu( expanded = expanded, - onDismissRequest = { - expanded = false - } ) { values.forEach { selectionOption -> DropdownMenuItem( onClick = { selection.value = selectionOption - expanded = false - } + expanded.value = false + }, + contentPadding = PaddingValues(horizontal = DEFAULT_PADDING * 1.5f) ) { Text( "$selectionOption $label", @@ -314,14 +313,14 @@ fun TimeoutSettingRow(title: String, selection: MutableState, values: List verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween ) { - var expanded by remember { mutableStateOf(false) } + val expanded = remember { mutableStateOf(false) } Text(title) ExposedDropdownMenuBox( - expanded = expanded, + expanded = expanded.value, onExpandedChange = { - expanded = !expanded + expanded.value = !expanded.value } ) { val df = DecimalFormat("#.#") @@ -339,24 +338,22 @@ fun TimeoutSettingRow(title: String, selection: MutableState, values: List ) Spacer(Modifier.size(4.dp)) Icon( - if (!expanded) Icons.Outlined.ExpandMore else Icons.Outlined.ExpandLess, + if (!expanded.value) Icons.Outlined.ExpandMore else Icons.Outlined.ExpandLess, generalGetString(R.string.invite_to_group_button), modifier = Modifier.padding(start = 8.dp), tint = HighOrLowlight ) } - ExposedDropdownMenu( - expanded = expanded, - onDismissRequest = { - expanded = false - } + DefaultExposedDropdownMenu( + expanded = expanded ) { values.forEach { selectionOption -> DropdownMenuItem( onClick = { selection.value = selectionOption - expanded = false - } + expanded.value = false + }, + contentPadding = PaddingValues(horizontal = DEFAULT_PADDING * 1.5f) ) { Text( "${df.format(selectionOption / 1_000_000.toDouble())} $label", diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/ProtocolServersView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/ProtocolServersView.kt index 210db9f61d..2b7224f8e9 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/ProtocolServersView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/ProtocolServersView.kt @@ -18,6 +18,7 @@ import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.* import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import chat.simplex.app.R import chat.simplex.app.model.* @@ -112,7 +113,7 @@ fun ProtocolServersView(m: ChatModel, serverProtocol: ServerProtocol, close: () // No saving until something will be changed on the next screen to prevent blank servers on the list showServer(servers.last()) }) { - Text(stringResource(R.string.smp_servers_enter_manually)) + Text(stringResource(R.string.smp_servers_enter_manually), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary) } SectionItemView({ AlertManager.shared.hideAlert() @@ -125,7 +126,7 @@ fun ProtocolServersView(m: ChatModel, serverProtocol: ServerProtocol, close: () } } ) { - Text(stringResource(R.string.smp_servers_scan_qr)) + Text(stringResource(R.string.smp_servers_scan_qr), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary) } val hasAllPresets = hasAllPresets(presetServers, servers, m) if (!hasAllPresets) { @@ -133,7 +134,7 @@ fun ProtocolServersView(m: ChatModel, serverProtocol: ServerProtocol, close: () AlertManager.shared.hideAlert() servers = (servers + addAllPresets(presetServers, servers, m)).sortedByDescending { it.preset } }) { - Text(stringResource(R.string.smp_servers_preset_add), color = MaterialTheme.colors.onBackground) + Text(stringResource(R.string.smp_servers_preset_add), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.onBackground) } } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserProfilesView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserProfilesView.kt index 8a4675b5ce..c28515e3a0 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserProfilesView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserProfilesView.kt @@ -19,6 +19,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.* import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import chat.simplex.app.R import chat.simplex.app.chatPasswordHash @@ -31,7 +32,6 @@ import chat.simplex.app.views.database.PassphraseField import chat.simplex.app.views.helpers.* import chat.simplex.app.views.onboarding.CreateProfile import kotlinx.coroutines.delay -import kotlinx.coroutines.launch @Composable fun UserProfilesView(m: ChatModel, search: MutableState, profileHidden: MutableState) { @@ -73,14 +73,14 @@ fun UserProfilesView(m: ChatModel, search: MutableState, profileHidden: AlertManager.shared.hideAlert() removeUser(m, user, users, true, searchTextOrPassword.value.trim()) }) { - Text(stringResource(R.string.users_delete_with_connections), color = Color.Red) + Text(stringResource(R.string.users_delete_with_connections), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = Color.Red) } SectionItemView({ AlertManager.shared.hideAlert() removeUser(m, user, users, false, searchTextOrPassword.value.trim()) } ) { - Text(stringResource(R.string.users_delete_data_only), color = Color.Red) + Text(stringResource(R.string.users_delete_data_only), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = Color.Red) } } } @@ -210,43 +210,39 @@ private fun UserView( unmuteUser: (User) -> Unit, showHiddenProfile: (User) -> Unit, ) { - var showDropdownMenu by remember { mutableStateOf(false) } - UserProfilePickerItem(user, onLongClick = { if (users.size > 1) showDropdownMenu = true }) { + val showMenu = remember { mutableStateOf(false) } + UserProfilePickerItem(user, onLongClick = { if (users.size > 1) showMenu.value = true }) { activateUser(user) } Box(Modifier.padding(horizontal = 16.dp)) { - DropdownMenu( - expanded = showDropdownMenu, - onDismissRequest = { showDropdownMenu = false }, - Modifier.width(220.dp) - ) { + DefaultDropdownMenu(showMenu) { if (user.hidden) { ItemAction(stringResource(R.string.user_unhide), Icons.Outlined.LockOpen, onClick = { - showDropdownMenu = false + showMenu.value = false unhideUser(user) }) } else { if (visibleUsersCount > 1) { ItemAction(stringResource(R.string.user_hide), Icons.Outlined.Lock, onClick = { - showDropdownMenu = false + showMenu.value = false showHiddenProfile(user) }) } if (user.showNtfs) { ItemAction(stringResource(R.string.user_mute), Icons.Outlined.NotificationsOff, onClick = { - showDropdownMenu = false + showMenu.value = false muteUser(user) }) } else { ItemAction(stringResource(R.string.user_unmute), Icons.Outlined.Notifications, onClick = { - showDropdownMenu = false + showMenu.value = false unmuteUser(user) }) } } ItemAction(stringResource(R.string.delete_verb), Icons.Outlined.Delete, color = Color.Red, onClick = { removeUser(user) - showDropdownMenu = false + showMenu.value = false }) } }