Allow configuration to provide multiple account providers. (#4742)

* Allow several account provider in configuration

* Target latest private modules.

* Fix tests

* Target latest private modules.

* Trigger CI

* Fix formatting issue
This commit is contained in:
Benoit Marty
2025-05-20 08:57:01 +02:00
committed by GitHub
parent 6995e62548
commit c6f6c2cd65
13 changed files with 58 additions and 40 deletions

View File

@@ -13,7 +13,7 @@ import io.element.android.libraries.matrix.api.core.SessionId
interface EnterpriseService {
val isEnterpriseBuild: Boolean
suspend fun isEnterpriseUser(sessionId: SessionId): Boolean
fun defaultHomeserver(): String?
fun defaultHomeserverList(): List<String>
suspend fun isAllowedToConnectToHomeserver(homeserverUrl: String): Boolean
fun semanticColorsLight(): SemanticColors

View File

@@ -22,7 +22,7 @@ class DefaultEnterpriseService @Inject constructor() : EnterpriseService {
override suspend fun isEnterpriseUser(sessionId: SessionId) = false
override fun defaultHomeserver() = null
override fun defaultHomeserverList(): List<String> = emptyList()
override suspend fun isAllowedToConnectToHomeserver(homeserverUrl: String) = true
override fun semanticColorsLight(): SemanticColors = compoundColorsLight

View File

@@ -21,9 +21,9 @@ class DefaultEnterpriseServiceTest {
}
@Test
fun `defaultHomeserver should return null`() {
fun `defaultHomeserverList should return empty list`() {
val defaultEnterpriseService = DefaultEnterpriseService()
assertThat<String?>(defaultEnterpriseService.defaultHomeserver()).isNull()
assertThat(defaultEnterpriseService.defaultHomeserverList()).isEmpty()
}
@Test

View File

@@ -16,7 +16,7 @@ import io.element.android.tests.testutils.simulateLongTask
class FakeEnterpriseService(
override val isEnterpriseBuild: Boolean = false,
private val isEnterpriseUserResult: (SessionId) -> Boolean = { lambdaError() },
private val defaultHomeserverResult: () -> String? = { A_FAKE_HOMESERVER },
private val defaultHomeserverListResult: () -> List<String> = { emptyList() },
private val isAllowedToConnectToHomeserverResult: (String) -> Boolean = { lambdaError() },
private val semanticColorsLightResult: () -> SemanticColors = { lambdaError() },
private val semanticColorsDarkResult: () -> SemanticColors = { lambdaError() },
@@ -27,8 +27,8 @@ class FakeEnterpriseService(
isEnterpriseUserResult(sessionId)
}
override fun defaultHomeserver(): String? {
return defaultHomeserverResult()
override fun defaultHomeserverList(): List<String> {
return defaultHomeserverListResult()
}
override suspend fun isAllowedToConnectToHomeserver(homeserverUrl: String): Boolean = simulateLongTask {

View File

@@ -20,14 +20,15 @@ import javax.inject.Inject
class AccountProviderDataSource @Inject constructor(
enterpriseService: EnterpriseService,
) {
private val defaultAccountProvider = (enterpriseService.defaultHomeserver() ?: AuthenticationConfig.MATRIX_ORG_URL).let { url ->
AccountProvider(
url = url,
subtitle = null,
isPublic = url == AuthenticationConfig.MATRIX_ORG_URL,
isMatrixOrg = url == AuthenticationConfig.MATRIX_ORG_URL,
)
}
private val defaultAccountProvider = (enterpriseService.defaultHomeserverList().firstOrNull() ?: AuthenticationConfig.MATRIX_ORG_URL)
.let { url ->
AccountProvider(
url = url,
subtitle = null,
isPublic = url == AuthenticationConfig.MATRIX_ORG_URL,
isMatrixOrg = url == AuthenticationConfig.MATRIX_ORG_URL,
)
}
private val accountProvider: MutableStateFlow<AccountProvider> = MutableStateFlow(
defaultAccountProvider

View File

@@ -8,29 +8,39 @@
package io.element.android.features.login.impl.screens.changeaccountprovider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import io.element.android.appconfig.AuthenticationConfig
import io.element.android.features.enterprise.api.EnterpriseService
import io.element.android.features.login.impl.accountprovider.AccountProvider
import io.element.android.features.login.impl.changeserver.ChangeServerState
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.core.uri.ensureProtocol
import javax.inject.Inject
class ChangeAccountProviderPresenter @Inject constructor(
private val changeServerPresenter: Presenter<ChangeServerState>,
private val enterpriseService: EnterpriseService,
) : Presenter<ChangeAccountProviderState> {
@Composable
override fun present(): ChangeAccountProviderState {
val staticAccountProviderList = remember {
enterpriseService.defaultHomeserverList()
.map { it.ensureProtocol() }
.ifEmpty { listOf(AuthenticationConfig.MATRIX_ORG_URL) }
.map { url ->
AccountProvider(
url = url,
subtitle = null,
isPublic = url == AuthenticationConfig.MATRIX_ORG_URL,
isMatrixOrg = url == AuthenticationConfig.MATRIX_ORG_URL,
isValid = true,
)
}
}
val changeServerState = changeServerPresenter.present()
return ChangeAccountProviderState(
// Just matrix.org by default for now
accountProviders = listOf(
AccountProvider(
url = AuthenticationConfig.MATRIX_ORG_URL,
subtitle = null,
isPublic = true,
isMatrixOrg = true,
isValid = true,
)
),
accountProviders = staticAccountProviderList,
changeServerState = changeServerState,
)
}

View File

@@ -11,7 +11,7 @@ import io.element.android.features.login.impl.accountprovider.AccountProvider
import io.element.android.features.login.impl.changeserver.ChangeServerState
// Do not use default value, so no member get forgotten in the presenters.
data class ChangeAccountProviderState constructor(
data class ChangeAccountProviderState(
val accountProviders: List<AccountProvider>,
val changeServerState: ChangeServerState,
)

View File

@@ -27,11 +27,11 @@ class AccountProviderDataSourceTest {
val initialState = awaitItem()
assertThat(initialState).isEqualTo(
AccountProvider(
url = FakeEnterpriseService.A_FAKE_HOMESERVER,
title = FakeEnterpriseService.A_FAKE_HOMESERVER,
url = AuthenticationConfig.MATRIX_ORG_URL,
title = "matrix.org",
subtitle = null,
isPublic = false,
isMatrixOrg = false,
isPublic = true,
isMatrixOrg = true,
isValid = false,
)
)
@@ -40,9 +40,11 @@ class AccountProviderDataSourceTest {
@Test
fun `present - initial state - matrix org`() = runTest {
val sut = AccountProviderDataSource(FakeEnterpriseService(
defaultHomeserverResult = { AuthenticationConfig.MATRIX_ORG_URL }
))
val sut = AccountProviderDataSource(
FakeEnterpriseService(
defaultHomeserverListResult = { listOf(AuthenticationConfig.MATRIX_ORG_URL) }
)
)
sut.flow.test {
val initialState = awaitItem()
assertThat(initialState).isEqualTo(
@@ -63,7 +65,7 @@ class AccountProviderDataSourceTest {
val sut = AccountProviderDataSource(FakeEnterpriseService())
sut.flow.test {
val initialState = awaitItem()
assertThat(initialState.url).isEqualTo(FakeEnterpriseService.A_FAKE_HOMESERVER)
assertThat(initialState.url).isEqualTo(AuthenticationConfig.MATRIX_ORG_URL)
sut.userSelection(AccountProvider(url = "https://example.com"))
val changedState = awaitItem()
assertThat(changedState).isEqualTo(
@@ -78,7 +80,7 @@ class AccountProviderDataSourceTest {
)
sut.reset()
val resetState = awaitItem()
assertThat(resetState.url).isEqualTo(FakeEnterpriseService.A_FAKE_HOMESERVER)
assertThat(resetState.url).isEqualTo(AuthenticationConfig.MATRIX_ORG_URL)
}
}
}

View File

@@ -11,6 +11,7 @@ import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
import io.element.android.features.enterprise.test.FakeEnterpriseService
import io.element.android.features.login.impl.accountprovider.AccountProvider
import io.element.android.features.login.impl.changeserver.aChangeServerState
import io.element.android.tests.testutils.WarmUpRule
@@ -25,7 +26,8 @@ class ChangeAccountProviderPresenterTest {
@Test
fun `present - initial state`() = runTest {
val presenter = ChangeAccountProviderPresenter(
changeServerPresenter = { aChangeServerState() }
changeServerPresenter = { aChangeServerState() },
enterpriseService = FakeEnterpriseService(),
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()

View File

@@ -11,6 +11,7 @@ import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
import io.element.android.appconfig.AuthenticationConfig
import io.element.android.features.enterprise.test.FakeEnterpriseService
import io.element.android.features.login.impl.DefaultLoginUserStory
import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource
@@ -44,7 +45,7 @@ class ConfirmAccountProviderPresenterTest {
val initialState = awaitItem()
assertThat(initialState.isAccountCreation).isFalse()
assertThat(initialState.submitEnabled).isTrue()
assertThat(initialState.accountProvider.url).isEqualTo(FakeEnterpriseService.A_FAKE_HOMESERVER)
assertThat(initialState.accountProvider.url).isEqualTo(AuthenticationConfig.MATRIX_ORG_URL)
assertThat(initialState.loginFlow).isEqualTo(AsyncData.Uninitialized)
}
}

View File

@@ -8,6 +8,7 @@
package io.element.android.features.login.impl.screens.createaccount
import com.google.common.truth.Truth.assertThat
import io.element.android.appconfig.AuthenticationConfig
import io.element.android.features.enterprise.test.FakeEnterpriseService
import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource
import io.element.android.libraries.matrix.api.auth.external.ExternalSession
@@ -60,7 +61,7 @@ class DefaultMessageParserTest {
// missing homeServer
assertThat(sut.parse(validMessage.replace(""""home_server": "home_server",""", ""))).isEqualTo(
anExternalSession(
homeserverUrl = FakeEnterpriseService.A_FAKE_HOMESERVER,
homeserverUrl = AuthenticationConfig.MATRIX_ORG_URL,
)
)
}

View File

@@ -8,6 +8,7 @@
package io.element.android.features.login.impl.screens.loginpassword
import com.google.common.truth.Truth.assertThat
import io.element.android.appconfig.AuthenticationConfig
import io.element.android.features.enterprise.test.FakeEnterpriseService
import io.element.android.features.login.impl.DefaultLoginUserStory
import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource
@@ -33,7 +34,7 @@ class LoginPasswordPresenterTest {
fun `present - initial state`() = runTest {
createLoginPasswordPresenter().test {
val initialState = awaitItem()
assertThat(initialState.accountProvider.url).isEqualTo(FakeEnterpriseService.A_FAKE_HOMESERVER)
assertThat(initialState.accountProvider.url).isEqualTo(AuthenticationConfig.MATRIX_ORG_URL)
assertThat(initialState.formState).isEqualTo(LoginFormState.Default)
assertThat(initialState.loginAction).isEqualTo(AsyncData.Uninitialized)
assertThat(initialState.submitEnabled).isFalse()