Merge branch 'develop' into yostyle/notifications_global_settings
This commit is contained in:
commit
2c16e8d9e2
22 changed files with 215 additions and 32 deletions
2
.idea/kotlinc.xml
generated
2
.idea/kotlinc.xml
generated
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="KotlinJpsPluginSettings">
|
<component name="KotlinJpsPluginSettings">
|
||||||
<option name="version" value="1.9.0" />
|
<option name="version" value="1.9.10" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
|
|
@ -22,4 +22,8 @@ sealed interface TimelineEvents {
|
||||||
data object LoadMore : TimelineEvents
|
data object LoadMore : TimelineEvents
|
||||||
data class SetHighlightedEvent(val eventId: EventId?) : TimelineEvents
|
data class SetHighlightedEvent(val eventId: EventId?) : TimelineEvents
|
||||||
data class OnScrollFinished(val firstIndex: Int) : TimelineEvents
|
data class OnScrollFinished(val firstIndex: Int) : TimelineEvents
|
||||||
|
data class PollAnswerSelected(
|
||||||
|
val pollStartId: EventId,
|
||||||
|
val answerId: String
|
||||||
|
) : TimelineEvents
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,13 @@ class TimelinePresenter @Inject constructor(
|
||||||
lastReadReceiptId = lastReadReceiptId
|
lastReadReceiptId = lastReadReceiptId
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
is TimelineEvents.PollAnswerSelected -> appScope.launch {
|
||||||
|
room.sendPollResponse(
|
||||||
|
pollStartId = event.pollStartId,
|
||||||
|
answers = listOf(event.answerId),
|
||||||
|
)
|
||||||
|
// TODO Polls: Send poll vote analytic
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,9 @@ fun TimelineView(
|
||||||
// TODO implement this logic once we have support to 'jump to event X' in sliding sync
|
// TODO implement this logic once we have support to 'jump to event X' in sliding sync
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onPollAnswerSelected(pollStartId: EventId, answerId: String) {
|
||||||
|
state.eventSink(TimelineEvents.PollAnswerSelected(pollStartId, answerId))
|
||||||
|
}
|
||||||
|
|
||||||
Box(modifier = modifier) {
|
Box(modifier = modifier) {
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
|
|
@ -125,6 +128,7 @@ fun TimelineView(
|
||||||
onReactionLongClick = onReactionLongClicked,
|
onReactionLongClick = onReactionLongClicked,
|
||||||
onMoreReactionsClick = onMoreReactionsClicked,
|
onMoreReactionsClick = onMoreReactionsClicked,
|
||||||
onTimestampClicked = onTimestampClicked,
|
onTimestampClicked = onTimestampClicked,
|
||||||
|
onPollAnswerSelected = ::onPollAnswerSelected,
|
||||||
onSwipeToReply = onSwipeToReply,
|
onSwipeToReply = onSwipeToReply,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -162,6 +166,7 @@ fun TimelineItemRow(
|
||||||
onMoreReactionsClick: (TimelineItem.Event) -> Unit,
|
onMoreReactionsClick: (TimelineItem.Event) -> Unit,
|
||||||
onTimestampClicked: (TimelineItem.Event) -> Unit,
|
onTimestampClicked: (TimelineItem.Event) -> Unit,
|
||||||
onSwipeToReply: (TimelineItem.Event) -> Unit,
|
onSwipeToReply: (TimelineItem.Event) -> Unit,
|
||||||
|
onPollAnswerSelected: (pollStartId: EventId, answerId: String) -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
when (timelineItem) {
|
when (timelineItem) {
|
||||||
|
|
@ -194,6 +199,7 @@ fun TimelineItemRow(
|
||||||
onMoreReactionsClick = onMoreReactionsClick,
|
onMoreReactionsClick = onMoreReactionsClick,
|
||||||
onTimestampClicked = onTimestampClicked,
|
onTimestampClicked = onTimestampClicked,
|
||||||
onSwipeToReply = { onSwipeToReply(timelineItem) },
|
onSwipeToReply = { onSwipeToReply(timelineItem) },
|
||||||
|
onPollAnswerSelected = onPollAnswerSelected,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -231,6 +237,7 @@ fun TimelineItemRow(
|
||||||
onReactionClick = onReactionClick,
|
onReactionClick = onReactionClick,
|
||||||
onReactionLongClick = onReactionLongClick,
|
onReactionLongClick = onReactionLongClick,
|
||||||
onMoreReactionsClick = onMoreReactionsClick,
|
onMoreReactionsClick = onMoreReactionsClick,
|
||||||
|
onPollAnswerSelected = onPollAnswerSelected,
|
||||||
onSwipeToReply = {},
|
onSwipeToReply = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -118,6 +118,7 @@ fun TimelineItemEventRow(
|
||||||
onReactionLongClick: (emoji: String, eventId: TimelineItem.Event) -> Unit,
|
onReactionLongClick: (emoji: String, eventId: TimelineItem.Event) -> Unit,
|
||||||
onMoreReactionsClick: (eventId: TimelineItem.Event) -> Unit,
|
onMoreReactionsClick: (eventId: TimelineItem.Event) -> Unit,
|
||||||
onSwipeToReply: () -> Unit,
|
onSwipeToReply: () -> Unit,
|
||||||
|
onPollAnswerSelected: (pollStartId: EventId, answerId: String) -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
|
@ -175,6 +176,7 @@ fun TimelineItemEventRow(
|
||||||
onReactionClicked = { emoji -> onReactionClick(emoji, event) },
|
onReactionClicked = { emoji -> onReactionClick(emoji, event) },
|
||||||
onReactionLongClicked = { emoji -> onReactionLongClick(emoji, event) },
|
onReactionLongClicked = { emoji -> onReactionLongClick(emoji, event) },
|
||||||
onMoreReactionsClicked = { onMoreReactionsClick(event) },
|
onMoreReactionsClicked = { onMoreReactionsClick(event) },
|
||||||
|
onPollAnswerSelected = onPollAnswerSelected,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -191,6 +193,7 @@ fun TimelineItemEventRow(
|
||||||
onReactionClicked = { emoji -> onReactionClick(emoji, event) },
|
onReactionClicked = { emoji -> onReactionClick(emoji, event) },
|
||||||
onReactionLongClicked = { emoji -> onReactionLongClick(emoji, event) },
|
onReactionLongClicked = { emoji -> onReactionLongClick(emoji, event) },
|
||||||
onMoreReactionsClicked = { onMoreReactionsClick(event) },
|
onMoreReactionsClicked = { onMoreReactionsClick(event) },
|
||||||
|
onPollAnswerSelected = onPollAnswerSelected,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -232,6 +235,7 @@ private fun TimelineItemEventRowContent(
|
||||||
onReactionClicked: (emoji: String) -> Unit,
|
onReactionClicked: (emoji: String) -> Unit,
|
||||||
onReactionLongClicked: (emoji: String) -> Unit,
|
onReactionLongClicked: (emoji: String) -> Unit,
|
||||||
onMoreReactionsClicked: (event: TimelineItem.Event) -> Unit,
|
onMoreReactionsClicked: (event: TimelineItem.Event) -> Unit,
|
||||||
|
onPollAnswerSelected: (pollStartId: EventId, answerId: String) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
fun ConstrainScope.linkStartOrEnd(event: TimelineItem.Event) = if (event.isMine) {
|
fun ConstrainScope.linkStartOrEnd(event: TimelineItem.Event) = if (event.isMine) {
|
||||||
|
|
@ -289,7 +293,8 @@ private fun TimelineItemEventRowContent(
|
||||||
inReplyToClick = inReplyToClicked,
|
inReplyToClick = inReplyToClicked,
|
||||||
onTimestampClicked = {
|
onTimestampClicked = {
|
||||||
onTimestampClicked(event)
|
onTimestampClicked(event)
|
||||||
}
|
},
|
||||||
|
onPollAnswerSelected = onPollAnswerSelected,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -360,6 +365,7 @@ private fun MessageEventBubbleContent(
|
||||||
onMessageLongClick: () -> Unit,
|
onMessageLongClick: () -> Unit,
|
||||||
inReplyToClick: () -> Unit,
|
inReplyToClick: () -> Unit,
|
||||||
onTimestampClicked: () -> Unit,
|
onTimestampClicked: () -> Unit,
|
||||||
|
onPollAnswerSelected: (pollStartId: EventId, answerId: String) -> Unit,
|
||||||
@SuppressLint("ModifierParameter") bubbleModifier: Modifier = Modifier, // need to rename this modifier to distinguish it from the following ones
|
@SuppressLint("ModifierParameter") bubbleModifier: Modifier = Modifier, // need to rename this modifier to distinguish it from the following ones
|
||||||
) {
|
) {
|
||||||
val timestampPosition = when (event.content) {
|
val timestampPosition = when (event.content) {
|
||||||
|
|
@ -385,6 +391,7 @@ private fun MessageEventBubbleContent(
|
||||||
onClick = onMessageClick,
|
onClick = onMessageClick,
|
||||||
onLongClick = onMessageLongClick,
|
onLongClick = onMessageLongClick,
|
||||||
extraPadding = event.toExtraPadding(),
|
extraPadding = event.toExtraPadding(),
|
||||||
|
onPollAnswerSelected = onPollAnswerSelected,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -607,6 +614,7 @@ private fun ContentToPreview() {
|
||||||
onMoreReactionsClick = {},
|
onMoreReactionsClick = {},
|
||||||
onTimestampClicked = {},
|
onTimestampClicked = {},
|
||||||
onSwipeToReply = {},
|
onSwipeToReply = {},
|
||||||
|
onPollAnswerSelected = { _, _ -> },
|
||||||
)
|
)
|
||||||
TimelineItemEventRow(
|
TimelineItemEventRow(
|
||||||
event = aTimelineItemEvent(
|
event = aTimelineItemEvent(
|
||||||
|
|
@ -627,6 +635,7 @@ private fun ContentToPreview() {
|
||||||
onMoreReactionsClick = {},
|
onMoreReactionsClick = {},
|
||||||
onTimestampClicked = {},
|
onTimestampClicked = {},
|
||||||
onSwipeToReply = {},
|
onSwipeToReply = {},
|
||||||
|
onPollAnswerSelected = { _, _ -> },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -674,6 +683,7 @@ private fun ContentToPreviewWithReply() {
|
||||||
onMoreReactionsClick = {},
|
onMoreReactionsClick = {},
|
||||||
onTimestampClicked = {},
|
onTimestampClicked = {},
|
||||||
onSwipeToReply = {},
|
onSwipeToReply = {},
|
||||||
|
onPollAnswerSelected = { _, _ -> },
|
||||||
)
|
)
|
||||||
TimelineItemEventRow(
|
TimelineItemEventRow(
|
||||||
event = aTimelineItemEvent(
|
event = aTimelineItemEvent(
|
||||||
|
|
@ -695,6 +705,7 @@ private fun ContentToPreviewWithReply() {
|
||||||
onMoreReactionsClick = {},
|
onMoreReactionsClick = {},
|
||||||
onTimestampClicked = {},
|
onTimestampClicked = {},
|
||||||
onSwipeToReply = {},
|
onSwipeToReply = {},
|
||||||
|
onPollAnswerSelected = { _, _ -> },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -752,6 +763,7 @@ private fun ContentTimestampToPreview(event: TimelineItem.Event) {
|
||||||
onMoreReactionsClick = {},
|
onMoreReactionsClick = {},
|
||||||
onTimestampClicked = {},
|
onTimestampClicked = {},
|
||||||
onSwipeToReply = {},
|
onSwipeToReply = {},
|
||||||
|
onPollAnswerSelected = { _, _ -> },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -792,6 +804,7 @@ private fun ContentWithManyReactionsToPreview() {
|
||||||
onMoreReactionsClick = {},
|
onMoreReactionsClick = {},
|
||||||
onSwipeToReply = {},
|
onSwipeToReply = {},
|
||||||
onTimestampClicked = {},
|
onTimestampClicked = {},
|
||||||
|
onPollAnswerSelected = { _, _ -> },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -816,6 +829,7 @@ internal fun TimelineItemEventRowLongSenderNamePreview() = ElementPreviewLight {
|
||||||
onMoreReactionsClick = {},
|
onMoreReactionsClick = {},
|
||||||
onSwipeToReply = {},
|
onSwipeToReply = {},
|
||||||
onTimestampClicked = {},
|
onTimestampClicked = {},
|
||||||
|
onPollAnswerSelected = { _, _ -> },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -836,5 +850,6 @@ internal fun TimelineItemEventTimestampBelowPreview() = ElementPreviewLight {
|
||||||
onMoreReactionsClick = {},
|
onMoreReactionsClick = {},
|
||||||
onSwipeToReply = {},
|
onSwipeToReply = {},
|
||||||
onTimestampClicked = {},
|
onTimestampClicked = {},
|
||||||
|
onPollAnswerSelected = { _, _ -> },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@ fun TimelineItemStateEventRow(
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
onLongClick = onLongClick,
|
onLongClick = onLongClick,
|
||||||
extraPadding = noExtraPadding,
|
extraPadding = noExtraPadding,
|
||||||
|
onPollAnswerSelected = { _, _ -> error("Polls are not supported in state events") },
|
||||||
modifier = Modifier.defaultTimelineContentPadding()
|
modifier = Modifier.defaultTimelineContentPadding()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
|
||||||
|
import io.element.android.libraries.matrix.api.core.EventId
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TimelineItemEventContentView(
|
fun TimelineItemEventContentView(
|
||||||
|
|
@ -39,6 +40,7 @@ fun TimelineItemEventContentView(
|
||||||
extraPadding: ExtraPadding,
|
extraPadding: ExtraPadding,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
onLongClick: () -> Unit,
|
onLongClick: () -> Unit,
|
||||||
|
onPollAnswerSelected: (pollStartId: EventId, answerId: String) -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
when (content) {
|
when (content) {
|
||||||
|
|
@ -93,7 +95,7 @@ fun TimelineItemEventContentView(
|
||||||
)
|
)
|
||||||
is TimelineItemPollContent -> TimelineItemPollView(
|
is TimelineItemPollContent -> TimelineItemPollView(
|
||||||
content = content,
|
content = content,
|
||||||
onAnswerSelected = {},
|
onAnswerSelected = onPollAnswerSelected,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,16 +24,17 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
|
||||||
import io.element.android.features.poll.api.PollContentView
|
import io.element.android.features.poll.api.PollContentView
|
||||||
import io.element.android.libraries.designsystem.preview.DayNightPreviews
|
import io.element.android.libraries.designsystem.preview.DayNightPreviews
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||||
import io.element.android.libraries.matrix.api.poll.PollAnswer
|
import io.element.android.libraries.matrix.api.core.EventId
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TimelineItemPollView(
|
fun TimelineItemPollView(
|
||||||
content: TimelineItemPollContent,
|
content: TimelineItemPollContent,
|
||||||
onAnswerSelected: (PollAnswer) -> Unit,
|
onAnswerSelected: (pollStartId: EventId, answerId: String) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
PollContentView(
|
PollContentView(
|
||||||
|
eventId = content.eventId,
|
||||||
question = content.question,
|
question = content.question,
|
||||||
answerItems = content.answerItems.toImmutableList(),
|
answerItems = content.answerItems.toImmutableList(),
|
||||||
pollKind = content.pollKind,
|
pollKind = content.pollKind,
|
||||||
|
|
@ -49,6 +50,6 @@ internal fun TimelineItemPollViewPreview(@PreviewParameter(TimelineItemPollConte
|
||||||
ElementPreview {
|
ElementPreview {
|
||||||
TimelineItemPollView(
|
TimelineItemPollView(
|
||||||
content = content,
|
content = content,
|
||||||
onAnswerSelected = {},
|
onAnswerSelected = { _, _ -> },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ class TimelineItemContentFactory @Inject constructor(
|
||||||
is RoomMembershipContent -> roomMembershipFactory.create(eventTimelineItem)
|
is RoomMembershipContent -> roomMembershipFactory.create(eventTimelineItem)
|
||||||
is StateContent -> stateFactory.create(eventTimelineItem)
|
is StateContent -> stateFactory.create(eventTimelineItem)
|
||||||
is StickerContent -> stickerFactory.create(itemContent)
|
is StickerContent -> stickerFactory.create(itemContent)
|
||||||
is PollContent -> pollFactory.create(itemContent)
|
is PollContent -> pollFactory.create(itemContent, eventTimelineItem.eventId)
|
||||||
is UnableToDecryptContent -> utdFactory.create(itemContent)
|
is UnableToDecryptContent -> utdFactory.create(itemContent)
|
||||||
is UnknownContent -> TimelineItemUnknownContent
|
is UnknownContent -> TimelineItemUnknownContent
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +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.FeatureFlagService
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
|
import io.element.android.libraries.matrix.api.core.EventId
|
||||||
import io.element.android.libraries.matrix.api.poll.isDisclosed
|
import io.element.android.libraries.matrix.api.poll.isDisclosed
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.PollContent
|
import io.element.android.libraries.matrix.api.timeline.item.event.PollContent
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
@ -32,7 +33,10 @@ class TimelineItemContentPollFactory @Inject constructor(
|
||||||
private val featureFlagService: FeatureFlagService,
|
private val featureFlagService: FeatureFlagService,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend fun create(content: PollContent): TimelineItemEventContent {
|
suspend fun create(
|
||||||
|
content: PollContent,
|
||||||
|
eventId: EventId?
|
||||||
|
): TimelineItemEventContent {
|
||||||
if (!featureFlagService.isFeatureEnabled(FeatureFlags.Polls)) return TimelineItemUnknownContent
|
if (!featureFlagService.isFeatureEnabled(FeatureFlags.Polls)) return TimelineItemUnknownContent
|
||||||
|
|
||||||
// Todo Move this computation to the matrix rust sdk
|
// Todo Move this computation to the matrix rust sdk
|
||||||
|
|
@ -67,6 +71,7 @@ class TimelineItemContentPollFactory @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
return TimelineItemPollContent(
|
return TimelineItemPollContent(
|
||||||
|
eventId = eventId,
|
||||||
question = content.question,
|
question = content.question,
|
||||||
answerItems = answerItems,
|
answerItems = answerItems,
|
||||||
pollKind = content.kind,
|
pollKind = content.kind,
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,11 @@
|
||||||
package io.element.android.features.messages.impl.timeline.model.event
|
package io.element.android.features.messages.impl.timeline.model.event
|
||||||
|
|
||||||
import io.element.android.features.poll.api.PollAnswerItem
|
import io.element.android.features.poll.api.PollAnswerItem
|
||||||
|
import io.element.android.libraries.matrix.api.core.EventId
|
||||||
import io.element.android.libraries.matrix.api.poll.PollKind
|
import io.element.android.libraries.matrix.api.poll.PollKind
|
||||||
|
|
||||||
data class TimelineItemPollContent(
|
data class TimelineItemPollContent(
|
||||||
|
val eventId: EventId?,
|
||||||
val question: String,
|
val question: String,
|
||||||
val answerItems: List<PollAnswerItem>,
|
val answerItems: List<PollAnswerItem>,
|
||||||
val pollKind: PollKind,
|
val pollKind: PollKind,
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package io.element.android.features.messages.impl.timeline.model.event
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.features.poll.api.aPollAnswerItemList
|
import io.element.android.features.poll.api.aPollAnswerItemList
|
||||||
|
import io.element.android.libraries.matrix.api.core.EventId
|
||||||
import io.element.android.libraries.matrix.api.poll.PollKind
|
import io.element.android.libraries.matrix.api.poll.PollKind
|
||||||
|
|
||||||
open class TimelineItemPollContentProvider : PreviewParameterProvider<TimelineItemPollContent> {
|
open class TimelineItemPollContentProvider : PreviewParameterProvider<TimelineItemPollContent> {
|
||||||
|
|
@ -30,6 +31,7 @@ open class TimelineItemPollContentProvider : PreviewParameterProvider<TimelineIt
|
||||||
|
|
||||||
fun aTimelineItemPollContent(): TimelineItemPollContent {
|
fun aTimelineItemPollContent(): TimelineItemPollContent {
|
||||||
return TimelineItemPollContent(
|
return TimelineItemPollContent(
|
||||||
|
eventId = EventId("\$anEventId"),
|
||||||
pollKind = PollKind.Disclosed,
|
pollKind = PollKind.Disclosed,
|
||||||
question = "What type of food should we have at the party?",
|
question = "What type of food should we have at the party?",
|
||||||
answerItems = aPollAnswerItemList(),
|
answerItems = aPollAnswerItemList(),
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ class MessageSummaryFormatterImpl @Inject constructor(
|
||||||
is TimelineItemLocationContent -> context.getString(CommonStrings.common_shared_location)
|
is TimelineItemLocationContent -> context.getString(CommonStrings.common_shared_location)
|
||||||
is TimelineItemEncryptedContent -> context.getString(CommonStrings.common_unable_to_decrypt)
|
is TimelineItemEncryptedContent -> context.getString(CommonStrings.common_unable_to_decrypt)
|
||||||
is TimelineItemRedactedContent -> context.getString(CommonStrings.common_message_removed)
|
is TimelineItemRedactedContent -> context.getString(CommonStrings.common_message_removed)
|
||||||
is TimelineItemPollContent, // Todo Polls: handle summary
|
is TimelineItemPollContent -> event.content.question
|
||||||
is TimelineItemUnknownContent -> context.getString(CommonStrings.common_unsupported_event)
|
is TimelineItemUnknownContent -> context.getString(CommonStrings.common_unsupported_event)
|
||||||
is TimelineItemImageContent -> context.getString(CommonStrings.common_image)
|
is TimelineItemImageContent -> context.getString(CommonStrings.common_image)
|
||||||
is TimelineItemVideoContent -> context.getString(CommonStrings.common_video)
|
is TimelineItemVideoContent -> context.getString(CommonStrings.common_video)
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemImageContent
|
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemImageContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemStateEventContent
|
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemStateEventContent
|
||||||
import io.element.android.features.poll.api.PollAnswerItem
|
import io.element.android.features.poll.api.PollAnswerItem
|
||||||
|
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.PollAnswer
|
||||||
import io.element.android.libraries.matrix.api.poll.PollKind
|
import io.element.android.libraries.matrix.api.poll.PollKind
|
||||||
import io.element.android.libraries.matrix.test.A_MESSAGE
|
import io.element.android.libraries.matrix.test.A_MESSAGE
|
||||||
|
|
@ -384,6 +385,7 @@ class ActionListPresenterTest {
|
||||||
val messageEvent = aMessageEvent(
|
val messageEvent = aMessageEvent(
|
||||||
isMine = true,
|
isMine = true,
|
||||||
content = TimelineItemPollContent(
|
content = TimelineItemPollContent(
|
||||||
|
eventId = EventId("\$anEventId"),
|
||||||
question = "Some question?",
|
question = "Some question?",
|
||||||
answerItems = listOf(
|
answerItems = listOf(
|
||||||
PollAnswerItem(
|
PollAnswerItem(
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ 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.TimelinePresenter
|
||||||
import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactory
|
import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactory
|
||||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||||
import io.element.android.libraries.matrix.ui.components.aMatrixUserList
|
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimeline
|
import io.element.android.libraries.matrix.api.timeline.MatrixTimeline
|
||||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
|
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventReaction
|
import io.element.android.libraries.matrix.api.timeline.item.event.EventReaction
|
||||||
|
|
@ -36,8 +36,10 @@ import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||||
import io.element.android.libraries.matrix.test.room.aMessageContent
|
import io.element.android.libraries.matrix.test.room.aMessageContent
|
||||||
import io.element.android.libraries.matrix.test.room.anEventTimelineItem
|
import io.element.android.libraries.matrix.test.room.anEventTimelineItem
|
||||||
import io.element.android.libraries.matrix.test.timeline.FakeMatrixTimeline
|
import io.element.android.libraries.matrix.test.timeline.FakeMatrixTimeline
|
||||||
|
import io.element.android.libraries.matrix.ui.components.aMatrixUserList
|
||||||
import io.element.android.tests.testutils.awaitWithLatch
|
import io.element.android.tests.testutils.awaitWithLatch
|
||||||
import io.element.android.tests.testutils.testCoroutineDispatchers
|
import io.element.android.tests.testutils.testCoroutineDispatchers
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.test.TestScope
|
import kotlinx.coroutines.test.TestScope
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
@ -248,6 +250,23 @@ class TimelinePresenterTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `present - PollAnswerSelected event calls into rust room api and analytics`() = runTest {
|
||||||
|
val room = FakeMatrixRoom()
|
||||||
|
val presenter = createTimelinePresenter(room)
|
||||||
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
|
presenter.present()
|
||||||
|
}.test {
|
||||||
|
val initialState = awaitItem()
|
||||||
|
initialState.eventSink.invoke(TimelineEvents.PollAnswerSelected(AN_EVENT_ID, "anAnswerId"))
|
||||||
|
}
|
||||||
|
delay(1)
|
||||||
|
assertThat(room.sendPollResponseInvocations.size).isEqualTo(1)
|
||||||
|
assertThat(room.sendPollResponseInvocations.first().answers).isEqualTo(listOf("anAnswerId"))
|
||||||
|
assertThat(room.sendPollResponseInvocations.first().pollStartId).isEqualTo(AN_EVENT_ID)
|
||||||
|
// TODO Polls: Test poll vote analytic
|
||||||
|
}
|
||||||
|
|
||||||
private fun TestScope.createTimelinePresenter(
|
private fun TestScope.createTimelinePresenter(
|
||||||
timeline: MatrixTimeline = FakeMatrixTimeline(),
|
timeline: MatrixTimeline = FakeMatrixTimeline(),
|
||||||
timelineItemsFactory: TimelineItemsFactory = aTimelineItemsFactory()
|
timelineItemsFactory: TimelineItemsFactory = aTimelineItemsFactory()
|
||||||
|
|
@ -259,4 +278,15 @@ class TimelinePresenterTest {
|
||||||
appScope = this
|
appScope = this
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun TestScope.createTimelinePresenter(
|
||||||
|
room: MatrixRoom,
|
||||||
|
): TimelinePresenter {
|
||||||
|
return TimelinePresenter(
|
||||||
|
timelineItemsFactory = aTimelineItemsFactory(),
|
||||||
|
room = room,
|
||||||
|
dispatchers = testCoroutineDispatchers(),
|
||||||
|
appScope = this
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,10 +22,12 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
|
||||||
import io.element.android.features.poll.api.PollAnswerItem
|
import io.element.android.features.poll.api.PollAnswerItem
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||||
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
||||||
|
import io.element.android.libraries.matrix.api.core.EventId
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
import io.element.android.libraries.matrix.api.poll.PollAnswer
|
import io.element.android.libraries.matrix.api.poll.PollAnswer
|
||||||
import io.element.android.libraries.matrix.api.poll.PollKind
|
import io.element.android.libraries.matrix.api.poll.PollKind
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.PollContent
|
import io.element.android.libraries.matrix.api.timeline.item.event.PollContent
|
||||||
|
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||||
import io.element.android.libraries.matrix.test.A_USER_ID_10
|
import io.element.android.libraries.matrix.test.A_USER_ID_10
|
||||||
import io.element.android.libraries.matrix.test.A_USER_ID_2
|
import io.element.android.libraries.matrix.test.A_USER_ID_2
|
||||||
|
|
@ -49,14 +51,14 @@ internal class TimelineItemContentPollFactoryTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Disclosed poll - not ended, no votes`() = runTest {
|
fun `Disclosed poll - not ended, no votes`() = runTest {
|
||||||
Truth.assertThat(factory.create(aPollContent())).isEqualTo(aTimelineItemPollContent())
|
Truth.assertThat(factory.create(aPollContent(), eventId = null)).isEqualTo(aTimelineItemPollContent())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Disclosed poll - not ended, some votes, including one from current user`() = runTest {
|
fun `Disclosed poll - not ended, some votes, including one from current user`() = runTest {
|
||||||
val votes = MY_USER_WINNING_VOTES.mapKeys { it.key.id }
|
val votes = MY_USER_WINNING_VOTES.mapKeys { it.key.id }
|
||||||
Truth.assertThat(
|
Truth.assertThat(
|
||||||
factory.create(aPollContent(votes = votes))
|
factory.create(aPollContent(votes = votes), eventId = null)
|
||||||
)
|
)
|
||||||
.isEqualTo(
|
.isEqualTo(
|
||||||
aTimelineItemPollContent(
|
aTimelineItemPollContent(
|
||||||
|
|
@ -73,7 +75,7 @@ internal class TimelineItemContentPollFactoryTest {
|
||||||
@Test
|
@Test
|
||||||
fun `Disclosed poll - ended, no votes, no winner`() = runTest {
|
fun `Disclosed poll - ended, no votes, no winner`() = runTest {
|
||||||
Truth.assertThat(
|
Truth.assertThat(
|
||||||
factory.create(aPollContent(endTime = 1UL))
|
factory.create(aPollContent(endTime = 1UL), eventId = null)
|
||||||
).isEqualTo(
|
).isEqualTo(
|
||||||
aTimelineItemPollContent().let {
|
aTimelineItemPollContent().let {
|
||||||
it.copy(
|
it.copy(
|
||||||
|
|
@ -88,7 +90,7 @@ internal class TimelineItemContentPollFactoryTest {
|
||||||
fun `Disclosed poll - ended, some votes, including one from current user (winner)`() = runTest {
|
fun `Disclosed poll - ended, some votes, including one from current user (winner)`() = runTest {
|
||||||
val votes = MY_USER_WINNING_VOTES.mapKeys { it.key.id }
|
val votes = MY_USER_WINNING_VOTES.mapKeys { it.key.id }
|
||||||
Truth.assertThat(
|
Truth.assertThat(
|
||||||
factory.create(aPollContent(votes = votes, endTime = 1UL))
|
factory.create(aPollContent(votes = votes, endTime = 1UL), eventId = null)
|
||||||
)
|
)
|
||||||
.isEqualTo(
|
.isEqualTo(
|
||||||
aTimelineItemPollContent(
|
aTimelineItemPollContent(
|
||||||
|
|
@ -107,7 +109,7 @@ internal class TimelineItemContentPollFactoryTest {
|
||||||
fun `Disclosed poll - ended, some votes, including one from current user (not winner) and two winning votes`() = runTest {
|
fun `Disclosed poll - ended, some votes, including one from current user (not winner) and two winning votes`() = runTest {
|
||||||
val votes = OTHER_WINNING_VOTES.mapKeys { it.key.id }
|
val votes = OTHER_WINNING_VOTES.mapKeys { it.key.id }
|
||||||
Truth.assertThat(
|
Truth.assertThat(
|
||||||
factory.create(aPollContent(votes = votes, endTime = 1UL))
|
factory.create(aPollContent(votes = votes, endTime = 1UL), eventId = null)
|
||||||
)
|
)
|
||||||
.isEqualTo(
|
.isEqualTo(
|
||||||
aTimelineItemPollContent(
|
aTimelineItemPollContent(
|
||||||
|
|
@ -125,9 +127,9 @@ internal class TimelineItemContentPollFactoryTest {
|
||||||
@Test
|
@Test
|
||||||
fun `Undisclosed poll - not ended, no votes`() = runTest {
|
fun `Undisclosed poll - not ended, no votes`() = runTest {
|
||||||
Truth.assertThat(
|
Truth.assertThat(
|
||||||
factory.create(aPollContent(PollKind.Undisclosed).copy())
|
factory.create(aPollContent(PollKind.Undisclosed).copy(), eventId = null)
|
||||||
).isEqualTo(
|
).isEqualTo(
|
||||||
aTimelineItemPollContent(PollKind.Undisclosed).let {
|
aTimelineItemPollContent(pollKind = PollKind.Undisclosed).let {
|
||||||
it.copy(answerItems = it.answerItems.map { answerItem -> answerItem.copy(isDisclosed = false) })
|
it.copy(answerItems = it.answerItems.map { answerItem -> answerItem.copy(isDisclosed = false) })
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -137,7 +139,7 @@ internal class TimelineItemContentPollFactoryTest {
|
||||||
fun `Undisclosed poll - not ended, some votes, including one from current user`() = runTest {
|
fun `Undisclosed poll - not ended, some votes, including one from current user`() = runTest {
|
||||||
val votes = MY_USER_WINNING_VOTES.mapKeys { it.key.id }
|
val votes = MY_USER_WINNING_VOTES.mapKeys { it.key.id }
|
||||||
Truth.assertThat(
|
Truth.assertThat(
|
||||||
factory.create(aPollContent(pollKind = PollKind.Undisclosed, votes = votes))
|
factory.create(aPollContent(pollKind = PollKind.Undisclosed, votes = votes), eventId = null)
|
||||||
)
|
)
|
||||||
.isEqualTo(
|
.isEqualTo(
|
||||||
aTimelineItemPollContent(
|
aTimelineItemPollContent(
|
||||||
|
|
@ -155,7 +157,7 @@ internal class TimelineItemContentPollFactoryTest {
|
||||||
@Test
|
@Test
|
||||||
fun `Undisclosed poll - ended, no votes, no winner`() = runTest {
|
fun `Undisclosed poll - ended, no votes, no winner`() = runTest {
|
||||||
Truth.assertThat(
|
Truth.assertThat(
|
||||||
factory.create(aPollContent(pollKind = PollKind.Undisclosed, endTime = 1UL))
|
factory.create(aPollContent(pollKind = PollKind.Undisclosed, endTime = 1UL), eventId = null)
|
||||||
).isEqualTo(
|
).isEqualTo(
|
||||||
aTimelineItemPollContent().let {
|
aTimelineItemPollContent().let {
|
||||||
it.copy(
|
it.copy(
|
||||||
|
|
@ -173,7 +175,7 @@ internal class TimelineItemContentPollFactoryTest {
|
||||||
fun `Undisclosed poll - ended, some votes, including one from current user (winner)`() = runTest {
|
fun `Undisclosed poll - ended, some votes, including one from current user (winner)`() = runTest {
|
||||||
val votes = MY_USER_WINNING_VOTES.mapKeys { it.key.id }
|
val votes = MY_USER_WINNING_VOTES.mapKeys { it.key.id }
|
||||||
Truth.assertThat(
|
Truth.assertThat(
|
||||||
factory.create(aPollContent(pollKind = PollKind.Undisclosed, votes = votes, endTime = 1UL))
|
factory.create(aPollContent(pollKind = PollKind.Undisclosed, votes = votes, endTime = 1UL), eventId = null)
|
||||||
)
|
)
|
||||||
.isEqualTo(
|
.isEqualTo(
|
||||||
aTimelineItemPollContent(
|
aTimelineItemPollContent(
|
||||||
|
|
@ -193,7 +195,7 @@ internal class TimelineItemContentPollFactoryTest {
|
||||||
fun `Undisclosed poll - ended, some votes, including one from current user (not winner) and two winning votes`() = runTest {
|
fun `Undisclosed poll - ended, some votes, including one from current user (not winner) and two winning votes`() = runTest {
|
||||||
val votes = OTHER_WINNING_VOTES.mapKeys { it.key.id }
|
val votes = OTHER_WINNING_VOTES.mapKeys { it.key.id }
|
||||||
Truth.assertThat(
|
Truth.assertThat(
|
||||||
factory.create(aPollContent(PollKind.Undisclosed).copy(votes = votes, endTime = 1UL))
|
factory.create(aPollContent(PollKind.Undisclosed).copy(votes = votes, endTime = 1UL), eventId = null)
|
||||||
)
|
)
|
||||||
.isEqualTo(
|
.isEqualTo(
|
||||||
aTimelineItemPollContent(
|
aTimelineItemPollContent(
|
||||||
|
|
@ -209,6 +211,15 @@ internal class TimelineItemContentPollFactoryTest {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `eventId is populated`() = runTest {
|
||||||
|
Truth.assertThat(factory.create(aPollContent(), eventId = null))
|
||||||
|
.isEqualTo(aTimelineItemPollContent(eventId = null))
|
||||||
|
|
||||||
|
Truth.assertThat(factory.create(aPollContent(), eventId = AN_EVENT_ID))
|
||||||
|
.isEqualTo(aTimelineItemPollContent(eventId = AN_EVENT_ID))
|
||||||
|
}
|
||||||
|
|
||||||
private fun aPollContent(
|
private fun aPollContent(
|
||||||
pollKind: PollKind = PollKind.Disclosed,
|
pollKind: PollKind = PollKind.Disclosed,
|
||||||
votes: Map<String, List<UserId>> = emptyMap(),
|
votes: Map<String, List<UserId>> = emptyMap(),
|
||||||
|
|
@ -223,6 +234,7 @@ internal class TimelineItemContentPollFactoryTest {
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun aTimelineItemPollContent(
|
private fun aTimelineItemPollContent(
|
||||||
|
eventId: EventId? = null,
|
||||||
pollKind: PollKind = PollKind.Disclosed,
|
pollKind: PollKind = PollKind.Disclosed,
|
||||||
answerItems: List<PollAnswerItem> = listOf(
|
answerItems: List<PollAnswerItem> = listOf(
|
||||||
aPollAnswerItem(A_POLL_ANSWER_1),
|
aPollAnswerItem(A_POLL_ANSWER_1),
|
||||||
|
|
@ -232,6 +244,7 @@ internal class TimelineItemContentPollFactoryTest {
|
||||||
),
|
),
|
||||||
isEnded: Boolean = false,
|
isEnded: Boolean = false,
|
||||||
) = TimelineItemPollContent(
|
) = TimelineItemPollContent(
|
||||||
|
eventId = eventId,
|
||||||
question = A_POLL_QUESTION,
|
question = A_POLL_QUESTION,
|
||||||
answerItems = answerItems,
|
answerItems = answerItems,
|
||||||
pollKind = pollKind,
|
pollKind = pollKind,
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ import io.element.android.libraries.designsystem.preview.DayNightPreviews
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
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.Icon
|
||||||
import io.element.android.libraries.designsystem.theme.components.Text
|
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.PollAnswer
|
||||||
import io.element.android.libraries.matrix.api.poll.PollKind
|
import io.element.android.libraries.matrix.api.poll.PollKind
|
||||||
import io.element.android.libraries.theme.ElementTheme
|
import io.element.android.libraries.theme.ElementTheme
|
||||||
|
|
@ -43,13 +44,18 @@ import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PollContentView(
|
fun PollContentView(
|
||||||
|
eventId: EventId?,
|
||||||
question: String,
|
question: String,
|
||||||
answerItems: ImmutableList<PollAnswerItem>,
|
answerItems: ImmutableList<PollAnswerItem>,
|
||||||
pollKind: PollKind,
|
pollKind: PollKind,
|
||||||
isPollEnded: Boolean,
|
isPollEnded: Boolean,
|
||||||
onAnswerSelected: (PollAnswer) -> Unit,
|
onAnswerSelected: (pollStartId: EventId, answerId: String) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
|
fun onAnswerSelected(pollAnswer: PollAnswer) {
|
||||||
|
eventId?.let { onAnswerSelected(it, pollAnswer.id) }
|
||||||
|
}
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.selectableGroup()
|
.selectableGroup()
|
||||||
|
|
@ -58,7 +64,7 @@ fun PollContentView(
|
||||||
) {
|
) {
|
||||||
PollTitle(title = question)
|
PollTitle(title = question)
|
||||||
|
|
||||||
PollAnswers(answerItems = answerItems, onAnswerSelected = onAnswerSelected)
|
PollAnswers(answerItems = answerItems, onAnswerSelected = ::onAnswerSelected)
|
||||||
|
|
||||||
when {
|
when {
|
||||||
isPollEnded || pollKind == PollKind.Disclosed -> DisclosedPollBottomNotice(answerItems)
|
isPollEnded || pollKind == PollKind.Disclosed -> DisclosedPollBottomNotice(answerItems)
|
||||||
|
|
@ -134,11 +140,12 @@ fun ColumnScope.UndisclosedPollBottomNotice(modifier: Modifier = Modifier) {
|
||||||
@Composable
|
@Composable
|
||||||
internal fun PollContentUndisclosedPreview() = ElementPreview {
|
internal fun PollContentUndisclosedPreview() = ElementPreview {
|
||||||
PollContentView(
|
PollContentView(
|
||||||
|
eventId = EventId("\$anEventId"),
|
||||||
question = "What type of food should we have at the party?",
|
question = "What type of food should we have at the party?",
|
||||||
answerItems = aPollAnswerItemList(isDisclosed = false),
|
answerItems = aPollAnswerItemList(isDisclosed = false),
|
||||||
pollKind = PollKind.Undisclosed,
|
pollKind = PollKind.Undisclosed,
|
||||||
isPollEnded = false,
|
isPollEnded = false,
|
||||||
onAnswerSelected = { },
|
onAnswerSelected = { _, _ -> },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -146,11 +153,12 @@ internal fun PollContentUndisclosedPreview() = ElementPreview {
|
||||||
@Composable
|
@Composable
|
||||||
internal fun PollContentDisclosedPreview() = ElementPreview {
|
internal fun PollContentDisclosedPreview() = ElementPreview {
|
||||||
PollContentView(
|
PollContentView(
|
||||||
|
eventId = EventId("\$anEventId"),
|
||||||
question = "What type of food should we have at the party?",
|
question = "What type of food should we have at the party?",
|
||||||
answerItems = aPollAnswerItemList(),
|
answerItems = aPollAnswerItemList(),
|
||||||
pollKind = PollKind.Disclosed,
|
pollKind = PollKind.Disclosed,
|
||||||
isPollEnded = false,
|
isPollEnded = false,
|
||||||
onAnswerSelected = { },
|
onAnswerSelected = { _, _ -> },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -158,10 +166,11 @@ internal fun PollContentDisclosedPreview() = ElementPreview {
|
||||||
@Composable
|
@Composable
|
||||||
internal fun PollContentEndedPreview() = ElementPreview {
|
internal fun PollContentEndedPreview() = ElementPreview {
|
||||||
PollContentView(
|
PollContentView(
|
||||||
|
eventId = EventId("\$anEventId"),
|
||||||
question = "What type of food should we have at the party?",
|
question = "What type of food should we have at the party?",
|
||||||
answerItems = aPollAnswerItemList(isEnded = true),
|
answerItems = aPollAnswerItemList(isEnded = true),
|
||||||
pollKind = PollKind.Disclosed,
|
pollKind = PollKind.Disclosed,
|
||||||
isPollEnded = false,
|
isPollEnded = false,
|
||||||
onAnswerSelected = { },
|
onAnswerSelected = { _, _ -> },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,7 @@ jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" }
|
||||||
appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" }
|
appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" }
|
||||||
molecule-runtime = { module = "app.cash.molecule:molecule-runtime", version.ref = "molecule" }
|
molecule-runtime = { module = "app.cash.molecule:molecule-runtime", version.ref = "molecule" }
|
||||||
timber = "com.jakewharton.timber:timber:5.0.1"
|
timber = "com.jakewharton.timber:timber:5.0.1"
|
||||||
matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.47"
|
matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.48"
|
||||||
sqldelight-driver-android = { module = "com.squareup.sqldelight:android-driver", version.ref = "sqldelight" }
|
sqldelight-driver-android = { module = "com.squareup.sqldelight:android-driver", version.ref = "sqldelight" }
|
||||||
sqldelight-driver-jvm = { module = "com.squareup.sqldelight:sqlite-driver", version.ref = "sqldelight" }
|
sqldelight-driver-jvm = { module = "com.squareup.sqldelight:sqlite-driver", version.ref = "sqldelight" }
|
||||||
sqldelight-coroutines = { module = "com.squareup.sqldelight:coroutines-extensions", version.ref = "sqldelight" }
|
sqldelight-coroutines = { module = "com.squareup.sqldelight:coroutines-extensions", version.ref = "sqldelight" }
|
||||||
|
|
|
||||||
|
|
@ -161,6 +161,22 @@ interface MatrixRoom : Closeable {
|
||||||
pollKind: PollKind,
|
pollKind: PollKind,
|
||||||
): Result<Unit>
|
): Result<Unit>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a response to a poll.
|
||||||
|
*
|
||||||
|
* @param pollStartId The event ID of the poll start event.
|
||||||
|
* @param answers The list of answer ids to send.
|
||||||
|
*/
|
||||||
|
suspend fun sendPollResponse(pollStartId: EventId, answers: List<String>): Result<Unit>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ends a poll in the room.
|
||||||
|
*
|
||||||
|
* @param pollStartId The event ID of the poll start event.
|
||||||
|
* @param text Fallback text of the poll end event.
|
||||||
|
*/
|
||||||
|
suspend fun endPoll(pollStartId: EventId, text: String): Result<Unit>
|
||||||
|
|
||||||
override fun close() = destroy()
|
override fun close() = destroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,8 @@ val oidcConfiguration: OidcConfiguration = OidcConfiguration(
|
||||||
clientName = "Element",
|
clientName = "Element",
|
||||||
redirectUri = OidcConfig.redirectUri,
|
redirectUri = OidcConfig.redirectUri,
|
||||||
clientUri = "https://element.io",
|
clientUri = "https://element.io",
|
||||||
tosUri = "https://element.io/user-terms-of-service",
|
logoUri = "https://element.io/mobile-icon.png",
|
||||||
|
tosUri = "https://element.io/acceptable-use-policy-terms",
|
||||||
policyUri = "https://element.io/privacy",
|
policyUri = "https://element.io/privacy",
|
||||||
/**
|
/**
|
||||||
* Some homeservers/auth issuers don't support dynamic client registration, and have to be registered manually
|
* Some homeservers/auth issuers don't support dynamic client registration, and have to be registered manually
|
||||||
|
|
@ -239,7 +239,7 @@ class RustMatrixRoom(
|
||||||
override suspend fun editMessage(originalEventId: EventId?, transactionId: TransactionId?, message: String): Result<Unit> = withContext(roomDispatcher) {
|
override suspend fun editMessage(originalEventId: EventId?, transactionId: TransactionId?, message: String): Result<Unit> = withContext(roomDispatcher) {
|
||||||
if (originalEventId != null) {
|
if (originalEventId != null) {
|
||||||
runCatching {
|
runCatching {
|
||||||
innerRoom.edit(/* TODO use content */ message, originalEventId.value, transactionId?.value)
|
innerRoom.edit(messageEventContentFromMarkdown(message), originalEventId.value, transactionId?.value)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
runCatching {
|
runCatching {
|
||||||
|
|
@ -250,10 +250,8 @@ class RustMatrixRoom(
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun replyMessage(eventId: EventId, message: String): Result<Unit> = withContext(roomDispatcher) {
|
override suspend fun replyMessage(eventId: EventId, message: String): Result<Unit> = withContext(roomDispatcher) {
|
||||||
val transactionId = genTransactionId()
|
|
||||||
// val content = messageEventContentFromMarkdown(message)
|
|
||||||
runCatching {
|
runCatching {
|
||||||
innerRoom.sendReply(/* TODO use content */ message, eventId.value, transactionId)
|
innerRoom.sendReply(messageEventContentFromMarkdown(message), eventId.value, genTransactionId())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -426,6 +424,32 @@ class RustMatrixRoom(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun sendPollResponse(
|
||||||
|
pollStartId: EventId,
|
||||||
|
answers: List<String>
|
||||||
|
): Result<Unit> = withContext(roomDispatcher) {
|
||||||
|
runCatching {
|
||||||
|
innerRoom.sendPollResponse(
|
||||||
|
pollStartId = pollStartId.value,
|
||||||
|
answers = answers,
|
||||||
|
txnId = genTransactionId(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun endPoll(
|
||||||
|
pollStartId: EventId,
|
||||||
|
text: String
|
||||||
|
): Result<Unit> = withContext(roomDispatcher) {
|
||||||
|
runCatching {
|
||||||
|
innerRoom.endPoll(
|
||||||
|
pollStartId = pollStartId.value,
|
||||||
|
text = text,
|
||||||
|
txnId = genTransactionId(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun sendAttachment(files: List<File>, handle: () -> SendAttachmentJoinHandle): Result<MediaUploadHandler> {
|
private suspend fun sendAttachment(files: List<File>, handle: () -> SendAttachmentJoinHandle): Result<MediaUploadHandler> {
|
||||||
return runCatching {
|
return runCatching {
|
||||||
MediaUploadHandlerImpl(files, handle())
|
MediaUploadHandlerImpl(files, handle())
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,8 @@ class FakeMatrixRoom(
|
||||||
private var reportContentResult = Result.success(Unit)
|
private var reportContentResult = Result.success(Unit)
|
||||||
private var sendLocationResult = Result.success(Unit)
|
private var sendLocationResult = Result.success(Unit)
|
||||||
private var createPollResult = Result.success(Unit)
|
private var createPollResult = Result.success(Unit)
|
||||||
|
private var sendPollResponseResult = Result.success(Unit)
|
||||||
|
private var endPollResult = Result.success(Unit)
|
||||||
private var progressCallbackValues = emptyList<Pair<Long, Long>>()
|
private var progressCallbackValues = emptyList<Pair<Long, Long>>()
|
||||||
val editMessageCalls = mutableListOf<String>()
|
val editMessageCalls = mutableListOf<String>()
|
||||||
|
|
||||||
|
|
@ -111,6 +113,12 @@ class FakeMatrixRoom(
|
||||||
private val _createPollInvocations = mutableListOf<CreatePollInvocation>()
|
private val _createPollInvocations = mutableListOf<CreatePollInvocation>()
|
||||||
val createPollInvocations: List<CreatePollInvocation> = _createPollInvocations
|
val createPollInvocations: List<CreatePollInvocation> = _createPollInvocations
|
||||||
|
|
||||||
|
private val _sendPollResponseInvocations = mutableListOf<SendPollResponseInvocation>()
|
||||||
|
val sendPollResponseInvocations: List<SendPollResponseInvocation> = _sendPollResponseInvocations
|
||||||
|
|
||||||
|
private val _endPollInvocations = mutableListOf<EndPollInvocation>()
|
||||||
|
val endPollInvocations: List<EndPollInvocation> = _endPollInvocations
|
||||||
|
|
||||||
var invitedUserId: UserId? = null
|
var invitedUserId: UserId? = null
|
||||||
private set
|
private set
|
||||||
|
|
||||||
|
|
@ -329,6 +337,22 @@ class FakeMatrixRoom(
|
||||||
return createPollResult
|
return createPollResult
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun sendPollResponse(
|
||||||
|
pollStartId: EventId,
|
||||||
|
answers: List<String>
|
||||||
|
): Result<Unit> = simulateLongTask {
|
||||||
|
_sendPollResponseInvocations.add(SendPollResponseInvocation(pollStartId, answers))
|
||||||
|
return sendPollResponseResult
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun endPoll(
|
||||||
|
pollStartId: EventId,
|
||||||
|
text: String
|
||||||
|
): Result<Unit> = simulateLongTask {
|
||||||
|
_endPollInvocations.add(EndPollInvocation(pollStartId, text))
|
||||||
|
return endPollResult
|
||||||
|
}
|
||||||
|
|
||||||
fun givenLeaveRoomError(throwable: Throwable?) {
|
fun givenLeaveRoomError(throwable: Throwable?) {
|
||||||
this.leaveRoomError = throwable
|
this.leaveRoomError = throwable
|
||||||
}
|
}
|
||||||
|
|
@ -425,6 +449,14 @@ class FakeMatrixRoom(
|
||||||
createPollResult = result
|
createPollResult = result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun givenSendPollResponseResult(result: Result<Unit>) {
|
||||||
|
sendPollResponseResult = result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun givenEndPollResult(result: Result<Unit>) {
|
||||||
|
endPollResult = result
|
||||||
|
}
|
||||||
|
|
||||||
fun givenProgressCallbackValues(values: List<Pair<Long, Long>>) {
|
fun givenProgressCallbackValues(values: List<Pair<Long, Long>>) {
|
||||||
progressCallbackValues = values
|
progressCallbackValues = values
|
||||||
}
|
}
|
||||||
|
|
@ -444,3 +476,13 @@ data class CreatePollInvocation(
|
||||||
val maxSelections: Int,
|
val maxSelections: Int,
|
||||||
val pollKind: PollKind,
|
val pollKind: PollKind,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
data class SendPollResponseInvocation(
|
||||||
|
val pollStartId: EventId,
|
||||||
|
val answers: List<String>,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class EndPollInvocation(
|
||||||
|
val pollStartId: EventId,
|
||||||
|
val text: String,
|
||||||
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue