Merge pull request #95 from kirmanak/bug-fix

Bugfix
This commit is contained in:
Kirill Kamakin
2022-11-17 22:29:40 +01:00
committed by GitHub
8 changed files with 55 additions and 22 deletions

View File

@@ -17,8 +17,8 @@ plugins {
android { android {
defaultConfig { defaultConfig {
applicationId = "gq.kirmanak.mealient" applicationId = "gq.kirmanak.mealient"
versionCode = 17 versionCode = 18
versionName = "0.3.2" versionName = "0.3.3"
} }
signingConfigs { signingConfigs {

View File

@@ -2,10 +2,13 @@ package gq.kirmanak.mealient.extensions
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.EditText import android.widget.EditText
import android.widget.TextView import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.core.content.getSystemService
import androidx.core.widget.doAfterTextChanged import androidx.core.widget.doAfterTextChanged
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
@@ -107,3 +110,8 @@ fun Context.showLongToast(@StringRes text: Int) = showLongToast(getString(text))
private fun Context.showToast(text: String, length: Int) { private fun Context.showToast(text: String, length: Int) {
Toast.makeText(this, text, length).show() Toast.makeText(this, text, length).show()
} }
fun View.hideKeyboard() {
val imm = context.getSystemService<InputMethodManager>()
imm?.hideSoftInputFromWindow(windowToken, 0)
}

View File

@@ -123,7 +123,13 @@ class MainActivity : AppCompatActivity(R.layout.main_activity) {
logger.e { "setupSearchItem: search item's actionView is null or not SearchView" } logger.e { "setupSearchItem: search item's actionView is null or not SearchView" }
return return
} }
searchView.queryHint = getString(R.string.search_recipes_hint) searchView.queryHint = getString(R.string.search_recipes_hint)
searchView.isSubmitButtonEnabled = false
searchView.setQuery(viewModel.lastSearchQuery, false)
searchView.isIconified = viewModel.lastSearchQuery.isNullOrBlank()
searchView.setOnCloseListener { searchView.setOnCloseListener {
logger.v { "onClose() called" } logger.v { "onClose() called" }
viewModel.onSearchQuery(null) viewModel.onSearchQuery(null)

View File

@@ -32,6 +32,9 @@ class MainActivityViewModel @Inject constructor(
private val _startDestination = MutableLiveData<Int>() private val _startDestination = MutableLiveData<Int>()
val startDestination: LiveData<Int> = _startDestination val startDestination: LiveData<Int> = _startDestination
var lastSearchQuery: String? = null
private set
init { init {
authRepo.isAuthorizedFlow authRepo.isAuthorizedFlow
.onEach { isAuthorized -> updateUiState { it.copy(isAuthorized = isAuthorized) } } .onEach { isAuthorized -> updateUiState { it.copy(isAuthorized = isAuthorized) } }
@@ -57,6 +60,9 @@ class MainActivityViewModel @Inject constructor(
fun onSearchQuery(query: String?) { fun onSearchQuery(query: String?) {
logger.v { "onSearchQuery() called with: query = $query" } logger.v { "onSearchQuery() called with: query = $query" }
recipeRepo.updateNameQuery(query) if (lastSearchQuery != query) {
lastSearchQuery = query
recipeRepo.updateNameQuery(query)
}
} }
} }

View File

@@ -127,9 +127,8 @@ class AddRecipeFragment : Fragment(R.layout.fragment_add_recipe) {
private fun saveValues() = with(binding) { private fun saveValues() = with(binding) {
logger.v { "saveValues() called" } logger.v { "saveValues() called" }
val instructions = val instructions = parseInputRows(instructionsFlow).map { AddRecipeInstructionInfo(it) }
parseInputRows(instructionsFlow).map { AddRecipeInstructionInfo(text = it) } val ingredients = parseInputRows(ingredientsFlow).map { AddRecipeIngredientInfo(it) }
val ingredients = parseInputRows(ingredientsFlow).map { AddRecipeIngredientInfo(note = it) }
val settings = AddRecipeSettingsInfo( val settings = AddRecipeSettingsInfo(
public = publicRecipe.isChecked, public = publicRecipe.isChecked,
disableComments = disableComments.isChecked, disableComments = disableComments.isChecked,
@@ -156,17 +155,18 @@ class AddRecipeFragment : Fragment(R.layout.fragment_add_recipe) {
private fun onSavedInputLoaded(request: AddRecipeInfo) = with(binding) { private fun onSavedInputLoaded(request: AddRecipeInfo) = with(binding) {
logger.v { "onSavedInputLoaded() called with: request = $request" } logger.v { "onSavedInputLoaded() called with: request = $request" }
recipeNameInput.setText(request.name)
recipeDescriptionInput.setText(request.description)
recipeYieldInput.setText(request.recipeYield)
publicRecipe.isChecked = request.settings.public
disableComments.isChecked = request.settings.disableComments
request.recipeIngredient.map { it.note } request.recipeIngredient.map { it.note }
.showIn(ingredientsFlow, R.string.fragment_add_recipe_ingredient_hint) .showIn(ingredientsFlow, R.string.fragment_add_recipe_ingredient_hint)
request.recipeInstructions.map { it.text } request.recipeInstructions.map { it.text }
.showIn(instructionsFlow, R.string.fragment_add_recipe_instruction_hint) .showIn(instructionsFlow, R.string.fragment_add_recipe_instruction_hint)
recipeNameInput.setText(request.name)
recipeDescriptionInput.setText(request.description)
recipeYieldInput.setText(request.recipeYield)
publicRecipe.isChecked = request.settings.public
disableComments.isChecked = request.settings.disableComments
} }
private fun Iterable<String>.showIn(flow: Flow, @StringRes hintId: Int) { private fun Iterable<String>.showIn(flow: Flow, @StringRes hintId: Int) {

View File

@@ -1,5 +1,6 @@
package gq.kirmanak.mealient.ui.recipes package gq.kirmanak.mealient.ui.recipes
import android.annotation.SuppressLint
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import androidx.annotation.StringRes import androidx.annotation.StringRes
@@ -17,10 +18,7 @@ import gq.kirmanak.mealient.R
import gq.kirmanak.mealient.database.recipe.entity.RecipeSummaryEntity import gq.kirmanak.mealient.database.recipe.entity.RecipeSummaryEntity
import gq.kirmanak.mealient.databinding.FragmentRecipesListBinding import gq.kirmanak.mealient.databinding.FragmentRecipesListBinding
import gq.kirmanak.mealient.datasource.NetworkError import gq.kirmanak.mealient.datasource.NetworkError
import gq.kirmanak.mealient.extensions.collectWhenViewResumed import gq.kirmanak.mealient.extensions.*
import gq.kirmanak.mealient.extensions.refreshRequestFlow
import gq.kirmanak.mealient.extensions.showLongToast
import gq.kirmanak.mealient.extensions.valueUpdatesOnly
import gq.kirmanak.mealient.logging.Logger import gq.kirmanak.mealient.logging.Logger
import gq.kirmanak.mealient.ui.activity.MainActivityViewModel import gq.kirmanak.mealient.ui.activity.MainActivityViewModel
import gq.kirmanak.mealient.ui.recipes.RecipesListFragmentDirections.Companion.actionRecipesFragmentToRecipeInfoFragment import gq.kirmanak.mealient.ui.recipes.RecipesListFragmentDirections.Companion.actionRecipesFragmentToRecipeInfoFragment
@@ -59,6 +57,15 @@ class RecipesListFragment : Fragment(R.layout.fragment_recipes_list) {
) )
} }
setupRecipeAdapter() setupRecipeAdapter()
hideKeyboardOnScroll()
}
@SuppressLint("ClickableViewAccessibility")
private fun hideKeyboardOnScroll() {
binding.recipes.setOnTouchListener { view, _ ->
view?.hideKeyboard()
false
}
} }
private fun navigateToRecipeInfo(id: String) { private fun navigateToRecipeInfo(id: String) {
@@ -70,11 +77,9 @@ class RecipesListFragment : Fragment(R.layout.fragment_recipes_list) {
private fun onRecipeClicked(recipe: RecipeSummaryEntity) { private fun onRecipeClicked(recipe: RecipeSummaryEntity) {
logger.v { "onRecipeClicked() called with: recipe = $recipe" } logger.v { "onRecipeClicked() called with: recipe = $recipe" }
binding.progress.isVisible = true binding.progress.isVisible = true
viewModel.refreshRecipeInfo(recipe.slug).observe(viewLifecycleOwner) { result -> viewModel.refreshRecipeInfo(recipe.slug).observe(viewLifecycleOwner) {
binding.progress.isVisible = false binding.progress.isVisible = false
if (result.isSuccess && !isNavigatingSomewhere()) { if (!isNavigatingSomewhere()) navigateToRecipeInfo(recipe.remoteId)
navigateToRecipeInfo(recipe.remoteId)
}
} }
} }

View File

@@ -63,7 +63,8 @@
<action <action
android:id="@+id/action_global_authenticationFragment" android:id="@+id/action_global_authenticationFragment"
app:destination="@id/authenticationFragment" /> app:destination="@id/authenticationFragment"
app:popUpTo="@id/recipesListFragment" />
<action <action
android:id="@+id/action_global_recipesListFragment" android:id="@+id/action_global_recipesListFragment"
@@ -71,9 +72,11 @@
<action <action
android:id="@+id/action_global_addRecipeFragment" android:id="@+id/action_global_addRecipeFragment"
app:destination="@id/addRecipeFragment" /> app:destination="@id/addRecipeFragment"
app:popUpTo="@id/recipesListFragment" />
<action <action
android:id="@+id/action_global_baseURLFragment" android:id="@+id/action_global_baseURLFragment"
app:destination="@id/baseURLFragment" /> app:destination="@id/baseURLFragment"
app:popUpTo="@id/recipesListFragment" />
</navigation> </navigation>

View File

@@ -4,7 +4,9 @@ import gq.kirmanak.mealient.data.auth.AuthRepo
import gq.kirmanak.mealient.data.baseurl.ServerInfoRepo import gq.kirmanak.mealient.data.baseurl.ServerInfoRepo
import gq.kirmanak.mealient.data.disclaimer.DisclaimerStorage import gq.kirmanak.mealient.data.disclaimer.DisclaimerStorage
import gq.kirmanak.mealient.data.recipes.RecipeRepo import gq.kirmanak.mealient.data.recipes.RecipeRepo
import gq.kirmanak.mealient.test.AuthImplTestData.TEST_BASE_URL
import gq.kirmanak.mealient.test.BaseUnitTest import gq.kirmanak.mealient.test.BaseUnitTest
import io.mockk.coEvery
import io.mockk.every import io.mockk.every
import io.mockk.impl.annotations.MockK import io.mockk.impl.annotations.MockK
import io.mockk.verify import io.mockk.verify
@@ -32,6 +34,8 @@ class MainActivityViewModelTest : BaseUnitTest() {
override fun setUp() { override fun setUp() {
super.setUp() super.setUp()
every { authRepo.isAuthorizedFlow } returns emptyFlow() every { authRepo.isAuthorizedFlow } returns emptyFlow()
coEvery { disclaimerStorage.isDisclaimerAccepted() } returns true
coEvery { serverInfoRepo.getUrl() } returns TEST_BASE_URL
subject = MainActivityViewModel( subject = MainActivityViewModel(
authRepo = authRepo, authRepo = authRepo,
logger = logger, logger = logger,
@@ -49,6 +53,7 @@ class MainActivityViewModelTest : BaseUnitTest() {
@Test @Test
fun `when onSearchQuery with null expect call to recipe repo`() { fun `when onSearchQuery with null expect call to recipe repo`() {
subject.onSearchQuery("query")
subject.onSearchQuery(null) subject.onSearchQuery(null)
verify { recipeRepo.updateNameQuery(null) } verify { recipeRepo.updateNameQuery(null) }
} }