Add feature flag for polls (#1064)

* Handle poll events from the sdk

* Render started poll event in the timeline

* Create poll module

* Check poll kind before revealing the results

* Check if user has voted before revealing the results

* Add active poll previews

* Minor cleanup

* Update todos

* Fix CI

* Remove hardcoded string

* Update preview

* changelog file

* Update screenshots

* Use CommonPlurals

* Set poll root view as selectableGroup

* Improve poll result rendering

* Update screenshots

* Add missing showkase processor

* Update screenshots

* Add feature flag for polls

* Add supporting text in PreferenceCheckbox

* Render poll events if feature flag is enabled

* changelog

* Update screenshots

* Fix tests

* Move feature flag check to poll factory

---------

Co-authored-by: ElementBot <benoitm+elementbot@element.io>
This commit is contained in:
Florian Renaud 2023-08-16 17:14:38 +02:00 committed by GitHub
parent 75c19a7e04
commit 41d0d21c80
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 72 additions and 31 deletions

1
changelog.d/1064.wip Normal file
View file

@ -0,0 +1 @@
[Poll] Add feature flag in developer options

View file

@ -89,7 +89,7 @@ class TimelineItemsFactory @Inject constructor(
this.timelineItems.emit(result)
}
private fun buildAndCacheItem(
private suspend fun buildAndCacheItem(
timelineItems: List<MatrixTimelineItem>,
index: Int
): TimelineItem? {

View file

@ -47,7 +47,7 @@ class TimelineItemContentFactory @Inject constructor(
private val failedToParseStateFactory: TimelineItemContentFailedToParseStateFactory
) {
fun create(eventTimelineItem: EventTimelineItem): TimelineItemEventContent {
suspend fun create(eventTimelineItem: EventTimelineItem): TimelineItemEventContent {
return when (val itemContent = eventTimelineItem.content) {
is FailedToParseMessageLikeContent -> failedToParseMessageFactory.create(itemContent)
is FailedToParseStateContent -> failedToParseStateFactory.create(itemContent)

View file

@ -18,7 +18,10 @@ package io.element.android.features.messages.impl.timeline.factories.event
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent
import io.element.android.features.poll.api.PollAnswerItem
import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.poll.PollKind
import io.element.android.libraries.matrix.api.timeline.item.event.PollContent
@ -26,9 +29,12 @@ import javax.inject.Inject
class TimelineItemContentPollFactory @Inject constructor(
private val matrixClient: MatrixClient,
private val featureFlagService: FeatureFlagService,
) {
fun create(content: PollContent): TimelineItemEventContent {
suspend fun create(content: PollContent): TimelineItemEventContent {
if (!featureFlagService.isFeatureEnabled(FeatureFlags.Polls)) return TimelineItemUnknownContent
// Todo Move this computation to the matrix rust sdk
val showResults = content.kind == PollKind.Disclosed && matrixClient.sessionId in content.votes.flatMap { it.value }
val pollVotesCount = content.votes.flatMap { it.value }.size

View file

@ -38,7 +38,7 @@ class TimelineItemEventFactory @Inject constructor(
private val matrixClient: MatrixClient,
) {
fun create(
suspend fun create(
currentTimelineItem: MatrixTimelineItem.Event,
index: Int,
timelineItems: List<MatrixTimelineItem>,

View file

@ -37,6 +37,7 @@ import io.element.android.features.messages.impl.timeline.util.FileExtensionExtr
import io.element.android.libraries.androidutils.filesize.FakeFileSizeFormatter
import io.element.android.libraries.dateformatter.test.FakeDaySeparatorFormatter
import io.element.android.libraries.eventformatter.api.TimelineEventFormatter
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem
import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.tests.testutils.testCoroutineDispatchers
@ -52,14 +53,14 @@ internal fun TestScope.aTimelineItemsFactory(): TimelineItemsFactory {
messageFactory = TimelineItemContentMessageFactory(FakeFileSizeFormatter(), FileExtensionExtractorWithoutValidation()),
redactedMessageFactory = TimelineItemContentRedactedFactory(),
stickerFactory = TimelineItemContentStickerFactory(),
pollFactory = TimelineItemContentPollFactory(matrixClient),
pollFactory = TimelineItemContentPollFactory(matrixClient, FakeFeatureFlagService()),
pollEndFactory = TimelineItemContentPollEndFactory(),
utdFactory = TimelineItemContentUTDFactory(),
roomMembershipFactory = TimelineItemContentRoomMembershipFactory(timelineEventFormatter),
profileChangeFactory = TimelineItemContentProfileChangeFactory(timelineEventFormatter),
stateFactory = TimelineItemContentStateFactory(timelineEventFormatter),
failedToParseMessageFactory = TimelineItemContentFailedToParseMessageFactory(),
failedToParseStateFactory = TimelineItemContentFailedToParseStateFactory()
failedToParseStateFactory = TimelineItemContentFailedToParseStateFactory(),
),
matrixClient = matrixClient,
),

View file

@ -110,6 +110,7 @@ class DeveloperSettingsPresenter @Inject constructor(
FeatureUiModel(
key = feature.key,
title = feature.title,
description = feature.description,
isEnabled = isEnabled
)
}

View file

@ -17,6 +17,8 @@
package io.element.android.libraries.designsystem.components.preferences
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxWidth
@ -35,6 +37,7 @@ import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.theme.components.Checkbox
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.toEnabledColor
import io.element.android.libraries.designsystem.toSecondaryEnabledColor
import io.element.android.libraries.theme.ElementTheme
@Composable
@ -42,6 +45,7 @@ fun PreferenceCheckbox(
title: String,
isChecked: Boolean,
modifier: Modifier = Modifier,
supportingText: String? = null,
enabled: Boolean = true,
icon: ImageVector? = null,
showIconAreaIfNoIcon: Boolean = false,
@ -60,13 +64,23 @@ fun PreferenceCheckbox(
enabled = enabled,
isVisible = showIconAreaIfNoIcon
)
Text(
modifier = Modifier
.weight(1f),
style = ElementTheme.typography.fontBodyLgRegular,
text = title,
color = enabled.toEnabledColor(),
)
Column(
modifier = Modifier.weight(1f),
verticalArrangement = Arrangement.spacedBy(4.dp),
) {
Text(
style = ElementTheme.typography.fontBodyLgRegular,
text = title,
color = enabled.toEnabledColor(),
)
if (supportingText != null) {
Text(
style = ElementTheme.typography.fontBodyMdRegular,
text = supportingText,
color = enabled.toSecondaryEnabledColor(),
)
}
}
Checkbox(
modifier = Modifier
.align(Alignment.CenterVertically),
@ -83,10 +97,19 @@ internal fun PreferenceCheckboxPreview() = ElementThemedPreview { ContentToPrevi
@Composable
private fun ContentToPreview() {
PreferenceCheckbox(
title = "Checkbox",
icon = Icons.Default.Announcement,
enabled = true,
isChecked = true
)
Column {
PreferenceCheckbox(
title = "Checkbox",
icon = Icons.Default.Announcement,
enabled = true,
isChecked = true
)
PreferenceCheckbox(
title = "Checkbox with supporting text",
supportingText = "Supporting text",
icon = Icons.Default.Announcement,
enabled = true,
isChecked = true
)
}
}

View file

@ -25,5 +25,11 @@ enum class FeatureFlags(
LocationSharing(
key = "feature.locationsharing",
title = "Allow user to share location",
),
Polls(
key = "feature.polls",
title = "Polls",
description = "Render poll events in the timeline",
defaultValue = false,
)
}

View file

@ -30,6 +30,7 @@ class BuildtimeFeatureFlagProvider @Inject constructor() :
return if (feature is FeatureFlags) {
when (feature) {
FeatureFlags.LocationSharing -> true
FeatureFlags.Polls -> false
}
} else {
false

View file

@ -54,6 +54,7 @@ fun FeaturePreferenceView(
) {
PreferenceCheckbox(
title = feature.title,
supportingText = feature.description,
isChecked = feature.isEnabled,
modifier = modifier,
onCheckedChange = onCheckedChange

View file

@ -19,5 +19,6 @@ package io.element.android.libraries.featureflag.ui.model
data class FeatureUiModel(
val key: String,
val title: String,
val description: String?,
val isEnabled: Boolean
)

View file

@ -21,7 +21,7 @@ import kotlinx.collections.immutable.persistentListOf
fun aFeatureUiModelList(): ImmutableList<FeatureUiModel> {
return persistentListOf(
FeatureUiModel("key1", "Display State Events", true),
FeatureUiModel("key2", "Display Room Events", false)
FeatureUiModel("key1", "Display State Events", "Show state events in the timeline", true),
FeatureUiModel("key2", "Display Room Events", null, false),
)
}

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4a9a3902dd33ab3e528fa9c534302228113cc468914025766a21397ad3eda9e2
size 45064
oid sha256:aa65609944cd9118668fcdf75a7882111230cceebbdc68bab764f51929dfc6f2
size 49584

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4a9a3902dd33ab3e528fa9c534302228113cc468914025766a21397ad3eda9e2
size 45064
oid sha256:aa65609944cd9118668fcdf75a7882111230cceebbdc68bab764f51929dfc6f2
size 49584

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:cd1cc710443af62efddf0ea165a6289912f471f669803255368d3266b872f5f6
size 49829
oid sha256:c571049a9cbf3dc13d8a700aa4d3639f8fca89db749c5ebfbb59ca6a0bb0b9eb
size 54206

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:cd1cc710443af62efddf0ea165a6289912f471f669803255368d3266b872f5f6
size 49829
oid sha256:c571049a9cbf3dc13d8a700aa4d3639f8fca89db749c5ebfbb59ca6a0bb0b9eb
size 54206

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:06813451633c48fccac6f70810ecb9465946b643376fccd61312b6064b0a3a40
size 11518
oid sha256:7ba8a794a8b65f412806ee1accc4f8678b046e880fd76c038eb054155ba514ea
size 28463