diff --git a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactDropDown.kt b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactDropDown.kt index 9740a14c63c3154b05b92580b51069e55d12b489..70c73a89f615bad46840c163eb99d03bb6b696cf 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactDropDown.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactDropDown.kt @@ -22,7 +22,7 @@ import androidx.compose.ui.unit.sp fun ContactDropDown( expanded: Boolean, isExpanded: (Boolean) -> Unit, - setInfoDrawer: (Boolean) -> Unit + onMakeIntroduction: () -> Unit, ) { var connectionMode by remember { mutableStateOf(false) } var contactMode by remember { mutableStateOf(false) } @@ -30,7 +30,7 @@ fun ContactDropDown( expanded = expanded, onDismissRequest = { isExpanded(false) }, ) { - DropdownMenuItem(onClick = { setInfoDrawer(true); isExpanded(false) }) { + DropdownMenuItem(onClick = { isExpanded(false); onMakeIntroduction() }) { Text("Make Introduction", fontSize = 14.sp) } DropdownMenuItem(onClick = {}) { diff --git a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactInfoDrawer.kt b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactInfoDrawer.kt index 99f247b2d9995ae95e7568f56d8a6d3062b2e0fb..f43736a975bd38b7427f2e45fc0ba9782d447131 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactInfoDrawer.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactInfoDrawer.kt @@ -1,8 +1,9 @@ package org.briarproject.briar.desktop.contact import androidx.compose.runtime.Composable -import org.briarproject.bramble.api.contact.Contact import org.briarproject.briar.desktop.contact.ContactInfoDrawerState.MakeIntro +import org.briarproject.briar.desktop.introduction.ContactDrawerMakeIntro +import org.briarproject.briar.desktop.introduction.IntroductionViewModel // Right drawer state enum class ContactInfoDrawerState { @@ -13,12 +14,11 @@ enum class ContactInfoDrawerState { @Composable fun ContactInfoDrawer( - contact: Contact, - contacts: List<Contact>, + introductionViewModel: IntroductionViewModel, setInfoDrawer: (Boolean) -> Unit, drawerState: ContactInfoDrawerState ) { when (drawerState) { - MakeIntro -> ContactDrawerMakeIntro(contact, contacts, setInfoDrawer) + MakeIntro -> ContactDrawerMakeIntro(introductionViewModel, setInfoDrawer) } } diff --git a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactItem.kt b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactItem.kt index 10c12cbdce721addc9455b316ecf766bb6d54b83..a20bb5bf3fc5edf6d09c96eb410a7cb76810730a 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactItem.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactItem.kt @@ -3,19 +3,24 @@ package org.briarproject.briar.desktop.contact import org.briarproject.bramble.api.contact.Contact import org.briarproject.briar.api.client.MessageTracker import org.briarproject.briar.api.conversation.ConversationMessageHeader -import org.briarproject.briar.api.identity.AuthorInfo import kotlin.math.max data class ContactItem( val contact: Contact, - private val authorInfo: AuthorInfo, - private val groupCount: MessageTracker.GroupCount, - val isConnected: Boolean, - val isEmpty: Boolean = groupCount.msgCount == 0, - val unread: Int = groupCount.unreadCount, - val timestamp: Long = groupCount.latestMsgTime + val isEmpty: Boolean, + val unread: Int, + val timestamp: Long ) { + + constructor(contact: Contact, isConnected: Boolean, groupCount: MessageTracker.GroupCount) + : this( + contact, isConnected, + isEmpty = groupCount.msgCount == 0, + unread = groupCount.unreadCount, + timestamp = groupCount.latestMsgTime + ) + fun updateFromMessageHeader(h: ConversationMessageHeader): ContactItem { return copy( isEmpty = false, 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 7fde6fe404e140b6f41e4e285f8fb34d0dfcff9a..4e769de56af0ec0447e45ebf101fed93f3042e5d 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactList.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactList.kt @@ -24,7 +24,7 @@ import org.briarproject.briar.desktop.ui.Constants.HEADER_SIZE @Composable fun ContactList( - viewModel: ContactsViewModel, + viewModel: ContactListViewModel, addContactViewModel: AddContactViewModel, ) { var isContactDialogVisible by remember { mutableStateOf(false) } diff --git a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactListViewModel.kt b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactListViewModel.kt new file mode 100644 index 0000000000000000000000000000000000000000..f9a3438af8de91d3e81f3a2c257e6413f43ade7a --- /dev/null +++ b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactListViewModel.kt @@ -0,0 +1,94 @@ +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.connection.ConnectionRegistry +import org.briarproject.bramble.api.contact.ContactId +import org.briarproject.bramble.api.contact.ContactManager +import org.briarproject.bramble.api.contact.event.ContactAliasChangedEvent +import org.briarproject.bramble.api.event.Event +import org.briarproject.bramble.api.event.EventBus +import org.briarproject.briar.api.conversation.ConversationManager +import org.briarproject.briar.api.conversation.event.ConversationMessageReceivedEvent +import java.util.logging.Logger +import javax.inject.Inject + +class ContactListViewModel +@Inject +constructor( + contactManager: ContactManager, + conversationManager: ConversationManager, + connectionRegistry: ConnectionRegistry, + eventBus: EventBus, +) : ContactsViewModel(contactManager, conversationManager, connectionRegistry) { + + companion object { + private val LOG = Logger.getLogger(ContactListViewModel::class.java.name) + } + + init { + //todo: where/when to remove listener again? + eventBus.addListener(this) + } + + private val _filteredContactList = mutableStateListOf<ContactItem>() + private val _filterBy = mutableStateOf("") + private var _selectedContactIndex = -1; + private val _selectedContact = mutableStateOf<ContactItem?>(null) + + override val contactList: List<ContactItem> = _filteredContactList + val filterBy: State<String> = _filterBy + val selectedContact: State<ContactItem?> = _selectedContact + + override fun loadContacts() { + super.loadContacts() + updateFilteredList() + } + + fun selectContact(index: Int) { + _selectedContactIndex = index + _selectedContact.value = _filteredContactList[index] + } + + fun isSelected(index: Int) = _selectedContactIndex == index + + private fun updateFilteredList() { + _filteredContactList.apply { + clear() + addAll(_contactList.filter { + // todo: also filter on alias? + it.contact.author.name.lowercase().contains(_filterBy.value) + }) + } + } + + fun setFilterBy(filter: String) { + _filterBy.value = filter + updateFilteredList() + } + + override fun eventOccurred(e: Event?) { + super.eventOccurred(e) + when (e) { + is ConversationMessageReceivedEvent<*> -> { + LOG.info("Conversation message received, updating item") + updateItem(e.contactId) { it.updateFromMessageHeader(e.messageHeader) } + } + //is AvatarUpdatedEvent -> {} + is ContactAliasChangedEvent -> { + updateItem(e.contactId) { it.updateAlias(e.alias) } + } + } + } + + override fun updateItem(contactId: ContactId, update: (ContactItem) -> ContactItem) { + super.updateItem(contactId, update) + updateFilteredList() + } + + override fun removeItem(contactId: ContactId) { + super.removeItem(contactId) + updateFilteredList() + } +} 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 32da44de5078d3351a54c97cea358fab413be821..61a56229033a80392daf47ad76827bef6c798c9f 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactsViewModel.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactsViewModel.kt @@ -1,91 +1,47 @@ 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.connection.ConnectionRegistry +import org.briarproject.bramble.api.contact.Contact import org.briarproject.bramble.api.contact.ContactId import org.briarproject.bramble.api.contact.ContactManager import org.briarproject.bramble.api.contact.event.ContactAddedEvent -import org.briarproject.bramble.api.contact.event.ContactAliasChangedEvent import org.briarproject.bramble.api.contact.event.ContactRemovedEvent import org.briarproject.bramble.api.event.Event -import org.briarproject.bramble.api.event.EventBus import org.briarproject.bramble.api.event.EventListener import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent import org.briarproject.briar.api.conversation.ConversationManager -import org.briarproject.briar.api.conversation.event.ConversationMessageReceivedEvent -import org.briarproject.briar.api.identity.AuthorManager import org.briarproject.briar.desktop.utils.removeFirst import org.briarproject.briar.desktop.utils.replaceFirst import java.util.logging.Logger -import javax.inject.Inject -class ContactsViewModel -@Inject -constructor( - private val contactManager: ContactManager, - private val authorManager: AuthorManager, +abstract class ContactsViewModel( + protected val contactManager: ContactManager, private val conversationManager: ConversationManager, - private val connectionRegistry: ConnectionRegistry, - private val eventBus: EventBus, + private val connectionRegistry: ConnectionRegistry ) : EventListener { companion object { private val LOG = Logger.getLogger(ContactsViewModel::class.java.name) } - init { - //todo: where/when to remove listener again? - eventBus.addListener(this) - } + protected val _contactList = mutableListOf<ContactItem>() - private val _contactList = mutableListOf<ContactItem>() - private val _filteredContactList = mutableStateListOf<ContactItem>() - private val _filterBy = mutableStateOf("") - private var _selectedContactIndex = -1; - private val _selectedContact = mutableStateOf<ContactItem?>(null) + abstract val contactList: List<ContactItem> - val contactList: List<ContactItem> = _filteredContactList - val filterBy: State<String> = _filterBy - val selectedContact: State<ContactItem?> = _selectedContact + protected open fun filterContact(contact: Contact) = true - internal fun loadContacts() { + open fun loadContacts() { _contactList.apply { clear() - addAll(contactManager.contacts.map { contact -> + addAll(contactManager.contacts.filter(::filterContact).map { contact -> ContactItem( contact, - authorManager.getAuthorInfo(contact), + connectionRegistry.isConnected(contact.id), conversationManager.getGroupCount(contact.id), - connectionRegistry.isConnected(contact.id) ) }) } - updateFilteredList() - } - - fun selectContact(index: Int) { - _selectedContactIndex = index - _selectedContact.value = _filteredContactList[index] - } - - fun isSelected(index: Int) = _selectedContactIndex == index - - private fun updateFilteredList() { - _filteredContactList.apply { - clear() - addAll(_contactList.filter { - // todo: also filter on alias? - it.contact.author.name.lowercase().contains(_filterBy.value) - }) - } - } - - fun setFilterBy(filter: String) { - _filterBy.value = filter - updateFilteredList() } override fun eventOccurred(e: Event?) { @@ -106,24 +62,14 @@ constructor( LOG.info("Contact removed, removing item") removeItem(e.contactId) } - is ConversationMessageReceivedEvent<*> -> { - LOG.info("Conversation message received, updating item") - updateItem(e.contactId) { it.updateFromMessageHeader(e.messageHeader) } - } - //is AvatarUpdatedEvent -> {} - is ContactAliasChangedEvent -> { - updateItem(e.contactId) { it.updateAlias(e.alias) } - } } } - private fun updateItem(contactId: ContactId, update: (ContactItem) -> ContactItem) { + protected open fun updateItem(contactId: ContactId, update: (ContactItem) -> ContactItem) { _contactList.replaceFirst({ it.contact.id == contactId }, update) - updateFilteredList() } - private fun removeItem(contactId: ContactId) { + protected open fun removeItem(contactId: ContactId) { _contactList.removeFirst { it.contact.id == contactId } - updateFilteredList() } } diff --git a/src/main/kotlin/org/briarproject/briar/desktop/conversation/Conversation.kt b/src/main/kotlin/org/briarproject/briar/desktop/conversation/Conversation.kt index 71f0cd34e0d8295041b8d35a331424c3cd62c2ed..23c61666c1115c531806d12bec299ece7df21be9 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/conversation/Conversation.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/conversation/Conversation.kt @@ -18,6 +18,7 @@ import androidx.compose.material.MaterialTheme import androidx.compose.material.Scaffold import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -25,6 +26,7 @@ import androidx.compose.ui.unit.dp import org.briarproject.bramble.api.contact.Contact import org.briarproject.briar.desktop.contact.ContactInfoDrawer import org.briarproject.briar.desktop.contact.ContactInfoDrawerState +import org.briarproject.briar.desktop.introduction.IntroductionViewModel import org.briarproject.briar.desktop.navigation.SIDEBAR_WIDTH import org.briarproject.briar.desktop.theme.surfaceVariant import org.briarproject.briar.desktop.ui.Constants.CONTACTLIST_WIDTH @@ -32,17 +34,25 @@ import org.briarproject.briar.desktop.ui.Constants.CONTACTLIST_WIDTH @Composable fun Conversation( contact: Contact, - contacts: List<Contact>, - expanded: Boolean, - setExpanded: (Boolean) -> Unit, - infoDrawer: Boolean, - setInfoDrawer: (Boolean) -> Unit, - drawerState: ContactInfoDrawerState + introductionViewModel: IntroductionViewModel, ) { + val (infoDrawer, setInfoDrawer) = remember { mutableStateOf(false) } + val (contactDrawerState, setDrawerState) = remember { mutableStateOf(ContactInfoDrawerState.MakeIntro) } BoxWithConstraints(Modifier.fillMaxSize()) { val animatedInfoDrawerOffsetX by animateDpAsState(if (infoDrawer) (-275).dp else 0.dp) Scaffold( - topBar = { ConversationHeader(contact, expanded, setExpanded, setInfoDrawer) }, + topBar = { + ConversationHeader( + contact, + onMakeIntroduction = { + introductionViewModel.apply { + setFirstContact(contact) + loadContacts() + } + setInfoDrawer(true) + } + ) + }, content = { padding -> Box(modifier = Modifier.padding(padding)) { val chat = ChatState(contact.id) @@ -71,7 +81,7 @@ fun Conversation( RoundedCornerShape(topStart = 10.dp, bottomStart = 10.dp) ) ) { - ContactInfoDrawer(contact, contacts, setInfoDrawer, drawerState) + ContactInfoDrawer(introductionViewModel, setInfoDrawer, contactDrawerState) } } } diff --git a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationHeader.kt b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationHeader.kt index c8c45f0e05b09738cef58bafb7f40507901c3c9a..a36dca368671dfc5302184dab43c84e45e50a6a5 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationHeader.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationHeader.kt @@ -14,6 +14,8 @@ import androidx.compose.material.Text import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.MoreVert import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.drawscope.withTransform @@ -29,10 +31,9 @@ import org.briarproject.briar.desktop.ui.HorizontalDivider @Composable fun ConversationHeader( contact: Contact, - expanded: Boolean, - isExpanded: (Boolean) -> Unit, - setInfoDrawer: (Boolean) -> Unit + onMakeIntroduction: () -> Unit, ) { + val (isExpanded, setExpanded) = remember { mutableStateOf(false) } // TODO hook up online indicator logic val onlineColor = MaterialTheme.colors.secondary val outlineColor = MaterialTheme.colors.outline @@ -57,11 +58,11 @@ fun ConversationHeader( ) } IconButton( - onClick = { isExpanded(!expanded) }, + onClick = { setExpanded(!isExpanded) }, modifier = Modifier.align(Alignment.CenterEnd).padding(end = 16.dp) ) { Icon(Icons.Filled.MoreVert, "contact info", modifier = Modifier.size(24.dp)) - ContactDropDown(expanded, isExpanded, setInfoDrawer) + ContactDropDown(isExpanded, setExpanded, onMakeIntroduction) } HorizontalDivider(modifier = Modifier.align(Alignment.BottomCenter)) } 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 80d1585025f1c81bd011338dd175a5067428aeb7..ae06e0135a6b6a8c462bd8a72497add5f8bfcefa 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/conversation/PrivateMessageView.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/conversation/PrivateMessageView.kt @@ -5,37 +5,28 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth 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.ContactInfoDrawerState.MakeIntro import org.briarproject.briar.desktop.contact.ContactList -import org.briarproject.briar.desktop.contact.ContactsViewModel +import org.briarproject.briar.desktop.contact.ContactListViewModel import org.briarproject.briar.desktop.contact.add.remote.AddContactViewModel +import org.briarproject.briar.desktop.introduction.IntroductionViewModel import org.briarproject.briar.desktop.ui.UiPlaceholder import org.briarproject.briar.desktop.ui.VerticalDivider @Composable fun PrivateMessageView( - contactsViewModel: ContactsViewModel, + contactListViewModel: ContactListViewModel, addContactViewModel: AddContactViewModel, + introductionViewModel: IntroductionViewModel, ) { - val (dropdownExpanded, setExpanded) = remember { mutableStateOf(false) } - val (infoDrawer, setInfoDrawer) = remember { mutableStateOf(false) } - val (contactDrawerState, setDrawerState) = remember { mutableStateOf(MakeIntro) } Row(modifier = Modifier.fillMaxWidth()) { - ContactList(contactsViewModel, addContactViewModel) + ContactList(contactListViewModel, addContactViewModel) VerticalDivider() Column(modifier = Modifier.weight(1f).fillMaxHeight()) { - contactsViewModel.selectedContact.value?.also { selectedContact -> + contactListViewModel.selectedContact.value?.also { selectedContact -> Conversation( selectedContact.contact, - contactsViewModel.contactList.map { c -> c.contact }, - dropdownExpanded, - setExpanded, - infoDrawer, - setInfoDrawer, - contactDrawerState + introductionViewModel ) } ?: UiPlaceholder() } diff --git a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactDrawerMakeIntro.kt b/src/main/kotlin/org/briarproject/briar/desktop/introduction/ContactDrawerMakeIntro.kt similarity index 55% rename from src/main/kotlin/org/briarproject/briar/desktop/contact/ContactDrawerMakeIntro.kt rename to src/main/kotlin/org/briarproject/briar/desktop/introduction/ContactDrawerMakeIntro.kt index 76e4c31547f7f835257621f1920b2ddd015d7a82..b140e1fa65051edec6a51d59b867b5ee15013b86 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactDrawerMakeIntro.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/introduction/ContactDrawerMakeIntro.kt @@ -1,4 +1,4 @@ -package org.briarproject.briar.desktop.contact +package org.briarproject.briar.desktop.introduction import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -7,8 +7,8 @@ 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.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items import androidx.compose.material.Icon import androidx.compose.material.IconButton import androidx.compose.material.Surface @@ -20,45 +20,45 @@ import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.SwapHoriz 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.graphics.Color -import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import org.briarproject.bramble.api.contact.Contact +import org.briarproject.briar.desktop.contact.ContactCard +import org.briarproject.briar.desktop.contact.ProfileCircle import org.briarproject.briar.desktop.ui.Constants.HEADER_SIZE import org.briarproject.briar.desktop.ui.HorizontalDivider @Composable -fun ContactDrawerMakeIntro(contact: Contact, contacts: List<Contact>, setInfoDrawer: (Boolean) -> Unit) { - var introNextPg by remember { mutableStateOf(false) } - val (introContact, onCancelSel) = remember { mutableStateOf(contact) } - if (!introNextPg) { +fun ContactDrawerMakeIntro( + viewModel: IntroductionViewModel, + setInfoDrawer: (Boolean) -> Unit +) { + if (!viewModel.secondScreen.value) { Surface { - Row(Modifier.fillMaxWidth().height(HEADER_SIZE)) { - IconButton( - onClick = { setInfoDrawer(false) }, - Modifier.padding(horizontal = 11.dp).size(32.dp).align(Alignment.CenterVertically) - ) { - Icon(Icons.Filled.Close, "close make intro screen") + Column { + Row(Modifier.fillMaxWidth().height(HEADER_SIZE)) { + IconButton( + onClick = { setInfoDrawer(false) }, + Modifier.padding(horizontal = 11.dp).size(32.dp).align(Alignment.CenterVertically) + ) { + Icon(Icons.Filled.Close, "close make intro screen") + } + Text( + text = "Introduce " + viewModel.firstContact.value!!.author.name + " to:", + fontSize = 16.sp, + modifier = Modifier.align(Alignment.CenterVertically) + ) } - Text( - text = "Introduce " + contact.author.name + " to:", - fontSize = 16.sp, - modifier = Modifier.align(Alignment.CenterVertically) - ) - } - HorizontalDivider() - Column(Modifier.verticalScroll(rememberScrollState())) { - for (c in contacts) { - if (c.id != contact.id) { - // todo: refactor to use contactItem in IntroductionViewModel - //ContactCard(c, { onCancelSel(c); introNextPg = true }, false, false) + HorizontalDivider() + LazyColumn { + items(viewModel.contactList) { contactItem -> + ContactCard( + contactItem, + { viewModel.setSecondContact(contactItem.contact) }, + false + ) } } } @@ -67,7 +67,7 @@ fun ContactDrawerMakeIntro(contact: Contact, contacts: List<Contact>, setInfoDra Column { Row(Modifier.fillMaxWidth().height(HEADER_SIZE)) { IconButton( - onClick = { introNextPg = false }, + onClick = viewModel::backToFirstScreen, Modifier.padding(horizontal = 11.dp).size(32.dp).align(Alignment.CenterVertically) ) { Icon(Icons.Filled.ArrowBack, "go back to make intro contact screen", tint = Color.White) @@ -80,26 +80,25 @@ fun ContactDrawerMakeIntro(contact: Contact, contacts: List<Contact>, setInfoDra } Row(Modifier.fillMaxWidth().padding(12.dp), horizontalArrangement = Arrangement.SpaceAround) { Column(Modifier.align(Alignment.CenterVertically)) { - ProfileCircle(36.dp, contact.author.id.bytes) - Text(contact.author.name, Modifier.padding(top = 4.dp), Color.White, 16.sp) + ProfileCircle(36.dp, viewModel.firstContact.value!!.author.id.bytes) + Text(viewModel.firstContact.value!!.author.name, Modifier.padding(top = 4.dp), Color.White, 16.sp) } Icon(Icons.Filled.SwapHoriz, "swap", modifier = Modifier.size(48.dp)) Column(Modifier.align(Alignment.CenterVertically)) { - ProfileCircle(36.dp, introContact.author.id.bytes) - Text(introContact.author.name, Modifier.padding(top = 4.dp), Color.White, 16.sp) + ProfileCircle(36.dp, viewModel.secondContact.value!!.author.id.bytes) + Text(viewModel.secondContact.value!!.author.name, Modifier.padding(top = 4.dp), Color.White, 16.sp) } } - var introText by remember { mutableStateOf(TextFieldValue("")) } Row(Modifier.padding(8.dp)) { TextField( - introText, - { introText = it }, + viewModel.introductionMessage.value, + viewModel::setIntroductionMessage, placeholder = { Text(text = "Add a message (optional)") }, ) } Row(Modifier.padding(8.dp)) { TextButton( - onClick = { setInfoDrawer(false); introNextPg = false; }, + onClick = { setInfoDrawer(false) }, Modifier.fillMaxWidth() ) { Text("MAKE INTRODUCTION") diff --git a/src/main/kotlin/org/briarproject/briar/desktop/introduction/IntroductionViewModel.kt b/src/main/kotlin/org/briarproject/briar/desktop/introduction/IntroductionViewModel.kt new file mode 100644 index 0000000000000000000000000000000000000000..0d64fda2b80053a45b20fea56dafc9084bbd82dd --- /dev/null +++ b/src/main/kotlin/org/briarproject/briar/desktop/introduction/IntroductionViewModel.kt @@ -0,0 +1,66 @@ +package org.briarproject.briar.desktop.introduction + +import androidx.compose.runtime.State +import androidx.compose.runtime.mutableStateOf +import org.briarproject.bramble.api.connection.ConnectionRegistry +import org.briarproject.bramble.api.contact.Contact +import org.briarproject.bramble.api.contact.ContactManager +import org.briarproject.bramble.api.event.EventBus +import org.briarproject.briar.api.conversation.ConversationManager +import org.briarproject.briar.desktop.contact.ContactItem +import org.briarproject.briar.desktop.contact.ContactsViewModel +import java.util.logging.Logger +import javax.inject.Inject + +class IntroductionViewModel +@Inject +constructor( + contactManager: ContactManager, + conversationManager: ConversationManager, + connectionRegistry: ConnectionRegistry, + eventBus: EventBus, +) : ContactsViewModel(contactManager, conversationManager, connectionRegistry) { + + companion object { + private val LOG = Logger.getLogger(IntroductionViewModel::class.java.name) + } + + init { + //todo: where/when to remove listener again? + eventBus.addListener(this) + } + + private val _firstContact = mutableStateOf<Contact?>(null) + private val _secondContact = mutableStateOf<Contact?>(null) + private val _secondScreen = mutableStateOf(false) + private val _introductionMessage = mutableStateOf("") + + override val contactList: List<ContactItem> = _contactList + val firstContact: State<Contact?> = _firstContact + val secondContact: State<Contact?> = _secondContact + val secondScreen: State<Boolean> = _secondScreen + val introductionMessage: State<String> = _introductionMessage + + fun setFirstContact(contact: Contact) { + _firstContact.value = contact + loadContacts() + backToFirstScreen() + } + + fun setSecondContact(contact: Contact) { + _secondContact.value = contact + _secondScreen.value = true + } + + fun backToFirstScreen() { + _secondScreen.value = false + } + + fun setIntroductionMessage(msg: String) { + _introductionMessage.value = msg + } + + override fun filterContact(contact: Contact): Boolean { + return _firstContact.value!!.id != contact.id + } +} diff --git a/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt b/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt index d20fffa6f2c69235aeb8e1e40f0960743a2360ef..afbda2623fdeeb8c14b7392255ea4193880b0acb 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt @@ -15,8 +15,9 @@ import org.briarproject.bramble.api.lifecycle.LifecycleManager import org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.RUNNING import org.briarproject.briar.api.conversation.ConversationManager import org.briarproject.briar.api.messaging.MessagingManager -import org.briarproject.briar.desktop.contact.ContactsViewModel +import org.briarproject.briar.desktop.contact.ContactListViewModel import org.briarproject.briar.desktop.contact.add.remote.AddContactViewModel +import org.briarproject.briar.desktop.introduction.IntroductionViewModel import org.briarproject.briar.desktop.login.Login import org.briarproject.briar.desktop.login.LoginViewModel import org.briarproject.briar.desktop.login.Registration @@ -53,8 +54,9 @@ internal class BriarUiImpl constructor( private val registrationViewModel: RegistrationViewModel, private val loginViewModel: LoginViewModel, - private val contactsViewModel: ContactsViewModel, + private val contactListViewModel: ContactListViewModel, private val addContactViewModel: AddContactViewModel, + private val introductionViewModel: IntroductionViewModel, private val accountManager: AccountManager, private val contactManager: ContactManager, private val conversationManager: ConversationManager, @@ -84,7 +86,7 @@ constructor( if (accountManager.hasDatabaseKey()) { // this should only happen during testing when we launch the main UI directly // without a need to enter the password. - contactsViewModel.loadContacts() + contactListViewModel.loadContacts() Screen.MAIN } else if (accountManager.accountExists()) { Screen.LOGIN @@ -102,12 +104,12 @@ constructor( when (screenState) { Screen.REGISTRATION -> Registration(registrationViewModel) { - contactsViewModel.loadContacts() + contactListViewModel.loadContacts() screenState = Screen.MAIN } Screen.LOGIN -> Login(loginViewModel) { - contactsViewModel.loadContacts() + contactListViewModel.loadContacts() screenState = Screen.MAIN } else -> @@ -117,7 +119,13 @@ constructor( MM provides messagingManager, IM provides identityManager, ) { - MainScreen(contactsViewModel, addContactViewModel, isDark, setDark) + MainScreen( + contactListViewModel, + addContactViewModel, + introductionViewModel, + isDark, + setDark + ) } } } diff --git a/src/main/kotlin/org/briarproject/briar/desktop/ui/MainScreen.kt b/src/main/kotlin/org/briarproject/briar/desktop/ui/MainScreen.kt index 74f57ea8046d4ddaae8bbc0fab0fca38aa046b5d..96622e0c932e6318b7dec7a94aa5756cbff42539 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/ui/MainScreen.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/ui/MainScreen.kt @@ -4,9 +4,10 @@ import androidx.compose.foundation.layout.Row import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import org.briarproject.briar.desktop.contact.ContactsViewModel +import org.briarproject.briar.desktop.contact.ContactListViewModel import org.briarproject.briar.desktop.contact.add.remote.AddContactViewModel import org.briarproject.briar.desktop.conversation.PrivateMessageView +import org.briarproject.briar.desktop.introduction.IntroductionViewModel import org.briarproject.briar.desktop.navigation.BriarSidebar import org.briarproject.briar.desktop.settings.PlaceHolderSettingsView @@ -17,8 +18,9 @@ import org.briarproject.briar.desktop.settings.PlaceHolderSettingsView */ @Composable fun MainScreen( - contactsViewModel: ContactsViewModel, + contactListViewModel: ContactListViewModel, addContactViewModel: AddContactViewModel, + introductionViewModel: IntroductionViewModel, isDark: Boolean, setDark: (Boolean) -> Unit ) { @@ -29,7 +31,7 @@ fun MainScreen( BriarSidebar(uiMode, setUiMode) VerticalDivider() when (uiMode) { - UiMode.CONTACTS -> PrivateMessageView(contactsViewModel, addContactViewModel) + UiMode.CONTACTS -> PrivateMessageView(contactListViewModel, addContactViewModel, introductionViewModel) UiMode.SETTINGS -> PlaceHolderSettingsView(isDark, setDark) else -> UiPlaceholder() }