Get more info from WellKnown request.
This commit is contained in:
parent
248d0bad83
commit
48a95c6e01
11 changed files with 103 additions and 56 deletions
|
|
@ -21,4 +21,6 @@ data class AccountProvider constructor(
|
||||||
val subtitle: String? = null,
|
val subtitle: String? = null,
|
||||||
val isPublic: Boolean = false,
|
val isPublic: Boolean = false,
|
||||||
val isMatrixOrg: Boolean = false,
|
val isMatrixOrg: Boolean = false,
|
||||||
|
val isValid: Boolean = false,
|
||||||
|
val supportSlidingSync: Boolean = false,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,9 @@ open class AccountProviderProvider : PreviewParameterProvider<AccountProvider> {
|
||||||
get() = sequenceOf(
|
get() = sequenceOf(
|
||||||
anAccountProvider(),
|
anAccountProvider(),
|
||||||
anAccountProvider().copy(subtitle = null),
|
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
|
// Add other state here
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -33,4 +35,6 @@ fun anAccountProvider() = AccountProvider(
|
||||||
subtitle = "Matrix.org is an open network for secure, decentralized communication.",
|
subtitle = "Matrix.org is an open network for secure, decentralized communication.",
|
||||||
isPublic = true,
|
isPublic = true,
|
||||||
isMatrixOrg = true,
|
isMatrixOrg = true,
|
||||||
|
isValid = true,
|
||||||
|
supportSlidingSync = true,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,8 @@ class ChangeAccountProviderPresenter @Inject constructor(
|
||||||
subtitle = null,
|
subtitle = null,
|
||||||
isPublic = true,
|
isPublic = true,
|
||||||
isMatrixOrg = true,
|
isMatrixOrg = true,
|
||||||
|
isValid = true,
|
||||||
|
supportSlidingSync = true,
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -39,23 +39,20 @@ fun aChangeAccountProviderFormState(
|
||||||
|
|
||||||
fun aHomeserverDataList(): List<HomeserverData> {
|
fun aHomeserverDataList(): List<HomeserverData> {
|
||||||
return listOf(
|
return listOf(
|
||||||
HomeserverData(
|
aHomeserverData(isWellknownValid = true, supportSlidingSync = true),
|
||||||
userInput = "matrix",
|
aHomeserverData(homeserverUrl = "https://no.sliding.sync", isWellknownValid = true, supportSlidingSync = false),
|
||||||
homeserverUrl = "https://matrix.org",
|
aHomeserverData(homeserverUrl = "https://invalid", isWellknownValid = false, supportSlidingSync = false),
|
||||||
isWellknownValid = true,
|
|
||||||
),
|
|
||||||
HomeserverData(
|
|
||||||
userInput = "matrix",
|
|
||||||
homeserverUrl = "https://matrix.io",
|
|
||||||
isWellknownValid = false,
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun aHomeserverData(): HomeserverData {
|
fun aHomeserverData(
|
||||||
|
homeserverUrl: String = "https://matrix.org",
|
||||||
|
isWellknownValid: Boolean = true,
|
||||||
|
supportSlidingSync: Boolean = true,
|
||||||
|
): HomeserverData {
|
||||||
return HomeserverData(
|
return HomeserverData(
|
||||||
userInput = "matrix",
|
homeserverUrl = homeserverUrl,
|
||||||
homeserverUrl = "https://matrix.org",
|
isWellknownValid = isWellknownValid,
|
||||||
isWellknownValid = true,
|
supportSlidingSync = supportSlidingSync,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -160,13 +160,7 @@ fun ChangeAccountProviderFormView(
|
||||||
}
|
}
|
||||||
is Async.Success -> {
|
is Async.Success -> {
|
||||||
state.userInputResult.state.forEach { homeserverData ->
|
state.userInputResult.state.forEach { homeserverData ->
|
||||||
val isMatrixOrg = homeserverData.homeserverUrl == "https://matrix.org"
|
val item = homeserverData.toAccountProvider()
|
||||||
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,
|
|
||||||
)
|
|
||||||
AccountProviderView(
|
AccountProviderView(
|
||||||
item = item,
|
item = item,
|
||||||
onClick = {
|
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
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
fun ChangeAccountProviderFormViewLightPreview(@PreviewParameter(ChangeAccountProviderFormStateProvider::class) state: ChangeAccountProviderFormState) =
|
fun ChangeAccountProviderFormViewLightPreview(@PreviewParameter(ChangeAccountProviderFormStateProvider::class) state: ChangeAccountProviderFormState) =
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ import javax.inject.Inject
|
||||||
class DefaultHomeserverResolver @Inject constructor(
|
class DefaultHomeserverResolver @Inject constructor(
|
||||||
private val dispatchers: CoroutineDispatchers,
|
private val dispatchers: CoroutineDispatchers,
|
||||||
private val wellknownRequest: WellknownRequest,
|
private val wellknownRequest: WellknownRequest,
|
||||||
): HomeserverResolver {
|
) : HomeserverResolver {
|
||||||
private val mutableFlow: MutableStateFlow<Async<List<HomeserverData>>> = MutableStateFlow(Async.Uninitialized)
|
private val mutableFlow: MutableStateFlow<Async<List<HomeserverData>>> = MutableStateFlow(Async.Uninitialized)
|
||||||
|
|
||||||
override fun flow(): StateFlow<Async<List<HomeserverData>>> = mutableFlow
|
override fun flow(): StateFlow<Async<List<HomeserverData>>> = mutableFlow
|
||||||
|
|
@ -52,14 +52,14 @@ class DefaultHomeserverResolver @Inject constructor(
|
||||||
|
|
||||||
override suspend fun accept(userInput: String) {
|
override suspend fun accept(userInput: String) {
|
||||||
currentJob?.cancel()
|
currentJob?.cancel()
|
||||||
val cleanedUpUserInput = userInput.trim()
|
val cleanedUpUserInput = userInput.trim().ensureProtocol().removeSuffix("/")
|
||||||
mutableFlow.tryEmit(Async.Uninitialized)
|
mutableFlow.tryEmit(Async.Uninitialized)
|
||||||
if (cleanedUpUserInput.length > 3) {
|
if (cleanedUpUserInput.length > 3) {
|
||||||
delay(300)
|
delay(300)
|
||||||
mutableFlow.tryEmit(Async.Loading())
|
mutableFlow.tryEmit(Async.Loading())
|
||||||
withContext(dispatchers.io) {
|
withContext(dispatchers.io) {
|
||||||
val list = getUrlCandidate(cleanedUpUserInput)
|
val list = getUrlCandidate(cleanedUpUserInput)
|
||||||
currentJob = resolveList(userInput, list)
|
currentJob = resolveList(cleanedUpUserInput, list)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -69,10 +69,18 @@ class DefaultHomeserverResolver @Inject constructor(
|
||||||
return launch {
|
return launch {
|
||||||
list.map {
|
list.map {
|
||||||
async {
|
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) {
|
if (isValid) {
|
||||||
// Emit the list as soon as possible
|
// 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))
|
mutableFlow.tryEmit(Async.Success(currentList))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -85,9 +93,9 @@ class DefaultHomeserverResolver @Inject constructor(
|
||||||
Async.Success(
|
Async.Success(
|
||||||
listOf(
|
listOf(
|
||||||
HomeserverData(
|
HomeserverData(
|
||||||
userInput = userInput,
|
|
||||||
homeserverUrl = userInput,
|
homeserverUrl = userInput,
|
||||||
isWellknownValid = false
|
isWellknownValid = false,
|
||||||
|
supportSlidingSync = false,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
@ -102,18 +110,15 @@ class DefaultHomeserverResolver @Inject constructor(
|
||||||
|
|
||||||
private fun getUrlCandidate(data: String): List<String> {
|
private fun getUrlCandidate(data: String): List<String> {
|
||||||
return buildList {
|
return buildList {
|
||||||
val s = data.ensureProtocol()
|
|
||||||
.removeSuffix("/")
|
|
||||||
|
|
||||||
// Always try what the user has entered
|
// Always try what the user has entered
|
||||||
add(s)
|
add(data)
|
||||||
|
|
||||||
if (s.contains(".")) {
|
if (data.contains(".")) {
|
||||||
// TLD detected?
|
// TLD detected?
|
||||||
} else {
|
} else {
|
||||||
add("${s}.org")
|
add("${data}.org")
|
||||||
add("${s}.com")
|
add("${data}.com")
|
||||||
add("${s}.io")
|
add("${data}.io")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,10 @@
|
||||||
package io.element.android.features.login.impl.changeaccountprovider.form
|
package io.element.android.features.login.impl.changeaccountprovider.form
|
||||||
|
|
||||||
data class HomeserverData constructor(
|
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
|
// The computed homeserver url, for which a wellknown file has been retrieved, or just a valid Url
|
||||||
val homeserverUrl: String,
|
val homeserverUrl: String,
|
||||||
// True if a wellknown file has been found and is valid. If false, it means that the [homeserverUrl] is valid
|
// True if a wellknown file has been found and is valid. If false, it means that the [homeserverUrl] is valid
|
||||||
val isWellknownValid: Boolean,
|
val isWellknownValid: Boolean,
|
||||||
|
// True if a wellknown file has been found and is valid and is claiming a sliding sync Url
|
||||||
|
val supportSlidingSync: Boolean,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package io.element.android.features.login.impl.changeaccountprovider.form.network
|
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.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
|
@ -28,6 +29,9 @@ import kotlinx.serialization.Serializable
|
||||||
* },
|
* },
|
||||||
* "m.identity_server": {
|
* "m.identity_server": {
|
||||||
* "base_url": "https://vector.im"
|
* "base_url": "https://vector.im"
|
||||||
|
* },
|
||||||
|
* "org.matrix.msc3575.proxy": {
|
||||||
|
* "url": "https://slidingsync.lab.matrix.org"
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
* </pre>
|
* </pre>
|
||||||
|
|
@ -40,4 +44,15 @@ data class WellKnown(
|
||||||
|
|
||||||
@SerialName("m.identity_server")
|
@SerialName("m.identity_server")
|
||||||
val identityServer: WellKnownBaseConfig? = null,
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
)
|
||||||
|
|
@ -15,32 +15,19 @@
|
||||||
*/
|
*/
|
||||||
package io.element.android.features.login.impl.changeaccountprovider.form.network
|
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 io.element.android.libraries.network.RetrofitFactory
|
||||||
import timber.log.Timber
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class WellknownRequest @Inject constructor(
|
class WellknownRequest @Inject constructor(
|
||||||
private val retrofitFactory: RetrofitFactory,
|
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
|
* @param baseUrl for instance https://matrix.org
|
||||||
*/
|
*/
|
||||||
suspend fun execute(baseUrl: String): Boolean {
|
suspend fun execute(baseUrl: String): WellKnown {
|
||||||
val wellknownApi = retrofitFactory.create(baseUrl)
|
val wellknownApi = retrofitFactory.create(baseUrl)
|
||||||
.create(WellknownAPI::class.java)
|
.create(WellknownAPI::class.java)
|
||||||
|
return wellknownApi.getWellKnown()
|
||||||
return try {
|
|
||||||
val response = wellknownApi.getWellKnown()
|
|
||||||
response.isValid()
|
|
||||||
} catch (throwable: Throwable) {
|
|
||||||
Timber.e(throwable)
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun WellKnown.isValid(): Boolean {
|
|
||||||
return homeServer?.baseURL?.isNotBlank().orFalse()
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,8 @@ class ChangeAccountProviderPresenterTest {
|
||||||
subtitle = null,
|
subtitle = null,
|
||||||
isPublic = true,
|
isPublic = true,
|
||||||
isMatrixOrg = true,
|
isMatrixOrg = true,
|
||||||
|
isValid = true,
|
||||||
|
supportSlidingSync = true,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue