diff --git a/briar-desktop/src/main/kotlin/androidx/compose/material/IconButtonExt.kt b/briar-desktop/src/main/kotlin/androidx/compose/material/IconButtonExt.kt new file mode 100644 index 0000000000000000000000000000000000000000..d6619e0d021a881cbe8b6ad6c9a498cb694d8b53 --- /dev/null +++ b/briar-desktop/src/main/kotlin/androidx/compose/material/IconButtonExt.kt @@ -0,0 +1,97 @@ +/* + * Briar Desktop + * Copyright (C) 2021-2022 The Briar Project + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package androidx.compose.material + +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.TooltipArea +import androidx.compose.foundation.interaction.Interaction +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.shadow +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp + +/** + * IconButton is a clickable icon, used to represent actions. An IconButton has an overall minimum + * touch target size of 48 x 48dp, to meet accessibility guidelines. + * This version of [IconButton] enables tooltips on Desktop Devices and internally uses an [Icon] to show [icon]. + * + * @param icon [ImageVector] to draw as icon inside this IconButton + * @param contentDescription text used by accessibility services and the tooltip + * to describe the action invoked by this IconButton. + * @param onClick the lambda to be invoked when this icon is pressed + * @param modifier optional [Modifier] for this IconButton + * @param iconTint tint to be applied to [icon]. See [Icon] for more information. + * @param enabled whether or not this IconButton will handle input events and appear enabled for + * semantics purposes + * @param interactionSource the [MutableInteractionSource] representing the stream of + * [Interaction]s for this IconButton. You can create and pass in your own remembered + * [MutableInteractionSource] if you want to observe [Interaction]s and customize the + * appearance / behavior of this IconButton in different [Interaction]s. + * @param extraContent content that is added after the [icon]. This is mainly used for showing [DropdownMenu]s. + */ +@OptIn(ExperimentalFoundationApi::class) +@Composable +fun IconButton( + icon: ImageVector, + contentDescription: String, + onClick: () -> Unit, + modifier: Modifier = Modifier, + iconSize: Dp = 24.dp, + iconTint: Color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current), + enabled: Boolean = true, + interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, + extraContent: (@Composable () -> Unit)? = null +) = TooltipArea( + tooltip = { + Surface( + modifier = Modifier.shadow(4.dp), + shape = RoundedCornerShape(4.dp), + ) { + Text( + text = contentDescription, + modifier = Modifier.padding(4.dp) + ) + } + }, + // taken from https://ux.stackexchange.com/questions/358/how-long-should-the-delay-be-before-a-tooltip-pops-up + delayMillis = 500, + modifier = modifier +) { + IconButton( + onClick = onClick, + enabled = enabled, + interactionSource = interactionSource + ) { + Icon( + icon, + contentDescription, + Modifier.size(iconSize), + iconTint + ) + extraContent?.invoke() + } +} diff --git a/briar-desktop/src/main/kotlin/androidx/compose/material/OutlinedTextFieldExt.kt b/briar-desktop/src/main/kotlin/androidx/compose/material/OutlinedTextFieldExt.kt index d2defb118f2a555ff0f74e30b06fb92be6f03e3f..bdcf40982561e41f354744b73443bb4ed069292e 100644 --- a/briar-desktop/src/main/kotlin/androidx/compose/material/OutlinedTextFieldExt.kt +++ b/briar-desktop/src/main/kotlin/androidx/compose/material/OutlinedTextFieldExt.kt @@ -228,20 +228,8 @@ fun OutlinedPasswordTextField( private fun ShowHidePasswordIcon( isVisible: Boolean, toggleIsVisible: () -> Unit, -) { - IconButton( - onClick = toggleIsVisible - ) { - if (isVisible) { - Icon( - imageVector = Icons.Filled.VisibilityOff, - contentDescription = i18n("access.password.show"), - ) - } else { - Icon( - imageVector = Icons.Filled.Visibility, - contentDescription = i18n("access.password.hide"), - ) - } - } -} +) = IconButton( + icon = if (isVisible) Icons.Filled.VisibilityOff else Icons.Filled.Visibility, + contentDescription = if (isVisible) i18n("access.password.hide") else i18n("access.password.show"), + onClick = toggleIsVisible +) diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactCard.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactCard.kt index 0e98b2bb6f26449763a98af6a3e678ec59d7eb70..73772d5611d06d8060fb4e34eaf4e093439e7cb3 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactCard.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactCard.kt @@ -32,7 +32,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.Card -import androidx.compose.material.Icon import androidx.compose.material.IconButton import androidx.compose.material.MaterialTheme import androidx.compose.material.Text @@ -161,11 +160,11 @@ private fun PendingContactRow(contactItem: PendingContactItem, onRemove: () -> U ) } IconButton( + icon = Icons.Filled.Delete, + contentDescription = i18n("access.contacts.pending.remove"), onClick = onRemove, modifier = Modifier.padding(end = 4.dp).align(Alignment.CenterVertically) - ) { - Icon(Icons.Filled.Delete, i18n("access.contacts.pending.remove")) - } + ) } } diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/SearchTextField.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/SearchTextField.kt index fa7db6ab6e46d5e49c41236cd4462320dee3d9f9..1d32ffc4ca6c1039454f5bac0b17d6dc908e83d8 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/SearchTextField.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/SearchTextField.kt @@ -20,7 +20,6 @@ package org.briarproject.briar.desktop.contact import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.Icon import androidx.compose.material.LocalTextStyle @@ -53,15 +52,12 @@ fun SearchTextField(searchValue: String, onValueChange: (String) -> Unit, onCont }, trailingIcon = { ColoredIconButton( + icon = Icons.Filled.PersonAdd, + iconSize = 20.dp, + contentDescription = i18n("access.contacts.add"), onClick = onContactAdd, modifier = Modifier.padding(end = 8.dp) - ) { - Icon( - Icons.Filled.PersonAdd, - i18n("access.contacts.add"), - modifier = Modifier.size(20.dp) - ) - } + ) }, modifier = Modifier.fillMaxSize() ) diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/add/remote/AddContactDialog.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/add/remote/AddContactDialog.kt index c497b390ef051a91ce401ae6b567e78cfc3d5d32..aab5f86a7dc9555cfee3de2c04009555f84caadd 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/add/remote/AddContactDialog.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/add/remote/AddContactDialog.kt @@ -362,7 +362,7 @@ fun OwnLink( } } -@OptIn(ExperimentalFoundationApi::class, ExperimentalComposeUiApi::class) +@OptIn(ExperimentalComposeUiApi::class) @Composable fun ContactLink( remoteHandshakeLink: String, @@ -394,55 +394,32 @@ fun ContactLink( letterSpacing = (-0.5).sp, ), trailingIcon = { - TooltipArea( - tooltip = { - Surface( - modifier = Modifier.shadow(4.dp), - shape = RoundedCornerShape(4.dp), - ) { - Text( - text = i18n("contact.add.remote.paste_tooltip"), - modifier = Modifier.padding(8.dp), - color = MaterialTheme.colors.onSurface, - ) + IconButton( + icon = Icons.Filled.ContentPaste, + iconTint = MaterialTheme.colors.onSurface, + contentDescription = i18n("contact.add.remote.paste_tooltip"), + onClick = { + val clipboardText = clipboardManager.getText().toString() + if (clipboardText.isNotEmpty()) { + setRemoteHandshakeLink(clipboardManager.getText().toString()) + coroutineScope.launch { + scaffoldState.snackbarHostState.showSnackbar( + message = i18n("contact.add.remote.link_pasted_snackbar"), + duration = SnackbarDuration.Short, + ) + } + aliasFocusRequester.requestFocus() + } else { + coroutineScope.launch { + scaffoldState.snackbarHostState.showSnackbar( + message = i18n("contact.add.remote.paste_error_snackbar"), + duration = SnackbarDuration.Short, + ) + } } }, - modifier = Modifier.padding(start = 30.dp), - delayMillis = 200, - tooltipPlacement = TooltipPlacement.ComponentRect( - alignment = Alignment.BottomCenter, - ) - ) { - IconButton( - { - val clipboardText = clipboardManager.getText().toString() - if (clipboardText.isNotEmpty()) { - setRemoteHandshakeLink(clipboardManager.getText().toString()) - coroutineScope.launch { - scaffoldState.snackbarHostState.showSnackbar( - message = i18n("contact.add.remote.link_pasted_snackbar"), - duration = SnackbarDuration.Short, - ) - } - aliasFocusRequester.requestFocus() - } else { - coroutineScope.launch { - scaffoldState.snackbarHostState.showSnackbar( - message = i18n("contact.add.remote.paste_error_snackbar"), - duration = SnackbarDuration.Short, - ) - } - } - }, - Modifier.pointerHoverIcon(PointerIconDefaults.Default) - ) { - Icon( - Icons.Filled.ContentPaste, - "contact.add.remote.paste_tooltip", - tint = MaterialTheme.colors.onSurface - ) - } - } + modifier = Modifier.pointerHoverIcon(PointerIconDefaults.Default) + ) } ) } diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationHeader.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationHeader.kt index a161b184b35a2ee179923fb6024bd90fbfff5fe7..eb6d737b942254902cd17f1688fdb8c45d2ae0ff 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationHeader.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationHeader.kt @@ -26,8 +26,6 @@ import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.material.Icon import androidx.compose.material.IconButton import androidx.compose.material.MaterialTheme import androidx.compose.material.Text @@ -92,10 +90,11 @@ fun ConversationHeader( ) } IconButton( + icon = Icons.Filled.MoreVert, + contentDescription = i18n("access.contact.menu"), onClick = { setMenuState(ContactDropDown.State.MAIN) }, modifier = Modifier.align(Alignment.CenterVertically).padding(end = 16.dp) ) { - Icon(Icons.Filled.MoreVert, i18n("access.contact.menu"), modifier = Modifier.size(24.dp)) ContactDropDown( menuState, setMenuState, diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationInput.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationInput.kt index ca555805176a263cca0b087ff4ee40e8be49f182..1787696cd4793c265fb8b2d21a18b104907d6969 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationInput.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationInput.kt @@ -27,7 +27,6 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.AlertDialog import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.material.Icon import androidx.compose.material.IconButton import androidx.compose.material.MaterialTheme import androidx.compose.material.Text @@ -115,6 +114,8 @@ fun ConversationInput( leadingIcon = { val windowScope = LocalWindowScope.current!! ColoredIconButton( + icon = if (image == null) Icons.Filled.Add else Icons.Filled.Close, + contentDescription = if (image == null) i18n("access.attachment_add") else i18n("access.attachment_remove"), onClick = { if (image == null) { pickImageUsingDialog(windowScope.window, updateImage) @@ -122,27 +123,17 @@ fun ConversationInput( updateImage(null) } }, - Modifier.padding(4.dp), - ) { - if (image == null) { - Icon(Icons.Filled.Add, i18n("access.attachment_add"), Modifier.size(24.dp)) - } else { - Icon(Icons.Filled.Close, i18n("access.attachment_remove"), Modifier.size(24.dp)) - } - } + modifier = Modifier.padding(4.dp), + ) }, trailingIcon = { IconButton( + icon = Icons.Filled.Send, + iconTint = MaterialTheme.colors.sendButton, + contentDescription = i18n("access.message.send"), onClick = onSend, modifier = Modifier.padding(4.dp).size(32.dp).pointerHoverIcon(PointerIconDefaults.Default), - ) { - Icon( - Icons.Filled.Send, - i18n("access.message.send"), - tint = MaterialTheme.colors.sendButton, - modifier = Modifier.size(24.dp) - ) - } + ) } ) } diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/conversation/PrivateMessageScreen.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/conversation/PrivateMessageScreen.kt index 1b374b6dc80e2c05342d6d3c4ab8da2031b0b400..67511a08d261cf4248df9a67f210356554ce04b8 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/conversation/PrivateMessageScreen.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/conversation/PrivateMessageScreen.kt @@ -18,7 +18,6 @@ package org.briarproject.briar.desktop.conversation -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -28,9 +27,6 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.widthIn -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.Icon -import androidx.compose.material.IconButton import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.material.icons.Icons @@ -38,7 +34,6 @@ import androidx.compose.material.icons.filled.PersonAdd import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import org.briarproject.briar.desktop.contact.ConfirmRemovePendingContactDialog @@ -49,6 +44,7 @@ import org.briarproject.briar.desktop.contact.RealContactIdWrapper import org.briarproject.briar.desktop.contact.add.remote.AddContactDialog import org.briarproject.briar.desktop.contact.add.remote.AddContactViewModel import org.briarproject.briar.desktop.ui.BriarLogo +import org.briarproject.briar.desktop.ui.ColoredIconButton import org.briarproject.briar.desktop.ui.Constants.PARAGRAPH_WIDTH import org.briarproject.briar.desktop.ui.VerticalDivider import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n @@ -104,18 +100,12 @@ fun NoContactsYet(onContactAdd: () -> Unit) = Explainer( headline = i18n("welcome.title"), text = i18n("welcome.text"), ) { - IconButton( + ColoredIconButton( + icon = Icons.Filled.PersonAdd, + iconSize = 20.dp, + contentDescription = i18n("access.contacts.add"), onClick = onContactAdd, - modifier = Modifier.padding(end = 10.dp).size(32.dp) - .background(MaterialTheme.colors.primary, CircleShape) - ) { - Icon( - Icons.Filled.PersonAdd, - i18n("access.contacts.add"), - tint = Color.White, - modifier = Modifier.size(20.dp) - ) - } + ) } @Composable diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/expiration/ExpirationBanner.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/expiration/ExpirationBanner.kt index a2b206d18a77939f1bc1702b0ce5bb42b95355cf..78a179ea7829394a66f4b71d8c8bcb6ab7f99d69 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/expiration/ExpirationBanner.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/expiration/ExpirationBanner.kt @@ -108,8 +108,11 @@ fun ExpirationBanner( style = MaterialTheme.typography.body2, modifier = Modifier.weight(1f, true).padding(vertical = 12.dp) ) - IconButton(hide, modifier = Modifier.padding(vertical = 4.dp)) { - Icon(Icons.Filled.Close, i18n("hide"), Modifier.size(24.dp)) - } + IconButton( + icon = Icons.Filled.Close, + contentDescription = i18n("hide"), + onClick = hide, + modifier = Modifier.padding(vertical = 4.dp) + ) } } diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/introduction/ContactDrawerMakeIntro.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/introduction/ContactDrawerMakeIntro.kt index ea12210b752777f5bb4ea3b2f8658df28a536cbe..54b0b9ce8b2d99867dd01acd788e8b5b60aa5568 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/introduction/ContactDrawerMakeIntro.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/introduction/ContactDrawerMakeIntro.kt @@ -70,11 +70,11 @@ fun ContactDrawerMakeIntro( if (!viewModel.secondScreen.value) { Row(Modifier.fillMaxWidth().height(HEADER_SIZE)) { IconButton( + icon = Icons.Filled.Close, + contentDescription = i18n("access.introduction.close"), onClick = { closeInfoDrawer(false) }, - Modifier.padding(start = 24.dp).size(24.dp).align(Alignment.CenterVertically) - ) { - Icon(Icons.Filled.Close, i18n("access.introduction.close")) - } + modifier = Modifier.padding(start = 24.dp).size(24.dp).align(Alignment.CenterVertically) + ) Text( text = i18nF("introduction.title_first", contactItem.displayName), modifier = Modifier.align(Alignment.CenterVertically).padding(start = 16.dp), @@ -97,11 +97,11 @@ fun ContactDrawerMakeIntro( } else { Row(Modifier.fillMaxWidth().height(HEADER_SIZE)) { IconButton( + icon = Icons.Filled.ArrowBack, + contentDescription = i18n("access.introduction.back.contact"), onClick = viewModel::backToFirstScreen, - Modifier.padding(start = 24.dp).size(24.dp).align(Alignment.CenterVertically) - ) { - Icon(Icons.Filled.ArrowBack, i18n("access.introduction.back.contact")) - } + modifier = Modifier.padding(start = 24.dp).size(24.dp).align(Alignment.CenterVertically) + ) Text( text = i18n("introduction.title_second"), modifier = Modifier.align(Alignment.CenterVertically).padding(start = 16.dp), diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/login/AboutSubViewModel.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/login/AboutSubViewModel.kt new file mode 100644 index 0000000000000000000000000000000000000000..63e2bfc697bef2bae09448b4218dc340af0e4df6 --- /dev/null +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/login/AboutSubViewModel.kt @@ -0,0 +1,23 @@ +/* + * Briar Desktop + * Copyright (C) 2021-2022 The Briar Project + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.briarproject.briar.desktop.login + +class AboutSubViewModel( + val onBackButton: () -> Unit, +) : StartupViewModel.SubViewModel diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/login/ErrorScreen.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/login/ErrorScreen.kt index adf541b9120046b5674a6e68756611baab2b4c4d..3439a6694acf72a99d72702c1226e39bcd4f1d31 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/login/ErrorScreen.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/login/ErrorScreen.kt @@ -53,6 +53,7 @@ import org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.DB_ER import org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SERVICE_ERROR import org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SUCCESS import org.briarproject.briar.desktop.theme.Red500 +import org.briarproject.briar.desktop.ui.AboutScreen import org.briarproject.briar.desktop.ui.Constants.STARTUP_FIELDS_WIDTH import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n import org.briarproject.briar.desktop.utils.PreviewUtils.preview @@ -72,19 +73,17 @@ fun main() = preview { } } - ErrorScreen(error, {}) {} + ErrorScreen(error) {} } @Composable fun ErrorScreen( - onShowAbout: () -> Unit, viewHolder: ErrorSubViewModel -) = ErrorScreen(viewHolder.error, onShowAbout, viewHolder.onBackButton) +) = ErrorScreen(viewHolder.error, viewHolder.onBackButton) @Composable fun ErrorScreen( error: ErrorSubViewModel.Error, - onShowAbout: () -> Unit, onBackButton: (() -> Unit)?, ) { val text = when (error) { @@ -101,45 +100,52 @@ fun ErrorScreen( } } - ErrorScreen(text, onShowAbout, onBackButton) + ErrorScreen(text, onBackButton) } @Composable fun ErrorScreen( text: String, - onShowAbout: () -> Unit, onBackButton: (() -> Unit)? = null, ) = Box { - Column( - modifier = Modifier.fillMaxSize().padding(32.dp), - horizontalAlignment = CenterHorizontally, - verticalArrangement = spacedBy(32.dp) - ) { - Icon( - imageVector = Icons.Filled.Error, - contentDescription = i18n("error"), - modifier = Modifier.size(128.dp), - tint = Red500 - ) + var showAbout by remember { mutableStateOf(false) } - Text(i18n("sorry"), style = MaterialTheme.typography.h5) - Text( - text = text, - style = MaterialTheme.typography.body1, - modifier = Modifier.widthIn(max = STARTUP_FIELDS_WIDTH) - ) - } + if (showAbout) { + AboutScreen { showAbout = false } + } else { + Column( + modifier = Modifier.fillMaxSize().padding(32.dp), + horizontalAlignment = CenterHorizontally, + verticalArrangement = spacedBy(32.dp) + ) { + Icon( + imageVector = Icons.Filled.Error, + contentDescription = i18n("error"), + modifier = Modifier.size(128.dp), + tint = Red500 + ) - if (onBackButton != null) { - IconButton(onClick = onBackButton) { - Icon(Icons.Filled.ArrowBack, i18n("access.return_to_previous_screen")) + Text(i18n("sorry"), style = MaterialTheme.typography.h5) + Text( + text = text, + style = MaterialTheme.typography.body1, + modifier = Modifier.widthIn(max = STARTUP_FIELDS_WIDTH) + ) } - } - IconButton( - onClick = onShowAbout, - modifier = Modifier.align(Alignment.BottomStart) - ) { - Icon(Icons.Filled.Info, i18n("access.about_briar_desktop")) + if (onBackButton != null) { + IconButton( + icon = Icons.Filled.ArrowBack, + contentDescription = i18n("access.return_to_previous_screen"), + onClick = onBackButton + ) + } + + IconButton( + icon = Icons.Filled.Info, + contentDescription = i18n("access.mode.about"), + onClick = { showAbout = true }, + modifier = Modifier.align(Alignment.BottomStart) + ) } } diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/login/StartupScreen.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/login/StartupScreen.kt index e03d14f2835de651b5392cd962f0a0248c59261f..122df268c848f34ab482997d1204da1b15139b16 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/login/StartupScreen.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/login/StartupScreen.kt @@ -25,7 +25,6 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width -import androidx.compose.material.Icon import androidx.compose.material.IconButton import androidx.compose.material.MaterialTheme import androidx.compose.material.Text @@ -38,19 +37,20 @@ import androidx.compose.ui.Alignment.Companion.CenterHorizontally import androidx.compose.ui.Alignment.Companion.CenterVertically import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import org.briarproject.briar.desktop.ui.AboutScreen import org.briarproject.briar.desktop.ui.BriarLogo import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n import org.briarproject.briar.desktop.viewmodel.viewModel @Composable fun StartupScreen( - onShowAbout: () -> Unit, viewModel: StartupViewModel = viewModel(), ) { when (val holder = viewModel.currentSubViewModel.value) { - is LoginSubViewModel -> LoginScreen(onShowAbout, holder) - is RegistrationSubViewModel -> RegistrationScreen(onShowAbout, holder) - is ErrorSubViewModel -> ErrorScreen(onShowAbout, holder) + is LoginSubViewModel -> LoginScreen(viewModel::showAbout, holder) + is RegistrationSubViewModel -> RegistrationScreen(viewModel::showAbout, holder) + is ErrorSubViewModel -> ErrorScreen(holder) + is AboutSubViewModel -> AboutScreen(holder.onBackButton) } } @@ -72,19 +72,19 @@ fun StartupScreenScaffold( if (showBackButton) { IconButton( + icon = Icons.Filled.ArrowBack, + contentDescription = i18n("access.return_to_previous_screen"), onClick = onBackButton, modifier = Modifier.align(Alignment.TopStart) - ) { - Icon(Icons.Filled.ArrowBack, i18n("access.return_to_previous_screen")) - } + ) } IconButton( + icon = Icons.Filled.Info, + contentDescription = i18n("access.mode.about"), onClick = onShowAbout, modifier = Modifier.align(Alignment.BottomStart) - ) { - Icon(Icons.Filled.Info, i18n("access.about_briar_desktop")) - } + ) } @Composable diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/login/StartupViewModel.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/login/StartupViewModel.kt index 21af167213a438998facd91090c13c9611617ad6..a51ba875fe9fb08d3097be14f36f8b11c970a438 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/login/StartupViewModel.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/login/StartupViewModel.kt @@ -90,6 +90,13 @@ constructor( _currentSubViewModel.value = makeError(error) } + private fun makeAbout(previous: SubViewModel) = + AboutSubViewModel { _currentSubViewModel.value = previous } + + fun showAbout() { + _currentSubViewModel.value = makeAbout(_currentSubViewModel.value) + } + override fun eventOccurred(e: Event) { if (e is LifecycleEvent) _currentSubViewModel.value.lifecycleStateChanged(e.lifecycleState) } diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/navigation/BriarSidebar.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/navigation/BriarSidebar.kt index fe080e6eade164bfb2f7089b6dd4e1b83a62c82d..6d5bdd8b69b382c798525b3140778c56909ee140 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/navigation/BriarSidebar.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/navigation/BriarSidebar.kt @@ -23,20 +23,10 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width -import androidx.compose.material.Icon import androidx.compose.material.IconButton import androidx.compose.material.MaterialTheme import androidx.compose.material.Surface -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ChromeReaderMode -import androidx.compose.material.icons.filled.Contacts -import androidx.compose.material.icons.filled.Forum -import androidx.compose.material.icons.filled.Group -import androidx.compose.material.icons.filled.Info -import androidx.compose.material.icons.filled.Settings -import androidx.compose.material.icons.filled.WifiTethering import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -56,17 +46,7 @@ fun BriarSidebar( account: LocalAuthor?, uiMode: UiMode, setUiMode: (UiMode) -> Unit, - showAbout: () -> Unit, ) { - val displayButton = @Composable { selectedMode: UiMode, mode: UiMode, icon: ImageVector -> - BriarSidebarButton( - selectedMode == mode, - { setUiMode(mode) }, - icon, - mode.toString() - ) - } - Surface(modifier = Modifier.width(SIDEBAR_WIDTH).fillMaxHeight(), color = MaterialTheme.colors.sidebarSurface) { Column(verticalArrangement = Arrangement.Top) { // profile button @@ -75,45 +55,65 @@ fun BriarSidebar( ) { account?.let { ProfileCircle(size = 45.dp, it.id.bytes) } } - val items = buildList { - add(Pair(UiMode.CONTACTS, Icons.Filled.Contacts)) + val modes = buildList { + add(UiMode.CONTACTS) val featureFlags = getDesktopFeatureFlags() - if (featureFlags.shouldEnablePrivateGroups()) add(Pair(UiMode.GROUPS, Icons.Filled.Group)) - if (featureFlags.shouldEnableForums()) add(Pair(UiMode.FORUMS, Icons.Filled.Forum)) - if (featureFlags.shouldEnableBlogs()) add(Pair(UiMode.BLOGS, Icons.Filled.ChromeReaderMode)) + if (featureFlags.shouldEnablePrivateGroups()) add(UiMode.GROUPS) + if (featureFlags.shouldEnableForums()) add(UiMode.FORUMS) + if (featureFlags.shouldEnableBlogs()) add(UiMode.BLOGS) } - for ((mode, icon) in items) { - displayButton(uiMode, mode, icon) + modes.forEach { mode -> + BriarSidebarButton( + currentMode = uiMode, + mode = mode, + setUiMode = setUiMode, + ) } } Column(verticalArrangement = Arrangement.Bottom) { - val items = buildList { + val modes = buildList { val featureFlags = getDesktopFeatureFlags() - if (featureFlags.shouldEnableTransportSettings()) add( - Pair(UiMode.TRANSPORTS, Icons.Filled.WifiTethering) - ) - add(Pair(UiMode.SETTINGS, Icons.Filled.Settings)) + if (featureFlags.shouldEnableTransportSettings()) add(UiMode.TRANSPORTS) + add(UiMode.SETTINGS) + add(UiMode.ABOUT) } - for ((mode, icon) in items) { - displayButton(uiMode, mode, icon) + modes.forEach { mode -> + BriarSidebarButton( + currentMode = uiMode, + mode = mode, + setUiMode = setUiMode, + ) } - BriarSidebarButton( - selected = false, - onClick = showAbout, - icon = Icons.Filled.Info, - contentDescription = i18n("access.about_briar_desktop") - ) } } } @Composable -fun BriarSidebarButton(selected: Boolean, onClick: () -> Unit, icon: ImageVector, contentDescription: String?) { +fun BriarSidebarButton( + mode: UiMode, + currentMode: UiMode, + setUiMode: (UiMode) -> Unit, +) = BriarSidebarButton( + currentMode == mode, + { setUiMode(mode) }, + mode.icon, + i18n(mode.contentDescriptionKey) +) + +@Composable +fun BriarSidebarButton( + selected: Boolean, + onClick: () -> Unit, + icon: ImageVector, + contentDescription: String, +) { val tint = if (selected) MaterialTheme.colors.primary else MaterialTheme.colors.onSurface IconButton( + icon = icon, + iconSize = 30.dp, + iconTint = tint, + contentDescription = contentDescription, + onClick = onClick, modifier = Modifier.padding(vertical = 4.dp, horizontal = 12.dp), - onClick = onClick - ) { - Icon(icon, contentDescription, tint = tint, modifier = Modifier.size(30.dp)) - } + ) } diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/AboutDialog.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/AboutDialog.kt deleted file mode 100644 index 5899d87ba3aa427a1a87305c6608aeff4239f598..0000000000000000000000000000000000000000 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/AboutDialog.kt +++ /dev/null @@ -1,128 +0,0 @@ -package org.briarproject.briar.desktop.ui - -import androidx.compose.foundation.VerticalScrollbar -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.IntrinsicSize -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.requiredSize -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items -import androidx.compose.foundation.lazy.rememberLazyListState -import androidx.compose.foundation.rememberScrollbarAdapter -import androidx.compose.foundation.text.selection.SelectionContainer -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.unit.dp -import org.briarproject.briar.desktop.BuildData -import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n -import org.briarproject.briar.desktop.utils.PreviewUtils.preview -import java.time.Instant -import java.time.ZoneId -import java.time.format.DateTimeFormatter - -@Suppress("HardCodedStringLiteral") -fun main() = preview( - "visible" to true, -) { - if (getBooleanParameter("visible")) { - AboutDialog( - onClose = { setBooleanParameter("visible", false) }, - ) - } -} - -@Composable -fun AboutDialog( - onClose: () -> Unit, -) { - // sizes of the two columns - val colSizes = listOf(0.3f, 0.7f) - - // format date - val buildTime = Instant.ofEpochMilli(BuildData.GIT_TIME).atZone(ZoneId.systemDefault()).toLocalDateTime() - - // rows displayed in table - val lines = buildList> { - add(i18n("about.copyright") to "The Briar Project") // NON-NLS - add(i18n("about.license") to "GNU Affero General Public License v3") // NON-NLS - add(i18n("about.version") to BuildData.VERSION) - add(i18n("about.version.core") to BuildData.CORE_VERSION) - if (BuildData.GIT_BRANCH != null) add("Git branch" to BuildData.GIT_BRANCH) // NON-NLS - if (BuildData.GIT_TAG != null) add("Git tag" to BuildData.GIT_TAG) // NON-NLS - if (BuildData.GIT_BRANCH == null && BuildData.GIT_TAG == null) add("Git branch/tag" to "None detected") // NON-NLS - add("Git hash" to BuildData.GIT_HASH) // NON-NLS - add("Commit time" to DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(buildTime)) // NON-NLS - add(i18n("about.website") to "https://briarproject.org") - add(i18n("about.contact") to "desktop@briarproject.org") // NON-NLS - } - - BriarDialog(onClose = onClose) { - val box = this - Column( - modifier = Modifier.requiredSize( - box.maxWidth.times(0.8f), box.maxHeight.times(0.8f) - ).padding(16.dp) - ) { - Row( - modifier = Modifier.padding(bottom = 8.dp).fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Center - ) { - BriarLogo(modifier = Modifier.height(48.dp)) - Text( - i18n("main.title"), - style = MaterialTheme.typography.h4, - modifier = Modifier.padding(start = 16.dp), - overflow = TextOverflow.Ellipsis, - maxLines = 1, - ) - } - val scrollState = rememberLazyListState() - Box { - LazyColumn(state = scrollState) { - item { - HorizontalDivider() - } - items(lines) { (key, value) -> - // this is required for Divider between Boxes to have appropriate size - Row(Modifier.fillMaxWidth().height(IntrinsicSize.Min)) { - Box(modifier = Modifier.weight(colSizes[0]).fillMaxHeight()) { - Text( - text = key, - modifier = Modifier.padding(horizontal = 8.dp) - .padding(vertical = 8.dp).padding(end = 8.dp) - .align(Alignment.CenterStart) - ) - } - VerticalDivider() - Box(modifier = Modifier.weight(colSizes[1]).fillMaxHeight()) { - SelectionContainer { - Text( - text = value, - modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp) - .padding(start = 8.dp) - ) - } - } - } - HorizontalDivider() - } - } - VerticalScrollbar( - adapter = rememberScrollbarAdapter(scrollState), - modifier = Modifier.align(Alignment.CenterEnd).fillMaxHeight() - ) - } - } - } -} diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/AboutScreen.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/AboutScreen.kt new file mode 100644 index 0000000000000000000000000000000000000000..113e670133c4ae1eca9ce4c02268036a0eb31184 --- /dev/null +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/AboutScreen.kt @@ -0,0 +1,182 @@ +package org.briarproject.briar.desktop.ui + +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.VerticalScrollbar +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.IntrinsicSize +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.foundation.rememberScrollbarAdapter +import androidx.compose.foundation.text.selection.SelectionContainer +import androidx.compose.material.IconButton +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.filled.ContentCopy +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalClipboardManager +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.semantics.text +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import org.briarproject.briar.desktop.BuildData +import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n +import org.briarproject.briar.desktop.utils.PreviewUtils.preview +import java.time.Instant +import java.time.ZoneId +import java.time.format.DateTimeFormatter + +@Suppress("HardCodedStringLiteral") +fun main() = preview( + "visible" to true, +) { + if (getBooleanParameter("visible")) { + AboutScreen( + onBackButton = { setBooleanParameter("visible", false) }, + ) + } +} + +@Composable +fun AboutScreen( + onBackButton: () -> Unit, +) = Box { + AboutScreen() + + IconButton( + icon = Icons.Filled.ArrowBack, + contentDescription = i18n("access.return_to_previous_screen"), + onClick = onBackButton, + modifier = Modifier.align(Alignment.TopStart) + ) +} + +@OptIn(ExperimentalFoundationApi::class) +@Composable +fun AboutScreen(modifier: Modifier = Modifier.padding(16.dp)) { + // format date + val buildTime = Instant.ofEpochMilli(BuildData.GIT_TIME).atZone(ZoneId.systemDefault()).toLocalDateTime() + + // rows displayed in table + val lines = buildList { + add(Entry(i18n("about.copyright"), "The Briar Project")) // NON-NLS + add(Entry(i18n("about.license"), "GNU Affero General Public License v3")) // NON-NLS + add(Entry(i18n("about.version"), BuildData.VERSION)) + add(Entry(i18n("about.version.core"), BuildData.CORE_VERSION)) + if (BuildData.GIT_BRANCH != null) add(Entry("Git branch", BuildData.GIT_BRANCH)) // NON-NLS + if (BuildData.GIT_TAG != null) add(Entry("Git tag", BuildData.GIT_TAG)) // NON-NLS + if (BuildData.GIT_BRANCH == null && BuildData.GIT_TAG == null) + add(Entry("Git branch/tag", "None detected")) // NON-NLS + add(Entry("Git hash", BuildData.GIT_HASH)) // NON-NLS + add(Entry("Commit time", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(buildTime))) // NON-NLS + add(Entry(i18n("about.website"), "https://briarproject.org", true)) + add(Entry(i18n("about.contact"), "desktop@briarproject.org", true)) // NON-NLS + } + + Column(modifier) { + Row( + modifier = Modifier.padding(bottom = 16.dp).fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center + ) { + BriarLogo(modifier = Modifier.height(48.dp)) + Text( + i18n("main.title"), + style = MaterialTheme.typography.h4, + modifier = Modifier.padding(start = 16.dp), + overflow = TextOverflow.Ellipsis, + maxLines = 1, + ) + } + val scrollState = rememberLazyListState() + Box { + LazyColumn( + modifier = Modifier.semantics { + contentDescription = i18n("access.about.list") + }, + state = scrollState + ) { + item { + HorizontalDivider() + } + items(lines) { + AboutEntry(it) + HorizontalDivider() + } + } + VerticalScrollbar( + adapter = rememberScrollbarAdapter(scrollState), + modifier = Modifier.align(Alignment.CenterEnd).fillMaxHeight() + ) + } + } +} + +private data class Entry( + val label: String, + val value: String, + val showCopy: Boolean = false +) + +// sizes of the two columns +private val colSizes = listOf(0.3f, 0.7f) + +@Composable +private fun AboutEntry(entry: Entry) = + Row( + Modifier + .fillMaxWidth() + // this is required for Divider between Boxes to have appropriate size + .height(IntrinsicSize.Min) + .semantics(mergeDescendants = true) { + // manual text setting can be removed if Compose issue resolved + // https://github.com/JetBrains/compose-jb/issues/2111 + text = buildAnnotatedString { append("${entry.label}: ${entry.value}") } + } + ) { + Box(modifier = Modifier.weight(colSizes[0]).fillMaxHeight()) { + Text( + text = entry.label, + modifier = Modifier.padding(8.dp).align(Alignment.CenterStart) + ) + } + VerticalDivider() + Box(modifier = Modifier.weight(colSizes[1]).fillMaxHeight()) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + SelectionContainer { + Text( + text = entry.value, + modifier = Modifier.padding(8.dp) + ) + } + if (entry.showCopy) { + val clipboardManager = LocalClipboardManager.current + IconButton( + icon = Icons.Filled.ContentCopy, + contentDescription = i18n("copy"), + onClick = { + clipboardManager.setText(AnnotatedString(entry.value)) + } + ) + } + } + } + } diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarDialog.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarDialog.kt deleted file mode 100644 index f2929d55169c2b67b606b308889c0fe11333c7ae..0000000000000000000000000000000000000000 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarDialog.kt +++ /dev/null @@ -1,46 +0,0 @@ -package org.briarproject.briar.desktop.ui - -import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.BoxWithConstraints -import androidx.compose.foundation.layout.BoxWithConstraintsScope -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.requiredSize -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Surface -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp - -@OptIn(ExperimentalMaterialApi::class, ExperimentalFoundationApi::class) -@Composable -fun BriarDialog( - onClose: () -> Unit, - content: @Composable BoxWithConstraintsScope.() -> Unit, -) { - BoxWithConstraints(Modifier.fillMaxSize()) { - // This adds a scrim that dims the background to make the dialog stand out visually - Box( - modifier = Modifier.requiredSize(maxWidth, maxHeight) - .background(MaterialTheme.colors.onSurface.copy(alpha = 0.32f)) - .clickable( - // prevent visual indication - interactionSource = remember { MutableInteractionSource() }, - indication = null - ) { - onClose() - } - ) - - Surface(modifier = Modifier.align(Alignment.Center), shape = RoundedCornerShape(8.dp)) { - content() - } - } -} diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt index 5f0f7eb729953b588418a07a4fb99e12f73a1162..cb7000b2706d0cba532ce6b6f6bfc57007a3fbc1 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt @@ -178,21 +178,17 @@ constructor( return@CompositionLocalProvider } - var showAbout by remember { mutableStateOf(false) } val isDarkTheme = unencryptedSettings.theme == DARK || (unencryptedSettings.theme == AUTO && isSystemInDarkTheme()) BriarTheme(isDarkTheme) { Column(Modifier.fillMaxSize()) { ExpirationBanner { screenState = EXPIRED; stop() } when (screenState) { - STARTUP -> StartupScreen(onShowAbout = { showAbout = true }) - MAIN -> MainScreen(onShowAbout = { showAbout = true }) - EXPIRED -> ErrorScreen(i18n("startup.failed.expired"), onShowAbout = { showAbout = true }) + STARTUP -> StartupScreen() + MAIN -> MainScreen() + EXPIRED -> ErrorScreen(i18n("startup.failed.expired")) } } - if (showAbout) { - AboutDialog(onClose = { showAbout = false }) - } } } } diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/ColoredIconButton.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/ColoredIconButton.kt index f47501cf9c84e8ce7630c07d24e78c2d6ee000d4..870c8ce0a492e9718afc8d43704568d8f814ab36 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/ColoredIconButton.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/ColoredIconButton.kt @@ -33,8 +33,10 @@ import androidx.compose.runtime.remember import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.input.pointer.PointerIconDefaults import androidx.compose.ui.input.pointer.pointerHoverIcon +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp @OptIn(ExperimentalComposeUiApi::class) @@ -62,3 +64,29 @@ fun ColoredIconButton( content = content ) } + +@OptIn(ExperimentalComposeUiApi::class) +@Composable +fun ColoredIconButton( + icon: ImageVector, + contentDescription: String, + onClick: () -> Unit, + modifier: Modifier = Modifier, + iconSize: Dp = 24.dp, + color: Color = MaterialTheme.colors.primary, + contentColor: Color = contentColorFor(color), + enabled: Boolean = true, + interactionSource: MutableInteractionSource = remember { MutableInteractionSource() } +) = IconButton( + icon = icon, + contentDescription = contentDescription, + onClick = onClick, + modifier = modifier + .pointerHoverIcon(PointerIconDefaults.Default) + .background(color, CircleShape) + .then(Modifier.size(32.dp)), + iconSize = iconSize, + iconTint = contentColor, + enabled = enabled, + interactionSource = interactionSource +) diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/MainScreen.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/MainScreen.kt index 48581bd3217a070476f52c40e78ea669ffd9e7f6..582ab6ab4c499692feef548752b464a2c88243c5 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/MainScreen.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/MainScreen.kt @@ -33,22 +33,19 @@ import org.briarproject.briar.desktop.viewmodel.viewModel * Multiplatform, stateless, composable are found in briarCompose (possible briar-compose project in the future) */ @Composable -fun MainScreen( - viewModel: SidebarViewModel = viewModel(), - onShowAbout: () -> Unit, -) { +fun MainScreen(viewModel: SidebarViewModel = viewModel()) { Row { BriarSidebar( viewModel.account.value, viewModel.uiMode.value, viewModel::setUiMode, - showAbout = onShowAbout, ) VerticalDivider() when (viewModel.uiMode.value) { UiMode.CONTACTS -> PrivateMessageScreen() UiMode.GROUPS -> PrivateGroupScreen() UiMode.SETTINGS -> SettingsScreen() + UiMode.ABOUT -> AboutScreen() else -> UiPlaceholder() } } diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/UiMode.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/UiMode.kt index 414966fb5347f4c1a2e83300f586fa244bb450f4..10a464edbea9cfb4230f02a92d64dd51079dc791 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/UiMode.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/UiMode.kt @@ -18,11 +18,22 @@ package org.briarproject.briar.desktop.ui -enum class UiMode { - CONTACTS, - GROUPS, - FORUMS, - BLOGS, - TRANSPORTS, - SETTINGS, +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ChromeReaderMode +import androidx.compose.material.icons.filled.Contacts +import androidx.compose.material.icons.filled.Forum +import androidx.compose.material.icons.filled.Group +import androidx.compose.material.icons.filled.Info +import androidx.compose.material.icons.filled.Settings +import androidx.compose.material.icons.filled.WifiTethering +import androidx.compose.ui.graphics.vector.ImageVector + +enum class UiMode(val icon: ImageVector, val contentDescriptionKey: String) { + CONTACTS(Icons.Filled.Contacts, "access.mode.contacts"), + GROUPS(Icons.Filled.Group, "access.mode.groups"), + FORUMS(Icons.Filled.Forum, "access.mode.forums"), + BLOGS(Icons.Filled.ChromeReaderMode, "access.mode.blogs"), + TRANSPORTS(Icons.Filled.WifiTethering, "access.mode.transports"), + SETTINGS(Icons.Filled.Settings, "access.mode.settings"), + ABOUT(Icons.Filled.Info, "access.mode.about"), } diff --git a/briar-desktop/src/main/resources/strings/BriarDesktop.properties b/briar-desktop/src/main/resources/strings/BriarDesktop.properties index f95ee998a6a978c1eed4c6eb8572f75db7707144..d4f2c7da2a33bce5ca73011fe0e9e1d2ce4d070d 100644 --- a/briar-desktop/src/main/resources/strings/BriarDesktop.properties +++ b/briar-desktop/src/main/resources/strings/BriarDesktop.properties @@ -14,7 +14,14 @@ access.message.send=Send message access.message.sent=Message sent access.logo=Briar logo access.swap=Icon showing errors between two contacts -access.about_briar_desktop=About Briar Desktop +access.mode.contacts=Contacts +access.mode.groups=Private Groups +access.mode.forums=Forums +access.mode.blogs=Blogs +access.mode.transports=Transport Settings +access.mode.settings=Settings +access.mode.about=About Briar Desktop +access.about.list=Information about your version of Briar Desktop, the Briar Project in general and how to get in touch access.password.show=Show password access.password.hide=Hide password access.settings.current_value=Current value