Skip to content

Commit

Permalink
Fix preview and avoid passing a viewModel to composable
Browse files Browse the repository at this point in the history
  • Loading branch information
tevincent committed Sep 20, 2024
1 parent 13fabbf commit 8a17ab9
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -32,9 +41,19 @@ class MainActivity : ComponentActivity() {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
SwissTransferTheme {
SwissTransferTheme(isDarkTheme = isDarkTheme()) {
MainScreen()
}
}
}
}

@Composable
fun isDarkTheme(): Boolean {
val settingsViewModel = hiltViewModel<SettingsViewModel>()
val appSettings by settingsViewModel.appSettingsFlow.collectAsStateWithLifecycle(null)

return appSettings?.let {
if (it.theme == Theme.SYSTEM) isSystemInDarkTheme() else it.theme == Theme.DARK
} ?: isSystemInDarkTheme()
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ import com.infomaniak.swisstransfer.ui.utils.PreviewMobile

@Composable
fun SettingsScreen(
appSettings: AppSettings,
theme: GetSetCallbacks<Theme>,
validityPeriod: GetSetCallbacks<ValidityPeriod>,
downloadLimit: GetSetCallbacks<DownloadLimit>,
emailLanguage: GetSetCallbacks<EmailLanguage>,
onItemClick: (SettingsOptionScreens) -> Unit,
getSelectedSetting: () -> SettingsOptionScreens?,
) {
Expand All @@ -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) },
)
Expand All @@ -95,23 +98,23 @@ 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) },
)
SettingItem(
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) },
)
SettingItem(
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) },
)
Expand Down Expand Up @@ -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 },
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<SettingsViewModel>(),
) {
val appSettings by settingsViewModel.appSettingsFlow.collectAsStateWithLifecycle(null)

appSettings?.let { safeAppSettings ->
TwoPaneScaffold<SettingsOptionScreens>(
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<SettingsOptionScreens>, appSettings: AppSettings) {
fun SettingsScreenWrapper(
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo(),
theme: GetSetCallbacks<Theme>,
validityPeriod: GetSetCallbacks<ValidityPeriod>,
downloadLimit: GetSetCallbacks<DownloadLimit>,
emailLanguage: GetSetCallbacks<EmailLanguage>,
) {
TwoPaneScaffold<SettingsOptionScreens>(
windowAdaptiveInfo = windowAdaptiveInfo,
listPane = { ListPane(navigator = this, theme, validityPeriod, downloadLimit, emailLanguage) },
detailPane = { DetailPane(navigator = this, theme, validityPeriod, downloadLimit, emailLanguage) },
)
}

@Immutable
class GetSetCallbacks<T>(
val get: () -> T,
val set: (T) -> Unit,
)

@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
private fun ListPane(
navigator: ThreePaneScaffoldNavigator<SettingsOptionScreens>,
theme: GetSetCallbacks<Theme>,
validityPeriod: GetSetCallbacks<ValidityPeriod>,
downloadLimit: GetSetCallbacks<DownloadLimit>,
emailLanguage: GetSetCallbacks<EmailLanguage>,
) {
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()
Expand All @@ -89,9 +123,11 @@ private fun ListPane(navigator: ThreePaneScaffoldNavigator<SettingsOptionScreens
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
private fun DetailPane(
appSettings: AppSettings,
settingsViewModel: SettingsViewModel,
navigator: ThreePaneScaffoldNavigator<SettingsOptionScreens>,
theme: GetSetCallbacks<Theme>,
validityPeriod: GetSetCallbacks<ValidityPeriod>,
downloadLimit: GetSetCallbacks<DownloadLimit>,
emailLanguage: GetSetCallbacks<EmailLanguage>,
) {
var lastSelectedScreen by rememberSaveable { mutableStateOf<SettingsOptionScreens?>(null) }

Expand All @@ -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,
Expand All @@ -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 = {}),
)
}
}
}
19 changes: 3 additions & 16 deletions app/src/main/java/com/infomaniak/swisstransfer/ui/theme/Theme.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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<CustomColorScheme> = staticCompositionLocalOf { CustomColorScheme() }

@Composable
fun SwissTransferTheme(
settingsViewModel: SettingsViewModel = hiltViewModel<SettingsViewModel>(),
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
Expand Down

0 comments on commit 8a17ab9

Please sign in to comment.