@@ -45,6 +45,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.ReactionSende
4545import io.element.android.libraries.matrix.api.timeline.item.event.Receipt
4646import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem
4747import io.element.android.libraries.matrix.test.AN_EVENT_ID
48+ import io.element.android.libraries.matrix.test.AN_EVENT_ID_2
4849import io.element.android.libraries.matrix.test.A_USER_ID
4950import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService
5051import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
@@ -60,15 +61,19 @@ import io.element.android.tests.testutils.awaitWithLatch
6061import io.element.android.tests.testutils.consumeItemsUntilPredicate
6162import io.element.android.tests.testutils.testCoroutineDispatchers
6263import kotlinx.collections.immutable.persistentListOf
64+ import kotlinx.coroutines.ExperimentalCoroutinesApi
6365import kotlinx.coroutines.delay
66+ import kotlinx.coroutines.test.StandardTestDispatcher
6467import kotlinx.coroutines.test.TestScope
68+ import kotlinx.coroutines.test.runCurrent
6569import kotlinx.coroutines.test.runTest
6670import org.junit.Rule
6771import org.junit.Test
6872import java.util.Date
6973import kotlin.time.Duration.Companion.seconds
7074
7175private const val FAKE_UNIQUE_ID = " FAKE_UNIQUE_ID"
76+ private const val FAKE_UNIQUE_ID_2 = " FAKE_UNIQUE_ID_2"
7277
7378class TimelinePresenterTest {
7479 @get:Rule
@@ -125,13 +130,47 @@ class TimelinePresenterTest {
125130 }
126131 }
127132
133+ @OptIn(ExperimentalCoroutinesApi ::class )
128134 @Test
129- fun `present - on scroll finished send read receipt if an event is before the index` () = runTest {
135+ fun `present - on scroll finished mark a room as read if the first visible index is 0 ` () = runTest( StandardTestDispatcher ()) {
130136 val timeline = FakeMatrixTimeline (
131137 initialTimelineItems = listOf (
132138 MatrixTimelineItem .Event (FAKE_UNIQUE_ID , anEventTimelineItem())
133139 )
134140 )
141+ val sessionPreferencesStore = InMemorySessionPreferencesStore (isSendPublicReadReceiptsEnabled = false )
142+ val room = FakeMatrixRoom (matrixTimeline = timeline)
143+ val presenter = createTimelinePresenter(
144+ timeline = timeline,
145+ room = room,
146+ sessionPreferencesStore = sessionPreferencesStore,
147+ )
148+ moleculeFlow(RecompositionMode .Immediate ) {
149+ presenter.present()
150+ }.test {
151+ assertThat(timeline.sentReadReceipts).isEmpty()
152+ val initialState = awaitFirstItem()
153+ initialState.eventSink.invoke(TimelineEvents .OnScrollFinished (0 ))
154+ runCurrent()
155+ assertThat(room.markAsReadCalls).isNotEmpty()
156+ cancelAndIgnoreRemainingEvents()
157+ }
158+ }
159+
160+ @Test
161+ fun `present - on scroll finished send read receipt if an event is before the index` () = runTest {
162+ val timeline = FakeMatrixTimeline (
163+ initialTimelineItems = listOf (
164+ MatrixTimelineItem .Event (FAKE_UNIQUE_ID , anEventTimelineItem()),
165+ MatrixTimelineItem .Event (
166+ uniqueId = FAKE_UNIQUE_ID_2 ,
167+ event = anEventTimelineItem(
168+ eventId = AN_EVENT_ID_2 ,
169+ content = aMessageContent(" Test message" )
170+ )
171+ )
172+ )
173+ )
135174 val presenter = createTimelinePresenter(timeline)
136175 moleculeFlow(RecompositionMode .Immediate ) {
137176 presenter.present()
@@ -140,7 +179,7 @@ class TimelinePresenterTest {
140179 val initialState = awaitFirstItem()
141180 awaitWithLatch { latch ->
142181 timeline.sendReadReceiptLatch = latch
143- initialState.eventSink.invoke(TimelineEvents .OnScrollFinished (0 ))
182+ initialState.eventSink.invoke(TimelineEvents .OnScrollFinished (1 ))
144183 }
145184 assertThat(timeline.sentReadReceipts).isNotEmpty()
146185 assertThat(timeline.sentReadReceipts.first().second).isEqualTo(ReceiptType .READ )
@@ -149,10 +188,17 @@ class TimelinePresenterTest {
149188 }
150189
151190 @Test
152- fun `present - on scroll finished send a private read receipt if an event is before the index and public read receipts are disabled` () = runTest {
191+ fun `present - on scroll finished send a private read receipt if an event is at an index other than 0 and public read receipts are disabled` () = runTest {
153192 val timeline = FakeMatrixTimeline (
154193 initialTimelineItems = listOf (
155- MatrixTimelineItem .Event (FAKE_UNIQUE_ID , anEventTimelineItem())
194+ MatrixTimelineItem .Event (FAKE_UNIQUE_ID , anEventTimelineItem()),
195+ MatrixTimelineItem .Event (
196+ uniqueId = FAKE_UNIQUE_ID_2 ,
197+ event = anEventTimelineItem(
198+ eventId = AN_EVENT_ID_2 ,
199+ content = aMessageContent(" Test message" )
200+ )
201+ )
156202 )
157203 )
158204 val sessionPreferencesStore = InMemorySessionPreferencesStore (isSendPublicReadReceiptsEnabled = false )
@@ -168,6 +214,7 @@ class TimelinePresenterTest {
168214 awaitWithLatch { latch ->
169215 timeline.sendReadReceiptLatch = latch
170216 initialState.eventSink.invoke(TimelineEvents .OnScrollFinished (0 ))
217+ initialState.eventSink.invoke(TimelineEvents .OnScrollFinished (1 ))
171218 }
172219 assertThat(timeline.sentReadReceipts).isNotEmpty()
173220 assertThat(timeline.sentReadReceipts.first().second).isEqualTo(ReceiptType .READ_PRIVATE )
@@ -176,10 +223,17 @@ class TimelinePresenterTest {
176223 }
177224
178225 @Test
179- fun `present - on scroll finished will not send read receipt if no event is before the index ` () = runTest {
226+ fun `present - on scroll finished will not send read receipt the first visible event is the same as before ` () = runTest {
180227 val timeline = FakeMatrixTimeline (
181228 initialTimelineItems = listOf (
182- MatrixTimelineItem .Event (FAKE_UNIQUE_ID , anEventTimelineItem())
229+ MatrixTimelineItem .Event (FAKE_UNIQUE_ID , anEventTimelineItem()),
230+ MatrixTimelineItem .Event (
231+ uniqueId = FAKE_UNIQUE_ID_2 ,
232+ event = anEventTimelineItem(
233+ eventId = AN_EVENT_ID_2 ,
234+ content = aMessageContent(" Test message" )
235+ )
236+ )
183237 )
184238 )
185239 val presenter = createTimelinePresenter(timeline)
@@ -191,8 +245,9 @@ class TimelinePresenterTest {
191245 awaitWithLatch { latch ->
192246 timeline.sendReadReceiptLatch = latch
193247 initialState.eventSink.invoke(TimelineEvents .OnScrollFinished (1 ))
248+ initialState.eventSink.invoke(TimelineEvents .OnScrollFinished (1 ))
194249 }
195- assertThat(timeline.sentReadReceipts).isEmpty( )
250+ assertThat(timeline.sentReadReceipts).hasSize( 1 )
196251 cancelAndIgnoreRemainingEvents()
197252 }
198253 }
@@ -201,6 +256,7 @@ class TimelinePresenterTest {
201256 fun `present - on scroll finished will not send read receipt only virtual events exist before the index` () = runTest {
202257 val timeline = FakeMatrixTimeline (
203258 initialTimelineItems = listOf (
259+ MatrixTimelineItem .Virtual (FAKE_UNIQUE_ID , VirtualTimelineItem .ReadMarker ),
204260 MatrixTimelineItem .Virtual (FAKE_UNIQUE_ID , VirtualTimelineItem .ReadMarker )
205261 )
206262 )
@@ -212,7 +268,7 @@ class TimelinePresenterTest {
212268 val initialState = awaitFirstItem()
213269 awaitWithLatch { latch ->
214270 timeline.sendReadReceiptLatch = latch
215- initialState.eventSink.invoke(TimelineEvents .OnScrollFinished (0 ))
271+ initialState.eventSink.invoke(TimelineEvents .OnScrollFinished (1 ))
216272 }
217273 assertThat(timeline.sentReadReceipts).isEmpty()
218274 cancelAndIgnoreRemainingEvents()
0 commit comments