diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/item/AccountProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/item/AccountProvider.kt index 6f8adff403..c09d6fab9b 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/item/AccountProvider.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/item/AccountProvider.kt @@ -21,4 +21,6 @@ data class AccountProvider constructor( val subtitle: String? = null, val isPublic: Boolean = false, val isMatrixOrg: Boolean = false, + val isValid: Boolean = false, + val supportSlidingSync: Boolean = false, ) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/item/AccountProviderProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/item/AccountProviderProvider.kt index c931c3e801..211aa4f5bc 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/item/AccountProviderProvider.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/item/AccountProviderProvider.kt @@ -23,7 +23,9 @@ open class AccountProviderProvider : PreviewParameterProvider { get() = sequenceOf( anAccountProvider(), anAccountProvider().copy(subtitle = null), - anAccountProvider().copy(title = "Other", subtitle = null, isPublic = false, isMatrixOrg = false), + anAccountProvider().copy(subtitle = null, title = "no.sliding.sync", supportSlidingSync = false), + anAccountProvider().copy(subtitle = null, title = "invalid", isValid = false, supportSlidingSync = false), + anAccountProvider().copy(subtitle = null, title = "Other", isPublic = false, isMatrixOrg = false), // Add other state here ) } @@ -33,4 +35,6 @@ fun anAccountProvider() = AccountProvider( subtitle = "Matrix.org is an open network for secure, decentralized communication.", isPublic = true, isMatrixOrg = true, + isValid = true, + supportSlidingSync = true, ) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeaccountprovider/ChangeAccountProviderPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeaccountprovider/ChangeAccountProviderPresenter.kt index 055541ca09..f80610d935 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeaccountprovider/ChangeAccountProviderPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeaccountprovider/ChangeAccountProviderPresenter.kt @@ -34,6 +34,8 @@ class ChangeAccountProviderPresenter @Inject constructor( subtitle = null, isPublic = true, isMatrixOrg = true, + isValid = true, + supportSlidingSync = true, ) ), ) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeaccountprovider/form/ChangeAccountProviderFormStateProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeaccountprovider/form/ChangeAccountProviderFormStateProvider.kt index f5232f8239..c73cbc5acb 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeaccountprovider/form/ChangeAccountProviderFormStateProvider.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeaccountprovider/form/ChangeAccountProviderFormStateProvider.kt @@ -39,23 +39,20 @@ fun aChangeAccountProviderFormState( fun aHomeserverDataList(): List { return listOf( - HomeserverData( - userInput = "matrix", - homeserverUrl = "https://matrix.org", - isWellknownValid = true, - ), - HomeserverData( - userInput = "matrix", - homeserverUrl = "https://matrix.io", - isWellknownValid = false, - ) + aHomeserverData(isWellknownValid = true, supportSlidingSync = true), + aHomeserverData(homeserverUrl = "https://no.sliding.sync", isWellknownValid = true, supportSlidingSync = false), + aHomeserverData(homeserverUrl = "https://invalid", isWellknownValid = false, supportSlidingSync = false), ) } -fun aHomeserverData(): HomeserverData { +fun aHomeserverData( + homeserverUrl: String = "https://matrix.org", + isWellknownValid: Boolean = true, + supportSlidingSync: Boolean = true, +): HomeserverData { return HomeserverData( - userInput = "matrix", - homeserverUrl = "https://matrix.org", - isWellknownValid = true, + homeserverUrl = homeserverUrl, + isWellknownValid = isWellknownValid, + supportSlidingSync = supportSlidingSync, ) } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeaccountprovider/form/ChangeAccountProviderFormView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeaccountprovider/form/ChangeAccountProviderFormView.kt index e301cfd546..16fdd10c5c 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeaccountprovider/form/ChangeAccountProviderFormView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeaccountprovider/form/ChangeAccountProviderFormView.kt @@ -160,13 +160,7 @@ fun ChangeAccountProviderFormView( } is Async.Success -> { state.userInputResult.state.forEach { homeserverData -> - val isMatrixOrg = homeserverData.homeserverUrl == "https://matrix.org" - val item = AccountProvider( - title = homeserverData.homeserverUrl.removePrefix("http://").removePrefix("https://"), - subtitle = if (isMatrixOrg) stringResource(id = R.string.screen_change_account_provider_matrix_org_subtitle) else null, - isPublic = isMatrixOrg, // There is no need to know for other servers right now - isMatrixOrg = isMatrixOrg, - ) + val item = homeserverData.toAccountProvider() AccountProviderView( item = item, onClick = { @@ -183,6 +177,19 @@ fun ChangeAccountProviderFormView( } } +@Composable +private fun HomeserverData.toAccountProvider(): AccountProvider { + val isMatrixOrg = homeserverUrl == "https://matrix.org" + return AccountProvider( + title = homeserverUrl.removePrefix("http://").removePrefix("https://"), + subtitle = if (isMatrixOrg) stringResource(id = R.string.screen_change_account_provider_matrix_org_subtitle) else null, + isPublic = isMatrixOrg, // There is no need to know for other servers right now + isMatrixOrg = isMatrixOrg, + isValid = isWellknownValid, + supportSlidingSync = supportSlidingSync, + ) +} + @Preview @Composable fun ChangeAccountProviderFormViewLightPreview(@PreviewParameter(ChangeAccountProviderFormStateProvider::class) state: ChangeAccountProviderFormState) = diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeaccountprovider/form/DefaultHomeserverResolver.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeaccountprovider/form/DefaultHomeserverResolver.kt index 73db831ac5..6f7274f35f 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeaccountprovider/form/DefaultHomeserverResolver.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeaccountprovider/form/DefaultHomeserverResolver.kt @@ -43,7 +43,7 @@ import javax.inject.Inject class DefaultHomeserverResolver @Inject constructor( private val dispatchers: CoroutineDispatchers, private val wellknownRequest: WellknownRequest, -): HomeserverResolver { +) : HomeserverResolver { private val mutableFlow: MutableStateFlow>> = MutableStateFlow(Async.Uninitialized) override fun flow(): StateFlow>> = mutableFlow @@ -52,14 +52,14 @@ class DefaultHomeserverResolver @Inject constructor( override suspend fun accept(userInput: String) { currentJob?.cancel() - val cleanedUpUserInput = userInput.trim() + val cleanedUpUserInput = userInput.trim().ensureProtocol().removeSuffix("/") mutableFlow.tryEmit(Async.Uninitialized) if (cleanedUpUserInput.length > 3) { delay(300) mutableFlow.tryEmit(Async.Loading()) withContext(dispatchers.io) { val list = getUrlCandidate(cleanedUpUserInput) - currentJob = resolveList(userInput, list) + currentJob = resolveList(cleanedUpUserInput, list) } } } @@ -69,10 +69,18 @@ class DefaultHomeserverResolver @Inject constructor( return launch { list.map { async { - val isValid = tryOrNull { wellknownRequest.execute(it) }.orFalse() + val wellKnown = tryOrNull { wellknownRequest.execute(it) } + val isValid = wellKnown?.isValid().orFalse() + val supportSlidingSync = wellKnown?.supportSlidingSync().orFalse() if (isValid) { // Emit the list as soon as possible - currentList.add(HomeserverData(userInput, it, true)) + currentList.add( + HomeserverData( + homeserverUrl = it, + isWellknownValid = true, + supportSlidingSync = supportSlidingSync + ) + ) mutableFlow.tryEmit(Async.Success(currentList)) } } @@ -85,9 +93,9 @@ class DefaultHomeserverResolver @Inject constructor( Async.Success( listOf( HomeserverData( - userInput = userInput, homeserverUrl = userInput, - isWellknownValid = false + isWellknownValid = false, + supportSlidingSync = false, ) ) ) @@ -102,18 +110,15 @@ class DefaultHomeserverResolver @Inject constructor( private fun getUrlCandidate(data: String): List { return buildList { - val s = data.ensureProtocol() - .removeSuffix("/") - // Always try what the user has entered - add(s) + add(data) - if (s.contains(".")) { + if (data.contains(".")) { // TLD detected? } else { - add("${s}.org") - add("${s}.com") - add("${s}.io") + add("${data}.org") + add("${data}.com") + add("${data}.io") } } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeaccountprovider/form/HomeserverData.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeaccountprovider/form/HomeserverData.kt index 44dd583f00..e0d7dbc0d5 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeaccountprovider/form/HomeserverData.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeaccountprovider/form/HomeserverData.kt @@ -17,10 +17,10 @@ package io.element.android.features.login.impl.changeaccountprovider.form data class HomeserverData constructor( - // What the user has entered - val userInput: String, // The computed homeserver url, for which a wellknown file has been retrieved, or just a valid Url val homeserverUrl: String, // True if a wellknown file has been found and is valid. If false, it means that the [homeserverUrl] is valid val isWellknownValid: Boolean, + // True if a wellknown file has been found and is valid and is claiming a sliding sync Url + val supportSlidingSync: Boolean, ) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeaccountprovider/form/network/WellKnown.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeaccountprovider/form/network/WellKnown.kt index 60006e7530..283e8eb496 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeaccountprovider/form/network/WellKnown.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeaccountprovider/form/network/WellKnown.kt @@ -16,6 +16,7 @@ package io.element.android.features.login.impl.changeaccountprovider.form.network +import io.element.android.libraries.core.bool.orFalse import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -28,6 +29,9 @@ import kotlinx.serialization.Serializable * }, * "m.identity_server": { * "base_url": "https://vector.im" + * }, + * "org.matrix.msc3575.proxy": { + * "url": "https://slidingsync.lab.matrix.org" * } * } * @@ -40,4 +44,15 @@ data class WellKnown( @SerialName("m.identity_server") val identityServer: WellKnownBaseConfig? = null, -) + + @SerialName("org.matrix.msc3575.proxy") + val slidingSyncProxy: WellKnownSlidingSyncConfig? = null, +) { + fun isValid(): Boolean { + return homeServer?.baseURL?.isNotBlank().orFalse() + } + + fun supportSlidingSync(): Boolean { + return slidingSyncProxy?.url?.isNotBlank().orFalse() + } +} diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeaccountprovider/form/network/WellKnownSlidyincSyncConfig.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeaccountprovider/form/network/WellKnownSlidyincSyncConfig.kt new file mode 100644 index 0000000000..4ff8633d0d --- /dev/null +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeaccountprovider/form/network/WellKnownSlidyincSyncConfig.kt @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023 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.features.login.impl.changeaccountprovider.form.network + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class WellKnownSlidingSyncConfig( + @SerialName("url") + val url: String? = null, +) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeaccountprovider/form/network/WellknownRequest.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeaccountprovider/form/network/WellknownRequest.kt index 709eea0255..1f18edc695 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeaccountprovider/form/network/WellknownRequest.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeaccountprovider/form/network/WellknownRequest.kt @@ -15,32 +15,19 @@ */ package io.element.android.features.login.impl.changeaccountprovider.form.network -import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.network.RetrofitFactory -import timber.log.Timber import javax.inject.Inject class WellknownRequest @Inject constructor( private val retrofitFactory: RetrofitFactory, ) { /** - * Return true if the wellknown can be retrieved and is valid. + * Return the WellKnown data, if found. * @param baseUrl for instance https://matrix.org */ - suspend fun execute(baseUrl: String): Boolean { + suspend fun execute(baseUrl: String): WellKnown { val wellknownApi = retrofitFactory.create(baseUrl) .create(WellknownAPI::class.java) - - return try { - val response = wellknownApi.getWellKnown() - response.isValid() - } catch (throwable: Throwable) { - Timber.e(throwable) - false - } + return wellknownApi.getWellKnown() } } - -private fun WellKnown.isValid(): Boolean { - return homeServer?.baseURL?.isNotBlank().orFalse() -} diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeaccountprovider/ChangeAccountProviderPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeaccountprovider/ChangeAccountProviderPresenterTest.kt index add636a4c4..911ecaab78 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeaccountprovider/ChangeAccountProviderPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeaccountprovider/ChangeAccountProviderPresenterTest.kt @@ -40,6 +40,8 @@ class ChangeAccountProviderPresenterTest { subtitle = null, isPublic = true, isMatrixOrg = true, + isValid = true, + supportSlidingSync = true, ) ) )