Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix issues around read receipt #1910

Merged
merged 13 commits into from
Nov 28, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package io.element.android.features.messages.impl.timeline

import io.element.android.features.messages.impl.timeline.components.receipt.aReadReceiptData
import io.element.android.features.messages.impl.timeline.model.InReplyToDetails
import io.element.android.features.messages.impl.timeline.model.ReadReceiptData
import io.element.android.features.messages.impl.timeline.model.TimelineItem
Expand Down Expand Up @@ -186,17 +187,27 @@ internal fun aTimelineItemReadReceipts(): TimelineItemReadReceipts {
)
}

fun aGroupedEvents(id: Long = 0): TimelineItem.GroupedEvents {
val event = aTimelineItemEvent(
internal fun aGroupedEvents(id: Long = 0): TimelineItem.GroupedEvents {
val event1 = aTimelineItemEvent(
isMine = true,
content = aTimelineItemStateEventContent(),
groupPosition = TimelineItemGroupPosition.None
groupPosition = TimelineItemGroupPosition.None,
readReceiptState = TimelineItemReadReceipts(
receipts = listOf(aReadReceiptData(0)).toPersistentList(),
),
)
val event2 = aTimelineItemEvent(
isMine = true,
content = aTimelineItemStateEventContent(body = "Another state event"),
groupPosition = TimelineItemGroupPosition.None,
readReceiptState = TimelineItemReadReceipts(
receipts = listOf(aReadReceiptData(1)).toPersistentList(),
),
)
val events = listOf(event1, event2)
return TimelineItem.GroupedEvents(
id = id.toString(),
events = listOf(
event,
event,
).toImmutableList()
events = events.toImmutableList(),
aggregatedReadReceipts = events.flatMap { it.readReceiptState.receipts }.toImmutableList(),
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,11 @@ package io.element.android.features.messages.impl.timeline

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.tween
import androidx.compose.animation.scaleIn
import androidx.compose.animation.scaleOut
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
Expand All @@ -42,36 +40,26 @@ import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.res.pluralStringResource
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.R
import io.element.android.features.messages.impl.timeline.components.TimelineItemEventRow
import io.element.android.features.messages.impl.timeline.components.TimelineItemStateEventRow
import io.element.android.features.messages.impl.timeline.components.TimelineItemVirtualRow
import io.element.android.features.messages.impl.timeline.components.group.GroupHeaderView
import io.element.android.features.messages.impl.timeline.components.TimelineItemRow
import io.element.android.features.messages.impl.timeline.components.virtual.TimelineItemRoomBeginningView
import io.element.android.features.messages.impl.timeline.components.virtual.TimelineLoadingMoreIndicator
import io.element.android.features.messages.impl.timeline.di.LocalTimelineItemPresenterFactories
import io.element.android.features.messages.impl.timeline.di.aFakeTimelineItemPresenterFactories
import io.element.android.features.messages.impl.timeline.model.TimelineItem
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContentProvider
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent
import io.element.android.features.messages.impl.timeline.model.event.canBeRepliedTo
import io.element.android.features.messages.impl.timeline.session.SessionState
import io.element.android.libraries.designsystem.animation.alphaAnimation
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
Expand Down Expand Up @@ -173,115 +161,6 @@ fun TimelineView(
}
}

@Composable
private fun TimelineItemRow(
timelineItem: TimelineItem,
showReadReceipts: Boolean,
isLastOutgoingMessage: Boolean,
highlightedItem: String?,
userHasPermissionToSendMessage: Boolean,
sessionState: SessionState,
onUserDataClick: (UserId) -> Unit,
onClick: (TimelineItem.Event) -> Unit,
onLongClick: (TimelineItem.Event) -> Unit,
inReplyToClick: (EventId) -> Unit,
onReactionClick: (key: String, TimelineItem.Event) -> Unit,
onReactionLongClick: (key: String, TimelineItem.Event) -> Unit,
onMoreReactionsClick: (TimelineItem.Event) -> Unit,
onReadReceiptClick: (TimelineItem.Event) -> Unit,
onTimestampClicked: (TimelineItem.Event) -> Unit,
onSwipeToReply: (TimelineItem.Event) -> Unit,
eventSink: (TimelineEvents) -> Unit,
modifier: Modifier = Modifier
) {
when (timelineItem) {
is TimelineItem.Virtual -> {
TimelineItemVirtualRow(
virtual = timelineItem,
sessionState = sessionState,
modifier = modifier,
)
}
is TimelineItem.Event -> {
if (timelineItem.content is TimelineItemStateContent) {
TimelineItemStateEventRow(
event = timelineItem,
isHighlighted = highlightedItem == timelineItem.identifier(),
onClick = { onClick(timelineItem) },
onLongClick = { onLongClick(timelineItem) },
eventSink = eventSink,
modifier = modifier,
)
} else {
TimelineItemEventRow(
event = timelineItem,
showReadReceipts = showReadReceipts,
isLastOutgoingMessage = isLastOutgoingMessage,
isHighlighted = highlightedItem == timelineItem.identifier(),
canReply = userHasPermissionToSendMessage && timelineItem.content.canBeRepliedTo(),
onClick = { onClick(timelineItem) },
onLongClick = { onLongClick(timelineItem) },
onUserDataClick = onUserDataClick,
inReplyToClick = inReplyToClick,
onReactionClick = onReactionClick,
onReactionLongClick = onReactionLongClick,
onMoreReactionsClick = onMoreReactionsClick,
onReadReceiptClick = onReadReceiptClick,
onTimestampClicked = onTimestampClicked,
onSwipeToReply = { onSwipeToReply(timelineItem) },
eventSink = eventSink,
modifier = modifier,
)
}
}
is TimelineItem.GroupedEvents -> {
val isExpanded = rememberSaveable(key = timelineItem.identifier()) { mutableStateOf(false) }

fun onExpandGroupClick() {
isExpanded.value = !isExpanded.value
}

Column(modifier = modifier.animateContentSize()) {
GroupHeaderView(
text = pluralStringResource(
id = R.plurals.room_timeline_state_changes,
count = timelineItem.events.size,
timelineItem.events.size
),
isExpanded = isExpanded.value,
isHighlighted = !isExpanded.value && timelineItem.events.any { it.identifier() == highlightedItem },
onClick = ::onExpandGroupClick,
)
if (isExpanded.value) {
Column {
timelineItem.events.forEach { subGroupEvent ->
TimelineItemRow(
timelineItem = subGroupEvent,
showReadReceipts = showReadReceipts,
isLastOutgoingMessage = isLastOutgoingMessage,
highlightedItem = highlightedItem,
sessionState = sessionState,
userHasPermissionToSendMessage = false,
onClick = onClick,
onLongClick = onLongClick,
inReplyToClick = inReplyToClick,
onUserDataClick = onUserDataClick,
onTimestampClicked = onTimestampClicked,
onReactionClick = onReactionClick,
onReactionLongClick = onReactionLongClick,
onMoreReactionsClick = onMoreReactionsClick,
onReadReceiptClick = onReadReceiptClick,
eventSink = eventSink,
onSwipeToReply = {},
)
}
}
}
}
}
}
}

@Composable
private fun BoxScope.TimelineScrollHelper(
isTimelineEmpty: Boolean,
Expand Down
Loading