Skip to content

Commit 173df4b

Browse files
committed
Refactor DeliveryReceiptsManager to store receipts locally
The `DeliveryReceiptsManager` has been renamed to `MessageReceiptManager`. Instead of making a direct API call to mark messages as delivered, the manager now creates `MessageReceipt` objects and upserts them into the local `MessageReceiptRepository`. This change also refactors the corresponding tests to verify the local database interaction rather than the API call.
1 parent 106f9ba commit 173df4b

File tree

2 files changed

+73
-40
lines changed

2 files changed

+73
-40
lines changed
Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,32 @@
1616

1717
package io.getstream.chat.android.client.receipts
1818

19-
import io.getstream.chat.android.client.ChatClient
19+
import io.getstream.chat.android.client.persistance.repository.MessageReceiptRepository
2020
import io.getstream.chat.android.client.utils.message.isDeleted
2121
import io.getstream.chat.android.models.Message
22+
import io.getstream.chat.android.models.MessageReceipt
2223
import io.getstream.chat.android.models.MessageType
2324
import io.getstream.chat.android.models.User
2425
import io.getstream.chat.android.models.UserId
2526
import io.getstream.log.taggedLogger
27+
import kotlinx.coroutines.CoroutineScope
28+
import kotlinx.coroutines.launch
29+
import java.util.Date
2630

27-
internal class DeliveryReceiptsManager(
28-
private val chatClient: ChatClient,
31+
/**
32+
* Manages message delivery receipts: creating and storing them in the repository.
33+
*/
34+
internal class MessageReceiptManager(
35+
private val scope: CoroutineScope,
36+
private val now: () -> Date,
2937
private val getCurrentUser: () -> User?,
38+
private val messageReceiptRepository: MessageReceiptRepository,
3039
) {
3140

32-
private val logger by taggedLogger("MessageDeliveryReceiptsManager")
41+
private val logger by taggedLogger("MessageReceiptManager")
3342

3443
fun markMessagesAsDelivered(messages: List<Message>) {
35-
logger.d { "[markMessagesAsDelivered] Preparing to send delivery receipts for ${messages.size} messages" }
44+
logger.d { "[markMessagesAsDelivered] Preparing delivery receipts for ${messages.size} messages" }
3645

3746
val currentUser = requireNotNull(getCurrentUser()) {
3847
"Cannot send delivery receipts: current user is null"
@@ -44,6 +53,7 @@ internal class DeliveryReceiptsManager(
4453
return
4554
}
4655

56+
// Filter out messages that shouldn't have delivery receipts sent
4757
val filteredMessages = messages.filter { message ->
4858
shouldSendDeliveryReceipt(currentUserId = currentUser.id, message = message)
4959
}
@@ -59,9 +69,12 @@ internal class DeliveryReceiptsManager(
5969
return
6070
}
6171

62-
logger.d { "[markMessagesAsDelivered] Sending ${filteredMessages.size} delivery receipts" }
63-
chatClient.markMessagesAsDelivered(filteredMessages)
64-
.execute()
72+
scope.launch {
73+
val receipts = filteredMessages.map { message -> message.toDeliveryReceipt() }
74+
messageReceiptRepository.upsert(receipts)
75+
76+
logger.d { "[markMessagesAsDelivered] ${filteredMessages.size} delivery receipts upserted" }
77+
}
6578
}
6679

6780
private fun shouldSendDeliveryReceipt(currentUserId: UserId, message: Message): Boolean {
@@ -82,6 +95,13 @@ internal class DeliveryReceiptsManager(
8295

8396
return true
8497
}
98+
99+
private fun Message.toDeliveryReceipt() = MessageReceipt(
100+
messageId = id,
101+
type = MessageReceipt.TYPE_DELIVERY,
102+
createdAt = now(),
103+
cid = cid,
104+
)
85105
}
86106

87107
private fun User.isDeliveryReceiptsEnabled(): Boolean =
Lines changed: 45 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -18,43 +18,54 @@ package io.getstream.chat.android.client.receipts
1818

1919
import io.getstream.chat.android.DeliveryReceipts
2020
import io.getstream.chat.android.PrivacySettings
21-
import io.getstream.chat.android.client.ChatClient
21+
import io.getstream.chat.android.client.persistance.repository.MessageReceiptRepository
2222
import io.getstream.chat.android.models.Message
23+
import io.getstream.chat.android.models.MessageReceipt
2324
import io.getstream.chat.android.models.User
25+
import io.getstream.chat.android.randomDate
2426
import io.getstream.chat.android.randomMessage
2527
import io.getstream.chat.android.randomMessageList
2628
import io.getstream.chat.android.randomUser
27-
import io.getstream.chat.android.test.asCall
29+
import kotlinx.coroutines.CoroutineScope
30+
import kotlinx.coroutines.test.UnconfinedTestDispatcher
31+
import kotlinx.coroutines.test.runTest
2832
import org.junit.Test
2933
import org.junit.jupiter.api.assertThrows
3034
import org.mockito.kotlin.any
31-
import org.mockito.kotlin.doReturn
3235
import org.mockito.kotlin.mock
33-
import org.mockito.kotlin.verify
34-
import org.mockito.kotlin.verifyNoInteractions
36+
import org.mockito.kotlin.never
37+
import org.mockito.kotlin.verifyBlocking
3538
import java.util.Date
3639

37-
internal class DeliveryReceiptsManagerTest {
40+
internal class MessageReceiptManagerTest {
3841

3942
@Test
40-
fun `mark messages as delivered`() {
41-
val deliveredMessage = randomMessage()
43+
fun `store message delivery receipts success`() = runTest {
44+
val deliveredMessage = randomMessage(deletedAt = null, deletedForMe = false)
4245
val messages = listOf(
4346
deliveredMessage,
4447
randomMessage(user = CurrentUser),
4548
randomMessage(type = "system"),
46-
randomMessage(deletedAt = Date()),
49+
randomMessage(deletedAt = randomDate()),
4750
)
4851
val fixture = Fixture()
4952
val sut = fixture.get()
5053

5154
sut.markMessagesAsDelivered(messages)
5255

53-
fixture.verifyMarkMessagesAsDelivered(listOf(deliveredMessage))
56+
val receipts = listOf(
57+
MessageReceipt(
58+
messageId = deliveredMessage.id,
59+
type = MessageReceipt.TYPE_DELIVERY,
60+
createdAt = Now,
61+
cid = deliveredMessage.cid,
62+
),
63+
)
64+
fixture.verifyUpsertMessageReceiptsCalled(receipts)
5465
}
5566

5667
@Test
57-
fun `should not mark messages as delivered when current user is null`() {
68+
fun `should not store message delivery receipts when current user is null`() = runTest {
5869
val messages = randomMessageList(10) { randomMessage() }
5970
val fixture = Fixture().givenCurrentUser(user = null)
6071
val sut = fixture.get()
@@ -65,19 +76,19 @@ internal class DeliveryReceiptsManagerTest {
6576
}
6677

6778
@Test
68-
fun `should skip mark messages as delivered when current user privacy settings are undefined`() {
79+
fun `should skip storing message delivery receipts when current user privacy settings are undefined`() = runTest {
6980
val currentUser = randomUser(privacySettings = null)
7081
val messages = randomMessageList(10) { randomMessage() }
7182
val fixture = Fixture().givenCurrentUser(currentUser)
7283
val sut = fixture.get()
7384

7485
sut.markMessagesAsDelivered(messages)
7586

76-
fixture.verifyNoInteractions()
87+
fixture.verifyUpsertNotCalled()
7788
}
7889

7990
@Test
80-
fun `should skip mark messages as delivered when delivery receipts are disabled`() {
91+
fun `should skip storing message delivery receipts when delivery receipts are disabled`() = runTest {
8192
val currentUser = randomUser(
8293
privacySettings = PrivacySettings(
8394
deliveryReceipts = DeliveryReceipts(enabled = false),
@@ -89,78 +100,80 @@ internal class DeliveryReceiptsManagerTest {
89100

90101
sut.markMessagesAsDelivered(messages)
91102

92-
fixture.verifyNoInteractions()
103+
fixture.verifyUpsertNotCalled()
93104
}
94105

95106
@Test
96-
fun `should skip mark messages as delivered with empty list`() {
107+
fun `should skip storing message delivery receipts with empty list`() = runTest {
97108
val messages = emptyList<Message>()
98109
val fixture = Fixture()
99110
val sut = fixture.get()
100111

101112
sut.markMessagesAsDelivered(messages)
102113

103-
fixture.verifyNoInteractions()
114+
fixture.verifyUpsertNotCalled()
104115
}
105116

106117
@Test
107-
fun `should skip mark messages from the current user as delivered`() {
118+
fun `should skip storing message delivery receipts from the current user`() = runTest {
108119
val messages = randomMessageList(10) { randomMessage(user = CurrentUser) }
109120
val fixture = Fixture()
110121
val sut = fixture.get()
111122

112123
sut.markMessagesAsDelivered(messages)
113124

114-
fixture.verifyNoInteractions()
125+
fixture.verifyUpsertNotCalled()
115126
}
116127

117128
@Test
118-
fun `should skip mark system messages as delivered`() {
129+
fun `should skip storing message delivery receipts from system messages`() = runTest {
119130
val messages = randomMessageList(10) { randomMessage(type = "system") }
120131
val fixture = Fixture()
121132
val sut = fixture.get()
122133

123134
sut.markMessagesAsDelivered(messages)
124135

125-
fixture.verifyNoInteractions()
136+
fixture.verifyUpsertNotCalled()
126137
}
127138

128139
@Test
129-
fun `should skip mark deleted messages as delivered`() {
140+
fun `should skip storing message delivery receipts from deleted messages`() = runTest {
130141
val messages = randomMessageList(10) { randomMessage(deletedAt = Date()) }
131142
val fixture = Fixture()
132143
val sut = fixture.get()
133144

134145
sut.markMessagesAsDelivered(messages)
135146

136-
fixture.verifyNoInteractions()
147+
fixture.verifyUpsertNotCalled()
137148
}
138149

139150
private class Fixture {
140-
private val mockChatClient = mock<ChatClient> {
141-
on { markMessagesAsDelivered(any()) } doReturn Unit.asCall()
142-
}
151+
private val mockMessageReceiptRepository = mock<MessageReceiptRepository>()
143152
private var getCurrentUser: () -> User? = { CurrentUser }
144153

145154
fun givenCurrentUser(user: User?) = apply {
146155
getCurrentUser = { user }
147156
}
148157

149-
fun verifyNoInteractions() {
150-
verifyNoInteractions(mockChatClient)
158+
fun verifyUpsertMessageReceiptsCalled(receipts: List<MessageReceipt>) {
159+
verifyBlocking(mockMessageReceiptRepository) { upsert(receipts) }
151160
}
152161

153-
fun verifyMarkMessagesAsDelivered(messages: List<Message>) {
154-
verify(mockChatClient).markMessagesAsDelivered(messages)
162+
fun verifyUpsertNotCalled() {
163+
verifyBlocking(mockMessageReceiptRepository, never()) { upsert(any()) }
155164
}
156165

157-
fun get() = DeliveryReceiptsManager(
158-
chatClient = mockChatClient,
166+
fun get() = MessageReceiptManager(
167+
scope = CoroutineScope(UnconfinedTestDispatcher()),
168+
now = { Now },
159169
getCurrentUser = getCurrentUser,
170+
messageReceiptRepository = mockMessageReceiptRepository,
160171
)
161172
}
162173
}
163174

175+
private val Now = Date()
176+
164177
private val CurrentUser = randomUser(
165178
privacySettings = PrivacySettings(
166179
deliveryReceipts = DeliveryReceipts(enabled = true),

0 commit comments

Comments
 (0)