Browse Source

Add pusher status in the state.

It improve the tests and we may want to render errors in the View at some point.
pull/3035/head
Benoit Marty 3 months ago committed by Benoit Marty
parent
commit
21ce1c40b3
  1. 29
      appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt
  2. 3
      appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInState.kt
  3. 2
      appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInStateProvider.kt
  4. 24
      appnav/src/main/kotlin/io/element/android/appnav/loggedin/PusherRegistrationFailure.kt
  5. 34
      appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt

29
appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt

@ -18,14 +18,17 @@ package io.element.android.appnav.loggedin @@ -18,14 +18,17 @@ package io.element.android.appnav.loggedin
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import im.vector.app.features.analytics.plan.CryptoSessionStateChange
import im.vector.app.features.analytics.plan.UserProperties
import io.element.android.features.networkmonitor.api.NetworkMonitor
import io.element.android.features.networkmonitor.api.NetworkStatus
import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.core.log.logger.LoggerTag
import io.element.android.libraries.matrix.api.MatrixClient
@ -55,13 +58,16 @@ class LoggedInPresenter @Inject constructor( @@ -55,13 +58,16 @@ class LoggedInPresenter @Inject constructor(
val isVerified by remember {
sessionVerificationService.sessionVerifiedStatus.map { it == SessionVerifiedStatus.Verified }
}.collectAsState(initial = false)
val pusherRegistrationState = remember<MutableState<AsyncData<Unit>>> { mutableStateOf(AsyncData.Uninitialized) }
if (isVerified) {
LaunchedEffect(Unit) {
ensurePusherIsRegistered()
ensurePusherIsRegistered(pusherRegistrationState)
}
} else {
LaunchedEffect(Unit) {
pusherRegistrationState.value = AsyncData.Failure(PusherRegistrationFailure.AccountNotVerified())
}
}
val syncIndicator by matrixClient.roomListService.syncIndicator.collectAsState()
val networkStatus by networkMonitor.connectivity.collectAsState()
val showSyncSpinner by remember {
@ -77,25 +83,32 @@ class LoggedInPresenter @Inject constructor( @@ -77,25 +83,32 @@ class LoggedInPresenter @Inject constructor(
return LoggedInState(
showSyncSpinner = showSyncSpinner,
pusherRegistrationState = pusherRegistrationState.value,
)
}
private suspend fun ensurePusherIsRegistered() {
private suspend fun ensurePusherIsRegistered(pusherRegistrationState: MutableState<AsyncData<Unit>>) {
Timber.tag(pusherTag.value).d("Ensure pusher is registered")
val currentPushProvider = pushService.getCurrentPushProvider()
val result = if (currentPushProvider == null) {
Timber.tag(pusherTag.value).d("Register with the first available push provider")
val pushProvider = pushService.getAvailablePushProviders().firstOrNull()
?: return Unit.also { Timber.tag(pusherTag.value).w("No push provider available") }
?: return Unit
.also { Timber.tag(pusherTag.value).w("No push providers available") }
.also { pusherRegistrationState.value = AsyncData.Failure(PusherRegistrationFailure.NoProvidersAvailable()) }
val distributor = pushProvider.getDistributors().firstOrNull()
?: return Unit.also { Timber.tag(pusherTag.value).w("No distributor available") }
?: return Unit
.also { Timber.tag(pusherTag.value).w("No distributors available") }
.also { pusherRegistrationState.value = AsyncData.Failure(PusherRegistrationFailure.NoDistributorsAvailable()) }
pushService.registerWith(matrixClient, pushProvider, distributor)
} else {
val currentPushDistributor = currentPushProvider.getCurrentDistributor(matrixClient)
if (currentPushDistributor == null) {
Timber.tag(pusherTag.value).d("Register with the first available distributor")
val distributor = currentPushProvider.getDistributors().firstOrNull()
?: return Unit.also { Timber.tag(pusherTag.value).w("No distributor available") }
?: return Unit
.also { Timber.tag(pusherTag.value).w("No distributors available") }
.also { pusherRegistrationState.value = AsyncData.Failure(PusherRegistrationFailure.NoDistributorsAvailable()) }
pushService.registerWith(matrixClient, currentPushProvider, distributor)
} else {
Timber.tag(pusherTag.value).d("Re-register with the current distributor")
@ -105,9 +118,11 @@ class LoggedInPresenter @Inject constructor( @@ -105,9 +118,11 @@ class LoggedInPresenter @Inject constructor(
result.fold(
onSuccess = {
Timber.tag(pusherTag.value).d("Pusher registered")
pusherRegistrationState.value = AsyncData.Success(Unit)
},
onFailure = {
Timber.tag(pusherTag.value).e(it, "Failed to register pusher")
pusherRegistrationState.value = AsyncData.Failure(PusherRegistrationFailure.RegistrationFailure(it))
}
)
}

3
appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInState.kt

@ -16,6 +16,9 @@ @@ -16,6 +16,9 @@
package io.element.android.appnav.loggedin
import io.element.android.libraries.architecture.AsyncData
data class LoggedInState(
val showSyncSpinner: Boolean,
val pusherRegistrationState: AsyncData<Unit>,
)

2
appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInStateProvider.kt

@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
package io.element.android.appnav.loggedin
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.libraries.architecture.AsyncData
open class LoggedInStateProvider : PreviewParameterProvider<LoggedInState> {
override val values: Sequence<LoggedInState>
@ -31,4 +32,5 @@ fun aLoggedInState( @@ -31,4 +32,5 @@ fun aLoggedInState(
showSyncSpinner: Boolean = true,
) = LoggedInState(
showSyncSpinner = showSyncSpinner,
pusherRegistrationState = AsyncData.Uninitialized,
)

24
appnav/src/main/kotlin/io/element/android/appnav/loggedin/PusherRegistrationFailure.kt

@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.appnav.loggedin
sealed class PusherRegistrationFailure : Exception() {
class AccountNotVerified : PusherRegistrationFailure()
class NoProvidersAvailable : PusherRegistrationFailure()
class NoDistributorsAvailable : PusherRegistrationFailure()
class RegistrationFailure(val failure: Throwable) : PusherRegistrationFailure()
}

34
appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt

@ -47,13 +47,10 @@ import io.element.android.tests.testutils.consumeItemsUntilPredicate @@ -47,13 +47,10 @@ import io.element.android.tests.testutils.consumeItemsUntilPredicate
import io.element.android.tests.testutils.lambda.any
import io.element.android.tests.testutils.lambda.lambdaRecorder
import io.element.android.tests.testutils.lambda.value
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
@OptIn(ExperimentalCoroutinesApi::class)
class LoggedInPresenterTest {
@get:Rule
val warmUpRule = WarmUpRule()
@ -66,6 +63,8 @@ class LoggedInPresenterTest { @@ -66,6 +63,8 @@ class LoggedInPresenterTest {
}.test {
val initialState = awaitItem()
assertThat(initialState.showSyncSpinner).isFalse()
assertThat(initialState.pusherRegistrationState.isUninitialized()).isTrue()
skipItems(1)
}
}
@ -106,7 +105,7 @@ class LoggedInPresenterTest { @@ -106,7 +105,7 @@ class LoggedInPresenterTest {
encryptionService.emitRecoveryState(RecoveryState.INCOMPLETE)
verificationService.emitVerifiedStatus(SessionVerifiedStatus.Verified)
skipItems(4)
skipItems(6)
assertThat(analyticsService.capturedEvents.size).isEqualTo(1)
assertThat(analyticsService.capturedEvents[0]).isInstanceOf(CryptoSessionStateChange::class.java)
@ -133,6 +132,9 @@ class LoggedInPresenterTest { @@ -133,6 +132,9 @@ class LoggedInPresenterTest {
presenter.present()
}.test {
skipItems(1)
val finalState = awaitItem()
assertThat(finalState.pusherRegistrationState.errorOrNull())
.isInstanceOf(PusherRegistrationFailure.AccountNotVerified::class.java)
lambda.assertions()
.isNeverCalled()
}
@ -156,7 +158,8 @@ class LoggedInPresenterTest { @@ -156,7 +158,8 @@ class LoggedInPresenterTest {
presenter.present()
}.test {
skipItems(2)
advanceUntilIdle()
val finalState = awaitItem()
assertThat(finalState.pusherRegistrationState.isSuccess()).isTrue()
lambda.assertions()
.isCalledOnce()
.with(
@ -188,7 +191,8 @@ class LoggedInPresenterTest { @@ -188,7 +191,8 @@ class LoggedInPresenterTest {
presenter.present()
}.test {
skipItems(2)
advanceUntilIdle()
val finalState = awaitItem()
assertThat(finalState.pusherRegistrationState.isFailure()).isTrue()
lambda.assertions()
.isCalledOnce()
.with(
@ -233,7 +237,8 @@ class LoggedInPresenterTest { @@ -233,7 +237,8 @@ class LoggedInPresenterTest {
presenter.present()
}.test {
skipItems(2)
advanceUntilIdle()
val finalState = awaitItem()
assertThat(finalState.pusherRegistrationState.isSuccess()).isTrue()
lambda.assertions()
.isCalledOnce()
.with(
@ -277,7 +282,8 @@ class LoggedInPresenterTest { @@ -277,7 +282,8 @@ class LoggedInPresenterTest {
presenter.present()
}.test {
skipItems(2)
advanceUntilIdle()
val finalState = awaitItem()
assertThat(finalState.pusherRegistrationState.isSuccess()).isTrue()
lambda.assertions()
.isCalledOnce()
.with(
@ -317,7 +323,9 @@ class LoggedInPresenterTest { @@ -317,7 +323,9 @@ class LoggedInPresenterTest {
presenter.present()
}.test {
skipItems(2)
advanceUntilIdle()
val finalState = awaitItem()
assertThat(finalState.pusherRegistrationState.errorOrNull())
.isInstanceOf(PusherRegistrationFailure.NoDistributorsAvailable::class.java)
lambda.assertions()
.isNeverCalled()
}
@ -343,7 +351,9 @@ class LoggedInPresenterTest { @@ -343,7 +351,9 @@ class LoggedInPresenterTest {
presenter.present()
}.test {
skipItems(2)
advanceUntilIdle()
val finalState = awaitItem()
assertThat(finalState.pusherRegistrationState.errorOrNull())
.isInstanceOf(PusherRegistrationFailure.NoProvidersAvailable::class.java)
lambda.assertions()
.isNeverCalled()
}
@ -374,7 +384,9 @@ class LoggedInPresenterTest { @@ -374,7 +384,9 @@ class LoggedInPresenterTest {
presenter.present()
}.test {
skipItems(2)
advanceUntilIdle()
val finalState = awaitItem()
assertThat(finalState.pusherRegistrationState.errorOrNull())
.isInstanceOf(PusherRegistrationFailure.NoDistributorsAvailable::class.java)
lambda.assertions()
.isNeverCalled()
}

Loading…
Cancel
Save