Skip to content

Commit 9b5a1c4

Browse files
committed
feat: Add delivered status indicator for messages
This commit introduces a new "delivered" status for messages, which is displayed when a message has been delivered but not yet read. **Key changes:** - Added `isMessageDelivered` property to `MessageItemState` and `MessageListItem`. - Introduced a new grey double-check icon (`stream_ui_ic_check_double_grey`) for the delivered state. - Updated `ChannelListViewStyle` and `MessageListItemStyle` to include `indicatorDeliveredIcon`. - The message status indicator logic in both UI Components and Compose has been updated to show the delivered icon between the "sent" and "read" states. - Added a new `stream_ui_message_list_semantics_message_status_delivered` string for accessibility. - In Compose, `MessageFooterStatusIndicator` is updated to accept `MessageFooterStatusIndicatorParams` to handle the new delivered state, deprecating the old signature.
1 parent b790d46 commit 9b5a1c4

File tree

20 files changed

+232
-60
lines changed

20 files changed

+232
-60
lines changed

stream-chat-android-compose/api/stream-chat-android-compose.api

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1613,7 +1613,7 @@ public final class io/getstream/chat/android/compose/ui/components/channels/Comp
16131613

16141614
public final class io/getstream/chat/android/compose/ui/components/channels/MessageReadStatusIconKt {
16151615
public static final fun MessageReadStatusIcon (Lio/getstream/chat/android/models/Channel;Lio/getstream/chat/android/models/Message;Lio/getstream/chat/android/models/User;Landroidx/compose/ui/Modifier;Landroidx/compose/runtime/Composer;II)V
1616-
public static final fun MessageReadStatusIcon (Lio/getstream/chat/android/models/Message;ZLandroidx/compose/ui/Modifier;ILkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;II)V
1616+
public static final fun MessageReadStatusIcon (Lio/getstream/chat/android/models/Message;ZLandroidx/compose/ui/Modifier;ZILkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;II)V
16171617
}
16181618

16191619
public final class io/getstream/chat/android/compose/ui/components/channels/UnreadCountIndicatorKt {
@@ -2908,6 +2908,7 @@ public abstract interface class io/getstream/chat/android/compose/ui/theme/ChatC
29082908
public abstract fun MessageFooterContent (Lio/getstream/chat/android/ui/common/state/messages/list/MessageItemState;Landroidx/compose/runtime/Composer;I)V
29092909
public abstract fun MessageFooterOnlyVisibleToYouContent (Lio/getstream/chat/android/ui/common/state/messages/list/MessageItemState;Landroidx/compose/runtime/Composer;I)V
29102910
public abstract fun MessageFooterStatusIndicator (Landroidx/compose/ui/Modifier;Lio/getstream/chat/android/models/Message;ZILandroidx/compose/runtime/Composer;I)V
2911+
public abstract fun MessageFooterStatusIndicator (Lio/getstream/chat/android/compose/ui/theme/MessageFooterStatusIndicatorParams;Landroidx/compose/runtime/Composer;I)V
29112912
public abstract fun MessageFooterUploadingContent (Landroidx/compose/ui/Modifier;Lio/getstream/chat/android/ui/common/state/messages/list/MessageItemState;Landroidx/compose/runtime/Composer;I)V
29122913
public abstract fun MessageGiphyContent (Lio/getstream/chat/android/models/Message;Lio/getstream/chat/android/models/User;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;I)V
29132914
public abstract fun MessageItemCenterContent (Landroidx/compose/foundation/layout/ColumnScope;Lio/getstream/chat/android/ui/common/state/messages/list/MessageItemState;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;II)V
@@ -3089,6 +3090,7 @@ public final class io/getstream/chat/android/compose/ui/theme/ChatComponentFacto
30893090
public static fun MessageFooterContent (Lio/getstream/chat/android/compose/ui/theme/ChatComponentFactory;Lio/getstream/chat/android/ui/common/state/messages/list/MessageItemState;Landroidx/compose/runtime/Composer;I)V
30903091
public static fun MessageFooterOnlyVisibleToYouContent (Lio/getstream/chat/android/compose/ui/theme/ChatComponentFactory;Lio/getstream/chat/android/ui/common/state/messages/list/MessageItemState;Landroidx/compose/runtime/Composer;I)V
30913092
public static fun MessageFooterStatusIndicator (Lio/getstream/chat/android/compose/ui/theme/ChatComponentFactory;Landroidx/compose/ui/Modifier;Lio/getstream/chat/android/models/Message;ZILandroidx/compose/runtime/Composer;I)V
3093+
public static fun MessageFooterStatusIndicator (Lio/getstream/chat/android/compose/ui/theme/ChatComponentFactory;Lio/getstream/chat/android/compose/ui/theme/MessageFooterStatusIndicatorParams;Landroidx/compose/runtime/Composer;I)V
30923094
public static fun MessageFooterUploadingContent (Lio/getstream/chat/android/compose/ui/theme/ChatComponentFactory;Landroidx/compose/ui/Modifier;Lio/getstream/chat/android/ui/common/state/messages/list/MessageItemState;Landroidx/compose/runtime/Composer;I)V
30933095
public static fun MessageGiphyContent (Lio/getstream/chat/android/compose/ui/theme/ChatComponentFactory;Lio/getstream/chat/android/models/Message;Lio/getstream/chat/android/models/User;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;I)V
30943096
public static fun MessageItemCenterContent (Lio/getstream/chat/android/compose/ui/theme/ChatComponentFactory;Landroidx/compose/foundation/layout/ColumnScope;Lio/getstream/chat/android/ui/common/state/messages/list/MessageItemState;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;II)V
@@ -3522,6 +3524,18 @@ public final class io/getstream/chat/android/compose/ui/theme/MessageDateSeparat
35223524
public final fun defaultTheme (Lio/getstream/chat/android/compose/ui/theme/StreamTypography;Lio/getstream/chat/android/compose/ui/theme/StreamColors;Landroidx/compose/runtime/Composer;II)Lio/getstream/chat/android/compose/ui/theme/MessageDateSeparatorTheme;
35233525
}
35243526

3527+
public final class io/getstream/chat/android/compose/ui/theme/MessageFooterStatusIndicatorParams {
3528+
public static final field $stable I
3529+
public fun <init> (Lio/getstream/chat/android/ui/common/state/messages/list/MessageItemState;)V
3530+
public final fun component1 ()Lio/getstream/chat/android/ui/common/state/messages/list/MessageItemState;
3531+
public final fun copy (Lio/getstream/chat/android/ui/common/state/messages/list/MessageItemState;)Lio/getstream/chat/android/compose/ui/theme/MessageFooterStatusIndicatorParams;
3532+
public static synthetic fun copy$default (Lio/getstream/chat/android/compose/ui/theme/MessageFooterStatusIndicatorParams;Lio/getstream/chat/android/ui/common/state/messages/list/MessageItemState;ILjava/lang/Object;)Lio/getstream/chat/android/compose/ui/theme/MessageFooterStatusIndicatorParams;
3533+
public fun equals (Ljava/lang/Object;)Z
3534+
public final fun getMessageItem ()Lio/getstream/chat/android/ui/common/state/messages/list/MessageItemState;
3535+
public fun hashCode ()I
3536+
public fun toString ()Ljava/lang/String;
3537+
}
3538+
35253539
public final class io/getstream/chat/android/compose/ui/theme/MessageOptionsTheme {
35263540
public static final field $stable I
35273541
public static final field Companion Lio/getstream/chat/android/compose/ui/theme/MessageOptionsTheme$Companion;

stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/channels/MessageReadStatusIcon.kt

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import androidx.compose.ui.semantics.contentDescription
3131
import androidx.compose.ui.semantics.semantics
3232
import androidx.compose.ui.tooling.preview.Preview
3333
import androidx.compose.ui.unit.dp
34+
import io.getstream.chat.android.client.extensions.deliveredReadsOf
3435
import io.getstream.chat.android.client.extensions.getCreatedAtOrThrow
3536
import io.getstream.chat.android.compose.R
3637
import io.getstream.chat.android.compose.ui.theme.ChatTheme
@@ -59,11 +60,13 @@ public fun MessageReadStatusIcon(
5960
val readStatuses = channel.getReadStatuses(userToIgnore = currentUser)
6061
val readCount = readStatuses.count { it.time >= message.getCreatedAtOrThrow().time }
6162
val isMessageRead = readCount != 0
63+
val isMessageDelivered = channel.deliveredReadsOf(message).isNotEmpty()
6264

6365
MessageReadStatusIcon(
6466
modifier = modifier,
6567
message = message,
6668
isMessageRead = isMessageRead,
69+
isMessageDelivered = isMessageDelivered,
6770
readCount = readCount,
6871
)
6972
}
@@ -73,26 +76,36 @@ public fun MessageReadStatusIcon(
7376
*
7477
* @param message The message with sync status to check.
7578
* @param isMessageRead If the message is read by any member.
79+
* @param isMessageDelivered If the message is delivered to any member.
7680
* @param modifier Modifier for styling.
7781
*/
7882
@Composable
7983
public fun MessageReadStatusIcon(
8084
message: Message,
8185
isMessageRead: Boolean,
8286
modifier: Modifier = Modifier,
87+
isMessageDelivered: Boolean = false,
8388
readCount: Int = 0,
8489
isReadIcon: @Composable () -> Unit = { IsReadCount(modifier = modifier, readCount = readCount) },
8590
isPendingIcon: @Composable () -> Unit = { IsPendingIcon(modifier = modifier) },
8691
isSentIcon: @Composable () -> Unit = { IsSentIcon(modifier = modifier) },
92+
isDeliveredIcon: @Composable () -> Unit = { IsDeliveredIcon(modifier = modifier) },
8793
) {
8894
val syncStatus = message.syncStatus
89-
when {
90-
isMessageRead -> isReadIcon()
9195

92-
syncStatus == SyncStatus.SYNC_NEEDED ||
93-
syncStatus == SyncStatus.AWAITING_ATTACHMENTS -> isPendingIcon()
96+
when (syncStatus) {
97+
SyncStatus.IN_PROGRESS,
98+
SyncStatus.SYNC_NEEDED,
99+
SyncStatus.AWAITING_ATTACHMENTS,
100+
-> isPendingIcon()
94101

95-
syncStatus == SyncStatus.COMPLETED -> isSentIcon()
102+
SyncStatus.COMPLETED -> when {
103+
isMessageRead -> isReadIcon()
104+
isMessageDelivered -> isDeliveredIcon()
105+
else -> isSentIcon()
106+
}
107+
108+
SyncStatus.FAILED_PERMANENTLY -> Unit
96109
}
97110
}
98111

@@ -110,7 +123,7 @@ private fun IsReadCount(
110123
Row(
111124
modifier = modifier
112125
.semantics { contentDescription = description }
113-
.padding(horizontal = 2.dp),
126+
.padding(start = 2.dp),
114127
verticalAlignment = Alignment.CenterVertically,
115128
horizontalArrangement = Arrangement.spacedBy(2.dp),
116129
) {
@@ -151,6 +164,18 @@ private fun IsSentIcon(modifier: Modifier) {
151164
)
152165
}
153166

167+
@Composable
168+
private fun IsDeliveredIcon(modifier: Modifier) {
169+
Icon(
170+
modifier = Modifier.testTag("Stream_MessageReadStatus_isDelivered"),
171+
painter = painterResource(id = R.drawable.stream_compose_message_seen),
172+
contentDescription = stringResource(
173+
R.string.stream_ui_message_list_semantics_message_status_delivered,
174+
),
175+
tint = ChatTheme.colors.disabled,
176+
)
177+
}
178+
154179
/**
155180
* Preview of [MessageReadStatusIcon] for a seen message.
156181
*

stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/MessageFooter.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import io.getstream.chat.android.compose.R
3939
import io.getstream.chat.android.compose.state.DateFormatType
4040
import io.getstream.chat.android.compose.ui.components.Timestamp
4141
import io.getstream.chat.android.compose.ui.theme.ChatTheme
42+
import io.getstream.chat.android.compose.ui.theme.MessageFooterStatusIndicatorParams
4243
import io.getstream.chat.android.compose.ui.util.clickable
4344
import io.getstream.chat.android.core.utils.date.truncateFuture
4445
import io.getstream.chat.android.models.Message
@@ -103,10 +104,9 @@ public fun MessageFooter(
103104
)
104105
} else {
105106
ChatTheme.componentFactory.MessageFooterStatusIndicator(
106-
modifier = Modifier.padding(end = 4.dp),
107-
message = message,
108-
isMessageRead = messageItem.isMessageRead,
109-
readCount = messageItem.messageReadBy.size,
107+
params = MessageFooterStatusIndicatorParams(
108+
messageItem = messageItem,
109+
),
110110
)
111111
}
112112

stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/ChatComponentFactory.kt

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1373,6 +1373,18 @@ public interface ChatComponentFactory {
13731373
/**
13741374
* The default read status indicator in the message footer, weather the message is sent, pending or read.
13751375
*/
1376+
@Deprecated(
1377+
message = "Use the new version of MessageFooterStatusIndicator that takes MessageFooterStatusIndicatorParams.",
1378+
replaceWith = ReplaceWith(
1379+
"MessageFooterStatusIndicator(\n" +
1380+
" params = MessageFooterStatusIndicatorParams(\n" +
1381+
" modifier = modifier,\n" +
1382+
" messageItem = messageItem,\n" +
1383+
" ),\n" +
1384+
")",
1385+
),
1386+
level = DeprecationLevel.WARNING,
1387+
)
13761388
@Composable
13771389
public fun MessageFooterStatusIndicator(
13781390
modifier: Modifier,
@@ -1388,6 +1400,28 @@ public interface ChatComponentFactory {
13881400
)
13891401
}
13901402

1403+
@Composable
1404+
public fun MessageFooterStatusIndicator(
1405+
params: MessageFooterStatusIndicatorParams,
1406+
) {
1407+
if (params.messageItem.isMessageDelivered) {
1408+
MessageReadStatusIcon(
1409+
modifier = Modifier.padding(end = 4.dp),
1410+
message = params.messageItem.message,
1411+
isMessageRead = params.messageItem.isMessageRead,
1412+
isMessageDelivered = params.messageItem.isMessageDelivered,
1413+
readCount = params.messageItem.messageReadBy.size,
1414+
)
1415+
} else {
1416+
MessageFooterStatusIndicator(
1417+
modifier = Modifier.padding(end = 4.dp),
1418+
message = params.messageItem.message,
1419+
isMessageRead = params.messageItem.isMessageRead,
1420+
readCount = params.messageItem.messageReadBy.size,
1421+
)
1422+
}
1423+
}
1424+
13911425
/**
13921426
* The default message composer that contains
13931427
* the message input, attachments, commands, recording actions, integrations, and the send button.

stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/ChatComponentFactoryParams.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import androidx.compose.runtime.Composable
2020
import androidx.compose.ui.Modifier
2121
import io.getstream.chat.android.compose.state.reactionoptions.ReactionOptionItemState
2222
import io.getstream.chat.android.models.Message
23+
import io.getstream.chat.android.ui.common.state.messages.list.MessageItemState
2324

2425
/**
2526
* Parameters for the [ChatComponentFactory.MessageReactionList] component.
@@ -59,3 +60,12 @@ public data class ChannelMediaAttachmentsPreviewBottomBarParams(
5960
val leadingContent: @Composable () -> Unit = {},
6061
val trailingContent: @Composable () -> Unit = {},
6162
)
63+
64+
/**
65+
* Parameters for the [ChatComponentFactory.MessageFooterStatusIndicator] component.
66+
*
67+
* @param messageItem The message item state.
68+
*/
69+
public data class MessageFooterStatusIndicatorParams(
70+
val messageItem: MessageItemState,
71+
)

stream-chat-android-ui-common/api/stream-chat-android-ui-common.api

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2433,23 +2433,24 @@ public final class io/getstream/chat/android/ui/common/state/messages/list/Messa
24332433

24342434
public final class io/getstream/chat/android/ui/common/state/messages/list/MessageItemState : io/getstream/chat/android/ui/common/state/messages/list/HasMessageListItemState {
24352435
public static final field $stable I
2436-
public fun <init> (Lio/getstream/chat/android/models/Message;Ljava/lang/String;ZZZLio/getstream/chat/android/models/User;Ljava/util/List;ZLio/getstream/chat/android/ui/common/state/messages/list/DeletedMessageVisibility;Lio/getstream/chat/android/ui/common/state/messages/list/MessageFocusState;Ljava/util/List;ZLjava/util/Set;)V
2437-
public synthetic fun <init> (Lio/getstream/chat/android/models/Message;Ljava/lang/String;ZZZLio/getstream/chat/android/models/User;Ljava/util/List;ZLio/getstream/chat/android/ui/common/state/messages/list/DeletedMessageVisibility;Lio/getstream/chat/android/ui/common/state/messages/list/MessageFocusState;Ljava/util/List;ZLjava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
2436+
public fun <init> (Lio/getstream/chat/android/models/Message;Ljava/lang/String;ZZZLio/getstream/chat/android/models/User;Ljava/util/List;ZZLio/getstream/chat/android/ui/common/state/messages/list/DeletedMessageVisibility;Lio/getstream/chat/android/ui/common/state/messages/list/MessageFocusState;Ljava/util/List;ZLjava/util/Set;)V
2437+
public synthetic fun <init> (Lio/getstream/chat/android/models/Message;Ljava/lang/String;ZZZLio/getstream/chat/android/models/User;Ljava/util/List;ZZLio/getstream/chat/android/ui/common/state/messages/list/DeletedMessageVisibility;Lio/getstream/chat/android/ui/common/state/messages/list/MessageFocusState;Ljava/util/List;ZLjava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
24382438
public final fun component1 ()Lio/getstream/chat/android/models/Message;
2439-
public final fun component10 ()Lio/getstream/chat/android/ui/common/state/messages/list/MessageFocusState;
2440-
public final fun component11 ()Ljava/util/List;
2441-
public final fun component12 ()Z
2442-
public final fun component13 ()Ljava/util/Set;
2439+
public final fun component10 ()Lio/getstream/chat/android/ui/common/state/messages/list/DeletedMessageVisibility;
2440+
public final fun component11 ()Lio/getstream/chat/android/ui/common/state/messages/list/MessageFocusState;
2441+
public final fun component12 ()Ljava/util/List;
2442+
public final fun component13 ()Z
2443+
public final fun component14 ()Ljava/util/Set;
24432444
public final fun component2 ()Ljava/lang/String;
24442445
public final fun component3 ()Z
24452446
public final fun component4 ()Z
24462447
public final fun component5 ()Z
24472448
public final fun component6 ()Lio/getstream/chat/android/models/User;
24482449
public final fun component7 ()Ljava/util/List;
24492450
public final fun component8 ()Z
2450-
public final fun component9 ()Lio/getstream/chat/android/ui/common/state/messages/list/DeletedMessageVisibility;
2451-
public final fun copy (Lio/getstream/chat/android/models/Message;Ljava/lang/String;ZZZLio/getstream/chat/android/models/User;Ljava/util/List;ZLio/getstream/chat/android/ui/common/state/messages/list/DeletedMessageVisibility;Lio/getstream/chat/android/ui/common/state/messages/list/MessageFocusState;Ljava/util/List;ZLjava/util/Set;)Lio/getstream/chat/android/ui/common/state/messages/list/MessageItemState;
2452-
public static synthetic fun copy$default (Lio/getstream/chat/android/ui/common/state/messages/list/MessageItemState;Lio/getstream/chat/android/models/Message;Ljava/lang/String;ZZZLio/getstream/chat/android/models/User;Ljava/util/List;ZLio/getstream/chat/android/ui/common/state/messages/list/DeletedMessageVisibility;Lio/getstream/chat/android/ui/common/state/messages/list/MessageFocusState;Ljava/util/List;ZLjava/util/Set;ILjava/lang/Object;)Lio/getstream/chat/android/ui/common/state/messages/list/MessageItemState;
2451+
public final fun component9 ()Z
2452+
public final fun copy (Lio/getstream/chat/android/models/Message;Ljava/lang/String;ZZZLio/getstream/chat/android/models/User;Ljava/util/List;ZZLio/getstream/chat/android/ui/common/state/messages/list/DeletedMessageVisibility;Lio/getstream/chat/android/ui/common/state/messages/list/MessageFocusState;Ljava/util/List;ZLjava/util/Set;)Lio/getstream/chat/android/ui/common/state/messages/list/MessageItemState;
2453+
public static synthetic fun copy$default (Lio/getstream/chat/android/ui/common/state/messages/list/MessageItemState;Lio/getstream/chat/android/models/Message;Ljava/lang/String;ZZZLio/getstream/chat/android/models/User;Ljava/util/List;ZZLio/getstream/chat/android/ui/common/state/messages/list/DeletedMessageVisibility;Lio/getstream/chat/android/ui/common/state/messages/list/MessageFocusState;Ljava/util/List;ZLjava/util/Set;ILjava/lang/Object;)Lio/getstream/chat/android/ui/common/state/messages/list/MessageItemState;
24532454
public fun equals (Ljava/lang/Object;)Z
24542455
public final fun getCurrentUser ()Lio/getstream/chat/android/models/User;
24552456
public final fun getDeletedMessageVisibility ()Lio/getstream/chat/android/ui/common/state/messages/list/DeletedMessageVisibility;
@@ -2463,6 +2464,7 @@ public final class io/getstream/chat/android/ui/common/state/messages/list/Messa
24632464
public final fun getShowOriginalText ()Z
24642465
public fun hashCode ()I
24652466
public final fun isInThread ()Z
2467+
public final fun isMessageDelivered ()Z
24662468
public final fun isMessageRead ()Z
24672469
public final fun isMine ()Z
24682470
public fun toString ()Ljava/lang/String;

0 commit comments

Comments
 (0)