ability to change theme
This commit is contained in:
@@ -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)
|
||||
|
||||
+2
-1
@@ -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(
|
||||
|
||||
+2
-1
@@ -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()
|
||||
|
||||
+2
-1
@@ -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
|
||||
) {
|
||||
|
||||
Reference in New Issue
Block a user