change (preferences) : move from dev settings to advanced settings and add new safety values
This commit is contained in:
@@ -16,6 +16,7 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.media.MediaPreviewValue
|
||||
import io.element.android.libraries.preferences.api.store.AppPreferencesStore
|
||||
import kotlinx.collections.immutable.toImmutableSet
|
||||
import javax.inject.Inject
|
||||
@@ -25,14 +26,14 @@ class TimelineProtectionPresenter @Inject constructor(
|
||||
) : Presenter<TimelineProtectionState> {
|
||||
@Composable
|
||||
override fun present(): TimelineProtectionState {
|
||||
val hideMediaContent by appPreferencesStore.doesHideImagesAndVideosFlow().collectAsState(initial = false)
|
||||
val mediaPreviewValue = appPreferencesStore.getTimelineMediaPreviewValueFlow().collectAsState(initial = MediaPreviewValue.Off)
|
||||
var allowedEvents by remember { mutableStateOf<Set<EventId>>(setOf()) }
|
||||
val protectionState by remember(hideMediaContent) {
|
||||
val protectionState by remember {
|
||||
derivedStateOf {
|
||||
if (hideMediaContent) {
|
||||
ProtectionState.RenderOnly(eventIds = allowedEvents.toImmutableSet())
|
||||
} else {
|
||||
if (mediaPreviewValue.value == MediaPreviewValue.On) {
|
||||
ProtectionState.RenderAll
|
||||
} else {
|
||||
ProtectionState.RenderOnly(eventIds = allowedEvents.toImmutableSet())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
package io.element.android.features.preferences.impl.advanced
|
||||
|
||||
import io.element.android.compound.theme.Theme
|
||||
import io.element.android.libraries.matrix.api.media.MediaPreviewValue
|
||||
|
||||
sealed interface AdvancedSettingsEvents {
|
||||
data class SetDeveloperModeEnabled(val enabled: Boolean) : AdvancedSettingsEvents
|
||||
@@ -16,4 +17,6 @@ sealed interface AdvancedSettingsEvents {
|
||||
data object ChangeTheme : AdvancedSettingsEvents
|
||||
data object CancelChangeTheme : AdvancedSettingsEvents
|
||||
data class SetTheme(val theme: Theme) : AdvancedSettingsEvents
|
||||
data class SetTimelineMediaPreviewValue(val value: MediaPreviewValue) : AdvancedSettingsEvents
|
||||
data class SetHideInviteAvatars(val value: Boolean) : AdvancedSettingsEvents
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import androidx.compose.runtime.setValue
|
||||
import io.element.android.compound.theme.Theme
|
||||
import io.element.android.compound.theme.mapToTheme
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.matrix.api.media.MediaPreviewValue
|
||||
import io.element.android.libraries.preferences.api.store.AppPreferencesStore
|
||||
import io.element.android.libraries.preferences.api.store.SessionPreferencesStore
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -44,6 +45,14 @@ class AdvancedSettingsPresenter @Inject constructor(
|
||||
.collectAsState(initial = Theme.System)
|
||||
var showChangeThemeDialog by remember { mutableStateOf(false) }
|
||||
|
||||
val hideInviteAvatars by remember {
|
||||
appPreferencesStore.getHideInviteAvatarsFlow()
|
||||
}.collectAsState(false)
|
||||
|
||||
val timelineMediaPreviewValue by remember {
|
||||
appPreferencesStore.getTimelineMediaPreviewValueFlow()
|
||||
}.collectAsState(initial = MediaPreviewValue.On)
|
||||
|
||||
fun handleEvents(event: AdvancedSettingsEvents) {
|
||||
when (event) {
|
||||
is AdvancedSettingsEvents.SetDeveloperModeEnabled -> localCoroutineScope.launch {
|
||||
@@ -61,6 +70,12 @@ class AdvancedSettingsPresenter @Inject constructor(
|
||||
appPreferencesStore.setTheme(event.theme.name)
|
||||
showChangeThemeDialog = false
|
||||
}
|
||||
is AdvancedSettingsEvents.SetHideInviteAvatars -> localCoroutineScope.launch {
|
||||
appPreferencesStore.setHideInviteAvatars(event.value)
|
||||
}
|
||||
is AdvancedSettingsEvents.SetTimelineMediaPreviewValue -> localCoroutineScope.launch {
|
||||
appPreferencesStore.setTimelineMediaPreviewValue(event.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +85,8 @@ class AdvancedSettingsPresenter @Inject constructor(
|
||||
doesCompressMedia = doesCompressMedia,
|
||||
theme = theme,
|
||||
showChangeThemeDialog = showChangeThemeDialog,
|
||||
hideInviteAvatars = hideInviteAvatars,
|
||||
timelineMediaPreviewValue = timelineMediaPreviewValue,
|
||||
eventSink = { handleEvents(it) }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
package io.element.android.features.preferences.impl.advanced
|
||||
|
||||
import io.element.android.compound.theme.Theme
|
||||
import io.element.android.libraries.matrix.api.media.MediaPreviewValue
|
||||
|
||||
data class AdvancedSettingsState(
|
||||
val isDeveloperModeEnabled: Boolean,
|
||||
@@ -15,5 +16,7 @@ data class AdvancedSettingsState(
|
||||
val doesCompressMedia: Boolean,
|
||||
val theme: Theme,
|
||||
val showChangeThemeDialog: Boolean,
|
||||
val hideInviteAvatars: Boolean,
|
||||
val timelineMediaPreviewValue: MediaPreviewValue,
|
||||
val eventSink: (AdvancedSettingsEvents) -> Unit
|
||||
)
|
||||
|
||||
@@ -9,6 +9,7 @@ package io.element.android.features.preferences.impl.advanced
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.compound.theme.Theme
|
||||
import io.element.android.libraries.matrix.api.media.MediaPreviewValue
|
||||
|
||||
open class AdvancedSettingsStateProvider : PreviewParameterProvider<AdvancedSettingsState> {
|
||||
override val values: Sequence<AdvancedSettingsState>
|
||||
@@ -18,6 +19,8 @@ open class AdvancedSettingsStateProvider : PreviewParameterProvider<AdvancedSett
|
||||
aAdvancedSettingsState(showChangeThemeDialog = true),
|
||||
aAdvancedSettingsState(isSharePresenceEnabled = true),
|
||||
aAdvancedSettingsState(doesCompressMedia = true),
|
||||
aAdvancedSettingsState(hideInviteAvatars = true),
|
||||
aAdvancedSettingsState(timelineMediaPreviewValue = MediaPreviewValue.Off)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -26,6 +29,8 @@ fun aAdvancedSettingsState(
|
||||
isSharePresenceEnabled: Boolean = false,
|
||||
doesCompressMedia: Boolean = false,
|
||||
showChangeThemeDialog: Boolean = false,
|
||||
hideInviteAvatars: Boolean = false,
|
||||
timelineMediaPreviewValue: MediaPreviewValue = MediaPreviewValue.On,
|
||||
eventSink: (AdvancedSettingsEvents) -> Unit = {},
|
||||
) = AdvancedSettingsState(
|
||||
isDeveloperModeEnabled = isDeveloperModeEnabled,
|
||||
@@ -33,5 +38,7 @@ fun aAdvancedSettingsState(
|
||||
doesCompressMedia = doesCompressMedia,
|
||||
theme = Theme.System,
|
||||
showChangeThemeDialog = showChangeThemeDialog,
|
||||
hideInviteAvatars = hideInviteAvatars,
|
||||
timelineMediaPreviewValue = timelineMediaPreviewValue,
|
||||
eventSink = eventSink
|
||||
)
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
package io.element.android.features.preferences.impl.advanced
|
||||
|
||||
import android.preference.PreferenceCategory
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
@@ -18,11 +19,18 @@ import io.element.android.features.preferences.impl.R
|
||||
import io.element.android.libraries.designsystem.components.dialogs.ListOption
|
||||
import io.element.android.libraries.designsystem.components.dialogs.SingleSelectionDialog
|
||||
import io.element.android.libraries.designsystem.components.list.ListItemContent
|
||||
import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory
|
||||
import io.element.android.libraries.designsystem.components.preferences.PreferenceDivider
|
||||
import io.element.android.libraries.designsystem.components.preferences.PreferencePage
|
||||
import io.element.android.libraries.designsystem.components.preferences.PreferenceSwitch
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.ListItem
|
||||
import io.element.android.libraries.designsystem.theme.components.ListSectionHeader
|
||||
import io.element.android.libraries.designsystem.theme.components.ListSupportingText
|
||||
import io.element.android.libraries.designsystem.theme.components.ListSupportingTextDefaults
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.matrix.api.media.MediaPreviewValue
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import io.element.android.services.analytics.compose.LocalAnalyticsService
|
||||
import io.element.android.services.analyticsproviders.api.trackers.captureInteraction
|
||||
@@ -98,6 +106,7 @@ fun AdvancedSettingsView(
|
||||
state.eventSink(AdvancedSettingsEvents.SetCompressMedia(newValue))
|
||||
}
|
||||
)
|
||||
ModerationAndSafety(state)
|
||||
}
|
||||
|
||||
if (state.showChangeThemeDialog) {
|
||||
@@ -116,6 +125,57 @@ fun AdvancedSettingsView(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ModerationAndSafety(
|
||||
state: AdvancedSettingsState,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
PreferenceCategory(
|
||||
modifier = modifier,
|
||||
title = stringResource(R.string.screen_advanced_settings_moderation_and_safety_section_title),
|
||||
showTopDivider = true
|
||||
) {
|
||||
PreferenceSwitch(
|
||||
title = stringResource(R.string.screen_advanced_settings_hide_invite_avatars_toggle_title),
|
||||
isChecked = state.hideInviteAvatars,
|
||||
onCheckedChange = {
|
||||
state.eventSink(AdvancedSettingsEvents.SetHideInviteAvatars(it))
|
||||
},
|
||||
)
|
||||
ListSectionHeader(
|
||||
title = stringResource(R.string.screen_advanced_settings_show_media_timeline_title),
|
||||
hasDivider = false,
|
||||
description = {
|
||||
ListSupportingText(
|
||||
text = stringResource(R.string.screen_advanced_settings_show_media_timeline_subtitle),
|
||||
contentPadding = ListSupportingTextDefaults.Padding.None,
|
||||
)
|
||||
}
|
||||
)
|
||||
ListItem(
|
||||
headlineContent = { Text(text = stringResource(R.string.screen_advanced_settings_show_media_timeline_always_hide)) },
|
||||
leadingContent = ListItemContent.RadioButton(selected = state.timelineMediaPreviewValue == MediaPreviewValue.Off, compact = true),
|
||||
onClick = {
|
||||
state.eventSink(AdvancedSettingsEvents.SetTimelineMediaPreviewValue(MediaPreviewValue.Off))
|
||||
},
|
||||
)
|
||||
ListItem(
|
||||
headlineContent = { Text(text = stringResource(R.string.screen_advanced_settings_show_media_timeline_private_rooms)) },
|
||||
leadingContent = ListItemContent.RadioButton(selected = state.timelineMediaPreviewValue == MediaPreviewValue.Private, compact = true),
|
||||
onClick = {
|
||||
state.eventSink(AdvancedSettingsEvents.SetTimelineMediaPreviewValue(MediaPreviewValue.Private))
|
||||
},
|
||||
)
|
||||
ListItem(
|
||||
headlineContent = { Text(text = stringResource(R.string.screen_advanced_settings_show_media_timeline_always_show)) },
|
||||
leadingContent = ListItemContent.RadioButton(selected = state.timelineMediaPreviewValue == MediaPreviewValue.On, compact = true),
|
||||
onClick = {
|
||||
state.eventSink(AdvancedSettingsEvents.SetTimelineMediaPreviewValue(MediaPreviewValue.On))
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun getOptions(): ImmutableList<ListOption> {
|
||||
return themes.map {
|
||||
|
||||
@@ -14,7 +14,6 @@ import io.element.android.libraries.matrix.api.tracing.TraceLogPack
|
||||
sealed interface DeveloperSettingsEvents {
|
||||
data class UpdateEnabledFeature(val feature: FeatureUiModel, val isEnabled: Boolean) : DeveloperSettingsEvents
|
||||
data class SetCustomElementCallBaseUrl(val baseUrl: String?) : DeveloperSettingsEvents
|
||||
data class SetHideImagesAndVideos(val value: Boolean) : DeveloperSettingsEvents
|
||||
data class SetTracingLogLevel(val logLevel: LogLevelItem) : DeveloperSettingsEvents
|
||||
data class ToggleTracingLogPack(val logPack: TraceLogPack, val enabled: Boolean) : DeveloperSettingsEvents
|
||||
data object ClearCache : DeveloperSettingsEvents
|
||||
|
||||
@@ -74,9 +74,6 @@ class DeveloperSettingsPresenter @Inject constructor(
|
||||
val customElementCallBaseUrl by appPreferencesStore
|
||||
.getCustomElementCallBaseUrlFlow()
|
||||
.collectAsState(initial = null)
|
||||
val hideImagesAndVideos by appPreferencesStore
|
||||
.doesHideImagesAndVideosFlow()
|
||||
.collectAsState(initial = false)
|
||||
|
||||
val tracingLogLevelFlow = remember {
|
||||
appPreferencesStore.getTracingLogLevelFlow().map { AsyncData.Success(it.toLogLevelItem()) }
|
||||
@@ -126,9 +123,6 @@ class DeveloperSettingsPresenter @Inject constructor(
|
||||
appPreferencesStore.setCustomElementCallBaseUrl(urlToSave)
|
||||
}
|
||||
DeveloperSettingsEvents.ClearCache -> coroutineScope.clearCache(clearCacheAction)
|
||||
is DeveloperSettingsEvents.SetHideImagesAndVideos -> coroutineScope.launch {
|
||||
appPreferencesStore.setHideImagesAndVideos(event.value)
|
||||
}
|
||||
is DeveloperSettingsEvents.SetTracingLogLevel -> coroutineScope.launch {
|
||||
appPreferencesStore.setTracingLogLevel(event.logLevel.toLogLevel())
|
||||
}
|
||||
@@ -153,7 +147,6 @@ class DeveloperSettingsPresenter @Inject constructor(
|
||||
baseUrl = customElementCallBaseUrl,
|
||||
validator = ::customElementCallUrlValidator,
|
||||
),
|
||||
hideImagesAndVideos = hideImagesAndVideos,
|
||||
tracingLogLevel = tracingLogLevel,
|
||||
tracingLogPacks = tracingLogPacks,
|
||||
eventSink = ::handleEvents
|
||||
|
||||
@@ -21,7 +21,6 @@ data class DeveloperSettingsState(
|
||||
val rageshakeState: RageshakePreferencesState,
|
||||
val clearCacheAction: AsyncAction<Unit>,
|
||||
val customElementCallBaseUrlState: CustomElementCallBaseUrlState,
|
||||
val hideImagesAndVideos: Boolean,
|
||||
val tracingLogLevel: AsyncData<LogLevelItem>,
|
||||
val tracingLogPacks: ImmutableList<TraceLogPack>,
|
||||
val eventSink: (DeveloperSettingsEvents) -> Unit
|
||||
|
||||
@@ -34,7 +34,6 @@ open class DeveloperSettingsStateProvider : PreviewParameterProvider<DeveloperSe
|
||||
fun aDeveloperSettingsState(
|
||||
clearCacheAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
|
||||
customElementCallBaseUrlState: CustomElementCallBaseUrlState = aCustomElementCallBaseUrlState(),
|
||||
hideImagesAndVideos: Boolean = false,
|
||||
traceLogPacks: List<TraceLogPack> = emptyList(),
|
||||
eventSink: (DeveloperSettingsEvents) -> Unit = {},
|
||||
) = DeveloperSettingsState(
|
||||
@@ -43,7 +42,6 @@ fun aDeveloperSettingsState(
|
||||
cacheSize = AsyncData.Success("1.2 MB"),
|
||||
clearCacheAction = clearCacheAction,
|
||||
customElementCallBaseUrlState = customElementCallBaseUrlState,
|
||||
hideImagesAndVideos = hideImagesAndVideos,
|
||||
tracingLogLevel = AsyncData.Success(LogLevelItem.INFO),
|
||||
tracingLogPacks = traceLogPacks.toPersistentList(),
|
||||
eventSink = eventSink,
|
||||
|
||||
@@ -51,7 +51,6 @@ fun DeveloperSettingsView(
|
||||
title = stringResource(id = CommonStrings.common_developer_options)
|
||||
) {
|
||||
// Note: this is OK to hardcode strings in this debug screen.
|
||||
SettingsCategory(state)
|
||||
PreferenceCategory(
|
||||
title = "Feature flags",
|
||||
showTopDivider = true,
|
||||
@@ -134,22 +133,6 @@ fun DeveloperSettingsView(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SettingsCategory(
|
||||
state: DeveloperSettingsState,
|
||||
) {
|
||||
PreferenceCategory(title = "Preferences", showTopDivider = false) {
|
||||
PreferenceSwitch(
|
||||
title = "Hide image & video previews",
|
||||
subtitle = "When toggled image & video will not render in the timeline by default.",
|
||||
isChecked = state.hideImagesAndVideos,
|
||||
onCheckedChange = {
|
||||
state.eventSink(DeveloperSettingsEvents.SetHideImagesAndVideos(it))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ElementCallCategory(
|
||||
state: DeveloperSettingsState,
|
||||
|
||||
@@ -19,6 +19,11 @@
|
||||
<string name="screen_advanced_settings_send_read_receipts_description">"If turned off, your read receipts won\'t be sent to anyone. You will still receive read receipts from other users."</string>
|
||||
<string name="screen_advanced_settings_share_presence">"Share presence"</string>
|
||||
<string name="screen_advanced_settings_share_presence_description">"If turned off, you won’t be able to send or receive read receipts or typing notifications."</string>
|
||||
<string name="screen_advanced_settings_show_media_timeline_always_hide">"Always hide"</string>
|
||||
<string name="screen_advanced_settings_show_media_timeline_always_show">"Always show"</string>
|
||||
<string name="screen_advanced_settings_show_media_timeline_private_rooms">"In private rooms"</string>
|
||||
<string name="screen_advanced_settings_show_media_timeline_subtitle">"A hidden media can always be shown by tapping on it"</string>
|
||||
<string name="screen_advanced_settings_show_media_timeline_title">"Show media in timeline"</string>
|
||||
<string name="screen_advanced_settings_view_source_description">"Enable option to view message source in the timeline."</string>
|
||||
<string name="screen_blocked_users_empty">"You have no blocked users"</string>
|
||||
<string name="screen_blocked_users_unblock_alert_action">"Unblock"</string>
|
||||
|
||||
@@ -147,28 +147,6 @@ class DeveloperSettingsPresenterTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - toggling hide image and video`() = runTest {
|
||||
val preferences = InMemoryAppPreferencesStore()
|
||||
val presenter = createDeveloperSettingsPresenter(preferencesStore = preferences)
|
||||
presenter.test {
|
||||
skipItems(2)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.hideImagesAndVideos).isFalse()
|
||||
state.eventSink(DeveloperSettingsEvents.SetHideImagesAndVideos(true))
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.hideImagesAndVideos).isTrue()
|
||||
assertThat(preferences.doesHideImagesAndVideosFlow().first()).isTrue()
|
||||
state.eventSink(DeveloperSettingsEvents.SetHideImagesAndVideos(false))
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.hideImagesAndVideos).isFalse()
|
||||
assertThat(preferences.doesHideImagesAndVideosFlow().first()).isFalse()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - changing tracing log level`() = runTest {
|
||||
val preferences = InMemoryAppPreferencesStore()
|
||||
|
||||
@@ -109,18 +109,6 @@ class DeveloperSettingsViewTest {
|
||||
rule.onNodeWithText("Clear cache").performClick()
|
||||
eventsRecorder.assertSingle(DeveloperSettingsEvents.ClearCache)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on the hide images and videos switch emits the expected event`() {
|
||||
val eventsRecorder = EventsRecorder<DeveloperSettingsEvents>()
|
||||
rule.setDeveloperSettingsView(
|
||||
state = aDeveloperSettingsState(
|
||||
eventSink = eventsRecorder
|
||||
),
|
||||
)
|
||||
rule.onNodeWithText("Hide image & video previews").performClick()
|
||||
eventsRecorder.assertSingle(DeveloperSettingsEvents.SetHideImagesAndVideos(true))
|
||||
}
|
||||
}
|
||||
|
||||
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setDeveloperSettingsView(
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* 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.libraries.matrix.api.media
|
||||
|
||||
/**
|
||||
* Represents the values for media preview settings.
|
||||
* - [On] means that media preview are enabled
|
||||
* - [Off] means that media preview are disabled
|
||||
* - [Private] means that media preview are enabled only for private chats.
|
||||
*/
|
||||
enum class MediaPreviewValue {
|
||||
On,
|
||||
Off,
|
||||
Private
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
package io.element.android.libraries.preferences.api.store
|
||||
|
||||
import io.element.android.libraries.matrix.api.media.MediaPreviewValue
|
||||
import io.element.android.libraries.matrix.api.tracing.LogLevel
|
||||
import io.element.android.libraries.matrix.api.tracing.TraceLogPack
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
@@ -21,8 +22,11 @@ interface AppPreferencesStore {
|
||||
suspend fun setTheme(theme: String)
|
||||
fun getThemeFlow(): Flow<String?>
|
||||
|
||||
suspend fun setHideImagesAndVideos(value: Boolean)
|
||||
fun doesHideImagesAndVideosFlow(): Flow<Boolean>
|
||||
suspend fun setHideInviteAvatars(value: Boolean)
|
||||
fun getHideInviteAvatarsFlow(): Flow<Boolean>
|
||||
|
||||
suspend fun setTimelineMediaPreviewValue(value: MediaPreviewValue)
|
||||
fun getTimelineMediaPreviewValueFlow(): Flow<MediaPreviewValue>
|
||||
|
||||
suspend fun setTracingLogLevel(logLevel: LogLevel)
|
||||
fun getTracingLogLevelFlow(): Flow<LogLevel>
|
||||
|
||||
@@ -19,6 +19,7 @@ import io.element.android.libraries.core.meta.BuildMeta
|
||||
import io.element.android.libraries.core.meta.BuildType
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.di.ApplicationContext
|
||||
import io.element.android.libraries.matrix.api.media.MediaPreviewValue
|
||||
import io.element.android.libraries.matrix.api.tracing.LogLevel
|
||||
import io.element.android.libraries.matrix.api.tracing.TraceLogPack
|
||||
import io.element.android.libraries.preferences.api.store.AppPreferencesStore
|
||||
@@ -31,7 +32,8 @@ private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(na
|
||||
private val developerModeKey = booleanPreferencesKey("developerMode")
|
||||
private val customElementCallBaseUrlKey = stringPreferencesKey("elementCallBaseUrl")
|
||||
private val themeKey = stringPreferencesKey("theme")
|
||||
private val hideImagesAndVideosKey = booleanPreferencesKey("hideImagesAndVideos")
|
||||
private val hideInviteAvatarsKey = booleanPreferencesKey("hideInviteAvatars")
|
||||
private val timelineMediaPreviewValueKey = stringPreferencesKey("timelineMediaPreviewValue")
|
||||
private val logLevelKey = stringPreferencesKey("logLevel")
|
||||
private val traceLogPacksKey = stringPreferencesKey("traceLogPacks")
|
||||
|
||||
@@ -83,15 +85,27 @@ class DefaultAppPreferencesStore @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun setHideImagesAndVideos(value: Boolean) {
|
||||
override suspend fun setHideInviteAvatars(value: Boolean) {
|
||||
store.edit { prefs ->
|
||||
prefs[hideImagesAndVideosKey] = value
|
||||
prefs[hideInviteAvatarsKey] = value
|
||||
}
|
||||
}
|
||||
|
||||
override fun doesHideImagesAndVideosFlow(): Flow<Boolean> {
|
||||
override fun getHideInviteAvatarsFlow(): Flow<Boolean> {
|
||||
return store.data.map { prefs ->
|
||||
prefs[hideImagesAndVideosKey] ?: false
|
||||
prefs[hideInviteAvatarsKey] == true
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun setTimelineMediaPreviewValue(value: MediaPreviewValue) {
|
||||
store.edit { prefs ->
|
||||
prefs[timelineMediaPreviewValueKey] = value.name
|
||||
}
|
||||
}
|
||||
|
||||
override fun getTimelineMediaPreviewValueFlow(): Flow<MediaPreviewValue> {
|
||||
return store.data.map { prefs ->
|
||||
prefs[timelineMediaPreviewValueKey]?.let { MediaPreviewValue.valueOf(it) } ?: MediaPreviewValue.On
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
package io.element.android.libraries.preferences.test
|
||||
|
||||
import io.element.android.libraries.matrix.api.media.MediaPreviewValue
|
||||
import io.element.android.libraries.matrix.api.tracing.LogLevel
|
||||
import io.element.android.libraries.matrix.api.tracing.TraceLogPack
|
||||
import io.element.android.libraries.preferences.api.store.AppPreferencesStore
|
||||
@@ -15,18 +16,20 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
class InMemoryAppPreferencesStore(
|
||||
isDeveloperModeEnabled: Boolean = false,
|
||||
hideImagesAndVideos: Boolean = false,
|
||||
customElementCallBaseUrl: String? = null,
|
||||
hideInviteAvatars: Boolean = false,
|
||||
timelineMediaPreviewValue: MediaPreviewValue = MediaPreviewValue.On,
|
||||
theme: String? = null,
|
||||
logLevel: LogLevel = LogLevel.INFO,
|
||||
traceLockPacks: Set<TraceLogPack> = emptySet(),
|
||||
) : AppPreferencesStore {
|
||||
private val isDeveloperModeEnabled = MutableStateFlow(isDeveloperModeEnabled)
|
||||
private val hideImagesAndVideos = MutableStateFlow(hideImagesAndVideos)
|
||||
private val customElementCallBaseUrl = MutableStateFlow(customElementCallBaseUrl)
|
||||
private val theme = MutableStateFlow(theme)
|
||||
private val logLevel = MutableStateFlow(logLevel)
|
||||
private val tracingLogPacks = MutableStateFlow(traceLockPacks)
|
||||
private val hideInviteAvatars = MutableStateFlow(hideInviteAvatars)
|
||||
private val timelineMediaPreviewValue = MutableStateFlow(timelineMediaPreviewValue)
|
||||
|
||||
override suspend fun setDeveloperModeEnabled(enabled: Boolean) {
|
||||
isDeveloperModeEnabled.value = enabled
|
||||
@@ -52,12 +55,20 @@ class InMemoryAppPreferencesStore(
|
||||
return theme
|
||||
}
|
||||
|
||||
override suspend fun setHideImagesAndVideos(value: Boolean) {
|
||||
hideImagesAndVideos.value = value
|
||||
override suspend fun setHideInviteAvatars(value: Boolean) {
|
||||
hideInviteAvatars.value = value
|
||||
}
|
||||
|
||||
override fun doesHideImagesAndVideosFlow(): Flow<Boolean> {
|
||||
return hideImagesAndVideos
|
||||
override fun getHideInviteAvatarsFlow(): Flow<Boolean> {
|
||||
return hideInviteAvatars
|
||||
}
|
||||
|
||||
override suspend fun setTimelineMediaPreviewValue(value: MediaPreviewValue) {
|
||||
timelineMediaPreviewValue.value = value
|
||||
}
|
||||
|
||||
override fun getTimelineMediaPreviewValueFlow(): Flow<MediaPreviewValue> {
|
||||
return timelineMediaPreviewValue
|
||||
}
|
||||
|
||||
override suspend fun setTracingLogLevel(logLevel: LogLevel) {
|
||||
|
||||
@@ -21,6 +21,7 @@ import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.api.core.ThreadId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.media.MediaPreviewValue
|
||||
import io.element.android.libraries.matrix.api.notification.NotificationContent
|
||||
import io.element.android.libraries.matrix.api.notification.NotificationData
|
||||
import io.element.android.libraries.matrix.api.permalink.PermalinkParser
|
||||
@@ -292,7 +293,7 @@ class DefaultNotifiableEventResolver @Inject constructor(
|
||||
}
|
||||
|
||||
private suspend fun NotificationContent.MessageLike.RoomMessage.fetchImageIfPresent(client: MatrixClient): Uri? {
|
||||
if (appPreferencesStore.doesHideImagesAndVideosFlow().first()) {
|
||||
if (appPreferencesStore.getTimelineMediaPreviewValueFlow().first() != MediaPreviewValue.On) {
|
||||
return null
|
||||
}
|
||||
val fileResult = when (val messageType = messageType) {
|
||||
@@ -319,7 +320,7 @@ class DefaultNotifiableEventResolver @Inject constructor(
|
||||
}
|
||||
|
||||
private suspend fun NotificationContent.MessageLike.RoomMessage.getImageMimetype(): String? {
|
||||
if (appPreferencesStore.doesHideImagesAndVideosFlow().first()) {
|
||||
if (appPreferencesStore.getTimelineMediaPreviewValueFlow().first() != MediaPreviewValue.On) {
|
||||
return null
|
||||
}
|
||||
return when (val messageType = messageType) {
|
||||
|
||||
Reference in New Issue
Block a user