feat: add login disclaimer support (#721)
* add login disclaimer support * refactor: move disclaimer to `UiState.Normal` This makes more sense in the current architecture * feat: add login disclaimer to tv version * refactor: add margin to bottom of disclaimer * lint: add missing trailing comma --------- Co-authored-by: Jarne Demeulemeester <jarnedemeulemeester@gmail.com>
This commit is contained in:
parent
f470cbed6c
commit
a78dafe387
4 changed files with 44 additions and 6 deletions
|
@ -1,6 +1,7 @@
|
||||||
package dev.jdtech.jellyfin.fragments
|
package dev.jdtech.jellyfin.fragments
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.text.Html.fromHtml
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
@ -82,7 +83,7 @@ class LoginFragment : Fragment() {
|
||||||
viewModel.uiState.collect { uiState ->
|
viewModel.uiState.collect { uiState ->
|
||||||
Timber.d("$uiState")
|
Timber.d("$uiState")
|
||||||
when (uiState) {
|
when (uiState) {
|
||||||
is LoginViewModel.UiState.Normal -> bindUiStateNormal()
|
is LoginViewModel.UiState.Normal -> bindUiStateNormal(uiState)
|
||||||
is LoginViewModel.UiState.Error -> bindUiStateError(uiState)
|
is LoginViewModel.UiState.Error -> bindUiStateError(uiState)
|
||||||
is LoginViewModel.UiState.Loading -> bindUiStateLoading()
|
is LoginViewModel.UiState.Loading -> bindUiStateLoading()
|
||||||
}
|
}
|
||||||
|
@ -135,11 +136,15 @@ class LoginFragment : Fragment() {
|
||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bindUiStateNormal() {
|
private fun bindUiStateNormal(uiState: LoginViewModel.UiState.Normal) {
|
||||||
binding.buttonLogin.isEnabled = true
|
binding.buttonLogin.isEnabled = true
|
||||||
binding.progressCircular.isVisible = false
|
binding.progressCircular.isVisible = false
|
||||||
binding.editTextUsernameLayout.isEnabled = true
|
binding.editTextUsernameLayout.isEnabled = true
|
||||||
binding.editTextPasswordLayout.isEnabled = true
|
binding.editTextPasswordLayout.isEnabled = true
|
||||||
|
|
||||||
|
uiState.disclaimer?.let { disclaimer ->
|
||||||
|
binding.loginDisclaimer.text = fromHtml(disclaimer, 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bindUiStateError(uiState: LoginViewModel.UiState.Error) {
|
private fun bindUiStateError(uiState: LoginViewModel.UiState.Error) {
|
||||||
|
|
|
@ -141,6 +141,15 @@
|
||||||
android:visibility="invisible" />
|
android:visibility="invisible" />
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/login_disclaimer"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="24dp"
|
||||||
|
android:layout_margin="24dp"
|
||||||
|
android:textSize="16sp"
|
||||||
|
tools:text="Sample login disclaimer" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
|
@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
|
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.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
|
@ -110,6 +111,14 @@ private fun LoginScreenLayout(
|
||||||
else -> Unit
|
else -> Unit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var disclaimer: String? by remember {
|
||||||
|
mutableStateOf(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uiState is LoginViewModel.UiState.Normal) {
|
||||||
|
disclaimer = uiState.disclaimer
|
||||||
|
}
|
||||||
|
|
||||||
val isError = uiState is LoginViewModel.UiState.Error
|
val isError = uiState is LoginViewModel.UiState.Error
|
||||||
val isLoading = uiState is LoginViewModel.UiState.Loading
|
val isLoading = uiState is LoginViewModel.UiState.Loading
|
||||||
|
|
||||||
|
@ -241,6 +250,10 @@ private fun LoginScreenLayout(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Text(
|
||||||
|
text = disclaimer ?: "",
|
||||||
|
modifier = Modifier.padding(MaterialTheme.spacings.default),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,7 +267,7 @@ private fun LoginScreenLayout(
|
||||||
private fun LoginScreenLayoutPreview() {
|
private fun LoginScreenLayoutPreview() {
|
||||||
FindroidTheme {
|
FindroidTheme {
|
||||||
LoginScreenLayout(
|
LoginScreenLayout(
|
||||||
uiState = LoginViewModel.UiState.Normal,
|
uiState = LoginViewModel.UiState.Normal(),
|
||||||
quickConnectUiState = LoginViewModel.QuickConnectUiState.Normal,
|
quickConnectUiState = LoginViewModel.QuickConnectUiState.Normal,
|
||||||
onLoginClick = { _, _ -> },
|
onLoginClick = { _, _ -> },
|
||||||
onQuickConnectClick = {},
|
onQuickConnectClick = {},
|
||||||
|
|
|
@ -19,6 +19,7 @@ import kotlinx.coroutines.flow.receiveAsFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.jellyfin.sdk.api.client.extensions.authenticateWithQuickConnect
|
import org.jellyfin.sdk.api.client.extensions.authenticateWithQuickConnect
|
||||||
|
import org.jellyfin.sdk.api.client.extensions.brandingApi
|
||||||
import org.jellyfin.sdk.model.api.AuthenticateUserByName
|
import org.jellyfin.sdk.model.api.AuthenticateUserByName
|
||||||
import org.jellyfin.sdk.model.api.AuthenticationResult
|
import org.jellyfin.sdk.model.api.AuthenticationResult
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -32,7 +33,7 @@ constructor(
|
||||||
private val jellyfinApi: JellyfinApi,
|
private val jellyfinApi: JellyfinApi,
|
||||||
private val database: ServerDatabaseDao,
|
private val database: ServerDatabaseDao,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
private val _uiState = MutableStateFlow<UiState>(UiState.Normal)
|
private val _uiState = MutableStateFlow<UiState>(UiState.Normal())
|
||||||
val uiState = _uiState.asStateFlow()
|
val uiState = _uiState.asStateFlow()
|
||||||
private val _usersState = MutableStateFlow<UsersState>(UsersState.Loading)
|
private val _usersState = MutableStateFlow<UsersState>(UsersState.Loading)
|
||||||
val usersState = _usersState.asStateFlow()
|
val usersState = _usersState.asStateFlow()
|
||||||
|
@ -44,8 +45,10 @@ constructor(
|
||||||
|
|
||||||
private var quickConnectJob: Job? = null
|
private var quickConnectJob: Job? = null
|
||||||
|
|
||||||
|
private var loginDisclaimer: String? = null
|
||||||
|
|
||||||
sealed class UiState {
|
sealed class UiState {
|
||||||
data object Normal : UiState()
|
data class Normal(val disclaimer: String? = null) : UiState()
|
||||||
data object Loading : UiState()
|
data object Loading : UiState()
|
||||||
data class Error(val message: UiText) : UiState()
|
data class Error(val message: UiText) : UiState()
|
||||||
}
|
}
|
||||||
|
@ -62,10 +65,18 @@ constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
loadDisclaimer()
|
||||||
loadPublicUsers()
|
loadPublicUsers()
|
||||||
loadQuickConnectAvailable()
|
loadQuickConnectAvailable()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun loadDisclaimer() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
loginDisclaimer = jellyfinApi.api.brandingApi.getBrandingOptions().content.loginDisclaimer
|
||||||
|
_uiState.emit(UiState.Normal(loginDisclaimer))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun loadPublicUsers() {
|
private fun loadPublicUsers() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
_usersState.emit(UsersState.Loading)
|
_usersState.emit(UsersState.Loading)
|
||||||
|
@ -121,7 +132,7 @@ constructor(
|
||||||
|
|
||||||
saveAuthenticationResult(authenticationResult)
|
saveAuthenticationResult(authenticationResult)
|
||||||
|
|
||||||
_uiState.emit(UiState.Normal)
|
_uiState.emit(UiState.Normal(loginDisclaimer))
|
||||||
eventsChannel.send(LoginEvent.NavigateToHome)
|
eventsChannel.send(LoginEvent.NavigateToHome)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
val message =
|
val message =
|
||||||
|
|
Loading…
Reference in a new issue