Show poll creator view in timeline (#1429)
- Shows edit/end poll buttons when the user is the creator of the poll. - Only the end poll button is wired right now as there is no "edit poll" screen yet.
This commit is contained in:
@@ -30,7 +30,6 @@ import androidx.compose.runtime.setValue
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import im.vector.app.features.analytics.plan.PollEnd
|
||||
import io.element.android.features.messages.impl.actionlist.ActionListEvents
|
||||
import io.element.android.features.messages.impl.actionlist.ActionListPresenter
|
||||
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
|
||||
@@ -39,6 +38,7 @@ import io.element.android.features.messages.impl.messagecomposer.MessageComposer
|
||||
import io.element.android.features.messages.impl.messagecomposer.MessageComposerState
|
||||
import io.element.android.features.messages.impl.timeline.TimelineEvents
|
||||
import io.element.android.features.messages.impl.timeline.TimelinePresenter
|
||||
import io.element.android.features.messages.impl.timeline.TimelineState
|
||||
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionPresenter
|
||||
import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryPresenter
|
||||
import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuPresenter
|
||||
@@ -76,7 +76,6 @@ import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailType
|
||||
import io.element.android.libraries.matrix.ui.room.canRedactAsState
|
||||
import io.element.android.libraries.matrix.ui.room.canSendMessageAsState
|
||||
import io.element.android.libraries.textcomposer.MessageComposerMode
|
||||
import io.element.android.services.analytics.api.AnalyticsService
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
@@ -95,7 +94,6 @@ class MessagesPresenter @AssistedInject constructor(
|
||||
private val messageSummaryFormatter: MessageSummaryFormatter,
|
||||
private val dispatchers: CoroutineDispatchers,
|
||||
private val clipboardHelper: ClipboardHelper,
|
||||
private val analyticsService: AnalyticsService,
|
||||
private val preferencesStore: PreferencesStore,
|
||||
@Assisted private val navigator: MessagesNavigator,
|
||||
) : Presenter<MessagesState> {
|
||||
@@ -155,6 +153,7 @@ class MessagesPresenter @AssistedInject constructor(
|
||||
targetEvent = event.event,
|
||||
composerState = composerState,
|
||||
enableTextFormatting = enableTextFormatting,
|
||||
timelineState = timelineState,
|
||||
)
|
||||
}
|
||||
is MessagesEvents.ToggleReaction -> {
|
||||
@@ -206,6 +205,7 @@ class MessagesPresenter @AssistedInject constructor(
|
||||
targetEvent: TimelineItem.Event,
|
||||
composerState: MessageComposerState,
|
||||
enableTextFormatting: Boolean,
|
||||
timelineState: TimelineState,
|
||||
) = launch {
|
||||
when (action) {
|
||||
TimelineItemAction.Copy -> handleCopyContents(targetEvent)
|
||||
@@ -216,7 +216,7 @@ class MessagesPresenter @AssistedInject constructor(
|
||||
TimelineItemAction.ViewSource -> handleShowDebugInfoAction(targetEvent)
|
||||
TimelineItemAction.Forward -> handleForwardAction(targetEvent)
|
||||
TimelineItemAction.ReportContent -> handleReportAction(targetEvent)
|
||||
TimelineItemAction.EndPoll -> handleEndPollAction(targetEvent)
|
||||
TimelineItemAction.EndPoll -> handleEndPollAction(targetEvent, timelineState)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,7 +266,7 @@ class MessagesPresenter @AssistedInject constructor(
|
||||
targetEvent: TimelineItem.Event,
|
||||
composerState: MessageComposerState,
|
||||
enableTextFormatting: Boolean,
|
||||
) {
|
||||
) {
|
||||
val composerMode = MessageComposerMode.Edit(
|
||||
targetEvent.eventId,
|
||||
(targetEvent.content as? TimelineItemTextBasedContent)?.let {
|
||||
@@ -344,11 +344,11 @@ class MessagesPresenter @AssistedInject constructor(
|
||||
navigator.onReportContentClicked(event.eventId, event.senderId)
|
||||
}
|
||||
|
||||
private suspend fun handleEndPollAction(event: TimelineItem.Event) {
|
||||
event.eventId?.let {
|
||||
room.endPoll(it, "The poll with event id: $it has ended.")
|
||||
analyticsService.capture(PollEnd())
|
||||
}
|
||||
private fun handleEndPollAction(
|
||||
event: TimelineItem.Event,
|
||||
timelineState: TimelineState,
|
||||
) {
|
||||
event.eventId?.let { timelineState.eventSink(TimelineEvents.PollEndClicked(it)) }
|
||||
}
|
||||
|
||||
private suspend fun handleCopyContents(event: TimelineItem.Event) {
|
||||
|
||||
@@ -108,7 +108,7 @@ class ActionListPresenter @Inject constructor(
|
||||
buildList {
|
||||
val isMineOrCanRedact = timelineItem.isMine || userCanRedact
|
||||
|
||||
// TODO Poll: Reply to poll. Ensure to update `fun TimelineItemEventContent.canBeReplied()`
|
||||
// TODO Polls: Reply to poll. Ensure to update `fun TimelineItemEventContent.canBeReplied()`
|
||||
// when touching this
|
||||
// if (timelineItem.isRemote) {
|
||||
// // Can only reply or forward messages already uploaded to the server
|
||||
|
||||
@@ -26,4 +26,8 @@ sealed interface TimelineEvents {
|
||||
val pollStartId: EventId,
|
||||
val answerId: String
|
||||
) : TimelineEvents
|
||||
|
||||
data class PollEndClicked(
|
||||
val pollStartId: EventId,
|
||||
) : TimelineEvents
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import im.vector.app.features.analytics.plan.PollEnd
|
||||
import im.vector.app.features.analytics.plan.PollVote
|
||||
import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactory
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
@@ -98,11 +99,18 @@ class TimelinePresenter @Inject constructor(
|
||||
)
|
||||
analyticsService.capture(PollVote())
|
||||
}
|
||||
is TimelineEvents.PollEndClicked -> appScope.launch {
|
||||
room.endPoll(
|
||||
pollStartId = event.pollStartId,
|
||||
text = "The poll with event id: ${event.pollStartId} has ended."
|
||||
)
|
||||
analyticsService.capture(PollEnd())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(timelineItems.size) {
|
||||
computeHasNewItems(timelineItems, prevMostRecentItemId, hasNewItems)
|
||||
computeHasNewItems(timelineItems, prevMostRecentItemId, hasNewItems)
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
|
||||
@@ -386,6 +386,7 @@ private fun MessageEventBubbleContent(
|
||||
) {
|
||||
TimelineItemEventContentView(
|
||||
content = event.content,
|
||||
isMine = event.isMine,
|
||||
interactionSource = interactionSource,
|
||||
onClick = onMessageClick,
|
||||
onLongClick = onMessageLongClick,
|
||||
|
||||
@@ -67,6 +67,7 @@ fun TimelineItemStateEventRow(
|
||||
) {
|
||||
TimelineItemEventContentView(
|
||||
content = event.content,
|
||||
isMine = event.isMine,
|
||||
interactionSource = interactionSource,
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
|
||||
@@ -36,6 +36,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
|
||||
@Composable
|
||||
fun TimelineItemEventContentView(
|
||||
content: TimelineItemEventContent,
|
||||
isMine: Boolean,
|
||||
interactionSource: MutableInteractionSource,
|
||||
extraPadding: ExtraPadding,
|
||||
onClick: () -> Unit,
|
||||
@@ -95,6 +96,7 @@ fun TimelineItemEventContentView(
|
||||
)
|
||||
is TimelineItemPollContent -> TimelineItemPollView(
|
||||
content = content,
|
||||
isMine = isMine,
|
||||
eventSink = eventSink,
|
||||
modifier = modifier,
|
||||
)
|
||||
|
||||
@@ -31,6 +31,7 @@ import kotlinx.collections.immutable.toImmutableList
|
||||
@Composable
|
||||
fun TimelineItemPollView(
|
||||
content: TimelineItemPollContent,
|
||||
isMine: Boolean,
|
||||
eventSink: (TimelineEvents) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
@@ -38,13 +39,20 @@ fun TimelineItemPollView(
|
||||
eventSink(TimelineEvents.PollAnswerSelected(pollStartId, answerId))
|
||||
}
|
||||
|
||||
fun onPollEnd(pollStartId: EventId) {
|
||||
eventSink(TimelineEvents.PollEndClicked(pollStartId))
|
||||
}
|
||||
|
||||
PollContentView(
|
||||
eventId = content.eventId,
|
||||
question = content.question,
|
||||
answerItems = content.answerItems.toImmutableList(),
|
||||
pollKind = content.pollKind,
|
||||
isPollEnded = content.isEnded,
|
||||
isMine = isMine,
|
||||
onAnswerSelected = ::onAnswerSelected,
|
||||
onPollEdit = {}, // TODO Polls: Wire up this callback once poll edit screen is done.
|
||||
onPollEnd = ::onPollEnd,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
@@ -55,6 +63,18 @@ internal fun TimelineItemPollViewPreview(@PreviewParameter(TimelineItemPollConte
|
||||
ElementPreview {
|
||||
TimelineItemPollView(
|
||||
content = content,
|
||||
isMine = false,
|
||||
eventSink = {},
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun TimelineItemPollCreatorViewPreview(@PreviewParameter(TimelineItemPollContentProvider::class) content: TimelineItemPollContent) =
|
||||
ElementPreview {
|
||||
TimelineItemPollView(
|
||||
content = content,
|
||||
isMine = true,
|
||||
eventSink = {},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -644,7 +644,6 @@ class MessagesPresenterTest {
|
||||
messageSummaryFormatter = FakeMessageSummaryFormatter(),
|
||||
navigator = navigator,
|
||||
clipboardHelper = clipboardHelper,
|
||||
analyticsService = analyticsService,
|
||||
preferencesStore = preferencesStore,
|
||||
dispatchers = coroutineDispatchers,
|
||||
)
|
||||
|
||||
@@ -20,7 +20,9 @@ import app.cash.molecule.RecompositionMode
|
||||
import app.cash.molecule.moleculeFlow
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import im.vector.app.features.analytics.plan.PollEnd
|
||||
import im.vector.app.features.analytics.plan.PollVote
|
||||
import io.element.android.features.messages.fixtures.aMessageEvent
|
||||
import io.element.android.features.messages.fixtures.aTimelineItemsFactory
|
||||
import io.element.android.features.messages.impl.timeline.TimelineEvents
|
||||
import io.element.android.features.messages.impl.timeline.TimelinePresenter
|
||||
@@ -42,6 +44,7 @@ import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
import io.element.android.tests.testutils.awaitWithLatch
|
||||
import io.element.android.tests.testutils.testCoroutineDispatchers
|
||||
import io.element.android.tests.testutils.waitForPredicate
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.runTest
|
||||
@@ -280,6 +283,29 @@ class TimelinePresenterTest {
|
||||
assertThat(analyticsService.capturedEvents.last()).isEqualTo(PollVote())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - PollEndClicked event calls into rust room api and analytics`() = runTest {
|
||||
val room = FakeMatrixRoom()
|
||||
val analyticsService = FakeAnalyticsService()
|
||||
val presenter = createTimelinePresenter(
|
||||
room = room,
|
||||
analyticsService = analyticsService,
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(TimelineEvents.PollEndClicked(aMessageEvent().eventId!!))
|
||||
waitForPredicate { room.endPollInvocations.size == 1 }
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
assertThat(room.endPollInvocations.size).isEqualTo(1)
|
||||
assertThat(room.endPollInvocations.first().pollStartId).isEqualTo(AN_EVENT_ID)
|
||||
assertThat(room.endPollInvocations.first().text).isEqualTo("The poll with event id: \$anEventId has ended.")
|
||||
assertThat(analyticsService.capturedEvents.size).isEqualTo(1)
|
||||
assertThat(analyticsService.capturedEvents.last()).isEqualTo(PollEnd())
|
||||
}
|
||||
}
|
||||
|
||||
private fun TestScope.createTimelinePresenter(
|
||||
timeline: MatrixTimeline = FakeMatrixTimeline(),
|
||||
timelineItemsFactory: TimelineItemsFactory = aTimelineItemsFactory()
|
||||
|
||||
@@ -19,13 +19,17 @@ package io.element.android.features.poll.api
|
||||
import io.element.android.libraries.matrix.api.poll.PollAnswer
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
fun aPollAnswerItemList(isEnded: Boolean = false, isDisclosed: Boolean = true) = persistentListOf(
|
||||
fun aPollAnswerItemList(
|
||||
hasVotes: Boolean = true,
|
||||
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,
|
||||
votesCount = if (hasVotes) 5 else 0,
|
||||
percentage = 0.5f
|
||||
),
|
||||
aPollAnswerItem(
|
||||
@@ -42,7 +46,7 @@ fun aPollAnswerItemList(isEnded: Boolean = false, isDisclosed: Boolean = true) =
|
||||
isEnabled = !isEnded,
|
||||
isWinner = false,
|
||||
isSelected = true,
|
||||
votesCount = 1,
|
||||
votesCount = if (hasVotes) 1 else 0,
|
||||
percentage = 0.1f
|
||||
),
|
||||
aPollAnswerItem(isDisclosed = isDisclosed, isEnabled = !isEnded),
|
||||
|
||||
@@ -26,14 +26,19 @@ import androidx.compose.foundation.layout.size
|
||||
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.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.Icon
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.designsystem.utils.CommonDrawables
|
||||
@@ -51,13 +56,37 @@ fun PollContentView(
|
||||
answerItems: ImmutableList<PollAnswerItem>,
|
||||
pollKind: PollKind,
|
||||
isPollEnded: Boolean,
|
||||
isMine: Boolean,
|
||||
onAnswerSelected: (pollStartId: EventId, answerId: String) -> Unit,
|
||||
onPollEdit: (pollStartId: EventId) -> Unit,
|
||||
onPollEnd: (pollStartId: EventId) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val votesCount = remember(answerItems) { answerItems.sumOf { it.votesCount } }
|
||||
|
||||
fun onAnswerSelected(pollAnswer: PollAnswer) {
|
||||
eventId?.let { onAnswerSelected(it, pollAnswer.id) }
|
||||
}
|
||||
|
||||
fun onPollEdit() {
|
||||
eventId?.let { onPollEdit(it) }
|
||||
}
|
||||
|
||||
fun onPollEnd() {
|
||||
eventId?.let { onPollEnd(it) }
|
||||
}
|
||||
|
||||
var showConfirmation: Boolean by remember { mutableStateOf(false) }
|
||||
|
||||
if (showConfirmation) ConfirmationDialog(
|
||||
content = stringResource(id = CommonStrings.common_poll_end_confirmation),
|
||||
onSubmitClicked = {
|
||||
onPollEnd()
|
||||
showConfirmation = false
|
||||
},
|
||||
onDismiss = { showConfirmation = false },
|
||||
)
|
||||
|
||||
Column(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
@@ -67,11 +96,20 @@ fun PollContentView(
|
||||
PollAnswers(answerItems = answerItems, onAnswerSelected = ::onAnswerSelected)
|
||||
|
||||
if (isPollEnded || pollKind == PollKind.Disclosed) {
|
||||
val votesCount = remember(answerItems) { answerItems.sumOf { it.votesCount } }
|
||||
DisclosedPollBottomNotice(votesCount = votesCount)
|
||||
} else {
|
||||
UndisclosedPollBottomNotice()
|
||||
}
|
||||
|
||||
if (isMine) {
|
||||
CreatorView(
|
||||
votesCount = 1, // TODO Polls: set to `votesCount` when edit poll screen is implemented.
|
||||
isPollEnded = isPollEnded,
|
||||
onPollEdit = ::onPollEdit,
|
||||
onPollEnd = { showConfirmation = true },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,6 +195,31 @@ private fun ColumnScope.UndisclosedPollBottomNotice(
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CreatorView(
|
||||
@Suppress("SameParameterValue") votesCount: Int, // TODO Polls: remove @Suppress when edit poll screen is implemented.
|
||||
isPollEnded: Boolean,
|
||||
onPollEdit: () -> Unit,
|
||||
onPollEnd: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
if (!isPollEnded) {
|
||||
if (votesCount == 0) {
|
||||
Button(
|
||||
text = stringResource(id = CommonStrings.action_edit_poll),
|
||||
onClick = onPollEdit,
|
||||
modifier = modifier,
|
||||
)
|
||||
} else {
|
||||
Button(
|
||||
text = stringResource(id = CommonStrings.action_end_poll),
|
||||
onClick = onPollEnd,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun PollContentUndisclosedPreview() = ElementPreview {
|
||||
@@ -166,7 +229,10 @@ internal fun PollContentUndisclosedPreview() = ElementPreview {
|
||||
answerItems = aPollAnswerItemList(isDisclosed = false),
|
||||
pollKind = PollKind.Undisclosed,
|
||||
isPollEnded = false,
|
||||
isMine = false,
|
||||
onAnswerSelected = { _, _ -> },
|
||||
onPollEdit = {},
|
||||
onPollEnd = {},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -179,7 +245,10 @@ internal fun PollContentDisclosedPreview() = ElementPreview {
|
||||
answerItems = aPollAnswerItemList(),
|
||||
pollKind = PollKind.Disclosed,
|
||||
isPollEnded = false,
|
||||
isMine = false,
|
||||
onAnswerSelected = { _, _ -> },
|
||||
onPollEdit = {},
|
||||
onPollEnd = {},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -192,6 +261,57 @@ internal fun PollContentEndedPreview() = ElementPreview {
|
||||
answerItems = aPollAnswerItemList(isEnded = true),
|
||||
pollKind = PollKind.Disclosed,
|
||||
isPollEnded = true,
|
||||
isMine = false,
|
||||
onAnswerSelected = { _, _ -> },
|
||||
onPollEdit = {},
|
||||
onPollEnd = {},
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun PollContentCreatorNoVotesPreview() = 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,
|
||||
isMine = true,
|
||||
onAnswerSelected = { _, _ -> },
|
||||
onPollEdit = {},
|
||||
onPollEnd = {},
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun PollContentCreatorPreview() = 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,
|
||||
isMine = true,
|
||||
onAnswerSelected = { _, _ -> },
|
||||
onPollEdit = {},
|
||||
onPollEnd = {},
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun PollContentCreatorEndedPreview() = 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,
|
||||
isMine = true,
|
||||
onAnswerSelected = { _, _ -> },
|
||||
onPollEdit = {},
|
||||
onPollEnd = {},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@
|
||||
<string name="action_take_photo">"Take photo"</string>
|
||||
<string name="action_view_source">"View Source"</string>
|
||||
<string name="action_yes">"Yes"</string>
|
||||
<string name="action_edit_poll">"Edit poll"</string>
|
||||
<string name="common_about">"About"</string>
|
||||
<string name="common_acceptable_use_policy">"Acceptable use policy"</string>
|
||||
<string name="common_advanced_settings">"Advanced settings"</string>
|
||||
@@ -149,6 +150,7 @@
|
||||
<string name="common_verification_complete">"Verification complete"</string>
|
||||
<string name="common_video">"Video"</string>
|
||||
<string name="common_waiting">"Waiting…"</string>
|
||||
<string name="common_poll_end_confirmation">"Are you sure you want to end this poll?"</string>
|
||||
<string name="common_poll_summary">"Poll: %1$s"</string>
|
||||
<string name="dialog_title_confirmation">"Confirmation"</string>
|
||||
<string name="dialog_title_warning">"Warning"</string>
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user