Skip to content

Commit

Permalink
Remove Element Call feature flag and revert base URL to `call.element…
Browse files Browse the repository at this point in the history
….io` (#1809)

Co-authored-by: ElementBot <[email protected]>
  • Loading branch information
jmartinesp and ElementBot authored Nov 15, 2023
1 parent a5bad53 commit f752147
Show file tree
Hide file tree
Showing 25 changed files with 164 additions and 191 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@
package io.element.android.appconfig

object ElementCallConfig {
const val DEFAULT_BASE_URL = "https://call.element.dev"
const val DEFAULT_BASE_URL = "https://call.element.io"
}
4 changes: 4 additions & 0 deletions changelog.d/+remove-element-call-flag.misc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Remove Element Call feature flag, it's not always enabled.

- Reverted the EC base URL to `https://call.element.io`.
- Moved the option to override this URL to developer settings from advanced settings.
Original file line number Diff line number Diff line change
Expand Up @@ -157,10 +157,10 @@ class MessagesPresenter @AssistedInject constructor(
val enableTextFormatting by preferencesStore.isRichTextEditorEnabledFlow().collectAsState(initial = true)

var enableVoiceMessages by remember { mutableStateOf(false) }
var enableInRoomCalls by remember { mutableStateOf(false) }
// TODO add min power level to use this feature in the future?
val enableInRoomCalls = true
LaunchedEffect(featureFlagsService) {
enableVoiceMessages = featureFlagsService.isFeatureEnabled(FeatureFlags.VoiceMessages)
enableInRoomCalls = featureFlagsService.isFeatureEnabled(FeatureFlags.InRoomCalls)
}

fun handleEvents(event: MessagesEvents) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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) {
Expand All @@ -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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {}
)
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand All @@ -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)) }
)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -39,13 +43,15 @@ 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(
private val featureFlagService: FeatureFlagService,
private val computeCacheSizeUseCase: ComputeCacheSizeUseCase,
private val clearCacheUseCase: ClearCacheUseCase,
private val rageshakePresenter: RageshakePreferencesPresenter,
private val preferencesStore: PreferencesStore,
) : Presenter<DeveloperSettingsState> {

@Composable
Expand All @@ -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)
}
Expand All @@ -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)
}
}
Expand All @@ -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
)
}
Expand Down Expand Up @@ -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
}



Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
)
),
)
}

Expand All @@ -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 = {}
)
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -47,6 +51,7 @@ fun DeveloperSettingsView(
PreferenceCategory(title = "Feature flags") {
FeatureListContent(state)
}
ElementCallCategory(state = state)
PreferenceCategory(title = "Rust SDK") {
PreferenceText(
title = "Configure tracing",
Expand Down Expand Up @@ -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,
Expand Down
Loading

0 comments on commit f752147

Please sign in to comment.