From 8a17ab9934f98d99c96f4af549601f36f167f5d5 Mon Sep 17 00:00:00 2001 From: Vincent TE Date: Fri, 20 Sep 2024 09:06:30 +0200 Subject: [PATCH] Fix preview and avoid passing a viewModel to composable --- .../swisstransfer/ui/MainActivity.kt | 21 ++++- .../ui/screen/main/settings/SettingsScreen.kt | 29 +++--- .../main/settings/SettingsScreenWrapper.kt | 89 ++++++++++++++----- .../swisstransfer/ui/theme/Theme.kt | 19 +--- 4 files changed, 104 insertions(+), 54 deletions(-) diff --git a/app/src/main/java/com/infomaniak/swisstransfer/ui/MainActivity.kt b/app/src/main/java/com/infomaniak/swisstransfer/ui/MainActivity.kt index 177e184c..41a7cf22 100644 --- a/app/src/main/java/com/infomaniak/swisstransfer/ui/MainActivity.kt +++ b/app/src/main/java/com/infomaniak/swisstransfer/ui/MainActivity.kt @@ -21,7 +21,16 @@ import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.infomaniak.multiplatform_swisstransfer.common.models.Theme import com.infomaniak.swisstransfer.ui.screen.main.MainScreen +import com.infomaniak.swisstransfer.ui.screen.main.settings.SettingsViewModel import com.infomaniak.swisstransfer.ui.theme.SwissTransferTheme import dagger.hilt.android.AndroidEntryPoint @@ -32,9 +41,19 @@ class MainActivity : ComponentActivity() { super.onCreate(savedInstanceState) enableEdgeToEdge() setContent { - SwissTransferTheme { + SwissTransferTheme(isDarkTheme = isDarkTheme()) { MainScreen() } } } } + +@Composable +fun isDarkTheme(): Boolean { + val settingsViewModel = hiltViewModel() + val appSettings by settingsViewModel.appSettingsFlow.collectAsStateWithLifecycle(null) + + return appSettings?.let { + if (it.theme == Theme.SYSTEM) isSystemInDarkTheme() else it.theme == Theme.DARK + } ?: isSystemInDarkTheme() +} diff --git a/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/settings/SettingsScreen.kt b/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/settings/SettingsScreen.kt index 483fb0e5..cf1024bb 100644 --- a/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/settings/SettingsScreen.kt +++ b/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/settings/SettingsScreen.kt @@ -52,7 +52,10 @@ import com.infomaniak.swisstransfer.ui.utils.PreviewMobile @Composable fun SettingsScreen( - appSettings: AppSettings, + theme: GetSetCallbacks, + validityPeriod: GetSetCallbacks, + downloadLimit: GetSetCallbacks, + emailLanguage: GetSetCallbacks, onItemClick: (SettingsOptionScreens) -> Unit, getSelectedSetting: () -> SettingsOptionScreens?, ) { @@ -76,7 +79,7 @@ fun SettingsScreen( titleRes = R.string.settingsOptionTheme, isSelected = { selectedSetting == THEME }, icon = AppIcons.PaintbrushPalette, - description = appSettings.theme.getString(), + description = theme.get().getString(), endIcon = CHEVRON, onClick = { onItemClick(THEME) }, ) @@ -95,7 +98,7 @@ fun SettingsScreen( titleRes = R.string.settingsOptionValidityPeriod, isSelected = { selectedSetting == VALIDITY_PERIOD }, icon = AppIcons.ArrowDownFile, - description = appSettings.validityPeriod.getString(), + description = validityPeriod.get().getString(), endIcon = CHEVRON, onClick = { onItemClick(VALIDITY_PERIOD) }, ) @@ -103,7 +106,7 @@ fun SettingsScreen( titleRes = R.string.settingsOptionDownloadLimit, isSelected = { selectedSetting == DOWNLOAD_LIMIT }, icon = AppIcons.Clock, - description = appSettings.downloadLimit.getString(), + description = downloadLimit.get().getString(), endIcon = CHEVRON, onClick = { onItemClick(DOWNLOAD_LIMIT) }, ) @@ -111,7 +114,7 @@ fun SettingsScreen( titleRes = R.string.settingsOptionEmailLanguage, isSelected = { selectedSetting == EMAIL_LANGUAGE }, icon = AppIcons.SpeechBubble, - description = appSettings.emailLanguage.getString(), + description = emailLanguage.get().getString(), endIcon = CHEVRON, onClick = { onItemClick(EMAIL_LANGUAGE) }, ) @@ -184,19 +187,19 @@ enum class SettingsOptionScreens { DISCOVER_INFOMANIAK, SHARE_IDEAS, GIVE_FEEDBACK, } -private class DummyAppSettings( - override var theme: Theme = Theme.SYSTEM, - override var downloadLimit: DownloadLimit = DownloadLimit.TWOHUNDREDFIFTY, - override var emailLanguage: EmailLanguage = EmailLanguage.FRENCH, - override var validityPeriod: ValidityPeriod = ValidityPeriod.THIRTY, -) : AppSettings - @PreviewMobile @Composable private fun SettingsScreenPreview() { SwissTransferTheme { Surface(color = MaterialTheme.colorScheme.background) { - SettingsScreen(appSettings = DummyAppSettings(), onItemClick = {}, getSelectedSetting = { null }) + SettingsScreen( + theme = GetSetCallbacks(get = { Theme.SYSTEM }, set = {}), + validityPeriod = GetSetCallbacks(get = { ValidityPeriod.THIRTY }, set = {}), + downloadLimit = GetSetCallbacks(get = { DownloadLimit.TWOHUNDREDFIFTY }, set = {}), + emailLanguage = GetSetCallbacks(get = { EmailLanguage.ENGLISH }, set = {}), + onItemClick = {}, + getSelectedSetting = { null }, + ) } } } diff --git a/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/settings/SettingsScreenWrapper.kt b/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/settings/SettingsScreenWrapper.kt index 432ef523..f3476bf6 100644 --- a/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/settings/SettingsScreenWrapper.kt +++ b/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/settings/SettingsScreenWrapper.kt @@ -27,49 +27,83 @@ import androidx.compose.material3.adaptive.WindowAdaptiveInfo import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffoldRole import androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.* import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.infomaniak.multiplatform_swisstransfer.common.interfaces.appSettings.AppSettings +import com.infomaniak.multiplatform_swisstransfer.common.models.DownloadLimit +import com.infomaniak.multiplatform_swisstransfer.common.models.EmailLanguage +import com.infomaniak.multiplatform_swisstransfer.common.models.Theme +import com.infomaniak.multiplatform_swisstransfer.common.models.ValidityPeriod import com.infomaniak.swisstransfer.R import com.infomaniak.swisstransfer.ui.components.TwoPaneScaffold import com.infomaniak.swisstransfer.ui.screen.main.settings.SettingsOptionScreens.* import com.infomaniak.swisstransfer.ui.theme.SwissTransferTheme import com.infomaniak.swisstransfer.ui.utils.* -@OptIn(ExperimentalMaterial3AdaptiveApi::class) @Composable fun SettingsScreenWrapper( windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo(), settingsViewModel: SettingsViewModel = hiltViewModel(), ) { val appSettings by settingsViewModel.appSettingsFlow.collectAsStateWithLifecycle(null) + appSettings?.let { safeAppSettings -> - TwoPaneScaffold( - windowAdaptiveInfo = windowAdaptiveInfo, - listPane = { ListPane(this, safeAppSettings) }, - detailPane = { DetailPane(safeAppSettings, settingsViewModel, navigator = this) }, - ) + val theme = GetSetCallbacks(get = { safeAppSettings.theme }, set = { settingsViewModel.setTheme(it) }) + val validityPeriod = + GetSetCallbacks(get = { safeAppSettings.validityPeriod }, set = { settingsViewModel.setValidityPeriod(it) }) + val downloadLimit = + GetSetCallbacks(get = { safeAppSettings.downloadLimit }, set = { settingsViewModel.setDownloadLimit(it) }) + val emailLanguage = + GetSetCallbacks(get = { safeAppSettings.emailLanguage }, set = { settingsViewModel.setEmailLanguage(it) }) + + SettingsScreenWrapper(windowAdaptiveInfo, theme, validityPeriod, downloadLimit, emailLanguage) } } @OptIn(ExperimentalMaterial3AdaptiveApi::class) @Composable -private fun ListPane(navigator: ThreePaneScaffoldNavigator, appSettings: AppSettings) { +fun SettingsScreenWrapper( + windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo(), + theme: GetSetCallbacks, + validityPeriod: GetSetCallbacks, + downloadLimit: GetSetCallbacks, + emailLanguage: GetSetCallbacks, +) { + TwoPaneScaffold( + windowAdaptiveInfo = windowAdaptiveInfo, + listPane = { ListPane(navigator = this, theme, validityPeriod, downloadLimit, emailLanguage) }, + detailPane = { DetailPane(navigator = this, theme, validityPeriod, downloadLimit, emailLanguage) }, + ) +} + +@Immutable +class GetSetCallbacks( + val get: () -> T, + val set: (T) -> Unit, +) + +@OptIn(ExperimentalMaterial3AdaptiveApi::class) +@Composable +private fun ListPane( + navigator: ThreePaneScaffoldNavigator, + theme: GetSetCallbacks, + validityPeriod: GetSetCallbacks, + downloadLimit: GetSetCallbacks, + emailLanguage: GetSetCallbacks, +) { val context = LocalContext.current val aboutURL = stringResource(R.string.urlAbout) val userReportURL = stringResource(R.string.urlUserReportAndroid) SettingsScreen( - appSettings, + theme, + validityPeriod, + downloadLimit, emailLanguage, onItemClick = { item -> when (item) { NOTIFICATIONS -> context.openAppNotificationSettings() @@ -89,9 +123,11 @@ private fun ListPane(navigator: ThreePaneScaffoldNavigator, + theme: GetSetCallbacks, + validityPeriod: GetSetCallbacks, + downloadLimit: GetSetCallbacks, + emailLanguage: GetSetCallbacks, ) { var lastSelectedScreen by rememberSaveable { mutableStateOf(null) } @@ -103,24 +139,24 @@ private fun DetailPane( when (destination) { THEME -> SettingsThemeScreen( - theme = appSettings.theme, + theme = theme.get(), navigateBack = navigateBack, - onThemeUpdate = settingsViewModel::setTheme, + onThemeUpdate = { theme.set(it) }, ) VALIDITY_PERIOD -> SettingsValidityPeriodScreen( - validityPeriod = appSettings.validityPeriod, + validityPeriod = validityPeriod.get(), navigateBack = navigateBack, - onValidityPeriodChange = settingsViewModel::setValidityPeriod, + onValidityPeriodChange = { validityPeriod.set(it) }, ) DOWNLOAD_LIMIT -> SettingsDownloadsLimitScreen( - downloadLimit = appSettings.downloadLimit, + downloadLimit = downloadLimit.get(), navigateBack = navigateBack, - onDownloadLimitChange = settingsViewModel::setDownloadLimit, + onDownloadLimitChange = { downloadLimit.set(it) }, ) EMAIL_LANGUAGE -> SettingsEmailLanguageScreen( - emailLanguage = appSettings.emailLanguage, + emailLanguage = emailLanguage.get(), navigateBack = navigateBack, - onEmailLanguageChange = settingsViewModel::setEmailLanguage, + onEmailLanguageChange = { emailLanguage.set(it) }, ) NOTIFICATIONS, DISCOVER_INFOMANIAK, @@ -144,7 +180,12 @@ private fun NoSelectionEmptyState() { private fun SettingsScreenWrapperPreview() { SwissTransferTheme { Surface(color = MaterialTheme.colorScheme.background) { - SettingsScreenWrapper() + SettingsScreenWrapper( + theme = GetSetCallbacks(get = { Theme.SYSTEM }, set = {}), + validityPeriod = GetSetCallbacks(get = { ValidityPeriod.THIRTY }, set = {}), + downloadLimit = GetSetCallbacks(get = { DownloadLimit.TWOHUNDREDFIFTY }, set = {}), + emailLanguage = GetSetCallbacks(get = { EmailLanguage.ENGLISH }, set = {}), + ) } } } diff --git a/app/src/main/java/com/infomaniak/swisstransfer/ui/theme/Theme.kt b/app/src/main/java/com/infomaniak/swisstransfer/ui/theme/Theme.kt index 5a60c8ad..c688ec87 100644 --- a/app/src/main/java/com/infomaniak/swisstransfer/ui/theme/Theme.kt +++ b/app/src/main/java/com/infomaniak/swisstransfer/ui/theme/Theme.kt @@ -23,42 +23,29 @@ import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.* import androidx.compose.ui.graphics.Color -import androidx.hilt.navigation.compose.hiltViewModel -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.infomaniak.multiplatform_swisstransfer.common.models.Theme -import com.infomaniak.swisstransfer.ui.screen.main.settings.SettingsViewModel val LocalCustomTypography = staticCompositionLocalOf { Typography } val LocalCustomColorScheme: ProvidableCompositionLocal = staticCompositionLocalOf { CustomColorScheme() } @Composable fun SwissTransferTheme( - settingsViewModel: SettingsViewModel = hiltViewModel(), - darkTheme: Boolean = isDarkTheme(settingsViewModel), + isDarkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit, ) { - val customColors = if (darkTheme) CustomDarkColorScheme else CustomLightColorScheme + val customColors = if (isDarkTheme) CustomDarkColorScheme else CustomLightColorScheme CompositionLocalProvider( LocalCustomTypography provides Typography, LocalTextStyle provides Typography.bodyRegular, LocalCustomColorScheme provides customColors, ) { MaterialTheme( - colorScheme = if (darkTheme) DarkColorScheme else LightColorScheme, + colorScheme = if (isDarkTheme) DarkColorScheme else LightColorScheme, shapes = Shapes, content = content, ) } } -@Composable -fun isDarkTheme(settingsViewModel: SettingsViewModel): Boolean { - val appSettings by settingsViewModel.appSettingsFlow.collectAsStateWithLifecycle(null) - return appSettings?.let { - if (it.theme == Theme.SYSTEM) isSystemInDarkTheme() else it.theme == Theme.DARK - } ?: isSystemInDarkTheme() -} - object SwissTransferTheme { val typography: CustomTypography @Composable