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 5442901fd7eab790c64078204c0c3342950f06ae..43795303df5094fbec9d930f6cdc372fc21ce62a 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 @@ -1,6 +1,6 @@ /* * Briar Desktop - * Copyright (C) 2021-2022 The Briar Project + * Copyright (C) 2021-2023 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 @@ -113,7 +113,6 @@ const val link = "briar://ady23gvb2r76afe5zhxh5kvnh4b22zrcnxibn63tfknrdcwrw7zrs" @Suppress("HardCodedStringLiteral") fun main() = preview( - "visible" to true, "remote link" to "", "local link" to link, "alias" to "Alice", @@ -132,9 +131,8 @@ fun main() = preview( ), ) { val localLink = getStringParameter("local link") - AddContactDialog( - onClose = { setBooleanParameter("visible", false) }, - visible = getBooleanParameter("visible"), + AddContactDialogContent( + onClose = {}, remoteHandshakeLink = getStringParameter("remote link"), setRemoteHandshakeLink = { link -> setStringParameter("remote link", link) }, alias = getStringParameter("alias"), @@ -166,33 +164,9 @@ private fun PreviewUtils.PreviewScope.mapErrors(name: String?): AddContactError? @Composable fun AddContactDialog( - viewModel: AddContactViewModel, -) = AddContactDialog( - viewModel::dismissDialog, - viewModel.visible.value, - viewModel.remoteHandshakeLink.value, - viewModel::setRemoteHandshakeLink, - viewModel.alias.value, - viewModel::setAddContactAlias, - viewModel.handshakeLink.value, - viewModel::onSubmitAddContactDialog, - viewModel.error.value, - viewModel::clearError, -) - -@OptIn(ExperimentalMaterialApi::class, ExperimentalFoundationApi::class) -@Composable -fun AddContactDialog( - onClose: () -> Unit, visible: Boolean, - remoteHandshakeLink: String, - setRemoteHandshakeLink: (String) -> Unit, - alias: String, - setAddContactAlias: (String) -> Unit, - handshakeLink: String, - onSubmitAddContactDialog: () -> Unit, - error: AddContactError?, - onErrorDialogDismissed: () -> Unit, + onClose: () -> Unit, + viewModel: AddContactViewModel = viewModel(), ) { if (!visible) { return @@ -208,74 +182,99 @@ fun AddContactDialog( CompositionLocalProvider(LocalDensity provides density) { window.minimumSize = DensityDimension(360, 512) window.preferredSize = DensityDimension(520, 512) - val clipboardManager = LocalClipboardManager.current - val scaffoldState = rememberScaffoldState() - val coroutineScope = rememberCoroutineScope() - val aliasFocusRequester = remember { FocusRequester() } - Surface { - Scaffold( - modifier = Modifier.padding(horizontal = 24.dp).padding(top = 24.dp, bottom = 12.dp), - topBar = { - Box(Modifier.fillMaxWidth()) { - Text( - i18n("contact.add.remote.title"), - style = MaterialTheme.typography.h6, - modifier = Modifier.padding(bottom = 12.dp) - ) - } - }, - scaffoldState = scaffoldState, - content = { - Column(Modifier.fillMaxSize()) { - if (error != null) { - AddContactErrorDialog(error, onErrorDialogDismissed) - } - OwnLink( - handshakeLink, - clipboardManager, - coroutineScope, - scaffoldState, - ) - ContactLink( - remoteHandshakeLink, - setRemoteHandshakeLink, - clipboardManager, - coroutineScope, - scaffoldState, - aliasFocusRequester, - ) - Alias( - alias, - setAddContactAlias, - aliasFocusRequester, - onSubmitAddContactDialog, - ) + AddContactDialogContent( + onClose, + viewModel.remoteHandshakeLink.value, + viewModel::setRemoteHandshakeLink, + viewModel.alias.value, + viewModel::setAddContactAlias, + viewModel.handshakeLink.value, + { viewModel.onSubmitAddContactDialog(onClose) }, + viewModel.error.value, + viewModel::clearError, + ) + } + } +} + +@Composable +private fun AddContactDialogContent( + onClose: () -> Unit, + remoteHandshakeLink: String, + setRemoteHandshakeLink: (String) -> Unit, + alias: String, + setAddContactAlias: (String) -> Unit, + handshakeLink: String, + onSubmitAddContactDialog: () -> Unit, + error: AddContactError?, + onErrorDialogDismissed: () -> Unit, +) { + val clipboardManager = LocalClipboardManager.current + val scaffoldState = rememberScaffoldState() + val coroutineScope = rememberCoroutineScope() + val aliasFocusRequester = remember { FocusRequester() } + Surface { + Scaffold( + modifier = Modifier.padding(horizontal = 24.dp).padding(top = 24.dp, bottom = 12.dp), + topBar = { + Box(Modifier.fillMaxWidth()) { + Text( + i18n("contact.add.remote.title"), + style = MaterialTheme.typography.h6, + modifier = Modifier.padding(bottom = 12.dp) + ) + } + }, + scaffoldState = scaffoldState, + content = { + Column(Modifier.fillMaxSize()) { + if (error != null) { + AddContactErrorDialog(error, onErrorDialogDismissed) + } + OwnLink( + handshakeLink, + clipboardManager, + coroutineScope, + scaffoldState, + ) + ContactLink( + remoteHandshakeLink, + setRemoteHandshakeLink, + clipboardManager, + coroutineScope, + scaffoldState, + aliasFocusRequester, + ) + Alias( + alias, + setAddContactAlias, + aliasFocusRequester, + onSubmitAddContactDialog, + ) + } + }, + bottomBar = { + Box(Modifier.fillMaxWidth()) { + Row(Modifier.align(Alignment.CenterEnd)) { + TextButton( + onClose, + colors = ButtonDefaults.textButtonColors(contentColor = MaterialTheme.colors.error) + ) { + Text(i18n("cancel")) } - }, - bottomBar = { - Box(Modifier.fillMaxWidth()) { - Row(Modifier.align(Alignment.CenterEnd)) { - TextButton( - onClose, - colors = ButtonDefaults.textButtonColors(contentColor = MaterialTheme.colors.error) - ) { - Text(i18n("cancel")) - } - Button(onSubmitAddContactDialog, modifier = Modifier.padding(start = 8.dp)) { - Text(i18n("add")) - } - } + Button(onSubmitAddContactDialog, modifier = Modifier.padding(start = 8.dp)) { + Text(i18n("add")) } - }, - ) - } - } + } + } + }, + ) } } @OptIn(ExperimentalMaterialApi::class) @Composable -fun AddContactErrorDialog(error: AddContactError, onErrorDialogDismissed: () -> Unit) { +private fun AddContactErrorDialog(error: AddContactError, onErrorDialogDismissed: () -> Unit) { val (type, title, message) = errorMessage(error) val (icon, color) = when (type) { WARNING -> Icons.Filled.Warning to Orange500 @@ -308,11 +307,11 @@ fun AddContactErrorDialog(error: AddContactError, onErrorDialogDismissed: () -> @OptIn(ExperimentalFoundationApi::class) @Composable -fun OwnLink( +private fun OwnLink( handshakeLink: String, clipboardManager: ClipboardManager, coroutineScope: CoroutineScope, - scaffoldState: ScaffoldState + scaffoldState: ScaffoldState, ) { Row(verticalAlignment = Alignment.CenterVertically) { Icon(Icons.Filled.NorthEast, "contact.add.remote.outgoing_arrow") @@ -367,7 +366,7 @@ fun OwnLink( @OptIn(ExperimentalComposeUiApi::class) @Composable -fun ContactLink( +private fun ContactLink( remoteHandshakeLink: String, setRemoteHandshakeLink: (String) -> Unit, clipboardManager: ClipboardManager, @@ -428,11 +427,11 @@ fun ContactLink( } @Composable -fun Alias( +private fun Alias( alias: String, setAddContactAlias: (String) -> Unit, aliasFocusRequester: FocusRequester, - onSubmitAddContactDialog: () -> Unit + onSubmitAddContactDialog: () -> Unit, ) { Row(verticalAlignment = Alignment.CenterVertically) { Icon(Icons.Filled.Person, "contact.add.remote.choose_nickname") @@ -454,7 +453,7 @@ fun Alias( ) } -fun errorMessage(error: AddContactError) = when (error) { +private fun errorMessage(error: AddContactError) = when (error) { is OwnLinkError -> Triple(ERROR, i18n("error"), i18n("contact.add.error.own_link")) is RemoteInvalidError -> Triple(ERROR, i18n("error"), i18n("contact.add.error.remote_invalid")) is AliasInvalidError -> Triple(ERROR, i18n("error"), i18n("contact.add.error.alias_invalid")) diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/add/remote/AddContactViewModel.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/add/remote/AddContactViewModel.kt index 486e39c1975a08073ffb067aecb665264b7f2751..0da354c91ba4f195da1541c49ebab2a3efe15a20 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/add/remote/AddContactViewModel.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/add/remote/AddContactViewModel.kt @@ -1,6 +1,6 @@ /* * Briar Desktop - * Copyright (C) 2021-2022 The Briar Project + * Copyright (C) 2021-2023 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 @@ -68,26 +68,16 @@ constructor( fetchHandshakeLink() } - private val _visible = mutableStateOf(false) private val _alias = mutableStateOf("") private val _remoteHandshakeLink = mutableStateOf("") private val _handshakeLink = mutableStateOf("") private val _error = mutableStateOf<AddContactError?>(null) - val visible = _visible.asState() val alias = _alias.asState() val remoteHandshakeLink = _remoteHandshakeLink.asState() val handshakeLink = _handshakeLink.asState() val error = _error.asState() - fun showDialog() { - _visible.value = true - } - - fun dismissDialog() { - _visible.value = false - } - fun setAddContactAlias(alias: String) { _alias.value = alias } @@ -101,17 +91,17 @@ constructor( txn.attach { _handshakeLink.value = link } } - fun onSubmitAddContactDialog() { + fun onSubmitAddContactDialog(onSuccess: () -> Unit) { val link = _remoteHandshakeLink.value val alias = _alias.value - addPendingContact(link, alias) + addPendingContact(link, alias, onSuccess) } fun clearError() { _error.value = null } - private fun addPendingContact(link: String, alias: String) { + private fun addPendingContact(link: String, alias: String, onSuccess: () -> Unit) { // ignore preceding and trailing whitespace val matcher = HandshakeLinkConstants.LINK_REGEX.matcher(link.trim()) // check if the link is well-formed @@ -139,7 +129,7 @@ constructor( try { contactManager.addPendingContact(txn, link, alias) txn.attach { - _visible.value = false + onSuccess() _alias.value = "" _remoteHandshakeLink.value = "" } 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 cafa8ef3eb0e6381dbfff46023eba7cca4212a0c..83b3955a31121bed25416e8db8e678aaeb28cb6f 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 @@ -1,6 +1,6 @@ /* * Briar Desktop - * Copyright (C) 2021-2022 The Briar Project + * Copyright (C) 2021-2023 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 @@ -32,6 +32,10 @@ import androidx.compose.material.Text import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.PersonAdd import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextAlign @@ -41,7 +45,6 @@ import org.briarproject.briar.desktop.contact.ContactItem import org.briarproject.briar.desktop.contact.ContactList import org.briarproject.briar.desktop.contact.ContactListViewModel import org.briarproject.briar.desktop.contact.add.remote.AddContactDialog -import org.briarproject.briar.desktop.contact.add.remote.AddContactViewModel import org.briarproject.briar.desktop.contact.add.remote.PendingContactItem import org.briarproject.briar.desktop.ui.BriarLogo import org.briarproject.briar.desktop.ui.ColoredIconButton @@ -53,9 +56,12 @@ import org.briarproject.briar.desktop.viewmodel.viewModel @Composable fun PrivateMessageScreen( viewModel: ContactListViewModel = viewModel(), - addContactViewModel: AddContactViewModel = viewModel(), ) { - AddContactDialog(addContactViewModel) + var addDialogVisible by remember { mutableStateOf(false) } + AddContactDialog( + visible = addDialogVisible, + onClose = { addDialogVisible = false } + ) ConfirmRemovePendingContactDialog( viewModel.removePendingContactDialogVisible.value, @@ -64,7 +70,7 @@ fun PrivateMessageScreen( ) if (viewModel.noContactsYet.value) { - NoContactsYet(onContactAdd = { addContactViewModel.showDialog() }) + NoContactsYet(onContactAdd = { addDialogVisible = true }) return } @@ -76,7 +82,7 @@ fun PrivateMessageScreen( viewModel::removePendingContact, viewModel.filterBy.value, viewModel::setFilterBy, - onContactAdd = { addContactViewModel.showDialog() } + onContactAdd = { addDialogVisible = true } ) VerticalDivider() Column(modifier = Modifier.weight(1f).fillMaxHeight()) { @@ -87,6 +93,7 @@ fun PrivateMessageScreen( is ContactItem -> { ConversationScreen(item.id) } + is PendingContactItem -> { PendingContactSelected() }