Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[둘리] 1, 2단계 쇼핑 장바구니 제출합니다. #20

Merged
merged 52 commits into from
May 14, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
d7cfbc7
docs: 기능 목록 작성
hyemdooly May 9, 2023
2584164
feat: Domain 모듈 생성
hyemdooly May 9, 2023
5ae38d7
feat: 상훔 목록을 관리하는 Repository Interface 생성
hyunji1203 May 9, 2023
d9c9fb4
feat: 장바구니를 관리하는 CartRepository Interface 생성
hyemdooly May 9, 2023
2676ad4
feat: 최근 본 상품 목록을 관리하는 Repository Interface 생성
hyunji1203 May 9, 2023
e65d164
feat: 상품 정보를 담는 Product, 가격을 담는 Price 생성
hyemdooly May 9, 2023
42ca12e
feat: 상품의 정보를 리턴하는 ProductMockRepository 구현
hyunji1203 May 9, 2023
25d3651
feat: ProductListActivity 이미지 제외한 화면 구성 완료
hyemdooly May 9, 2023
ccb8fc1
feat: 메인 색깔 변경
hyemdooly May 9, 2023
52e3bc5
feat: 액션 바에 장바구니 바로가기 메뉴 추가
hyunji1203 May 9, 2023
143e31c
feat: 장바구니 화면의 액션바 안의 백버튼을 누르면 뒤로 이동하는 기능 추가
hyunji1203 May 9, 2023
633ef00
feat: 상품 이미지 Glide 적용
hyemdooly May 9, 2023
1f24cee
refactor: 패키지 이동
hyemdooly May 9, 2023
8dc0249
refactor: ProductListActivity MVP 패턴 적용
hyunji1203 May 9, 2023
7cca774
refactor: 장바구니 아이템 구현
hyemdooly May 10, 2023
f45ba61
feat: 장바구니 리스트 뷰 구현
hyunji1203 May 10, 2023
b58de23
feat: 상품 상세 뷰 구현
hyemdooly May 10, 2023
964767b
feat: 장바구니 DataBase 생성 로직 구현
hyunji1203 May 10, 2023
c2fc7af
feat: CartDbRepository 구현
hyemdooly May 10, 2023
78d597e
feat: ProductList OnClick 추가, DB 적용
hyemdooly May 10, 2023
605c01c
feat: 상품 상세 페이지 액션 바에 뒤로가기 버튼 추가
hyunji1203 May 10, 2023
0aaecc9
refactor: MVP 패턴으로 변경
hyunji1203 May 10, 2023
a2eb1fe
feat: 최근 본 상품 리스트 추가
hyemdooly May 10, 2023
8da62a1
feat: 최근 본 상품 datebase 기능 추가
hyunji1203 May 10, 2023
9270d70
feat: CartProduct DB 적용, 삭제 기능 추가
hyemdooly May 10, 2023
3136470
refactor: find 리턴값 수정
hyemdooly May 11, 2023
3206da5
refactor: notifyItemRemoved 적용
hyemdooly May 11, 2023
b83401a
feat: 페이지네이션 구현 중
hyunji1203 May 11, 2023
f858baa
feat: 더보기 구현중
hyemdooly May 11, 2023
ea478c3
feat: 더보기 구현 완료
hyemdooly May 11, 2023
a143b22
feat: 카트 페이지네이션 구현 중
hyunji1203 May 11, 2023
3bf6ad5
test: ProductListPresenterTest 작성
hyemdooly May 11, 2023
315a8ce
refactor: ProductDetailPresenter cartRepository 추상화 적용
hyemdooly May 11, 2023
9599973
test: ProductDetailPresenter 테스트 추가
hyunji1203 May 11, 2023
8fd10a2
refactor: Pagination 적용중
hyemdooly May 11, 2023
08ccb68
refactor: Pagination 적용중
hyemdooly May 11, 2023
1330e9d
feat: Pagination Button onClick 수정
hyemdooly May 11, 2023
65cee56
fix: 페이지가 넘어가지 않는 오류 수정
hyemdooly May 12, 2023
ef8e6c9
refactor: onCreate 코드 함수 분리
hyemdooly May 12, 2023
bb1c417
refactor: 너무 긴 코드 개행
hyemdooly May 12, 2023
bf285df
refactor: 상수 선언
hyemdooly May 12, 2023
377f7b3
refactor: constraintLayout 자식 뷰 match_parent -> 0dp로 수정
hyemdooly May 12, 2023
11cfe23
refactor: xml formatting
hyemdooly May 12, 2023
4fbc614
refactor: asSequence 사용하여 map 호출 시간 단축
hyemdooly May 12, 2023
68d3fdb
refactor: Next, Prev Pagination 분리
hyemdooly May 13, 2023
5e57cd5
refactor: CartPageStatus 생성, CartAdapter 리팩터링
hyemdooly May 13, 2023
023a703
refactor: LayoutManager xml로 이동
hyemdooly May 13, 2023
d3a1f22
refactor: 상수 선언
hyemdooly May 13, 2023
41b0e02
refactor: CartAdapter에서 CartViewItem 사용하여 Items를 하나로 묶음
hyemdooly May 13, 2023
5eb3cfc
refactor: ProductListActivity 리사이클러뷰 리팩터링
hyemdooly May 13, 2023
6e8f667
test: package 이동, ProductListPresenterTest 수정, CartPresenterTest 작성
hyemdooly May 13, 2023
0b23bbd
refactor: 테스트 코드에서 whildcard import 수정
hyemdooly May 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
refactor: ProductListActivity 리사이클러뷰 리팩터링
  • Loading branch information
hyemdooly committed May 13, 2023
commit 5eb3cfc1c87c47afa4a6a5f4ef9fc8137b8d0ec3
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package woowacourse.shopping.view.cart
package woowacourse.shopping.model

import woowacourse.shopping.model.NextPagination
import woowacourse.shopping.model.PrevPagination
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package woowacourse.shopping.view.productlist
package woowacourse.shopping.model

import woowacourse.shopping.model.NextPagination
import woowacourse.shopping.domain.Product
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package woowacourse.shopping.view.cart
import woowacourse.shopping.domain.CartProduct
import woowacourse.shopping.domain.CartRepository
import woowacourse.shopping.domain.ProductRepository
import woowacourse.shopping.model.CartPagination
import woowacourse.shopping.model.toUiModel

class CartPresenter(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import woowacourse.shopping.model.ProductModel
import woowacourse.shopping.util.PriceFormatter
import woowacourse.shopping.util.getParcelableCompat
import woowacourse.shopping.view.cart.CartActivity
import woowacourse.shopping.view.productlist.ProductListActivity.Companion.ID
import woowacourse.shopping.view.productlist.ProductListActivity.Companion.RESULT_VIEWED

class ProductDetailActivity : AppCompatActivity(), ProductDetailContract.View {
private lateinit var binding: ActivityProductDetailBinding
Expand All @@ -32,6 +34,7 @@ class ProductDetailActivity : AppCompatActivity(), ProductDetailContract.View {
return
}
setUpInitView(product)
setUpResult(product.id)
presenter.updateRecentViewedProducts(product.id)
}

Expand All @@ -57,6 +60,11 @@ class ProductDetailActivity : AppCompatActivity(), ProductDetailContract.View {
getString(R.string.korean_won, PriceFormatter.format(product.price))
}

private fun setUpResult(id: Int) {
intent.putExtra(ID, id)
setResult(RESULT_VIEWED, intent)
}

override fun startCartActivity() {
val intent = CartActivity.newIntent(this)
startActivity(intent)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import android.os.Bundle
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.GridLayoutManager
import woowacourse.shopping.R
Expand All @@ -18,12 +20,20 @@ import woowacourse.shopping.view.productdetail.ProductDetailActivity
class ProductListActivity : AppCompatActivity(), ProductListContract.View {
private lateinit var binding: ActivityProductListBinding
private lateinit var presenter: ProductListContract.Presenter
private val resultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if(it.resultCode == RESULT_VIEWED) {
val id = it.data?.getIntExtra("id", -1)
presenter.updateRecentViewed(id ?: -1)
}
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setUpBinding()
setContentView(binding.root)
setUpPresenter()
setUpActionBar()
presenter.fetchProducts()
}

private fun setUpBinding() {
Expand All @@ -39,21 +49,12 @@ class ProductListActivity : AppCompatActivity(), ProductListContract.View {
supportActionBar?.setDisplayShowCustomEnabled(true)
}

override fun onResume() {
super.onResume()
presenter.fetchProducts()
}

override fun showProducts(
recentViewedProducts: List<ProductModel>,
products: List<ProductModel>
) {
override fun showProducts(items: List<ProductListViewItem>) {
val gridLayoutManager = GridLayoutManagerWrapper(this, 2)
gridLayoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
val isHeader = recentViewedProducts.isNotEmpty() && position == 0
val isFooter =
if (recentViewedProducts.isNotEmpty()) position == products.size + 1 else position == products.size
val isHeader = items[position].type == ProductListViewType.RECENT_VIEWED_ITEM
val isFooter = items[position].type == ProductListViewType.SHOW_MORE_ITEM
return if (isHeader || isFooter) {
HEADER_FOOTER_SPAN
} else {
Expand All @@ -63,8 +64,7 @@ class ProductListActivity : AppCompatActivity(), ProductListContract.View {
}
binding.gridProducts.layoutManager = gridLayoutManager
binding.gridProducts.adapter = ProductListAdapter(
recentViewedProducts,
products,
items,
object : ProductListAdapter.OnItemClick {
override fun onProductClick(product: ProductModel) {
showProductDetail(product)
Expand All @@ -81,9 +81,13 @@ class ProductListActivity : AppCompatActivity(), ProductListContract.View {
binding.gridProducts.adapter?.notifyItemRangeInserted(position, size)
}

override fun notifyRecentViewedChanged() {
binding.gridProducts.adapter?.notifyItemChanged(0)
}

private fun showProductDetail(product: ProductModel) {
val intent = ProductDetailActivity.newIntent(binding.root.context, product)
startActivity(intent)
resultLauncher.launch(intent)
}

override fun onCreateOptionsMenu(menu: Menu?): Boolean {
Expand All @@ -104,5 +108,7 @@ class ProductListActivity : AppCompatActivity(), ProductListContract.View {
companion object {
private const val HEADER_FOOTER_SPAN = 2
private const val PRODUCT_ITEM_SPAN = 1
const val RESULT_VIEWED = 200
const val ID = "id"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import androidx.recyclerview.widget.RecyclerView
import woowacourse.shopping.model.ProductModel

class ProductListAdapter(
private val recentViewedProducts: List<ProductModel>,
private val products: List<ProductModel>,
private val items: List<ProductListViewItem>,
private val onItemClick: OnItemClick,
) : RecyclerView.Adapter<ProductViewHolder>() {
interface OnItemClick {
Expand All @@ -19,32 +18,22 @@ class ProductListAdapter(
}

override fun getItemViewType(position: Int): Int {
return if (position == 0 && isRecentViewedExist()) {
ProductListViewType.RECENT_VIEWED_ITEM.ordinal
} else if (position == itemCount - 1) {
ProductListViewType.SHOW_MORE_ITEM.ordinal
} else {
ProductListViewType.PRODUCT_ITEM.ordinal
}
return items[position].type.ordinal
}

override fun getItemCount(): Int =
if (isRecentViewedExist()) products.size + 2 else products.size + 1
override fun getItemCount(): Int = items.size

override fun onBindViewHolder(holder: ProductViewHolder, position: Int) {
when (holder) {
is ProductViewHolder.RecentViewedViewHolder -> {
holder.bind(recentViewedProducts, onItemClick)
holder.bind(items[position] as ProductListViewItem.RecentViewedItem, onItemClick)
}
is ProductViewHolder.ProductItemViewHolder -> {
val convertPosition = if (isRecentViewedExist()) position - 1 else position
holder.bind(products[convertPosition])
holder.bind(items[position] as ProductListViewItem.ProductItem)
}
is ProductViewHolder.ShowMoreViewHolder -> {
return
}
}
}

private fun isRecentViewedExist(): Boolean = recentViewedProducts.isNotEmpty()
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@ import woowacourse.shopping.model.ProductModel

interface ProductListContract {
interface View {
fun showProducts(recentViewedProducts: List<ProductModel>, products: List<ProductModel>)
fun showProducts(items: List<ProductListViewItem>)
fun notifyAddProducts(position: Int, size: Int)

fun notifyRecentViewedChanged()
}

interface Presenter {
fun fetchProducts()
fun showMoreProducts()
fun updateRecentViewed(id: Int)
}
}
Original file line number Diff line number Diff line change
@@ -1,29 +1,58 @@
package woowacourse.shopping.view.productlist

import android.util.Log
import woowacourse.shopping.domain.ProductRepository
import woowacourse.shopping.domain.RecentViewedRepository
import woowacourse.shopping.model.ProductListPagination
import woowacourse.shopping.model.toUiModel

class ProductListPresenter(
private val view: ProductListContract.View,
private val productRepository: ProductRepository,
recentViewedRepository: RecentViewedRepository,
private val recentViewedRepository: RecentViewedRepository,
) : ProductListContract.Presenter {
private val productListPagination = ProductListPagination(PAGINATION_SIZE, productRepository)
private val products = productListPagination.nextItems().map { it.toUiModel() }.toMutableList()
private val viewedProducts = recentViewedRepository.findAll()
private val viewedProducts = recentViewedRepository.findAll().toMutableList()

private val productsListItems = mutableListOf<ProductListViewItem>()
override fun fetchProducts() {
val viewedProductsUiModel =
viewedProducts.map { productRepository.find(it).toUiModel() }.reversed()
view.showProducts(viewedProductsUiModel, products)
// 최근 본 항목
val viewedProducts = recentViewedRepository.findAll().reversed()
val viewedProductsItem =
ProductListViewItem.RecentViewedItem(viewedProducts.map { convertIdToProductModel(it) })
productsListItems.add(viewedProductsItem)
// 상품 리스트
productsListItems.addAll(products.map { ProductListViewItem.ProductItem(it) })
// 더보기
if(productListPagination.isNextEnabled) productsListItems.add(ProductListViewItem.ShowMoreItem())
view.showProducts(productsListItems)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

코드에 주석을 추가하여 각 코드블럭이 어떤 것을 의미하는지 이해하기가 쉬워졌네요. 👍
하지만 지금과 같은 코드 주석은, 오히려 하나의 함수가 두 가지 이상의 일을 한다는 것을 의미하기도 합니다.
그렇다면 함수는 한 가지 일만 하도록 개선해보면 어떨까요? (이하 관련 내용 동일)

}

override fun showMoreProducts() {
val mark = if (viewedProducts.isNotEmpty()) products.size + 1 else products.size
products.addAll(productListPagination.nextItems().map { it.toUiModel() })
val mark = if (isExistRecentViewed()) products.size + 1 else products.size
val nextProducts = productListPagination.nextItems().map { it.toUiModel() }
products.addAll(nextProducts)
// RecyclerView Items 수정
productsListItems.removeLast()
productsListItems.addAll(nextProducts.map { ProductListViewItem.ProductItem(it) })
if(productListPagination.isNextEnabled) productsListItems.add(ProductListViewItem.ShowMoreItem())
// Notify
view.notifyAddProducts(mark, PAGINATION_SIZE)
}

override fun updateRecentViewed(id: Int) {
if (id == -1) return
viewedProducts.add(0, id)
if (isExistRecentViewed()) productsListItems.removeAt(0)
productsListItems.add(0, ProductListViewItem.RecentViewedItem(viewedProducts.map { convertIdToProductModel(it) }))
view.notifyRecentViewedChanged()
}

private fun convertIdToProductModel(id: Int) = productRepository.find(id).toUiModel()

private fun isExistRecentViewed(): Boolean = productsListItems[0] is ProductListViewItem.RecentViewedItem

companion object {
private const val PAGINATION_SIZE = 20
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package woowacourse.shopping.view.productlist

import woowacourse.shopping.model.CartPageStatus
import woowacourse.shopping.model.CartProductModel
import woowacourse.shopping.model.ProductModel

sealed interface ProductListViewItem {
val type: ProductListViewType
data class RecentViewedItem(val products: List<ProductModel>) : ProductListViewItem {
override val type = ProductListViewType.RECENT_VIEWED_ITEM
}
data class ProductItem(val product: ProductModel) : ProductListViewItem {
override val type = ProductListViewType.PRODUCT_ITEM
}
class ShowMoreItem : ProductListViewItem {
override val type = ProductListViewType.SHOW_MORE_ITEM
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ sealed class ProductViewHolder(view: View) : RecyclerView.ViewHolder(view) {
class RecentViewedViewHolder(private val binding: ItemRecentViewedBinding) :
ProductViewHolder(binding.root) {
fun bind(
recentViewedProducts: List<ProductModel>,
item: ProductListViewItem.RecentViewedItem,
onItemClick: ProductListAdapter.OnItemClick
) {
binding.recyclerRecentViewed.adapter =
RecentViewedAdapter(recentViewedProducts, onItemClick)
RecentViewedAdapter(item.products, onItemClick)
}
}

Expand All @@ -33,13 +33,13 @@ sealed class ProductViewHolder(view: View) : RecyclerView.ViewHolder(view) {
binding.onItemClick = onItemClick
}

fun bind(product: ProductModel) {
binding.product = product
fun bind(item: ProductListViewItem.ProductItem) {
binding.product = item.product
binding.textPrice.text = binding.root.context.getString(
R.string.korean_won,
PriceFormatter.format(product.price)
PriceFormatter.format(item.product.price)
)
Glide.with(binding.root.context).load(product.imageUrl).into(binding.imgProduct)
Glide.with(binding.root.context).load(item.product.imageUrl).into(binding.imgProduct)
}
}

Expand Down