Skip to content

[AND-406] Draft Messages on UI Components #5687

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Apr 1, 2025
Merged
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
- Enable pagination in `MentionListView`. [#5692](https://github.com/GetStream/stream-chat-android/pull/5692)

### ✅ Added
- Add `ChatUI.draftMessagesEnabled` property to enable/disable Draft Messages. [#5687](https://github.com/GetStream/stream-chat-android/pull/5687)

### ⚠️ Changed
- 🚨Breaking change: Move `MentionListViewModel` logic and its state to a shared component so they can be reused in Compose. [#5692](https://github.com/GetStream/stream-chat-android/pull/5692)
Expand All @@ -82,6 +83,8 @@
### ⬆️ Improved

### ✅ Added
- Add `MessagesViewModelFactory.isComposerDraftMessageEnabled` property to enable/disable Draft Messages within `MessageComposer`. [#5687](https://github.com/GetStream/stream-chat-android/pull/5687)
- Add `ChannelViewModelFactory.isDraftMessageEnabled` property to enable/disable Draft Messages within `ChannelList`. [#5687](https://github.com/GetStream/stream-chat-android/pull/5687)

### ⚠️ Changed
- `defaultMessageOptionsState()` now accepts an `isInThread` flag to show/hide the "Thread reply" option. [#5683](https://github.com/GetStream/stream-chat-android/pull/5683)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1916,6 +1916,16 @@ internal constructor(
}
}

/**
* Create a new draft message.
* The call will be retried accordingly to [retryPolicy].
*
* @param channelType The channel type. ie messaging.
* @param channelId The channel id. ie 123.
* @param message The draft message to create.
*
* @return Executable async [Call] responsible for creating a draft message.
*/
@CheckResult
public fun createDraftMessage(
channelType: String,
Expand All @@ -1935,6 +1945,16 @@ internal constructor(
}
}

/**
* Delete a draft message.
* The call will be retried accordingly to [retryPolicy].
*
* @param channelType The channel type. ie messaging.
* @param channelId The channel id. ie 123.
* @param message The draft message to delete.
*
* @return Executable async [Call] responsible for deleting a draft message.
*/
@CheckResult
public fun deleteDraftMessages(
channelType: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ internal class DomainMapping(
attachments = message.attachments?.map { it.toDomain() } ?: emptyList(),
cid = channel_cid,
id = message.id,
parentId = parent_message?.id,
parentId = parent_message?.id ?: parent_id,
replyMessage = quoted_message?.toDomain(),
showInChannel = message.show_in_channel,
mentionedUsersIds = message.mentioned_users?.map { it.id } ?: emptyList(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ internal data class DownstreamDraftDto(
val message: DownstreamDraftMessageDto,
val channel_cid: String,
val quoted_message: DownstreamMessageDto? = null,
val parent_id: String? = null,
val parent_message: DownstreamMessageDto? = null,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ class ChannelsActivity : BaseConnectedActivity() {
Filters.or(Filters.notExists(CHANNEL_ARG_DRAFT), Filters.eq(CHANNEL_ARG_DRAFT, false)),
),
chatEventHandlerFactory = CustomChatEventHandlerFactory(),
isDraftMessageEnabled = true,
)
}
private val threadsViewModelFactory by lazy { ThreadsViewModelFactory() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ class MessagesActivity : BaseConnectedActivity() {
deletedMessageVisibility = DeletedMessageVisibility.ALWAYS_VISIBLE,
messageId = intent.getStringExtra(KEY_MESSAGE_ID),
parentMessageId = intent.getStringExtra(KEY_PARENT_MESSAGE_ID),
isComposerDraftMessageEnabled = true,
)
}

Expand Down
25 changes: 14 additions & 11 deletions stream-chat-android-compose/api/stream-chat-android-compose.api
Original file line number Diff line number Diff line change
Expand Up @@ -86,15 +86,17 @@ public abstract class io/getstream/chat/android/compose/state/channels/list/Item

public final class io/getstream/chat/android/compose/state/channels/list/ItemState$ChannelItemState : io/getstream/chat/android/compose/state/channels/list/ItemState {
public static final field $stable I
public fun <init> (Lio/getstream/chat/android/models/Channel;ZLjava/util/List;)V
public synthetic fun <init> (Lio/getstream/chat/android/models/Channel;ZLjava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Lio/getstream/chat/android/models/Channel;ZLjava/util/List;Lio/getstream/chat/android/models/DraftMessage;)V
public synthetic fun <init> (Lio/getstream/chat/android/models/Channel;ZLjava/util/List;Lio/getstream/chat/android/models/DraftMessage;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Lio/getstream/chat/android/models/Channel;
public final fun component2 ()Z
public final fun component3 ()Ljava/util/List;
public final fun copy (Lio/getstream/chat/android/models/Channel;ZLjava/util/List;)Lio/getstream/chat/android/compose/state/channels/list/ItemState$ChannelItemState;
public static synthetic fun copy$default (Lio/getstream/chat/android/compose/state/channels/list/ItemState$ChannelItemState;Lio/getstream/chat/android/models/Channel;ZLjava/util/List;ILjava/lang/Object;)Lio/getstream/chat/android/compose/state/channels/list/ItemState$ChannelItemState;
public final fun component4 ()Lio/getstream/chat/android/models/DraftMessage;
public final fun copy (Lio/getstream/chat/android/models/Channel;ZLjava/util/List;Lio/getstream/chat/android/models/DraftMessage;)Lio/getstream/chat/android/compose/state/channels/list/ItemState$ChannelItemState;
public static synthetic fun copy$default (Lio/getstream/chat/android/compose/state/channels/list/ItemState$ChannelItemState;Lio/getstream/chat/android/models/Channel;ZLjava/util/List;Lio/getstream/chat/android/models/DraftMessage;ILjava/lang/Object;)Lio/getstream/chat/android/compose/state/channels/list/ItemState$ChannelItemState;
public fun equals (Ljava/lang/Object;)Z
public final fun getChannel ()Lio/getstream/chat/android/models/Channel;
public final fun getDraftMessage ()Lio/getstream/chat/android/models/DraftMessage;
public fun getKey ()Ljava/lang/String;
public final fun getTypingUsers ()Ljava/util/List;
public fun hashCode ()I
Expand Down Expand Up @@ -3715,12 +3717,13 @@ public final class io/getstream/chat/android/compose/ui/util/MessageListUtilsKt

public abstract interface class io/getstream/chat/android/compose/ui/util/MessagePreviewFormatter {
public static final field Companion Lio/getstream/chat/android/compose/ui/util/MessagePreviewFormatter$Companion;
public abstract fun formatDraftMessagePreview (Lio/getstream/chat/android/models/DraftMessage;)Landroidx/compose/ui/text/AnnotatedString;
public abstract fun formatMessagePreview (Lio/getstream/chat/android/models/Message;Lio/getstream/chat/android/models/User;)Landroidx/compose/ui/text/AnnotatedString;
public abstract fun formatMessageTitle (Lio/getstream/chat/android/models/Message;)Landroidx/compose/ui/text/AnnotatedString;
}

public final class io/getstream/chat/android/compose/ui/util/MessagePreviewFormatter$Companion {
public final fun defaultFormatter (Landroid/content/Context;ZLio/getstream/chat/android/compose/ui/theme/StreamTypography;Ljava/util/List;)Lio/getstream/chat/android/compose/ui/util/MessagePreviewFormatter;
public final fun defaultFormatter (Landroid/content/Context;ZLio/getstream/chat/android/compose/ui/theme/StreamTypography;Ljava/util/List;Lio/getstream/chat/android/compose/ui/theme/StreamColors;)Lio/getstream/chat/android/compose/ui/util/MessagePreviewFormatter;
}

public abstract interface class io/getstream/chat/android/compose/ui/util/MessagePreviewIconFactory {
Expand Down Expand Up @@ -3911,8 +3914,8 @@ public final class io/getstream/chat/android/compose/util/KeyValuePair {

public final class io/getstream/chat/android/compose/viewmodel/channels/ChannelListViewModel : androidx/lifecycle/ViewModel {
public static final field $stable I
public fun <init> (Lio/getstream/chat/android/client/ChatClient;Lio/getstream/chat/android/models/querysort/QuerySorter;Lio/getstream/chat/android/models/FilterObject;IIILio/getstream/chat/android/state/event/handler/chat/factory/ChatEventHandlerFactory;J)V
public synthetic fun <init> (Lio/getstream/chat/android/client/ChatClient;Lio/getstream/chat/android/models/querysort/QuerySorter;Lio/getstream/chat/android/models/FilterObject;IIILio/getstream/chat/android/state/event/handler/chat/factory/ChatEventHandlerFactory;JILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Lio/getstream/chat/android/client/ChatClient;Lio/getstream/chat/android/models/querysort/QuerySorter;Lio/getstream/chat/android/models/FilterObject;IIILio/getstream/chat/android/state/event/handler/chat/factory/ChatEventHandlerFactory;JZ)V
public synthetic fun <init> (Lio/getstream/chat/android/client/ChatClient;Lio/getstream/chat/android/models/querysort/QuerySorter;Lio/getstream/chat/android/models/FilterObject;IIILio/getstream/chat/android/state/event/handler/chat/factory/ChatEventHandlerFactory;JZILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun archiveChannel (Lio/getstream/chat/android/models/Channel;)V
public final fun deleteConversation (Lio/getstream/chat/android/models/Channel;)V
public final fun dismissChannelAction ()V
Expand Down Expand Up @@ -3944,8 +3947,8 @@ public final class io/getstream/chat/android/compose/viewmodel/channels/ChannelL
public final class io/getstream/chat/android/compose/viewmodel/channels/ChannelViewModelFactory : androidx/lifecycle/ViewModelProvider$Factory {
public static final field $stable I
public fun <init> ()V
public fun <init> (Lio/getstream/chat/android/client/ChatClient;Lio/getstream/chat/android/models/querysort/QuerySorter;Lio/getstream/chat/android/models/FilterObject;IIILio/getstream/chat/android/state/event/handler/chat/factory/ChatEventHandlerFactory;)V
public synthetic fun <init> (Lio/getstream/chat/android/client/ChatClient;Lio/getstream/chat/android/models/querysort/QuerySorter;Lio/getstream/chat/android/models/FilterObject;IIILio/getstream/chat/android/state/event/handler/chat/factory/ChatEventHandlerFactory;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Lio/getstream/chat/android/client/ChatClient;Lio/getstream/chat/android/models/querysort/QuerySorter;Lio/getstream/chat/android/models/FilterObject;IIILio/getstream/chat/android/state/event/handler/chat/factory/ChatEventHandlerFactory;Z)V
public synthetic fun <init> (Lio/getstream/chat/android/client/ChatClient;Lio/getstream/chat/android/models/querysort/QuerySorter;Lio/getstream/chat/android/models/FilterObject;IIILio/getstream/chat/android/state/event/handler/chat/factory/ChatEventHandlerFactory;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun create (Ljava/lang/Class;)Landroidx/lifecycle/ViewModel;
}

Expand Down Expand Up @@ -4134,8 +4137,8 @@ public final class io/getstream/chat/android/compose/viewmodel/messages/MessageL

public final class io/getstream/chat/android/compose/viewmodel/messages/MessagesViewModelFactory : androidx/lifecycle/ViewModelProvider$Factory {
public static final field $stable I
public fun <init> (Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLio/getstream/chat/android/client/ChatClient;Lio/getstream/chat/android/client/setup/state/ClientState;Lio/getstream/sdk/chat/audio/recording/StreamMediaRecorder;Lio/getstream/chat/android/ui/common/feature/messages/composer/mention/UserLookupHandler;Lkotlin/jvm/functions/Function1;ILio/getstream/chat/android/ui/common/helper/ClipboardHandler;ZIZLio/getstream/chat/android/ui/common/state/messages/list/DeletedMessageVisibility;Lio/getstream/chat/android/ui/common/state/messages/list/MessageFooterVisibility;Lio/getstream/chat/android/ui/common/feature/messages/list/DateSeparatorHandler;Lio/getstream/chat/android/ui/common/feature/messages/list/DateSeparatorHandler;Lio/getstream/chat/android/ui/common/feature/messages/list/MessagePositionHandler;ZZZZ)V
public synthetic fun <init> (Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLio/getstream/chat/android/client/ChatClient;Lio/getstream/chat/android/client/setup/state/ClientState;Lio/getstream/sdk/chat/audio/recording/StreamMediaRecorder;Lio/getstream/chat/android/ui/common/feature/messages/composer/mention/UserLookupHandler;Lkotlin/jvm/functions/Function1;ILio/getstream/chat/android/ui/common/helper/ClipboardHandler;ZIZLio/getstream/chat/android/ui/common/state/messages/list/DeletedMessageVisibility;Lio/getstream/chat/android/ui/common/state/messages/list/MessageFooterVisibility;Lio/getstream/chat/android/ui/common/feature/messages/list/DateSeparatorHandler;Lio/getstream/chat/android/ui/common/feature/messages/list/DateSeparatorHandler;Lio/getstream/chat/android/ui/common/feature/messages/list/MessagePositionHandler;ZZZZILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLio/getstream/chat/android/client/ChatClient;Lio/getstream/chat/android/client/setup/state/ClientState;Lio/getstream/sdk/chat/audio/recording/StreamMediaRecorder;Lio/getstream/chat/android/ui/common/feature/messages/composer/mention/UserLookupHandler;Lkotlin/jvm/functions/Function1;ILio/getstream/chat/android/ui/common/helper/ClipboardHandler;ZIZLio/getstream/chat/android/ui/common/state/messages/list/DeletedMessageVisibility;Lio/getstream/chat/android/ui/common/state/messages/list/MessageFooterVisibility;Lio/getstream/chat/android/ui/common/feature/messages/list/DateSeparatorHandler;Lio/getstream/chat/android/ui/common/feature/messages/list/DateSeparatorHandler;Lio/getstream/chat/android/ui/common/feature/messages/list/MessagePositionHandler;ZZZZZ)V
public synthetic fun <init> (Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLio/getstream/chat/android/client/ChatClient;Lio/getstream/chat/android/client/setup/state/ClientState;Lio/getstream/sdk/chat/audio/recording/StreamMediaRecorder;Lio/getstream/chat/android/ui/common/feature/messages/composer/mention/UserLookupHandler;Lkotlin/jvm/functions/Function1;ILio/getstream/chat/android/ui/common/helper/ClipboardHandler;ZIZLio/getstream/chat/android/ui/common/state/messages/list/DeletedMessageVisibility;Lio/getstream/chat/android/ui/common/state/messages/list/MessageFooterVisibility;Lio/getstream/chat/android/ui/common/feature/messages/list/DateSeparatorHandler;Lio/getstream/chat/android/ui/common/feature/messages/list/DateSeparatorHandler;Lio/getstream/chat/android/ui/common/feature/messages/list/MessagePositionHandler;ZZZZZILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun create (Ljava/lang/Class;)Landroidx/lifecycle/ViewModel;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package io.getstream.chat.android.compose.state.channels.list

import io.getstream.chat.android.models.Channel
import io.getstream.chat.android.models.DraftMessage
import io.getstream.chat.android.models.Message
import io.getstream.chat.android.models.User

Expand All @@ -32,11 +33,13 @@ public sealed class ItemState {
* @param channel The channel to show.
* @param isMuted If the channel is muted for the current user.
* @param typingUsers The list of users currently typing in the channel.
* @param draftMessage The draft message for the current user in the channel.
*/
public data class ChannelItemState(
val channel: Channel,
val isMuted: Boolean = false,
val typingUsers: List<User> = emptyList(),
val draftMessage: DraftMessage? = null,
) : ItemState() {
override val key: String = channel.cid
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import io.getstream.chat.android.compose.ui.theme.ChatPreviewTheme
import io.getstream.chat.android.compose.ui.theme.ChatTheme
import io.getstream.chat.android.compose.ui.util.getLastMessage
import io.getstream.chat.android.models.Channel
import io.getstream.chat.android.models.DraftMessage
import io.getstream.chat.android.models.User
import io.getstream.chat.android.previewdata.PreviewChannelData
import io.getstream.chat.android.previewdata.PreviewUserData
Expand Down Expand Up @@ -216,9 +217,13 @@ internal fun RowScope.DefaultChannelItemCenterContent(
if (channelItemState.typingUsers.isNotEmpty()) {
UserTypingIndicator(channelItemState.typingUsers)
} else {
val lastMessageText = channelItemState.channel.getLastMessage(currentUser)?.let { lastMessage ->
ChatTheme.messagePreviewFormatter.formatMessagePreview(lastMessage, currentUser)
} ?: AnnotatedString("")
val lastMessageText =
channelItemState.draftMessage
?.let { ChatTheme.messagePreviewFormatter.formatDraftMessagePreview(it) }
?: channelItemState.channel.getLastMessage(currentUser)?.let { lastMessage ->
ChatTheme.messagePreviewFormatter.formatMessagePreview(lastMessage, currentUser)
}
?: AnnotatedString("")

if (lastMessageText.isNotEmpty()) {
Text(
Expand Down Expand Up @@ -377,13 +382,15 @@ private fun ChannelItemPreview(
channel: Channel,
isMuted: Boolean = false,
currentUser: User? = null,
draftMessage: DraftMessage? = null,
) {
ChatPreviewTheme {
ChannelItem(
channelItem = ItemState.ChannelItemState(
channel = channel,
isMuted = isMuted,
typingUsers = emptyList(),
draftMessage = draftMessage,
),
currentUser = currentUser,
onChannelClick = {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import io.getstream.chat.android.models.Message
import io.getstream.chat.android.models.User
import io.getstream.chat.android.models.querysort.QuerySortByField
import io.getstream.chat.android.previewdata.PreviewChannelData
import io.getstream.chat.android.previewdata.PreviewMessageData
import io.getstream.chat.android.previewdata.PreviewUserData

/**
Expand Down Expand Up @@ -379,22 +380,32 @@ private fun ChannelListForContentStatePreview() {
ItemState.ChannelItemState(
channel = PreviewChannelData.channelWithImage,
typingUsers = emptyList(),
draftMessage = null,
),
ItemState.ChannelItemState(
channel = PreviewChannelData.channelWithMessages,
typingUsers = emptyList(),
draftMessage = null,
),
ItemState.ChannelItemState(
channel = PreviewChannelData.channelWithFewMembers,
typingUsers = emptyList(),
draftMessage = null,
),
ItemState.ChannelItemState(
channel = PreviewChannelData.channelWithManyMembers,
typingUsers = emptyList(),
draftMessage = null,
),
ItemState.ChannelItemState(
channel = PreviewChannelData.channelWithOnlineUser,
typingUsers = emptyList(),
draftMessage = null,
),
ItemState.ChannelItemState(
channel = PreviewChannelData.channelWithOnlineUser,
typingUsers = emptyList(),
draftMessage = PreviewMessageData.draftMessage,
),
),
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,7 @@ public fun ChatTheme(
typography = typography,
attachmentFactories = attachmentFactories,
autoTranslationEnabled = autoTranslationEnabled,
colors = colors,
),
searchResultNameFormatter: SearchResultNameFormatter = SearchResultNameFormatter.defaultFormatter(),
imageLoaderFactory: StreamCoilImageLoaderFactory = StreamCoilImageLoaderFactory.defaultFactory(),
Expand Down
Loading
Loading