Implement showing recipe pictures in the list

This commit is contained in:
Kirill Kamakin
2021-11-08 21:09:55 +03:00
parent 67b75b710d
commit 407a73d444
10 changed files with 64 additions and 12 deletions

View File

@@ -89,7 +89,7 @@ dependencies {
def glide_version = "4.12.0"
implementation "com.github.bumptech.glide:glide:$glide_version"
annotationProcessor "com.github.bumptech.glide:compiler:$glide_version"
kapt "com.github.bumptech.glide:compiler:$glide_version"
androidTestImplementation "androidx.test.ext:junit:1.1.3"
androidTestImplementation "androidx.test.espresso:espresso-core:3.4.0"

View File

@@ -12,10 +12,9 @@ import javax.inject.Inject
class RetrofitBuilder @Inject constructor(private val okHttpBuilder: OkHttpBuilder) {
fun buildRetrofit(baseUrl: String, token: String? = null): Retrofit {
Timber.v("buildRetrofit() called with: baseUrl = $baseUrl, token = $token")
val url = if (baseUrl.startsWith("http")) baseUrl else "https://$baseUrl"
val contentType = "application/json".toMediaType()
return Retrofit.Builder()
.baseUrl(url)
.baseUrl(baseUrl)
.client(okHttpBuilder.buildOkHttp(token))
.addConverterFactory(Json.asConverterFactory(contentType))
.build()

View File

@@ -20,11 +20,12 @@ class AuthRepoImpl @Inject constructor(
baseUrl: String
): Throwable? {
Timber.v("authenticate() called with: username = $username, password = $password, baseUrl = $baseUrl")
val authResult = dataSource.authenticate(username, password, baseUrl)
val url = if (baseUrl.startsWith("http")) baseUrl else "https://$baseUrl"
val authResult = dataSource.authenticate(username, password, url)
Timber.d("authenticate result is $authResult")
if (authResult.isFailure) return authResult.exceptionOrNull()
val token = checkNotNull(authResult.getOrNull())
storage.storeAuthData(token, baseUrl)
storage.storeAuthData(token, url)
return null
}

View File

@@ -0,0 +1,20 @@
package gq.kirmanak.mealie.data.recipes
import android.widget.ImageView
import gq.kirmanak.mealie.R
import gq.kirmanak.mealie.data.auth.AuthRepo
import gq.kirmanak.mealie.ui.ImageLoader
import javax.inject.Inject
class RecipeImageLoader @Inject constructor(
private val imageLoader: ImageLoader,
private val authRepo: AuthRepo
) {
suspend fun loadRecipeImage(view: ImageView, slug: String?) {
val baseUrl = authRepo.getBaseUrl()
val recipeImageUrl =
if (baseUrl.isNullOrBlank()) null
else "$baseUrl/api/media/recipes/$slug/images/original.webp"
imageLoader.loadImage(recipeImageUrl, R.drawable.placeholder_recipe, view)
}
}

View File

@@ -13,7 +13,7 @@ class RecipeRepoImpl @Inject constructor(
private val storage: RecipeStorage
) : RecipeRepo {
override fun createPager(): Pager<Int, RecipeEntity> {
val pagingConfig = PagingConfig(pageSize = 30)
val pagingConfig = PagingConfig(pageSize = 30, enablePlaceholders = false, prefetchDistance = 10)
return Pager(pagingConfig, 0, mediator) {
storage.queryRecipes()
}

View File

@@ -0,0 +1,15 @@
package gq.kirmanak.mealie.ui
import android.widget.ImageView
import androidx.annotation.DrawableRes
import com.bumptech.glide.Glide
import javax.inject.Inject
class ImageLoader @Inject constructor() {
fun loadImage(url: String?, @DrawableRes placeholderId: Int, imageView: ImageView) {
with(Glide.with(imageView)) {
if (url.isNullOrBlank()) clear(imageView)
else load(url).placeholder(placeholderId).into(imageView)
}
}
}

View File

@@ -1,18 +1,30 @@
package gq.kirmanak.mealie.ui.recipes
import android.widget.ImageView
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.Pager
import androidx.paging.PagingData
import androidx.paging.cachedIn
import dagger.hilt.android.lifecycle.HiltViewModel
import gq.kirmanak.mealie.data.recipes.RecipeImageLoader
import gq.kirmanak.mealie.data.recipes.RecipeRepo
import gq.kirmanak.mealie.data.recipes.db.RecipeEntity
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class RecipeViewModel @Inject constructor(private val recipeRepo: RecipeRepo) : ViewModel() {
class RecipeViewModel @Inject constructor(
private val recipeRepo: RecipeRepo,
private val recipeImageLoader: RecipeImageLoader
) : ViewModel() {
private val pager: Pager<Int, RecipeEntity> by lazy { recipeRepo.createPager() }
val recipeFlow: Flow<PagingData<RecipeEntity>> by lazy { pager.flow.cachedIn(viewModelScope) }
fun loadRecipeImage(view: ImageView, recipe: RecipeEntity?) {
viewModelScope.launch {
recipeImageLoader.loadRecipeImage(view, recipe?.slug)
}
}
}

View File

@@ -31,7 +31,7 @@ class RecipesFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.recipes.layoutManager = LinearLayoutManager(requireContext())
val recipesPagingAdapter = RecipesPagingAdapter()
val recipesPagingAdapter = RecipesPagingAdapter(viewModel)
binding.recipes.adapter = recipesPagingAdapter
lifecycleScope.launchWhenResumed {
viewModel.recipeFlow.collectLatest {

View File

@@ -9,7 +9,9 @@ import gq.kirmanak.mealie.data.recipes.db.RecipeEntity
import gq.kirmanak.mealie.databinding.ViewHolderRecipeBinding
import timber.log.Timber
class RecipesPagingAdapter : PagingDataAdapter<RecipeEntity, RecipeViewHolder>(RecipeDiffCallback) {
class RecipesPagingAdapter(
private val viewModel: RecipeViewModel
) : PagingDataAdapter<RecipeEntity, RecipeViewHolder>(RecipeDiffCallback) {
override fun onBindViewHolder(holder: RecipeViewHolder, position: Int) {
val item = getItem(position)
holder.bind(item)
@@ -19,14 +21,17 @@ class RecipesPagingAdapter : PagingDataAdapter<RecipeEntity, RecipeViewHolder>(R
Timber.v("onCreateViewHolder() called with: parent = $parent, viewType = $viewType")
val inflater = LayoutInflater.from(parent.context)
val binding = ViewHolderRecipeBinding.inflate(inflater, parent, false)
return RecipeViewHolder(binding)
return RecipeViewHolder(binding, viewModel)
}
}
class RecipeViewHolder(private val binding: ViewHolderRecipeBinding) :
RecyclerView.ViewHolder(binding.root) {
class RecipeViewHolder(
private val binding: ViewHolderRecipeBinding,
private val recipeViewModel: RecipeViewModel
) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: RecipeEntity?) {
binding.name.text = item?.name
recipeViewModel.loadRecipeImage(binding.image, item)
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB