diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt index 850f709847..906ed731a7 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt @@ -53,8 +53,6 @@ import androidx.compose.ui.platform.ViewConfiguration import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp @@ -63,7 +61,6 @@ import androidx.constraintlayout.compose.ConstrainScope import androidx.constraintlayout.compose.ConstraintLayout import io.element.android.features.messages.impl.timeline.TimelineEvents import io.element.android.features.messages.impl.timeline.aTimelineItemEvent -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.toExtraPadding import io.element.android.features.messages.impl.timeline.components.receipt.ReadReceiptViewState @@ -75,17 +72,14 @@ import io.element.android.features.messages.impl.timeline.model.bubble.BubbleSta 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.TimelineItemPollContent -import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemImageContent -import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemPollContent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent import io.element.android.libraries.designsystem.colors.AvatarColorsProvider import io.element.android.libraries.designsystem.components.EqualWidthColumn import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.preview.ElementPreview -import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.swipe.SwipeableActionsState import io.element.android.libraries.designsystem.swipe.rememberSwipeableActionsState @@ -101,7 +95,6 @@ import io.element.android.libraries.matrix.api.timeline.item.event.ImageMessageT import io.element.android.libraries.matrix.api.timeline.item.event.LocationMessageType import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent import io.element.android.libraries.matrix.api.timeline.item.event.PollContent -import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType import io.element.android.libraries.matrix.api.timeline.item.event.VoiceMessageType import io.element.android.libraries.matrix.ui.components.AttachmentThumbnail @@ -110,7 +103,6 @@ import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailType import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.coroutines.launch -import org.jsoup.Jsoup import kotlin.math.abs import kotlin.math.roundToInt @@ -748,211 +740,3 @@ internal fun TimelineItemEventRowPreview() = ElementPreview { } } } - -@PreviewsDayNight -@Composable -internal fun TimelineItemEventRowWithReplyPreview() = ElementPreview { - Column { - sequenceOf(false, true).forEach { - val replyContent = if (it) { - // Short - "Message which are being replied." - } else { - // Long, to test 2 lines and ellipsis) - "Message which are being replied, and which was long enough to be displayed on two lines (only!)." - } - TimelineItemEventRow( - event = aTimelineItemEvent( - isMine = it, - content = aTimelineItemTextContent().copy( - body = "A long text which will be displayed on several lines and" + - " hopefully can be manually adjusted to test different behaviors." - ), - inReplyTo = aInReplyToDetails(replyContent), - groupPosition = TimelineItemGroupPosition.First, - ), - showReadReceipts = false, - isLastOutgoingMessage = false, - isHighlighted = false, - canReply = true, - onClick = {}, - onLongClick = {}, - onUserDataClick = {}, - inReplyToClick = {}, - onReactionClick = { _, _ -> }, - onReactionLongClick = { _, _ -> }, - onMoreReactionsClick = {}, - onReadReceiptClick = {}, - onTimestampClicked = {}, - onSwipeToReply = {}, - eventSink = {}, - ) - TimelineItemEventRow( - event = aTimelineItemEvent( - isMine = it, - content = aTimelineItemImageContent().copy( - aspectRatio = 5f - ), - inReplyTo = aInReplyToDetails(replyContent), - isThreaded = true, - groupPosition = TimelineItemGroupPosition.Last, - ), - showReadReceipts = false, - isLastOutgoingMessage = false, - isHighlighted = false, - canReply = true, - onClick = {}, - onLongClick = {}, - onUserDataClick = {}, - inReplyToClick = {}, - onReactionClick = { _, _ -> }, - onReactionLongClick = { _, _ -> }, - onMoreReactionsClick = {}, - onReadReceiptClick = {}, - onTimestampClicked = {}, - onSwipeToReply = {}, - eventSink = {}, - ) - } - } -} - -private fun aInReplyToDetails( - replyContent: String, -): InReplyToDetails { - return InReplyToDetails( - eventId = EventId("\$event"), - eventContent = MessageContent(replyContent, null, false, false, TextMessageType(replyContent, null)), - senderId = UserId("@Sender:domain"), - senderDisplayName = "Sender", - senderAvatarUrl = null, - textContent = replyContent, - ) -} - -@PreviewsDayNight -@Composable -internal fun TimelineItemEventRowTimestampPreview( - @PreviewParameter(TimelineItemEventForTimestampViewProvider::class) event: TimelineItem.Event -) = ElementPreview { - Column { - val oldContent = event.content as TimelineItemTextContent - listOf( - "Text", - "Text longer, displayed on 1 line", - "Text which should be rendered on several lines", - ).forEach { str -> - listOf(false, true).forEach { useDocument -> - TimelineItemEventRow( - event = event.copy( - content = oldContent.copy( - body = str, - htmlDocument = if (useDocument) Jsoup.parse(str) else null, - ), - reactionsState = aTimelineItemReactions(count = 0), - senderDisplayName = if (useDocument) "Document case" else "Text case", - ), - showReadReceipts = false, - isLastOutgoingMessage = false, - isHighlighted = false, - canReply = true, - onClick = {}, - onLongClick = {}, - onUserDataClick = {}, - inReplyToClick = {}, - onReactionClick = { _, _ -> }, - onReactionLongClick = { _, _ -> }, - onMoreReactionsClick = {}, - onReadReceiptClick = {}, - onTimestampClicked = {}, - onSwipeToReply = {}, - eventSink = {}, - ) - } - } - } -} - -@PreviewsDayNight -@Composable -internal fun TimelineItemEventRowWithManyReactionsPreview() = ElementPreview { - Column { - listOf(false, true).forEach { isMine -> - TimelineItemEventRow( - event = aTimelineItemEvent( - isMine = isMine, - content = aTimelineItemTextContent().copy( - body = "A couple of multi-line messages with many reactions attached." + - " One sent by me and another from someone else." - ), - timelineItemReactions = aTimelineItemReactions(count = 20), - ), - showReadReceipts = false, - isLastOutgoingMessage = false, - isHighlighted = false, - canReply = true, - onClick = {}, - onLongClick = {}, - onUserDataClick = {}, - inReplyToClick = {}, - onReactionClick = { _, _ -> }, - onReactionLongClick = { _, _ -> }, - onMoreReactionsClick = {}, - onReadReceiptClick = {}, - onSwipeToReply = {}, - onTimestampClicked = {}, - eventSink = {}, - ) - } - } -} - -// Note: no need for light/dark variant for this preview -@Preview -@Composable -internal fun TimelineItemEventRowLongSenderNamePreview() = ElementPreviewLight { - TimelineItemEventRow( - event = aTimelineItemEvent( - senderDisplayName = "a long sender display name to test single line and ellipsis at the end of the line", - ), - showReadReceipts = false, - isLastOutgoingMessage = false, - isHighlighted = false, - canReply = true, - onClick = {}, - onLongClick = {}, - onUserDataClick = {}, - inReplyToClick = {}, - onReactionClick = { _, _ -> }, - onReactionLongClick = { _, _ -> }, - onMoreReactionsClick = {}, - onReadReceiptClick = {}, - onSwipeToReply = {}, - onTimestampClicked = {}, - eventSink = {}, - ) -} - -// Note: no need for light/dark variant for this preview, we only look at the timestamp position -@Preview -@Composable -internal fun TimelineItemEventTimestampBelowPreview() = ElementPreviewLight { - TimelineItemEventRow( - event = aTimelineItemEvent(content = aTimelineItemPollContent()), - showReadReceipts = false, - isLastOutgoingMessage = false, - isHighlighted = false, - canReply = true, - onClick = {}, - onLongClick = {}, - onUserDataClick = {}, - inReplyToClick = {}, - onReactionClick = { _, _ -> }, - onReactionLongClick = { _, _ -> }, - onMoreReactionsClick = {}, - onReadReceiptClick = {}, - onSwipeToReply = {}, - onTimestampClicked = {}, - eventSink = {}, - ) -} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowLongSenderNamePreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowLongSenderNamePreview.kt new file mode 100644 index 0000000000..3e282279a3 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowLongSenderNamePreview.kt @@ -0,0 +1,48 @@ +/* + * 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.features.messages.impl.timeline.components + +import androidx.compose.runtime.Composable +import androidx.compose.ui.tooling.preview.Preview +import io.element.android.features.messages.impl.timeline.aTimelineItemEvent +import io.element.android.libraries.designsystem.preview.ElementPreviewLight + +// Note: no need for light/dark variant for this preview +@Preview +@Composable +internal fun TimelineItemEventRowLongSenderNamePreview() = ElementPreviewLight { + TimelineItemEventRow( + event = aTimelineItemEvent( + senderDisplayName = "a long sender display name to test single line and ellipsis at the end of the line", + ), + showReadReceipts = false, + isLastOutgoingMessage = false, + isHighlighted = false, + canReply = true, + onClick = {}, + onLongClick = {}, + onUserDataClick = {}, + inReplyToClick = {}, + onReactionClick = { _, _ -> }, + onReactionLongClick = { _, _ -> }, + onMoreReactionsClick = {}, + onReadReceiptClick = {}, + onSwipeToReply = {}, + onTimestampClicked = {}, + eventSink = {}, + ) +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowTimestampPreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowTimestampPreview.kt new file mode 100644 index 0000000000..554754568d --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowTimestampPreview.kt @@ -0,0 +1,70 @@ +/* + * 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.features.messages.impl.timeline.components + +import androidx.compose.foundation.layout.Column +import androidx.compose.runtime.Composable +import androidx.compose.ui.tooling.preview.PreviewParameter +import io.element.android.features.messages.impl.timeline.aTimelineItemReactions +import io.element.android.features.messages.impl.timeline.model.TimelineItem +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import org.jsoup.Jsoup + +@PreviewsDayNight +@Composable +internal fun TimelineItemEventRowTimestampPreview( + @PreviewParameter(TimelineItemEventForTimestampViewProvider::class) event: TimelineItem.Event +) = ElementPreview { + Column { + val oldContent = event.content as TimelineItemTextContent + listOf( + "Text", + "Text longer, displayed on 1 line", + "Text which should be rendered on several lines", + ).forEach { str -> + listOf(false, true).forEach { useDocument -> + TimelineItemEventRow( + event = event.copy( + content = oldContent.copy( + body = str, + htmlDocument = if (useDocument) Jsoup.parse(str) else null, + ), + reactionsState = aTimelineItemReactions(count = 0), + senderDisplayName = if (useDocument) "Document case" else "Text case", + ), + showReadReceipts = false, + isLastOutgoingMessage = false, + isHighlighted = false, + canReply = true, + onClick = {}, + onLongClick = {}, + onUserDataClick = {}, + inReplyToClick = {}, + onReactionClick = { _, _ -> }, + onReactionLongClick = { _, _ -> }, + onMoreReactionsClick = {}, + onReadReceiptClick = {}, + onTimestampClicked = {}, + onSwipeToReply = {}, + eventSink = {}, + ) + } + } + } +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithManyReactionsPreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithManyReactionsPreview.kt new file mode 100644 index 0000000000..0857572a2a --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithManyReactionsPreview.kt @@ -0,0 +1,60 @@ +/* + * 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.features.messages.impl.timeline.components + +import androidx.compose.foundation.layout.Column +import androidx.compose.runtime.Composable +import io.element.android.features.messages.impl.timeline.aTimelineItemEvent +import io.element.android.features.messages.impl.timeline.aTimelineItemReactions +import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight + +@PreviewsDayNight +@Composable +internal fun TimelineItemEventRowWithManyReactionsPreview() = ElementPreview { + Column { + listOf(false, true).forEach { isMine -> + TimelineItemEventRow( + event = aTimelineItemEvent( + isMine = isMine, + content = aTimelineItemTextContent().copy( + body = "A couple of multi-line messages with many reactions attached." + + " One sent by me and another from someone else." + ), + timelineItemReactions = aTimelineItemReactions(count = 20), + ), + showReadReceipts = false, + isLastOutgoingMessage = false, + isHighlighted = false, + canReply = true, + onClick = {}, + onLongClick = {}, + onUserDataClick = {}, + inReplyToClick = {}, + onReactionClick = { _, _ -> }, + onReactionLongClick = { _, _ -> }, + onMoreReactionsClick = {}, + onReadReceiptClick = {}, + onSwipeToReply = {}, + onTimestampClicked = {}, + eventSink = {}, + ) + } + } +} + diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyPreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyPreview.kt new file mode 100644 index 0000000000..41bbbd46a9 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyPreview.kt @@ -0,0 +1,112 @@ +/* + * 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.features.messages.impl.timeline.components + +import androidx.compose.foundation.layout.Column +import androidx.compose.runtime.Composable +import io.element.android.features.messages.impl.timeline.aTimelineItemEvent +import io.element.android.features.messages.impl.timeline.model.InReplyToDetails +import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition +import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemImageContent +import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +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.timeline.item.event.MessageContent +import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType + +@PreviewsDayNight +@Composable +internal fun TimelineItemEventRowWithReplyPreview() = ElementPreview { + Column { + sequenceOf(false, true).forEach { + val replyContent = if (it) { + // Short + "Message which are being replied." + } else { + // Long, to test 2 lines and ellipsis) + "Message which are being replied, and which was long enough to be displayed on two lines (only!)." + } + TimelineItemEventRow( + event = aTimelineItemEvent( + isMine = it, + content = aTimelineItemTextContent().copy( + body = "A long text which will be displayed on several lines and" + + " hopefully can be manually adjusted to test different behaviors." + ), + inReplyTo = aInReplyToDetails(replyContent), + groupPosition = TimelineItemGroupPosition.First, + ), + showReadReceipts = false, + isLastOutgoingMessage = false, + isHighlighted = false, + canReply = true, + onClick = {}, + onLongClick = {}, + onUserDataClick = {}, + inReplyToClick = {}, + onReactionClick = { _, _ -> }, + onReactionLongClick = { _, _ -> }, + onMoreReactionsClick = {}, + onReadReceiptClick = {}, + onTimestampClicked = {}, + onSwipeToReply = {}, + eventSink = {}, + ) + TimelineItemEventRow( + event = aTimelineItemEvent( + isMine = it, + content = aTimelineItemImageContent().copy( + aspectRatio = 5f + ), + inReplyTo = aInReplyToDetails(replyContent), + isThreaded = true, + groupPosition = TimelineItemGroupPosition.Last, + ), + showReadReceipts = false, + isLastOutgoingMessage = false, + isHighlighted = false, + canReply = true, + onClick = {}, + onLongClick = {}, + onUserDataClick = {}, + inReplyToClick = {}, + onReactionClick = { _, _ -> }, + onReactionLongClick = { _, _ -> }, + onMoreReactionsClick = {}, + onReadReceiptClick = {}, + onTimestampClicked = {}, + onSwipeToReply = {}, + eventSink = {}, + ) + } + } +} + +private fun aInReplyToDetails( + replyContent: String, +): InReplyToDetails { + return InReplyToDetails( + eventId = EventId("\$event"), + eventContent = MessageContent(replyContent, null, false, false, TextMessageType(replyContent, null)), + senderId = UserId("@Sender:domain"), + senderDisplayName = "Sender", + senderAvatarUrl = null, + textContent = replyContent, + ) +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventTimestampBelowPreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventTimestampBelowPreview.kt new file mode 100644 index 0000000000..51cb4bb020 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventTimestampBelowPreview.kt @@ -0,0 +1,47 @@ +/* + * 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.features.messages.impl.timeline.components + +import androidx.compose.runtime.Composable +import androidx.compose.ui.tooling.preview.Preview +import io.element.android.features.messages.impl.timeline.aTimelineItemEvent +import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemPollContent +import io.element.android.libraries.designsystem.preview.ElementPreviewLight + +// Note: no need for light/dark variant for this preview, we only look at the timestamp position +@Preview +@Composable +internal fun TimelineItemEventTimestampBelowPreview() = ElementPreviewLight { + TimelineItemEventRow( + event = aTimelineItemEvent(content = aTimelineItemPollContent()), + showReadReceipts = false, + isLastOutgoingMessage = false, + isHighlighted = false, + canReply = true, + onClick = {}, + onLongClick = {}, + onUserDataClick = {}, + inReplyToClick = {}, + onReactionClick = { _, _ -> }, + onReactionLongClick = { _, _ -> }, + onMoreReactionsClick = {}, + onReadReceiptClick = {}, + onSwipeToReply = {}, + onTimestampClicked = {}, + eventSink = {}, + ) +}