Fix showing login/logout button on initial screens
This commit is contained in:
@@ -51,7 +51,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
private fun listenToAuthStatuses() {
|
private fun listenToAuthStatuses() {
|
||||||
Timber.v("listenToAuthStatuses() called")
|
Timber.v("listenToAuthStatuses() called")
|
||||||
authViewModel.authenticationState.observe(this, ::onAuthStateUpdate)
|
authViewModel.authenticationStateLive.observe(this, ::onAuthStateUpdate)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onAuthStateUpdate(authState: AuthenticationState) {
|
private fun onAuthStateUpdate(authState: AuthenticationState) {
|
||||||
@@ -71,12 +71,9 @@ class MainActivity : AppCompatActivity() {
|
|||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
Timber.v("onOptionsItemSelected() called with: item = $item")
|
Timber.v("onOptionsItemSelected() called with: item = $item")
|
||||||
val result = when (item.itemId) {
|
val result = when (item.itemId) {
|
||||||
R.id.logout -> {
|
R.id.logout, R.id.login -> {
|
||||||
authViewModel.logout()
|
// When user clicks logout they don't want to be authorized
|
||||||
true
|
authViewModel.authRequested = item.itemId == R.id.login
|
||||||
}
|
|
||||||
R.id.login -> {
|
|
||||||
authViewModel.enableLoginRequest()
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
else -> super.onOptionsItemSelected(item)
|
else -> super.onOptionsItemSelected(item)
|
||||||
|
|||||||
@@ -5,13 +5,12 @@ import android.view.View
|
|||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.lifecycle.LiveData
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import by.kirich1409.viewbindingdelegate.viewBinding
|
import by.kirich1409.viewbindingdelegate.viewBinding
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import gq.kirmanak.mealient.R
|
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.databinding.FragmentAuthenticationBinding
|
||||||
import gq.kirmanak.mealient.extensions.checkIfInputIsEmpty
|
import gq.kirmanak.mealient.extensions.checkIfInputIsEmpty
|
||||||
import gq.kirmanak.mealient.extensions.executeOnceOnBackPressed
|
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 binding by viewBinding(FragmentAuthenticationBinding::bind)
|
||||||
private val viewModel by activityViewModels<AuthenticationViewModel>()
|
private val viewModel by activityViewModels<AuthenticationViewModel>()
|
||||||
|
|
||||||
private val authStatuses: LiveData<AuthenticationState>
|
|
||||||
get() = viewModel.authenticationState
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
Timber.v("onCreate() called with: savedInstanceState = $savedInstanceState")
|
Timber.v("onCreate() called with: savedInstanceState = $savedInstanceState")
|
||||||
authStatuses.observe(this, ::onAuthStatusChange)
|
executeOnceOnBackPressed { viewModel.authRequested = false }
|
||||||
executeOnceOnBackPressed { viewModel.disableLoginRequest() }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
@@ -38,13 +33,7 @@ class AuthenticationFragment : Fragment(R.layout.fragment_authentication) {
|
|||||||
binding.button.setOnClickListener { onLoginClicked() }
|
binding.button.setOnClickListener { onLoginClicked() }
|
||||||
(requireActivity() as? AppCompatActivity)?.supportActionBar?.title =
|
(requireActivity() as? AppCompatActivity)?.supportActionBar?.title =
|
||||||
getString(R.string.app_name)
|
getString(R.string.app_name)
|
||||||
}
|
viewModel.authenticationResult.observe(viewLifecycleOwner, ::onAuthenticationResult)
|
||||||
|
|
||||||
private fun onAuthStatusChange(isAuthenticated: AuthenticationState) {
|
|
||||||
Timber.v("onAuthStatusChange() called with: isAuthenticated = $isAuthenticated")
|
|
||||||
if (isAuthenticated == AuthenticationState.AUTHORIZED) {
|
|
||||||
findNavController().popBackStack()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onLoginClicked(): Unit = with(binding) {
|
private fun onLoginClicked(): Unit = with(binding) {
|
||||||
@@ -59,14 +48,21 @@ class AuthenticationFragment : Fragment(R.layout.fragment_authentication) {
|
|||||||
} ?: return
|
} ?: return
|
||||||
|
|
||||||
button.isClickable = false
|
button.isClickable = false
|
||||||
viewModel.authenticate(email, pass).observe(viewLifecycleOwner) {
|
viewModel.authenticate(email, pass)
|
||||||
Timber.d("onLoginClicked: result $it")
|
}
|
||||||
passwordInputLayout.error = when (it.exceptionOrNull()) {
|
|
||||||
is Unauthorized -> getString(R.string.fragment_authentication_credentials_incorrect)
|
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
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
button.isClickable = true
|
binding.button.isClickable = true
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,16 +5,19 @@ import timber.log.Timber
|
|||||||
enum class AuthenticationState {
|
enum class AuthenticationState {
|
||||||
AUTHORIZED,
|
AUTHORIZED,
|
||||||
AUTH_REQUESTED,
|
AUTH_REQUESTED,
|
||||||
UNAUTHORIZED;
|
UNAUTHORIZED,
|
||||||
|
UNKNOWN;
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
fun determineState(
|
fun determineState(
|
||||||
isLoginRequested: Boolean,
|
isLoginRequested: Boolean,
|
||||||
|
showLoginButton: Boolean,
|
||||||
isAuthorized: Boolean,
|
isAuthorized: Boolean,
|
||||||
): AuthenticationState {
|
): AuthenticationState {
|
||||||
Timber.v("determineState() called with: isLoginRequested = $isLoginRequested, isAuthorized = $isAuthorized")
|
Timber.v("determineState() called with: isLoginRequested = $isLoginRequested, showLoginButton = $showLoginButton, isAuthorized = $isAuthorized")
|
||||||
val result = when {
|
val result = when {
|
||||||
|
!showLoginButton -> UNKNOWN
|
||||||
isAuthorized -> AUTHORIZED
|
isAuthorized -> AUTHORIZED
|
||||||
isLoginRequested -> AUTH_REQUESTED
|
isLoginRequested -> AUTH_REQUESTED
|
||||||
else -> UNAUTHORIZED
|
else -> UNAUTHORIZED
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import androidx.lifecycle.*
|
|||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import gq.kirmanak.mealient.data.auth.AuthRepo
|
import gq.kirmanak.mealient.data.auth.AuthRepo
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.collect
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
@@ -14,48 +15,44 @@ class AuthenticationViewModel @Inject constructor(
|
|||||||
private val authRepo: AuthRepo,
|
private val authRepo: AuthRepo,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val loginRequestsFlow = MutableStateFlow(false)
|
private val authRequestsFlow = MutableStateFlow(false)
|
||||||
val authenticationState: LiveData<AuthenticationState> = loginRequestsFlow.combine(
|
private val showLoginButtonFlow = MutableStateFlow(false)
|
||||||
flow = authRepo.isAuthorizedFlow,
|
private val authenticationStateFlow = combine(
|
||||||
transform = AuthenticationState::determineState
|
authRequestsFlow,
|
||||||
).asLiveData()
|
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")
|
Timber.v("authenticate() called with: username = $username, password = $password")
|
||||||
val result = MutableLiveData<Result<Unit>>()
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
runCatching {
|
runCatching {
|
||||||
authRepo.authenticate(username, password)
|
authRepo.authenticate(username, password)
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
Timber.e(it, "authenticate: can't authenticate")
|
Timber.e(it, "authenticate: can't authenticate")
|
||||||
result.value = Result.failure(it)
|
_authenticationResult.value = Result.failure(it)
|
||||||
}.onSuccess {
|
}.onSuccess {
|
||||||
Timber.d("authenticate: authenticated")
|
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) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -27,7 +27,8 @@ class RecipesFragment : Fragment(R.layout.fragment_recipes) {
|
|||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
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) {
|
private fun onAuthStateChange(authenticationState: AuthenticationState) {
|
||||||
@@ -40,6 +41,7 @@ class RecipesFragment : Fragment(R.layout.fragment_recipes) {
|
|||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
Timber.v("onViewCreated() called with: view = $view, savedInstanceState = $savedInstanceState")
|
Timber.v("onViewCreated() called with: view = $view, savedInstanceState = $savedInstanceState")
|
||||||
|
authViewModel.showLoginButton = true
|
||||||
setupRecipeAdapter()
|
setupRecipeAdapter()
|
||||||
(requireActivity() as? AppCompatActivity)?.supportActionBar?.title = null
|
(requireActivity() as? AppCompatActivity)?.supportActionBar?.title = null
|
||||||
}
|
}
|
||||||
@@ -78,5 +80,6 @@ class RecipesFragment : Fragment(R.layout.fragment_recipes) {
|
|||||||
Timber.v("onDestroyView() called")
|
Timber.v("onDestroyView() called")
|
||||||
// Prevent RV leaking through mObservers list in adapter
|
// Prevent RV leaking through mObservers list in adapter
|
||||||
binding.recipes.adapter = null
|
binding.recipes.adapter = null
|
||||||
|
authViewModel.showLoginButton = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user