Simplify syncing the room list when receiving a push (#4915)
This commit is contained in:
parent
ebda14b37b
commit
9c6aa97818
2 changed files with 7 additions and 62 deletions
|
|
@ -8,21 +8,15 @@
|
||||||
package io.element.android.libraries.push.impl.push
|
package io.element.android.libraries.push.impl.push
|
||||||
|
|
||||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||||
import io.element.android.libraries.core.coroutine.parallelMap
|
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||||
import io.element.android.libraries.matrix.api.MatrixClientProvider
|
import io.element.android.libraries.matrix.api.MatrixClientProvider
|
||||||
import io.element.android.libraries.matrix.api.core.EventId
|
|
||||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
|
||||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
|
|
||||||
import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent
|
import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent
|
||||||
import io.element.android.services.appnavstate.api.ActiveRoomsHolder
|
|
||||||
import io.element.android.services.appnavstate.api.AppForegroundStateService
|
import io.element.android.services.appnavstate.api.AppForegroundStateService
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.coroutines.withTimeoutOrNull
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.time.Duration
|
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
class SyncOnNotifiableEvent @Inject constructor(
|
class SyncOnNotifiableEvent @Inject constructor(
|
||||||
|
|
@ -30,7 +24,6 @@ class SyncOnNotifiableEvent @Inject constructor(
|
||||||
private val featureFlagService: FeatureFlagService,
|
private val featureFlagService: FeatureFlagService,
|
||||||
private val appForegroundStateService: AppForegroundStateService,
|
private val appForegroundStateService: AppForegroundStateService,
|
||||||
private val dispatchers: CoroutineDispatchers,
|
private val dispatchers: CoroutineDispatchers,
|
||||||
private val activeRoomsHolder: ActiveRoomsHolder,
|
|
||||||
) {
|
) {
|
||||||
suspend operator fun invoke(notifiableEvents: List<NotifiableEvent>) = withContext(dispatchers.io) {
|
suspend operator fun invoke(notifiableEvents: List<NotifiableEvent>) = withContext(dispatchers.io) {
|
||||||
if (!featureFlagService.isFeatureEnabled(FeatureFlags.SyncOnPush)) {
|
if (!featureFlagService.isFeatureEnabled(FeatureFlags.SyncOnPush)) {
|
||||||
|
|
@ -41,6 +34,7 @@ class SyncOnNotifiableEvent @Inject constructor(
|
||||||
val eventsBySession = notifiableEvents.groupBy { it.sessionId }
|
val eventsBySession = notifiableEvents.groupBy { it.sessionId }
|
||||||
|
|
||||||
appForegroundStateService.updateIsSyncingNotificationEvent(true)
|
appForegroundStateService.updateIsSyncingNotificationEvent(true)
|
||||||
|
Timber.d("Starting opportunistic room list sync | In foreground: ${appForegroundStateService.isInForeground.value}")
|
||||||
|
|
||||||
for ((sessionId, events) in eventsBySession) {
|
for ((sessionId, events) in eventsBySession) {
|
||||||
val client = matrixClientProvider.getOrRestore(sessionId).getOrNull() ?: continue
|
val client = matrixClientProvider.getOrRestore(sessionId).getOrNull() ?: continue
|
||||||
|
|
@ -49,38 +43,13 @@ class SyncOnNotifiableEvent @Inject constructor(
|
||||||
client.roomListService.subscribeToVisibleRooms(eventsByRoomId.keys.toList())
|
client.roomListService.subscribeToVisibleRooms(eventsByRoomId.keys.toList())
|
||||||
|
|
||||||
if (!appForegroundStateService.isInForeground.value) {
|
if (!appForegroundStateService.isInForeground.value) {
|
||||||
for ((roomId, eventsInRoom) in eventsByRoomId) {
|
// Give the sync some time to complete in background
|
||||||
val activeRoom = activeRoomsHolder.getActiveRoomMatching(sessionId, roomId)
|
delay(10.seconds)
|
||||||
val room = activeRoom ?: client.getJoinedRoom(roomId)
|
|
||||||
|
|
||||||
if (room != null) {
|
|
||||||
eventsInRoom.parallelMap { event ->
|
|
||||||
room.waitsUntilEventIsKnown(event.eventId, timeout = 10.seconds)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (room != null && activeRoom == null) {
|
|
||||||
// Destroy the room we just instantiated to reset its live timeline
|
|
||||||
room.destroy()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
Timber.d("Finished opportunistic room list sync")
|
||||||
appForegroundStateService.updateIsSyncingNotificationEvent(false)
|
appForegroundStateService.updateIsSyncingNotificationEvent(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun JoinedRoom.waitsUntilEventIsKnown(eventId: EventId, timeout: Duration) {
|
|
||||||
withTimeoutOrNull(timeout) {
|
|
||||||
liveTimeline.timelineItems.first { timelineItems ->
|
|
||||||
timelineItems.any { timelineItem ->
|
|
||||||
when (timelineItem) {
|
|
||||||
is MatrixTimelineItem.Event -> timelineItem.eventId == eventId
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,27 +13,20 @@ import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||||
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
import io.element.android.libraries.matrix.api.sync.SyncState
|
import io.element.android.libraries.matrix.api.sync.SyncState
|
||||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
|
|
||||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||||
import io.element.android.libraries.matrix.test.A_UNIQUE_ID
|
|
||||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||||
import io.element.android.libraries.matrix.test.FakeMatrixClientProvider
|
import io.element.android.libraries.matrix.test.FakeMatrixClientProvider
|
||||||
import io.element.android.libraries.matrix.test.room.FakeBaseRoom
|
import io.element.android.libraries.matrix.test.room.FakeBaseRoom
|
||||||
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
|
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
|
||||||
import io.element.android.libraries.matrix.test.room.aRoomInfo
|
import io.element.android.libraries.matrix.test.room.aRoomInfo
|
||||||
import io.element.android.libraries.matrix.test.sync.FakeSyncService
|
import io.element.android.libraries.matrix.test.sync.FakeSyncService
|
||||||
import io.element.android.libraries.matrix.test.timeline.FakeTimeline
|
|
||||||
import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem
|
|
||||||
import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiableCallEvent
|
import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiableCallEvent
|
||||||
import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiableMessageEvent
|
import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiableMessageEvent
|
||||||
import io.element.android.services.appnavstate.api.ActiveRoomsHolder
|
|
||||||
import io.element.android.services.appnavstate.test.FakeAppForegroundStateService
|
import io.element.android.services.appnavstate.test.FakeAppForegroundStateService
|
||||||
import io.element.android.tests.testutils.lambda.assert
|
import io.element.android.tests.testutils.lambda.assert
|
||||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||||
import io.element.android.tests.testutils.testCoroutineDispatchers
|
import io.element.android.tests.testutils.testCoroutineDispatchers
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.test.TestScope
|
import kotlinx.coroutines.test.TestScope
|
||||||
import kotlinx.coroutines.test.advanceTimeBy
|
import kotlinx.coroutines.test.advanceTimeBy
|
||||||
|
|
@ -43,16 +36,11 @@ import java.util.concurrent.atomic.AtomicBoolean
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
class SyncOnNotifiableEventTest {
|
class SyncOnNotifiableEventTest {
|
||||||
private val timelineItems = MutableStateFlow<List<MatrixTimelineItem>>(emptyList())
|
|
||||||
private val startSyncLambda = lambdaRecorder<Result<Unit>> { Result.success(Unit) }
|
private val startSyncLambda = lambdaRecorder<Result<Unit>> { Result.success(Unit) }
|
||||||
private val stopSyncLambda = lambdaRecorder<Result<Unit>> { Result.success(Unit) }
|
private val stopSyncLambda = lambdaRecorder<Result<Unit>> { Result.success(Unit) }
|
||||||
private val subscribeToSyncLambda = lambdaRecorder<Unit> { }
|
private val subscribeToSyncLambda = lambdaRecorder<Unit> { }
|
||||||
|
|
||||||
private val liveTimeline = FakeTimeline(
|
|
||||||
timelineItems = timelineItems,
|
|
||||||
)
|
|
||||||
private val room = FakeJoinedRoom(
|
private val room = FakeJoinedRoom(
|
||||||
liveTimeline = liveTimeline,
|
|
||||||
baseRoom = FakeBaseRoom(
|
baseRoom = FakeBaseRoom(
|
||||||
roomId = A_ROOM_ID,
|
roomId = A_ROOM_ID,
|
||||||
subscribeToSyncLambda = subscribeToSyncLambda,
|
subscribeToSyncLambda = subscribeToSyncLambda,
|
||||||
|
|
@ -130,10 +118,6 @@ class SyncOnNotifiableEventTest {
|
||||||
)
|
)
|
||||||
val sut = createSyncOnNotifiableEvent(client = client, appForegroundStateService = appForegroundStateService, isSyncOnPushEnabled = true)
|
val sut = createSyncOnNotifiableEvent(client = client, appForegroundStateService = appForegroundStateService, isSyncOnPushEnabled = true)
|
||||||
|
|
||||||
timelineItems.emit(
|
|
||||||
listOf(MatrixTimelineItem.Event(A_UNIQUE_ID, anEventTimelineItem()))
|
|
||||||
)
|
|
||||||
|
|
||||||
appForegroundStateService.isSyncingNotificationEvent.test {
|
appForegroundStateService.isSyncingNotificationEvent.test {
|
||||||
syncService.emitSyncState(SyncState.Running)
|
syncService.emitSyncState(SyncState.Running)
|
||||||
sut(listOf(notifiableEvent))
|
sut(listOf(notifiableEvent))
|
||||||
|
|
@ -150,7 +134,7 @@ class SyncOnNotifiableEventTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `when feature flag is enabled and app is in background, running multiple time only call once`() = runTest {
|
fun `when feature flag is enabled and app is in background, running multiple times only call once`() = runTest {
|
||||||
val appForegroundStateService = FakeAppForegroundStateService(
|
val appForegroundStateService = FakeAppForegroundStateService(
|
||||||
initialForegroundValue = false,
|
initialForegroundValue = false,
|
||||||
)
|
)
|
||||||
|
|
@ -159,12 +143,6 @@ class SyncOnNotifiableEventTest {
|
||||||
appForegroundStateService.isSyncingNotificationEvent.test {
|
appForegroundStateService.isSyncingNotificationEvent.test {
|
||||||
launch { sut(listOf(notifiableEvent)) }
|
launch { sut(listOf(notifiableEvent)) }
|
||||||
launch { sut(listOf(notifiableEvent)) }
|
launch { sut(listOf(notifiableEvent)) }
|
||||||
launch {
|
|
||||||
delay(1)
|
|
||||||
timelineItems.emit(
|
|
||||||
listOf(MatrixTimelineItem.Event(A_UNIQUE_ID, anEventTimelineItem()))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// It's initially false
|
// It's initially false
|
||||||
assertThat(awaitItem()).isFalse()
|
assertThat(awaitItem()).isFalse()
|
||||||
|
|
@ -183,7 +161,6 @@ class SyncOnNotifiableEventTest {
|
||||||
appForegroundStateService: FakeAppForegroundStateService = FakeAppForegroundStateService(
|
appForegroundStateService: FakeAppForegroundStateService = FakeAppForegroundStateService(
|
||||||
initialForegroundValue = true,
|
initialForegroundValue = true,
|
||||||
),
|
),
|
||||||
activeRoomsHolder: ActiveRoomsHolder = ActiveRoomsHolder(),
|
|
||||||
): SyncOnNotifiableEvent {
|
): SyncOnNotifiableEvent {
|
||||||
val featureFlagService = FakeFeatureFlagService(
|
val featureFlagService = FakeFeatureFlagService(
|
||||||
initialState = mapOf(
|
initialState = mapOf(
|
||||||
|
|
@ -196,7 +173,6 @@ class SyncOnNotifiableEventTest {
|
||||||
featureFlagService = featureFlagService,
|
featureFlagService = featureFlagService,
|
||||||
appForegroundStateService = appForegroundStateService,
|
appForegroundStateService = appForegroundStateService,
|
||||||
dispatchers = testCoroutineDispatchers(),
|
dispatchers = testCoroutineDispatchers(),
|
||||||
activeRoomsHolder = activeRoomsHolder,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue