Merge pull request #3023 from element-hq/feature/fga/update_rust_sdk_0.2.25
Feature/fga/update rust sdk 0.2.25
This commit is contained in:
commit
5b18f9cddd
184 changed files with 656 additions and 1047 deletions
|
|
@ -46,7 +46,6 @@ import io.element.android.features.messages.impl.timeline.TimelineState
|
|||
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionPresenter
|
||||
import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryPresenter
|
||||
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetPresenter
|
||||
import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuPresenter
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemCallNotifyContent
|
||||
|
|
@ -107,7 +106,6 @@ class MessagesPresenter @AssistedInject constructor(
|
|||
private val actionListPresenter: ActionListPresenter,
|
||||
private val customReactionPresenter: CustomReactionPresenter,
|
||||
private val reactionSummaryPresenter: ReactionSummaryPresenter,
|
||||
private val retrySendMenuPresenter: RetrySendMenuPresenter,
|
||||
private val readReceiptBottomSheetPresenter: ReadReceiptBottomSheetPresenter,
|
||||
private val networkMonitor: NetworkMonitor,
|
||||
private val snackbarDispatcher: SnackbarDispatcher,
|
||||
|
|
@ -140,7 +138,6 @@ class MessagesPresenter @AssistedInject constructor(
|
|||
val actionListState = actionListPresenter.present()
|
||||
val customReactionState = customReactionPresenter.present()
|
||||
val reactionSummaryState = reactionSummaryPresenter.present()
|
||||
val retryState = retrySendMenuPresenter.present()
|
||||
val readReceiptBottomSheetState = readReceiptBottomSheetPresenter.present()
|
||||
|
||||
val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
|
||||
|
|
@ -231,7 +228,6 @@ class MessagesPresenter @AssistedInject constructor(
|
|||
actionListState = actionListState,
|
||||
customReactionState = customReactionState,
|
||||
reactionSummaryState = reactionSummaryState,
|
||||
retrySendMenuState = retryState,
|
||||
readReceiptBottomSheetState = readReceiptBottomSheetState,
|
||||
hasNetworkConnection = networkConnectionStatus == NetworkStatus.Online,
|
||||
snackbarMessage = snackbarMessage,
|
||||
|
|
@ -309,11 +305,9 @@ class MessagesPresenter @AssistedInject constructor(
|
|||
}
|
||||
|
||||
private suspend fun handleActionRedact(event: TimelineItem.Event) {
|
||||
if (event.failedToSend) {
|
||||
// If the message hasn't been sent yet, just cancel it
|
||||
event.transactionId?.let { room.cancelSend(it) }
|
||||
} else if (event.eventId != null) {
|
||||
room.redactEvent(event.eventId)
|
||||
timelineController.invokeOnCurrentTimeline {
|
||||
redactEvent(eventId = event.eventId, transactionId = event.transactionId, reason = null)
|
||||
.onFailure { Timber.e(it) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ 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
|
||||
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetState
|
||||
import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuState
|
||||
import io.element.android.features.messages.impl.typing.TypingNotificationState
|
||||
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerState
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
|
|
@ -47,7 +46,6 @@ data class MessagesState(
|
|||
val actionListState: ActionListState,
|
||||
val customReactionState: CustomReactionState,
|
||||
val reactionSummaryState: ReactionSummaryState,
|
||||
val retrySendMenuState: RetrySendMenuState,
|
||||
val readReceiptBottomSheetState: ReadReceiptBottomSheetState,
|
||||
val hasNetworkConnection: Boolean,
|
||||
val snackbarMessage: SnackbarMessage?,
|
||||
|
|
|
|||
|
|
@ -31,8 +31,6 @@ import io.element.android.features.messages.impl.timeline.components.reactionsum
|
|||
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.ReadReceiptBottomSheetState
|
||||
import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuState
|
||||
import io.element.android.features.messages.impl.timeline.components.retrysendmenu.aRetrySendMenuState
|
||||
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.features.messages.impl.typing.aTypingNotificationState
|
||||
|
|
@ -110,7 +108,6 @@ fun aMessagesState(
|
|||
// Render a focused event for an event with sender information displayed
|
||||
focusedEventIndex = 2,
|
||||
),
|
||||
retrySendMenuState: RetrySendMenuState = aRetrySendMenuState(),
|
||||
readReceiptBottomSheetState: ReadReceiptBottomSheetState = aReadReceiptBottomSheetState(),
|
||||
actionListState: ActionListState = anActionListState(),
|
||||
customReactionState: CustomReactionState = aCustomReactionState(),
|
||||
|
|
@ -132,7 +129,6 @@ fun aMessagesState(
|
|||
voiceMessageComposerState = voiceMessageComposerState,
|
||||
typingNotificationState = aTypingNotificationState(),
|
||||
timelineState = timelineState,
|
||||
retrySendMenuState = retrySendMenuState,
|
||||
readReceiptBottomSheetState = readReceiptBottomSheetState,
|
||||
actionListState = actionListState,
|
||||
customReactionState = customReactionState,
|
||||
|
|
|
|||
|
|
@ -74,8 +74,6 @@ import io.element.android.features.messages.impl.timeline.components.reactionsum
|
|||
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.retrysendmenu.RetrySendMenuEvents
|
||||
import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMessageMenu
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerEvents
|
||||
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessagePermissionRationaleDialog
|
||||
|
|
@ -103,7 +101,6 @@ import io.element.android.libraries.designsystem.utils.OnLifecycleEvent
|
|||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarHost
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import timber.log.Timber
|
||||
|
|
@ -212,11 +209,6 @@ fun MessagesView(
|
|||
onMessageLongClick = ::onMessageLongClick,
|
||||
onUserDataClick = onUserDataClick,
|
||||
onLinkClick = onLinkClick,
|
||||
onTimestampClick = { event ->
|
||||
if (event.localSendState is LocalEventSendState.SendingFailed) {
|
||||
state.retrySendMenuState.eventSink(RetrySendMenuEvents.EventSelected(event))
|
||||
}
|
||||
},
|
||||
onReactionClick = ::onEmojiReactionClick,
|
||||
onReactionLongClick = ::onEmojiReactionLongClick,
|
||||
onMoreReactionsClick = ::onMoreReactionsClick,
|
||||
|
|
@ -258,7 +250,6 @@ fun MessagesView(
|
|||
)
|
||||
|
||||
ReactionSummaryView(state = state.reactionSummaryState)
|
||||
RetrySendMessageMenu(state = state.retrySendMenuState)
|
||||
ReadReceiptBottomSheet(
|
||||
state = state.readReceiptBottomSheetState,
|
||||
onUserDataClick = onUserDataClick,
|
||||
|
|
@ -319,7 +310,6 @@ private fun MessagesViewContent(
|
|||
onMoreReactionsClick: (TimelineItem.Event) -> Unit,
|
||||
onReadReceiptClick: (TimelineItem.Event) -> Unit,
|
||||
onMessageLongClick: (TimelineItem.Event) -> Unit,
|
||||
onTimestampClick: (TimelineItem.Event) -> Unit,
|
||||
onSendLocationClick: () -> Unit,
|
||||
onCreatePollClick: () -> Unit,
|
||||
onJoinCallClick: () -> Unit,
|
||||
|
|
@ -387,7 +377,6 @@ private fun MessagesViewContent(
|
|||
onLinkClick = onLinkClick,
|
||||
onMessageClick = onMessageClick,
|
||||
onMessageLongClick = onMessageLongClick,
|
||||
onTimestampClick = onTimestampClick,
|
||||
onSwipeToReply = onSwipeToReply,
|
||||
onReactionClick = onReactionClick,
|
||||
onReactionLongClick = onReactionLongClick,
|
||||
|
|
|
|||
|
|
@ -181,7 +181,7 @@ class ActionListPresenter @Inject constructor(
|
|||
add(TimelineItemAction.Forward)
|
||||
}
|
||||
}
|
||||
if (timelineItem.isMine && timelineItem.isTextMessage) {
|
||||
if (timelineItem.isEditable) {
|
||||
add(TimelineItemAction.Edit)
|
||||
}
|
||||
if (timelineItem.content.canBeCopied()) {
|
||||
|
|
|
|||
|
|
@ -83,7 +83,6 @@ fun TimelineView(
|
|||
onLinkClick: (String) -> Unit,
|
||||
onMessageClick: (TimelineItem.Event) -> Unit,
|
||||
onMessageLongClick: (TimelineItem.Event) -> Unit,
|
||||
onTimestampClick: (TimelineItem.Event) -> Unit,
|
||||
onSwipeToReply: (TimelineItem.Event) -> Unit,
|
||||
onReactionClick: (emoji: String, TimelineItem.Event) -> Unit,
|
||||
onReactionLongClick: (emoji: String, TimelineItem.Event) -> Unit,
|
||||
|
|
@ -148,7 +147,6 @@ fun TimelineView(
|
|||
onReactionLongClick = onReactionLongClick,
|
||||
onMoreReactionsClick = onMoreReactionsClick,
|
||||
onReadReceiptClick = onReadReceiptClick,
|
||||
onTimestampClick = onTimestampClick,
|
||||
eventSink = state.eventSink,
|
||||
onSwipeToReply = onSwipeToReply,
|
||||
onJoinCallClick = onJoinCallClick,
|
||||
|
|
@ -245,8 +243,8 @@ private fun BoxScope.TimelineScrollHelper(
|
|||
// Use inverse of canAutoScroll otherwise we might briefly see the before the scroll animation is triggered
|
||||
isVisible = !canAutoScroll || forceJumpToBottomVisibility || !isLive,
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomEnd)
|
||||
.padding(end = 24.dp, bottom = 12.dp),
|
||||
.align(Alignment.BottomEnd)
|
||||
.padding(end = 24.dp, bottom = 12.dp),
|
||||
onClick = { jumpToBottom() },
|
||||
)
|
||||
}
|
||||
|
|
@ -273,8 +271,8 @@ private fun JumpToBottomButton(
|
|||
) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.size(24.dp)
|
||||
.rotate(90f),
|
||||
.size(24.dp)
|
||||
.rotate(90f),
|
||||
imageVector = CompoundIcons.ArrowRight(),
|
||||
contentDescription = stringResource(id = CommonStrings.a11y_jump_to_bottom)
|
||||
)
|
||||
|
|
@ -301,7 +299,6 @@ internal fun TimelineViewPreview(
|
|||
onLinkClick = {},
|
||||
onMessageClick = {},
|
||||
onMessageLongClick = {},
|
||||
onTimestampClick = {},
|
||||
onSwipeToReply = {},
|
||||
onReactionClick = { _, _ -> },
|
||||
onReactionLongClick = { _, _ -> },
|
||||
|
|
|
|||
|
|
@ -45,6 +45,5 @@ internal fun ATimelineItemEventRow(
|
|||
onMoreReactionsClick = {},
|
||||
onReadReceiptClick = {},
|
||||
onSwipeToReply = {},
|
||||
onTimestampClick = {},
|
||||
eventSink = {},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -16,61 +16,34 @@
|
|||
|
||||
package io.element.android.features.messages.impl.timeline.components
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material.ripple.rememberRipple
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
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.model.TimelineItem
|
||||
import io.element.android.features.messages.impl.timeline.model.event.isEdited
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
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.ui.strings.CommonStrings
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun TimelineEventTimestampView(
|
||||
event: TimelineItem.Event,
|
||||
onClick: () -> Unit,
|
||||
onLongClick: () -> Unit,
|
||||
formattedTime: String,
|
||||
isMessageEdited: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val formattedTime = event.sentTime
|
||||
val hasMessageSendingFailed = event.localSendState is LocalEventSendState.SendingFailed
|
||||
val isMessageEdited = event.content.isEdited()
|
||||
val tint = if (hasMessageSendingFailed) MaterialTheme.colorScheme.error else null
|
||||
val clickModifier = if (hasMessageSendingFailed) {
|
||||
Modifier.combinedClickable(
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
indication = rememberRipple(bounded = false),
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
)
|
||||
} else {
|
||||
Modifier
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.then(clickModifier)
|
||||
// Add extra padding for touch target size
|
||||
.padding(PaddingValues(start = TimelineEventTimestampViewDefaults.spacing))
|
||||
.then(modifier),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
|
|
@ -79,35 +52,22 @@ fun TimelineEventTimestampView(
|
|||
Text(
|
||||
stringResource(CommonStrings.common_edited_suffix),
|
||||
style = ElementTheme.typography.fontBodyXsRegular,
|
||||
color = tint ?: MaterialTheme.colorScheme.secondary,
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
)
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
}
|
||||
Text(
|
||||
formattedTime,
|
||||
style = ElementTheme.typography.fontBodyXsRegular,
|
||||
color = tint ?: MaterialTheme.colorScheme.secondary,
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
)
|
||||
if (hasMessageSendingFailed && tint != null) {
|
||||
Spacer(modifier = Modifier.width(2.dp))
|
||||
Icon(
|
||||
imageVector = CompoundIcons.Error(),
|
||||
contentDescription = stringResource(id = CommonStrings.common_sending_failed),
|
||||
tint = tint,
|
||||
modifier = Modifier.size(15.dp, 18.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun TimelineEventTimestampViewPreview(@PreviewParameter(TimelineItemEventForTimestampViewProvider::class) event: TimelineItem.Event) = ElementPreview {
|
||||
TimelineEventTimestampView(
|
||||
event = event,
|
||||
onClick = {},
|
||||
onLongClick = {},
|
||||
)
|
||||
TimelineEventTimestampView(formattedTime = event.sentTime, isMessageEdited = event.content.isEdited())
|
||||
}
|
||||
|
||||
object TimelineEventTimestampViewDefaults {
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
|
|||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemImageContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.canBeRepliedTo
|
||||
import io.element.android.features.messages.impl.timeline.model.event.isEdited
|
||||
import io.element.android.features.messages.impl.timeline.model.eventId
|
||||
import io.element.android.features.messages.impl.timeline.model.metadata
|
||||
import io.element.android.libraries.designsystem.atomic.atoms.PlaceholderAtom
|
||||
|
|
@ -130,7 +131,6 @@ fun TimelineItemEventRow(
|
|||
onLinkClick: (String) -> Unit,
|
||||
onUserDataClick: (UserId) -> Unit,
|
||||
inReplyToClick: (EventId) -> Unit,
|
||||
onTimestampClick: (TimelineItem.Event) -> Unit,
|
||||
onReactionClick: (emoji: String, eventId: TimelineItem.Event) -> Unit,
|
||||
onReactionLongClick: (emoji: String, eventId: TimelineItem.Event) -> Unit,
|
||||
onMoreReactionsClick: (eventId: TimelineItem.Event) -> Unit,
|
||||
|
|
@ -190,7 +190,6 @@ fun TimelineItemEventRow(
|
|||
interactionSource = interactionSource,
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
onTimestampClick = onTimestampClick,
|
||||
inReplyToClick = ::inReplyToClick,
|
||||
onUserDataClick = ::onUserDataClick,
|
||||
onReactionClick = { emoji -> onReactionClick(emoji, event) },
|
||||
|
|
@ -209,7 +208,6 @@ fun TimelineItemEventRow(
|
|||
interactionSource = interactionSource,
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
onTimestampClick = onTimestampClick,
|
||||
inReplyToClick = ::inReplyToClick,
|
||||
onUserDataClick = ::onUserDataClick,
|
||||
onReactionClick = { emoji -> onReactionClick(emoji, event) },
|
||||
|
|
@ -265,7 +263,6 @@ private fun TimelineItemEventRowContent(
|
|||
interactionSource: MutableInteractionSource,
|
||||
onClick: () -> Unit,
|
||||
onLongClick: () -> Unit,
|
||||
onTimestampClick: (TimelineItem.Event) -> Unit,
|
||||
inReplyToClick: () -> Unit,
|
||||
onUserDataClick: () -> Unit,
|
||||
onReactionClick: (emoji: String) -> Unit,
|
||||
|
|
@ -337,9 +334,6 @@ private fun TimelineItemEventRowContent(
|
|||
event = event,
|
||||
onMessageLongClick = onLongClick,
|
||||
inReplyToClick = inReplyToClick,
|
||||
onTimestampClick = {
|
||||
onTimestampClick(event)
|
||||
},
|
||||
onLinkClick = onLinkClick,
|
||||
eventSink = eventSink,
|
||||
)
|
||||
|
|
@ -419,7 +413,6 @@ private fun MessageEventBubbleContent(
|
|||
event: TimelineItem.Event,
|
||||
onMessageLongClick: () -> Unit,
|
||||
inReplyToClick: () -> Unit,
|
||||
onTimestampClick: () -> Unit,
|
||||
onLinkClick: (String) -> Unit,
|
||||
eventSink: (TimelineEvents.EventFromTimelineItem) -> Unit,
|
||||
@SuppressLint("ModifierParameter")
|
||||
|
|
@ -467,9 +460,8 @@ private fun MessageEventBubbleContent(
|
|||
Box(modifier, contentAlignment = Alignment.Center) {
|
||||
content {}
|
||||
TimelineEventTimestampView(
|
||||
event = event,
|
||||
onClick = onTimestampClick,
|
||||
onLongClick = ::onTimestampLongClick,
|
||||
formattedTime = event.sentTime,
|
||||
isMessageEdited = event.content.isEdited(),
|
||||
modifier = Modifier
|
||||
// Outer padding
|
||||
.padding(horizontal = 4.dp, vertical = 4.dp)
|
||||
|
|
@ -489,9 +481,8 @@ private fun MessageEventBubbleContent(
|
|||
content = { content(this::onContentLayoutChange) },
|
||||
overlay = {
|
||||
TimelineEventTimestampView(
|
||||
event = event,
|
||||
onClick = onTimestampClick,
|
||||
onLongClick = ::onTimestampLongClick,
|
||||
formattedTime = event.sentTime,
|
||||
isMessageEdited = event.content.isEdited(),
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 8.dp, vertical = 4.dp)
|
||||
)
|
||||
|
|
@ -501,9 +492,8 @@ private fun MessageEventBubbleContent(
|
|||
Column(modifier) {
|
||||
content {}
|
||||
TimelineEventTimestampView(
|
||||
event = event,
|
||||
onClick = onTimestampClick,
|
||||
onLongClick = ::onTimestampLongClick,
|
||||
formattedTime = event.sentTime,
|
||||
isMessageEdited = event.content.isEdited(),
|
||||
modifier = Modifier
|
||||
.align(Alignment.End)
|
||||
.padding(horizontal = 8.dp, vertical = 4.dp)
|
||||
|
|
|
|||
|
|
@ -49,7 +49,6 @@ fun TimelineItemGroupedEventsRow(
|
|||
inReplyToClick: (EventId) -> Unit,
|
||||
onUserDataClick: (UserId) -> Unit,
|
||||
onLinkClick: (String) -> Unit,
|
||||
onTimestampClick: (TimelineItem.Event) -> Unit,
|
||||
onReactionClick: (key: String, TimelineItem.Event) -> Unit,
|
||||
onReactionLongClick: (key: String, TimelineItem.Event) -> Unit,
|
||||
onMoreReactionsClick: (TimelineItem.Event) -> Unit,
|
||||
|
|
@ -76,7 +75,6 @@ fun TimelineItemGroupedEventsRow(
|
|||
inReplyToClick = inReplyToClick,
|
||||
onUserDataClick = onUserDataClick,
|
||||
onLinkClick = onLinkClick,
|
||||
onTimestampClick = onTimestampClick,
|
||||
onReactionClick = onReactionClick,
|
||||
onReactionLongClick = onReactionLongClick,
|
||||
onMoreReactionsClick = onMoreReactionsClick,
|
||||
|
|
@ -100,7 +98,6 @@ private fun TimelineItemGroupedEventsRowContent(
|
|||
inReplyToClick: (EventId) -> Unit,
|
||||
onUserDataClick: (UserId) -> Unit,
|
||||
onLinkClick: (String) -> Unit,
|
||||
onTimestampClick: (TimelineItem.Event) -> Unit,
|
||||
onReactionClick: (key: String, TimelineItem.Event) -> Unit,
|
||||
onReactionLongClick: (key: String, TimelineItem.Event) -> Unit,
|
||||
onMoreReactionsClick: (TimelineItem.Event) -> Unit,
|
||||
|
|
@ -133,7 +130,6 @@ private fun TimelineItemGroupedEventsRowContent(
|
|||
inReplyToClick = inReplyToClick,
|
||||
onUserDataClick = onUserDataClick,
|
||||
onLinkClick = onLinkClick,
|
||||
onTimestampClick = onTimestampClick,
|
||||
onReactionClick = onReactionClick,
|
||||
onReactionLongClick = onReactionLongClick,
|
||||
onMoreReactionsClick = onMoreReactionsClick,
|
||||
|
|
@ -175,7 +171,6 @@ internal fun TimelineItemGroupedEventsRowContentExpandedPreview() = ElementPrevi
|
|||
inReplyToClick = {},
|
||||
onUserDataClick = {},
|
||||
onLinkClick = {},
|
||||
onTimestampClick = {},
|
||||
onReactionClick = { _, _ -> },
|
||||
onReactionLongClick = { _, _ -> },
|
||||
onMoreReactionsClick = {},
|
||||
|
|
@ -200,7 +195,6 @@ internal fun TimelineItemGroupedEventsRowContentCollapsePreview() = ElementPrevi
|
|||
inReplyToClick = {},
|
||||
onUserDataClick = {},
|
||||
onLinkClick = {},
|
||||
onTimestampClick = {},
|
||||
onReactionClick = { _, _ -> },
|
||||
onReactionLongClick = { _, _ -> },
|
||||
onMoreReactionsClick = {},
|
||||
|
|
|
|||
|
|
@ -54,7 +54,6 @@ internal fun TimelineItemRow(
|
|||
onReactionLongClick: (key: String, TimelineItem.Event) -> Unit,
|
||||
onMoreReactionsClick: (TimelineItem.Event) -> Unit,
|
||||
onReadReceiptClick: (TimelineItem.Event) -> Unit,
|
||||
onTimestampClick: (TimelineItem.Event) -> Unit,
|
||||
onSwipeToReply: (TimelineItem.Event) -> Unit,
|
||||
onJoinCallClick: () -> Unit,
|
||||
eventSink: (TimelineEvents.EventFromTimelineItem) -> Unit,
|
||||
|
|
@ -118,7 +117,6 @@ internal fun TimelineItemRow(
|
|||
onReactionLongClick = onReactionLongClick,
|
||||
onMoreReactionsClick = onMoreReactionsClick,
|
||||
onReadReceiptClick = onReadReceiptClick,
|
||||
onTimestampClick = onTimestampClick,
|
||||
onSwipeToReply = { onSwipeToReply(timelineItem) },
|
||||
eventSink = eventSink,
|
||||
)
|
||||
|
|
@ -137,7 +135,6 @@ internal fun TimelineItemRow(
|
|||
inReplyToClick = inReplyToClick,
|
||||
onUserDataClick = onUserDataClick,
|
||||
onLinkClick = onLinkClick,
|
||||
onTimestampClick = onTimestampClick,
|
||||
onReactionClick = onReactionClick,
|
||||
onReactionLongClick = onReactionLongClick,
|
||||
onMoreReactionsClick = onMoreReactionsClick,
|
||||
|
|
|
|||
|
|
@ -81,7 +81,8 @@ fun TimelineItemReadReceiptView(
|
|||
}
|
||||
} else {
|
||||
when (state.sendState) {
|
||||
LocalEventSendState.NotSentYet -> {
|
||||
is LocalEventSendState.SendingFailed,
|
||||
is LocalEventSendState.NotSentYet -> {
|
||||
ReadReceiptsRow(modifier) {
|
||||
Icon(
|
||||
modifier = Modifier.padding(2.dp),
|
||||
|
|
@ -91,10 +92,6 @@ fun TimelineItemReadReceiptView(
|
|||
)
|
||||
}
|
||||
}
|
||||
LocalEventSendState.Canceled -> Unit
|
||||
is LocalEventSendState.SendingFailed -> {
|
||||
// Error? The timestamp is already displayed in red
|
||||
}
|
||||
null,
|
||||
is LocalEventSendState.Sent -> {
|
||||
if (state.isLastOutgoingMessage) {
|
||||
|
|
|
|||
|
|
@ -123,7 +123,6 @@ private fun ReadReceiptBottomSheetContent(
|
|||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun ReadReceiptBottomSheetPreview(@PreviewParameter(ReadReceiptBottomSheetStateProvider::class) state: ReadReceiptBottomSheetState) = ElementPreview {
|
||||
// TODO restore RetrySendMessageMenuBottomSheet once the issue with bottom sheet not being previewable is fixed
|
||||
Column {
|
||||
ReadReceiptBottomSheetContent(
|
||||
state = state,
|
||||
|
|
|
|||
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.features.messages.impl.timeline.components.retrysendmenu
|
||||
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
|
||||
sealed interface RetrySendMenuEvents {
|
||||
data class EventSelected(val event: TimelineItem.Event) : RetrySendMenuEvents
|
||||
data object Retry : RetrySendMenuEvents
|
||||
data object Remove : RetrySendMenuEvents
|
||||
data object Dismiss : RetrySendMenuEvents
|
||||
}
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.features.messages.impl.timeline.components.retrysendmenu
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
class RetrySendMenuPresenter @Inject constructor(
|
||||
private val room: MatrixRoom,
|
||||
) : Presenter<RetrySendMenuState> {
|
||||
@Composable
|
||||
override fun present(): RetrySendMenuState {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
var selectedEvent: TimelineItem.Event? by remember { mutableStateOf(null) }
|
||||
|
||||
fun handleEvent(event: RetrySendMenuEvents) {
|
||||
when (event) {
|
||||
is RetrySendMenuEvents.EventSelected -> {
|
||||
selectedEvent = event.event
|
||||
}
|
||||
RetrySendMenuEvents.Retry -> {
|
||||
coroutineScope.launch {
|
||||
selectedEvent?.transactionId?.let { transactionId ->
|
||||
room.retrySendMessage(transactionId)
|
||||
}
|
||||
selectedEvent = null
|
||||
}
|
||||
}
|
||||
RetrySendMenuEvents.Remove -> {
|
||||
coroutineScope.launch {
|
||||
selectedEvent?.transactionId?.let { transactionId ->
|
||||
room.cancelSend(transactionId)
|
||||
}
|
||||
selectedEvent = null
|
||||
}
|
||||
}
|
||||
RetrySendMenuEvents.Dismiss -> {
|
||||
selectedEvent = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return RetrySendMenuState(
|
||||
selectedEvent = selectedEvent,
|
||||
eventSink = { handleEvent(it) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.features.messages.impl.timeline.components.retrysendmenu
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
|
||||
@Immutable
|
||||
data class RetrySendMenuState(
|
||||
val selectedEvent: TimelineItem.Event?,
|
||||
val eventSink: (RetrySendMenuEvents) -> Unit,
|
||||
)
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.features.messages.impl.timeline.components.retrysendmenu
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.features.messages.impl.timeline.aTimelineItemEvent
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
|
||||
class RetrySendMenuStateProvider : PreviewParameterProvider<RetrySendMenuState> {
|
||||
override val values: Sequence<RetrySendMenuState> = sequenceOf(
|
||||
aRetrySendMenuState(),
|
||||
aRetrySendMenuState(event = aTimelineItemEvent()),
|
||||
)
|
||||
}
|
||||
|
||||
fun aRetrySendMenuState(
|
||||
event: TimelineItem.Event? = null,
|
||||
eventSink: (RetrySendMenuEvents) -> Unit = {},
|
||||
) = RetrySendMenuState(
|
||||
selectedEvent = event,
|
||||
eventSink = eventSink,
|
||||
)
|
||||
|
|
@ -1,159 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.features.messages.impl.timeline.components.retrysendmenu
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.ListItemDefaults
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.SheetState
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
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.impl.R
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.ModalBottomSheet
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
internal fun RetrySendMessageMenu(
|
||||
state: RetrySendMenuState,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val isVisible = state.selectedEvent != null
|
||||
|
||||
fun onDismiss() {
|
||||
state.eventSink(RetrySendMenuEvents.Dismiss)
|
||||
}
|
||||
|
||||
fun onRetry() {
|
||||
state.eventSink(RetrySendMenuEvents.Retry)
|
||||
}
|
||||
|
||||
fun onRemove() {
|
||||
state.eventSink(RetrySendMenuEvents.Remove)
|
||||
}
|
||||
|
||||
RetrySendMessageMenuBottomSheet(
|
||||
modifier = modifier,
|
||||
isVisible = isVisible,
|
||||
onRetry = ::onRetry,
|
||||
onRemove = ::onRemove,
|
||||
onDismiss = ::onDismiss
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun RetrySendMessageMenuBottomSheet(
|
||||
isVisible: Boolean,
|
||||
onRetry: () -> Unit,
|
||||
onRemove: () -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val sheetState = rememberModalBottomSheetState()
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
if (isVisible) {
|
||||
ModalBottomSheet(
|
||||
modifier = modifier,
|
||||
// modifier = modifier.navigationBarsPadding() - FIXME after https://issuetracker.google.com/issues/275849044
|
||||
// .imePadding()
|
||||
sheetState = sheetState,
|
||||
onDismissRequest = {
|
||||
coroutineScope.launch {
|
||||
sheetState.hide()
|
||||
onDismiss()
|
||||
}
|
||||
}
|
||||
) {
|
||||
RetrySendMenuContents(
|
||||
onRetry = onRetry,
|
||||
onRemove = onRemove,
|
||||
)
|
||||
// FIXME remove after https://issuetracker.google.com/issues/275849044
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun ColumnScope.RetrySendMenuContents(
|
||||
onRetry: () -> Unit,
|
||||
onRemove: () -> Unit,
|
||||
sheetState: SheetState = rememberModalBottomSheetState(),
|
||||
) {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
ListItem(headlineContent = {
|
||||
Text(
|
||||
text = stringResource(R.string.screen_room_retry_send_menu_title),
|
||||
style = ElementTheme.typography.fontBodyLgMedium,
|
||||
)
|
||||
})
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(
|
||||
text = stringResource(R.string.screen_room_retry_send_menu_send_again_action),
|
||||
style = ElementTheme.typography.fontBodyLgRegular,
|
||||
)
|
||||
},
|
||||
modifier = Modifier.clickable {
|
||||
coroutineScope.launch {
|
||||
sheetState.hide()
|
||||
onRetry()
|
||||
}
|
||||
}
|
||||
)
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(
|
||||
text = stringResource(CommonStrings.action_remove),
|
||||
style = ElementTheme.typography.fontBodyLgRegular,
|
||||
)
|
||||
},
|
||||
colors = ListItemDefaults.colors(headlineColor = MaterialTheme.colorScheme.error),
|
||||
modifier = Modifier.clickable {
|
||||
coroutineScope.launch {
|
||||
sheetState.hide()
|
||||
onRemove()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun RetrySendMessageMenuPreview(@PreviewParameter(RetrySendMenuStateProvider::class) state: RetrySendMenuState) = ElementPreview {
|
||||
RetrySendMessageMenu(
|
||||
state = state,
|
||||
)
|
||||
}
|
||||
|
|
@ -38,7 +38,6 @@ import io.element.android.features.messages.impl.timeline.components.customreact
|
|||
import io.element.android.features.messages.impl.timeline.components.customreaction.FakeEmojibaseProvider
|
||||
import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryPresenter
|
||||
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetPresenter
|
||||
import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuPresenter
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent
|
||||
|
|
@ -64,19 +63,18 @@ import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatch
|
|||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.TransactionId
|
||||
import io.element.android.libraries.matrix.api.media.MediaSource
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
|
||||
import io.element.android.libraries.matrix.api.room.MessageEventType
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipState
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState
|
||||
import io.element.android.libraries.matrix.api.user.CurrentSessionIdHolder
|
||||
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
|
||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID_2
|
||||
import io.element.android.libraries.matrix.test.A_TRANSACTION_ID
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
||||
import io.element.android.libraries.matrix.test.permalink.FakePermalinkBuilder
|
||||
|
|
@ -445,41 +443,25 @@ class MessagesPresenterTest {
|
|||
@Test
|
||||
fun `present - handle action redact`() = runTest {
|
||||
val coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true)
|
||||
val matrixRoom = FakeMatrixRoom()
|
||||
val presenter = createMessagesPresenter(matrixRoom = matrixRoom, coroutineDispatchers = coroutineDispatchers)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
skipItems(1)
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Redact, aMessageEvent()))
|
||||
assertThat(matrixRoom.redactEventEventIdParam).isEqualTo(AN_EVENT_ID)
|
||||
assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - handle action redact message in error, in this case the message is just cancelled`() = runTest {
|
||||
val coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true)
|
||||
val matrixRoom = FakeMatrixRoom()
|
||||
val liveTimeline = FakeTimeline()
|
||||
val matrixRoom = FakeMatrixRoom(liveTimeline = liveTimeline)
|
||||
|
||||
val redactEventLambda = lambdaRecorder { _: EventId?, _: TransactionId?, _: String? -> Result.success(true) }
|
||||
liveTimeline.redactEventLambda = redactEventLambda
|
||||
|
||||
val presenter = createMessagesPresenter(matrixRoom = matrixRoom, coroutineDispatchers = coroutineDispatchers)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
skipItems(1)
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink.invoke(
|
||||
MessagesEvents.HandleAction(
|
||||
action = TimelineItemAction.Redact,
|
||||
event = aMessageEvent(
|
||||
transactionId = A_TRANSACTION_ID,
|
||||
sendState = LocalEventSendState.SendingFailed("Failed to send message")
|
||||
)
|
||||
)
|
||||
)
|
||||
assertThat(matrixRoom.cancelSendCount).isEqualTo(1)
|
||||
assertThat(matrixRoom.redactEventEventIdParam).isNull()
|
||||
val messageEvent = aMessageEvent()
|
||||
initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Redact, messageEvent))
|
||||
assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None)
|
||||
assert(redactEventLambda)
|
||||
.isCalledOnce()
|
||||
.with(value(messageEvent.eventId), value(messageEvent.transactionId), value(null))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -839,7 +821,6 @@ class MessagesPresenterTest {
|
|||
val readReceiptBottomSheetPresenter = ReadReceiptBottomSheetPresenter()
|
||||
val customReactionPresenter = CustomReactionPresenter(emojibaseProvider = FakeEmojibaseProvider())
|
||||
val reactionSummaryPresenter = ReactionSummaryPresenter(room = matrixRoom)
|
||||
val retrySendMenuPresenter = RetrySendMenuPresenter(room = matrixRoom)
|
||||
return MessagesPresenter(
|
||||
room = matrixRoom,
|
||||
composerPresenter = messageComposerPresenter,
|
||||
|
|
@ -849,7 +830,6 @@ class MessagesPresenterTest {
|
|||
actionListPresenter = actionListPresenter,
|
||||
customReactionPresenter = customReactionPresenter,
|
||||
reactionSummaryPresenter = reactionSummaryPresenter,
|
||||
retrySendMenuPresenter = retrySendMenuPresenter,
|
||||
readReceiptBottomSheetPresenter = readReceiptBottomSheetPresenter,
|
||||
networkMonitor = FakeNetworkMonitor(),
|
||||
snackbarDispatcher = SnackbarDispatcher(),
|
||||
|
|
|
|||
|
|
@ -52,8 +52,6 @@ import io.element.android.features.messages.impl.timeline.components.customreact
|
|||
import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryEvents
|
||||
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.retrysendmenu.RetrySendMenuEvents
|
||||
import io.element.android.features.messages.impl.timeline.components.retrysendmenu.aRetrySendMenuState
|
||||
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.UserId
|
||||
|
|
@ -74,6 +72,7 @@ import org.junit.Rule
|
|||
import org.junit.Test
|
||||
import org.junit.rules.TestRule
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.annotation.Config
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class MessagesViewTest {
|
||||
|
|
@ -145,22 +144,6 @@ class MessagesViewTest {
|
|||
callback.assertSuccess()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on an Event timestamp in error emits the expected Event`() {
|
||||
val eventsRecorder = EventsRecorder<RetrySendMenuEvents>()
|
||||
val state = aMessagesState(
|
||||
retrySendMenuState = aRetrySendMenuState(
|
||||
eventSink = eventsRecorder
|
||||
),
|
||||
)
|
||||
val timelineItem = state.timelineState.timelineItems[1] as TimelineItem.Event
|
||||
rule.setMessagesView(
|
||||
state = state,
|
||||
)
|
||||
rule.onAllNodesWithText(timelineItem.sentTime)[1].performClick()
|
||||
eventsRecorder.assertSingle(RetrySendMenuEvents.EventSelected(timelineItem))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `long clicking on an Event emits the expected Event userHasPermissionToSendMessage`() {
|
||||
`long clicking on an Event emits the expected Event`(userHasPermissionToSendMessage = true)
|
||||
|
|
@ -313,6 +296,7 @@ class MessagesViewTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Config(qualifiers = "h1024dp")
|
||||
fun `clicking on the sender of an Event invoke expected callback`() {
|
||||
val eventsRecorder = EventsRecorder<MessagesEvents>(expectEvents = false)
|
||||
val state = aMessagesState(
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ class ActionListPresenterTest {
|
|||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(isMine = true, content = TimelineItemRedactedContent)
|
||||
val messageEvent = aMessageEvent(isMine = true, isEditable = false, content = TimelineItemRedactedContent)
|
||||
initialState.eventSink.invoke(
|
||||
ActionListEvents.ComputeForMessage(
|
||||
event = messageEvent,
|
||||
|
|
@ -96,7 +96,11 @@ class ActionListPresenterTest {
|
|||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(isMine = false, content = TimelineItemRedactedContent)
|
||||
val messageEvent = aMessageEvent(
|
||||
isMine = false,
|
||||
isEditable = false,
|
||||
content = TimelineItemRedactedContent
|
||||
)
|
||||
initialState.eventSink.invoke(
|
||||
ActionListEvents.ComputeForMessage(
|
||||
event = messageEvent,
|
||||
|
|
@ -132,6 +136,7 @@ class ActionListPresenterTest {
|
|||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(
|
||||
isMine = false,
|
||||
isEditable = false,
|
||||
content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false, formattedBody = null)
|
||||
)
|
||||
initialState.eventSink.invoke(
|
||||
|
|
@ -174,6 +179,7 @@ class ActionListPresenterTest {
|
|||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(
|
||||
isMine = false,
|
||||
isEditable = false,
|
||||
content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false, formattedBody = null)
|
||||
)
|
||||
initialState.eventSink.invoke(
|
||||
|
|
@ -215,6 +221,7 @@ class ActionListPresenterTest {
|
|||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(
|
||||
isMine = false,
|
||||
isEditable = false,
|
||||
content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false, formattedBody = null)
|
||||
)
|
||||
initialState.eventSink.invoke(
|
||||
|
|
@ -256,6 +263,7 @@ class ActionListPresenterTest {
|
|||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(
|
||||
isMine = false,
|
||||
isEditable = false,
|
||||
content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false, formattedBody = null)
|
||||
)
|
||||
initialState.eventSink.invoke(
|
||||
|
|
@ -382,6 +390,7 @@ class ActionListPresenterTest {
|
|||
val initialState = awaitItem()
|
||||
val messageEvent = aMessageEvent(
|
||||
isMine = true,
|
||||
isEditable = false,
|
||||
content = aTimelineItemImageContent(),
|
||||
)
|
||||
initialState.eventSink.invoke(
|
||||
|
|
|
|||
|
|
@ -106,7 +106,6 @@ private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setTimel
|
|||
onLinkClick: (String) -> Unit = EnsureNeverCalledWithParam(),
|
||||
onMessageClick: (TimelineItem.Event) -> Unit = EnsureNeverCalledWithParam(),
|
||||
onMessageLongClick: (TimelineItem.Event) -> Unit = EnsureNeverCalledWithParam(),
|
||||
onTimestampClick: (TimelineItem.Event) -> Unit = EnsureNeverCalledWithParam(),
|
||||
onSwipeToReply: (TimelineItem.Event) -> Unit = EnsureNeverCalledWithParam(),
|
||||
onReactionClick: (emoji: String, TimelineItem.Event) -> Unit = EnsureNeverCalledWithTwoParams(),
|
||||
onReactionLongClick: (emoji: String, TimelineItem.Event) -> Unit = EnsureNeverCalledWithTwoParams(),
|
||||
|
|
@ -123,7 +122,6 @@ private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setTimel
|
|||
onLinkClick = onLinkClick,
|
||||
onMessageClick = onMessageClick,
|
||||
onMessageLongClick = onMessageLongClick,
|
||||
onTimestampClick = onTimestampClick,
|
||||
onSwipeToReply = onSwipeToReply,
|
||||
onReactionClick = onReactionClick,
|
||||
onReactionLongClick = onReactionLongClick,
|
||||
|
|
|
|||
|
|
@ -1,163 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.features.messages.impl.timeline.components.retrysendmenu
|
||||
|
||||
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.libraries.matrix.test.A_TRANSACTION_ID
|
||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class RetrySendMenuPresenterTest {
|
||||
@get:Rule
|
||||
val warmUpRule = WarmUpRule()
|
||||
|
||||
private val room = FakeMatrixRoom()
|
||||
private val presenter = RetrySendMenuPresenter(room)
|
||||
|
||||
@Test
|
||||
fun `present - handle event selected`() = runTest {
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
val selectedEvent = aTimelineItemEvent()
|
||||
initialState.eventSink(RetrySendMenuEvents.EventSelected(selectedEvent))
|
||||
assertThat(awaitItem().selectedEvent).isSameInstanceAs(selectedEvent)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - handle dismiss`() = runTest {
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
val selectedEvent = aTimelineItemEvent()
|
||||
initialState.eventSink(RetrySendMenuEvents.EventSelected(selectedEvent))
|
||||
skipItems(1)
|
||||
initialState.eventSink(RetrySendMenuEvents.Dismiss)
|
||||
assertThat(room.cancelSendCount).isEqualTo(0)
|
||||
assertThat(room.retrySendMessageCount).isEqualTo(0)
|
||||
assertThat(awaitItem().selectedEvent).isNull()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - handle resend with transactionId`() = runTest {
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
val selectedEvent = aTimelineItemEvent(transactionId = A_TRANSACTION_ID)
|
||||
initialState.eventSink(RetrySendMenuEvents.EventSelected(selectedEvent))
|
||||
skipItems(1)
|
||||
initialState.eventSink(RetrySendMenuEvents.Retry)
|
||||
assertThat(room.cancelSendCount).isEqualTo(0)
|
||||
assertThat(room.retrySendMessageCount).isEqualTo(1)
|
||||
assertThat(awaitItem().selectedEvent).isNull()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - handle resend without transactionId`() = runTest {
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
val selectedEvent = aTimelineItemEvent(transactionId = null)
|
||||
initialState.eventSink(RetrySendMenuEvents.EventSelected(selectedEvent))
|
||||
skipItems(1)
|
||||
initialState.eventSink(RetrySendMenuEvents.Retry)
|
||||
assertThat(room.cancelSendCount).isEqualTo(0)
|
||||
assertThat(room.retrySendMessageCount).isEqualTo(0)
|
||||
assertThat(awaitItem().selectedEvent).isNull()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - handle resend with error`() = runTest {
|
||||
room.givenRetrySendMessageResult(Result.failure(IllegalStateException("An error")))
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
val selectedEvent = aTimelineItemEvent(transactionId = A_TRANSACTION_ID)
|
||||
initialState.eventSink(RetrySendMenuEvents.EventSelected(selectedEvent))
|
||||
skipItems(1)
|
||||
initialState.eventSink(RetrySendMenuEvents.Retry)
|
||||
assertThat(room.cancelSendCount).isEqualTo(0)
|
||||
assertThat(room.retrySendMessageCount).isEqualTo(1)
|
||||
assertThat(awaitItem().selectedEvent).isNull()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - handle remove failed message with transactionId`() = runTest {
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
val selectedEvent = aTimelineItemEvent(transactionId = A_TRANSACTION_ID)
|
||||
initialState.eventSink(RetrySendMenuEvents.EventSelected(selectedEvent))
|
||||
skipItems(1)
|
||||
initialState.eventSink(RetrySendMenuEvents.Remove)
|
||||
assertThat(room.cancelSendCount).isEqualTo(1)
|
||||
assertThat(room.retrySendMessageCount).isEqualTo(0)
|
||||
assertThat(awaitItem().selectedEvent).isNull()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - handle remove failed message without transactionId`() = runTest {
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
val selectedEvent = aTimelineItemEvent(transactionId = null)
|
||||
initialState.eventSink(RetrySendMenuEvents.EventSelected(selectedEvent))
|
||||
skipItems(1)
|
||||
initialState.eventSink(RetrySendMenuEvents.Remove)
|
||||
assertThat(room.cancelSendCount).isEqualTo(0)
|
||||
assertThat(room.retrySendMessageCount).isEqualTo(0)
|
||||
assertThat(awaitItem().selectedEvent).isNull()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - handle remove failed message with error`() = runTest {
|
||||
room.givenRetrySendMessageResult(Result.failure(IllegalStateException("An error")))
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
val selectedEvent = aTimelineItemEvent(transactionId = A_TRANSACTION_ID)
|
||||
initialState.eventSink(RetrySendMenuEvents.EventSelected(selectedEvent))
|
||||
skipItems(1)
|
||||
initialState.eventSink(RetrySendMenuEvents.Remove)
|
||||
assertThat(room.cancelSendCount).isEqualTo(1)
|
||||
assertThat(room.retrySendMessageCount).isEqualTo(0)
|
||||
assertThat(awaitItem().selectedEvent).isNull()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.features.messages.impl.timeline.components.retrysendmenu
|
||||
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
|
||||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import io.element.android.features.messages.impl.R
|
||||
import io.element.android.features.messages.impl.timeline.aTimelineItemEvent
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import io.element.android.tests.testutils.EventsRecorder
|
||||
import io.element.android.tests.testutils.clickOn
|
||||
import io.element.android.tests.testutils.pressBackKey
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TestRule
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.annotation.Config
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class RetrySendMessageMenuTest {
|
||||
@get:Rule val rule = createAndroidComposeRule<ComponentActivity>()
|
||||
|
||||
@Test
|
||||
fun `dismiss the bottom sheet emits the expected event`() {
|
||||
val eventsRecorder = EventsRecorder<RetrySendMenuEvents>()
|
||||
rule.setRetrySendMessageMenu(
|
||||
aRetrySendMenuState(
|
||||
event = aTimelineItemEvent(),
|
||||
eventSink = eventsRecorder
|
||||
),
|
||||
)
|
||||
rule.pressBackKey()
|
||||
// Cannot test this for now.
|
||||
// eventsRecorder.assertSingle(RetrySendMenuEvents.Dismiss)
|
||||
}
|
||||
|
||||
@Config(qualifiers = "h1024dp")
|
||||
@Test
|
||||
fun `retry to send the event emits the expected event`() {
|
||||
val eventsRecorder = EventsRecorder<RetrySendMenuEvents>()
|
||||
rule.setRetrySendMessageMenu(
|
||||
aRetrySendMenuState(
|
||||
event = aTimelineItemEvent(),
|
||||
eventSink = eventsRecorder
|
||||
),
|
||||
)
|
||||
rule.clickOn(R.string.screen_room_retry_send_menu_send_again_action)
|
||||
eventsRecorder.assertSingle(RetrySendMenuEvents.Retry)
|
||||
}
|
||||
|
||||
@Config(qualifiers = "h1024dp")
|
||||
@Test
|
||||
fun `remove the event emits the expected event`() {
|
||||
val eventsRecorder = EventsRecorder<RetrySendMenuEvents>()
|
||||
rule.setRetrySendMessageMenu(
|
||||
aRetrySendMenuState(
|
||||
event = aTimelineItemEvent(),
|
||||
eventSink = eventsRecorder
|
||||
),
|
||||
)
|
||||
rule.clickOn(CommonStrings.action_remove)
|
||||
eventsRecorder.assertSingle(RetrySendMenuEvents.Remove)
|
||||
}
|
||||
}
|
||||
|
||||
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setRetrySendMessageMenu(
|
||||
state: RetrySendMenuState,
|
||||
) {
|
||||
setContent {
|
||||
RetrySendMessageMenu(
|
||||
state = state,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.features.migration.impl.migrations
|
||||
|
||||
import com.squareup.anvil.annotations.ContributesMultibinding
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.sessionstorage.api.SessionStore
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
|
||||
@ContributesMultibinding(AppScope::class)
|
||||
class AppMigration05 @Inject constructor(
|
||||
private val sessionStore: SessionStore,
|
||||
private val baseDirectory: File,
|
||||
) : AppMigration {
|
||||
override val order: Int = 5
|
||||
|
||||
override suspend fun migrate() {
|
||||
val allSessions = sessionStore.getAllSessions()
|
||||
for (session in allSessions) {
|
||||
if (session.sessionPath.isEmpty()) {
|
||||
val sessionPath = File(baseDirectory, session.userId.replace(':', '_')).absolutePath
|
||||
sessionStore.updateData(session.copy(sessionPath = sessionPath))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -68,8 +68,12 @@ class PollRepository @Inject constructor(
|
|||
|
||||
suspend fun deletePoll(
|
||||
pollStartId: EventId,
|
||||
): Result<Unit> =
|
||||
room.redactEvent(
|
||||
eventId = pollStartId,
|
||||
)
|
||||
): Result<Boolean> =
|
||||
timelineProvider
|
||||
.getActiveTimeline()
|
||||
.redactEvent(
|
||||
eventId = pollStartId,
|
||||
transactionId = null,
|
||||
reason = null,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import io.element.android.features.poll.impl.aPollTimelineItems
|
|||
import io.element.android.features.poll.impl.anOngoingPollContent
|
||||
import io.element.android.features.poll.impl.data.PollRepository
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.TransactionId
|
||||
import io.element.android.libraries.matrix.api.poll.PollKind
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.PollContent
|
||||
|
|
@ -39,6 +40,7 @@ import io.element.android.libraries.matrix.test.timeline.FakeTimeline
|
|||
import io.element.android.libraries.matrix.test.timeline.LiveTimelineProvider
|
||||
import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
import io.element.android.tests.testutils.lambda.any
|
||||
import io.element.android.tests.testutils.lambda.assert
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
import io.element.android.tests.testutils.lambda.value
|
||||
|
|
@ -455,47 +457,53 @@ import org.junit.Test
|
|||
@Test
|
||||
fun `delete confirms`() = runTest {
|
||||
val presenter = createCreatePollPresenter(mode = CreatePollMode.EditPoll(pollEventId))
|
||||
val redactEventLambda = lambdaRecorder { _: EventId?, _: TransactionId?, _: String? -> Result.success(true) }
|
||||
timeline.redactEventLambda = redactEventLambda
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
awaitDefaultItem()
|
||||
awaitPollLoaded().eventSink(CreatePollEvents.Delete(confirmed = false))
|
||||
awaitDeleteConfirmation()
|
||||
assertThat(fakeMatrixRoom.redactEventEventIdParam).isNull()
|
||||
assert(redactEventLambda).isNeverCalled()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `delete can be cancelled`() = runTest {
|
||||
val presenter = createCreatePollPresenter(mode = CreatePollMode.EditPoll(pollEventId))
|
||||
val redactEventLambda = lambdaRecorder { _: EventId?, _: TransactionId?, _: String? -> Result.success(true) }
|
||||
timeline.redactEventLambda = redactEventLambda
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
awaitDefaultItem()
|
||||
awaitPollLoaded().eventSink(CreatePollEvents.Delete(confirmed = false))
|
||||
assertThat(fakeMatrixRoom.redactEventEventIdParam).isNull()
|
||||
awaitDeleteConfirmation().eventSink(CreatePollEvents.HideConfirmation)
|
||||
awaitPollLoaded().apply {
|
||||
assertThat(showDeleteConfirmation).isFalse()
|
||||
}
|
||||
assertThat(fakeMatrixRoom.redactEventEventIdParam).isNull()
|
||||
assert(redactEventLambda).isNeverCalled()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `delete can be confirmed`() = runTest {
|
||||
val presenter = createCreatePollPresenter(mode = CreatePollMode.EditPoll(pollEventId))
|
||||
val redactEventLambda = lambdaRecorder { _: EventId?, _: TransactionId?, _: String? -> Result.success(true) }
|
||||
timeline.redactEventLambda = redactEventLambda
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
awaitDefaultItem()
|
||||
awaitPollLoaded().eventSink(CreatePollEvents.Delete(confirmed = false))
|
||||
assertThat(fakeMatrixRoom.redactEventEventIdParam).isNull()
|
||||
awaitDeleteConfirmation().eventSink(CreatePollEvents.Delete(confirmed = true))
|
||||
awaitPollLoaded().apply {
|
||||
assertThat(showDeleteConfirmation).isFalse()
|
||||
}
|
||||
assertThat(fakeMatrixRoom.redactEventEventIdParam).isEqualTo(pollEventId)
|
||||
assert(redactEventLambda)
|
||||
.isCalledOnce()
|
||||
.with(value(pollEventId), value(null), any())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -287,7 +287,8 @@ class DefaultBugReporterTest {
|
|||
oidcData = null,
|
||||
refreshToken = null,
|
||||
slidingSyncProxy = null,
|
||||
passphrase = null
|
||||
passphrase = null,
|
||||
sessionPath = "session",
|
||||
)
|
||||
@Test
|
||||
fun `test sendBugReport error`() = runTest {
|
||||
|
|
|
|||
|
|
@ -51,5 +51,6 @@ fun aSessionData(
|
|||
isTokenValid = isTokenValid,
|
||||
loginType = LoginType.UNKNOWN,
|
||||
passphrase = null,
|
||||
sessionPath = "/a/path/to/a/session",
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue