voyager -> androidx.compose.navigation
This commit is contained in:
@@ -60,8 +60,7 @@ kotlin {
|
|||||||
implementation(compose.material3)
|
implementation(compose.material3)
|
||||||
implementation(compose.materialIconsExtended)
|
implementation(compose.materialIconsExtended)
|
||||||
implementation(compose.components.resources)
|
implementation(compose.components.resources)
|
||||||
implementation(libs.voyager.navigator)
|
implementation(libs.androidx.navigation.compose)
|
||||||
implementation(libs.voyager.transitions)
|
|
||||||
implementation(libs.coil)
|
implementation(libs.coil)
|
||||||
implementation(libs.coil.network.ktor)
|
implementation(libs.coil.network.ktor)
|
||||||
implementation(libs.kotlinx.coroutines.core)
|
implementation(libs.kotlinx.coroutines.core)
|
||||||
@@ -165,7 +164,13 @@ android {
|
|||||||
getByName("release") {
|
getByName("release") {
|
||||||
signingConfig = signingConfigs.getByName("release")
|
signingConfig = signingConfigs.getByName("release")
|
||||||
|
|
||||||
isMinifyEnabled = false
|
isMinifyEnabled = true
|
||||||
|
isShrinkResources = true
|
||||||
|
|
||||||
|
proguardFiles(
|
||||||
|
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||||
|
"proguard-rules.pro"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
compileOptions {
|
compileOptions {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package dev.meloda.overseerr.model
|
package dev.meloda.overseerr.model
|
||||||
|
|
||||||
actual class Platform actual constructor() {
|
internal actual class Platform actual constructor() {
|
||||||
actual val name: String
|
actual val name: String
|
||||||
get() = "Android"
|
get() = "Android"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.ui.platform.LocalView
|
import androidx.compose.ui.platform.LocalView
|
||||||
import androidx.core.view.WindowInsetsControllerCompat
|
import androidx.core.view.WindowInsetsControllerCompat
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal actual fun SystemAppearance(isDark: Boolean) {
|
internal actual fun SystemAppearance(isDark: Boolean) {
|
||||||
@@ -16,4 +17,8 @@ internal actual fun SystemAppearance(isDark: Boolean) {
|
|||||||
isAppearanceLightNavigationBars = isDark
|
isAppearanceLightNavigationBars = isDark
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal actual fun NavigationSettings(navController: NavController) {
|
||||||
}
|
}
|
||||||
@@ -7,11 +7,16 @@ import androidx.compose.runtime.LaunchedEffect
|
|||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import cafe.adriel.voyager.navigator.Navigator
|
import androidx.navigation.compose.NavHost
|
||||||
import cafe.adriel.voyager.transitions.FadeTransition
|
import androidx.navigation.compose.composable
|
||||||
|
import androidx.navigation.compose.rememberNavController
|
||||||
|
import dev.meloda.overseerr.screens.login.presentation.LoginScreen
|
||||||
import dev.meloda.overseerr.screens.main.MainScreen
|
import dev.meloda.overseerr.screens.main.MainScreen
|
||||||
|
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.SettingsController
|
||||||
import dev.meloda.overseerr.theme.AppTheme
|
import dev.meloda.overseerr.theme.AppTheme
|
||||||
|
import dev.meloda.overseerr.theme.NavigationSettings
|
||||||
import io.github.aakira.napier.DebugAntilog
|
import io.github.aakira.napier.DebugAntilog
|
||||||
import io.github.aakira.napier.Napier
|
import io.github.aakira.napier.Napier
|
||||||
import org.koin.compose.KoinContext
|
import org.koin.compose.KoinContext
|
||||||
@@ -32,10 +37,31 @@ internal fun App() = KoinContext {
|
|||||||
settingsController.loadAppSettings()
|
settingsController.loadAppSettings()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val navController = rememberNavController()
|
||||||
|
|
||||||
|
NavigationSettings(navController)
|
||||||
|
|
||||||
AppTheme(themeMode = settings.themeMode) {
|
AppTheme(themeMode = settings.themeMode) {
|
||||||
Surface(modifier = Modifier.fillMaxSize()) {
|
Surface(modifier = Modifier.fillMaxSize()) {
|
||||||
Navigator(MainScreen()) { navigator ->
|
NavHost(
|
||||||
FadeTransition(navigator)
|
navController = navController,
|
||||||
|
startDestination = MainScreen
|
||||||
|
) {
|
||||||
|
composable<MainScreen> {
|
||||||
|
MainScreen(navController)
|
||||||
|
}
|
||||||
|
|
||||||
|
composable<LoginScreen> {
|
||||||
|
LoginScreen(onBack = navController::popBackStack)
|
||||||
|
}
|
||||||
|
|
||||||
|
composable<RequestsScreen> {
|
||||||
|
RequestsScreen(onBack = navController::popBackStack)
|
||||||
|
}
|
||||||
|
|
||||||
|
composable<UrlScreen> {
|
||||||
|
UrlScreen(onBack = navController::popBackStack)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
package dev.meloda.overseerr.model
|
package dev.meloda.overseerr.model
|
||||||
|
|
||||||
expect class Platform() {
|
internal expect class Platform() {
|
||||||
val name: String
|
val name: String
|
||||||
}
|
}
|
||||||
|
|||||||
+100
-102
@@ -8,8 +8,11 @@ import androidx.compose.material.icons.automirrored.rounded.ArrowForward
|
|||||||
import androidx.compose.material.icons.rounded.Visibility
|
import androidx.compose.material.icons.rounded.Visibility
|
||||||
import androidx.compose.material.icons.rounded.VisibilityOff
|
import androidx.compose.material.icons.rounded.VisibilityOff
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.text.input.ImeAction
|
import androidx.compose.ui.text.input.ImeAction
|
||||||
@@ -18,117 +21,112 @@ import androidx.compose.ui.text.input.PasswordVisualTransformation
|
|||||||
import androidx.compose.ui.text.input.VisualTransformation
|
import androidx.compose.ui.text.input.VisualTransformation
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
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.LoginViewModel
|
import dev.meloda.overseerr.screens.login.LoginViewModel
|
||||||
import dev.meloda.overseerr.screens.login.LoginViewModelImpl
|
import dev.meloda.overseerr.screens.login.LoginViewModelImpl
|
||||||
import dev.meloda.overseerr.screens.login.model.LoginScreenState
|
import dev.meloda.overseerr.screens.login.model.LoginScreenState
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
import org.koin.compose.viewmodel.koinViewModel
|
import org.koin.compose.viewmodel.koinViewModel
|
||||||
|
|
||||||
class LoginScreen : Screen {
|
@Serializable
|
||||||
|
data object LoginScreen
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
override fun Content() {
|
fun LoginScreen(onBack: () -> Unit = {}) {
|
||||||
val navigator = LocalNavigator.currentOrThrow
|
val viewModel: LoginViewModel = koinViewModel<LoginViewModelImpl>()
|
||||||
val viewModel: LoginViewModel = koinViewModel<LoginViewModelImpl>()
|
val screenState: LoginScreenState by viewModel.screenState.collectAsStateWithLifecycle()
|
||||||
val screenState: LoginScreenState by viewModel.screenState.collectAsStateWithLifecycle()
|
|
||||||
|
|
||||||
var loginValue by rememberSaveable {
|
var loginValue by rememberSaveable {
|
||||||
mutableStateOf(screenState.login)
|
mutableStateOf(screenState.login)
|
||||||
}
|
}
|
||||||
var passwordValue by rememberSaveable {
|
var passwordValue by rememberSaveable {
|
||||||
mutableStateOf(screenState.password)
|
mutableStateOf(screenState.password)
|
||||||
}
|
}
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
title = {
|
title = {
|
||||||
Text(text = "Log in")
|
Text(text = "Log in")
|
||||||
},
|
},
|
||||||
navigationIcon = {
|
navigationIcon = {
|
||||||
IconButton(
|
IconButton(onClick = onBack) {
|
||||||
onClick = navigator::pop
|
Icon(
|
||||||
) {
|
imageVector = Icons.AutoMirrored.Rounded.ArrowBack,
|
||||||
Icon(
|
contentDescription = null
|
||||||
imageVector = Icons.AutoMirrored.Rounded.ArrowBack,
|
)
|
||||||
contentDescription = null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
|
||||||
}
|
|
||||||
) { padding ->
|
|
||||||
Column(
|
|
||||||
modifier = Modifier.fillMaxSize()
|
|
||||||
.padding(padding),
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
|
||||||
) {
|
|
||||||
TextField(
|
|
||||||
modifier = Modifier.fillMaxWidth(0.9f),
|
|
||||||
value = loginValue,
|
|
||||||
onValueChange = { newText ->
|
|
||||||
loginValue = newText
|
|
||||||
viewModel.onLoginInputChanged(newText)
|
|
||||||
},
|
|
||||||
placeholder = { Text(text = "Login") },
|
|
||||||
isError = screenState.isLoginEmptyError,
|
|
||||||
singleLine = true
|
|
||||||
)
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
|
||||||
|
|
||||||
TextField(
|
|
||||||
modifier = Modifier.fillMaxWidth(0.9f),
|
|
||||||
value = passwordValue,
|
|
||||||
onValueChange = { newText ->
|
|
||||||
passwordValue = newText
|
|
||||||
viewModel.onPasswordInputChanged(newText)
|
|
||||||
},
|
|
||||||
placeholder = { Text(text = "Password") },
|
|
||||||
isError = screenState.isPasswordEmptyError,
|
|
||||||
trailingIcon = {
|
|
||||||
IconButton(onClick = viewModel::onPasswordVisibilityButtonClicked) {
|
|
||||||
Icon(
|
|
||||||
imageVector = if (screenState.isPasswordVisible) {
|
|
||||||
Icons.Rounded.VisibilityOff
|
|
||||||
} else {
|
|
||||||
Icons.Rounded.Visibility
|
|
||||||
},
|
|
||||||
contentDescription = if (screenState.isPasswordVisible) "Password visible icon"
|
|
||||||
else "Password invisible icon"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
keyboardOptions = KeyboardOptions.Default.copy(
|
|
||||||
imeAction = ImeAction.Go,
|
|
||||||
keyboardType = KeyboardType.Password
|
|
||||||
),
|
|
||||||
visualTransformation = if (screenState.isPasswordVisible) {
|
|
||||||
VisualTransformation.None
|
|
||||||
} else {
|
|
||||||
PasswordVisualTransformation()
|
|
||||||
},
|
|
||||||
singleLine = true
|
|
||||||
)
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
|
||||||
|
|
||||||
Button(
|
|
||||||
onClick = {
|
|
||||||
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
Text(text = "Authorize")
|
|
||||||
Spacer(modifier = Modifier.width(4.dp))
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.AutoMirrored.Rounded.ArrowForward,
|
|
||||||
contentDescription = null
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) { padding ->
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxSize()
|
||||||
|
.padding(padding),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
TextField(
|
||||||
|
modifier = Modifier.fillMaxWidth(0.9f),
|
||||||
|
value = loginValue,
|
||||||
|
onValueChange = { newText ->
|
||||||
|
loginValue = newText
|
||||||
|
viewModel.onLoginInputChanged(newText)
|
||||||
|
},
|
||||||
|
placeholder = { Text(text = "Login") },
|
||||||
|
isError = screenState.isLoginEmptyError,
|
||||||
|
singleLine = true
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
TextField(
|
||||||
|
modifier = Modifier.fillMaxWidth(0.9f),
|
||||||
|
value = passwordValue,
|
||||||
|
onValueChange = { newText ->
|
||||||
|
passwordValue = newText
|
||||||
|
viewModel.onPasswordInputChanged(newText)
|
||||||
|
},
|
||||||
|
placeholder = { Text(text = "Password") },
|
||||||
|
isError = screenState.isPasswordEmptyError,
|
||||||
|
trailingIcon = {
|
||||||
|
IconButton(onClick = viewModel::onPasswordVisibilityButtonClicked) {
|
||||||
|
Icon(
|
||||||
|
imageVector = if (screenState.isPasswordVisible) {
|
||||||
|
Icons.Rounded.VisibilityOff
|
||||||
|
} else {
|
||||||
|
Icons.Rounded.Visibility
|
||||||
|
},
|
||||||
|
contentDescription = if (screenState.isPasswordVisible) "Password visible icon"
|
||||||
|
else "Password invisible icon"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
keyboardOptions = KeyboardOptions.Default.copy(
|
||||||
|
imeAction = ImeAction.Go,
|
||||||
|
keyboardType = KeyboardType.Password
|
||||||
|
),
|
||||||
|
visualTransformation = if (screenState.isPasswordVisible) {
|
||||||
|
VisualTransformation.None
|
||||||
|
} else {
|
||||||
|
PasswordVisualTransformation()
|
||||||
|
},
|
||||||
|
singleLine = true
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(text = "Authorize")
|
||||||
|
Spacer(modifier = Modifier.width(4.dp))
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.AutoMirrored.Rounded.ArrowForward,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,85 +10,82 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import cafe.adriel.voyager.core.screen.Screen
|
import androidx.navigation.NavController
|
||||||
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.login.presentation.LoginScreen
|
||||||
import dev.meloda.overseerr.screens.requests.presentation.RequestsScreen
|
import dev.meloda.overseerr.screens.requests.presentation.RequestsScreen
|
||||||
import dev.meloda.overseerr.screens.url.presentation.UrlScreen
|
import dev.meloda.overseerr.screens.url.presentation.UrlScreen
|
||||||
import dev.meloda.overseerr.settings.SettingsController
|
import dev.meloda.overseerr.settings.SettingsController
|
||||||
import dev.meloda.overseerr.settings.model.ThemeMode
|
import dev.meloda.overseerr.settings.model.ThemeMode
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
import org.koin.compose.koinInject
|
import org.koin.compose.koinInject
|
||||||
|
|
||||||
class MainScreen : Screen {
|
@Serializable
|
||||||
|
data object MainScreen
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
override fun Content() {
|
fun MainScreen(navController: NavController) {
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
|
||||||
val navigator = LocalNavigator.currentOrThrow
|
val settingsController: SettingsController = koinInject()
|
||||||
|
val settings by settingsController.settings.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
val settingsController: SettingsController = koinInject()
|
Scaffold(
|
||||||
val settings by settingsController.settings.collectAsStateWithLifecycle()
|
topBar = {
|
||||||
|
TopAppBar(
|
||||||
|
title = { Text(text = "Main screen") },
|
||||||
|
actions = {
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
val newThemeMode = ThemeMode.entries.getOrElse(
|
||||||
|
ThemeMode.entries.indexOf(settings.themeMode) + 1
|
||||||
|
) { ThemeMode.System }
|
||||||
|
|
||||||
Scaffold(
|
settingsController.updateThemeMode(newThemeMode)
|
||||||
topBar = {
|
coroutineScope.launch {
|
||||||
TopAppBar(
|
settingsController.saveAppSettings()
|
||||||
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"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = when (settings.themeMode) {
|
||||||
|
ThemeMode.System -> "System"
|
||||||
|
ThemeMode.Dark -> "Dark"
|
||||||
|
ThemeMode.Light -> "Light"
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
}
|
)
|
||||||
) { padding ->
|
}
|
||||||
Row(
|
) { padding ->
|
||||||
modifier = Modifier
|
Row(
|
||||||
.padding(padding)
|
modifier = Modifier
|
||||||
.padding(horizontal = 16.dp),
|
.padding(padding)
|
||||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
.padding(horizontal = 16.dp),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
|
) {
|
||||||
|
Button(
|
||||||
|
onClick = { navController.navigate(UrlScreen) },
|
||||||
|
modifier = Modifier.weight(0.3f)
|
||||||
) {
|
) {
|
||||||
Button(
|
Text(text = "Url")
|
||||||
onClick = { navigator.push(UrlScreen()) },
|
}
|
||||||
modifier = Modifier.weight(0.3f)
|
|
||||||
) {
|
|
||||||
Text(text = "Url")
|
|
||||||
}
|
|
||||||
|
|
||||||
Button(
|
Button(
|
||||||
onClick = { navigator.push(LoginScreen()) },
|
onClick = { navController.navigate(LoginScreen) },
|
||||||
modifier = Modifier.weight(0.3f)
|
modifier = Modifier.weight(0.3f)
|
||||||
) {
|
) {
|
||||||
Text(text = "Login")
|
Text(text = "Login")
|
||||||
}
|
}
|
||||||
|
|
||||||
Button(
|
Button(
|
||||||
onClick = { navigator.push(RequestsScreen()) },
|
onClick = { navController.navigate(RequestsScreen) },
|
||||||
modifier = Modifier.weight(0.3f)
|
modifier = Modifier.weight(0.3f)
|
||||||
) {
|
) {
|
||||||
Text(text = "Requests")
|
Text(text = "Requests")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+130
-126
@@ -12,161 +12,165 @@ import androidx.compose.material3.*
|
|||||||
import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults.Indicator
|
import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults.Indicator
|
||||||
import androidx.compose.material3.pulltorefresh.pullToRefresh
|
import androidx.compose.material3.pulltorefresh.pullToRefresh
|
||||||
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
|
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.unit.LayoutDirection
|
import androidx.compose.ui.unit.LayoutDirection
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import cafe.adriel.voyager.core.screen.Screen
|
import dev.chrisbanes.haze.HazeState
|
||||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
import dev.chrisbanes.haze.hazeEffect
|
||||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
import dev.chrisbanes.haze.hazeSource
|
||||||
import dev.chrisbanes.haze.*
|
|
||||||
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
|
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
|
||||||
import dev.chrisbanes.haze.materials.HazeMaterials
|
import dev.chrisbanes.haze.materials.HazeMaterials
|
||||||
import dev.meloda.overseerr.screens.requests.RequestsViewModel
|
import dev.meloda.overseerr.screens.requests.RequestsViewModel
|
||||||
import dev.meloda.overseerr.screens.requests.RequestsViewModelImpl
|
import dev.meloda.overseerr.screens.requests.RequestsViewModelImpl
|
||||||
import dev.meloda.overseerr.screens.requests.model.RequestsScreenState
|
import dev.meloda.overseerr.screens.requests.model.RequestsScreenState
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
import org.koin.compose.viewmodel.koinViewModel
|
import org.koin.compose.viewmodel.koinViewModel
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
class RequestsScreen : Screen {
|
@Serializable
|
||||||
|
data object RequestsScreen
|
||||||
|
|
||||||
@OptIn(
|
@OptIn(
|
||||||
ExperimentalMaterial3Api::class,
|
ExperimentalMaterial3Api::class,
|
||||||
ExperimentalHazeMaterialsApi::class, ExperimentalMaterial3Api::class
|
ExperimentalHazeMaterialsApi::class
|
||||||
)
|
)
|
||||||
@Composable
|
@Composable
|
||||||
override fun Content() {
|
fun RequestsScreen(
|
||||||
val navigator = LocalNavigator.currentOrThrow
|
onBack: () -> Unit = {}
|
||||||
val viewModel: RequestsViewModel = koinViewModel<RequestsViewModelImpl>()
|
) {
|
||||||
val screenState: RequestsScreenState by viewModel.screenState.collectAsStateWithLifecycle()
|
val viewModel: RequestsViewModel = koinViewModel<RequestsViewModelImpl>()
|
||||||
|
val screenState: RequestsScreenState by viewModel.screenState.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
val hazeState = remember { HazeState() }
|
val hazeState = remember { HazeState() }
|
||||||
val hazeStyle = HazeMaterials.ultraThin()
|
val hazeStyle = HazeMaterials.ultraThin()
|
||||||
|
|
||||||
val refreshState = rememberPullToRefreshState()
|
val refreshState = rememberPullToRefreshState()
|
||||||
|
|
||||||
LaunchedEffect(screenState) {
|
LaunchedEffect(screenState) {
|
||||||
if (screenState.apiInfo != null) {
|
if (screenState.apiInfo != null) {
|
||||||
delay(5.seconds)
|
delay(5.seconds)
|
||||||
viewModel.onSuccessMessageShown()
|
viewModel.onSuccessMessageShown()
|
||||||
}
|
|
||||||
|
|
||||||
if (screenState.apiErrorText != null) {
|
|
||||||
delay(5.seconds)
|
|
||||||
viewModel.onErrorMessageShown()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Scaffold(
|
if (screenState.apiErrorText != null) {
|
||||||
modifier = Modifier.fillMaxSize(),
|
delay(5.seconds)
|
||||||
topBar = {
|
viewModel.onErrorMessageShown()
|
||||||
TopAppBar(
|
}
|
||||||
title = { Text(text = "Requests") },
|
}
|
||||||
navigationIcon = {
|
|
||||||
IconButton(onClick = navigator::pop) {
|
Scaffold(
|
||||||
Icon(
|
modifier = Modifier.fillMaxSize(),
|
||||||
imageVector = Icons.AutoMirrored.Rounded.ArrowBack,
|
topBar = {
|
||||||
contentDescription = null
|
TopAppBar(
|
||||||
|
title = { Text(text = "Requests") },
|
||||||
|
navigationIcon = {
|
||||||
|
IconButton(onClick = onBack) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.AutoMirrored.Rounded.ArrowBack,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
colors = TopAppBarDefaults.topAppBarColors(containerColor = Color.Transparent),
|
||||||
|
modifier = Modifier
|
||||||
|
.hazeEffect(
|
||||||
|
state = hazeState,
|
||||||
|
style = hazeStyle
|
||||||
|
).fillMaxWidth(),
|
||||||
|
actions = {
|
||||||
|
IconButton(
|
||||||
|
onClick = viewModel::onRefresh
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Rounded.Refresh,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) { padding ->
|
||||||
|
val bottomPadding = padding.calculateBottomPadding()
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(start = padding.calculateStartPadding(LayoutDirection.Ltr))
|
||||||
|
.padding(end = padding.calculateEndPadding(LayoutDirection.Ltr))
|
||||||
|
) {
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.hazeSource(state = hazeState)
|
||||||
|
.pullToRefresh(
|
||||||
|
isRefreshing = screenState.isLoading,
|
||||||
|
state = refreshState,
|
||||||
|
onRefresh = viewModel::onRefresh
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
item {
|
||||||
|
Spacer(modifier = Modifier.height(padding.calculateTopPadding()))
|
||||||
|
}
|
||||||
|
item {
|
||||||
|
AnimatedVisibility(screenState.apiErrorText != null || screenState.apiInfo != null) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.background(
|
||||||
|
if (screenState.apiInfo != null) Color(0xffb00b69)
|
||||||
|
else Color.Red
|
||||||
|
),
|
||||||
|
contentAlignment = Alignment.CenterStart
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = screenState.apiErrorText ?: screenState.apiInfo.toString(),
|
||||||
|
style = MaterialTheme.typography.headlineMedium,
|
||||||
|
modifier = Modifier.padding(10.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
colors = TopAppBarDefaults.topAppBarColors(containerColor = Color.Transparent),
|
}
|
||||||
|
items(items = screenState.dummyItems) { index ->
|
||||||
|
Text(
|
||||||
|
text = "Text #${index + 1}",
|
||||||
|
style = MaterialTheme.typography.headlineLarge,
|
||||||
|
modifier = Modifier.background(Color.Red)
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(64.dp))
|
||||||
|
}
|
||||||
|
item {
|
||||||
|
Spacer(modifier = Modifier.height(bottomPadding))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Indicator(
|
||||||
|
state = refreshState,
|
||||||
|
isRefreshing = screenState.isLoading,
|
||||||
|
modifier = Modifier.align(Alignment.TopCenter)
|
||||||
|
.padding(top = padding.calculateTopPadding())
|
||||||
|
)
|
||||||
|
|
||||||
|
if (bottomPadding.value > 0) {
|
||||||
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
.align(Alignment.BottomCenter)
|
||||||
.hazeEffect(
|
.hazeEffect(
|
||||||
state = hazeState,
|
state = hazeState,
|
||||||
style = hazeStyle
|
style = hazeStyle
|
||||||
).fillMaxWidth(),
|
|
||||||
actions = {
|
|
||||||
IconButton(
|
|
||||||
onClick = viewModel::onRefresh
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Rounded.Refresh,
|
|
||||||
contentDescription = null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
) { padding ->
|
|
||||||
val bottomPadding = padding.calculateBottomPadding()
|
|
||||||
|
|
||||||
Box(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.padding(start = padding.calculateStartPadding(LayoutDirection.Ltr))
|
|
||||||
.padding(end = padding.calculateEndPadding(LayoutDirection.Ltr))
|
|
||||||
) {
|
|
||||||
LazyColumn(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.hazeSource(state = hazeState)
|
|
||||||
.pullToRefresh(
|
|
||||||
isRefreshing = screenState.isLoading,
|
|
||||||
state = refreshState,
|
|
||||||
onRefresh = viewModel::onRefresh
|
|
||||||
)
|
)
|
||||||
) {
|
.background(Color.Transparent)
|
||||||
item {
|
.height(bottomPadding)
|
||||||
Spacer(modifier = Modifier.height(padding.calculateTopPadding()))
|
.fillMaxWidth()
|
||||||
}
|
|
||||||
item {
|
|
||||||
AnimatedVisibility(screenState.apiErrorText != null || screenState.apiInfo != null) {
|
|
||||||
Box(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.background(
|
|
||||||
if (screenState.apiInfo != null) Color(0xffb00b69)
|
|
||||||
else Color.Red
|
|
||||||
),
|
|
||||||
contentAlignment = Alignment.CenterStart
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = screenState.apiErrorText ?: screenState.apiInfo.toString(),
|
|
||||||
style = MaterialTheme.typography.headlineMedium,
|
|
||||||
modifier = Modifier.padding(10.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
items(items = screenState.dummyItems) { index ->
|
|
||||||
Text(
|
|
||||||
text = "Text #${index + 1}",
|
|
||||||
style = MaterialTheme.typography.headlineLarge,
|
|
||||||
modifier = Modifier.background(Color.Red)
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.height(64.dp))
|
|
||||||
}
|
|
||||||
item {
|
|
||||||
Spacer(modifier = Modifier.height(bottomPadding))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Indicator(
|
|
||||||
state = refreshState,
|
|
||||||
isRefreshing = screenState.isLoading,
|
|
||||||
modifier = Modifier.align(Alignment.TopCenter)
|
|
||||||
.padding(top = padding.calculateTopPadding())
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if (bottomPadding.value > 0) {
|
|
||||||
Box(
|
|
||||||
modifier = Modifier
|
|
||||||
.align(Alignment.BottomCenter)
|
|
||||||
.hazeEffect(
|
|
||||||
state = hazeState,
|
|
||||||
style = hazeStyle
|
|
||||||
)
|
|
||||||
.background(Color.Transparent)
|
|
||||||
.height(bottomPadding)
|
|
||||||
.fillMaxWidth()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+77
-80
@@ -13,99 +13,96 @@ import androidx.compose.ui.text.input.ImeAction
|
|||||||
import androidx.compose.ui.text.input.KeyboardType
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
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.url.UrlViewModel
|
import dev.meloda.overseerr.screens.url.UrlViewModel
|
||||||
import dev.meloda.overseerr.screens.url.UrlViewModelImpl
|
import dev.meloda.overseerr.screens.url.UrlViewModelImpl
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
import org.koin.compose.viewmodel.koinViewModel
|
import org.koin.compose.viewmodel.koinViewModel
|
||||||
|
|
||||||
class UrlScreen : Screen {
|
@Serializable
|
||||||
|
data object UrlScreen
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
override fun Content() {
|
fun UrlScreen(onBack: () -> Unit = {}) {
|
||||||
val navigator = LocalNavigator.currentOrThrow
|
val viewModel: UrlViewModel = koinViewModel<UrlViewModelImpl>()
|
||||||
val viewModel: UrlViewModel = koinViewModel<UrlViewModelImpl>()
|
val screenState by viewModel.screenState.collectAsStateWithLifecycle()
|
||||||
val screenState by viewModel.screenState.collectAsStateWithLifecycle()
|
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
title = { Text(text = "Url") },
|
title = { Text(text = "Url") },
|
||||||
navigationIcon = {
|
navigationIcon = {
|
||||||
IconButton(onClick = navigator::pop) {
|
IconButton(onClick = onBack) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.AutoMirrored.Rounded.ArrowBack,
|
imageVector = Icons.AutoMirrored.Rounded.ArrowBack,
|
||||||
contentDescription = null
|
contentDescription = null
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) { padding ->
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(padding)
|
||||||
|
.padding(horizontal = 16.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
TextField(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
value = screenState.url,
|
||||||
|
onValueChange = viewModel::onUrlInputChanged,
|
||||||
|
placeholder = { Text(text = "Url") },
|
||||||
|
isError = screenState.isWrongUrlError,
|
||||||
|
singleLine = true,
|
||||||
|
keyboardOptions = KeyboardOptions.Default.copy(
|
||||||
|
imeAction = ImeAction.Go,
|
||||||
|
keyboardType = KeyboardType.Uri
|
||||||
)
|
)
|
||||||
}
|
)
|
||||||
) { padding ->
|
|
||||||
Column(
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize()
|
TextField(
|
||||||
.padding(padding)
|
modifier = Modifier.fillMaxWidth(),
|
||||||
.padding(horizontal = 16.dp),
|
value = screenState.plexToken,
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
onValueChange = viewModel::onPlexTokenInputChanged,
|
||||||
|
placeholder = { Text(text = "Token") },
|
||||||
|
singleLine = true,
|
||||||
|
keyboardOptions = KeyboardOptions.Default.copy(
|
||||||
|
imeAction = ImeAction.Go
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
) {
|
) {
|
||||||
TextField(
|
Button(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
onClick = viewModel::onLoadButtonClicked,
|
||||||
value = screenState.url,
|
modifier = Modifier.weight(0.3f)
|
||||||
onValueChange = viewModel::onUrlInputChanged,
|
|
||||||
placeholder = { Text(text = "Url") },
|
|
||||||
isError = screenState.isWrongUrlError,
|
|
||||||
singleLine = true,
|
|
||||||
keyboardOptions = KeyboardOptions.Default.copy(
|
|
||||||
imeAction = ImeAction.Go,
|
|
||||||
keyboardType = KeyboardType.Uri
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
|
||||||
|
|
||||||
TextField(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
value = screenState.plexToken,
|
|
||||||
onValueChange = viewModel::onPlexTokenInputChanged,
|
|
||||||
placeholder = { Text(text = "Token") },
|
|
||||||
singleLine = true,
|
|
||||||
keyboardOptions = KeyboardOptions.Default.copy(
|
|
||||||
imeAction = ImeAction.Go
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
|
||||||
|
|
||||||
Row(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
|
||||||
) {
|
) {
|
||||||
Button(
|
Text(text = "Load")
|
||||||
onClick = viewModel::onLoadButtonClicked,
|
}
|
||||||
modifier = Modifier.weight(0.3f)
|
|
||||||
) {
|
|
||||||
Text(text = "Load")
|
|
||||||
}
|
|
||||||
|
|
||||||
Button(
|
Button(
|
||||||
onClick = viewModel::onSaveButtonClicked,
|
onClick = viewModel::onSaveButtonClicked,
|
||||||
modifier = Modifier.weight(0.3f)
|
modifier = Modifier.weight(0.3f)
|
||||||
) {
|
) {
|
||||||
Text(text = "Save")
|
Text(text = "Save")
|
||||||
}
|
}
|
||||||
|
|
||||||
Button(
|
Button(
|
||||||
onClick = viewModel::onTestButtonClicked,
|
onClick = viewModel::onTestButtonClicked,
|
||||||
modifier = Modifier.weight(0.3f)
|
modifier = Modifier.weight(0.3f)
|
||||||
) {
|
) {
|
||||||
Text(text = "Test")
|
Text(text = "Test")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,7 @@ import androidx.compose.material3.Surface
|
|||||||
import androidx.compose.material3.darkColorScheme
|
import androidx.compose.material3.darkColorScheme
|
||||||
import androidx.compose.material3.lightColorScheme
|
import androidx.compose.material3.lightColorScheme
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.navigation.NavController
|
||||||
import dev.meloda.overseerr.settings.model.ThemeMode
|
import dev.meloda.overseerr.settings.model.ThemeMode
|
||||||
|
|
||||||
internal val LocalThemeIsDark = compositionLocalOf { mutableStateOf(true) }
|
internal val LocalThemeIsDark = compositionLocalOf { mutableStateOf(true) }
|
||||||
@@ -36,3 +37,6 @@ internal fun AppTheme(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal expect fun SystemAppearance(isDark: Boolean)
|
internal expect fun SystemAppearance(isDark: Boolean)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal expect fun NavigationSettings(navController: NavController)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package dev.meloda.overseerr.model
|
package dev.meloda.overseerr.model
|
||||||
|
|
||||||
actual class Platform actual constructor() {
|
internal actual class Platform actual constructor() {
|
||||||
actual val name: String
|
actual val name: String
|
||||||
get() = "iOS"
|
get() = "iOS"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package dev.meloda.overseerr.theme
|
|||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.navigation.NavController
|
||||||
import platform.UIKit.UIApplication
|
import platform.UIKit.UIApplication
|
||||||
import platform.UIKit.UIStatusBarStyleDarkContent
|
import platform.UIKit.UIStatusBarStyleDarkContent
|
||||||
import platform.UIKit.UIStatusBarStyleLightContent
|
import platform.UIKit.UIStatusBarStyleLightContent
|
||||||
@@ -14,4 +15,8 @@ internal actual fun SystemAppearance(isDark: Boolean) {
|
|||||||
if (isDark) UIStatusBarStyleDarkContent else UIStatusBarStyleLightContent
|
if (isDark) UIStatusBarStyleDarkContent else UIStatusBarStyleLightContent
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal actual fun NavigationSettings(navController: NavController) {
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package dev.meloda.overseerr.model
|
package dev.meloda.overseerr.model
|
||||||
|
|
||||||
actual class Platform actual constructor() {
|
internal actual class Platform actual constructor() {
|
||||||
actual val name: String
|
actual val name: String
|
||||||
get() = "JVM"
|
get() = "JVM"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
package dev.meloda.overseerr.theme
|
package dev.meloda.overseerr.theme
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal actual fun SystemAppearance(isDark: Boolean) {
|
internal actual fun SystemAppearance(isDark: Boolean) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal actual fun NavigationSettings(navController: NavController) {
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
package dev.meloda.overseerr.model
|
package dev.meloda.overseerr.model
|
||||||
|
|
||||||
actual class Platform actual constructor() {
|
internal actual class Platform actual constructor() {
|
||||||
actual val name: String = "JS"
|
actual val name: String = "JS"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,20 @@
|
|||||||
package dev.meloda.overseerr.theme
|
package dev.meloda.overseerr.theme
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.navigation.ExperimentalBrowserHistoryApi
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import androidx.navigation.bindToNavigation
|
||||||
|
import kotlinx.browser.window
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal actual fun SystemAppearance(isDark: Boolean) {
|
internal actual fun SystemAppearance(isDark: Boolean) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalBrowserHistoryApi::class)
|
||||||
|
@Composable
|
||||||
|
internal actual fun NavigationSettings(navController: NavController) {
|
||||||
|
LaunchedEffect(navController) {
|
||||||
|
window.bindToNavigation(navController)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,7 +6,6 @@ agp = "8.7.3"
|
|||||||
androidx-lifecycle = "2.8.4"
|
androidx-lifecycle = "2.8.4"
|
||||||
androidx-activity-compose = "1.10.1"
|
androidx-activity-compose = "1.10.1"
|
||||||
androidx-uitest = "1.7.8"
|
androidx-uitest = "1.7.8"
|
||||||
voyager = "1.1.0-beta03"
|
|
||||||
coil = "3.0.4"
|
coil = "3.0.4"
|
||||||
kotlinx-coroutines = "1.10.1"
|
kotlinx-coroutines = "1.10.1"
|
||||||
ktor = "3.0.1"
|
ktor = "3.0.1"
|
||||||
@@ -17,6 +16,7 @@ haze = "1.5.2"
|
|||||||
kstore = "0.9.1"
|
kstore = "0.9.1"
|
||||||
appdirs = "1.2.2"
|
appdirs = "1.2.2"
|
||||||
napier = "2.7.1"
|
napier = "2.7.1"
|
||||||
|
androidx-navigation-compose = "2.9.0-alpha15"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
|
|
||||||
@@ -24,10 +24,9 @@ androidx-activityCompose = { module = "androidx.activity:activity-compose", vers
|
|||||||
androidx-lifecycle-viewmodel-compose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "viewmodel-compose" }
|
androidx-lifecycle-viewmodel-compose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "viewmodel-compose" }
|
||||||
androidx-lifecycle-viewmodel = { group = "org.jetbrains.androidx.lifecycle", name = "lifecycle-viewmodel", version.ref = "androidx-lifecycle" }
|
androidx-lifecycle-viewmodel = { group = "org.jetbrains.androidx.lifecycle", name = "lifecycle-viewmodel", version.ref = "androidx-lifecycle" }
|
||||||
androidx-lifecycle-runtime-compose = { group = "org.jetbrains.androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "androidx-lifecycle" }
|
androidx-lifecycle-runtime-compose = { group = "org.jetbrains.androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "androidx-lifecycle" }
|
||||||
|
androidx-navigation-compose = { module = "org.jetbrains.androidx.navigation:navigation-compose", version.ref = "androidx-navigation-compose" }
|
||||||
androidx-uitest-testManifest = { module = "androidx.compose.ui:ui-test-manifest", version.ref = "androidx-uitest" }
|
androidx-uitest-testManifest = { module = "androidx.compose.ui:ui-test-manifest", version.ref = "androidx-uitest" }
|
||||||
androidx-uitest-junit4 = { module = "androidx.compose.ui:ui-test-junit4", version.ref = "androidx-uitest" }
|
androidx-uitest-junit4 = { module = "androidx.compose.ui:ui-test-junit4", version.ref = "androidx-uitest" }
|
||||||
voyager-navigator = { module = "cafe.adriel.voyager:voyager-navigator", version.ref = "voyager" }
|
|
||||||
voyager-transitions = { module = "cafe.adriel.voyager:voyager-transitions", version.ref = "voyager" }
|
|
||||||
coil = { module = "io.coil-kt.coil3:coil-compose-core", version.ref = "coil" }
|
coil = { module = "io.coil-kt.coil3:coil-compose-core", version.ref = "coil" }
|
||||||
coil-network-ktor = { module = "io.coil-kt.coil3:coil-network-ktor3", version.ref = "coil" }
|
coil-network-ktor = { module = "io.coil-kt.coil3:coil-network-ktor3", version.ref = "coil" }
|
||||||
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" }
|
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" }
|
||||||
|
|||||||
Reference in New Issue
Block a user