From 5bbba6552da4e8f169fd1c46bb1ad62dd9c8b127 Mon Sep 17 00:00:00 2001 From: Phillip Thelen Date: Fri, 1 Oct 2021 18:49:29 +0200 Subject: [PATCH] Fixes #1616 --- .../ui/adapter/NavigationDrawerAdapter.kt | 25 ++- .../ui/fragments/NavigationDrawerFragment.kt | 145 ++++++++++++------ 2 files changed, 117 insertions(+), 53 deletions(-) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/NavigationDrawerAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/NavigationDrawerAdapter.kt index 2897dadff..6d48c28cb 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/NavigationDrawerAdapter.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/NavigationDrawerAdapter.kt @@ -33,7 +33,11 @@ class NavigationDrawerAdapter(tintColor: Int, backgroundTintColor: Int) : Recycl var backgroundTintColor: Int = backgroundTintColor set(value) { field = value - notifyDataSetChanged() + for (item in items) { + if (item.isHeader) { + notifyItemChanged(getVisibleItemPosition(item.identifier)) + } + } } internal val items: MutableList = ArrayList() @@ -56,11 +60,18 @@ class NavigationDrawerAdapter(tintColor: Int, backgroundTintColor: Int) : Recycl private fun getItemPosition(identifier: String): Int = items.indexOfFirst { it.identifier == identifier } + private fun getVisibleItemPosition(identifier: String): Int = + items.filter { it.isVisible }.indexOfFirst { it.identifier == identifier } fun updateItem(item: HabiticaDrawerItem) { val position = getItemPosition(item.identifier) - items[position] = item - notifyDataSetChanged() + if (position == -1) { + items.add(item) + notifyItemInserted(items.size - 1) + } else { + items[position] = item + notifyItemChanged(getVisibleItemPosition(item.identifier)) + } } fun updateItems(newItems: List) { @@ -83,14 +94,16 @@ class NavigationDrawerAdapter(tintColor: Int, backgroundTintColor: Int) : Recycl if (teamHeaderIndex != -1 && nextHeaderIndex != -1) { for (x in nextHeaderIndex - 1 downTo teamHeaderIndex + 1) { items.removeAt(x) + notifyItemRemoved(x) } for ((index, team) in teams.withIndex()) { val item = HabiticaDrawerItem(R.id.teamBoardFragment, team.id, team.summary) item.bundle = bundleOf(Pair("teamID", team.id)) - items.add(teamHeaderIndex + index + 1, item) + val newIndex = teamHeaderIndex + index + 1 + items.add(newIndex, item) + notifyItemInserted(newIndex) } } - notifyDataSetChanged() } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { @@ -99,7 +112,6 @@ class NavigationDrawerAdapter(tintColor: Int, backgroundTintColor: Int) : Recycl getItemViewType(position) == 0 -> { val itemHolder = holder as? DrawerItemViewHolder itemHolder?.tintColor = tintColor - itemHolder?.backgroundTintColor = backgroundTintColor itemHolder?.bind(drawerItem, drawerItem.transitionId == selectedItem) itemHolder?.itemView?.setOnClickListener { itemSelectedEvents.onNext(drawerItem) } } @@ -168,7 +180,6 @@ class NavigationDrawerAdapter(tintColor: Int, backgroundTintColor: Int) : Recycl class DrawerItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { var tintColor: Int = 0 - var backgroundTintColor: Int = 0 private val titleTextView: TextView? = itemView.findViewById(R.id.titleTextView) private val pillView: TextView? = itemView.findViewById(R.id.pillView) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/NavigationDrawerFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/NavigationDrawerFragment.kt index ee260d8bd..836c98900 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/NavigationDrawerFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/NavigationDrawerFragment.kt @@ -49,10 +49,8 @@ import java.util.* import java.util.concurrent.TimeUnit import javax.inject.Inject import kotlin.collections.ArrayList +import kotlin.time.Duration import kotlin.time.ExperimentalTime -import kotlin.time.hours -import kotlin.time.minutes -import kotlin.time.seconds class NavigationDrawerFragment : DialogFragment() { @@ -110,22 +108,22 @@ class NavigationDrawerFragment : DialogFragment() { adapter.backgroundTintColor = it.getThemeColor(R.attr.colorPrimary) } } - adapter.items.filter { it.identifier == SIDEBAR_TAVERN }.forEach { - it.subtitle = null + getItemWithIdentifier(SIDEBAR_TAVERN)?.let { tavern -> + tavern.subtitle = context?.getString(R.string.active_world_boss) + adapter.updateItem(tavern) } return } binding?.questMenuView?.visibility = View.VISIBLE - binding?.menuHeaderView?.setBackgroundColor(questContent.colors?.darkColor ?: 0) binding?.questMenuView?.configure(quest) binding?.questMenuView?.configure(questContent) - adapter.tintColor = questContent.colors?.extraLightColor ?: 0 - adapter.backgroundTintColor = questContent.colors?.darkColor ?: 0 - - binding?.messagesBadge?.visibility = View.GONE - binding?.settingsBadge?.visibility = View.GONE - binding?.notificationsBadge?.visibility = View.GONE + questContent.colors?.let { colors -> + binding?.menuHeaderView?.setBackgroundColor(colors.darkColor) + adapter.tintColor = colors.extraLightColor + adapter.backgroundTintColor = colors.darkColor + } + binding?.questMenuView?.setBackgroundColor(context?.getThemeColor(R.attr.colorPrimaryDark) ?: 0) /* Reenable this once the boss art can be displayed correctly. @@ -135,18 +133,18 @@ class NavigationDrawerFragment : DialogFragment() { } else { questMenuView.showBossArt() }*/ - binding?.questMenuView?.hideBossArt() - - adapter.items.filter { it.identifier == SIDEBAR_TAVERN }.forEach { - it.subtitle = context?.getString(R.string.active_world_boss) - } - adapter.notifyDataSetChanged() + //binding?.questMenuView?.hideBossArt() + /*getItemWithIdentifier(SIDEBAR_TAVERN)?.let { tavern -> + tavern.subtitle = context?.getString(R.string.active_world_boss) + adapter.updateItem(tavern) + }*/ binding?.questMenuView?.setOnClickListener { - val context = this.context + setSelection(R.id.partyFragment) + /*val context = this.context if (context != null) { TavernDetailFragment.showWorldBossInfoDialog(context, questContent) - } + }*/ } } @@ -174,7 +172,7 @@ class NavigationDrawerFragment : DialogFragment() { savedInstanceState: Bundle? ): View? = inflater.inflate(R.layout.drawer_main, container, false) as? ViewGroup - private var seasonalShopJob: Job? = null + private var updatingJobs = mutableMapOf() @OptIn(ExperimentalTime::class) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -200,7 +198,6 @@ class NavigationDrawerFragment : DialogFragment() { putBoolean("hide$it", true) } updatePromo() - adapter.notifyDataSetChanged() }, RxErrorHandler.handleEmptyError() ) @@ -211,14 +208,25 @@ class NavigationDrawerFragment : DialogFragment() { .doOnNext { quest = it.quest } .filter { it.hasActiveQuest } .flatMapMaybe { inventoryRepository.getQuestContent(it.quest?.key ?: "").firstElement() } - .subscribe( - { + .subscribe({ questContent = it }, RxErrorHandler.handleEmptyError() ) ) + subscriptions?.add( + socialRepository.getGroup(Group.TAVERN_ID) + .doOnNext { quest = it.quest } + .filter { it.hasActiveQuest } + .flatMapMaybe { inventoryRepository.getQuestContent(it.quest?.key ?: "").firstElement() } + .subscribe({ + questContent = it + }, + RxErrorHandler.handleEmptyError() + ) + ) + subscriptions?.add( Flowable.combineLatest( contentRepository.getWorldState(), inventoryRepository.getAvailableLimitedItems(), @@ -227,15 +235,14 @@ class NavigationDrawerFragment : DialogFragment() { } ).subscribe( { pair -> - updateSeasonalMenuEntries(pair.first, pair.second) - seasonalShopJob?.cancel() - seasonalShopJob = lifecycleScope.launch(Dispatchers.Main) { - val gearEvent = pair.first.events.firstOrNull { it.gear } - while (gearEvent?.end?.after(Date()) == true || pair.second.isNotEmpty()) { - updateSeasonalMenuEntries(pair.first, pair.second) - val diff = (gearEvent?.end?.time ?: 0) - Date().time - delay(if (diff < (1.hours.inMilliseconds)) 1.seconds else 1.minutes) - } + val gearEvent = pair.first.events.firstOrNull { it.gear } + createUpdatingJob("seasonal", { + gearEvent?.end?.after(Date()) == true || pair.second.isNotEmpty() + }, { + val diff = (gearEvent?.end?.time ?: 0) - Date().time + if (diff < (Duration.hours(1).inWholeMilliseconds)) Duration.seconds(1) else Duration.minutes(1) + }) { + updateSeasonalMenuEntries(pair.first, pair.second) } }, RxErrorHandler.handleEmptyError() @@ -259,9 +266,20 @@ class NavigationDrawerFragment : DialogFragment() { } subscriptions?.add( - userRepository.getUser().subscribe( - { + userRepository.getUser() + .doOnNext { updateUser(it) + } + .flatMap { socialRepository.getGroup(it.party?.id ?: "") } + .filter { it.quest?.key != null } + .map { + quest = it.quest + it.quest?.key ?: "" + } + .flatMapMaybe { inventoryRepository.getQuestContent(it).firstElement() } + .subscribe( + { + questContent = it }, RxErrorHandler.handleEmptyError() ) @@ -272,6 +290,25 @@ class NavigationDrawerFragment : DialogFragment() { binding?.notificationsButtonWrapper?.setOnClickListener { startNotificationsActivity() } } + @OptIn(ExperimentalTime::class) + private fun createUpdatingJob( + key: String, + endingCondition: () -> Boolean, + delayFunc: () -> Duration, + function: () -> Unit + ) { + function() + if (updatingJobs[key]?.isActive == true) { + updatingJobs[key]?.cancel() + } + updatingJobs[key] = lifecycleScope.launch(Dispatchers.Main) { + while (endingCondition()) { + function() + delay(delayFunc()) + } + } + } + private fun updateSeasonalMenuEntries(worldState: WorldState, items: List) { val market = getItemWithIdentifier(SIDEBAR_SHOPS_MARKET) ?: return if (items.isNotEmpty() && items.firstOrNull()?.event?.end?.after(Date()) == true) { @@ -281,6 +318,7 @@ class NavigationDrawerFragment : DialogFragment() { market.pillText = null market.subtitle = null } + adapter.updateItem(market) val shop = getItemWithIdentifier(SIDEBAR_SHOPS_SEASONAL) ?: return shop.pillText = context?.getString(R.string.open) @@ -291,7 +329,7 @@ class NavigationDrawerFragment : DialogFragment() { } else { shop.isVisible = false } - adapter.notifyDataSetChanged() + adapter.updateItem(shop) } private fun updateUser(user: User) { @@ -399,6 +437,8 @@ class NavigationDrawerFragment : DialogFragment() { socialRepository.close() inventoryRepository.close() userRepository.close() + updatingJobs.forEach { it.value.cancel() } + updatingJobs.clear() super.onDestroy() } @@ -606,24 +646,38 @@ class NavigationDrawerFragment : DialogFragment() { } } + @OptIn(ExperimentalTime::class) fun updatePromo() { activePromo = configManager.activePromo() val promoItem = getItemWithIdentifier(SIDEBAR_PROMO) ?: return - if (activePromo != null && !sharedPreferences.getBoolean("hide${activePromo?.identifier}", false)) { - promoItem.isVisible = true - adapter.activePromo = activePromo + activePromo?.let { activePromo -> + if (sharedPreferences.getBoolean("hide${activePromo.identifier}", false)) { + promoItem.isVisible = true + adapter.activePromo = activePromo + } else { + promoItem.isVisible = false + } var promotedItem: HabiticaDrawerItem? = null - if (activePromo?.promoType == PromoType.GEMS_AMOUNT || activePromo?.promoType == PromoType.GEMS_PRICE) { + if (activePromo.promoType == PromoType.GEMS_AMOUNT || activePromo.promoType == PromoType.GEMS_PRICE) { promotedItem = getItemWithIdentifier(SIDEBAR_GEMS) } - if (activePromo?.promoType == PromoType.SUBSCRIPTION) { + if (activePromo.promoType == PromoType.SUBSCRIPTION) { promotedItem = getItemWithIdentifier(SIDEBAR_SUBSCRIPTION) } - promotedItem?.pillText = context?.getString(R.string.sale) - promotedItem?.pillBackground = context?.let { activePromo?.pillBackgroundDrawable(it) } - promotedItem?.let { updateItem(it) } - } else { + if (promotedItem == null) return@let + promotedItem.pillText = context?.getString(R.string.sale) + promotedItem.pillBackground = context?.let { activePromo.pillBackgroundDrawable(it) } + createUpdatingJob(activePromo.promoType.name, { + activePromo.endDate.after(Date()) + }, { + val diff = activePromo.endDate.time - Date().time + if (diff < (Duration.hours(1).inWholeMilliseconds)) Duration.seconds(1) else Duration.minutes(1) + }) { + promotedItem.subtitle = context?.getString(R.string.open_for, activePromo.endDate.getShortRemainingString()) + updateItem(promotedItem) + } + } ?: run { promoItem.isVisible = false } updateItem(promoItem) @@ -653,7 +707,6 @@ class NavigationDrawerFragment : DialogFragment() { const val SIDEBAR_GEMS = "gems" const val SIDEBAR_SUBSCRIPTION = "subscription" const val SIDEBAR_SUBSCRIPTION_PROMO = "subscriptionpromo" - const val SIDEBAR_G1G1_PROMO = "g1g1promo" const val SIDEBAR_PROMO = "promo" const val SIDEBAR_ADVENTURE_GUIDE = "adventureguide" const val SIDEBAR_ABOUT_HEADER = "about_header"