diff --git a/src/main/kotlin/org/briarproject/briar/desktop/privategroups/PrivateGroupInfoDrawer.kt b/src/main/kotlin/org/briarproject/briar/desktop/privategroups/PrivateGroupInfoDrawer.kt deleted file mode 100644 index ae32c89a81a0a8994801582ac9c6256675916eab..0000000000000000000000000000000000000000 --- a/src/main/kotlin/org/briarproject/briar/desktop/privategroups/PrivateGroupInfoDrawer.kt +++ /dev/null @@ -1,25 +0,0 @@ -package org.briarproject.briar.desktop.privategroups - -import androidx.compose.runtime.Composable -import org.briarproject.briar.api.privategroup.PrivateGroup -import org.briarproject.briar.desktop.contact.ContactInfoDrawerState - -// Right drawer state -enum class PrivateGroupInfoDrawerState { - MakeIntro, - ConnectBT, - ConnectRD -} - -@Composable -fun ContactInfoDrawer( - privateGroup: PrivateGroup, - setInfoDrawer: (Boolean) -> Unit, - drawerState: ContactInfoDrawerState -) { - /* TODO - when (drawerState) { - MakeIntro -> ContactDrawerMakeIntro(privateGroup, setInfoDrawer) - } - */ -} diff --git a/src/main/kotlin/org/briarproject/briar/desktop/privategroups/PrivateGroupList.kt b/src/main/kotlin/org/briarproject/briar/desktop/privategroups/PrivateGroupList.kt index 10304c6115c8fbb1d8dc44faf452459baa95ffc5..634a8671f46aa17edeb4fc8f306c7318ff213181 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/privategroups/PrivateGroupList.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/privategroups/PrivateGroupList.kt @@ -1,9 +1,6 @@ package org.briarproject.briar.desktop.privategroups -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items @@ -11,16 +8,10 @@ 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.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp import org.briarproject.bramble.api.sync.GroupId -import org.briarproject.briar.desktop.contact.SearchTextField -import org.briarproject.briar.desktop.contact.add.remote.AddContactDialog import org.briarproject.briar.desktop.theme.surfaceVariant -import org.briarproject.briar.desktop.ui.Constants.HEADER_SIZE import org.briarproject.briar.desktop.ui.Constants.PRIVATE_GROUP_LIST_WIDTH @Composable @@ -28,25 +19,12 @@ fun PrivateGroupList( privateGroupList: List<PrivateGroupItem>, isSelected: (GroupId) -> Boolean, selectPrivateGroup: (GroupId) -> Unit, - filterBy: String, - setFilterBy: (String) -> Unit, ) { - var isCreatePrivateGroupDialogVisible by remember { mutableStateOf(false) } - if (isCreatePrivateGroupDialogVisible) AddContactDialog(onClose = { isCreatePrivateGroupDialogVisible = false }) + // TODO AddPrivateGroupDialog Scaffold( modifier = Modifier.fillMaxHeight().width(PRIVATE_GROUP_LIST_WIDTH), backgroundColor = MaterialTheme.colors.surfaceVariant, - topBar = { - Column( - modifier = Modifier.fillMaxWidth().height(HEADER_SIZE + 1.dp), - ) { - SearchTextField( - filterBy, - onValueChange = setFilterBy, - onContactAdd = { isCreatePrivateGroupDialogVisible = true } - ) - } - }, + // TODO SearchTextField content = { LazyColumn { items(privateGroupList) { privateGroupItem -> diff --git a/src/main/kotlin/org/briarproject/briar/desktop/privategroups/PrivateGroupListViewModel.kt b/src/main/kotlin/org/briarproject/briar/desktop/privategroups/PrivateGroupListViewModel.kt index d3f27d8443d961fa24a61de0c8fece1bb835d71c..6e7c7de3e5c8a119ed357b1b2f63b621532e3264 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/privategroups/PrivateGroupListViewModel.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/privategroups/PrivateGroupListViewModel.kt @@ -1,42 +1,31 @@ package org.briarproject.briar.desktop.privategroups 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.event.Event import org.briarproject.bramble.api.event.EventBus import org.briarproject.bramble.api.sync.GroupId import org.briarproject.briar.api.conversation.ConversationManager -import org.briarproject.briar.api.privategroup.PrivateGroup import org.briarproject.briar.api.privategroup.PrivateGroupManager -import org.briarproject.briar.desktop.utils.removeFirst -import org.briarproject.briar.desktop.utils.replaceFirst import org.briarproject.briar.desktop.viewmodel.BriarEventListenerViewModel -import java.util.logging.Logger import javax.inject.Inject class PrivateGroupListViewModel @Inject constructor( - val privateGroupManager: PrivateGroupManager, + private val privateGroupManager: PrivateGroupManager, val conversationManager: ConversationManager, val connectionRegistry: ConnectionRegistry, eventBus: EventBus, ) : BriarEventListenerViewModel(eventBus) { - companion object { - private val LOG = Logger.getLogger(PrivateGroupListViewModel::class.java.name) - } - - private val _fullContactList = mutableListOf<PrivateGroupItem>() - private val _filteredContactList = mutableStateListOf<PrivateGroupItem>() + private val _fullPrivateGroupList = mutableListOf<PrivateGroupItem>() - val privateGroupList: List<PrivateGroupItem> = _filteredContactList + val privateGroupList: List<PrivateGroupItem> = _fullPrivateGroupList private fun loadPrivateGroups() { - _fullContactList.apply { + _fullPrivateGroupList.apply { clear() addAll( privateGroupManager.privateGroups.map { privateGroup -> @@ -47,17 +36,6 @@ constructor( } ) } - updateFilteredList() - } - - private fun updateItem(contactId: ContactId, update: (PrivateGroupItem) -> PrivateGroupItem) { - _fullContactList.replaceFirst({ it.privateGroup.id == contactId }, update) - updateFilteredList() - } - - private fun removeItem(groupId: GroupId) { - _fullContactList.removeFirst { it.privateGroup.id == groupId } - updateFilteredList() } override fun onInit() { @@ -65,10 +43,8 @@ constructor( loadPrivateGroups() } - private val _filterBy = mutableStateOf("") private val _selectedContactId = mutableStateOf<GroupId?>(null) - val filterBy: State<String> = _filterBy val selectedPrivateGroupId: State<GroupId?> = _selectedContactId fun selectPrivateGroup(privateGroupId: GroupId) { @@ -77,55 +53,7 @@ constructor( fun isSelected(privateGroupId: GroupId) = _selectedContactId.value == privateGroupId - private fun filterContact(privateGroup: PrivateGroup) = - // todo: also filter on alias - privateGroup.name.contains(_filterBy.value, ignoreCase = true) - - fun setFilterBy(filter: String) { - _filterBy.value = filter - updateFilteredList() - } - - // todo: when migrated to StateFlow, this could be done implicitly instead - fun updateFilteredList() { - _filteredContactList.apply { - clear() - addAll(_fullContactList.filter { filterContact(it.privateGroup) }.sortedByDescending { it.timestamp }) - } - - // reset selected contact to null if not available after filtering - val id = _selectedContactId.value - if (id != null && !privateGroupList.map { it.privateGroup.id }.contains(id)) { - _selectedContactId.value = null - } - } - override fun eventOccurred(e: Event?) { - /* - when (e) { - is ContactAddedEvent -> { - LOG.info("Contact added, reloading") - loadPrivateGroups() - } - is ContactRemovedEvent -> { - LOG.info("Contact removed, removing item") - removeItem(e.contactId) - } - } - when (e) { - is ConversationMessageReceivedEvent<*> -> { - LOG.info("Conversation message received, updating item") - updateItem(e.contactId) { it.updateFromMessageHeader(e.messageHeader) } - } - is ConversationMessageToBeSentEvent -> { - LOG.info("Conversation message added, updating item") - updateItem(e.contactId) { it.updateFromMessageHeader(e.messageHeader) } - } - // is AvatarUpdatedEvent -> {} - is ContactAliasChangedEvent -> { - updateItem(e.contactId) { it.updateAlias(e.alias) } - } - } - */ + // TODO } } diff --git a/src/main/kotlin/org/briarproject/briar/desktop/privategroups/PrivateGroupScreen.kt b/src/main/kotlin/org/briarproject/briar/desktop/privategroups/PrivateGroupScreen.kt index 2046b4689db0c5ae9fc31d991d45aab6af81972b..7c3dac2f9e02a3ec8b2ce4e6fed530679e488227 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/privategroups/PrivateGroupScreen.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/privategroups/PrivateGroupScreen.kt @@ -19,8 +19,6 @@ fun PrivateGroupScreen( viewModel.privateGroupList, viewModel::isSelected, viewModel::selectPrivateGroup, - viewModel.filterBy.value, - viewModel::setFilterBy ) VerticalDivider() Column(modifier = Modifier.weight(1f).fillMaxHeight()) { diff --git a/src/main/kotlin/org/briarproject/briar/desktop/privategroups/ThreadedConversationHeader.kt b/src/main/kotlin/org/briarproject/briar/desktop/privategroups/ThreadedConversationHeader.kt deleted file mode 100644 index 5146a684aa0f5fcbc86429e9a5526c39ea383d5c..0000000000000000000000000000000000000000 --- a/src/main/kotlin/org/briarproject/briar/desktop/privategroups/ThreadedConversationHeader.kt +++ /dev/null @@ -1,55 +0,0 @@ -package org.briarproject.briar.desktop.privategroups - -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Row -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 -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.unit.dp -import androidx.compose.ui.unit.sp -import org.briarproject.briar.desktop.contact.ContactDropDown -import org.briarproject.briar.desktop.contact.ProfileCircle -import org.briarproject.briar.desktop.theme.outline -import org.briarproject.briar.desktop.ui.Constants.HEADER_SIZE -import org.briarproject.briar.desktop.ui.HorizontalDivider -import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n - -@Composable -fun ThreadedConversationHeader( - privateGroupItem: PrivateGroupItem, - onMakeIntroduction: () -> Unit, -) { - val (isExpanded, setExpanded) = remember { mutableStateOf(false) } - val outlineColor = MaterialTheme.colors.outline - - Box(modifier = Modifier.fillMaxWidth().height(HEADER_SIZE + 1.dp)) { - Row(modifier = Modifier.align(Alignment.Center)) { - ProfileCircle(36.dp, privateGroupItem.privateGroup.id.bytes) - Text( - privateGroupItem.privateGroup.name, - modifier = Modifier.align(Alignment.CenterVertically).padding(start = 12.dp), - fontSize = 20.sp - ) - } - IconButton( - onClick = { setExpanded(!isExpanded) }, - modifier = Modifier.align(Alignment.CenterEnd).padding(end = 16.dp) - ) { - Icon(Icons.Filled.MoreVert, i18n("access.contact.menu"), modifier = Modifier.size(24.dp)) - ContactDropDown(isExpanded, setExpanded, onMakeIntroduction) - } - HorizontalDivider(modifier = Modifier.align(Alignment.BottomCenter)) - } -} diff --git a/src/main/kotlin/org/briarproject/briar/desktop/privategroups/ThreadedConversationItem.kt b/src/main/kotlin/org/briarproject/briar/desktop/privategroups/ThreadedConversationItem.kt deleted file mode 100644 index 4ba592fd8867e9ebb646d30e5783c507d944fd73..0000000000000000000000000000000000000000 --- a/src/main/kotlin/org/briarproject/briar/desktop/privategroups/ThreadedConversationItem.kt +++ /dev/null @@ -1,31 +0,0 @@ -package org.briarproject.briar.desktop.privategroups - -import org.briarproject.bramble.api.sync.GroupId -import org.briarproject.bramble.api.sync.MessageId - -sealed class ThreadedConversationItem { - abstract val id: MessageId - abstract val groupId: GroupId - abstract val time: Long - abstract val autoDeleteTimer: Long - abstract val isIncoming: Boolean - - /** - * Only useful for incoming messages. - */ - abstract val isRead: Boolean - - /** - * Only useful for outgoing messages. - */ - abstract val isSent: Boolean - - /** - * Only useful for outgoing messages. - */ - abstract val isSeen: Boolean - - abstract fun mark(sent: Boolean, seen: Boolean): ThreadedConversationItem - - abstract fun markRead(): ThreadedConversationItem -} diff --git a/src/main/kotlin/org/briarproject/briar/desktop/privategroups/ThreadedConversationMessageItem.kt b/src/main/kotlin/org/briarproject/briar/desktop/privategroups/ThreadedConversationMessageItem.kt deleted file mode 100644 index f9b64bd3a4061aeeaef8912f117be08bdcb3acdd..0000000000000000000000000000000000000000 --- a/src/main/kotlin/org/briarproject/briar/desktop/privategroups/ThreadedConversationMessageItem.kt +++ /dev/null @@ -1,41 +0,0 @@ -package org.briarproject.briar.desktop.privategroups - -import org.briarproject.bramble.api.sync.GroupId -import org.briarproject.bramble.api.sync.MessageId -import org.briarproject.briar.api.conversation.ConversationMessageHeader - -data class ThreadedConversationMessageItem( - var text: String? = null, - override val id: MessageId, - override val groupId: GroupId, - override val time: Long, - override val autoDeleteTimer: Long, - override val isIncoming: Boolean, - override var isRead: Boolean, - override var isSent: Boolean, - override var isSeen: Boolean, - - // todo: support attachments - // val attachments: List<AttachmentItem> -) : ThreadedConversationItem() { - - constructor(h: ConversationMessageHeader) : - this( - id = h.id, - groupId = h.groupId, - time = h.timestamp, - autoDeleteTimer = h.autoDeleteTimer, - isRead = h.isRead, - isSent = h.isSent, - isSeen = h.isSeen, - isIncoming = !h.isLocal, - ) - - override fun mark(sent: Boolean, seen: Boolean): ThreadedConversationItem { - return copy(isSent = sent, isSeen = seen) - } - - override fun markRead(): ThreadedConversationItem { - return copy(isRead = true) - } -} diff --git a/src/main/kotlin/org/briarproject/briar/desktop/privategroups/ThreadedConversationScreen.kt b/src/main/kotlin/org/briarproject/briar/desktop/privategroups/ThreadedConversationScreen.kt index db0b50ffc31f9f53eee2cc97b7d10beccdd50bc4..9dc2541a0aa18f8579795036be39ba955bde1ac2 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/privategroups/ThreadedConversationScreen.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/privategroups/ThreadedConversationScreen.kt @@ -1,116 +1,12 @@ package org.briarproject.briar.desktop.privategroups -import androidx.compose.animation.core.animateDpAsState -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.BoxWithConstraints -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.offset -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.requiredSize -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Scaffold import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -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 -import androidx.compose.ui.unit.dp import org.briarproject.bramble.api.sync.GroupId -import org.briarproject.briar.desktop.contact.ContactInfoDrawer -import org.briarproject.briar.desktop.contact.ContactInfoDrawerState -import org.briarproject.briar.desktop.conversation.ConversationInput -import org.briarproject.briar.desktop.navigation.SIDEBAR_WIDTH -import org.briarproject.briar.desktop.theme.surfaceVariant -import org.briarproject.briar.desktop.ui.Constants.CONTACTLIST_WIDTH -import org.briarproject.briar.desktop.ui.Loader +import org.briarproject.briar.desktop.ui.UiPlaceholder import org.briarproject.briar.desktop.viewmodel.viewModel @Composable fun ThreadedConversationScreen( groupId: GroupId, viewModel: ThreadedConversationViewModel = viewModel(), -) { - LaunchedEffect(groupId) { - viewModel.setGroupId(groupId) - } - - val contactItem = viewModel.contactItem.value - - if (contactItem == null) { - Loader() - return - } - - 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 = { - ThreadedConversationHeader( - contactItem, - onMakeIntroduction = { - setInfoDrawer(true) - } - ) - }, - content = { padding -> - LazyColumn( - verticalArrangement = Arrangement.spacedBy(8.dp), - // reverseLayout to display most recent message (index 0) at the bottom - reverseLayout = true, - contentPadding = PaddingValues(8.dp), - modifier = Modifier.padding(padding).fillMaxHeight() - ) { - items(viewModel.messages) { m -> - if (m is ThreadedConversationMessageItem) - ThreadedText(m) - } - } - }, - bottomBar = { - ConversationInput( - viewModel.newMessage.value, - viewModel::setNewMessage, - viewModel::sendMessage - ) - }, - ) - if (infoDrawer) { - // TODO Find non-hacky way of setting scrim on entire app - Box( - Modifier.offset(-(CONTACTLIST_WIDTH + SIDEBAR_WIDTH)) - .requiredSize(maxWidth + CONTACTLIST_WIDTH + SIDEBAR_WIDTH, maxHeight) - .background(Color(0, 0, 0, 100)) - .clickable( - // prevent visual indication - interactionSource = remember { MutableInteractionSource() }, - indication = null - ) { setInfoDrawer(false) } - ) - Column( - modifier = Modifier.fillMaxHeight().width(CONTACTLIST_WIDTH) - .offset(maxWidth + animatedInfoDrawerOffsetX) - .background( - MaterialTheme.colors.surfaceVariant, - RoundedCornerShape(topStart = 10.dp, bottomStart = 10.dp) - ) - ) { - ContactInfoDrawer(contactItem.privateGroup, setInfoDrawer, contactDrawerState) - } - } - } -} +) = UiPlaceholder() diff --git a/src/main/kotlin/org/briarproject/briar/desktop/privategroups/ThreadedConversationViewModel.kt b/src/main/kotlin/org/briarproject/briar/desktop/privategroups/ThreadedConversationViewModel.kt index 05bc249a66785d6448ac61ebe24c8d442047fee6..fc5c31571a51e5e9a3eaa6603003c9b861beef4b 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/privategroups/ThreadedConversationViewModel.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/privategroups/ThreadedConversationViewModel.kt @@ -1,33 +1,12 @@ package org.briarproject.briar.desktop.privategroups -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.event.ContactRemovedEvent -import org.briarproject.bramble.api.db.DbException -import org.briarproject.bramble.api.db.NoSuchContactException import org.briarproject.bramble.api.event.Event import org.briarproject.bramble.api.event.EventBus -import org.briarproject.bramble.api.sync.GroupId -import org.briarproject.bramble.api.sync.MessageId -import org.briarproject.bramble.api.sync.event.MessagesAckedEvent -import org.briarproject.bramble.api.sync.event.MessagesSentEvent -import org.briarproject.bramble.api.versioning.event.ClientVersionUpdatedEvent -import org.briarproject.bramble.util.LogUtils -import org.briarproject.briar.api.autodelete.UnexpectedTimerException -import org.briarproject.briar.api.autodelete.event.ConversationMessagesDeletedEvent import org.briarproject.briar.api.conversation.ConversationManager -import org.briarproject.briar.api.conversation.event.ConversationMessageReceivedEvent import org.briarproject.briar.api.messaging.MessagingManager -import org.briarproject.briar.api.messaging.PrivateMessage import org.briarproject.briar.api.messaging.PrivateMessageFactory -import org.briarproject.briar.api.messaging.PrivateMessageHeader import org.briarproject.briar.api.privategroup.PrivateGroupManager -import org.briarproject.briar.desktop.utils.replaceIf import org.briarproject.briar.desktop.viewmodel.BriarEventListenerViewModel -import java.util.Date -import java.util.logging.Level import java.util.logging.Logger import javax.inject.Inject @@ -45,188 +24,7 @@ constructor( private val LOG = Logger.getLogger(ThreadedConversationViewModel::class.java.name) } - private val _privateGroupId = mutableStateOf<GroupId?>(null) - private val _privateGroupItem = mutableStateOf<PrivateGroupItem?>(null) - private val _messages = mutableStateListOf<ThreadedConversationItem>() - - private val _newMessage = mutableStateOf("") - - val contactItem: State<PrivateGroupItem?> = _privateGroupItem - val messages: List<ThreadedConversationItem> = _messages - - val newMessage: State<String> = _newMessage - - fun setGroupId(id: GroupId) { - if (_privateGroupId.value == id) - return - - _privateGroupId.value = id - _privateGroupItem.value = PrivateGroupItem( - privateGroupManager.getPrivateGroup(id), - privateGroupManager.getGroupCount(id), - ) - loadMessages() - setNewMessage("") - } - - fun setNewMessage(msg: String) { - _newMessage.value = msg - } - - fun sendMessage() { - try { - val text = _newMessage.value - _newMessage.value = "" - - // don't send empty or blank messages - if (text.isBlank()) return - - val start = LogUtils.now() - val m = createMessage(text) - messagingManager.addLocalMessage(m) - LogUtils.logDuration(LOG, "Storing message", start) - - val message = m.message - val h = PrivateMessageHeader( - message.id, message.groupId, - message.timestamp, true, true, false, false, - m.hasText(), m.attachmentHeaders, - m.autoDeleteTimer - ) - _messages.add(0, messageHeaderToItem(h)) - // eventBus.broadcast(ConversationMessageToBeSentEvent(h, _contactId.value!!)) - } catch (e: UnexpectedTimerException) { - LogUtils.logException(LOG, Level.WARNING, e) - } catch (e: DbException) { - LogUtils.logException(LOG, Level.WARNING, e) - } - } - - @Throws(DbException::class) - private fun createMessage(text: String): PrivateMessage { - val groupId = _privateGroupItem.value!!.privateGroup.id - // todo: this API call needs a database transaction context - // val timestamp = conversationManager.getTimestampForOutgoingMessage(_contactId.value!!) - val timestamp = Date().time - try { - return privateMessageFactory.createLegacyPrivateMessage( - groupId, timestamp, text - ) - } catch (e: FormatException) { - throw AssertionError(e) - } - } - - private fun loadMessages() { - try { - val start = LogUtils.now() - val headers = privateGroupManager.getHeaders(_privateGroupId.value!!) - LogUtils.logDuration(LOG, "Loading message headers", start) - // Sort headers by timestamp in *descending* order - val sorted = headers.sortedByDescending { it.timestamp } - _messages.apply { - clear() - val start = LogUtils.now() - addAll( - // todo: use ConversationVisitor to also display Request and Notice Messages - sorted.filterIsInstance<PrivateMessageHeader>().map(::messageHeaderToItem) - ) - LogUtils.logDuration(LOG, "Loading messages", start) - } - } catch (e: NoSuchContactException) { - LogUtils.logException(LOG, Level.WARNING, e) - } catch (e: DbException) { - LogUtils.logException(LOG, Level.WARNING, e) - } - } - - private fun messageHeaderToItem(h: PrivateMessageHeader): ThreadedConversationMessageItem { - // todo: use ConversationVisitor instead and support other MessageHeader - val item = ThreadedConversationMessageItem(h) - if (h.hasText()) { - item.text = loadMessageText(h.id) - } else { - LOG.warning { "private message without text" } - } - return item - } - - private fun loadMessageText(m: MessageId): String? { - try { - return messagingManager.getMessageText(m) - } catch (e: DbException) { - LogUtils.logException(LOG, Level.WARNING, e) - } - return null - } - override fun eventOccurred(e: Event?) { - when (e) { - is ContactRemovedEvent -> { - if (e.contactId == _privateGroupId.value) { - LOG.info("Contact removed") - // todo: we probably don't need to react to this here as the ContactsViewModel should already handle it - } - } - is ConversationMessageReceivedEvent<*> -> { - if (e.contactId == _privateGroupId.value) { - LOG.info("Message received, adding") - val h = e.messageHeader - if (h is PrivateMessageHeader) { - // insert at start of list according to descending sort order - _messages.add(0, messageHeaderToItem(h)) - } - } - } - is MessagesSentEvent -> { - if (e.contactId == _privateGroupId.value) { - LOG.info("Messages sent") - markMessages(e.messageIds, sent = true, seen = false) - } - } - is MessagesAckedEvent -> { - if (e.contactId == _privateGroupId.value) { - LOG.info("Messages acked") - markMessages(e.messageIds, sent = true, seen = true) - } - } - is ConversationMessagesDeletedEvent -> { - if (e.contactId == _privateGroupId.value) { - LOG.info("Messages auto-deleted") - val messages = HashSet(e.messageIds) - _messages.removeIf { messages.contains(it.id) } - } - } - /* - is ContactConnectedEvent -> { - if (e.contactId == _privateGroupId.value) { - LOG.info("Contact connected") - _privateGroupItem.value = _privateGroupItem.value!!.updateIsConnected(true) - } - } - is ContactDisconnectedEvent -> { - if (e.contactId == _privateGroupId.value) { - LOG.info("Contact disconnected") - _privateGroupItem.value = _privateGroupItem.value!!.updateIsConnected(false) - } - } - */ - is ClientVersionUpdatedEvent -> { - if (e.contactId == _privateGroupId.value) { - // todo: still not implemented - } - } - } - } - - private fun markMessages( - messageIds: Collection<MessageId>, - sent: Boolean, - seen: Boolean - ) { - val messages = HashSet(messageIds) - _messages.replaceIf({ !it.isIncoming && messages.contains(it.id) }) { - it.mark(sent, seen) - } + // TODO } } diff --git a/src/main/kotlin/org/briarproject/briar/desktop/privategroups/ThreadedText.kt b/src/main/kotlin/org/briarproject/briar/desktop/privategroups/ThreadedText.kt deleted file mode 100644 index 4b5eabad729c1f2dcaabae2ab265ee8870a5dc94..0000000000000000000000000000000000000000 --- a/src/main/kotlin/org/briarproject/briar/desktop/privategroups/ThreadedText.kt +++ /dev/null @@ -1,58 +0,0 @@ -package org.briarproject.briar.desktop.privategroups - -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -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.MaterialTheme -import androidx.compose.material.Text -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Done -import androidx.compose.material.icons.filled.DoneAll -import androidx.compose.material.icons.filled.Schedule -import androidx.compose.runtime.Composable -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.briar.desktop.theme.awayMsgBubble -import org.briarproject.briar.desktop.theme.localMsgBubble -import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n -import org.briarproject.briar.desktop.utils.TimeUtils - -@Composable -fun ThreadedText(m: ThreadedConversationMessageItem) { - val alignment = if (m.isIncoming) Alignment.Start else Alignment.End - val color = if (m.isIncoming) MaterialTheme.colors.awayMsgBubble else MaterialTheme.colors.localMsgBubble - val shape = if (m.isIncoming) - RoundedCornerShape(topStart = 10.dp, topEnd = 10.dp, bottomEnd = 10.dp) - else - RoundedCornerShape(topStart = 10.dp, topEnd = 10.dp, bottomStart = 10.dp) - - Column(Modifier.fillMaxWidth()) { - Column(Modifier.fillMaxWidth(fraction = 0.8f).align(alignment)) { - Card(Modifier.align(alignment), backgroundColor = color, shape = shape) { - Column( - Modifier.padding(8.dp) - ) { - Text(m.text!!, fontSize = 14.sp, modifier = Modifier.align(Alignment.Start)) - Row(modifier = Modifier.padding(top = 4.dp)) { - Text(TimeUtils.getFormattedTimestamp(m.time), Modifier.padding(end = 4.dp), fontSize = 10.sp) - if (!m.isIncoming) { - val modifier = Modifier.size(12.dp).align(Alignment.CenterVertically) - val icon = - if (m.isSeen) Icons.Filled.DoneAll // acknowledged - else if (m.isSent) Icons.Filled.Done // sent - else Icons.Filled.Schedule // waiting - Icon(icon, i18n("access.message.sent"), modifier) - } - } - } - } - } - } -}