Merge branch 'develop' into feature/bma/dataObject
This commit is contained in:
commit
1a6376e723
129 changed files with 2140 additions and 487 deletions
|
|
@ -5,6 +5,6 @@
|
|||
<string name="screen_analytics_prompt_read_terms">"Du kannst alle unsere Nutzerbedingungen %1$s lesen."</string>
|
||||
<string name="screen_analytics_prompt_read_terms_content_link">"hier"</string>
|
||||
<string name="screen_analytics_prompt_settings">"Du kannst dies jederzeit deaktivieren"</string>
|
||||
<string name="screen_analytics_prompt_third_party_sharing">"Wir geben "<b>"keine"</b>" Informationen an Dritte weiter"</string>
|
||||
<string name="screen_analytics_prompt_third_party_sharing">"Wir geben deine Daten nicht an Dritte weiter"</string>
|
||||
<string name="screen_analytics_prompt_title">"Hilf uns, %1$s zu verbessern"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_welcome_bullet_1">"Hovory, hlasování, vyhledávání a další budou přidány koncem tohoto roku."</string>
|
||||
<string name="screen_welcome_bullet_2">"Historie zpráv šifrovaných místností nebude v této aktualizaci k dispozici."</string>
|
||||
<string name="screen_welcome_bullet_3">"Rádi bychom se od vás dozvěděli, co si o tom myslíte, dejte nám vědět prostřednictvím stránky s nastavením."</string>
|
||||
<string name="screen_welcome_button">"Jdeme na to!"</string>
|
||||
<string name="screen_welcome_subtitle">"Zde je to, co potřebujete vědět:"</string>
|
||||
<string name="screen_welcome_title">"Vítá vás %1$s!"</string>
|
||||
</resources>
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_welcome_bullet_1">"Anrufe, Standortfreigabe, Suche und mehr werden später in diesem Jahr hinzugefügt."</string>
|
||||
<string name="screen_welcome_bullet_1">"Anrufe, Umfragen, Suche und mehr werden später in diesem Jahr hinzugefügt."</string>
|
||||
<string name="screen_welcome_bullet_2">"Der Nachrichtenverlauf für verschlüsselte Räume wird in diesem Update nicht verfügbar sein."</string>
|
||||
<string name="screen_welcome_bullet_3">"Wir würden uns freuen, wenn du uns über die Einstellungsseite deine Meinung mitteilst."</string>
|
||||
<string name="screen_welcome_button">"Los geht\'s!"</string>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
<string name="screen_account_provider_signin_title">"Chystáte se přihlásit do %s"</string>
|
||||
<string name="screen_account_provider_signup_subtitle">"Zde budou uloženy vaše konverzace - podobně jako u poskytovatele e-mailových služeb uchováváte své e-maily."</string>
|
||||
<string name="screen_account_provider_signup_title">"Chystáte se vytvořit účet na %s"</string>
|
||||
<string name="screen_change_account_provider_matrix_org_subtitle">"Matrix.org je otevřená síť pro bezpečnou, decentralizovanou komunikaci."</string>
|
||||
<string name="screen_change_account_provider_matrix_org_subtitle">"Matrix.org je velký bezplatný server ve veřejné síti Matrix pro bezpečnou decentralizovanou komunikaci, který provozuje nadace Matrix.org."</string>
|
||||
<string name="screen_change_account_provider_other">"Jiný"</string>
|
||||
<string name="screen_change_account_provider_subtitle">"Použijte jiného poskytovatele účtu, například vlastní soukromý server nebo pracovní účet."</string>
|
||||
<string name="screen_change_account_provider_title">"Změnit poskytovatele účtu"</string>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
<string name="screen_account_provider_signin_title">"Du bist dabei dich bei %s anzumelden"</string>
|
||||
<string name="screen_account_provider_signup_subtitle">"Hier werden deine Konversationen stattfinden — genauso wie du einen E-Mail-Anbieter verwenden würdest, um deine E-Mails aufzubewahren."</string>
|
||||
<string name="screen_account_provider_signup_title">"Du bist dabei ein Konto auf %s zu erstellen"</string>
|
||||
<string name="screen_change_account_provider_matrix_org_subtitle">"Matrix.org ist ein offenes Netzwerk für sichere, dezentralisierte Kommunikation."</string>
|
||||
<string name="screen_change_account_provider_matrix_org_subtitle">"Matrix.org ist ein großer, kostenloser Server im öffentlichen Matrix-Netzwerk für sichere, dezentrale Kommunikation, der von der Matrix.org Foundation betrieben wird."</string>
|
||||
<string name="screen_change_account_provider_other">"Andere"</string>
|
||||
<string name="screen_change_account_provider_subtitle">"Verwende einen anderen Kontoanbieter, z. B. deinen eigenen privaten Server oder ein Arbeitskonto."</string>
|
||||
<string name="screen_change_account_provider_title">"Kontoanbieter ändern"</string>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
<string name="screen_account_provider_signin_title">"Vous êtes sur le point de vous connecter à %s"</string>
|
||||
<string name="screen_account_provider_signup_subtitle">"C\'est ici que vos conversations seront stockées - tout comme vous utiliseriez un fournisseur de messagerie pour conserver vos e-mails."</string>
|
||||
<string name="screen_account_provider_signup_title">"Vous êtes sur le point de créer un compte sur %s"</string>
|
||||
<string name="screen_change_account_provider_matrix_org_subtitle">"Matrix.org est un réseau ouvert pour des communications sécurisées et décentralisées."</string>
|
||||
<string name="screen_change_account_provider_other">"Autre"</string>
|
||||
<string name="screen_change_account_provider_subtitle">"Utilisez un autre fournisseur de compte, tel que votre propre serveur ou un compte professionnel."</string>
|
||||
<string name="screen_change_account_provider_title">"Changer de fournisseur"</string>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
<string name="screen_account_provider_signin_title">"Sunteți pe cale să vă conectați la %s"</string>
|
||||
<string name="screen_account_provider_signup_subtitle">"Aici vor trăi conversațiile - la fel cum ați folosi un furnizor de e-mail pentru a vă păstra e-mailurile."</string>
|
||||
<string name="screen_account_provider_signup_title">"Sunteți pe cale să creați un cont pe %s"</string>
|
||||
<string name="screen_change_account_provider_matrix_org_subtitle">"Matrix.org este o rețea deschisă pentru o comunicare sigură și descentralizată."</string>
|
||||
<string name="screen_change_account_provider_other">"Altul"</string>
|
||||
<string name="screen_change_account_provider_subtitle">"Utilizați un alt furnizor de cont, cum ar fi propriul server privat sau un cont de serviciu."</string>
|
||||
<string name="screen_change_account_provider_title">"Schimbați furnizorul contului"</string>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
<string name="screen_account_provider_signin_title">"Вы собираетесь войти в %s"</string>
|
||||
<string name="screen_account_provider_signup_subtitle">"Здесь будут храниться ваши разговоры - точно так же, как вы используете почтового провайдера для хранения своих писем."</string>
|
||||
<string name="screen_account_provider_signup_title">"Вы собираетесь создать учетную запись на %s"</string>
|
||||
<string name="screen_change_account_provider_matrix_org_subtitle">"Matrix.org — это открытая сеть для безопасной децентрализованной связи."</string>
|
||||
<string name="screen_change_account_provider_other">"Другое"</string>
|
||||
<string name="screen_change_account_provider_subtitle">"Используйте другого поставщика учетных записей, например, собственный частный сервер или рабочую учетную запись."</string>
|
||||
<string name="screen_change_account_provider_title">"Сменить поставщика учетной записи"</string>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
<string name="screen_account_provider_signin_title">"Chystáte sa prihlásiť do %s"</string>
|
||||
<string name="screen_account_provider_signup_subtitle">"Tu budú žiť vaše konverzácie — podobne ako používate poskytovateľa e-mailových služieb na uchovávanie e-mailov."</string>
|
||||
<string name="screen_account_provider_signup_title">"Chystáte sa vytvoriť účet na %s"</string>
|
||||
<string name="screen_change_account_provider_matrix_org_subtitle">"Matrix.org je otvorená sieť pre bezpečnú a decentralizovanú komunikáciu."</string>
|
||||
<string name="screen_change_account_provider_other">"Iný"</string>
|
||||
<string name="screen_change_account_provider_subtitle">"Použite iného poskytovateľa účtu, ako napríklad vlastný súkromný server alebo pracovný účet."</string>
|
||||
<string name="screen_change_account_provider_title">"Zmeniť poskytovateľa účtu"</string>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
<string name="screen_account_provider_signin_title">"You’re about to sign in to %s"</string>
|
||||
<string name="screen_account_provider_signup_subtitle">"This is where your conversations will live — just like you would use an email provider to keep your emails."</string>
|
||||
<string name="screen_account_provider_signup_title">"You’re about to create an account on %s"</string>
|
||||
<string name="screen_change_account_provider_matrix_org_subtitle">"Matrix.org is an open network for secure, decentralized communication."</string>
|
||||
<string name="screen_change_account_provider_matrix_org_subtitle">"Matrix.org is a large, free server on the public Matrix network for secure, decentralised communication, run by the Matrix.org Foundation."</string>
|
||||
<string name="screen_change_account_provider_other">"Other"</string>
|
||||
<string name="screen_change_account_provider_subtitle">"Use a different account provider, such as your own private server or a work account."</string>
|
||||
<string name="screen_change_account_provider_title">"Change account provider"</string>
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package io.element.android.features.messages.impl.timeline.components
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
|
|
@ -68,9 +69,11 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItemGrou
|
|||
import io.element.android.features.messages.impl.timeline.model.bubble.BubbleState
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemImageContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemPollContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent
|
||||
import io.element.android.libraries.designsystem.components.EqualWidthColumn
|
||||
import io.element.android.libraries.designsystem.components.avatar.Avatar
|
||||
|
|
@ -357,11 +360,15 @@ private fun MessageEventBubbleContent(
|
|||
onMessageLongClick: () -> Unit,
|
||||
inReplyToClick: () -> Unit,
|
||||
onTimestampClicked: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
@SuppressLint("ModifierParameter") bubbleModifier: Modifier = Modifier, // need to rename this modifier to distinguish it from the following ones
|
||||
) {
|
||||
val isMediaItem = event.content is TimelineItemImageContent
|
||||
|| event.content is TimelineItemVideoContent
|
||||
|| event.content is TimelineItemLocationContent
|
||||
val timestampPosition = when (event.content) {
|
||||
is TimelineItemImageContent,
|
||||
is TimelineItemVideoContent,
|
||||
is TimelineItemLocationContent -> TimestampPosition.Overlay
|
||||
is TimelineItemPollContent -> TimestampPosition.Below
|
||||
else -> TimestampPosition.Default
|
||||
}
|
||||
val replyToDetails = event.inReplyTo as? InReplyTo.Ready
|
||||
|
||||
// Long clicks are not not automatically propagated from a `clickable`
|
||||
|
|
@ -384,96 +391,97 @@ private fun MessageEventBubbleContent(
|
|||
|
||||
@Composable
|
||||
fun ContentAndTimestampView(
|
||||
overlayTimestamp: Boolean,
|
||||
timestampPosition: TimestampPosition,
|
||||
modifier: Modifier = Modifier,
|
||||
contentModifier: Modifier = Modifier,
|
||||
timestampModifier: Modifier = Modifier,
|
||||
) {
|
||||
if (overlayTimestamp) {
|
||||
Box(modifier) {
|
||||
ContentView(modifier = contentModifier)
|
||||
TimelineEventTimestampView(
|
||||
event = event,
|
||||
onClick = onTimestampClicked,
|
||||
onLongClick = ::onTimestampLongClick,
|
||||
modifier = timestampModifier
|
||||
.padding(horizontal = 4.dp, vertical = 4.dp) // Outer padding
|
||||
.background(ElementTheme.colors.bgSubtleSecondary, RoundedCornerShape(10.0.dp))
|
||||
.align(Alignment.BottomEnd)
|
||||
.padding(horizontal = 4.dp, vertical = 2.dp) // Inner padding
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Box(modifier) {
|
||||
ContentView(modifier = contentModifier)
|
||||
TimelineEventTimestampView(
|
||||
event = event,
|
||||
onClick = onTimestampClicked,
|
||||
onLongClick = ::onTimestampLongClick,
|
||||
modifier = timestampModifier
|
||||
.align(Alignment.BottomEnd)
|
||||
.padding(horizontal = 8.dp, vertical = 4.dp)
|
||||
)
|
||||
}
|
||||
when (timestampPosition) {
|
||||
TimestampPosition.Overlay ->
|
||||
Box(modifier) {
|
||||
ContentView(modifier = contentModifier)
|
||||
TimelineEventTimestampView(
|
||||
event = event,
|
||||
onClick = onTimestampClicked,
|
||||
onLongClick = ::onTimestampLongClick,
|
||||
modifier = timestampModifier
|
||||
.padding(horizontal = 4.dp, vertical = 4.dp) // Outer padding
|
||||
.background(ElementTheme.colors.bgSubtleSecondary, RoundedCornerShape(10.0.dp))
|
||||
.align(Alignment.BottomEnd)
|
||||
.padding(horizontal = 4.dp, vertical = 2.dp) // Inner padding
|
||||
)
|
||||
}
|
||||
TimestampPosition.Aligned ->
|
||||
Box(modifier) {
|
||||
ContentView(modifier = contentModifier)
|
||||
TimelineEventTimestampView(
|
||||
event = event,
|
||||
onClick = onTimestampClicked,
|
||||
onLongClick = ::onTimestampLongClick,
|
||||
modifier = timestampModifier
|
||||
.align(Alignment.BottomEnd)
|
||||
.padding(horizontal = 8.dp, vertical = 4.dp)
|
||||
)
|
||||
}
|
||||
TimestampPosition.Below ->
|
||||
Column(modifier) {
|
||||
ContentView(modifier = contentModifier)
|
||||
TimelineEventTimestampView(
|
||||
event = event,
|
||||
onClick = onTimestampClicked,
|
||||
onLongClick = ::onTimestampLongClick,
|
||||
modifier = timestampModifier
|
||||
.align(Alignment.End)
|
||||
.padding(horizontal = 8.dp, vertical = 4.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Used only for media items, with no reply to metadata. It displays the contents with no paddings. */
|
||||
@Composable
|
||||
fun SimpleMediaItemLayout(modifier: Modifier = Modifier) {
|
||||
ContentAndTimestampView(overlayTimestamp = true, modifier = modifier)
|
||||
}
|
||||
|
||||
/** Used for every other type of message, groups the different components in a Column with some space between them. */
|
||||
/** Groups the different components in a Column with some space between them. */
|
||||
@Composable
|
||||
fun CommonLayout(
|
||||
inReplyToDetails: InReplyTo.Ready?,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
var modifierWithPadding: Modifier = Modifier
|
||||
var contentModifier: Modifier = Modifier
|
||||
EqualWidthColumn(modifier = modifier, spacing = 8.dp) {
|
||||
if (inReplyToDetails != null) {
|
||||
val senderName = inReplyToDetails.senderDisplayName ?: inReplyToDetails.senderId.value
|
||||
val attachmentThumbnailInfo = attachmentThumbnailInfoForInReplyTo(inReplyToDetails)
|
||||
val text = textForInReplyTo(inReplyToDetails)
|
||||
ReplyToContent(
|
||||
senderName = senderName,
|
||||
text = text,
|
||||
attachmentThumbnailInfo = attachmentThumbnailInfo,
|
||||
modifier = Modifier
|
||||
.padding(top = 8.dp, start = 8.dp, end = 8.dp)
|
||||
.clip(RoundedCornerShape(6.dp))
|
||||
.clickable(enabled = true, onClick = inReplyToClick),
|
||||
)
|
||||
}
|
||||
val modifierWithPadding = if (isMediaItem) {
|
||||
Modifier.padding(start = 8.dp, end = 8.dp, bottom = 8.dp)
|
||||
} else {
|
||||
Modifier
|
||||
}
|
||||
|
||||
val contentModifier = if (isMediaItem) {
|
||||
Modifier.clip(RoundedCornerShape(12.dp))
|
||||
} else {
|
||||
if (inReplyToDetails != null) {
|
||||
Modifier.padding(start = 12.dp, end = 12.dp, top = 0.dp, bottom = 8.dp)
|
||||
} else {
|
||||
Modifier.padding(start = 12.dp, end = 12.dp, top = 8.dp, bottom = 8.dp)
|
||||
when {
|
||||
inReplyToDetails != null -> {
|
||||
val senderName = inReplyToDetails.senderDisplayName ?: inReplyToDetails.senderId.value
|
||||
val attachmentThumbnailInfo = attachmentThumbnailInfoForInReplyTo(inReplyToDetails)
|
||||
val text = textForInReplyTo(inReplyToDetails)
|
||||
ReplyToContent(
|
||||
senderName = senderName,
|
||||
text = text,
|
||||
attachmentThumbnailInfo = attachmentThumbnailInfo,
|
||||
modifier = Modifier
|
||||
.padding(top = 8.dp, start = 8.dp, end = 8.dp)
|
||||
.clip(RoundedCornerShape(6.dp))
|
||||
.clickable(enabled = true, onClick = inReplyToClick),
|
||||
)
|
||||
if (timestampPosition == TimestampPosition.Overlay) {
|
||||
modifierWithPadding = Modifier.padding(start = 8.dp, end = 8.dp, bottom = 8.dp)
|
||||
contentModifier = Modifier.clip(RoundedCornerShape(12.dp))
|
||||
} else {
|
||||
contentModifier = Modifier.padding(start = 12.dp, end = 12.dp, top = 0.dp, bottom = 8.dp)
|
||||
}
|
||||
}
|
||||
timestampPosition != TimestampPosition.Overlay -> {
|
||||
contentModifier = Modifier.padding(start = 12.dp, end = 12.dp, top = 8.dp, bottom = 8.dp)
|
||||
}
|
||||
}
|
||||
|
||||
ContentAndTimestampView(
|
||||
overlayTimestamp = isMediaItem,
|
||||
timestampPosition = timestampPosition,
|
||||
contentModifier = contentModifier,
|
||||
modifier = modifierWithPadding,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (isMediaItem && replyToDetails == null) {
|
||||
SimpleMediaItemLayout()
|
||||
} else {
|
||||
CommonLayout(inReplyToDetails = replyToDetails, modifier = modifier)
|
||||
}
|
||||
CommonLayout(inReplyToDetails = replyToDetails, modifier = bubbleModifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -810,3 +818,23 @@ internal fun TimelineItemEventRowLongSenderNamePreview() = ElementPreviewLight {
|
|||
onTimestampClicked = {},
|
||||
)
|
||||
}
|
||||
|
||||
// Note: no need for light/dark variant for this preview, we only look at the timestamp position
|
||||
@Preview
|
||||
@Composable
|
||||
internal fun TimelineItemEventTimestampBelowPreview() = ElementPreviewLight {
|
||||
TimelineItemEventRow(
|
||||
event = aTimelineItemEvent(content = aTimelineItemPollContent()),
|
||||
isHighlighted = false,
|
||||
canReply = true,
|
||||
onClick = {},
|
||||
onLongClick = {},
|
||||
onUserDataClick = {},
|
||||
inReplyToClick = {},
|
||||
onReactionClick = { _, _ -> },
|
||||
onReactionLongClick = { _, _ -> },
|
||||
onMoreReactionsClick = {},
|
||||
onSwipeToReply = {},
|
||||
onTimestampClicked = {},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.features.messages.impl.timeline.components
|
||||
|
||||
enum class TimestampPosition {
|
||||
/**
|
||||
* Timestamp should overlay the timeline event content (eg. image).
|
||||
*/
|
||||
Overlay,
|
||||
|
||||
/**
|
||||
* Timestamp should be aligned with the timeline event content if this is possible (eg. text).
|
||||
*/
|
||||
Aligned,
|
||||
|
||||
/**
|
||||
* Timestamp should always be rendered below the timeline event content (eg. poll).
|
||||
*/
|
||||
Below;
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Default timestamp position for timeline event contents.
|
||||
*/
|
||||
val Default: TimestampPosition = Aligned
|
||||
}
|
||||
}
|
||||
|
|
@ -21,7 +21,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContentProvider
|
||||
import io.element.android.features.poll.api.ActivePollContentView
|
||||
import io.element.android.features.poll.api.PollContentView
|
||||
import io.element.android.libraries.designsystem.preview.DayNightPreviews
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.matrix.api.poll.PollAnswer
|
||||
|
|
@ -33,10 +33,11 @@ fun TimelineItemPollView(
|
|||
onAnswerSelected: (PollAnswer) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
ActivePollContentView(
|
||||
PollContentView(
|
||||
question = content.question,
|
||||
answerItems = content.answerItems.toImmutableList(),
|
||||
pollKind = content.pollKind,
|
||||
isPollEnded = content.isEnded,
|
||||
onAnswerSelected = onAnswerSelected,
|
||||
modifier = modifier,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import io.element.android.features.poll.api.PollAnswerItem
|
|||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.poll.PollKind
|
||||
import io.element.android.libraries.matrix.api.poll.isDisclosed
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.PollContent
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -36,18 +36,33 @@ class TimelineItemContentPollFactory @Inject constructor(
|
|||
if (!featureFlagService.isFeatureEnabled(FeatureFlags.Polls)) return TimelineItemUnknownContent
|
||||
|
||||
// Todo Move this computation to the matrix rust sdk
|
||||
val showResults = content.kind == PollKind.Disclosed && matrixClient.sessionId in content.votes.flatMap { it.value }
|
||||
val pollVotesCount = content.votes.flatMap { it.value }.size
|
||||
val userVotes = content.votes.filter { matrixClient.sessionId in it.value }.keys
|
||||
val totalVoteCount = content.votes.flatMap { it.value }.size
|
||||
val myVotes = content.votes.filter { matrixClient.sessionId in it.value }.keys
|
||||
val isEndedPoll = content.endTime != null
|
||||
val winnerIds = if (!isEndedPoll) {
|
||||
emptyList()
|
||||
} else {
|
||||
content.answers
|
||||
.map { answer -> answer.id }
|
||||
.groupBy { answerId -> content.votes[answerId]?.size ?: 0 } // Group by votes count
|
||||
.maxByOrNull { (votes, _) -> votes } // Keep max voted answers
|
||||
?.takeIf { (votes, _) -> votes > 0 } // Ignore if no option has been voted
|
||||
?.value
|
||||
.orEmpty()
|
||||
}
|
||||
val answerItems = content.answers.map { answer ->
|
||||
val votesCount = content.votes[answer.id]?.size ?: 0
|
||||
val progress = if (pollVotesCount > 0) votesCount.toFloat() / pollVotesCount.toFloat() else 0f
|
||||
val answerVoteCount = content.votes[answer.id]?.size ?: 0
|
||||
val isSelected = answer.id in myVotes
|
||||
val isWinner = answer.id in winnerIds
|
||||
val percentage = if (totalVoteCount > 0) answerVoteCount.toFloat() / totalVoteCount.toFloat() else 0f
|
||||
PollAnswerItem(
|
||||
answer = answer,
|
||||
isSelected = answer.id in userVotes,
|
||||
isDisclosed = showResults,
|
||||
votesCount = votesCount,
|
||||
progress = progress,
|
||||
isSelected = isSelected,
|
||||
isEnabled = !isEndedPoll,
|
||||
isWinner = isWinner,
|
||||
isDisclosed = content.kind.isDisclosed || isEndedPoll,
|
||||
votesCount = answerVoteCount,
|
||||
percentage = percentage,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -56,7 +71,7 @@ class TimelineItemContentPollFactory @Inject constructor(
|
|||
answerItems = answerItems,
|
||||
votes = content.votes,
|
||||
pollKind = content.kind,
|
||||
isDisclosed = showResults
|
||||
isEnded = isEndedPoll,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ data class TimelineItemPollContent(
|
|||
val answerItems: List<PollAnswerItem>,
|
||||
val votes: Map<String, List<UserId>>,
|
||||
val pollKind: PollKind,
|
||||
val isDisclosed: Boolean,
|
||||
val isEnded: Boolean,
|
||||
) : TimelineItemEventContent {
|
||||
override val type: String = "TimelineItemPollContent"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,16 +24,16 @@ open class TimelineItemPollContentProvider : PreviewParameterProvider<TimelineIt
|
|||
override val values: Sequence<TimelineItemPollContent>
|
||||
get() = sequenceOf(
|
||||
aTimelineItemPollContent(),
|
||||
aTimelineItemPollContent().copy(isDisclosed = true),
|
||||
aTimelineItemPollContent().copy(pollKind = PollKind.Undisclosed),
|
||||
)
|
||||
}
|
||||
|
||||
fun aTimelineItemPollContent(): TimelineItemPollContent {
|
||||
return TimelineItemPollContent(
|
||||
pollKind = PollKind.Disclosed,
|
||||
isDisclosed = false,
|
||||
question = "What type of food should we have at the party?",
|
||||
answerItems = aPollAnswerItemList(),
|
||||
isEnded = false,
|
||||
votes = emptyMap(),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,19 +5,43 @@
|
|||
<item quantity="few">"%1$d změny místnosti"</item>
|
||||
<item quantity="other">"%1$d změn místnosti"</item>
|
||||
</plurals>
|
||||
<plurals name="screen_room_timeline_more_reactions">
|
||||
<item quantity="one">"%1$d další"</item>
|
||||
<item quantity="few">"%1$d další"</item>
|
||||
<item quantity="other">"%1$d dalších"</item>
|
||||
</plurals>
|
||||
<string name="screen_room_attachment_source_camera">"Fotoaparát"</string>
|
||||
<string name="screen_room_attachment_source_camera_photo">"Vyfotit"</string>
|
||||
<string name="screen_room_attachment_source_camera_video">"Natočit video"</string>
|
||||
<string name="screen_room_attachment_source_files">"Příloha"</string>
|
||||
<string name="screen_room_attachment_source_gallery">"Knihovna fotografií a videí"</string>
|
||||
<string name="screen_room_attachment_source_location">"Poloha"</string>
|
||||
<string name="screen_room_attachment_source_poll">"Hlasování"</string>
|
||||
<string name="screen_room_encrypted_history_banner">"Historie zpráv je momentálně v této místnosti nedostupná"</string>
|
||||
<string name="screen_room_error_failed_retrieving_user_details">"Nepodařilo se načíst údaje o uživateli"</string>
|
||||
<string name="screen_room_invite_again_alert_message">"Chtěli byste je pozvat zpět?"</string>
|
||||
<string name="screen_room_invite_again_alert_title">"V tomto chatu jste sami"</string>
|
||||
<string name="screen_room_message_copied">"Zpráva zkopírována"</string>
|
||||
<string name="screen_room_no_permission_to_post">"Nemáte oprávnění zveřejňovat příspěvky v této místnosti"</string>
|
||||
<string name="screen_room_notification_settings_allow_custom">"Povolit vlastní nastavení"</string>
|
||||
<string name="screen_room_notification_settings_allow_custom_footnote">"Zapnutím této funkce přepíšete výchozí nastavení"</string>
|
||||
<string name="screen_room_notification_settings_custom_settings_title">"Upozornit mě v tomto chatu na"</string>
|
||||
<string name="screen_room_notification_settings_default_setting_footnote">"Můžete změnit ve vašem %1$s."</string>
|
||||
<string name="screen_room_notification_settings_default_setting_footnote_content_link">"globální nastavení"</string>
|
||||
<string name="screen_room_notification_settings_default_setting_title">"Výchozí nastavení"</string>
|
||||
<string name="screen_room_notification_settings_edit_remove_setting">"Odebrat vlastní nastavení"</string>
|
||||
<string name="screen_room_notification_settings_error_loading_settings">"Při načítání nastavení oznámení došlo k chybě."</string>
|
||||
<string name="screen_room_notification_settings_error_restoring_default">"Obnovení výchozího režimu se nezdařilo, zkuste to prosím znovu."</string>
|
||||
<string name="screen_room_notification_settings_error_setting_mode">"Nastavení režimu se nezdařilo, zkuste to prosím znovu."</string>
|
||||
<string name="screen_room_notification_settings_mode_all_messages">"Všechny zprávy"</string>
|
||||
<string name="screen_room_notification_settings_mode_mentions_and_keywords">"Pouze zmínky a klíčová slova"</string>
|
||||
<string name="screen_room_notification_settings_room_custom_settings_title">"V této místnosti mě upozornit na"</string>
|
||||
<string name="screen_room_reactions_show_less">"Zobrazit méně"</string>
|
||||
<string name="screen_room_reactions_show_more">"Zobrazit více"</string>
|
||||
<string name="screen_room_retry_send_menu_send_again_action">"Odeslat znovu"</string>
|
||||
<string name="screen_room_retry_send_menu_title">"Vaši zprávu se nepodařilo odeslat"</string>
|
||||
<string name="screen_room_timeline_add_reaction">"Přidat emoji"</string>
|
||||
<string name="screen_room_timeline_less_reactions">"Zobrazit méně"</string>
|
||||
<string name="screen_room_error_failed_processing_media">"Nahrání média se nezdařilo, zkuste to prosím znovu."</string>
|
||||
<string name="screen_room_retry_send_menu_remove_action">"Odstranit"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -10,16 +10,32 @@
|
|||
<string name="screen_room_attachment_source_files">"Anhang"</string>
|
||||
<string name="screen_room_attachment_source_gallery">"Foto- & Video-Bibliothek"</string>
|
||||
<string name="screen_room_attachment_source_location">"Standort"</string>
|
||||
<string name="screen_room_attachment_source_poll">"Umfrage"</string>
|
||||
<string name="screen_room_encrypted_history_banner">"Der Nachrichtenverlauf ist in diesem Raum derzeit nicht verfügbar"</string>
|
||||
<string name="screen_room_error_failed_retrieving_user_details">"Benutzerdetails konnten nicht abgerufen werden"</string>
|
||||
<string name="screen_room_invite_again_alert_message">"Möchtest du sie wieder einladen?"</string>
|
||||
<string name="screen_room_invite_again_alert_title">"Du bist allein in diesem Chat"</string>
|
||||
<string name="screen_room_message_copied">"Nachricht kopiert"</string>
|
||||
<string name="screen_room_no_permission_to_post">"Du bist keine Berechtigung, um in diesem Raum zu posten"</string>
|
||||
<string name="screen_room_notification_settings_allow_custom">"Benutzerdefinierte Einstellung zulassen"</string>
|
||||
<string name="screen_room_notification_settings_allow_custom_footnote">"Das Aktivieren dieser Option wird die Standardeinstellungen überschreiben."</string>
|
||||
<string name="screen_room_notification_settings_custom_settings_title">"Benachrichtige mich in diesem Chat für"</string>
|
||||
<string name="screen_room_notification_settings_default_setting_footnote">"Du kannst es in deinem %1$s ändern."</string>
|
||||
<string name="screen_room_notification_settings_default_setting_footnote_content_link">"Globale Einstellungen"</string>
|
||||
<string name="screen_room_notification_settings_default_setting_title">"Standardeinstellung"</string>
|
||||
<string name="screen_room_notification_settings_edit_remove_setting">"Benutzerdefinierte Einstellung entfernen"</string>
|
||||
<string name="screen_room_notification_settings_error_loading_settings">"Beim Laden der Benachrichtigungseinstellungen ist ein Fehler aufgetreten."</string>
|
||||
<string name="screen_room_notification_settings_error_restoring_default">"Wiederherstellung des Standardmodus fehlgeschlagen. Bitte versuche es erneut."</string>
|
||||
<string name="screen_room_notification_settings_error_setting_mode">"Fehler beim Einstellen des Modus. Bitte versuche es erneut."</string>
|
||||
<string name="screen_room_notification_settings_mode_all_messages">"Alle Nachrichten"</string>
|
||||
<string name="screen_room_notification_settings_mode_mentions_and_keywords">"Nur Erwähnungen und Schlüsselwörter"</string>
|
||||
<string name="screen_room_notification_settings_room_custom_settings_title">"In diesem Raum, benachrichtige mich für"</string>
|
||||
<string name="screen_room_reactions_show_less">"Weniger anzeigen"</string>
|
||||
<string name="screen_room_reactions_show_more">"Mehr anzeigen"</string>
|
||||
<string name="screen_room_retry_send_menu_send_again_action">"Erneut senden"</string>
|
||||
<string name="screen_room_retry_send_menu_title">"Ihre Nachricht konnte nicht gesendet werden"</string>
|
||||
<string name="screen_room_timeline_add_reaction">"Emoji hinzufügen"</string>
|
||||
<string name="screen_room_timeline_less_reactions">"Weniger anzeigen"</string>
|
||||
<string name="screen_room_error_failed_processing_media">"Fehler bei der Verarbeitung von Medien zum Hochladen, bitte versuche es erneut."</string>
|
||||
<string name="screen_room_retry_send_menu_remove_action">"Entfernen"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -5,6 +5,11 @@
|
|||
<item quantity="few">"%1$d изменения в комнате"</item>
|
||||
<item quantity="many">"%1$d изменений в комнате"</item>
|
||||
</plurals>
|
||||
<plurals name="screen_room_timeline_more_reactions">
|
||||
<item quantity="one">"И ещё %1$d"</item>
|
||||
<item quantity="few">"И ещё %1$d"</item>
|
||||
<item quantity="many">"И ещё %1$d"</item>
|
||||
</plurals>
|
||||
<string name="screen_room_attachment_source_camera">"Камера"</string>
|
||||
<string name="screen_room_attachment_source_camera_photo">"Сделать фото"</string>
|
||||
<string name="screen_room_attachment_source_camera_video">"Записать видео"</string>
|
||||
|
|
@ -24,11 +29,13 @@
|
|||
<string name="screen_room_notification_settings_default_setting_footnote">"Вы можете изменить его в своем %1$s."</string>
|
||||
<string name="screen_room_notification_settings_default_setting_footnote_content_link">"Основные Настройки"</string>
|
||||
<string name="screen_room_notification_settings_default_setting_title">"Настройка по умолчанию"</string>
|
||||
<string name="screen_room_notification_settings_edit_remove_setting">"Удалить пользовательскую настройку"</string>
|
||||
<string name="screen_room_notification_settings_error_loading_settings">"Произошла ошибка при загрузке настроек уведомлений."</string>
|
||||
<string name="screen_room_notification_settings_error_restoring_default">"Не удалось восстановить режим по умолчанию, попробуйте еще раз."</string>
|
||||
<string name="screen_room_notification_settings_error_setting_mode">"Не удалось настроить режим, попробуйте еще раз."</string>
|
||||
<string name="screen_room_notification_settings_mode_all_messages">"Все сообщения"</string>
|
||||
<string name="screen_room_notification_settings_mode_mentions_and_keywords">"Только упоминания и ключевые слова"</string>
|
||||
<string name="screen_room_notification_settings_room_custom_settings_title">"В этой комнате уведомить меня о"</string>
|
||||
<string name="screen_room_reactions_show_less">"Показать меньше"</string>
|
||||
<string name="screen_room_reactions_show_more">"Показать больше"</string>
|
||||
<string name="screen_room_retry_send_menu_send_again_action">"Отправить снова"</string>
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
<string name="screen_room_attachment_source_files">"Príloha"</string>
|
||||
<string name="screen_room_attachment_source_gallery">"Knižnica fotografií a videí"</string>
|
||||
<string name="screen_room_attachment_source_location">"Poloha"</string>
|
||||
<string name="screen_room_attachment_source_poll">"Anketa"</string>
|
||||
<string name="screen_room_encrypted_history_banner">"História správ v tejto miestnosti nie je momentálne k dispozícii"</string>
|
||||
<string name="screen_room_error_failed_retrieving_user_details">"Nepodarilo sa získať údaje o používateľovi"</string>
|
||||
<string name="screen_room_invite_again_alert_message">"Chceli by ste ich pozvať späť?"</string>
|
||||
|
|
@ -28,11 +29,13 @@
|
|||
<string name="screen_room_notification_settings_default_setting_footnote">"Môžete to zmeniť vo svojich %1$s."</string>
|
||||
<string name="screen_room_notification_settings_default_setting_footnote_content_link">"všeobecných nastaveniach"</string>
|
||||
<string name="screen_room_notification_settings_default_setting_title">"Predvolené nastavenie"</string>
|
||||
<string name="screen_room_notification_settings_edit_remove_setting">"Odstrániť vlastné nastavenie"</string>
|
||||
<string name="screen_room_notification_settings_error_loading_settings">"Pri načítavaní nastavení oznámení došlo k chybe."</string>
|
||||
<string name="screen_room_notification_settings_error_restoring_default">"Nepodarilo sa obnoviť predvolený režim, skúste to prosím znova."</string>
|
||||
<string name="screen_room_notification_settings_error_setting_mode">"Nepodarilo sa nastaviť režim, skúste to prosím znova."</string>
|
||||
<string name="screen_room_notification_settings_mode_all_messages">"Všetky správy"</string>
|
||||
<string name="screen_room_notification_settings_mode_mentions_and_keywords">"Iba zmienky a kľúčové slová"</string>
|
||||
<string name="screen_room_notification_settings_room_custom_settings_title">"V tejto miestnosti ma upozorniť na"</string>
|
||||
<string name="screen_room_reactions_show_less">"Zobraziť menej"</string>
|
||||
<string name="screen_room_reactions_show_more">"Zobraziť viac"</string>
|
||||
<string name="screen_room_retry_send_menu_send_again_action">"Odoslať znova"</string>
|
||||
|
|
|
|||
|
|
@ -27,8 +27,6 @@ dependencies {
|
|||
implementation(projects.libraries.architecture)
|
||||
implementation(projects.libraries.designsystem)
|
||||
implementation(projects.libraries.uiStrings)
|
||||
implementation(libs.androidx.constraintlayout)
|
||||
implementation(libs.androidx.constraintlayout.compose)
|
||||
implementation(projects.libraries.matrix.api)
|
||||
|
||||
ksp(libs.showkase.processor)
|
||||
|
|
|
|||
|
|
@ -1,118 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.features.poll.api
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
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.selection.selectableGroup
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.BarChart
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.libraries.designsystem.preview.DayNightPreviews
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.matrix.api.poll.PollAnswer
|
||||
import io.element.android.libraries.matrix.api.poll.PollKind
|
||||
import io.element.android.libraries.theme.ElementTheme
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Composable
|
||||
fun ActivePollContentView(
|
||||
question: String,
|
||||
answerItems: ImmutableList<PollAnswerItem>,
|
||||
pollKind: PollKind,
|
||||
onAnswerSelected: (PollAnswer) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val showResults = answerItems.any { it.isSelected }
|
||||
Column(
|
||||
modifier = modifier
|
||||
.selectableGroup()
|
||||
.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
Icon(imageVector = Icons.Default.BarChart, contentDescription = null)
|
||||
Text(
|
||||
text = question,
|
||||
style = ElementTheme.typography.fontBodyLgMedium
|
||||
)
|
||||
}
|
||||
|
||||
answerItems.forEach { answerItem ->
|
||||
PollAnswerView(
|
||||
answerItem = answerItem,
|
||||
onClick = { onAnswerSelected(answerItem.answer) }
|
||||
)
|
||||
}
|
||||
|
||||
val votesCount = answerItems.sumOf { it.votesCount }
|
||||
when {
|
||||
pollKind == PollKind.Undisclosed -> {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.align(Alignment.Start)
|
||||
.padding(start = 32.dp),
|
||||
style = ElementTheme.typography.fontBodyXsRegular,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
text = stringResource(CommonStrings.common_poll_undisclosed_text),
|
||||
)
|
||||
}
|
||||
showResults -> {
|
||||
Text(
|
||||
modifier = Modifier.align(Alignment.End),
|
||||
style = ElementTheme.typography.fontBodyXsRegular,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
text = stringResource(CommonStrings.common_poll_total_votes, votesCount),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DayNightPreviews
|
||||
@Composable
|
||||
internal fun ActivePollContentNoResultsPreview() = ElementPreview {
|
||||
ActivePollContentView(
|
||||
question = "What type of food should we have at the party?",
|
||||
answerItems = aPollAnswerItemList(isDisclosed = false),
|
||||
pollKind = PollKind.Undisclosed,
|
||||
onAnswerSelected = { },
|
||||
)
|
||||
}
|
||||
|
||||
@DayNightPreviews
|
||||
@Composable
|
||||
internal fun ActivePollContentWithResultsPreview() = ElementPreview {
|
||||
ActivePollContentView(
|
||||
question = "What type of food should we have at the party?",
|
||||
answerItems = aPollAnswerItemList(),
|
||||
pollKind = PollKind.Disclosed,
|
||||
onAnswerSelected = { },
|
||||
)
|
||||
}
|
||||
|
|
@ -23,14 +23,18 @@ import io.element.android.libraries.matrix.api.poll.PollAnswer
|
|||
*
|
||||
* @property answer the poll answer.
|
||||
* @property isSelected whether the user has selected this answer.
|
||||
* @property isEnabled whether the answer can be voted.
|
||||
* @property isWinner whether this is the winner answer in the poll.
|
||||
* @property isDisclosed whether the votes for this answer should be disclosed.
|
||||
* @property votesCount the number of votes for this answer.
|
||||
* @property progress the percentage of votes for this answer.
|
||||
* @property percentage the percentage of votes for this answer.
|
||||
*/
|
||||
data class PollAnswerItem(
|
||||
val answer: PollAnswer,
|
||||
val isSelected: Boolean,
|
||||
val isEnabled: Boolean,
|
||||
val isWinner: Boolean,
|
||||
val isDisclosed: Boolean,
|
||||
val votesCount: Int,
|
||||
val progress: Float,
|
||||
val percentage: Float,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -16,110 +16,166 @@
|
|||
|
||||
package io.element.android.features.poll.api
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.selection.selectable
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.CheckCircle
|
||||
import androidx.compose.material.icons.filled.RadioButtonUnchecked
|
||||
import androidx.compose.material3.IconButtonDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.StrokeCap
|
||||
import androidx.compose.ui.res.pluralStringResource
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.constraintlayout.compose.ConstraintLayout
|
||||
import androidx.constraintlayout.compose.Dimension
|
||||
import androidx.constraintlayout.compose.Visibility
|
||||
import io.element.android.libraries.designsystem.preview.DayNightPreviews
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
|
||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||
import io.element.android.libraries.designsystem.theme.components.IconToggleButton
|
||||
import io.element.android.libraries.designsystem.theme.components.LinearProgressIndicator
|
||||
import io.element.android.libraries.designsystem.theme.components.RadioButton
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.designsystem.toEnabledColor
|
||||
import io.element.android.libraries.theme.ElementTheme
|
||||
import io.element.android.libraries.ui.strings.CommonPlurals
|
||||
|
||||
@Suppress("DestructuringDeclarationWithTooManyEntries") // This is necessary to declare the constraints ids
|
||||
@Composable
|
||||
fun PollAnswerView(
|
||||
answerItem: PollAnswerItem,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
ConstraintLayout(
|
||||
Row(
|
||||
modifier
|
||||
.wrapContentHeight()
|
||||
.fillMaxWidth()
|
||||
.selectable(
|
||||
selected = answerItem.isSelected,
|
||||
enabled = answerItem.isEnabled,
|
||||
onClick = onClick,
|
||||
role = Role.RadioButton,
|
||||
)
|
||||
) {
|
||||
val (radioButton, answerText, votesText, progressBar) = createRefs()
|
||||
RadioButton(
|
||||
modifier = Modifier.constrainAs(radioButton) {
|
||||
top.linkTo(answerText.top)
|
||||
bottom.linkTo(answerText.bottom)
|
||||
start.linkTo(parent.start)
|
||||
end.linkTo(answerText.start)
|
||||
},
|
||||
selected = answerItem.isSelected,
|
||||
onClick = null // null recommended for accessibility with screenreaders
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.constrainAs(answerText) {
|
||||
width = Dimension.fillToConstraints
|
||||
top.linkTo(parent.top)
|
||||
start.linkTo(radioButton.end, margin = 8.dp)
|
||||
end.linkTo(votesText.start)
|
||||
bottom.linkTo(progressBar.top)
|
||||
},
|
||||
text = answerItem.answer.text,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.constrainAs(votesText) {
|
||||
start.linkTo(answerText.end)
|
||||
end.linkTo(parent.end)
|
||||
bottom.linkTo(answerText.bottom)
|
||||
visibility = if (answerItem.isDisclosed) Visibility.Visible else Visibility.Gone
|
||||
},
|
||||
text = pluralStringResource(
|
||||
id = CommonPlurals.common_poll_votes_count,
|
||||
count = answerItem.votesCount,
|
||||
answerItem.votesCount
|
||||
IconToggleButton(
|
||||
modifier = Modifier.size(22.dp),
|
||||
checked = answerItem.isSelected,
|
||||
enabled = answerItem.isEnabled,
|
||||
colors = IconButtonDefaults.iconToggleButtonColors(
|
||||
contentColor = ElementTheme.colors.iconSecondary,
|
||||
checkedContentColor = ElementTheme.colors.iconPrimary,
|
||||
disabledContentColor = ElementTheme.colors.iconDisabled,
|
||||
),
|
||||
style = ElementTheme.typography.fontBodySmRegular,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
)
|
||||
LinearProgressIndicator(
|
||||
progress = answerItem.progress,
|
||||
modifier = Modifier
|
||||
.constrainAs(progressBar) {
|
||||
start.linkTo(answerText.start)
|
||||
end.linkTo(votesText.end)
|
||||
top.linkTo(answerText.bottom, margin = 10.dp)
|
||||
bottom.linkTo(parent.bottom)
|
||||
width = Dimension.fillToConstraints
|
||||
visibility = if (answerItem.isDisclosed) Visibility.Visible else Visibility.Gone
|
||||
|
||||
onCheckedChange = { onClick() },
|
||||
) {
|
||||
Icon(
|
||||
imageVector = if (answerItem.isSelected) {
|
||||
Icons.Default.CheckCircle
|
||||
} else {
|
||||
Icons.Default.RadioButtonUnchecked
|
||||
},
|
||||
strokeCap = StrokeCap.Round,
|
||||
)
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.width(12.dp))
|
||||
Column {
|
||||
Row {
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = answerItem.answer.text,
|
||||
style = if (answerItem.isWinner) ElementTheme.typography.fontBodyLgMedium else ElementTheme.typography.fontBodyLgRegular,
|
||||
)
|
||||
if (answerItem.isDisclosed) {
|
||||
Text(
|
||||
modifier = Modifier.align(Alignment.Bottom),
|
||||
text = pluralStringResource(
|
||||
id = CommonPlurals.common_poll_votes_count,
|
||||
count = answerItem.votesCount,
|
||||
answerItem.votesCount
|
||||
),
|
||||
style = if (answerItem.isWinner) ElementTheme.typography.fontBodySmMedium else ElementTheme.typography.fontBodySmRegular,
|
||||
color = if (answerItem.isWinner) ElementTheme.colors.textPrimary else ElementTheme.colors.textSecondary,
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
LinearProgressIndicator(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
color = if (answerItem.isWinner) ElementTheme.colors.textSuccessPrimary else answerItem.isEnabled.toEnabledColor(),
|
||||
progress = when {
|
||||
answerItem.isDisclosed -> answerItem.percentage
|
||||
answerItem.isSelected -> 1f
|
||||
else -> 0f
|
||||
},
|
||||
strokeCap = StrokeCap.Round,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DayNightPreviews
|
||||
@Preview
|
||||
@Composable
|
||||
internal fun PollAnswerViewNoResultsPreview() = ElementPreview {
|
||||
internal fun PollAnswerDisclosedNotSelectedPreview() = ElementThemedPreview {
|
||||
PollAnswerView(
|
||||
answerItem = aPollAnswerItem(),
|
||||
answerItem = aPollAnswerItem(isDisclosed = true, isSelected = false),
|
||||
onClick = { },
|
||||
)
|
||||
}
|
||||
|
||||
@DayNightPreviews
|
||||
@Preview
|
||||
@Composable
|
||||
internal fun PollAnswerViewWithResultPreview() = ElementPreview {
|
||||
internal fun PollAnswerDisclosedSelectedPreview() = ElementThemedPreview {
|
||||
PollAnswerView(
|
||||
answerItem = aPollAnswerItem(isDisclosed = true),
|
||||
answerItem = aPollAnswerItem(isDisclosed = true, isSelected = true),
|
||||
onClick = { }
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
internal fun PollAnswerUndisclosedNotSelectedPreview() = ElementThemedPreview {
|
||||
PollAnswerView(
|
||||
answerItem = aPollAnswerItem(isDisclosed = false, isSelected = false),
|
||||
onClick = { },
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
internal fun PollAnswerUndisclosedSelectedPreview() = ElementThemedPreview {
|
||||
PollAnswerView(
|
||||
answerItem = aPollAnswerItem(isDisclosed = false, isSelected = true),
|
||||
onClick = { }
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
internal fun PollAnswerEndedWinnerNotSelectedPreview() = ElementThemedPreview {
|
||||
PollAnswerView(
|
||||
answerItem = aPollAnswerItem(isDisclosed = true, isSelected = false, isEnabled = false, isWinner = true),
|
||||
onClick = { }
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
internal fun PollAnswerEndedWinnerSelectedPreview() = ElementThemedPreview {
|
||||
PollAnswerView(
|
||||
answerItem = aPollAnswerItem(isDisclosed = true, isSelected = true, isEnabled = false, isWinner = true),
|
||||
onClick = { }
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
internal fun PollAnswerEndedSelectedPreview() = ElementThemedPreview {
|
||||
PollAnswerView(
|
||||
answerItem = aPollAnswerItem(isDisclosed = true, isSelected = true, isEnabled = false, isWinner = false),
|
||||
onClick = { }
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,27 +19,33 @@ package io.element.android.features.poll.api
|
|||
import io.element.android.libraries.matrix.api.poll.PollAnswer
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
fun aPollAnswerItemList(isDisclosed: Boolean = true) = persistentListOf(
|
||||
fun aPollAnswerItemList(isEnded: Boolean = false, isDisclosed: Boolean = true) = persistentListOf(
|
||||
aPollAnswerItem(
|
||||
answer = PollAnswer("option_1", "Italian \uD83C\uDDEE\uD83C\uDDF9"),
|
||||
isDisclosed = isDisclosed,
|
||||
isEnabled = !isEnded,
|
||||
isWinner = isEnded,
|
||||
votesCount = 5,
|
||||
progress = 0.5f
|
||||
percentage = 0.5f
|
||||
),
|
||||
aPollAnswerItem(
|
||||
answer = PollAnswer("option_2", "Chinese \uD83C\uDDE8\uD83C\uDDF3"),
|
||||
isDisclosed = isDisclosed,
|
||||
isEnabled = !isEnded,
|
||||
isWinner = false,
|
||||
votesCount = 0,
|
||||
progress = 0f
|
||||
percentage = 0f
|
||||
),
|
||||
aPollAnswerItem(
|
||||
answer = PollAnswer("option_3", "Brazilian \uD83C\uDDE7\uD83C\uDDF7"),
|
||||
isDisclosed = isDisclosed,
|
||||
isEnabled = !isEnded,
|
||||
isWinner = false,
|
||||
isSelected = true,
|
||||
votesCount = 1,
|
||||
progress = 0.1f
|
||||
percentage = 0.1f
|
||||
),
|
||||
aPollAnswerItem(isDisclosed = isDisclosed),
|
||||
aPollAnswerItem(isDisclosed = isDisclosed, isEnabled = !isEnded),
|
||||
)
|
||||
|
||||
fun aPollAnswerItem(
|
||||
|
|
@ -48,13 +54,17 @@ fun aPollAnswerItem(
|
|||
"French \uD83C\uDDEB\uD83C\uDDF7 But make it a very very very long option then this should just keep expanding"
|
||||
),
|
||||
isSelected: Boolean = false,
|
||||
isEnabled: Boolean = true,
|
||||
isWinner: Boolean = false,
|
||||
isDisclosed: Boolean = true,
|
||||
votesCount: Int = 4,
|
||||
progress: Float = 0.4f,
|
||||
percentage: Float = 0.4f,
|
||||
) = PollAnswerItem(
|
||||
answer = answer,
|
||||
isSelected = isSelected,
|
||||
isEnabled = isEnabled,
|
||||
isWinner = isWinner,
|
||||
isDisclosed = isDisclosed,
|
||||
votesCount = votesCount,
|
||||
progress = progress
|
||||
percentage = percentage
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.features.poll.api
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
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.selection.selectableGroup
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Poll
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.libraries.designsystem.preview.DayNightPreviews
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.matrix.api.poll.PollAnswer
|
||||
import io.element.android.libraries.matrix.api.poll.PollKind
|
||||
import io.element.android.libraries.theme.ElementTheme
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Composable
|
||||
fun PollContentView(
|
||||
question: String,
|
||||
answerItems: ImmutableList<PollAnswerItem>,
|
||||
pollKind: PollKind,
|
||||
isPollEnded: Boolean,
|
||||
onAnswerSelected: (PollAnswer) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.selectableGroup()
|
||||
.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
PollTitle(title = question)
|
||||
|
||||
PollAnswers(answerItems = answerItems, onAnswerSelected = onAnswerSelected)
|
||||
|
||||
when {
|
||||
isPollEnded || pollKind == PollKind.Disclosed -> DisclosedPollBottomNotice(answerItems)
|
||||
pollKind == PollKind.Undisclosed -> UndisclosedPollBottomNotice()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun PollTitle(
|
||||
title: String,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(22.dp),
|
||||
imageVector = Icons.Outlined.Poll,
|
||||
contentDescription = null
|
||||
)
|
||||
Text(
|
||||
text = title,
|
||||
style = ElementTheme.typography.fontBodyLgMedium
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun PollAnswers(
|
||||
answerItems: ImmutableList<PollAnswerItem>,
|
||||
onAnswerSelected: (PollAnswer) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
|
||||
answerItems.forEach { answerItem ->
|
||||
PollAnswerView(
|
||||
modifier = modifier,
|
||||
answerItem = answerItem,
|
||||
onClick = { onAnswerSelected(answerItem.answer) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun ColumnScope.DisclosedPollBottomNotice(
|
||||
answerItems: ImmutableList<PollAnswerItem>,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val votesCount = answerItems.sumOf { it.votesCount }
|
||||
Text(
|
||||
modifier = modifier.align(Alignment.End),
|
||||
style = ElementTheme.typography.fontBodyXsRegular,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
text = stringResource(CommonStrings.common_poll_total_votes, votesCount),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ColumnScope.UndisclosedPollBottomNotice(modifier: Modifier = Modifier) {
|
||||
Text(
|
||||
modifier = modifier
|
||||
.align(Alignment.Start)
|
||||
.padding(start = 34.dp),
|
||||
style = ElementTheme.typography.fontBodyXsRegular,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
text = stringResource(CommonStrings.common_poll_undisclosed_text),
|
||||
)
|
||||
}
|
||||
|
||||
@DayNightPreviews
|
||||
@Composable
|
||||
internal fun PollContentUndisclosedPreview() = ElementPreview {
|
||||
PollContentView(
|
||||
question = "What type of food should we have at the party?",
|
||||
answerItems = aPollAnswerItemList(isDisclosed = false),
|
||||
pollKind = PollKind.Undisclosed,
|
||||
isPollEnded = false,
|
||||
onAnswerSelected = { },
|
||||
)
|
||||
}
|
||||
|
||||
@DayNightPreviews
|
||||
@Composable
|
||||
internal fun PollContentDisclosedPreview() = ElementPreview {
|
||||
PollContentView(
|
||||
question = "What type of food should we have at the party?",
|
||||
answerItems = aPollAnswerItemList(),
|
||||
pollKind = PollKind.Disclosed,
|
||||
isPollEnded = false,
|
||||
onAnswerSelected = { },
|
||||
)
|
||||
}
|
||||
|
||||
@DayNightPreviews
|
||||
@Composable
|
||||
internal fun PollContentEndedPreview() = ElementPreview {
|
||||
PollContentView(
|
||||
question = "What type of food should we have at the party?",
|
||||
answerItems = aPollAnswerItemList(isEnded = true),
|
||||
pollKind = PollKind.Disclosed,
|
||||
isPollEnded = false,
|
||||
onAnswerSelected = { },
|
||||
)
|
||||
}
|
||||
|
|
@ -20,7 +20,6 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
|
|
@ -34,7 +33,6 @@ import io.element.android.libraries.matrix.api.MatrixClient
|
|||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.api.user.getCurrentUser
|
||||
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
|
||||
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
|
||||
import io.element.android.services.analytics.api.AnalyticsService
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -62,11 +60,8 @@ class PreferencesRootPresenter @Inject constructor(
|
|||
val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState()
|
||||
val hasAnalyticsProviders = remember { analyticsService.getAvailableAnalyticsProviders().isNotEmpty() }
|
||||
|
||||
// Session verification status (unknown, not verified, verified)
|
||||
val sessionVerifiedStatus by sessionVerificationService.sessionVerifiedStatus.collectAsState()
|
||||
val sessionIsNotVerified by remember {
|
||||
derivedStateOf { sessionVerifiedStatus == SessionVerifiedStatus.NotVerified }
|
||||
}
|
||||
// We should display the 'complete verification' option if the current session can be verified
|
||||
val showCompleteVerification by sessionVerificationService.canVerifySessionFlow.collectAsState(false)
|
||||
|
||||
val accountManagementUrl: MutableState<String?> = remember {
|
||||
mutableStateOf(null)
|
||||
|
|
@ -82,7 +77,7 @@ class PreferencesRootPresenter @Inject constructor(
|
|||
logoutState = logoutState,
|
||||
myUser = matrixUser.value,
|
||||
version = versionFormatter.get(),
|
||||
showCompleteVerification = sessionIsNotVerified,
|
||||
showCompleteVerification = showCompleteVerification,
|
||||
accountManagementUrl = accountManagementUrl.value,
|
||||
showAnalyticsSettings = hasAnalyticsProviders,
|
||||
showDeveloperSettings = showDeveloperSettings,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_bug_report_attach_screenshot">"Připojit snímek obrazovky"</string>
|
||||
<string name="screen_bug_report_contact_me">"V případě dalších dotazů se na mě můžete obrátit"</string>
|
||||
<string name="screen_bug_report_contact_me">"V případě dalších dotazů se na mě můžete obrátit."</string>
|
||||
<string name="screen_bug_report_contact_me_title">"Kontaktujte mě"</string>
|
||||
<string name="screen_bug_report_edit_screenshot">"Upravit snímek obrazovky"</string>
|
||||
<string name="screen_bug_report_editor_description">"Popište prosím chybu. Co jste udělali? Co jste očekávali, že se stane? Co se ve skutečnosti stalo? Uveďte co nejvíce podrobností."</string>
|
||||
|
|
|
|||
|
|
@ -10,6 +10,6 @@
|
|||
<string name="screen_bug_report_include_crash_logs">"Absturzprotokolle senden"</string>
|
||||
<string name="screen_bug_report_include_logs">"Logs zulassen"</string>
|
||||
<string name="screen_bug_report_include_screenshot">"Bildschirmfoto senden"</string>
|
||||
<string name="screen_bug_report_logs_description">"Um zu überprüfen, ob alles wie vorgesehen funktioniert, werden Protokolle mit deiner Nachricht gesendet. Diese werden privat sein. Um nur Ihre Nachricht zu senden, schalte diese Einstellung aus."</string>
|
||||
<string name="screen_bug_report_logs_description">"Deiner Nachricht werden Protokolle beigefügt, um sicherzustellen, dass alles ordnungsgemäß funktioniert. Um deine Nachricht ohne Logs zu senden, deaktiviere diese Einstellung."</string>
|
||||
<string name="screen_bug_report_rash_logs_alert_title">"%1$s ist bei der letzten Verwendung abgestürzt. Möchtest du uns einen Absturzbericht senden?"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,12 @@
|
|||
<string name="screen_room_details_edition_error_title">"Nelze aktualizovat místnost"</string>
|
||||
<string name="screen_room_details_encryption_enabled_subtitle">"Zprávy jsou zabezpečeny zámky. Pouze vy a příjemci máte jedinečné klíče k jejich odemčení."</string>
|
||||
<string name="screen_room_details_encryption_enabled_title">"Šifrování zpráv povoleno"</string>
|
||||
<string name="screen_room_details_error_loading_notification_settings">"Při načítání nastavení oznámení došlo k chybě."</string>
|
||||
<string name="screen_room_details_error_muting">"Ztišení této místnosti se nezdařilo, zkuste to prosím znovu."</string>
|
||||
<string name="screen_room_details_error_unmuting">"Nepodařilo se zrušit ztišení této místnosti, zkuste to prosím znovu."</string>
|
||||
<string name="screen_room_details_invite_people_title">"Pozvat lidi"</string>
|
||||
<string name="screen_room_details_notification_mode_custom">"Vlastní"</string>
|
||||
<string name="screen_room_details_notification_mode_default">"Výchozí"</string>
|
||||
<string name="screen_room_details_notification_title">"Oznámení"</string>
|
||||
<string name="screen_room_details_room_name_label">"Název místnosti"</string>
|
||||
<string name="screen_room_details_share_room_title">"Sdílet místnost"</string>
|
||||
|
|
|
|||
|
|
@ -1,18 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<plurals name="screen_room_member_list_header_title">
|
||||
<item quantity="one">"1 Person"</item>
|
||||
<item quantity="one">"%1$d Person"</item>
|
||||
<item quantity="other">"%1$d Personen"</item>
|
||||
</plurals>
|
||||
<string name="screen_room_details_add_topic_title">"Thema hinzufügen"</string>
|
||||
<string name="screen_room_details_already_a_member">"Bereits Mitglied"</string>
|
||||
<string name="screen_room_details_already_invited">"Bereits eingeladen"</string>
|
||||
<string name="screen_room_details_edit_room_title">"Raum bearbeiten"</string>
|
||||
<string name="screen_room_details_edition_error">"Wir konnten nicht alle Informationen für diesen Raum aktualisieren."</string>
|
||||
<string name="screen_room_details_edition_error">"Es gab einen unbekannten Fehler und die Informationen konnten nicht geändert werden."</string>
|
||||
<string name="screen_room_details_edition_error_title">"Raum konnte nicht aktualisiert werden"</string>
|
||||
<string name="screen_room_details_encryption_enabled_subtitle">"Nachrichten sind mit Schlössern gesichert. Nur du und der Empfänger haben die eindeutigen Schlüssel, um sie zu entsperren."</string>
|
||||
<string name="screen_room_details_encryption_enabled_title">"Nachrichtenverschlüsselung aktiviert"</string>
|
||||
<string name="screen_room_details_error_loading_notification_settings">"Beim Laden der Benachrichtigungseinstellungen ist ein Fehler aufgetreten."</string>
|
||||
<string name="screen_room_details_error_muting">"Das Stummschalten dieses Raums ist fehlgeschlagen. Bitte versuche es erneut."</string>
|
||||
<string name="screen_room_details_error_unmuting">"Die Stummschaltung dieses Raums konnte nicht aufgehoben werden. Bitte versuchen Sie es erneut."</string>
|
||||
<string name="screen_room_details_invite_people_title">"Personen einladen"</string>
|
||||
<string name="screen_room_details_notification_mode_custom">"Benutzerdefiniert"</string>
|
||||
<string name="screen_room_details_notification_mode_default">"Standard"</string>
|
||||
<string name="screen_room_details_notification_title">"Benachrichtigungen"</string>
|
||||
<string name="screen_room_details_room_name_label">"Raumname"</string>
|
||||
<string name="screen_room_details_share_room_title">"Raum teilen"</string>
|
||||
|
|
@ -20,10 +25,10 @@
|
|||
<string name="screen_room_member_list_pending_header_title">"Ausstehend"</string>
|
||||
<string name="screen_room_member_list_room_members_header_title">"Raummitglieder"</string>
|
||||
<string name="screen_dm_details_block_alert_action">"Blockieren"</string>
|
||||
<string name="screen_dm_details_block_alert_description">"Blockierte Benutzer können dir keine Nachrichten senden und alle Nachrichten von ihnen werden ausgeblendet. Du kannst diese Aktion jederzeit rückgängig machen."</string>
|
||||
<string name="screen_dm_details_block_alert_description">"Blockierte Benutzer können dir keine Nachrichten senden und alle ihre Nachrichten werden ausgeblendet. Du kannst sie jederzeit entsperren."</string>
|
||||
<string name="screen_dm_details_block_user">"Nutzer blockieren"</string>
|
||||
<string name="screen_dm_details_unblock_alert_action">"Blockierung aufheben"</string>
|
||||
<string name="screen_dm_details_unblock_alert_description">"Wenn du den Benutzer entsperrst, kannst du wieder alle Nachrichten von ihm sehen."</string>
|
||||
<string name="screen_dm_details_unblock_alert_description">"Du wirst alle ihre Nachrichten wieder sehen."</string>
|
||||
<string name="screen_dm_details_unblock_user">"Nutzer entblockieren"</string>
|
||||
<string name="screen_room_details_leave_room_title">"Raum verlassen"</string>
|
||||
<string name="screen_room_details_people_title">"Personen"</string>
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ import io.element.android.libraries.matrix.api.MatrixClient
|
|||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.api.user.getCurrentUser
|
||||
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
|
||||
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
|
@ -73,11 +72,11 @@ class RoomListPresenter @Inject constructor(
|
|||
}
|
||||
|
||||
// Session verification status (unknown, not verified, verified)
|
||||
val sessionVerifiedStatus by sessionVerificationService.sessionVerifiedStatus.collectAsState()
|
||||
val canVerifySession by sessionVerificationService.canVerifySessionFlow.collectAsState(initial = false)
|
||||
var verificationPromptDismissed by rememberSaveable { mutableStateOf(false) }
|
||||
// We combine both values to only display the prompt if the session is not verified and it wasn't dismissed
|
||||
val displayVerificationPrompt by remember {
|
||||
derivedStateOf { sessionVerifiedStatus == SessionVerifiedStatus.NotVerified && !verificationPromptDismissed }
|
||||
derivedStateOf { canVerifySession && !verificationPromptDismissed }
|
||||
}
|
||||
|
||||
var displaySearchResults by rememberSaveable { mutableStateOf(false) }
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_roomlist_a11y_create_message">"Vytvořte novou konverzaci nebo místnost"</string>
|
||||
<string name="screen_roomlist_empty_message">"Začněte tím, že někomu pošnete zprávu."</string>
|
||||
<string name="screen_roomlist_empty_title">"Zatím žádné konverzace."</string>
|
||||
<string name="screen_roomlist_main_space_title">"Všechny chaty"</string>
|
||||
<string name="session_verification_banner_message">"Zdá se, že používáte nové zařízení. Ověřte přihlášení, abyste měli přístup k zašifrovaným zprávám."</string>
|
||||
<string name="session_verification_banner_title">"Přístup k historii zpráv"</string>
|
||||
<string name="session_verification_banner_title">"Ověřte, že jste to vy"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_roomlist_a11y_create_message">"Ein neues Gespräch oder einen neuen Raum erstellen"</string>
|
||||
<string name="screen_roomlist_empty_message">"Beginnen, indem du jemandem eine Nachricht sendest."</string>
|
||||
<string name="screen_roomlist_empty_title">"Noch keine Chats."</string>
|
||||
<string name="screen_roomlist_main_space_title">"Alle Chats"</string>
|
||||
<string name="session_verification_banner_message">"Es sieht so aus, als ob du ein neues Gerät verwendest. Verifiziere, dass du es bist, um auf deine verschlüsselten Nachrichten zuzugreifen."</string>
|
||||
<string name="session_verification_banner_title">"Greife auf deine Nachrichten-Historie zu"</string>
|
||||
<string name="session_verification_banner_title">"Verifiziere, dass du es bist"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -202,7 +202,7 @@ class RoomListPresenterTests {
|
|||
fun `present - handle DismissRequestVerificationPrompt`() = runTest {
|
||||
val roomListService = FakeRoomListService()
|
||||
val matrixClient = FakeMatrixClient(
|
||||
roomListService = roomListService
|
||||
roomListService = roomListService,
|
||||
)
|
||||
val presenter = createRoomListPresenter(
|
||||
client = matrixClient,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue