Rename "Trending" to "TrendingTags" or similar where necessary (#3906)

The "trending" functionality will expand to include trending links and
posts. But at the moment the "Trending" references in the code are
exclusively to hashtags.

Rename "Trending" to "TrendingTags" or similar everywhere necessary in
order to prepare for this.

This includes a database migration, as the identifier for the "Trending
tags" tab in the account preferences was changed from "Trending" to
"TrendingTags". The migration updates the stored value if necessary.
This commit is contained in:
Nik Clayton 2023-08-19 12:54:35 +02:00 committed by GitHub
parent dc9e9f2aeb
commit b6102a755a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 1077 additions and 57 deletions

View File

@ -1531,7 +1531,7 @@
errorLine1=" android:background="?android:attr/colorBackground">" errorLine1=" android:background="?android:attr/colorBackground">"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location <location
file="src/main/res/layout/fragment_trending.xml" file="src/main/res/layout/fragment_trending_tags.xml"
line="7" line="7"
column="5"/> column="5"/>
</issue> </issue>
@ -3991,22 +3991,22 @@
<issue <issue
id="SyntheticAccessor" id="SyntheticAccessor"
message="Access to `private` method `getBinding` of class `TrendingFragment` requires synthetic accessor" message="Access to `private` method `getBinding` of class `TrendingTagsFragment` requires synthetic accessor"
errorLine1=" binding.recyclerView.post {" errorLine1=" binding.recyclerView.post {"
errorLine2=" ~~~~~~~"> errorLine2=" ~~~~~~~">
<location <location
file="src/main/java/com/keylesspalace/tusky/components/trending/TrendingFragment.kt" file="src/main/java/com/keylesspalace/tusky/components/trending/TrendingTagsFragment.kt"
line="81" line="81"
column="21"/> column="21"/>
</issue> </issue>
<issue <issue
id="SyntheticAccessor" id="SyntheticAccessor"
message="Access to `private` method `getBinding` of class `TrendingFragment` requires synthetic accessor" message="Access to `private` method `getBinding` of class `TrendingTagsFragment` requires synthetic accessor"
errorLine1=" binding.recyclerView.scrollBy(" errorLine1=" binding.recyclerView.scrollBy("
errorLine2=" ~~~~~~~"> errorLine2=" ~~~~~~~">
<location <location
file="src/main/java/com/keylesspalace/tusky/components/trending/TrendingFragment.kt" file="src/main/java/com/keylesspalace/tusky/components/trending/TrendingTagsFragment.kt"
line="83" line="83"
column="29"/> column="29"/>
</issue> </issue>

File diff suppressed because it is too large Load Diff

View File

@ -274,7 +274,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
setupDrawer( setupDrawer(
savedInstanceState, savedInstanceState,
addSearchButton = hideTopToolbar, addSearchButton = hideTopToolbar,
addTrendingButton = !accountManager.activeAccount!!.tabPreferences.hasTab(TRENDING) addTrendingTagsButton = !accountManager.activeAccount!!.tabPreferences.hasTab(TRENDING_TAGS)
) )
/* Fetch user info while we're doing other things. This has to be done after setting up the /* Fetch user info while we're doing other things. This has to be done after setting up the
@ -299,7 +299,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
is MainTabsChangedEvent -> { is MainTabsChangedEvent -> {
refreshMainDrawerItems( refreshMainDrawerItems(
addSearchButton = hideTopToolbar, addSearchButton = hideTopToolbar,
addTrendingButton = !event.newTabs.hasTab(TRENDING) addTrendingTagsButton = !event.newTabs.hasTab(TRENDING_TAGS)
) )
setupTabs(false) setupTabs(false)
@ -453,7 +453,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
private fun setupDrawer( private fun setupDrawer(
savedInstanceState: Bundle?, savedInstanceState: Bundle?,
addSearchButton: Boolean, addSearchButton: Boolean,
addTrendingButton: Boolean addTrendingTagsButton: Boolean
) { ) {
val drawerOpenClickListener = View.OnClickListener { binding.mainDrawerLayout.open() } val drawerOpenClickListener = View.OnClickListener { binding.mainDrawerLayout.open() }
@ -514,12 +514,12 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
}) })
binding.mainDrawer.apply { binding.mainDrawer.apply {
refreshMainDrawerItems(addSearchButton, addTrendingButton) refreshMainDrawerItems(addSearchButton, addTrendingTagsButton)
setSavedInstance(savedInstanceState) setSavedInstance(savedInstanceState)
} }
} }
private fun refreshMainDrawerItems(addSearchButton: Boolean, addTrendingButton: Boolean) { private fun refreshMainDrawerItems(addSearchButton: Boolean, addTrendingTagsButton: Boolean) {
binding.mainDrawer.apply { binding.mainDrawer.apply {
itemAdapter.clear() itemAdapter.clear()
tintStatusBar = true tintStatusBar = true
@ -636,7 +636,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
) )
} }
if (addTrendingButton) { if (addTrendingTagsButton) {
binding.mainDrawer.addItemsAtPosition( binding.mainDrawer.addItemsAtPosition(
5, 5,
primaryDrawerItem { primaryDrawerItem {

View File

@ -23,7 +23,7 @@ import com.keylesspalace.tusky.components.conversation.ConversationsFragment
import com.keylesspalace.tusky.components.notifications.NotificationsFragment import com.keylesspalace.tusky.components.notifications.NotificationsFragment
import com.keylesspalace.tusky.components.timeline.TimelineFragment import com.keylesspalace.tusky.components.timeline.TimelineFragment
import com.keylesspalace.tusky.components.timeline.viewmodel.TimelineViewModel import com.keylesspalace.tusky.components.timeline.viewmodel.TimelineViewModel
import com.keylesspalace.tusky.components.trending.TrendingFragment import com.keylesspalace.tusky.components.trending.TrendingTagsFragment
import java.util.Objects import java.util.Objects
/** this would be a good case for a sealed class, but that does not work nice with Room */ /** this would be a good case for a sealed class, but that does not work nice with Room */
@ -33,7 +33,7 @@ const val NOTIFICATIONS = "Notifications"
const val LOCAL = "Local" const val LOCAL = "Local"
const val FEDERATED = "Federated" const val FEDERATED = "Federated"
const val DIRECT = "Direct" const val DIRECT = "Direct"
const val TRENDING = "Trending" const val TRENDING_TAGS = "TrendingTags"
const val HASHTAG = "Hashtag" const val HASHTAG = "Hashtag"
const val LIST = "List" const val LIST = "List"
@ -92,11 +92,11 @@ fun createTabDataFromId(id: String, arguments: List<String> = emptyList()): TabD
icon = R.drawable.ic_reblog_direct_24dp, icon = R.drawable.ic_reblog_direct_24dp,
fragment = { ConversationsFragment.newInstance() } fragment = { ConversationsFragment.newInstance() }
) )
TRENDING -> TabData( TRENDING_TAGS -> TabData(
id = TRENDING, id = TRENDING_TAGS,
text = R.string.title_public_trending_hashtags, text = R.string.title_public_trending_hashtags,
icon = R.drawable.ic_trending_up_24px, icon = R.drawable.ic_trending_up_24px,
fragment = { TrendingFragment.newInstance() } fragment = { TrendingTagsFragment.newInstance() }
) )
HASHTAG -> TabData( HASHTAG -> TabData(
id = HASHTAG, id = HASHTAG,

View File

@ -378,9 +378,9 @@ class TabPreferenceActivity : BaseActivity(), Injectable, ItemInteractionListene
if (!currentTabs.contains(directMessagesTab)) { if (!currentTabs.contains(directMessagesTab)) {
addableTabs.add(directMessagesTab) addableTabs.add(directMessagesTab)
} }
val trendingTab = createTabDataFromId(TRENDING) val trendingTagsTab = createTabDataFromId(TRENDING_TAGS)
if (!currentTabs.contains(trendingTab)) { if (!currentTabs.contains(trendingTagsTab)) {
addableTabs.add(trendingTab) addableTabs.add(trendingTagsTab)
} }
addableTabs.add(createTabDataFromId(HASHTAG)) addableTabs.add(createTabDataFromId(HASHTAG))

View File

@ -48,7 +48,7 @@ class TrendingActivity : BaseActivity(), HasAndroidInjector {
if (supportFragmentManager.findFragmentById(R.id.fragmentContainer) == null) { if (supportFragmentManager.findFragmentById(R.id.fragmentContainer) == null) {
supportFragmentManager.commit { supportFragmentManager.commit {
val fragment = TrendingFragment.newInstance() val fragment = TrendingTagsFragment.newInstance()
replace(R.id.fragmentContainer, fragment) replace(R.id.fragmentContainer, fragment)
} }
} }

View File

@ -24,7 +24,7 @@ import com.keylesspalace.tusky.databinding.ItemTrendingCellBinding
import com.keylesspalace.tusky.databinding.ItemTrendingDateBinding import com.keylesspalace.tusky.databinding.ItemTrendingDateBinding
import com.keylesspalace.tusky.viewdata.TrendingViewData import com.keylesspalace.tusky.viewdata.TrendingViewData
class TrendingAdapter( class TrendingTagsAdapter(
private val onViewTag: (String) -> Unit private val onViewTag: (String) -> Unit
) : ListAdapter<TrendingViewData, RecyclerView.ViewHolder>(TrendingDifferCallback) { ) : ListAdapter<TrendingViewData, RecyclerView.ViewHolder>(TrendingDifferCallback) {

View File

@ -33,8 +33,8 @@ import at.connyduck.sparkbutton.helpers.Utils
import com.keylesspalace.tusky.BaseActivity import com.keylesspalace.tusky.BaseActivity
import com.keylesspalace.tusky.R import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.StatusListActivity import com.keylesspalace.tusky.StatusListActivity
import com.keylesspalace.tusky.components.trending.viewmodel.TrendingViewModel import com.keylesspalace.tusky.components.trending.viewmodel.TrendingTagsViewModel
import com.keylesspalace.tusky.databinding.FragmentTrendingBinding import com.keylesspalace.tusky.databinding.FragmentTrendingTagsBinding
import com.keylesspalace.tusky.di.Injectable import com.keylesspalace.tusky.di.Injectable
import com.keylesspalace.tusky.di.ViewModelFactory import com.keylesspalace.tusky.di.ViewModelFactory
import com.keylesspalace.tusky.interfaces.ActionButtonActivity import com.keylesspalace.tusky.interfaces.ActionButtonActivity
@ -48,8 +48,8 @@ import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
class TrendingFragment : class TrendingTagsFragment :
Fragment(R.layout.fragment_trending), Fragment(R.layout.fragment_trending_tags),
OnRefreshListener, OnRefreshListener,
Injectable, Injectable,
ReselectableFragment, ReselectableFragment,
@ -58,11 +58,11 @@ class TrendingFragment :
@Inject @Inject
lateinit var viewModelFactory: ViewModelFactory lateinit var viewModelFactory: ViewModelFactory
private val viewModel: TrendingViewModel by viewModels { viewModelFactory } private val viewModel: TrendingTagsViewModel by viewModels { viewModelFactory }
private val binding by viewBinding(FragmentTrendingBinding::bind) private val binding by viewBinding(FragmentTrendingTagsBinding::bind)
private val adapter = TrendingAdapter(::onViewTag) private val adapter = TrendingTagsAdapter(::onViewTag)
override fun onConfigurationChanged(newConfig: Configuration) { override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig) super.onConfigurationChanged(newConfig)
@ -111,8 +111,8 @@ class TrendingFragment :
spanSizeLookup = object : SpanSizeLookup() { spanSizeLookup = object : SpanSizeLookup() {
override fun getSpanSize(position: Int): Int { override fun getSpanSize(position: Int): Int {
return when (adapter.getItemViewType(position)) { return when (adapter.getItemViewType(position)) {
TrendingAdapter.VIEW_TYPE_HEADER -> columnCount TrendingTagsAdapter.VIEW_TYPE_HEADER -> columnCount
TrendingAdapter.VIEW_TYPE_TAG -> 1 TrendingTagsAdapter.VIEW_TYPE_TAG -> 1
else -> -1 else -> -1
} }
} }
@ -139,15 +139,15 @@ class TrendingFragment :
(requireActivity() as BaseActivity).startActivityWithSlideInAnimation(StatusListActivity.newHashtagIntent(requireContext(), tag)) (requireActivity() as BaseActivity).startActivityWithSlideInAnimation(StatusListActivity.newHashtagIntent(requireContext(), tag))
} }
private fun processViewState(uiState: TrendingViewModel.TrendingUiState) { private fun processViewState(uiState: TrendingTagsViewModel.TrendingTagsUiState) {
Log.d(TAG, uiState.loadingState.name) Log.d(TAG, uiState.loadingState.name)
when (uiState.loadingState) { when (uiState.loadingState) {
TrendingViewModel.LoadingState.INITIAL -> clearLoadingState() TrendingTagsViewModel.LoadingState.INITIAL -> clearLoadingState()
TrendingViewModel.LoadingState.LOADING -> applyLoadingState() TrendingTagsViewModel.LoadingState.LOADING -> applyLoadingState()
TrendingViewModel.LoadingState.REFRESHING -> applyRefreshingState() TrendingTagsViewModel.LoadingState.REFRESHING -> applyRefreshingState()
TrendingViewModel.LoadingState.LOADED -> applyLoadedState(uiState.trendingViewData) TrendingTagsViewModel.LoadingState.LOADED -> applyLoadedState(uiState.trendingViewData)
TrendingViewModel.LoadingState.ERROR_NETWORK -> networkError() TrendingTagsViewModel.LoadingState.ERROR_NETWORK -> networkError()
TrendingViewModel.LoadingState.ERROR_OTHER -> otherError() TrendingTagsViewModel.LoadingState.ERROR_OTHER -> otherError()
} }
} }
@ -247,8 +247,8 @@ class TrendingFragment :
} }
companion object { companion object {
private const val TAG = "TrendingFragment" private const val TAG = "TrendingTagsFragment"
fun newInstance() = TrendingFragment() fun newInstance() = TrendingTagsFragment()
} }
} }

View File

@ -35,7 +35,7 @@ import kotlinx.coroutines.launch
import java.io.IOException import java.io.IOException
import javax.inject.Inject import javax.inject.Inject
class TrendingViewModel @Inject constructor( class TrendingTagsViewModel @Inject constructor(
private val mastodonApi: MastodonApi, private val mastodonApi: MastodonApi,
private val eventHub: EventHub private val eventHub: EventHub
) : ViewModel() { ) : ViewModel() {
@ -43,13 +43,13 @@ class TrendingViewModel @Inject constructor(
INITIAL, LOADING, REFRESHING, LOADED, ERROR_NETWORK, ERROR_OTHER INITIAL, LOADING, REFRESHING, LOADED, ERROR_NETWORK, ERROR_OTHER
} }
data class TrendingUiState( data class TrendingTagsUiState(
val trendingViewData: List<TrendingViewData>, val trendingViewData: List<TrendingViewData>,
val loadingState: LoadingState val loadingState: LoadingState
) )
val uiState: Flow<TrendingUiState> get() = _uiState val uiState: Flow<TrendingTagsUiState> get() = _uiState
private val _uiState = MutableStateFlow(TrendingUiState(listOf(), LoadingState.INITIAL)) private val _uiState = MutableStateFlow(TrendingTagsUiState(listOf(), LoadingState.INITIAL))
init { init {
invalidate() invalidate()
@ -73,9 +73,9 @@ class TrendingViewModel @Inject constructor(
*/ */
fun invalidate(refresh: Boolean = false) = viewModelScope.launch { fun invalidate(refresh: Boolean = false) = viewModelScope.launch {
if (refresh) { if (refresh) {
_uiState.value = TrendingUiState(emptyList(), LoadingState.REFRESHING) _uiState.value = TrendingTagsUiState(emptyList(), LoadingState.REFRESHING)
} else { } else {
_uiState.value = TrendingUiState(emptyList(), LoadingState.LOADING) _uiState.value = TrendingTagsUiState(emptyList(), LoadingState.LOADING)
} }
val deferredFilters = async { mastodonApi.getFilters() } val deferredFilters = async { mastodonApi.getFilters() }
@ -85,7 +85,7 @@ class TrendingViewModel @Inject constructor(
val firstTag = tagResponse.firstOrNull() val firstTag = tagResponse.firstOrNull()
_uiState.value = if (firstTag == null) { _uiState.value = if (firstTag == null) {
TrendingUiState(emptyList(), LoadingState.LOADED) TrendingTagsUiState(emptyList(), LoadingState.LOADED)
} else { } else {
val homeFilters = deferredFilters.await().getOrNull()?.filter { filter -> val homeFilters = deferredFilters.await().getOrNull()?.filter { filter ->
filter.context.contains(Filter.Kind.HOME.kind) filter.context.contains(Filter.Kind.HOME.kind)
@ -100,15 +100,15 @@ class TrendingViewModel @Inject constructor(
.toViewData() .toViewData()
val header = TrendingViewData.Header(firstTag.start(), firstTag.end()) val header = TrendingViewData.Header(firstTag.start(), firstTag.end())
TrendingUiState(listOf(header) + tags, LoadingState.LOADED) TrendingTagsUiState(listOf(header) + tags, LoadingState.LOADED)
} }
}, },
{ error -> { error ->
Log.w(TAG, "failed loading trending tags", error) Log.w(TAG, "failed loading trending tags", error)
if (error is IOException) { if (error is IOException) {
_uiState.value = TrendingUiState(emptyList(), LoadingState.ERROR_NETWORK) _uiState.value = TrendingTagsUiState(emptyList(), LoadingState.ERROR_NETWORK)
} else { } else {
_uiState.value = TrendingUiState(emptyList(), LoadingState.ERROR_OTHER) _uiState.value = TrendingTagsUiState(emptyList(), LoadingState.ERROR_OTHER)
} }
} }
) )

View File

@ -42,12 +42,12 @@ import java.io.File;
TimelineAccountEntity.class, TimelineAccountEntity.class,
ConversationEntity.class ConversationEntity.class
}, },
version = 52, version = 53,
autoMigrations = { autoMigrations = {
@AutoMigration(from = 48, to = 49), @AutoMigration(from = 48, to = 49),
@AutoMigration(from = 49, to = 50, spec = AppDatabase.MIGRATION_49_50.class), @AutoMigration(from = 49, to = 50, spec = AppDatabase.MIGRATION_49_50.class),
@AutoMigration(from = 50, to = 51), @AutoMigration(from = 50, to = 51),
@AutoMigration(from = 51, to = 52) @AutoMigration(from = 51, to = 52),
} }
) )
public abstract class AppDatabase extends RoomDatabase { public abstract class AppDatabase extends RoomDatabase {
@ -674,4 +674,15 @@ public abstract class AppDatabase extends RoomDatabase {
@DeleteColumn(tableName = "AccountEntity", columnName = "activeNotifications") @DeleteColumn(tableName = "AccountEntity", columnName = "activeNotifications")
static class MIGRATION_49_50 implements AutoMigrationSpec { } static class MIGRATION_49_50 implements AutoMigrationSpec { }
/**
* TabData.TRENDING was renamed to TabData.TRENDING_TAGS, and the text
* representation was changed from "Trending" to "TrendingTags".
*/
public static final Migration MIGRATION_52_53 = new Migration(52, 53) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("UPDATE `AccountEntity` SET `tabpreferences` = REPLACE(tabpreferences, 'Trending:', 'TrendingTags:')");
}
};
} }

View File

@ -68,7 +68,7 @@ class AppModule {
AppDatabase.MIGRATION_38_39, AppDatabase.MIGRATION_39_40, AppDatabase.MIGRATION_40_41, AppDatabase.MIGRATION_38_39, AppDatabase.MIGRATION_39_40, AppDatabase.MIGRATION_40_41,
AppDatabase.MIGRATION_41_42, AppDatabase.MIGRATION_42_43, AppDatabase.MIGRATION_43_44, AppDatabase.MIGRATION_41_42, AppDatabase.MIGRATION_42_43, AppDatabase.MIGRATION_43_44,
AppDatabase.MIGRATION_44_45, AppDatabase.MIGRATION_45_46, AppDatabase.MIGRATION_46_47, AppDatabase.MIGRATION_44_45, AppDatabase.MIGRATION_45_46, AppDatabase.MIGRATION_46_47,
AppDatabase.MIGRATION_47_48 AppDatabase.MIGRATION_47_48, AppDatabase.MIGRATION_52_53
) )
.build() .build()
} }

View File

@ -32,7 +32,7 @@ import com.keylesspalace.tusky.components.search.fragments.SearchAccountsFragmen
import com.keylesspalace.tusky.components.search.fragments.SearchHashtagsFragment import com.keylesspalace.tusky.components.search.fragments.SearchHashtagsFragment
import com.keylesspalace.tusky.components.search.fragments.SearchStatusesFragment import com.keylesspalace.tusky.components.search.fragments.SearchStatusesFragment
import com.keylesspalace.tusky.components.timeline.TimelineFragment import com.keylesspalace.tusky.components.timeline.TimelineFragment
import com.keylesspalace.tusky.components.trending.TrendingFragment import com.keylesspalace.tusky.components.trending.TrendingTagsFragment
import com.keylesspalace.tusky.components.viewthread.ViewThreadFragment import com.keylesspalace.tusky.components.viewthread.ViewThreadFragment
import com.keylesspalace.tusky.components.viewthread.edits.ViewEditsFragment import com.keylesspalace.tusky.components.viewthread.edits.ViewEditsFragment
import com.keylesspalace.tusky.fragment.ViewVideoFragment import com.keylesspalace.tusky.fragment.ViewVideoFragment
@ -99,7 +99,7 @@ abstract class FragmentBuildersModule {
abstract fun listsForAccountFragment(): ListsForAccountFragment abstract fun listsForAccountFragment(): ListsForAccountFragment
@ContributesAndroidInjector @ContributesAndroidInjector
abstract fun trendingFragment(): TrendingFragment abstract fun trendingTagsFragment(): TrendingTagsFragment
@ContributesAndroidInjector @ContributesAndroidInjector
abstract fun viewVideoFragment(): ViewVideoFragment abstract fun viewVideoFragment(): ViewVideoFragment

View File

@ -38,7 +38,7 @@ import com.keylesspalace.tusky.components.scheduled.ScheduledStatusViewModel
import com.keylesspalace.tusky.components.search.SearchViewModel import com.keylesspalace.tusky.components.search.SearchViewModel
import com.keylesspalace.tusky.components.timeline.viewmodel.CachedTimelineViewModel import com.keylesspalace.tusky.components.timeline.viewmodel.CachedTimelineViewModel
import com.keylesspalace.tusky.components.timeline.viewmodel.NetworkTimelineViewModel import com.keylesspalace.tusky.components.timeline.viewmodel.NetworkTimelineViewModel
import com.keylesspalace.tusky.components.trending.viewmodel.TrendingViewModel import com.keylesspalace.tusky.components.trending.viewmodel.TrendingTagsViewModel
import com.keylesspalace.tusky.components.viewthread.ViewThreadViewModel import com.keylesspalace.tusky.components.viewthread.ViewThreadViewModel
import com.keylesspalace.tusky.components.viewthread.edits.ViewEditsViewModel import com.keylesspalace.tusky.components.viewthread.edits.ViewEditsViewModel
import com.keylesspalace.tusky.viewmodel.AccountsInListViewModel import com.keylesspalace.tusky.viewmodel.AccountsInListViewModel
@ -172,8 +172,8 @@ abstract class ViewModelModule {
@Binds @Binds
@IntoMap @IntoMap
@ViewModelKey(TrendingViewModel::class) @ViewModelKey(TrendingTagsViewModel::class)
internal abstract fun trendingViewModel(viewModel: TrendingViewModel): ViewModel internal abstract fun trendingTagsViewModel(viewModel: TrendingTagsViewModel): ViewModel
@Binds @Binds
@IntoMap @IntoMap