Browse Source

Merge pull request #826 from vector-im/feature/bma/swipeAction

Improve swipe to reply rendering
pull/835/head
Benoit Marty 1 year ago committed by GitHub
parent
commit
0fbf799d15
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      .idea/dictionaries/shared.xml
  2. 1
      build.gradle.kts
  3. 111
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt
  4. 75
      libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/swipe/SwipeableActionsState.kt
  5. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowDarkPreview_0_null,NEXUS_5,1.0,en].png
  6. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowLightPreview_0_null,NEXUS_5,1.0,en].png
  7. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowTimestampDarkPreview_0_null_0,NEXUS_5,1.0,en].png
  8. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowTimestampDarkPreview_0_null_1,NEXUS_5,1.0,en].png
  9. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowTimestampDarkPreview_0_null_2,NEXUS_5,1.0,en].png
  10. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowTimestampDarkPreview_0_null_3,NEXUS_5,1.0,en].png
  11. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowTimestampLightPreview_0_null_0,NEXUS_5,1.0,en].png
  12. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowTimestampLightPreview_0_null_1,NEXUS_5,1.0,en].png
  13. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowTimestampLightPreview_0_null_2,NEXUS_5,1.0,en].png
  14. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowTimestampLightPreview_0_null_3,NEXUS_5,1.0,en].png
  15. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithManyReactionsDarkPreview_0_null,NEXUS_5,1.0,en].png
  16. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithManyReactionsLightPreview_0_null,NEXUS_5,1.0,en].png
  17. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithReplyDarkPreview_0_null,NEXUS_5,1.0,en].png
  18. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithReplyLightPreview_0_null,NEXUS_5,1.0,en].png

1
.idea/dictionaries/shared.xml

@ -9,6 +9,7 @@
<w>placeables</w> <w>placeables</w>
<w>showkase</w> <w>showkase</w>
<w>snackbar</w> <w>snackbar</w>
<w>swipeable</w>
<w>textfields</w> <w>textfields</w>
</words> </words>
</dictionary> </dictionary>

1
build.gradle.kts

@ -247,6 +247,7 @@ koverMerged {
excludes += "io.element.android.features.messages.impl.media.local.LocalMediaViewState" excludes += "io.element.android.features.messages.impl.media.local.LocalMediaViewState"
excludes += "io.element.android.features.location.impl.map.MapState" excludes += "io.element.android.features.location.impl.map.MapState"
excludes += "io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState*" excludes += "io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState*"
excludes += "io.element.android.libraries.designsystem.swipe.SwipeableActionsState*"
} }
bound { bound {
minValue = 90 minValue = 90

111
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt

@ -14,13 +14,13 @@
* limitations under the License. * limitations under the License.
*/ */
@file:OptIn(ExperimentalMaterial3Api::class)
package io.element.android.features.messages.impl.timeline.components package io.element.android.features.messages.impl.timeline.components
import androidx.compose.foundation.Canvas import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.draggable
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
@ -28,6 +28,7 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.absoluteOffset
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@ -35,26 +36,25 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.DismissDirection
import androidx.compose.material3.DismissState
import androidx.compose.material3.DismissValue
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SwipeToDismiss
import androidx.compose.material3.rememberDismissState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.clipToBounds import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.platform.LocalViewConfiguration
import androidx.compose.ui.platform.ViewConfiguration
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.compose.ui.zIndex import androidx.compose.ui.zIndex
@ -66,6 +66,7 @@ import io.element.android.features.messages.impl.timeline.aTimelineItemReactions
import io.element.android.features.messages.impl.timeline.components.event.TimelineItemEventContentView import io.element.android.features.messages.impl.timeline.components.event.TimelineItemEventContentView
import io.element.android.features.messages.impl.timeline.components.event.toExtraPadding import io.element.android.features.messages.impl.timeline.components.event.toExtraPadding
import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.TimelineItem
import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition
import io.element.android.features.messages.impl.timeline.model.bubble.BubbleState import io.element.android.features.messages.impl.timeline.model.bubble.BubbleState
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent
@ -78,6 +79,9 @@ import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.swipe.SwipeableActionsState
import io.element.android.libraries.designsystem.swipe.rememberSwipeableActionsState
import io.element.android.libraries.designsystem.text.toPx
import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.core.UserId
@ -93,7 +97,10 @@ import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailInfo
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailType import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailType
import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.strings.CommonStrings
import kotlinx.coroutines.launch
import org.jsoup.Jsoup import org.jsoup.Jsoup
import kotlin.math.abs
import kotlin.math.roundToInt
@Composable @Composable
fun TimelineItemEventRow( fun TimelineItemEventRow(
@ -110,6 +117,7 @@ fun TimelineItemEventRow(
onSwipeToReply: () -> Unit, onSwipeToReply: () -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
val coroutineScope = rememberCoroutineScope()
val interactionSource = remember { MutableInteractionSource() } val interactionSource = remember { MutableInteractionSource() }
fun onUserDataClicked() { fun onUserDataClicked() {
@ -121,24 +129,38 @@ fun TimelineItemEventRow(
inReplyToClick(inReplyToEventId) inReplyToClick(inReplyToEventId)
} }
Column(modifier = modifier.fillMaxWidth()) {
if (event.groupPosition.isNew()) {
Spacer(modifier = Modifier.height(16.dp))
} else {
Spacer(modifier = Modifier.height(2.dp))
}
if (canReply) { if (canReply) {
val dismissState = rememberDismissState( val state: SwipeableActionsState = rememberSwipeableActionsState()
confirmValueChange = { val offset = state.offset.value
if (it == DismissValue.DismissedToEnd) { val swipeThresholdPx = 40.dp.toPx()
val thresholdCrossed = abs(offset) > swipeThresholdPx
SwipeSensitivity(3f) {
Box(Modifier.fillMaxWidth()) {
Row(modifier = Modifier.matchParentSize()) {
ReplySwipeIndicator({ offset / 120 })
}
TimelineItemEventRowContent(
modifier = Modifier
.absoluteOffset { IntOffset(x = offset.roundToInt(), y = 0) }
.draggable(
orientation = Orientation.Horizontal,
enabled = !state.isResettingOnRelease,
onDragStopped = {
coroutineScope.launch {
if (thresholdCrossed) {
onSwipeToReply() onSwipeToReply()
} }
// Do not dismiss the message, return false! state.resetOffset()
false
} }
)
SwipeToDismiss(
state = dismissState,
background = {
ReplySwipeIndicator({ dismissState.toSwipeProgress() })
}, },
directions = setOf(DismissDirection.StartToEnd), state = state.draggableState,
dismissContent = { ),
TimelineItemEventRowContent(
event = event, event = event,
isHighlighted = isHighlighted, isHighlighted = isHighlighted,
interactionSource = interactionSource, interactionSource = interactionSource,
@ -151,7 +173,7 @@ fun TimelineItemEventRow(
onMoreReactionsClicked = { onMoreReactionsClick(event) }, onMoreReactionsClicked = { onMoreReactionsClick(event) },
) )
} }
) }
} else { } else {
TimelineItemEventRowContent( TimelineItemEventRowContent(
event = event, event = event,
@ -166,11 +188,29 @@ fun TimelineItemEventRow(
onMoreReactionsClicked = { onMoreReactionsClick(event) }, onMoreReactionsClicked = { onMoreReactionsClick(event) },
) )
} }
// This is assuming that we are in a ColumnScope, but this is OK, for both Preview and real usage. }
if (event.groupPosition.isNew()) { }
Spacer(modifier = modifier.height(16.dp))
} else { /**
Spacer(modifier = modifier.height(2.dp)) * Impact ViewConfiguration.touchSlop by [sensitivityFactor].
* Inspired from https://issuetracker.google.com/u/1/issues/269627294.
* @param sensitivityFactor the factor to multiply the touchSlop by. The highest value, the more the user will
* have to drag to start the drag.
* @param content the content to display.
*/
@Composable
fun SwipeSensitivity(
sensitivityFactor: Float,
content: @Composable () -> Unit,
) {
val current = LocalViewConfiguration.current
CompositionLocalProvider(
LocalViewConfiguration provides object : ViewConfiguration by current {
override val touchSlop: Float
get() = current.touchSlop * sensitivityFactor
}
) {
content()
} }
} }
@ -266,14 +306,6 @@ private fun TimelineItemEventRowContent(
} }
} }
private fun DismissState.toSwipeProgress(): Float {
return when (targetValue) {
DismissValue.Default -> 0f
DismissValue.DismissedToEnd -> progress * 3
DismissValue.DismissedToStart -> progress * 3
}
}
@Composable @Composable
private fun MessageSenderInformation( private fun MessageSenderInformation(
sender: String, sender: String,
@ -544,6 +576,7 @@ private fun ContentToPreview() {
body = "A long text which will be displayed on several lines and" + body = "A long text which will be displayed on several lines and" +
" hopefully can be manually adjusted to test different behaviors." " hopefully can be manually adjusted to test different behaviors."
), ),
groupPosition = TimelineItemGroupPosition.First,
), ),
isHighlighted = false, isHighlighted = false,
canReply = true, canReply = true,
@ -562,6 +595,7 @@ private fun ContentToPreview() {
content = aTimelineItemImageContent().copy( content = aTimelineItemImageContent().copy(
aspectRatio = 5f aspectRatio = 5f
), ),
groupPosition = TimelineItemGroupPosition.Last,
), ),
isHighlighted = false, isHighlighted = false,
canReply = true, canReply = true,
@ -606,7 +640,8 @@ private fun ContentToPreviewWithReply() {
body = "A long text which will be displayed on several lines and" + body = "A long text which will be displayed on several lines and" +
" hopefully can be manually adjusted to test different behaviors." " hopefully can be manually adjusted to test different behaviors."
), ),
inReplyTo = aInReplyToReady(replyContent) inReplyTo = aInReplyToReady(replyContent),
groupPosition = TimelineItemGroupPosition.First,
), ),
isHighlighted = false, isHighlighted = false,
canReply = true, canReply = true,
@ -625,7 +660,8 @@ private fun ContentToPreviewWithReply() {
content = aTimelineItemImageContent().copy( content = aTimelineItemImageContent().copy(
aspectRatio = 5f aspectRatio = 5f
), ),
inReplyTo = aInReplyToReady(replyContent) inReplyTo = aInReplyToReady(replyContent),
groupPosition = TimelineItemGroupPosition.Last,
), ),
isHighlighted = false, isHighlighted = false,
canReply = true, canReply = true,
@ -699,7 +735,6 @@ private fun ContentTimestampToPreview(event: TimelineItem.Event) {
} }
} }
@Preview @Preview
@Composable @Composable
internal fun TimelineItemEventRowWithManyReactionsLightPreview() = internal fun TimelineItemEventRowWithManyReactionsLightPreview() =

75
libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/swipe/SwipeableActionsState.kt

@ -0,0 +1,75 @@
/*
* 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.libraries.designsystem.swipe
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.tween
import androidx.compose.foundation.MutatePriority
import androidx.compose.foundation.gestures.DraggableState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
/**
* Inspired from https://github.com/bmarty/swipe/blob/trunk/swipe/src/main/kotlin/me/saket/swipe/SwipeableActionsState.kt
*/
@Composable
fun rememberSwipeableActionsState(): SwipeableActionsState {
return remember { SwipeableActionsState() }
}
@Stable
class SwipeableActionsState {
/**
* The current position (in pixels) of the content.
*/
val offset: State<Float> get() = offsetState
private var offsetState = mutableStateOf(0f)
/**
* Whether the content is currently animating to reset its offset after it was swiped.
*/
var isResettingOnRelease: Boolean by mutableStateOf(false)
private set
val draggableState = DraggableState { delta ->
val targetOffset = offsetState.value + delta
val isAllowed = isResettingOnRelease || targetOffset > 0f
offsetState.value += if (isAllowed) delta else 0f
}
suspend fun resetOffset() {
draggableState.drag(MutatePriority.PreventUserInput) {
isResettingOnRelease = true
try {
Animatable(offsetState.value).animateTo(
targetValue = 0f,
animationSpec = tween(durationMillis = 300),
) {
dragBy(value - offsetState.value)
}
} finally {
isResettingOnRelease = false
}
}
}
}

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowDarkPreview_0_null,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowLightPreview_0_null,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowTimestampDarkPreview_0_null_0,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowTimestampDarkPreview_0_null_1,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowTimestampDarkPreview_0_null_2,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowTimestampDarkPreview_0_null_3,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowTimestampLightPreview_0_null_0,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowTimestampLightPreview_0_null_1,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowTimestampLightPreview_0_null_2,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowTimestampLightPreview_0_null_3,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithManyReactionsDarkPreview_0_null,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithManyReactionsLightPreview_0_null,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithReplyDarkPreview_0_null,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithReplyLightPreview_0_null,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.
Loading…
Cancel
Save