mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-11-02 21:32:34 +00:00
Save the account provider to history on successful sign-in
This commit is contained in:
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.login.impl.accountprovider
|
||||
|
||||
import dev.zacsweers.metro.Inject
|
||||
import io.element.android.libraries.preferences.api.store.AppPreferencesStore
|
||||
|
||||
/**
|
||||
* Persist the account provider the user just authenticated against, so that it can be offered as
|
||||
* the default (see [AccountProviderDataSource]) and as an autocomplete suggestion on the next
|
||||
* sign-in. To be called from the manual sign-in / account creation flows only, on success.
|
||||
*
|
||||
* See [AppPreferencesStore.addHomeserverToHistory].
|
||||
*/
|
||||
@Inject
|
||||
class SaveAccountProviderToHistory(
|
||||
private val accountProviderDataSource: AccountProviderDataSource,
|
||||
private val appPreferencesStore: AppPreferencesStore,
|
||||
) {
|
||||
suspend operator fun invoke() {
|
||||
appPreferencesStore.addHomeserverToHistory(accountProviderDataSource.flow.value.url)
|
||||
}
|
||||
}
|
||||
+3
@@ -13,6 +13,7 @@ import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import dev.zacsweers.metro.Inject
|
||||
import io.element.android.features.login.impl.accountprovider.SaveAccountProviderToHistory
|
||||
import io.element.android.features.login.impl.error.ChangeServerError
|
||||
import io.element.android.features.login.impl.localnetwork.LocalNetworkPermissionGate
|
||||
import io.element.android.features.login.impl.screens.chooseaccountprovider.ChooseAccountProviderPresenter
|
||||
@@ -41,6 +42,7 @@ class LoginModePresenter(
|
||||
private val authenticationService: MatrixAuthenticationService,
|
||||
private val webClientUrlForAuthenticationRetriever: WebClientUrlForAuthenticationRetriever,
|
||||
private val localNetworkPermissionGate: LocalNetworkPermissionGate,
|
||||
private val saveAccountProviderToHistory: SaveAccountProviderToHistory,
|
||||
) : Presenter<LoginModeState> {
|
||||
@Composable
|
||||
override fun present(): LoginModeState {
|
||||
@@ -120,6 +122,7 @@ class LoginModePresenter(
|
||||
.onSuccess { loginMode.value = AsyncData.Uninitialized }
|
||||
.onFailure { loginMode.value = AsyncData.Failure(it) }
|
||||
is OAuthAction.Success -> authenticationService.loginWithOAuth(action.url)
|
||||
.onSuccess { saveAccountProviderToHistory() }
|
||||
.onFailure { loginMode.value = AsyncData.Failure(it) }
|
||||
}
|
||||
oAuthActionFlow.reset()
|
||||
|
||||
+3
@@ -17,6 +17,7 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||
import dev.zacsweers.metro.Assisted
|
||||
import dev.zacsweers.metro.AssistedFactory
|
||||
import dev.zacsweers.metro.AssistedInject
|
||||
import io.element.android.features.login.impl.accountprovider.SaveAccountProviderToHistory
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.core.extensions.flatMap
|
||||
@@ -33,6 +34,7 @@ class CreateAccountPresenter(
|
||||
private val authenticationService: MatrixAuthenticationService,
|
||||
private val messageParser: MessageParser,
|
||||
private val buildMeta: BuildMeta,
|
||||
private val saveAccountProviderToHistory: SaveAccountProviderToHistory,
|
||||
) : Presenter<CreateAccountState> {
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
@@ -74,6 +76,7 @@ class CreateAccountPresenter(
|
||||
}.flatMap { externalSession ->
|
||||
authenticationService.importCreatedSession(externalSession)
|
||||
}.onSuccess { sessionId ->
|
||||
saveAccountProviderToHistory()
|
||||
loggedInState.value = AsyncAction.Success(sessionId)
|
||||
}.onFailure { failure ->
|
||||
loggedInState.value = AsyncAction.Failure(failure)
|
||||
|
||||
+3
@@ -20,6 +20,7 @@ import dev.zacsweers.metro.Assisted
|
||||
import dev.zacsweers.metro.AssistedFactory
|
||||
import dev.zacsweers.metro.AssistedInject
|
||||
import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource
|
||||
import io.element.android.features.login.impl.accountprovider.SaveAccountProviderToHistory
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
|
||||
@@ -33,6 +34,7 @@ class LoginPasswordPresenter(
|
||||
private val initialLogin: String,
|
||||
private val authenticationService: MatrixAuthenticationService,
|
||||
private val accountProviderDataSource: AccountProviderDataSource,
|
||||
private val saveAccountProviderToHistory: SaveAccountProviderToHistory,
|
||||
) : Presenter<LoginPasswordState> {
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
@@ -83,6 +85,7 @@ class LoginPasswordPresenter(
|
||||
loggedInState.value = AsyncData.Loading()
|
||||
authenticationService.login(formState.login.trim(), formState.password)
|
||||
.onSuccess { sessionId ->
|
||||
saveAccountProviderToHistory()
|
||||
loggedInState.value = AsyncData.Success(sessionId)
|
||||
}
|
||||
.onFailure { failure ->
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.login.impl.accountprovider
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class SaveAccountProviderToHistoryTest {
|
||||
@get:Rule
|
||||
val warmUpRule = WarmUpRule()
|
||||
|
||||
@Test
|
||||
fun `invoke saves the current account provider url to history`() = runTest {
|
||||
val appPreferencesStore = InMemoryAppPreferencesStore()
|
||||
val accountProviderDataSource = anAccountProviderDataSource()
|
||||
accountProviderDataSource.setUrl("https://example.com")
|
||||
val sut = SaveAccountProviderToHistory(accountProviderDataSource, appPreferencesStore)
|
||||
|
||||
sut()
|
||||
|
||||
assertThat(appPreferencesStore.getHomeserverHistoryFlow().first())
|
||||
.containsExactly("https://example.com")
|
||||
}
|
||||
}
|
||||
+30
@@ -9,6 +9,10 @@
|
||||
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.login.impl.accountprovider.AccountProviderDataSource
|
||||
import io.element.android.features.login.impl.accountprovider.SaveAccountProviderToHistory
|
||||
import io.element.android.features.login.impl.accountprovider.anAccountProviderDataSource
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.core.meta.BuildMeta
|
||||
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
|
||||
@@ -19,10 +23,13 @@ import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService
|
||||
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
||||
import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService
|
||||
import io.element.android.libraries.preferences.api.store.AppPreferencesStore
|
||||
import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
import io.element.android.tests.testutils.lambda.value
|
||||
import io.element.android.tests.testutils.test
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
@@ -94,6 +101,26 @@ class CreateAccountPresenterTest {
|
||||
lambda.assertions().isCalledOnce().with(value("aMessage"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - successful account creation saves the account provider to history`() = runTest {
|
||||
val appPreferencesStore = InMemoryAppPreferencesStore()
|
||||
val presenter = createPresenter(
|
||||
authenticationService = FakeMatrixAuthenticationService(
|
||||
importCreatedSessionLambda = { Result.success(A_SESSION_ID) }
|
||||
),
|
||||
messageParser = FakeMessageParser { anExternalSession() },
|
||||
appPreferencesStore = appPreferencesStore,
|
||||
)
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(CreateAccountEvents.OnMessageReceived("aMessage"))
|
||||
assertThat(awaitItem().createAction.isLoading()).isTrue()
|
||||
assertThat(awaitItem().createAction.dataOrNull()).isEqualTo(A_SESSION_ID)
|
||||
assertThat(appPreferencesStore.getHomeserverHistoryFlow().first())
|
||||
.containsExactly(AuthenticationConfig.MATRIX_ORG_URL)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - receiving a message able to be parsed but error in importing change the state to error`() = runTest {
|
||||
val presenter = createPresenter(
|
||||
@@ -115,10 +142,13 @@ class CreateAccountPresenterTest {
|
||||
authenticationService: MatrixAuthenticationService = FakeMatrixAuthenticationService(),
|
||||
messageParser: MessageParser = FakeMessageParser(),
|
||||
buildMeta: BuildMeta = aBuildMeta(),
|
||||
appPreferencesStore: AppPreferencesStore = InMemoryAppPreferencesStore(),
|
||||
accountProviderDataSource: AccountProviderDataSource = anAccountProviderDataSource(),
|
||||
) = CreateAccountPresenter(
|
||||
url = url,
|
||||
authenticationService = authenticationService,
|
||||
messageParser = messageParser,
|
||||
buildMeta = buildMeta,
|
||||
saveAccountProviderToHistory = SaveAccountProviderToHistory(accountProviderDataSource, appPreferencesStore),
|
||||
)
|
||||
}
|
||||
|
||||
+32
@@ -11,6 +11,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.login.impl.accountprovider.AccountProviderDataSource
|
||||
import io.element.android.features.login.impl.accountprovider.SaveAccountProviderToHistory
|
||||
import io.element.android.features.login.impl.accountprovider.anAccountProviderDataSource
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
@@ -21,8 +22,11 @@ import io.element.android.libraries.matrix.test.A_USER_NAME
|
||||
import io.element.android.libraries.matrix.test.A_USER_NAME_2
|
||||
import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService
|
||||
import io.element.android.libraries.matrix.test.auth.aMatrixHomeServerDetails
|
||||
import io.element.android.libraries.preferences.api.store.AppPreferencesStore
|
||||
import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
import io.element.android.tests.testutils.test
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
@@ -101,6 +105,32 @@ class LoginPasswordPresenterTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - successful login saves the account provider to history`() = runTest {
|
||||
val authenticationService = FakeMatrixAuthenticationService(
|
||||
setHomeserverResult = {
|
||||
Result.success(aMatrixHomeServerDetails())
|
||||
},
|
||||
)
|
||||
val appPreferencesStore = InMemoryAppPreferencesStore()
|
||||
createLoginPasswordPresenter(
|
||||
authenticationService = authenticationService,
|
||||
appPreferencesStore = appPreferencesStore,
|
||||
).test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink.invoke(LoginPasswordEvents.SetLogin(A_USER_NAME))
|
||||
initialState.eventSink.invoke(LoginPasswordEvents.SetPassword(A_PASSWORD))
|
||||
skipItems(1)
|
||||
val loginAndPasswordState = awaitItem()
|
||||
loginAndPasswordState.eventSink.invoke(LoginPasswordEvents.Submit)
|
||||
skipItems(1)
|
||||
val loggedInState = awaitItem()
|
||||
assertThat(loggedInState.loginAction).isEqualTo(AsyncData.Success(A_SESSION_ID))
|
||||
assertThat(appPreferencesStore.getHomeserverHistoryFlow().first())
|
||||
.containsExactly(AuthenticationConfig.MATRIX_ORG_URL)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - submit with error`() = runTest {
|
||||
val authenticationService = FakeMatrixAuthenticationService(
|
||||
@@ -158,9 +188,11 @@ class LoginPasswordPresenterTest {
|
||||
initialLogin: String = "",
|
||||
authenticationService: FakeMatrixAuthenticationService = FakeMatrixAuthenticationService(),
|
||||
accountProviderDataSource: AccountProviderDataSource = anAccountProviderDataSource(),
|
||||
appPreferencesStore: AppPreferencesStore = InMemoryAppPreferencesStore(),
|
||||
): LoginPasswordPresenter = LoginPasswordPresenter(
|
||||
initialLogin = initialLogin,
|
||||
authenticationService = authenticationService,
|
||||
accountProviderDataSource = accountProviderDataSource,
|
||||
saveAccountProviderToHistory = SaveAccountProviderToHistory(accountProviderDataSource, appPreferencesStore),
|
||||
)
|
||||
}
|
||||
|
||||
+5
@@ -15,6 +15,7 @@ import io.element.android.features.enterprise.api.EnterpriseService
|
||||
import io.element.android.features.enterprise.test.FakeEnterpriseService
|
||||
import io.element.android.features.login.impl.accesscontrol.DefaultAccountProviderAccessControl
|
||||
import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource
|
||||
import io.element.android.features.login.impl.accountprovider.SaveAccountProviderToHistory
|
||||
import io.element.android.features.login.impl.accountprovider.anAccountProviderDataSource
|
||||
import io.element.android.features.login.impl.localnetwork.LocalNetworkPermissionGate
|
||||
import io.element.android.features.login.impl.login.LoginModePresenter
|
||||
@@ -36,6 +37,7 @@ import io.element.android.libraries.matrix.test.core.aBuildMeta
|
||||
import io.element.android.libraries.oauth.api.OAuthActionFlow
|
||||
import io.element.android.libraries.oauth.test.customtab.FakeOAuthActionFlow
|
||||
import io.element.android.libraries.permissions.api.localnetwork.LocalNetworkPermissionAdvisor
|
||||
import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore
|
||||
import io.element.android.libraries.sessionstorage.api.SessionStore
|
||||
import io.element.android.libraries.sessionstorage.test.InMemorySessionStore
|
||||
import io.element.android.libraries.sessionstorage.test.aSessionData
|
||||
@@ -322,6 +324,8 @@ fun createLoginModePresenter(
|
||||
io.element.android.libraries.permissions.test.FakeLocalNetworkPermissionAdvisor(),
|
||||
permissionsPresenterFactory: io.element.android.libraries.permissions.api.PermissionsPresenter.Factory =
|
||||
io.element.android.libraries.permissions.test.FakePermissionsPresenterFactory(),
|
||||
saveAccountProviderToHistory: SaveAccountProviderToHistory =
|
||||
SaveAccountProviderToHistory(anAccountProviderDataSource(), InMemoryAppPreferencesStore()),
|
||||
): LoginModePresenter = LoginModePresenter(
|
||||
oAuthActionFlow = oAuthActionFlow,
|
||||
authenticationService = authenticationService,
|
||||
@@ -330,4 +334,5 @@ fun createLoginModePresenter(
|
||||
advisor = localNetworkPermissionAdvisor,
|
||||
permissionsPresenterFactory = permissionsPresenterFactory,
|
||||
),
|
||||
saveAccountProviderToHistory = saveAccountProviderToHistory,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user