diff --git a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationItemView.kt b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationItemView.kt index 6a5507360948cbc9053871709f0fb16c4e98a2e6..d746990c38859cccfb836c8f874665cde0568a80 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationItemView.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationItemView.kt @@ -42,8 +42,8 @@ fun main() = preview { item { ConversationNoticeItemView( ConversationNoticeItem( - msgText = "Let's test a received notice message.", - text = "Text of notice message.", + notice = "Text of notice message.", + text = "Let's test a received notice message.", id = MessageId(getRandomId()), groupId = GroupId(getRandomId()), time = Instant.now().toEpochMilli(), @@ -77,8 +77,8 @@ fun main() = preview { requestType = INTRODUCTION, sessionId = SessionId(getRandomId()), answered = false, - msgText = "Short message.", - text = "Text of notice message.", + notice = "Text of notice message.", + text = "Short message.", id = MessageId(getRandomId()), groupId = GroupId(getRandomId()), time = Instant.now().toEpochMilli(), @@ -93,8 +93,8 @@ fun main() = preview { item { ConversationNoticeItemView( ConversationNoticeItem( - msgText = "This is a long long long message that spans over several lines.\n\nIt ends here.", - text = "Text of notice message.", + notice = "Text of notice message.", + text = "This is a long long long message that spans over several lines.\n\nIt ends here.", id = MessageId(getRandomId()), groupId = GroupId(getRandomId()), time = Instant.now().toEpochMilli(), @@ -121,6 +121,22 @@ fun main() = preview { ) ) } + item { + ConversationNoticeItemView( + ConversationNoticeItem( + notice = "Text of notice message.", + text = null, + id = MessageId(getRandomId()), + groupId = GroupId(getRandomId()), + time = Instant.now().toEpochMilli(), + autoDeleteTimer = 0, + isIncoming = false, + isRead = false, + isSent = true, + isSeen = true, + ) + ) + } } } diff --git a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationNoticeItem.kt b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationNoticeItem.kt index a3858be9257e88dd27f9abf4ed04c2d6e9faa3c7..91df76f236f7871415ea49ef27a5986d0605b9de 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationNoticeItem.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationNoticeItem.kt @@ -6,7 +6,7 @@ import org.briarproject.briar.api.conversation.ConversationRequest import org.briarproject.briar.api.conversation.ConversationResponse data class ConversationNoticeItem( - val msgText: String, + val notice: String, override var text: String?, override val id: MessageId, override val groupId: GroupId, @@ -18,8 +18,8 @@ data class ConversationNoticeItem( override var isSeen: Boolean, ) : ConversationItem() { - constructor(msgText: String, r: ConversationRequest<*>) : this( - msgText = msgText, + constructor(notice: String, r: ConversationRequest<*>) : this( + notice = notice, text = r.text, id = r.id, groupId = r.groupId, @@ -31,8 +31,8 @@ data class ConversationNoticeItem( isIncoming = !r.isLocal, ) - constructor(msgText: String, r: ConversationResponse) : this( - msgText = msgText, + constructor(notice: String, r: ConversationResponse) : this( + notice = notice, text = null, id = r.id, groupId = r.groupId, diff --git a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationNoticeItemView.kt b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationNoticeItemView.kt index 6bea0b49922220fb54bdfd8c41c66fecb462fda1..fab635a35b9731315f2395f39698e1dd3d44b2eb 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationNoticeItemView.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationNoticeItemView.kt @@ -26,8 +26,8 @@ import org.briarproject.briar.desktop.utils.PreviewUtils.preview import java.time.Instant fun main() = preview( - "msgText" to "This is a long long long message that spans over several lines.\n\nIt ends here.", - "text" to "Text of notice message.", + "notice" to "Text of notice message.", + "text" to "This is a long long long message that spans over several lines.\n\nIt ends here.", "time" to Instant.now().toEpochMilli(), "isIncoming" to false, "isRead" to false, @@ -36,7 +36,7 @@ fun main() = preview( ) { ConversationNoticeItemView( ConversationNoticeItem( - msgText = getStringParameter("msgText"), + notice = getStringParameter("notice"), text = getStringParameter("text"), id = MessageId(getRandomId()), groupId = GroupId(getRandomId()), @@ -55,19 +55,22 @@ fun ConversationNoticeItemView(m: ConversationNoticeItem) { val textColor = if (m.isIncoming) MaterialTheme.colors.textPrimary else Color.White val noticeBackground = if (m.isIncoming) MaterialTheme.colors.noticeIn else MaterialTheme.colors.noticeOut val noticeColor = if (m.isIncoming) MaterialTheme.colors.textSecondary else MaterialTheme.colors.privateMessageDate + val text = m.text ConversationItemView(m) { Column(Modifier.width(IntrinsicSize.Max)) { - Text( - m.msgText, - fontSize = 16.sp, - color = textColor, - modifier = Modifier.padding(12.dp, 8.dp).align(Alignment.Start) - ) + if (text != null) { + Text( + text, + fontSize = 16.sp, + color = textColor, + modifier = Modifier.padding(12.dp, 8.dp).align(Alignment.Start) + ) + } Column( Modifier.fillMaxWidth().background(noticeBackground).padding(12.dp, 8.dp) ) { Text( - text = m.text!!, + text = m.notice, fontSize = 14.sp, fontStyle = FontStyle.Italic, color = noticeColor, diff --git a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationRequestItem.kt b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationRequestItem.kt index a960f2988fbf58486d35157c5ce61b8410f5c3e9..ec1f3e69880d5f052bc885ae5f3221e0afa8c5b0 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationRequestItem.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationRequestItem.kt @@ -12,7 +12,7 @@ data class ConversationRequestItem( val requestType: RequestType, val sessionId: SessionId, val answered: Boolean, - val msgText: String, + val notice: String, override var text: String?, override val id: MessageId, override val groupId: GroupId, @@ -28,12 +28,12 @@ data class ConversationRequestItem( INTRODUCTION, FORUM, BLOG, GROUP } - constructor(msgText: String, type: RequestType, r: ConversationRequest<*>) : this( + constructor(notice: String, type: RequestType, r: ConversationRequest<*>) : this( requestedGroupId = if (r is InvitationRequest) (r.nameable as Shareable).id else null, requestType = type, sessionId = r.sessionId, answered = r.wasAnswered(), - msgText = msgText, + notice = notice, text = r.text, id = r.id, groupId = r.groupId, diff --git a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationRequestItemView.kt b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationRequestItemView.kt index 07b6bf6d6f5fb75ed4e6a7efc6b4046e80d25484..6a9614840ae79b811dca85015f5a83d0a47a93f5 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationRequestItemView.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationRequestItemView.kt @@ -28,12 +28,13 @@ import org.briarproject.briar.desktop.theme.noticeOut import org.briarproject.briar.desktop.theme.privateMessageDate import org.briarproject.briar.desktop.theme.textPrimary import org.briarproject.briar.desktop.theme.textSecondary +import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n import org.briarproject.briar.desktop.utils.PreviewUtils.preview import java.time.Instant fun main() = preview( - "msgText" to "Short message", - "text" to "Text of notice message.", + "notice" to "Text of notice message.", + "text" to "Short message", "time" to Instant.now().toEpochMilli(), "isIncoming" to true, "isRead" to false, @@ -46,7 +47,7 @@ fun main() = preview( requestType = INTRODUCTION, sessionId = SessionId(getRandomId()), answered = false, - msgText = getStringParameter("msgText"), + notice = getStringParameter("notice"), text = getStringParameter("text"), id = MessageId(getRandomId()), groupId = GroupId(getRandomId()), @@ -61,7 +62,11 @@ fun main() = preview( } @Composable -fun ConversationRequestItemView(m: ConversationRequestItem) { +fun ConversationRequestItemView( + m: ConversationRequestItem, + onAccept: () -> Unit = {}, + onDecline: () -> Unit = {} +) { val statusAlignment = if (m.isIncoming) Alignment.End else Alignment.Start val textColor = if (m.isIncoming) MaterialTheme.colors.textPrimary else Color.White val noticeBackground = if (m.isIncoming) MaterialTheme.colors.noticeIn else MaterialTheme.colors.noticeOut @@ -69,7 +74,7 @@ fun ConversationRequestItemView(m: ConversationRequestItem) { ConversationItemView(m) { Column(Modifier.width(IntrinsicSize.Max)) { Text( - m.msgText, + m.text!!, fontSize = 16.sp, color = textColor, modifier = Modifier.padding(12.dp, 8.dp).align(Alignment.Start) @@ -78,18 +83,26 @@ fun ConversationRequestItemView(m: ConversationRequestItem) { Modifier.fillMaxWidth().background(noticeBackground).padding(12.dp, 8.dp) ) { Text( - text = m.text!!, + text = m.notice, fontSize = 14.sp, fontStyle = FontStyle.Italic, color = noticeColor, modifier = Modifier.align(Alignment.Start), ) Row(modifier = Modifier.align(statusAlignment)) { - TextButton({}) { - Text("Decline".uppercase(), fontSize = 16.sp, color = MaterialTheme.colors.buttonTextNegative) + TextButton(onDecline) { + Text( + i18n("decline").uppercase(), + fontSize = 16.sp, + color = MaterialTheme.colors.buttonTextNegative + ) } - TextButton({}) { - Text("Accept".uppercase(), fontSize = 16.sp, color = MaterialTheme.colors.buttonTextPositive) + TextButton(onAccept) { + Text( + i18n("accept").uppercase(), + fontSize = 16.sp, + color = MaterialTheme.colors.buttonTextPositive + ) } } ConversationItemStatusView(m) diff --git a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationScreen.kt b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationScreen.kt index 654a3472ec4557e99f407100e412bd97b42d2e48..3d2864931a30d35b4a76db2d38d2e0e1bd53f2fa 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationScreen.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationScreen.kt @@ -4,7 +4,6 @@ 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 @@ -71,7 +70,6 @@ fun ConversationScreen( }, content = { padding -> LazyColumn( - verticalArrangement = Arrangement.spacedBy(8.dp), state = scrollState, // reverseLayout to display most recent message (index 0) at the bottom reverseLayout = true, diff --git a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationVisitor.kt b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationVisitor.kt index 6ea0b61aee65c0ad25e4b241ac73d8c48092098d..f266b00cfb1ff4f0e484a60c6e9b6ae9ae487cce 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationVisitor.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationVisitor.kt @@ -14,6 +14,7 @@ import org.briarproject.briar.api.introduction.IntroductionResponse import org.briarproject.briar.api.messaging.PrivateMessageHeader import org.briarproject.briar.api.privategroup.invitation.GroupInvitationRequest import org.briarproject.briar.api.privategroup.invitation.GroupInvitationResponse +import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18nF import org.briarproject.briar.desktop.utils.UiUtils.getContactDisplayName @@ -41,33 +42,42 @@ internal class ConversationVisitor( return if (r.isLocal) ConversationNoticeItem( - i18nF("blogs_sharing_invitation_sent", r.name, contactName), + i18nF("blog.invitation.sent", r.name, contactName), r ) - else - ConversationRequestItem( - i18nF("blogs_sharing_invitation_received", contactName, r.name), - ConversationRequestItem.RequestType.BLOG, r - ) + else { + val text = i18nF("blog.invitation.received", contactName, r.name) + // todo: add proper check for feature support + if (false) + ConversationRequestItem( + text, + ConversationRequestItem.RequestType.BLOG, r + ) + else + ConversationNoticeItem( + text + "\n" + i18n("unsupported_feature"), + r + ) + } } override fun visitBlogInvitationResponse(r: BlogInvitationResponse): ConversationItem { return if (r.isLocal) { val text = when { r.wasAccepted() -> - i18nF("blogs_sharing_response_accepted_sent", contactName) + i18nF("blog.invitation.response.accepted.sent", contactName) r.isAutoDecline -> - i18nF("blogs_sharing_response_declined_auto", contactName) + i18nF("blog.invitation.response.declined.auto", contactName) else -> - i18nF("blogs_sharing_response_declined_sent", contactName) + i18nF("blog.invitation.response.declined.sent", contactName) } ConversationNoticeItem(text, r) } else { val text = when { r.wasAccepted() -> - i18nF("blogs_sharing_response_accepted_received", contactName) + i18nF("blog.invitation.response.accepted.received", contactName) else -> - i18nF("blogs_sharing_response_declined_received", contactName) + i18nF("blog.invitation.response.declined.received", contactName) } ConversationNoticeItem(text, r) } @@ -76,33 +86,42 @@ internal class ConversationVisitor( override fun visitForumInvitationRequest(r: ForumInvitationRequest): ConversationItem { return if (r.isLocal) ConversationNoticeItem( - i18nF("forum_invitation_sent", r.name, contactName), + i18nF("forum.invitation.sent", r.name, contactName), r ) - else - ConversationRequestItem( - i18nF("forum_invitation_received", contactName, r.name), - ConversationRequestItem.RequestType.FORUM, r - ) + else { + val text = i18nF("forum.invitation.received", contactName, r.name) + // todo: add proper check for feature support + if (false) + ConversationRequestItem( + text, + ConversationRequestItem.RequestType.FORUM, r + ) + else + ConversationNoticeItem( + text + "\n" + i18n("unsupported_feature"), + r + ) + } } override fun visitForumInvitationResponse(r: ForumInvitationResponse): ConversationItem { return if (r.isLocal) { val text = when { r.wasAccepted() -> - i18nF("forum_invitation_response_accepted_sent", contactName) + i18nF("forum.invitation.response.accepted.sent", contactName) r.isAutoDecline -> - i18nF("forum_invitation_response_declined_auto", contactName) + i18nF("forum.invitation.response.declined.auto", contactName) else -> - i18nF("forum_invitation_response_declined_sent", contactName) + i18nF("forum.invitation.response.declined.sent", contactName) } ConversationNoticeItem(text, r) } else { val text = when { r.wasAccepted() -> - i18nF("forum_invitation_response_accepted_received", contactName) + i18nF("forum.invitation.response.accepted.received", contactName) else -> - i18nF("forum_invitation_response_declined_received", contactName) + i18nF("forum.invitation.response.declined.received", contactName) } ConversationNoticeItem(text, r) } @@ -111,33 +130,42 @@ internal class ConversationVisitor( override fun visitGroupInvitationRequest(r: GroupInvitationRequest): ConversationItem { return if (r.isLocal) ConversationNoticeItem( - i18nF("groups_invitations_invitation_sent", contactName, r.name), + i18nF("group.invitation.sent", contactName, r.name), r ) - else - ConversationRequestItem( - i18nF("groups_invitations_invitation_received", contactName, r.name), - ConversationRequestItem.RequestType.GROUP, r - ) + else { + val text = i18nF("group.invitation.received", contactName, r.name) + // todo: add proper check for feature support + if (false) + ConversationRequestItem( + text, + ConversationRequestItem.RequestType.GROUP, r + ) + else + ConversationNoticeItem( + text + "\n" + i18n("unsupported_feature"), + r + ) + } } override fun visitGroupInvitationResponse(r: GroupInvitationResponse): ConversationItem { return if (r.isLocal) { val text = when { r.wasAccepted() -> - i18nF("groups_invitations_response_accepted_sent", contactName) + i18nF("group.invitation.response.accepted.sent", contactName) r.isAutoDecline -> - i18nF("groups_invitations_response_declined_auto", contactName) + i18nF("group.invitation.response.declined.auto", contactName) else -> - i18nF("groups_invitations_response_declined_sent", contactName) + i18nF("group.invitation.response.declined.sent", contactName) } ConversationNoticeItem(text, r) } else { val text = when { r.wasAccepted() -> - i18nF("groups_invitations_response_accepted_received", contactName) + i18nF("group.invitation.response.accepted.received", contactName) else -> - i18nF("groups_invitations_response_declined_received", contactName) + i18nF("group.invitation.response.declined.received", contactName) } ConversationNoticeItem(text, r) } @@ -147,17 +175,17 @@ internal class ConversationVisitor( val name = getContactDisplayName(r.nameable.name, r.alias) return if (r.isLocal) ConversationNoticeItem( - i18nF("introduction_request_sent", contactName, name), + i18nF("introduction.request.sent", contactName, name), r ) else { val text = when { r.wasAnswered() -> - i18nF("introduction_request_answered_received", contactName, name) + i18nF("introduction.request.answered.received", contactName, name) r.isContact -> - i18nF("introduction_request_exists_received", contactName, name) + i18nF("introduction.request.exists.received", contactName, name) else -> - i18nF("introduction_request_received", contactName, name) + i18nF("introduction.request.received", contactName, name) } ConversationRequestItem( text, @@ -172,24 +200,24 @@ internal class ConversationVisitor( val text = when { r.wasAccepted() -> { val suffix = if (r.canSucceed()) - "\n\n" + i18nF("introduction_response_accepted_sent_info", name) + "\n\n" + i18nF("introduction.response.accepted.sent.info", name) else "" - i18nF("introduction_request_answered_received", name) + suffix + i18nF("introduction.response.accepted.sent", name) + suffix } r.isAutoDecline -> - i18nF("introduction_response_declined_auto", name) + i18nF("introduction.response.declined.auto", name) else -> - i18nF("introduction_response_declined_sent", name) + i18nF("introduction.response.declined.sent", name) } ConversationNoticeItem(text, r) } else { val text = when { r.wasAccepted() -> - i18nF("introduction_response_accepted_received", contactName, name) + i18nF("introduction.response.accepted.received", contactName, name) r.isIntroducer -> - i18nF("introduction_response_declined_received", contactName, name) + i18nF("introduction.response.declined.received", contactName, name) else -> - i18nF("introduction_response_declined_received_by_introducee", contactName, name) + i18nF("introduction.response.declined.received_by_introducee", contactName, name) } ConversationNoticeItem(text, r) } diff --git a/src/main/resources/strings/BriarDesktop.properties b/src/main/resources/strings/BriarDesktop.properties index 94117f65d4c199db78e838138b8e23c69be71cd7..9321ee2aff7de8490544e073922e9a63a5d42cf6 100644 --- a/src/main/resources/strings/BriarDesktop.properties +++ b/src/main/resources/strings/BriarDesktop.properties @@ -35,6 +35,45 @@ introduction.introduce=Make Introduction introduction.message=Add a message (optional) introduction.title_first=Introduce {0} to introduction.title_second=Introduce Contacts +introduction.request.sent=You have asked to introduce {0} to {1}. +introduction.request.received={0} has asked to introduce you to {1}. Do you want to add {1} to your contact list? +introduction.request.exists.received={0} has asked to introduce you to {1}, but {1} is already in your contact list. Since {0} might not know that, you can still respond: +introduction.request.answered.received={0} has asked to introduce you to {1}. +introduction.response.accepted.sent=You accepted the introduction to {0}. +introduction.response.accepted.sent.info=Before {0} gets added to your contacts, they need to accept the introduction as well. This might take some time. +introduction.response.declined.sent=You declined the introduction to {0}. +introduction.response.declined.auto=The introduction to {0} was automatically declined. +introduction.response.accepted.received={0} accepted the introduction to {1}. +introduction.response.declined.received={0} declined the introduction to {1}. +introduction.response.declined.received_by_introducee={0} says that {1} declined the introduction. + +# Private Group Sharing +group.invitation.received={0} has invited you to join the group "{1}". +group.invitation.sent=You have invited {0} to join the group "{1}". +group.invitation.response.accepted.sent=You accepted the group invitation from {0}. +group.invitation.response.declined.sent=You declined the group invitation from {0}. +group.invitation.response.declined.auto=The group invitation from {0} was automatically declined. +group.invitation.response.accepted.received={0} accepted the group invitation. +group.invitation.response.declined.received={0} declined the group invitation. + +# Forum Sharing +forum.invitation.received={0} has shared the forum "{1}" with you. +forum.invitation.sent=You have shared the forum "{0}" with {1}. +forum.invitation.response.accepted.sent=You accepted the forum invitation from {0}. +forum.invitation.response.declined.sent=You declined the forum invitation from {0}. +forum.invitation.response.declined.auto=The forum invitation from {0} was automatically declined. +forum.invitation.response.accepted.received={0} accepted the forum invitation. +forum.invitation.response.declined.received={0} declined the forum invitation. + +# Blog Sharing +blog.invitation.received={0} has shared the blog "{1}" with you. +blog.invitation.sent=You have shared the blog "{0}" with {1}. +blog.invitation.response.accepted.sent=You accepted the blog invitation from {0}. +blog.invitation.response.declined.sent=You declined the blog invitation from {0}. +blog.invitation.response.declined.auto=The blog invitation from {0} was automatically declined. +blog.invitation.response.accepted.received={0} accepted the blog invitation. +blog.invitation.response.declined.received={0} declined the blog invitation. + # Login screen login.login=Login @@ -50,6 +89,9 @@ main.help.tor.port.control=Tor Control Port # Miscellaneous password=Password +accept=Accept +decline=Decline +unsupported_feature=Unfortunately, this feature is not yet supported by Briar Desktop. # Registration screen registration.username=Username