First Commit
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id("io.element.android-compose-library")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "io.element.android.features.poll.api"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(projects.libraries.architecture)
|
||||
implementation(projects.libraries.designsystem)
|
||||
implementation(projects.libraries.uiStrings)
|
||||
implementation(projects.libraries.matrix.api)
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.poll.api.actions
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
|
||||
interface EndPollAction {
|
||||
suspend fun execute(timeline: Timeline, pollStartId: EventId): Result<Unit>
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.poll.api.actions
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
|
||||
interface SendPollResponseAction {
|
||||
suspend fun execute(
|
||||
timeline: Timeline,
|
||||
pollStartId: EventId,
|
||||
answerId: String
|
||||
): Result<Unit>
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.poll.api.create
|
||||
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.bumble.appyx.core.node.Node
|
||||
import io.element.android.libraries.architecture.FeatureEntryPoint
|
||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
|
||||
interface CreatePollEntryPoint : FeatureEntryPoint {
|
||||
data class Params(
|
||||
val timelineMode: Timeline.Mode,
|
||||
val mode: CreatePollMode,
|
||||
)
|
||||
|
||||
fun createNode(
|
||||
parentNode: Node,
|
||||
buildContext: BuildContext,
|
||||
params: Params,
|
||||
): Node
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.poll.api.create
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
|
||||
sealed interface CreatePollMode {
|
||||
data object NewPoll : CreatePollMode
|
||||
data class EditPoll(val eventId: EventId) : CreatePollMode
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.poll.api.history
|
||||
|
||||
import io.element.android.libraries.architecture.SimpleFeatureEntryPoint
|
||||
|
||||
interface PollHistoryEntryPoint : SimpleFeatureEntryPoint
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.poll.api.pollcontent
|
||||
|
||||
import io.element.android.libraries.matrix.api.poll.PollAnswer
|
||||
|
||||
/**
|
||||
* UI model for a [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 showVotes whether the votes for this answer should be displayed.
|
||||
* @property votesCount the number 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 showVotes: Boolean,
|
||||
val votesCount: Int,
|
||||
val percentage: Float,
|
||||
)
|
||||
+204
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.poll.api.pollcontent
|
||||
|
||||
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.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
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.res.stringResource
|
||||
import androidx.compose.ui.semantics.clearAndSetSemantics
|
||||
import androidx.compose.ui.semantics.contentDescription
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
import io.element.android.features.poll.api.R
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||
import io.element.android.libraries.designsystem.theme.components.LinearProgressIndicator
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.designsystem.theme.progressIndicatorTrackColor
|
||||
import io.element.android.libraries.designsystem.toEnabledColor
|
||||
import io.element.android.libraries.designsystem.utils.CommonDrawables
|
||||
import io.element.android.libraries.ui.strings.CommonPlurals
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
@Composable
|
||||
internal fun PollAnswerView(
|
||||
answerItem: PollAnswerItem,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val nbVotesText = pluralStringResource(
|
||||
id = CommonPlurals.common_poll_votes_count,
|
||||
count = answerItem.votesCount,
|
||||
answerItem.votesCount,
|
||||
)
|
||||
val a11yText = buildString {
|
||||
val sentenceDelimiter = stringResource(CommonStrings.common_sentence_delimiter)
|
||||
append(answerItem.answer.text.removeSuffix("."))
|
||||
if (answerItem.showVotes) {
|
||||
append(sentenceDelimiter)
|
||||
append(nbVotesText)
|
||||
if (answerItem.votesCount != 0) {
|
||||
append(sentenceDelimiter)
|
||||
(answerItem.percentage * 100).toInt().let { percent ->
|
||||
append(pluralStringResource(R.plurals.a11y_polls_percent_of_total, percent, percent))
|
||||
}
|
||||
}
|
||||
if (answerItem.isWinner) {
|
||||
append(sentenceDelimiter)
|
||||
append(stringResource(R.string.a11y_polls_winning_answer))
|
||||
}
|
||||
}
|
||||
}
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clearAndSetSemantics {
|
||||
contentDescription = a11yText
|
||||
},
|
||||
) {
|
||||
Icon(
|
||||
imageVector = if (answerItem.isSelected) {
|
||||
CompoundIcons.CheckCircleSolid()
|
||||
} else {
|
||||
CompoundIcons.Circle()
|
||||
},
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.padding(0.5.dp)
|
||||
.size(22.dp),
|
||||
tint = if (answerItem.isEnabled) {
|
||||
if (answerItem.isSelected) {
|
||||
ElementTheme.colors.iconPrimary
|
||||
} else {
|
||||
ElementTheme.colors.iconSecondary
|
||||
}
|
||||
} else {
|
||||
ElementTheme.colors.iconDisabled
|
||||
},
|
||||
)
|
||||
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.showVotes) {
|
||||
Row(
|
||||
modifier = Modifier.align(Alignment.Bottom),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
if (answerItem.isWinner) {
|
||||
Icon(
|
||||
resourceId = CommonDrawables.ic_winner,
|
||||
contentDescription = null,
|
||||
tint = ElementTheme.colors.iconAccentTertiary,
|
||||
)
|
||||
Spacer(modifier = Modifier.width(2.dp))
|
||||
Text(
|
||||
text = nbVotesText,
|
||||
style = ElementTheme.typography.fontBodySmMedium,
|
||||
color = ElementTheme.colors.textPrimary,
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
text = nbVotesText,
|
||||
style = ElementTheme.typography.fontBodySmRegular,
|
||||
color = 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.showVotes -> answerItem.percentage
|
||||
answerItem.isSelected -> 1f
|
||||
else -> 0f
|
||||
}
|
||||
},
|
||||
trackColor = ElementTheme.colors.progressIndicatorTrackColor,
|
||||
strokeCap = StrokeCap.Round,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun PollAnswerViewDisclosedNotSelectedPreview() = ElementPreview {
|
||||
PollAnswerView(
|
||||
answerItem = aPollAnswerItem(showVotes = true, isSelected = false),
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun PollAnswerViewDisclosedSelectedPreview() = ElementPreview {
|
||||
PollAnswerView(
|
||||
answerItem = aPollAnswerItem(showVotes = true, isSelected = true),
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun PollAnswerViewUndisclosedNotSelectedPreview() = ElementPreview {
|
||||
PollAnswerView(
|
||||
answerItem = aPollAnswerItem(showVotes = false, isSelected = false),
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun PollAnswerViewUndisclosedSelectedPreview() = ElementPreview {
|
||||
PollAnswerView(
|
||||
answerItem = aPollAnswerItem(showVotes = false, isSelected = true),
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun PollAnswerViewEndedWinnerNotSelectedPreview() = ElementPreview {
|
||||
PollAnswerView(
|
||||
answerItem = aPollAnswerItem(showVotes = true, isSelected = false, isEnabled = false, isWinner = true),
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun PollAnswerViewEndedWinnerSelectedPreview() = ElementPreview {
|
||||
PollAnswerView(
|
||||
answerItem = aPollAnswerItem(showVotes = true, isSelected = true, isEnabled = false, isWinner = true),
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun PollAnswerViewEndedSelectedPreview() = ElementPreview {
|
||||
PollAnswerView(
|
||||
answerItem = aPollAnswerItem(showVotes = true, isSelected = true, isEnabled = false, isWinner = false),
|
||||
)
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.poll.api.pollcontent
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.poll.PollKind
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
/**
|
||||
* UI model for a PollContent.
|
||||
* @property eventId the event id of the poll.
|
||||
* @property question the poll question.
|
||||
* @property answerItems the list of answers.
|
||||
* @property pollKind the kind of poll.
|
||||
* @property isPollEditable whether the poll is editable.
|
||||
* @property isPollEnded whether the poll is ended.
|
||||
* @property isMine whether the poll has been created by me.
|
||||
*/
|
||||
data class PollContentState(
|
||||
val eventId: EventId?,
|
||||
val question: String,
|
||||
val answerItems: ImmutableList<PollAnswerItem>,
|
||||
val pollKind: PollKind,
|
||||
val isPollEditable: Boolean,
|
||||
val isPollEnded: Boolean,
|
||||
val isMine: Boolean,
|
||||
)
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.poll.api.pollcontent
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.PollContent
|
||||
|
||||
interface PollContentStateFactory {
|
||||
suspend fun create(eventTimelineItem: EventTimelineItem, content: PollContent): PollContentState {
|
||||
return create(
|
||||
eventId = eventTimelineItem.eventId,
|
||||
isEditable = eventTimelineItem.isEditable,
|
||||
isOwn = eventTimelineItem.isOwn,
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
suspend fun create(eventId: EventId?, isEditable: Boolean, isOwn: Boolean, content: PollContent): PollContentState
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.poll.api.pollcontent
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.poll.PollAnswer
|
||||
import io.element.android.libraries.matrix.api.poll.PollKind
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
fun aPollQuestion() = "What type of food should we have at the party?"
|
||||
|
||||
fun aPollAnswerItemList(
|
||||
hasVotes: Boolean = true,
|
||||
isEnded: Boolean = false,
|
||||
showVotes: Boolean = true,
|
||||
) = persistentListOf(
|
||||
aPollAnswerItem(
|
||||
answer = PollAnswer("option_1", "Italian \uD83C\uDDEE\uD83C\uDDF9"),
|
||||
showVotes = showVotes,
|
||||
isEnabled = !isEnded,
|
||||
isWinner = isEnded,
|
||||
votesCount = if (hasVotes) 5 else 0,
|
||||
percentage = if (hasVotes) 0.5f else 0f
|
||||
),
|
||||
aPollAnswerItem(
|
||||
answer = PollAnswer("option_2", "Chinese \uD83C\uDDE8\uD83C\uDDF3"),
|
||||
showVotes = showVotes,
|
||||
isEnabled = !isEnded,
|
||||
isWinner = false,
|
||||
votesCount = 0,
|
||||
percentage = 0f
|
||||
),
|
||||
aPollAnswerItem(
|
||||
answer = PollAnswer("option_3", "Brazilian \uD83C\uDDE7\uD83C\uDDF7"),
|
||||
showVotes = showVotes,
|
||||
isEnabled = !isEnded,
|
||||
isWinner = false,
|
||||
isSelected = true,
|
||||
votesCount = if (hasVotes) 1 else 0,
|
||||
percentage = if (hasVotes) 0.1f else 0f
|
||||
),
|
||||
aPollAnswerItem(
|
||||
showVotes = showVotes,
|
||||
isEnabled = !isEnded,
|
||||
votesCount = if (hasVotes) 4 else 0,
|
||||
percentage = if (hasVotes) 0.4f else 0f,
|
||||
),
|
||||
)
|
||||
|
||||
fun aPollAnswerItem(
|
||||
answer: PollAnswer = PollAnswer(
|
||||
"option_4",
|
||||
"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,
|
||||
showVotes: Boolean = true,
|
||||
votesCount: Int = 4,
|
||||
percentage: Float = 0.4f,
|
||||
) = PollAnswerItem(
|
||||
answer = answer,
|
||||
isSelected = isSelected,
|
||||
isEnabled = isEnabled,
|
||||
isWinner = isWinner,
|
||||
showVotes = showVotes,
|
||||
votesCount = votesCount,
|
||||
percentage = percentage
|
||||
)
|
||||
|
||||
fun aPollContentState(
|
||||
eventId: EventId? = null,
|
||||
isMine: Boolean = false,
|
||||
isEnded: Boolean = false,
|
||||
showVotes: Boolean = true,
|
||||
isPollEditable: Boolean = true,
|
||||
hasVotes: Boolean = true,
|
||||
question: String = aPollQuestion(),
|
||||
pollKind: PollKind = PollKind.Disclosed,
|
||||
answerItems: ImmutableList<PollAnswerItem> = aPollAnswerItemList(
|
||||
isEnded = isEnded,
|
||||
showVotes = showVotes,
|
||||
hasVotes = hasVotes
|
||||
),
|
||||
) = PollContentState(
|
||||
eventId = eventId,
|
||||
question = question,
|
||||
answerItems = answerItems,
|
||||
pollKind = pollKind,
|
||||
isPollEditable = isMine && !isEnded && isPollEditable,
|
||||
isPollEnded = isEnded,
|
||||
isMine = isMine,
|
||||
)
|
||||
+302
@@ -0,0 +1,302 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.poll.api.pollcontent
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.selection.selectable
|
||||
import androidx.compose.foundation.selection.selectableGroup
|
||||
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.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.Button
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.poll.PollAnswer
|
||||
import io.element.android.libraries.matrix.api.poll.PollKind
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Composable
|
||||
fun PollContentView(
|
||||
state: PollContentState,
|
||||
onSelectAnswer: (pollStartId: EventId, answerId: String) -> Unit,
|
||||
onEditPoll: (pollStartId: EventId) -> Unit,
|
||||
onEndPoll: (pollStartId: EventId) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
PollContentView(
|
||||
eventId = state.eventId,
|
||||
question = state.question,
|
||||
answerItems = state.answerItems,
|
||||
pollKind = state.pollKind,
|
||||
isPollEditable = state.isPollEditable,
|
||||
isPollEnded = state.isPollEnded,
|
||||
isMine = state.isMine,
|
||||
onEditPoll = onEditPoll,
|
||||
onSelectAnswer = onSelectAnswer,
|
||||
onEndPoll = onEndPoll,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PollContentView(
|
||||
eventId: EventId?,
|
||||
question: String,
|
||||
answerItems: ImmutableList<PollAnswerItem>,
|
||||
pollKind: PollKind,
|
||||
isPollEditable: Boolean,
|
||||
isPollEnded: Boolean,
|
||||
isMine: Boolean,
|
||||
onSelectAnswer: (pollStartId: EventId, answerId: String) -> Unit,
|
||||
onEditPoll: (pollStartId: EventId) -> Unit,
|
||||
onEndPoll: (pollStartId: EventId) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val votesCount = remember(answerItems) { answerItems.sumOf { it.votesCount } }
|
||||
|
||||
fun onSelectAnswer(pollAnswer: PollAnswer) {
|
||||
eventId?.let { onSelectAnswer(it, pollAnswer.id) }
|
||||
}
|
||||
|
||||
fun onEditPoll() {
|
||||
eventId?.let { onEditPoll(it) }
|
||||
}
|
||||
|
||||
fun onEndPoll() {
|
||||
eventId?.let { onEndPoll(it) }
|
||||
}
|
||||
|
||||
var showConfirmation: Boolean by remember { mutableStateOf(false) }
|
||||
|
||||
if (showConfirmation) {
|
||||
ConfirmationDialog(
|
||||
content = stringResource(id = CommonStrings.common_poll_end_confirmation),
|
||||
onSubmitClick = {
|
||||
onEndPoll()
|
||||
showConfirmation = false
|
||||
},
|
||||
onDismiss = { showConfirmation = false },
|
||||
)
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
PollTitleView(title = question, isPollEnded = isPollEnded)
|
||||
|
||||
PollAnswers(answerItems = answerItems, onSelectAnswer = ::onSelectAnswer)
|
||||
|
||||
if (isPollEnded || pollKind == PollKind.Disclosed) {
|
||||
DisclosedPollBottomNotice(votesCount = votesCount)
|
||||
} else {
|
||||
UndisclosedPollBottomNotice()
|
||||
}
|
||||
|
||||
if (isMine) {
|
||||
CreatorView(
|
||||
isPollEnded = isPollEnded,
|
||||
isPollEditable = isPollEditable,
|
||||
onEditPoll = ::onEditPoll,
|
||||
onEndPoll = { showConfirmation = true },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PollAnswers(
|
||||
answerItems: ImmutableList<PollAnswerItem>,
|
||||
onSelectAnswer: (PollAnswer) -> Unit,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.selectableGroup(),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
answerItems.forEach {
|
||||
PollAnswerView(
|
||||
answerItem = it,
|
||||
modifier = Modifier
|
||||
.selectable(
|
||||
selected = it.isSelected,
|
||||
enabled = it.isEnabled,
|
||||
onClick = { onSelectAnswer(it.answer) },
|
||||
role = Role.RadioButton,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ColumnScope.DisclosedPollBottomNotice(
|
||||
votesCount: Int,
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.align(Alignment.End),
|
||||
style = ElementTheme.typography.fontBodyXsRegular,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
text = stringResource(CommonStrings.common_poll_total_votes, votesCount),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private 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),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CreatorView(
|
||||
isPollEnded: Boolean,
|
||||
isPollEditable: Boolean,
|
||||
onEditPoll: () -> Unit,
|
||||
onEndPoll: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
when {
|
||||
isPollEditable ->
|
||||
Button(
|
||||
text = stringResource(id = CommonStrings.action_edit_poll),
|
||||
onClick = onEditPoll,
|
||||
modifier = modifier,
|
||||
)
|
||||
!isPollEnded ->
|
||||
Button(
|
||||
text = stringResource(id = CommonStrings.action_end_poll),
|
||||
onClick = onEndPoll,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun PollContentViewUndisclosedPreview() = ElementPreview {
|
||||
PollContentView(
|
||||
eventId = EventId("\$anEventId"),
|
||||
question = "What type of food should we have at the party?",
|
||||
answerItems = aPollAnswerItemList(showVotes = false),
|
||||
pollKind = PollKind.Undisclosed,
|
||||
isPollEnded = false,
|
||||
isPollEditable = false,
|
||||
isMine = false,
|
||||
onSelectAnswer = { _, _ -> },
|
||||
onEditPoll = {},
|
||||
onEndPoll = {},
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun PollContentViewDisclosedPreview() = ElementPreview {
|
||||
PollContentView(
|
||||
eventId = EventId("\$anEventId"),
|
||||
question = "What type of food should we have at the party?",
|
||||
answerItems = aPollAnswerItemList(),
|
||||
pollKind = PollKind.Disclosed,
|
||||
isPollEnded = false,
|
||||
isPollEditable = false,
|
||||
isMine = false,
|
||||
onSelectAnswer = { _, _ -> },
|
||||
onEditPoll = {},
|
||||
onEndPoll = {},
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun PollContentViewEndedPreview() = ElementPreview {
|
||||
PollContentView(
|
||||
eventId = EventId("\$anEventId"),
|
||||
question = "What type of food should we have at the party?",
|
||||
answerItems = aPollAnswerItemList(isEnded = true),
|
||||
pollKind = PollKind.Disclosed,
|
||||
isPollEnded = true,
|
||||
isPollEditable = false,
|
||||
isMine = false,
|
||||
onSelectAnswer = { _, _ -> },
|
||||
onEditPoll = {},
|
||||
onEndPoll = {},
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun PollContentViewCreatorEditablePreview() = ElementPreview {
|
||||
PollContentView(
|
||||
eventId = EventId("\$anEventId"),
|
||||
question = "What type of food should we have at the party?",
|
||||
answerItems = aPollAnswerItemList(hasVotes = false, isEnded = false),
|
||||
pollKind = PollKind.Disclosed,
|
||||
isPollEnded = false,
|
||||
isPollEditable = true,
|
||||
isMine = true,
|
||||
onSelectAnswer = { _, _ -> },
|
||||
onEditPoll = {},
|
||||
onEndPoll = {},
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun PollContentViewCreatorPreview() = ElementPreview {
|
||||
PollContentView(
|
||||
eventId = EventId("\$anEventId"),
|
||||
question = "What type of food should we have at the party?",
|
||||
answerItems = aPollAnswerItemList(isEnded = false),
|
||||
pollKind = PollKind.Disclosed,
|
||||
isPollEnded = false,
|
||||
isPollEditable = false,
|
||||
isMine = true,
|
||||
onSelectAnswer = { _, _ -> },
|
||||
onEditPoll = {},
|
||||
onEndPoll = {},
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun PollContentViewCreatorEndedPreview() = ElementPreview {
|
||||
PollContentView(
|
||||
eventId = EventId("\$anEventId"),
|
||||
question = "What type of food should we have at the party?",
|
||||
answerItems = aPollAnswerItemList(isEnded = true),
|
||||
pollKind = PollKind.Disclosed,
|
||||
isPollEnded = true,
|
||||
isPollEditable = false,
|
||||
isMine = true,
|
||||
onSelectAnswer = { _, _ -> },
|
||||
onEditPoll = {},
|
||||
onEndPoll = {},
|
||||
)
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.poll.api.pollcontent
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
@Composable
|
||||
fun PollTitleView(
|
||||
title: String,
|
||||
isPollEnded: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
if (isPollEnded) {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.PollsEnd(),
|
||||
contentDescription = stringResource(id = CommonStrings.a11y_poll_end),
|
||||
modifier = Modifier.size(22.dp)
|
||||
)
|
||||
} else {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.Polls(),
|
||||
contentDescription = stringResource(id = CommonStrings.a11y_poll),
|
||||
modifier = Modifier.size(22.dp)
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = title,
|
||||
style = ElementTheme.typography.fontBodyLgMedium
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun PollTitleViewPreview() = ElementPreview {
|
||||
PollTitleView(
|
||||
title = "What is your favorite color?",
|
||||
isPollEnded = false
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<plurals name="a11y_polls_percent_of_total">
|
||||
<item quantity="one">"%1$d procento z celkového počtu hlasů"</item>
|
||||
<item quantity="few">"%1$d procenta z celkového počtu hlasů"</item>
|
||||
<item quantity="other">"%1$d procent z celkového počtu hlasů"</item>
|
||||
</plurals>
|
||||
<string name="a11y_polls_will_remove_selection">"Odstraní předchozí výběr"</string>
|
||||
<string name="a11y_polls_winning_answer">"Toto je vítězná odpověď"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<plurals name="a11y_polls_percent_of_total">
|
||||
<item quantity="zero">"%1$d y cant o\'r holl bleidleisiau"</item>
|
||||
<item quantity="one">"%1$d y cant o\'r holl bleidleisiau"</item>
|
||||
<item quantity="two">"%1$d y cant o\'r holl bleidleisiau"</item>
|
||||
<item quantity="few">"%1$d y cant o\'r holl bleidleisiau"</item>
|
||||
<item quantity="many">"%1$d y cant o\'r holl bleidleisiau"</item>
|
||||
<item quantity="other">"%1$d y cant o\'r holl bleidleisiau"</item>
|
||||
</plurals>
|
||||
<string name="a11y_polls_will_remove_selection">"Bydd yn dileu\'r dewis blaenorol"</string>
|
||||
<string name="a11y_polls_winning_answer">"Dyma\'r ateb buddugol"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<plurals name="a11y_polls_percent_of_total">
|
||||
<item quantity="one">"%1$d% af de samlede stemmer"</item>
|
||||
<item quantity="other">"%1$d procent af det samlede antal stemmer"</item>
|
||||
</plurals>
|
||||
<string name="a11y_polls_will_remove_selection">"Fjerner tidligere valg"</string>
|
||||
<string name="a11y_polls_winning_answer">"Dette er det vindende svar"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<plurals name="a11y_polls_percent_of_total">
|
||||
<item quantity="one">"%1$d Prozent aller Stimmen"</item>
|
||||
<item quantity="other">"%1$d Prozent aller Stimmen"</item>
|
||||
</plurals>
|
||||
<string name="a11y_polls_will_remove_selection">"Entfernt die vorherige Auswahl"</string>
|
||||
<string name="a11y_polls_winning_answer">"Das ist die meistgewählte Antwort"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<plurals name="a11y_polls_percent_of_total">
|
||||
<item quantity="one">"%1$d τοις εκατό των συνολικών ψήφων"</item>
|
||||
<item quantity="other">"%1$d τοις εκατό του συνόλου των ψήφων"</item>
|
||||
</plurals>
|
||||
<string name="a11y_polls_winning_answer">"Αυτή είναι η νικητήρια απάντηση"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<plurals name="a11y_polls_percent_of_total">
|
||||
<item quantity="one">"%1$d protsent kõikidest antud häältest"</item>
|
||||
<item quantity="other">"%1$d protsenti kõikidest antud häältest"</item>
|
||||
</plurals>
|
||||
<string name="a11y_polls_will_remove_selection">"See kustutab eelmise valiku"</string>
|
||||
<string name="a11y_polls_winning_answer">"See vastus võitis"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="a11y_polls_will_remove_selection">"Aurreko hautaketa kenduko du"</string>
|
||||
<string name="a11y_polls_winning_answer">"Erantzun hau gailendu da"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="a11y_polls_will_remove_selection">"گزینش پیشین را برخواهد داشت"</string>
|
||||
<string name="a11y_polls_winning_answer">"این پاسخ برنده است"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<plurals name="a11y_polls_percent_of_total">
|
||||
<item quantity="one">"%1$d prosentti kaikista äänistä"</item>
|
||||
<item quantity="other">"%1$d prosenttia kaikista äänistä"</item>
|
||||
</plurals>
|
||||
<string name="a11y_polls_will_remove_selection">"Poistaa edellisen valinnan"</string>
|
||||
<string name="a11y_polls_winning_answer">"Tämä on voittava vastaus"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<plurals name="a11y_polls_percent_of_total">
|
||||
<item quantity="one">"%1$d pour cent du total des votes"</item>
|
||||
<item quantity="other">"%1$d pour cent du total des votes"</item>
|
||||
</plurals>
|
||||
<string name="a11y_polls_will_remove_selection">"Supprimera la sélection précédente"</string>
|
||||
<string name="a11y_polls_winning_answer">"C’est la réponse gagnante"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<plurals name="a11y_polls_percent_of_total">
|
||||
<item quantity="one">"az összes szavazat %1$d százaléka"</item>
|
||||
<item quantity="other">"az összes szavazat %1$d százaléka"</item>
|
||||
</plurals>
|
||||
<string name="a11y_polls_will_remove_selection">"Eltávolítja a korábbi kijelölést"</string>
|
||||
<string name="a11y_polls_winning_answer">"Ez a győztes válasz"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<plurals name="a11y_polls_percent_of_total">
|
||||
<item quantity="other">"%1$d persen dari total suara"</item>
|
||||
</plurals>
|
||||
<string name="a11y_polls_winning_answer">"Ini adalah jawaban yang menang"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<plurals name="a11y_polls_percent_of_total">
|
||||
<item quantity="one">"%1$d percento dei voti totali"</item>
|
||||
<item quantity="other">"%1$d percento dei voti totali"</item>
|
||||
</plurals>
|
||||
<string name="a11y_polls_will_remove_selection">"Rimuoverà la selezione precedente"</string>
|
||||
<string name="a11y_polls_winning_answer">"Questa è la risposta vincente"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<plurals name="a11y_polls_percent_of_total">
|
||||
<item quantity="other">"%1$d 총 투표율"</item>
|
||||
</plurals>
|
||||
<string name="a11y_polls_will_remove_selection">"이전 선택 항목을 제거합니다"</string>
|
||||
<string name="a11y_polls_winning_answer">"이것이 승리의 답입니다"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<plurals name="a11y_polls_percent_of_total">
|
||||
<item quantity="one">"%1$d prosent av totalt antall stemmer"</item>
|
||||
<item quantity="other">"%1$d prosent av totalt antall stemmer"</item>
|
||||
</plurals>
|
||||
<string name="a11y_polls_will_remove_selection">"Vil fjerne forrige valg"</string>
|
||||
<string name="a11y_polls_winning_answer">"Dette er vinnersvaret"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="a11y_polls_will_remove_selection">"Verwijdert de vorige selectie"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<plurals name="a11y_polls_percent_of_total">
|
||||
<item quantity="one">"%1$d procent wszystkich głosów"</item>
|
||||
<item quantity="few">"%1$d procenty wszystkich głosów"</item>
|
||||
<item quantity="many">"%1$d procent wszystkich głosów"</item>
|
||||
</plurals>
|
||||
<string name="a11y_polls_will_remove_selection">"Spowoduje to usunięcie poprzedniego zaznaczenia"</string>
|
||||
<string name="a11y_polls_winning_answer">"Zwycięska odpowiedź"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<plurals name="a11y_polls_percent_of_total">
|
||||
<item quantity="one">"%1$d por cento de todos os votos"</item>
|
||||
<item quantity="other">"%1$d por cento de todos os votos"</item>
|
||||
</plurals>
|
||||
<string name="a11y_polls_will_remove_selection">"Removerá a seleção anterior"</string>
|
||||
<string name="a11y_polls_winning_answer">"Esta é a resposta vencedora"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<plurals name="a11y_polls_percent_of_total">
|
||||
<item quantity="one">"%1$d porcento de todos os votos"</item>
|
||||
<item quantity="other">"%1$d porcento de todos os votos"</item>
|
||||
</plurals>
|
||||
<string name="a11y_polls_will_remove_selection">"Irá remover seleção anterior"</string>
|
||||
<string name="a11y_polls_winning_answer">"Esta é a reposta vencedora"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<plurals name="a11y_polls_percent_of_total">
|
||||
<item quantity="one">"%1$d la suta din totalul voturilor"</item>
|
||||
<item quantity="few">"%1$d la suta din totalul voturilor"</item>
|
||||
<item quantity="other">"%1$d la suta din totalul voturilor"</item>
|
||||
</plurals>
|
||||
<string name="a11y_polls_will_remove_selection">"Va șterge selecția anterioară"</string>
|
||||
<string name="a11y_polls_winning_answer">"Acesta este votul câștigător"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<plurals name="a11y_polls_percent_of_total">
|
||||
<item quantity="one">"%1$d процент от общего числа голосов"</item>
|
||||
<item quantity="few">"%1$d процента от общего числа голосов"</item>
|
||||
<item quantity="many">"%1$d процентов от общего числа голосов"</item>
|
||||
</plurals>
|
||||
<string name="a11y_polls_will_remove_selection">"Удалить предыдущий ответ"</string>
|
||||
<string name="a11y_polls_winning_answer">"Это лучший ответ"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<plurals name="a11y_polls_percent_of_total">
|
||||
<item quantity="one">"%1$d percento z celkového počtu hlasov"</item>
|
||||
<item quantity="few">"%1$d percentá z celkového počtu hlasov"</item>
|
||||
<item quantity="other">"%1$d percent z celkového počtu hlasov"</item>
|
||||
</plurals>
|
||||
<string name="a11y_polls_will_remove_selection">"Odstráni predchádzajúci výber"</string>
|
||||
<string name="a11y_polls_winning_answer">"Toto je víťazná odpoveď"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<plurals name="a11y_polls_percent_of_total">
|
||||
<item quantity="one">"%1$d procent av totala röster"</item>
|
||||
<item quantity="other">"%1$d procent av totala röster"</item>
|
||||
</plurals>
|
||||
<string name="a11y_polls_will_remove_selection">"Kommer att ta bort föregående val"</string>
|
||||
<string name="a11y_polls_winning_answer">"Detta är det vinnande svaret"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<plurals name="a11y_polls_percent_of_total">
|
||||
<item quantity="one">"%1$d відсоток від усіх голосів"</item>
|
||||
<item quantity="few">"%1$d відсотки від усіх голосів"</item>
|
||||
<item quantity="many">"%1$d відсотків від усіх голосів"</item>
|
||||
</plurals>
|
||||
<string name="a11y_polls_will_remove_selection">"Попередній вибір буде прибрано"</string>
|
||||
<string name="a11y_polls_winning_answer">"Ця відповідь перемогла"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<plurals name="a11y_polls_percent_of_total">
|
||||
<item quantity="one">"Jami ovozlarning %1$d foizi"</item>
|
||||
<item quantity="other">"Jami ovozlarning %1$d foizi"</item>
|
||||
</plurals>
|
||||
<string name="a11y_polls_will_remove_selection">"Oldingi tanlov olib tashlanadi"</string>
|
||||
<string name="a11y_polls_winning_answer">"Bu g\'alaba qozongan javob"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<plurals name="a11y_polls_percent_of_total">
|
||||
<item quantity="other">"總票數的百分之 %1$d"</item>
|
||||
</plurals>
|
||||
<string name="a11y_polls_will_remove_selection">"將會移除先前的選擇"</string>
|
||||
<string name="a11y_polls_winning_answer">"這是得票數最高的選項"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<plurals name="a11y_polls_percent_of_total">
|
||||
<item quantity="other">"%1$d 总投票百分比"</item>
|
||||
</plurals>
|
||||
<string name="a11y_polls_will_remove_selection">"将移除之前的选择"</string>
|
||||
<string name="a11y_polls_winning_answer">"这是获胜的答案"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<plurals name="a11y_polls_percent_of_total">
|
||||
<item quantity="one">"%1$d percent of total votes"</item>
|
||||
<item quantity="other">"%1$d percents of total votes"</item>
|
||||
</plurals>
|
||||
<string name="a11y_polls_will_remove_selection">"Will remove previous selection"</string>
|
||||
<string name="a11y_polls_winning_answer">"This is the winning answer"</string>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user