diff --git a/app/lint-baseline.xml b/app/lint-baseline.xml index 03638736..a02d2c01 100644 --- a/app/lint-baseline.xml +++ b/app/lint-baseline.xml @@ -113,7 +113,7 @@ errorLine2=" ~~~~~~~"> @@ -311,7 +311,7 @@ errorLine2=" ^"> @@ -355,7 +355,7 @@ errorLine2=" ^"> @@ -366,7 +366,7 @@ errorLine2=" ^"> @@ -388,7 +388,7 @@ errorLine2=" ^"> @@ -399,7 +399,7 @@ errorLine2=" ^"> @@ -410,7 +410,7 @@ errorLine2=" ^"> @@ -432,7 +432,7 @@ errorLine2=" ^"> @@ -454,7 +454,7 @@ errorLine2=" ^"> @@ -465,7 +465,7 @@ errorLine2=" ^"> @@ -476,7 +476,7 @@ errorLine2=" ^"> @@ -487,7 +487,7 @@ errorLine2=" ^"> @@ -498,7 +498,7 @@ errorLine2=" ^"> @@ -509,7 +509,7 @@ errorLine2=" ^"> @@ -520,7 +520,7 @@ errorLine2=" ^"> @@ -531,7 +531,7 @@ errorLine2=" ^"> @@ -542,7 +542,7 @@ errorLine2=" ^"> @@ -553,7 +553,7 @@ errorLine2=" ^"> @@ -564,7 +564,7 @@ errorLine2=" ^"> @@ -575,7 +575,7 @@ errorLine2=" ^"> @@ -586,7 +586,7 @@ errorLine2=" ^"> @@ -597,7 +597,7 @@ errorLine2=" ^"> @@ -784,7 +784,7 @@ errorLine2=" ^"> @@ -795,7 +795,7 @@ errorLine2=" ^"> @@ -806,7 +806,7 @@ errorLine2=" ^"> @@ -817,7 +817,7 @@ errorLine2=" ^"> @@ -828,7 +828,7 @@ errorLine2=" ^"> @@ -839,7 +839,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -850,7 +850,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -894,7 +894,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -905,7 +905,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -960,7 +960,7 @@ errorLine2=" ^"> @@ -971,7 +971,7 @@ errorLine2=" ^"> @@ -982,7 +982,7 @@ errorLine2=" ^"> @@ -993,7 +993,7 @@ errorLine2=" ^"> @@ -1004,7 +1004,7 @@ errorLine2=" ^"> @@ -1015,7 +1015,7 @@ errorLine2=" ^"> @@ -1026,7 +1026,7 @@ errorLine2=" ^"> @@ -1037,7 +1037,7 @@ errorLine2=" ^"> @@ -1048,7 +1048,7 @@ errorLine2=" ^"> @@ -1059,7 +1059,7 @@ errorLine2=" ^"> @@ -1070,7 +1070,7 @@ errorLine2=" ^"> @@ -1081,7 +1081,7 @@ errorLine2=" ^"> @@ -1092,7 +1092,7 @@ errorLine2=" ^"> @@ -1103,7 +1103,7 @@ errorLine2=" ^"> @@ -1114,21 +1114,21 @@ errorLine2=" ^"> + + + + - - - - @@ -1147,7 +1147,7 @@ errorLine2=" ^"> @@ -1158,7 +1158,7 @@ errorLine2=" ^"> @@ -1169,7 +1169,7 @@ errorLine2=" ^"> @@ -1180,7 +1180,7 @@ errorLine2=" ^"> @@ -1191,7 +1191,7 @@ errorLine2=" ^"> @@ -1202,7 +1202,7 @@ errorLine2=" ^"> @@ -1213,7 +1213,7 @@ errorLine2=" ^"> @@ -1224,7 +1224,7 @@ errorLine2=" ^"> @@ -1235,7 +1235,7 @@ errorLine2=" ^"> @@ -1246,7 +1246,7 @@ errorLine2=" ^"> @@ -1257,7 +1257,7 @@ errorLine2=" ^"> @@ -1268,7 +1268,7 @@ errorLine2=" ^"> @@ -1279,7 +1279,7 @@ errorLine2=" ^"> @@ -1290,7 +1290,7 @@ errorLine2=" ^"> @@ -1301,7 +1301,7 @@ errorLine2=" ^"> @@ -1312,7 +1312,7 @@ errorLine2=" ^"> @@ -1323,7 +1323,7 @@ errorLine2=" ^"> @@ -1334,7 +1334,7 @@ errorLine2=" ^"> @@ -1345,7 +1345,7 @@ errorLine2=" ^"> @@ -1356,7 +1356,7 @@ errorLine2=" ^"> @@ -1367,7 +1367,7 @@ errorLine2=" ^"> @@ -1378,7 +1378,7 @@ errorLine2=" ^"> @@ -1389,7 +1389,7 @@ errorLine2=" ^"> @@ -1400,7 +1400,7 @@ errorLine2=" ^"> @@ -1411,7 +1411,7 @@ errorLine2=" ^"> @@ -1422,7 +1422,7 @@ errorLine2=" ^"> @@ -1433,7 +1433,7 @@ errorLine2=" ^"> @@ -2548,17 +2548,6 @@ column="13"/> - - - - @@ -2588,7 +2577,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2599,7 +2588,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~"> @@ -2610,7 +2599,7 @@ errorLine2=" ~~~~~~~~~~~~"> @@ -2621,7 +2610,7 @@ errorLine2=" ~~~~~~~~~~~~~~"> @@ -2632,7 +2621,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2643,7 +2632,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2654,7 +2643,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~"> @@ -2665,7 +2654,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2676,7 +2665,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~"> @@ -2687,7 +2676,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2698,7 +2687,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2709,7 +2698,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~"> @@ -2720,7 +2709,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2731,7 +2720,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2742,7 +2731,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2753,7 +2742,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~"> @@ -2764,7 +2753,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2775,7 +2764,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2786,7 +2775,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2797,7 +2786,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -4157,7 +4146,7 @@ + errorLine1=" binding.recyclerView.scrollBy(0, Utils.dpToPx(requireContext(), -30))" + errorLine2=" ~~~~~~~"> + line="215" + column="29"/> @@ -4645,29 +4634,29 @@ errorLine2=" ~~~~~~~"> @@ -4677,8 +4666,8 @@ errorLine1=" binding.totalAccounts.text = formatNumber(totalAccounts)" errorLine2=" ~~~~~~~~~~~~"> @@ -4688,8 +4677,8 @@ errorLine1=" binding.totalAccounts.text = formatNumber(totalAccounts)" errorLine2=" ~~~~~~~~~~~~"> @@ -4843,7 +4832,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -4854,7 +4843,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -4865,7 +4854,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -4876,7 +4865,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -4920,7 +4909,7 @@ errorLine2=" ~~~~~~~~~~"> @@ -5019,7 +5008,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~"> @@ -5030,7 +5019,7 @@ errorLine2=" ~~~~~~~~~"> @@ -5041,7 +5030,7 @@ errorLine2=" ~~~~~~~~~"> @@ -5052,7 +5041,7 @@ errorLine2=" ~~~~~~~~~"> @@ -5063,7 +5052,7 @@ errorLine2=" ~~~~~~~~~"> @@ -5074,18 +5063,18 @@ errorLine2=" ~~~~~~~~~"> @@ -5118,7 +5107,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~"> @@ -5129,7 +5118,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~"> @@ -5140,7 +5129,7 @@ errorLine2=" ^"> @@ -5151,7 +5140,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -5162,7 +5151,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~"> @@ -5173,7 +5162,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -5184,7 +5173,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~"> @@ -5195,17 +5184,10 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - - - - @@ -5686,7 +5668,7 @@ errorLine2=" ~~~~~~~~"> @@ -5697,7 +5679,7 @@ errorLine2=" ~~~~~~~~"> @@ -5708,7 +5690,7 @@ errorLine2=" ~~~~~~~~"> @@ -6192,7 +6174,7 @@ errorLine2=" ~~~~~~~~"> @@ -6203,7 +6185,7 @@ errorLine2=" ~~~~~~~~"> @@ -6214,7 +6196,7 @@ errorLine2=" ~~~~~~~~"> @@ -6225,7 +6207,7 @@ errorLine2=" ~~~~~~~~"> @@ -6588,7 +6570,7 @@ errorLine2=" ~~~~~~~~~"> @@ -6702,28 +6684,6 @@ column="9"/> - - - - - - - - @@ -7369,7 +7329,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~"> @@ -7380,7 +7340,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~"> @@ -7391,7 +7351,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~"> @@ -7402,7 +7362,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~"> @@ -7413,7 +7373,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~"> @@ -7424,7 +7384,7 @@ errorLine2=" ~~~~~~"> @@ -7435,7 +7395,7 @@ errorLine2=" ~~~~~~"> @@ -7446,7 +7406,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~"> @@ -7457,7 +7417,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~"> @@ -7468,7 +7428,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~"> @@ -7479,7 +7439,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~"> @@ -7490,7 +7450,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~"> @@ -7501,7 +7461,7 @@ errorLine2=" ~~~~~~~~~~~~"> @@ -7512,7 +7472,7 @@ errorLine2=" ~~~~~~~"> @@ -7523,7 +7483,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~"> @@ -7534,7 +7494,7 @@ errorLine2=" ~~~~~~~~~~~~"> @@ -7545,7 +7505,7 @@ errorLine2=" ~~~~~~~"> @@ -7556,7 +7516,7 @@ errorLine2=" ~~~~~~~~~~~~"> @@ -7567,7 +7527,7 @@ errorLine2=" ~~~~~~~"> @@ -7578,7 +7538,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~"> @@ -7589,7 +7549,7 @@ errorLine2=" ~~~~~~~~~~~~"> @@ -7600,7 +7560,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~"> @@ -7611,7 +7571,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~"> diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/TrendingTagViewHolder.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/TrendingTagViewHolder.kt deleted file mode 100644 index 71a83c98..00000000 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/TrendingTagViewHolder.kt +++ /dev/null @@ -1,71 +0,0 @@ -/* Copyright 2023 Tusky Contributors - * - * This file is a part of Tusky. - * - * This program is free software; you can redistribute it and/or modify it under the terms of the - * GNU General Public License as published by the Free Software Foundation; either version 3 of the - * License, or (at your option) any later version. - * - * Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even - * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General - * Public License for more details. - * - * You should have received a copy of the GNU General Public License along with Tusky; if not, - * see . */ - -package com.keylesspalace.tusky.adapter - -import androidx.recyclerview.widget.RecyclerView -import com.keylesspalace.tusky.R -import com.keylesspalace.tusky.databinding.ItemTrendingCellBinding -import com.keylesspalace.tusky.entity.TrendingTagHistory -import com.keylesspalace.tusky.interfaces.LinkListener -import com.keylesspalace.tusky.util.formatNumber -import com.keylesspalace.tusky.viewdata.TrendingViewData - -class TrendingTagViewHolder( - private val binding: ItemTrendingCellBinding -) : RecyclerView.ViewHolder(binding.root) { - - fun setup( - tagViewData: TrendingViewData.Tag, - maxTrendingValue: Long, - trendingListener: LinkListener - ) { - val reversedHistory = tagViewData.tag.history.reversed() - setGraph(reversedHistory, maxTrendingValue) - setTag(tagViewData.tag.name) - - val totalUsage = tagViewData.tag.history.sumOf { it.uses.toLongOrNull() ?: 0 } - binding.totalUsage.text = formatNumber(totalUsage) - - val totalAccounts = tagViewData.tag.history.sumOf { it.accounts.toLongOrNull() ?: 0 } - binding.totalAccounts.text = formatNumber(totalAccounts) - - binding.currentUsage.text = reversedHistory.last().uses - binding.currentAccounts.text = reversedHistory.last().accounts - - itemView.setOnClickListener { - trendingListener.onViewTag(tagViewData.tag.name) - } - - setAccessibility(totalAccounts, tagViewData.tag.name) - } - - private fun setGraph(history: List, maxTrendingValue: Long) { - binding.graph.maxTrendingValue = maxTrendingValue - binding.graph.primaryLineData = history - .mapNotNull { it.uses.toLongOrNull() } - binding.graph.secondaryLineData = history - .mapNotNull { it.accounts.toLongOrNull() } - } - - private fun setTag(tag: String) { - binding.tag.text = binding.root.context.getString(R.string.title_tag, tag) - } - - private fun setAccessibility(totalAccounts: Long, tag: String) { - itemView.contentDescription = - itemView.context.getString(R.string.accessibility_talking_about_tag, totalAccounts, tag) - } -} diff --git a/app/src/main/java/com/keylesspalace/tusky/components/trending/TrendingActivity.kt b/app/src/main/java/com/keylesspalace/tusky/components/trending/TrendingActivity.kt index e9bd3b70..3270e9c2 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/trending/TrendingActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/trending/TrendingActivity.kt @@ -19,23 +19,19 @@ import android.content.Context import android.content.Intent import android.os.Bundle import androidx.fragment.app.commit -import com.keylesspalace.tusky.BottomSheetActivity +import com.keylesspalace.tusky.BaseActivity import com.keylesspalace.tusky.R -import com.keylesspalace.tusky.appstore.EventHub import com.keylesspalace.tusky.databinding.ActivityTrendingBinding import com.keylesspalace.tusky.util.viewBinding import dagger.android.DispatchingAndroidInjector import dagger.android.HasAndroidInjector import javax.inject.Inject -class TrendingActivity : BottomSheetActivity(), HasAndroidInjector { +class TrendingActivity : BaseActivity(), HasAndroidInjector { @Inject lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector - @Inject - lateinit var eventHub: EventHub - private val binding: ActivityTrendingBinding by viewBinding(ActivityTrendingBinding::inflate) override fun onCreate(savedInstanceState: Bundle?) { @@ -44,10 +40,8 @@ class TrendingActivity : BottomSheetActivity(), HasAndroidInjector { setSupportActionBar(binding.includedToolbar.toolbar) - val title = getString(R.string.title_public_trending_hashtags) - supportActionBar?.run { - setTitle(title) + setTitle(R.string.title_public_trending_hashtags) setDisplayHomeAsUpEnabled(true) setDisplayShowHomeEnabled(true) } @@ -63,10 +57,6 @@ class TrendingActivity : BottomSheetActivity(), HasAndroidInjector { override fun androidInjector() = dispatchingAndroidInjector companion object { - const val TAG = "TrendingActivity" - - @JvmStatic - fun getIntent(context: Context) = - Intent(context, TrendingActivity::class.java) + fun getIntent(context: Context) = Intent(context, TrendingActivity::class.java) } } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/trending/TrendingAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/components/trending/TrendingAdapter.kt index 6f405e2f..b40d6767 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/trending/TrendingAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/trending/TrendingAdapter.kt @@ -20,15 +20,12 @@ import android.view.ViewGroup import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView -import com.keylesspalace.tusky.adapter.TrendingDateViewHolder -import com.keylesspalace.tusky.adapter.TrendingTagViewHolder import com.keylesspalace.tusky.databinding.ItemTrendingCellBinding import com.keylesspalace.tusky.databinding.ItemTrendingDateBinding -import com.keylesspalace.tusky.interfaces.LinkListener import com.keylesspalace.tusky.viewdata.TrendingViewData class TrendingAdapter( - private val trendingListener: LinkListener + private val onViewTag: (String) -> Unit ) : ListAdapter(TrendingDifferCallback) { init { @@ -42,7 +39,6 @@ class TrendingAdapter( ItemTrendingCellBinding.inflate(LayoutInflater.from(viewGroup.context)) TrendingTagViewHolder(binding) } - else -> { val binding = ItemTrendingDateBinding.inflate(LayoutInflater.from(viewGroup.context)) @@ -52,38 +48,15 @@ class TrendingAdapter( } override fun onBindViewHolder(viewHolder: RecyclerView.ViewHolder, position: Int) { - bindViewHolder(viewHolder, position, null) - } - - override fun onBindViewHolder( - viewHolder: RecyclerView.ViewHolder, - position: Int, - payloads: List<*> - ) { - bindViewHolder(viewHolder, position, payloads) - } - - private fun bindViewHolder( - viewHolder: RecyclerView.ViewHolder, - position: Int, - payloads: List<*>? - ) { - when (val header = getItem(position)) { + when (val viewData = getItem(position)) { is TrendingViewData.Tag -> { - val maxTrendingValue = currentList - .flatMap { trendingViewData -> - trendingViewData.asTagOrNull()?.tag?.history.orEmpty() - } - .mapNotNull { it.uses.toLongOrNull() } - .maxOrNull() ?: 1 - val holder = viewHolder as TrendingTagViewHolder - holder.setup(header, maxTrendingValue, trendingListener) + holder.setup(viewData, onViewTag) } is TrendingViewData.Header -> { val holder = viewHolder as TrendingDateViewHolder - holder.setup(header.start, header.end) + holder.setup(viewData.start, viewData.end) } } } @@ -112,14 +85,7 @@ class TrendingAdapter( oldItem: TrendingViewData, newItem: TrendingViewData ): Boolean { - return false - } - - override fun getChangePayload( - oldItem: TrendingViewData, - newItem: TrendingViewData - ): Any? { - return null + return oldItem == newItem } } } diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/TrendingDateViewHolder.kt b/app/src/main/java/com/keylesspalace/tusky/components/trending/TrendingDateViewHolder.kt similarity index 96% rename from app/src/main/java/com/keylesspalace/tusky/adapter/TrendingDateViewHolder.kt rename to app/src/main/java/com/keylesspalace/tusky/components/trending/TrendingDateViewHolder.kt index 48157306..5d9e3c3a 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/TrendingDateViewHolder.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/trending/TrendingDateViewHolder.kt @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky.adapter +package com.keylesspalace.tusky.components.trending import androidx.recyclerview.widget.RecyclerView import com.keylesspalace.tusky.R diff --git a/app/src/main/java/com/keylesspalace/tusky/components/trending/TrendingFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/trending/TrendingFragment.kt index 4148a108..bcb22d05 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/trending/TrendingFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/trending/TrendingFragment.kt @@ -15,17 +15,14 @@ package com.keylesspalace.tusky.components.trending -import android.content.Context import android.content.res.Configuration import android.os.Bundle import android.util.Log -import android.view.LayoutInflater import android.view.View -import android.view.ViewGroup import android.view.accessibility.AccessibilityManager import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment -import androidx.lifecycle.ViewModelProvider +import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup @@ -33,18 +30,14 @@ import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.SimpleItemAnimator import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener import at.connyduck.sparkbutton.helpers.Utils -import com.keylesspalace.tusky.BottomSheetActivity -import com.keylesspalace.tusky.PostLookupFallbackBehavior +import com.keylesspalace.tusky.BaseActivity import com.keylesspalace.tusky.R import com.keylesspalace.tusky.StatusListActivity -import com.keylesspalace.tusky.appstore.EventHub import com.keylesspalace.tusky.components.trending.viewmodel.TrendingViewModel import com.keylesspalace.tusky.databinding.FragmentTrendingBinding -import com.keylesspalace.tusky.db.AccountManager import com.keylesspalace.tusky.di.Injectable import com.keylesspalace.tusky.di.ViewModelFactory import com.keylesspalace.tusky.interfaces.ActionButtonActivity -import com.keylesspalace.tusky.interfaces.LinkListener import com.keylesspalace.tusky.interfaces.RefreshableFragment import com.keylesspalace.tusky.interfaces.ReselectableFragment import com.keylesspalace.tusky.util.hide @@ -56,48 +49,20 @@ import kotlinx.coroutines.launch import javax.inject.Inject class TrendingFragment : - Fragment(), + Fragment(R.layout.fragment_trending), OnRefreshListener, - LinkListener, Injectable, ReselectableFragment, RefreshableFragment { - private lateinit var bottomSheetActivity: BottomSheetActivity - @Inject lateinit var viewModelFactory: ViewModelFactory - @Inject - lateinit var accountManager: AccountManager - - @Inject - lateinit var eventHub: EventHub - - private val viewModel: TrendingViewModel by lazy { - ViewModelProvider(this, viewModelFactory)[TrendingViewModel::class.java] - } + private val viewModel: TrendingViewModel by viewModels { viewModelFactory } private val binding by viewBinding(FragmentTrendingBinding::bind) - private lateinit var adapter: TrendingAdapter - - override fun onAttach(context: Context) { - super.onAttach(context) - bottomSheetActivity = if (context is BottomSheetActivity) { - context - } else { - throw IllegalStateException("Fragment must be attached to a BottomSheetActivity!") - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - adapter = TrendingAdapter( - this - ) - } + private val adapter = TrendingAdapter(::onViewTag) override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) @@ -106,14 +71,6 @@ class TrendingFragment : setupLayoutManager(columnCount) } - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - return inflater.inflate(R.layout.fragment_trending, container, false) - } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { setupSwipeRefreshLayout() setupRecyclerView() @@ -175,25 +132,19 @@ class TrendingFragment : } override fun onRefresh() { - viewModel.invalidate() + viewModel.invalidate(true) } - override fun onViewUrl(url: String) { - bottomSheetActivity.viewUrl(url, PostLookupFallbackBehavior.OPEN_IN_BROWSER) - } - - override fun onViewTag(tag: String) { - bottomSheetActivity.startActivityWithSlideInAnimation(StatusListActivity.newHashtagIntent(requireContext(), tag)) - } - - override fun onViewAccount(id: String) { - bottomSheetActivity.viewAccount(id) + fun onViewTag(tag: String) { + (requireActivity() as BaseActivity).startActivityWithSlideInAnimation(StatusListActivity.newHashtagIntent(requireContext(), tag)) } private fun processViewState(uiState: TrendingViewModel.TrendingUiState) { + Log.d(TAG, uiState.loadingState.name) when (uiState.loadingState) { TrendingViewModel.LoadingState.INITIAL -> clearLoadingState() TrendingViewModel.LoadingState.LOADING -> applyLoadingState() + TrendingViewModel.LoadingState.REFRESHING -> applyRefreshingState() TrendingViewModel.LoadingState.LOADED -> applyLoadedState(uiState.trendingViewData) TrendingViewModel.LoadingState.ERROR_NETWORK -> networkError() TrendingViewModel.LoadingState.ERROR_OTHER -> otherError() @@ -203,8 +154,9 @@ class TrendingFragment : private fun applyLoadedState(viewData: List) { clearLoadingState() + adapter.submitList(viewData) + if (viewData.isEmpty()) { - adapter.submitList(emptyList()) binding.recyclerView.hide() binding.messageView.show() binding.messageView.setup( @@ -213,16 +165,16 @@ class TrendingFragment : null ) } else { - val viewDataWithDates = listOf(viewData.first().asHeaderOrNull()) + viewData - - adapter.submitList(viewDataWithDates) - binding.recyclerView.show() binding.messageView.hide() } binding.progressBar.hide() } + private fun applyRefreshingState() { + binding.swipeRefreshLayout.isRefreshing = true + } + private fun applyLoadingState() { binding.recyclerView.hide() binding.messageView.hide() @@ -297,8 +249,6 @@ class TrendingFragment : companion object { private const val TAG = "TrendingFragment" - fun newInstance(): TrendingFragment { - return TrendingFragment() - } + fun newInstance() = TrendingFragment() } } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/trending/TrendingTagViewHolder.kt b/app/src/main/java/com/keylesspalace/tusky/components/trending/TrendingTagViewHolder.kt new file mode 100644 index 00000000..58aad9e8 --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/components/trending/TrendingTagViewHolder.kt @@ -0,0 +1,57 @@ +/* Copyright 2023 Tusky Contributors + * + * This file is a part of Tusky. + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Tusky; if not, + * see . */ + +package com.keylesspalace.tusky.components.trending + +import androidx.recyclerview.widget.RecyclerView +import com.keylesspalace.tusky.R +import com.keylesspalace.tusky.databinding.ItemTrendingCellBinding +import com.keylesspalace.tusky.util.formatNumber +import com.keylesspalace.tusky.viewdata.TrendingViewData + +class TrendingTagViewHolder( + private val binding: ItemTrendingCellBinding +) : RecyclerView.ViewHolder(binding.root) { + + fun setup( + tagViewData: TrendingViewData.Tag, + onViewTag: (String) -> Unit + ) { + binding.tag.text = binding.root.context.getString(R.string.title_tag, tagViewData.name) + + binding.graph.maxTrendingValue = tagViewData.maxTrendingValue + binding.graph.primaryLineData = tagViewData.usage + binding.graph.secondaryLineData = tagViewData.accounts + + binding.totalUsage.text = formatNumber(tagViewData.usage.sum(), 1000) + + val totalAccounts = tagViewData.accounts.sum() + binding.totalAccounts.text = formatNumber(totalAccounts, 1000) + + binding.currentUsage.text = tagViewData.usage.last().toString() + binding.currentAccounts.text = tagViewData.usage.last().toString() + + itemView.setOnClickListener { + onViewTag(tagViewData.name) + } + + itemView.contentDescription = + itemView.context.getString( + R.string.accessibility_talking_about_tag, + totalAccounts, + tagViewData.name + ) + } +} diff --git a/app/src/main/java/com/keylesspalace/tusky/components/trending/viewmodel/TrendingViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/trending/viewmodel/TrendingViewModel.kt index 7bf3aecf..d1877a2f 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/trending/viewmodel/TrendingViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/trending/viewmodel/TrendingViewModel.kt @@ -15,11 +15,15 @@ package com.keylesspalace.tusky.components.trending.viewmodel +import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import at.connyduck.calladapter.networkresult.fold import com.keylesspalace.tusky.appstore.EventHub import com.keylesspalace.tusky.appstore.PreferenceChangedEvent import com.keylesspalace.tusky.entity.Filter +import com.keylesspalace.tusky.entity.end +import com.keylesspalace.tusky.entity.start import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.util.toViewData import com.keylesspalace.tusky.viewdata.TrendingViewData @@ -28,7 +32,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.launch -import okio.IOException +import java.io.IOException import javax.inject.Inject class TrendingViewModel @Inject constructor( @@ -36,7 +40,7 @@ class TrendingViewModel @Inject constructor( private val eventHub: EventHub ) : ViewModel() { enum class LoadingState { - INITIAL, LOADING, LOADED, ERROR_NETWORK, ERROR_OTHER + INITIAL, LOADING, REFRESHING, LOADED, ERROR_NETWORK, ERROR_OTHER } data class TrendingUiState( @@ -67,37 +71,43 @@ class TrendingViewModel @Inject constructor( * * A tag is excluded if it is filtered by the user on their home timeline. */ - fun invalidate() = viewModelScope.launch { - _uiState.value = TrendingUiState(emptyList(), LoadingState.LOADING) - - try { - val deferredFilters = async { mastodonApi.getFilters() } - val response = mastodonApi.trendingTags() - if (!response.isSuccessful) { - _uiState.value = TrendingUiState(emptyList(), LoadingState.ERROR_NETWORK) - return@launch - } - - val homeFilters = deferredFilters.await().getOrNull()?.filter { - it.context.contains(Filter.Kind.HOME.kind) - } - - val tags = response.body()!! - .filter { - homeFilters?.none { filter -> - filter.keywords.any { keyword -> keyword.keyword.equals(it.name, ignoreCase = true) } - } ?: false - } - .sortedBy { tag -> tag.history.sumOf { it.uses.toLongOrNull() ?: 0 } } - .map { it.toViewData() } - .asReversed() - - _uiState.value = TrendingUiState(tags, LoadingState.LOADED) - } catch (e: IOException) { - _uiState.value = TrendingUiState(emptyList(), LoadingState.ERROR_NETWORK) - } catch (e: Exception) { - _uiState.value = TrendingUiState(emptyList(), LoadingState.ERROR_OTHER) + fun invalidate(refresh: Boolean = false) = viewModelScope.launch { + if (refresh) { + _uiState.value = TrendingUiState(emptyList(), LoadingState.REFRESHING) + } else { + _uiState.value = TrendingUiState(emptyList(), LoadingState.LOADING) } + + val deferredFilters = async { mastodonApi.getFilters() } + + mastodonApi.trendingTags().fold( + { tagResponse -> + val homeFilters = deferredFilters.await().getOrNull()?.filter { filter -> + filter.context.contains(Filter.Kind.HOME.kind) + } + val tags = tagResponse + .filter { tag -> + homeFilters?.none { filter -> + filter.keywords.any { keyword -> keyword.keyword.equals(tag.name, ignoreCase = true) } + } ?: false + } + .sortedByDescending { tag -> tag.history.sumOf { it.uses.toLongOrNull() ?: 0 } } + .toViewData() + + val firstTag = tagResponse.first() + val header = TrendingViewData.Header(firstTag.start(), firstTag.end()) + + _uiState.value = TrendingUiState(listOf(header) + tags, LoadingState.LOADED) + }, + { error -> + Log.w(TAG, "failed loading trending tags", error) + if (error is IOException) { + _uiState.value = TrendingUiState(emptyList(), LoadingState.ERROR_NETWORK) + } else { + _uiState.value = TrendingUiState(emptyList(), LoadingState.ERROR_OTHER) + } + } + ) } companion object { diff --git a/app/src/main/java/com/keylesspalace/tusky/entity/TrendingTagsResult.kt b/app/src/main/java/com/keylesspalace/tusky/entity/TrendingTagsResult.kt index 7baca6fe..78669555 100644 --- a/app/src/main/java/com/keylesspalace/tusky/entity/TrendingTagsResult.kt +++ b/app/src/main/java/com/keylesspalace/tusky/entity/TrendingTagsResult.kt @@ -21,15 +21,13 @@ import java.util.Date * Mastodon API Documentation: https://docs.joinmastodon.org/methods/trends/#tags * * @param name The name of the hashtag (after the #). The "caturday" in "#caturday". - * @param url The URL to your mastodon instance list for this hashtag. + * (@param url The URL to your mastodon instance list for this hashtag.) * @param history A list of [TrendingTagHistory]. Each element contains metrics per day for this hashtag. - * @param following This is not listed in the APIs at the time of writing, but an instance is delivering it. + * (@param following This is not listed in the APIs at the time of writing, but an instance is delivering it.) */ data class TrendingTag( val name: String, - val url: String, - val history: List, - val following: Boolean + val history: List ) /** diff --git a/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt b/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt index 3eb83840..a50ca796 100644 --- a/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt +++ b/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt @@ -782,5 +782,5 @@ interface MastodonApi { suspend fun unfollowTag(@Path("name") name: String): NetworkResult @GET("api/v1/trends/tags") - suspend fun trendingTags(): Response> + suspend fun trendingTags(): NetworkResult> } diff --git a/app/src/main/java/com/keylesspalace/tusky/util/ViewDataUtils.kt b/app/src/main/java/com/keylesspalace/tusky/util/ViewDataUtils.kt index 1d630f39..f6ae9e7d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/ViewDataUtils.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/ViewDataUtils.kt @@ -40,7 +40,6 @@ import com.keylesspalace.tusky.viewdata.NotificationViewData import com.keylesspalace.tusky.viewdata.StatusViewData import com.keylesspalace.tusky.viewdata.TrendingViewData -@JvmName("statusToViewData") fun Status.toViewData( isShowingContent: Boolean, isExpanded: Boolean, @@ -56,7 +55,6 @@ fun Status.toViewData( ) } -@JvmName("notificationToViewData") fun Notification.toViewData( isShowingContent: Boolean, isExpanded: Boolean, @@ -71,9 +69,20 @@ fun Notification.toViewData( ) } -@JvmName("tagToViewData") -fun TrendingTag.toViewData(): TrendingViewData.Tag { - return TrendingViewData.Tag( - tag = this - ) +fun List.toViewData(): List { + val maxTrendingValue = flatMap { tag -> tag.history } + .mapNotNull { it.uses.toLongOrNull() } + .maxOrNull() ?: 1 + + return map { tag -> + + val reversedHistory = tag.history.asReversed() + + TrendingViewData.Tag( + name = tag.name, + usage = reversedHistory.mapNotNull { it.uses.toLongOrNull() }, + accounts = reversedHistory.mapNotNull { it.accounts.toLongOrNull() }, + maxTrendingValue = maxTrendingValue + ) + } } diff --git a/app/src/main/java/com/keylesspalace/tusky/view/GraphView.kt b/app/src/main/java/com/keylesspalace/tusky/view/GraphView.kt index 565d58a3..2aecad08 100644 --- a/app/src/main/java/com/keylesspalace/tusky/view/GraphView.kt +++ b/app/src/main/java/com/keylesspalace/tusky/view/GraphView.kt @@ -22,10 +22,9 @@ import android.graphics.Path import android.graphics.PathMeasure import android.graphics.Rect import android.util.AttributeSet +import android.view.View import androidx.annotation.ColorInt import androidx.annotation.Dimension -import androidx.appcompat.widget.AppCompatImageView -import androidx.core.content.ContextCompat import androidx.core.content.res.use import com.keylesspalace.tusky.R import kotlin.math.max @@ -33,9 +32,8 @@ import kotlin.math.max class GraphView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, - defStyleAttr: Int = 0, - defStyleRes: Int = 0 -) : AppCompatImageView(context, attrs, defStyleAttr) { + defStyleAttr: Int = 0 +) : View(context, attrs, defStyleAttr) { @get:ColorInt @ColorInt var primaryLineColor = 0 @@ -55,7 +53,7 @@ class GraphView @JvmOverloads constructor( @ColorInt var metaColor = 0 - var proportionalTrending = false + private var proportionalTrending = false private lateinit var primaryLinePaint: Paint private lateinit var secondaryLinePaint: Paint @@ -129,16 +127,14 @@ class GraphView @JvmOverloads constructor( private fun initFromXML(attr: AttributeSet?) { context.obtainStyledAttributes(attr, R.styleable.GraphView).use { a -> - primaryLineColor = ContextCompat.getColor( - context, + primaryLineColor = context.getColor( a.getResourceId( R.styleable.GraphView_primaryLineColor, R.color.tusky_blue ) ) - secondaryLineColor = ContextCompat.getColor( - context, + secondaryLineColor = context.getColor( a.getResourceId( R.styleable.GraphView_secondaryLineColor, R.color.tusky_red @@ -150,16 +146,14 @@ class GraphView @JvmOverloads constructor( R.dimen.graph_line_thickness ).toFloat() - graphColor = ContextCompat.getColor( - context, + graphColor = context.getColor( a.getResourceId( R.styleable.GraphView_graphColor, R.color.colorBackground ) ) - metaColor = ContextCompat.getColor( - context, + metaColor = context.getColor( a.getResourceId( R.styleable.GraphView_metaColor, R.color.dividerColor diff --git a/app/src/main/java/com/keylesspalace/tusky/viewdata/TrendingViewData.kt b/app/src/main/java/com/keylesspalace/tusky/viewdata/TrendingViewData.kt index c018aebc..6eae7331 100644 --- a/app/src/main/java/com/keylesspalace/tusky/viewdata/TrendingViewData.kt +++ b/app/src/main/java/com/keylesspalace/tusky/viewdata/TrendingViewData.kt @@ -15,9 +15,6 @@ package com.keylesspalace.tusky.viewdata -import com.keylesspalace.tusky.entity.TrendingTag -import com.keylesspalace.tusky.entity.end -import com.keylesspalace.tusky.entity.start import java.util.Date sealed class TrendingViewData { @@ -31,18 +28,13 @@ sealed class TrendingViewData { get() = start.toString() + end.toString() } - fun asHeaderOrNull(): Header? { - val tag = (this as? Tag)?.tag - ?: return null - return Header(tag.start(), tag.end()) - } - data class Tag( - val tag: TrendingTag + val name: String, + val usage: List, + val accounts: List, + val maxTrendingValue: Long ) : TrendingViewData() { override val id: String - get() = tag.name + get() = name } - - fun asTagOrNull() = this as? Tag }