diff --git a/changelog.d/1113.wip b/changelog.d/1113.wip new file mode 100644 index 0000000000..f95dc6ba88 --- /dev/null +++ b/changelog.d/1113.wip @@ -0,0 +1 @@ +[Polls] Improve UI and render ended state diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemPollView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemPollView.kt index db3503be37..3608593cde 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemPollView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemPollView.kt @@ -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, ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentPollFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentPollFactory.kt index 7c61466337..4a21874e1c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentPollFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentPollFactory.kt @@ -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, ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemPollContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemPollContent.kt index b8a2fa8bca..3c0e0edfd4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemPollContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemPollContent.kt @@ -25,7 +25,7 @@ data class TimelineItemPollContent( val answerItems: List, val votes: Map>, val pollKind: PollKind, - val isDisclosed: Boolean, + val isEnded: Boolean, ) : TimelineItemEventContent { override val type: String = "TimelineItemPollContent" } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemPollContentProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemPollContentProvider.kt index 665d507ead..49dfa58c8a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemPollContentProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemPollContentProvider.kt @@ -24,16 +24,16 @@ open class TimelineItemPollContentProvider : PreviewParameterProvider 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(), ) } diff --git a/features/poll/api/build.gradle.kts b/features/poll/api/build.gradle.kts index be198ba740..6d94fa1b2f 100644 --- a/features/poll/api/build.gradle.kts +++ b/features/poll/api/build.gradle.kts @@ -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) diff --git a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/ActivePollContentView.kt b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/ActivePollContentView.kt deleted file mode 100644 index 587c3306b1..0000000000 --- a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/ActivePollContentView.kt +++ /dev/null @@ -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, - 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 = { }, - ) -} diff --git a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerItem.kt b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerItem.kt index 24db33ad1f..1955701c5b 100644 --- a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerItem.kt +++ b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerItem.kt @@ -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, ) diff --git a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerView.kt b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerView.kt index 26fa6fbb71..3e52cc5fc7 100644 --- a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerView.kt +++ b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerView.kt @@ -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 = { } ) } diff --git a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerViewProvider.kt b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerViewProvider.kt index 062d09fd88..e94b5adeeb 100644 --- a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerViewProvider.kt +++ b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerViewProvider.kt @@ -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 ) diff --git a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollContentView.kt b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollContentView.kt new file mode 100644 index 0000000000..419aa21204 --- /dev/null +++ b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollContentView.kt @@ -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, + 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, + 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, + 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 = { }, + ) +} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/IconToggleButton.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/IconToggleButton.kt new file mode 100644 index 0000000000..fd355d3860 --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/IconToggleButton.kt @@ -0,0 +1,89 @@ +/* + * 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.libraries.designsystem.theme.components + +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +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.material3.IconToggleButtonColors +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import io.element.android.libraries.designsystem.preview.ElementThemedPreview +import io.element.android.libraries.designsystem.preview.PreviewGroup + +@Composable +fun IconToggleButton( + checked: Boolean, + onCheckedChange: (Boolean) -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + colors: IconToggleButtonColors = IconButtonDefaults.iconToggleButtonColors(), + interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, + content: @Composable () -> Unit +) { + androidx.compose.material3.IconToggleButton( + checked = checked, + onCheckedChange = onCheckedChange, + modifier = modifier, + enabled = enabled, + colors = colors, + interactionSource = interactionSource, + content = content, + ) +} + +@Preview(group = PreviewGroup.Toggles) +@Composable +internal fun IconToggleButtonPreview() = ElementThemedPreview(vertical = false) { ContentToPreview() } + +@Composable +private fun ContentToPreview() { + var checked by remember { mutableStateOf(false) } + Column { + Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) { + val icon: @Composable () -> Unit = { + Icon( + imageVector = if (checked) Icons.Default.CheckCircle else Icons.Default.RadioButtonUnchecked, + contentDescription = "IconToggleButton" + ) + } + IconToggleButton(checked = checked, enabled = true, onCheckedChange = { checked = !checked }, content = icon) + IconToggleButton(checked = checked, enabled = false, onCheckedChange = { checked = !checked }, content = icon) + } + Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) { + val icon: @Composable () -> Unit = { + Icon( + imageVector = if (!checked) Icons.Default.CheckCircle else Icons.Default.RadioButtonUnchecked, + contentDescription = "IconToggleButton" + ) + } + IconToggleButton(checked = !checked, enabled = true, onCheckedChange = { checked = !checked }, content = icon) + IconToggleButton(checked = !checked, enabled = false, onCheckedChange = { checked = !checked }, content = icon) + } + } +} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/RadioButton.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/RadioButton.kt index a8b186a6b2..9f373d1b71 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/RadioButton.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/RadioButton.kt @@ -23,7 +23,10 @@ import androidx.compose.foundation.layout.Row import androidx.compose.material3.RadioButtonColors import androidx.compose.material3.RadioButtonDefaults import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -67,14 +70,15 @@ internal fun RadioButtonPreview() = ElementThemedPreview(vertical = false) { Con @Composable private fun ContentToPreview() { + var checked by remember { mutableStateOf(false) } Column { Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) { - RadioButton(selected = false, onClick = {}) - RadioButton(selected = false, enabled = false, onClick = {}) + RadioButton(selected = checked, enabled = true, onClick = { checked = !checked }) + RadioButton(selected = checked, enabled = false, onClick = { checked = !checked }) } Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) { - RadioButton(selected = true, onClick = {}) - RadioButton(selected = true, enabled = false, onClick = {}) + RadioButton(selected = !checked, enabled = true, onClick = { checked = !checked }) + RadioButton(selected = !checked, enabled = false, onClick = { checked = !checked }) } } } diff --git a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt index 65fc191e53..a0a0525fb5 100644 --- a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt +++ b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt @@ -95,7 +95,7 @@ class DefaultRoomLastMessageFormatter @Inject constructor( is StateContent -> { stateContentFormatter.format(content, senderDisplayName, isOutgoing, RenderingMode.RoomList) } - is PollContent, + is PollContent, // TODO Polls: handle last message is FailedToParseMessageLikeContent, is FailedToParseStateContent, is UnknownContent -> { prefixIfNeeded(sp.getString(CommonStrings.common_unsupported_event), senderDisplayName, isDmRoom) } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/poll/PollKind.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/poll/PollKind.kt index 85bb7c0256..b78f00bc86 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/poll/PollKind.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/poll/PollKind.kt @@ -20,5 +20,8 @@ enum class PollKind { Disclosed, /** Results should be only revealed when the poll is ended. */ - Undisclosed + Undisclosed, } + +val PollKind.isDisclosed: Boolean + get() = this == PollKind.Disclosed diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-D-13_14_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-D-13_14_null_0,NEXUS_5,1.0,en].png index 0134aee79e..9bd8087f0c 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-D-13_14_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-D-13_14_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f704348b48d03ce3e788e7e37298a008116c66f3ad0a074d164df4ebbb05d9d8 -size 48964 +oid sha256:15d14bf99af3cd0433870ebd6032d9bf4a45196e2ef1df7184cc55859a704dee +size 49008 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-D-13_14_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-D-13_14_null_1,NEXUS_5,1.0,en].png index 0134aee79e..170f3d997a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-D-13_14_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-D-13_14_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f704348b48d03ce3e788e7e37298a008116c66f3ad0a074d164df4ebbb05d9d8 -size 48964 +oid sha256:6e47fc219bbd63b76d01a5e50c3e4c6b1b0a8b4ec40b08b13271d0b2673d8d5e +size 50932 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-N-13_15_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-N-13_15_null_0,NEXUS_5,1.0,en].png index f327dc9154..cb9d6334b0 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-N-13_15_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-N-13_15_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:945d8f7b4226c451c8d33f51bf051c7f08a316c786259d49475b2e2cde407bd7 -size 46415 +oid sha256:cacb91ffca97f21bbc19b29525813f58fc8017a858aa28ccd4e620b70a8cd9ca +size 46119 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-N-13_15_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-N-13_15_null_1,NEXUS_5,1.0,en].png index f327dc9154..ec2787d842 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-N-13_15_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-N-13_15_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:945d8f7b4226c451c8d33f51bf051c7f08a316c786259d49475b2e2cde407bd7 -size 46415 +oid sha256:3f1c14a23ee598ece6843a68d3ca0b1d1f725f53174bd493e27c6137de70c508 +size 48296 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_ActivePollContentNoResults-D-0_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_ActivePollContentNoResults-D-0_0_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 57ddb69c7a..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_ActivePollContentNoResults-D-0_0_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fb9e4bbe341a84452206a7485a477d81725b535369b1dfad3cf430548dbb21e8 -size 46450 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_ActivePollContentNoResults-N-0_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_ActivePollContentNoResults-N-0_1_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 7a31eae39a..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_ActivePollContentNoResults-N-0_1_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8bfbbae8e27c4be7ea7fadeff2470773775cb476ef37b25c8f1bb8c35b5eddd9 -size 43082 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_ActivePollContentWithResults-D-1_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_ActivePollContentWithResults-D-1_1_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 0134aee79e..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_ActivePollContentWithResults-D-1_1_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f704348b48d03ce3e788e7e37298a008116c66f3ad0a074d164df4ebbb05d9d8 -size 48964 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_ActivePollContentWithResults-N-1_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_ActivePollContentWithResults-N-1_2_null,NEXUS_5,1.0,en].png deleted file mode 100644 index f327dc9154..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_ActivePollContentWithResults-N-1_2_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:945d8f7b4226c451c8d33f51bf051c7f08a316c786259d49475b2e2cde407bd7 -size 46415 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerDisclosedNotSelected_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerDisclosedNotSelected_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..a3e90a69fe --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerDisclosedNotSelected_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:74508ef7f77a9c8713c75586ae4d34a9daab2608dbbd2f20de3e4d4a9a9be7e9 +size 39225 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerDisclosedSelected_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerDisclosedSelected_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..7be79c2135 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerDisclosedSelected_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4ac9e0523fc99d472a1fe8f21719e64dbb7d1ec01059dd4183aa4a152f8ead55 +size 38673 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerEndedSelected_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerEndedSelected_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..7ee39de1a4 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerEndedSelected_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ed68d9ebe67d5dad938a3efcd8c2b680444c650e635bdc04cfd140ba694d9f1d +size 38928 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerEndedWinnerNotSelected_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerEndedWinnerNotSelected_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..b2d901dc0d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerEndedWinnerNotSelected_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1d5c9d2042dad75b48b61cdbae5b2425d7c935eb860df5d5c6fa3bcb327d13d1 +size 38842 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerEndedWinnerSelected_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerEndedWinnerSelected_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..4998418006 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerEndedWinnerSelected_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d4ab648f5651b7457635be3eabb914be1c976154d12a163f1c1bb2cd92168824 +size 38730 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerUndisclosedNotSelected_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerUndisclosedNotSelected_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..a2cd64d048 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerUndisclosedNotSelected_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fbb713d36f8b36ce6b55f38c10323e784a80f6187e6613ded91dc531b23a7cb7 +size 36444 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerUndisclosedSelected_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerUndisclosedSelected_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..2d973414d1 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerUndisclosedSelected_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:31735c42ea83974595544a0f03636a2337210f23e60d4b7f8e41d46ba21d483f +size 35920 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerViewNoResults-D-2_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerViewNoResults-D-2_2_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 1eef37d750..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerViewNoResults-D-2_2_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c4c5bc5f2086bd78a364e9996f0f41526d73d69857875594f5de7ea9998333d7 -size 23388 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerViewNoResults-N-2_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerViewNoResults-N-2_3_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 95c64c4548..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerViewNoResults-N-2_3_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:42848910a949ec938ccc7a3547b19d1c2c81193f9a93b1db21ca77e4d9ed9663 -size 21761 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerViewWithResult-D-3_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerViewWithResult-D-3_3_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 1eef37d750..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerViewWithResult-D-3_3_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c4c5bc5f2086bd78a364e9996f0f41526d73d69857875594f5de7ea9998333d7 -size 23388 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerViewWithResult-N-3_4_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerViewWithResult-N-3_4_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 95c64c4548..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerViewWithResult-N-3_4_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:42848910a949ec938ccc7a3547b19d1c2c81193f9a93b1db21ca77e4d9ed9663 -size 21761 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentDisclosed-D-1_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentDisclosed-D-1_1_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..9bd8087f0c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentDisclosed-D-1_1_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:15d14bf99af3cd0433870ebd6032d9bf4a45196e2ef1df7184cc55859a704dee +size 49008 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentDisclosed-N-1_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentDisclosed-N-1_2_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..cb9d6334b0 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentDisclosed-N-1_2_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cacb91ffca97f21bbc19b29525813f58fc8017a858aa28ccd4e620b70a8cd9ca +size 46119 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentEnded-D-2_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentEnded-D-2_2_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..f148a727ae --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentEnded-D-2_2_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5a9ccbbc0a208ac398f07f0070a43d0ca5ab67ef322b274b641270a9c21a4a60 +size 48972 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentEnded-N-2_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentEnded-N-2_3_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..df2a5c30b9 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentEnded-N-2_3_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5736eb1d232b841d62f9d96070fc9b2993453652f5d7c448b6a819db9543615e +size 45756 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentUndisclosed-D-0_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentUndisclosed-D-0_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..7319bf6694 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentUndisclosed-D-0_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2264139761f7fe3977ece3c9a7d949ac51223dd209497b7b311987cba3e5a069 +size 47154 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentUndisclosed-N-0_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentUndisclosed-N-0_1_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..3a129abb0b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentUndisclosed-N-0_1_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f3ff35998ce8558b5a7af84e378bee039e36099084b357fffccf329a4983b035 +size 43551 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Toggles_IconToggleButton_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Toggles_IconToggleButton_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..d27488dc29 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Toggles_IconToggleButton_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9f9b51e2b099380112683be07ce85679436fdaa7765cc72c200cbaca1e0b843e +size 13557