From c7fc3d70aa52bbe0af726919430dd5b0a44bc0ad Mon Sep 17 00:00:00 2001 From: Phillip Thelen Date: Mon, 27 Mar 2023 16:09:49 +0200 Subject: [PATCH] show armoire in market if all gear was purchased --- Habitica/res/layout/shop_armoire_gear.xml | 47 +++++++++++ Habitica/res/values/strings.xml | 2 + .../habitica/data/InventoryRepository.kt | 4 +- .../implementation/InventoryRepositoryImpl.kt | 6 +- .../data/local/InventoryLocalRepository.kt | 4 +- .../RealmInventoryLocalRepository.kt | 16 +++- .../habitica/ui/activities/ArmoireActivity.kt | 9 +- .../habitica/ui/activities/DeathActivity.kt | 4 +- .../adapter/inventory/ShopRecyclerAdapter.kt | 83 ++++++++++++------- .../fragments/inventory/shops/ShopFragment.kt | 13 ++- .../ui/viewmodels/MainActivityViewModel.kt | 1 + .../habitica/ui/views/shops/PurchaseDialog.kt | 4 + 12 files changed, 154 insertions(+), 39 deletions(-) create mode 100644 Habitica/res/layout/shop_armoire_gear.xml diff --git a/Habitica/res/layout/shop_armoire_gear.xml b/Habitica/res/layout/shop_armoire_gear.xml new file mode 100644 index 000000000..f72adffa0 --- /dev/null +++ b/Habitica/res/layout/shop_armoire_gear.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + diff --git a/Habitica/res/values/strings.xml b/Habitica/res/values/strings.xml index bae8dfe37..0c27c68f6 100644 --- a/Habitica/res/values/strings.xml +++ b/Habitica/res/values/strings.xml @@ -1378,6 +1378,8 @@ Leave Party Finder This equipment is class-locked Change class to %s + You own all %s gear + New gear is released during the seasonal Galas. Until then, theres %d pieces of gear in the Enchanted Armoire to find! You diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/InventoryRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/InventoryRepository.kt index 16263bfb4..4afd8f3ef 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/InventoryRepository.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/InventoryRepository.kt @@ -22,9 +22,11 @@ import kotlinx.coroutines.flow.Flow interface InventoryRepository : BaseRepository { - fun getArmoireRemainingCount(): Long + fun getArmoireRemainingCount(): Flow fun getInAppRewards(): Flow> + fun getInAppReward(key: String): Flow + fun getOwnedEquipment(): Flow> fun getMounts(): Flow> diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/InventoryRepositoryImpl.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/InventoryRepositoryImpl.kt index 55fe6b4f8..ae27eefa1 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/InventoryRepositoryImpl.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/InventoryRepositoryImpl.kt @@ -39,7 +39,7 @@ class InventoryRepositoryImpl( return localRepository.getEquipment(searchedKeys) } - override fun getArmoireRemainingCount(): Long { + override fun getArmoireRemainingCount(): Flow { return localRepository.getArmoireRemainingCount() } @@ -47,6 +47,10 @@ class InventoryRepositoryImpl( return localRepository.getInAppRewards() } + override fun getInAppReward(key : String) : Flow { + return localRepository.getInAppReward(key) + } + override suspend fun retrieveInAppRewards(): List? { val rewards = apiClient.retrieveInAppRewards() if (rewards != null) { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/InventoryLocalRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/InventoryLocalRepository.kt index 6e5b7230e..91d76ffe4 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/InventoryLocalRepository.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/InventoryLocalRepository.kt @@ -15,7 +15,7 @@ import kotlinx.coroutines.flow.Flow interface InventoryLocalRepository : ContentLocalRepository { - fun getArmoireRemainingCount(): Long + fun getArmoireRemainingCount(): Flow fun getOwnedEquipment(): Flow> fun getMounts(): Flow> @@ -27,6 +27,8 @@ interface InventoryLocalRepository : ContentLocalRepository { fun getOwnedPets(userID: String): Flow> fun getInAppRewards(): Flow> + fun getInAppReward(key: String): Flow + fun getQuestContent(key: String): Flow fun getQuestContent(keys: List): Flow> diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmInventoryLocalRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmInventoryLocalRepository.kt index f1cc2859d..58c3c2812 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmInventoryLocalRepository.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmInventoryLocalRepository.kt @@ -57,7 +57,7 @@ class RealmInventoryLocalRepository(realm: Realm) : .filter { it.isLoaded } } - override fun getArmoireRemainingCount(): Long { + override fun getArmoireRemainingCount(): Flow { return realm.where(Equipment::class.java) .equalTo("klass", "armoire") .beginGroup() @@ -65,7 +65,9 @@ class RealmInventoryLocalRepository(realm: Realm) : .or() .isNull("owned") .endGroup() - .count() + .findAll() + .toFlow() + .map { it.count() } } override fun getOwnedEquipment(type: String): Flow> { @@ -318,6 +320,16 @@ class RealmInventoryLocalRepository(realm: Realm) : .filter { it.isLoaded } } + override fun getInAppReward(key: String): Flow { + return realm.where(ShopItem::class.java) + .equalTo("key", key) + .findAll() + .toFlow() + .filter { it.isLoaded } + .map { it.firstOrNull() } + .filterNotNull() + } + override fun saveInAppRewards(onlineItems: List) { val localItems = realm.where(ShopItem::class.java).findAll().createSnapshot() executeTransaction { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ArmoireActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ArmoireActivity.kt index 57706b907..edce32582 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ArmoireActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ArmoireActivity.kt @@ -27,6 +27,7 @@ import com.habitrpg.common.habitica.helpers.launchCatching import com.plattysoft.leonids.ParticleSystem import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.MainScope +import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.launch import java.util.Locale import javax.inject.Inject @@ -66,9 +67,11 @@ class ArmoireActivity : BaseActivity() { if (gold == null) { gold = user?.stats?.gp } - val remaining = inventoryRepository.getArmoireRemainingCount() - binding.equipmentCountView.text = getString(R.string.equipment_remaining, remaining) - binding.noEquipmentView.visibility = if (remaining > 0) View.GONE else View.VISIBLE + lifecycleScope.launchCatching { + val remaining = inventoryRepository.getArmoireRemainingCount().firstOrNull() ?: 0 + binding.equipmentCountView.text = getString(R.string.equipment_remaining, remaining) + binding.noEquipmentView.visibility = if (remaining > 0) View.GONE else View.VISIBLE + } } if (appConfigManager.enableArmoireAds()) { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/DeathActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/DeathActivity.kt index 598772dda..7552d08cf 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/DeathActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/DeathActivity.kt @@ -35,7 +35,7 @@ class DeathActivity : BaseActivity() { @Inject lateinit var userViewModel: MainUserViewModel - override fun getLayoutResId(): Int = R.layout.activity_armoire + override fun getLayoutResId(): Int = R.layout.activity_death override fun getContentView(layoutResId: Int?): View { binding = ActivityDeathBinding.inflate(layoutInflater) @@ -66,7 +66,7 @@ class DeathActivity : BaseActivity() { binding.adButton.visibility = View.INVISIBLE } } - binding.adButton.updateForAdType(AdType.ARMOIRE, lifecycleScope) + binding.adButton.updateForAdType(AdType.FAINT, lifecycleScope) binding.adButton.setOnClickListener { binding.adButton.state = AdButton.State.LOADING handler.show() diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/ShopRecyclerAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/ShopRecyclerAdapter.kt index 2ac7cab06..5d61e83ea 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/ShopRecyclerAdapter.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/ShopRecyclerAdapter.kt @@ -6,7 +6,9 @@ import android.view.View import android.view.ViewGroup import android.widget.Button import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView.ViewHolder import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.databinding.ShopArmoireGearBinding import com.habitrpg.android.habitica.databinding.ShopHeaderBinding import com.habitrpg.android.habitica.extensions.inflate import com.habitrpg.android.habitica.helpers.MainNavigationController @@ -19,15 +21,18 @@ import com.habitrpg.android.habitica.ui.viewHolders.SectionViewHolder import com.habitrpg.android.habitica.ui.viewHolders.ShopItemViewHolder import com.habitrpg.android.habitica.ui.views.getTranslatedClassName import com.habitrpg.common.habitica.extensions.fromHtml +import com.habitrpg.common.habitica.extensions.loadImage -class ShopRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter() { +class ShopRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter() { + var armoireCount : Int = 0 var onNeedsRefresh: (() -> Unit)? = null var onShowPurchaseDialog: ((ShopItem, Boolean) -> Unit)? = null private val items: MutableList = ArrayList() private var shopIdentifier: String? = null private var ownedItems: Map = HashMap() + var armoireItem: ShopItem? = null var changeClassEvents: ((String) -> Unit)? = null @@ -88,11 +93,18 @@ class ShopRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter ShopHeaderViewHolder(parent) 1 -> SectionViewHolder(parent.inflate(R.layout.shop_section_header)) 2 -> EmptyStateViewHolder(parent.inflate(emptyViewResource)) + 3 -> { + val viewHolder = ArmoireGearViewHolder(parent.inflate(R.layout.shop_armoire_gear)) + viewHolder.itemView.setOnClickListener { + armoireItem?.let { it1 -> onShowPurchaseDialog?.invoke(it1, true) } + } + viewHolder + } else -> { val view = parent.inflate(R.layout.row_shopitem) val viewHolder = ShopItemViewHolder(view) @@ -105,28 +117,27 @@ class ShopRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter (obj as? Shop)?.let { (holder as? ShopHeaderViewHolder)?.bind(it, shopSpriteSuffix) } - ShopCategory::class.java -> { - val category = obj as? ShopCategory + when (obj) { + is Shop -> (holder as? ShopHeaderViewHolder)?.bind(obj, shopSpriteSuffix) + is ShopCategory -> { val sectionHolder = holder as? SectionViewHolder ?: return - sectionHolder.bind(category?.text ?: "") - if (gearCategories.contains(category)) { + sectionHolder.bind(obj.text) + if (gearCategories.contains(obj)) { context?.let { context -> val adapter = HabiticaClassArrayAdapter(context, R.layout.class_spinner_dropdown_item, gearCategories.map { it.identifier }) sectionHolder.spinnerAdapter = adapter - sectionHolder.selectedItem = gearCategories.indexOf(category) + sectionHolder.selectedItem = gearCategories.indexOf(obj) sectionHolder.spinnerSelectionChanged = { if (selectedGearCategory != gearCategories[holder.selectedItem].identifier) { selectedGearCategory = gearCategories[holder.selectedItem].identifier } } - if (user?.stats?.habitClass != category?.identifier && category?.identifier != "none") { + if (user?.stats?.habitClass != obj.identifier && obj.identifier != "none") { if (user?.hasClass == true) { sectionHolder.switchClassButton?.setOnClickListener { changeClassEvents?.invoke(selectedGearCategory) @@ -146,14 +157,14 @@ class ShopRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter { - val item = obj as? ShopItem ?: return + is ShopItem -> { val itemHolder = holder as? ShopItemViewHolder ?: return - val numberOwned = ownedItems[item.key + "-" + item.purchaseType]?.numberOwned ?: 0 - itemHolder.bind(item, item.canAfford(user, 1), numberOwned) - itemHolder.isPinned = pinnedItemKeys.contains(item.key) + val numberOwned = ownedItems[obj.key + "-" + obj.purchaseType]?.numberOwned ?: 0 + itemHolder.bind(obj, obj.canAfford(user, 1), numberOwned) + itemHolder.isPinned = pinnedItemKeys.contains(obj.key) } - String::class.java -> (holder as? EmptyStateViewHolder)?.text = obj as? String + is String -> (holder as? EmptyStateViewHolder)?.text = obj + is Pair<*, *> -> (holder as? ArmoireGearViewHolder)?.bind(obj.first as? String ?: "", obj.second as? Int ?: 0) } } } @@ -167,14 +178,15 @@ class ShopRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter { - val category = getSelectedShopCategory() - category?.text = context?.getString(R.string.class_equipment) ?: "" - category + selectedGearCategory?.text = context?.getString(R.string.class_equipment) ?: "" + selectedGearCategory } - (getSelectedShopCategory()?.items?.size ?: 0) <= position - 2 -> return context?.getString(R.string.equipment_empty) - else -> getSelectedShopCategory()?.items?.get(position - 2) + (selectedGearCategory?.items?.size ?: 0) <= position - 2 -> return Pair( + context?.resources?.let { getTranslatedClassName(it, selectedGearCategory?.identifier) } ?: selectedGearCategory?.identifier, armoireCount) + else -> selectedGearCategory?.items?.get(position - 2) } } else { val itemPosition = position - getGearItemCount() @@ -185,10 +197,11 @@ class ShopRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter 0 - ShopCategory::class.java -> 1 - ShopItem::class.java -> 3 + override fun getItemViewType(position: Int): Int = when (getItem(position)) { + is Shop -> 0 + is ShopCategory -> 1 + is Pair<*, *> -> 3 + is ShopItem -> 4 else -> 2 } @@ -233,7 +246,7 @@ class ShopRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter() adapter?.changeClassEvents = { showClassChangeDialog(it) } + + lifecycleScope.launchCatching { + inventoryRepository.getInAppReward("armoire").collect { + adapter?.armoireItem = it + } + } + lifecycleScope.launchCatching { + inventoryRepository.getArmoireRemainingCount().collect { + adapter?.armoireCount = it + } + } } if (binding?.recyclerView?.layoutManager == null) { layoutManager = GridLayoutManager(context, 2) layoutManager?.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() { override fun getSpanSize(position: Int): Int { - return if ((adapter?.getItemViewType(position) ?: 0) < 3) { + return if ((adapter?.getItemViewType(position) ?: 0) < 4) { layoutManager?.spanCount ?: 1 } else { 1 diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/MainActivityViewModel.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/MainActivityViewModel.kt index e1d2cedf3..91802037c 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/MainActivityViewModel.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/MainActivityViewModel.kt @@ -113,6 +113,7 @@ class MainActivityViewModel @Inject constructor( pushNotificationManager.addPushDeviceUsingStoredToken() } } + inventoryRepository.retrieveInAppRewards() contentRepository.retrieveContent() } viewModelScope.launchCatching { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/shops/PurchaseDialog.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/shops/PurchaseDialog.kt index ac613f40e..f2f8ea9a1 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/shops/PurchaseDialog.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/shops/PurchaseDialog.kt @@ -255,6 +255,10 @@ class PurchaseDialog(context: Context, private val userRepository : UserReposito lifecycleScope.launchCatching { userRepository.getUser().filterNotNull().collect { setUser(it) } } + + if (item.key == "armoire") { + pinButton.visibility = View.GONE + } } private fun setUser(user: User) {