From 3328c33d9991998fa3abc8e1563224b9af71f6af Mon Sep 17 00:00:00 2001 From: aksworns22 Date: Mon, 9 Feb 2026 16:06:47 +0900 Subject: [PATCH 01/15] =?UTF-8?q?feat(CustomItemDto):=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=EB=90=9C=20api=EC=99=80=20=EC=9D=BC=EC=B9=98=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/egobook/app/ui/shop/StoreRepository.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/egobook/app/ui/shop/StoreRepository.kt b/app/src/main/java/com/egobook/app/ui/shop/StoreRepository.kt index fbfc1b75..1e1c4bb5 100644 --- a/app/src/main/java/com/egobook/app/ui/shop/StoreRepository.kt +++ b/app/src/main/java/com/egobook/app/ui/shop/StoreRepository.kt @@ -23,14 +23,16 @@ interface NetworkStoreService { @GET("/shop/items") suspend fun loadItemsResponse( @Query("category") category: String, - @Query("slice") slice: Int, + @Query("page") slice: Int, @Query("size") size: Int ): BaseResponse } data class CustomItemDto( val itemId: Int, - val imageUrl: String, + val itemCategory: String?, + val shopImageUrl: String, + val myImageUrl: String, val price: Int, val isPurchased: Boolean, val isEquipped: Boolean @@ -80,7 +82,7 @@ class NetworkStoreRepository @Inject constructor( itemType, Price(dto.price), if (dto.isPurchased) ItemStatus.PURCHASED else ItemStatus.PURCHASABLE, - ItemImage.Url(dto.imageUrl) + ItemImage.Url(dto.shopImageUrl) ) ) } From 23be1d074b0b4d7b4ab4acbee0a8edf99bab09e4 Mon Sep 17 00:00:00 2001 From: aksworns22 Date: Mon, 9 Feb 2026 16:18:35 +0900 Subject: [PATCH 02/15] =?UTF-8?q?refactor:=20=EC=9D=B8=ED=84=B0=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=8A=A4=20=EC=9D=B4=EB=A6=84=EC=9D=84=20=EA=B5=AC?= =?UTF-8?q?=EC=B2=B4=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/egobook/app/ui/shop/StoreRepository.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/egobook/app/ui/shop/StoreRepository.kt b/app/src/main/java/com/egobook/app/ui/shop/StoreRepository.kt index 1e1c4bb5..ef1df8d8 100644 --- a/app/src/main/java/com/egobook/app/ui/shop/StoreRepository.kt +++ b/app/src/main/java/com/egobook/app/ui/shop/StoreRepository.kt @@ -19,7 +19,7 @@ data class BaseResponse( val data: T ) -interface NetworkStoreService { +interface NetworkStoreItemService { @GET("/shop/items") suspend fun loadItemsResponse( @Query("category") category: String, @@ -61,7 +61,7 @@ class NetworkStoreRepository @Inject constructor( ) : StoreRepository { private val storeService by lazy { - retrofit.create(NetworkStoreService::class.java) + retrofit.create(NetworkStoreItemService::class.java) } override fun loadStoreItems(itemType: ItemType): Flow { @@ -82,7 +82,8 @@ class NetworkStoreRepository @Inject constructor( itemType, Price(dto.price), if (dto.isPurchased) ItemStatus.PURCHASED else ItemStatus.PURCHASABLE, - ItemImage.Url(dto.shopImageUrl) + ItemImage.Url(dto.shopImageUrl), + ItemImage.Url(dto.myImageUrl) ) ) } From aad96a2e120b79080a76a2f649badc43b8b2bf20 Mon Sep 17 00:00:00 2001 From: aksworns22 Date: Mon, 9 Feb 2026 17:45:48 +0900 Subject: [PATCH 03/15] =?UTF-8?q?design:=20=EC=BB=A4=EC=8A=A4=ED=85=80=20?= =?UTF-8?q?=EC=95=84=EC=9D=B4=ED=85=9C=20=EC=9E=A5=EC=B0=A9=20=EC=9C=84?= =?UTF-8?q?=EC=B9=98=20=EC=A7=80=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/res/layout/fragment_store.xml | 65 ++++++++++++++++++++-- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/app/src/main/res/layout/fragment_store.xml b/app/src/main/res/layout/fragment_store.xml index 4f453329..9138f3c8 100644 --- a/app/src/main/res/layout/fragment_store.xml +++ b/app/src/main/res/layout/fragment_store.xml @@ -6,6 +6,7 @@ android:background="@color/layer_white"> - + + + + - + + app:layout_constraintTop_toBottomOf="@id/ink_layout" /> + + + + + + + + + Date: Mon, 9 Feb 2026 17:46:14 +0900 Subject: [PATCH 04/15] =?UTF-8?q?feat:=20=EC=84=9C=EB=B2=84=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=9E=A5=EC=B0=A9=EB=90=9C=20=EC=95=84=EC=9D=B4?= =?UTF-8?q?=ED=85=9C=EC=9D=84=20=EB=A1=9C=EB=93=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egobook/app/ui/shop/CustomItem.kt | 5 +- .../egobook/app/ui/shop/StoreRepository.kt | 56 ++++++++++++++++++- 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/egobook/app/ui/shop/CustomItem.kt b/app/src/main/java/com/egobook/app/ui/shop/CustomItem.kt index d8c3ad28..be3ae80a 100644 --- a/app/src/main/java/com/egobook/app/ui/shop/CustomItem.kt +++ b/app/src/main/java/com/egobook/app/ui/shop/CustomItem.kt @@ -5,9 +5,10 @@ data class CustomItem( val type: ItemType, val price: Price, val itemStatus: ItemStatus, - val image: ItemImage? = null + val image: ItemImage? = null, + val outfitImage: ItemImage? = null ) sealed class ItemImage { data class Url(val path: String): ItemImage() -} \ No newline at end of file +} diff --git a/app/src/main/java/com/egobook/app/ui/shop/StoreRepository.kt b/app/src/main/java/com/egobook/app/ui/shop/StoreRepository.kt index ef1df8d8..e3a4a368 100644 --- a/app/src/main/java/com/egobook/app/ui/shop/StoreRepository.kt +++ b/app/src/main/java/com/egobook/app/ui/shop/StoreRepository.kt @@ -8,8 +8,18 @@ import retrofit2.http.Query import javax.inject.Inject import javax.inject.Singleton +private fun String.toItemType(): ItemType = when(this) { + "BACK" -> ItemType.BACK + "SKIN" -> ItemType.SKIN + "DECOR_ONE" -> ItemType.DECO_1 + "DECOR_TWO" -> ItemType.DECO_2 + "BACKGROUND" -> ItemType.BACKGROUND + else -> throw IllegalArgumentException("${this}은 알 수 없는 아이템 타입 이름입니다") +} + interface StoreRepository { fun loadStoreItems(itemType: ItemType): Flow + suspend fun loadEquippedItems(): List } data class BaseResponse( @@ -28,6 +38,38 @@ interface NetworkStoreItemService { ): BaseResponse } +interface NetworkEquippedItemService { + @GET("/shop/items/equipped") + suspend fun loadEquippedItemsResponse(): BaseResponse> +} + +data class EquippedItemDto( + val itemId: Int, + val itemCategory: String, + val imageUrl: String, + val price: Int, + val isPurchased: Boolean, + val isEquipped: Boolean +) { + fun toDomain(): CustomItem? { + val itemType = itemCategory.toItemType() + if(itemType == ItemType.DECO_1 && itemId == 17) { + return null + } + if(itemType == ItemType.DECO_2 && itemId == 25) { + return null + } + return CustomItem( + id = itemId.toString(), + type = itemType, + price = Price(price), + itemStatus = if (isPurchased) ItemStatus.PURCHASED else ItemStatus.PURCHASABLE, + image = null, + outfitImage = ItemImage.Url(imageUrl) + ) + } +} + data class CustomItemDto( val itemId: Int, val itemCategory: String?, @@ -59,11 +101,15 @@ data class CustomItemGroupDto( class NetworkStoreRepository @Inject constructor( private val retrofit: Retrofit ) : StoreRepository { - + private val storeService by lazy { retrofit.create(NetworkStoreItemService::class.java) } - + + private val equippedItemService by lazy { + retrofit.create(NetworkEquippedItemService::class.java) + } + override fun loadStoreItems(itemType: ItemType): Flow { return flow { var currentPage = 1 @@ -103,5 +149,11 @@ class NetworkStoreRepository @Inject constructor( } } } + + override suspend fun loadEquippedItems(): List { + val equippedItemsResponse: BaseResponse> = + equippedItemService.loadEquippedItemsResponse() + return equippedItemsResponse.data.mapNotNull { it.toDomain() } + } } From 8868f314237ae48ab1351631316d8a2eafd2d94d Mon Sep 17 00:00:00 2001 From: aksworns22 Date: Mon, 9 Feb 2026 17:46:34 +0900 Subject: [PATCH 05/15] =?UTF-8?q?feat:=20=EC=95=84=EC=9D=B4=ED=85=9C?= =?UTF-8?q?=EC=9D=98=20=EC=9E=84=EC=8B=9C=20=EC=B0=A9=EC=9A=A9/=ED=95=B4?= =?UTF-8?q?=EC=A0=9C=EB=A5=BC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egobook/app/ui/shop/ItemAdapter.kt | 9 ++-- .../app/ui/shop/StoreCollectionFragment.kt | 11 ++-- .../com/egobook/app/ui/shop/StoreFragment.kt | 50 +++++++++++++++++++ .../com/egobook/app/ui/shop/StoreViewModel.kt | 38 ++++++++++++-- 4 files changed, 98 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/egobook/app/ui/shop/ItemAdapter.kt b/app/src/main/java/com/egobook/app/ui/shop/ItemAdapter.kt index f2be3f34..a11bfd0c 100644 --- a/app/src/main/java/com/egobook/app/ui/shop/ItemAdapter.kt +++ b/app/src/main/java/com/egobook/app/ui/shop/ItemAdapter.kt @@ -15,20 +15,23 @@ import androidx.recyclerview.widget.RecyclerView import coil.load import com.egobook.app.R -class ItemAdapter : - ListAdapter(ItemDiffCallback) { +class ItemAdapter( + private val onItemClick: (CustomItem) -> Unit +) : ListAdapter(ItemDiffCallback) { override fun onCreateViewHolder( parent: ViewGroup, viewType: Int ): ItemViewHolder { val view = LayoutInflater.from(parent.context) .inflate(R.layout.view_holder_store_item, parent, false) - return ItemViewHolder(view) } override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { holder.binding(getItem(position)) + holder.root.setOnClickListener { + onItemClick(getItem(position)) + } } class ItemViewHolder(view: View) : RecyclerView.ViewHolder(view) { diff --git a/app/src/main/java/com/egobook/app/ui/shop/StoreCollectionFragment.kt b/app/src/main/java/com/egobook/app/ui/shop/StoreCollectionFragment.kt index c3bb22e4..cd05fe5a 100644 --- a/app/src/main/java/com/egobook/app/ui/shop/StoreCollectionFragment.kt +++ b/app/src/main/java/com/egobook/app/ui/shop/StoreCollectionFragment.kt @@ -1,11 +1,13 @@ package com.egobook.app.ui.shop import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.core.os.BundleCompat import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope @@ -31,13 +33,15 @@ class StoreCollectionFragment(): Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - val viewModel: StoreViewModel by viewModels() + val viewModel: StoreViewModel by activityViewModels() val targetBundle = checkNotNull(arguments) { "구현 오류: 올바른 탭을 표시하기 위해 번들은 필수입니다"} val tabItem = checkNotNull(BundleCompat.getParcelable(targetBundle, ItemTab.BUNDLE_KEY, ItemTab::class.java)) { "구현 오류: 올바른 탭을 표시하기 위해 tabItem을 번들을 통해 넘겨야 합니다." } - val itemAdapter = ItemAdapter() + val itemAdapter = ItemAdapter { customItem -> + viewModel.equipItem(customItem) + } binding.rvItems.apply { adapter = itemAdapter @@ -47,10 +51,11 @@ class StoreCollectionFragment(): Fragment() { viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.items.collect { newItems -> - itemAdapter.submitList(newItems) + itemAdapter.submitList(newItems[tabItem.type]) } } } + Log.d("jang", "$tabItem") viewModel.loadItems(tabItem.type) } diff --git a/app/src/main/java/com/egobook/app/ui/shop/StoreFragment.kt b/app/src/main/java/com/egobook/app/ui/shop/StoreFragment.kt index 46b0325a..ddc81e49 100644 --- a/app/src/main/java/com/egobook/app/ui/shop/StoreFragment.kt +++ b/app/src/main/java/com/egobook/app/ui/shop/StoreFragment.kt @@ -1,18 +1,23 @@ package com.egobook.app.ui.shop import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels +import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import androidx.viewpager2.widget.ViewPager2 +import coil.load import com.egobook.app.R import com.egobook.app.databinding.FragmentStoreBinding import com.google.android.material.tabs.TabLayoutMediator import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.launch @AndroidEntryPoint class StoreFragment: Fragment() { @@ -44,16 +49,61 @@ class StoreFragment: Fragment() { tab.text = ItemTab.of(position).text }.attach() + val viewModel: StoreViewModel by activityViewModels() + binding.ivBack.setOnClickListener { + viewModel.resetEquipItems() findNavController().navigate(R.id.action_storeFragment_to_homeFragment) } + + + viewLifecycleOwner.lifecycleScope.launch { + viewModel.equippedItems.collect { equippedList -> + equippedList.forEach { equippedItem -> + updateEquipItemUi(equippedItem) + } + } + } + } override fun onDestroyView() { super.onDestroyView() _binding = null } + + private fun updateEquipItemUi(item: CustomItem) { + Log.d("jang", "$item") + when (item.type) { + ItemType.BACK -> { + if (item.outfitImage is ItemImage.Url) { + binding.ivStoreTurtleBack.load(item.outfitImage.path) + } + } + ItemType.SKIN -> { + if (item.outfitImage is ItemImage.Url) { + binding.ivStoreTurtleSkin.load(item.outfitImage.path) + } + } + ItemType.DECO_1 -> { + if (item.outfitImage is ItemImage.Url) { + binding.ivStoreTurtleDeco1.load(item.outfitImage.path) + } + } + ItemType.DECO_2 -> { + if (item.outfitImage is ItemImage.Url) { + binding.ivStoreTurtleDeco2.load(item.outfitImage.path) + } + } + ItemType.BACKGROUND -> { + if (item.outfitImage is ItemImage.Url) { + binding.ivStoreBackground.load(item.outfitImage.path) + } + } + } + } + } diff --git a/app/src/main/java/com/egobook/app/ui/shop/StoreViewModel.kt b/app/src/main/java/com/egobook/app/ui/shop/StoreViewModel.kt index 33195448..cc1b2474 100644 --- a/app/src/main/java/com/egobook/app/ui/shop/StoreViewModel.kt +++ b/app/src/main/java/com/egobook/app/ui/shop/StoreViewModel.kt @@ -1,11 +1,13 @@ package com.egobook.app.ui.shop +import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import javax.inject.Inject @@ -13,15 +15,43 @@ import javax.inject.Inject class StoreViewModel @Inject constructor( private val storeRepository: StoreRepository ) : ViewModel() { - private val _items = MutableStateFlow>(emptyList()) - val items: StateFlow> = _items.asStateFlow() + + init { + loadEquippedItems() + } + + private val _equippedItems = MutableStateFlow>(emptyList()) + val equippedItems: StateFlow> = _equippedItems + private val _items = MutableStateFlow>>(mutableMapOf()) + val items: StateFlow>> = _items.asStateFlow() fun loadItems(type: ItemType) { + if (_items.value.containsKey(type)) return viewModelScope.launch { - _items.value = emptyList() storeRepository.loadStoreItems(type) .collect { newItem -> - _items.value += newItem + val loadedList = mutableListOf() + storeRepository.loadStoreItems(type).collect { newItem -> + loadedList.add(newItem) + } + + _items.update { currentMap -> + currentMap + (type to loadedList) + } } } } + + fun loadEquippedItems() { + viewModelScope.launch { + _equippedItems.value = storeRepository.loadEquippedItems() + } + } + + fun equipItem(item: CustomItem) { + _equippedItems.value = _equippedItems.value.filter { it.type != item.type } + item + } + + fun resetEquipItems() { + _equippedItems.value = emptyList() + } } From 692efb525fb71b8a3dd3bd7af17047f3f08d2922 Mon Sep 17 00:00:00 2001 From: aksworns22 Date: Mon, 9 Feb 2026 19:22:00 +0900 Subject: [PATCH 06/15] =?UTF-8?q?fix:=20=EA=B8=B0=EC=A1=B4=20=EC=B0=A9?= =?UTF-8?q?=EC=9A=A9=EB=90=9C=20=EC=95=84=EC=9D=B4=ED=85=9C=20=EB=A1=9C?= =?UTF-8?q?=EB=93=9C=EA=B0=80=20=EC=95=88=EB=90=98=EB=8A=94=20=EB=AC=B8?= =?UTF-8?q?=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/com/egobook/app/ui/shop/StoreFragment.kt | 4 ++-- app/src/main/java/com/egobook/app/ui/shop/StoreViewModel.kt | 5 ----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/egobook/app/ui/shop/StoreFragment.kt b/app/src/main/java/com/egobook/app/ui/shop/StoreFragment.kt index ddc81e49..ae705ea2 100644 --- a/app/src/main/java/com/egobook/app/ui/shop/StoreFragment.kt +++ b/app/src/main/java/com/egobook/app/ui/shop/StoreFragment.kt @@ -50,7 +50,7 @@ class StoreFragment: Fragment() { }.attach() val viewModel: StoreViewModel by activityViewModels() - + viewModel.loadEquippedItems() binding.ivBack.setOnClickListener { viewModel.resetEquipItems() findNavController().navigate(R.id.action_storeFragment_to_homeFragment) @@ -74,7 +74,7 @@ class StoreFragment: Fragment() { } private fun updateEquipItemUi(item: CustomItem) { - Log.d("jang", "$item") + binding.ivStoreTurtle.visibility = View.INVISIBLE when (item.type) { ItemType.BACK -> { if (item.outfitImage is ItemImage.Url) { diff --git a/app/src/main/java/com/egobook/app/ui/shop/StoreViewModel.kt b/app/src/main/java/com/egobook/app/ui/shop/StoreViewModel.kt index cc1b2474..c2be30b3 100644 --- a/app/src/main/java/com/egobook/app/ui/shop/StoreViewModel.kt +++ b/app/src/main/java/com/egobook/app/ui/shop/StoreViewModel.kt @@ -15,11 +15,6 @@ import javax.inject.Inject class StoreViewModel @Inject constructor( private val storeRepository: StoreRepository ) : ViewModel() { - - init { - loadEquippedItems() - } - private val _equippedItems = MutableStateFlow>(emptyList()) val equippedItems: StateFlow> = _equippedItems private val _items = MutableStateFlow>>(mutableMapOf()) From df989c97c87c01e6660c34232ac364bccd3067d9 Mon Sep 17 00:00:00 2001 From: aksworns22 Date: Mon, 9 Feb 2026 20:03:54 +0900 Subject: [PATCH 07/15] =?UTF-8?q?design:=20critical=20=EC=83=89=EC=83=81?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/res/values/colors.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 40b6d0aa..9cdd9b4d 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -33,6 +33,8 @@ #191818 #E8EED9 #F4F7EE + #E14444 + #E69090 From 2513cdcac3718f3a27f1cf756fc9e2f58d77a981 Mon Sep 17 00:00:00 2001 From: aksworns22 Date: Mon, 9 Feb 2026 20:04:15 +0900 Subject: [PATCH 08/15] =?UTF-8?q?design:=20=EC=83=81=EC=A0=90=20=EB=82=98?= =?UTF-8?q?=EA=B0=80=EA=B8=B0=20=EB=8B=A4=EC=9D=B4=EC=96=B4=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=20ui=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../neutral_radius_border_dialog_button.xml | 5 + .../main/res/layout/dialog_leaving_store.xml | 93 +++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 app/src/main/res/drawable/neutral_radius_border_dialog_button.xml create mode 100644 app/src/main/res/layout/dialog_leaving_store.xml diff --git a/app/src/main/res/drawable/neutral_radius_border_dialog_button.xml b/app/src/main/res/drawable/neutral_radius_border_dialog_button.xml new file mode 100644 index 00000000..08e676da --- /dev/null +++ b/app/src/main/res/drawable/neutral_radius_border_dialog_button.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/layout/dialog_leaving_store.xml b/app/src/main/res/layout/dialog_leaving_store.xml new file mode 100644 index 00000000..ce6eb355 --- /dev/null +++ b/app/src/main/res/layout/dialog_leaving_store.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + From 19e4bd8f193667ba35bb80b53ba5e1423dea24eb Mon Sep 17 00:00:00 2001 From: aksworns22 Date: Mon, 9 Feb 2026 20:04:21 +0900 Subject: [PATCH 09/15] =?UTF-8?q?feat:=20=EC=83=81=EC=A0=90=20=EB=82=98?= =?UTF-8?q?=EA=B0=80=EA=B8=B0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egobook/app/ui/shop/StoreFragment.kt | 11 +++- .../egobook/app/ui/shop/StoreLeavingDialog.kt | 53 +++++++++++++++++++ 2 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/com/egobook/app/ui/shop/StoreLeavingDialog.kt diff --git a/app/src/main/java/com/egobook/app/ui/shop/StoreFragment.kt b/app/src/main/java/com/egobook/app/ui/shop/StoreFragment.kt index ae705ea2..cb5277cc 100644 --- a/app/src/main/java/com/egobook/app/ui/shop/StoreFragment.kt +++ b/app/src/main/java/com/egobook/app/ui/shop/StoreFragment.kt @@ -13,8 +13,11 @@ import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import androidx.viewpager2.widget.ViewPager2 import coil.load +import com.egobook.app.BlurLevel import com.egobook.app.R +import com.egobook.app.applyScreenBlur import com.egobook.app.databinding.FragmentStoreBinding +import com.egobook.app.ui.home.ui.AdDialog import com.google.android.material.tabs.TabLayoutMediator import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch @@ -52,8 +55,12 @@ class StoreFragment: Fragment() { val viewModel: StoreViewModel by activityViewModels() viewModel.loadEquippedItems() binding.ivBack.setOnClickListener { - viewModel.resetEquipItems() - findNavController().navigate(R.id.action_storeFragment_to_homeFragment) + applyScreenBlur(BlurLevel.BASE) + val dialog = StoreLeavingDialog() + dialog.isCancelable = false + dialog.show(parentFragmentManager, "StoreLeavingDialog") +// viewModel.resetEquipItems() +// findNavController().navigate(R.id.action_storeFragment_to_homeFragment) } diff --git a/app/src/main/java/com/egobook/app/ui/shop/StoreLeavingDialog.kt b/app/src/main/java/com/egobook/app/ui/shop/StoreLeavingDialog.kt new file mode 100644 index 00000000..fe3f492e --- /dev/null +++ b/app/src/main/java/com/egobook/app/ui/shop/StoreLeavingDialog.kt @@ -0,0 +1,53 @@ +package com.egobook.app.ui.shop + +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.DialogFragment +import androidx.fragment.app.activityViewModels +import androidx.navigation.fragment.findNavController +import com.egobook.app.R +import com.egobook.app.databinding.DialogLeavingStoreBinding +import com.egobook.app.removeScreenBlur + +class StoreLeavingDialog: DialogFragment() { + private var _binding: DialogLeavingStoreBinding? = null + private val binding get() = checkNotNull(_binding) { "Fragment가 제거되었습니다." } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + dialog?.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + _binding = DialogLeavingStoreBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + val viewModel: StoreViewModel by activityViewModels() + binding.btnRemain.setOnClickListener { + removeScreenBlur() + dismiss() + } + + + binding.btnLeave.setOnClickListener { + removeScreenBlur() + dismiss() + viewModel.resetEquipItems() + findNavController().navigate(R.id.action_storeFragment_to_homeFragment) + } + } + + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } +} From 55638a9eb8df6541725ced0019dd1656cba9702b Mon Sep 17 00:00:00 2001 From: aksworns22 Date: Mon, 9 Feb 2026 21:06:02 +0900 Subject: [PATCH 10/15] =?UTF-8?q?feat:=20=EC=95=84=EC=9D=B4=ED=85=9C=20?= =?UTF-8?q?=EC=84=A0=ED=83=9D=20=ED=85=8C=EB=91=90=EB=A6=AC=20ui=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egobook/app/ui/shop/ItemAdapter.kt | 24 ++++++++++------- .../app/ui/shop/StoreCollectionFragment.kt | 3 +-- .../com/egobook/app/ui/shop/StoreViewModel.kt | 27 ++++++++++++++++++- 3 files changed, 41 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/egobook/app/ui/shop/ItemAdapter.kt b/app/src/main/java/com/egobook/app/ui/shop/ItemAdapter.kt index a11bfd0c..77f3235b 100644 --- a/app/src/main/java/com/egobook/app/ui/shop/ItemAdapter.kt +++ b/app/src/main/java/com/egobook/app/ui/shop/ItemAdapter.kt @@ -17,7 +17,7 @@ import com.egobook.app.R class ItemAdapter( private val onItemClick: (CustomItem) -> Unit -) : ListAdapter(ItemDiffCallback) { +) : ListAdapter(ItemDiffCallback) { override fun onCreateViewHolder( parent: ViewGroup, viewType: Int @@ -30,7 +30,7 @@ class ItemAdapter( override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { holder.binding(getItem(position)) holder.root.setOnClickListener { - onItemClick(getItem(position)) + onItemClick(getItem(position).item) } } @@ -41,8 +41,8 @@ class ItemAdapter( val itemImage: ImageView = view.findViewById(R.id.iv_item) val root: View = view.rootView - fun binding(item: CustomItem) { - + fun binding(itemState: CustomItemState) { + val item = itemState.item item.image?.let { image -> when(image) { is ItemImage.Url -> itemImage.load(image.path) @@ -59,25 +59,29 @@ class ItemAdapter( itemStatus.text = "구독전용" itemInfoLayout.setPadding(24,6,24,6) itemPriceIcon.visibility = GONE - root.background = null } ItemStatus.PURCHASABLE -> { itemStatus.text = item.price.toString() itemInfoLayout.updatePadding(left=12, right=16) itemPriceIcon.visibility = VISIBLE - root.background = null } } + + if (itemState.isSelected) { + root.setBackgroundResource(R.drawable.store_item_selection_border) + } else { + root.background = null + } } } companion object { - private val ItemDiffCallback = object : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: CustomItem, newItem: CustomItem): Boolean { - return oldItem.id == newItem.id // 고유 ID 비교 + private val ItemDiffCallback = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: CustomItemState, newItem: CustomItemState): Boolean { + return oldItem.item.id == newItem.item.id // 고유 ID 비교 } - override fun areContentsTheSame(oldItem: CustomItem, newItem: CustomItem): Boolean { + override fun areContentsTheSame(oldItem: CustomItemState, newItem: CustomItemState): Boolean { return oldItem == newItem // 전체 객체 내용 비교 } } diff --git a/app/src/main/java/com/egobook/app/ui/shop/StoreCollectionFragment.kt b/app/src/main/java/com/egobook/app/ui/shop/StoreCollectionFragment.kt index cd05fe5a..1e089f46 100644 --- a/app/src/main/java/com/egobook/app/ui/shop/StoreCollectionFragment.kt +++ b/app/src/main/java/com/egobook/app/ui/shop/StoreCollectionFragment.kt @@ -8,7 +8,6 @@ import android.view.ViewGroup import androidx.core.os.BundleCompat import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels -import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle @@ -50,7 +49,7 @@ class StoreCollectionFragment(): Fragment() { viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.items.collect { newItems -> + viewModel.itemStates.collect { newItems -> itemAdapter.submitList(newItems[tabItem.type]) } } diff --git a/app/src/main/java/com/egobook/app/ui/shop/StoreViewModel.kt b/app/src/main/java/com/egobook/app/ui/shop/StoreViewModel.kt index c2be30b3..421a6ab1 100644 --- a/app/src/main/java/com/egobook/app/ui/shop/StoreViewModel.kt +++ b/app/src/main/java/com/egobook/app/ui/shop/StoreViewModel.kt @@ -1,16 +1,22 @@ package com.egobook.app.ui.shop -import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import javax.inject.Inject +data class CustomItemState( + val isSelected: Boolean, + val item: CustomItem +) @HiltViewModel class StoreViewModel @Inject constructor( private val storeRepository: StoreRepository @@ -19,6 +25,25 @@ class StoreViewModel @Inject constructor( val equippedItems: StateFlow> = _equippedItems private val _items = MutableStateFlow>>(mutableMapOf()) val items: StateFlow>> = _items.asStateFlow() + + val itemStates: StateFlow>> = + combine(_items, _equippedItems) { itemsMap, equippedList -> + + val equippedIds = equippedList.map { it.id }.toSet() + + itemsMap.mapValues { (_, items) -> + items.map { item -> + CustomItemState( + item = item, + isSelected = equippedIds.contains(item.id) + ) + } + } + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5000), + initialValue = emptyMap() + ) fun loadItems(type: ItemType) { if (_items.value.containsKey(type)) return viewModelScope.launch { From 5efe1a8bf0417085879d60eb83014250363cddd8 Mon Sep 17 00:00:00 2001 From: aksworns22 Date: Mon, 9 Feb 2026 21:07:03 +0900 Subject: [PATCH 11/15] =?UTF-8?q?style:=20=EC=A3=BC=EC=84=9D=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/com/egobook/app/ui/shop/StoreFragment.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/src/main/java/com/egobook/app/ui/shop/StoreFragment.kt b/app/src/main/java/com/egobook/app/ui/shop/StoreFragment.kt index cb5277cc..a9b4d6fb 100644 --- a/app/src/main/java/com/egobook/app/ui/shop/StoreFragment.kt +++ b/app/src/main/java/com/egobook/app/ui/shop/StoreFragment.kt @@ -59,8 +59,6 @@ class StoreFragment: Fragment() { val dialog = StoreLeavingDialog() dialog.isCancelable = false dialog.show(parentFragmentManager, "StoreLeavingDialog") -// viewModel.resetEquipItems() -// findNavController().navigate(R.id.action_storeFragment_to_homeFragment) } From 364a185880277a79066108ce2b1b5cb03682498c Mon Sep 17 00:00:00 2001 From: aksworns22 Date: Mon, 9 Feb 2026 21:36:58 +0900 Subject: [PATCH 12/15] =?UTF-8?q?style:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EB=A1=9C=EA=B7=B8=20=EC=B6=9C=EB=A0=A5=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/egobook/app/ui/shop/StoreCollectionFragment.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/com/egobook/app/ui/shop/StoreCollectionFragment.kt b/app/src/main/java/com/egobook/app/ui/shop/StoreCollectionFragment.kt index 1e089f46..25894f46 100644 --- a/app/src/main/java/com/egobook/app/ui/shop/StoreCollectionFragment.kt +++ b/app/src/main/java/com/egobook/app/ui/shop/StoreCollectionFragment.kt @@ -54,7 +54,6 @@ class StoreCollectionFragment(): Fragment() { } } } - Log.d("jang", "$tabItem") viewModel.loadItems(tabItem.type) } From bc0835772110d5b37a3dd4415e5d4a0e4c0560f5 Mon Sep 17 00:00:00 2001 From: aksworns22 Date: Mon, 9 Feb 2026 21:37:08 +0900 Subject: [PATCH 13/15] =?UTF-8?q?style:=20=ED=83=AD=20=EC=A0=84=ED=99=98?= =?UTF-8?q?=20=EC=8B=9C=20=EC=B0=A9=EC=9A=A9=20=EC=95=84=EC=9D=B4=ED=85=9C?= =?UTF-8?q?=20=ED=95=B4=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egobook/app/ui/shop/StoreFragment.kt | 19 +++++++++++++++++-- .../egobook/app/ui/shop/StoreRepository.kt | 10 ++-------- .../com/egobook/app/ui/shop/StoreViewModel.kt | 4 ++++ 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/egobook/app/ui/shop/StoreFragment.kt b/app/src/main/java/com/egobook/app/ui/shop/StoreFragment.kt index a9b4d6fb..88b72451 100644 --- a/app/src/main/java/com/egobook/app/ui/shop/StoreFragment.kt +++ b/app/src/main/java/com/egobook/app/ui/shop/StoreFragment.kt @@ -44,7 +44,7 @@ class StoreFragment: Fragment() { v.setPadding(0, 0, 0, systemBars.bottom) insets } - + val viewModel: StoreViewModel by activityViewModels() viewPager = binding.vp2StoreCollectionContainer viewPager.adapter = StoreCollectionAdapter(this) val tabLayout = binding.tlTabs @@ -52,7 +52,14 @@ class StoreFragment: Fragment() { tab.text = ItemTab.of(position).text }.attach() - val viewModel: StoreViewModel by activityViewModels() + viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { + override fun onPageSelected(position: Int) { + super.onPageSelected(position) + viewModel.loadEquippedItems() + Log.d("jang", "페이지 변경 감지됨: $position") // 이 로그가 뜨는지 확인! + } + }) + viewModel.loadEquippedItems() binding.ivBack.setOnClickListener { applyScreenBlur(BlurLevel.BASE) @@ -93,11 +100,19 @@ class StoreFragment: Fragment() { } ItemType.DECO_1 -> { if (item.outfitImage is ItemImage.Url) { + if(item.outfitImage.path.contains("Default")) { + binding.ivStoreTurtleDeco1.load(null) + return + } binding.ivStoreTurtleDeco1.load(item.outfitImage.path) } } ItemType.DECO_2 -> { if (item.outfitImage is ItemImage.Url) { + if(item.outfitImage.path.contains("Default")) { + binding.ivStoreTurtleDeco2.load(null) + return + } binding.ivStoreTurtleDeco2.load(item.outfitImage.path) } } diff --git a/app/src/main/java/com/egobook/app/ui/shop/StoreRepository.kt b/app/src/main/java/com/egobook/app/ui/shop/StoreRepository.kt index e3a4a368..094fcd7a 100644 --- a/app/src/main/java/com/egobook/app/ui/shop/StoreRepository.kt +++ b/app/src/main/java/com/egobook/app/ui/shop/StoreRepository.kt @@ -51,14 +51,8 @@ data class EquippedItemDto( val isPurchased: Boolean, val isEquipped: Boolean ) { - fun toDomain(): CustomItem? { + fun toDomain(): CustomItem { val itemType = itemCategory.toItemType() - if(itemType == ItemType.DECO_1 && itemId == 17) { - return null - } - if(itemType == ItemType.DECO_2 && itemId == 25) { - return null - } return CustomItem( id = itemId.toString(), type = itemType, @@ -153,7 +147,7 @@ class NetworkStoreRepository @Inject constructor( override suspend fun loadEquippedItems(): List { val equippedItemsResponse: BaseResponse> = equippedItemService.loadEquippedItemsResponse() - return equippedItemsResponse.data.mapNotNull { it.toDomain() } + return equippedItemsResponse.data.map { it.toDomain() } } } diff --git a/app/src/main/java/com/egobook/app/ui/shop/StoreViewModel.kt b/app/src/main/java/com/egobook/app/ui/shop/StoreViewModel.kt index 421a6ab1..52915aed 100644 --- a/app/src/main/java/com/egobook/app/ui/shop/StoreViewModel.kt +++ b/app/src/main/java/com/egobook/app/ui/shop/StoreViewModel.kt @@ -1,5 +1,6 @@ package com.egobook.app.ui.shop +import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel @@ -64,11 +65,14 @@ class StoreViewModel @Inject constructor( fun loadEquippedItems() { viewModelScope.launch { _equippedItems.value = storeRepository.loadEquippedItems() + Log.d("jang", "load: ${_equippedItems.value}") } + } fun equipItem(item: CustomItem) { _equippedItems.value = _equippedItems.value.filter { it.type != item.type } + item + Log.d("jang", "equip: ${_equippedItems.value}") } fun resetEquipItems() { From 267432a03be7f434ee22276ecd01e9498895fe2a Mon Sep 17 00:00:00 2001 From: aksworns22 Date: Mon, 9 Feb 2026 21:41:55 +0900 Subject: [PATCH 14/15] =?UTF-8?q?feat:=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20=ED=95=A8=EC=88=98=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/com/egobook/app/ui/shop/StoreViewModel.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/egobook/app/ui/shop/StoreViewModel.kt b/app/src/main/java/com/egobook/app/ui/shop/StoreViewModel.kt index 52915aed..56c7d000 100644 --- a/app/src/main/java/com/egobook/app/ui/shop/StoreViewModel.kt +++ b/app/src/main/java/com/egobook/app/ui/shop/StoreViewModel.kt @@ -71,8 +71,9 @@ class StoreViewModel @Inject constructor( } fun equipItem(item: CustomItem) { - _equippedItems.value = _equippedItems.value.filter { it.type != item.type } + item - Log.d("jang", "equip: ${_equippedItems.value}") + _equippedItems.update { currentList -> + currentList.filter { it.type != item.type } + item + } } fun resetEquipItems() { From 87039e923e43b1874c79270a6ddec48b90b38efa Mon Sep 17 00:00:00 2001 From: aksworns22 Date: Tue, 10 Feb 2026 17:19:14 +0900 Subject: [PATCH 15/15] =?UTF-8?q?fix:=20=EB=B7=B0=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A0=80=EC=97=90=EC=84=9C=20=EC=98=AC=EB=B0=94=EB=A5=B4?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EC=9D=80=20=EC=A0=84=ED=99=98=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/com/egobook/app/ui/shop/StoreFragment.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/egobook/app/ui/shop/StoreFragment.kt b/app/src/main/java/com/egobook/app/ui/shop/StoreFragment.kt index 88b72451..8f2a6ee4 100644 --- a/app/src/main/java/com/egobook/app/ui/shop/StoreFragment.kt +++ b/app/src/main/java/com/egobook/app/ui/shop/StoreFragment.kt @@ -28,6 +28,8 @@ class StoreFragment: Fragment() { private val binding get() = checkNotNull(_binding) { "Fragment가 제거되었습니다." } private lateinit var viewPager: ViewPager2 + private var lastSelected = -1 + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -55,12 +57,13 @@ class StoreFragment: Fragment() { viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { override fun onPageSelected(position: Int) { super.onPageSelected(position) + if (position == lastSelected) return + lastSelected = position viewModel.loadEquippedItems() - Log.d("jang", "페이지 변경 감지됨: $position") // 이 로그가 뜨는지 확인! + Log.d("jang", "페이지 변경 감지됨: $position") } }) - viewModel.loadEquippedItems() binding.ivBack.setOnClickListener { applyScreenBlur(BlurLevel.BASE) val dialog = StoreLeavingDialog()