-
Notifications
You must be signed in to change notification settings - Fork 71
[쇼핑 주문 1, 2 단계] 모찌 미션 제출합니다 #111
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
Merged
Merged
Changes from all commits
Commits
Show all changes
72 commits
Select commit
Hold shift + click to select a range
cd91a8d
feat: set up the project
ijh1298 42ba4db
docs: 기능 요구 사항 작성
wondroid-world 2f1c5da
build: shimmer 의존성 추가
wondroid-world c491ab8
feat: 상품 목록 shimmer layout 구현
wondroid-world 12887e0
feat: recyclerView Item에 상품 목록 shimmer 추가
wondroid-world 4d19a69
feat: recyclerView LoadingStateProduct의 ViewHolder, Adapter 적용
ijh1298 e7533ee
feat: 카탈로그 화면에 Shimmer UI 추가
ijh1298 2bcc720
feat: 장바구니 화면 로딩 UI 추가
ijh1298 f828b60
feat: 사용자 인증 정보 저장
wondroid-world d474112
build: retrofit 의존성 추가
wondroid-world abc8f6c
feat: 장바구니 아이템 DTO 추가
wondroid-world 632b3c1
feat: Product service, Product retrofit 구현
wondroid-world f870e90
feat: RemoteCatalogProductRepositoryImpl 구현 및 적용
ijh1298 9b5bb82
feat: Retrofit 서비스 싱글톤으로 변경 및 로깅 인터셉터 추가
ijh1298 521cccd
refactor: 잘못 사용한 DTO import 변경
ijh1298 d2875b5
feat: 서버에서 상품 목록 불러오는 기능 구현
wondroid-world c28ff41
feat: 서버에서 상세 페이지 조회 구현
wondroid-world fe6ba66
feat: detailActivty에서 최근 본 상품 조회 구현
wondroid-world 47e8031
feat: CartItem service, retrofit 구현
wondroid-world 2ce319a
refactor: CartProductRepository를 Remote로 변경
ijh1298 bf436d4
feat: 장바구니에 상품 추가 후, cartItemId 콜백 받도록 구현
wondroid-world c2952ba
feat: 장바구니에 담긴 상품 수량 표시 및 상품 상세 화면으로 이동 시 상품 정보 전달 기능 개선
ijh1298 70bb188
feat: 제품 상세 페이지에서 장바구니에 없는 상품을 추가하면, 서버에 연동되는 로직 구현
wondroid-world 220a27f
feat: 상품 목록 페이지 수량 증가 감소 버튼 로직 구현
wondroid-world 89fff2f
fix: 상품 수량 1개일 때 장바구니에서 상품 삭제되도록 수정
ijh1298 879e6c2
feat: 상품 수량이 0인 경우 카트에 상품 추가, 0이 아닌 경우 수량 변경 기능 추가
ijh1298 ee1a4ab
refactor: 불필요한 레포지토리 및 객체 코드 제거
ijh1298 4a0656d
refactor: 불필요한 레포지토리 및 객체 코드 제거
ijh1298 9571d82
Merge branch 'step1' of https://github.com/ijh1298/android-shopping-o…
ijh1298 062b45c
docs: 2단계 기능 요구 사항 작성
wondroid-world f3cc100
feat: 주문 화면 레이아웃 구현
wondroid-world 01c149e
feat: 장바구니 상품 추천 fragment layout 구현
wondroid-world 0eaf9d4
feat: cart selection fragment 추가
wondroid-world e45ece4
fix: CartSelectionFragment 뷰 바인딩 오류 수정 및 레이아웃 조정
ijh1298 91e1b54
feat: 장바구니 상품 개별 선택 기능 추가
ijh1298 2069315
feat: cartActity에 cartViewModel 추가
wondroid-world ca132f9
feat: 장바구니 전체 금액, 총 개수 표시 기능 추가
ijh1298 a982974
fix: 장바구니 상품 선택 기능 및 총 금액 계산 기능 수정
ijh1298 4938217
feat: 장바구니에 담긴 상품이 없을 때 추천 상품 화면 표시 기능 추가
ijh1298 f2ca80f
feat: 추천상품 받아올 수 있는 로직 구현
wondroid-world 43abf6a
feat: 장바구니 추천 상품 UI 적용
ijh1298 0f46e95
fix: 카트에 상품 목록이 없을 때, 상품 추천이 뜨도록 추천
wondroid-world 80c2696
docs: 코니 요구 사항 일부 반영
wondroid-world 8984d08
refactor: ShoppingApplication에 thread { DevMockServer.start() }와 사용하지…
wondroid-world e634d11
refactor: 장바구니 추천 상품에서 리사이클러뷰 4방향 다 제약 조건 설정
wondroid-world 895a357
feat: local.properties에 아이디와 비밀번호를 저장하도록 로직 수정
wondroid-world 47674c5
fix: 최근 상품 목록이 0개인 경우, 최근 본 상품 목록 뷰가 보이지 않게 하기
wondroid-world 9e25d5a
refactor: 하드 코딩된 값이 아닌, BuildConfig에서 아이디와 비밀번호 불러오도록 로직 수정
wondroid-world 0136aa4
refactor: CatalogViewModel에서 catalogProduct, quantity 사용하지 않는 값 삭제
wondroid-world 4dcda41
refactor: CatalogViewModel에서 interface에서 remoteCatalogProductReposito…
wondroid-world 29bb4c7
refactor: baseUrl 노출하지 않도록 수정
wondroid-world 94d58dd
fix: 장바구니 개수 받아오는 api의 HTTP method 변경
wondroid-world e5ef783
refactor: cartItemCount를 관찰하여, fragment 변경되도록 로직 변경
wondroid-world bf2ff7a
refactor: cartItemCount 초기값 상수화
wondroid-world 858d04d
refactor: activity 함수 분리
wondroid-world 06d05b7
refactor: CartRecommendationFragment에서 사용하지 않는 함수와 Log 삭제
wondroid-world 89deb67
refactor: 추천 리스트 adapter에서 사용하지 않는 함수는 override 하지 않도록 interface에서 U…
wondroid-world 2db3e40
refactor: log 삭제
wondroid-world ef9a4d4
feat: 상품 목록에서 장바구니에 제품을 담으면, 장바구니 아이템 개수를 앱바에 노출
wondroid-world e373288
feat: 장바구니 추천상품에서 상품을 담을 수 있음
wondroid-world 623e989
fix: 상품의 개수에 따라 전체 가격 변동 하도록 수정
wondroid-world 986e49b
chore: 폴더 구조 변경
wondroid-world c5af081
fix: 장바구니 페이지에서 추천 상품을 장바구니에 담은 개수가 1개 초과했을 때, 발생하는 문제 해결
wondroid-world 551aa90
refactor: 상수화
wondroid-world 61626e6
refactor: Repository와 DataSource로 분리
wondroid-world 6b256ca
refactor: 상수화
wondroid-world 497488f
feat: 장바구니 상품 체크 박스 구현
wondroid-world ffb3a56
refactor: badge_background.xml에서 하드코딩된 색상값을 컬러 리소스로 변경
wondroid-world 633608a
refactor: fragment에서 View가 inflate된 후, observeData를 구독하도록 수정
wondroid-world 43a47af
refactor: 주석 삭제
wondroid-world e7407a5
refactor: Ktlint 적용
wondroid-world 3e24363
docs: Readme 업데이트
wondroid-world File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,56 @@ | ||
# android-shopping-order | ||
# android-shopping-order | ||
|
||
### 1,2단계 기능 요구 사항 | ||
- 스켈레톤 UI 노출 | ||
- [x] 상품 목록 | ||
- [x] 장바구니 | ||
- 서버 연동 | ||
- [x] 장바구니 아이템 | ||
- [ ] 주문 | ||
- [x] 상품 | ||
- [x] 사용자 인증 정보 저장 | ||
- [x] 장바구니 화면에서 특정 상품만 골라 주문하기 버튼을 누를 수 있다. | ||
- [x] 별도의 화면에서 상품 추천 알고리즘으로 사용자에게 적절한 상품을 추천해준다. (쿠팡 UX 참고) | ||
- [x] 상품 추천 알고리즘은 최근 본 상품 카테고리를 기반으로 최대 10개 노출한다. | ||
- [x] 예를 들어 가장 최근에 본 상품이 fashion 카테고리라면, fashion 상품 10개 노출 | ||
- [x] 해당 카테고리 상품이 10개 미만이라면 해당하는 개수만큼만 노출 | ||
- [x] 장바구니에 이미 추가된 상품이라면 미노출 | ||
- [x] 추천된 상품을 해당 화면에서 바로 추가하여 같이 주문할 수 있다. | ||
|
||
### 코니 요구사항 | ||
- [x] 리드미 작성, PR 작성 잘하기 | ||
|
||
상품 목록 화면 구현 | ||
- [x] 최근 본 상품이 0개인 경우, 최근 본 상품 목록 삭제 | ||
|
||
Cart 화면 구현 | ||
- [x] 추천된 상품을 해당 화면에서 바로 추가하여 같이 주문할 수 있다. 구현 | ||
- [x] 총 가격은 변하는데 왜 체크 박스는 변하지 않는 기능 수정 | ||
|
||
- [x] ShoppingApplication에 thread { DevMockServer.start() } 삭제 | ||
- [x] ShoppingApplication에서 정말 SharedPreferences에 값을 저장하고 있는 지 확인 + SharedPreference에서 어떻게 동작하는 지 확인 | ||
- [x] CatalogViewModel에서 catalogProduct, quantity 사용하지 않는 값 삭제 | ||
- [x] CatalogViewModel에서 interface에서 remoteCatalogProductRepositoryImpl 실제 구현체 주입 -> 수정 | ||
- [x] 최근 상품 목록이 0개인 경우, 최근 본 상품 목록 뷰가 보이지 않게 하기 | ||
- [x] baseUrl 노출하지 않도록 수정 | ||
- [x] 사용자 정보 local.properties에 저장 -> BuildConfig로 갖고 오게 끔 구현 | ||
- [ ] 레벨업 부분 ) 인증에 필요한 key은 secerets에 저장 | ||
- CartActivity에서 생각해야 할 부분 | ||
- [x] CartActivity에서 hasHandledTotalCount가 정말 필요한 로직일지 생각 | ||
- [x] View에서 totalCount가 몇인지에 따라서 어떤한 fragment를 commit할지 아닌 ViewModel에서 어떠한 상태를 두고 그 상태에 따라서 화면을 이동하는 것 | ||
- [x] View까지 와서 단순 ViewModel의 함수를 호출하는 observe를 해야하는 지 생각 -> ViewModel의 일은 스스로 하게 변 | ||
- CartRecommendationFragment | ||
- [x] 로그 삭제 | ||
- [x] 이런 식으로 Unit으로 처리를 하게 될 함수가 필수 overide 라면 ProductActionListener 에서 저 함수가 필수가 아니었던 게 아닌지 생각 | ||
- [x] collect는 viewLifecycleOwner 따라가게끔 해주셨는데, 데이터 바인딩에 사용될 lifecycleOwner 는 this 를 넘기고 있음 | ||
- CartSelectionFragment | ||
- [x] 아직 View가 만들어지기 전에 view에 필요한 데이터를 미리 observe를 하고 adpater를 세팅할 필요한지 | ||
- [x] onCreateView에서는 어떠한 동작을 해야 하고 어떠한 단계인지 생각 | ||
- [x] ViewModelStoreOwner를 Activity로 잡은 점 | ||
- [x] Application이 왜 필요로 하는가? | ||
- [x] Enabled 된 것이랑 updateCartItems 를 호출하는 상관관계 -> 전반적으로 view까지 observe가 되어야 했던 값들일까 하는 의문 | ||
- [x] 데이터 바인딩 적용 | ||
- [x] updateCartItems()가 view에서 처리해야하는 지 생각 | ||
- CartViewModel | ||
- [x] totalCount가 -1 라는 것은 논리적으로 읽을 수 없음 -> 상수화 | ||
----- https://github.com/woowacourse/android-shopping-order/pull/111#discussion_r2113932605 여기 부터 적용 다시 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
14 changes: 14 additions & 0 deletions
14
app/src/main/java/woowacourse/shopping/ShoppingApplication.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package woowacourse.shopping | ||
|
||
import android.app.Application | ||
import woowacourse.shopping.data.database.ShoppingDatabase | ||
|
||
class ShoppingApplication : Application() { | ||
lateinit var database: ShoppingDatabase | ||
private set | ||
|
||
override fun onCreate() { | ||
super.onCreate() | ||
database = ShoppingDatabase.getInstance(applicationContext) | ||
} | ||
} |
79 changes: 79 additions & 0 deletions
79
app/src/main/java/woowacourse/shopping/cart/CartActivity.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package woowacourse.shopping.cart | ||
|
||
import android.content.Context | ||
import android.content.Intent | ||
import android.os.Bundle | ||
import androidx.activity.enableEdgeToEdge | ||
import androidx.appcompat.app.AppCompatActivity | ||
import androidx.core.view.ViewCompat | ||
import androidx.core.view.WindowInsetsCompat | ||
import androidx.databinding.DataBindingUtil | ||
import androidx.fragment.app.commit | ||
import androidx.lifecycle.ViewModelProvider | ||
import woowacourse.shopping.R | ||
import woowacourse.shopping.cart.recoomendation.RecommendationFragment | ||
import woowacourse.shopping.cart.selection.SelectionFragment | ||
import woowacourse.shopping.databinding.ActivityCartBinding | ||
|
||
class CartActivity : AppCompatActivity() { | ||
private val binding: ActivityCartBinding by lazy { | ||
DataBindingUtil.setContentView(this, R.layout.activity_cart) | ||
} | ||
private val viewModel: CartViewModel by lazy { | ||
ViewModelProvider( | ||
this, | ||
CartViewModelFactory(), | ||
)[CartViewModel::class.java] | ||
} | ||
|
||
override fun onCreate(savedInstanceState: Bundle?) { | ||
super.onCreate(savedInstanceState) | ||
enableEdgeToEdge() | ||
|
||
initView() | ||
initActionBar() | ||
initDataBinding() | ||
observeData() | ||
} | ||
|
||
private fun initView() { | ||
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { v, insets -> | ||
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) | ||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom) | ||
insets | ||
} | ||
} | ||
|
||
private fun initActionBar() { | ||
supportActionBar?.setDisplayHomeAsUpEnabled(true) | ||
supportActionBar?.title = getString(R.string.text_cart_action_bar) | ||
} | ||
|
||
override fun onSupportNavigateUp(): Boolean { | ||
finish() | ||
return super.onSupportNavigateUp() | ||
} | ||
|
||
private fun initDataBinding() { | ||
binding.vm = viewModel | ||
binding.lifecycleOwner = this | ||
} | ||
|
||
private fun observeData() { | ||
viewModel.totalPurchaseCount.observe(this) { totalPurchaseCount -> | ||
val fragment = | ||
when (totalPurchaseCount) { | ||
0 -> RecommendationFragment() | ||
else -> SelectionFragment() | ||
} | ||
supportFragmentManager.commit { | ||
setReorderingAllowed(true) | ||
replace(R.id.fragment_container_cart_selection, fragment) | ||
} | ||
} | ||
} | ||
|
||
companion object { | ||
fun newIntent(context: Context): Intent = Intent(context, CartActivity::class.java) | ||
} | ||
} |
64 changes: 64 additions & 0 deletions
64
app/src/main/java/woowacourse/shopping/cart/CartAdapter.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package woowacourse.shopping.cart | ||
|
||
import android.view.ViewGroup | ||
import androidx.recyclerview.widget.RecyclerView | ||
import woowacourse.shopping.cart.CartItem.PaginationButtonItem | ||
import woowacourse.shopping.cart.CartItem.ProductItem | ||
import woowacourse.shopping.product.catalog.ProductUiModel | ||
import woowacourse.shopping.product.catalog.QuantityControlListener | ||
|
||
class CartAdapter( | ||
cartItems: List<CartItem>, | ||
private val onDeleteProductClick: DeleteProductClickListener, | ||
private val onPaginationButtonClick: PaginationButtonClickListener, | ||
private val quantityControlListener: QuantityControlListener, | ||
private val onCheckClick: CheckClickListener, | ||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() { | ||
private val cartItems: MutableList<CartItem> = cartItems.toMutableList() | ||
|
||
fun setCartItems(cartProducts: List<CartItem>) { | ||
notifyItemRangeRemoved(0, cartItems.size) | ||
cartItems.clear() | ||
cartItems.addAll(cartProducts) | ||
notifyItemRangeInserted(0, cartItems.size) | ||
} | ||
|
||
fun setCartItem(product: ProductUiModel) { | ||
val index = cartItems.filterIsInstance<ProductItem>().indexOfFirst { it.productItem.id == product.id } | ||
cartItems[index] = ProductItem(product) | ||
notifyItemChanged(index) | ||
} | ||
lee-ji-hoon marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
override fun onCreateViewHolder( | ||
parent: ViewGroup, | ||
viewType: Int, | ||
): RecyclerView.ViewHolder = | ||
if (viewType == CART_PRODUCT) { | ||
CartViewHolder.from(parent, onDeleteProductClick, quantityControlListener, onCheckClick) | ||
} else { | ||
PaginationButtonViewHolder.from(parent, onPaginationButtonClick) | ||
} | ||
|
||
override fun onBindViewHolder( | ||
holder: RecyclerView.ViewHolder, | ||
position: Int, | ||
) { | ||
when (holder) { | ||
is CartViewHolder -> holder.bind((cartItems[position] as ProductItem).productItem) | ||
is PaginationButtonViewHolder -> holder.bind(cartItems[position] as PaginationButtonItem) | ||
} | ||
lee-ji-hoon marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
override fun getItemViewType(position: Int): Int = | ||
when (cartItems[position]) { | ||
is PaginationButtonItem -> PAGINATION_BUTTON | ||
is ProductItem -> CART_PRODUCT | ||
} | ||
|
||
override fun getItemCount(): Int = cartItems.size | ||
|
||
companion object { | ||
private const val CART_PRODUCT = 1 | ||
private const val PAGINATION_BUTTON = 2 | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package woowacourse.shopping.cart | ||
|
||
import woowacourse.shopping.product.catalog.ProductUiModel | ||
|
||
sealed class CartItem { | ||
data class ProductItem( | ||
val productItem: ProductUiModel, | ||
) : CartItem() | ||
|
||
data class PaginationButtonItem( | ||
val page: Int, | ||
val isNextButtonEnabled: Boolean, | ||
val isPrevButtonEnabled: Boolean, | ||
) : CartItem() | ||
} |
34 changes: 34 additions & 0 deletions
34
app/src/main/java/woowacourse/shopping/cart/CartViewHolder.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package woowacourse.shopping.cart | ||
|
||
import android.view.LayoutInflater | ||
import android.view.ViewGroup | ||
import androidx.recyclerview.widget.RecyclerView | ||
import woowacourse.shopping.databinding.CartItemBinding | ||
import woowacourse.shopping.product.catalog.ProductUiModel | ||
import woowacourse.shopping.product.catalog.QuantityControlListener | ||
|
||
class CartViewHolder( | ||
private val binding: CartItemBinding, | ||
) : RecyclerView.ViewHolder(binding.root) { | ||
fun bind(cartProduct: ProductUiModel) { | ||
binding.cartProduct = cartProduct | ||
binding.checkboxSelection.isChecked = cartProduct.isChecked | ||
} | ||
|
||
companion object { | ||
fun from( | ||
parent: ViewGroup, | ||
onDeleteProductClick: DeleteProductClickListener, | ||
quantityControlListener: QuantityControlListener, | ||
onCheckClick: CheckClickListener, | ||
): CartViewHolder { | ||
val inflater = LayoutInflater.from(parent.context) | ||
val binding = CartItemBinding.inflate(inflater, parent, false) | ||
binding.clickListener = onDeleteProductClick | ||
binding.checkClickListener = onCheckClick | ||
binding.layoutQuantityControlBar.quantityControlListener = quantityControlListener | ||
|
||
return CartViewHolder(binding) | ||
} | ||
} | ||
Comment on lines
+18
to
+33
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion factory method의 책임 분배에 대해 생각해보세요 companion object의 from 메서드에서 많은 일들을 처리하고 있네요! 다음 질문들을 통해 설계를 다시 한번 검토해보시면 어떨까요:
코드의 책임을 어떻게 나누면 더 테스트하기 쉽고 유지보수하기 좋을지 고민해보세요! 🤖 Prompt for AI Agents
|
||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.