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 0d3dfcfd7c1911b6442f13c51e486c688809a561..91bf6153a9ae5dd0c166e3ef2eb1515f5f555409 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 @@ -29,6 +29,7 @@ import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size 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.Text @@ -38,6 +39,11 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.semantics.text +import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.style.TextOverflow.Companion.Ellipsis import androidx.compose.ui.unit.dp import org.briarproject.bramble.api.contact.ContactId @@ -49,8 +55,11 @@ import org.briarproject.briar.desktop.ui.Constants.HEADER_SIZE import org.briarproject.briar.desktop.ui.HorizontalDivider import org.briarproject.briar.desktop.ui.MessageCounter 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.preview import org.briarproject.briar.desktop.utils.TimeUtils.getFormattedTimestamp +import org.briarproject.briar.desktop.utils.appendCommaSeparated import java.time.Instant @Suppress("HardCodedStringLiteral") @@ -63,7 +72,7 @@ fun main() = preview( "timestamp" to Instant.now().toEpochMilli(), "selected" to false, ) { - Column { + Column(Modifier.selectableGroup()) { ContactCard( ContactItem( idWrapper = RealContactIdWrapper(ContactId(0)), @@ -103,9 +112,14 @@ fun ContactCard( modifier = Modifier .fillMaxWidth() .defaultMinSize(minHeight = HEADER_SIZE) - .selectable(selected, onClick = onSel) - .background(bgColor) - .padding(vertical = 8.dp), + .semantics { + contentDescription = if (selected) i18n("access.list.selected.yes") + else i18n("access.list.selected.no") + // todo: stateDescription apparently not used + // stateDescription = if (selected) "selected" else "not selected" + } + .selectable(selected, onClick = onSel, role = Role.Button) + .background(bgColor), verticalArrangement = Arrangement.Center ) { when (contactItem) { @@ -124,11 +138,33 @@ fun ContactCard( private fun RealContactRow(contactItem: ContactItem) { Row( horizontalArrangement = Arrangement.SpaceBetween, - modifier = Modifier.fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .semantics { + text = buildAnnotatedString { + append(i18nF("access.contact.with_name", contactItem.displayName)) + appendCommaSeparated( + if (contactItem.isConnected) i18n("access.contact.connected.yes") + else i18n("access.contact.connected.no") + ) + if (contactItem.unread > 0) + appendCommaSeparated(i18nP("access.contact.unread_count", contactItem.unread)) + if (contactItem.isEmpty) + appendCommaSeparated(i18n("contacts.card.nothing")) + else + appendCommaSeparated( + i18nF( + "access.contact.last_message_timestamp", + getFormattedTimestamp(contactItem.timestamp) + ) + ) + append('.') + } + } ) { Row( - modifier = Modifier.padding(start = 16.dp, end = 8.dp) + modifier = Modifier.padding(start = 16.dp, end = 8.dp).padding(vertical = 8.dp) ) { Box { ProfileCircle(36.dp, contactItem) @@ -152,11 +188,24 @@ private fun RealContactRow(contactItem: ContactItem) { private fun PendingContactRow(contactItem: PendingContactItem, onRemove: () -> Unit) { Row( horizontalArrangement = Arrangement.SpaceBetween, - modifier = Modifier.fillMaxWidth(), + modifier = Modifier + .fillMaxWidth() + .semantics { + text = buildAnnotatedString { + append(i18nF("access.contact.pending.with_name", contactItem.displayName)) + // todo: include pending status + appendCommaSeparated( + i18nF( + "access.contact.pending.added_timestamp", + getFormattedTimestamp(contactItem.timestamp) + ) + ) + } + }, verticalAlignment = Alignment.CenterVertically ) { Row( - modifier = Modifier.padding(start = 16.dp, end = 8.dp) + modifier = Modifier.padding(start = 16.dp, end = 8.dp).padding(vertical = 8.dp) ) { ProfileCircle(36.dp) PendingContactInfo( diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactList.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactList.kt index 8aff4a2d84bae10b7d1c745cfd90eea7a2b8aa1f..f7f6bff50d06c0190407b3872c65f193bbf2a56d 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactList.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactList.kt @@ -21,7 +21,6 @@ package org.briarproject.briar.desktop.contact import androidx.compose.foundation.VerticalScrollbar import androidx.compose.foundation.layout.Box 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.fillMaxWidth @@ -37,10 +36,13 @@ import androidx.compose.material.Surface import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.dp import org.briarproject.briar.desktop.theme.surfaceVariant import org.briarproject.briar.desktop.ui.Constants.COLUMN_WIDTH import org.briarproject.briar.desktop.ui.Constants.HEADER_SIZE +import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n @Composable fun ContactList( @@ -70,7 +72,14 @@ fun ContactList( } Box(modifier = Modifier.fillMaxSize()) { - LazyColumn(state = scrollState, modifier = Modifier.selectableGroup()) { + LazyColumn( + state = scrollState, + modifier = Modifier + .semantics { + contentDescription = i18n("access.contact.list") + } + .selectableGroup() + ) { items(contactList) { contactItem -> ContactCard( contactItem, diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/introduction/ContactDrawerMakeIntro.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/introduction/ContactDrawerMakeIntro.kt index 49e33ff1fbac87d5a974408ca156e7c032143f36..de17bd810dce5f79345f5b1ee9c5795eb82fe79b 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/introduction/ContactDrawerMakeIntro.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/introduction/ContactDrawerMakeIntro.kt @@ -20,7 +20,6 @@ package org.briarproject.briar.desktop.introduction import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/utils/AnnotatedStringUtils.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/utils/AnnotatedStringUtils.kt new file mode 100644 index 0000000000000000000000000000000000000000..e1496c4ee7df07eb646286fa0a0b68b92df9e2e7 --- /dev/null +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/utils/AnnotatedStringUtils.kt @@ -0,0 +1,29 @@ +/* + * Briar Desktop + * Copyright (C) 2021-2022 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 + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +package org.briarproject.briar.desktop.utils + +import androidx.compose.ui.text.AnnotatedString + +fun AnnotatedString.Builder.appendLeading(leading: String, text: String? = null) { + append(leading) + if (text != null) append(text) +} + +fun AnnotatedString.Builder.appendCommaSeparated(text: String? = null) = + appendLeading(", ", text) diff --git a/briar-desktop/src/main/resources/strings/BriarDesktop.properties b/briar-desktop/src/main/resources/strings/BriarDesktop.properties index 0586f6f815ec43e8f8be53da492969986a6ae05b..c390a330870c18ad1b59e8a3204a9af1d4e3bca3 100644 --- a/briar-desktop/src/main/resources/strings/BriarDesktop.properties +++ b/briar-desktop/src/main/resources/strings/BriarDesktop.properties @@ -2,6 +2,14 @@ access.attachment_add=Add attachment access.attachment_remove=Remove attachment access.contacts.add=Add contact +access.contact.list=contact list +access.contact.with_name=Contact "{0}" +access.contact.connected.yes=connected +access.contact.connected.no=not connected +access.contact.unread_count={0, plural, one {one unread message} other {{0} unread messages}} +access.contact.last_message_timestamp=last message: {0} +access.contact.pending.with_name=Pending contact "{0}" +access.contact.pending.added_timestamp=added: {0} access.contact.menu=Show contact menu access.contacts.dropdown.connections.expand=Expand connection menu access.contacts.dropdown.contacts.expand=Expand contact menu @@ -12,6 +20,8 @@ access.introduction.close=Close introduction screen access.message.jump_to_unread=Jump to next unread message access.message.send=Send message access.message.sent=Message sent +access.list.selected.yes=currently selected +access.list.selected.no=currently not selected, click to select access.logo=Briar logo access.swap=Icon showing errors between two contacts access.mode.contacts=Contacts @@ -34,7 +44,7 @@ contacts.none_selected.title=No contact selected contacts.none_selected.hint=Select a contact to start chatting contacts.pending_selected.title=Pending contact selected contacts.pending_selected.hint=You need to wait until the process of adding each other as contacts has finished before you can start chatting. -contacts.card.nothing=No messages. +contacts.card.nothing=No messages contacts.dropdown.connections=Connections contacts.dropdown.connections.title=Connections contacts.dropdown.connections.bluetooth=Connect via Bluetooth