Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
66c9207
Add ChatClient.markMessagesAsDelivered function to mark messages as d…
andremion Oct 22, 2025
7a8cebd
Add delivery receipts support to user privacy settings
andremion Oct 23, 2025
3ff7e40
Increase max return count in detekt configuration
andremion Oct 23, 2025
289545d
Introduce a initial DeliveryReceiptsManager to handle message deliver…
andremion Oct 23, 2025
3325414
Introduce MessageReceiptDao and MessageReceiptEntity for handling mes…
andremion Oct 23, 2025
d671cfd
Introduce MessageReceipt model and repository for handling message de…
andremion Oct 23, 2025
3d76663
Make MessageReceiptRepository `getAllByType` return a Flow and add a …
andremion Oct 24, 2025
89336e6
Refactor DeliveryReceiptsManager to store receipts locally
andremion Oct 24, 2025
67b2ffe
Introduced `MessageReceiptReporter`, a new class responsible for obse…
andremion Oct 24, 2025
c0e62d6
Prapare to move internal persistence to the client module
andremion Oct 27, 2025
f2258d2
Add ChatClientDatabase, DateConverter, and ChatClientRepository for m…
andremion Oct 27, 2025
0e144f5
Refactor: Move message receipt logic to client
andremion Oct 27, 2025
af8db42
Support `message.delivered` event
andremion Oct 28, 2025
23ca9b0
Refactor: Add default empty implementations for `QueryChannelsListene…
andremion Oct 28, 2025
707afd2
Deprecate `hasUnread` in favor of `currentUserUnreadCount`
andremion Oct 28, 2025
65e0e64
Add `userRead` and `deliveredReads` helper functions
andremion Oct 28, 2025
d808a53
Introduce `markChannelsAsDelivered`, a new function to mark the last …
andremion Oct 28, 2025
06b56f0
Refactor MessageReceiptReporter to use a polling mechanism
andremion Oct 28, 2025
3ec5b3f
Moves the user ID update in the `switchUser` function to after the us…
andremion Oct 29, 2025
bf001e6
Rename MessageReceiptReporter.init to start and add logging
andremion Oct 29, 2025
ebe10e8
Refactor MessageReceiptManagerTest to standardize verification method…
andremion Oct 29, 2025
4907a8d
Automatically mark messages as delivered when querying channels
andremion Oct 29, 2025
b8788df
Introduce `ChatClientRepository` to encapsulate internal repositories…
andremion Oct 29, 2025
6f48e0a
Add MessageReceiptManager and MessageReceiptReporter to ChatClient
andremion Oct 29, 2025
3e3e995
Decoupled `MessageReceiptReporter` from `ChatClient` by passing `Chat…
andremion Oct 29, 2025
000e2bd
Refactor ChatClientTest to simplify test setup
andremion Oct 29, 2025
b790d46
Rename deliveredReads to deliveredReadsOf
andremion Oct 29, 2025
9b5a1c4
feat: Add delivered status indicator for messages
andremion Oct 29, 2025
576b335
Fix flaky test
andremion Oct 29, 2025
be6b153
Add more tests
andremion Oct 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions config/detekt/detekt.yml
Original file line number Diff line number Diff line change
Expand Up @@ -731,8 +731,8 @@ style:
active: false
ReturnCount:
active: true
max: 2
excludedFunctions: 'equals'
max: 10
excludedFunctions: ['equals']
excludeLabeled: false
excludeReturnFromLambda: true
excludeGuardClauses: false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import io.getstream.chat.android.client.events.ConnectedEvent
import io.getstream.chat.android.client.events.MarkAllReadEvent
import io.getstream.chat.android.client.events.MemberAddedEvent
import io.getstream.chat.android.client.events.MemberRemovedEvent
import io.getstream.chat.android.client.events.MessageDeliveredEvent
import io.getstream.chat.android.client.events.MessageReadEvent
import io.getstream.chat.android.client.events.MessageUpdatedEvent
import io.getstream.chat.android.client.events.NewMessageEvent
Expand Down Expand Up @@ -260,6 +261,26 @@ public fun randomMessageReadEvent(
)
}

public fun randomMessageDeliveredEvent(
createdAt: Date = Date(),
user: User = randomUser(),
cid: String = randomCID(),
channelType: String = randomString(),
channelId: String = randomString(),
lastDeliveredAt: Date = randomDate(),
lastDeliveredMessageId: String = randomString(),
) = MessageDeliveredEvent(
type = EventType.MESSAGE_DELIVERED,
createdAt = createdAt,
rawCreatedAt = streamFormatter.format(createdAt),
user = user,
cid = cid,
channelType = channelType,
channelId = channelId,
lastDeliveredAt = lastDeliveredAt,
lastDeliveredMessageId = lastDeliveredMessageId,
)

public fun randomNotificationMarkReadEvent(
createdAt: Date = Date(),
user: User = randomUser(),
Expand Down
63 changes: 54 additions & 9 deletions stream-chat-android-client/api/stream-chat-android-client.api
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ public final class io/getstream/chat/android/client/ChatClient {
public static synthetic fun keystroke$default (Lio/getstream/chat/android/client/ChatClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lio/getstream/result/call/Call;
public final fun markAllRead ()Lio/getstream/result/call/Call;
public final fun markMessageRead (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lio/getstream/result/call/Call;
public final fun markMessagesAsDelivered (Ljava/util/List;)Lio/getstream/result/call/Call;
public final fun markRead (Ljava/lang/String;Ljava/lang/String;)Lio/getstream/result/call/Call;
public final fun markThreadRead (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lio/getstream/result/call/Call;
public final fun markThreadUnread (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lio/getstream/result/call/Call;
Expand Down Expand Up @@ -1619,6 +1620,33 @@ public final class io/getstream/chat/android/client/events/MessageDeletedEvent :
public fun toString ()Ljava/lang/String;
}

public final class io/getstream/chat/android/client/events/MessageDeliveredEvent : io/getstream/chat/android/client/events/CidEvent, io/getstream/chat/android/client/events/UserEvent {
public fun <init> (Ljava/lang/String;Ljava/util/Date;Ljava/lang/String;Lio/getstream/chat/android/models/User;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Date;Ljava/lang/String;)V
public final fun component1 ()Ljava/lang/String;
public final fun component2 ()Ljava/util/Date;
public final fun component3 ()Ljava/lang/String;
public final fun component4 ()Lio/getstream/chat/android/models/User;
public final fun component5 ()Ljava/lang/String;
public final fun component6 ()Ljava/lang/String;
public final fun component7 ()Ljava/lang/String;
public final fun component8 ()Ljava/util/Date;
public final fun component9 ()Ljava/lang/String;
public final fun copy (Ljava/lang/String;Ljava/util/Date;Ljava/lang/String;Lio/getstream/chat/android/models/User;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Date;Ljava/lang/String;)Lio/getstream/chat/android/client/events/MessageDeliveredEvent;
public static synthetic fun copy$default (Lio/getstream/chat/android/client/events/MessageDeliveredEvent;Ljava/lang/String;Ljava/util/Date;Ljava/lang/String;Lio/getstream/chat/android/models/User;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Date;Ljava/lang/String;ILjava/lang/Object;)Lio/getstream/chat/android/client/events/MessageDeliveredEvent;
public fun equals (Ljava/lang/Object;)Z
public fun getChannelId ()Ljava/lang/String;
public fun getChannelType ()Ljava/lang/String;
public fun getCid ()Ljava/lang/String;
public fun getCreatedAt ()Ljava/util/Date;
public final fun getLastDeliveredAt ()Ljava/util/Date;
public final fun getLastDeliveredMessageId ()Ljava/lang/String;
public fun getRawCreatedAt ()Ljava/lang/String;
public fun getType ()Ljava/lang/String;
public fun getUser ()Lio/getstream/chat/android/models/User;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final class io/getstream/chat/android/client/events/MessageReadEvent : io/getstream/chat/android/client/events/CidEvent, io/getstream/chat/android/client/events/UserEvent {
public fun <init> (Ljava/lang/String;Ljava/util/Date;Ljava/lang/String;Lio/getstream/chat/android/models/User;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/getstream/chat/android/models/ThreadInfo;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/util/Date;Ljava/lang/String;Lio/getstream/chat/android/models/User;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/getstream/chat/android/models/ThreadInfo;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
Expand Down Expand Up @@ -2631,12 +2659,14 @@ public final class io/getstream/chat/android/client/extensions/ChannelExtensionK
public static final fun countUnreadMentionsForUser (Lio/getstream/chat/android/models/Channel;Lio/getstream/chat/android/models/User;)I
public static final fun currentUserUnreadCount (Lio/getstream/chat/android/models/Channel;Ljava/lang/String;)I
public static synthetic fun currentUserUnreadCount$default (Lio/getstream/chat/android/models/Channel;Ljava/lang/String;ILjava/lang/Object;)I
public static final fun deliveredReadsOf (Lio/getstream/chat/android/models/Channel;Lio/getstream/chat/android/models/Message;)Ljava/util/List;
public static final fun isAnonymousChannel (Lio/getstream/chat/android/models/Channel;)Z
public static final fun isArchive (Lio/getstream/chat/android/models/Channel;)Z
public static final fun isMutedFor (Lio/getstream/chat/android/models/Channel;Lio/getstream/chat/android/models/User;)Z
public static final fun isPinned (Lio/getstream/chat/android/models/Channel;)Z
public static final fun syncUnreadCountWithReads (Lio/getstream/chat/android/models/Channel;Ljava/lang/String;)Lio/getstream/chat/android/models/Channel;
public static synthetic fun syncUnreadCountWithReads$default (Lio/getstream/chat/android/models/Channel;Ljava/lang/String;ILjava/lang/Object;)Lio/getstream/chat/android/models/Channel;
public static final fun userRead (Lio/getstream/chat/android/models/Channel;Ljava/lang/String;)Lio/getstream/chat/android/models/ChannelUserRead;
}

public final class io/getstream/chat/android/client/extensions/FlowExtensions {
Expand Down Expand Up @@ -2982,6 +3012,24 @@ public abstract interface class io/getstream/chat/android/client/persistance/rep
public abstract fun createRepositoryFactory (Lio/getstream/chat/android/models/User;)Lio/getstream/chat/android/client/persistance/repository/factory/RepositoryFactory;
}

public final class io/getstream/chat/android/client/persistence/db/ChatClientDatabase_Impl {
public static final field Companion Lio/getstream/chat/android/client/persistence/db/ChatClientDatabase$Companion;
public fun <init> ()V
public fun clearAllTables ()V
public fun getAutoMigrations (Ljava/util/Map;)Ljava/util/List;
public fun getRequiredAutoMigrationSpecs ()Ljava/util/Set;
public fun messageReceiptDao ()Lio/getstream/chat/android/client/persistence/db/dao/MessageReceiptDao;
}

public final class io/getstream/chat/android/client/persistence/db/dao/MessageReceiptDao_Impl : io/getstream/chat/android/client/persistence/db/dao/MessageReceiptDao {
public fun <init> (Landroidx/room/RoomDatabase;)V
public fun deleteAll (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun deleteByMessageIds (Ljava/util/List;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static fun getRequiredConverters ()Ljava/util/List;
public fun selectAllByType (Ljava/lang/String;ILkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun upsert (Ljava/util/List;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public abstract interface class io/getstream/chat/android/client/plugin/Plugin : io/getstream/chat/android/client/plugin/DependencyResolver, io/getstream/chat/android/client/plugin/listeners/BlockUserListener, io/getstream/chat/android/client/plugin/listeners/ChannelMarkReadListener, io/getstream/chat/android/client/plugin/listeners/CreateChannelListener, io/getstream/chat/android/client/plugin/listeners/DeleteChannelListener, io/getstream/chat/android/client/plugin/listeners/DeleteMessageForMeListener, io/getstream/chat/android/client/plugin/listeners/DeleteMessageListener, io/getstream/chat/android/client/plugin/listeners/DeleteReactionListener, io/getstream/chat/android/client/plugin/listeners/DraftMessageListener, io/getstream/chat/android/client/plugin/listeners/EditMessageListener, io/getstream/chat/android/client/plugin/listeners/FetchCurrentUserListener, io/getstream/chat/android/client/plugin/listeners/GetMessageListener, io/getstream/chat/android/client/plugin/listeners/HideChannelListener, io/getstream/chat/android/client/plugin/listeners/LiveLocationListener, io/getstream/chat/android/client/plugin/listeners/MarkAllReadListener, io/getstream/chat/android/client/plugin/listeners/PushPreferencesListener, io/getstream/chat/android/client/plugin/listeners/QueryBlockedUsersListener, io/getstream/chat/android/client/plugin/listeners/QueryChannelListener, io/getstream/chat/android/client/plugin/listeners/QueryChannelsListener, io/getstream/chat/android/client/plugin/listeners/QueryMembersListener, io/getstream/chat/android/client/plugin/listeners/QueryThreadsListener, io/getstream/chat/android/client/plugin/listeners/SendAttachmentListener, io/getstream/chat/android/client/plugin/listeners/SendGiphyListener, io/getstream/chat/android/client/plugin/listeners/SendMessageListener, io/getstream/chat/android/client/plugin/listeners/SendReactionListener, io/getstream/chat/android/client/plugin/listeners/ShuffleGiphyListener, io/getstream/chat/android/client/plugin/listeners/ThreadQueryListener, io/getstream/chat/android/client/plugin/listeners/TypingEventListener, io/getstream/chat/android/client/plugin/listeners/UnblockUserListener {
public fun getErrorHandler ()Lio/getstream/chat/android/client/errorhandler/ErrorHandler;
public fun onAttachmentSendRequest (Ljava/lang/String;Ljava/lang/String;Lio/getstream/chat/android/models/Message;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
Expand Down Expand Up @@ -3061,12 +3109,6 @@ public abstract interface class io/getstream/chat/android/client/plugin/Plugin :
public static synthetic fun onQueryChannelRequest$suspendImpl (Lio/getstream/chat/android/client/plugin/Plugin;Ljava/lang/String;Ljava/lang/String;Lio/getstream/chat/android/client/api/models/QueryChannelRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun onQueryChannelResult (Lio/getstream/result/Result;Ljava/lang/String;Ljava/lang/String;Lio/getstream/chat/android/client/api/models/QueryChannelRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun onQueryChannelResult$suspendImpl (Lio/getstream/chat/android/client/plugin/Plugin;Lio/getstream/result/Result;Ljava/lang/String;Ljava/lang/String;Lio/getstream/chat/android/client/api/models/QueryChannelRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun onQueryChannelsPrecondition (Lio/getstream/chat/android/client/api/models/QueryChannelsRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun onQueryChannelsPrecondition$suspendImpl (Lio/getstream/chat/android/client/plugin/Plugin;Lio/getstream/chat/android/client/api/models/QueryChannelsRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun onQueryChannelsRequest (Lio/getstream/chat/android/client/api/models/QueryChannelsRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun onQueryChannelsRequest$suspendImpl (Lio/getstream/chat/android/client/plugin/Plugin;Lio/getstream/chat/android/client/api/models/QueryChannelsRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun onQueryChannelsResult (Lio/getstream/result/Result;Lio/getstream/chat/android/client/api/models/QueryChannelsRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun onQueryChannelsResult$suspendImpl (Lio/getstream/chat/android/client/plugin/Plugin;Lio/getstream/result/Result;Lio/getstream/chat/android/client/api/models/QueryChannelsRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun onQueryDraftMessagesResult (Lio/getstream/result/Result;Lio/getstream/chat/android/models/FilterObject;ILjava/lang/String;Lio/getstream/chat/android/models/querysort/QuerySorter;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun onQueryDraftMessagesResult (Lio/getstream/result/Result;Ljava/lang/Integer;Ljava/lang/Integer;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun onQueryDraftMessagesResult$suspendImpl (Lio/getstream/chat/android/client/plugin/Plugin;Lio/getstream/result/Result;Lio/getstream/chat/android/models/FilterObject;ILjava/lang/String;Lio/getstream/chat/android/models/querysort/QuerySorter;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
Expand Down Expand Up @@ -3203,9 +3245,12 @@ public abstract interface class io/getstream/chat/android/client/plugin/listener
}

public abstract interface class io/getstream/chat/android/client/plugin/listeners/QueryChannelsListener {
public abstract fun onQueryChannelsPrecondition (Lio/getstream/chat/android/client/api/models/QueryChannelsRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun onQueryChannelsRequest (Lio/getstream/chat/android/client/api/models/QueryChannelsRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun onQueryChannelsResult (Lio/getstream/result/Result;Lio/getstream/chat/android/client/api/models/QueryChannelsRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun onQueryChannelsPrecondition (Lio/getstream/chat/android/client/api/models/QueryChannelsRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun onQueryChannelsPrecondition$suspendImpl (Lio/getstream/chat/android/client/plugin/listeners/QueryChannelsListener;Lio/getstream/chat/android/client/api/models/QueryChannelsRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun onQueryChannelsRequest (Lio/getstream/chat/android/client/api/models/QueryChannelsRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun onQueryChannelsRequest$suspendImpl (Lio/getstream/chat/android/client/plugin/listeners/QueryChannelsListener;Lio/getstream/chat/android/client/api/models/QueryChannelsRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun onQueryChannelsResult (Lio/getstream/result/Result;Lio/getstream/chat/android/client/api/models/QueryChannelsRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun onQueryChannelsResult$suspendImpl (Lio/getstream/chat/android/client/plugin/listeners/QueryChannelsListener;Lio/getstream/result/Result;Lio/getstream/chat/android/client/api/models/QueryChannelsRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public abstract interface class io/getstream/chat/android/client/plugin/listeners/QueryMembersListener {
Expand Down
5 changes: 5 additions & 0 deletions stream-chat-android-client/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ dependencies {
implementation(libs.okhttp.logging.interceptor)
implementation(libs.ok2curl)

implementation(libs.androidx.room.runtime)
implementation(libs.androidx.room.ktx)
ksp(libs.androidx.room.compiler)

// Unused dependencies: The following dependencies (appcompat, constraintlayout, livedata-ktx) are not used in the
// `stream-chat-android-client` module. They are still declared here to prevent potential breaking changes for
// integrations that might be relying on them transitively. Consider removing them in future major releases.
Expand All @@ -114,6 +118,7 @@ dependencies {
testImplementation(libs.androidx.lifecycle.runtime.testing)
testImplementation(libs.junit.jupiter.api)
testImplementation(libs.junit.jupiter.params)
testImplementation(libs.turbine)
testRuntimeOnly(libs.junit.jupiter.engine)
testRuntimeOnly(libs.junit.vintage.engine)

Expand Down
Loading
Loading