Update UI for poll in the timeline

This commit is contained in:
Florian Renaud 2023-08-17 14:16:22 +02:00
parent 93e3029f92
commit 079d89ace5
7 changed files with 109 additions and 101 deletions

View file

@ -18,12 +18,14 @@ 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.filled.BarChart
import androidx.compose.material.icons.outlined.Poll
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@ -47,54 +49,80 @@ fun ActivePollContentView(
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
)
}
PollTitle(title = question)
answerItems.forEach { answerItem ->
PollAnswerView(
answerItem = answerItem,
onClick = { onAnswerSelected(answerItem.answer) }
)
}
PollAnswers(answerItems = answerItems, onAnswerSelected = onAnswerSelected)
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),
)
}
when (pollKind) {
PollKind.Disclosed -> DisclosedPollBottomNotice(answerItems)
PollKind.Undisclosed -> UndisclosedPollBottomNotice()
}
}
}
@Composable
internal fun PollTitle(
title: String,
) {
Row(
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,
) {
answerItems.forEach { answerItem ->
PollAnswerView(
answerItem = answerItem,
onClick = { onAnswerSelected(answerItem.answer) }
)
}
}
@Composable
internal fun ColumnScope.DisclosedPollBottomNotice(
answerItems: ImmutableList<PollAnswerItem>,
) {
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() {
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 ActivePollContentNoResultsPreview() = ElementPreview {

View file

@ -16,18 +16,21 @@
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.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.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.theme.components.LinearProgressIndicator
@ -36,16 +39,14 @@ import io.element.android.libraries.designsystem.theme.components.Text
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,
@ -53,56 +54,38 @@ fun PollAnswerView(
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)
},
modifier = Modifier.size(22.dp),
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
),
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
},
strokeCap = StrokeCap.Round,
)
Spacer(modifier = Modifier.width(12.dp))
Column {
Row {
Text(
modifier = Modifier.weight(1f),
text = answerItem.answer.text
)
if (answerItem.isDisclosed) {
Text(
modifier = Modifier.align(Alignment.Bottom),
text = pluralStringResource(
id = CommonPlurals.common_poll_votes_count,
count = answerItem.votesCount,
answerItem.votesCount
),
style = ElementTheme.typography.fontBodySmRegular,
color = ElementTheme.colors.textSecondary,
)
}
}
Spacer(modifier = Modifier.height(10.dp))
LinearProgressIndicator(
modifier = Modifier.fillMaxWidth(),
progress = answerItem.progress,
strokeCap = StrokeCap.Round,
)
}
}
}