ability to change theme

This commit is contained in:
2024-08-14 23:28:00 +03:00
parent 638d3868de
commit 602db20f12
9 changed files with 74 additions and 7 deletions
@@ -4,7 +4,9 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.transitions.FadeTransition
import dev.meloda.overseerr.screens.main.MainScreen
@@ -24,12 +26,13 @@ internal fun App() = KoinContext {
}
val settingsController: SettingsController = koinInject()
val settings by settingsController.settings.collectAsStateWithLifecycle()
LaunchedEffect(true) {
settingsController.loadAppSettings()
}
AppTheme {
AppTheme(themeMode = settings.themeMode) {
Surface(modifier = Modifier.fillMaxSize()) {
Navigator(MainScreen()) { navigator ->
FadeTransition(navigator)
@@ -17,6 +17,7 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
@@ -32,7 +33,7 @@ class LoginScreen : Screen {
override fun Content() {
val navigator = LocalNavigator.currentOrThrow
val viewModel: LoginViewModel = koinViewModel<LoginViewModelImpl>()
val screenState: LoginScreenState by viewModel.screenState.collectAsState()
val screenState: LoginScreenState by viewModel.screenState.collectAsStateWithLifecycle()
var loginValue by rememberSaveable {
mutableStateOf(screenState.login)
@@ -5,25 +5,61 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import dev.meloda.overseerr.screens.login.presentation.LoginScreen
import dev.meloda.overseerr.screens.requests.presentation.RequestsScreen
import dev.meloda.overseerr.screens.url.presentation.UrlScreen
import dev.meloda.overseerr.settings.SettingsController
import dev.meloda.overseerr.settings.model.ThemeMode
import kotlinx.coroutines.launch
import org.koin.compose.koinInject
class MainScreen : Screen {
@OptIn(ExperimentalMaterial3Api::class)
@Composable
override fun Content() {
val coroutineScope = rememberCoroutineScope()
val navigator = LocalNavigator.currentOrThrow
val settingsController: SettingsController = koinInject()
val settings by settingsController.settings.collectAsStateWithLifecycle()
Scaffold(
topBar = {
TopAppBar(title = { Text(text = "Main screen") })
TopAppBar(
title = { Text(text = "Main screen") },
actions = {
TextButton(
onClick = {
val newThemeMode = ThemeMode.entries.getOrElse(
ThemeMode.entries.indexOf(settings.themeMode) + 1
) { ThemeMode.System }
settingsController.updateThemeMode(newThemeMode)
coroutineScope.launch {
settingsController.saveAppSettings()
}
}
) {
Text(
text = when (settings.themeMode) {
ThemeMode.System -> "System"
ThemeMode.Dark -> "Dark"
ThemeMode.Light -> "Light"
}
)
}
}
)
}
) { padding ->
Row(
@@ -18,6 +18,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
@@ -43,7 +44,7 @@ class RequestsScreen : Screen {
override fun Content() {
val navigator = LocalNavigator.currentOrThrow
val viewModel: RequestsViewModel = koinViewModel<RequestsViewModelImpl>()
val screenState: RequestsScreenState by viewModel.screenState.collectAsState()
val screenState: RequestsScreenState by viewModel.screenState.collectAsStateWithLifecycle()
val hazeState = remember { HazeState() }
val hazeStyle = HazeMaterials.ultraThin()
@@ -13,6 +13,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
@@ -27,7 +28,7 @@ class UrlScreen : Screen {
override fun Content() {
val navigator = LocalNavigator.currentOrThrow
val viewModel: UrlViewModel = koinViewModel<UrlViewModelImpl>()
val screenState by viewModel.screenState.collectAsState()
val screenState by viewModel.screenState.collectAsStateWithLifecycle()
Scaffold(
modifier = Modifier.fillMaxSize(),
@@ -2,6 +2,7 @@ package dev.meloda.overseerr.settings
import dev.meloda.overseerr.ext.setValue
import dev.meloda.overseerr.settings.model.AppSettings
import dev.meloda.overseerr.settings.model.ThemeMode
import io.github.xxfast.kstore.KStore
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -9,8 +10,11 @@ import kotlinx.coroutines.flow.StateFlow
interface SettingsController {
val settings: StateFlow<AppSettings>
suspend fun saveAppSettings()
suspend fun updateAppSettings(update: (AppSettings) -> AppSettings)
suspend fun loadAppSettings(): AppSettings
fun updateThemeMode(newThemeMode: ThemeMode)
}
class SettingsControllerImpl(
@@ -19,6 +23,10 @@ class SettingsControllerImpl(
override val settings = MutableStateFlow(AppSettings.EMPTY)
override suspend fun saveAppSettings() {
store.set(settings.value)
}
override suspend fun updateAppSettings(update: (AppSettings) -> AppSettings) {
store.set(update(settings.value))
}
@@ -28,4 +36,8 @@ class SettingsControllerImpl(
settings.setValue { loadedSettings }
return loadedSettings
}
override fun updateThemeMode(newThemeMode: ThemeMode) {
settings.setValue { old -> old.copy(themeMode = newThemeMode) }
}
}
@@ -5,7 +5,8 @@ import kotlinx.serialization.Serializable
@Serializable
data class AppSettings(
val url: String = "",
val plexToken: String = ""
val plexToken: String = "",
val themeMode: ThemeMode = ThemeMode.System
) {
companion object {
val EMPTY: AppSettings = AppSettings()
@@ -0,0 +1,5 @@
package dev.meloda.overseerr.settings.model
enum class ThemeMode {
System, Dark, Light
}
@@ -6,15 +6,22 @@ import androidx.compose.material3.Surface
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.*
import dev.meloda.overseerr.settings.model.ThemeMode
internal val LocalThemeIsDark = compositionLocalOf { mutableStateOf(true) }
@Composable
internal fun AppTheme(
themeMode: ThemeMode = ThemeMode.System,
content: @Composable () -> Unit
) {
val systemIsDark = isSystemInDarkTheme()
val isDarkState = remember { mutableStateOf(systemIsDark) }
val isDarkState = remember(themeMode, systemIsDark) {
mutableStateOf(
if (themeMode == ThemeMode.System) systemIsDark
else themeMode == ThemeMode.Dark
)
}
CompositionLocalProvider(
LocalThemeIsDark provides isDarkState
) {