Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,29 @@

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

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.animation.fadeIn
import androidx.compose.animation.slideInVertically
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.movableContentOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawWithCache
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.isTraversalGroup
Expand All @@ -45,6 +57,7 @@
import io.element.android.libraries.designsystem.text.toPx
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.timeline.Timeline
import io.element.android.libraries.matrix.api.timeline.item.event.TimelineItemEventOrigin
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.libraries.ui.utils.a11y.isTalkbackActive
Expand Down Expand Up @@ -102,8 +115,98 @@
} else {
Modifier
}
Box(modifier = modifier.then(backgroundModifier)) {
when (timelineItem) {
val content = @Composable {
Box(modifier = modifier.then(backgroundModifier)) {
RowContent(
timelineItem = timelineItem,
timelineMode = timelineMode,
timelineRoomInfo = timelineRoomInfo,
isLastOutgoingMessage = isLastOutgoingMessage,
timelineProtectionState = timelineProtectionState,
focusedEventId = focusedEventId,
displayThreadSummaries = displayThreadSummaries,
onUserDataClick = onUserDataClick,
onLinkClick = onLinkClick,
onLinkLongClick = onLinkLongClick,
onContentClick = onContentClick,
onGalleryItemClick = onGalleryItemClick,
onLongClick = onLongClick,
inReplyToClick = inReplyToClick,
onReactionClick = onReactionClick,
onReactionLongClick = onReactionLongClick,
onMoreReactionsClick = onMoreReactionsClick,
onReadReceiptClick = onReadReceiptClick,
onJoinCallClick = onJoinCallClick,
onSwipeToReply = onSwipeToReply,
eventSink = eventSink,
eventContentView = eventContentView,
)
}
}

WithEnterAnimation(
timelineItem = timelineItem,
content = content,
)
}

@Composable
private fun WithEnterAnimation(
timelineItem: TimelineItem,
content: @Composable () -> Unit,
) {
val movableContent = remember { movableContentOf(content) }
// Only events with SYNC or LOCAL origin should have an enter animation, to avoid animating events that are already in the timeline
// when the user opens the room.
val needsEnterAnimation = remember {
timelineItem is TimelineItem.Event &&
(timelineItem.origin == TimelineItemEventOrigin.SYNC || timelineItem.origin == TimelineItemEventOrigin.LOCAL)
}
if (needsEnterAnimation && !LocalInspectionMode.current) {
// Animate the item in the first time it is displayed, e.g. when a new message arrives.
// We also remember the animation state so it doesn't re-animate when the timeline recomposes or we come back to the screen it's in.
var isDisplayed by rememberSaveable { mutableStateOf(false) }
val visibleState = remember { MutableTransitionState(initialState = isDisplayed).apply { targetState = true } }
LaunchedEffect(visibleState.currentState) {
isDisplayed = visibleState.currentState
}
AnimatedVisibility(
visibleState = visibleState,
enter = fadeIn() + slideInVertically { fullHeight -> fullHeight / 2 },
) {
movableContent()
}
} else {
movableContent()
}
}

@Composable
private fun RowContent(

Check warning on line 185 in features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

This function has 22 parameters, which is greater than the 7 authorized.

See more on https://sonarcloud.io/project/issues?id=element-x-android&issues=AaCLdcbkkcuVuZeQAeuj&open=AaCLdcbkkcuVuZeQAeuj&pullRequest=7686

Check failure on line 185 in features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this method to reduce its Cognitive Complexity from 16 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=element-x-android&issues=AaCLdcbkkcuVuZeQAeuk&open=AaCLdcbkkcuVuZeQAeuk&pullRequest=7686
timelineItem: TimelineItem,
timelineMode: Timeline.Mode,
timelineRoomInfo: TimelineRoomInfo,
isLastOutgoingMessage: Boolean,
timelineProtectionState: TimelineProtectionState,
focusedEventId: EventId?,
displayThreadSummaries: Boolean,
onUserDataClick: (MatrixUser) -> Unit,
onLinkClick: (Link) -> Unit,
onLinkLongClick: (Link) -> Unit,
onContentClick: (TimelineItem.Event) -> Unit,
onGalleryItemClick: (TimelineItem.Event, Int) -> 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,
onJoinCallClick: (isAudioCall: Boolean) -> Unit,
onSwipeToReply: (TimelineItem.Event) -> Unit,
eventSink: (TimelineEvent.TimelineItemEvent) -> Unit,
eventContentView: @Composable (TimelineItem.Event, Modifier, (ContentAvoidingLayoutData) -> Unit) -> Unit,
) {
when (timelineItem) {
is TimelineItem.Virtual -> {
TimelineItemVirtualRow(
virtual = timelineItem,
Expand Down Expand Up @@ -226,7 +329,6 @@
eventSink = eventSink,
)
}
}
}
}

Expand Down
Loading