Handle logic login, save tokens, change theme, change server url
This commit is contained in:
@@ -20,7 +20,7 @@
|
|||||||
<string name="auto_connect">Auto-connect device</string>
|
<string name="auto_connect">Auto-connect device</string>
|
||||||
|
|
||||||
<string name="lbl_server_url">Server URL</string>
|
<string name="lbl_server_url">Server URL</string>
|
||||||
<string name="server_url_placeholder">Ex: http://192.168.1.6:8001 or https://example.com</string>
|
<string name="server_url_placeholder">Ex: https://example.com</string>
|
||||||
<string name="lbl_username">Username</string>
|
<string name="lbl_username">Username</string>
|
||||||
<string name="lbl_password">Password</string>
|
<string name="lbl_password">Password</string>
|
||||||
<string name="btn_login">Login</string>
|
<string name="btn_login">Login</string>
|
||||||
|
|||||||
+3
-1
@@ -15,9 +15,11 @@ import io.ktor.http.contentType
|
|||||||
|
|
||||||
class AuthApi(
|
class AuthApi(
|
||||||
private val client: HttpClient,
|
private val client: HttpClient,
|
||||||
private val baseUrl: String,
|
|
||||||
private val tokenManager: TokenManager
|
private val tokenManager: TokenManager
|
||||||
) {
|
) {
|
||||||
|
private val baseUrl: String?
|
||||||
|
get() = tokenManager.getServerUrl()
|
||||||
|
|
||||||
suspend fun login(username: String, password: String): Boolean {
|
suspend fun login(username: String, password: String): Boolean {
|
||||||
return try {
|
return try {
|
||||||
val res: LoginResponseDto = client.post("$baseUrl${Endpoints.AUTH_TOKEN}") {
|
val res: LoginResponseDto = client.post("$baseUrl${Endpoints.AUTH_TOKEN}") {
|
||||||
|
|||||||
+5
-1
@@ -2,6 +2,7 @@ package com.digitoolsolutions.app.torquevaultkmp.data.network.api
|
|||||||
|
|
||||||
import com.digitoolsolutions.app.torquevaultkmp.data.network.dto.ConfirmRequestDto
|
import com.digitoolsolutions.app.torquevaultkmp.data.network.dto.ConfirmRequestDto
|
||||||
import com.digitoolsolutions.app.torquevaultkmp.data.network.dto.WorkOrderDto
|
import com.digitoolsolutions.app.torquevaultkmp.data.network.dto.WorkOrderDto
|
||||||
|
import com.digitoolsolutions.app.torquevaultkmp.data.storage.TokenManager
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
import io.ktor.client.call.body
|
import io.ktor.client.call.body
|
||||||
import io.ktor.client.request.get
|
import io.ktor.client.request.get
|
||||||
@@ -11,8 +12,11 @@ import io.ktor.client.request.setBody
|
|||||||
|
|
||||||
class WorkOrderApi(
|
class WorkOrderApi(
|
||||||
private val client: HttpClient,
|
private val client: HttpClient,
|
||||||
private val baseUrl: String
|
private val tokenManager: TokenManager
|
||||||
) {
|
) {
|
||||||
|
private val baseUrl: String?
|
||||||
|
get() = tokenManager.getServerUrl()
|
||||||
|
|
||||||
suspend fun fetchAbleWorkOrders(page: Int): List<WorkOrderDto> {
|
suspend fun fetchAbleWorkOrders(page: Int): List<WorkOrderDto> {
|
||||||
return client.get("$baseUrl${Endpoints.WORK_ORDERS}") {
|
return client.get("$baseUrl${Endpoints.WORK_ORDERS}") {
|
||||||
parameter("page", page)
|
parameter("page", page)
|
||||||
|
|||||||
+2
@@ -3,5 +3,7 @@ package com.digitoolsolutions.app.torquevaultkmp.data.storage
|
|||||||
object ReferKeys {
|
object ReferKeys {
|
||||||
const val ACCESS_TOKEN = "access_token"
|
const val ACCESS_TOKEN = "access_token"
|
||||||
const val REFRESH_TOKEN = "refresh_token"
|
const val REFRESH_TOKEN = "refresh_token"
|
||||||
|
const val SERVER_URL = "server_url"
|
||||||
const val THEME = "theme"
|
const val THEME = "theme"
|
||||||
|
const val DEVICE_AUTO_CONNECT = "device_auto_connect"
|
||||||
}
|
}
|
||||||
|
|||||||
+34
@@ -4,6 +4,9 @@ class TokenManager(private val storage: AppStorage) {
|
|||||||
private var accessToken: String? = null
|
private var accessToken: String? = null
|
||||||
private var refreshToken: String? = null
|
private var refreshToken: String? = null
|
||||||
|
|
||||||
|
// Cache for the full API URL
|
||||||
|
private var cachedFullUrl: String? = null
|
||||||
|
|
||||||
fun getAccessToken(): String? {
|
fun getAccessToken(): String? {
|
||||||
if (accessToken == null) accessToken = storage.load(ReferKeys.ACCESS_TOKEN)
|
if (accessToken == null) accessToken = storage.load(ReferKeys.ACCESS_TOKEN)
|
||||||
return accessToken
|
return accessToken
|
||||||
@@ -13,6 +16,27 @@ class TokenManager(private val storage: AppStorage) {
|
|||||||
return refreshToken
|
return refreshToken
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the full API URL (e.g., http://domain:3000/api/v1)
|
||||||
|
*/
|
||||||
|
fun getServerUrl(): String? {
|
||||||
|
if (cachedFullUrl != null) return cachedFullUrl
|
||||||
|
|
||||||
|
val base: String = storage.load(ReferKeys.SERVER_URL) ?: ""
|
||||||
|
if (base.isEmpty()) return null
|
||||||
|
|
||||||
|
val url = base.trim().trimEnd('/')
|
||||||
|
cachedFullUrl = if (url.endsWith("/api/v1")) url else "$url/api/v1"
|
||||||
|
return cachedFullUrl
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the base domain for UI display (e.g., http://domain:3000)
|
||||||
|
*/
|
||||||
|
fun getBaseUrl(): String? {
|
||||||
|
return storage.load(ReferKeys.SERVER_URL)
|
||||||
|
}
|
||||||
|
|
||||||
fun saveTokens(access: String, refresh: String) {
|
fun saveTokens(access: String, refresh: String) {
|
||||||
accessToken = access
|
accessToken = access
|
||||||
refreshToken = refresh
|
refreshToken = refresh
|
||||||
@@ -20,6 +44,16 @@ class TokenManager(private val storage: AppStorage) {
|
|||||||
storage.save(ReferKeys.REFRESH_TOKEN, refresh)
|
storage.save(ReferKeys.REFRESH_TOKEN, refresh)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun saveServerUrl(url: String) {
|
||||||
|
// Clean the input: remove trailing slashes and redundant /api/v1
|
||||||
|
val base = url.trim().trimEnd('/').removeSuffix("/api/v1").trimEnd('/')
|
||||||
|
|
||||||
|
storage.save(ReferKeys.SERVER_URL, base)
|
||||||
|
|
||||||
|
// Invalidate cache so it's recomputed on next call
|
||||||
|
cachedFullUrl = if (base.isEmpty()) null else "$base/api/v1"
|
||||||
|
}
|
||||||
|
|
||||||
fun clearTokens() {
|
fun clearTokens() {
|
||||||
accessToken = null
|
accessToken = null
|
||||||
refreshToken = null
|
refreshToken = null
|
||||||
|
|||||||
+4
-4
@@ -10,6 +10,7 @@ import com.digitoolsolutions.app.torquevaultkmp.data.storage.TokenManager
|
|||||||
import com.digitoolsolutions.app.torquevaultkmp.domain.usecase.FetchWorkOrdersUseCase
|
import com.digitoolsolutions.app.torquevaultkmp.domain.usecase.FetchWorkOrdersUseCase
|
||||||
import com.digitoolsolutions.app.torquevaultkmp.screens.auth.AuthViewModel
|
import com.digitoolsolutions.app.torquevaultkmp.screens.auth.AuthViewModel
|
||||||
import com.digitoolsolutions.app.torquevaultkmp.screens.home.HomeViewModel
|
import com.digitoolsolutions.app.torquevaultkmp.screens.home.HomeViewModel
|
||||||
|
import com.digitoolsolutions.app.torquevaultkmp.screens.settings.SettingViewModel
|
||||||
import org.koin.core.module.Module
|
import org.koin.core.module.Module
|
||||||
import org.koin.core.module.dsl.factoryOf
|
import org.koin.core.module.dsl.factoryOf
|
||||||
import org.koin.core.qualifier.named
|
import org.koin.core.qualifier.named
|
||||||
@@ -23,13 +24,11 @@ val appModule = module {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val networkModule = module {
|
val networkModule = module {
|
||||||
single { "http://digitoolsolutions.synology.me:3000/api/v1" } // base URL
|
|
||||||
|
|
||||||
// Auth client (no interceptor to avoid circular dependency and recursion)
|
// Auth client (no interceptor to avoid circular dependency and recursion)
|
||||||
single(named("authClient")) { createPlatformHttpClient(get(), null) }
|
single(named("authClient")) { createPlatformHttpClient(get(), null) }
|
||||||
|
|
||||||
// AuthApi uses the auth client
|
// AuthApi uses the auth client
|
||||||
single<AuthApi> { AuthApi(get(named("authClient")), get(), get()) }
|
single<AuthApi> { AuthApi(get(named("authClient")), get()) }
|
||||||
|
|
||||||
// Main client uses AuthApi for its interceptor
|
// Main client uses AuthApi for its interceptor
|
||||||
single { createPlatformHttpClient(get(), get()) }
|
single { createPlatformHttpClient(get(), get()) }
|
||||||
@@ -41,6 +40,7 @@ val networkModule = module {
|
|||||||
|
|
||||||
val viewModelModule = module {
|
val viewModelModule = module {
|
||||||
factoryOf(::HomeViewModel)
|
factoryOf(::HomeViewModel)
|
||||||
factoryOf(::AuthViewModel)
|
factory { AuthViewModel(get(), get()) }
|
||||||
factoryOf(::AppViewModel)
|
factoryOf(::AppViewModel)
|
||||||
|
factoryOf(::SettingViewModel)
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-1
@@ -3,6 +3,7 @@ package com.digitoolsolutions.app.torquevaultkmp.screens.auth
|
|||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.digitoolsolutions.app.torquevaultkmp.data.network.api.AuthApi
|
import com.digitoolsolutions.app.torquevaultkmp.data.network.api.AuthApi
|
||||||
|
import com.digitoolsolutions.app.torquevaultkmp.data.storage.TokenManager
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -15,15 +16,24 @@ sealed class LoginUiState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class AuthViewModel(
|
class AuthViewModel(
|
||||||
private val authApi: AuthApi
|
private val authApi: AuthApi,
|
||||||
|
private val tokenManager: TokenManager
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val _loginState = MutableStateFlow<LoginUiState>(LoginUiState.Idle)
|
private val _loginState = MutableStateFlow<LoginUiState>(LoginUiState.Idle)
|
||||||
val loginState = _loginState.asStateFlow()
|
val loginState = _loginState.asStateFlow()
|
||||||
|
|
||||||
|
private val _serverUrl = MutableStateFlow(tokenManager.getBaseUrl() ?: "")
|
||||||
|
val serverUrl = _serverUrl.asStateFlow()
|
||||||
|
|
||||||
|
fun onChangeServerUrl(url: String) {
|
||||||
|
_serverUrl.value = url
|
||||||
|
}
|
||||||
|
|
||||||
fun login(username: String, password: String) {
|
fun login(username: String, password: String) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
_loginState.value = LoginUiState.Loading
|
_loginState.value = LoginUiState.Loading
|
||||||
|
tokenManager.saveServerUrl(_serverUrl.value)
|
||||||
try {
|
try {
|
||||||
val success = authApi.login(username, password)
|
val success = authApi.login(username, password)
|
||||||
if (success) {
|
if (success) {
|
||||||
|
|||||||
+39
-18
@@ -2,6 +2,7 @@ package com.digitoolsolutions.app.torquevaultkmp.screens.auth
|
|||||||
|
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.gestures.detectTapGestures
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
@@ -13,9 +14,11 @@ import androidx.compose.foundation.layout.imePadding
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.text.KeyboardActions
|
import androidx.compose.foundation.text.KeyboardActions
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Visibility
|
import androidx.compose.material.icons.filled.Visibility
|
||||||
import androidx.compose.material.icons.filled.VisibilityOff
|
import androidx.compose.material.icons.filled.VisibilityOff
|
||||||
@@ -42,7 +45,9 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.focus.FocusDirection
|
import androidx.compose.ui.focus.FocusDirection
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
import androidx.compose.ui.platform.LocalFocusManager
|
import androidx.compose.ui.platform.LocalFocusManager
|
||||||
|
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||||
import androidx.compose.ui.text.input.ImeAction
|
import androidx.compose.ui.text.input.ImeAction
|
||||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||||
import androidx.compose.ui.text.input.VisualTransformation
|
import androidx.compose.ui.text.input.VisualTransformation
|
||||||
@@ -69,16 +74,20 @@ fun LoginScreen(
|
|||||||
) {
|
) {
|
||||||
val viewModel = koinViewModel<AuthViewModel>()
|
val viewModel = koinViewModel<AuthViewModel>()
|
||||||
val loginState by viewModel.loginState.collectAsState()
|
val loginState by viewModel.loginState.collectAsState()
|
||||||
|
val serverUrl by viewModel.serverUrl.collectAsState()
|
||||||
|
|
||||||
LoginScreenContent(
|
LoginScreenContent(
|
||||||
viewModel = viewModel,
|
viewModel = viewModel,
|
||||||
loginState = loginState,
|
loginState = loginState,
|
||||||
onLogin = { serverUrl, username, password ->
|
serverUrl = serverUrl,
|
||||||
// viewModel.updateServerUrl(serverUrl)
|
onLogin = { username, password ->
|
||||||
viewModel.login(username, password)
|
viewModel.login(username, password)
|
||||||
},
|
},
|
||||||
onLoginSuccess = {
|
onLoginSuccess = {
|
||||||
navController.navigate(HomeDestination.route)
|
navController.navigate(HomeDestination.route) {
|
||||||
|
popUpTo(AuthDestination.route) { inclusive = true }
|
||||||
|
launchSingleTop = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -88,12 +97,12 @@ fun LoginScreen(
|
|||||||
fun LoginScreenContent(
|
fun LoginScreenContent(
|
||||||
viewModel: AuthViewModel,
|
viewModel: AuthViewModel,
|
||||||
loginState: LoginUiState,
|
loginState: LoginUiState,
|
||||||
onLogin: (String, String, String) -> Unit,
|
serverUrl: String,
|
||||||
|
onLogin: (String, String) -> Unit,
|
||||||
onLoginSuccess: () -> Unit
|
onLoginSuccess: () -> Unit
|
||||||
) {
|
) {
|
||||||
val focusManager = LocalFocusManager.current
|
val focusManager = LocalFocusManager.current
|
||||||
// val serverUrl by viewModel.serverUrl.collectAsState()
|
val keyboardController = LocalSoftwareKeyboardController.current
|
||||||
val serverUrl = ""
|
|
||||||
var username by remember { mutableStateOf("admin") }
|
var username by remember { mutableStateOf("admin") }
|
||||||
var password by remember { mutableStateOf("admin") }
|
var password by remember { mutableStateOf("admin") }
|
||||||
var passwordVisible by remember { mutableStateOf(false) }
|
var passwordVisible by remember { mutableStateOf(false) }
|
||||||
@@ -109,14 +118,20 @@ fun LoginScreenContent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = Modifier.imePadding(),
|
snackbarHost = { SnackbarHost(snackbarHostState) }
|
||||||
snackbarHost = { SnackbarHost(snackbarHostState) })
|
) { padding ->
|
||||||
{ padding ->
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.background(MaterialTheme.colorScheme.background)
|
.background(MaterialTheme.colorScheme.background)
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
.pointerInput(Unit) {
|
||||||
|
detectTapGestures(onTap = {
|
||||||
|
focusManager.clearFocus()
|
||||||
|
})
|
||||||
|
}
|
||||||
.padding(padding)
|
.padding(padding)
|
||||||
|
.imePadding()
|
||||||
|
.verticalScroll(rememberScrollState())
|
||||||
.padding(16.dp),
|
.padding(16.dp),
|
||||||
verticalArrangement = Arrangement.Center,
|
verticalArrangement = Arrangement.Center,
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
@@ -144,7 +159,7 @@ fun LoginScreenContent(
|
|||||||
onNext = { focusManager.moveFocus(FocusDirection.Down) }
|
onNext = { focusManager.moveFocus(FocusDirection.Down) }
|
||||||
),
|
),
|
||||||
value = serverUrl,
|
value = serverUrl,
|
||||||
onValueChange = { /** viewModel.onChangeServerUrl(it) */ },
|
onValueChange = { viewModel.onChangeServerUrl(it) },
|
||||||
label = { Text(stringResource(Res.string.lbl_server_url)) },
|
label = { Text(stringResource(Res.string.lbl_server_url)) },
|
||||||
placeholder = { Text(stringResource(Res.string.server_url_placeholder)) }
|
placeholder = { Text(stringResource(Res.string.server_url_placeholder)) }
|
||||||
|
|
||||||
@@ -179,12 +194,15 @@ fun LoginScreenContent(
|
|||||||
keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Done),
|
keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Done),
|
||||||
keyboardActions = KeyboardActions(
|
keyboardActions = KeyboardActions(
|
||||||
onDone = {
|
onDone = {
|
||||||
if (username.isBlank() || password.isBlank())
|
if (username.isBlank() || password.isBlank()) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
snackbarHostState.showSnackbar("Username and Password is required!")
|
snackbarHostState.showSnackbar("Username and Password is required!")
|
||||||
}
|
}
|
||||||
else
|
} else {
|
||||||
onLogin(serverUrl, username, password)
|
onLogin(username, password)
|
||||||
|
keyboardController?.hide()
|
||||||
|
focusManager.clearFocus()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
value = password,
|
value = password,
|
||||||
@@ -197,12 +215,15 @@ fun LoginScreenContent(
|
|||||||
.width(150.dp)
|
.width(150.dp)
|
||||||
.height(45.dp),
|
.height(45.dp),
|
||||||
onClick = {
|
onClick = {
|
||||||
if (username.isBlank() || password.isBlank())
|
if (username.isBlank() || password.isBlank()) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
snackbarHostState.showSnackbar("Username and password is required!")
|
snackbarHostState.showSnackbar("Username and password is required!")
|
||||||
}
|
}
|
||||||
else
|
} else {
|
||||||
onLogin(serverUrl.trim(), username.trim(), password.trim())
|
onLogin(username.trim(), password.trim())
|
||||||
|
keyboardController?.hide()
|
||||||
|
focusManager.clearFocus()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
enabled = loginState !is LoginUiState.Loading
|
enabled = loginState !is LoginUiState.Loading
|
||||||
) {
|
) {
|
||||||
@@ -214,7 +235,6 @@ fun LoginScreenContent(
|
|||||||
color = Color.Blue,
|
color = Color.Blue,
|
||||||
strokeWidth = 2.dp
|
strokeWidth = 2.dp
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -227,7 +247,8 @@ private fun LoginScreenPreview() {
|
|||||||
LoginScreenContent(
|
LoginScreenContent(
|
||||||
viewModel = viewModel(),
|
viewModel = viewModel(),
|
||||||
loginState = LoginUiState.Idle,
|
loginState = LoginUiState.Idle,
|
||||||
onLogin = { _, _, _ -> },
|
serverUrl = "http://localhost:3000",
|
||||||
|
onLogin = {_, _ -> },
|
||||||
onLoginSuccess = {}
|
onLoginSuccess = {}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-14
@@ -32,7 +32,9 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import com.digitoolsolutions.app.torquevaultkmp.components.AppBar
|
import com.digitoolsolutions.app.torquevaultkmp.components.AppBar
|
||||||
|
import com.digitoolsolutions.app.torquevaultkmp.screens.auth.AuthDestination
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import org.koin.compose.viewmodel.koinViewModel
|
||||||
import org.jetbrains.compose.resources.stringResource
|
import org.jetbrains.compose.resources.stringResource
|
||||||
import torquevaultkmp.composeapp.generated.resources.Res
|
import torquevaultkmp.composeapp.generated.resources.Res
|
||||||
import torquevaultkmp.composeapp.generated.resources.auto_connect
|
import torquevaultkmp.composeapp.generated.resources.auto_connect
|
||||||
@@ -53,29 +55,28 @@ fun SettingScreen(
|
|||||||
navController: NavController,
|
navController: NavController,
|
||||||
isDarkTheme: Boolean,
|
isDarkTheme: Boolean,
|
||||||
onThemeToggle: (Boolean) -> Unit,
|
onThemeToggle: (Boolean) -> Unit,
|
||||||
// viewModel: SettingViewModel = hiltViewModel()
|
|
||||||
) {
|
) {
|
||||||
// val serverUrl by viewModel.serverUrl.collectAsState()
|
val viewModel = koinViewModel<SettingViewModel>()
|
||||||
// val token by viewModel.refreshToken.collectAsState()
|
val serverUrl by viewModel.serverUrl.collectAsState()
|
||||||
// val autoConnect by viewModel.autoConnect.collectAsState()
|
val token by viewModel.refreshToken.collectAsState()
|
||||||
val serverUrl = ""
|
val autoConnect by viewModel.autoConnect.collectAsState()
|
||||||
val token = ""
|
|
||||||
val autoConnect = ""
|
|
||||||
|
|
||||||
SettingContent(
|
SettingContent(
|
||||||
isDarkTheme = isDarkTheme,
|
isDarkTheme = isDarkTheme,
|
||||||
onThemeToggle = onThemeToggle,
|
onThemeToggle = onThemeToggle,
|
||||||
serverUrl = serverUrl,
|
serverUrl = serverUrl,
|
||||||
onServerUrlChange = { /** viewModel.updateServerUrl(it) */ },
|
onServerUrlChange = { viewModel.updateServerUrl(it) },
|
||||||
token = token,
|
token = token,
|
||||||
autoConnect = autoConnect == "true",
|
autoConnect = autoConnect,
|
||||||
onTokenChange = { /** viewModel.updateRefreshToken(it) */ },
|
onTokenChange = { viewModel.updateRefreshToken(it) },
|
||||||
onSaveSettings = { /** viewModel.saveSettings() */ },
|
onSaveSettings = { viewModel.saveSettings() },
|
||||||
onLogout = {
|
onLogout = {
|
||||||
// viewModel.logout()
|
viewModel.logout()
|
||||||
// navController.navigate(AuthDestination.route)
|
navController.navigate(AuthDestination.route) {
|
||||||
|
popUpTo(0)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onChangeAutoConnect = { /** viewModel.updateAutoConnect(it) */ }
|
onChangeAutoConnect = { viewModel.updateAutoConnect(it) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+46
@@ -0,0 +1,46 @@
|
|||||||
|
package com.digitoolsolutions.app.torquevaultkmp.screens.settings
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import com.digitoolsolutions.app.torquevaultkmp.data.storage.AppStorage
|
||||||
|
import com.digitoolsolutions.app.torquevaultkmp.data.storage.ReferKeys
|
||||||
|
import com.digitoolsolutions.app.torquevaultkmp.data.storage.TokenManager
|
||||||
|
import com.digitoolsolutions.app.torquevaultkmp.data.storage.load
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
|
||||||
|
class SettingViewModel(
|
||||||
|
private val tokenManager: TokenManager,
|
||||||
|
private val storage: AppStorage
|
||||||
|
) : ViewModel() {
|
||||||
|
private val _serverUrl = MutableStateFlow(tokenManager.getBaseUrl() ?: "")
|
||||||
|
val serverUrl: StateFlow<String> = _serverUrl.asStateFlow()
|
||||||
|
|
||||||
|
private val _refreshToken = MutableStateFlow(tokenManager.getRefreshToken() ?: "")
|
||||||
|
val refreshToken: StateFlow<String> = _refreshToken.asStateFlow()
|
||||||
|
|
||||||
|
private val _autoConnect = MutableStateFlow(storage.load<Boolean>(ReferKeys.DEVICE_AUTO_CONNECT) ?: false)
|
||||||
|
val autoConnect: StateFlow<Boolean> = _autoConnect.asStateFlow()
|
||||||
|
|
||||||
|
fun updateServerUrl(url: String) {
|
||||||
|
_serverUrl.value = url
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateRefreshToken(token: String) {
|
||||||
|
_refreshToken.value = token
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveSettings() {
|
||||||
|
tokenManager.saveServerUrl(_serverUrl.value)
|
||||||
|
tokenManager.saveTokens("", _refreshToken.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun logout() {
|
||||||
|
tokenManager.clearTokens()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateAutoConnect(enabled: Boolean) {
|
||||||
|
_autoConnect.value = enabled
|
||||||
|
storage.save(ReferKeys.DEVICE_AUTO_CONNECT, enabled)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user