Fix showing login/logout button on initial screens

This commit is contained in:
Kirill Kamakin
2022-04-04 19:34:21 +05:00
parent c98feceab4
commit fb10333c2c
5 changed files with 59 additions and 63 deletions

View File

@@ -51,7 +51,7 @@ class MainActivity : AppCompatActivity() {
private fun listenToAuthStatuses() {
Timber.v("listenToAuthStatuses() called")
authViewModel.authenticationState.observe(this, ::onAuthStateUpdate)
authViewModel.authenticationStateLive.observe(this, ::onAuthStateUpdate)
}
private fun onAuthStateUpdate(authState: AuthenticationState) {
@@ -71,12 +71,9 @@ class MainActivity : AppCompatActivity() {
override fun onOptionsItemSelected(item: MenuItem): Boolean {
Timber.v("onOptionsItemSelected() called with: item = $item")
val result = when (item.itemId) {
R.id.logout -> {
authViewModel.logout()
true
}
R.id.login -> {
authViewModel.enableLoginRequest()
R.id.logout, R.id.login -> {
// When user clicks logout they don't want to be authorized
authViewModel.authRequested = item.itemId == R.id.login
true
}
else -> super.onOptionsItemSelected(item)

View File

@@ -5,13 +5,12 @@ import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.LiveData
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import by.kirich1409.viewbindingdelegate.viewBinding
import dagger.hilt.android.AndroidEntryPoint
import gq.kirmanak.mealient.R
import gq.kirmanak.mealient.data.network.NetworkError.Unauthorized
import gq.kirmanak.mealient.data.network.NetworkError
import gq.kirmanak.mealient.databinding.FragmentAuthenticationBinding
import gq.kirmanak.mealient.extensions.checkIfInputIsEmpty
import gq.kirmanak.mealient.extensions.executeOnceOnBackPressed
@@ -22,14 +21,10 @@ class AuthenticationFragment : Fragment(R.layout.fragment_authentication) {
private val binding by viewBinding(FragmentAuthenticationBinding::bind)
private val viewModel by activityViewModels<AuthenticationViewModel>()
private val authStatuses: LiveData<AuthenticationState>
get() = viewModel.authenticationState
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Timber.v("onCreate() called with: savedInstanceState = $savedInstanceState")
authStatuses.observe(this, ::onAuthStatusChange)
executeOnceOnBackPressed { viewModel.disableLoginRequest() }
executeOnceOnBackPressed { viewModel.authRequested = false }
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@@ -38,13 +33,7 @@ class AuthenticationFragment : Fragment(R.layout.fragment_authentication) {
binding.button.setOnClickListener { onLoginClicked() }
(requireActivity() as? AppCompatActivity)?.supportActionBar?.title =
getString(R.string.app_name)
}
private fun onAuthStatusChange(isAuthenticated: AuthenticationState) {
Timber.v("onAuthStatusChange() called with: isAuthenticated = $isAuthenticated")
if (isAuthenticated == AuthenticationState.AUTHORIZED) {
findNavController().popBackStack()
}
viewModel.authenticationResult.observe(viewLifecycleOwner, ::onAuthenticationResult)
}
private fun onLoginClicked(): Unit = with(binding) {
@@ -59,14 +48,21 @@ class AuthenticationFragment : Fragment(R.layout.fragment_authentication) {
} ?: return
button.isClickable = false
viewModel.authenticate(email, pass).observe(viewLifecycleOwner) {
Timber.d("onLoginClicked: result $it")
passwordInputLayout.error = when (it.exceptionOrNull()) {
is Unauthorized -> getString(R.string.fragment_authentication_credentials_incorrect)
else -> null
}
viewModel.authenticate(email, pass)
}
button.isClickable = true
private fun onAuthenticationResult(result: Result<Unit>) {
Timber.v("onAuthenticationResult() called with: result = $result")
if (result.isSuccess) {
findNavController().popBackStack()
return
}
binding.passwordInputLayout.error = when (result.exceptionOrNull()) {
is NetworkError.Unauthorized -> getString(R.string.fragment_authentication_credentials_incorrect)
else -> null
}
binding.button.isClickable = true
}
}

View File

@@ -5,16 +5,19 @@ import timber.log.Timber
enum class AuthenticationState {
AUTHORIZED,
AUTH_REQUESTED,
UNAUTHORIZED;
UNAUTHORIZED,
UNKNOWN;
companion object {
fun determineState(
isLoginRequested: Boolean,
showLoginButton: Boolean,
isAuthorized: Boolean,
): AuthenticationState {
Timber.v("determineState() called with: isLoginRequested = $isLoginRequested, isAuthorized = $isAuthorized")
Timber.v("determineState() called with: isLoginRequested = $isLoginRequested, showLoginButton = $showLoginButton, isAuthorized = $isAuthorized")
val result = when {
!showLoginButton -> UNKNOWN
isAuthorized -> AUTHORIZED
isLoginRequested -> AUTH_REQUESTED
else -> UNAUTHORIZED

View File

@@ -4,6 +4,7 @@ import androidx.lifecycle.*
import dagger.hilt.android.lifecycle.HiltViewModel
import gq.kirmanak.mealient.data.auth.AuthRepo
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
import timber.log.Timber
@@ -14,48 +15,44 @@ class AuthenticationViewModel @Inject constructor(
private val authRepo: AuthRepo,
) : ViewModel() {
private val loginRequestsFlow = MutableStateFlow(false)
val authenticationState: LiveData<AuthenticationState> = loginRequestsFlow.combine(
flow = authRepo.isAuthorizedFlow,
transform = AuthenticationState::determineState
).asLiveData()
private val authRequestsFlow = MutableStateFlow(false)
private val showLoginButtonFlow = MutableStateFlow(false)
private val authenticationStateFlow = combine(
authRequestsFlow,
showLoginButtonFlow,
authRepo.isAuthorizedFlow,
AuthenticationState::determineState
)
val authenticationStateLive: LiveData<AuthenticationState>
get() = authenticationStateFlow.asLiveData()
var authRequested: Boolean by authRequestsFlow::value
var showLoginButton: Boolean by showLoginButtonFlow::value
fun authenticate(username: String, password: String): LiveData<Result<Unit>> {
private val _authenticationResult = MutableLiveData<Result<Unit>>()
val authenticationResult: LiveData<Result<Unit>>
get() = _authenticationResult
init {
viewModelScope.launch {
authRequestsFlow.collect { isRequested ->
// Clear auth token on logout request
if (!isRequested) authRepo.logout()
}
}
}
fun authenticate(username: String, password: String) {
Timber.v("authenticate() called with: username = $username, password = $password")
val result = MutableLiveData<Result<Unit>>()
viewModelScope.launch {
runCatching {
authRepo.authenticate(username, password)
}.onFailure {
Timber.e(it, "authenticate: can't authenticate")
result.value = Result.failure(it)
_authenticationResult.value = Result.failure(it)
}.onSuccess {
Timber.d("authenticate: authenticated")
result.value = Result.success(Unit)
_authenticationResult.value = Result.success(Unit)
}
}
return result
}
fun logout() {
Timber.v("logout() called")
viewModelScope.launch {
loginRequestsFlow.emit(false)
authRepo.logout()
}
}
fun enableLoginRequest() {
Timber.v("enableLoginRequest() called")
updateIsLoginRequested(true)
}
fun disableLoginRequest() {
Timber.v("disableLoginRequest() called")
updateIsLoginRequested(false)
}
private fun updateIsLoginRequested(isRequested: Boolean) {
viewModelScope.launch { loginRequestsFlow.emit(isRequested) }
}
}

View File

@@ -27,7 +27,8 @@ class RecipesFragment : Fragment(R.layout.fragment_recipes) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
authViewModel.authenticationState.observe(this, ::onAuthStateChange)
Timber.v("onCreate() called with: savedInstanceState = $savedInstanceState")
authViewModel.authenticationStateLive.observe(this, ::onAuthStateChange)
}
private fun onAuthStateChange(authenticationState: AuthenticationState) {
@@ -40,6 +41,7 @@ class RecipesFragment : Fragment(R.layout.fragment_recipes) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Timber.v("onViewCreated() called with: view = $view, savedInstanceState = $savedInstanceState")
authViewModel.showLoginButton = true
setupRecipeAdapter()
(requireActivity() as? AppCompatActivity)?.supportActionBar?.title = null
}
@@ -78,5 +80,6 @@ class RecipesFragment : Fragment(R.layout.fragment_recipes) {
Timber.v("onDestroyView() called")
// Prevent RV leaking through mObservers list in adapter
binding.recipes.adapter = null
authViewModel.showLoginButton = false
}
}