Merge pull request #6092 from element-hq/feature/bma/endPollConfirmation
Display a confirmation dialog when ending a poll from the event bottom sheet
This commit is contained in:
@@ -13,13 +13,12 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
|
||||
sealed interface MessagesEvents {
|
||||
data class HandleAction(val action: TimelineItemAction, val event: TimelineItem.Event) : MessagesEvents
|
||||
data class ToggleReaction(val emoji: String, val eventOrTransactionId: EventOrTransactionId) : MessagesEvents
|
||||
data class InviteDialogDismissed(val action: InviteDialogAction) : MessagesEvents
|
||||
data class OnUserClicked(val user: MatrixUser) : MessagesEvents
|
||||
data object Dismiss : MessagesEvents
|
||||
data object MarkAsFullyReadAndExit : MessagesEvents
|
||||
sealed interface MessagesEvent {
|
||||
data class HandleAction(val action: TimelineItemAction, val event: TimelineItem.Event) : MessagesEvent
|
||||
data class ToggleReaction(val emoji: String, val eventOrTransactionId: EventOrTransactionId) : MessagesEvent
|
||||
data class InviteDialogDismissed(val action: InviteDialogAction) : MessagesEvent
|
||||
data class OnUserClicked(val user: MatrixUser) : MessagesEvent
|
||||
data object MarkAsFullyReadAndExit : MessagesEvent
|
||||
}
|
||||
|
||||
enum class InviteDialogAction {
|
||||
@@ -36,7 +36,7 @@ import io.element.android.features.messages.impl.attachments.Attachment
|
||||
import io.element.android.features.messages.impl.messagecomposer.MessageComposerEvent
|
||||
import io.element.android.features.messages.impl.messagecomposer.MessageComposerPresenter
|
||||
import io.element.android.features.messages.impl.timeline.TimelineController
|
||||
import io.element.android.features.messages.impl.timeline.TimelineEvents
|
||||
import io.element.android.features.messages.impl.timeline.TimelineEvent
|
||||
import io.element.android.features.messages.impl.timeline.TimelinePresenter
|
||||
import io.element.android.features.messages.impl.timeline.di.LocalTimelineItemPresenterFactories
|
||||
import io.element.android.features.messages.impl.timeline.di.TimelineItemPresenterFactories
|
||||
@@ -151,7 +151,7 @@ class MessagesNode(
|
||||
activity: Activity,
|
||||
darkTheme: Boolean,
|
||||
url: String,
|
||||
eventSink: (TimelineEvents) -> Unit,
|
||||
eventSink: (TimelineEvent) -> Unit,
|
||||
customTab: Boolean
|
||||
) {
|
||||
when (val permalink = permalinkParser.parse(url)) {
|
||||
@@ -178,12 +178,12 @@ class MessagesNode(
|
||||
|
||||
private fun handleRoomLinkClick(
|
||||
roomLink: PermalinkData.RoomLink,
|
||||
eventSink: (TimelineEvents) -> Unit,
|
||||
eventSink: (TimelineEvent) -> Unit,
|
||||
) {
|
||||
if (room.matches(roomLink.roomIdOrAlias)) {
|
||||
val eventId = roomLink.eventId
|
||||
if (eventId != null) {
|
||||
eventSink(TimelineEvents.FocusOnEvent(eventId))
|
||||
eventSink(TimelineEvent.FocusOnEvent(eventId))
|
||||
} else {
|
||||
// Click on the same room, ignore
|
||||
displaySameRoomToast()
|
||||
@@ -242,7 +242,7 @@ class MessagesNode(
|
||||
val state = presenter.present()
|
||||
|
||||
BackHandler {
|
||||
state.eventSink(MessagesEvents.MarkAsFullyReadAndExit)
|
||||
state.eventSink(MessagesEvent.MarkAsFullyReadAndExit)
|
||||
}
|
||||
|
||||
OnLifecycleEvent { _, event ->
|
||||
@@ -253,7 +253,7 @@ class MessagesNode(
|
||||
}
|
||||
MessagesView(
|
||||
state = state,
|
||||
onBackClick = { state.eventSink(MessagesEvents.MarkAsFullyReadAndExit) },
|
||||
onBackClick = { state.eventSink(MessagesEvent.MarkAsFullyReadAndExit) },
|
||||
onRoomDetailsClick = callback::navigateToRoomDetails,
|
||||
onEventContentClick = { isLive, event ->
|
||||
if (isLive) {
|
||||
@@ -305,7 +305,7 @@ class MessagesNode(
|
||||
}
|
||||
LaunchedEffect(focusedEventId) {
|
||||
if (focusedEventId != null) {
|
||||
state.timelineState.eventSink(TimelineEvents.FocusOnEvent(focusedEventId!!))
|
||||
state.timelineState.eventSink(TimelineEvent.FocusOnEvent(focusedEventId!!))
|
||||
focusedEventId = null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ import dev.zacsweers.metro.AssistedInject
|
||||
import im.vector.app.features.analytics.plan.PinUnpinAction
|
||||
import io.element.android.appconfig.MessageComposerConfig
|
||||
import io.element.android.features.messages.api.timeline.HtmlConverterProvider
|
||||
import io.element.android.features.messages.impl.actionlist.ActionListEvents
|
||||
import io.element.android.features.messages.impl.actionlist.ActionListState
|
||||
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
|
||||
import io.element.android.features.messages.impl.crypto.historyvisible.HistoryVisibleState
|
||||
@@ -38,7 +37,7 @@ import io.element.android.features.messages.impl.messagecomposer.MessageComposer
|
||||
import io.element.android.features.messages.impl.pinned.banner.PinnedMessagesBannerState
|
||||
import io.element.android.features.messages.impl.timeline.MarkAsFullyRead
|
||||
import io.element.android.features.messages.impl.timeline.TimelineController
|
||||
import io.element.android.features.messages.impl.timeline.TimelineEvents
|
||||
import io.element.android.features.messages.impl.timeline.TimelineEvent
|
||||
import io.element.android.features.messages.impl.timeline.TimelineState
|
||||
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionState
|
||||
import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryState
|
||||
@@ -221,9 +220,9 @@ class MessagesPresenter(
|
||||
onPauseOrDispose {}
|
||||
}
|
||||
|
||||
fun handleEvent(event: MessagesEvents) {
|
||||
fun handleEvent(event: MessagesEvent) {
|
||||
when (event) {
|
||||
is MessagesEvents.HandleAction -> {
|
||||
is MessagesEvent.HandleAction -> {
|
||||
localCoroutineScope.handleTimelineAction(
|
||||
action = event.action,
|
||||
targetEvent = event.event,
|
||||
@@ -233,21 +232,20 @@ class MessagesPresenter(
|
||||
timelineProtectionState = timelineProtectionState,
|
||||
)
|
||||
}
|
||||
is MessagesEvents.ToggleReaction -> {
|
||||
is MessagesEvent.ToggleReaction -> {
|
||||
localCoroutineScope.toggleReaction(event.emoji, event.eventOrTransactionId)
|
||||
}
|
||||
is MessagesEvents.InviteDialogDismissed -> {
|
||||
is MessagesEvent.InviteDialogDismissed -> {
|
||||
hasDismissedInviteDialog = true
|
||||
|
||||
if (event.action == InviteDialogAction.Invite) {
|
||||
localCoroutineScope.reinviteOtherUser(inviteProgress)
|
||||
}
|
||||
}
|
||||
is MessagesEvents.Dismiss -> actionListState.eventSink(ActionListEvents.Clear)
|
||||
is MessagesEvents.OnUserClicked -> {
|
||||
is MessagesEvent.OnUserClicked -> {
|
||||
roomMemberModerationState.eventSink(RoomMemberModerationEvents.ShowActionsForUser(event.user))
|
||||
}
|
||||
is MessagesEvents.MarkAsFullyReadAndExit -> coroutineScope.launch {
|
||||
is MessagesEvent.MarkAsFullyReadAndExit -> coroutineScope.launch {
|
||||
if (!markingAsReadAndExiting.getAndSet(true)) {
|
||||
val latestEventId = room.liveTimeline.getLatestEventId().getOrElse {
|
||||
Timber.w(it, "Failed to get latest event id to mark as fully read")
|
||||
@@ -529,7 +527,7 @@ class MessagesPresenter(
|
||||
event: TimelineItem.Event,
|
||||
timelineState: TimelineState,
|
||||
) {
|
||||
event.eventId?.let { timelineState.eventSink(TimelineEvents.EndPoll(it)) }
|
||||
event.eventId?.let { timelineState.eventSink(TimelineEvent.EndPoll(it)) }
|
||||
}
|
||||
|
||||
private suspend fun handleCopyLink(event: TimelineItem.Event) {
|
||||
|
||||
@@ -57,7 +57,7 @@ data class MessagesState(
|
||||
val dmUserVerificationState: IdentityState?,
|
||||
val roomMemberModerationState: RoomMemberModerationState,
|
||||
val successorRoom: SuccessorRoom?,
|
||||
val eventSink: (MessagesEvents) -> Unit
|
||||
val eventSink: (MessagesEvent) -> Unit
|
||||
) {
|
||||
val isTombstoned = successorRoom != null
|
||||
}
|
||||
|
||||
@@ -28,11 +28,11 @@ import io.element.android.features.messages.impl.pinned.banner.aLoadedPinnedMess
|
||||
import io.element.android.features.messages.impl.timeline.TimelineState
|
||||
import io.element.android.features.messages.impl.timeline.aTimelineItemList
|
||||
import io.element.android.features.messages.impl.timeline.aTimelineState
|
||||
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionEvents
|
||||
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionEvent
|
||||
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionState
|
||||
import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryEvents
|
||||
import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryEvent
|
||||
import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryState
|
||||
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetEvents
|
||||
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetEvent
|
||||
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetState
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent
|
||||
@@ -133,7 +133,7 @@ fun aMessagesState(
|
||||
dmUserVerificationState: IdentityState? = null,
|
||||
roomMemberModerationState: RoomMemberModerationState = aRoomMemberModerationState(),
|
||||
successorRoom: SuccessorRoom? = null,
|
||||
eventSink: (MessagesEvents) -> Unit = {},
|
||||
eventSink: (MessagesEvent) -> Unit = {},
|
||||
) = MessagesState(
|
||||
roomId = RoomId("!id:domain"),
|
||||
roomName = roomName,
|
||||
@@ -187,7 +187,7 @@ fun aUserEventPermissions(
|
||||
|
||||
fun aReactionSummaryState(
|
||||
target: ReactionSummaryState.Summary? = null,
|
||||
eventSink: (ReactionSummaryEvents) -> Unit = {}
|
||||
eventSink: (ReactionSummaryEvent) -> Unit = {}
|
||||
) = ReactionSummaryState(
|
||||
target = target,
|
||||
eventSink = eventSink,
|
||||
@@ -196,7 +196,7 @@ fun aReactionSummaryState(
|
||||
fun aCustomReactionState(
|
||||
target: CustomReactionState.Target = CustomReactionState.Target.None,
|
||||
recentEmojis: ImmutableList<String> = persistentListOf(),
|
||||
eventSink: (CustomReactionEvents) -> Unit = {},
|
||||
eventSink: (CustomReactionEvent) -> Unit = {},
|
||||
) = CustomReactionState(
|
||||
target = target,
|
||||
recentEmojis = recentEmojis,
|
||||
@@ -206,7 +206,7 @@ fun aCustomReactionState(
|
||||
|
||||
fun aReadReceiptBottomSheetState(
|
||||
selectedEvent: TimelineItem.Event? = null,
|
||||
eventSink: (ReadReceiptBottomSheetEvents) -> Unit = {},
|
||||
eventSink: (ReadReceiptBottomSheetEvent) -> Unit = {},
|
||||
) = ReadReceiptBottomSheetState(
|
||||
selectedEvent = selectedEvent,
|
||||
eventSink = eventSink,
|
||||
|
||||
@@ -31,6 +31,7 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
@@ -51,12 +52,12 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.features.messages.api.timeline.voicemessages.composer.VoiceMessageComposerEvent
|
||||
import io.element.android.features.messages.impl.actionlist.ActionListEvents
|
||||
import io.element.android.features.messages.impl.actionlist.ActionListEvent
|
||||
import io.element.android.features.messages.impl.actionlist.ActionListView
|
||||
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
|
||||
import io.element.android.features.messages.impl.crypto.historyvisible.HistoryVisibleStateView
|
||||
import io.element.android.features.messages.impl.crypto.identity.IdentityChangeStateView
|
||||
import io.element.android.features.messages.impl.link.LinkEvents
|
||||
import io.element.android.features.messages.impl.link.LinkEvent
|
||||
import io.element.android.features.messages.impl.link.LinkView
|
||||
import io.element.android.features.messages.impl.messagecomposer.AttachmentsBottomSheet
|
||||
import io.element.android.features.messages.impl.messagecomposer.DisabledComposerView
|
||||
@@ -67,18 +68,18 @@ import io.element.android.features.messages.impl.pinned.banner.PinnedMessagesBan
|
||||
import io.element.android.features.messages.impl.pinned.banner.PinnedMessagesBannerView
|
||||
import io.element.android.features.messages.impl.pinned.banner.PinnedMessagesBannerViewDefaults
|
||||
import io.element.android.features.messages.impl.timeline.FOCUS_ON_PINNED_EVENT_DEBOUNCE_DURATION_IN_MILLIS
|
||||
import io.element.android.features.messages.impl.timeline.TimelineEvents
|
||||
import io.element.android.features.messages.impl.timeline.TimelineEvent
|
||||
import io.element.android.features.messages.impl.timeline.TimelineView
|
||||
import io.element.android.features.messages.impl.timeline.aGroupedEvents
|
||||
import io.element.android.features.messages.impl.timeline.aTimelineItemDaySeparator
|
||||
import io.element.android.features.messages.impl.timeline.aTimelineItemEvent
|
||||
import io.element.android.features.messages.impl.timeline.aTimelineState
|
||||
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionBottomSheet
|
||||
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionEvents
|
||||
import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryEvents
|
||||
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionEvent
|
||||
import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryEvent
|
||||
import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryView
|
||||
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheet
|
||||
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetEvents
|
||||
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetEvent
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition
|
||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemStateEventContent
|
||||
@@ -168,7 +169,7 @@ fun MessagesView(
|
||||
Timber.v("OnMessageLongClicked= ${event.id}")
|
||||
hidingKeyboard {
|
||||
state.actionListState.eventSink(
|
||||
ActionListEvents.ComputeForMessage(
|
||||
ActionListEvent.ComputeForMessage(
|
||||
event = event,
|
||||
userEventPermissions = state.userEventPermissions,
|
||||
)
|
||||
@@ -177,20 +178,20 @@ fun MessagesView(
|
||||
}
|
||||
|
||||
fun onActionSelected(action: TimelineItemAction, event: TimelineItem.Event) {
|
||||
state.eventSink(MessagesEvents.HandleAction(action, event))
|
||||
state.eventSink(MessagesEvent.HandleAction(action, event))
|
||||
}
|
||||
|
||||
fun onEmojiReactionClick(emoji: String, event: TimelineItem.Event) {
|
||||
state.eventSink(MessagesEvents.ToggleReaction(emoji, event.eventOrTransactionId))
|
||||
state.eventSink(MessagesEvent.ToggleReaction(emoji, event.eventOrTransactionId))
|
||||
}
|
||||
|
||||
fun onEmojiReactionLongClick(emoji: String, event: TimelineItem.Event) {
|
||||
if (event.eventId == null) return
|
||||
state.reactionSummaryState.eventSink(ReactionSummaryEvents.ShowReactionSummary(event.eventId, event.reactionsState.reactions, emoji))
|
||||
state.reactionSummaryState.eventSink(ReactionSummaryEvent.ShowReactionSummary(event.eventId, event.reactionsState.reactions, emoji))
|
||||
}
|
||||
|
||||
fun onMoreReactionsClick(event: TimelineItem.Event) {
|
||||
state.customReactionState.eventSink(CustomReactionEvents.ShowCustomReactionSheet(event))
|
||||
state.customReactionState.eventSink(CustomReactionEvent.ShowCustomReactionSheet(event))
|
||||
}
|
||||
|
||||
val expandableState = rememberExpandableBottomSheetLayoutState()
|
||||
@@ -243,7 +244,7 @@ fun MessagesView(
|
||||
onMessageLongClick = ::onMessageLongClick,
|
||||
onUserDataClick = {
|
||||
hidingKeyboard {
|
||||
state.eventSink(MessagesEvents.OnUserClicked(it))
|
||||
state.eventSink(MessagesEvent.OnUserClicked(it))
|
||||
}
|
||||
},
|
||||
onLinkClick = { link, customTab ->
|
||||
@@ -251,19 +252,19 @@ fun MessagesView(
|
||||
onLinkClick(link.url, true)
|
||||
// Do not check those links, they are internal link only
|
||||
} else {
|
||||
state.linkState.eventSink(LinkEvents.OnLinkClick(link))
|
||||
state.linkState.eventSink(LinkEvent.OnLinkClick(link))
|
||||
}
|
||||
},
|
||||
onReactionClick = ::onEmojiReactionClick,
|
||||
onReactionLongClick = ::onEmojiReactionLongClick,
|
||||
onMoreReactionsClick = ::onMoreReactionsClick,
|
||||
onReadReceiptClick = { event ->
|
||||
state.readReceiptBottomSheetState.eventSink(ReadReceiptBottomSheetEvents.EventSelected(event))
|
||||
state.readReceiptBottomSheetState.eventSink(ReadReceiptBottomSheetEvent.EventSelected(event))
|
||||
},
|
||||
onSendLocationClick = onSendLocationClick,
|
||||
onCreatePollClick = onCreatePollClick,
|
||||
onSwipeToReply = { targetEvent ->
|
||||
state.eventSink(MessagesEvents.HandleAction(TimelineItemAction.Reply, targetEvent))
|
||||
state.eventSink(MessagesEvent.HandleAction(TimelineItemAction.Reply, targetEvent))
|
||||
},
|
||||
forceJumpToBottomVisibility = forceJumpToBottomVisibility,
|
||||
onJoinCallClick = onJoinCallClick,
|
||||
@@ -300,7 +301,7 @@ fun MessagesView(
|
||||
state = state,
|
||||
onLinkClick = { url, customTab -> onLinkClick(url, customTab) },
|
||||
onRoomSuccessorClick = { roomId ->
|
||||
state.timelineState.eventSink(TimelineEvents.NavigateToPredecessorOrSuccessorRoom(roomId = roomId))
|
||||
state.timelineState.eventSink(TimelineEvent.NavigateToPredecessorOrSuccessorRoom(roomId = roomId))
|
||||
},
|
||||
)
|
||||
},
|
||||
@@ -341,22 +342,43 @@ fun MessagesView(
|
||||
maxBottomSheetContentHeight = maxComposerHeightPx.toDp(),
|
||||
)
|
||||
|
||||
var endPollConfirmingEvent: TimelineItem.Event? by remember { mutableStateOf(null) }
|
||||
|
||||
if (endPollConfirmingEvent != null) {
|
||||
ConfirmationDialog(
|
||||
content = stringResource(id = CommonStrings.common_poll_end_confirmation),
|
||||
onSubmitClick = {
|
||||
endPollConfirmingEvent?.let { event ->
|
||||
onActionSelected(TimelineItemAction.EndPoll, event)
|
||||
}
|
||||
endPollConfirmingEvent = null
|
||||
},
|
||||
onDismiss = { endPollConfirmingEvent = null },
|
||||
)
|
||||
}
|
||||
|
||||
ActionListView(
|
||||
state = state.actionListState,
|
||||
onSelectAction = ::onActionSelected,
|
||||
onSelectAction = { action: TimelineItemAction, event: TimelineItem.Event ->
|
||||
if (action == TimelineItemAction.EndPoll) {
|
||||
endPollConfirmingEvent = event
|
||||
} else {
|
||||
onActionSelected(action, event)
|
||||
}
|
||||
},
|
||||
onCustomReactionClick = { event ->
|
||||
state.customReactionState.eventSink(CustomReactionEvents.ShowCustomReactionSheet(event))
|
||||
state.customReactionState.eventSink(CustomReactionEvent.ShowCustomReactionSheet(event))
|
||||
},
|
||||
onEmojiReactionClick = ::onEmojiReactionClick,
|
||||
onVerifiedUserSendFailureClick = { event ->
|
||||
state.timelineState.eventSink(TimelineEvents.ComputeVerifiedUserSendFailure(event))
|
||||
state.timelineState.eventSink(TimelineEvent.ComputeVerifiedUserSendFailure(event))
|
||||
},
|
||||
)
|
||||
|
||||
CustomReactionBottomSheet(
|
||||
state = state.customReactionState,
|
||||
onSelectEmoji = { uniqueId, emoji ->
|
||||
state.eventSink(MessagesEvents.ToggleReaction(emoji.unicode, uniqueId))
|
||||
state.eventSink(MessagesEvent.ToggleReaction(emoji.unicode, uniqueId))
|
||||
}
|
||||
)
|
||||
|
||||
@@ -382,8 +404,8 @@ private fun ReinviteDialog(state: MessagesState) {
|
||||
content = stringResource(id = R.string.screen_room_invite_again_alert_message),
|
||||
cancelText = stringResource(id = CommonStrings.action_cancel),
|
||||
submitText = stringResource(id = CommonStrings.action_invite),
|
||||
onSubmitClick = { state.eventSink(MessagesEvents.InviteDialogDismissed(InviteDialogAction.Invite)) },
|
||||
onDismiss = { state.eventSink(MessagesEvents.InviteDialogDismissed(InviteDialogAction.Cancel)) }
|
||||
onSubmitClick = { state.eventSink(MessagesEvent.InviteDialogDismissed(InviteDialogAction.Invite)) },
|
||||
onDismiss = { state.eventSink(MessagesEvent.InviteDialogDismissed(InviteDialogAction.Cancel)) }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -467,7 +489,7 @@ private fun MessagesViewContent(
|
||||
) {
|
||||
fun focusOnPinnedEvent(eventId: EventId) {
|
||||
state.timelineState.eventSink(
|
||||
TimelineEvents.FocusOnEvent(eventId = eventId, debounce = FOCUS_ON_PINNED_EVENT_DEBOUNCE_DURATION_IN_MILLIS.milliseconds)
|
||||
TimelineEvent.FocusOnEvent(eventId = eventId, debounce = FOCUS_ON_PINNED_EVENT_DEBOUNCE_DURATION_IN_MILLIS.milliseconds)
|
||||
)
|
||||
}
|
||||
PinnedMessagesBannerView(
|
||||
|
||||
@@ -11,10 +11,10 @@ package io.element.android.features.messages.impl.actionlist
|
||||
import io.element.android.features.messages.impl.UserEventPermissions
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
|
||||
sealed interface ActionListEvents {
|
||||
data object Clear : ActionListEvents
|
||||
sealed interface ActionListEvent {
|
||||
data object Clear : ActionListEvent
|
||||
data class ComputeForMessage(
|
||||
val event: TimelineItem.Event,
|
||||
val userEventPermissions: UserEventPermissions,
|
||||
) : ActionListEvents
|
||||
) : ActionListEvent
|
||||
}
|
||||
@@ -107,10 +107,10 @@ class DefaultActionListPresenter(
|
||||
|
||||
val isThreadsEnabled = featureFlagService.isFeatureEnabledFlow(FeatureFlags.Threads).collectAsState(false)
|
||||
|
||||
fun handleEvent(event: ActionListEvents) {
|
||||
fun handleEvent(event: ActionListEvent) {
|
||||
when (event) {
|
||||
ActionListEvents.Clear -> target.value = ActionListState.Target.None
|
||||
is ActionListEvents.ComputeForMessage -> localCoroutineScope.computeForMessage(
|
||||
ActionListEvent.Clear -> target.value = ActionListState.Target.None
|
||||
is ActionListEvent.ComputeForMessage -> localCoroutineScope.computeForMessage(
|
||||
timelineItem = event.event,
|
||||
usersEventPermissions = event.userEventPermissions,
|
||||
isDeveloperModeEnabled = isDeveloperModeEnabled,
|
||||
|
||||
@@ -16,7 +16,7 @@ import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
data class ActionListState(
|
||||
val target: Target,
|
||||
val eventSink: (ActionListEvents) -> Unit,
|
||||
val eventSink: (ActionListEvent) -> Unit,
|
||||
) {
|
||||
@Immutable
|
||||
sealed interface Target {
|
||||
|
||||
@@ -192,7 +192,7 @@ open class ActionListStateProvider : PreviewParameterProvider<ActionListState> {
|
||||
|
||||
fun anActionListState(
|
||||
target: ActionListState.Target = ActionListState.Target.None,
|
||||
eventSink: (ActionListEvents) -> Unit = {},
|
||||
eventSink: (ActionListEvent) -> Unit = {},
|
||||
) = ActionListState(
|
||||
target = target,
|
||||
eventSink = eventSink
|
||||
|
||||
@@ -118,7 +118,7 @@ fun ActionListView(
|
||||
) {
|
||||
if (targetItem == null) return
|
||||
sheetState.hide(coroutineScope) {
|
||||
state.eventSink(ActionListEvents.Clear)
|
||||
state.eventSink(ActionListEvent.Clear)
|
||||
onSelectAction(itemAction, targetItem)
|
||||
}
|
||||
}
|
||||
@@ -126,7 +126,7 @@ fun ActionListView(
|
||||
fun onEmojiReactionClick(emoji: String) {
|
||||
if (targetItem == null) return
|
||||
sheetState.hide(coroutineScope) {
|
||||
state.eventSink(ActionListEvents.Clear)
|
||||
state.eventSink(ActionListEvent.Clear)
|
||||
onEmojiReactionClick(emoji, targetItem)
|
||||
}
|
||||
}
|
||||
@@ -134,19 +134,19 @@ fun ActionListView(
|
||||
fun onCustomReactionClick() {
|
||||
if (targetItem == null) return
|
||||
sheetState.hide(coroutineScope) {
|
||||
state.eventSink(ActionListEvents.Clear)
|
||||
state.eventSink(ActionListEvent.Clear)
|
||||
onCustomReactionClick(targetItem)
|
||||
}
|
||||
}
|
||||
|
||||
fun onDismiss() {
|
||||
state.eventSink(ActionListEvents.Clear)
|
||||
state.eventSink(ActionListEvent.Clear)
|
||||
}
|
||||
|
||||
fun onVerifiedUserSendFailureClick() {
|
||||
if (targetItem == null) return
|
||||
sheetState.hide(coroutineScope) {
|
||||
state.eventSink(ActionListEvents.Clear)
|
||||
state.eventSink(ActionListEvent.Clear)
|
||||
onVerifiedUserSendFailureClick(targetItem)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
|
||||
package io.element.android.features.messages.impl.attachments.preview
|
||||
|
||||
sealed interface AttachmentsPreviewEvents {
|
||||
data object SendAttachment : AttachmentsPreviewEvents
|
||||
data object CancelAndDismiss : AttachmentsPreviewEvents
|
||||
data object CancelAndClearSendState : AttachmentsPreviewEvents
|
||||
sealed interface AttachmentsPreviewEvent {
|
||||
data object SendAttachment : AttachmentsPreviewEvent
|
||||
data object CancelAndDismiss : AttachmentsPreviewEvent
|
||||
data object CancelAndClearSendState : AttachmentsPreviewEvent
|
||||
}
|
||||
@@ -140,9 +140,9 @@ class AttachmentsPreviewPresenter(
|
||||
}
|
||||
}
|
||||
|
||||
fun handleEvent(event: AttachmentsPreviewEvents) {
|
||||
fun handleEvent(event: AttachmentsPreviewEvent) {
|
||||
when (event) {
|
||||
is AttachmentsPreviewEvents.SendAttachment -> {
|
||||
is AttachmentsPreviewEvent.SendAttachment -> {
|
||||
ongoingSendAttachmentJob.value = coroutineScope.launch {
|
||||
// If the media optimization selector is displayed, we need to wait for the user to select the options
|
||||
// before we can pre-process the media.
|
||||
@@ -191,7 +191,7 @@ class AttachmentsPreviewPresenter(
|
||||
}
|
||||
}
|
||||
}
|
||||
AttachmentsPreviewEvents.CancelAndDismiss -> {
|
||||
AttachmentsPreviewEvent.CancelAndDismiss -> {
|
||||
displayFileTooLargeError = false
|
||||
|
||||
// Cancel media preprocessing and sending
|
||||
@@ -206,7 +206,7 @@ class AttachmentsPreviewPresenter(
|
||||
sendActionState,
|
||||
)
|
||||
}
|
||||
AttachmentsPreviewEvents.CancelAndClearSendState -> {
|
||||
AttachmentsPreviewEvent.CancelAndClearSendState -> {
|
||||
// Cancel media sending
|
||||
ongoingSendAttachmentJob.value?.let {
|
||||
it.cancel()
|
||||
|
||||
@@ -20,7 +20,7 @@ data class AttachmentsPreviewState(
|
||||
val textEditorState: TextEditorState,
|
||||
val mediaOptimizationSelectorState: MediaOptimizationSelectorState,
|
||||
val displayFileTooLargeError: Boolean,
|
||||
val eventSink: (AttachmentsPreviewEvents) -> Unit
|
||||
val eventSink: (AttachmentsPreviewEvent) -> Unit,
|
||||
)
|
||||
|
||||
@Immutable
|
||||
|
||||
@@ -82,15 +82,15 @@ fun AttachmentsPreviewView(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
fun postSendAttachment() {
|
||||
state.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
||||
state.eventSink(AttachmentsPreviewEvent.SendAttachment)
|
||||
}
|
||||
|
||||
fun postCancel() {
|
||||
state.eventSink(AttachmentsPreviewEvents.CancelAndDismiss)
|
||||
state.eventSink(AttachmentsPreviewEvent.CancelAndDismiss)
|
||||
}
|
||||
|
||||
fun postClearSendState() {
|
||||
state.eventSink(AttachmentsPreviewEvents.CancelAndClearSendState)
|
||||
state.eventSink(AttachmentsPreviewEvent.CancelAndClearSendState)
|
||||
}
|
||||
|
||||
BackHandler(enabled = state.sendActionState !is SendActionState.Sending.Uploading && state.sendActionState !is SendActionState.Done) {
|
||||
@@ -199,7 +199,7 @@ private fun AttachmentPreviewContent(
|
||||
AlertDialog(
|
||||
title = stringResource(CommonStrings.dialog_file_too_large_to_upload_title),
|
||||
content = content,
|
||||
onDismiss = { state.eventSink(AttachmentsPreviewEvents.CancelAndDismiss) },
|
||||
onDismiss = { state.eventSink(AttachmentsPreviewEvent.CancelAndDismiss) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,12 +10,12 @@ package io.element.android.features.messages.impl.crypto.sendfailure.resolve
|
||||
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
|
||||
sealed interface ResolveVerifiedUserSendFailureEvents {
|
||||
sealed interface ResolveVerifiedUserSendFailureEvent {
|
||||
data class ComputeForMessage(
|
||||
val messageEvent: TimelineItem.Event,
|
||||
) : ResolveVerifiedUserSendFailureEvents
|
||||
) : ResolveVerifiedUserSendFailureEvent
|
||||
|
||||
data object ResolveAndResend : ResolveVerifiedUserSendFailureEvents
|
||||
data object Retry : ResolveVerifiedUserSendFailureEvents
|
||||
data object Dismiss : ResolveVerifiedUserSendFailureEvents
|
||||
data object ResolveAndResend : ResolveVerifiedUserSendFailureEvent
|
||||
data object Retry : ResolveVerifiedUserSendFailureEvent
|
||||
data object Dismiss : ResolveVerifiedUserSendFailureEvent
|
||||
}
|
||||
@@ -48,9 +48,9 @@ class ResolveVerifiedUserSendFailurePresenter(
|
||||
}
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
fun handleEvent(event: ResolveVerifiedUserSendFailureEvents) {
|
||||
fun handleEvent(event: ResolveVerifiedUserSendFailureEvent) {
|
||||
when (event) {
|
||||
is ResolveVerifiedUserSendFailureEvents.ComputeForMessage -> {
|
||||
is ResolveVerifiedUserSendFailureEvent.ComputeForMessage -> {
|
||||
val sendState = event.messageEvent.localSendState as? LocalEventSendState.Failed.VerifiedUser
|
||||
val transactionId = event.messageEvent.transactionId
|
||||
val sendHandle = event.messageEvent.sendhandle
|
||||
@@ -65,10 +65,10 @@ class ResolveVerifiedUserSendFailurePresenter(
|
||||
null
|
||||
}
|
||||
}
|
||||
ResolveVerifiedUserSendFailureEvents.Dismiss -> {
|
||||
ResolveVerifiedUserSendFailureEvent.Dismiss -> {
|
||||
resolver = null
|
||||
}
|
||||
ResolveVerifiedUserSendFailureEvents.Retry -> {
|
||||
ResolveVerifiedUserSendFailureEvent.Retry -> {
|
||||
coroutineScope.launch {
|
||||
resolver?.run {
|
||||
runUpdatingState(retryAction) {
|
||||
@@ -77,7 +77,7 @@ class ResolveVerifiedUserSendFailurePresenter(
|
||||
}
|
||||
}
|
||||
}
|
||||
ResolveVerifiedUserSendFailureEvents.ResolveAndResend -> {
|
||||
ResolveVerifiedUserSendFailureEvent.ResolveAndResend -> {
|
||||
coroutineScope.launch {
|
||||
resolver?.run {
|
||||
runUpdatingState(resolveAction) {
|
||||
|
||||
@@ -15,5 +15,5 @@ data class ResolveVerifiedUserSendFailureState(
|
||||
val verifiedUserSendFailure: VerifiedUserSendFailure,
|
||||
val resolveAction: AsyncAction<Unit>,
|
||||
val retryAction: AsyncAction<Unit>,
|
||||
val eventSink: (ResolveVerifiedUserSendFailureEvents) -> Unit
|
||||
val eventSink: (ResolveVerifiedUserSendFailureEvent) -> Unit
|
||||
)
|
||||
|
||||
@@ -29,7 +29,7 @@ fun aResolveVerifiedUserSendFailureState(
|
||||
verifiedUserSendFailure: VerifiedUserSendFailure = VerifiedUserSendFailure.None,
|
||||
resolveAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
|
||||
retryAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
|
||||
eventSink: (ResolveVerifiedUserSendFailureEvents) -> Unit = {}
|
||||
eventSink: (ResolveVerifiedUserSendFailureEvent) -> Unit = {}
|
||||
) = ResolveVerifiedUserSendFailureState(
|
||||
verifiedUserSendFailure = verifiedUserSendFailure,
|
||||
resolveAction = resolveAction,
|
||||
|
||||
@@ -47,15 +47,15 @@ fun ResolveVerifiedUserSendFailureView(
|
||||
var showSheet by remember { mutableStateOf(false) }
|
||||
|
||||
fun dismiss() {
|
||||
state.eventSink(ResolveVerifiedUserSendFailureEvents.Dismiss)
|
||||
state.eventSink(ResolveVerifiedUserSendFailureEvent.Dismiss)
|
||||
}
|
||||
|
||||
fun onRetryClick() {
|
||||
state.eventSink(ResolveVerifiedUserSendFailureEvents.Retry)
|
||||
state.eventSink(ResolveVerifiedUserSendFailureEvent.Retry)
|
||||
}
|
||||
|
||||
fun onResolveAndResendClick() {
|
||||
state.eventSink(ResolveVerifiedUserSendFailureEvents.ResolveAndResend)
|
||||
state.eventSink(ResolveVerifiedUserSendFailureEvent.ResolveAndResend)
|
||||
}
|
||||
|
||||
LaunchedEffect(state.verifiedUserSendFailure) {
|
||||
|
||||
@@ -10,8 +10,8 @@ package io.element.android.features.messages.impl.link
|
||||
|
||||
import io.element.android.wysiwyg.link.Link
|
||||
|
||||
sealed interface LinkEvents {
|
||||
data class OnLinkClick(val link: Link) : LinkEvents
|
||||
data object Confirm : LinkEvents
|
||||
data object Cancel : LinkEvents
|
||||
sealed interface LinkEvent {
|
||||
data class OnLinkClick(val link: Link) : LinkEvent
|
||||
data object Confirm : LinkEvent
|
||||
data object Cancel : LinkEvent
|
||||
}
|
||||
@@ -25,9 +25,9 @@ class LinkPresenter(
|
||||
override fun present(): LinkState {
|
||||
val linkClick: MutableState<AsyncAction<Link>> = remember { mutableStateOf(AsyncAction.Uninitialized) }
|
||||
|
||||
fun handleEvent(event: LinkEvents) {
|
||||
fun handleEvent(event: LinkEvent) {
|
||||
when (event) {
|
||||
is LinkEvents.OnLinkClick -> {
|
||||
is LinkEvent.OnLinkClick -> {
|
||||
linkClick.value = AsyncAction.Loading
|
||||
val result = linkChecker.isSafe(event.link)
|
||||
if (result) {
|
||||
@@ -37,12 +37,12 @@ class LinkPresenter(
|
||||
linkClick.value = ConfirmingLinkClick(event.link)
|
||||
}
|
||||
}
|
||||
LinkEvents.Confirm -> {
|
||||
LinkEvent.Confirm -> {
|
||||
linkClick.value = (linkClick.value as? ConfirmingLinkClick)
|
||||
?.let { AsyncAction.Success(it.link) }
|
||||
?: AsyncAction.Uninitialized
|
||||
}
|
||||
LinkEvents.Cancel -> {
|
||||
LinkEvent.Cancel -> {
|
||||
linkClick.value = AsyncAction.Uninitialized
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,5 +13,5 @@ import io.element.android.wysiwyg.link.Link
|
||||
|
||||
data class LinkState(
|
||||
val linkClick: AsyncAction<Link>,
|
||||
val eventSink: (LinkEvents) -> Unit,
|
||||
val eventSink: (LinkEvent) -> Unit,
|
||||
)
|
||||
|
||||
@@ -29,7 +29,7 @@ open class LinkStateProvider : PreviewParameterProvider<LinkState> {
|
||||
|
||||
fun aLinkState(
|
||||
linkClick: AsyncAction<Link> = AsyncAction.Uninitialized,
|
||||
eventSink: (LinkEvents) -> Unit = {},
|
||||
eventSink: (LinkEvent) -> Unit = {},
|
||||
) = LinkState(
|
||||
linkClick = linkClick,
|
||||
eventSink = eventSink,
|
||||
|
||||
@@ -46,10 +46,10 @@ fun LinkView(
|
||||
),
|
||||
submitText = stringResource(CommonStrings.action_continue),
|
||||
onSubmitClick = {
|
||||
state.eventSink(LinkEvents.Confirm)
|
||||
state.eventSink(LinkEvent.Confirm)
|
||||
},
|
||||
onDismiss = {
|
||||
state.eventSink(LinkEvents.Cancel)
|
||||
state.eventSink(LinkEvent.Cancel)
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -58,7 +58,7 @@ fun LinkView(
|
||||
val latestOnLinkValid by rememberUpdatedState(onLinkValid)
|
||||
LaunchedEffect(state.linkClick.data) {
|
||||
latestOnLinkValid(state.linkClick.data)
|
||||
state.eventSink(LinkEvents.Cancel)
|
||||
state.eventSink(LinkEvent.Cancel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,6 @@
|
||||
|
||||
package io.element.android.features.messages.impl.pinned.banner
|
||||
|
||||
sealed interface PinnedMessagesBannerEvents {
|
||||
data object MoveToNextPinned : PinnedMessagesBannerEvents
|
||||
sealed interface PinnedMessagesBannerEvent {
|
||||
data object MoveToNextPinned : PinnedMessagesBannerEvent
|
||||
}
|
||||
@@ -58,9 +58,9 @@ class PinnedMessagesBannerPresenter(
|
||||
},
|
||||
)
|
||||
|
||||
fun handleEvent(event: PinnedMessagesBannerEvents) {
|
||||
fun handleEvent(event: PinnedMessagesBannerEvent) {
|
||||
when (event) {
|
||||
is PinnedMessagesBannerEvents.MoveToNextPinned -> {
|
||||
is PinnedMessagesBannerEvent.MoveToNextPinned -> {
|
||||
val loadedCount = pinnedItems.value.dataOrNull().orEmpty().size
|
||||
currentPinnedMessageIndex = (currentPinnedMessageIndex - 1).mod(loadedCount)
|
||||
}
|
||||
@@ -80,7 +80,7 @@ class PinnedMessagesBannerPresenter(
|
||||
expectedPinnedMessagesCount: Int,
|
||||
pinnedItems: AsyncData<ImmutableList<PinnedMessagesBannerItem>>,
|
||||
currentPinnedMessageIndex: Int,
|
||||
eventSink: (PinnedMessagesBannerEvents) -> Unit
|
||||
eventSink: (PinnedMessagesBannerEvent) -> Unit
|
||||
): PinnedMessagesBannerState {
|
||||
return when (pinnedItems) {
|
||||
is AsyncData.Failure, is AsyncData.Uninitialized -> PinnedMessagesBannerState.Hidden
|
||||
|
||||
@@ -41,6 +41,6 @@ sealed interface PinnedMessagesBannerState {
|
||||
val currentPinnedMessage: PinnedMessagesBannerItem,
|
||||
val currentPinnedMessageIndex: Int,
|
||||
val loadedPinnedMessagesCount: Int,
|
||||
val eventSink: (PinnedMessagesBannerEvents) -> Unit
|
||||
val eventSink: (PinnedMessagesBannerEvent) -> Unit
|
||||
) : Visible
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ internal fun aLoadedPinnedMessagesBannerState(
|
||||
eventId = EventId("\$" + Random.nextInt().toString()),
|
||||
formatted = AnnotatedString(message)
|
||||
),
|
||||
eventSink: (PinnedMessagesBannerEvents) -> Unit = {}
|
||||
eventSink: (PinnedMessagesBannerEvent) -> Unit = {}
|
||||
) = PinnedMessagesBannerState.Loaded(
|
||||
currentPinnedMessage = currentPinnedMessage,
|
||||
currentPinnedMessageIndex = currentPinnedMessageIndex,
|
||||
|
||||
@@ -96,7 +96,7 @@ private fun PinnedMessagesBannerRow(
|
||||
if (state is PinnedMessagesBannerState.Loaded) {
|
||||
analyticsService.captureInteraction(Interaction.Name.PinnedMessageBannerClick)
|
||||
onClick(state.currentPinnedMessage.eventId)
|
||||
state.eventSink(PinnedMessagesBannerEvents.MoveToNextPinned)
|
||||
state.eventSink(PinnedMessagesBannerEvent.MoveToNextPinned)
|
||||
}
|
||||
},
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
|
||||
@@ -11,6 +11,6 @@ package io.element.android.features.messages.impl.pinned.list
|
||||
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
|
||||
sealed interface PinnedMessagesListEvents {
|
||||
data class HandleAction(val action: TimelineItemAction, val event: TimelineItem.Event) : PinnedMessagesListEvents
|
||||
sealed interface PinnedMessagesListEvent {
|
||||
data class HandleAction(val action: TimelineItemAction, val event: TimelineItem.Event) : PinnedMessagesListEvent
|
||||
}
|
||||
@@ -134,9 +134,9 @@ class PinnedMessagesListPresenter(
|
||||
}
|
||||
)
|
||||
|
||||
fun handleEvent(event: PinnedMessagesListEvents) {
|
||||
fun handleEvent(event: PinnedMessagesListEvent) {
|
||||
when (event) {
|
||||
is PinnedMessagesListEvents.HandleAction -> sessionCoroutineScope.handleTimelineAction(event.action, event.event)
|
||||
is PinnedMessagesListEvent.HandleAction -> sessionCoroutineScope.handleTimelineAction(event.action, event.event)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,7 +232,7 @@ class PinnedMessagesListPresenter(
|
||||
linkState: LinkState,
|
||||
userEventPermissions: UserEventPermissions,
|
||||
timelineItems: AsyncData<ImmutableList<TimelineItem>>,
|
||||
eventSink: (PinnedMessagesListEvents) -> Unit
|
||||
eventSink: (PinnedMessagesListEvent) -> Unit
|
||||
): PinnedMessagesListState {
|
||||
return when (timelineItems) {
|
||||
AsyncData.Uninitialized, is AsyncData.Loading -> PinnedMessagesListState.Loading
|
||||
|
||||
@@ -35,7 +35,7 @@ sealed interface PinnedMessagesListState {
|
||||
val actionListState: ActionListState,
|
||||
val linkState: LinkState,
|
||||
val displayThreadSummaries: Boolean,
|
||||
val eventSink: (PinnedMessagesListEvents) -> Unit,
|
||||
val eventSink: (PinnedMessagesListEvent) -> Unit,
|
||||
) : PinnedMessagesListState {
|
||||
val loadedPinnedMessagesCount = timelineItems.count { timelineItem -> timelineItem is TimelineItem.Event }
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ fun aLoadedPinnedMessagesListState(
|
||||
actionListState: ActionListState = anActionListState(),
|
||||
aUserEventPermissions: UserEventPermissions = UserEventPermissions.DEFAULT,
|
||||
displayThreadSummaries: Boolean = false,
|
||||
eventSink: (PinnedMessagesListEvents) -> Unit = {}
|
||||
eventSink: (PinnedMessagesListEvent) -> Unit = {}
|
||||
) = PinnedMessagesListState.Filled(
|
||||
timelineRoomInfo = timelineRoomInfo,
|
||||
timelineProtectionState = timelineProtectionState,
|
||||
|
||||
@@ -25,10 +25,10 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import im.vector.app.features.analytics.plan.Interaction
|
||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
import io.element.android.features.messages.impl.actionlist.ActionListEvents
|
||||
import io.element.android.features.messages.impl.actionlist.ActionListEvent
|
||||
import io.element.android.features.messages.impl.actionlist.ActionListView
|
||||
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
|
||||
import io.element.android.features.messages.impl.link.LinkEvents
|
||||
import io.element.android.features.messages.impl.link.LinkEvent
|
||||
import io.element.android.features.messages.impl.link.LinkView
|
||||
import io.element.android.features.messages.impl.timeline.components.TimelineItemRow
|
||||
import io.element.android.features.messages.impl.timeline.components.event.TimelineItemEventContentView
|
||||
@@ -175,10 +175,10 @@ private fun PinnedMessagesListLoaded(
|
||||
) {
|
||||
fun onActionSelected(timelineItemAction: TimelineItemAction, event: TimelineItem.Event) {
|
||||
state.actionListState.eventSink(
|
||||
ActionListEvents.Clear
|
||||
ActionListEvent.Clear
|
||||
)
|
||||
state.eventSink(
|
||||
PinnedMessagesListEvents.HandleAction(
|
||||
PinnedMessagesListEvent.HandleAction(
|
||||
action = timelineItemAction,
|
||||
event = event,
|
||||
)
|
||||
@@ -187,7 +187,7 @@ private fun PinnedMessagesListLoaded(
|
||||
|
||||
fun onMessageLongClick(event: TimelineItem.Event) {
|
||||
state.actionListState.eventSink(
|
||||
ActionListEvents.ComputeForMessage(
|
||||
ActionListEvent.ComputeForMessage(
|
||||
event = event,
|
||||
userEventPermissions = state.userEventPermissions,
|
||||
)
|
||||
@@ -222,7 +222,7 @@ private fun PinnedMessagesListLoaded(
|
||||
focusedEventId = null,
|
||||
onUserDataClick = onUserDataClick,
|
||||
onLinkClick = { link ->
|
||||
state.linkState.eventSink(LinkEvents.OnLinkClick(link))
|
||||
state.linkState.eventSink(LinkEvent.OnLinkClick(link))
|
||||
},
|
||||
onLinkLongClick = onLinkLongClick,
|
||||
onContentClick = onEventClick,
|
||||
@@ -243,7 +243,7 @@ private fun PinnedMessagesListLoaded(
|
||||
onContentClick = { onEventClick(event) },
|
||||
onLongClick = { onMessageLongClick(event) },
|
||||
onLinkClick = { link ->
|
||||
state.linkState.eventSink(LinkEvents.OnLinkClick(link))
|
||||
state.linkState.eventSink(LinkEvent.OnLinkClick(link))
|
||||
},
|
||||
onLinkLongClick = onLinkLongClick,
|
||||
modifier = contentModifier,
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
|
||||
package io.element.android.features.messages.impl.report
|
||||
|
||||
sealed interface ReportMessageEvents {
|
||||
data class UpdateReason(val reason: String) : ReportMessageEvents
|
||||
data object ToggleBlockUser : ReportMessageEvents
|
||||
data object Report : ReportMessageEvents
|
||||
data object ClearError : ReportMessageEvents
|
||||
sealed interface ReportMessageEvent {
|
||||
data class UpdateReason(val reason: String) : ReportMessageEvent
|
||||
data object ToggleBlockUser : ReportMessageEvent
|
||||
data object Report : ReportMessageEvent
|
||||
data object ClearError : ReportMessageEvent
|
||||
}
|
||||
@@ -54,12 +54,12 @@ class ReportMessagePresenter(
|
||||
var blockUser by rememberSaveable { mutableStateOf(false) }
|
||||
var result: MutableState<AsyncAction<Unit>> = remember { mutableStateOf(AsyncAction.Uninitialized) }
|
||||
|
||||
fun handleEvent(event: ReportMessageEvents) {
|
||||
fun handleEvent(event: ReportMessageEvent) {
|
||||
when (event) {
|
||||
is ReportMessageEvents.UpdateReason -> reason = event.reason
|
||||
ReportMessageEvents.ToggleBlockUser -> blockUser = !blockUser
|
||||
ReportMessageEvents.Report -> coroutineScope.report(inputs.eventId, inputs.senderId, reason, blockUser, result)
|
||||
ReportMessageEvents.ClearError -> result.value = AsyncAction.Uninitialized
|
||||
is ReportMessageEvent.UpdateReason -> reason = event.reason
|
||||
ReportMessageEvent.ToggleBlockUser -> blockUser = !blockUser
|
||||
ReportMessageEvent.Report -> coroutineScope.report(inputs.eventId, inputs.senderId, reason, blockUser, result)
|
||||
ReportMessageEvent.ClearError -> result.value = AsyncAction.Uninitialized
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,5 +14,5 @@ data class ReportMessageState(
|
||||
val reason: String,
|
||||
val blockUser: Boolean,
|
||||
val result: AsyncAction<Unit>,
|
||||
val eventSink: (ReportMessageEvents) -> Unit
|
||||
val eventSink: (ReportMessageEvent) -> Unit
|
||||
)
|
||||
|
||||
@@ -57,7 +57,7 @@ fun ReportMessageView(
|
||||
progressDialog = {},
|
||||
onSuccess = { onBackClick() },
|
||||
errorMessage = { stringResource(CommonStrings.error_unknown) },
|
||||
onErrorDismiss = { state.eventSink(ReportMessageEvents.ClearError) }
|
||||
onErrorDismiss = { state.eventSink(ReportMessageEvent.ClearError) }
|
||||
)
|
||||
|
||||
Scaffold(
|
||||
@@ -84,7 +84,7 @@ fun ReportMessageView(
|
||||
|
||||
TextField(
|
||||
value = state.reason,
|
||||
onValueChange = { state.eventSink(ReportMessageEvents.UpdateReason(it)) },
|
||||
onValueChange = { state.eventSink(ReportMessageEvent.UpdateReason(it)) },
|
||||
placeholder = stringResource(R.string.screen_report_content_hint),
|
||||
minLines = 3,
|
||||
enabled = !isSending,
|
||||
@@ -113,7 +113,7 @@ fun ReportMessageView(
|
||||
Switch(
|
||||
enabled = !isSending,
|
||||
checked = state.blockUser,
|
||||
onCheckedChange = { state.eventSink(ReportMessageEvents.ToggleBlockUser) },
|
||||
onCheckedChange = { state.eventSink(ReportMessageEvent.ToggleBlockUser) },
|
||||
)
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ fun ReportMessageView(
|
||||
showProgress = isSending,
|
||||
onClick = {
|
||||
focusManager.clearFocus(force = true)
|
||||
state.eventSink(ReportMessageEvents.Report)
|
||||
state.eventSink(ReportMessageEvent.Report)
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
|
||||
@@ -36,7 +36,7 @@ import io.element.android.features.messages.impl.attachments.Attachment
|
||||
import io.element.android.features.messages.impl.messagecomposer.MessageComposerEvent
|
||||
import io.element.android.features.messages.impl.messagecomposer.MessageComposerPresenter
|
||||
import io.element.android.features.messages.impl.timeline.TimelineController
|
||||
import io.element.android.features.messages.impl.timeline.TimelineEvents
|
||||
import io.element.android.features.messages.impl.timeline.TimelineEvent
|
||||
import io.element.android.features.messages.impl.timeline.TimelinePresenter
|
||||
import io.element.android.features.messages.impl.timeline.di.LocalTimelineItemPresenterFactories
|
||||
import io.element.android.features.messages.impl.timeline.di.TimelineItemPresenterFactories
|
||||
@@ -148,7 +148,7 @@ class ThreadedMessagesNode(
|
||||
activity: Activity,
|
||||
darkTheme: Boolean,
|
||||
url: String,
|
||||
eventSink: (TimelineEvents) -> Unit,
|
||||
eventSink: (TimelineEvent) -> Unit,
|
||||
customTab: Boolean
|
||||
) {
|
||||
when (val permalink = permalinkParser.parse(url)) {
|
||||
@@ -175,12 +175,12 @@ class ThreadedMessagesNode(
|
||||
|
||||
private fun handleRoomLinkClick(
|
||||
roomLink: PermalinkData.RoomLink,
|
||||
eventSink: (TimelineEvents) -> Unit,
|
||||
eventSink: (TimelineEvent) -> Unit,
|
||||
) {
|
||||
if (room.matches(roomLink.roomIdOrAlias)) {
|
||||
val eventId = roomLink.eventId
|
||||
if (eventId != null) {
|
||||
eventSink(TimelineEvents.FocusOnEvent(eventId))
|
||||
eventSink(TimelineEvent.FocusOnEvent(eventId))
|
||||
} else {
|
||||
// Click on the same room, navigate up
|
||||
// Note that it can not be enough to go back to the room if the thread has been opened
|
||||
@@ -277,7 +277,7 @@ class ThreadedMessagesNode(
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
focusedEventId?.also { eventId ->
|
||||
state.timelineState.eventSink(TimelineEvents.FocusOnEvent(eventId))
|
||||
state.timelineState.eventSink(TimelineEvent.FocusOnEvent(eventId))
|
||||
}
|
||||
// Reset the focused event id to null to avoid refocusing when restoring node.
|
||||
focusedEventId = null
|
||||
|
||||
@@ -16,45 +16,45 @@ import io.element.android.libraries.matrix.api.core.ThreadId
|
||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
import kotlin.time.Duration
|
||||
|
||||
sealed interface TimelineEvents {
|
||||
data class OnScrollFinished(val firstIndex: Int) : TimelineEvents
|
||||
data class FocusOnEvent(val eventId: EventId, val debounce: Duration = Duration.ZERO) : TimelineEvents
|
||||
data object ClearFocusRequestState : TimelineEvents
|
||||
data object OnFocusEventRender : TimelineEvents
|
||||
data object JumpToLive : TimelineEvents
|
||||
sealed interface TimelineEvent {
|
||||
data class OnScrollFinished(val firstIndex: Int) : TimelineEvent
|
||||
data class FocusOnEvent(val eventId: EventId, val debounce: Duration = Duration.ZERO) : TimelineEvent
|
||||
data object ClearFocusRequestState : TimelineEvent
|
||||
data object OnFocusEventRender : TimelineEvent
|
||||
data object JumpToLive : TimelineEvent
|
||||
|
||||
data object HideShieldDialog : TimelineEvents
|
||||
data object HideShieldDialog : TimelineEvent
|
||||
|
||||
/**
|
||||
* Events coming from a timeline item.
|
||||
*/
|
||||
sealed interface EventFromTimelineItem : TimelineEvents
|
||||
sealed interface TimelineItemEvent : TimelineEvent
|
||||
|
||||
data class ComputeVerifiedUserSendFailure(val event: TimelineItem.Event) : EventFromTimelineItem
|
||||
data class ShowShieldDialog(val messageShieldData: MessageShieldData) : EventFromTimelineItem
|
||||
data class LoadMore(val direction: Timeline.PaginationDirection) : EventFromTimelineItem
|
||||
data class OpenThread(val threadRootEventId: ThreadId, val focusedEvent: EventId?) : EventFromTimelineItem
|
||||
data class ComputeVerifiedUserSendFailure(val event: TimelineItem.Event) : TimelineItemEvent
|
||||
data class ShowShieldDialog(val messageShieldData: MessageShieldData) : TimelineItemEvent
|
||||
data class LoadMore(val direction: Timeline.PaginationDirection) : TimelineItemEvent
|
||||
data class OpenThread(val threadRootEventId: ThreadId, val focusedEvent: EventId?) : TimelineItemEvent
|
||||
|
||||
/**
|
||||
* Navigate to the predecessor or successor room of the current room.
|
||||
*/
|
||||
data class NavigateToPredecessorOrSuccessorRoom(val roomId: RoomId) : EventFromTimelineItem
|
||||
data class NavigateToPredecessorOrSuccessorRoom(val roomId: RoomId) : TimelineItemEvent
|
||||
|
||||
/**
|
||||
* Events coming from a poll item.
|
||||
*/
|
||||
sealed interface TimelineItemPollEvents : EventFromTimelineItem
|
||||
sealed interface TimelineItemPollEvent : TimelineItemEvent
|
||||
|
||||
data class SelectPollAnswer(
|
||||
val pollStartId: EventId,
|
||||
val answerId: String
|
||||
) : TimelineItemPollEvents
|
||||
) : TimelineItemPollEvent
|
||||
|
||||
data class EndPoll(
|
||||
val pollStartId: EventId,
|
||||
) : TimelineItemPollEvents
|
||||
) : TimelineItemPollEvent
|
||||
|
||||
data class EditPoll(
|
||||
val pollStartId: EventId,
|
||||
) : TimelineItemPollEvents
|
||||
) : TimelineItemPollEvent
|
||||
}
|
||||
@@ -25,7 +25,7 @@ import dev.zacsweers.metro.AssistedFactory
|
||||
import dev.zacsweers.metro.AssistedInject
|
||||
import io.element.android.features.messages.impl.MessagesNavigator
|
||||
import io.element.android.features.messages.impl.UserEventPermissions
|
||||
import io.element.android.features.messages.impl.crypto.sendfailure.resolve.ResolveVerifiedUserSendFailureEvents
|
||||
import io.element.android.features.messages.impl.crypto.sendfailure.resolve.ResolveVerifiedUserSendFailureEvent
|
||||
import io.element.android.features.messages.impl.crypto.sendfailure.resolve.ResolveVerifiedUserSendFailureState
|
||||
import io.element.android.features.messages.impl.timeline.components.MessageShieldData
|
||||
import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactory
|
||||
@@ -150,9 +150,9 @@ class TimelinePresenter(
|
||||
value = featureFlagService.isFeatureEnabled(FeatureFlags.Threads)
|
||||
}
|
||||
|
||||
fun handleEvent(event: TimelineEvents) {
|
||||
fun handleEvent(event: TimelineEvent) {
|
||||
when (event) {
|
||||
is TimelineEvents.LoadMore -> {
|
||||
is TimelineEvent.LoadMore -> {
|
||||
if (event.direction == Timeline.PaginationDirection.FORWARDS && timelineMode is Timeline.Mode.Thread) {
|
||||
// Do not paginate forwards in thread mode, as it's not supported
|
||||
return
|
||||
@@ -161,7 +161,7 @@ class TimelinePresenter(
|
||||
timelineController.paginate(direction = event.direction)
|
||||
}
|
||||
}
|
||||
is TimelineEvents.OnScrollFinished -> {
|
||||
is TimelineEvent.OnScrollFinished -> {
|
||||
if (isLive) {
|
||||
if (event.firstIndex == 0) {
|
||||
newEventState.value = NewEventState.None
|
||||
@@ -177,7 +177,7 @@ class TimelinePresenter(
|
||||
newEventState.value = NewEventState.None
|
||||
}
|
||||
}
|
||||
is TimelineEvents.SelectPollAnswer -> sessionCoroutineScope.launch {
|
||||
is TimelineEvent.SelectPollAnswer -> sessionCoroutineScope.launch {
|
||||
timelineController.invokeOnCurrentTimeline {
|
||||
sendPollResponseAction.execute(
|
||||
timeline = this,
|
||||
@@ -186,7 +186,7 @@ class TimelinePresenter(
|
||||
)
|
||||
}
|
||||
}
|
||||
is TimelineEvents.EndPoll -> sessionCoroutineScope.launch {
|
||||
is TimelineEvent.EndPoll -> sessionCoroutineScope.launch {
|
||||
timelineController.invokeOnCurrentTimeline {
|
||||
endPollAction.execute(
|
||||
timeline = this,
|
||||
@@ -194,38 +194,38 @@ class TimelinePresenter(
|
||||
)
|
||||
}
|
||||
}
|
||||
is TimelineEvents.EditPoll -> {
|
||||
is TimelineEvent.EditPoll -> {
|
||||
navigator.navigateToEditPoll(event.pollStartId)
|
||||
}
|
||||
is TimelineEvents.FocusOnEvent -> sessionCoroutineScope.launch {
|
||||
is TimelineEvent.FocusOnEvent -> sessionCoroutineScope.launch {
|
||||
focusRequestState.value = FocusRequestState.Requested(event.eventId, event.debounce)
|
||||
delay(event.debounce)
|
||||
Timber.tag(tag).d("Started focus on ${event.eventId}")
|
||||
focusOnEvent(event.eventId, focusRequestState)
|
||||
}.start()
|
||||
is TimelineEvents.OnFocusEventRender -> {
|
||||
is TimelineEvent.OnFocusEventRender -> {
|
||||
// If there was a pending 'notification tap opens timeline' transaction, finish it now we're focused in the required event
|
||||
analyticsService.finishLongRunningTransaction(NotificationToMessage)
|
||||
|
||||
focusRequestState.value = focusRequestState.value.onFocusEventRender()
|
||||
}
|
||||
is TimelineEvents.ClearFocusRequestState -> {
|
||||
is TimelineEvent.ClearFocusRequestState -> {
|
||||
focusRequestState.value = FocusRequestState.None
|
||||
}
|
||||
is TimelineEvents.JumpToLive -> {
|
||||
is TimelineEvent.JumpToLive -> {
|
||||
timelineController.focusOnLive()
|
||||
}
|
||||
TimelineEvents.HideShieldDialog -> messageShieldDialogData.value = null
|
||||
is TimelineEvents.ShowShieldDialog -> messageShieldDialogData.value = event.messageShieldData
|
||||
is TimelineEvents.ComputeVerifiedUserSendFailure -> {
|
||||
resolveVerifiedUserSendFailureState.eventSink(ResolveVerifiedUserSendFailureEvents.ComputeForMessage(event.event))
|
||||
TimelineEvent.HideShieldDialog -> messageShieldDialogData.value = null
|
||||
is TimelineEvent.ShowShieldDialog -> messageShieldDialogData.value = event.messageShieldData
|
||||
is TimelineEvent.ComputeVerifiedUserSendFailure -> {
|
||||
resolveVerifiedUserSendFailureState.eventSink(ResolveVerifiedUserSendFailureEvent.ComputeForMessage(event.event))
|
||||
}
|
||||
is TimelineEvents.NavigateToPredecessorOrSuccessorRoom -> {
|
||||
is TimelineEvent.NavigateToPredecessorOrSuccessorRoom -> {
|
||||
// Navigate to the predecessor or successor room
|
||||
val serverNames = calculateServerNamesForRoom(room)
|
||||
navigator.navigateToRoom(event.roomId, null, serverNames)
|
||||
}
|
||||
is TimelineEvents.OpenThread -> {
|
||||
is TimelineEvent.OpenThread -> {
|
||||
navigator.navigateToThread(
|
||||
threadRootId = event.threadRootEventId,
|
||||
focusedEventId = event.focusedEvent,
|
||||
|
||||
@@ -34,7 +34,7 @@ data class TimelineState(
|
||||
val messageShieldDialogData: MessageShieldData?,
|
||||
val resolveVerifiedUserSendFailureState: ResolveVerifiedUserSendFailureState,
|
||||
val displayThreadSummaries: Boolean,
|
||||
val eventSink: (TimelineEvents) -> Unit,
|
||||
val eventSink: (TimelineEvent) -> Unit,
|
||||
) {
|
||||
private val lastTimelineEvent = timelineItems.firstOrNull { it is TimelineItem.Event } as? TimelineItem.Event
|
||||
val hasAnyEvent = lastTimelineEvent != null
|
||||
|
||||
@@ -56,7 +56,7 @@ fun aTimelineState(
|
||||
messageShield: MessageShield? = null,
|
||||
resolveVerifiedUserSendFailureState: ResolveVerifiedUserSendFailureState = aResolveVerifiedUserSendFailureState(),
|
||||
displayThreadSummaries: Boolean = false,
|
||||
eventSink: (TimelineEvents) -> Unit = {},
|
||||
eventSink: (TimelineEvent) -> Unit = {},
|
||||
): TimelineState {
|
||||
val focusedEventId = timelineItems.filterIsInstance<TimelineItem.Event>().getOrNull(focusedEventIndex)?.eventId
|
||||
val focusRequestState = if (focusedEventId != null) {
|
||||
|
||||
@@ -107,19 +107,19 @@ fun TimelineView(
|
||||
nestedScrollConnection: NestedScrollConnection = rememberNestedScrollInteropConnection(),
|
||||
) {
|
||||
fun clearFocusRequestState() {
|
||||
state.eventSink(TimelineEvents.ClearFocusRequestState)
|
||||
state.eventSink(TimelineEvent.ClearFocusRequestState)
|
||||
}
|
||||
|
||||
fun onScrollFinishAt(firstVisibleIndex: Int) {
|
||||
state.eventSink(TimelineEvents.OnScrollFinished(firstVisibleIndex))
|
||||
state.eventSink(TimelineEvent.OnScrollFinished(firstVisibleIndex))
|
||||
}
|
||||
|
||||
fun onFocusEventRender() {
|
||||
state.eventSink(TimelineEvents.OnFocusEventRender)
|
||||
state.eventSink(TimelineEvent.OnFocusEventRender)
|
||||
}
|
||||
|
||||
fun onJumpToLive() {
|
||||
state.eventSink(TimelineEvents.JumpToLive)
|
||||
state.eventSink(TimelineEvent.JumpToLive)
|
||||
}
|
||||
|
||||
val context = LocalContext.current
|
||||
@@ -129,7 +129,7 @@ fun TimelineView(
|
||||
val useReverseLayout = !isTalkbackActive()
|
||||
|
||||
fun inReplyToClick(eventId: EventId) {
|
||||
state.eventSink(TimelineEvents.FocusOnEvent(eventId))
|
||||
state.eventSink(TimelineEvent.FocusOnEvent(eventId))
|
||||
}
|
||||
|
||||
fun onLinkLongClick(link: Link) {
|
||||
@@ -143,7 +143,7 @@ fun TimelineView(
|
||||
}
|
||||
|
||||
fun prefetchMoreItems() {
|
||||
state.eventSink(TimelineEvents.LoadMore(Timeline.PaginationDirection.BACKWARDS))
|
||||
state.eventSink(TimelineEvent.LoadMore(Timeline.PaginationDirection.BACKWARDS))
|
||||
}
|
||||
|
||||
// Animate alpha when timeline is first displayed, to avoid flashes or glitching when viewing rooms
|
||||
@@ -223,7 +223,7 @@ private fun MessageShieldDialog(state: TimelineState) {
|
||||
val messageShield = state.messageShieldDialogData ?: return
|
||||
AlertDialog(
|
||||
content = messageShield.toText(),
|
||||
onDismiss = { state.eventSink.invoke(TimelineEvents.HideShieldDialog) },
|
||||
onDismiss = { state.eventSink.invoke(TimelineEvent.HideShieldDialog) },
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
import io.element.android.features.messages.impl.timeline.TimelineEvents
|
||||
import io.element.android.features.messages.impl.timeline.TimelineEvent
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
import io.element.android.features.messages.impl.timeline.model.event.isEdited
|
||||
import io.element.android.features.messages.impl.timeline.model.event.isRedacted
|
||||
@@ -33,13 +33,12 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.isCritical
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
@Composable
|
||||
fun TimelineEventTimestampView(
|
||||
event: TimelineItem.Event,
|
||||
eventSink: (TimelineEvents.EventFromTimelineItem) -> Unit,
|
||||
eventSink: (TimelineEvent.TimelineItemEvent) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val formattedTime = event.sentTime
|
||||
@@ -80,7 +79,7 @@ fun TimelineEventTimestampView(
|
||||
enabled = isVerifiedUserSendFailure,
|
||||
onClickLabel = stringResource(CommonStrings.action_open_context_menu),
|
||||
) {
|
||||
eventSink(TimelineEvents.ComputeVerifiedUserSendFailure(event))
|
||||
eventSink(TimelineEvent.ComputeVerifiedUserSendFailure(event))
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -96,7 +95,7 @@ fun TimelineEventTimestampView(
|
||||
.clickable(
|
||||
onClickLabel = stringResource(CommonStrings.a11y_view_details),
|
||||
) {
|
||||
eventSink(TimelineEvents.ShowShieldDialog(shield))
|
||||
eventSink(TimelineEvent.ShowShieldDialog(shield))
|
||||
},
|
||||
tint = shield.toIconColor(),
|
||||
)
|
||||
|
||||
@@ -57,7 +57,7 @@ import androidx.constraintlayout.compose.ConstrainScope
|
||||
import androidx.constraintlayout.compose.ConstraintLayout
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
import io.element.android.features.messages.impl.timeline.TimelineEvents
|
||||
import io.element.android.features.messages.impl.timeline.TimelineEvent
|
||||
import io.element.android.features.messages.impl.timeline.TimelineRoomInfo
|
||||
import io.element.android.features.messages.impl.timeline.aTimelineItemEvent
|
||||
import io.element.android.features.messages.impl.timeline.components.event.TimelineItemEventContentView
|
||||
@@ -153,7 +153,7 @@ fun TimelineItemEventRow(
|
||||
onMoreReactionsClick: (eventId: TimelineItem.Event) -> Unit,
|
||||
onReadReceiptClick: (event: TimelineItem.Event) -> Unit,
|
||||
onSwipeToReply: () -> Unit,
|
||||
eventSink: (TimelineEvents.EventFromTimelineItem) -> Unit,
|
||||
eventSink: (TimelineEvent.TimelineItemEvent) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
eventContentView: @Composable (Modifier, (ContentAvoidingLayoutData) -> Unit) -> Unit = { contentModifier, onContentLayoutChange ->
|
||||
// Only pass down a custom clickable lambda if the content can be clicked separately
|
||||
@@ -278,7 +278,7 @@ fun TimelineItemEventRow(
|
||||
isOutgoing = event.isMine,
|
||||
onClick = {
|
||||
event.eventId?.let {
|
||||
eventSink(TimelineEvents.OpenThread(it.toThreadId(), null))
|
||||
eventSink(TimelineEvent.OpenThread(it.toThreadId(), null))
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -410,7 +410,7 @@ private fun TimelineItemEventRowContent(
|
||||
onReactionClick: (emoji: String) -> Unit,
|
||||
onReactionLongClick: (emoji: String) -> Unit,
|
||||
onMoreReactionsClick: (event: TimelineItem.Event) -> Unit,
|
||||
eventSink: (TimelineEvents.EventFromTimelineItem) -> Unit,
|
||||
eventSink: (TimelineEvent.TimelineItemEvent) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
eventContentView: @Composable (Modifier, (ContentAvoidingLayoutData) -> Unit) -> Unit,
|
||||
) {
|
||||
@@ -585,7 +585,7 @@ private fun MessageEventBubbleContent(
|
||||
timelineProtectionState: TimelineProtectionState,
|
||||
onMessageLongClick: () -> Unit,
|
||||
inReplyToClick: () -> Unit,
|
||||
eventSink: (TimelineEvents.EventFromTimelineItem) -> Unit,
|
||||
eventSink: (TimelineEvent.TimelineItemEvent) -> Unit,
|
||||
@SuppressLint("ModifierParameter")
|
||||
// need to rename this modifier to prevent linter false positives
|
||||
@Suppress("ModifierNaming")
|
||||
@@ -623,7 +623,7 @@ private fun MessageEventBubbleContent(
|
||||
@Composable
|
||||
fun WithTimestampLayout(
|
||||
timestampPosition: TimestampPosition,
|
||||
eventSink: (TimelineEvents.EventFromTimelineItem) -> Unit,
|
||||
eventSink: (TimelineEvent.TimelineItemEvent) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
canShrinkContent: Boolean = false,
|
||||
content: @Composable (onContentLayoutChange: (ContentAvoidingLayoutData) -> Unit) -> Unit,
|
||||
|
||||
@@ -16,7 +16,7 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.pluralStringResource
|
||||
import io.element.android.features.messages.impl.R
|
||||
import io.element.android.features.messages.impl.timeline.TimelineEvents
|
||||
import io.element.android.features.messages.impl.timeline.TimelineEvent
|
||||
import io.element.android.features.messages.impl.timeline.TimelineRoomInfo
|
||||
import io.element.android.features.messages.impl.timeline.aGroupedEvents
|
||||
import io.element.android.features.messages.impl.timeline.aTimelineRoomInfo
|
||||
@@ -57,7 +57,7 @@ fun TimelineItemGroupedEventsRow(
|
||||
onReactionLongClick: (key: String, TimelineItem.Event) -> Unit,
|
||||
onMoreReactionsClick: (TimelineItem.Event) -> Unit,
|
||||
onReadReceiptClick: (TimelineItem.Event) -> Unit,
|
||||
eventSink: (TimelineEvents.EventFromTimelineItem) -> Unit,
|
||||
eventSink: (TimelineEvent.TimelineItemEvent) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
eventContentView: @Composable (TimelineItem.Event, Modifier, (ContentAvoidingLayoutData) -> Unit) -> Unit =
|
||||
{ event, contentModifier, onContentLayoutChange ->
|
||||
@@ -130,7 +130,7 @@ private fun TimelineItemGroupedEventsRowContent(
|
||||
onReactionLongClick: (key: String, TimelineItem.Event) -> Unit,
|
||||
onMoreReactionsClick: (TimelineItem.Event) -> Unit,
|
||||
onReadReceiptClick: (TimelineItem.Event) -> Unit,
|
||||
eventSink: (TimelineEvents.EventFromTimelineItem) -> Unit,
|
||||
eventSink: (TimelineEvent.TimelineItemEvent) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
eventContentView: @Composable (TimelineItem.Event, Modifier, (ContentAvoidingLayoutData) -> Unit) -> Unit =
|
||||
{ event, contentModifier, onContentLayoutChange ->
|
||||
|
||||
@@ -26,7 +26,7 @@ import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.features.messages.impl.timeline.TimelineEvents
|
||||
import io.element.android.features.messages.impl.timeline.TimelineEvent
|
||||
import io.element.android.features.messages.impl.timeline.TimelineRoomInfo
|
||||
import io.element.android.features.messages.impl.timeline.components.event.TimelineItemEventContentView
|
||||
import io.element.android.features.messages.impl.timeline.components.layout.ContentAvoidingLayoutData
|
||||
@@ -73,7 +73,7 @@ internal fun TimelineItemRow(
|
||||
onReadReceiptClick: (TimelineItem.Event) -> Unit,
|
||||
onSwipeToReply: (TimelineItem.Event) -> Unit,
|
||||
onJoinCallClick: () -> Unit,
|
||||
eventSink: (TimelineEvents.EventFromTimelineItem) -> Unit,
|
||||
eventSink: (TimelineEvent.TimelineItemEvent) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
eventContentView: @Composable (TimelineItem.Event, Modifier, (ContentAvoidingLayoutData) -> Unit) -> Unit =
|
||||
{ event, contentModifier, onContentLayoutChange ->
|
||||
|
||||
@@ -21,7 +21,7 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.zIndex
|
||||
import io.element.android.features.messages.impl.timeline.TimelineEvents
|
||||
import io.element.android.features.messages.impl.timeline.TimelineEvent
|
||||
import io.element.android.features.messages.impl.timeline.aTimelineItemEvent
|
||||
import io.element.android.features.messages.impl.timeline.components.event.TimelineItemEventContentView
|
||||
import io.element.android.features.messages.impl.timeline.components.receipt.ReadReceiptViewState
|
||||
@@ -44,7 +44,7 @@ fun TimelineItemStateEventRow(
|
||||
onClick: () -> Unit,
|
||||
onLongClick: () -> Unit,
|
||||
onReadReceiptsClick: (event: TimelineItem.Event) -> Unit,
|
||||
eventSink: (TimelineEvents.EventFromTimelineItem) -> Unit,
|
||||
eventSink: (TimelineEvent.TimelineItemEvent) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
|
||||
@@ -15,7 +15,7 @@ import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.ui.Modifier
|
||||
import io.element.android.features.messages.impl.timeline.TimelineEvents
|
||||
import io.element.android.features.messages.impl.timeline.TimelineEvent
|
||||
import io.element.android.features.messages.impl.timeline.TimelineRoomInfo
|
||||
import io.element.android.features.messages.impl.timeline.components.virtual.TimelineItemDaySeparatorView
|
||||
import io.element.android.features.messages.impl.timeline.components.virtual.TimelineItemReadMarkerView
|
||||
@@ -35,7 +35,7 @@ import timber.log.Timber
|
||||
fun TimelineItemVirtualRow(
|
||||
virtual: TimelineItem.Virtual,
|
||||
timelineRoomInfo: TimelineRoomInfo,
|
||||
eventSink: (TimelineEvents.EventFromTimelineItem) -> Unit,
|
||||
eventSink: (TimelineEvent.TimelineItemEvent) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Box(modifier = modifier) {
|
||||
@@ -48,7 +48,7 @@ fun TimelineItemVirtualRow(
|
||||
roomName = timelineRoomInfo.name,
|
||||
isDm = timelineRoomInfo.isDm,
|
||||
onPredecessorRoomClick = { roomId ->
|
||||
eventSink(TimelineEvents.NavigateToPredecessorOrSuccessorRoom(roomId))
|
||||
eventSink(TimelineEvent.NavigateToPredecessorOrSuccessorRoom(roomId))
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -57,7 +57,7 @@ fun TimelineItemVirtualRow(
|
||||
val latestEventSink by rememberUpdatedState(eventSink)
|
||||
LaunchedEffect(virtual.model.timestamp) {
|
||||
Timber.d("Pagination triggered by load more indicator")
|
||||
latestEventSink(TimelineEvents.LoadMore(virtual.model.direction))
|
||||
latestEventSink(TimelineEvent.LoadMore(virtual.model.direction))
|
||||
}
|
||||
}
|
||||
// Empty model trick to avoid timeline jumping during forward pagination.
|
||||
|
||||
@@ -35,13 +35,13 @@ fun CustomReactionBottomSheet(
|
||||
val target = state.target as? CustomReactionState.Target.Success
|
||||
|
||||
fun onDismiss() {
|
||||
state.eventSink(CustomReactionEvents.DismissCustomReactionSheet)
|
||||
state.eventSink(CustomReactionEvent.DismissCustomReactionSheet)
|
||||
}
|
||||
|
||||
fun onEmojiSelectedDismiss(emoji: Emoji) {
|
||||
if (target?.event == null) return
|
||||
sheetState.hide(coroutineScope) {
|
||||
state.eventSink(CustomReactionEvents.DismissCustomReactionSheet)
|
||||
state.eventSink(CustomReactionEvent.DismissCustomReactionSheet)
|
||||
onSelectEmoji(target.event.eventOrTransactionId, emoji)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ package io.element.android.features.messages.impl.timeline.components.customreac
|
||||
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
|
||||
sealed interface CustomReactionEvents {
|
||||
data class ShowCustomReactionSheet(val event: TimelineItem.Event) : CustomReactionEvents
|
||||
data object DismissCustomReactionSheet : CustomReactionEvents
|
||||
sealed interface CustomReactionEvent {
|
||||
data class ShowCustomReactionSheet(val event: TimelineItem.Event) : CustomReactionEvent
|
||||
data object DismissCustomReactionSheet : CustomReactionEvent
|
||||
}
|
||||
@@ -54,10 +54,10 @@ class CustomReactionPresenter(
|
||||
target.value = CustomReactionState.Target.None
|
||||
}
|
||||
|
||||
fun handleEvent(event: CustomReactionEvents) {
|
||||
fun handleEvent(event: CustomReactionEvent) {
|
||||
when (event) {
|
||||
is CustomReactionEvents.ShowCustomReactionSheet -> handleShowCustomReactionSheet(event.event)
|
||||
is CustomReactionEvents.DismissCustomReactionSheet -> handleDismissCustomReactionSheet()
|
||||
is CustomReactionEvent.ShowCustomReactionSheet -> handleShowCustomReactionSheet(event.event)
|
||||
is CustomReactionEvent.DismissCustomReactionSheet -> handleDismissCustomReactionSheet()
|
||||
}
|
||||
}
|
||||
val event = (target.value as? CustomReactionState.Target.Success)?.event
|
||||
|
||||
@@ -17,7 +17,7 @@ data class CustomReactionState(
|
||||
val target: Target,
|
||||
val selectedEmoji: ImmutableSet<String>,
|
||||
val recentEmojis: ImmutableList<String>,
|
||||
val eventSink: (CustomReactionEvents) -> Unit,
|
||||
val eventSink: (CustomReactionEvent) -> Unit,
|
||||
) {
|
||||
sealed interface Target {
|
||||
data object None : Target
|
||||
|
||||
@@ -60,7 +60,7 @@ fun EmojiPicker(
|
||||
queryState = state.searchQuery,
|
||||
resultState = state.searchResults,
|
||||
active = state.isSearchActive,
|
||||
onActiveChange = { state.eventSink(EmojiPickerEvents.ToggleSearchActive(it)) },
|
||||
onActiveChange = { state.eventSink(EmojiPickerEvent.ToggleSearchActive(it)) },
|
||||
windowInsets = WindowInsets(0, 0, 0, 0),
|
||||
placeHolderTitle = stringResource(CommonStrings.emoji_picker_search_placeholder),
|
||||
) { emojis ->
|
||||
|
||||
@@ -8,6 +8,6 @@
|
||||
|
||||
package io.element.android.features.messages.impl.timeline.components.customreaction.picker
|
||||
|
||||
sealed interface EmojiPickerEvents {
|
||||
data class ToggleSearchActive(val isActive: Boolean) : EmojiPickerEvents
|
||||
sealed interface EmojiPickerEvent {
|
||||
data class ToggleSearchActive(val isActive: Boolean) : EmojiPickerEvent
|
||||
}
|
||||
@@ -93,10 +93,10 @@ class EmojiPickerPresenter(
|
||||
}
|
||||
|
||||
val isInPreview = LocalInspectionMode.current
|
||||
fun handleEvent(event: EmojiPickerEvents) {
|
||||
fun handleEvent(event: EmojiPickerEvent) {
|
||||
when (event) {
|
||||
// For some reason, in preview mode the SearchBar emits this event with an `isActive = true` value automatically
|
||||
is EmojiPickerEvents.ToggleSearchActive -> if (!isInPreview) {
|
||||
is EmojiPickerEvent.ToggleSearchActive -> if (!isInPreview) {
|
||||
isSearchActive = event.isActive
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ data class EmojiPickerState(
|
||||
val searchQuery: TextFieldState,
|
||||
val isSearchActive: Boolean,
|
||||
val searchResults: SearchBarResultState<ImmutableList<Emoji>>,
|
||||
val eventSink: (EmojiPickerEvents) -> Unit,
|
||||
val eventSink: (EmojiPickerEvent) -> Unit,
|
||||
)
|
||||
|
||||
/**
|
||||
|
||||
@@ -73,7 +73,7 @@ internal fun anEmojiPickerState(
|
||||
searchQuery: String = "",
|
||||
isSearchActive: Boolean = false,
|
||||
searchResults: SearchBarResultState<ImmutableList<Emoji>> = SearchBarResultState.Initial(),
|
||||
eventSink: (EmojiPickerEvents) -> Unit = {},
|
||||
eventSink: (EmojiPickerEvent) -> Unit = {},
|
||||
) = EmojiPickerState(
|
||||
categories = categories,
|
||||
allEmojis = allEmojis,
|
||||
|
||||
@@ -10,7 +10,7 @@ package io.element.android.features.messages.impl.timeline.components.event
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import io.element.android.features.messages.impl.timeline.TimelineEvents
|
||||
import io.element.android.features.messages.impl.timeline.TimelineEvent
|
||||
import io.element.android.features.messages.impl.timeline.components.layout.ContentAvoidingLayoutData
|
||||
import io.element.android.features.messages.impl.timeline.di.LocalTimelineItemPresenterFactories
|
||||
import io.element.android.features.messages.impl.timeline.di.rememberPresenter
|
||||
@@ -43,7 +43,7 @@ fun TimelineItemEventContentView(
|
||||
onShowContentClick: () -> Unit,
|
||||
onLinkClick: (Link) -> Unit,
|
||||
onLinkLongClick: (Link) -> Unit,
|
||||
eventSink: (TimelineEvents.EventFromTimelineItem) -> Unit,
|
||||
eventSink: (TimelineEvent.TimelineItemEvent) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
onContentLayoutChange: (ContentAvoidingLayoutData) -> Unit = {},
|
||||
) {
|
||||
|
||||
@@ -11,7 +11,7 @@ package io.element.android.features.messages.impl.timeline.components.event
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import io.element.android.features.messages.impl.timeline.TimelineEvents
|
||||
import io.element.android.features.messages.impl.timeline.TimelineEvent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContentProvider
|
||||
import io.element.android.features.poll.api.pollcontent.PollContentView
|
||||
@@ -23,19 +23,19 @@ import kotlinx.collections.immutable.toImmutableList
|
||||
@Composable
|
||||
fun TimelineItemPollView(
|
||||
content: TimelineItemPollContent,
|
||||
eventSink: (TimelineEvents.TimelineItemPollEvents) -> Unit,
|
||||
eventSink: (TimelineEvent.TimelineItemPollEvent) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
fun onSelectAnswer(pollStartId: EventId, answerId: String) {
|
||||
eventSink(TimelineEvents.SelectPollAnswer(pollStartId, answerId))
|
||||
eventSink(TimelineEvent.SelectPollAnswer(pollStartId, answerId))
|
||||
}
|
||||
|
||||
fun onEndPoll(pollStartId: EventId) {
|
||||
eventSink(TimelineEvents.EndPoll(pollStartId))
|
||||
eventSink(TimelineEvent.EndPoll(pollStartId))
|
||||
}
|
||||
|
||||
fun onEditPoll(pollStartId: EventId) {
|
||||
eventSink(TimelineEvents.EditPoll(pollStartId))
|
||||
eventSink(TimelineEvent.EditPoll(pollStartId))
|
||||
}
|
||||
|
||||
PollContentView(
|
||||
|
||||
@@ -11,7 +11,7 @@ package io.element.android.features.messages.impl.timeline.components.reactionsu
|
||||
import io.element.android.features.messages.impl.timeline.model.AggregatedReaction
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
|
||||
sealed interface ReactionSummaryEvents {
|
||||
data object Clear : ReactionSummaryEvents
|
||||
data class ShowReactionSummary(val eventId: EventId, val reactions: List<AggregatedReaction>, val selectedKey: String) : ReactionSummaryEvents
|
||||
sealed interface ReactionSummaryEvent {
|
||||
data object Clear : ReactionSummaryEvent
|
||||
data class ShowReactionSummary(val eventId: EventId, val reactions: List<AggregatedReaction>, val selectedKey: String) : ReactionSummaryEvent
|
||||
}
|
||||
@@ -37,14 +37,14 @@ class ReactionSummaryPresenter(
|
||||
}
|
||||
val targetWithAvatars = populateSenderAvatars(members = membersState.roomMembers().orEmpty().toImmutableList(), summary = target.value)
|
||||
|
||||
fun handleEvent(event: ReactionSummaryEvents) {
|
||||
fun handleEvent(event: ReactionSummaryEvent) {
|
||||
when (event) {
|
||||
is ReactionSummaryEvents.ShowReactionSummary -> target.value = ReactionSummaryState.Summary(
|
||||
is ReactionSummaryEvent.ShowReactionSummary -> target.value = ReactionSummaryState.Summary(
|
||||
reactions = event.reactions.toImmutableList(),
|
||||
selectedKey = event.selectedKey,
|
||||
selectedEventId = event.eventId
|
||||
)
|
||||
ReactionSummaryEvents.Clear -> target.value = null
|
||||
ReactionSummaryEvent.Clear -> target.value = null
|
||||
}
|
||||
}
|
||||
return ReactionSummaryState(
|
||||
|
||||
@@ -14,7 +14,7 @@ import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
data class ReactionSummaryState(
|
||||
val target: Summary?,
|
||||
val eventSink: (ReactionSummaryEvents) -> Unit
|
||||
val eventSink: (ReactionSummaryEvent) -> Unit
|
||||
) {
|
||||
data class Summary(
|
||||
val reactions: ImmutableList<AggregatedReaction>,
|
||||
|
||||
@@ -84,7 +84,7 @@ fun ReactionSummaryView(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
fun onDismiss() {
|
||||
state.eventSink(ReactionSummaryEvents.Clear)
|
||||
state.eventSink(ReactionSummaryEvent.Clear)
|
||||
}
|
||||
|
||||
if (state.target != null) {
|
||||
|
||||
@@ -55,7 +55,7 @@ internal fun ReadReceiptBottomSheet(
|
||||
onDismissRequest = {
|
||||
coroutineScope.launch {
|
||||
sheetState.hide()
|
||||
state.eventSink(ReadReceiptBottomSheetEvents.Dismiss)
|
||||
state.eventSink(ReadReceiptBottomSheetEvent.Dismiss)
|
||||
}
|
||||
}
|
||||
) {
|
||||
@@ -64,7 +64,7 @@ internal fun ReadReceiptBottomSheet(
|
||||
onUserDataClick = {
|
||||
coroutineScope.launch {
|
||||
sheetState.hide()
|
||||
state.eventSink(ReadReceiptBottomSheetEvents.Dismiss)
|
||||
state.eventSink(ReadReceiptBottomSheetEvent.Dismiss)
|
||||
onUserDataClick.invoke(it)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -10,7 +10,7 @@ package io.element.android.features.messages.impl.timeline.components.receipt.bo
|
||||
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
|
||||
sealed interface ReadReceiptBottomSheetEvents {
|
||||
data class EventSelected(val event: TimelineItem.Event) : ReadReceiptBottomSheetEvents
|
||||
data object Dismiss : ReadReceiptBottomSheetEvents
|
||||
sealed interface ReadReceiptBottomSheetEvent {
|
||||
data class EventSelected(val event: TimelineItem.Event) : ReadReceiptBottomSheetEvent
|
||||
data object Dismiss : ReadReceiptBottomSheetEvent
|
||||
}
|
||||
@@ -23,13 +23,13 @@ class ReadReceiptBottomSheetPresenter : Presenter<ReadReceiptBottomSheetState> {
|
||||
override fun present(): ReadReceiptBottomSheetState {
|
||||
var selectedEvent: TimelineItem.Event? by remember { mutableStateOf(null) }
|
||||
|
||||
fun handleEvent(event: ReadReceiptBottomSheetEvents) {
|
||||
fun handleEvent(event: ReadReceiptBottomSheetEvent) {
|
||||
@Suppress("LiftReturnOrAssignment")
|
||||
when (event) {
|
||||
is ReadReceiptBottomSheetEvents.EventSelected -> {
|
||||
is ReadReceiptBottomSheetEvent.EventSelected -> {
|
||||
selectedEvent = event.event
|
||||
}
|
||||
ReadReceiptBottomSheetEvents.Dismiss -> {
|
||||
ReadReceiptBottomSheetEvent.Dismiss -> {
|
||||
selectedEvent = null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,5 +12,5 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
|
||||
data class ReadReceiptBottomSheetState(
|
||||
val selectedEvent: TimelineItem.Event?,
|
||||
val eventSink: (ReadReceiptBottomSheetEvents) -> Unit,
|
||||
val eventSink: (ReadReceiptBottomSheetEvent) -> Unit,
|
||||
)
|
||||
|
||||
@@ -13,7 +13,7 @@ package io.element.android.features.messages.impl
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import im.vector.app.features.analytics.plan.PinUnpinAction
|
||||
import io.element.android.features.messages.impl.actionlist.ActionListEvents
|
||||
import io.element.android.features.messages.impl.actionlist.ActionListEvent
|
||||
import io.element.android.features.messages.impl.actionlist.ActionListState
|
||||
import io.element.android.features.messages.impl.actionlist.anActionListState
|
||||
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
|
||||
@@ -28,7 +28,7 @@ import io.element.android.features.messages.impl.pinned.banner.aLoadedPinnedMess
|
||||
import io.element.android.features.messages.impl.timeline.FakeMarkAsFullyRead
|
||||
import io.element.android.features.messages.impl.timeline.MarkAsFullyRead
|
||||
import io.element.android.features.messages.impl.timeline.TimelineController
|
||||
import io.element.android.features.messages.impl.timeline.TimelineEvents
|
||||
import io.element.android.features.messages.impl.timeline.TimelineEvent
|
||||
import io.element.android.features.messages.impl.timeline.aTimelineState
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItemThreadInfo
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent
|
||||
@@ -185,14 +185,14 @@ class MessagesPresenterTest {
|
||||
presenter.testWithLifecycleOwner {
|
||||
skipItems(1)
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(MessagesEvents.ToggleReaction("👍", AN_EVENT_ID.toEventOrTransactionId()))
|
||||
initialState.eventSink(MessagesEvent.ToggleReaction("👍", AN_EVENT_ID.toEventOrTransactionId()))
|
||||
advanceUntilIdle()
|
||||
assert(toggleReactionSuccess)
|
||||
.isCalledOnce()
|
||||
.with(value("👍"), value(AN_EVENT_ID.toEventOrTransactionId()))
|
||||
// No crashes when sending a reaction failed
|
||||
timeline.toggleReactionLambda = toggleReactionFailure
|
||||
initialState.eventSink(MessagesEvents.ToggleReaction("👍", AN_EVENT_ID.toEventOrTransactionId()))
|
||||
initialState.eventSink(MessagesEvent.ToggleReaction("👍", AN_EVENT_ID.toEventOrTransactionId()))
|
||||
advanceUntilIdle()
|
||||
assert(toggleReactionFailure)
|
||||
.isCalledOnce()
|
||||
@@ -230,8 +230,8 @@ class MessagesPresenterTest {
|
||||
)
|
||||
presenter.testWithLifecycleOwner {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(MessagesEvents.ToggleReaction("👍", AN_EVENT_ID.toEventOrTransactionId()))
|
||||
initialState.eventSink(MessagesEvents.ToggleReaction("👍", AN_EVENT_ID.toEventOrTransactionId()))
|
||||
initialState.eventSink(MessagesEvent.ToggleReaction("👍", AN_EVENT_ID.toEventOrTransactionId()))
|
||||
initialState.eventSink(MessagesEvent.ToggleReaction("👍", AN_EVENT_ID.toEventOrTransactionId()))
|
||||
advanceUntilIdle()
|
||||
assert(toggleReactionSuccess)
|
||||
.isCalledExactly(2)
|
||||
@@ -253,7 +253,7 @@ class MessagesPresenterTest {
|
||||
val presenter = createMessagesPresenter(navigator = navigator)
|
||||
presenter.testWithLifecycleOwner {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.Forward, aMessageEvent()))
|
||||
initialState.eventSink(MessagesEvent.HandleAction(TimelineItemAction.Forward, aMessageEvent()))
|
||||
assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None)
|
||||
onForwardEventClickLambda.assertions().isCalledOnce().with(value(AN_EVENT_ID))
|
||||
}
|
||||
@@ -266,7 +266,7 @@ class MessagesPresenterTest {
|
||||
val presenter = createMessagesPresenter(clipboardHelper = clipboardHelper)
|
||||
presenter.testWithLifecycleOwner {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.CopyText, event))
|
||||
initialState.eventSink(MessagesEvent.HandleAction(TimelineItemAction.CopyText, event))
|
||||
skipItems(2)
|
||||
assertThat(clipboardHelper.clipboardContents).isEqualTo((event.content as TimelineItemTextContent).body)
|
||||
}
|
||||
@@ -289,7 +289,7 @@ class MessagesPresenterTest {
|
||||
)
|
||||
presenter.testWithLifecycleOwner {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.CopyLink, event))
|
||||
initialState.eventSink(MessagesEvent.HandleAction(TimelineItemAction.CopyLink, event))
|
||||
skipItems(2)
|
||||
assertThat(clipboardHelper.clipboardContents).isEqualTo("a link")
|
||||
}
|
||||
@@ -303,7 +303,7 @@ class MessagesPresenterTest {
|
||||
)
|
||||
presenter.testWithLifecycleOwner {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.Reply, aMessageEvent()))
|
||||
initialState.eventSink(MessagesEvent.HandleAction(TimelineItemAction.Reply, aMessageEvent()))
|
||||
awaitItem()
|
||||
composerRecorder.assertSingle(
|
||||
MessageComposerEvent.SetMode(
|
||||
@@ -321,7 +321,7 @@ class MessagesPresenterTest {
|
||||
val presenter = createMessagesPresenter()
|
||||
presenter.testWithLifecycleOwner {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.Reply, aMessageEvent(eventId = null)))
|
||||
initialState.eventSink(MessagesEvent.HandleAction(TimelineItemAction.Reply, aMessageEvent(eventId = null)))
|
||||
skipItems(1)
|
||||
}
|
||||
}
|
||||
@@ -354,7 +354,7 @@ class MessagesPresenterTest {
|
||||
formattedFileSize = "4MB"
|
||||
)
|
||||
)
|
||||
initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.Reply, mediaMessage))
|
||||
initialState.eventSink(MessagesEvent.HandleAction(TimelineItemAction.Reply, mediaMessage))
|
||||
awaitItem()
|
||||
composerRecorder.assertSingle(
|
||||
MessageComposerEvent.SetMode(
|
||||
@@ -396,7 +396,7 @@ class MessagesPresenterTest {
|
||||
formattedFileSize = "50MB"
|
||||
)
|
||||
)
|
||||
initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.Reply, mediaMessage))
|
||||
initialState.eventSink(MessagesEvent.HandleAction(TimelineItemAction.Reply, mediaMessage))
|
||||
awaitItem()
|
||||
composerRecorder.assertSingle(
|
||||
MessageComposerEvent.SetMode(
|
||||
@@ -431,7 +431,7 @@ class MessagesPresenterTest {
|
||||
fileExtension = "pdf",
|
||||
)
|
||||
)
|
||||
initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.Reply, mediaMessage))
|
||||
initialState.eventSink(MessagesEvent.HandleAction(TimelineItemAction.Reply, mediaMessage))
|
||||
awaitItem()
|
||||
composerRecorder.assertSingle(
|
||||
MessageComposerEvent.SetMode(
|
||||
@@ -452,7 +452,7 @@ class MessagesPresenterTest {
|
||||
)
|
||||
presenter.testWithLifecycleOwner {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.Edit, aMessageEvent()))
|
||||
initialState.eventSink(MessagesEvent.HandleAction(TimelineItemAction.Edit, aMessageEvent()))
|
||||
awaitItem()
|
||||
composerRecorder.assertSingle(
|
||||
MessageComposerEvent.SetMode(
|
||||
@@ -474,7 +474,7 @@ class MessagesPresenterTest {
|
||||
val presenter = createMessagesPresenter(navigator = navigator)
|
||||
presenter.testWithLifecycleOwner {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.EditPoll, aMessageEvent(content = aTimelineItemPollContent())))
|
||||
initialState.eventSink(MessagesEvent.HandleAction(TimelineItemAction.EditPoll, aMessageEvent(content = aTimelineItemPollContent())))
|
||||
awaitItem()
|
||||
onEditPollClickLambda.assertions().isCalledOnce().with(value(AN_EVENT_ID))
|
||||
}
|
||||
@@ -482,13 +482,13 @@ class MessagesPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - handle action end poll`() = runTest {
|
||||
val timelineEventSink = EventsRecorder<TimelineEvents>()
|
||||
val timelineEventSink = EventsRecorder<TimelineEvent>()
|
||||
val presenter = createMessagesPresenter(timelineEventSink = timelineEventSink)
|
||||
presenter.testWithLifecycleOwner {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.EndPoll, aMessageEvent(content = aTimelineItemPollContent())))
|
||||
initialState.eventSink(MessagesEvent.HandleAction(TimelineItemAction.EndPoll, aMessageEvent(content = aTimelineItemPollContent())))
|
||||
delay(1)
|
||||
timelineEventSink.assertSingle(TimelineEvents.EndPoll(AN_EVENT_ID))
|
||||
timelineEventSink.assertSingle(TimelineEvent.EndPoll(AN_EVENT_ID))
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
}
|
||||
@@ -516,7 +516,7 @@ class MessagesPresenterTest {
|
||||
presenter.testWithLifecycleOwner {
|
||||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent()
|
||||
initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.Redact, messageEvent))
|
||||
initialState.eventSink(MessagesEvent.HandleAction(TimelineItemAction.Redact, messageEvent))
|
||||
awaitItem()
|
||||
assert(redactEventLambda)
|
||||
.isCalledOnce()
|
||||
@@ -533,22 +533,12 @@ class MessagesPresenterTest {
|
||||
val presenter = createMessagesPresenter(navigator = navigator)
|
||||
presenter.testWithLifecycleOwner {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.ReportContent, aMessageEvent()))
|
||||
initialState.eventSink(MessagesEvent.HandleAction(TimelineItemAction.ReportContent, aMessageEvent()))
|
||||
assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None)
|
||||
onReportContentClickLambda.assertions().isCalledOnce().with(value(AN_EVENT_ID), value(A_USER_ID))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - handle dismiss action`() = runTest {
|
||||
val presenter = createMessagesPresenter()
|
||||
presenter.testWithLifecycleOwner {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(MessagesEvents.Dismiss)
|
||||
assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - handle action show developer info`() = runTest {
|
||||
val onShowEventDebugInfoClickLambda = lambdaRecorder { _: EventId?, _: TimelineItemDebugInfo -> }
|
||||
@@ -558,7 +548,7 @@ class MessagesPresenterTest {
|
||||
val presenter = createMessagesPresenter(navigator = navigator)
|
||||
presenter.testWithLifecycleOwner {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.ViewSource, aMessageEvent()))
|
||||
initialState.eventSink(MessagesEvent.HandleAction(TimelineItemAction.ViewSource, aMessageEvent()))
|
||||
assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None)
|
||||
onShowEventDebugInfoClickLambda.assertions().isCalledOnce().with(value(AN_EVENT_ID), value(aTimelineItemDebugInfo()))
|
||||
}
|
||||
@@ -586,7 +576,7 @@ class MessagesPresenterTest {
|
||||
val focusedState = awaitItem()
|
||||
assertThat(focusedState.showReinvitePrompt).isTrue()
|
||||
// If it's dismissed then we stop showing the alert
|
||||
initialState.eventSink(MessagesEvents.InviteDialogDismissed(InviteDialogAction.Cancel))
|
||||
initialState.eventSink(MessagesEvent.InviteDialogDismissed(InviteDialogAction.Cancel))
|
||||
skipItems(1)
|
||||
val dismissedState = awaitItem()
|
||||
assertThat(dismissedState.showReinvitePrompt).isFalse()
|
||||
@@ -658,7 +648,7 @@ class MessagesPresenterTest {
|
||||
val presenter = createMessagesPresenter(joinedRoom = room)
|
||||
presenter.testWithLifecycleOwner {
|
||||
val initialState = consumeItemsUntilTimeout().last()
|
||||
initialState.eventSink(MessagesEvents.InviteDialogDismissed(InviteDialogAction.Invite))
|
||||
initialState.eventSink(MessagesEvent.InviteDialogDismissed(InviteDialogAction.Invite))
|
||||
skipItems(1)
|
||||
val loadingState = awaitItem()
|
||||
assertThat(loadingState.inviteProgress.isLoading()).isTrue()
|
||||
@@ -690,7 +680,7 @@ class MessagesPresenterTest {
|
||||
val presenter = createMessagesPresenter(joinedRoom = room)
|
||||
presenter.testWithLifecycleOwner {
|
||||
val initialState = consumeItemsUntilTimeout().last()
|
||||
initialState.eventSink(MessagesEvents.InviteDialogDismissed(InviteDialogAction.Invite))
|
||||
initialState.eventSink(MessagesEvent.InviteDialogDismissed(InviteDialogAction.Invite))
|
||||
skipItems(1)
|
||||
val loadingState = consumeItemsUntilPredicate { state ->
|
||||
state.inviteProgress.isLoading()
|
||||
@@ -714,7 +704,7 @@ class MessagesPresenterTest {
|
||||
val presenter = createMessagesPresenter(joinedRoom = room)
|
||||
presenter.testWithLifecycleOwner {
|
||||
val initialState = consumeItemsUntilTimeout().last()
|
||||
initialState.eventSink(MessagesEvents.InviteDialogDismissed(InviteDialogAction.Invite))
|
||||
initialState.eventSink(MessagesEvent.InviteDialogDismissed(InviteDialogAction.Invite))
|
||||
skipItems(1)
|
||||
val loadingState = awaitItem()
|
||||
assertThat(loadingState.inviteProgress.isLoading()).isTrue()
|
||||
@@ -743,7 +733,7 @@ class MessagesPresenterTest {
|
||||
val presenter = createMessagesPresenter(joinedRoom = room)
|
||||
presenter.testWithLifecycleOwner {
|
||||
val initialState = consumeItemsUntilTimeout().last()
|
||||
initialState.eventSink(MessagesEvents.InviteDialogDismissed(InviteDialogAction.Invite))
|
||||
initialState.eventSink(MessagesEvent.InviteDialogDismissed(InviteDialogAction.Invite))
|
||||
|
||||
val loadingState = consumeItemsUntilPredicate { state ->
|
||||
state.inviteProgress.isLoading()
|
||||
@@ -840,7 +830,7 @@ class MessagesPresenterTest {
|
||||
val poll = aMessageEvent(
|
||||
content = aTimelineItemPollContent()
|
||||
)
|
||||
initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.Reply, poll))
|
||||
initialState.eventSink(MessagesEvent.HandleAction(TimelineItemAction.Reply, poll))
|
||||
skipItems(1)
|
||||
composerRecorder.assertSingle(
|
||||
MessageComposerEvent.SetMode(
|
||||
@@ -878,11 +868,11 @@ class MessagesPresenterTest {
|
||||
val initialState = awaitItem()
|
||||
|
||||
timeline.pinEventLambda = successPinEventLambda
|
||||
initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.Pin, messageEvent))
|
||||
initialState.eventSink(MessagesEvent.HandleAction(TimelineItemAction.Pin, messageEvent))
|
||||
assert(successPinEventLambda).isCalledOnce().with(value(messageEvent.eventId))
|
||||
|
||||
timeline.pinEventLambda = failurePinEventLambda
|
||||
initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.Pin, messageEvent))
|
||||
initialState.eventSink(MessagesEvent.HandleAction(TimelineItemAction.Pin, messageEvent))
|
||||
assert(failurePinEventLambda).isCalledOnce().with(value(messageEvent.eventId))
|
||||
skipItems(1)
|
||||
assertThat(awaitItem().snackbarMessage).isNotNull()
|
||||
@@ -918,11 +908,11 @@ class MessagesPresenterTest {
|
||||
val initialState = awaitItem()
|
||||
|
||||
timeline.unpinEventLambda = successUnpinEventLambda
|
||||
initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.Unpin, messageEvent))
|
||||
initialState.eventSink(MessagesEvent.HandleAction(TimelineItemAction.Unpin, messageEvent))
|
||||
assert(successUnpinEventLambda).isCalledOnce().with(value(messageEvent.eventId))
|
||||
|
||||
timeline.unpinEventLambda = failureUnpinEventLambda
|
||||
initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.Unpin, messageEvent))
|
||||
initialState.eventSink(MessagesEvent.HandleAction(TimelineItemAction.Unpin, messageEvent))
|
||||
assert(failureUnpinEventLambda).isCalledOnce().with(value(messageEvent.eventId))
|
||||
skipItems(1)
|
||||
assertThat(awaitItem().snackbarMessage).isNotNull()
|
||||
@@ -946,7 +936,7 @@ class MessagesPresenterTest {
|
||||
)
|
||||
presenter.testWithLifecycleOwner {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.EditCaption, messageEvent))
|
||||
initialState.eventSink(MessagesEvent.HandleAction(TimelineItemAction.EditCaption, messageEvent))
|
||||
awaitItem()
|
||||
composerRecorder.assertSingle(
|
||||
MessageComposerEvent.SetMode(
|
||||
@@ -972,7 +962,7 @@ class MessagesPresenterTest {
|
||||
)
|
||||
presenter.testWithLifecycleOwner {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.AddCaption, messageEvent))
|
||||
initialState.eventSink(MessagesEvent.HandleAction(TimelineItemAction.AddCaption, messageEvent))
|
||||
awaitItem()
|
||||
composerRecorder.assertSingle(
|
||||
MessageComposerEvent.SetMode(
|
||||
@@ -1010,7 +1000,7 @@ class MessagesPresenterTest {
|
||||
presenter.testWithLifecycleOwner {
|
||||
skipItems(1)
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.RemoveCaption, messageEvent))
|
||||
initialState.eventSink(MessagesEvent.HandleAction(TimelineItemAction.RemoveCaption, messageEvent))
|
||||
editCaptionLambda.assertions().isCalledOnce().with(value(AN_EVENT_ID.toEventOrTransactionId()), value(null), value(null))
|
||||
}
|
||||
}
|
||||
@@ -1024,7 +1014,7 @@ class MessagesPresenterTest {
|
||||
presenter.testWithLifecycleOwner {
|
||||
skipItems(1)
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.ViewInTimeline, messageEvent))
|
||||
initialState.eventSink(MessagesEvent.HandleAction(TimelineItemAction.ViewInTimeline, messageEvent))
|
||||
// No op!
|
||||
}
|
||||
}
|
||||
@@ -1118,7 +1108,7 @@ class MessagesPresenterTest {
|
||||
presenter.testWithLifecycleOwner {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(
|
||||
MessagesEvents.HandleAction(
|
||||
MessagesEvent.HandleAction(
|
||||
action = TimelineItemAction.ReplyInThread,
|
||||
event = aMessageEvent(threadInfo = TimelineItemThreadInfo.ThreadResponse(A_THREAD_ID))
|
||||
)
|
||||
@@ -1140,7 +1130,7 @@ class MessagesPresenterTest {
|
||||
presenter.testWithLifecycleOwner {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(
|
||||
MessagesEvents.HandleAction(
|
||||
MessagesEvent.HandleAction(
|
||||
action = TimelineItemAction.ReplyInThread,
|
||||
event = aMessageEvent(
|
||||
// The event id will be used as the thread id instead
|
||||
@@ -1165,7 +1155,7 @@ class MessagesPresenterTest {
|
||||
)
|
||||
presenter.testWithLifecycleOwner {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.ReplyInThread, aMessageEvent()))
|
||||
initialState.eventSink(MessagesEvent.HandleAction(TimelineItemAction.ReplyInThread, aMessageEvent()))
|
||||
awaitItem()
|
||||
composerRecorder.assertSingle(
|
||||
MessageComposerEvent.SetMode(
|
||||
@@ -1192,7 +1182,7 @@ class MessagesPresenterTest {
|
||||
)
|
||||
presenter.testWithLifecycleOwner {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(MessagesEvents.MarkAsFullyReadAndExit)
|
||||
initialState.eventSink(MessagesEvent.MarkAsFullyReadAndExit)
|
||||
|
||||
runCurrent()
|
||||
|
||||
@@ -1216,7 +1206,7 @@ class MessagesPresenterTest {
|
||||
)
|
||||
presenter.testWithLifecycleOwner {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(MessagesEvents.MarkAsFullyReadAndExit)
|
||||
initialState.eventSink(MessagesEvent.MarkAsFullyReadAndExit)
|
||||
|
||||
runCurrent()
|
||||
|
||||
@@ -1273,7 +1263,7 @@ class MessagesPresenterTest {
|
||||
navigator: FakeMessagesNavigator = FakeMessagesNavigator(),
|
||||
clipboardHelper: FakeClipboardHelper = FakeClipboardHelper(),
|
||||
analyticsService: FakeAnalyticsService = FakeAnalyticsService(),
|
||||
timelineEventSink: (TimelineEvents) -> Unit = {},
|
||||
timelineEventSink: (TimelineEvent) -> Unit = {},
|
||||
permalinkParser: PermalinkParser = FakePermalinkParser(),
|
||||
messageComposerPresenter: Presenter<MessageComposerState> = Presenter {
|
||||
aMessageComposerState(
|
||||
@@ -1286,7 +1276,7 @@ class MessagesPresenterTest {
|
||||
},
|
||||
encryptionService: FakeEncryptionService = FakeEncryptionService(),
|
||||
featureFlagService: FakeFeatureFlagService = FakeFeatureFlagService(),
|
||||
actionListEventSink: (ActionListEvents) -> Unit = {},
|
||||
actionListEventSink: (ActionListEvent) -> Unit = {},
|
||||
addRecentEmoji: AddRecentEmoji = AddRecentEmoji { _ -> lambdaError() },
|
||||
markAsFullyRead: MarkAsFullyRead = FakeMarkAsFullyRead(),
|
||||
): MessagesPresenter {
|
||||
|
||||
@@ -30,7 +30,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import io.element.android.emojibasebindings.Emoji
|
||||
import io.element.android.emojibasebindings.EmojibaseCategory
|
||||
import io.element.android.emojibasebindings.EmojibaseStore
|
||||
import io.element.android.features.messages.impl.actionlist.ActionListEvents
|
||||
import io.element.android.features.messages.impl.actionlist.ActionListEvent
|
||||
import io.element.android.features.messages.impl.actionlist.ActionListState
|
||||
import io.element.android.features.messages.impl.actionlist.anActionListState
|
||||
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
|
||||
@@ -40,17 +40,17 @@ import io.element.android.features.messages.impl.messagecomposer.aMessageCompose
|
||||
import io.element.android.features.messages.impl.pinned.banner.PinnedMessagesBannerItem
|
||||
import io.element.android.features.messages.impl.pinned.banner.aLoadedPinnedMessagesBannerState
|
||||
import io.element.android.features.messages.impl.timeline.FOCUS_ON_PINNED_EVENT_DEBOUNCE_DURATION_IN_MILLIS
|
||||
import io.element.android.features.messages.impl.timeline.TimelineEvents
|
||||
import io.element.android.features.messages.impl.timeline.TimelineEvent
|
||||
import io.element.android.features.messages.impl.timeline.aTimelineItemEvent
|
||||
import io.element.android.features.messages.impl.timeline.aTimelineItemList
|
||||
import io.element.android.features.messages.impl.timeline.aTimelineItemReadReceipts
|
||||
import io.element.android.features.messages.impl.timeline.aTimelineRoomInfo
|
||||
import io.element.android.features.messages.impl.timeline.aTimelineState
|
||||
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionEvents
|
||||
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionEvent
|
||||
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionState
|
||||
import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryEvents
|
||||
import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryEvent
|
||||
import io.element.android.features.messages.impl.timeline.components.receipt.aReadReceiptData
|
||||
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetEvents
|
||||
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetEvent
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
@@ -88,7 +88,7 @@ class MessagesViewTest {
|
||||
|
||||
@Test
|
||||
fun `clicking on back invoke expected callback`() {
|
||||
val eventsRecorder = EventsRecorder<MessagesEvents>(expectEvents = false)
|
||||
val eventsRecorder = EventsRecorder<MessagesEvent>(expectEvents = false)
|
||||
val state = aMessagesState(
|
||||
eventSink = eventsRecorder
|
||||
)
|
||||
@@ -103,7 +103,7 @@ class MessagesViewTest {
|
||||
|
||||
@Test
|
||||
fun `clicking on room name invoke expected callback`() {
|
||||
val eventsRecorder = EventsRecorder<MessagesEvents>(expectEvents = false)
|
||||
val eventsRecorder = EventsRecorder<MessagesEvent>(expectEvents = false)
|
||||
val state = aMessagesState(
|
||||
eventSink = eventsRecorder
|
||||
)
|
||||
@@ -118,7 +118,7 @@ class MessagesViewTest {
|
||||
|
||||
@Test
|
||||
fun `clicking on join call invoke expected callback`() {
|
||||
val eventsRecorder = EventsRecorder<MessagesEvents>(expectEvents = false)
|
||||
val eventsRecorder = EventsRecorder<MessagesEvent>(expectEvents = false)
|
||||
val state = aMessagesState(
|
||||
eventSink = eventsRecorder
|
||||
)
|
||||
@@ -134,7 +134,7 @@ class MessagesViewTest {
|
||||
|
||||
@Test
|
||||
fun `clicking on an Event invoke expected callback`() {
|
||||
val eventsRecorder = EventsRecorder<MessagesEvents>(expectEvents = false)
|
||||
val eventsRecorder = EventsRecorder<MessagesEvent>(expectEvents = false)
|
||||
val state = aMessagesState(
|
||||
timelineState = aTimelineState(
|
||||
timelineItems = aTimelineItemList(aTimelineItemTextContent()),
|
||||
@@ -183,7 +183,7 @@ class MessagesViewTest {
|
||||
userHasPermissionToSendReaction: Boolean = false,
|
||||
userCanPinEvent: Boolean = false,
|
||||
) {
|
||||
val eventsRecorder = EventsRecorder<ActionListEvents>()
|
||||
val eventsRecorder = EventsRecorder<ActionListEvent>()
|
||||
val state = aMessagesState(
|
||||
actionListState = anActionListState(
|
||||
eventSink = eventsRecorder
|
||||
@@ -206,7 +206,7 @@ class MessagesViewTest {
|
||||
// Cannot perform click on "Text", it's not detected. Use tag instead
|
||||
rule.onAllNodesWithTag(TestTags.messageBubble.value).onFirst().performTouchInput { longClick() }
|
||||
eventsRecorder.assertSingle(
|
||||
ActionListEvents.ComputeForMessage(
|
||||
ActionListEvent.ComputeForMessage(
|
||||
event = timelineItem,
|
||||
userEventPermissions = state.userEventPermissions,
|
||||
)
|
||||
@@ -216,7 +216,7 @@ class MessagesViewTest {
|
||||
@Test
|
||||
@Config(qualifiers = "h1024dp")
|
||||
fun `clicking on a read receipt list emits the expected Event`() {
|
||||
val eventsRecorder = EventsRecorder<ReadReceiptBottomSheetEvents>()
|
||||
val eventsRecorder = EventsRecorder<ReadReceiptBottomSheetEvent>()
|
||||
val state = aMessagesState(
|
||||
timelineState = aTimelineState(
|
||||
renderReadReceipts = true,
|
||||
@@ -239,7 +239,7 @@ class MessagesViewTest {
|
||||
state = state,
|
||||
)
|
||||
rule.onNodeWithTag(TestTags.messageReadReceipts.value, useUnmergedTree = true).performClick()
|
||||
eventsRecorder.assertSingle(ReadReceiptBottomSheetEvents.EventSelected(timelineItem))
|
||||
eventsRecorder.assertSingle(ReadReceiptBottomSheetEvent.EventSelected(timelineItem))
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -253,7 +253,7 @@ class MessagesViewTest {
|
||||
}
|
||||
|
||||
private fun swipeTest(userHasPermissionToSendMessage: Boolean) {
|
||||
val eventsRecorder = EventsRecorder<MessagesEvents>()
|
||||
val eventsRecorder = EventsRecorder<MessagesEvent>()
|
||||
val canBeRepliedEvent = aTimelineItemEvent(canBeRepliedTo = true)
|
||||
val cannotBeRepliedEvent = aTimelineItemEvent(canBeRepliedTo = false)
|
||||
val state = aMessagesState(
|
||||
@@ -273,7 +273,7 @@ class MessagesViewTest {
|
||||
onLast().performTouchInput { swipeRight(endX = 200f) }
|
||||
}
|
||||
if (userHasPermissionToSendMessage) {
|
||||
eventsRecorder.assertSingle(MessagesEvents.HandleAction(TimelineItemAction.Reply, canBeRepliedEvent))
|
||||
eventsRecorder.assertSingle(MessagesEvent.HandleAction(TimelineItemAction.Reply, canBeRepliedEvent))
|
||||
} else {
|
||||
eventsRecorder.assertEmpty()
|
||||
}
|
||||
@@ -281,7 +281,7 @@ class MessagesViewTest {
|
||||
|
||||
@Test
|
||||
fun `clicking on send location invoke expected callback`() {
|
||||
val eventsRecorder = EventsRecorder<MessagesEvents>(expectEvents = false)
|
||||
val eventsRecorder = EventsRecorder<MessagesEvent>(expectEvents = false)
|
||||
val state = aMessagesState(
|
||||
composerState = aMessageComposerState(
|
||||
showAttachmentSourcePicker = true
|
||||
@@ -299,7 +299,7 @@ class MessagesViewTest {
|
||||
|
||||
@Test
|
||||
fun `clicking on create poll invoke expected callback`() {
|
||||
val eventsRecorder = EventsRecorder<MessagesEvents>(expectEvents = false)
|
||||
val eventsRecorder = EventsRecorder<MessagesEvent>(expectEvents = false)
|
||||
val state = aMessagesState(
|
||||
composerState = aMessageComposerState(
|
||||
showAttachmentSourcePicker = true
|
||||
@@ -319,7 +319,7 @@ class MessagesViewTest {
|
||||
@Test
|
||||
@Config(qualifiers = "h1024dp")
|
||||
fun `clicking on the avatar of the sender of an Event emits the expected event`() {
|
||||
val eventsRecorder = EventsRecorder<MessagesEvents>()
|
||||
val eventsRecorder = EventsRecorder<MessagesEvent>()
|
||||
val state = aMessagesState(
|
||||
eventSink = eventsRecorder
|
||||
)
|
||||
@@ -327,7 +327,7 @@ class MessagesViewTest {
|
||||
rule.setMessagesView(state = state)
|
||||
rule.onNodeWithTag(TestTags.timelineItemSenderAvatar.value, useUnmergedTree = true).performClick()
|
||||
eventsRecorder.assertSingle(
|
||||
MessagesEvents.OnUserClicked(
|
||||
MessagesEvent.OnUserClicked(
|
||||
MatrixUser(
|
||||
userId = timelineEvent.senderId,
|
||||
displayName = timelineEvent.senderProfile.getDisplayName(),
|
||||
@@ -340,13 +340,13 @@ class MessagesViewTest {
|
||||
@Test
|
||||
@Config(qualifiers = "h1024dp")
|
||||
fun `clicking on the display name of the sender of an Event emits expected event`() {
|
||||
val eventsRecorder = EventsRecorder<MessagesEvents>()
|
||||
val eventsRecorder = EventsRecorder<MessagesEvent>()
|
||||
val state = aMessagesState(eventSink = eventsRecorder)
|
||||
val timelineEvent = state.timelineState.timelineItems.filterIsInstance<TimelineItem.Event>().first()
|
||||
rule.setMessagesView(state = state)
|
||||
rule.onNodeWithTag(TestTags.timelineItemSenderAvatar.value, useUnmergedTree = true).performClick()
|
||||
eventsRecorder.assertSingle(
|
||||
MessagesEvents.OnUserClicked(
|
||||
MessagesEvent.OnUserClicked(
|
||||
MatrixUser(
|
||||
userId = timelineEvent.senderId,
|
||||
displayName = timelineEvent.senderProfile.getDisplayName(),
|
||||
@@ -358,7 +358,7 @@ class MessagesViewTest {
|
||||
|
||||
@Test
|
||||
fun `selecting a action on a message emits the expected Event`() {
|
||||
val eventsRecorder = EventsRecorder<MessagesEvents>()
|
||||
val eventsRecorder = EventsRecorder<MessagesEvent>()
|
||||
val state = aMessagesState(
|
||||
eventSink = eventsRecorder
|
||||
)
|
||||
@@ -381,12 +381,12 @@ class MessagesViewTest {
|
||||
rule.clickOn(CommonStrings.action_edit)
|
||||
// Give time for the close animation to complete
|
||||
rule.mainClock.advanceTimeBy(milliseconds = 1_000)
|
||||
eventsRecorder.assertSingle(MessagesEvents.HandleAction(TimelineItemAction.Edit, timelineItem))
|
||||
eventsRecorder.assertSingle(MessagesEvent.HandleAction(TimelineItemAction.Edit, timelineItem))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on a reaction emits the expected Event`() {
|
||||
val eventsRecorder = EventsRecorder<MessagesEvents>()
|
||||
val eventsRecorder = EventsRecorder<MessagesEvent>()
|
||||
val state = aMessagesState(
|
||||
timelineState = aTimelineState(
|
||||
timelineItems = aTimelineItemList(aTimelineItemTextContent()),
|
||||
@@ -401,12 +401,12 @@ class MessagesViewTest {
|
||||
text = "👍️",
|
||||
useUnmergedTree = true,
|
||||
).onFirst().performClick()
|
||||
eventsRecorder.assertSingle(MessagesEvents.ToggleReaction("👍️", timelineItem.eventOrTransactionId))
|
||||
eventsRecorder.assertSingle(MessagesEvent.ToggleReaction("👍️", timelineItem.eventOrTransactionId))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `long clicking on a reaction emits the expected Event`() {
|
||||
val eventsRecorder = EventsRecorder<ReactionSummaryEvents>()
|
||||
val eventsRecorder = EventsRecorder<ReactionSummaryEvent>()
|
||||
val state = aMessagesState(
|
||||
timelineState = aTimelineState(
|
||||
timelineItems = aTimelineItemList(aTimelineItemTextContent()),
|
||||
@@ -424,12 +424,12 @@ class MessagesViewTest {
|
||||
text = "👍️",
|
||||
useUnmergedTree = true,
|
||||
).onFirst().performTouchInput { longClick() }
|
||||
eventsRecorder.assertSingle(ReactionSummaryEvents.ShowReactionSummary(timelineItem.eventId!!, timelineItem.reactionsState.reactions, "👍️"))
|
||||
eventsRecorder.assertSingle(ReactionSummaryEvent.ShowReactionSummary(timelineItem.eventId!!, timelineItem.reactionsState.reactions, "👍️"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on more reaction emits the expected Event`() {
|
||||
val eventsRecorder = EventsRecorder<CustomReactionEvents>()
|
||||
val eventsRecorder = EventsRecorder<CustomReactionEvent>()
|
||||
val state = aMessagesState(
|
||||
timelineState = aTimelineState(
|
||||
timelineItems = aTimelineItemList(aTimelineItemTextContent()),
|
||||
@@ -444,12 +444,12 @@ class MessagesViewTest {
|
||||
)
|
||||
val moreReactionContentDescription = rule.activity.getString(R.string.screen_room_timeline_add_reaction)
|
||||
rule.onAllNodesWithContentDescription(moreReactionContentDescription).onFirst().performClick()
|
||||
eventsRecorder.assertSingle(CustomReactionEvents.ShowCustomReactionSheet(timelineItem))
|
||||
eventsRecorder.assertSingle(CustomReactionEvent.ShowCustomReactionSheet(timelineItem))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on more reaction from action list emits the expected Event`() {
|
||||
val eventsRecorder = EventsRecorder<CustomReactionEvents>()
|
||||
val eventsRecorder = EventsRecorder<CustomReactionEvent>()
|
||||
val state = aMessagesState(
|
||||
timelineState = aTimelineState(
|
||||
timelineItems = aTimelineItemList(aTimelineItemTextContent()),
|
||||
@@ -478,12 +478,12 @@ class MessagesViewTest {
|
||||
rule.onNodeWithContentDescription(moreReactionContentDescription).performClick()
|
||||
// Give time for the close animation to complete
|
||||
rule.mainClock.advanceTimeBy(milliseconds = 1_000)
|
||||
eventsRecorder.assertSingle(CustomReactionEvents.ShowCustomReactionSheet(timelineItem))
|
||||
eventsRecorder.assertSingle(CustomReactionEvent.ShowCustomReactionSheet(timelineItem))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on verified user send failure from action list emits the expected Event`() {
|
||||
val eventsRecorder = EventsRecorder<TimelineEvents>()
|
||||
val eventsRecorder = EventsRecorder<TimelineEvent>()
|
||||
val state = aMessagesState()
|
||||
val timelineItem = state.timelineState.timelineItems.first() as TimelineItem.Event
|
||||
val stateWithActionListState = state.copy(
|
||||
@@ -506,14 +506,14 @@ class MessagesViewTest {
|
||||
rule.onNodeWithText(verifiedUserSendFailure).performClick()
|
||||
// Give time for the close animation to complete
|
||||
rule.mainClock.advanceTimeBy(milliseconds = 1_000)
|
||||
eventsRecorder.assertSingle(TimelineEvents.ComputeVerifiedUserSendFailure(timelineItem))
|
||||
eventsRecorder.assertSingle(TimelineEvent.ComputeVerifiedUserSendFailure(timelineItem))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on a custom emoji emits the expected Events`() {
|
||||
val aUnicode = "🙈"
|
||||
val customReactionStateEventsRecorder = EventsRecorder<CustomReactionEvents>()
|
||||
val eventsRecorder = EventsRecorder<MessagesEvents>()
|
||||
val customReactionStateEventsRecorder = EventsRecorder<CustomReactionEvent>()
|
||||
val eventsRecorder = EventsRecorder<MessagesEvent>()
|
||||
val state = aMessagesState(
|
||||
eventSink = eventsRecorder,
|
||||
)
|
||||
@@ -546,13 +546,13 @@ class MessagesViewTest {
|
||||
rule.onNodeWithText(aUnicode, useUnmergedTree = true).performClick()
|
||||
// Give time for the close animation to complete
|
||||
rule.mainClock.advanceTimeBy(milliseconds = 1_000)
|
||||
customReactionStateEventsRecorder.assertSingle(CustomReactionEvents.DismissCustomReactionSheet)
|
||||
eventsRecorder.assertSingle(MessagesEvents.ToggleReaction(aUnicode, timelineItem.eventOrTransactionId))
|
||||
customReactionStateEventsRecorder.assertSingle(CustomReactionEvent.DismissCustomReactionSheet)
|
||||
eventsRecorder.assertSingle(MessagesEvent.ToggleReaction(aUnicode, timelineItem.eventOrTransactionId))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on pinned messages banner emits the expected Event`() {
|
||||
val eventsRecorder = EventsRecorder<TimelineEvents>()
|
||||
val eventsRecorder = EventsRecorder<TimelineEvent>()
|
||||
val state = aMessagesState(
|
||||
timelineState = aTimelineState(eventSink = eventsRecorder),
|
||||
pinnedMessagesBannerState = aLoadedPinnedMessagesBannerState(
|
||||
@@ -566,12 +566,12 @@ class MessagesViewTest {
|
||||
)
|
||||
rule.setMessagesView(state = state)
|
||||
rule.onNodeWithText("This is a pinned message").performClick()
|
||||
eventsRecorder.assertSingle(TimelineEvents.FocusOnEvent(AN_EVENT_ID, debounce = FOCUS_ON_PINNED_EVENT_DEBOUNCE_DURATION_IN_MILLIS.milliseconds))
|
||||
eventsRecorder.assertSingle(TimelineEvent.FocusOnEvent(AN_EVENT_ID, debounce = FOCUS_ON_PINNED_EVENT_DEBOUNCE_DURATION_IN_MILLIS.milliseconds))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on successor room button emits expected event`() {
|
||||
val eventsRecorder = EventsRecorder<TimelineEvents>()
|
||||
val eventsRecorder = EventsRecorder<TimelineEvent>()
|
||||
val successorRoomId = RoomId("!successor:server.org")
|
||||
val state = aMessagesState(
|
||||
successorRoom = SuccessorRoom(
|
||||
@@ -584,12 +584,12 @@ class MessagesViewTest {
|
||||
val text = rule.activity.getString(R.string.screen_room_timeline_tombstoned_room_action)
|
||||
// The bottomsheet subcompose seems to make the node to appear twice
|
||||
rule.onAllNodesWithText(text).onFirst().performClick()
|
||||
eventsRecorder.assertSingle(TimelineEvents.NavigateToPredecessorOrSuccessorRoom(successorRoomId))
|
||||
eventsRecorder.assertSingle(TimelineEvent.NavigateToPredecessorOrSuccessorRoom(successorRoomId))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `no banner shown when there is no successor room`() {
|
||||
val eventsRecorder = EventsRecorder<MessagesEvents>(expectEvents = false)
|
||||
val eventsRecorder = EventsRecorder<MessagesEvent>(expectEvents = false)
|
||||
val state = aMessagesState(
|
||||
successorRoom = null,
|
||||
eventSink = eventsRecorder
|
||||
|
||||
@@ -8,9 +8,6 @@
|
||||
|
||||
package io.element.android.features.messages.impl.actionlist
|
||||
|
||||
import app.cash.molecule.RecompositionMode
|
||||
import app.cash.molecule.moleculeFlow
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.messages.impl.aUserEventPermissions
|
||||
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
|
||||
@@ -62,9 +59,7 @@ class ActionListPresenterTest {
|
||||
@Test
|
||||
fun `present - initial state`() = runTest {
|
||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.target).isEqualTo(ActionListState.Target.None)
|
||||
}
|
||||
@@ -73,13 +68,11 @@ class ActionListPresenterTest {
|
||||
@Test
|
||||
fun `present - compute for message from me redacted`() = runTest {
|
||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(isMine = true, isEditable = false, content = TimelineItemRedactedContent)
|
||||
initialState.eventSink.invoke(
|
||||
ActionListEvents.ComputeForMessage(
|
||||
ActionListEvent.ComputeForMessage(
|
||||
event = messageEvent,
|
||||
userEventPermissions = aUserEventPermissions(
|
||||
canRedactOwn = false,
|
||||
@@ -103,7 +96,7 @@ class ActionListPresenterTest {
|
||||
recentEmojis = suggestedEmojis,
|
||||
)
|
||||
)
|
||||
initialState.eventSink.invoke(ActionListEvents.Clear)
|
||||
initialState.eventSink.invoke(ActionListEvent.Clear)
|
||||
assertThat(awaitItem().target).isEqualTo(ActionListState.Target.None)
|
||||
}
|
||||
}
|
||||
@@ -111,9 +104,7 @@ class ActionListPresenterTest {
|
||||
@Test
|
||||
fun `present - compute for message from others redacted`() = runTest {
|
||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(
|
||||
isMine = false,
|
||||
@@ -121,7 +112,7 @@ class ActionListPresenterTest {
|
||||
content = TimelineItemRedactedContent
|
||||
)
|
||||
initialState.eventSink.invoke(
|
||||
ActionListEvents.ComputeForMessage(
|
||||
ActionListEvent.ComputeForMessage(
|
||||
event = messageEvent,
|
||||
userEventPermissions = aUserEventPermissions(
|
||||
canRedactOwn = false,
|
||||
@@ -145,7 +136,7 @@ class ActionListPresenterTest {
|
||||
recentEmojis = suggestedEmojis,
|
||||
)
|
||||
)
|
||||
initialState.eventSink.invoke(ActionListEvents.Clear)
|
||||
initialState.eventSink.invoke(ActionListEvent.Clear)
|
||||
assertThat(awaitItem().target).isEqualTo(ActionListState.Target.None)
|
||||
}
|
||||
}
|
||||
@@ -153,9 +144,7 @@ class ActionListPresenterTest {
|
||||
@Test
|
||||
fun `present - compute for others message`() = runTest {
|
||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(
|
||||
isMine = false,
|
||||
@@ -163,7 +152,7 @@ class ActionListPresenterTest {
|
||||
content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false, formattedBody = A_MESSAGE)
|
||||
)
|
||||
initialState.eventSink.invoke(
|
||||
ActionListEvents.ComputeForMessage(
|
||||
ActionListEvent.ComputeForMessage(
|
||||
event = messageEvent,
|
||||
userEventPermissions = aUserEventPermissions(
|
||||
canRedactOwn = false,
|
||||
@@ -193,7 +182,7 @@ class ActionListPresenterTest {
|
||||
recentEmojis = suggestedEmojis,
|
||||
)
|
||||
)
|
||||
initialState.eventSink.invoke(ActionListEvents.Clear)
|
||||
initialState.eventSink.invoke(ActionListEvent.Clear)
|
||||
assertThat(awaitItem().target).isEqualTo(ActionListState.Target.None)
|
||||
}
|
||||
}
|
||||
@@ -210,7 +199,7 @@ class ActionListPresenterTest {
|
||||
content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false, formattedBody = A_MESSAGE)
|
||||
)
|
||||
initialState.eventSink.invoke(
|
||||
ActionListEvents.ComputeForMessage(
|
||||
ActionListEvent.ComputeForMessage(
|
||||
event = messageEvent,
|
||||
userEventPermissions = aUserEventPermissions(
|
||||
canRedactOwn = false,
|
||||
@@ -240,7 +229,7 @@ class ActionListPresenterTest {
|
||||
recentEmojis = suggestedEmojis,
|
||||
)
|
||||
)
|
||||
initialState.eventSink.invoke(ActionListEvents.Clear)
|
||||
initialState.eventSink.invoke(ActionListEvent.Clear)
|
||||
assertThat(awaitItem().target).isEqualTo(ActionListState.Target.None)
|
||||
}
|
||||
}
|
||||
@@ -248,9 +237,7 @@ class ActionListPresenterTest {
|
||||
@Test
|
||||
fun `present - compute for others message cannot sent message`() = runTest {
|
||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(
|
||||
isMine = false,
|
||||
@@ -258,7 +245,7 @@ class ActionListPresenterTest {
|
||||
content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false, formattedBody = A_MESSAGE)
|
||||
)
|
||||
initialState.eventSink.invoke(
|
||||
ActionListEvents.ComputeForMessage(
|
||||
ActionListEvent.ComputeForMessage(
|
||||
event = messageEvent,
|
||||
userEventPermissions = aUserEventPermissions(
|
||||
canRedactOwn = true,
|
||||
@@ -287,7 +274,7 @@ class ActionListPresenterTest {
|
||||
recentEmojis = suggestedEmojis,
|
||||
)
|
||||
)
|
||||
initialState.eventSink.invoke(ActionListEvents.Clear)
|
||||
initialState.eventSink.invoke(ActionListEvent.Clear)
|
||||
assertThat(awaitItem().target).isEqualTo(ActionListState.Target.None)
|
||||
}
|
||||
}
|
||||
@@ -295,9 +282,7 @@ class ActionListPresenterTest {
|
||||
@Test
|
||||
fun `present - compute for others message and can redact`() = runTest {
|
||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(
|
||||
isMine = false,
|
||||
@@ -305,7 +290,7 @@ class ActionListPresenterTest {
|
||||
content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false, formattedBody = A_MESSAGE)
|
||||
)
|
||||
initialState.eventSink.invoke(
|
||||
ActionListEvents.ComputeForMessage(
|
||||
ActionListEvent.ComputeForMessage(
|
||||
event = messageEvent,
|
||||
userEventPermissions = aUserEventPermissions(
|
||||
canRedactOwn = false,
|
||||
@@ -336,7 +321,7 @@ class ActionListPresenterTest {
|
||||
recentEmojis = suggestedEmojis,
|
||||
)
|
||||
)
|
||||
initialState.eventSink.invoke(ActionListEvents.Clear)
|
||||
initialState.eventSink.invoke(ActionListEvent.Clear)
|
||||
assertThat(awaitItem().target).isEqualTo(ActionListState.Target.None)
|
||||
}
|
||||
}
|
||||
@@ -344,9 +329,7 @@ class ActionListPresenterTest {
|
||||
@Test
|
||||
fun `present - compute for others message and cannot send reaction`() = runTest {
|
||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(
|
||||
isMine = false,
|
||||
@@ -354,7 +337,7 @@ class ActionListPresenterTest {
|
||||
content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false, formattedBody = A_MESSAGE)
|
||||
)
|
||||
initialState.eventSink.invoke(
|
||||
ActionListEvents.ComputeForMessage(
|
||||
ActionListEvent.ComputeForMessage(
|
||||
event = messageEvent,
|
||||
userEventPermissions = aUserEventPermissions(
|
||||
canRedactOwn = false,
|
||||
@@ -385,7 +368,7 @@ class ActionListPresenterTest {
|
||||
recentEmojis = suggestedEmojis,
|
||||
)
|
||||
)
|
||||
initialState.eventSink.invoke(ActionListEvents.Clear)
|
||||
initialState.eventSink.invoke(ActionListEvent.Clear)
|
||||
assertThat(awaitItem().target).isEqualTo(ActionListState.Target.None)
|
||||
}
|
||||
}
|
||||
@@ -393,16 +376,14 @@ class ActionListPresenterTest {
|
||||
@Test
|
||||
fun `present - compute for my message`() = runTest {
|
||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(
|
||||
isMine = true,
|
||||
content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false, formattedBody = A_MESSAGE)
|
||||
)
|
||||
initialState.eventSink.invoke(
|
||||
ActionListEvents.ComputeForMessage(
|
||||
ActionListEvent.ComputeForMessage(
|
||||
event = messageEvent,
|
||||
userEventPermissions = aUserEventPermissions(
|
||||
canRedactOwn = true,
|
||||
@@ -433,7 +414,7 @@ class ActionListPresenterTest {
|
||||
recentEmojis = suggestedEmojis,
|
||||
)
|
||||
)
|
||||
initialState.eventSink.invoke(ActionListEvents.Clear)
|
||||
initialState.eventSink.invoke(ActionListEvent.Clear)
|
||||
assertThat(awaitItem().target).isEqualTo(ActionListState.Target.None)
|
||||
}
|
||||
}
|
||||
@@ -449,7 +430,7 @@ class ActionListPresenterTest {
|
||||
content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false, formattedBody = A_MESSAGE)
|
||||
)
|
||||
initialState.eventSink.invoke(
|
||||
ActionListEvents.ComputeForMessage(
|
||||
ActionListEvent.ComputeForMessage(
|
||||
event = messageEvent,
|
||||
userEventPermissions = aUserEventPermissions(
|
||||
canRedactOwn = true,
|
||||
@@ -480,7 +461,7 @@ class ActionListPresenterTest {
|
||||
recentEmojis = suggestedEmojis,
|
||||
)
|
||||
)
|
||||
initialState.eventSink.invoke(ActionListEvents.Clear)
|
||||
initialState.eventSink.invoke(ActionListEvent.Clear)
|
||||
assertThat(awaitItem().target).isEqualTo(ActionListState.Target.None)
|
||||
}
|
||||
}
|
||||
@@ -488,16 +469,14 @@ class ActionListPresenterTest {
|
||||
@Test
|
||||
fun `present - compute for my message cannot redact`() = runTest {
|
||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(
|
||||
isMine = true,
|
||||
content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false, formattedBody = A_MESSAGE)
|
||||
)
|
||||
initialState.eventSink.invoke(
|
||||
ActionListEvents.ComputeForMessage(
|
||||
ActionListEvent.ComputeForMessage(
|
||||
event = messageEvent,
|
||||
userEventPermissions = aUserEventPermissions(
|
||||
canRedactOwn = false,
|
||||
@@ -527,7 +506,7 @@ class ActionListPresenterTest {
|
||||
recentEmojis = suggestedEmojis,
|
||||
)
|
||||
)
|
||||
initialState.eventSink.invoke(ActionListEvents.Clear)
|
||||
initialState.eventSink.invoke(ActionListEvent.Clear)
|
||||
assertThat(awaitItem().target).isEqualTo(ActionListState.Target.None)
|
||||
}
|
||||
}
|
||||
@@ -535,16 +514,14 @@ class ActionListPresenterTest {
|
||||
@Test
|
||||
fun `present - compute for my message no permission`() = runTest {
|
||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(
|
||||
isMine = true,
|
||||
content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false, formattedBody = A_MESSAGE)
|
||||
)
|
||||
initialState.eventSink.invoke(
|
||||
ActionListEvents.ComputeForMessage(
|
||||
ActionListEvent.ComputeForMessage(
|
||||
event = messageEvent,
|
||||
userEventPermissions = aUserEventPermissions(
|
||||
canRedactOwn = false,
|
||||
@@ -571,7 +548,7 @@ class ActionListPresenterTest {
|
||||
recentEmojis = suggestedEmojis,
|
||||
)
|
||||
)
|
||||
initialState.eventSink.invoke(ActionListEvents.Clear)
|
||||
initialState.eventSink.invoke(ActionListEvent.Clear)
|
||||
assertThat(awaitItem().target).isEqualTo(ActionListState.Target.None)
|
||||
}
|
||||
}
|
||||
@@ -579,9 +556,7 @@ class ActionListPresenterTest {
|
||||
@Test
|
||||
fun `present - compute for a media item`() = runTest {
|
||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(
|
||||
isMine = true,
|
||||
@@ -589,7 +564,7 @@ class ActionListPresenterTest {
|
||||
content = aTimelineItemImageContent(),
|
||||
)
|
||||
initialState.eventSink.invoke(
|
||||
ActionListEvents.ComputeForMessage(
|
||||
ActionListEvent.ComputeForMessage(
|
||||
event = messageEvent,
|
||||
userEventPermissions = aUserEventPermissions(
|
||||
canRedactOwn = true,
|
||||
@@ -619,7 +594,7 @@ class ActionListPresenterTest {
|
||||
recentEmojis = suggestedEmojis,
|
||||
)
|
||||
)
|
||||
initialState.eventSink.invoke(ActionListEvents.Clear)
|
||||
initialState.eventSink.invoke(ActionListEvent.Clear)
|
||||
assertThat(awaitItem().target).isEqualTo(ActionListState.Target.None)
|
||||
}
|
||||
}
|
||||
@@ -627,9 +602,7 @@ class ActionListPresenterTest {
|
||||
@Test
|
||||
fun `present - compute for a media with caption item`() = runTest {
|
||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(
|
||||
isMine = true,
|
||||
@@ -639,7 +612,7 @@ class ActionListPresenterTest {
|
||||
),
|
||||
)
|
||||
initialState.eventSink.invoke(
|
||||
ActionListEvents.ComputeForMessage(
|
||||
ActionListEvent.ComputeForMessage(
|
||||
event = messageEvent,
|
||||
userEventPermissions = aUserEventPermissions(
|
||||
canRedactOwn = true,
|
||||
@@ -671,7 +644,7 @@ class ActionListPresenterTest {
|
||||
recentEmojis = suggestedEmojis,
|
||||
)
|
||||
)
|
||||
initialState.eventSink.invoke(ActionListEvents.Clear)
|
||||
initialState.eventSink.invoke(ActionListEvent.Clear)
|
||||
assertThat(awaitItem().target).isEqualTo(ActionListState.Target.None)
|
||||
}
|
||||
}
|
||||
@@ -679,9 +652,7 @@ class ActionListPresenterTest {
|
||||
@Test
|
||||
fun `present - compute for a media with caption item - other user event`() = runTest {
|
||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(
|
||||
isMine = false,
|
||||
@@ -691,7 +662,7 @@ class ActionListPresenterTest {
|
||||
),
|
||||
)
|
||||
initialState.eventSink.invoke(
|
||||
ActionListEvents.ComputeForMessage(
|
||||
ActionListEvent.ComputeForMessage(
|
||||
event = messageEvent,
|
||||
userEventPermissions = aUserEventPermissions(
|
||||
canRedactOwn = true,
|
||||
@@ -721,7 +692,7 @@ class ActionListPresenterTest {
|
||||
recentEmojis = suggestedEmojis,
|
||||
)
|
||||
)
|
||||
initialState.eventSink.invoke(ActionListEvents.Clear)
|
||||
initialState.eventSink.invoke(ActionListEvent.Clear)
|
||||
assertThat(awaitItem().target).isEqualTo(ActionListState.Target.None)
|
||||
}
|
||||
}
|
||||
@@ -729,16 +700,14 @@ class ActionListPresenterTest {
|
||||
@Test
|
||||
fun `present - compute for a state item in debug build`() = runTest {
|
||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
val stateEvent = aTimelineItemEvent(
|
||||
isMine = true,
|
||||
content = aTimelineItemStateEventContent(),
|
||||
)
|
||||
initialState.eventSink.invoke(
|
||||
ActionListEvents.ComputeForMessage(
|
||||
ActionListEvent.ComputeForMessage(
|
||||
event = stateEvent,
|
||||
userEventPermissions = aUserEventPermissions(
|
||||
canRedactOwn = false,
|
||||
@@ -762,7 +731,7 @@ class ActionListPresenterTest {
|
||||
recentEmojis = suggestedEmojis,
|
||||
)
|
||||
)
|
||||
initialState.eventSink.invoke(ActionListEvents.Clear)
|
||||
initialState.eventSink.invoke(ActionListEvent.Clear)
|
||||
assertThat(awaitItem().target).isEqualTo(ActionListState.Target.None)
|
||||
}
|
||||
}
|
||||
@@ -770,16 +739,14 @@ class ActionListPresenterTest {
|
||||
@Test
|
||||
fun `present - compute for a state item in non-debuggable build`() = runTest {
|
||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = false)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
val stateEvent = aTimelineItemEvent(
|
||||
isMine = true,
|
||||
content = aTimelineItemStateEventContent(),
|
||||
)
|
||||
initialState.eventSink.invoke(
|
||||
ActionListEvents.ComputeForMessage(
|
||||
ActionListEvent.ComputeForMessage(
|
||||
event = stateEvent,
|
||||
userEventPermissions = aUserEventPermissions(
|
||||
canRedactOwn = false,
|
||||
@@ -797,16 +764,14 @@ class ActionListPresenterTest {
|
||||
@Test
|
||||
fun `present - compute message in non-debuggable build`() = runTest {
|
||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = false)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(
|
||||
isMine = true,
|
||||
content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false, formattedBody = A_MESSAGE)
|
||||
)
|
||||
initialState.eventSink.invoke(
|
||||
ActionListEvents.ComputeForMessage(
|
||||
ActionListEvent.ComputeForMessage(
|
||||
event = messageEvent,
|
||||
userEventPermissions = aUserEventPermissions(
|
||||
canRedactOwn = true,
|
||||
@@ -836,7 +801,7 @@ class ActionListPresenterTest {
|
||||
recentEmojis = suggestedEmojis,
|
||||
)
|
||||
)
|
||||
initialState.eventSink.invoke(ActionListEvents.Clear)
|
||||
initialState.eventSink.invoke(ActionListEvent.Clear)
|
||||
assertThat(awaitItem().target).isEqualTo(ActionListState.Target.None)
|
||||
}
|
||||
}
|
||||
@@ -844,16 +809,14 @@ class ActionListPresenterTest {
|
||||
@Test
|
||||
fun `present - compute message when user can't pin`() = runTest {
|
||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(
|
||||
isMine = true,
|
||||
content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false, formattedBody = A_MESSAGE)
|
||||
)
|
||||
initialState.eventSink.invoke(
|
||||
ActionListEvents.ComputeForMessage(
|
||||
ActionListEvent.ComputeForMessage(
|
||||
event = messageEvent,
|
||||
userEventPermissions = aUserEventPermissions(
|
||||
canRedactOwn = true,
|
||||
@@ -883,7 +846,7 @@ class ActionListPresenterTest {
|
||||
recentEmojis = suggestedEmojis,
|
||||
)
|
||||
)
|
||||
initialState.eventSink.invoke(ActionListEvents.Clear)
|
||||
initialState.eventSink.invoke(ActionListEvent.Clear)
|
||||
assertThat(awaitItem().target).isEqualTo(ActionListState.Target.None)
|
||||
}
|
||||
}
|
||||
@@ -897,16 +860,14 @@ class ActionListPresenterTest {
|
||||
isDeveloperModeEnabled = true,
|
||||
room = room
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(
|
||||
isMine = true,
|
||||
content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false, formattedBody = A_MESSAGE)
|
||||
)
|
||||
initialState.eventSink.invoke(
|
||||
ActionListEvents.ComputeForMessage(
|
||||
ActionListEvent.ComputeForMessage(
|
||||
event = messageEvent,
|
||||
userEventPermissions = aUserEventPermissions(
|
||||
canRedactOwn = true,
|
||||
@@ -937,7 +898,7 @@ class ActionListPresenterTest {
|
||||
recentEmojis = suggestedEmojis,
|
||||
)
|
||||
)
|
||||
initialState.eventSink.invoke(ActionListEvents.Clear)
|
||||
initialState.eventSink.invoke(ActionListEvent.Clear)
|
||||
assertThat(awaitItem().target).isEqualTo(ActionListState.Target.None)
|
||||
}
|
||||
}
|
||||
@@ -945,9 +906,7 @@ class ActionListPresenterTest {
|
||||
@Test
|
||||
fun `present - compute message with no actions`() = runTest {
|
||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = false)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(
|
||||
isMine = true,
|
||||
@@ -959,7 +918,7 @@ class ActionListPresenterTest {
|
||||
)
|
||||
|
||||
initialState.eventSink.invoke(
|
||||
ActionListEvents.ComputeForMessage(
|
||||
ActionListEvent.ComputeForMessage(
|
||||
event = messageEvent,
|
||||
userEventPermissions = aUserEventPermissions(
|
||||
canRedactOwn = false,
|
||||
@@ -973,7 +932,7 @@ class ActionListPresenterTest {
|
||||
assertThat(awaitItem().target).isInstanceOf(ActionListState.Target.Success::class.java)
|
||||
|
||||
initialState.eventSink.invoke(
|
||||
ActionListEvents.ComputeForMessage(
|
||||
ActionListEvent.ComputeForMessage(
|
||||
event = redactedEvent,
|
||||
userEventPermissions = aUserEventPermissions(
|
||||
canRedactOwn = false,
|
||||
@@ -992,9 +951,7 @@ class ActionListPresenterTest {
|
||||
@Test
|
||||
fun `present - compute not sent message`() = runTest {
|
||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = false)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(
|
||||
// No event id, so it's not sent yet
|
||||
@@ -1005,7 +962,7 @@ class ActionListPresenterTest {
|
||||
)
|
||||
|
||||
initialState.eventSink.invoke(
|
||||
ActionListEvents.ComputeForMessage(
|
||||
ActionListEvent.ComputeForMessage(
|
||||
event = messageEvent,
|
||||
userEventPermissions = aUserEventPermissions(
|
||||
canRedactOwn = true,
|
||||
@@ -1037,9 +994,7 @@ class ActionListPresenterTest {
|
||||
@Test
|
||||
fun `present - compute for editable poll message`() = runTest {
|
||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = false)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(
|
||||
isMine = true,
|
||||
@@ -1047,7 +1002,7 @@ class ActionListPresenterTest {
|
||||
content = aTimelineItemPollContent(answerItems = aPollAnswerItemList(hasVotes = false)),
|
||||
)
|
||||
initialState.eventSink.invoke(
|
||||
ActionListEvents.ComputeForMessage(
|
||||
ActionListEvent.ComputeForMessage(
|
||||
event = messageEvent,
|
||||
userEventPermissions = aUserEventPermissions(
|
||||
canRedactOwn = true,
|
||||
@@ -1082,9 +1037,7 @@ class ActionListPresenterTest {
|
||||
@Test
|
||||
fun `present - compute for non-editable poll message`() = runTest {
|
||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = false)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(
|
||||
isMine = true,
|
||||
@@ -1092,7 +1045,7 @@ class ActionListPresenterTest {
|
||||
content = aTimelineItemPollContent(answerItems = aPollAnswerItemList(hasVotes = true)),
|
||||
)
|
||||
initialState.eventSink.invoke(
|
||||
ActionListEvents.ComputeForMessage(
|
||||
ActionListEvent.ComputeForMessage(
|
||||
event = messageEvent,
|
||||
userEventPermissions = aUserEventPermissions(
|
||||
canRedactOwn = true,
|
||||
@@ -1126,9 +1079,7 @@ class ActionListPresenterTest {
|
||||
@Test
|
||||
fun `present - compute for ended poll message`() = runTest {
|
||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = false)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(
|
||||
isMine = true,
|
||||
@@ -1136,7 +1087,7 @@ class ActionListPresenterTest {
|
||||
content = aTimelineItemPollContent(isEnded = true),
|
||||
)
|
||||
initialState.eventSink.invoke(
|
||||
ActionListEvents.ComputeForMessage(
|
||||
ActionListEvent.ComputeForMessage(
|
||||
event = messageEvent,
|
||||
userEventPermissions = aUserEventPermissions(
|
||||
canRedactOwn = true,
|
||||
@@ -1169,9 +1120,7 @@ class ActionListPresenterTest {
|
||||
@Test
|
||||
fun `present - compute for voice message`() = runTest {
|
||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = false)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(
|
||||
isMine = true,
|
||||
@@ -1181,7 +1130,7 @@ class ActionListPresenterTest {
|
||||
),
|
||||
)
|
||||
initialState.eventSink.invoke(
|
||||
ActionListEvents.ComputeForMessage(
|
||||
ActionListEvent.ComputeForMessage(
|
||||
event = messageEvent,
|
||||
userEventPermissions = aUserEventPermissions(
|
||||
canRedactOwn = true,
|
||||
@@ -1215,16 +1164,14 @@ class ActionListPresenterTest {
|
||||
@Test
|
||||
fun `present - compute for call notify`() = runTest {
|
||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(
|
||||
isMine = true,
|
||||
content = TimelineItemRtcNotificationContent(),
|
||||
)
|
||||
initialState.eventSink.invoke(
|
||||
ActionListEvents.ComputeForMessage(
|
||||
ActionListEvent.ComputeForMessage(
|
||||
event = messageEvent,
|
||||
userEventPermissions = aUserEventPermissions(
|
||||
canRedactOwn = true,
|
||||
@@ -1256,15 +1203,13 @@ class ActionListPresenterTest {
|
||||
userDisplayNameResult = { Result.success("Alice") }
|
||||
)
|
||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = false, room = room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(
|
||||
sendState = LocalEventSendState.Failed.VerifiedUserChangedIdentity(users = listOf(A_USER_ID)),
|
||||
)
|
||||
initialState.eventSink.invoke(
|
||||
ActionListEvents.ComputeForMessage(
|
||||
ActionListEvent.ComputeForMessage(
|
||||
event = messageEvent,
|
||||
userEventPermissions = aUserEventPermissions(),
|
||||
)
|
||||
@@ -1283,9 +1228,7 @@ class ActionListPresenterTest {
|
||||
timelineMode = Timeline.Mode.Thread(A_THREAD_ID),
|
||||
featureFlagService = FakeFeatureFlagService(initialState = mapOf(FeatureFlags.Threads.key to true)),
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(
|
||||
isMine = true,
|
||||
@@ -1296,7 +1239,7 @@ class ActionListPresenterTest {
|
||||
threadInfo = TimelineItemThreadInfo.ThreadResponse(threadRootId = A_THREAD_ID)
|
||||
)
|
||||
initialState.eventSink.invoke(
|
||||
ActionListEvents.ComputeForMessage(
|
||||
ActionListEvent.ComputeForMessage(
|
||||
event = messageEvent,
|
||||
userEventPermissions = aUserEventPermissions(
|
||||
canRedactOwn = true,
|
||||
@@ -1334,9 +1277,7 @@ class ActionListPresenterTest {
|
||||
isDeveloperModeEnabled = false,
|
||||
featureFlagService = FakeFeatureFlagService(initialState = mapOf(FeatureFlags.Threads.key to true)),
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(
|
||||
eventId = AN_EVENT_ID,
|
||||
@@ -1350,7 +1291,7 @@ class ActionListPresenterTest {
|
||||
assertThat(messageEvent.isRemote).isTrue()
|
||||
|
||||
initialState.eventSink.invoke(
|
||||
ActionListEvents.ComputeForMessage(
|
||||
ActionListEvent.ComputeForMessage(
|
||||
event = messageEvent,
|
||||
userEventPermissions = aUserEventPermissions(
|
||||
canRedactOwn = true,
|
||||
@@ -1388,9 +1329,7 @@ class ActionListPresenterTest {
|
||||
isDeveloperModeEnabled = false,
|
||||
featureFlagService = FakeFeatureFlagService(initialState = mapOf(FeatureFlags.Threads.key to true)),
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(
|
||||
eventId = AN_EVENT_ID,
|
||||
@@ -1405,7 +1344,7 @@ class ActionListPresenterTest {
|
||||
assertThat(messageEvent.isRemote).isTrue()
|
||||
|
||||
initialState.eventSink.invoke(
|
||||
ActionListEvents.ComputeForMessage(
|
||||
ActionListEvent.ComputeForMessage(
|
||||
event = messageEvent,
|
||||
userEventPermissions = aUserEventPermissions(
|
||||
canRedactOwn = true,
|
||||
@@ -1443,9 +1382,7 @@ class ActionListPresenterTest {
|
||||
isDeveloperModeEnabled = false,
|
||||
featureFlagService = FakeFeatureFlagService(initialState = mapOf(FeatureFlags.Threads.key to true)),
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(
|
||||
eventId = null,
|
||||
@@ -1460,7 +1397,7 @@ class ActionListPresenterTest {
|
||||
assertThat(messageEvent.isRemote).isFalse()
|
||||
|
||||
initialState.eventSink.invoke(
|
||||
ActionListEvents.ComputeForMessage(
|
||||
ActionListEvent.ComputeForMessage(
|
||||
event = messageEvent,
|
||||
userEventPermissions = aUserEventPermissions(
|
||||
canRedactOwn = true,
|
||||
@@ -1498,9 +1435,7 @@ class ActionListPresenterTest {
|
||||
isDeveloperModeEnabled = false,
|
||||
recentEmojis = GetRecentEmojis { Result.success((listOf("👍️", ":)", "❤️") + otherEmojis).toImmutableList()) },
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(
|
||||
eventId = null,
|
||||
@@ -1513,7 +1448,7 @@ class ActionListPresenterTest {
|
||||
)
|
||||
|
||||
initialState.eventSink.invoke(
|
||||
ActionListEvents.ComputeForMessage(
|
||||
ActionListEvent.ComputeForMessage(
|
||||
event = messageEvent,
|
||||
userEventPermissions = aUserEventPermissions(
|
||||
canRedactOwn = true,
|
||||
|
||||
@@ -11,11 +11,8 @@
|
||||
package io.element.android.features.messages.impl.attachments
|
||||
|
||||
import android.net.Uri
|
||||
import app.cash.molecule.RecompositionMode
|
||||
import app.cash.molecule.moleculeFlow
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.messages.impl.attachments.preview.AttachmentsPreviewEvents
|
||||
import io.element.android.features.messages.impl.attachments.preview.AttachmentsPreviewEvent
|
||||
import io.element.android.features.messages.impl.attachments.preview.AttachmentsPreviewPresenter
|
||||
import io.element.android.features.messages.impl.attachments.preview.OnDoneListener
|
||||
import io.element.android.features.messages.impl.attachments.preview.SendActionState
|
||||
@@ -111,13 +108,11 @@ class AttachmentsPreviewPresenterTest {
|
||||
mediaPreProcessor = mediaPreProcessor,
|
||||
onDoneListener = { onDoneListener() },
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false))
|
||||
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
||||
initialState.eventSink(AttachmentsPreviewEvent.SendAttachment)
|
||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = true))
|
||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.ReadyToUpload(mediaUploadInfo))
|
||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Uploading(mediaUploadInfo))
|
||||
@@ -147,16 +142,14 @@ class AttachmentsPreviewPresenterTest {
|
||||
mediaPreProcessor = mediaPreProcessor,
|
||||
onDoneListener = { onDoneListener() },
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
||||
// Pre-processing finishes
|
||||
processLatch.complete(Unit)
|
||||
advanceUntilIdle()
|
||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false))
|
||||
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
||||
initialState.eventSink(AttachmentsPreviewEvent.SendAttachment)
|
||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.ReadyToUpload(mediaUploadInfo))
|
||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Uploading(mediaUploadInfo))
|
||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Done)
|
||||
@@ -185,13 +178,11 @@ class AttachmentsPreviewPresenterTest {
|
||||
mediaPreProcessor = mediaPreProcessor,
|
||||
onDoneListener = { onDoneListener() },
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false))
|
||||
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
||||
initialState.eventSink(AttachmentsPreviewEvent.SendAttachment)
|
||||
// Pre-processing finishes
|
||||
processLatch.complete(Unit)
|
||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = true))
|
||||
@@ -216,12 +207,10 @@ class AttachmentsPreviewPresenterTest {
|
||||
},
|
||||
onDoneListener = { onDoneListener() },
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
||||
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
||||
initialState.eventSink(AttachmentsPreviewEvent.SendAttachment)
|
||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false))
|
||||
// Pre-processing finishes
|
||||
processLatch.complete(Unit)
|
||||
@@ -241,15 +230,13 @@ class AttachmentsPreviewPresenterTest {
|
||||
},
|
||||
onDoneListener = { onDoneListener() },
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
||||
// Pre-processing finishes
|
||||
processLatch.complete(Unit)
|
||||
advanceUntilIdle()
|
||||
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
||||
initialState.eventSink(AttachmentsPreviewEvent.SendAttachment)
|
||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false))
|
||||
assertThat(awaitItem().sendActionState).isInstanceOf(SendActionState.Failure::class.java)
|
||||
}
|
||||
@@ -265,12 +252,10 @@ class AttachmentsPreviewPresenterTest {
|
||||
temporaryUriDeleter = FakeTemporaryUriDeleter(deleteCallback),
|
||||
onDoneListener = { onDoneListener() },
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
||||
initialState.eventSink(AttachmentsPreviewEvents.CancelAndDismiss)
|
||||
initialState.eventSink(AttachmentsPreviewEvent.CancelAndDismiss)
|
||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Done)
|
||||
deleteCallback.assertions().isCalledOnce()
|
||||
onDoneListener.assertions().isCalledOnce()
|
||||
@@ -298,13 +283,11 @@ class AttachmentsPreviewPresenterTest {
|
||||
mediaPreProcessor = mediaPreProcessor,
|
||||
onDoneListener = { onDoneListener() },
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
||||
initialState.textEditorState.setMarkdown(A_CAPTION)
|
||||
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
||||
initialState.eventSink(AttachmentsPreviewEvent.SendAttachment)
|
||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false))
|
||||
assertThat(awaitItem().sendActionState).isInstanceOf(SendActionState.Sending.ReadyToUpload::class.java)
|
||||
assertThat(awaitItem().sendActionState).isInstanceOf(SendActionState.Sending.Uploading::class.java)
|
||||
@@ -341,13 +324,11 @@ class AttachmentsPreviewPresenterTest {
|
||||
mediaPreProcessor = mediaPreProcessor,
|
||||
onDoneListener = { onDoneListener() },
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
||||
initialState.textEditorState.setMarkdown(A_CAPTION)
|
||||
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
||||
initialState.eventSink(AttachmentsPreviewEvent.SendAttachment)
|
||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false))
|
||||
assertThat(awaitItem().sendActionState).isInstanceOf(SendActionState.Sending.ReadyToUpload::class.java)
|
||||
assertThat(awaitItem().sendActionState).isInstanceOf(SendActionState.Sending.Uploading::class.java)
|
||||
@@ -388,7 +369,7 @@ class AttachmentsPreviewPresenterTest {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
||||
initialState.textEditorState.setMarkdown(A_CAPTION)
|
||||
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
||||
initialState.eventSink(AttachmentsPreviewEvent.SendAttachment)
|
||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false))
|
||||
assertThat(awaitItem().sendActionState).isInstanceOf(SendActionState.Sending.ReadyToUpload::class.java)
|
||||
assertThat(awaitItem().sendActionState).isInstanceOf(SendActionState.Sending.Uploading::class.java)
|
||||
@@ -418,12 +399,10 @@ class AttachmentsPreviewPresenterTest {
|
||||
},
|
||||
)
|
||||
val presenter = createAttachmentsPreviewPresenter(room = room, onDoneListener = onDoneListenerResult)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
||||
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
||||
initialState.eventSink(AttachmentsPreviewEvent.SendAttachment)
|
||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false))
|
||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.ReadyToUpload(mediaUploadInfo))
|
||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Uploading(mediaUploadInfo))
|
||||
@@ -434,7 +413,7 @@ class AttachmentsPreviewPresenterTest {
|
||||
val failureState = awaitItem()
|
||||
assertThat(failureState.sendActionState).isEqualTo(SendActionState.Failure(failure, mediaUploadInfo))
|
||||
sendFileResult.assertions().isCalledOnce()
|
||||
failureState.eventSink(AttachmentsPreviewEvents.CancelAndClearSendState)
|
||||
failureState.eventSink(AttachmentsPreviewEvent.CancelAndClearSendState)
|
||||
val clearedState = awaitLastSequentialItem()
|
||||
assertThat(clearedState.sendActionState).isEqualTo(SendActionState.Sending.ReadyToUpload(mediaUploadInfo))
|
||||
}
|
||||
@@ -453,16 +432,14 @@ class AttachmentsPreviewPresenterTest {
|
||||
),
|
||||
onDoneListener = onDoneListenerResult,
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
||||
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
||||
initialState.eventSink(AttachmentsPreviewEvent.SendAttachment)
|
||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false))
|
||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.ReadyToUpload(mediaUploadInfo))
|
||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Uploading(mediaUploadInfo))
|
||||
initialState.eventSink(AttachmentsPreviewEvents.CancelAndClearSendState)
|
||||
initialState.eventSink(AttachmentsPreviewEvent.CancelAndClearSendState)
|
||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.ReadyToUpload(mediaUploadInfo))
|
||||
// The sending is cancelled and the state is kept at ReadyToUpload
|
||||
ensureAllEventsConsumed()
|
||||
@@ -503,9 +480,7 @@ class AttachmentsPreviewPresenterTest {
|
||||
}
|
||||
)
|
||||
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
assertThat(localMedia.info.fileSize).isGreaterThan(maxUploadSize)
|
||||
|
||||
consumeItemsUntilPredicate { it.mediaOptimizationSelectorState.maxUploadSize.isSuccess() }
|
||||
@@ -563,9 +538,7 @@ class AttachmentsPreviewPresenterTest {
|
||||
}
|
||||
)
|
||||
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
consumeItemsUntilPredicate {
|
||||
it.mediaOptimizationSelectorState.maxUploadSize.isSuccess() &&
|
||||
it.mediaOptimizationSelectorState.videoSizeEstimations.dataOrNull()?.isNotEmpty() == true
|
||||
|
||||
@@ -11,9 +11,6 @@ package io.element.android.features.messages.impl.attachments.video
|
||||
import android.net.Uri
|
||||
import android.util.Size
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import app.cash.molecule.RecompositionMode
|
||||
import app.cash.molecule.moleculeFlow
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.messages.test.attachments.video.FakeVideoMetadataExtractor
|
||||
import io.element.android.features.messages.test.attachments.video.FakeVideoMetadataExtractorFactory
|
||||
@@ -29,6 +26,7 @@ import io.element.android.libraries.mediaviewer.api.local.LocalMedia
|
||||
import io.element.android.libraries.mediaviewer.test.viewer.aLocalMedia
|
||||
import io.element.android.libraries.preferences.api.store.VideoCompressionPreset
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
import io.element.android.tests.testutils.test
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
@@ -46,9 +44,7 @@ class DefaultMediaOptimizationSelectorPresenterTest {
|
||||
@Test
|
||||
fun `present - initial state`() = runTest {
|
||||
val presenter = createDefaultMediaOptimizationSelectorPresenter()
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
awaitItem().run {
|
||||
// Loading
|
||||
assertThat(videoSizeEstimations).isInstanceOf(AsyncData.Loading::class.java)
|
||||
@@ -77,9 +73,7 @@ class DefaultMediaOptimizationSelectorPresenterTest {
|
||||
val presenter = createDefaultMediaOptimizationSelectorPresenter(
|
||||
localMedia = aLocalMedia(mockMediaUrl, anImageMediaInfo())
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
// Skip loading state
|
||||
skipItems(1)
|
||||
|
||||
@@ -94,9 +88,7 @@ class DefaultMediaOptimizationSelectorPresenterTest {
|
||||
@Test
|
||||
fun `present - OpenVideoPresetSelectorDialog displays it, DismissVideoPresetSelectorDialog hides it`() = runTest {
|
||||
val presenter = createDefaultMediaOptimizationSelectorPresenter()
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
// Skip loading state
|
||||
val eventSink = awaitItem().eventSink
|
||||
|
||||
@@ -115,9 +107,7 @@ class DefaultMediaOptimizationSelectorPresenterTest {
|
||||
@Test
|
||||
fun `present - SelectVideoPreset sets it and dismisses the dialog`() = runTest {
|
||||
val presenter = createDefaultMediaOptimizationSelectorPresenter()
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
// Skip loading state
|
||||
val eventSink = awaitItem().eventSink
|
||||
|
||||
@@ -139,9 +129,7 @@ class DefaultMediaOptimizationSelectorPresenterTest {
|
||||
val presenter = createDefaultMediaOptimizationSelectorPresenter(
|
||||
mediaExtractorFactory = FakeVideoMetadataExtractorFactory(FakeVideoMetadataExtractor(sizeResult = Result.failure(AN_EXCEPTION))),
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
// Skip loading state
|
||||
val eventSink = awaitItem().eventSink
|
||||
|
||||
@@ -163,9 +151,7 @@ class DefaultMediaOptimizationSelectorPresenterTest {
|
||||
)
|
||||
),
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
// Skip loading and loaded states
|
||||
val eventSink = awaitItem().eventSink
|
||||
skipItems(1)
|
||||
@@ -188,9 +174,7 @@ class DefaultMediaOptimizationSelectorPresenterTest {
|
||||
val presenter = createDefaultMediaOptimizationSelectorPresenter(
|
||||
localMedia = aLocalMedia(mockMediaUrl, anImageMediaInfo()),
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
// Skip loading state
|
||||
val eventSink = awaitItem().eventSink
|
||||
|
||||
@@ -207,9 +191,7 @@ class DefaultMediaOptimizationSelectorPresenterTest {
|
||||
val presenter = createDefaultMediaOptimizationSelectorPresenter(
|
||||
maxUploadSizeProvider = MaxUploadSizeProvider { Result.failure(AN_EXCEPTION) }
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
// Skip loading and loaded state
|
||||
skipItems(1)
|
||||
assertThat(awaitItem().maxUploadSize.dataOrNull()).isEqualTo(1024 * 1024 * 100)
|
||||
@@ -221,9 +203,7 @@ class DefaultMediaOptimizationSelectorPresenterTest {
|
||||
val presenter = createDefaultMediaOptimizationSelectorPresenter(
|
||||
featureFlagService = FakeFeatureFlagService(mapOf(FeatureFlags.SelectableMediaQuality.key to false)),
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
// Skip loading and loaded state
|
||||
skipItems(1)
|
||||
assertThat(awaitItem().displayMediaSelectorViews).isFalse()
|
||||
|
||||
@@ -47,7 +47,7 @@ class ResolveVerifiedUserSendFailurePresenterTest {
|
||||
val sentMessage = aMessageEvent()
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.None)
|
||||
initialState.eventSink(ResolveVerifiedUserSendFailureEvents.ComputeForMessage(sentMessage))
|
||||
initialState.eventSink(ResolveVerifiedUserSendFailureEvent.ComputeForMessage(sentMessage))
|
||||
ensureAllEventsConsumed()
|
||||
}
|
||||
}
|
||||
@@ -61,7 +61,7 @@ class ResolveVerifiedUserSendFailurePresenterTest {
|
||||
)
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.None)
|
||||
initialState.eventSink(ResolveVerifiedUserSendFailureEvents.ComputeForMessage(sentMessage))
|
||||
initialState.eventSink(ResolveVerifiedUserSendFailureEvent.ComputeForMessage(sentMessage))
|
||||
ensureAllEventsConsumed()
|
||||
}
|
||||
}
|
||||
@@ -75,7 +75,7 @@ class ResolveVerifiedUserSendFailurePresenterTest {
|
||||
)
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.None)
|
||||
initialState.eventSink(ResolveVerifiedUserSendFailureEvents.ComputeForMessage(failedMessage))
|
||||
initialState.eventSink(ResolveVerifiedUserSendFailureEvent.ComputeForMessage(failedMessage))
|
||||
ensureAllEventsConsumed()
|
||||
}
|
||||
}
|
||||
@@ -94,11 +94,11 @@ class ResolveVerifiedUserSendFailurePresenterTest {
|
||||
val failedMessage = aVerifiedUserHasUnsignedDeviceFailedMessage()
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.None)
|
||||
initialState.eventSink(ResolveVerifiedUserSendFailureEvents.ComputeForMessage(failedMessage))
|
||||
initialState.eventSink(ResolveVerifiedUserSendFailureEvent.ComputeForMessage(failedMessage))
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.UnsignedDevice.FromYou)
|
||||
state.eventSink(ResolveVerifiedUserSendFailureEvents.Dismiss)
|
||||
state.eventSink(ResolveVerifiedUserSendFailureEvent.Dismiss)
|
||||
}
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
@@ -122,12 +122,12 @@ class ResolveVerifiedUserSendFailurePresenterTest {
|
||||
val failedMessage = aVerifiedUserHasUnsignedDeviceFailedMessage()
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.None)
|
||||
initialState.eventSink(ResolveVerifiedUserSendFailureEvents.ComputeForMessage(failedMessage))
|
||||
initialState.eventSink(ResolveVerifiedUserSendFailureEvent.ComputeForMessage(failedMessage))
|
||||
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.UnsignedDevice.FromYou)
|
||||
state.eventSink(ResolveVerifiedUserSendFailureEvents.Retry)
|
||||
state.eventSink(ResolveVerifiedUserSendFailureEvent.Retry)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.retryAction).isEqualTo(AsyncAction.Loading)
|
||||
@@ -158,12 +158,12 @@ class ResolveVerifiedUserSendFailurePresenterTest {
|
||||
val failedMessage = aVerifiedUserHasUnsignedDeviceFailedMessage()
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.None)
|
||||
initialState.eventSink(ResolveVerifiedUserSendFailureEvents.ComputeForMessage(failedMessage))
|
||||
initialState.eventSink(ResolveVerifiedUserSendFailureEvent.ComputeForMessage(failedMessage))
|
||||
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.UnsignedDevice.FromYou)
|
||||
state.eventSink(ResolveVerifiedUserSendFailureEvents.ResolveAndResend)
|
||||
state.eventSink(ResolveVerifiedUserSendFailureEvent.ResolveAndResend)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.resolveAction).isEqualTo(AsyncAction.Loading)
|
||||
@@ -173,7 +173,7 @@ class ResolveVerifiedUserSendFailurePresenterTest {
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.UnsignedDevice.FromOther(A_USER_ID_2.value))
|
||||
assertThat(state.resolveAction).isEqualTo(AsyncAction.Success(Unit))
|
||||
state.eventSink(ResolveVerifiedUserSendFailureEvents.ResolveAndResend)
|
||||
state.eventSink(ResolveVerifiedUserSendFailureEvent.ResolveAndResend)
|
||||
}
|
||||
|
||||
skipItems(3)
|
||||
@@ -201,12 +201,12 @@ class ResolveVerifiedUserSendFailurePresenterTest {
|
||||
val failedMessage = aVerifiedUserHasUnsignedDeviceFailedMessage()
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.None)
|
||||
initialState.eventSink(ResolveVerifiedUserSendFailureEvents.ComputeForMessage(failedMessage))
|
||||
initialState.eventSink(ResolveVerifiedUserSendFailureEvent.ComputeForMessage(failedMessage))
|
||||
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.UnsignedDevice.FromYou)
|
||||
state.eventSink(ResolveVerifiedUserSendFailureEvents.ResolveAndResend)
|
||||
state.eventSink(ResolveVerifiedUserSendFailureEvent.ResolveAndResend)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.resolveAction).isEqualTo(AsyncAction.Loading)
|
||||
@@ -233,12 +233,12 @@ class ResolveVerifiedUserSendFailurePresenterTest {
|
||||
val failedMessage = aVerifiedUserChangedIdentityMessage()
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.None)
|
||||
initialState.eventSink(ResolveVerifiedUserSendFailureEvents.ComputeForMessage(failedMessage))
|
||||
initialState.eventSink(ResolveVerifiedUserSendFailureEvent.ComputeForMessage(failedMessage))
|
||||
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.ChangedIdentity(A_USER_ID.value))
|
||||
state.eventSink(ResolveVerifiedUserSendFailureEvents.Retry)
|
||||
state.eventSink(ResolveVerifiedUserSendFailureEvent.Retry)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.retryAction).isEqualTo(AsyncAction.Loading)
|
||||
@@ -269,12 +269,12 @@ class ResolveVerifiedUserSendFailurePresenterTest {
|
||||
val failedMessage = aVerifiedUserChangedIdentityMessage()
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.None)
|
||||
initialState.eventSink(ResolveVerifiedUserSendFailureEvents.ComputeForMessage(failedMessage))
|
||||
initialState.eventSink(ResolveVerifiedUserSendFailureEvent.ComputeForMessage(failedMessage))
|
||||
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.ChangedIdentity(A_USER_ID.value))
|
||||
state.eventSink(ResolveVerifiedUserSendFailureEvents.ResolveAndResend)
|
||||
state.eventSink(ResolveVerifiedUserSendFailureEvent.ResolveAndResend)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.resolveAction).isEqualTo(AsyncAction.Loading)
|
||||
@@ -284,7 +284,7 @@ class ResolveVerifiedUserSendFailurePresenterTest {
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.ChangedIdentity(A_USER_ID_2.value))
|
||||
assertThat(state.resolveAction).isEqualTo(AsyncAction.Success(Unit))
|
||||
state.eventSink(ResolveVerifiedUserSendFailureEvents.ResolveAndResend)
|
||||
state.eventSink(ResolveVerifiedUserSendFailureEvent.ResolveAndResend)
|
||||
}
|
||||
|
||||
skipItems(3)
|
||||
@@ -312,12 +312,12 @@ class ResolveVerifiedUserSendFailurePresenterTest {
|
||||
val failedMessage = aVerifiedUserChangedIdentityMessage()
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.None)
|
||||
initialState.eventSink(ResolveVerifiedUserSendFailureEvents.ComputeForMessage(failedMessage))
|
||||
initialState.eventSink(ResolveVerifiedUserSendFailureEvent.ComputeForMessage(failedMessage))
|
||||
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.ChangedIdentity(A_USER_ID.value))
|
||||
state.eventSink(ResolveVerifiedUserSendFailureEvents.ResolveAndResend)
|
||||
state.eventSink(ResolveVerifiedUserSendFailureEvent.ResolveAndResend)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.resolveAction).isEqualTo(AsyncAction.Loading)
|
||||
|
||||
@@ -27,7 +27,7 @@ class ResolveVerifiedUserSendFailureViewTest {
|
||||
|
||||
@Test
|
||||
fun `clicking on resolve and resend emit the expected event`() {
|
||||
val eventsRecorder = EventsRecorder<ResolveVerifiedUserSendFailureEvents>()
|
||||
val eventsRecorder = EventsRecorder<ResolveVerifiedUserSendFailureEvent>()
|
||||
rule.setResolveVerifiedUserSendFailureView(
|
||||
state = aResolveVerifiedUserSendFailureState(
|
||||
verifiedUserSendFailure = aChangedIdentitySendFailure(),
|
||||
@@ -36,12 +36,12 @@ class ResolveVerifiedUserSendFailureViewTest {
|
||||
)
|
||||
|
||||
rule.clickOn(res = CommonStrings.screen_resolve_send_failure_changed_identity_primary_button_title)
|
||||
eventsRecorder.assertSingle(ResolveVerifiedUserSendFailureEvents.ResolveAndResend)
|
||||
eventsRecorder.assertSingle(ResolveVerifiedUserSendFailureEvent.ResolveAndResend)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on retry emit the expected event`() {
|
||||
val eventsRecorder = EventsRecorder<ResolveVerifiedUserSendFailureEvents>()
|
||||
val eventsRecorder = EventsRecorder<ResolveVerifiedUserSendFailureEvent>()
|
||||
rule.setResolveVerifiedUserSendFailureView(
|
||||
state = aResolveVerifiedUserSendFailureState(
|
||||
verifiedUserSendFailure = aChangedIdentitySendFailure(),
|
||||
@@ -50,7 +50,7 @@ class ResolveVerifiedUserSendFailureViewTest {
|
||||
)
|
||||
|
||||
rule.clickOn(res = CommonStrings.action_retry)
|
||||
eventsRecorder.assertSingle(ResolveVerifiedUserSendFailureEvents.Retry)
|
||||
eventsRecorder.assertSingle(ResolveVerifiedUserSendFailureEvent.Retry)
|
||||
}
|
||||
|
||||
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setResolveVerifiedUserSendFailureView(
|
||||
|
||||
@@ -45,7 +45,7 @@ class LinkPresenterTest {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.linkClick).isEqualTo(AsyncAction.Uninitialized)
|
||||
initialState.eventSink(LinkEvents.OnLinkClick(aLink))
|
||||
initialState.eventSink(LinkEvent.OnLinkClick(aLink))
|
||||
assertThat(awaitItem().linkClick).isEqualTo(AsyncAction.Loading)
|
||||
val state = awaitItem()
|
||||
assertThat(state.linkClick).isEqualTo(AsyncAction.Success(aLink))
|
||||
@@ -61,11 +61,11 @@ class LinkPresenterTest {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.linkClick).isEqualTo(AsyncAction.Uninitialized)
|
||||
initialState.eventSink(LinkEvents.OnLinkClick(aLink))
|
||||
initialState.eventSink(LinkEvent.OnLinkClick(aLink))
|
||||
assertThat(awaitItem().linkClick).isEqualTo(AsyncAction.Loading)
|
||||
val state = awaitItem()
|
||||
assertThat(state.linkClick).isEqualTo(ConfirmingLinkClick(aLink))
|
||||
state.eventSink(LinkEvents.Cancel)
|
||||
state.eventSink(LinkEvent.Cancel)
|
||||
val finalState = awaitItem()
|
||||
assertThat(finalState.linkClick).isEqualTo(AsyncAction.Uninitialized)
|
||||
}
|
||||
@@ -79,11 +79,11 @@ class LinkPresenterTest {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.linkClick).isEqualTo(AsyncAction.Uninitialized)
|
||||
initialState.eventSink(LinkEvents.OnLinkClick(aLink))
|
||||
initialState.eventSink(LinkEvent.OnLinkClick(aLink))
|
||||
assertThat(awaitItem().linkClick).isEqualTo(AsyncAction.Loading)
|
||||
val state = awaitItem()
|
||||
assertThat(state.linkClick).isEqualTo(ConfirmingLinkClick(aLink))
|
||||
state.eventSink(LinkEvents.Confirm)
|
||||
state.eventSink(LinkEvent.Confirm)
|
||||
val finalState = awaitItem()
|
||||
assertThat(finalState.linkClick).isEqualTo(AsyncAction.Success(aLink))
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ class LinkViewTest {
|
||||
|
||||
@Test
|
||||
fun `clicking on cancel emits the expected event`() {
|
||||
val eventsRecorder = EventsRecorder<LinkEvents>()
|
||||
val eventsRecorder = EventsRecorder<LinkEvent>()
|
||||
rule.setLinkView(
|
||||
aLinkState(
|
||||
linkClick = ConfirmingLinkClick(aLink),
|
||||
@@ -40,13 +40,13 @@ class LinkViewTest {
|
||||
)
|
||||
rule.clickOn(CommonStrings.action_cancel)
|
||||
eventsRecorder.assertSingle(
|
||||
LinkEvents.Cancel
|
||||
LinkEvent.Cancel
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on continue emits the expected event`() {
|
||||
val eventsRecorder = EventsRecorder<LinkEvents>()
|
||||
val eventsRecorder = EventsRecorder<LinkEvent>()
|
||||
rule.setLinkView(
|
||||
aLinkState(
|
||||
linkClick = ConfirmingLinkClick(aLink),
|
||||
@@ -55,13 +55,13 @@ class LinkViewTest {
|
||||
)
|
||||
rule.clickOn(CommonStrings.action_continue)
|
||||
eventsRecorder.assertSingle(
|
||||
LinkEvents.Confirm
|
||||
LinkEvent.Confirm
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `success state invokes the callback and emits the expected event`() {
|
||||
val eventsRecorder = EventsRecorder<LinkEvents>()
|
||||
val eventsRecorder = EventsRecorder<LinkEvent>()
|
||||
ensureCalledOnceWithParam(aLink) { callback ->
|
||||
rule.setLinkView(
|
||||
aLinkState(
|
||||
@@ -72,7 +72,7 @@ class LinkViewTest {
|
||||
)
|
||||
}
|
||||
eventsRecorder.assertSingle(
|
||||
LinkEvents.Cancel
|
||||
LinkEvent.Cancel
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,9 +137,7 @@ class MessageComposerPresenterTest {
|
||||
@Test
|
||||
fun `present - initial state`() = runTest {
|
||||
val presenter = createPresenter()
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitFirstItem()
|
||||
assertThat(initialState.isFullScreen).isFalse()
|
||||
assertThat(initialState.textEditorState.messageHtml()).isEqualTo("")
|
||||
@@ -152,9 +150,7 @@ class MessageComposerPresenterTest {
|
||||
@Test
|
||||
fun `present - toggle fullscreen`() = runTest {
|
||||
val presenter = createPresenter()
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.eventSink.invoke(MessageComposerEvent.ToggleFullScreenState)
|
||||
val fullscreenState = awaitItem()
|
||||
@@ -168,9 +164,7 @@ class MessageComposerPresenterTest {
|
||||
@Test
|
||||
fun `present - change message`() = runTest {
|
||||
val presenter = createPresenter()
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.textEditorState.setHtml(A_MESSAGE)
|
||||
assertThat(initialState.textEditorState.messageHtml()).isEqualTo(A_MESSAGE)
|
||||
@@ -347,9 +341,7 @@ class MessageComposerPresenterTest {
|
||||
@Test
|
||||
fun `present - change mode to reply`() = runTest {
|
||||
val presenter = createPresenter()
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
var state = awaitFirstItem()
|
||||
val mode = aReplyMode()
|
||||
state.eventSink.invoke(MessageComposerEvent.SetMode(mode))
|
||||
@@ -363,9 +355,7 @@ class MessageComposerPresenterTest {
|
||||
@Test
|
||||
fun `present - cancel reply`() = runTest {
|
||||
val presenter = createPresenter()
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
var state = awaitFirstItem()
|
||||
val mode = aReplyMode()
|
||||
state.eventSink.invoke(MessageComposerEvent.SetMode(mode))
|
||||
@@ -625,9 +615,7 @@ class MessageComposerPresenterTest {
|
||||
val presenter = createPresenter(
|
||||
joinedRoom,
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitFirstItem()
|
||||
assertThat(initialState.textEditorState.messageHtml()).isEqualTo("")
|
||||
val mode = aReplyMode()
|
||||
@@ -661,9 +649,7 @@ class MessageComposerPresenterTest {
|
||||
@Test
|
||||
fun `present - Open attachments menu`() = runTest {
|
||||
val presenter = createPresenter()
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitFirstItem()
|
||||
assertThat(initialState.showAttachmentSourcePicker).isFalse()
|
||||
initialState.eventSink(MessageComposerEvent.AddAttachment)
|
||||
@@ -674,9 +660,7 @@ class MessageComposerPresenterTest {
|
||||
@Test
|
||||
fun `present - Dismiss attachments menu`() = runTest {
|
||||
val presenter = createPresenter()
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.eventSink(MessageComposerEvent.AddAttachment)
|
||||
skipItems(1)
|
||||
@@ -717,9 +701,7 @@ class MessageComposerPresenterTest {
|
||||
)
|
||||
)
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.eventSink(MessageComposerEvent.PickAttachmentSource.FromGallery)
|
||||
onPreviewAttachmentLambda.assertions().isCalledOnce()
|
||||
@@ -758,9 +740,7 @@ class MessageComposerPresenterTest {
|
||||
)
|
||||
)
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.eventSink(MessageComposerEvent.PickAttachmentSource.FromGallery)
|
||||
onPreviewAttachmentLambda.assertions().isCalledOnce()
|
||||
@@ -774,9 +754,7 @@ class MessageComposerPresenterTest {
|
||||
givenResult(null) // Simulate a user canceling the flow
|
||||
givenMimeType(MimeTypes.Images)
|
||||
}
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.eventSink(MessageComposerEvent.PickAttachmentSource.FromGallery)
|
||||
// No crashes here, otherwise it fails
|
||||
@@ -796,9 +774,7 @@ class MessageComposerPresenterTest {
|
||||
room = room,
|
||||
navigator = navigator,
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.eventSink(MessageComposerEvent.PickAttachmentSource.FromFiles)
|
||||
onPreviewAttachmentLambda.assertions().isCalledOnce()
|
||||
@@ -811,9 +787,7 @@ class MessageComposerPresenterTest {
|
||||
typingNoticeResult = { Result.success(Unit) }
|
||||
)
|
||||
val presenter = createPresenter(room = room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.eventSink(MessageComposerEvent.AddAttachment)
|
||||
val attachmentOpenState = awaitItem()
|
||||
@@ -830,9 +804,7 @@ class MessageComposerPresenterTest {
|
||||
typingNoticeResult = { Result.success(Unit) }
|
||||
)
|
||||
val presenter = createPresenter(room = room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.eventSink(MessageComposerEvent.AddAttachment)
|
||||
val attachmentOpenState = awaitItem()
|
||||
@@ -858,9 +830,7 @@ class MessageComposerPresenterTest {
|
||||
permissionPresenter = permissionPresenter,
|
||||
navigator = navigator,
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.eventSink(MessageComposerEvent.PickAttachmentSource.PhotoFromCamera)
|
||||
onPreviewAttachmentLambda.assertions().isCalledOnce()
|
||||
@@ -882,9 +852,7 @@ class MessageComposerPresenterTest {
|
||||
permissionPresenter = permissionPresenter,
|
||||
navigator = navigator,
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.eventSink(MessageComposerEvent.PickAttachmentSource.PhotoFromCamera)
|
||||
permissionPresenter.setPermissionGranted()
|
||||
@@ -908,9 +876,7 @@ class MessageComposerPresenterTest {
|
||||
permissionPresenter = permissionPresenter,
|
||||
navigator = navigator,
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.eventSink(MessageComposerEvent.PickAttachmentSource.VideoFromCamera)
|
||||
onPreviewAttachmentLambda.assertions().isCalledOnce()
|
||||
@@ -932,9 +898,7 @@ class MessageComposerPresenterTest {
|
||||
permissionPresenter = permissionPresenter,
|
||||
navigator = navigator,
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.eventSink(MessageComposerEvent.PickAttachmentSource.VideoFromCamera)
|
||||
val permissionState = awaitItem()
|
||||
@@ -949,9 +913,7 @@ class MessageComposerPresenterTest {
|
||||
fun `present - errors are tracked`() = runTest {
|
||||
val testException = Exception("Test error")
|
||||
val presenter = createPresenter()
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.eventSink(MessageComposerEvent.Error(testException))
|
||||
assertThat(analyticsService.trackedErrors).containsExactly(testException)
|
||||
@@ -961,9 +923,7 @@ class MessageComposerPresenterTest {
|
||||
@Test
|
||||
fun `present - ToggleTextFormatting toggles text formatting`() = runTest {
|
||||
val presenter = createPresenter(isRichTextEditorEnabled = false)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitFirstItem()
|
||||
assertThat(initialState.showTextFormatting).isFalse()
|
||||
initialState.eventSink(MessageComposerEvent.AddAttachment)
|
||||
@@ -1008,9 +968,7 @@ class MessageComposerPresenterTest {
|
||||
givenRoomInfo(aRoomInfo(isDirect = false))
|
||||
}
|
||||
val presenter = createPresenter(room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
|
||||
// A null suggestion (no suggestion was received) returns nothing
|
||||
@@ -1062,9 +1020,7 @@ class MessageComposerPresenterTest {
|
||||
givenRoomInfo(aRoomInfo(isDirect = false))
|
||||
}
|
||||
val presenter = createPresenter(room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
// An empty suggestion returns the joined members that are not the current user, but not the room
|
||||
initialState.eventSink(MessageComposerEvent.SuggestionReceived(Suggestion(0, 0, SuggestionType.Mention, "")))
|
||||
@@ -1099,9 +1055,7 @@ class MessageComposerPresenterTest {
|
||||
)
|
||||
}
|
||||
val presenter = createPresenter(room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
// An empty suggestion returns the joined members that are not the current user, but not the room
|
||||
initialState.eventSink(MessageComposerEvent.SuggestionReceived(Suggestion(0, 0, SuggestionType.Mention, "")))
|
||||
@@ -1119,9 +1073,7 @@ class MessageComposerPresenterTest {
|
||||
}
|
||||
)
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.textEditorState.setHtml("Hey @bo")
|
||||
initialState.eventSink(MessageComposerEvent.InsertSuggestion(ResolvedSuggestion.Member(aRoomMember(userId = A_USER_ID_2))))
|
||||
@@ -1153,9 +1105,7 @@ class MessageComposerPresenterTest {
|
||||
typingNoticeResult = { Result.success(Unit) }
|
||||
)
|
||||
val presenter = createPresenter(room = room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitFirstItem()
|
||||
|
||||
// Check intentional mentions on message sent
|
||||
@@ -1242,9 +1192,7 @@ class MessageComposerPresenterTest {
|
||||
typingNoticeResult = typingNoticeResult,
|
||||
)
|
||||
val presenter = createPresenter(room = room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitFirstItem()
|
||||
typingNoticeResult.assertions().isNeverCalled()
|
||||
initialState.eventSink.invoke(MessageComposerEvent.TypingNotice(true))
|
||||
@@ -1268,9 +1216,7 @@ class MessageComposerPresenterTest {
|
||||
isSendTypingNotificationsEnabled = false
|
||||
)
|
||||
val presenter = createPresenter(room = room, sessionPreferencesStore = store)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitFirstItem()
|
||||
typingNoticeResult.assertions().isNeverCalled()
|
||||
initialState.eventSink.invoke(MessageComposerEvent.TypingNotice(true))
|
||||
@@ -1286,9 +1232,7 @@ class MessageComposerPresenterTest {
|
||||
this.loadDraftLambda = loadDraftLambda
|
||||
}
|
||||
val presenter = createPresenter(draftService = composerDraftService)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
awaitFirstItem()
|
||||
assert(loadDraftLambda)
|
||||
.isCalledOnce()
|
||||
@@ -1311,9 +1255,7 @@ class MessageComposerPresenterTest {
|
||||
draftService = composerDraftService,
|
||||
permalinkBuilder = permalinkBuilder,
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
skipItems(2)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.textEditorState.messageMarkdown(permalinkBuilder)).isEqualTo(A_MESSAGE)
|
||||
@@ -1344,9 +1286,7 @@ class MessageComposerPresenterTest {
|
||||
draftService = composerDraftService,
|
||||
permalinkBuilder = permalinkBuilder,
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.showTextFormatting).isTrue()
|
||||
@@ -1377,9 +1317,7 @@ class MessageComposerPresenterTest {
|
||||
draftService = composerDraftService,
|
||||
permalinkBuilder = permalinkBuilder,
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
skipItems(2)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.showTextFormatting).isFalse()
|
||||
@@ -1423,9 +1361,7 @@ class MessageComposerPresenterTest {
|
||||
draftService = composerDraftService,
|
||||
permalinkBuilder = permalinkBuilder,
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
skipItems(2)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.showTextFormatting).isFalse()
|
||||
@@ -1452,9 +1388,7 @@ class MessageComposerPresenterTest {
|
||||
this.saveDraftLambda = saveDraftLambda
|
||||
}
|
||||
val presenter = createPresenter(draftService = composerDraftService)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.eventSink.invoke(MessageComposerEvent.SaveDraft)
|
||||
advanceUntilIdle()
|
||||
|
||||
@@ -127,7 +127,7 @@ class PinnedMessagesBannerPresenterTest {
|
||||
assertThat(loadedState.currentPinnedMessageIndex).isEqualTo(1)
|
||||
assertThat(loadedState.loadedPinnedMessagesCount).isEqualTo(2)
|
||||
assertThat(loadedState.currentPinnedMessage.formatted.text).isEqualTo(messageContent2.toString())
|
||||
loadedState.eventSink(PinnedMessagesBannerEvents.MoveToNextPinned)
|
||||
loadedState.eventSink(PinnedMessagesBannerEvent.MoveToNextPinned)
|
||||
}
|
||||
|
||||
awaitItem().also { loadedState ->
|
||||
@@ -135,7 +135,7 @@ class PinnedMessagesBannerPresenterTest {
|
||||
assertThat(loadedState.currentPinnedMessageIndex).isEqualTo(0)
|
||||
assertThat(loadedState.loadedPinnedMessagesCount).isEqualTo(2)
|
||||
assertThat(loadedState.currentPinnedMessage.formatted.text).isEqualTo(messageContent1.toString())
|
||||
loadedState.eventSink(PinnedMessagesBannerEvents.MoveToNextPinned)
|
||||
loadedState.eventSink(PinnedMessagesBannerEvent.MoveToNextPinned)
|
||||
}
|
||||
|
||||
awaitItem().also { loadedState ->
|
||||
|
||||
@@ -33,7 +33,7 @@ class PinnedMessagesBannerViewTest {
|
||||
|
||||
@Test
|
||||
fun `clicking on the banner invoke expected callback`() {
|
||||
val eventsRecorder = EventsRecorder<PinnedMessagesBannerEvents>()
|
||||
val eventsRecorder = EventsRecorder<PinnedMessagesBannerEvent>()
|
||||
val state = aLoadedPinnedMessagesBannerState(
|
||||
eventSink = eventsRecorder
|
||||
)
|
||||
@@ -44,13 +44,13 @@ class PinnedMessagesBannerViewTest {
|
||||
onClick = callback
|
||||
)
|
||||
rule.onRoot().performClick()
|
||||
eventsRecorder.assertSingle(PinnedMessagesBannerEvents.MoveToNextPinned)
|
||||
eventsRecorder.assertSingle(PinnedMessagesBannerEvent.MoveToNextPinned)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on view all emit the expected event`() {
|
||||
val eventsRecorder = EventsRecorder<PinnedMessagesBannerEvents>(expectEvents = true)
|
||||
val eventsRecorder = EventsRecorder<PinnedMessagesBannerEvent>(expectEvents = true)
|
||||
val state = aLoadedPinnedMessagesBannerState(
|
||||
eventSink = eventsRecorder
|
||||
)
|
||||
|
||||
@@ -152,11 +152,11 @@ class PinnedMessagesListPresenterTest {
|
||||
val eventItem = filledState.timelineItems.first() as TimelineItem.Event
|
||||
|
||||
pinnedEventsTimeline.unpinEventLambda = successUnpinEventLambda
|
||||
filledState.eventSink(PinnedMessagesListEvents.HandleAction(TimelineItemAction.Unpin, eventItem))
|
||||
filledState.eventSink(PinnedMessagesListEvent.HandleAction(TimelineItemAction.Unpin, eventItem))
|
||||
advanceUntilIdle()
|
||||
|
||||
pinnedEventsTimeline.unpinEventLambda = failureUnpinEventLambda
|
||||
filledState.eventSink(PinnedMessagesListEvents.HandleAction(TimelineItemAction.Unpin, eventItem))
|
||||
filledState.eventSink(PinnedMessagesListEvent.HandleAction(TimelineItemAction.Unpin, eventItem))
|
||||
advanceUntilIdle()
|
||||
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
@@ -196,7 +196,7 @@ class PinnedMessagesListPresenterTest {
|
||||
skipItems(3)
|
||||
val filledState = awaitItem() as PinnedMessagesListState.Filled
|
||||
val eventItem = filledState.timelineItems.first() as TimelineItem.Event
|
||||
filledState.eventSink(PinnedMessagesListEvents.HandleAction(TimelineItemAction.ViewInTimeline, eventItem))
|
||||
filledState.eventSink(PinnedMessagesListEvent.HandleAction(TimelineItemAction.ViewInTimeline, eventItem))
|
||||
advanceUntilIdle()
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
assert(onViewInTimelineClickLambda)
|
||||
@@ -225,7 +225,7 @@ class PinnedMessagesListPresenterTest {
|
||||
skipItems(3)
|
||||
val filledState = awaitItem() as PinnedMessagesListState.Filled
|
||||
val eventItem = filledState.timelineItems.first() as TimelineItem.Event
|
||||
filledState.eventSink(PinnedMessagesListEvents.HandleAction(TimelineItemAction.ViewSource, eventItem))
|
||||
filledState.eventSink(PinnedMessagesListEvent.HandleAction(TimelineItemAction.ViewSource, eventItem))
|
||||
advanceUntilIdle()
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
assert(onShowEventDebugInfoClickLambda)
|
||||
@@ -254,7 +254,7 @@ class PinnedMessagesListPresenterTest {
|
||||
skipItems(3)
|
||||
val filledState = awaitItem() as PinnedMessagesListState.Filled
|
||||
val eventItem = filledState.timelineItems.first() as TimelineItem.Event
|
||||
filledState.eventSink(PinnedMessagesListEvents.HandleAction(TimelineItemAction.Forward, eventItem))
|
||||
filledState.eventSink(PinnedMessagesListEvent.HandleAction(TimelineItemAction.Forward, eventItem))
|
||||
advanceUntilIdle()
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
assert(onForwardEventClickLambda)
|
||||
|
||||
@@ -17,7 +17,7 @@ import androidx.compose.ui.test.onFirst
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.compose.ui.test.performTouchInput
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import io.element.android.features.messages.impl.actionlist.ActionListEvents
|
||||
import io.element.android.features.messages.impl.actionlist.ActionListEvent
|
||||
import io.element.android.features.messages.impl.actionlist.anActionListState
|
||||
import io.element.android.features.messages.impl.timeline.aTimelineItemList
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
@@ -43,7 +43,7 @@ class PinnedMessagesListViewTest {
|
||||
|
||||
@Test
|
||||
fun `clicking on back calls the expected callback`() {
|
||||
val eventsRecorder = EventsRecorder<PinnedMessagesListEvents>(expectEvents = false)
|
||||
val eventsRecorder = EventsRecorder<PinnedMessagesListEvent>(expectEvents = false)
|
||||
val state = aLoadedPinnedMessagesListState(
|
||||
eventSink = eventsRecorder
|
||||
)
|
||||
@@ -58,7 +58,7 @@ class PinnedMessagesListViewTest {
|
||||
|
||||
@Test
|
||||
fun `click on an event calls the expected callback`() {
|
||||
val eventsRecorder = EventsRecorder<PinnedMessagesListEvents>(expectEvents = false)
|
||||
val eventsRecorder = EventsRecorder<PinnedMessagesListEvent>(expectEvents = false)
|
||||
val content = aTimelineItemFileContent()
|
||||
val state = aLoadedPinnedMessagesListState(
|
||||
timelineItems = aTimelineItemList(content),
|
||||
@@ -77,7 +77,7 @@ class PinnedMessagesListViewTest {
|
||||
|
||||
@Test
|
||||
fun `long click on an event emits the expected event`() {
|
||||
val eventsRecorder = EventsRecorder<ActionListEvents>(expectEvents = true)
|
||||
val eventsRecorder = EventsRecorder<ActionListEvent>(expectEvents = true)
|
||||
val content = aTimelineItemFileContent()
|
||||
val state = aLoadedPinnedMessagesListState(
|
||||
timelineItems = aTimelineItemList(content),
|
||||
@@ -92,7 +92,7 @@ class PinnedMessagesListViewTest {
|
||||
longClick()
|
||||
}
|
||||
val event = state.timelineItems.first() as TimelineItem.Event
|
||||
eventsRecorder.assertSingle(ActionListEvents.ComputeForMessage(event, state.userEventPermissions))
|
||||
eventsRecorder.assertSingle(ActionListEvent.ComputeForMessage(event, state.userEventPermissions))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,9 +8,6 @@
|
||||
|
||||
package io.element.android.features.messages.impl.report
|
||||
|
||||
import app.cash.molecule.RecompositionMode
|
||||
import app.cash.molecule.moleculeFlow
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
||||
@@ -22,6 +19,7 @@ import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
import io.element.android.tests.testutils.test
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
@@ -33,9 +31,7 @@ class ReportMessagePresenterTest {
|
||||
@Test
|
||||
fun `presenter - initial state`() = runTest {
|
||||
val presenter = createReportMessagePresenter()
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.reason).isEmpty()
|
||||
assertThat(initialState.blockUser).isFalse()
|
||||
@@ -46,12 +42,10 @@ class ReportMessagePresenterTest {
|
||||
@Test
|
||||
fun `presenter - update reason`() = runTest {
|
||||
val presenter = createReportMessagePresenter()
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
val reason = "This user is making the chat very toxic."
|
||||
initialState.eventSink(ReportMessageEvents.UpdateReason(reason))
|
||||
initialState.eventSink(ReportMessageEvent.UpdateReason(reason))
|
||||
|
||||
assertThat(awaitItem().reason).isEqualTo(reason)
|
||||
}
|
||||
@@ -60,15 +54,13 @@ class ReportMessagePresenterTest {
|
||||
@Test
|
||||
fun `presenter - toggle block user`() = runTest {
|
||||
val presenter = createReportMessagePresenter()
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(ReportMessageEvents.ToggleBlockUser)
|
||||
initialState.eventSink(ReportMessageEvent.ToggleBlockUser)
|
||||
|
||||
assertThat(awaitItem().blockUser).isTrue()
|
||||
|
||||
initialState.eventSink(ReportMessageEvents.ToggleBlockUser)
|
||||
initialState.eventSink(ReportMessageEvent.ToggleBlockUser)
|
||||
|
||||
assertThat(awaitItem().blockUser).isFalse()
|
||||
}
|
||||
@@ -83,13 +75,11 @@ class ReportMessagePresenterTest {
|
||||
reportContentResult = reportContentResult
|
||||
)
|
||||
val presenter = createReportMessagePresenter(joinedRoom = room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(ReportMessageEvents.ToggleBlockUser)
|
||||
initialState.eventSink(ReportMessageEvent.ToggleBlockUser)
|
||||
skipItems(1)
|
||||
initialState.eventSink(ReportMessageEvents.Report)
|
||||
initialState.eventSink(ReportMessageEvent.Report)
|
||||
assertThat(awaitItem().result).isInstanceOf(AsyncAction.Loading::class.java)
|
||||
assertThat(awaitItem().result).isInstanceOf(AsyncAction.Success::class.java)
|
||||
reportContentResult.assertions().isCalledOnce()
|
||||
@@ -105,11 +95,9 @@ class ReportMessagePresenterTest {
|
||||
reportContentResult = reportContentResult
|
||||
)
|
||||
val presenter = createReportMessagePresenter(joinedRoom = room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(ReportMessageEvents.Report)
|
||||
initialState.eventSink(ReportMessageEvent.Report)
|
||||
assertThat(awaitItem().result).isInstanceOf(AsyncAction.Loading::class.java)
|
||||
assertThat(awaitItem().result).isInstanceOf(AsyncAction.Success::class.java)
|
||||
reportContentResult.assertions().isCalledOnce()
|
||||
@@ -125,17 +113,15 @@ class ReportMessagePresenterTest {
|
||||
reportContentResult = reportContentResult
|
||||
)
|
||||
val presenter = createReportMessagePresenter(joinedRoom = room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(ReportMessageEvents.Report)
|
||||
initialState.eventSink(ReportMessageEvent.Report)
|
||||
assertThat(awaitItem().result).isInstanceOf(AsyncAction.Loading::class.java)
|
||||
val resultState = awaitItem()
|
||||
assertThat(resultState.result).isInstanceOf(AsyncAction.Failure::class.java)
|
||||
reportContentResult.assertions().isCalledOnce()
|
||||
|
||||
resultState.eventSink(ReportMessageEvents.ClearError)
|
||||
resultState.eventSink(ReportMessageEvent.ClearError)
|
||||
assertThat(awaitItem().result).isInstanceOf(AsyncAction.Uninitialized::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
package io.element.android.features.messages.impl.timeline
|
||||
|
||||
import app.cash.turbine.ReceiveTurbine
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.messages.impl.FakeMessagesNavigator
|
||||
import io.element.android.features.messages.impl.crypto.sendfailure.resolve.aResolveVerifiedUserSendFailureState
|
||||
@@ -120,8 +119,8 @@ class TimelinePresenterTest {
|
||||
val presenter = createTimelinePresenter(timeline = timeline)
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink.invoke(TimelineEvents.LoadMore(Timeline.PaginationDirection.BACKWARDS))
|
||||
initialState.eventSink.invoke(TimelineEvents.LoadMore(Timeline.PaginationDirection.FORWARDS))
|
||||
initialState.eventSink.invoke(TimelineEvent.LoadMore(Timeline.PaginationDirection.BACKWARDS))
|
||||
initialState.eventSink.invoke(TimelineEvent.LoadMore(Timeline.PaginationDirection.FORWARDS))
|
||||
assert(paginateLambda)
|
||||
.isCalledExactly(2)
|
||||
.withSequence(
|
||||
@@ -173,7 +172,7 @@ class TimelinePresenterTest {
|
||||
)
|
||||
presenter.test {
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.eventSink.invoke(TimelineEvents.OnScrollFinished(0))
|
||||
initialState.eventSink.invoke(TimelineEvent.OnScrollFinished(0))
|
||||
runCurrent()
|
||||
assert(markAsReadResult)
|
||||
.isCalledOnce()
|
||||
@@ -207,7 +206,7 @@ class TimelinePresenterTest {
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
awaitItem().run {
|
||||
eventSink.invoke(TimelineEvents.OnScrollFinished(1))
|
||||
eventSink.invoke(TimelineEvent.OnScrollFinished(1))
|
||||
}
|
||||
advanceUntilIdle()
|
||||
assert(sendReadReceiptsLambda)
|
||||
@@ -246,8 +245,8 @@ class TimelinePresenterTest {
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
awaitItem().run {
|
||||
eventSink.invoke(TimelineEvents.OnScrollFinished(0))
|
||||
eventSink.invoke(TimelineEvents.OnScrollFinished(1))
|
||||
eventSink.invoke(TimelineEvent.OnScrollFinished(0))
|
||||
eventSink.invoke(TimelineEvent.OnScrollFinished(1))
|
||||
}
|
||||
advanceUntilIdle()
|
||||
assert(sendReadReceiptsLambda)
|
||||
@@ -282,8 +281,8 @@ class TimelinePresenterTest {
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
awaitItem().run {
|
||||
eventSink.invoke(TimelineEvents.OnScrollFinished(1))
|
||||
eventSink.invoke(TimelineEvents.OnScrollFinished(1))
|
||||
eventSink.invoke(TimelineEvent.OnScrollFinished(1))
|
||||
eventSink.invoke(TimelineEvent.OnScrollFinished(1))
|
||||
}
|
||||
advanceUntilIdle()
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
@@ -310,7 +309,7 @@ class TimelinePresenterTest {
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.eventSink.invoke(TimelineEvents.OnScrollFinished(1))
|
||||
initialState.eventSink.invoke(TimelineEvent.OnScrollFinished(1))
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
assert(sendReadReceiptsLambda).isNeverCalled()
|
||||
}
|
||||
@@ -349,7 +348,7 @@ class TimelinePresenterTest {
|
||||
consumeItemsUntilPredicate { it.timelineItems.size == 3 }
|
||||
|
||||
// Scroll to bottom to clear previous FromMe
|
||||
initialState.eventSink.invoke(TimelineEvents.OnScrollFinished(0))
|
||||
initialState.eventSink.invoke(TimelineEvent.OnScrollFinished(0))
|
||||
awaitLastSequentialItem().also { state ->
|
||||
assertThat(state.newEventState).isEqualTo(NewEventState.None)
|
||||
}
|
||||
@@ -429,7 +428,7 @@ class TimelinePresenterTest {
|
||||
)
|
||||
presenter.test {
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.eventSink.invoke(TimelineEvents.SelectPollAnswer(AN_EVENT_ID, "anAnswerId"))
|
||||
initialState.eventSink.invoke(TimelineEvent.SelectPollAnswer(AN_EVENT_ID, "anAnswerId"))
|
||||
}
|
||||
delay(1)
|
||||
sendPollResponseAction.verifyExecutionCount(1)
|
||||
@@ -443,7 +442,7 @@ class TimelinePresenterTest {
|
||||
)
|
||||
presenter.test {
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.eventSink.invoke(TimelineEvents.EndPoll(AN_EVENT_ID))
|
||||
initialState.eventSink.invoke(TimelineEvent.EndPoll(AN_EVENT_ID))
|
||||
}
|
||||
delay(1)
|
||||
endPollAction.verifyExecutionCount(1)
|
||||
@@ -459,7 +458,7 @@ class TimelinePresenterTest {
|
||||
messagesNavigator = navigator,
|
||||
)
|
||||
presenter.test {
|
||||
awaitFirstItem().eventSink(TimelineEvents.EditPoll(AN_EVENT_ID))
|
||||
awaitFirstItem().eventSink(TimelineEvent.EditPoll(AN_EVENT_ID))
|
||||
onEditPollClickLambda.assertions().isCalledOnce().with(value(AN_EVENT_ID))
|
||||
}
|
||||
}
|
||||
@@ -510,7 +509,7 @@ class TimelinePresenterTest {
|
||||
)
|
||||
presenter.test {
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.eventSink.invoke(TimelineEvents.FocusOnEvent(AN_EVENT_ID))
|
||||
initialState.eventSink.invoke(TimelineEvent.FocusOnEvent(AN_EVENT_ID))
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.focusedEventId).isEqualTo(AN_EVENT_ID)
|
||||
assertThat(state.focusRequestState).isEqualTo(FocusRequestState.Requested(AN_EVENT_ID, Duration.ZERO))
|
||||
@@ -524,7 +523,7 @@ class TimelinePresenterTest {
|
||||
assertThat(state.focusRequestState).isEqualTo(FocusRequestState.Success(AN_EVENT_ID))
|
||||
assertThat(state.timelineItems).isNotEmpty()
|
||||
}
|
||||
initialState.eventSink.invoke(TimelineEvents.JumpToLive)
|
||||
initialState.eventSink.invoke(TimelineEvent.JumpToLive)
|
||||
skipItems(2)
|
||||
awaitItem().also { state ->
|
||||
// Event stays focused
|
||||
@@ -564,7 +563,7 @@ class TimelinePresenterTest {
|
||||
// Pre-populate the indexer after the first items have been retrieved
|
||||
timelineItemIndexer.process(listOf(aMessageEvent(eventId = AN_EVENT_ID)))
|
||||
|
||||
initialState.eventSink.invoke(TimelineEvents.FocusOnEvent(AN_EVENT_ID))
|
||||
initialState.eventSink.invoke(TimelineEvent.FocusOnEvent(AN_EVENT_ID))
|
||||
|
||||
advanceUntilIdle()
|
||||
|
||||
@@ -595,7 +594,7 @@ class TimelinePresenterTest {
|
||||
)
|
||||
presenter.test {
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.eventSink(TimelineEvents.FocusOnEvent(AN_EVENT_ID))
|
||||
initialState.eventSink(TimelineEvent.FocusOnEvent(AN_EVENT_ID))
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.focusedEventId).isEqualTo(AN_EVENT_ID)
|
||||
assertThat(state.focusRequestState).isEqualTo(FocusRequestState.Requested(AN_EVENT_ID, Duration.ZERO))
|
||||
@@ -606,7 +605,7 @@ class TimelinePresenterTest {
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.focusRequestState).isInstanceOf(FocusRequestState.Failure::class.java)
|
||||
state.eventSink(TimelineEvents.ClearFocusRequestState)
|
||||
state.eventSink(TimelineEvent.ClearFocusRequestState)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.focusRequestState).isEqualTo(FocusRequestState.None)
|
||||
@@ -648,7 +647,7 @@ class TimelinePresenterTest {
|
||||
)
|
||||
presenter.test {
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.eventSink.invoke(TimelineEvents.FocusOnEvent(AN_EVENT_ID))
|
||||
initialState.eventSink.invoke(TimelineEvent.FocusOnEvent(AN_EVENT_ID))
|
||||
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.focusedEventId).isEqualTo(AN_EVENT_ID)
|
||||
@@ -707,7 +706,7 @@ class TimelinePresenterTest {
|
||||
)
|
||||
presenter.test {
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.eventSink.invoke(TimelineEvents.FocusOnEvent(AN_EVENT_ID))
|
||||
initialState.eventSink.invoke(TimelineEvent.FocusOnEvent(AN_EVENT_ID))
|
||||
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.focusedEventId).isEqualTo(AN_EVENT_ID)
|
||||
@@ -762,7 +761,7 @@ class TimelinePresenterTest {
|
||||
)
|
||||
presenter.test {
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.eventSink.invoke(TimelineEvents.FocusOnEvent(AN_EVENT_ID))
|
||||
initialState.eventSink.invoke(TimelineEvent.FocusOnEvent(AN_EVENT_ID))
|
||||
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.focusedEventId).isEqualTo(AN_EVENT_ID)
|
||||
@@ -821,7 +820,7 @@ class TimelinePresenterTest {
|
||||
)
|
||||
presenter.test {
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.eventSink.invoke(TimelineEvents.FocusOnEvent(AN_EVENT_ID))
|
||||
initialState.eventSink.invoke(TimelineEvent.FocusOnEvent(AN_EVENT_ID))
|
||||
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.focusedEventId).isEqualTo(AN_EVENT_ID)
|
||||
@@ -854,10 +853,10 @@ class TimelinePresenterTest {
|
||||
val initialState = awaitFirstItem()
|
||||
assertThat(initialState.messageShieldDialogData).isNull()
|
||||
val shieldData = MessageShieldData(shield, null, null)
|
||||
initialState.eventSink(TimelineEvents.ShowShieldDialog(shieldData))
|
||||
initialState.eventSink(TimelineEvent.ShowShieldDialog(shieldData))
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.messageShieldDialogData).isEqualTo(shieldData)
|
||||
state.eventSink(TimelineEvents.HideShieldDialog)
|
||||
state.eventSink(TimelineEvent.HideShieldDialog)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.messageShieldDialogData).isNull()
|
||||
@@ -963,7 +962,7 @@ class TimelinePresenterTest {
|
||||
val presenter = createTimelinePresenter(room = room, messagesNavigator = navigator)
|
||||
presenter.test {
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.eventSink(TimelineEvents.NavigateToPredecessorOrSuccessorRoom(A_ROOM_ID))
|
||||
initialState.eventSink(TimelineEvent.NavigateToPredecessorOrSuccessorRoom(A_ROOM_ID))
|
||||
assert(onNavigateToRoomLambda)
|
||||
.isCalledOnce()
|
||||
.with(
|
||||
|
||||
@@ -50,7 +50,7 @@ class TimelineViewTest {
|
||||
|
||||
@Test
|
||||
fun `reaching the end of the timeline with more events to load emits a LoadMore event`() {
|
||||
val eventsRecorder = EventsRecorder<TimelineEvents>()
|
||||
val eventsRecorder = EventsRecorder<TimelineEvent>()
|
||||
rule.setTimelineView(
|
||||
state = aTimelineState(
|
||||
timelineItems = persistentListOf<TimelineItem>(
|
||||
@@ -62,12 +62,12 @@ class TimelineViewTest {
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
)
|
||||
eventsRecorder.assertSingle(TimelineEvents.LoadMore(Timeline.PaginationDirection.BACKWARDS))
|
||||
eventsRecorder.assertSingle(TimelineEvent.LoadMore(Timeline.PaginationDirection.BACKWARDS))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reaching the end of the timeline does not send a LoadMore event`() {
|
||||
val eventsRecorder = EventsRecorder<TimelineEvents>(expectEvents = false)
|
||||
val eventsRecorder = EventsRecorder<TimelineEvent>(expectEvents = false)
|
||||
rule.setTimelineView(
|
||||
state = aTimelineState(
|
||||
eventSink = eventsRecorder,
|
||||
@@ -77,7 +77,7 @@ class TimelineViewTest {
|
||||
|
||||
@Test
|
||||
fun `scroll to bottom on live timeline does not emit the Event`() {
|
||||
val eventsRecorder = EventsRecorder<TimelineEvents>(expectEvents = false)
|
||||
val eventsRecorder = EventsRecorder<TimelineEvent>(expectEvents = false)
|
||||
rule.setTimelineView(
|
||||
state = aTimelineState(
|
||||
isLive = true,
|
||||
@@ -91,7 +91,7 @@ class TimelineViewTest {
|
||||
|
||||
@Test
|
||||
fun `scroll to bottom on detached timeline emits the expected Event`() {
|
||||
val eventsRecorder = EventsRecorder<TimelineEvents>()
|
||||
val eventsRecorder = EventsRecorder<TimelineEvent>()
|
||||
rule.setTimelineView(
|
||||
state = aTimelineState(
|
||||
isLive = false,
|
||||
@@ -100,12 +100,12 @@ class TimelineViewTest {
|
||||
)
|
||||
val contentDescription = rule.activity.getString(CommonStrings.a11y_jump_to_bottom)
|
||||
rule.onNodeWithContentDescription(contentDescription).performClick()
|
||||
eventsRecorder.assertSingle(TimelineEvents.JumpToLive)
|
||||
eventsRecorder.assertSingle(TimelineEvent.JumpToLive)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `show shield dialog`() {
|
||||
val eventsRecorder = EventsRecorder<TimelineEvents>()
|
||||
val eventsRecorder = EventsRecorder<TimelineEvent>()
|
||||
rule.setTimelineView(
|
||||
state = aTimelineState(
|
||||
timelineItems = persistentListOf<TimelineItem>(
|
||||
@@ -122,15 +122,15 @@ class TimelineViewTest {
|
||||
rule.onNodeWithContentDescription(contentDescription).performClick()
|
||||
eventsRecorder.assertList(
|
||||
listOf(
|
||||
TimelineEvents.OnScrollFinished(0),
|
||||
TimelineEvents.ShowShieldDialog(MessageShieldData(MessageShield.UnverifiedIdentity(true))),
|
||||
TimelineEvent.OnScrollFinished(0),
|
||||
TimelineEvent.ShowShieldDialog(MessageShieldData(MessageShield.UnverifiedIdentity(true))),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `hide shield dialog`() {
|
||||
val eventsRecorder = EventsRecorder<TimelineEvents>()
|
||||
val eventsRecorder = EventsRecorder<TimelineEvent>()
|
||||
rule.setTimelineView(
|
||||
state = aTimelineState(
|
||||
isLive = false,
|
||||
@@ -139,12 +139,12 @@ class TimelineViewTest {
|
||||
),
|
||||
)
|
||||
rule.clickOn(CommonStrings.action_ok)
|
||||
eventsRecorder.assertSingle(TimelineEvents.HideShieldDialog)
|
||||
eventsRecorder.assertSingle(TimelineEvent.HideShieldDialog)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `scrolling near to the start of the loaded items triggers a pre-fetch`() {
|
||||
val eventsRecorder = EventsRecorder<TimelineEvents>()
|
||||
val eventsRecorder = EventsRecorder<TimelineEvent>()
|
||||
val items = List<TimelineItem>(200) {
|
||||
aTimelineItemEvent(
|
||||
eventId = EventId("\$event_$it"),
|
||||
@@ -167,8 +167,8 @@ class TimelineViewTest {
|
||||
|
||||
eventsRecorder.assertList(
|
||||
listOf(
|
||||
TimelineEvents.OnScrollFinished(firstIndex = 0),
|
||||
TimelineEvents.LoadMore(Timeline.PaginationDirection.BACKWARDS),
|
||||
TimelineEvent.OnScrollFinished(firstIndex = 0),
|
||||
TimelineEvent.LoadMore(Timeline.PaginationDirection.BACKWARDS),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -8,15 +8,13 @@
|
||||
|
||||
package io.element.android.features.messages.impl.timeline.components.customreaction
|
||||
|
||||
import app.cash.molecule.RecompositionMode
|
||||
import app.cash.molecule.moleculeFlow
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.messages.impl.timeline.aTimelineItemEvent
|
||||
import io.element.android.features.messages.impl.timeline.aTimelineItemReactions
|
||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||
import io.element.android.libraries.recentemojis.test.FakeEmojibaseProvider
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
import io.element.android.tests.testutils.test
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
@@ -33,37 +31,33 @@ class CustomReactionPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - handle selecting and de-selecting an event`() = runTest {
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val event = aTimelineItemEvent(eventId = AN_EVENT_ID)
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.target).isEqualTo(CustomReactionState.Target.None)
|
||||
|
||||
initialState.eventSink(CustomReactionEvents.ShowCustomReactionSheet(event))
|
||||
initialState.eventSink(CustomReactionEvent.ShowCustomReactionSheet(event))
|
||||
|
||||
assertThat(awaitItem().target).isEqualTo(CustomReactionState.Target.Loading(event))
|
||||
|
||||
val eventId = (awaitItem().target as? CustomReactionState.Target.Success)?.event?.eventId
|
||||
assertThat(eventId).isEqualTo(AN_EVENT_ID)
|
||||
|
||||
initialState.eventSink(CustomReactionEvents.DismissCustomReactionSheet)
|
||||
initialState.eventSink(CustomReactionEvent.DismissCustomReactionSheet)
|
||||
assertThat(awaitItem().target).isEqualTo(CustomReactionState.Target.None)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - handle selected emojis`() = runTest {
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val reactions = aTimelineItemReactions(count = 1, isHighlighted = true)
|
||||
val event = aTimelineItemEvent(eventId = AN_EVENT_ID, timelineItemReactions = reactions)
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.target).isEqualTo(CustomReactionState.Target.None)
|
||||
|
||||
val key = reactions.reactions.first().key
|
||||
initialState.eventSink(CustomReactionEvents.ShowCustomReactionSheet(event))
|
||||
initialState.eventSink(CustomReactionEvent.ShowCustomReactionSheet(event))
|
||||
|
||||
assertThat(awaitItem().target).isEqualTo(CustomReactionState.Target.Loading(event))
|
||||
|
||||
|
||||
@@ -66,10 +66,10 @@ class EmojiPickerPresenterTest {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.isSearchActive).isFalse()
|
||||
|
||||
initialState.eventSink(EmojiPickerEvents.ToggleSearchActive(true))
|
||||
initialState.eventSink(EmojiPickerEvent.ToggleSearchActive(true))
|
||||
assertThat(awaitItem().isSearchActive).isTrue()
|
||||
|
||||
initialState.eventSink(EmojiPickerEvents.ToggleSearchActive(false))
|
||||
initialState.eventSink(EmojiPickerEvent.ToggleSearchActive(false))
|
||||
assertThat(awaitItem().isSearchActive).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import androidx.compose.ui.test.hasText
|
||||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import io.element.android.features.messages.impl.timeline.TimelineEvents
|
||||
import io.element.android.features.messages.impl.timeline.TimelineEvent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemPollContent
|
||||
import io.element.android.libraries.testtags.TestTags
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
@@ -39,7 +39,7 @@ class TimelineItemPollViewTest {
|
||||
}
|
||||
|
||||
private fun testAnswer(answerIndex: Int) {
|
||||
val eventsRecorder = EventsRecorder<TimelineEvents.TimelineItemPollEvents>()
|
||||
val eventsRecorder = EventsRecorder<TimelineEvent.TimelineItemPollEvent>()
|
||||
val content = aTimelineItemPollContent()
|
||||
rule.setContent {
|
||||
TimelineItemPollView(
|
||||
@@ -52,12 +52,12 @@ class TimelineItemPollViewTest {
|
||||
matcher = hasText(answer.text),
|
||||
useUnmergedTree = true,
|
||||
).performClick()
|
||||
eventsRecorder.assertSingle(TimelineEvents.SelectPollAnswer(content.eventId!!, answer.id))
|
||||
eventsRecorder.assertSingle(TimelineEvent.SelectPollAnswer(content.eventId!!, answer.id))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `editing a poll should emit a PollEditClicked event`() {
|
||||
val eventsRecorder = EventsRecorder<TimelineEvents.TimelineItemPollEvents>()
|
||||
val eventsRecorder = EventsRecorder<TimelineEvent.TimelineItemPollEvent>()
|
||||
val content = aTimelineItemPollContent(
|
||||
isMine = true,
|
||||
isEditable = true,
|
||||
@@ -69,12 +69,12 @@ class TimelineItemPollViewTest {
|
||||
)
|
||||
}
|
||||
rule.clickOn(CommonStrings.action_edit_poll)
|
||||
eventsRecorder.assertSingle(TimelineEvents.EditPoll(content.eventId!!))
|
||||
eventsRecorder.assertSingle(TimelineEvent.EditPoll(content.eventId!!))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `closing a poll should emit a PollEndClicked event`() {
|
||||
val eventsRecorder = EventsRecorder<TimelineEvents.TimelineItemPollEvents>()
|
||||
val eventsRecorder = EventsRecorder<TimelineEvent.TimelineItemPollEvent>()
|
||||
val content = aTimelineItemPollContent(
|
||||
isMine = true,
|
||||
)
|
||||
@@ -88,6 +88,6 @@ class TimelineItemPollViewTest {
|
||||
// A confirmation dialog should be shown
|
||||
eventsRecorder.assertEmpty()
|
||||
rule.pressTag(TestTags.dialogPositive.value)
|
||||
eventsRecorder.assertSingle(TimelineEvents.EndPoll(content.eventId!!))
|
||||
eventsRecorder.assertSingle(TimelineEvent.EndPoll(content.eventId!!))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,6 @@
|
||||
|
||||
package io.element.android.features.messages.impl.timeline.components.reactionsummary
|
||||
|
||||
import app.cash.molecule.RecompositionMode
|
||||
import app.cash.molecule.moleculeFlow
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.messages.impl.timeline.model.anAggregatedReaction
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembersState
|
||||
@@ -21,6 +18,7 @@ import io.element.android.libraries.matrix.test.A_USER_NAME
|
||||
import io.element.android.libraries.matrix.test.room.FakeBaseRoom
|
||||
import io.element.android.libraries.matrix.test.room.aRoomMember
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
import io.element.android.tests.testutils.test
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
@@ -32,7 +30,7 @@ class ReactionSummaryPresenterTest {
|
||||
|
||||
private val aggregatedReaction = anAggregatedReaction(userId = A_USER_ID, key = "👍", isHighlighted = true)
|
||||
private val roomMember = aRoomMember(userId = A_USER_ID, avatarUrl = AN_AVATAR_URL, displayName = A_USER_NAME)
|
||||
private val summaryEvent = ReactionSummaryEvents.ShowReactionSummary(AN_EVENT_ID, listOf(aggregatedReaction), aggregatedReaction.key)
|
||||
private val summaryEvent = ReactionSummaryEvent.ShowReactionSummary(AN_EVENT_ID, listOf(aggregatedReaction), aggregatedReaction.key)
|
||||
private val room = FakeBaseRoom().apply {
|
||||
givenRoomMembersState(RoomMembersState.Ready(persistentListOf(roomMember)))
|
||||
}
|
||||
@@ -40,25 +38,21 @@ class ReactionSummaryPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - handle showing and hiding the reaction summary`() = runTest {
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.target).isNull()
|
||||
|
||||
initialState.eventSink(summaryEvent)
|
||||
assertThat(awaitItem().target).isNotNull()
|
||||
|
||||
initialState.eventSink(ReactionSummaryEvents.Clear)
|
||||
initialState.eventSink(ReactionSummaryEvent.Clear)
|
||||
assertThat(awaitItem().target).isNull()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - handle reaction summary content and avatars populated`() = runTest {
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.target).isNull()
|
||||
|
||||
|
||||
@@ -8,12 +8,10 @@
|
||||
|
||||
package io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet
|
||||
|
||||
import app.cash.molecule.RecompositionMode
|
||||
import app.cash.molecule.moleculeFlow
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.messages.impl.timeline.aTimelineItemEvent
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
import io.element.android.tests.testutils.test
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
@@ -25,12 +23,10 @@ class ReadReceiptBottomSheetPresenterTest {
|
||||
@Test
|
||||
fun `present - handle event selected`() = runTest {
|
||||
val presenter = createPresenter()
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
val selectedEvent = aTimelineItemEvent()
|
||||
initialState.eventSink(ReadReceiptBottomSheetEvents.EventSelected(selectedEvent))
|
||||
initialState.eventSink(ReadReceiptBottomSheetEvent.EventSelected(selectedEvent))
|
||||
assertThat(awaitItem().selectedEvent).isSameInstanceAs(selectedEvent)
|
||||
}
|
||||
}
|
||||
@@ -38,14 +34,12 @@ class ReadReceiptBottomSheetPresenterTest {
|
||||
@Test
|
||||
fun `present - handle dismiss`() = runTest {
|
||||
val presenter = createPresenter()
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
val selectedEvent = aTimelineItemEvent()
|
||||
initialState.eventSink(ReadReceiptBottomSheetEvents.EventSelected(selectedEvent))
|
||||
initialState.eventSink(ReadReceiptBottomSheetEvent.EventSelected(selectedEvent))
|
||||
skipItems(1)
|
||||
initialState.eventSink(ReadReceiptBottomSheetEvents.Dismiss)
|
||||
initialState.eventSink(ReadReceiptBottomSheetEvent.Dismiss)
|
||||
assertThat(awaitItem().selectedEvent).isNull()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,10 +8,7 @@
|
||||
|
||||
package io.element.android.features.messages.impl.typing
|
||||
|
||||
import app.cash.molecule.RecompositionMode
|
||||
import app.cash.molecule.moleculeFlow
|
||||
import app.cash.turbine.Event
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||
@@ -26,6 +23,7 @@ import io.element.android.libraries.matrix.test.room.aRoomMember
|
||||
import io.element.android.libraries.preferences.api.store.SessionPreferencesStore
|
||||
import io.element.android.libraries.preferences.test.InMemorySessionPreferencesStore
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
import io.element.android.tests.testutils.test
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.test.runTest
|
||||
@@ -40,9 +38,7 @@ class TypingNotificationPresenterTest {
|
||||
@Test
|
||||
fun `present - initial state`() = runTest {
|
||||
val presenter = createPresenter()
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.renderTypingNotifications).isTrue()
|
||||
assertThat(initialState.typingMembers).isEmpty()
|
||||
@@ -61,9 +57,7 @@ class TypingNotificationPresenterTest {
|
||||
joinedRoom = room,
|
||||
sessionPreferencesStore = sessionPreferencesStore,
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.renderTypingNotifications).isFalse()
|
||||
@@ -95,9 +89,7 @@ class TypingNotificationPresenterTest {
|
||||
val typingMembersFlow = MutableStateFlow<List<UserId>>(emptyList())
|
||||
val room = FakeJoinedRoom(roomTypingMembersFlow = typingMembersFlow)
|
||||
val presenter = createPresenter(joinedRoom = room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.typingMembers).isEmpty()
|
||||
typingMembersFlow.emit(listOf(A_USER_ID_2))
|
||||
@@ -133,9 +125,7 @@ class TypingNotificationPresenterTest {
|
||||
)
|
||||
}
|
||||
val presenter = createPresenter(joinedRoom = room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.typingMembers).isEmpty()
|
||||
typingMembersFlow.emit(listOf(A_USER_ID_2))
|
||||
@@ -160,9 +150,7 @@ class TypingNotificationPresenterTest {
|
||||
val typingMembersFlow = MutableStateFlow<List<UserId>>(emptyList())
|
||||
val room = FakeJoinedRoom(roomTypingMembersFlow = typingMembersFlow)
|
||||
val presenter = createPresenter(joinedRoom = room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.typingMembers).isEmpty()
|
||||
typingMembersFlow.emit(listOf(A_USER_ID_2))
|
||||
@@ -194,9 +182,7 @@ class TypingNotificationPresenterTest {
|
||||
val typingMembersFlow = MutableStateFlow<List<UserId>>(emptyList())
|
||||
val room = FakeJoinedRoom(roomTypingMembersFlow = typingMembersFlow)
|
||||
val presenter = createPresenter(joinedRoom = room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.typingMembers).isEmpty()
|
||||
typingMembersFlow.emit(listOf(A_USER_ID_2))
|
||||
|
||||
Reference in New Issue
Block a user