@@ -17,8 +17,8 @@ plugins {
|
||||
android {
|
||||
defaultConfig {
|
||||
applicationId = "gq.kirmanak.mealient"
|
||||
versionCode = 17
|
||||
versionName = "0.3.2"
|
||||
versionCode = 18
|
||||
versionName = "0.3.3"
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
|
||||
@@ -2,10 +2,13 @@ package gq.kirmanak.mealient.extensions
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.view.View
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.EditText
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.core.widget.doAfterTextChanged
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
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) {
|
||||
Toast.makeText(this, text, length).show()
|
||||
}
|
||||
|
||||
fun View.hideKeyboard() {
|
||||
val imm = context.getSystemService<InputMethodManager>()
|
||||
imm?.hideSoftInputFromWindow(windowToken, 0)
|
||||
}
|
||||
@@ -123,7 +123,13 @@ class MainActivity : AppCompatActivity(R.layout.main_activity) {
|
||||
logger.e { "setupSearchItem: search item's actionView is null or not SearchView" }
|
||||
return
|
||||
}
|
||||
|
||||
searchView.queryHint = getString(R.string.search_recipes_hint)
|
||||
searchView.isSubmitButtonEnabled = false
|
||||
|
||||
searchView.setQuery(viewModel.lastSearchQuery, false)
|
||||
searchView.isIconified = viewModel.lastSearchQuery.isNullOrBlank()
|
||||
|
||||
searchView.setOnCloseListener {
|
||||
logger.v { "onClose() called" }
|
||||
viewModel.onSearchQuery(null)
|
||||
|
||||
@@ -32,6 +32,9 @@ class MainActivityViewModel @Inject constructor(
|
||||
private val _startDestination = MutableLiveData<Int>()
|
||||
val startDestination: LiveData<Int> = _startDestination
|
||||
|
||||
var lastSearchQuery: String? = null
|
||||
private set
|
||||
|
||||
init {
|
||||
authRepo.isAuthorizedFlow
|
||||
.onEach { isAuthorized -> updateUiState { it.copy(isAuthorized = isAuthorized) } }
|
||||
@@ -57,6 +60,9 @@ class MainActivityViewModel @Inject constructor(
|
||||
|
||||
fun onSearchQuery(query: String?) {
|
||||
logger.v { "onSearchQuery() called with: query = $query" }
|
||||
recipeRepo.updateNameQuery(query)
|
||||
if (lastSearchQuery != query) {
|
||||
lastSearchQuery = query
|
||||
recipeRepo.updateNameQuery(query)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,9 +127,8 @@ class AddRecipeFragment : Fragment(R.layout.fragment_add_recipe) {
|
||||
|
||||
private fun saveValues() = with(binding) {
|
||||
logger.v { "saveValues() called" }
|
||||
val instructions =
|
||||
parseInputRows(instructionsFlow).map { AddRecipeInstructionInfo(text = it) }
|
||||
val ingredients = parseInputRows(ingredientsFlow).map { AddRecipeIngredientInfo(note = it) }
|
||||
val instructions = parseInputRows(instructionsFlow).map { AddRecipeInstructionInfo(it) }
|
||||
val ingredients = parseInputRows(ingredientsFlow).map { AddRecipeIngredientInfo(it) }
|
||||
val settings = AddRecipeSettingsInfo(
|
||||
public = publicRecipe.isChecked,
|
||||
disableComments = disableComments.isChecked,
|
||||
@@ -156,17 +155,18 @@ class AddRecipeFragment : Fragment(R.layout.fragment_add_recipe) {
|
||||
|
||||
private fun onSavedInputLoaded(request: AddRecipeInfo) = with(binding) {
|
||||
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 }
|
||||
.showIn(ingredientsFlow, R.string.fragment_add_recipe_ingredient_hint)
|
||||
|
||||
request.recipeInstructions.map { it.text }
|
||||
.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) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package gq.kirmanak.mealient.ui.recipes
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
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.databinding.FragmentRecipesListBinding
|
||||
import gq.kirmanak.mealient.datasource.NetworkError
|
||||
import gq.kirmanak.mealient.extensions.collectWhenViewResumed
|
||||
import gq.kirmanak.mealient.extensions.refreshRequestFlow
|
||||
import gq.kirmanak.mealient.extensions.showLongToast
|
||||
import gq.kirmanak.mealient.extensions.valueUpdatesOnly
|
||||
import gq.kirmanak.mealient.extensions.*
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import gq.kirmanak.mealient.ui.activity.MainActivityViewModel
|
||||
import gq.kirmanak.mealient.ui.recipes.RecipesListFragmentDirections.Companion.actionRecipesFragmentToRecipeInfoFragment
|
||||
@@ -59,6 +57,15 @@ class RecipesListFragment : Fragment(R.layout.fragment_recipes_list) {
|
||||
)
|
||||
}
|
||||
setupRecipeAdapter()
|
||||
hideKeyboardOnScroll()
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private fun hideKeyboardOnScroll() {
|
||||
binding.recipes.setOnTouchListener { view, _ ->
|
||||
view?.hideKeyboard()
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
private fun navigateToRecipeInfo(id: String) {
|
||||
@@ -70,11 +77,9 @@ class RecipesListFragment : Fragment(R.layout.fragment_recipes_list) {
|
||||
private fun onRecipeClicked(recipe: RecipeSummaryEntity) {
|
||||
logger.v { "onRecipeClicked() called with: recipe = $recipe" }
|
||||
binding.progress.isVisible = true
|
||||
viewModel.refreshRecipeInfo(recipe.slug).observe(viewLifecycleOwner) { result ->
|
||||
viewModel.refreshRecipeInfo(recipe.slug).observe(viewLifecycleOwner) {
|
||||
binding.progress.isVisible = false
|
||||
if (result.isSuccess && !isNavigatingSomewhere()) {
|
||||
navigateToRecipeInfo(recipe.remoteId)
|
||||
}
|
||||
if (!isNavigatingSomewhere()) navigateToRecipeInfo(recipe.remoteId)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -63,7 +63,8 @@
|
||||
|
||||
<action
|
||||
android:id="@+id/action_global_authenticationFragment"
|
||||
app:destination="@id/authenticationFragment" />
|
||||
app:destination="@id/authenticationFragment"
|
||||
app:popUpTo="@id/recipesListFragment" />
|
||||
|
||||
<action
|
||||
android:id="@+id/action_global_recipesListFragment"
|
||||
@@ -71,9 +72,11 @@
|
||||
|
||||
<action
|
||||
android:id="@+id/action_global_addRecipeFragment"
|
||||
app:destination="@id/addRecipeFragment" />
|
||||
app:destination="@id/addRecipeFragment"
|
||||
app:popUpTo="@id/recipesListFragment" />
|
||||
|
||||
<action
|
||||
android:id="@+id/action_global_baseURLFragment"
|
||||
app:destination="@id/baseURLFragment" />
|
||||
app:destination="@id/baseURLFragment"
|
||||
app:popUpTo="@id/recipesListFragment" />
|
||||
</navigation>
|
||||
@@ -4,7 +4,9 @@ import gq.kirmanak.mealient.data.auth.AuthRepo
|
||||
import gq.kirmanak.mealient.data.baseurl.ServerInfoRepo
|
||||
import gq.kirmanak.mealient.data.disclaimer.DisclaimerStorage
|
||||
import gq.kirmanak.mealient.data.recipes.RecipeRepo
|
||||
import gq.kirmanak.mealient.test.AuthImplTestData.TEST_BASE_URL
|
||||
import gq.kirmanak.mealient.test.BaseUnitTest
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.every
|
||||
import io.mockk.impl.annotations.MockK
|
||||
import io.mockk.verify
|
||||
@@ -32,6 +34,8 @@ class MainActivityViewModelTest : BaseUnitTest() {
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
every { authRepo.isAuthorizedFlow } returns emptyFlow()
|
||||
coEvery { disclaimerStorage.isDisclaimerAccepted() } returns true
|
||||
coEvery { serverInfoRepo.getUrl() } returns TEST_BASE_URL
|
||||
subject = MainActivityViewModel(
|
||||
authRepo = authRepo,
|
||||
logger = logger,
|
||||
@@ -49,6 +53,7 @@ class MainActivityViewModelTest : BaseUnitTest() {
|
||||
|
||||
@Test
|
||||
fun `when onSearchQuery with null expect call to recipe repo`() {
|
||||
subject.onSearchQuery("query")
|
||||
subject.onSearchQuery(null)
|
||||
verify { recipeRepo.updateNameQuery(null) }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user