diff --git a/app/src/main/kotlin/com/wire/android/ui/common/WireLabelledCheckbox.kt b/app/src/main/kotlin/com/wire/android/ui/common/WireLabelledCheckbox.kt index 4576a43604..7d4fd19eb4 100644 --- a/app/src/main/kotlin/com/wire/android/ui/common/WireLabelledCheckbox.kt +++ b/app/src/main/kotlin/com/wire/android/ui/common/WireLabelledCheckbox.kt @@ -90,8 +90,8 @@ fun WireLabelledCheckbox( fun WireCheckbox( checked: Boolean, onCheckedChange: ((Boolean) -> Unit)?, - enabled: Boolean = true, modifier: Modifier = Modifier, + enabled: Boolean = true, ) { Checkbox( checked = checked, diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/composer/MessageComposerViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/composer/MessageComposerViewModel.kt index d221c26fc5..60daf40c4c 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/composer/MessageComposerViewModel.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/composer/MessageComposerViewModel.kt @@ -114,7 +114,7 @@ class MessageComposerViewModel @Inject constructor( } private fun getEnterToSendState() { - viewModelScope.launch(dispatchers.io()) { + viewModelScope.launch { globalDataStore.enterToSendFlow().first().also { messageComposerViewState.value = messageComposerViewState.value.copy(enterToSend = it) } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/EnabledMessageComposer.kt b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/EnabledMessageComposer.kt index 13a513e7fb..b2b7aff64c 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/EnabledMessageComposer.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/EnabledMessageComposer.kt @@ -225,13 +225,14 @@ fun EnabledMessageComposer( } val keyboardOptions by remember { derivedStateOf { - if (messageComposerStateHolder.messageComposerViewState.value.enterToSend) { + if (messageComposerViewState.value.enterToSend) { KeyboardOptions.Companion.MessageComposerEnterToSend } else { KeyboardOptions.Companion.MessageComposerDefault } } } + val keyboardActionHandler by remember { derivedStateOf { KeyboardActionHandler { diff --git a/app/src/main/kotlin/com/wire/android/ui/home/settings/SettingsItem.kt b/app/src/main/kotlin/com/wire/android/ui/home/settings/SettingsItem.kt index d90df2654e..eadf970982 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/settings/SettingsItem.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/settings/SettingsItem.kt @@ -46,7 +46,7 @@ import com.wire.android.ui.common.clickable import com.wire.android.ui.common.dimensions import com.wire.android.ui.destinations.AboutThisAppScreenDestination import com.wire.android.ui.destinations.AppSettingsScreenDestination -import com.wire.android.ui.destinations.AppearanceScreenDestination +import com.wire.android.ui.destinations.CustomizationScreenDestination import com.wire.android.ui.destinations.BackupAndRestoreScreenDestination import com.wire.android.ui.destinations.DebugScreenDestination import com.wire.android.ui.destinations.DependenciesScreenDestination @@ -157,10 +157,10 @@ sealed class SettingsItem(open val id: String, open val title: UIText) { direction = MyAccountScreenDestination ) - data object Appearance : DirectionItem( - id = "appearance_settings", - title = UIText.StringResource(R.string.settings_appearance_label), - direction = AppearanceScreenDestination + data object Customization : DirectionItem( + id = "customization_settings", + title = UIText.StringResource(R.string.settings_customization_label), + direction = CustomizationScreenDestination ) data object NetworkSettings : DirectionItem( diff --git a/app/src/main/kotlin/com/wire/android/ui/home/settings/SettingsScreen.kt b/app/src/main/kotlin/com/wire/android/ui/home/settings/SettingsScreen.kt index 10afe2caad..203b617728 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/settings/SettingsScreen.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/settings/SettingsScreen.kt @@ -98,7 +98,6 @@ fun SettingsScreenContent( header = context.getString(R.string.settings_account_settings_label), items = buildList { add(SettingsItem.YourAccount) - add(SettingsItem.Appearance) add(SettingsItem.PrivacySettings) add(SettingsItem.ManageDevices) if (BackUpSettings) { @@ -117,6 +116,7 @@ fun SettingsScreenContent( folderWithElements( header = context.getString(R.string.app_settings_screen_title), items = buildList { + add(SettingsItem.Customization) if (AppSettings) { add(SettingsItem.AppSettings) } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/settings/appearance/AppearanceScreen.kt b/app/src/main/kotlin/com/wire/android/ui/home/settings/appearance/CustomizationScreen.kt similarity index 78% rename from app/src/main/kotlin/com/wire/android/ui/home/settings/appearance/AppearanceScreen.kt rename to app/src/main/kotlin/com/wire/android/ui/home/settings/appearance/CustomizationScreen.kt index 8c818bf3c8..8e5a1805e7 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/settings/appearance/AppearanceScreen.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/settings/appearance/CustomizationScreen.kt @@ -19,6 +19,7 @@ package com.wire.android.ui.home.settings.appearance import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -48,6 +49,10 @@ import com.wire.android.ui.common.dimensions import com.wire.android.ui.common.scaffold.WireScaffold import com.wire.android.ui.common.selectableBackground import com.wire.android.ui.common.topappbar.WireCenterAlignedTopAppBar +import com.wire.android.ui.home.conversations.details.options.ArrowType +import com.wire.android.ui.home.conversations.details.options.GroupConversationOptionsItem +import com.wire.android.ui.home.conversationslist.common.FolderHeader +import com.wire.android.ui.home.settings.SwitchState import com.wire.android.ui.theme.ThemeData import com.wire.android.ui.theme.ThemeOption import com.wire.android.ui.theme.wireColorScheme @@ -58,24 +63,26 @@ import com.wire.android.util.ui.PreviewMultipleThemes @RootNavGraph @WireDestination @Composable -fun AppearanceScreen( +fun CustomizationScreen( navigator: Navigator, - viewModel: AppearanceViewModel = hiltViewModel() + viewModel: CustomizationViewModel = hiltViewModel() ) { val lazyListState: LazyListState = rememberLazyListState() - AppearanceScreenContent( + CustomizationScreenContent( lazyListState = lazyListState, state = viewModel.state, onThemeOptionChanged = viewModel::selectThemeOption, - onBackPressed = navigator::navigateBack + onBackPressed = navigator::navigateBack, + onEnterToSendClicked = viewModel::selectPressEnterToSendOption, ) } @Composable -fun AppearanceScreenContent( - state: AppearanceState, +fun CustomizationScreenContent( + state: CustomizationState, onThemeOptionChanged: (ThemeOption) -> Unit, onBackPressed: () -> Unit, + onEnterToSendClicked: (Boolean) -> Unit, modifier: Modifier = Modifier, lazyListState: LazyListState = rememberLazyListState() ) { @@ -86,7 +93,7 @@ fun AppearanceScreenContent( WireCenterAlignedTopAppBar( onNavigationPressed = onBackPressed, elevation = 0.dp, - title = stringResource(id = R.string.settings_appearance_label) + title = stringResource(id = R.string.settings_customization_label) ) } ) { internalPadding -> @@ -105,10 +112,36 @@ fun AppearanceScreenContent( }, onItemClicked = onThemeOptionChanged ) + item { + CustomizationOptionsContent( + enterToSendState = state.pressEnterToSentState, + enterToSendClicked = onEnterToSendClicked + ) + } } } } +@Composable +fun CustomizationOptionsContent( + enterToSendState: Boolean, + enterToSendClicked: (Boolean) -> Unit, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .fillMaxWidth() + ) { + FolderHeader(stringResource(R.string.custimization_options_header_title)) + GroupConversationOptionsItem( + title = stringResource(R.string.press_enter_to_send_title), + switchState = SwitchState.Enabled(value = enterToSendState, onCheckedChange = enterToSendClicked), + arrowType = ArrowType.NONE, + subtitle = stringResource(id = R.string.press_enter_to_send_text) + ) + } +} + private fun LazyListScope.folderWithElements( header: String, items: List, @@ -167,8 +200,9 @@ fun ThemeOptionItem( @PreviewMultipleThemes @Composable fun PreviewSettingsScreen() { - AppearanceScreenContent( - AppearanceState(), + CustomizationScreenContent( + CustomizationState(), + {}, {}, {}, ) diff --git a/app/src/main/kotlin/com/wire/android/ui/home/settings/appearance/AppearanceState.kt b/app/src/main/kotlin/com/wire/android/ui/home/settings/appearance/CustomizationState.kt similarity index 91% rename from app/src/main/kotlin/com/wire/android/ui/home/settings/appearance/AppearanceState.kt rename to app/src/main/kotlin/com/wire/android/ui/home/settings/appearance/CustomizationState.kt index 2c5be7a841..21df95bc4b 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/settings/appearance/AppearanceState.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/settings/appearance/CustomizationState.kt @@ -19,6 +19,7 @@ package com.wire.android.ui.home.settings.appearance import com.wire.android.ui.theme.ThemeOption -data class AppearanceState( +data class CustomizationState( val selectedThemeOption: ThemeOption = ThemeOption.SYSTEM, + val pressEnterToSentState: Boolean = false, ) diff --git a/app/src/main/kotlin/com/wire/android/ui/home/settings/appearance/AppearanceViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/home/settings/appearance/CustomizationViewModel.kt similarity index 73% rename from app/src/main/kotlin/com/wire/android/ui/home/settings/appearance/AppearanceViewModel.kt rename to app/src/main/kotlin/com/wire/android/ui/home/settings/appearance/CustomizationViewModel.kt index 4ca0993902..0557bc0a4a 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/settings/appearance/AppearanceViewModel.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/settings/appearance/CustomizationViewModel.kt @@ -30,18 +30,35 @@ import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel -class AppearanceViewModel @Inject constructor( +class CustomizationViewModel @Inject constructor( private val globalDataStore: GlobalDataStore, ) : ViewModel() { - var state by mutableStateOf(AppearanceState()) + var state by mutableStateOf(CustomizationState()) private set init { + observeThemeState() + observePressEnterToSendState() + } + + private fun observePressEnterToSendState() { + viewModelScope.launch { + globalDataStore.enterToSendFlow().collect { state = state.copy(pressEnterToSentState = it) } + } + } + + private fun observeThemeState() { viewModelScope.launch { globalDataStore.selectedThemeOptionFlow().collect { option -> state = state.copy(selectedThemeOption = option) } } } + fun selectPressEnterToSendOption(option: Boolean) { + viewModelScope.launch { + globalDataStore.setEnterToSend(option) + } + } + fun selectThemeOption(option: ThemeOption) { viewModelScope.launch { globalDataStore.setThemeOption(option) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index af4c405390..0087c4920e 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -1012,7 +1012,6 @@ Verbessern Sie den Empfang von Benachrichtigungen, indem Sie eine ständige Verbindung zu %1$s aufrechterhalten. Es ersetzt die Benachrichtigungsdienste, wenn die Google-Dienste auf Ihrem Gerät nicht verfügbar sind. Dienst wird ausgeführt - Erscheinungsbild Hintergrund Mit Systemeinstellungen synchronisieren (Standard) diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 62dc757df8..df30a08196 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -1068,7 +1068,6 @@ Javítson az értesítések fogadásán a(z) %1$s szolgáltatáshoz való állandó kapcsolattal. Ez helyettesíti az értesítési szolgáltatásokat, ha a Google-szolgáltatások nem elérhetők az eszközén. szolgáltatás fut - Megjelenés Téma Rendszerbeállítások használata (Alapértelmezett) diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 643ac1310f..97c0cd37f2 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -884,7 +884,6 @@ Rispondendo qui, verrà riagganciata l\'altra chiamata. Migliora la ricezione di notifiche mantenendo una costante connessione a %1$s. Questo servizio sostituirà i servizi di notifica se Google Services non sono disponibili sul tuo dispositivo. il servizio è in esecuzione - Aspetto Tema Sincronizza con le impostazioni di sistema (Predefinito) diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 1ef56d7ed7..26bded1640 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -1103,7 +1103,6 @@ Позволяет оптимизировать получение уведомлений, поддерживая постоянное соединение с %1$s. Эта опция заменит службу уведомлений, если Google Services недоступны на устройстве. активен - Внешний вид Тема Как в системе (По умолчанию) diff --git a/app/src/main/res/values-si/strings.xml b/app/src/main/res/values-si/strings.xml index c6a7341bf0..b07e18c2d8 100644 --- a/app/src/main/res/values-si/strings.xml +++ b/app/src/main/res/values-si/strings.xml @@ -895,7 +895,6 @@ %1$s වෙත ස්ථායී සම්බන්ධතාවයක් පවත්වා ගැනීමෙන් දැනුම්දීම් ලැබීම වැඩි දියුණු කරගන්න. ඔබගේ උපාංගයේ ගූගල් සේවා නැති නම් දැනුම්දීමේ සේවා සඳහා විසඳුමක් වේ. සේවාව ධාවනය වෙමින් - පෙනුම තේමාව පද්ධතියේ සැකසුම් සමඟ සමමුහූර්තය (පෙරනිමි) diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 0c77f7c169..6b8946f405 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -505,7 +505,6 @@ Nätverksinställningar Hantera dina enheter - Utseende Tema (Standard) Ljust läge diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6a283fd6e0..943109205a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1109,7 +1109,7 @@ Improve receiving notifications by keeping a constant connection to %1$s. It will replace notification services if Google Services are not available on your device. service is running - Appearance + Customization Theme Sync with system settings (Default) @@ -1696,5 +1696,8 @@ In group conversations, the group admin can overwrite this setting. Please enter folder name. Minimum of 1 and maximum of 64 characters “%1$s” folder could not be added + Press Enter to send + If this is on, messages can be sent with the Enter key on the Keyboard + Options diff --git a/app/src/test/kotlin/com/wire/android/ui/home/conversations/composer/MessageComposerViewModelArrangement.kt b/app/src/test/kotlin/com/wire/android/ui/home/conversations/composer/MessageComposerViewModelArrangement.kt index d13a5151eb..db63ce1058 100644 --- a/app/src/test/kotlin/com/wire/android/ui/home/conversations/composer/MessageComposerViewModelArrangement.kt +++ b/app/src/test/kotlin/com/wire/android/ui/home/conversations/composer/MessageComposerViewModelArrangement.kt @@ -171,7 +171,9 @@ internal class MessageComposerViewModelArrangement { ) } - fun withSuccessfulViewModelInit() = apply { + fun withSuccessfulViewModelInit( + enterToSend: Boolean = false, + ) = apply { coEvery { isFileSharingEnabledUseCase() } returns FileSharingStatus(FileSharingStatus.Value.EnabledAll, null) coEvery { observeOngoingCallsUseCase() } returns emptyFlow() coEvery { observeEstablishedCallsUseCase() } returns emptyFlow() @@ -180,6 +182,7 @@ internal class MessageComposerViewModelArrangement { InteractionAvailability.ENABLED ) ) + coEvery { globalDataStore.enterToSendFlow() } returns flowOf(enterToSend) } fun withSaveDraftMessage() = apply { diff --git a/app/src/test/kotlin/com/wire/android/ui/home/conversations/composer/MessageComposerViewModelTest.kt b/app/src/test/kotlin/com/wire/android/ui/home/conversations/composer/MessageComposerViewModelTest.kt index 8c643a426c..bd174a6259 100644 --- a/app/src/test/kotlin/com/wire/android/ui/home/conversations/composer/MessageComposerViewModelTest.kt +++ b/app/src/test/kotlin/com/wire/android/ui/home/conversations/composer/MessageComposerViewModelTest.kt @@ -31,6 +31,7 @@ import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest import org.amshove.kluent.internal.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @@ -94,4 +95,28 @@ class MessageComposerViewModelTest { // then assertEquals(InteractionAvailability.DISABLED, viewModel.messageComposerViewState.value.interactionAvailability) } + + @Test + fun `given enter to send is enabled, when init, then update state`() = runTest { + // given + val (_, viewModel) = MessageComposerViewModelArrangement() + .withSuccessfulViewModelInit(enterToSend = true) + .arrange() + // when + + // then + assertTrue(viewModel.messageComposerViewState.value.enterToSend) + } + + @Test + fun `given enter to send is disabled, when init, then update state`() = runTest { + // given + val (_, viewModel) = MessageComposerViewModelArrangement() + .withSuccessfulViewModelInit(enterToSend = false) + .arrange() + // when + + // then + assertTrue(!viewModel.messageComposerViewState.value.enterToSend) + } } diff --git a/app/src/test/kotlin/com/wire/android/ui/home/settings/appearance/AppearanceViewModelTest.kt b/app/src/test/kotlin/com/wire/android/ui/home/settings/appearance/AppearanceViewModelTest.kt index 3c1f8626e9..eb2761c300 100644 --- a/app/src/test/kotlin/com/wire/android/ui/home/settings/appearance/AppearanceViewModelTest.kt +++ b/app/src/test/kotlin/com/wire/android/ui/home/settings/appearance/AppearanceViewModelTest.kt @@ -25,6 +25,7 @@ import io.mockk.coEvery import io.mockk.coVerify import io.mockk.every import io.mockk.impl.annotations.MockK +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Test @@ -36,6 +37,7 @@ class AppearanceViewModelTest { @Test fun `given theme option, when changing it, then should update global data store`() = runTest { val (arrangement, viewModel) = Arrangement() + .withEnterToSend(flowOf(false)) .arrange() viewModel.selectThemeOption(ThemeOption.DARK) @@ -43,6 +45,17 @@ class AppearanceViewModelTest { coVerify(exactly = 1) { arrangement.globalDataStore.setThemeOption(ThemeOption.DARK) } } + @Test + fun `given enter to send option, when changing it, then should update global data store`() = runTest { + val (arrangement, viewModel) = Arrangement() + .withEnterToSend(flowOf(true)) + .arrange() + + viewModel.selectPressEnterToSendOption(false) + + coVerify(exactly = 1) { arrangement.globalDataStore.setEnterToSend(false) } + } + private class Arrangement { @MockK lateinit var globalDataStore: GlobalDataStore @@ -53,8 +66,10 @@ class AppearanceViewModelTest { every { globalDataStore.selectedThemeOptionFlow() } returns flowOf(ThemeOption.DARK) } - private val viewModel = AppearanceViewModel(globalDataStore) + fun withEnterToSend(result: Flow) = apply { + every { globalDataStore.enterToSendFlow() } returns result + } - fun arrange() = this to viewModel + fun arrange() = this to CustomizationViewModel(globalDataStore) } }