Skip to content

Commit

Permalink
ability to change theme
Browse files Browse the repository at this point in the history
  • Loading branch information
melod1n committed Aug 14, 2024
1 parent 638d386 commit 602db20
Show file tree
Hide file tree
Showing 9 changed files with 74 additions and 7 deletions.
5 changes: 4 additions & 1 deletion composeApp/src/commonMain/kotlin/dev/meloda/overseerr/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@ 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

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(
Expand All @@ -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))
}
Expand All @@ -28,4 +36,8 @@ class SettingsControllerImpl(
settings.setValue { loadedSettings }
return loadedSettings
}

override fun updateThemeMode(newThemeMode: ThemeMode) {
settings.setValue { old -> old.copy(themeMode = newThemeMode) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package dev.meloda.overseerr.settings.model

enum class ThemeMode {
System, Dark, Light
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
) {
Expand Down

0 comments on commit 602db20

Please sign in to comment.