Fix handling result in BaseURLFragment and AuthenticationFragment
This commit is contained in:
@@ -17,7 +17,7 @@
|
|||||||
tools:ignore="UnusedAttribute"
|
tools:ignore="UnusedAttribute"
|
||||||
android:theme="@style/Theme.Mealient">
|
android:theme="@style/Theme.Mealient">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".ui.activity.MainActivity"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package gq.kirmanak.mealient
|
package gq.kirmanak.mealient.ui.activity
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
@@ -10,17 +10,17 @@ import androidx.navigation.findNavController
|
|||||||
import com.google.android.material.shape.CornerFamily
|
import com.google.android.material.shape.CornerFamily
|
||||||
import com.google.android.material.shape.MaterialShapeDrawable
|
import com.google.android.material.shape.MaterialShapeDrawable
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import gq.kirmanak.mealient.R
|
||||||
import gq.kirmanak.mealient.databinding.MainActivityBinding
|
import gq.kirmanak.mealient.databinding.MainActivityBinding
|
||||||
import gq.kirmanak.mealient.ui.auth.AuthenticationState
|
import gq.kirmanak.mealient.ui.auth.AuthenticationState
|
||||||
import gq.kirmanak.mealient.ui.auth.AuthenticationState.AUTHORIZED
|
import gq.kirmanak.mealient.ui.auth.AuthenticationState.AUTHORIZED
|
||||||
import gq.kirmanak.mealient.ui.auth.AuthenticationState.UNAUTHORIZED
|
import gq.kirmanak.mealient.ui.auth.AuthenticationState.UNAUTHORIZED
|
||||||
import gq.kirmanak.mealient.ui.auth.AuthenticationViewModel
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
private lateinit var binding: MainActivityBinding
|
private lateinit var binding: MainActivityBinding
|
||||||
private val authViewModel by viewModels<AuthenticationViewModel>()
|
private val viewModel by viewModels<MainActivityViewModel>()
|
||||||
private var lastAuthenticationState: AuthenticationState? = null
|
private var lastAuthenticationState: AuthenticationState? = null
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
@@ -53,7 +53,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
private fun listenToAuthStatuses() {
|
private fun listenToAuthStatuses() {
|
||||||
Timber.v("listenToAuthStatuses() called")
|
Timber.v("listenToAuthStatuses() called")
|
||||||
authViewModel.authenticationStateLive.observe(this, ::onAuthStateUpdate)
|
viewModel.authenticationStateLive.observe(this, ::onAuthStateUpdate)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onAuthStateUpdate(authState: AuthenticationState) {
|
private fun onAuthStateUpdate(authState: AuthenticationState) {
|
||||||
@@ -78,7 +78,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.logout -> {
|
R.id.logout -> {
|
||||||
authViewModel.logout()
|
viewModel.logout()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
else -> super.onOptionsItemSelected(item)
|
else -> super.onOptionsItemSelected(item)
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package gq.kirmanak.mealient.ui.activity
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.asLiveData
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import gq.kirmanak.mealient.data.auth.AuthRepo
|
||||||
|
import gq.kirmanak.mealient.ui.auth.AuthenticationState
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import timber.log.Timber
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class MainActivityViewModel @Inject constructor(
|
||||||
|
private val authRepo: AuthRepo,
|
||||||
|
) : ViewModel() {
|
||||||
|
|
||||||
|
private val showLoginButtonFlow = MutableStateFlow(false)
|
||||||
|
var showLoginButton: Boolean by showLoginButtonFlow::value
|
||||||
|
|
||||||
|
private val authenticationStateFlow = combine(
|
||||||
|
showLoginButtonFlow,
|
||||||
|
authRepo.isAuthorizedFlow,
|
||||||
|
AuthenticationState::determineState
|
||||||
|
)
|
||||||
|
val authenticationStateLive = authenticationStateFlow.asLiveData()
|
||||||
|
|
||||||
|
fun logout() {
|
||||||
|
Timber.v("logout() called")
|
||||||
|
viewModelScope.launch { authRepo.logout() }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ import android.os.Bundle
|
|||||||
import android.view.View
|
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.viewModels
|
||||||
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
|
||||||
@@ -12,13 +12,12 @@ import gq.kirmanak.mealient.R
|
|||||||
import gq.kirmanak.mealient.data.network.NetworkError
|
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.launchWithViewLifecycle
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class AuthenticationFragment : Fragment(R.layout.fragment_authentication) {
|
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 viewModels<AuthenticationViewModel>()
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
@@ -26,6 +25,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 onLoginClicked(): Unit = with(binding) {
|
private fun onLoginClicked(): Unit = with(binding) {
|
||||||
@@ -45,7 +45,7 @@ class AuthenticationFragment : Fragment(R.layout.fragment_authentication) {
|
|||||||
) ?: return
|
) ?: return
|
||||||
|
|
||||||
button.isClickable = false
|
button.isClickable = false
|
||||||
launchWithViewLifecycle { onAuthenticationResult(viewModel.authenticate(email, pass)) }
|
viewModel.authenticate(email, pass)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onAuthenticationResult(result: Result<Unit>) {
|
private fun onAuthenticationResult(result: Result<Unit>) {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import timber.log.Timber
|
|||||||
enum class AuthenticationState {
|
enum class AuthenticationState {
|
||||||
AUTHORIZED,
|
AUTHORIZED,
|
||||||
UNAUTHORIZED,
|
UNAUTHORIZED,
|
||||||
UNKNOWN;
|
HIDDEN;
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ enum class AuthenticationState {
|
|||||||
): AuthenticationState {
|
): AuthenticationState {
|
||||||
Timber.v("determineState() called with: showLoginButton = $showLoginButton, isAuthorized = $isAuthorized")
|
Timber.v("determineState() called with: showLoginButton = $showLoginButton, isAuthorized = $isAuthorized")
|
||||||
val result = when {
|
val result = when {
|
||||||
!showLoginButton -> UNKNOWN
|
!showLoginButton -> HIDDEN
|
||||||
isAuthorized -> AUTHORIZED
|
isAuthorized -> AUTHORIZED
|
||||||
else -> UNAUTHORIZED
|
else -> UNAUTHORIZED
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
package gq.kirmanak.mealient.ui.auth
|
package gq.kirmanak.mealient.ui.auth
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.asLiveData
|
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
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 gq.kirmanak.mealient.extensions.runCatchingExceptCancel
|
import gq.kirmanak.mealient.extensions.runCatchingExceptCancel
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
import kotlinx.coroutines.flow.combine
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@@ -18,24 +16,16 @@ class AuthenticationViewModel @Inject constructor(
|
|||||||
private val authRepo: AuthRepo,
|
private val authRepo: AuthRepo,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val showLoginButtonFlow = MutableStateFlow(false)
|
private val _authenticationResult = MutableLiveData<Result<Unit>>()
|
||||||
private val authenticationStateFlow = combine(
|
val authenticationResult: LiveData<Result<Unit>>
|
||||||
showLoginButtonFlow,
|
get() = _authenticationResult
|
||||||
authRepo.isAuthorizedFlow,
|
|
||||||
AuthenticationState::determineState
|
|
||||||
)
|
|
||||||
val authenticationStateLive: LiveData<AuthenticationState>
|
|
||||||
get() = authenticationStateFlow.asLiveData()
|
|
||||||
var showLoginButton: Boolean by showLoginButtonFlow::value
|
|
||||||
|
|
||||||
suspend fun authenticate(email: String, password: String) = runCatchingExceptCancel {
|
fun authenticate(email: String, password: String) {
|
||||||
|
Timber.v("authenticate() called with: email = $email, password = $password")
|
||||||
|
viewModelScope.launch {
|
||||||
|
_authenticationResult.value = runCatchingExceptCancel {
|
||||||
authRepo.authenticate(email, password)
|
authRepo.authenticate(email, password)
|
||||||
}.onFailure {
|
}
|
||||||
Timber.e(it, "authenticate: can't authenticate")
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fun logout() {
|
|
||||||
Timber.v("logout() called")
|
|
||||||
viewModelScope.launch { authRepo.logout() }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,7 +11,6 @@ import gq.kirmanak.mealient.R
|
|||||||
import gq.kirmanak.mealient.data.network.NetworkError
|
import gq.kirmanak.mealient.data.network.NetworkError
|
||||||
import gq.kirmanak.mealient.databinding.FragmentBaseUrlBinding
|
import gq.kirmanak.mealient.databinding.FragmentBaseUrlBinding
|
||||||
import gq.kirmanak.mealient.extensions.checkIfInputIsEmpty
|
import gq.kirmanak.mealient.extensions.checkIfInputIsEmpty
|
||||||
import gq.kirmanak.mealient.extensions.launchWithViewLifecycle
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
@@ -24,6 +23,7 @@ class BaseURLFragment : Fragment(R.layout.fragment_base_url) {
|
|||||||
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")
|
||||||
binding.button.setOnClickListener(::onProceedClick)
|
binding.button.setOnClickListener(::onProceedClick)
|
||||||
|
viewModel.checkURLResult.observe(viewLifecycleOwner, ::onCheckURLResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onProceedClick(view: View) {
|
private fun onProceedClick(view: View) {
|
||||||
@@ -33,7 +33,7 @@ class BaseURLFragment : Fragment(R.layout.fragment_base_url) {
|
|||||||
lifecycleOwner = viewLifecycleOwner,
|
lifecycleOwner = viewLifecycleOwner,
|
||||||
stringId = R.string.fragment_baseurl_url_input_empty,
|
stringId = R.string.fragment_baseurl_url_input_empty,
|
||||||
) ?: return
|
) ?: return
|
||||||
launchWithViewLifecycle { onCheckURLResult(viewModel.saveBaseUrl(url)) }
|
viewModel.saveBaseUrl(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onCheckURLResult(result: Result<Unit>) {
|
private fun onCheckURLResult(result: Result<Unit>) {
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
package gq.kirmanak.mealient.ui.baseurl
|
package gq.kirmanak.mealient.ui.baseurl
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import gq.kirmanak.mealient.data.baseurl.BaseURLStorage
|
import gq.kirmanak.mealient.data.baseurl.BaseURLStorage
|
||||||
import gq.kirmanak.mealient.data.baseurl.VersionDataSource
|
import gq.kirmanak.mealient.data.baseurl.VersionDataSource
|
||||||
import gq.kirmanak.mealient.extensions.runCatchingExceptCancel
|
import gq.kirmanak.mealient.extensions.runCatchingExceptCancel
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@@ -14,21 +18,25 @@ class BaseURLViewModel @Inject constructor(
|
|||||||
private val versionDataSource: VersionDataSource,
|
private val versionDataSource: VersionDataSource,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
suspend fun saveBaseUrl(baseURL: String): Result<Unit> {
|
private val _checkURLResult = MutableLiveData<Result<Unit>>()
|
||||||
|
val checkURLResult: LiveData<Result<Unit>> get() = _checkURLResult
|
||||||
|
|
||||||
|
fun saveBaseUrl(baseURL: String) {
|
||||||
Timber.v("saveBaseUrl() called with: baseURL = $baseURL")
|
Timber.v("saveBaseUrl() called with: baseURL = $baseURL")
|
||||||
val hasPrefix = ALLOWED_PREFIXES.any { baseURL.startsWith(it) }
|
val hasPrefix = ALLOWED_PREFIXES.any { baseURL.startsWith(it) }
|
||||||
val url = baseURL.takeIf { hasPrefix } ?: WITH_PREFIX_FORMAT.format(baseURL)
|
val url = baseURL.takeIf { hasPrefix } ?: WITH_PREFIX_FORMAT.format(baseURL)
|
||||||
return checkBaseURL(url)
|
viewModelScope.launch { checkBaseURL(url) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun checkBaseURL(baseURL: String): Result<Unit> {
|
private suspend fun checkBaseURL(baseURL: String) {
|
||||||
Timber.v("checkBaseURL() called with: baseURL = $baseURL")
|
Timber.v("checkBaseURL() called with: baseURL = $baseURL")
|
||||||
val result = runCatchingExceptCancel {
|
val result = runCatchingExceptCancel {
|
||||||
// If it returns proper version info then it must be a Mealie
|
// If it returns proper version info then it must be a Mealie
|
||||||
versionDataSource.getVersionInfo(baseURL)
|
versionDataSource.getVersionInfo(baseURL)
|
||||||
}
|
|
||||||
baseURLStorage.storeBaseURL(baseURL)
|
baseURLStorage.storeBaseURL(baseURL)
|
||||||
return result.map { }
|
}
|
||||||
|
Timber.i("checkBaseURL: result is $result")
|
||||||
|
_checkURLResult.value = result
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -14,19 +14,19 @@ import gq.kirmanak.mealient.data.recipes.db.entity.RecipeSummaryEntity
|
|||||||
import gq.kirmanak.mealient.databinding.FragmentRecipesBinding
|
import gq.kirmanak.mealient.databinding.FragmentRecipesBinding
|
||||||
import gq.kirmanak.mealient.extensions.collectWithViewLifecycle
|
import gq.kirmanak.mealient.extensions.collectWithViewLifecycle
|
||||||
import gq.kirmanak.mealient.extensions.refreshRequestFlow
|
import gq.kirmanak.mealient.extensions.refreshRequestFlow
|
||||||
import gq.kirmanak.mealient.ui.auth.AuthenticationViewModel
|
import gq.kirmanak.mealient.ui.activity.MainActivityViewModel
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class RecipesFragment : Fragment(R.layout.fragment_recipes) {
|
class RecipesFragment : Fragment(R.layout.fragment_recipes) {
|
||||||
private val binding by viewBinding(FragmentRecipesBinding::bind)
|
private val binding by viewBinding(FragmentRecipesBinding::bind)
|
||||||
private val viewModel by viewModels<RecipeViewModel>()
|
private val viewModel by viewModels<RecipeViewModel>()
|
||||||
private val authViewModel by activityViewModels<AuthenticationViewModel>()
|
private val activityViewModel by activityViewModels<MainActivityViewModel>()
|
||||||
|
|
||||||
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
|
activityViewModel.showLoginButton = true
|
||||||
setupRecipeAdapter()
|
setupRecipeAdapter()
|
||||||
(requireActivity() as? AppCompatActivity)?.supportActionBar?.title = null
|
(requireActivity() as? AppCompatActivity)?.supportActionBar?.title = null
|
||||||
}
|
}
|
||||||
@@ -64,6 +64,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
|
activityViewModel.showLoginButton = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context=".MainActivity">
|
tools:context=".ui.activity.MainActivity">
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:id="@+id/toolbar_holder"
|
android:id="@+id/toolbar_holder"
|
||||||
|
|||||||
Reference in New Issue
Block a user