Message Status Indicators

For messages sent by the current user, the status indicators represent the state of the message. It is shown below the message bubble, by default.

Status indicators states

The possible states of the status indicators are:

  • pending: The message is pending to be sent to the server.
  • sent: The message has been sent to the server successfully. It is shown as a gray checkmark.
  • delivered: The message has been delivered to at least one of the channel members devices. It is shown as double gray checkmark.
  • read: The message has been read by at least one of the channel members. It is shown as double blue checkmark.

The delivered state is only available since version 6.28.0 and it needs to be enabled in the Dashboard for each channel type.

PendingSentDeliveredReadRead by many
Message Status Pending
Message Status Sent
Message Status Delivered
Message Status Read
Message Status Read Group

If read_events are turned OFF for the channel, read indicators are hidden.

If delivery_events are turned OFF for the channel, delivery indicators are hidden.

Basic Customization

Hide status indicators

In case your app does not need to show the status indicators, you can hide them by overriding the MessageFooterStatusIndicator component factory method.

class CustomChatComponentFactory : ChatComponentFactory {

    @Composable
    override fun ChannelItemReadStatusIndicator(
        channel: Channel,
        message: Message,
        currentUser: User?,
        modifier: Modifier
    ) {
        // Do not show any status indicator
    }

    @Composable
    override fun MessageFooterStatusIndicator(
        params: MessageFooterStatusIndicatorParams,
    ) {
        // Do not show any status indicator
    }
}
BeforeAfter
Hide Message Status Before
Hide Message Status After

Advanced Customization

Show all read and delivered members

To show all the read and delivered members, you can create a custom screen that shows the members that have read the message and the ones that were delivered but not read yet.

For this, we can extend the message menu to add a message info option to show a screen with the read and delivered members.

class CustomChatComponentFactory : ChatComponentFactory {
    /**
     * Creates a message menu with option for message info.
     */
    @Composable
    override fun MessageMenu(
        modifier: Modifier,
        message: Message,
        messageOptions: List<MessageOptionItemState>,
        ownCapabilities: Set<String>,
        onMessageAction: (MessageAction) -> Unit,
        onShowMore: () -> Unit,
        onDismiss: () -> Unit,
    ) {
        var showMessageInfoDialog by remember { mutableStateOf(false) }

        val allOptions = listOf(
            MessageOptionItemState(
                title = R.string.message_option_message_info,
                titleColor = ChatTheme.colors.textHighEmphasis,
                iconPainter = rememberVectorPainter(Icons.Outlined.Info),
                iconColor = ChatTheme.colors.textLowEmphasis,
                action = CustomAction(message, mapOf("message_info" to true)),
            ),
        ) + messageOptions

        val extendedOnMessageAction: (MessageAction) -> Unit = { action ->
            when {
                action is CustomAction && action.extraProperties.contains("message_info") ->
                    showMessageInfoDialog = true

                else -> onMessageAction(action)
            }
        }

        var dismissed by remember { mutableStateOf(false) }

        if (showMessageInfoDialog) {
            ModalBottomSheet(
                onDismissRequest = {
                    showMessageInfoDialog = false
                    onDismiss()
                    dismissed = true // Mark as dismissed to avoid animating the menu again
                },
                containerColor = ChatTheme.colors.appBackground,
            ) {
                val coroutineScope = rememberCoroutineScope()
                val state by readsOf(message, coroutineScope).collectAsState(null)
                state?.let {
                    val (reads, deliveredReads) = it
                    MessageInfoContent(
                        reads = reads,
                        deliveredReads = deliveredReads,
                    )
                }
            }
        } else if (!dismissed) {
            super.MessageMenu(
                modifier = modifier,
                message = message,
                messageOptions = allOptions,
                ownCapabilities = ownCapabilities,
                onMessageAction = extendedOnMessageAction,
                onShowMore = onShowMore,
                onDismiss = onDismiss,
            )
        }
    }
}

You can check the full sample implementation here, which renders a bottom sheet showing the members who read the message, and the members who had the message delivered.

Message Delivery State Read and Delivered

At the moment, showing the timestamps of the read and delivered members is not recommended since the timestamp is always related to the latest message in the channel, and not for each message individually.

© Getstream.io, Inc. All Rights Reserved.