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 2a8e0a1eede1978d673530da6ee6f0f38f373019..fddcb4f6c2a4dc34fadb9c1188569be59906e645 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 @@ -20,6 +20,7 @@ package org.briarproject.briar.desktop.contact import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Arrangement.spacedBy import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -32,6 +33,7 @@ import androidx.compose.foundation.selection.selectable import androidx.compose.foundation.selection.selectableGroup import androidx.compose.material.IconButton import androidx.compose.material.MaterialTheme +import androidx.compose.material.ProvideTextStyle import androidx.compose.material.Text import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Delete @@ -49,13 +51,16 @@ import org.briarproject.bramble.api.contact.ContactId import org.briarproject.bramble.api.contact.PendingContactId import org.briarproject.bramble.api.contact.PendingContactState import org.briarproject.bramble.api.identity.AuthorId +import org.briarproject.briar.api.identity.AuthorInfo.Status import org.briarproject.briar.desktop.theme.selectedCard import org.briarproject.briar.desktop.ui.Constants.HEADER_SIZE import org.briarproject.briar.desktop.ui.HorizontalDivider import org.briarproject.briar.desktop.ui.NumberBadge +import org.briarproject.briar.desktop.ui.TrustIndicatorLong import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18nF import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18nP +import org.briarproject.briar.desktop.utils.PreviewUtils.DropDownValues import org.briarproject.briar.desktop.utils.PreviewUtils.preview import org.briarproject.briar.desktop.utils.TimeUtils.getFormattedTimestamp import org.briarproject.briar.desktop.utils.appendCommaSeparated @@ -66,6 +71,7 @@ import java.time.Instant fun main() = preview( "name" to "Paul", "alias" to "UI Master", + "trustLevel" to DropDownValues(0, Status.values().filterNot { it == Status.NONE }.map { it.name }), "isConnected" to true, "isEmpty" to false, "unread" to 3, @@ -77,6 +83,7 @@ fun main() = preview( ContactItem( idWrapper = RealContactIdWrapper(ContactId(0)), authorId = AuthorId(getRandomIdPersistent()), + trustLevel = Status.valueOf(getStringParameter("trustLevel")), name = getStringParameter("name"), alias = getStringParameter("alias"), isConnected = getBooleanParameter("isConnected"), @@ -164,7 +171,8 @@ private fun RealContactRow(contactItem: ContactItem) { } ) { Row( - modifier = Modifier.padding(start = 16.dp, end = 8.dp).padding(vertical = 8.dp) + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.padding(start = 16.dp, end = 8.dp).padding(vertical = 8.dp), ) { Box { ProfileCircle(36.dp, contactItem) @@ -223,14 +231,20 @@ private fun PendingContactRow(contactItem: PendingContactItem, onRemove: () -> U @Composable private fun RealContactInfo(contactItem: ContactItem, modifier: Modifier = Modifier) { - Column(modifier = modifier.padding(start = 12.dp)) { + Column( + horizontalAlignment = Alignment.Start, + verticalArrangement = spacedBy(2.dp), + modifier = modifier.padding(start = 12.dp), + ) { Text( - contactItem.displayName, + text = contactItem.displayName, style = MaterialTheme.typography.body1, maxLines = 3, overflow = Ellipsis, - modifier = Modifier.align(Alignment.Start).padding(bottom = 2.dp) ) + ProvideTextStyle(MaterialTheme.typography.caption) { + TrustIndicatorLong(contactItem.trustLevel) + } Text( if (contactItem.isEmpty) i18n("contacts.card.nothing") else getFormattedTimestamp( contactItem.timestamp diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactItem.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactItem.kt index b17f536a1c8d6bc0a938ba3e94dbe447f0a20300..0321f6d0616f61e691f3cf7718b0bef6f36b795c 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactItem.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactItem.kt @@ -22,12 +22,14 @@ import androidx.compose.ui.graphics.ImageBitmap import org.briarproject.bramble.api.contact.Contact import org.briarproject.bramble.api.identity.AuthorId import org.briarproject.briar.api.client.MessageTracker +import org.briarproject.briar.api.identity.AuthorInfo import org.briarproject.briar.desktop.utils.UiUtils.getContactDisplayName import kotlin.math.max data class ContactItem( override val idWrapper: RealContactIdWrapper, val authorId: AuthorId, + val trustLevel: AuthorInfo.Status, private val name: String, val alias: String?, val isConnected: Boolean, @@ -41,12 +43,14 @@ data class ContactItem( constructor( contact: Contact, + authorInfo: AuthorInfo, isConnected: Boolean, groupCount: MessageTracker.GroupCount, avatar: ImageBitmap? ) : this( idWrapper = RealContactIdWrapper(contact.id), authorId = contact.author.id, + trustLevel = authorInfo.status, name = contact.author.name, alias = contact.alias, isConnected = isConnected, diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactsViewModel.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactsViewModel.kt index 2fe5fc8caefc322e1da60359e2f3cbe15cceede5..f8af78fb4c3988bbbc446fbc572275e31b2eb242 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactsViewModel.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactsViewModel.kt @@ -38,7 +38,7 @@ import org.briarproject.briar.api.attachment.AttachmentReader import org.briarproject.briar.api.conversation.ConversationManager import org.briarproject.briar.api.identity.AuthorManager import org.briarproject.briar.desktop.threading.BriarExecutors -import org.briarproject.briar.desktop.utils.ImageUtils.loadAvatar +import org.briarproject.briar.desktop.utils.ImageUtils import org.briarproject.briar.desktop.utils.KLoggerUtils.i import org.briarproject.briar.desktop.utils.clearAndAddAll import org.briarproject.briar.desktop.utils.removeFirst @@ -83,11 +83,13 @@ abstract class ContactsViewModel( ) contactList.addAll( contactManager.getContacts(txn).map { contact -> + val authorInfo = authorManager.getAuthorInfo(txn, contact) ContactItem( contact, + authorInfo, connectionRegistry.isConnected(contact.id), conversationManager.getGroupCount(txn, contact.id), - loadAvatar(authorManager, attachmentReader, txn, contact), + authorInfo.avatarHeader?.let { ImageUtils.loadImage(txn, attachmentReader, it) }, ) } ) diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationViewModel.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationViewModel.kt index 4f7a9b4a11cd8ab95782a7af49e73f4f01ede236..2df8d312002f14035387dff51e6b5c464d8861d1 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationViewModel.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationViewModel.kt @@ -61,7 +61,7 @@ import org.briarproject.briar.desktop.attachment.media.ImageCompressor import org.briarproject.briar.desktop.contact.ContactItem import org.briarproject.briar.desktop.conversation.ConversationRequestItem.RequestType.INTRODUCTION import org.briarproject.briar.desktop.threading.BriarExecutors -import org.briarproject.briar.desktop.utils.ImageUtils.loadAvatar +import org.briarproject.briar.desktop.utils.ImageUtils import org.briarproject.briar.desktop.utils.KLoggerUtils.i import org.briarproject.briar.desktop.utils.KLoggerUtils.logDuration import org.briarproject.briar.desktop.utils.KLoggerUtils.w @@ -280,12 +280,13 @@ constructor( val start = LogUtils.now() val contact = contactManager.getContact(txn, id) - + val authorInfo = authorManager.getAuthorInfo(txn, contact) val contactItem = ContactItem( contact, + authorInfo, connectionRegistry.isConnected(id), conversationManager.getGroupCount(txn, id), - loadAvatar(authorManager, attachmentReader, txn, contact), + authorInfo.avatarHeader?.let { ImageUtils.loadImage(txn, attachmentReader, it) }, ) LOG.logDuration("Loading contact", start) txn.attach { diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ThreadItemView.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ThreadItemView.kt index 48e4e108ce4cd7b3ab81d6f6778bf7cf00e9892f..92654b2e3efd1e930cdeabc3578cf99a6729c4f3 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ThreadItemView.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ThreadItemView.kt @@ -48,7 +48,7 @@ import org.briarproject.briar.desktop.contact.ProfileCircle import org.briarproject.briar.desktop.theme.Blue500 import org.briarproject.briar.desktop.ui.HorizontalDivider import org.briarproject.briar.desktop.ui.Tooltip -import org.briarproject.briar.desktop.ui.TrustIndicator +import org.briarproject.briar.desktop.ui.TrustIndicatorShort import org.briarproject.briar.desktop.ui.VerticalDivider import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n import org.briarproject.briar.desktop.utils.PreviewUtils.preview @@ -136,7 +136,7 @@ fun ThreadItemContentComposable( maxLines = 1, overflow = TextOverflow.Ellipsis, ) - TrustIndicator(item.authorInfo.status) + TrustIndicatorShort(item.authorInfo.status) } Text( text = getFormattedTimestamp(item.timestamp), diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/TrustIndicator.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/TrustIndicator.kt index 608d7a0e85ea2c35605bd008d680a18288baf438..73bb089b58bcd3bb90c45fbe20c8121e9f2101ce 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/TrustIndicator.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/TrustIndicator.kt @@ -19,6 +19,8 @@ package org.briarproject.briar.desktop.ui import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.layout.Arrangement.spacedBy +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.size import androidx.compose.material.Icon import androidx.compose.material.MaterialTheme @@ -26,6 +28,7 @@ import androidx.compose.material.Text import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Person import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.buildAnnotatedString @@ -33,7 +36,12 @@ import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.withStyle import androidx.compose.ui.unit.dp -import org.briarproject.briar.api.identity.AuthorInfo +import org.briarproject.briar.api.identity.AuthorInfo.Status +import org.briarproject.briar.api.identity.AuthorInfo.Status.NONE +import org.briarproject.briar.api.identity.AuthorInfo.Status.OURSELVES +import org.briarproject.briar.api.identity.AuthorInfo.Status.UNKNOWN +import org.briarproject.briar.api.identity.AuthorInfo.Status.UNVERIFIED +import org.briarproject.briar.api.identity.AuthorInfo.Status.VERIFIED import org.briarproject.briar.desktop.theme.Lime500 import org.briarproject.briar.desktop.theme.Orange500 import org.briarproject.briar.desktop.theme.Red500 @@ -41,16 +49,32 @@ import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n @OptIn(ExperimentalFoundationApi::class) @Composable -fun TrustIndicator(status: AuthorInfo.Status) = Tooltip( - text = when (status) { - AuthorInfo.Status.NONE -> error("Unexpected status: $status") - AuthorInfo.Status.UNKNOWN -> i18n("peer.trust.stranger") - AuthorInfo.Status.UNVERIFIED -> i18n("peer.trust.unverified") - AuthorInfo.Status.VERIFIED -> i18n("peer.trust.verified") - AuthorInfo.Status.OURSELVES -> i18n("peer.trust.ourselves") - } +fun TrustIndicatorShort(status: Status) = Tooltip( + text = getDescription(status) +) { + TrustIndicatorContent(status) +} + +@Composable +fun TrustIndicatorLong(status: Status) = Row( + horizontalArrangement = spacedBy(2.dp), + verticalAlignment = Alignment.CenterVertically, ) { - if (status == AuthorInfo.Status.OURSELVES) { + TrustIndicatorContent(status) + Text(getDescription(status)) +} + +private fun getDescription(status: Status) = when (status) { + NONE -> error("Unexpected status: $status") + UNKNOWN -> i18n("peer.trust.stranger") + UNVERIFIED -> i18n("peer.trust.unverified") + VERIFIED -> i18n("peer.trust.verified") + OURSELVES -> i18n("peer.trust.ourselves") +} + +@Composable +private fun TrustIndicatorContent(status: Status) { + if (status == OURSELVES) { Icon( imageVector = Icons.Filled.Person, contentDescription = i18n("access.ourselves"), @@ -60,11 +84,11 @@ fun TrustIndicator(status: AuthorInfo.Status) = Tooltip( } else { val gray = MaterialTheme.colors.onSurface val (first, second, third) = when (status) { - AuthorInfo.Status.NONE -> error("Unexpected status: $status") - AuthorInfo.Status.UNKNOWN -> Triple(Red500, gray, gray) - AuthorInfo.Status.UNVERIFIED -> Triple(Orange500, Orange500, gray) - AuthorInfo.Status.VERIFIED -> Triple(Lime500, Lime500, Lime500) - AuthorInfo.Status.OURSELVES -> error("Unexpected status: $status") + NONE -> error("Unexpected status: $status") + UNKNOWN -> Triple(Red500, gray, gray) + UNVERIFIED -> Triple(Orange500, Orange500, gray) + VERIFIED -> Triple(Lime500, Lime500, Lime500) + OURSELVES -> error("Unexpected status: $status") } Text( text = buildAnnotatedString { diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/utils/ImageUtils.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/utils/ImageUtils.kt index 27a455182e564c2184e421dafd4d73681b2f8655..9bdbdd9b7bd7cd392ba93d0062910ffcf0c53a9a 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/utils/ImageUtils.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/utils/ImageUtils.kt @@ -20,25 +20,12 @@ package org.briarproject.briar.desktop.utils import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.res.loadImageBitmap -import org.briarproject.bramble.api.contact.Contact import org.briarproject.bramble.api.db.Transaction import org.briarproject.briar.api.attachment.AttachmentHeader import org.briarproject.briar.api.attachment.AttachmentReader -import org.briarproject.briar.api.identity.AuthorManager object ImageUtils { - fun loadAvatar( - authorManager: AuthorManager, - attachmentReader: AttachmentReader, - txn: Transaction, - contact: Contact, - ): ImageBitmap? { - val authorInfo = authorManager.getAuthorInfo(txn, contact) - val avatarHeader = authorInfo.avatarHeader ?: return null - return loadImage(txn, attachmentReader, avatarHeader) - } - fun loadImage( txn: Transaction, attachmentReader: AttachmentReader, diff --git a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/contact/ContactItemTest.kt b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/contact/ContactItemTest.kt index 9e4034d52b329956b99aed6b4017cc9dfcd34563..7b927614517101499c2dffd5ea61de4fbf52dfe6 100644 --- a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/contact/ContactItemTest.kt +++ b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/contact/ContactItemTest.kt @@ -26,6 +26,7 @@ import org.briarproject.bramble.api.crypto.SignaturePublicKey import org.briarproject.bramble.api.identity.Author import org.briarproject.bramble.api.identity.AuthorId import org.briarproject.briar.api.client.MessageTracker +import org.briarproject.briar.api.identity.AuthorInfo import kotlin.random.Random import kotlin.test.Test import kotlin.test.assertEquals @@ -53,6 +54,7 @@ class ContactItemTest { ) val item = ContactItem( contact = contact, + authorInfo = AuthorInfo(AuthorInfo.Status.UNKNOWN), isConnected = false, groupCount = MessageTracker.GroupCount(0, 0, System.currentTimeMillis()), avatar = null