Skip to content

Commit

Permalink
Added FavoritesFragment.
Browse files Browse the repository at this point in the history
  • Loading branch information
klauz42 committed Feb 5, 2024
1 parent 05928e6 commit 50ac8ba
Show file tree
Hide file tree
Showing 17 changed files with 276 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import dagger.Module
import dagger.multibindings.IntoMap
import ru.klauz42.yetanotheronlinestore.ViewModelFactory
import ru.klauz42.yetanotheronlinestore.presentation.catalog.CatalogViewModel
import ru.klauz42.yetanotheronlinestore.presentation.favorites.FavoritesViewModel
import ru.klauz42.yetanotheronlinestore.presentation.product.ProductViewModel
import ru.klauz42.yetanotheronlinestore.presentation.profile.ProfileViewModel
import ru.klauz42.yetanotheronlinestore.presentation.signin.SignInViewModel
Expand Down Expand Up @@ -37,6 +38,11 @@ interface ViewModelModule {
@IntoMap
@ViewModelKey(ProfileViewModel::class)
fun bindProfileViewModel(viewModel: ProfileViewModel): ViewModel

@Binds
@IntoMap
@ViewModelKey(FavoritesViewModel::class)
fun bindFavoritesViewModel(viewModel: FavoritesViewModel): ViewModel
}

@MustBeDocumented
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package ru.klauz42.yetanotheronlinestore.domain.usecases

import kotlinx.coroutines.flow.Flow
import ru.klauz42.yetanotheronlinestore.domain.models.FavoritesRepository
import ru.klauz42.yetanotheronlinestore.domain.models.entities.Product
import javax.inject.Inject

class GetFavoriteProductsUseCase @Inject constructor(
private val repository: FavoritesRepository
) {
operator fun invoke(): Flow<List<Product>> {
return repository.getFavorites()
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package ru.klauz42.yetanotheronlinestore.presentation.catalog
package ru.klauz42.yetanotheronlinestore.presentation

import android.graphics.Rect
import android.view.View
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package ru.klauz42.yetanotheronlinestore.presentation.catalog
package ru.klauz42.yetanotheronlinestore.presentation

import android.widget.CompoundButton
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.tabs.TabLayoutMediator
import ru.klauz42.yetanotheronlinestore.databinding.CardViewProductBinding
import ru.klauz42.yetanotheronlinestore.presentation.ImageCarouselAdapter
import ru.klauz42.yetanotheronlinestore.presentation.ProductWithImages


class ProductViewHolder(private val binding: CardViewProductBinding) :
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package ru.klauz42.yetanotheronlinestore.presentation.catalog
package ru.klauz42.yetanotheronlinestore.presentation

import android.view.LayoutInflater
import android.view.ViewGroup
Expand All @@ -7,7 +7,6 @@ import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import ru.klauz42.yetanotheronlinestore.databinding.CardViewProductBinding
import ru.klauz42.yetanotheronlinestore.di.scopes.FragmentScope
import ru.klauz42.yetanotheronlinestore.presentation.ProductWithImages
import javax.inject.Inject


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ import ru.klauz42.yetanotheronlinestore.domain.models.entities.Product
import ru.klauz42.yetanotheronlinestore.domain.models.entities.SortType
import ru.klauz42.yetanotheronlinestore.domain.models.entities.TagCheckBoxItem
import ru.klauz42.yetanotheronlinestore.presentation.MainActivity
import ru.klauz42.yetanotheronlinestore.presentation.ProductMarginItemDecorator
import ru.klauz42.yetanotheronlinestore.presentation.ProductWithImages
import ru.klauz42.yetanotheronlinestore.presentation.ProductsAdapter
import javax.inject.Inject


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,145 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import ru.klauz42.yetanotheronlinestore.R
import androidx.fragment.app.viewModels
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.tabs.TabLayout
import ru.klauz42.yetanotheronlinestore.databinding.FragmentFavoritesBinding
import ru.klauz42.yetanotheronlinestore.di.components.DaggerFragmentComponent
import ru.klauz42.yetanotheronlinestore.di.components.FragmentComponent
import ru.klauz42.yetanotheronlinestore.domain.models.entities.Product
import ru.klauz42.yetanotheronlinestore.presentation.MainActivity
import ru.klauz42.yetanotheronlinestore.presentation.ProductMarginItemDecorator
import ru.klauz42.yetanotheronlinestore.presentation.ProductWithImages
import ru.klauz42.yetanotheronlinestore.presentation.ProductsAdapter
import javax.inject.Inject


class FavoritesFragment
: Fragment(),
ProductsAdapter.Listener {

private var _binding: FragmentFavoritesBinding? = null
val binding get() = _binding!!

private lateinit var fragmentComponent: FragmentComponent

@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
private val viewModel: FavoritesViewModel by viewModels { viewModelFactory }

@Inject
lateinit var productsAdapter: ProductsAdapter

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

fragmentComponent =
DaggerFragmentComponent.builder()
.activityComponent((requireActivity() as MainActivity).activityComponent).build()
fragmentComponent.inject(this)
}

class FavoritesFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_stub, container, false)
): View {
_binding = FragmentFavoritesBinding.inflate(inflater, container, false)

return binding.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

setupProductsRecyclerView()

viewModel.favoriteIds.observe(viewLifecycleOwner) {
updateLikes(it)
}

viewModel.productsLiveData.observe(viewLifecycleOwner) { items ->
submitProductsAdapterList(items)
}

binding.header.buttonBack.setOnClickListener {
findNavController().navigateUp()
}

//todo: fix hardcode
binding.tabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab?) {
when (tab?.position) {
0 -> {
binding.content.recyclerViewProducts.visibility = View.VISIBLE
}

1 -> {
binding.content.recyclerViewProducts.visibility = View.GONE
}
}
}

override fun onTabUnselected(tab: TabLayout.Tab?) {}
override fun onTabReselected(tab: TabLayout.Tab?) {}
})
}


private fun submitProductsAdapterList(itemList: List<Product>) {

val productsWithImages: List<ProductWithImages> = itemList.map { product ->
ProductWithImages.from(product)
}

(binding.content.recyclerViewProducts.adapter as ProductsAdapter).submitList(
productsWithImages
)
}

private fun updateLikes(favoriteIds: List<String>) {
(binding.content.recyclerViewProducts.adapter as ProductsAdapter).updateFavoriteIds(
favoriteIds
)
}

private val reuseViewHolderItemAnimator = object : DefaultItemAnimator() {
override fun canReuseUpdatedViewHolder(viewHolder: RecyclerView.ViewHolder) = true
}

private fun setupProductsRecyclerView() {
productsAdapter.setAdapterListener(this)

binding.content.recyclerViewProducts.apply {
layoutManager = GridLayoutManager(context, 2)
addItemDecoration(ProductMarginItemDecorator())
adapter = productsAdapter
itemAnimator = reuseViewHolderItemAnimator
}
}

private fun navigateToProduct(id: String) {
val action =
FavoritesFragmentDirections.actionFavoritesToProduct(id)
findNavController().navigate(action)
}

override fun onDestroy() {
_binding = null

super.onDestroy()
}

override fun itemClickListener(id: String) {
navigateToProduct(id)
}

override fun favoriteCheckListener(id: String, isFavorite: Boolean) {
viewModel.updateLikedStatus(id, isFavorite)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package ru.klauz42.yetanotheronlinestore.presentation.favorites

import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import ru.klauz42.yetanotheronlinestore.domain.models.entities.Product
import ru.klauz42.yetanotheronlinestore.domain.usecases.GetFavoriteProductsUseCase
import ru.klauz42.yetanotheronlinestore.domain.usecases.GetFavoritesIdUseCase
import ru.klauz42.yetanotheronlinestore.domain.usecases.SetLikedProductStatusUseCase
import javax.inject.Inject

class FavoritesViewModel @Inject constructor(
getFavoritesUseCase: GetFavoriteProductsUseCase,
private val setLikedProductStatusUseCase: SetLikedProductStatusUseCase,
getFavoriteIds: GetFavoritesIdUseCase,
) : ViewModel() {

fun updateLikedStatus(id: String, isLiked: Boolean) {
viewModelScope.launch(Dispatchers.IO) {
setLikedProductStatusUseCase(id, isLiked)
}
}

private val _productsLiveData =
getFavoritesUseCase().asLiveData(viewModelScope.coroutineContext)
val productsLiveData: LiveData<List<Product>> = _productsLiveData

val favoriteIds = getFavoriteIds().asLiveData(viewModelScope.coroutineContext)
}
19 changes: 19 additions & 0 deletions app/src/main/res/drawable/tab_background_selector.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true">
<inset android:inset="3dp">
<shape android:shape="rectangle">
<solid android:color="@color/white" />
<corners android:radius="@dimen/button_large_corner_radius" />
</shape>
</inset>
</item>
<item android:state_enabled="false">
<inset android:inset="3dp">
<shape android:shape="rectangle">
<solid android:color="@color/transparent" />
<corners android:radius="@dimen/button_large_corner_radius" />
</shape>
</inset>
</item>
</selector>
9 changes: 9 additions & 0 deletions app/src/main/res/drawable/tab_layout_favorites_background.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="true">
<shape android:shape="rectangle">
<solid android:color="@color/text_edit_sign_in_bg_color"/>
<corners android:radius="@dimen/text_input_sign_in_radius"/>
</shape>
</item>
</selector>
2 changes: 1 addition & 1 deletion app/src/main/res/layout/card_view_product.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
type="ru.klauz42.yetanotheronlinestore.presentation.ProductWithImages" />
<variable
name="checkBoxListener"
type="ru.klauz42.yetanotheronlinestore.presentation.catalog.ProductsAdapter.FavoriteCheckListener" />
type="ru.klauz42.yetanotheronlinestore.presentation.ProductsAdapter.FavoriteCheckListener" />
</data>

<androidx.cardview.widget.CardView
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/layout/content_catalog.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
app:layout_constraintEnd_toStartOf="@id/border_end"
app:layout_constraintStart_toEndOf="@id/border_start"
app:layout_constraintTop_toBottomOf="@id/recycler_view_tags"
android:overScrollMode="never"
tools:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
tools:listitem="@layout/card_view_product"
tools:spanCount="2" />
Expand Down
5 changes: 3 additions & 2 deletions app/src/main/res/layout/content_favorites.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view_products"
android:layout_width="0dp"
android:layout_height="0dp"
android:paddingTop="62dp"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/border_end"
app:layout_constraintStart_toEndOf="@id/border_start"
app:layout_constraintTop_toTopOf="parent"
android:paddingTop="19dp"
android:clipToPadding="false"
android:overScrollMode="never"
tools:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
tools:listitem="@layout/card_view_product"
tools:spanCount="2" />
Expand Down
33 changes: 31 additions & 2 deletions app/src/main/res/layout/fragment_favorites.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:background="@color/transparent">

<com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar_layout"
Expand All @@ -17,12 +18,40 @@
layout="@layout/toolbar_favorites"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

<com.google.android.material.tabs.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="42dp"
android:layout_marginTop="4dp"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginHorizontal="16dp"
android:background="@drawable/text_edit_sign_in_background"
app:tabIndicatorHeight="0dp"
app:tabSelectedTextAppearance="@style/SelectedFavoritesTabTextStyle"
app:tabTextAppearance="@style/DefaultFavoritesTabTextStyle"
app:tabTextColor="@color/grey"
app:tabSelectedTextColor="@color/black"
app:tabPadding="3dp"
app:tabRippleColor="@color/transparent"
app:tabBackground="@drawable/tab_background_selector">
<com.google.android.material.tabs.TabItem
android:id="@+id/tabProducts"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/tab_products" />
<com.google.android.material.tabs.TabItem
android:id="@+id/tabBrands"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/tab_brands"/>
</com.google.android.material.tabs.TabLayout>
</com.google.android.material.appbar.AppBarLayout>

<androidx.core.widget.NestedScrollView
android:id="@+id/nested_scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="wrap_content"
android:background="@color/back_primary"
app:layout_behavior="@string/appbar_scrolling_view_behavior">

Expand Down
4 changes: 3 additions & 1 deletion app/src/main/res/values-ru/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
<string name="promotions">Акции</string>
<string name="shopping_cart">Корзина</string>
<string name="destination_main">Главная</string>
<string name="destination_catalog">Катало</string>
<string name="destination_catalog">Каталог</string>
<string name="destination_shopping_cart">Корзина</string>
<string name="destination_promotions">Акции</string>
<string name="destination_profile">Профиль</string>
Expand All @@ -51,4 +51,6 @@
<string name="products_one">%1$d товар</string>
<string name="products_few">%1$d товара</string>
<string name="products_many">%1$d товаров</string>
<string name="tab_products">Продукты</string>
<string name="tab_brands">Бренды</string>
</resources>
Loading

0 comments on commit 50ac8ba

Please sign in to comment.