Toggle reactions from the timeline (#707)
This commit is contained in:
parent
fe3deeec2f
commit
9e8257cc4c
13 changed files with 84 additions and 33 deletions
|
|
@ -22,6 +22,6 @@ import io.element.android.libraries.matrix.api.core.EventId
|
||||||
|
|
||||||
sealed interface MessagesEvents {
|
sealed interface MessagesEvents {
|
||||||
data class HandleAction(val action: TimelineItemAction, val event: TimelineItem.Event) : MessagesEvents
|
data class HandleAction(val action: TimelineItemAction, val event: TimelineItem.Event) : MessagesEvents
|
||||||
data class SendReaction(val emoji: String, val eventId: EventId) : MessagesEvents
|
data class ToggleReaction(val emoji: String, val eventId: EventId) : MessagesEvents
|
||||||
object Dismiss : MessagesEvents
|
object Dismiss : MessagesEvents
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -130,8 +130,8 @@ class MessagesPresenter @AssistedInject constructor(
|
||||||
is MessagesEvents.HandleAction -> {
|
is MessagesEvents.HandleAction -> {
|
||||||
localCoroutineScope.handleTimelineAction(event.action, event.event, composerState)
|
localCoroutineScope.handleTimelineAction(event.action, event.event, composerState)
|
||||||
}
|
}
|
||||||
is MessagesEvents.SendReaction -> {
|
is MessagesEvents.ToggleReaction -> {
|
||||||
localCoroutineScope.sendReaction(event.emoji, event.eventId)
|
localCoroutineScope.toggleReaction(event.emoji, event.eventId)
|
||||||
}
|
}
|
||||||
is MessagesEvents.Dismiss -> actionListState.eventSink(ActionListEvents.Clear)
|
is MessagesEvents.Dismiss -> actionListState.eventSink(ActionListEvents.Clear)
|
||||||
}
|
}
|
||||||
|
|
@ -168,11 +168,11 @@ class MessagesPresenter @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CoroutineScope.sendReaction(
|
private fun CoroutineScope.toggleReaction(
|
||||||
emoji: String,
|
emoji: String,
|
||||||
eventId: EventId,
|
eventId: EventId,
|
||||||
) = launch(dispatchers.io) {
|
) = launch(dispatchers.io) {
|
||||||
room.sendReaction(emoji, eventId)
|
room.toggleReaction(emoji, eventId)
|
||||||
.onFailure { Timber.e(it) }
|
.onFailure { Timber.e(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,7 @@ fun MessagesView(
|
||||||
|
|
||||||
fun onEmojiReactionClicked(emoji: String, event: TimelineItem.Event) {
|
fun onEmojiReactionClicked(emoji: String, event: TimelineItem.Event) {
|
||||||
if (event.eventId == null) return
|
if (event.eventId == null) return
|
||||||
state.eventSink(MessagesEvents.SendReaction(emoji, event.eventId))
|
state.eventSink(MessagesEvents.ToggleReaction(emoji, event.eventId))
|
||||||
}
|
}
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
|
|
@ -150,7 +150,8 @@ fun MessagesView(
|
||||||
if (event.sendState is EventSendState.SendingFailed) {
|
if (event.sendState is EventSendState.SendingFailed) {
|
||||||
state.retrySendMenuState.eventSink(RetrySendMenuEvents.EventSelected(event))
|
state.retrySendMenuState.eventSink(RetrySendMenuEvents.EventSelected(event))
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
onReactionClicked = ::onEmojiReactionClicked
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
snackbarHost = {
|
snackbarHost = {
|
||||||
|
|
@ -174,7 +175,7 @@ fun MessagesView(
|
||||||
state = state.customReactionState,
|
state = state.customReactionState,
|
||||||
onEmojiSelected = { emoji ->
|
onEmojiSelected = { emoji ->
|
||||||
state.customReactionState.selectedEventId?.let { eventId ->
|
state.customReactionState.selectedEventId?.let { eventId ->
|
||||||
state.eventSink(MessagesEvents.SendReaction(emoji.unicode, eventId))
|
state.eventSink(MessagesEvents.ToggleReaction(emoji.unicode, eventId))
|
||||||
state.customReactionState.eventSink(CustomReactionEvents.UpdateSelectedEvent(null))
|
state.customReactionState.eventSink(CustomReactionEvents.UpdateSelectedEvent(null))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -204,6 +205,7 @@ fun MessagesViewContent(
|
||||||
state: MessagesState,
|
state: MessagesState,
|
||||||
onMessageClicked: (TimelineItem.Event) -> Unit,
|
onMessageClicked: (TimelineItem.Event) -> Unit,
|
||||||
onUserDataClicked: (UserId) -> Unit,
|
onUserDataClicked: (UserId) -> Unit,
|
||||||
|
onReactionClicked: (key: String, TimelineItem.Event) -> Unit,
|
||||||
onMessageLongClicked: (TimelineItem.Event) -> Unit,
|
onMessageLongClicked: (TimelineItem.Event) -> Unit,
|
||||||
onTimestampClicked: (TimelineItem.Event) -> Unit,
|
onTimestampClicked: (TimelineItem.Event) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
|
@ -223,6 +225,7 @@ fun MessagesViewContent(
|
||||||
onMessageLongClicked = onMessageLongClicked,
|
onMessageLongClicked = onMessageLongClicked,
|
||||||
onUserDataClicked = onUserDataClicked,
|
onUserDataClicked = onUserDataClicked,
|
||||||
onTimestampClicked = onTimestampClicked,
|
onTimestampClicked = onTimestampClicked,
|
||||||
|
onReactionClicked = onReactionClicked,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (state.userHasPermissionToSendMessage) {
|
if (state.userHasPermissionToSendMessage) {
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,7 @@ fun TimelineView(
|
||||||
onMessageClicked: (TimelineItem.Event) -> Unit,
|
onMessageClicked: (TimelineItem.Event) -> Unit,
|
||||||
onMessageLongClicked: (TimelineItem.Event) -> Unit,
|
onMessageLongClicked: (TimelineItem.Event) -> Unit,
|
||||||
onTimestampClicked: (TimelineItem.Event) -> Unit,
|
onTimestampClicked: (TimelineItem.Event) -> Unit,
|
||||||
|
onReactionClicked: (emoji: String, TimelineItem.Event) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
fun onReachedLoadMore() {
|
fun onReachedLoadMore() {
|
||||||
|
|
@ -103,6 +104,7 @@ fun TimelineView(
|
||||||
onLongClick = onMessageLongClicked,
|
onLongClick = onMessageLongClicked,
|
||||||
onUserDataClick = onUserDataClicked,
|
onUserDataClick = onUserDataClicked,
|
||||||
inReplyToClick = ::inReplyToClicked,
|
inReplyToClick = ::inReplyToClicked,
|
||||||
|
onReactionClick = onReactionClicked,
|
||||||
onTimestampClicked = onTimestampClicked,
|
onTimestampClicked = onTimestampClicked,
|
||||||
)
|
)
|
||||||
if (index == state.timelineItems.lastIndex) {
|
if (index == state.timelineItems.lastIndex) {
|
||||||
|
|
@ -127,6 +129,7 @@ fun TimelineItemRow(
|
||||||
onClick: (TimelineItem.Event) -> Unit,
|
onClick: (TimelineItem.Event) -> Unit,
|
||||||
onLongClick: (TimelineItem.Event) -> Unit,
|
onLongClick: (TimelineItem.Event) -> Unit,
|
||||||
inReplyToClick: (EventId) -> Unit,
|
inReplyToClick: (EventId) -> Unit,
|
||||||
|
onReactionClick: (key: String, TimelineItem.Event) -> Unit,
|
||||||
onTimestampClicked: (TimelineItem.Event) -> Unit,
|
onTimestampClicked: (TimelineItem.Event) -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
|
|
@ -162,6 +165,7 @@ fun TimelineItemRow(
|
||||||
onLongClick = ::onLongClick,
|
onLongClick = ::onLongClick,
|
||||||
onUserDataClick = onUserDataClick,
|
onUserDataClick = onUserDataClick,
|
||||||
inReplyToClick = inReplyToClick,
|
inReplyToClick = inReplyToClick,
|
||||||
|
onReactionClick = onReactionClick,
|
||||||
onTimestampClicked = onTimestampClicked,
|
onTimestampClicked = onTimestampClicked,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
|
|
@ -196,6 +200,7 @@ fun TimelineItemRow(
|
||||||
inReplyToClick = inReplyToClick,
|
inReplyToClick = inReplyToClick,
|
||||||
onUserDataClick = onUserDataClick,
|
onUserDataClick = onUserDataClick,
|
||||||
onTimestampClicked = onTimestampClicked,
|
onTimestampClicked = onTimestampClicked,
|
||||||
|
onReactionClick = onReactionClick,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -286,5 +291,6 @@ private fun ContentToPreview(content: TimelineItemEventContent) {
|
||||||
onTimestampClicked = {},
|
onTimestampClicked = {},
|
||||||
onUserDataClicked = {},
|
onUserDataClicked = {},
|
||||||
onMessageLongClicked = {},
|
onMessageLongClicked = {},
|
||||||
|
onReactionClicked = { _, _ -> },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package io.element.android.features.messages.impl.timeline.components
|
||||||
|
|
||||||
import androidx.compose.foundation.BorderStroke
|
import androidx.compose.foundation.BorderStroke
|
||||||
import androidx.compose.foundation.border
|
import androidx.compose.foundation.border
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
|
@ -43,10 +44,10 @@ import io.element.android.libraries.designsystem.theme.components.Text
|
||||||
import io.element.android.libraries.theme.ElementTheme
|
import io.element.android.libraries.theme.ElementTheme
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MessagesReactionButton(reaction: AggregatedReaction, modifier: Modifier = Modifier) {
|
fun MessagesReactionButton(reaction: AggregatedReaction, modifier: Modifier = Modifier, onClick: () -> Unit) {
|
||||||
// First Surface is to render a border with the same background color as the background
|
// First Surface is to render a border with the same background color as the background
|
||||||
Surface(
|
Surface(
|
||||||
modifier = modifier,
|
modifier = modifier.clickable(onClick = onClick::invoke),
|
||||||
// TODO Should use compound.bgSubtlePrimary
|
// TODO Should use compound.bgSubtlePrimary
|
||||||
color = ElementTheme.legacyColors.gray300,
|
color = ElementTheme.legacyColors.gray300,
|
||||||
border = BorderStroke(2.dp, MaterialTheme.colorScheme.background),
|
border = BorderStroke(2.dp, MaterialTheme.colorScheme.background),
|
||||||
|
|
@ -90,5 +91,5 @@ internal fun MessagesReactionButtonDarkPreview(@PreviewParameter(AggregatedReact
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ContentToPreview(reaction: AggregatedReaction) {
|
private fun ContentToPreview(reaction: AggregatedReaction) {
|
||||||
MessagesReactionButton(reaction)
|
MessagesReactionButton(reaction, onClick = { })
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,7 @@ fun TimelineItemEventRow(
|
||||||
onUserDataClick: (UserId) -> Unit,
|
onUserDataClick: (UserId) -> Unit,
|
||||||
inReplyToClick: (EventId) -> Unit,
|
inReplyToClick: (EventId) -> Unit,
|
||||||
onTimestampClicked: (TimelineItem.Event) -> Unit,
|
onTimestampClicked: (TimelineItem.Event) -> Unit,
|
||||||
|
onReactionClick: (emoji: String, eventId: TimelineItem.Event) -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val interactionSource = remember { MutableInteractionSource() }
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
|
|
@ -95,6 +96,9 @@ fun TimelineItemEventRow(
|
||||||
onUserDataClick(event.senderId)
|
onUserDataClick(event.senderId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onReactionClicked(emoji: String) =
|
||||||
|
onReactionClick(emoji, event)
|
||||||
|
|
||||||
fun inReplyToClicked() {
|
fun inReplyToClicked() {
|
||||||
val inReplyToEventId = (event.inReplyTo as? InReplyTo.Ready)?.eventId ?: return
|
val inReplyToEventId = (event.inReplyTo as? InReplyTo.Ready)?.eventId ?: return
|
||||||
inReplyToClick(inReplyToEventId)
|
inReplyToClick(inReplyToEventId)
|
||||||
|
|
@ -157,6 +161,7 @@ fun TimelineItemEventRow(
|
||||||
if (event.reactionsState.reactions.isNotEmpty()) {
|
if (event.reactionsState.reactions.isNotEmpty()) {
|
||||||
TimelineItemReactionsView(
|
TimelineItemReactionsView(
|
||||||
reactionsState = event.reactionsState,
|
reactionsState = event.reactionsState,
|
||||||
|
onReactionClicked = ::onReactionClicked,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(if (event.isMine) Alignment.BottomEnd else Alignment.BottomStart)
|
.align(if (event.isMine) Alignment.BottomEnd else Alignment.BottomStart)
|
||||||
.padding(start = if (event.isMine) 16.dp else 36.dp, end = 16.dp)
|
.padding(start = if (event.isMine) 16.dp else 36.dp, end = 16.dp)
|
||||||
|
|
@ -422,6 +427,7 @@ private fun ContentToPreview() {
|
||||||
onLongClick = {},
|
onLongClick = {},
|
||||||
onUserDataClick = {},
|
onUserDataClick = {},
|
||||||
inReplyToClick = {},
|
inReplyToClick = {},
|
||||||
|
onReactionClick = { _, _ -> },
|
||||||
onTimestampClicked = {},
|
onTimestampClicked = {},
|
||||||
)
|
)
|
||||||
TimelineItemEventRow(
|
TimelineItemEventRow(
|
||||||
|
|
@ -436,6 +442,7 @@ private fun ContentToPreview() {
|
||||||
onLongClick = {},
|
onLongClick = {},
|
||||||
onUserDataClick = {},
|
onUserDataClick = {},
|
||||||
inReplyToClick = {},
|
inReplyToClick = {},
|
||||||
|
onReactionClick = { _, _ -> },
|
||||||
onTimestampClicked = {},
|
onTimestampClicked = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -476,6 +483,7 @@ private fun ContentTimestampToPreview(event: TimelineItem.Event) {
|
||||||
onLongClick = {},
|
onLongClick = {},
|
||||||
onUserDataClick = {},
|
onUserDataClick = {},
|
||||||
inReplyToClick = {},
|
inReplyToClick = {},
|
||||||
|
onReactionClick = { _, _ -> },
|
||||||
onTimestampClicked = {},
|
onTimestampClicked = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreviewLight
|
||||||
fun TimelineItemReactionsView(
|
fun TimelineItemReactionsView(
|
||||||
reactionsState: TimelineItemReactions,
|
reactionsState: TimelineItemReactions,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
onReactionClicked: (emoji: String) -> Unit
|
||||||
) {
|
) {
|
||||||
FlowRow(
|
FlowRow(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
|
|
@ -37,7 +38,10 @@ fun TimelineItemReactionsView(
|
||||||
crossAxisSpacing = 8.dp,
|
crossAxisSpacing = 8.dp,
|
||||||
) {
|
) {
|
||||||
reactionsState.reactions.forEach { reaction ->
|
reactionsState.reactions.forEach { reaction ->
|
||||||
MessagesReactionButton(reaction = reaction)
|
MessagesReactionButton(
|
||||||
|
reaction = reaction,
|
||||||
|
onClick = { onReactionClicked(reaction.key) }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -55,6 +59,7 @@ internal fun TimelineItemReactionsViewDarkPreview() =
|
||||||
@Composable
|
@Composable
|
||||||
private fun ContentToPreview() {
|
private fun ContentToPreview() {
|
||||||
TimelineItemReactionsView(
|
TimelineItemReactionsView(
|
||||||
reactionsState = aTimelineItemReactions()
|
reactionsState = aTimelineItemReactions(),
|
||||||
|
onReactionClicked = { }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ class MessagesPresenterTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - handle sending a reaction`() = runTest {
|
fun `present - handle toggling a reaction`() = runTest {
|
||||||
val coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true)
|
val coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true)
|
||||||
val room = FakeMatrixRoom()
|
val room = FakeMatrixRoom()
|
||||||
val presenter = createMessagePresenter(matrixRoom = room, coroutineDispatchers = coroutineDispatchers)
|
val presenter = createMessagePresenter(matrixRoom = room, coroutineDispatchers = coroutineDispatchers)
|
||||||
|
|
@ -89,17 +89,35 @@ class MessagesPresenterTest {
|
||||||
}.test {
|
}.test {
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
initialState.eventSink.invoke(MessagesEvents.SendReaction("👍", AN_EVENT_ID))
|
initialState.eventSink.invoke(MessagesEvents.ToggleReaction("👍", AN_EVENT_ID))
|
||||||
assertThat(room.sendReactionCount).isEqualTo(1)
|
assertThat(room.myReactions.count()).isEqualTo(1)
|
||||||
|
|
||||||
// No crashes when sending a reaction failed
|
// No crashes when sending a reaction failed
|
||||||
room.givenSendReactionResult(Result.failure(IllegalStateException("Failed to send reaction")))
|
room.givenToggleReactionResult(Result.failure(IllegalStateException("Failed to send reaction")))
|
||||||
initialState.eventSink.invoke(MessagesEvents.SendReaction("👍", AN_EVENT_ID))
|
initialState.eventSink.invoke(MessagesEvents.ToggleReaction("👍", AN_EVENT_ID))
|
||||||
assertThat(room.sendReactionCount).isEqualTo(2)
|
assertThat(room.myReactions.count()).isEqualTo(1)
|
||||||
assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None)
|
assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `present - handle toggling a reaction twice`() = runTest {
|
||||||
|
val coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true)
|
||||||
|
val room = FakeMatrixRoom()
|
||||||
|
val presenter = createMessagePresenter(matrixRoom = room, coroutineDispatchers = coroutineDispatchers)
|
||||||
|
moleculeFlow(RecompositionClock.Immediate) {
|
||||||
|
presenter.present()
|
||||||
|
}.test {
|
||||||
|
skipItems(1)
|
||||||
|
val initialState = awaitItem()
|
||||||
|
initialState.eventSink.invoke(MessagesEvents.ToggleReaction("👍", AN_EVENT_ID))
|
||||||
|
assertThat(room.myReactions.count()).isEqualTo(1)
|
||||||
|
|
||||||
|
initialState.eventSink.invoke(MessagesEvents.ToggleReaction("👍", AN_EVENT_ID))
|
||||||
|
assertThat(room.myReactions.count()).isEqualTo(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - handle action forward`() = runTest {
|
fun `present - handle action forward`() = runTest {
|
||||||
val navigator = FakeMessagesNavigator()
|
val navigator = FakeMessagesNavigator()
|
||||||
|
|
|
||||||
|
|
@ -142,7 +142,7 @@ jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" }
|
||||||
appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" }
|
appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" }
|
||||||
molecule-runtime = { module = "app.cash.molecule:molecule-runtime", version.ref = "molecule" }
|
molecule-runtime = { module = "app.cash.molecule:molecule-runtime", version.ref = "molecule" }
|
||||||
timber = "com.jakewharton.timber:timber:5.0.1"
|
timber = "com.jakewharton.timber:timber:5.0.1"
|
||||||
matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.25"
|
matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.26"
|
||||||
sqldelight-driver-android = { module = "com.squareup.sqldelight:android-driver", version.ref = "sqldelight" }
|
sqldelight-driver-android = { module = "com.squareup.sqldelight:android-driver", version.ref = "sqldelight" }
|
||||||
sqldelight-driver-jvm = { module = "com.squareup.sqldelight:sqlite-driver", version.ref = "sqldelight" }
|
sqldelight-driver-jvm = { module = "com.squareup.sqldelight:sqlite-driver", version.ref = "sqldelight" }
|
||||||
sqldelight-coroutines = { module = "com.squareup.sqldelight:coroutines-extensions", version.ref = "sqldelight" }
|
sqldelight-coroutines = { module = "com.squareup.sqldelight:coroutines-extensions", version.ref = "sqldelight" }
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ import io.element.android.libraries.matrix.api.media.FileInfo
|
||||||
import io.element.android.libraries.matrix.api.media.ImageInfo
|
import io.element.android.libraries.matrix.api.media.ImageInfo
|
||||||
import io.element.android.libraries.matrix.api.media.VideoInfo
|
import io.element.android.libraries.matrix.api.media.VideoInfo
|
||||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimeline
|
import io.element.android.libraries.matrix.api.timeline.MatrixTimeline
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import java.io.Closeable
|
import java.io.Closeable
|
||||||
|
|
@ -83,7 +82,7 @@ interface MatrixRoom : Closeable {
|
||||||
|
|
||||||
suspend fun sendFile(file: File, fileInfo: FileInfo, progressCallback: ProgressCallback?): Result<Unit>
|
suspend fun sendFile(file: File, fileInfo: FileInfo, progressCallback: ProgressCallback?): Result<Unit>
|
||||||
|
|
||||||
suspend fun sendReaction(emoji: String, eventId: EventId): Result<Unit>
|
suspend fun toggleReaction(emoji: String, eventId: EventId): Result<Unit>
|
||||||
|
|
||||||
suspend fun forwardEvent(eventId: EventId, rooms: List<RoomId>): Result<Unit>
|
suspend fun forwardEvent(eventId: EventId, rooms: List<RoomId>): Result<Unit>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,8 @@ class RustMatrixAuthenticationService @Inject constructor(
|
||||||
passphrase = null,
|
passphrase = null,
|
||||||
// TODO Oidc
|
// TODO Oidc
|
||||||
// oidcClientMetadata = oidcClientMetadata,
|
// oidcClientMetadata = oidcClientMetadata,
|
||||||
customSlidingSyncProxy = null
|
customSlidingSyncProxy = null,
|
||||||
|
userAgent = null, // TODO
|
||||||
)
|
)
|
||||||
private var currentHomeserver = MutableStateFlow<MatrixHomeServerDetails?>(null)
|
private var currentHomeserver = MutableStateFlow<MatrixHomeServerDetails?>(null)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -273,9 +273,9 @@ class RustMatrixRoom(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun sendReaction(emoji: String, eventId: EventId): Result<Unit> = withContext(coroutineDispatchers.io) {
|
override suspend fun toggleReaction(emoji: String, eventId: EventId): Result<Unit> = withContext(coroutineDispatchers.io) {
|
||||||
runCatching {
|
runCatching {
|
||||||
innerRoom.sendReaction(key = emoji, eventId = eventId.value)
|
innerRoom.toggleReaction(key = emoji, eventId = eventId.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ class FakeMatrixRoom(
|
||||||
private var setTopicResult = Result.success(Unit)
|
private var setTopicResult = Result.success(Unit)
|
||||||
private var updateAvatarResult = Result.success(Unit)
|
private var updateAvatarResult = Result.success(Unit)
|
||||||
private var removeAvatarResult = Result.success(Unit)
|
private var removeAvatarResult = Result.success(Unit)
|
||||||
private var sendReactionResult = Result.success(Unit)
|
private var toggleReactionResult = Result.success(Unit)
|
||||||
private var retrySendMessageResult = Result.success(Unit)
|
private var retrySendMessageResult = Result.success(Unit)
|
||||||
private var cancelSendResult = Result.success(Unit)
|
private var cancelSendResult = Result.success(Unit)
|
||||||
private var forwardEventResult = Result.success(Unit)
|
private var forwardEventResult = Result.success(Unit)
|
||||||
|
|
@ -82,8 +82,8 @@ class FakeMatrixRoom(
|
||||||
var sendMediaCount = 0
|
var sendMediaCount = 0
|
||||||
private set
|
private set
|
||||||
|
|
||||||
var sendReactionCount = 0
|
private val _myReactions = mutableSetOf<String>()
|
||||||
private set
|
val myReactions: Set<String> = _myReactions
|
||||||
|
|
||||||
var retrySendMessageCount: Int = 0
|
var retrySendMessageCount: Int = 0
|
||||||
private set
|
private set
|
||||||
|
|
@ -146,9 +146,19 @@ class FakeMatrixRoom(
|
||||||
Result.success(Unit)
|
Result.success(Unit)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun sendReaction(emoji: String, eventId: EventId): Result<Unit> {
|
override suspend fun toggleReaction(emoji: String, eventId: EventId): Result<Unit> {
|
||||||
sendReactionCount++
|
if (toggleReactionResult.isFailure) {
|
||||||
return sendReactionResult
|
// Don't do the toggle if we failed
|
||||||
|
return toggleReactionResult
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_myReactions.contains(emoji)) {
|
||||||
|
_myReactions.remove(emoji)
|
||||||
|
} else {
|
||||||
|
_myReactions.add(emoji)
|
||||||
|
}
|
||||||
|
|
||||||
|
return toggleReactionResult
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun retrySendMessage(transactionId: String): Result<Unit> {
|
override suspend fun retrySendMessage(transactionId: String): Result<Unit> {
|
||||||
|
|
@ -348,8 +358,8 @@ class FakeMatrixRoom(
|
||||||
setTopicResult = result
|
setTopicResult = result
|
||||||
}
|
}
|
||||||
|
|
||||||
fun givenSendReactionResult(result: Result<Unit>) {
|
fun givenToggleReactionResult(result: Result<Unit>) {
|
||||||
sendReactionResult = result
|
toggleReactionResult = result
|
||||||
}
|
}
|
||||||
|
|
||||||
fun givenRetrySendMessageResult(result: Result<Unit>) {
|
fun givenRetrySendMessageResult(result: Result<Unit>) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue