@@ -19,8 +19,7 @@ import timber.log.Timber
|
||||
class MainActivity : AppCompatActivity() {
|
||||
private lateinit var binding: MainActivityBinding
|
||||
private val authViewModel by viewModels<AuthenticationViewModel>()
|
||||
private val authenticationState: AuthenticationState
|
||||
get() = authViewModel.currentAuthenticationState
|
||||
private var lastAuthenticationState: AuthenticationState? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -52,31 +51,29 @@ 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) {
|
||||
Timber.v("onAuthStateUpdate() called with: it = $authState")
|
||||
lastAuthenticationState = authState
|
||||
invalidateOptionsMenu()
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
Timber.v("onCreateOptionsMenu() called with: menu = $menu")
|
||||
menuInflater.inflate(R.menu.main_toolbar, menu)
|
||||
menu.findItem(R.id.logout).isVisible = authenticationState == AUTHORIZED
|
||||
menu.findItem(R.id.login).isVisible = authenticationState == UNAUTHORIZED
|
||||
menu.findItem(R.id.logout).isVisible = lastAuthenticationState == AUTHORIZED
|
||||
menu.findItem(R.id.login).isVisible = lastAuthenticationState == UNAUTHORIZED
|
||||
return true
|
||||
}
|
||||
|
||||
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.login()
|
||||
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)
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package gq.kirmanak.mealient.extensions
|
||||
|
||||
import androidx.activity.OnBackPressedDispatcher
|
||||
import androidx.activity.addCallback
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
fun Fragment.executeOnceOnBackPressed(action: () -> Unit) {
|
||||
val onBackPressedDispatcher = requireActivity().onBackPressedDispatcher
|
||||
lifecycleScope.launch {
|
||||
onBackPressedDispatcher.backPressedFlow().first()
|
||||
action()
|
||||
onBackPressedDispatcher.onBackPressed() // Execute other callbacks now
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
fun OnBackPressedDispatcher.backPressedFlow(): Flow<Unit> = callbackFlow {
|
||||
val callback = addCallback { trySend(Unit) }
|
||||
awaitClose {
|
||||
callback.isEnabled = false
|
||||
callback.remove()
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <T> Fragment.collectWithViewLifecycle(
|
||||
flow: Flow<T>,
|
||||
crossinline collector: suspend (T) -> Unit,
|
||||
) = viewLifecycleOwner.lifecycleScope.launch { flow.collect(collector) }
|
||||
@@ -1,4 +1,4 @@
|
||||
package gq.kirmanak.mealient.ui
|
||||
package gq.kirmanak.mealient.extensions
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Build
|
||||
@@ -10,8 +10,6 @@ import androidx.annotation.RequiresApi
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.widget.doAfterTextChanged
|
||||
import androidx.lifecycle.LifecycleCoroutineScope
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.asLiveData
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
@@ -26,20 +24,17 @@ import kotlinx.coroutines.flow.first
|
||||
import timber.log.Timber
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
fun SwipeRefreshLayout.refreshesLiveData(): LiveData<Unit> {
|
||||
val callbackFlow: Flow<Unit> = callbackFlow {
|
||||
val listener = SwipeRefreshLayout.OnRefreshListener {
|
||||
Timber.v("Refresh requested")
|
||||
trySend(Unit).logErrors("refreshesFlow")
|
||||
}
|
||||
Timber.v("Adding refresh request listener")
|
||||
setOnRefreshListener(listener)
|
||||
awaitClose {
|
||||
Timber.v("Removing refresh request listener")
|
||||
setOnRefreshListener(null)
|
||||
}
|
||||
fun SwipeRefreshLayout.refreshRequestFlow(): Flow<Unit> = callbackFlow {
|
||||
Timber.v("refreshRequestFlow() called")
|
||||
val listener = SwipeRefreshLayout.OnRefreshListener {
|
||||
Timber.v("refreshRequestFlow: listener called")
|
||||
trySend(Unit).logErrors("refreshesFlow")
|
||||
}
|
||||
setOnRefreshListener(listener)
|
||||
awaitClose {
|
||||
Timber.v("Removing refresh request listener")
|
||||
setOnRefreshListener(null)
|
||||
}
|
||||
return callbackFlow.asLiveData()
|
||||
}
|
||||
|
||||
fun Activity.setSystemUiVisibility(isVisible: Boolean) {
|
||||
@@ -5,15 +5,16 @@ 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.ui.checkIfInputIsEmpty
|
||||
import gq.kirmanak.mealient.extensions.checkIfInputIsEmpty
|
||||
import gq.kirmanak.mealient.extensions.executeOnceOnBackPressed
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
||||
@AndroidEntryPoint
|
||||
@@ -21,13 +22,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.authRequested = false }
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
@@ -38,13 +36,6 @@ class AuthenticationFragment : Fragment(R.layout.fragment_authentication) {
|
||||
getString(R.string.app_name)
|
||||
}
|
||||
|
||||
private fun onAuthStatusChange(isAuthenticated: AuthenticationState) {
|
||||
Timber.v("onAuthStatusChange() called with: isAuthenticated = $isAuthenticated")
|
||||
if (isAuthenticated == AuthenticationState.AUTHORIZED) {
|
||||
findNavController().popBackStack()
|
||||
}
|
||||
}
|
||||
|
||||
private fun onLoginClicked(): Unit = with(binding) {
|
||||
Timber.v("onLoginClicked() called")
|
||||
|
||||
@@ -57,14 +48,23 @@ 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
|
||||
}
|
||||
|
||||
button.isClickable = true
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
onAuthenticationResult(viewModel.authenticate(email, pass))
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
package gq.kirmanak.mealient.ui.auth
|
||||
|
||||
import androidx.lifecycle.*
|
||||
import androidx.lifecycle.LiveData
|
||||
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 kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
@@ -14,41 +18,31 @@ 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()
|
||||
val currentAuthenticationState: AuthenticationState
|
||||
get() = checkNotNull(authenticationState.value) { "Auth state flow mustn't be null" }
|
||||
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>> {
|
||||
Timber.v("authenticate() called with: username = $username, password = $password")
|
||||
val result = MutableLiveData<Result<Unit>>()
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
runCatching {
|
||||
authRepo.authenticate(username, password)
|
||||
}.onFailure {
|
||||
Timber.e(it, "authenticate: can't authenticate")
|
||||
result.value = Result.failure(it)
|
||||
}.onSuccess {
|
||||
Timber.d("authenticate: authenticated")
|
||||
result.value = Result.success(Unit)
|
||||
authRequestsFlow.collect { isRequested ->
|
||||
// Clear auth token on logout request
|
||||
if (!isRequested) authRepo.logout()
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
fun logout() {
|
||||
Timber.v("logout() called")
|
||||
viewModelScope.launch {
|
||||
loginRequestsFlow.emit(false)
|
||||
authRepo.logout()
|
||||
}
|
||||
}
|
||||
|
||||
fun login() {
|
||||
Timber.v("login() called")
|
||||
viewModelScope.launch { loginRequestsFlow.emit(true) }
|
||||
suspend fun authenticate(username: String, password: String): Result<Unit> = runCatching {
|
||||
authRepo.authenticate(username, password)
|
||||
}.onFailure {
|
||||
Timber.e(it, "authenticate: can't authenticate")
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ import dagger.hilt.android.AndroidEntryPoint
|
||||
import gq.kirmanak.mealient.R
|
||||
import gq.kirmanak.mealient.data.network.NetworkError
|
||||
import gq.kirmanak.mealient.databinding.FragmentBaseUrlBinding
|
||||
import gq.kirmanak.mealient.ui.checkIfInputIsEmpty
|
||||
import gq.kirmanak.mealient.extensions.checkIfInputIsEmpty
|
||||
import timber.log.Timber
|
||||
|
||||
@AndroidEntryPoint
|
||||
|
||||
@@ -9,7 +9,7 @@ import timber.log.Timber
|
||||
class RecipeViewHolder(
|
||||
private val binding: ViewHolderRecipeBinding,
|
||||
private val recipeViewModel: RecipeViewModel,
|
||||
private val clickListener: (RecipeSummaryEntity) -> Unit
|
||||
private val clickListener: (RecipeSummaryEntity) -> Unit,
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
private val loadingPlaceholder by lazy {
|
||||
binding.root.resources.getString(R.string.view_holder_recipe_text_placeholder)
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package gq.kirmanak.mealient.ui.recipes
|
||||
|
||||
import android.widget.ImageView
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.paging.cachedIn
|
||||
@@ -10,34 +8,17 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import gq.kirmanak.mealient.data.recipes.RecipeImageLoader
|
||||
import gq.kirmanak.mealient.data.recipes.RecipeRepo
|
||||
import gq.kirmanak.mealient.data.recipes.db.entity.RecipeSummaryEntity
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.receiveAsFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class RecipeViewModel @Inject constructor(
|
||||
private val recipeRepo: RecipeRepo,
|
||||
recipeRepo: RecipeRepo,
|
||||
private val recipeImageLoader: RecipeImageLoader
|
||||
) : ViewModel() {
|
||||
private var _isRefreshing = MutableLiveData<Boolean>()
|
||||
val isRefreshing: LiveData<Boolean> get() = _isRefreshing
|
||||
|
||||
private val _nextRecipeInfoChannel = Channel<RecipeSummaryEntity>()
|
||||
val nextRecipeInfo: Flow<RecipeSummaryEntity> =
|
||||
_nextRecipeInfoChannel.receiveAsFlow()
|
||||
|
||||
val adapter = RecipesPagingAdapter(this) {
|
||||
Timber.d("onClick: recipe clicked $it")
|
||||
viewModelScope.launch { _nextRecipeInfoChannel.send(it) }
|
||||
}
|
||||
|
||||
init {
|
||||
setupAdapter()
|
||||
}
|
||||
val pagingData = recipeRepo.createPager().flow.cachedIn(viewModelScope)
|
||||
|
||||
fun loadRecipeImage(view: ImageView, recipeSummary: RecipeSummaryEntity?) {
|
||||
Timber.v("loadRecipeImage() called with: view = $view, recipeSummary = $recipeSummary")
|
||||
@@ -45,21 +26,4 @@ class RecipeViewModel @Inject constructor(
|
||||
recipeImageLoader.loadRecipeImage(view, recipeSummary?.slug)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupAdapter() {
|
||||
with(viewModelScope) {
|
||||
launch {
|
||||
recipeRepo.createPager().flow.cachedIn(this).collect {
|
||||
Timber.d("setupAdapter: received data update")
|
||||
adapter.submitData(it)
|
||||
}
|
||||
}
|
||||
launch {
|
||||
adapter.onPagesUpdatedFlow.collect {
|
||||
Timber.d("setupAdapter: pages have been updated")
|
||||
_isRefreshing.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,17 +6,16 @@ import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.fragment.app.viewModels
|
||||
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.recipes.db.entity.RecipeSummaryEntity
|
||||
import gq.kirmanak.mealient.databinding.FragmentRecipesBinding
|
||||
import gq.kirmanak.mealient.extensions.collectWithViewLifecycle
|
||||
import gq.kirmanak.mealient.extensions.refreshRequestFlow
|
||||
import gq.kirmanak.mealient.ui.auth.AuthenticationState
|
||||
import gq.kirmanak.mealient.ui.auth.AuthenticationViewModel
|
||||
import gq.kirmanak.mealient.ui.refreshesLiveData
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import timber.log.Timber
|
||||
|
||||
@AndroidEntryPoint
|
||||
@@ -27,7 +26,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 +40,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
|
||||
}
|
||||
@@ -56,20 +57,19 @@ class RecipesFragment : Fragment(R.layout.fragment_recipes) {
|
||||
|
||||
private fun setupRecipeAdapter() {
|
||||
Timber.v("setupRecipeAdapter() called")
|
||||
binding.recipes.adapter = viewModel.adapter
|
||||
viewModel.isRefreshing.observe(viewLifecycleOwner) {
|
||||
Timber.d("setupRecipeAdapter: isRefreshing = $it")
|
||||
binding.refresher.isRefreshing = it
|
||||
val adapter = RecipesPagingAdapter(viewModel, ::navigateToRecipeInfo)
|
||||
binding.recipes.adapter = adapter
|
||||
collectWithViewLifecycle(viewModel.pagingData) {
|
||||
Timber.v("setupRecipeAdapter: received data update")
|
||||
adapter.submitData(lifecycle, it)
|
||||
}
|
||||
binding.refresher.refreshesLiveData().observe(viewLifecycleOwner) {
|
||||
Timber.d("setupRecipeAdapter: received refresh request")
|
||||
viewModel.adapter.refresh()
|
||||
collectWithViewLifecycle(adapter.onPagesUpdatedFlow) {
|
||||
Timber.v("setupRecipeAdapter: pages updated")
|
||||
binding.refresher.isRefreshing = false
|
||||
}
|
||||
viewLifecycleOwner.lifecycleScope.launchWhenResumed {
|
||||
viewModel.nextRecipeInfo.collect {
|
||||
Timber.d("setupRecipeAdapter: navigating to recipe $it")
|
||||
navigateToRecipeInfo(it)
|
||||
}
|
||||
collectWithViewLifecycle(binding.refresher.refreshRequestFlow()) {
|
||||
Timber.v("setupRecipeAdapter: received refresh request")
|
||||
adapter.refresh()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,5 +78,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
|
||||
}
|
||||
}
|
||||
@@ -9,8 +9,8 @@ import androidx.navigation.NavDirections
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import gq.kirmanak.mealient.R
|
||||
import gq.kirmanak.mealient.ui.setActionBarVisibility
|
||||
import gq.kirmanak.mealient.ui.setSystemUiVisibility
|
||||
import gq.kirmanak.mealient.extensions.setActionBarVisibility
|
||||
import gq.kirmanak.mealient.extensions.setSystemUiVisibility
|
||||
import timber.log.Timber
|
||||
|
||||
@AndroidEntryPoint
|
||||
|
||||
Reference in New Issue
Block a user