diff --git a/stream-chat-android-compose/api/stream-chat-android-compose.api b/stream-chat-android-compose/api/stream-chat-android-compose.api index 13f8ceebd70..ec03265529c 100644 --- a/stream-chat-android-compose/api/stream-chat-android-compose.api +++ b/stream-chat-android-compose/api/stream-chat-android-compose.api @@ -1197,7 +1197,9 @@ public final class io/getstream/chat/android/compose/ui/components/avatar/UserAv public final class io/getstream/chat/android/compose/ui/components/button/ComposableSingletons$StreamButtonKt { public static final field INSTANCE Lio/getstream/chat/android/compose/ui/components/button/ComposableSingletons$StreamButtonKt; public fun ()V - public final fun getLambda$-166546107$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2; + public final fun getLambda$-371513815$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2; + public final fun getLambda$183536504$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2; + public final fun getLambda$461605447$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2; } public final class io/getstream/chat/android/compose/ui/components/channels/ChannelMembersKt { @@ -1295,14 +1297,14 @@ public final class io/getstream/chat/android/compose/ui/components/common/Compos public final class io/getstream/chat/android/compose/ui/components/common/ComposableSingletons$ContextualMenuKt { public static final field INSTANCE Lio/getstream/chat/android/compose/ui/components/common/ComposableSingletons$ContextualMenuKt; public fun ()V - public final fun getLambda$400372830$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function3; + public final fun getLambda$-1814646166$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function3; public final fun getLambda$757952752$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2; } public final class io/getstream/chat/android/compose/ui/components/common/ComposableSingletons$CountBadgeKt { public static final field INSTANCE Lio/getstream/chat/android/compose/ui/components/common/ComposableSingletons$CountBadgeKt; public fun ()V - public final fun getLambda$-956196504$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2; + public final fun getLambda$-1802414546$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2; } public final class io/getstream/chat/android/compose/ui/components/common/ComposableSingletons$MediaBadgesKt { @@ -1429,8 +1431,9 @@ public final class io/getstream/chat/android/compose/ui/components/messageaction public final class io/getstream/chat/android/compose/ui/components/messageactions/ComposableSingletons$UserReactionRowKt { public static final field INSTANCE Lio/getstream/chat/android/compose/ui/components/messageactions/ComposableSingletons$UserReactionRowKt; public fun ()V - public final fun getLambda$-1334175414$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2; - public final fun getLambda$-819920205$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2; + public final fun getLambda$-1303366844$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2; + public final fun getLambda$-1404854311$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2; + public final fun getLambda$1975375011$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2; } public final class io/getstream/chat/android/compose/ui/components/messageactions/MessageActionsHeaderKt { @@ -1493,10 +1496,9 @@ public final class io/getstream/chat/android/compose/ui/components/messages/Comp public final class io/getstream/chat/android/compose/ui/components/messages/ComposableSingletons$MessageReactionsKt { public static final field INSTANCE Lio/getstream/chat/android/compose/ui/components/messages/ComposableSingletons$MessageReactionsKt; public fun ()V - public final fun getLambda$-1033212521$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2; - public final fun getLambda$-2112297624$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2; - public final fun getLambda$263020848$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2; - public final fun getLambda$886436303$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2; + public final fun getLambda$-1247075191$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2; + public final fun getLambda$-1998294682$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2; + public final fun getLambda$-702061313$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2; } public final class io/getstream/chat/android/compose/ui/components/messages/ComposableSingletons$MessageTextKt { @@ -1519,6 +1521,17 @@ public final class io/getstream/chat/android/compose/ui/components/messages/Comp public final fun getLambda$73995334$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2; } +public final class io/getstream/chat/android/compose/ui/components/messages/ComposableSingletons$QuotedMessageKt { + public static final field INSTANCE Lio/getstream/chat/android/compose/ui/components/messages/ComposableSingletons$QuotedMessageKt; + public fun ()V + public final fun getLambda$-242957275$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2; + public final fun getLambda$-615170823$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2; + public final fun getLambda$1327756724$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2; + public final fun getLambda$1642749758$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2; + public final fun getLambda$1784593268$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2; + public final fun getLambda$976393970$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2; +} + public final class io/getstream/chat/android/compose/ui/components/messages/ComposableSingletons$ScrollToBottomButtonKt { public static final field INSTANCE Lio/getstream/chat/android/compose/ui/components/messages/ComposableSingletons$ScrollToBottomButtonKt; public fun ()V @@ -1598,9 +1611,11 @@ public final class io/getstream/chat/android/compose/ui/components/poll/Composab public static final field INSTANCE Lio/getstream/chat/android/compose/ui/components/poll/ComposableSingletons$PollAnswersKt; public fun ()V public final fun getLambda$-1411166830$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2; + public final fun getLambda$-1780669891$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2; public final fun getLambda$-1980307438$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function3; + public final fun getLambda$-232122758$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2; public final fun getLambda$-49181804$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function3; - public final fun getLambda$-608288022$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2; + public final fun getLambda$705192511$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2; } public final class io/getstream/chat/android/compose/ui/components/poll/ComposableSingletons$PollDialogHeaderKt { diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/button/StreamButton.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/button/StreamButton.kt index 8be96ed8b2d..b54935d70e1 100644 --- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/button/StreamButton.kt +++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/button/StreamButton.kt @@ -114,39 +114,77 @@ internal fun StreamButton( @Preview(showBackground = true) @Composable -private fun StreamButtonPreview() { +private fun StreamButtonEnabledPreview() { ChatTheme { - val styles = listOf( - StreamButtonStyleDefaults.primarySolid, - StreamButtonStyleDefaults.primaryOutline, - StreamButtonStyleDefaults.primaryGhost, - StreamButtonStyleDefaults.secondarySolid, - StreamButtonStyleDefaults.secondaryOutline, - StreamButtonStyleDefaults.secondaryGhost, - StreamButtonStyleDefaults.destructiveSolid, - StreamButtonStyleDefaults.destructiveOutline, - StreamButtonStyleDefaults.destructiveGhost, - ) + StreamButtonStyles(enabled = true) + } +} - Column( - modifier = Modifier.padding(StreamTokens.spacingMd), - verticalArrangement = Arrangement.spacedBy(StreamTokens.spacingXs), - ) { - styles.forEach { style -> - Row(horizontalArrangement = Arrangement.spacedBy(StreamTokens.spacingXs)) { - val painter = painterResource(R.drawable.stream_design_ic_checkmark) - StreamButton(onClick = {}, style = style) { - Icon(painter, null) - } - StreamTextButton( - onClick = {}, - style = style, - leadingIcon = painter, - text = "{{ label }}", - trailingIcon = painter, - ) +@Preview(showBackground = true) +@Composable +private fun StreamButtonDisabledPreview() { + ChatTheme { + StreamButtonStyles(enabled = false) + } +} + +@Composable +internal fun StreamButtonStyles(enabled: Boolean) { + val styles = listOf( + StreamButtonStyleDefaults.primarySolid, + StreamButtonStyleDefaults.primaryOutline, + StreamButtonStyleDefaults.primaryGhost, + StreamButtonStyleDefaults.secondarySolid, + StreamButtonStyleDefaults.secondaryOutline, + StreamButtonStyleDefaults.secondaryGhost, + StreamButtonStyleDefaults.destructiveSolid, + StreamButtonStyleDefaults.destructiveOutline, + StreamButtonStyleDefaults.destructiveGhost, + ) + + Column( + modifier = Modifier.padding(StreamTokens.spacingMd), + verticalArrangement = Arrangement.spacedBy(StreamTokens.spacingXs), + ) { + styles.forEach { style -> + Row(horizontalArrangement = Arrangement.spacedBy(StreamTokens.spacingXs)) { + val painter = painterResource(R.drawable.stream_design_ic_checkmark) + StreamButton(onClick = {}, style = style, enabled = enabled) { + Icon(painter, null) } + StreamTextButton( + onClick = {}, + style = style, + enabled = enabled, + leadingIcon = painter, + text = "{{ label }}", + trailingIcon = painter, + ) } } } } + +@Preview(showBackground = true) +@Composable +private fun StreamButtonSizesPreview() { + ChatTheme { + StreamButtonSizes() + } +} + +@Composable +internal fun StreamButtonSizes() { + Column( + modifier = Modifier.padding(StreamTokens.spacingMd), + verticalArrangement = Arrangement.spacedBy(StreamTokens.spacingXs), + ) { + StreamButtonSize.entries.forEach { size -> + StreamTextButton( + onClick = {}, + text = "Button $size", + size = size, + ) + } + } +} diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/common/ContextualMenu.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/common/ContextualMenu.kt index 06e0065e4c5..759830104d3 100644 --- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/common/ContextualMenu.kt +++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/common/ContextualMenu.kt @@ -133,23 +133,22 @@ internal fun ContextualMenuDivider(modifier: Modifier = Modifier) { @Preview(showBackground = true) @Composable private fun ContextualMenuPreview() { - ChatTheme { - ContextualMenu( - Modifier - .padding(32.dp) - .width(IntrinsicSize.Min), - ) { - MenuItemPreview(enabled = true, destructive = false) - MenuItemPreview(enabled = false, destructive = false) - MenuItemPreview(enabled = false, destructive = true) - ContextualMenuDivider() - MenuItemPreview(enabled = true, destructive = true) - } + ChatTheme { PopulatedContextualMenu() } +} + +@Composable +internal fun PopulatedContextualMenu() { + ContextualMenu(Modifier.width(IntrinsicSize.Min)) { + MenuItem(enabled = true, destructive = false) + MenuItem(enabled = false, destructive = false) + MenuItem(enabled = false, destructive = true) + ContextualMenuDivider() + MenuItem(enabled = true, destructive = true) } } @Composable -private fun MenuItemPreview(enabled: Boolean, destructive: Boolean) { +private fun MenuItem(enabled: Boolean, destructive: Boolean) { ContextualMenuItem( label = "{{ label }}", destructive = destructive, diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/common/CountBadge.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/common/CountBadge.kt index 074b00ea35d..ca87d80b2b7 100644 --- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/common/CountBadge.kt +++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/common/CountBadge.kt @@ -99,12 +99,18 @@ internal data class CountBadgeSize( @Preview @Composable -private fun CountBadgePreview() { - ChatTheme { - Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp)) { - CountBadgeSize.entries.forEach { size -> - CountBadge(text = "+99", size = size) - } +private fun CountBadgeSizesPreview() { + ChatTheme { CountBadgeSizes() } +} + +@Composable +internal fun CountBadgeSizes() { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(4.dp), + ) { + CountBadgeSize.entries.forEach { size -> + CountBadge(text = "+99", size = size) } } } diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messageactions/UserReactionRow.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messageactions/UserReactionRow.kt index 046dd076efb..d8d27c6135f 100644 --- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messageactions/UserReactionRow.kt +++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messageactions/UserReactionRow.kt @@ -100,24 +100,35 @@ internal fun UserReactionRow( } } -/** - * Preview of the [UserReactionRow] component with a reaction left by the current user. - */ @Preview(showBackground = true) @Composable -private fun CurrentUserReactionItemPreview() { - ChatPreviewTheme { - UserReactionRow(item = PreviewUserReactionData.user1Reaction()) - } +private fun OwnRemovableUserReactionRowPreview() { + ChatPreviewTheme { OwnRemovableUserReactionRow() } } -/** - * Preview of the [UserReactionRow] component with a reaction left by another user. - */ @Preview(showBackground = true) @Composable -private fun OtherUserReactionItemPreview() { - ChatPreviewTheme { - UserReactionRow(item = PreviewUserReactionData.user2Reaction()) - } +private fun OwnUserReactionRowPreview() { + ChatPreviewTheme { OwnUserReactionRow() } +} + +@Preview(showBackground = true) +@Composable +private fun OtherUserReactionRowPreview() { + ChatPreviewTheme { OtherUserReactionRow() } +} + +@Composable +internal fun OwnRemovableUserReactionRow() { + UserReactionRow(item = PreviewUserReactionData.user1Reaction(), onClick = {}) +} + +@Composable +internal fun OwnUserReactionRow() { + UserReactionRow(item = PreviewUserReactionData.user1Reaction()) +} + +@Composable +internal fun OtherUserReactionRow() { + UserReactionRow(item = PreviewUserReactionData.user2Reaction()) } diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/MessageReactions.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/MessageReactions.kt index bdef34b4d2c..5395c3628ea 100644 --- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/MessageReactions.kt +++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/MessageReactions.kt @@ -191,32 +191,38 @@ private fun ReactionChip( @Preview @Composable -private fun SingleClusteredMessageReactionsPreview() { - ChatTheme { - ClusteredMessageReactions(reactions = PreviewReactionData.oneReaction()) - } +private fun ManyClusteredMessageReactionsPreview() { + ChatTheme { ManyClusteredMessageReactions() } } @Preview @Composable -private fun MultipleClusteredMessageReactionsPreview() { - ChatTheme { - ClusteredMessageReactions(reactions = PreviewReactionData.manyReactions()) - } +private fun ManySegmentedMessageReactionsPreview() { + ChatTheme { ManySegmentedMessageReactions() } } @Preview @Composable -private fun SingleSegmentedMessageReactionsPreview() { - ChatTheme { - SegmentedMessageReactions(reactions = PreviewReactionData.oneReaction()) - } +private fun OverflowSegmentedMessageReactionsPreview() { + ChatTheme { OverflowSegmentedMessageReactions() } } -@Preview @Composable -private fun MultipleSegmentedMessageReactionsPreview() { - ChatTheme { - SegmentedMessageReactions(reactions = PreviewReactionData.manyReactions()) - } +internal fun ManyClusteredMessageReactions() { + ClusteredMessageReactions(reactions = PreviewReactionData.manyReactions()) +} + +@Composable +internal fun ManySegmentedMessageReactions() { + SegmentedMessageReactions(reactions = PreviewReactionData.manyReactions()) +} + +@Composable +internal fun OverflowSegmentedMessageReactions() { + SegmentedMessageReactions( + reactions = PreviewReactionData.manyReactions() + listOf( + MessageReactionItemState(type = "haha", emoji = "😂", count = 3), + MessageReactionItemState(type = "fire", emoji = "🔥", count = 7), + ), + ) } diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/QuotedMessage.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/QuotedMessage.kt index f69fe452c93..8aa2464d743 100644 --- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/QuotedMessage.kt +++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/QuotedMessage.kt @@ -44,6 +44,7 @@ import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import io.getstream.chat.android.compose.R import io.getstream.chat.android.compose.ui.components.ComposerCancelIcon @@ -55,8 +56,11 @@ import io.getstream.chat.android.compose.ui.theme.ChatTheme import io.getstream.chat.android.compose.ui.theme.MessageStyling import io.getstream.chat.android.compose.ui.theme.StreamTokens import io.getstream.chat.android.compose.ui.util.StreamAsyncImage +import io.getstream.chat.android.models.Attachment +import io.getstream.chat.android.models.AttachmentType import io.getstream.chat.android.models.Message import io.getstream.chat.android.models.User +import io.getstream.chat.android.previewdata.PreviewUserData import io.getstream.chat.android.ui.common.utils.extensions.isMine /** @@ -229,3 +233,127 @@ internal data class QuotedMessageBody( val videoPreviewData: Any? = null, val previewIcon: FileIconData? = null, ) + +@Preview +@Composable +private fun QuotedMessageFromOtherUserPreview() { + ChatTheme { QuotedMessageFromOtherUser() } +} + +@Preview +@Composable +private fun QuotedMessageFromSelfPreview() { + ChatTheme { QuotedMessageFromSelf() } +} + +@Preview +@Composable +private fun QuotedMessageWithLongTextPreview() { + ChatTheme { QuotedMessageWithLongText() } +} + +@Preview +@Composable +private fun QuotedMessageWithImageAttachmentPreview() { + ChatTheme { QuotedMessageWithImageAttachment() } +} + +@Preview +@Composable +private fun QuotedMessageWithFileAttachmentPreview() { + ChatTheme { QuotedMessageWithFileAttachment() } +} + +@Preview +@Composable +private fun QuotedMessageInComposerPreview() { + ChatTheme { QuotedMessageInComposer() } +} + +@Composable +internal fun QuotedMessageFromOtherUser() { + QuotedMessage( + message = Message( + id = "msg-1", + text = "Hey, did you see the new design?", + user = PreviewUserData.user2, + ), + currentUser = PreviewUserData.user1, + ) +} + +@Composable +internal fun QuotedMessageFromSelf() { + QuotedMessage( + message = Message( + id = "msg-2", + text = "Yes, looks great!", + user = PreviewUserData.user1, + ), + currentUser = PreviewUserData.user1, + ) +} + +@Composable +internal fun QuotedMessageWithLongText() { + QuotedMessage( + message = Message( + id = "msg-3", + text = "This is a very long quoted message that should overflow with ellipsis " + + "because it does not fit on a single line in the quoted preview", + user = PreviewUserData.user2, + ), + currentUser = PreviewUserData.user1, + ) +} + +@Composable +internal fun QuotedMessageWithImageAttachment() { + QuotedMessage( + message = Message( + id = "msg-4", + text = "Check this out", + user = PreviewUserData.user2, + attachments = mutableListOf( + Attachment( + type = AttachmentType.IMAGE, + imageUrl = "https://example.com/image.jpg", + ), + ), + ), + currentUser = PreviewUserData.user1, + ) +} + +@Composable +internal fun QuotedMessageWithFileAttachment() { + QuotedMessage( + message = Message( + id = "msg-5", + text = "", + user = PreviewUserData.user2, + attachments = mutableListOf( + Attachment( + type = AttachmentType.FILE, + title = "Q1-report.pdf", + mimeType = "application/pdf", + fileSize = 1024 * 256, + ), + ), + ), + currentUser = PreviewUserData.user1, + ) +} + +@Composable +internal fun QuotedMessageInComposer() { + MessageComposerQuotedMessage( + message = Message( + id = "msg-6", + text = "Reply target", + user = PreviewUserData.user2, + ), + currentUser = PreviewUserData.user1, + onCancelClick = {}, + ) +} diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollAnswers.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollAnswers.kt index 3dd4341e901..7ae00621940 100644 --- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollAnswers.kt +++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollAnswers.kt @@ -341,37 +341,79 @@ private fun AddAnswerDialogInput(newOption: MutableState, modifier: Modi @Preview @Composable -private fun PollAnswersDialogPreview() { +private fun PollAnswersContentPreview() { ChatTheme { - val now = Date() - val pollWithAnswers = PreviewPollData.poll1.copy( - answers = listOf( - Answer( - id = "preview1", - pollId = "", - text = "I think we should go with option A, it makes the most sense.", - createdAt = now, - updatedAt = now, - user = PreviewUserData.user1, - ), - Answer( - id = "preview2", - pollId = "", - text = "This is my own comment on the poll.", - createdAt = now, - updatedAt = now, - user = PreviewUserData.user2, - ), - Answer( - id = "preview3", - pollId = "", - text = "Option B is clearly better!", - createdAt = now, - updatedAt = now, - user = PreviewUserData.user3, - ), - ), - ) - Content(poll = pollWithAnswers) + PollAnswersContent() + } +} + +@Composable +internal fun PollAnswersContent() { + Content(poll = previewPollWithAnswers()) +} + +@Preview +@Composable +private fun PollAnswersWithCurrentUserContentPreview() { + ChatTheme { + PollAnswersWithCurrentUserContent() + } +} + +@Composable +internal fun PollAnswersWithCurrentUserContent() { + val poll = previewPollWithAnswers() + val currentUserAnswer = poll.answers.first { it.user?.id == PreviewUserData.user1.id } + Content(poll = poll, currentUserAnswer = currentUserAnswer) +} + +@Preview +@Composable +private fun PollAnswersClosedAnonymousContentPreview() { + ChatTheme { + PollAnswersClosedAnonymousContent() } } + +@Composable +internal fun PollAnswersClosedAnonymousContent() { + Content( + poll = previewPollWithAnswers().copy( + closed = true, + votingVisibility = VotingVisibility.ANONYMOUS, + ), + ) +} + +private fun previewPollWithAnswers(): Poll { + val previewDate = Date(0) + + return PreviewPollData.poll1.copy( + answers = listOf( + Answer( + id = "preview1", + pollId = "", + text = "I think we should go with option A, it makes the most sense.", + createdAt = previewDate, + updatedAt = previewDate, + user = PreviewUserData.user1, + ), + Answer( + id = "preview2", + pollId = "", + text = "This is my own comment on the poll.", + createdAt = previewDate, + updatedAt = previewDate, + user = PreviewUserData.user2, + ), + Answer( + id = "preview3", + pollId = "", + text = "Option B is clearly better!", + createdAt = previewDate, + updatedAt = previewDate, + user = PreviewUserData.user3, + ), + ), + ) +} diff --git a/stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/button/StreamButtonTest.kt b/stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/button/StreamButtonTest.kt new file mode 100644 index 00000000000..a076f72781d --- /dev/null +++ b/stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/button/StreamButtonTest.kt @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2014-2026 Stream.io Inc. All rights reserved. + * + * Licensed under the Stream License; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/GetStream/stream-chat-android/blob/main/LICENSE + * + * 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.getstream.chat.android.compose.ui.components.button + +import app.cash.paparazzi.DeviceConfig +import app.cash.paparazzi.Paparazzi +import com.android.ide.common.rendering.api.SessionParams +import com.android.resources.ScreenOrientation +import io.getstream.chat.android.compose.ui.PaparazziComposeTest +import org.junit.Rule +import org.junit.Test + +internal class StreamButtonTest : PaparazziComposeTest { + + @get:Rule + override val paparazzi = Paparazzi( + deviceConfig = DeviceConfig.PIXEL_2.copy(orientation = ScreenOrientation.LANDSCAPE), + renderingMode = SessionParams.RenderingMode.SHRINK, + ) + + @Test + fun `enabled buttons`() { + snapshotWithDarkModeRow { + StreamButtonStyles(enabled = true) + } + } + + @Test + fun `disabled buttons`() { + snapshotWithDarkModeRow { + StreamButtonStyles(enabled = false) + } + } + + @Test + fun `button sizes`() { + snapshotWithDarkModeRow { + StreamButtonSizes() + } + } +} diff --git a/stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/common/CountBadgeTest.kt b/stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/common/CountBadgeTest.kt new file mode 100644 index 00000000000..df0a30044c7 --- /dev/null +++ b/stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/common/CountBadgeTest.kt @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2014-2026 Stream.io Inc. All rights reserved. + * + * Licensed under the Stream License; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/GetStream/stream-chat-android/blob/main/LICENSE + * + * 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.getstream.chat.android.compose.ui.components.common + +import app.cash.paparazzi.DeviceConfig +import app.cash.paparazzi.Paparazzi +import com.android.ide.common.rendering.api.SessionParams +import io.getstream.chat.android.compose.ui.PaparazziComposeTest +import org.junit.Rule +import org.junit.Test + +internal class CountBadgeTest : PaparazziComposeTest { + + @get:Rule + override val paparazzi = Paparazzi( + deviceConfig = DeviceConfig.PIXEL_2, + renderingMode = SessionParams.RenderingMode.SHRINK, + ) + + @Test + fun `count badge sizes`() { + snapshotWithDarkModeRow { CountBadgeSizes() } + } +} diff --git a/stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/messages/MessageFooterTest.kt b/stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/messages/MessageFooterTest.kt index f2803363d2d..2783f12c865 100644 --- a/stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/messages/MessageFooterTest.kt +++ b/stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/messages/MessageFooterTest.kt @@ -181,6 +181,27 @@ internal class MessageFooterTest : PaparazziComposeTest { ) } } + + @Test + fun `outgoing thread start with replies`() { + snapshotWithDarkModeRow { + MessageFooter( + messageItem = MessageItemState( + message = Message( + id = "msg-8", + text = "Outgoing thread with replies", + createdAt = FixedDate, + replyCount = 5, + threadParticipants = listOf(PreviewUserData.user1, PreviewUserData.user2), + ), + isMine = true, + isInThread = false, + showMessageFooter = false, + ownCapabilities = ChannelCapabilities.toSet(), + ), + ) + } + } } private val FixedDate: Date = Calendar.getInstance(TimeZone.getTimeZone("UTC")).apply { diff --git a/stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/messages/MessageReactionsTest.kt b/stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/messages/MessageReactionsTest.kt new file mode 100644 index 00000000000..f33ea23cdd8 --- /dev/null +++ b/stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/messages/MessageReactionsTest.kt @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2014-2026 Stream.io Inc. All rights reserved. + * + * Licensed under the Stream License; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/GetStream/stream-chat-android/blob/main/LICENSE + * + * 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.getstream.chat.android.compose.ui.components.messages + +import app.cash.paparazzi.DeviceConfig +import app.cash.paparazzi.Paparazzi +import com.android.ide.common.rendering.api.SessionParams +import io.getstream.chat.android.compose.ui.PaparazziComposeTest +import org.junit.Rule +import org.junit.Test + +internal class MessageReactionsTest : PaparazziComposeTest { + + @get:Rule + override val paparazzi = Paparazzi( + deviceConfig = DeviceConfig.PIXEL_2, + renderingMode = SessionParams.RenderingMode.SHRINK, + ) + + @Test + fun `clustered reactions`() { + snapshotWithDarkModeRow { ManyClusteredMessageReactions() } + } + + @Test + fun `segmented reactions`() { + snapshotWithDarkModeRow { ManySegmentedMessageReactions() } + } + + @Test + fun `overflow segmented reactions`() { + snapshot { OverflowSegmentedMessageReactions() } + } + + @Test + fun `overflow segmented reactions in dark mode`() { + snapshot(isInDarkMode = true) { OverflowSegmentedMessageReactions() } + } +} diff --git a/stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/messages/QuotedMessageTest.kt b/stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/messages/QuotedMessageTest.kt new file mode 100644 index 00000000000..65d7ea00b31 --- /dev/null +++ b/stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/messages/QuotedMessageTest.kt @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2014-2026 Stream.io Inc. All rights reserved. + * + * Licensed under the Stream License; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/GetStream/stream-chat-android/blob/main/LICENSE + * + * 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.getstream.chat.android.compose.ui.components.messages + +import app.cash.paparazzi.DeviceConfig +import app.cash.paparazzi.Paparazzi +import com.android.ide.common.rendering.api.SessionParams +import io.getstream.chat.android.compose.ui.PaparazziComposeTest +import org.junit.Rule +import org.junit.Test + +internal class QuotedMessageTest : PaparazziComposeTest { + + @get:Rule + override val paparazzi = Paparazzi( + deviceConfig = DeviceConfig.PIXEL_2, + renderingMode = SessionParams.RenderingMode.SHRINK, + ) + + @Test + fun `from other user`() { + snapshotWithDarkModeRow { QuotedMessageFromOtherUser() } + } + + @Test + fun `from self`() { + snapshotWithDarkModeRow { QuotedMessageFromSelf() } + } + + @Test + fun `with long text`() { + snapshotWithDarkModeRow { QuotedMessageWithLongText() } + } + + @Test + fun `with image attachment`() { + snapshotWithDarkModeRow { QuotedMessageWithImageAttachment() } + } + + @Test + fun `with file attachment`() { + snapshotWithDarkModeRow { QuotedMessageWithFileAttachment() } + } + + @Test + fun `in composer`() { + snapshotWithDarkModeRow { QuotedMessageInComposer() } + } +} diff --git a/stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/poll/PollAnswersTest.kt b/stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/poll/PollAnswersTest.kt new file mode 100644 index 00000000000..827a5a35950 --- /dev/null +++ b/stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/poll/PollAnswersTest.kt @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2014-2026 Stream.io Inc. All rights reserved. + * + * Licensed under the Stream License; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/GetStream/stream-chat-android/blob/main/LICENSE + * + * 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.getstream.chat.android.compose.ui.components.poll + +import app.cash.paparazzi.DeviceConfig +import app.cash.paparazzi.Paparazzi +import io.getstream.chat.android.compose.ui.PaparazziComposeTest +import org.junit.Rule +import org.junit.Test + +internal class PollAnswersTest : PaparazziComposeTest { + + @get:Rule + override val paparazzi: Paparazzi = Paparazzi(deviceConfig = DeviceConfig.PIXEL_2) + + @Test + fun content() { + snapshotWithDarkMode { + PollAnswersContent() + } + } + + @Test + fun `with current user answer`() { + snapshotWithDarkMode { + PollAnswersWithCurrentUserContent() + } + } + + @Test + fun `closed anonymous`() { + snapshotWithDarkMode { + PollAnswersClosedAnonymousContent() + } + } +} diff --git a/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.button_StreamButtonTest_button_sizes.png b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.button_StreamButtonTest_button_sizes.png new file mode 100644 index 00000000000..0debf66a206 Binary files /dev/null and b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.button_StreamButtonTest_button_sizes.png differ diff --git a/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.button_StreamButtonTest_disabled_buttons.png b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.button_StreamButtonTest_disabled_buttons.png new file mode 100644 index 00000000000..ff000255a5b Binary files /dev/null and b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.button_StreamButtonTest_disabled_buttons.png differ diff --git a/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.button_StreamButtonTest_enabled_buttons.png b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.button_StreamButtonTest_enabled_buttons.png new file mode 100644 index 00000000000..2c73a2d4790 Binary files /dev/null and b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.button_StreamButtonTest_enabled_buttons.png differ diff --git a/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.common_CountBadgeTest_count_badge_sizes.png b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.common_CountBadgeTest_count_badge_sizes.png new file mode 100644 index 00000000000..bccaea827ed Binary files /dev/null and b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.common_CountBadgeTest_count_badge_sizes.png differ diff --git a/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.messages_MessageFooterTest_outgoing_thread_start_with_replies.png b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.messages_MessageFooterTest_outgoing_thread_start_with_replies.png new file mode 100644 index 00000000000..14e967d2d7b Binary files /dev/null and b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.messages_MessageFooterTest_outgoing_thread_start_with_replies.png differ diff --git a/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.messages_MessageReactionsTest_clustered_reactions.png b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.messages_MessageReactionsTest_clustered_reactions.png new file mode 100644 index 00000000000..d5d1c78243c Binary files /dev/null and b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.messages_MessageReactionsTest_clustered_reactions.png differ diff --git a/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.messages_MessageReactionsTest_overflow_segmented_reactions.png b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.messages_MessageReactionsTest_overflow_segmented_reactions.png new file mode 100644 index 00000000000..a7ae4e32a85 Binary files /dev/null and b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.messages_MessageReactionsTest_overflow_segmented_reactions.png differ diff --git a/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.messages_MessageReactionsTest_overflow_segmented_reactions_in_dark_mode.png b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.messages_MessageReactionsTest_overflow_segmented_reactions_in_dark_mode.png new file mode 100644 index 00000000000..70cbbc61c66 Binary files /dev/null and b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.messages_MessageReactionsTest_overflow_segmented_reactions_in_dark_mode.png differ diff --git a/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.messages_MessageReactionsTest_segmented_reactions.png b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.messages_MessageReactionsTest_segmented_reactions.png new file mode 100644 index 00000000000..7e0d94b722a Binary files /dev/null and b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.messages_MessageReactionsTest_segmented_reactions.png differ diff --git a/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.messages_QuotedMessageTest_from_other_user.png b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.messages_QuotedMessageTest_from_other_user.png new file mode 100644 index 00000000000..ac1f844c1df Binary files /dev/null and b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.messages_QuotedMessageTest_from_other_user.png differ diff --git a/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.messages_QuotedMessageTest_from_self.png b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.messages_QuotedMessageTest_from_self.png new file mode 100644 index 00000000000..4a1a141c12e Binary files /dev/null and b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.messages_QuotedMessageTest_from_self.png differ diff --git a/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.messages_QuotedMessageTest_in_composer.png b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.messages_QuotedMessageTest_in_composer.png new file mode 100644 index 00000000000..a3fc7d687a7 Binary files /dev/null and b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.messages_QuotedMessageTest_in_composer.png differ diff --git a/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.messages_QuotedMessageTest_with_file_attachment.png b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.messages_QuotedMessageTest_with_file_attachment.png new file mode 100644 index 00000000000..8c755b7f9a9 Binary files /dev/null and b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.messages_QuotedMessageTest_with_file_attachment.png differ diff --git a/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.messages_QuotedMessageTest_with_image_attachment.png b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.messages_QuotedMessageTest_with_image_attachment.png new file mode 100644 index 00000000000..04df5c36203 Binary files /dev/null and b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.messages_QuotedMessageTest_with_image_attachment.png differ diff --git a/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.messages_QuotedMessageTest_with_long_text.png b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.messages_QuotedMessageTest_with_long_text.png new file mode 100644 index 00000000000..c62b390b2ef Binary files /dev/null and b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.messages_QuotedMessageTest_with_long_text.png differ diff --git a/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.poll_PollAnswersTest_closed_anonymous.png b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.poll_PollAnswersTest_closed_anonymous.png new file mode 100644 index 00000000000..724e813287a Binary files /dev/null and b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.poll_PollAnswersTest_closed_anonymous.png differ diff --git a/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.poll_PollAnswersTest_content.png b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.poll_PollAnswersTest_content.png new file mode 100644 index 00000000000..f8414ef0978 Binary files /dev/null and b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.poll_PollAnswersTest_content.png differ diff --git a/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.poll_PollAnswersTest_with_current_user_answer.png b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.poll_PollAnswersTest_with_current_user_answer.png new file mode 100644 index 00000000000..5b29efb80b3 Binary files /dev/null and b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.poll_PollAnswersTest_with_current_user_answer.png differ diff --git a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/widgets/typing/internal/TypingIndicatorAnimationView.kt b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/widgets/typing/internal/TypingIndicatorAnimationView.kt index 00a5e86487d..18466f37a6c 100644 --- a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/widgets/typing/internal/TypingIndicatorAnimationView.kt +++ b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/widgets/typing/internal/TypingIndicatorAnimationView.kt @@ -16,11 +16,13 @@ package io.getstream.chat.android.ui.widgets.typing.internal +import android.animation.ValueAnimator import android.content.Context import android.graphics.Canvas import android.graphics.ColorFilter import android.graphics.Paint import android.graphics.PixelFormat +import android.graphics.drawable.Animatable import android.graphics.drawable.Drawable import android.util.AttributeSet import androidx.appcompat.widget.AppCompatImageView @@ -53,8 +55,10 @@ internal class TypingIndicatorAnimationView : AppCompatImageView { /** * A Drawable that renders an animation with 3 dots. + * + * Driven by a [ValueAnimator] so that test infrastructure can freeze it deterministically. */ -private class TypingDrawable(context: Context) : Drawable() { +private class TypingDrawable(context: Context) : Drawable(), Animatable { private val paint: Paint = Paint() private val intrinsicHeight: Int @@ -64,6 +68,13 @@ private class TypingDrawable(context: Context) : Drawable() { private val dotDiameterPx: Float private val dotRadiusPx: Float + private val animator = ValueAnimator.ofFloat(0f, FULL_ANIMATION_DURATION.toFloat()).apply { + duration = FULL_ANIMATION_DURATION.toLong() + repeatCount = ValueAnimator.INFINITE + repeatMode = ValueAnimator.RESTART + addUpdateListener { invalidateSelf() } + } + init { paint.color = context.getColorCompat(R.color.stream_ui_grey) paint.style = Paint.Style.FILL @@ -77,6 +88,22 @@ private class TypingDrawable(context: Context) : Drawable() { intrinsicHeight = DOT_SIZE_DP.dpToPx() } + override fun start() { + if (!animator.isStarted) animator.start() + } + + override fun stop() { + animator.cancel() + } + + override fun isRunning(): Boolean = animator.isRunning + + override fun setVisible(visible: Boolean, restart: Boolean): Boolean { + val changed = super.setVisible(visible, restart) + if (visible) start() else stop() + return changed + } + /** * Returns the intrinsic or minimum width of the Drawable. */ @@ -88,13 +115,15 @@ private class TypingDrawable(context: Context) : Drawable() { override fun getIntrinsicHeight(): Int = intrinsicHeight /** - * Draws the current frame of the animation and schedules the next frame. + * Draws the current frame of the animation. The animator schedules the next frame via + * [invalidateSelf] from its update listener. * * @param canvas The canvas to draw into. */ override fun draw(canvas: Canvas) { + val phase = (animator.animatedValue as? Float) ?: 0f for (dotIndex in 0 until DOT_COUNT) { - paint.alpha = calculateAlpha(dotIndex) + paint.alpha = calculateAlpha(dotIndex, phase) canvas.drawCircle( calculateCx(dotIndex), dotRadiusPx, @@ -102,18 +131,18 @@ private class TypingDrawable(context: Context) : Drawable() { paint, ) } - invalidateSelf() } /** - * Calculates the alpha of the dot to be drawn according to the current timestamp. + * Calculates the alpha of the dot to be drawn at the given animation [phase]. * * @param dotIndex The index of the current dot to be drawn. + * @param phase The current animation phase, in millis within a cycle, supplied by the animator. * @return The alpha component of the paint's color from 0 to 255. */ - private fun calculateAlpha(dotIndex: Int): Int { + private fun calculateAlpha(dotIndex: Int, phase: Float): Int { val animationOffset = (DOT_COUNT - dotIndex) * ANIMATION_OFFSET_MILLIS - val timeInCycle = (System.currentTimeMillis() + animationOffset) % FULL_ANIMATION_DURATION + val timeInCycle = (phase + animationOffset) % FULL_ANIMATION_DURATION val coefficient: Float = if (timeInCycle > DOTS_ANIMATION_DURATION_MILLIS) { 0f diff --git a/stream-chat-android-ui-components/src/test/kotlin/io/getstream/chat/android/ui/PaparazziViewTest.kt b/stream-chat-android-ui-components/src/test/kotlin/io/getstream/chat/android/ui/PaparazziViewTest.kt index 0e9aafe5c66..7840c63e8f4 100644 --- a/stream-chat-android-ui-components/src/test/kotlin/io/getstream/chat/android/ui/PaparazziViewTest.kt +++ b/stream-chat-android-ui-components/src/test/kotlin/io/getstream/chat/android/ui/PaparazziViewTest.kt @@ -19,6 +19,7 @@ package io.getstream.chat.android.ui import android.content.Context import android.view.View import android.widget.LinearLayout +import app.cash.paparazzi.InstantAnimationsRule import app.cash.paparazzi.Paparazzi import coil3.ComponentRegistry import coil3.ImageLoader @@ -55,6 +56,9 @@ internal abstract class PaparazziViewTest : MockedChatClientTest { uiMode = Configuration.UI_MODE_NIGHT_YES })*/ + @get:Rule + val instantAnimations = InstantAnimationsRule() + @Before fun prepare() { ChatUI.appContext = paparazzi.context diff --git a/stream-chat-android-ui-components/src/test/snapshots/images/io.getstream.chat.android.ui.feature.channels.header_ChannelListHeaderViewTest_connecting,_no_user.png b/stream-chat-android-ui-components/src/test/snapshots/images/io.getstream.chat.android.ui.feature.channels.header_ChannelListHeaderViewTest_connecting,_no_user.png index 6190840b92d..619e44eeb7a 100644 Binary files a/stream-chat-android-ui-components/src/test/snapshots/images/io.getstream.chat.android.ui.feature.channels.header_ChannelListHeaderViewTest_connecting,_no_user.png and b/stream-chat-android-ui-components/src/test/snapshots/images/io.getstream.chat.android.ui.feature.channels.header_ChannelListHeaderViewTest_connecting,_no_user.png differ diff --git a/stream-chat-android-ui-components/src/test/snapshots/images/io.getstream.chat.android.ui.feature.channels.header_ChannelListHeaderViewTest_connecting,_with_user.png b/stream-chat-android-ui-components/src/test/snapshots/images/io.getstream.chat.android.ui.feature.channels.header_ChannelListHeaderViewTest_connecting,_with_user.png index 055e6e2081d..3038eb2c2d3 100644 Binary files a/stream-chat-android-ui-components/src/test/snapshots/images/io.getstream.chat.android.ui.feature.channels.header_ChannelListHeaderViewTest_connecting,_with_user.png and b/stream-chat-android-ui-components/src/test/snapshots/images/io.getstream.chat.android.ui.feature.channels.header_ChannelListHeaderViewTest_connecting,_with_user.png differ diff --git a/stream-chat-android-ui-components/src/test/snapshots/images/io.getstream.chat.android.ui.feature.channels.list_ChannelListViewTest_loaded_channels.png b/stream-chat-android-ui-components/src/test/snapshots/images/io.getstream.chat.android.ui.feature.channels.list_ChannelListViewTest_loaded_channels.png index c80b7ab5a9f..392fffb29a7 100644 Binary files a/stream-chat-android-ui-components/src/test/snapshots/images/io.getstream.chat.android.ui.feature.channels.list_ChannelListViewTest_loaded_channels.png and b/stream-chat-android-ui-components/src/test/snapshots/images/io.getstream.chat.android.ui.feature.channels.list_ChannelListViewTest_loaded_channels.png differ diff --git a/stream-chat-android-ui-components/src/test/snapshots/images/io.getstream.chat.android.ui.feature.channels.list_ChannelListViewTest_loading_channels.png b/stream-chat-android-ui-components/src/test/snapshots/images/io.getstream.chat.android.ui.feature.channels.list_ChannelListViewTest_loading_channels.png index 80cd1e08e7c..bdeea24ffb6 100644 Binary files a/stream-chat-android-ui-components/src/test/snapshots/images/io.getstream.chat.android.ui.feature.channels.list_ChannelListViewTest_loading_channels.png and b/stream-chat-android-ui-components/src/test/snapshots/images/io.getstream.chat.android.ui.feature.channels.list_ChannelListViewTest_loading_channels.png differ