Allow polls to be edited (#1869)
Polls can be edited if they do not have any votes --------- Co-authored-by: ElementBot <benoitm+elementbot@element.io>
This commit is contained in:
parent
4e52244b86
commit
8fcec4a006
50 changed files with 827 additions and 173 deletions
|
|
@ -50,6 +50,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
|
|||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
|
||||
import io.element.android.features.poll.api.create.CreatePollEntryPoint
|
||||
import io.element.android.features.poll.api.create.CreatePollMode
|
||||
import io.element.android.libraries.architecture.BackstackNode
|
||||
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler
|
||||
import io.element.android.libraries.architecture.createNode
|
||||
|
|
@ -113,6 +114,9 @@ class MessagesFlowNode @AssistedInject constructor(
|
|||
|
||||
@Parcelize
|
||||
data object CreatePoll : NavTarget
|
||||
|
||||
@Parcelize
|
||||
data class EditPoll(val eventId: EventId) : NavTarget
|
||||
}
|
||||
|
||||
private val callback = plugins<MessagesEntryPoint.Callback>().firstOrNull()
|
||||
|
|
@ -157,6 +161,10 @@ class MessagesFlowNode @AssistedInject constructor(
|
|||
backstack.push(NavTarget.CreatePoll)
|
||||
}
|
||||
|
||||
override fun onEditPollClicked(eventId: EventId) {
|
||||
backstack.push(NavTarget.EditPoll(eventId))
|
||||
}
|
||||
|
||||
override fun onJoinCallClicked(roomId: RoomId) {
|
||||
val inputs = CallType.RoomCall(
|
||||
sessionId = matrixClient.sessionId,
|
||||
|
|
@ -204,7 +212,14 @@ class MessagesFlowNode @AssistedInject constructor(
|
|||
sendLocationEntryPoint.createNode(this, buildContext)
|
||||
}
|
||||
NavTarget.CreatePoll -> {
|
||||
createPollEntryPoint.createNode(this, buildContext)
|
||||
createPollEntryPoint.nodeBuilder(this, buildContext)
|
||||
.params(CreatePollEntryPoint.Params(mode = CreatePollMode.NewPoll))
|
||||
.build()
|
||||
}
|
||||
is NavTarget.EditPoll -> {
|
||||
createPollEntryPoint.nodeBuilder(this, buildContext)
|
||||
.params(CreatePollEntryPoint.Params(mode = CreatePollMode.EditPoll(eventId = navTarget.eventId)))
|
||||
.build()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,4 +24,5 @@ interface MessagesNavigator {
|
|||
fun onShowEventDebugInfoClicked(eventId: EventId?, debugInfo: TimelineItemDebugInfo)
|
||||
fun onForwardEventClicked(eventId: EventId)
|
||||
fun onReportContentClicked(eventId: EventId, senderId: UserId)
|
||||
fun onEditPollClicked(eventId: EventId)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ class MessagesNode @AssistedInject constructor(
|
|||
fun onReportMessage(eventId: EventId, senderId: UserId)
|
||||
fun onSendLocationClicked()
|
||||
fun onCreatePollClicked()
|
||||
fun onEditPollClicked(eventId: EventId)
|
||||
fun onJoinCallClicked(roomId: RoomId)
|
||||
}
|
||||
|
||||
|
|
@ -107,6 +108,10 @@ class MessagesNode @AssistedInject constructor(
|
|||
callback?.onReportMessage(eventId, senderId)
|
||||
}
|
||||
|
||||
override fun onEditPollClicked(eventId: EventId) {
|
||||
callback?.onEditPollClicked(eventId)
|
||||
}
|
||||
|
||||
private fun onSendLocationClicked() {
|
||||
callback?.onSendLocationClicked()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ class MessagesPresenter @AssistedInject constructor(
|
|||
private val room: MatrixRoom,
|
||||
private val composerPresenter: MessageComposerPresenter,
|
||||
private val voiceMessageComposerPresenter: VoiceMessageComposerPresenter,
|
||||
private val timelinePresenter: TimelinePresenter,
|
||||
timelinePresenterFactory: TimelinePresenter.Factory,
|
||||
private val actionListPresenter: ActionListPresenter,
|
||||
private val customReactionPresenter: CustomReactionPresenter,
|
||||
private val reactionSummaryPresenter: ReactionSummaryPresenter,
|
||||
|
|
@ -110,6 +110,8 @@ class MessagesPresenter @AssistedInject constructor(
|
|||
private val buildMeta: BuildMeta,
|
||||
) : Presenter<MessagesState> {
|
||||
|
||||
private val timelinePresenter = timelinePresenterFactory.create(navigator = navigator)
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(navigator: MessagesNavigator): MessagesPresenter
|
||||
|
|
@ -294,20 +296,28 @@ class MessagesPresenter @AssistedInject constructor(
|
|||
composerState: MessageComposerState,
|
||||
enableTextFormatting: Boolean,
|
||||
) {
|
||||
val composerMode = MessageComposerMode.Edit(
|
||||
targetEvent.eventId,
|
||||
(targetEvent.content as? TimelineItemTextBasedContent)?.let {
|
||||
if (enableTextFormatting) {
|
||||
it.htmlBody ?: it.body
|
||||
} else {
|
||||
it.body
|
||||
}
|
||||
}.orEmpty(),
|
||||
targetEvent.transactionId,
|
||||
)
|
||||
composerState.eventSink(
|
||||
MessageComposerEvents.SetMode(composerMode)
|
||||
)
|
||||
when (targetEvent.content) {
|
||||
is TimelineItemPollContent -> {
|
||||
if (targetEvent.eventId == null) return
|
||||
navigator.onEditPollClicked(targetEvent.eventId)
|
||||
}
|
||||
else -> {
|
||||
val composerMode = MessageComposerMode.Edit(
|
||||
targetEvent.eventId,
|
||||
(targetEvent.content as? TimelineItemTextBasedContent)?.let {
|
||||
if (enableTextFormatting) {
|
||||
it.htmlBody ?: it.body
|
||||
} else {
|
||||
it.body
|
||||
}
|
||||
}.orEmpty(),
|
||||
targetEvent.transactionId,
|
||||
)
|
||||
composerState.eventSink(
|
||||
MessageComposerEvents.SetMode(composerMode)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleActionReply(targetEvent: TimelineItem.Event, composerState: MessageComposerState) {
|
||||
|
|
|
|||
|
|
@ -112,7 +112,10 @@ class ActionListPresenter @Inject constructor(
|
|||
// Can only reply or forward messages already uploaded to the server
|
||||
add(TimelineItemAction.Reply)
|
||||
}
|
||||
if (!timelineItem.content.isEnded && timelineItem.isRemote && isMineOrCanRedact) {
|
||||
if (timelineItem.isRemote && timelineItem.isEditable) {
|
||||
add(TimelineItemAction.Edit)
|
||||
}
|
||||
if (timelineItem.isRemote && !timelineItem.content.isEnded && isMineOrCanRedact) {
|
||||
add(TimelineItemAction.EndPoll)
|
||||
}
|
||||
if (timelineItem.content.canBeCopied()) {
|
||||
|
|
|
|||
|
|
@ -30,4 +30,8 @@ sealed interface TimelineEvents {
|
|||
data class PollEndClicked(
|
||||
val pollStartId: EventId,
|
||||
) : TimelineEvents
|
||||
|
||||
data class PollEditClicked(
|
||||
val pollStartId: EventId,
|
||||
) : TimelineEvents
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,8 +27,12 @@ import androidx.compose.runtime.mutableStateOf
|
|||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import im.vector.app.features.analytics.plan.PollEnd
|
||||
import im.vector.app.features.analytics.plan.PollVote
|
||||
import io.element.android.features.messages.impl.MessagesNavigator
|
||||
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.session.SessionState
|
||||
|
|
@ -54,16 +58,16 @@ import kotlinx.coroutines.flow.launchIn
|
|||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val BACK_PAGINATION_EVENT_LIMIT = 20
|
||||
private const val BACK_PAGINATION_PAGE_SIZE = 50
|
||||
|
||||
class TimelinePresenter @Inject constructor(
|
||||
class TimelinePresenter @AssistedInject constructor(
|
||||
private val timelineItemsFactory: TimelineItemsFactory,
|
||||
private val room: MatrixRoom,
|
||||
private val dispatchers: CoroutineDispatchers,
|
||||
private val appScope: CoroutineScope,
|
||||
@Assisted private val navigator: MessagesNavigator,
|
||||
private val analyticsService: AnalyticsService,
|
||||
private val verificationService: SessionVerificationService,
|
||||
private val encryptionService: EncryptionService,
|
||||
|
|
@ -71,6 +75,11 @@ class TimelinePresenter @Inject constructor(
|
|||
private val redactedVoiceMessageManager: RedactedVoiceMessageManager,
|
||||
) : Presenter<TimelineState> {
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(navigator: MessagesNavigator): TimelinePresenter
|
||||
}
|
||||
|
||||
private val timeline = room.timeline
|
||||
|
||||
@Composable
|
||||
|
|
@ -135,6 +144,8 @@ class TimelinePresenter @Inject constructor(
|
|||
)
|
||||
analyticsService.capture(PollEnd())
|
||||
}
|
||||
is TimelineEvents.PollEditClicked ->
|
||||
navigator.onEditPollClicked(event.pollStartId)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@ internal fun aTimelineItemEvent(
|
|||
eventId: EventId = EventId("\$" + Random.nextInt().toString()),
|
||||
transactionId: TransactionId? = null,
|
||||
isMine: Boolean = false,
|
||||
isEditable: Boolean = false,
|
||||
senderDisplayName: String = "Sender",
|
||||
content: TimelineItemEventContent = aTimelineItemTextContent(),
|
||||
groupPosition: TimelineItemGroupPosition = TimelineItemGroupPosition.None,
|
||||
|
|
@ -139,6 +140,7 @@ internal fun aTimelineItemEvent(
|
|||
readReceiptState = readReceiptState,
|
||||
sentTime = "12:34",
|
||||
isMine = isMine,
|
||||
isEditable = isEditable,
|
||||
senderDisplayName = senderDisplayName,
|
||||
groupPosition = groupPosition,
|
||||
localSendState = sendState,
|
||||
|
|
|
|||
|
|
@ -526,6 +526,7 @@ private fun MessageEventBubbleContent(
|
|||
TimelineItemEventContentView(
|
||||
content = event.content,
|
||||
isMine = event.isMine,
|
||||
isEditable = event.isEditable,
|
||||
interactionSource = interactionSource,
|
||||
onClick = onMessageClick,
|
||||
onLongClick = onMessageLongClick,
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ fun TimelineItemStateEventRow(
|
|||
TimelineItemEventContentView(
|
||||
content = event.content,
|
||||
isMine = event.isMine,
|
||||
isEditable = event.isEditable,
|
||||
interactionSource = interactionSource,
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ import io.element.android.libraries.architecture.Presenter
|
|||
fun TimelineItemEventContentView(
|
||||
content: TimelineItemEventContent,
|
||||
isMine: Boolean,
|
||||
isEditable: Boolean,
|
||||
interactionSource: MutableInteractionSource,
|
||||
extraPadding: ExtraPadding,
|
||||
onClick: () -> Unit,
|
||||
|
|
@ -103,6 +104,7 @@ fun TimelineItemEventContentView(
|
|||
is TimelineItemPollContent -> TimelineItemPollView(
|
||||
content = content,
|
||||
isMine = isMine,
|
||||
isEditable = isEditable,
|
||||
eventSink = eventSink,
|
||||
modifier = modifier,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import kotlinx.collections.immutable.toImmutableList
|
|||
fun TimelineItemPollView(
|
||||
content: TimelineItemPollContent,
|
||||
isMine: Boolean,
|
||||
isEditable: Boolean,
|
||||
eventSink: (TimelineEvents) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
|
|
@ -43,15 +44,20 @@ fun TimelineItemPollView(
|
|||
eventSink(TimelineEvents.PollEndClicked(pollStartId))
|
||||
}
|
||||
|
||||
fun onPollEdit(pollStartId: EventId) {
|
||||
eventSink(TimelineEvents.PollEditClicked(pollStartId))
|
||||
}
|
||||
|
||||
PollContentView(
|
||||
eventId = content.eventId,
|
||||
question = content.question,
|
||||
answerItems = content.answerItems.toImmutableList(),
|
||||
pollKind = content.pollKind,
|
||||
isPollEnded = content.isEnded,
|
||||
isPollEditable = isEditable,
|
||||
isMine = isMine,
|
||||
onAnswerSelected = ::onAnswerSelected,
|
||||
onPollEdit = {}, // TODO Polls: Wire up this callback once poll edit screen is done.
|
||||
onPollEdit = ::onPollEdit,
|
||||
onPollEnd = ::onPollEnd,
|
||||
modifier = modifier,
|
||||
)
|
||||
|
|
@ -64,6 +70,7 @@ internal fun TimelineItemPollViewPreview(@PreviewParameter(TimelineItemPollConte
|
|||
TimelineItemPollView(
|
||||
content = content,
|
||||
isMine = false,
|
||||
isEditable = false,
|
||||
eventSink = {},
|
||||
)
|
||||
}
|
||||
|
|
@ -75,6 +82,7 @@ internal fun TimelineItemPollCreatorViewPreview(@PreviewParameter(TimelineItemPo
|
|||
TimelineItemPollView(
|
||||
content = content,
|
||||
isMine = true,
|
||||
isEditable = false,
|
||||
eventSink = {},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ class TimelineItemEventFactory @Inject constructor(
|
|||
senderAvatar = senderAvatarData,
|
||||
content = contentFactory.create(currentTimelineItem.event),
|
||||
isMine = currentTimelineItem.event.isOwn,
|
||||
isEditable = currentTimelineItem.event.isEditable,
|
||||
sentTime = sentTime,
|
||||
groupPosition = groupPosition,
|
||||
reactionsState = currentTimelineItem.computeReactionsState(),
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ sealed interface TimelineItem {
|
|||
val content: TimelineItemEventContent,
|
||||
val sentTime: String = "",
|
||||
val isMine: Boolean = false,
|
||||
val isEditable: Boolean,
|
||||
val groupPosition: TimelineItemGroupPosition = TimelineItemGroupPosition.None,
|
||||
val reactionsState: TimelineItemReactions,
|
||||
val readReceiptState: TimelineItemReadReceipts,
|
||||
|
|
|
|||
|
|
@ -18,10 +18,6 @@ package io.element.android.features.messages.impl.timeline.model.event
|
|||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.features.location.api.Location
|
||||
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.PollKind
|
||||
|
||||
open class TimelineItemLocationContentProvider : PreviewParameterProvider<TimelineItemLocationContent> {
|
||||
override val values: Sequence<TimelineItemLocationContent>
|
||||
|
|
@ -41,31 +37,3 @@ fun aTimelineItemLocationContent(description: String? = null) = TimelineItemLoca
|
|||
description = description,
|
||||
)
|
||||
|
||||
fun aTimelineItemPollContent(
|
||||
isEnded: Boolean = false,
|
||||
) = TimelineItemPollContent(
|
||||
eventId = EventId("\$anEventId"),
|
||||
question = "Some question?",
|
||||
answerItems = listOf(
|
||||
PollAnswerItem(
|
||||
answer = PollAnswer("id_1", "Answer1"),
|
||||
isSelected = false,
|
||||
isEnabled = false,
|
||||
isWinner = false,
|
||||
isDisclosed = false,
|
||||
votesCount = 0,
|
||||
percentage = 0.0f,
|
||||
),
|
||||
PollAnswerItem(
|
||||
answer = PollAnswer("id_2", "Answer2"),
|
||||
isSelected = false,
|
||||
isEnabled = false,
|
||||
isWinner = false,
|
||||
isDisclosed = false,
|
||||
votesCount = 0,
|
||||
percentage = 0.0f,
|
||||
),
|
||||
),
|
||||
pollKind = PollKind.Disclosed,
|
||||
isEnded = isEnded,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -17,7 +17,9 @@
|
|||
package io.element.android.features.messages.impl.timeline.model.event
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.features.poll.api.PollAnswerItem
|
||||
import io.element.android.features.poll.api.aPollAnswerItemList
|
||||
import io.element.android.features.poll.api.aPollQuestion
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.poll.PollKind
|
||||
|
||||
|
|
@ -29,12 +31,16 @@ open class TimelineItemPollContentProvider : PreviewParameterProvider<TimelineIt
|
|||
)
|
||||
}
|
||||
|
||||
fun aTimelineItemPollContent(): TimelineItemPollContent {
|
||||
fun aTimelineItemPollContent(
|
||||
question: String = aPollQuestion(),
|
||||
answerItems: List<PollAnswerItem> = aPollAnswerItemList(),
|
||||
isEnded: Boolean = false,
|
||||
): TimelineItemPollContent {
|
||||
return TimelineItemPollContent(
|
||||
eventId = EventId("\$anEventId"),
|
||||
pollKind = PollKind.Disclosed,
|
||||
question = "What type of food should we have at the party?",
|
||||
answerItems = aPollAnswerItemList(),
|
||||
isEnded = false,
|
||||
question = question,
|
||||
answerItems = answerItems,
|
||||
isEnded = isEnded,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,9 @@ class FakeMessagesNavigator : MessagesNavigator {
|
|||
var onReportContentClickedCount = 0
|
||||
private set
|
||||
|
||||
var onEditPollClickedCount = 0
|
||||
private set
|
||||
|
||||
override fun onShowEventDebugInfoClicked(eventId: EventId?, debugInfo: TimelineItemDebugInfo) {
|
||||
onShowEventDebugInfoClickedCount++
|
||||
}
|
||||
|
|
@ -41,4 +44,8 @@ class FakeMessagesNavigator : MessagesNavigator {
|
|||
override fun onReportContentClicked(eventId: EventId, senderId: UserId) {
|
||||
onReportContentClickedCount++
|
||||
}
|
||||
|
||||
override fun onEditPollClicked(eventId: EventId) {
|
||||
onEditPollClickedCount++
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -327,6 +327,20 @@ class MessagesPresenterTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - handle action edit poll`() = runTest {
|
||||
val navigator = FakeMessagesNavigator()
|
||||
val presenter = createMessagesPresenter(navigator = navigator)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
skipItems(1)
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Edit, aMessageEvent(content = aTimelineItemPollContent())))
|
||||
assertThat(navigator.onEditPollClickedCount).isEqualTo(1)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - handle action redact`() = runTest {
|
||||
val coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true)
|
||||
|
|
@ -672,12 +686,18 @@ class MessagesPresenterTest {
|
|||
room = matrixRoom,
|
||||
dispatchers = coroutineDispatchers,
|
||||
appScope = this,
|
||||
navigator = navigator,
|
||||
analyticsService = analyticsService,
|
||||
encryptionService = FakeEncryptionService(),
|
||||
verificationService = FakeSessionVerificationService(),
|
||||
featureFlagService = FakeFeatureFlagService(),
|
||||
redactedVoiceMessageManager = FakeRedactedVoiceMessageManager(),
|
||||
)
|
||||
val timelinePresenterFactory = object: TimelinePresenter.Factory {
|
||||
override fun create(navigator: MessagesNavigator): TimelinePresenter {
|
||||
return timelinePresenter
|
||||
}
|
||||
}
|
||||
val preferencesStore = InMemoryPreferencesStore(isRichTextEditorEnabled = true)
|
||||
val actionListPresenter = ActionListPresenter(preferencesStore = preferencesStore)
|
||||
val readReceiptBottomSheetPresenter = ReadReceiptBottomSheetPresenter()
|
||||
|
|
@ -688,7 +708,7 @@ class MessagesPresenterTest {
|
|||
room = matrixRoom,
|
||||
composerPresenter = messageComposerPresenter,
|
||||
voiceMessageComposerPresenter = voiceMessageComposerPresenter,
|
||||
timelinePresenter = timelinePresenter,
|
||||
timelinePresenterFactory = timelinePresenterFactory,
|
||||
actionListPresenter = actionListPresenter,
|
||||
customReactionPresenter = customReactionPresenter,
|
||||
reactionSummaryPresenter = reactionSummaryPresenter,
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import io.element.android.features.messages.impl.timeline.model.event.aTimelineI
|
|||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemPollContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemStateEventContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemVoiceContent
|
||||
import io.element.android.features.poll.api.aPollAnswerItemList
|
||||
import io.element.android.libraries.featureflag.test.InMemoryPreferencesStore
|
||||
import io.element.android.libraries.matrix.test.A_MESSAGE
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
|
|
@ -407,7 +408,7 @@ class ActionListPresenterTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `present - compute for poll message`() = runTest {
|
||||
fun `present - compute for editable poll message`() = runTest {
|
||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = false)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
|
|
@ -415,7 +416,36 @@ class ActionListPresenterTest {
|
|||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(
|
||||
isMine = true,
|
||||
content = aTimelineItemPollContent(),
|
||||
isEditable = true,
|
||||
content = aTimelineItemPollContent(answerItems = aPollAnswerItemList(hasVotes = false)),
|
||||
)
|
||||
initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, canRedact = false, canSendMessage = true))
|
||||
val successState = awaitItem()
|
||||
assertThat(successState.target).isEqualTo(
|
||||
ActionListState.Target.Success(
|
||||
messageEvent,
|
||||
persistentListOf(
|
||||
TimelineItemAction.Reply,
|
||||
TimelineItemAction.Edit,
|
||||
TimelineItemAction.EndPoll,
|
||||
TimelineItemAction.Redact,
|
||||
)
|
||||
)
|
||||
)
|
||||
assertThat(successState.displayEmojiReactions).isTrue()
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun `present - compute for non-editable poll message`() = runTest {
|
||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = false)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(
|
||||
isMine = true,
|
||||
isEditable = false,
|
||||
content = aTimelineItemPollContent(answerItems = aPollAnswerItemList(hasVotes = true)),
|
||||
)
|
||||
initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, canRedact = false, canSendMessage = true))
|
||||
val successState = awaitItem()
|
||||
|
|
@ -442,6 +472,7 @@ class ActionListPresenterTest {
|
|||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(
|
||||
isMine = true,
|
||||
isEditable = false,
|
||||
content = aTimelineItemPollContent(isEnded = true),
|
||||
)
|
||||
initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, canRedact = false, canSendMessage = true))
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import kotlinx.collections.immutable.toImmutableList
|
|||
internal fun aMessageEvent(
|
||||
eventId: EventId? = AN_EVENT_ID,
|
||||
isMine: Boolean = true,
|
||||
isEditable: Boolean = true,
|
||||
content: TimelineItemEventContent = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false),
|
||||
inReplyTo: InReplyToDetails? = null,
|
||||
isThreaded: Boolean = false,
|
||||
|
|
@ -52,6 +53,7 @@ internal fun aMessageEvent(
|
|||
content = content,
|
||||
sentTime = "",
|
||||
isMine = isMine,
|
||||
isEditable = isEditable,
|
||||
reactionsState = aTimelineItemReactions(count = 0),
|
||||
readReceiptState = TimelineItemReadReceipts(emptyList<ReadReceiptData>().toImmutableList()),
|
||||
localSendState = sendState,
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ 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.impl.FakeMessagesNavigator
|
||||
import io.element.android.features.messages.impl.fixtures.aMessageEvent
|
||||
import io.element.android.features.messages.impl.fixtures.aTimelineItemsFactory
|
||||
import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactory
|
||||
|
|
@ -223,10 +224,10 @@ class TimelinePresenterTest {
|
|||
assertThat(initialState.hasNewItems).isFalse()
|
||||
assertThat(initialState.timelineItems.size).isEqualTo(0)
|
||||
val now = Date().time
|
||||
val minuteInMilis = 60 * 1000
|
||||
val minuteInMillis = 60 * 1000
|
||||
// Use index as a convenient value for timestamp
|
||||
val (alice, bob, charlie) = aMatrixUserList().take(3).mapIndexed { i, user ->
|
||||
ReactionSender(senderId = user.userId, timestamp = now + i * minuteInMilis)
|
||||
ReactionSender(senderId = user.userId, timestamp = now + i * minuteInMillis)
|
||||
}
|
||||
val oneReaction = listOf(
|
||||
EventReaction(
|
||||
|
|
@ -312,6 +313,20 @@ class TimelinePresenterTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - PollEditClicked event navigates`() = runTest {
|
||||
val navigator = FakeMessagesNavigator()
|
||||
val presenter = createTimelinePresenter(
|
||||
messagesNavigator = navigator,
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
awaitItem().eventSink(TimelineEvents.PollEditClicked(AN_EVENT_ID))
|
||||
assertThat(navigator.onEditPollClickedCount).isEqualTo(1)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - side effect on redacted items is invoked`() = runTest {
|
||||
val redactedVoiceMessageManager = FakeRedactedVoiceMessageManager()
|
||||
|
|
@ -337,12 +352,14 @@ class TimelinePresenterTest {
|
|||
timeline: MatrixTimeline = FakeMatrixTimeline(),
|
||||
timelineItemsFactory: TimelineItemsFactory = aTimelineItemsFactory(),
|
||||
redactedVoiceMessageManager: RedactedVoiceMessageManager = FakeRedactedVoiceMessageManager(),
|
||||
messagesNavigator: FakeMessagesNavigator = FakeMessagesNavigator(),
|
||||
): TimelinePresenter {
|
||||
return TimelinePresenter(
|
||||
timelineItemsFactory = timelineItemsFactory,
|
||||
room = FakeMatrixRoom(matrixTimeline = timeline),
|
||||
dispatchers = testCoroutineDispatchers(),
|
||||
appScope = this,
|
||||
navigator = messagesNavigator,
|
||||
analyticsService = FakeAnalyticsService(),
|
||||
encryptionService = FakeEncryptionService(),
|
||||
verificationService = FakeSessionVerificationService(),
|
||||
|
|
@ -360,6 +377,7 @@ class TimelinePresenterTest {
|
|||
room = room,
|
||||
dispatchers = testCoroutineDispatchers(),
|
||||
appScope = this,
|
||||
navigator = FakeMessagesNavigator(),
|
||||
analyticsService = analyticsService,
|
||||
encryptionService = FakeEncryptionService(),
|
||||
verificationService = FakeSessionVerificationService(),
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ class TimelineItemGrouperTest {
|
|||
reactionsState = aTimelineItemReactions(count = 0),
|
||||
readReceiptState = TimelineItemReadReceipts(emptyList<ReadReceiptData>().toImmutableList()),
|
||||
localSendState = LocalEventSendState.Sent(AN_EVENT_ID),
|
||||
isEditable = false,
|
||||
inReplyTo = null,
|
||||
isThreaded = false,
|
||||
debugInfo = aTimelineItemDebugInfo(),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue