Remove Element Call feature flag and revert base URL to call.element.io (#1809)
Co-authored-by: ElementBot <benoitm+elementbot@element.io>
This commit is contained in:
parent
14cf2b0440
commit
5701e98d77
25 changed files with 164 additions and 191 deletions
|
|
@ -19,5 +19,4 @@ package io.element.android.features.preferences.impl.advanced
|
|||
sealed interface AdvancedSettingsEvents {
|
||||
data class SetRichTextEditorEnabled(val enabled: Boolean) : AdvancedSettingsEvents
|
||||
data class SetDeveloperModeEnabled(val enabled: Boolean) : AdvancedSettingsEvents
|
||||
data class SetCustomElementCallBaseUrl(val baseUrl: String?) : AdvancedSettingsEvents
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,25 +17,16 @@
|
|||
package io.element.android.features.preferences.impl.advanced
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import io.element.android.appconfig.ElementCallConfig
|
||||
import io.element.android.features.preferences.api.store.PreferencesStore
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||
import kotlinx.coroutines.launch
|
||||
import java.net.URL
|
||||
import javax.inject.Inject
|
||||
|
||||
class AdvancedSettingsPresenter @Inject constructor(
|
||||
private val preferencesStore: PreferencesStore,
|
||||
private val featureFlagService: FeatureFlagService,
|
||||
) : Presenter<AdvancedSettingsState> {
|
||||
|
||||
@Composable
|
||||
|
|
@ -47,14 +38,6 @@ class AdvancedSettingsPresenter @Inject constructor(
|
|||
val isDeveloperModeEnabled by preferencesStore
|
||||
.isDeveloperModeEnabledFlow()
|
||||
.collectAsState(initial = false)
|
||||
val customElementCallBaseUrl by preferencesStore
|
||||
.getCustomElementCallBaseUrlFlow()
|
||||
.collectAsState(initial = null)
|
||||
|
||||
var canDisplayElementCallSettings by remember { mutableStateOf(false) }
|
||||
LaunchedEffect(Unit) {
|
||||
canDisplayElementCallSettings = featureFlagService.isFeatureEnabled(FeatureFlags.InRoomCalls)
|
||||
}
|
||||
|
||||
fun handleEvents(event: AdvancedSettingsEvents) {
|
||||
when (event) {
|
||||
|
|
@ -64,34 +47,13 @@ class AdvancedSettingsPresenter @Inject constructor(
|
|||
is AdvancedSettingsEvents.SetDeveloperModeEnabled -> localCoroutineScope.launch {
|
||||
preferencesStore.setDeveloperModeEnabled(event.enabled)
|
||||
}
|
||||
is AdvancedSettingsEvents.SetCustomElementCallBaseUrl -> localCoroutineScope.launch {
|
||||
// If the URL is either empty or the default one, we want to save 'null' to remove the custom URL
|
||||
val urlToSave = event.baseUrl.takeIf { !it.isNullOrEmpty() && it != ElementCallConfig.DEFAULT_BASE_URL }
|
||||
preferencesStore.setCustomElementCallBaseUrl(urlToSave)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return AdvancedSettingsState(
|
||||
isRichTextEditorEnabled = isRichTextEditorEnabled,
|
||||
isDeveloperModeEnabled = isDeveloperModeEnabled,
|
||||
customElementCallBaseUrlState = if (canDisplayElementCallSettings) {
|
||||
CustomElementCallBaseUrlState(
|
||||
baseUrl = customElementCallBaseUrl,
|
||||
defaultUrl = ElementCallConfig.DEFAULT_BASE_URL,
|
||||
validator = ::customElementCallUrlValidator,
|
||||
)
|
||||
} else null,
|
||||
eventSink = { handleEvents(it) }
|
||||
)
|
||||
}
|
||||
|
||||
private fun customElementCallUrlValidator(url: String?): Boolean {
|
||||
return runCatching {
|
||||
if (url.isNullOrEmpty()) return@runCatching
|
||||
val parsedUrl = URL(url)
|
||||
if (parsedUrl.protocol !in listOf("http", "https")) error("Incorrect protocol")
|
||||
if (parsedUrl.host.isNullOrBlank()) error("Missing host")
|
||||
}.isSuccess
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,12 +19,5 @@ package io.element.android.features.preferences.impl.advanced
|
|||
data class AdvancedSettingsState(
|
||||
val isRichTextEditorEnabled: Boolean,
|
||||
val isDeveloperModeEnabled: Boolean,
|
||||
val customElementCallBaseUrlState: CustomElementCallBaseUrlState?,
|
||||
val eventSink: (AdvancedSettingsEvents) -> Unit
|
||||
)
|
||||
|
||||
data class CustomElementCallBaseUrlState(
|
||||
val baseUrl: String?,
|
||||
val defaultUrl: String,
|
||||
val validator: (String?) -> Boolean,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -24,17 +24,14 @@ open class AdvancedSettingsStateProvider : PreviewParameterProvider<AdvancedSett
|
|||
aAdvancedSettingsState(),
|
||||
aAdvancedSettingsState(isRichTextEditorEnabled = true),
|
||||
aAdvancedSettingsState(isDeveloperModeEnabled = true),
|
||||
aAdvancedSettingsState(customElementCallBaseUrl = "https://call.element.io"),
|
||||
)
|
||||
}
|
||||
|
||||
fun aAdvancedSettingsState(
|
||||
isRichTextEditorEnabled: Boolean = false,
|
||||
isDeveloperModeEnabled: Boolean = false,
|
||||
customElementCallBaseUrl: String? = null,
|
||||
) = AdvancedSettingsState(
|
||||
isRichTextEditorEnabled = isRichTextEditorEnabled,
|
||||
isDeveloperModeEnabled = isDeveloperModeEnabled,
|
||||
customElementCallBaseUrlState = customElementCallBaseUrl?.let { CustomElementCallBaseUrlState(it, "https://call.element.io") { true } },
|
||||
eventSink = {}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -16,16 +16,13 @@
|
|||
|
||||
package io.element.android.features.preferences.impl.advanced
|
||||
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import io.element.android.features.preferences.impl.R
|
||||
import io.element.android.libraries.designsystem.components.preferences.PreferencePage
|
||||
import io.element.android.libraries.designsystem.components.preferences.PreferenceSwitch
|
||||
import io.element.android.libraries.designsystem.components.preferences.PreferenceTextField
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
|
@ -36,11 +33,6 @@ fun AdvancedSettingsView(
|
|||
onBackPressed: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
fun isUsingDefaultUrl(value: String?): Boolean {
|
||||
val defaultUrl = state.customElementCallBaseUrlState?.defaultUrl ?: return false
|
||||
return value.isNullOrEmpty() || value == defaultUrl
|
||||
}
|
||||
|
||||
PreferencePage(
|
||||
modifier = modifier,
|
||||
onBackPressed = onBackPressed,
|
||||
|
|
@ -58,23 +50,6 @@ fun AdvancedSettingsView(
|
|||
isChecked = state.isDeveloperModeEnabled,
|
||||
onCheckedChange = { state.eventSink(AdvancedSettingsEvents.SetDeveloperModeEnabled(it)) },
|
||||
)
|
||||
state.customElementCallBaseUrlState?.let { callUrlState ->
|
||||
val supportingText = if (isUsingDefaultUrl(callUrlState.baseUrl)) {
|
||||
stringResource(R.string.screen_advanced_settings_element_call_base_url_description)
|
||||
} else {
|
||||
callUrlState.baseUrl
|
||||
}
|
||||
PreferenceTextField(
|
||||
headline = stringResource(R.string.screen_advanced_settings_element_call_base_url),
|
||||
value = callUrlState.baseUrl ?: callUrlState.defaultUrl,
|
||||
supportingText = supportingText,
|
||||
validation = callUrlState.validator,
|
||||
onValidationErrorMessage = stringResource(R.string.screen_advanced_settings_element_call_base_url_validation_error),
|
||||
displayValue = { value -> !isUsingDefaultUrl(value) },
|
||||
keyboardOptions = KeyboardOptions.Default.copy(autoCorrect = false, keyboardType = KeyboardType.Uri),
|
||||
onChange = { state.eventSink(AdvancedSettingsEvents.SetCustomElementCallBaseUrl(it)) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,5 +20,6 @@ import io.element.android.libraries.featureflag.ui.model.FeatureUiModel
|
|||
|
||||
sealed interface DeveloperSettingsEvents {
|
||||
data class UpdateEnabledFeature(val feature: FeatureUiModel, val isEnabled: Boolean) : DeveloperSettingsEvents
|
||||
data class SetCustomElementCallBaseUrl(val baseUrl: String?) : DeveloperSettingsEvents
|
||||
data object ClearCache: DeveloperSettingsEvents
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,12 +19,16 @@ package io.element.android.features.preferences.impl.developer
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.key
|
||||
import androidx.compose.runtime.mutableStateMapOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateMap
|
||||
import io.element.android.appconfig.ElementCallConfig
|
||||
import io.element.android.features.preferences.api.store.PreferencesStore
|
||||
import io.element.android.features.preferences.impl.tasks.ClearCacheUseCase
|
||||
import io.element.android.features.preferences.impl.tasks.ComputeCacheSizeUseCase
|
||||
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesPresenter
|
||||
|
|
@ -39,6 +43,7 @@ import io.element.android.libraries.featureflag.ui.model.FeatureUiModel
|
|||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import java.net.URL
|
||||
import javax.inject.Inject
|
||||
|
||||
class DeveloperSettingsPresenter @Inject constructor(
|
||||
|
|
@ -46,6 +51,7 @@ class DeveloperSettingsPresenter @Inject constructor(
|
|||
private val computeCacheSizeUseCase: ComputeCacheSizeUseCase,
|
||||
private val clearCacheUseCase: ClearCacheUseCase,
|
||||
private val rageshakePresenter: RageshakePreferencesPresenter,
|
||||
private val preferencesStore: PreferencesStore,
|
||||
) : Presenter<DeveloperSettingsState> {
|
||||
|
||||
@Composable
|
||||
|
|
@ -64,8 +70,12 @@ class DeveloperSettingsPresenter @Inject constructor(
|
|||
val clearCacheAction = remember {
|
||||
mutableStateOf<Async<Unit>>(Async.Uninitialized)
|
||||
}
|
||||
val customElementCallBaseUrl by preferencesStore
|
||||
.getCustomElementCallBaseUrlFlow()
|
||||
.collectAsState(initial = null)
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
FeatureFlags.values().forEach { feature ->
|
||||
FeatureFlags.entries.forEach { feature ->
|
||||
features[feature.key] = feature
|
||||
enabledFeatures[feature.key] = featureFlagService.isFeatureEnabled(feature)
|
||||
}
|
||||
|
|
@ -86,6 +96,11 @@ class DeveloperSettingsPresenter @Inject constructor(
|
|||
event.isEnabled,
|
||||
triggerClearCache = { handleEvents(DeveloperSettingsEvents.ClearCache) }
|
||||
)
|
||||
is DeveloperSettingsEvents.SetCustomElementCallBaseUrl -> coroutineScope.launch {
|
||||
// If the URL is either empty or the default one, we want to save 'null' to remove the custom URL
|
||||
val urlToSave = event.baseUrl.takeIf { !it.isNullOrEmpty() && it != ElementCallConfig.DEFAULT_BASE_URL }
|
||||
preferencesStore.setCustomElementCallBaseUrl(urlToSave)
|
||||
}
|
||||
DeveloperSettingsEvents.ClearCache -> coroutineScope.clearCache(clearCacheAction)
|
||||
}
|
||||
}
|
||||
|
|
@ -95,6 +110,11 @@ class DeveloperSettingsPresenter @Inject constructor(
|
|||
cacheSize = cacheSize.value,
|
||||
clearCacheAction = clearCacheAction.value,
|
||||
rageshakeState = rageshakeState,
|
||||
customElementCallBaseUrlState = CustomElementCallBaseUrlState(
|
||||
baseUrl = customElementCallBaseUrl,
|
||||
defaultUrl = ElementCallConfig.DEFAULT_BASE_URL,
|
||||
validator = ::customElementCallUrlValidator,
|
||||
),
|
||||
eventSink = ::handleEvents
|
||||
)
|
||||
}
|
||||
|
|
@ -145,5 +165,14 @@ class DeveloperSettingsPresenter @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun customElementCallUrlValidator(url: String?): Boolean {
|
||||
return runCatching {
|
||||
if (url.isNullOrEmpty()) return@runCatching
|
||||
val parsedUrl = URL(url)
|
||||
if (parsedUrl.protocol !in listOf("http", "https")) error("Incorrect protocol")
|
||||
if (parsedUrl.host.isNullOrBlank()) error("Missing host")
|
||||
}.isSuccess
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -21,10 +21,17 @@ import io.element.android.libraries.architecture.Async
|
|||
import io.element.android.libraries.featureflag.ui.model.FeatureUiModel
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
data class DeveloperSettingsState constructor(
|
||||
data class DeveloperSettingsState(
|
||||
val features: ImmutableList<FeatureUiModel>,
|
||||
val cacheSize: Async<String>,
|
||||
val rageshakeState: RageshakePreferencesState,
|
||||
val clearCacheAction: Async<Unit>,
|
||||
val customElementCallBaseUrlState: CustomElementCallBaseUrlState,
|
||||
val eventSink: (DeveloperSettingsEvents) -> Unit
|
||||
)
|
||||
|
||||
data class CustomElementCallBaseUrlState(
|
||||
val baseUrl: String?,
|
||||
val defaultUrl: String,
|
||||
val validator: (String?) -> Boolean,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -26,6 +26,13 @@ open class DeveloperSettingsStateProvider : PreviewParameterProvider<DeveloperSe
|
|||
get() = sequenceOf(
|
||||
aDeveloperSettingsState(),
|
||||
aDeveloperSettingsState().copy(clearCacheAction = Async.Loading()),
|
||||
aDeveloperSettingsState().copy(
|
||||
customElementCallBaseUrlState = CustomElementCallBaseUrlState(
|
||||
baseUrl = "https://call.element.ahoy",
|
||||
defaultUrl = "https://call.element.io",
|
||||
validator = { true }
|
||||
)
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -34,5 +41,6 @@ fun aDeveloperSettingsState() = DeveloperSettingsState(
|
|||
rageshakeState = aRageshakePreferencesState(),
|
||||
cacheSize = Async.Success("1.2 MB"),
|
||||
clearCacheAction = Async.Uninitialized,
|
||||
customElementCallBaseUrlState = CustomElementCallBaseUrlState(baseUrl = null, defaultUrl = "https://call.element.io", validator = { true }),
|
||||
eventSink = {}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -16,16 +16,20 @@
|
|||
|
||||
package io.element.android.features.preferences.impl.developer
|
||||
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import io.element.android.features.preferences.impl.R
|
||||
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesView
|
||||
import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory
|
||||
import io.element.android.libraries.designsystem.components.preferences.PreferenceText
|
||||
import io.element.android.libraries.designsystem.components.preferences.PreferencePage
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.components.preferences.PreferenceText
|
||||
import io.element.android.libraries.designsystem.components.preferences.PreferenceTextField
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.featureflag.ui.FeatureListView
|
||||
import io.element.android.libraries.featureflag.ui.model.FeatureUiModel
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
|
@ -47,6 +51,7 @@ fun DeveloperSettingsView(
|
|||
PreferenceCategory(title = "Feature flags") {
|
||||
FeatureListContent(state)
|
||||
}
|
||||
ElementCallCategory(state = state)
|
||||
PreferenceCategory(title = "Rust SDK") {
|
||||
PreferenceText(
|
||||
title = "Configure tracing",
|
||||
|
|
@ -84,6 +89,34 @@ fun DeveloperSettingsView(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ElementCallCategory(
|
||||
state: DeveloperSettingsState,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
PreferenceCategory(modifier = modifier, title = "Element Call", showDivider = true) {
|
||||
val callUrlState = state.customElementCallBaseUrlState
|
||||
fun isUsingDefaultUrl(value: String?): Boolean {
|
||||
return value.isNullOrEmpty() || value == callUrlState.defaultUrl
|
||||
}
|
||||
val supportingText = if (isUsingDefaultUrl(callUrlState.baseUrl)) {
|
||||
stringResource(R.string.screen_advanced_settings_element_call_base_url_description)
|
||||
} else {
|
||||
callUrlState.baseUrl
|
||||
}
|
||||
PreferenceTextField(
|
||||
headline = stringResource(R.string.screen_advanced_settings_element_call_base_url),
|
||||
value = callUrlState.baseUrl ?: callUrlState.defaultUrl,
|
||||
supportingText = supportingText,
|
||||
validation = callUrlState.validator,
|
||||
onValidationErrorMessage = stringResource(R.string.screen_advanced_settings_element_call_base_url_validation_error),
|
||||
displayValue = { value -> !isUsingDefaultUrl(value) },
|
||||
keyboardOptions = KeyboardOptions.Default.copy(autoCorrect = false, keyboardType = KeyboardType.Uri),
|
||||
onChange = { state.eventSink(DeveloperSettingsEvents.SetCustomElementCallBaseUrl(it)) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FeatureListContent(
|
||||
state: DeveloperSettingsState,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue