mirror of
https://github.com/bbara04/Purefin.git
synced 2026-03-31 17:10:08 +02:00
feat: enhance login flow with error handling and user feedback
This commit is contained in:
@@ -11,6 +11,9 @@ import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator
|
||||
@@ -30,6 +33,7 @@ import coil3.util.DebugLogger
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import hu.bbara.purefin.client.JellyfinApiClient
|
||||
import hu.bbara.purefin.client.JellyfinAuthInterceptor
|
||||
import hu.bbara.purefin.common.ui.PurefinWaitingScreen
|
||||
import hu.bbara.purefin.login.ui.LoginScreen
|
||||
import hu.bbara.purefin.navigation.LocalNavigationManager
|
||||
import hu.bbara.purefin.navigation.NavigationCommand
|
||||
@@ -126,8 +130,20 @@ class PurefinActivity : ComponentActivity() {
|
||||
entryBuilders: Set<@JvmSuppressWildcards EntryProviderScope<Route>.() -> Unit>,
|
||||
navigationManager: NavigationManager
|
||||
) {
|
||||
|
||||
var sessionLoaded by remember { mutableStateOf(false) }
|
||||
val isLoggedIn by userSessionRepository.isLoggedIn.collectAsState(initial = false)
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
userSessionRepository.isLoggedIn.collect {
|
||||
sessionLoaded = true
|
||||
}
|
||||
}
|
||||
|
||||
if (!sessionLoaded) {
|
||||
PurefinWaitingScreen(modifier = Modifier.fillMaxSize())
|
||||
return
|
||||
}
|
||||
|
||||
if (isLoggedIn) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val backStack = rememberNavBackStack(Route.Home) as NavBackStack<Route>
|
||||
|
||||
@@ -71,6 +71,7 @@ class JellyfinApiClient @Inject constructor(
|
||||
return false
|
||||
}
|
||||
api.update(baseUrl = trimmedUrl)
|
||||
return try {
|
||||
val response = api.userApi.authenticateUserByName(username = username, password = password)
|
||||
val authResult = response.content
|
||||
|
||||
@@ -80,7 +81,11 @@ class JellyfinApiClient @Inject constructor(
|
||||
userSessionRepository.setUserId(userId)
|
||||
userSessionRepository.setLoggedIn(true)
|
||||
api.update(accessToken = token)
|
||||
return true
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
Log.e("JellyfinApiClient", "Login failed", e)
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun updateApiClient() {
|
||||
|
||||
@@ -16,7 +16,7 @@ object MediaDatabaseModule {
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideDatabase(@ApplicationContext context: Context): MediaDatabase =
|
||||
Room.inMemoryDatabaseBuilder(context, MediaDatabase::class.java)
|
||||
Room.databaseBuilder(context, MediaDatabase::class.java, "media_database")
|
||||
.fallbackToDestructiveMigration()
|
||||
.build()
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ fun LoginScreen(
|
||||
val serverUrl by viewModel.url.collectAsState()
|
||||
val username by viewModel.username.collectAsState()
|
||||
val password by viewModel.password.collectAsState()
|
||||
val errorMessage by viewModel.errorMessage.collectAsState()
|
||||
var isLoggingIn by remember { mutableStateOf(false) }
|
||||
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
@@ -120,10 +121,29 @@ fun LoginScreen(
|
||||
.padding(bottom = 24.dp)
|
||||
)
|
||||
|
||||
if (errorMessage != null) {
|
||||
Text(
|
||||
text = errorMessage!!,
|
||||
color = scheme.error,
|
||||
fontSize = 14.sp,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(
|
||||
scheme.errorContainer,
|
||||
RoundedCornerShape(8.dp)
|
||||
)
|
||||
.padding(12.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
|
||||
PurefinComplexTextField(
|
||||
label = "Server URL",
|
||||
value = serverUrl,
|
||||
onValueChange = { viewModel.setUrl(it) },
|
||||
onValueChange = {
|
||||
viewModel.clearError()
|
||||
viewModel.setUrl(it)
|
||||
},
|
||||
placeholder = "http://192.168.1.100:8096",
|
||||
leadingIcon = Icons.Default.Storage
|
||||
)
|
||||
@@ -133,7 +153,10 @@ fun LoginScreen(
|
||||
PurefinComplexTextField(
|
||||
label = "Username",
|
||||
value = username,
|
||||
onValueChange = { viewModel.setUsername(it) },
|
||||
onValueChange = {
|
||||
viewModel.clearError()
|
||||
viewModel.setUsername(it)
|
||||
},
|
||||
placeholder = "Enter your username",
|
||||
leadingIcon = Icons.Default.Person
|
||||
)
|
||||
@@ -143,7 +166,10 @@ fun LoginScreen(
|
||||
PurefinPasswordField(
|
||||
label = "Password",
|
||||
value = password,
|
||||
onValueChange = { viewModel.setPassword(it) },
|
||||
onValueChange = {
|
||||
viewModel.clearError()
|
||||
viewModel.setPassword(it)
|
||||
},
|
||||
placeholder = "••••••••",
|
||||
leadingIcon = Icons.Default.Lock,
|
||||
)
|
||||
|
||||
@@ -23,6 +23,8 @@ class LoginViewModel @Inject constructor(
|
||||
val password: StateFlow<String> = _password.asStateFlow()
|
||||
private val _url = MutableStateFlow("")
|
||||
val url: StateFlow<String> = _url.asStateFlow()
|
||||
private val _errorMessage = MutableStateFlow<String?>(null)
|
||||
val errorMessage: StateFlow<String?> = _errorMessage.asStateFlow()
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
@@ -42,6 +44,10 @@ class LoginViewModel @Inject constructor(
|
||||
_password.value = password
|
||||
}
|
||||
|
||||
fun clearError() {
|
||||
_errorMessage.value = null
|
||||
}
|
||||
|
||||
suspend fun clearFields() {
|
||||
userSessionRepository.setServerUrl("");
|
||||
_username.value = ""
|
||||
@@ -49,8 +55,13 @@ class LoginViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
suspend fun login(): Boolean {
|
||||
_errorMessage.value = null
|
||||
userSessionRepository.setServerUrl(url.value)
|
||||
return jellyfinApiClient.login(url.value, username.value, password.value)
|
||||
val success = jellyfinApiClient.login(url.value, username.value, password.value)
|
||||
if (!success) {
|
||||
_errorMessage.value = "Login failed. Check your server URL, username, and password."
|
||||
}
|
||||
return success
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ espressoCore = "3.6.1"
|
||||
lifecycleRuntimeKtx = "2.8.7"
|
||||
activityCompose = "1.9.3"
|
||||
kotlin = "2.1.0"
|
||||
composeBom = "2024.12.01"
|
||||
composeBom = "2025.02.00"
|
||||
jellyfin-core = "1.8.5"
|
||||
hilt = "2.54"
|
||||
hiltNavigationCompose = "1.2.0"
|
||||
|
||||
Reference in New Issue
Block a user