diff --git a/src/main/kotlin/org/briarproject/briar/desktop/contact/AddContactDialog.kt b/src/main/kotlin/org/briarproject/briar/desktop/contact/AddContactDialog.kt index 2d2f5ae5b0d2c8485a73f838a60de3ce0ce4f911..7a623d2e8d0e25875b92d2d7e767dc8a38845832 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/contact/AddContactDialog.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/contact/AddContactDialog.kt @@ -15,35 +15,16 @@ import androidx.compose.material.Text import androidx.compose.material.TextButton import androidx.compose.material.TextField 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.unit.dp import androidx.compose.ui.unit.sp -import org.briarproject.bramble.api.FormatException -import org.briarproject.bramble.api.contact.ContactManager -import org.briarproject.bramble.api.db.ContactExistsException -import org.briarproject.bramble.api.db.PendingContactExistsException -import org.briarproject.bramble.api.identity.AuthorConstants -import org.briarproject.bramble.util.StringUtils -import org.briarproject.briar.desktop.ui.CTM -import java.security.GeneralSecurityException @OptIn(ExperimentalMaterialApi::class) @Composable -fun AddContactDialog(isVisible: Boolean, setDialogVisibility: (Boolean) -> Unit) { - if (!isVisible) { - return - } - var contactAlias by remember { mutableStateOf("") } - var contactLink by remember { mutableStateOf("") } - val contactManager = CTM.current - val ownLink = CTM.current.handshakeLink +fun AddContactDialog(viewModel: ContactsViewModel) { AlertDialog( - onDismissRequest = { setDialogVisibility(false) }, + onDismissRequest = viewModel::closeAddContactDialog, text = { Column(modifier = Modifier.fillMaxWidth()) { Row(Modifier.fillMaxWidth().padding(vertical = 16.dp)) { @@ -58,14 +39,22 @@ fun AddContactDialog(isVisible: Boolean, setDialogVisibility: (Boolean) -> Unit) "Contact's Link", Modifier.width(128.dp).align(Alignment.CenterVertically), ) - TextField(contactLink, onValueChange = { contactLink = it }, modifier = Modifier.fillMaxWidth()) + TextField( + viewModel.addContactLink.value, + viewModel::setAddContactLink, + modifier = Modifier.fillMaxWidth() + ) } Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) { Text( "Contact's Name", Modifier.width(128.dp).align(Alignment.CenterVertically), ) - TextField(contactAlias, onValueChange = { contactAlias = it }, modifier = Modifier.fillMaxWidth()) + TextField( + viewModel.addContactAlias.value, + viewModel::setAddContactAlias, + modifier = Modifier.fillMaxWidth() + ) } Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) { Text( @@ -73,7 +62,7 @@ fun AddContactDialog(isVisible: Boolean, setDialogVisibility: (Boolean) -> Unit) modifier = Modifier.width(128.dp).align(Alignment.CenterVertically), ) TextField( - ownLink, + viewModel.addContactOwnLink, onValueChange = {}, modifier = Modifier.fillMaxWidth() ) @@ -81,23 +70,13 @@ fun AddContactDialog(isVisible: Boolean, setDialogVisibility: (Boolean) -> Unit) } }, confirmButton = { - Button( - onClick = { - if (ownLink.equals(contactLink)) { - println("Please enter contact's link, not your own") - setDialogVisibility(false) - return@Button - } - addPendingContact(contactManager, contactAlias, contactLink) - setDialogVisibility(false) - }, - ) { + Button(onClick = viewModel::onSubmitAddContactDialog) { Text("Add") } }, dismissButton = { TextButton( - onClick = { setDialogVisibility(false) } + onClick = viewModel::closeAddContactDialog ) { Text("Cancel", color = MaterialTheme.colors.onSurface) } @@ -105,38 +84,3 @@ fun AddContactDialog(isVisible: Boolean, setDialogVisibility: (Boolean) -> Unit) modifier = Modifier.size(600.dp, 300.dp), ) } - -private fun addPendingContact(contactManager: ContactManager, alias: String, link: String) { - if (aliasIsInvalid(alias)) { - println("Alias is invalid") - return - } - try { - contactManager.addPendingContact(link, alias) - } catch (e: FormatException) { - println("Link is invalid") - println(e.stackTrace) - } catch (e: GeneralSecurityException) { - println("Public key is invalid") - println(e.stackTrace) - } - /* - TODO: Warn user that the following two errors might be an attack - - Use `e.pendingContact.id.bytes` and `e.pendingContact.alias` to implement the following logic: - https://code.briarproject.org/briar/briar-gtk/-/merge_requests/97 - - */ - catch (e: ContactExistsException) { - println("Contact already exists") - println(e.stackTrace) - } catch (e: PendingContactExistsException) { - println("Pending Contact already exists") - println(e.stackTrace) - } -} - -private fun aliasIsInvalid(alias: String): Boolean { - val aliasUtf8 = StringUtils.toUtf8(alias) - return aliasUtf8.isEmpty() || aliasUtf8.size > AuthorConstants.MAX_AUTHOR_NAME_LENGTH -} diff --git a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactList.kt b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactList.kt index 42edcbf1a70fcb48875265490f5a92655992a354..528a52b6967bf73f272cf7b5396c6f35b6e54de9 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactList.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactList.kt @@ -18,9 +18,9 @@ import org.briarproject.briar.desktop.ui.Constants.HEADER_SIZE @Composable fun ContactList( - contacts: ContactsViewModel, - onContactAdd: () -> Unit, + viewModel: ContactsViewModel, ) { + if (viewModel.addContactDialogVisible.value) AddContactDialog(viewModel) Scaffold( modifier = Modifier.fillMaxHeight().width(CONTACTLIST_WIDTH), backgroundColor = MaterialTheme.colors.surfaceVariant, @@ -29,16 +29,16 @@ fun ContactList( modifier = Modifier.fillMaxWidth().height(HEADER_SIZE + 1.dp), ) { SearchTextField( - contacts.filterBy.value, - onValueChange = contacts::setFilterBy, - onContactAdd + viewModel.filterBy.value, + onValueChange = viewModel::setFilterBy, + onContactAdd = viewModel::openAddContactDialog ) } }, content = { LazyColumn { - itemsIndexed(contacts.contactList) { index, contact -> - ContactCard(contact, { contacts.selectContact(index) }, contacts.isSelected(index), true) + itemsIndexed(viewModel.contactList) { index, contact -> + ContactCard(contact, { viewModel.selectContact(index) }, viewModel.isSelected(index), true) } } }, diff --git a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactsViewModel.kt b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactsViewModel.kt index 9bc56f9778384b0006acbb55c087eb4194bf8141..b653272c29d7223b14fb0ab321b8fa22ed155797 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactsViewModel.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactsViewModel.kt @@ -3,8 +3,14 @@ package org.briarproject.briar.desktop.contact import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf +import org.briarproject.bramble.api.FormatException import org.briarproject.bramble.api.contact.Contact import org.briarproject.bramble.api.contact.ContactManager +import org.briarproject.bramble.api.db.ContactExistsException +import org.briarproject.bramble.api.db.PendingContactExistsException +import org.briarproject.bramble.api.identity.AuthorConstants +import org.briarproject.bramble.util.StringUtils +import java.security.GeneralSecurityException import java.util.logging.Logger import javax.inject.Inject @@ -24,10 +30,19 @@ constructor( private var _selectedContactIndex = -1; private val _selectedContact = mutableStateOf<Contact?>(null) + private val _addContactDialogVisible = mutableStateOf(false) + private val _addContactAlias = mutableStateOf("") + private val _addContactLink = mutableStateOf("") + val contactList: List<Contact> = _filteredContactList val filterBy: State<String> = _filterBy val selectedContact: State<Contact?> = _selectedContact + val addContactDialogVisible: State<Boolean> = _addContactDialogVisible + val addContactAlias: State<String> = _addContactAlias + val addContactLink: State<String> = _addContactLink + val addContactOwnLink = contactManager.handshakeLink + internal fun loadContacts() { _contactList.apply { clear() @@ -57,4 +72,67 @@ constructor( _filterBy.value = filter updateFilteredList() } + + fun openAddContactDialog() { + _addContactDialogVisible.value = true + } + + fun closeAddContactDialog() { + _addContactDialogVisible.value = false + } + + fun setAddContactAlias(alias: String) { + _addContactAlias.value = alias + } + + fun setAddContactLink(link: String) { + _addContactLink.value = link + } + + fun onSubmitAddContactDialog() { + val link = _addContactLink.value + val alias = _addContactAlias.value + addPendingContact(link, alias) + closeAddContactDialog() + } + + private fun addPendingContact(link: String, alias: String) { + if (addContactOwnLink.equals(link)) { + println("Please enter contact's link, not your own") + return + } + if (aliasIsInvalid(alias)) { + println("Alias is invalid") + return + } + + try { + contactManager.addPendingContact(link, alias) + } catch (e: FormatException) { + println("Link is invalid") + println(e.stackTrace) + } catch (e: GeneralSecurityException) { + println("Public key is invalid") + println(e.stackTrace) + } + /* + TODO: Warn user that the following two errors might be an attack + + Use `e.pendingContact.id.bytes` and `e.pendingContact.alias` to implement the following logic: + https://code.briarproject.org/briar/briar-gtk/-/merge_requests/97 + + */ + catch (e: ContactExistsException) { + println("Contact already exists") + println(e.stackTrace) + } catch (e: PendingContactExistsException) { + println("Pending Contact already exists") + println(e.stackTrace) + } + } + + private fun aliasIsInvalid(alias: String): Boolean { + val aliasUtf8 = StringUtils.toUtf8(alias) + return aliasUtf8.isEmpty() || aliasUtf8.size > AuthorConstants.MAX_AUTHOR_NAME_LENGTH + } } diff --git a/src/main/kotlin/org/briarproject/briar/desktop/conversation/PrivateMessageView.kt b/src/main/kotlin/org/briarproject/briar/desktop/conversation/PrivateMessageView.kt index 74b89783592ba6dec3e65c948e174c05d59ab001..4b30eddff7f738067cd626062b149ea8261bce00 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/conversation/PrivateMessageView.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/conversation/PrivateMessageView.kt @@ -8,7 +8,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier -import org.briarproject.briar.desktop.contact.AddContactDialog import org.briarproject.briar.desktop.contact.ContactInfoDrawerState.MakeIntro import org.briarproject.briar.desktop.contact.ContactList import org.briarproject.briar.desktop.contact.ContactsViewModel @@ -18,13 +17,11 @@ import org.briarproject.briar.desktop.ui.VerticalDivider fun PrivateMessageView( contacts: ContactsViewModel, ) { - val (isDialogVisible, setDialogVisibility) = remember { mutableStateOf(false) } val (dropdownExpanded, setExpanded) = remember { mutableStateOf(false) } val (infoDrawer, setInfoDrawer) = remember { mutableStateOf(false) } val (contactDrawerState, setDrawerState) = remember { mutableStateOf(MakeIntro) } - AddContactDialog(isDialogVisible, setDialogVisibility) Row(modifier = Modifier.fillMaxWidth()) { - ContactList(contacts) { setDialogVisibility(true) } + ContactList(contacts) VerticalDivider() Column(modifier = Modifier.weight(1f).fillMaxHeight()) { contacts.selectedContact.value?.let { selectedContact ->