diff --git a/app/src/main/java/org/sopt/kream/data/datasource/ProductRemoteDataSource.kt b/app/src/main/java/org/sopt/kream/data/datasource/ProductRemoteDataSource.kt index 3c2f06e..32dc1a0 100644 --- a/app/src/main/java/org/sopt/kream/data/datasource/ProductRemoteDataSource.kt +++ b/app/src/main/java/org/sopt/kream/data/datasource/ProductRemoteDataSource.kt @@ -1,6 +1,7 @@ package org.sopt.kream.data.datasource import org.sopt.kream.data.model.response.ResponseProductDetailDto +import org.sopt.kream.data.model.response.ResponseRecommendProductDto import org.sopt.kream.data.model.response.ResponseSearchProductDto import org.sopt.kream.util.base.BaseResponse @@ -8,4 +9,6 @@ interface ProductRemoteDataSource { suspend fun getSearchProduct(findName: String): BaseResponse suspend fun getProductDetail(productId: Int): BaseResponse + + suspend fun getRecommendProduct(memberId: Int): BaseResponse } diff --git a/app/src/main/java/org/sopt/kream/data/datasourceimpl/ProductRemoteDataSourceImpl.kt b/app/src/main/java/org/sopt/kream/data/datasourceimpl/ProductRemoteDataSourceImpl.kt index 9508534..40bc651 100644 --- a/app/src/main/java/org/sopt/kream/data/datasourceimpl/ProductRemoteDataSourceImpl.kt +++ b/app/src/main/java/org/sopt/kream/data/datasourceimpl/ProductRemoteDataSourceImpl.kt @@ -3,6 +3,7 @@ package org.sopt.kream.data.datasourceimpl import org.sopt.kream.data.ServicePool import org.sopt.kream.data.datasource.ProductRemoteDataSource import org.sopt.kream.data.model.response.ResponseProductDetailDto +import org.sopt.kream.data.model.response.ResponseRecommendProductDto import org.sopt.kream.data.model.response.ResponseSearchProductDto import org.sopt.kream.util.base.BaseResponse @@ -12,4 +13,6 @@ class ProductRemoteDataSourceImpl : ProductRemoteDataSource { override suspend fun getSearchProduct(findName: String): BaseResponse = productService.getSearchProduct(findName = findName) override suspend fun getProductDetail(productId: Int): BaseResponse = productService.getProductDetail(productId = productId) + + override suspend fun getRecommendProduct(memberId: Int): BaseResponse = productService.getRecommendProduct(memberId = memberId) } diff --git a/app/src/main/java/org/sopt/kream/data/mapper/RecommendForYouProductResponseDtoMapper.kt b/app/src/main/java/org/sopt/kream/data/mapper/RecommendForYouProductResponseDtoMapper.kt new file mode 100644 index 0000000..5a1fbf9 --- /dev/null +++ b/app/src/main/java/org/sopt/kream/data/mapper/RecommendForYouProductResponseDtoMapper.kt @@ -0,0 +1,13 @@ +package org.sopt.kream.data.mapper + +import org.sopt.kream.data.model.response.ResponseRecommendProductDto.ResponseForYouProductDto +import org.sopt.kream.domain.model.RecommendForYouProductModel + +fun ResponseForYouProductDto.toRecommendForYouProductModel() = + RecommendForYouProductModel( + thumbnailUrl = this.thumbnailUrl, + brandTitle = this.brandTitle, + engTitle = this.engTitle, + price = this.price, + transactionCount = this.transactionCount, + ) diff --git a/app/src/main/java/org/sopt/kream/data/mapper/RecommendJustDroppedProductResponseDtoMapper.kt b/app/src/main/java/org/sopt/kream/data/mapper/RecommendJustDroppedProductResponseDtoMapper.kt new file mode 100644 index 0000000..f1a977c --- /dev/null +++ b/app/src/main/java/org/sopt/kream/data/mapper/RecommendJustDroppedProductResponseDtoMapper.kt @@ -0,0 +1,18 @@ +package org.sopt.kream.data.mapper + +import org.sopt.kream.data.model.response.ResponseRecommendProductDto.ResponseJustDroppedProductDto +import org.sopt.kream.domain.model.RecommendJustDroppedProductModel + +fun ResponseJustDroppedProductDto.toRecommendJustDroppedProductModel() = + RecommendJustDroppedProductModel( + thumbnailUrl = this.thumbnailUrl, + brandTitle = this.brandTitle, + engTitle = this.engTitle, + price = this.price, + transactionCount = this.transactionCount, + isScrap = this.isScrap, + isFast = this.isFast, + isFreeDeliver = this.isFreeDeliver, + isSave = this.isSave, + isCoupon = this.isCoupon, + ) diff --git a/app/src/main/java/org/sopt/kream/data/mapper/ResponseRecommendProductDtoMapper.kt b/app/src/main/java/org/sopt/kream/data/mapper/ResponseRecommendProductDtoMapper.kt new file mode 100644 index 0000000..7b3666d --- /dev/null +++ b/app/src/main/java/org/sopt/kream/data/mapper/ResponseRecommendProductDtoMapper.kt @@ -0,0 +1,10 @@ +package org.sopt.kream.data.mapper + +import org.sopt.kream.data.model.response.ResponseRecommendProductDto +import org.sopt.kream.domain.model.RecommendProductModel + +fun ResponseRecommendProductDto.toRecommendProductModel() = + RecommendProductModel( + recommendForYouProducts = this.forYouList.map { it.toRecommendForYouProductModel() }, + recommendJustDroppedProducts = this.justDropList.map { it.toRecommendJustDroppedProductModel() }, + ) diff --git a/app/src/main/java/org/sopt/kream/data/model/response/ResponseRecommendProductDto.kt b/app/src/main/java/org/sopt/kream/data/model/response/ResponseRecommendProductDto.kt new file mode 100644 index 0000000..936d179 --- /dev/null +++ b/app/src/main/java/org/sopt/kream/data/model/response/ResponseRecommendProductDto.kt @@ -0,0 +1,50 @@ +package org.sopt.kream.data.model.response + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class ResponseRecommendProductDto( + @SerialName("forYouList") + val forYouList: List, + @SerialName("justDropList") + val justDropList: List, +) { + @Serializable + data class ResponseForYouProductDto( + @SerialName("thumbnailUrl") + val thumbnailUrl: String, + @SerialName("brandTitle") + val brandTitle: String, + @SerialName("engTitle") + val engTitle: String, + @SerialName("price") + val price: String, + @SerialName("transactionCount") + val transactionCount: String, + ) + + @Serializable + data class ResponseJustDroppedProductDto( + @SerialName("thumbnailUrl") + val thumbnailUrl: String, + @SerialName("brandTitle") + val brandTitle: String, + @SerialName("engTitle") + val engTitle: String, + @SerialName("price") + val price: String, + @SerialName("transactionCount") + val transactionCount: String, + @SerialName("isScrap") + val isScrap: Boolean, + @SerialName("isFast") + val isFast: Boolean, + @SerialName("isFreeDeliver") + val isFreeDeliver: Boolean, + @SerialName("isSave") + val isSave: Boolean, + @SerialName("isCoupon") + val isCoupon: Boolean, + ) +} diff --git a/app/src/main/java/org/sopt/kream/data/repository/ProductRepositoryImpl.kt b/app/src/main/java/org/sopt/kream/data/repository/ProductRepositoryImpl.kt index 2ada6bd..b60c5a3 100644 --- a/app/src/main/java/org/sopt/kream/data/repository/ProductRepositoryImpl.kt +++ b/app/src/main/java/org/sopt/kream/data/repository/ProductRepositoryImpl.kt @@ -2,8 +2,10 @@ package org.sopt.kream.data.repository import org.sopt.kream.data.datasource.ProductRemoteDataSource import org.sopt.kream.data.mapper.toProductDetailModel +import org.sopt.kream.data.mapper.toRecommendProductModel import org.sopt.kream.data.mapper.toSearchProductModel import org.sopt.kream.domain.model.ProductDetailModel +import org.sopt.kream.domain.model.RecommendProductModel import org.sopt.kream.domain.model.SearchProductModel import org.sopt.kream.domain.repository.ProductRepository @@ -19,4 +21,9 @@ class ProductRepositoryImpl( runCatching { productRemoteDataSource.getProductDetail(productId = productId).data.toProductDetailModel() } + + override suspend fun getRecommendProduct(memberId: Int): Result = + runCatching { + productRemoteDataSource.getRecommendProduct(memberId = memberId).data.toRecommendProductModel() + } } diff --git a/app/src/main/java/org/sopt/kream/data/service/ProductService.kt b/app/src/main/java/org/sopt/kream/data/service/ProductService.kt index 53a1c77..200c576 100644 --- a/app/src/main/java/org/sopt/kream/data/service/ProductService.kt +++ b/app/src/main/java/org/sopt/kream/data/service/ProductService.kt @@ -1,6 +1,7 @@ package org.sopt.kream.data.service import org.sopt.kream.data.model.response.ResponseProductDetailDto +import org.sopt.kream.data.model.response.ResponseRecommendProductDto import org.sopt.kream.data.model.response.ResponseReleaseProductDto import org.sopt.kream.data.model.response.ResponseSearchProductDto import org.sopt.kream.util.base.BaseResponse @@ -35,4 +36,9 @@ interface ProductService { companion object { const val MEMBER_ID = 1 } + + @GET("product/recommend") + suspend fun getRecommendProduct( + @Header("memberId") memberId: Int, + ): BaseResponse } diff --git a/app/src/main/java/org/sopt/kream/domain/model/RecommendForYouProductModel.kt b/app/src/main/java/org/sopt/kream/domain/model/RecommendForYouProductModel.kt new file mode 100644 index 0000000..13029d9 --- /dev/null +++ b/app/src/main/java/org/sopt/kream/domain/model/RecommendForYouProductModel.kt @@ -0,0 +1,9 @@ +package org.sopt.kream.domain.model + +data class RecommendForYouProductModel( + val thumbnailUrl: String, + val brandTitle: String, + val engTitle: String, + val price: String, + val transactionCount: String, +) diff --git a/app/src/main/java/org/sopt/kream/domain/model/RecommendJustDroppedProductModel.kt b/app/src/main/java/org/sopt/kream/domain/model/RecommendJustDroppedProductModel.kt new file mode 100644 index 0000000..f663c55 --- /dev/null +++ b/app/src/main/java/org/sopt/kream/domain/model/RecommendJustDroppedProductModel.kt @@ -0,0 +1,14 @@ +package org.sopt.kream.domain.model + +data class RecommendJustDroppedProductModel( + val thumbnailUrl: String, + val brandTitle: String, + val engTitle: String, + val price: String, + val transactionCount: String, + val isScrap: Boolean, + val isFast: Boolean, + val isFreeDeliver: Boolean, + val isSave: Boolean, + val isCoupon: Boolean, +) diff --git a/app/src/main/java/org/sopt/kream/domain/model/RecommendProductModel.kt b/app/src/main/java/org/sopt/kream/domain/model/RecommendProductModel.kt new file mode 100644 index 0000000..441fe08 --- /dev/null +++ b/app/src/main/java/org/sopt/kream/domain/model/RecommendProductModel.kt @@ -0,0 +1,6 @@ +package org.sopt.kream.domain.model + +data class RecommendProductModel( + val recommendForYouProducts: List, + val recommendJustDroppedProducts: List, +) diff --git a/app/src/main/java/org/sopt/kream/domain/repository/ProductRepository.kt b/app/src/main/java/org/sopt/kream/domain/repository/ProductRepository.kt index 1983c8e..b99f7bc 100644 --- a/app/src/main/java/org/sopt/kream/domain/repository/ProductRepository.kt +++ b/app/src/main/java/org/sopt/kream/domain/repository/ProductRepository.kt @@ -1,10 +1,13 @@ package org.sopt.kream.domain.repository import org.sopt.kream.domain.model.ProductDetailModel +import org.sopt.kream.domain.model.RecommendProductModel import org.sopt.kream.domain.model.SearchProductModel interface ProductRepository { suspend fun getSearchProduct(findName: String): Result suspend fun getProductDetail(productId: Int): Result + + suspend fun getRecommendProduct(memberId: Int): Result } diff --git a/app/src/main/java/org/sopt/kream/presentation/common/ViewModelFactory.kt b/app/src/main/java/org/sopt/kream/presentation/common/ViewModelFactory.kt index 2a31247..1dbff58 100644 --- a/app/src/main/java/org/sopt/kream/presentation/common/ViewModelFactory.kt +++ b/app/src/main/java/org/sopt/kream/presentation/common/ViewModelFactory.kt @@ -7,6 +7,7 @@ import org.sopt.kream.data.datasourceimpl.ProductRemoteDataSourceImpl import org.sopt.kream.data.repository.DummyRepositoryImpl import org.sopt.kream.data.repository.ProductRepositoryImpl import org.sopt.kream.presentation.ui.dummy.DummyViewModel +import org.sopt.kream.presentation.ui.main.home.recommend.RecommendViewModel import org.sopt.kream.presentation.ui.productdetail.ProductDetailViewModel import org.sopt.kream.presentation.ui.search.SearchViewModel @@ -18,6 +19,8 @@ class ViewModelFactory : ViewModelProvider.Factory { return SearchViewModel(ProductRepositoryImpl(ProductRemoteDataSourceImpl())) as T } else if (modelClass.isAssignableFrom(ProductDetailViewModel::class.java)) { return ProductDetailViewModel(ProductRepositoryImpl(ProductRemoteDataSourceImpl())) as T + } else if (modelClass.isAssignableFrom(RecommendViewModel::class.java)) { + return RecommendViewModel(ProductRepositoryImpl(ProductRemoteDataSourceImpl())) as T } throw IllegalArgumentException("Unknown ViewModel Class") } diff --git a/app/src/main/java/org/sopt/kream/presentation/model/InstagramModel.kt b/app/src/main/java/org/sopt/kream/presentation/model/InstagramModel.kt new file mode 100644 index 0000000..b3c642c --- /dev/null +++ b/app/src/main/java/org/sopt/kream/presentation/model/InstagramModel.kt @@ -0,0 +1,8 @@ +package org.sopt.kream.presentation.model + +import androidx.annotation.DrawableRes + +data class InstagramModel( + @DrawableRes val image: Int, + val id: String, +) diff --git a/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendAdvertisementViewHolder.kt b/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendAdvertisementViewHolder.kt new file mode 100644 index 0000000..359aa0c --- /dev/null +++ b/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendAdvertisementViewHolder.kt @@ -0,0 +1,16 @@ +package org.sopt.kream.presentation.ui.main.home.recommend + +import androidx.recyclerview.widget.RecyclerView +import org.sopt.kream.databinding.ItemRecommendAdvertisementBinding + +class RecommendAdvertisementViewHolder( + private val binding: ItemRecommendAdvertisementBinding, +) : RecyclerView.ViewHolder(binding.root) { + fun onBind( + item: Int, + ) { + with(binding) { + ivRecommendAdvertisement.setImageResource(item) + } + } +} diff --git a/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendAdvertisementViewPagerAdapter.kt b/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendAdvertisementViewPagerAdapter.kt new file mode 100644 index 0000000..a6f3788 --- /dev/null +++ b/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendAdvertisementViewPagerAdapter.kt @@ -0,0 +1,27 @@ +package org.sopt.kream.presentation.ui.main.home.recommend + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import org.sopt.kream.databinding.ItemRecommendAdvertisementBinding + +class RecommendAdvertisementViewPagerAdapter(private val data: List) : RecyclerView.Adapter() { + private val item = data + + override fun getItemCount(): Int = item.size + + override fun onBindViewHolder( + holder: RecommendAdvertisementViewHolder, + position: Int, + ) { + holder.onBind(item[position]) + } + + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int, + ): RecommendAdvertisementViewHolder { + val binding = ItemRecommendAdvertisementBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return RecommendAdvertisementViewHolder(binding) + } +} diff --git a/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendCircleMenuAdapter.kt b/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendCircleMenuAdapter.kt new file mode 100644 index 0000000..b1b969e --- /dev/null +++ b/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendCircleMenuAdapter.kt @@ -0,0 +1,29 @@ +package org.sopt.kream.presentation.ui.main.home.recommend + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import org.sopt.kream.databinding.ItemRecommendCircleMenuBinding +import org.sopt.kream.presentation.ui.type.RecommendCircleMenuType +import kotlin.enums.EnumEntries + +class RecommendCircleMenuAdapter(private val list: EnumEntries) : RecyclerView.Adapter() { + private val item = list + + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int, + ): RecommendCircleMenuViewHolder { + val binding = ItemRecommendCircleMenuBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return RecommendCircleMenuViewHolder(binding) + } + + override fun onBindViewHolder( + holder: RecommendCircleMenuViewHolder, + position: Int, + ) { + holder.onBind(item.get(position)) + } + + override fun getItemCount(): Int = list.size +} diff --git a/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendCircleMenuViewHolder.kt b/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendCircleMenuViewHolder.kt new file mode 100644 index 0000000..45c3e32 --- /dev/null +++ b/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendCircleMenuViewHolder.kt @@ -0,0 +1,18 @@ +package org.sopt.kream.presentation.ui.main.home.recommend + +import androidx.recyclerview.widget.RecyclerView +import org.sopt.kream.databinding.ItemRecommendCircleMenuBinding +import org.sopt.kream.presentation.ui.type.RecommendCircleMenuType + +class RecommendCircleMenuViewHolder( + private val binding: ItemRecommendCircleMenuBinding, +) : RecyclerView.ViewHolder(binding.root) { + fun onBind( + item: RecommendCircleMenuType, + ) { + with(binding) { + ivCircleMenu.setImageResource(item.image) + tvCircleMenuTitle.setText(item.menu) + } + } +} diff --git a/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendForYouAdapter.kt b/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendForYouAdapter.kt new file mode 100644 index 0000000..87973d8 --- /dev/null +++ b/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendForYouAdapter.kt @@ -0,0 +1,44 @@ +package org.sopt.kream.presentation.ui.main.home.recommend + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.ListAdapter +import org.sopt.kream.databinding.ItemRecommendForYouProductBinding +import org.sopt.kream.domain.model.RecommendForYouProductModel +import org.sopt.kream.util.view.ItemDiffCallback + +class RecommendForYouAdapter( + private val navigateToProductDetail: (Int) -> Unit, + private val page: Int, +) : ListAdapter< + RecommendForYouProductModel, + RecommendForYouViewHolder, + >( + ItemDiffCallback( + onContentsTheSame = { old, new -> old == new }, + onItemsTheSame = { old, new -> old.engTitle == new.engTitle }, + ), + ) { + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int, + ): RecommendForYouViewHolder = + RecommendForYouViewHolder( + ItemRecommendForYouProductBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false, + ), + navigateToProductDetail, + ) + + override fun onBindViewHolder( + holder: RecommendForYouViewHolder, + position: Int, + ) { + holder.onBind( + recommendForYouProductModel = currentList[page * 6 + position], + position = position, + ) + } +} diff --git a/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendForYouViewHolder.kt b/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendForYouViewHolder.kt new file mode 100644 index 0000000..711942e --- /dev/null +++ b/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendForYouViewHolder.kt @@ -0,0 +1,26 @@ +package org.sopt.kream.presentation.ui.main.home.recommend + +import android.view.View +import androidx.recyclerview.widget.RecyclerView +import coil.load +import org.sopt.kream.databinding.ItemRecommendForYouProductBinding +import org.sopt.kream.domain.model.RecommendForYouProductModel + +class RecommendForYouViewHolder( + private val binding: ItemRecommendForYouProductBinding, + private val navigateToProductDetail: (Int) -> Unit, +) : RecyclerView.ViewHolder(binding.root) { + fun onBind( + recommendForYouProductModel: RecommendForYouProductModel, + position: Int, + ) { + with(binding) { + ivForYou.load(recommendForYouProductModel.thumbnailUrl) + tvForYouBrand.text = recommendForYouProductModel.brandTitle + tvForYouProduct.text = recommendForYouProductModel.engTitle + tvForYouPrice.text = recommendForYouProductModel.price + tvForYouTransaction.text = recommendForYouProductModel.transactionCount + tvForYouSee.visibility = View.INVISIBLE + } + } +} diff --git a/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendForYouViewPagerAdapter.kt b/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendForYouViewPagerAdapter.kt new file mode 100644 index 0000000..6458be4 --- /dev/null +++ b/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendForYouViewPagerAdapter.kt @@ -0,0 +1,36 @@ +package org.sopt.kream.presentation.ui.main.home.recommend + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.ListAdapter +import org.sopt.kream.databinding.ItemRecommendForYouBinding +import org.sopt.kream.domain.model.RecommendForYouProductModel +import org.sopt.kream.util.view.ItemDiffCallback + +class RecommendForYouViewPagerAdapter( + private val navigateToProductDetail: (Int) -> Unit, + private val navigateToSearch: (String) -> Unit, +) : ListAdapter< + List, + RecommendForYouViewPagerViewHolder, + >( + ItemDiffCallback>( + onContentsTheSame = { old, new -> old == new }, + onItemsTheSame = { old, new -> old[0].engTitle == new[0].engTitle }, + ), + ) { + override fun onBindViewHolder( + holder: RecommendForYouViewPagerViewHolder, + position: Int, + ) { + holder.onBind(currentList[position], position) + } + + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int, + ): RecommendForYouViewPagerViewHolder { + val binding = ItemRecommendForYouBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return RecommendForYouViewPagerViewHolder(binding, navigateToProductDetail, navigateToSearch) + } +} diff --git a/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendForYouViewPagerViewHolder.kt b/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendForYouViewPagerViewHolder.kt new file mode 100644 index 0000000..e1b82be --- /dev/null +++ b/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendForYouViewPagerViewHolder.kt @@ -0,0 +1,24 @@ +package org.sopt.kream.presentation.ui.main.home.recommend + +import androidx.recyclerview.widget.RecyclerView +import org.sopt.kream.databinding.ItemRecommendForYouBinding +import org.sopt.kream.domain.model.RecommendForYouProductModel + +class RecommendForYouViewPagerViewHolder( + private val binding: ItemRecommendForYouBinding, + private val navigateToProductDetail: (Int) -> Unit, + private val navigateToSearch: (String) -> Unit, +) : RecyclerView.ViewHolder(binding.root) { + fun onBind( + item: List, + position: Int, + ) { + val recommendForYouAdapter = RecommendForYouAdapter(navigateToProductDetail, position) + binding.rvForYouContent.adapter = recommendForYouAdapter + recommendForYouAdapter.submitList(item) + + binding.clRecommendForYouMore.setOnClickListener { + navigateToSearch("아디다스") + } + } +} diff --git a/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendFragment.kt b/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendFragment.kt index bf9a8d2..127371f 100644 --- a/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendFragment.kt +++ b/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendFragment.kt @@ -2,24 +2,113 @@ package org.sopt.kream.presentation.ui.main.home.recommend import android.os.Bundle import android.view.View +import androidx.core.os.bundleOf +import androidx.fragment.app.viewModels +import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController +import com.google.android.material.bottomsheet.BottomSheetDialog +import com.google.android.material.tabs.TabLayoutMediator +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import org.sopt.kream.R import org.sopt.kream.databinding.FragmentRecommendBinding +import org.sopt.kream.presentation.common.ViewModelFactory +import org.sopt.kream.presentation.ui.type.RecommendAdvertisementType +import org.sopt.kream.presentation.ui.type.RecommendCircleMenuType import org.sopt.kream.util.base.BindingFragment +import org.sopt.kream.util.view.UiState class RecommendFragment : BindingFragment({ FragmentRecommendBinding.inflate(it) }) { + private val recommendViewModel: RecommendViewModel by viewModels { ViewModelFactory() } + private var memberId: Int = 1 + private var forYouTotalPage: Int = 1 + private lateinit var advertisementAdapter: RecommendAdvertisementViewPagerAdapter + private lateinit var circleMenuAdapter: RecommendCircleMenuAdapter + private lateinit var forYouAdapter: RecommendForYouViewPagerAdapter + private lateinit var justDroppedAdapter: RecommendJustDroppedAdapter + private lateinit var styleAdapter: RecommendStyleAdapter + override fun onViewCreated( view: View, savedInstanceState: Bundle?, ) { super.onViewCreated(view, savedInstanceState) - initBtnProductDetail() + recommendViewModel.getRecommendProduct(memberId = memberId) + initAdapter() + setBottomSheet() + collectRecommendProductState() + setForYouPage() + } + + private fun initAdapter() { + advertisementAdapter = RecommendAdvertisementViewPagerAdapter(RecommendAdvertisementType.RECOMMEND_ADVERTISEMENT.advertisementList) + circleMenuAdapter = RecommendCircleMenuAdapter(RecommendCircleMenuType.entries) + forYouAdapter = RecommendForYouViewPagerAdapter(::navigateToProductDetail, ::navigateToSearch) + justDroppedAdapter = RecommendJustDroppedAdapter(::navigateToProductDetail) + styleAdapter = RecommendStyleAdapter() + + with(binding) { + vpRecommendAdvertisement.adapter = advertisementAdapter + TabLayoutMediator(includeKreamIndicator.tabKreamIndicator, vpRecommendAdvertisement) { tab, position -> + }.attach() + rvRecommendCircleMenu.adapter = circleMenuAdapter + vpRecommendForYouContent.adapter = forYouAdapter + rvRecommendJustDroppedContent.adapter = justDroppedAdapter + rvRecommendStyle.adapter = styleAdapter + } + } + + private fun collectRecommendProductState() { + recommendViewModel.recommendProductState.flowWithLifecycle(viewLifecycleOwner.lifecycle) + .onEach { recommendProductState -> + when (recommendProductState) { + is UiState.Success -> { + with(recommendProductState.data) { + forYouTotalPage = recommendForYouProducts.size / 6 + forYouAdapter.submitList(listOf(recommendForYouProducts)) + justDroppedAdapter.submitList(recommendJustDroppedProducts) + styleAdapter.submitList(recommendViewModel.instagramList) + } + } + + else -> Unit + } + }.launchIn(viewLifecycleOwner.lifecycleScope) + } + + fun setForYouPage() { + with(binding) { + tvRecommendForYouTotalPage.text = forYouTotalPage.toString() + tvRecommendForYouCurrentPage.text = (vpRecommendForYouContent.currentItem + 1).toString() + } } - private fun initBtnProductDetail() { - binding.btnToProductDetail.setOnClickListener { - findNavController().navigate(R.id.action_recommend_to_product_detail) + private fun navigateToProductDetail(productId: Int) { + findNavController().navigate(R.id.action_recommend_to_product_detail, bundleOf(PRODUCT_ID to productId)) + } + + private fun navigateToSearch(searchKeyword: String) { + findNavController().navigate(R.id.action_home_to_search, bundleOf(SEARCH_WORD to searchKeyword)) + } + + private fun setBottomSheet() { + val forYouBottomSheet = BottomSheetDialog(requireContext()) + forYouBottomSheet.setContentView( + layoutInflater.inflate( + R.layout.fragment_recommend_for_you_bottom_sheet, + null, + ), + ) + + binding.ivRecommendForYouEtc.setOnClickListener { + forYouBottomSheet.show() } } + + companion object { + const val PRODUCT_ID = "productId" + const val SEARCH_WORD = "searchWord" + } } diff --git a/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendJustDroppedAdapter.kt b/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendJustDroppedAdapter.kt new file mode 100644 index 0000000..50df1d8 --- /dev/null +++ b/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendJustDroppedAdapter.kt @@ -0,0 +1,43 @@ +package org.sopt.kream.presentation.ui.main.home.recommend + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.ListAdapter +import org.sopt.kream.databinding.ItemRecommendJustDroppedProductBinding +import org.sopt.kream.domain.model.RecommendJustDroppedProductModel +import org.sopt.kream.util.view.ItemDiffCallback + +class RecommendJustDroppedAdapter( + private val navigateToProductDetail: (Int) -> Unit, +) : ListAdapter< + RecommendJustDroppedProductModel, + RecommendJustDroppedViewHolder, + >( + ItemDiffCallback( + onContentsTheSame = { old, new -> old == new }, + onItemsTheSame = { old, new -> old.engTitle == new.engTitle }, + ), + ) { + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int, + ): RecommendJustDroppedViewHolder = + RecommendJustDroppedViewHolder( + ItemRecommendJustDroppedProductBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false, + ), + navigateToProductDetail, + ) + + override fun onBindViewHolder( + holder: RecommendJustDroppedViewHolder, + position: Int, + ) { + holder.onBind( + recommendJustDroppedProductModel = currentList[position], + position = position, + ) + } +} diff --git a/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendJustDroppedViewHolder.kt b/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendJustDroppedViewHolder.kt new file mode 100644 index 0000000..fa97b61 --- /dev/null +++ b/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendJustDroppedViewHolder.kt @@ -0,0 +1,51 @@ +package org.sopt.kream.presentation.ui.main.home.recommend + +import android.view.View +import androidx.recyclerview.widget.RecyclerView +import coil.load +import org.sopt.kream.R +import org.sopt.kream.databinding.ItemRecommendJustDroppedProductBinding +import org.sopt.kream.domain.model.RecommendJustDroppedProductModel + +class RecommendJustDroppedViewHolder( + private val binding: ItemRecommendJustDroppedProductBinding, + private val navigateToProductDetail: (Int) -> Unit, +) : RecyclerView.ViewHolder(binding.root) { + fun onBind( + recommendJustDroppedProductModel: RecommendJustDroppedProductModel, + position: Int, + ) { + with(binding) { + ivJustDroppedProduct.load(recommendJustDroppedProductModel.thumbnailUrl) + tvJustDroppedProductBrand.text = recommendJustDroppedProductModel.brandTitle + tvJustDroppedProductName.text = recommendJustDroppedProductModel.engTitle + tvJustDroppedProductPrice.text = recommendJustDroppedProductModel.price + tvJustDroppedProductTransaction.text = recommendJustDroppedProductModel.transactionCount + ivJustDroppedProductScrap.setImageResource( + if (recommendJustDroppedProductModel.isScrap) { + R.drawable.ic_saved_2_on_24 + } else { + R.drawable.ic_saved_2_off_24 + }, + ) + tvJustDroppedProductCoupon.visibility = + if (recommendJustDroppedProductModel.isCoupon) { + View.VISIBLE + } else { + View.GONE + } + tvJustDroppedProductSave.visibility = + if (recommendJustDroppedProductModel.isSave) { + View.VISIBLE + } else { + View.GONE + } + tvJustDroppedProductFreeDeliver.visibility = + if (recommendJustDroppedProductModel.isFreeDeliver) { + View.VISIBLE + } else { + View.INVISIBLE + } + } + } +} diff --git a/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendStyleAdapter.kt b/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendStyleAdapter.kt new file mode 100644 index 0000000..4ed0dfa --- /dev/null +++ b/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendStyleAdapter.kt @@ -0,0 +1,40 @@ +package org.sopt.kream.presentation.ui.main.home.recommend + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.ListAdapter +import org.sopt.kream.databinding.ItemRecommendStyleBinding +import org.sopt.kream.presentation.model.InstagramModel +import org.sopt.kream.util.view.ItemDiffCallback + +class RecommendStyleAdapter() : ListAdapter< + InstagramModel, + RecommendStyleViewHolder, + >( + ItemDiffCallback( + onContentsTheSame = { old, new -> old == new }, + onItemsTheSame = { old, new -> old.id == new.id }, + ), +) { + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int, + ): RecommendStyleViewHolder = + RecommendStyleViewHolder( + ItemRecommendStyleBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false, + ), + ) + + override fun onBindViewHolder( + holder: RecommendStyleViewHolder, + position: Int, + ) { + holder.onBind( + recommendInstagram = currentList[position], + position = position, + ) + } +} diff --git a/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendStyleViewHolder.kt b/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendStyleViewHolder.kt new file mode 100644 index 0000000..2c90c9f --- /dev/null +++ b/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendStyleViewHolder.kt @@ -0,0 +1,19 @@ +package org.sopt.kream.presentation.ui.main.home.recommend + +import androidx.recyclerview.widget.RecyclerView +import org.sopt.kream.databinding.ItemRecommendStyleBinding +import org.sopt.kream.presentation.model.InstagramModel + +class RecommendStyleViewHolder( + private val binding: ItemRecommendStyleBinding, +) : RecyclerView.ViewHolder(binding.root) { + fun onBind( + recommendInstagram: InstagramModel, + position: Int, + ) { + with(binding) { + ivStyle.setImageResource(recommendInstagram.image) + tvStyleId.text = recommendInstagram.id + } + } +} diff --git a/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendViewModel.kt b/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendViewModel.kt new file mode 100644 index 0000000..39e5069 --- /dev/null +++ b/app/src/main/java/org/sopt/kream/presentation/ui/main/home/recommend/RecommendViewModel.kt @@ -0,0 +1,55 @@ +package org.sopt.kream.presentation.ui.main.home.recommend + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch +import org.sopt.kream.R +import org.sopt.kream.domain.model.RecommendProductModel +import org.sopt.kream.domain.repository.ProductRepository +import org.sopt.kream.presentation.model.InstagramModel +import org.sopt.kream.util.view.UiState + +class RecommendViewModel( + private val productRepository: ProductRepository, +) : ViewModel() { + private val _recommendProductState = MutableStateFlow>(UiState.Empty) + val recommendProductState get() = _recommendProductState.asStateFlow() + + private val _instagramList = + listOf( + InstagramModel( + image = R.drawable.img_recommend_style_01, + id = "@zzz.myam", + ), + InstagramModel( + image = R.drawable.img_recommend_style_02, + id = "@minimin.0.0", + ), + InstagramModel( + image = R.drawable.img_recommend_style_03, + id = "@jyeo._.ni__", + ), + InstagramModel( + image = R.drawable.img_recommend_style_04, + id = "@_minsuk__", + ), + InstagramModel( + image = R.drawable.img_recommend_style_05, + id = "@hyobeen_0926", + ), + ) + val instagramList get() = _instagramList + + fun getRecommendProduct(memberId: Int) { + viewModelScope.launch { + _recommendProductState.value = UiState.Loading + productRepository.getRecommendProduct(memberId = memberId).onSuccess { recommendProductModel -> + _recommendProductState.value = UiState.Success(recommendProductModel) + }.onFailure { exception: Throwable -> + _recommendProductState.value = UiState.Error(exception.message) + } + } + } +} diff --git a/app/src/main/java/org/sopt/kream/presentation/ui/type/RecommendAdvertisementType.kt b/app/src/main/java/org/sopt/kream/presentation/ui/type/RecommendAdvertisementType.kt new file mode 100644 index 0000000..bb33efb --- /dev/null +++ b/app/src/main/java/org/sopt/kream/presentation/ui/type/RecommendAdvertisementType.kt @@ -0,0 +1,16 @@ +package org.sopt.kream.presentation.ui.type + +import org.sopt.kream.R + +enum class RecommendAdvertisementType( + val advertisementList: List, +) { + RECOMMEND_ADVERTISEMENT( + listOf( + R.drawable.img_recommend_ad_01, + R.drawable.img_recommend_ad_02, + R.drawable.img_recommend_ad_03, + R.drawable.img_recommend_ad_04, + ), + ), +} diff --git a/app/src/main/java/org/sopt/kream/presentation/ui/type/RecommendCircleMenuType.kt b/app/src/main/java/org/sopt/kream/presentation/ui/type/RecommendCircleMenuType.kt new file mode 100644 index 0000000..1c5323e --- /dev/null +++ b/app/src/main/java/org/sopt/kream/presentation/ui/type/RecommendCircleMenuType.kt @@ -0,0 +1,50 @@ +package org.sopt.kream.presentation.ui.type + +import androidx.annotation.DrawableRes +import org.sopt.kream.R + +enum class RecommendCircleMenuType( + @DrawableRes val image: Int, + val menu: Int, +) { + KREAM_CARD( + image = R.drawable.view2_img_cricle_01, + menu = R.string.type_circle_menu_kream_card, + ), + CREAM_DRAW( + image = R.drawable.view2_img_cricle_02, + menu = R.string.type_circle_menu_kream_draw, + ), + MAN_RECOMMEND( + image = R.drawable.view2_img_cricle_03, + menu = R.string.type_circle_menu_man_recommend, + ), + WOMAN_RECOMMEND( + image = R.drawable.view2_img_cricle_04, + menu = R.string.type_circle_menu_woman_recommend, + ), + NEW_RECOMMEND( + image = R.drawable.view2_img_cricle_05, + menu = R.string.type_circle_menu_new_recommend, + ), + UNDER_PRICE( + image = R.drawable.view2_img_cricle_06, + menu = R.string.type_circle_menu_under_price, + ), + SPRING_SALE( + image = R.drawable.view2_img_cricle_07, + menu = R.string.type_circle_menu_spring_sale, + ), + CHANEL( + image = R.drawable.view2_img_cricle_08, + menu = R.string.type_circle_menu_chanel, + ), + MAY( + image = R.drawable.view2_img_cricle_09, + menu = R.string.type_circle_menu_may, + ), + SONY_SUPREME( + image = R.drawable.view2_img_cricle_10, + menu = R.string.type_circle_menu_sony_supreme, + ), +} diff --git a/app/src/main/res/drawable/ic_delete_black_14.xml b/app/src/main/res/drawable/ic_delete_black_14.xml new file mode 100644 index 0000000..d425c73 --- /dev/null +++ b/app/src/main/res/drawable/ic_delete_black_14.xml @@ -0,0 +1,12 @@ + + + diff --git a/app/src/main/res/drawable/ic_etc_back_17.xml b/app/src/main/res/drawable/ic_etc_back_17.xml new file mode 100644 index 0000000..7e0e7fb --- /dev/null +++ b/app/src/main/res/drawable/ic_etc_back_17.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_etc_next_16.xml b/app/src/main/res/drawable/ic_etc_next_17.xml similarity index 70% rename from app/src/main/res/drawable/ic_etc_next_16.xml rename to app/src/main/res/drawable/ic_etc_next_17.xml index 3b101b4..55190d4 100644 --- a/app/src/main/res/drawable/ic_etc_next_16.xml +++ b/app/src/main/res/drawable/ic_etc_next_17.xml @@ -1,8 +1,8 @@ + android:width="17dp" + android:height="17dp" + android:viewportWidth="17" + android:viewportHeight="17"> + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_border_radius_4.xml b/app/src/main/res/drawable/shape_border_radius_4.xml new file mode 100644 index 0000000..750af11 --- /dev/null +++ b/app/src/main/res/drawable/shape_border_radius_4.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_border_radius_7.xml b/app/src/main/res/drawable/shape_border_radius_7.xml new file mode 100644 index 0000000..d89a8fa --- /dev/null +++ b/app/src/main/res/drawable/shape_border_radius_7.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_line_gray04_radius_5.xml b/app/src/main/res/drawable/shape_line_gray04_radius_5.xml new file mode 100644 index 0000000..5267a5f --- /dev/null +++ b/app/src/main/res/drawable/shape_line_gray04_radius_5.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_oval.xml b/app/src/main/res/drawable/shape_oval.xml new file mode 100644 index 0000000..2c18f17 --- /dev/null +++ b/app/src/main/res/drawable/shape_oval.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_recommend_bottom_sheet_top_radius_10.xml b/app/src/main/res/drawable/shape_recommend_bottom_sheet_top_radius_10.xml new file mode 100644 index 0000000..b84db73 --- /dev/null +++ b/app/src/main/res/drawable/shape_recommend_bottom_sheet_top_radius_10.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/view2_img_cricle_01.png b/app/src/main/res/drawable/view2_img_cricle_01.png new file mode 100644 index 0000000..f9464c6 Binary files /dev/null and b/app/src/main/res/drawable/view2_img_cricle_01.png differ diff --git a/app/src/main/res/drawable/view2_img_cricle_02.png b/app/src/main/res/drawable/view2_img_cricle_02.png new file mode 100644 index 0000000..a8af7e7 Binary files /dev/null and b/app/src/main/res/drawable/view2_img_cricle_02.png differ diff --git a/app/src/main/res/drawable/view2_img_cricle_03.png b/app/src/main/res/drawable/view2_img_cricle_03.png new file mode 100644 index 0000000..a930303 Binary files /dev/null and b/app/src/main/res/drawable/view2_img_cricle_03.png differ diff --git a/app/src/main/res/drawable/view2_img_cricle_04.png b/app/src/main/res/drawable/view2_img_cricle_04.png new file mode 100644 index 0000000..e3ff5b9 Binary files /dev/null and b/app/src/main/res/drawable/view2_img_cricle_04.png differ diff --git a/app/src/main/res/drawable/view2_img_cricle_05.png b/app/src/main/res/drawable/view2_img_cricle_05.png new file mode 100644 index 0000000..2000c08 Binary files /dev/null and b/app/src/main/res/drawable/view2_img_cricle_05.png differ diff --git a/app/src/main/res/drawable/view2_img_cricle_06.png b/app/src/main/res/drawable/view2_img_cricle_06.png new file mode 100644 index 0000000..2d08314 Binary files /dev/null and b/app/src/main/res/drawable/view2_img_cricle_06.png differ diff --git a/app/src/main/res/drawable/view2_img_cricle_07.png b/app/src/main/res/drawable/view2_img_cricle_07.png new file mode 100644 index 0000000..b635870 Binary files /dev/null and b/app/src/main/res/drawable/view2_img_cricle_07.png differ diff --git a/app/src/main/res/drawable/view2_img_cricle_08.png b/app/src/main/res/drawable/view2_img_cricle_08.png new file mode 100644 index 0000000..06499e0 Binary files /dev/null and b/app/src/main/res/drawable/view2_img_cricle_08.png differ diff --git a/app/src/main/res/drawable/view2_img_cricle_09.png b/app/src/main/res/drawable/view2_img_cricle_09.png new file mode 100644 index 0000000..9560f11 Binary files /dev/null and b/app/src/main/res/drawable/view2_img_cricle_09.png differ diff --git a/app/src/main/res/drawable/view2_img_cricle_10.png b/app/src/main/res/drawable/view2_img_cricle_10.png new file mode 100644 index 0000000..7687512 Binary files /dev/null and b/app/src/main/res/drawable/view2_img_cricle_10.png differ diff --git a/app/src/main/res/layout/fragment_recommend.xml b/app/src/main/res/layout/fragment_recommend.xml index 150e013..dc63b1a 100644 --- a/app/src/main/res/layout/fragment_recommend.xml +++ b/app/src/main/res/layout/fragment_recommend.xml @@ -1,27 +1,268 @@ - - - -