Update dependency org.matrix.rustcomponents:sdk-android to v0.2.41 (#3384)
* Introduce value class UniqueId. * Allow reactions on non-sent Event, the SDK can now handle it. Also the SDK will manage local echo for reactions. * Update dependency org.matrix.rustcomponents:sdk-android to v0.2.41 * Fixes after SDK upgrade: - Use `ClientBuilderSlidingSync` to set `SlidingSyncVersionBuilder` in `RustMatrixClientFactory`. - `Room.toggleReaction(emoji: String, eventId: EventId)` is now `Room.toggleReaction(emoji: String, uniqueId: UniqueId)`, since reactions can now be applied to local echoes too in the SDK. * Rename exception case * Fix wrong error case being used in test --------- Co-authored-by: Benoit Marty <benoit@matrix.org> Co-authored-by: Benoit Marty <benoitm@matrix.org> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Jorge Martín <jorgem@element.io>
This commit is contained in:
parent
5920fd3f45
commit
9fb82a1e86
47 changed files with 237 additions and 168 deletions
|
|
@ -18,11 +18,11 @@ package io.element.android.features.messages.impl
|
|||
|
||||
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.UniqueId
|
||||
|
||||
sealed interface MessagesEvents {
|
||||
data class HandleAction(val action: TimelineItemAction, val event: TimelineItem.Event) : MessagesEvents
|
||||
data class ToggleReaction(val emoji: String, val eventId: EventId) : MessagesEvents
|
||||
data class ToggleReaction(val emoji: String, val uniqueId: UniqueId) : MessagesEvents
|
||||
data class InviteDialogDismissed(val action: InviteDialogAction) : MessagesEvents
|
||||
data object Dismiss : MessagesEvents
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
|
|||
import io.element.android.libraries.designsystem.utils.snackbar.collectSnackbarMessageAsState
|
||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.UniqueId
|
||||
import io.element.android.libraries.matrix.api.permalink.PermalinkParser
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
|
||||
|
|
@ -192,7 +192,7 @@ class MessagesPresenter @AssistedInject constructor(
|
|||
)
|
||||
}
|
||||
is MessagesEvents.ToggleReaction -> {
|
||||
localCoroutineScope.toggleReaction(event.emoji, event.eventId)
|
||||
localCoroutineScope.toggleReaction(event.emoji, event.uniqueId)
|
||||
}
|
||||
is MessagesEvents.InviteDialogDismissed -> {
|
||||
hasDismissedInviteDialog = true
|
||||
|
|
@ -313,10 +313,10 @@ class MessagesPresenter @AssistedInject constructor(
|
|||
|
||||
private fun CoroutineScope.toggleReaction(
|
||||
emoji: String,
|
||||
eventId: EventId,
|
||||
uniqueId: UniqueId,
|
||||
) = launch(dispatchers.io) {
|
||||
timelineController.invokeOnCurrentTimeline {
|
||||
toggleReaction(emoji, eventId)
|
||||
toggleReaction(emoji, uniqueId)
|
||||
.onFailure { Timber.e(it) }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -175,8 +175,7 @@ fun MessagesView(
|
|||
}
|
||||
|
||||
fun onEmojiReactionClick(emoji: String, event: TimelineItem.Event) {
|
||||
if (event.eventId == null) return
|
||||
state.eventSink(MessagesEvents.ToggleReaction(emoji, event.eventId))
|
||||
state.eventSink(MessagesEvents.ToggleReaction(emoji, event.id))
|
||||
}
|
||||
|
||||
fun onEmojiReactionLongClick(emoji: String, event: TimelineItem.Event) {
|
||||
|
|
@ -248,7 +247,6 @@ fun MessagesView(
|
|||
state = state.actionListState,
|
||||
onSelectAction = ::onActionSelected,
|
||||
onCustomReactionClick = { event ->
|
||||
if (event.eventId == null) return@ActionListView
|
||||
state.customReactionState.eventSink(CustomReactionEvents.ShowCustomReactionSheet(event))
|
||||
},
|
||||
onEmojiReactionClick = ::onEmojiReactionClick,
|
||||
|
|
@ -256,8 +254,8 @@ fun MessagesView(
|
|||
|
||||
CustomReactionBottomSheet(
|
||||
state = state.customReactionState,
|
||||
onSelectEmoji = { eventId, emoji ->
|
||||
state.eventSink(MessagesEvents.ToggleReaction(emoji.unicode, eventId))
|
||||
onSelectEmoji = { uniqueId, emoji ->
|
||||
state.eventSink(MessagesEvents.ToggleReaction(emoji.unicode, uniqueId))
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -106,7 +106,6 @@ class ActionListPresenter @Inject constructor(
|
|||
isEventPinned = pinnedEventIds.contains(timelineItem.eventId),
|
||||
)
|
||||
val displayEmojiReactions = usersEventPermissions.canSendReaction &&
|
||||
timelineItem.isRemote &&
|
||||
timelineItem.content.canReact()
|
||||
if (actions.isNotEmpty() || displayEmojiReactions) {
|
||||
target.value = ActionListState.Target.Success(
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ import io.element.android.libraries.architecture.Presenter
|
|||
import io.element.android.libraries.core.bool.orFalse
|
||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.UniqueId
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
import io.element.android.libraries.matrix.api.room.MessageEventType
|
||||
import io.element.android.libraries.matrix.api.room.isDm
|
||||
|
|
@ -95,7 +96,7 @@ class TimelinePresenter @AssistedInject constructor(
|
|||
val userHasPermissionToSendMessage by room.canSendMessageAsState(type = MessageEventType.ROOM_MESSAGE, updateKey = syncUpdateFlow.value)
|
||||
val userHasPermissionToSendReaction by room.canSendMessageAsState(type = MessageEventType.REACTION, updateKey = syncUpdateFlow.value)
|
||||
|
||||
val prevMostRecentItemId = rememberSaveable { mutableStateOf<String?>(null) }
|
||||
val prevMostRecentItemId = rememberSaveable { mutableStateOf<UniqueId?>(null) }
|
||||
|
||||
val newEventState = remember { mutableStateOf(NewEventState.None) }
|
||||
val messageShield: MutableState<MessageShield?> = remember { mutableStateOf(null) }
|
||||
|
|
@ -242,7 +243,7 @@ class TimelinePresenter @AssistedInject constructor(
|
|||
*/
|
||||
private suspend fun computeNewItemState(
|
||||
timelineItems: ImmutableList<TimelineItem>,
|
||||
prevMostRecentItemId: MutableState<String?>,
|
||||
prevMostRecentItemId: MutableState<UniqueId?>,
|
||||
newEventState: MutableState<NewEventState>
|
||||
) = withContext(dispatchers.computation) {
|
||||
// FromMe is prioritized over FromOther, so skip if we already have a FromMe
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
|||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||
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.core.UniqueId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState
|
||||
|
|
@ -122,7 +123,10 @@ internal fun aTimelineItemList(content: TimelineItemEventContent): ImmutableList
|
|||
}
|
||||
|
||||
fun aTimelineItemDaySeparator(): TimelineItem.Virtual {
|
||||
return TimelineItem.Virtual(UUID.randomUUID().toString(), aTimelineItemDaySeparatorModel("Today"))
|
||||
return TimelineItem.Virtual(
|
||||
id = UniqueId(UUID.randomUUID().toString()),
|
||||
model = aTimelineItemDaySeparatorModel("Today"),
|
||||
)
|
||||
}
|
||||
|
||||
internal fun aTimelineItemEvent(
|
||||
|
|
@ -144,7 +148,7 @@ internal fun aTimelineItemEvent(
|
|||
messageShield: MessageShield? = null,
|
||||
): TimelineItem.Event {
|
||||
return TimelineItem.Event(
|
||||
id = UUID.randomUUID().toString(),
|
||||
id = UniqueId(UUID.randomUUID().toString()),
|
||||
eventId = eventId,
|
||||
transactionId = transactionId,
|
||||
senderId = UserId("@senderId:domain"),
|
||||
|
|
@ -210,7 +214,7 @@ internal fun aTimelineItemReadReceipts(
|
|||
}
|
||||
|
||||
internal fun aGroupedEvents(
|
||||
id: Long = 0,
|
||||
id: UniqueId = UniqueId("0"),
|
||||
withReadReceipts: Boolean = false,
|
||||
): TimelineItem.GroupedEvents {
|
||||
val event1 = aTimelineItemEvent(
|
||||
|
|
@ -231,7 +235,7 @@ internal fun aGroupedEvents(
|
|||
)
|
||||
val events = listOf(event1, event2)
|
||||
return TimelineItem.GroupedEvents(
|
||||
id = id.toString(),
|
||||
id = id,
|
||||
events = events.toImmutableList(),
|
||||
aggregatedReadReceipts = events.flatMap { it.readReceiptState.receipts }.toImmutableList(),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ fun TimelineItemGroupedEventsRow(
|
|||
eventSink: (TimelineEvents.EventFromTimelineItem) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val isExpanded = rememberSaveable(key = timelineItem.identifier()) { mutableStateOf(false) }
|
||||
val isExpanded = rememberSaveable(key = timelineItem.identifier().value) { mutableStateOf(false) }
|
||||
|
||||
fun onExpandGroupClick() {
|
||||
isExpanded.value = !isExpanded.value
|
||||
|
|
|
|||
|
|
@ -25,13 +25,13 @@ import androidx.compose.ui.Modifier
|
|||
import io.element.android.emojibasebindings.Emoji
|
||||
import io.element.android.libraries.designsystem.theme.components.ModalBottomSheet
|
||||
import io.element.android.libraries.designsystem.theme.components.hide
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.UniqueId
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun CustomReactionBottomSheet(
|
||||
state: CustomReactionState,
|
||||
onSelectEmoji: (EventId, Emoji) -> Unit,
|
||||
onSelectEmoji: (UniqueId, Emoji) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val sheetState = rememberModalBottomSheetState()
|
||||
|
|
@ -43,10 +43,10 @@ fun CustomReactionBottomSheet(
|
|||
}
|
||||
|
||||
fun onEmojiSelectedDismiss(emoji: Emoji) {
|
||||
if (target?.event?.eventId == null) return
|
||||
if (target?.event == null) return
|
||||
sheetState.hide(coroutineScope) {
|
||||
state.eventSink(CustomReactionEvents.DismissCustomReactionSheet)
|
||||
onSelectEmoji(target.event.eventId, emoji)
|
||||
onSelectEmoji(target.event.id, emoji)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import androidx.annotation.VisibleForTesting
|
|||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
import io.element.android.libraries.di.RoomScope
|
||||
import io.element.android.libraries.di.SingleIn
|
||||
import io.element.android.libraries.matrix.api.core.UniqueId
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -71,7 +72,7 @@ private fun MutableList<TimelineItem>.addGroup(
|
|||
val groupId = groupIds.getOrPutGroupId(groupOfItems)
|
||||
add(
|
||||
TimelineItem.GroupedEvents(
|
||||
id = groupId,
|
||||
id = UniqueId(groupId),
|
||||
events = groupOfItems.toImmutableList(),
|
||||
aggregatedReadReceipts = groupOfItems.flatMap { it.readReceiptState.receipts }.toImmutableList()
|
||||
)
|
||||
|
|
@ -83,15 +84,15 @@ private fun MutableMap<String, String>.getOrPutGroupId(timelineItems: List<Timel
|
|||
assert(timelineItems.isNotEmpty())
|
||||
for (item in timelineItems) {
|
||||
val itemIdentifier = item.identifier()
|
||||
if (this.contains(itemIdentifier)) {
|
||||
return this[itemIdentifier]!!
|
||||
if (this.contains(itemIdentifier.value)) {
|
||||
return this[itemIdentifier.value]!!
|
||||
}
|
||||
}
|
||||
val timelineItem = timelineItems.first()
|
||||
return computeGroupIdWith(timelineItem).also { groupId ->
|
||||
this[timelineItem.identifier()] = groupId
|
||||
return computeGroupIdWith(timelineItem).value.also { groupId ->
|
||||
this[timelineItem.identifier().value] = groupId
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
internal fun computeGroupIdWith(timelineItem: TimelineItem): String = "${timelineItem.identifier()}_group"
|
||||
internal fun computeGroupIdWith(timelineItem: TimelineItem): UniqueId = UniqueId("${timelineItem.identifier()}_group")
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import io.element.android.features.messages.impl.timeline.model.virtual.Timeline
|
|||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||
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.core.UniqueId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState
|
||||
|
|
@ -36,7 +37,7 @@ import kotlinx.collections.immutable.ImmutableList
|
|||
|
||||
@Immutable
|
||||
sealed interface TimelineItem {
|
||||
fun identifier(): String = when (this) {
|
||||
fun identifier(): UniqueId = when (this) {
|
||||
is Event -> id
|
||||
is Virtual -> id
|
||||
is GroupedEvents -> id
|
||||
|
|
@ -58,13 +59,13 @@ sealed interface TimelineItem {
|
|||
|
||||
@Immutable
|
||||
data class Virtual(
|
||||
val id: String,
|
||||
val id: UniqueId,
|
||||
val model: TimelineItemVirtualModel
|
||||
) : TimelineItem
|
||||
|
||||
@Immutable
|
||||
data class Event(
|
||||
val id: String,
|
||||
val id: UniqueId,
|
||||
// Note: eventId can be null when the event is a local echo
|
||||
val eventId: EventId? = null,
|
||||
val transactionId: TransactionId? = null,
|
||||
|
|
@ -101,7 +102,7 @@ sealed interface TimelineItem {
|
|||
|
||||
@Immutable
|
||||
data class GroupedEvents(
|
||||
val id: String,
|
||||
val id: UniqueId,
|
||||
val events: ImmutableList<Event>,
|
||||
val aggregatedReadReceipts: ImmutableList<ReadReceiptData>,
|
||||
) : TimelineItem
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue