refactor: Remove `TabData` type (#576)
`TabData` recorded the type of the timeline the user had added to a tab. `TimelineKind` is another type that records general information about configured timelines, with identical properties. There's no need for both, so remove `TabData` and use `TimelineKind` in its place. `TimelineKind` is itself mis-named; it's not just the timeline's kind but also holds data necessary to display that timeline (e.g., the list ID if it's a `.UserList`, or the hashtags if it's a `.Hashtags`) so rename to `Timeline` to better reflect its usage. Move it to a new `core.model` module.
This commit is contained in:
parent
09ca6ae0a8
commit
8257ded395
|
@ -127,6 +127,7 @@ dependencies {
|
|||
implementation(projects.core.data)
|
||||
implementation(projects.core.database)
|
||||
implementation(projects.core.designsystem)
|
||||
implementation(projects.core.model)
|
||||
implementation(projects.core.navigation)
|
||||
implementation(projects.core.network)
|
||||
implementation(projects.core.preferences)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<issues format="6" by="lint 8.3.0" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0)" variant="all" version="8.3.0">
|
||||
<issues format="6" by="lint 8.3.1" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.1)" variant="all" version="8.3.1">
|
||||
|
||||
<issue
|
||||
id="InvalidPackage"
|
||||
|
@ -2617,7 +2617,7 @@
|
|||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/app/pachli/util/ShareShortcutHelper.kt"
|
||||
line="84"
|
||||
line="96"
|
||||
column="5"/>
|
||||
</issue>
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
|
||||
-keepclassmembers class app.pachli.core.database.model.ConversationAccountEntity { *; }
|
||||
-keepclassmembers class app.pachli.core.database.model.DraftAttachment { *; }
|
||||
-keep class app.pachli.core.database.model.TabDataJsonAdapter { *; }
|
||||
-keep class app.pachli.core.model.TimelineJsonAdapter { *; }
|
||||
|
||||
-keep enum app.pachli.core.database.model.DraftAttachment$Type {
|
||||
public *;
|
||||
|
|
|
@ -79,9 +79,9 @@ import app.pachli.core.data.repository.Lists
|
|||
import app.pachli.core.data.repository.ListsRepository
|
||||
import app.pachli.core.data.repository.ListsRepository.Companion.compareByListTitle
|
||||
import app.pachli.core.database.model.AccountEntity
|
||||
import app.pachli.core.database.model.TabData
|
||||
import app.pachli.core.designsystem.EmbeddedFontFamily
|
||||
import app.pachli.core.designsystem.R as DR
|
||||
import app.pachli.core.model.Timeline
|
||||
import app.pachli.core.navigation.AboutActivityIntent
|
||||
import app.pachli.core.navigation.AccountActivityIntent
|
||||
import app.pachli.core.navigation.AccountListActivityIntent
|
||||
|
@ -369,7 +369,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider {
|
|||
refreshMainDrawerItems(addSearchButton = hideTopToolbar)
|
||||
|
||||
// Any lists in tabs might have changed titles, update those
|
||||
if (lists is Lists.Loaded && tabAdapter.tabs.any { it.tabData is TabData.UserList }) {
|
||||
if (lists is Lists.Loaded && tabAdapter.tabs.any { it.timeline is Timeline.UserList }) {
|
||||
setupTabs(false)
|
||||
}
|
||||
}
|
||||
|
@ -868,8 +868,8 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider {
|
|||
tabLayoutMediator = TabLayoutMediator(activeTabLayout, binding.viewPager, true) {
|
||||
tab: TabLayout.Tab, position: Int ->
|
||||
tab.icon = AppCompatResources.getDrawable(this@MainActivity, tabs[position].icon)
|
||||
tab.contentDescription = when (tabs[position].tabData) {
|
||||
is TabData.UserList -> tabs[position].title(this@MainActivity)
|
||||
tab.contentDescription = when (tabs[position].timeline) {
|
||||
is Timeline.UserList -> tabs[position].title(this@MainActivity)
|
||||
else -> getString(tabs[position].text)
|
||||
}
|
||||
}.also { it.attach() }
|
||||
|
@ -880,12 +880,12 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider {
|
|||
// - Tabs containing lists are compared by list ID, in case the list was renamed
|
||||
// - Left-most tab
|
||||
val position = if (selectNotificationTab) {
|
||||
tabs.indexOfFirst { it.tabData is TabData.Notifications }
|
||||
tabs.indexOfFirst { it.timeline is Timeline.Notifications }
|
||||
} else {
|
||||
previousTab?.let {
|
||||
tabs.indexOfFirst {
|
||||
if (it.tabData is TabData.UserList && previousTab.tabData is TabData.UserList) {
|
||||
it.tabData.listId == previousTab.tabData.listId
|
||||
if (it.timeline is Timeline.UserList && previousTab.timeline is Timeline.UserList) {
|
||||
it.timeline.listId == previousTab.timeline.listId
|
||||
} else {
|
||||
it == previousTab
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ import app.pachli.components.timeline.TimelineFragment
|
|||
import app.pachli.core.activity.BottomSheetActivity
|
||||
import app.pachli.core.common.extensions.viewBinding
|
||||
import app.pachli.core.common.util.unsafeLazy
|
||||
import app.pachli.core.model.Timeline
|
||||
import app.pachli.core.navigation.ComposeActivityIntent
|
||||
import app.pachli.core.navigation.ComposeActivityIntent.ComposeOptions
|
||||
import app.pachli.core.navigation.StatusListActivityIntent
|
||||
|
@ -36,7 +37,6 @@ import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_FILTERS_SERVER
|
|||
import app.pachli.core.network.model.Filter
|
||||
import app.pachli.core.network.model.FilterContext
|
||||
import app.pachli.core.network.model.FilterV1
|
||||
import app.pachli.core.network.model.TimelineKind
|
||||
import app.pachli.databinding.ActivityStatuslistBinding
|
||||
import app.pachli.interfaces.ActionButtonActivity
|
||||
import app.pachli.interfaces.AppBarLayoutHost
|
||||
|
@ -66,7 +66,7 @@ class StatusListActivity : BottomSheetActivity(), AppBarLayoutHost, ActionButton
|
|||
lateinit var serverRepository: ServerRepository
|
||||
|
||||
private val binding: ActivityStatuslistBinding by viewBinding(ActivityStatuslistBinding::inflate)
|
||||
private lateinit var timelineKind: TimelineKind
|
||||
private lateinit var timeline: Timeline
|
||||
|
||||
override val appBarLayout: AppBarLayout
|
||||
get() = binding.includedToolbar.appbar
|
||||
|
@ -94,16 +94,16 @@ class StatusListActivity : BottomSheetActivity(), AppBarLayoutHost, ActionButton
|
|||
|
||||
setSupportActionBar(binding.includedToolbar.toolbar)
|
||||
|
||||
timelineKind = StatusListActivityIntent.getKind(intent)
|
||||
timeline = StatusListActivityIntent.getKind(intent)
|
||||
|
||||
val title = when (timelineKind) {
|
||||
is TimelineKind.Favourites -> getString(R.string.title_favourites)
|
||||
is TimelineKind.Bookmarks -> getString(R.string.title_bookmarks)
|
||||
is TimelineKind.Tag -> {
|
||||
hashtag = (timelineKind as TimelineKind.Tag).tags.first()
|
||||
val title = when (timeline) {
|
||||
is Timeline.Favourites -> getString(R.string.title_favourites)
|
||||
is Timeline.Bookmarks -> getString(R.string.title_bookmarks)
|
||||
is Timeline.Hashtags -> {
|
||||
hashtag = (timeline as Timeline.Hashtags).tags.first()
|
||||
getString(R.string.title_tag).format(hashtag)
|
||||
}
|
||||
is TimelineKind.UserList -> (timelineKind as TimelineKind.UserList).title
|
||||
is Timeline.UserList -> (timeline as Timeline.UserList).title
|
||||
else -> "Missing title!!!"
|
||||
}
|
||||
|
||||
|
@ -115,14 +115,14 @@ class StatusListActivity : BottomSheetActivity(), AppBarLayoutHost, ActionButton
|
|||
|
||||
if (supportFragmentManager.findFragmentById(R.id.fragmentContainer) == null) {
|
||||
supportFragmentManager.commit {
|
||||
val fragment = TimelineFragment.newInstance(timelineKind)
|
||||
val fragment = TimelineFragment.newInstance(timeline)
|
||||
replace(R.id.fragmentContainer, fragment)
|
||||
}
|
||||
}
|
||||
|
||||
val composeIntent = when (timelineKind) {
|
||||
is TimelineKind.Tag -> {
|
||||
val tag = (timelineKind as TimelineKind.Tag).tags.first()
|
||||
val composeIntent = when (timeline) {
|
||||
is Timeline.Hashtags -> {
|
||||
val tag = (timeline as Timeline.Hashtags).tags.first()
|
||||
ComposeActivityIntent(
|
||||
this,
|
||||
ComposeOptions(
|
||||
|
@ -131,9 +131,9 @@ class StatusListActivity : BottomSheetActivity(), AppBarLayoutHost, ActionButton
|
|||
),
|
||||
)
|
||||
}
|
||||
is TimelineKind.Bookmarks,
|
||||
is TimelineKind.Favourites,
|
||||
is TimelineKind.UserList,
|
||||
is Timeline.Bookmarks,
|
||||
is Timeline.Favourites,
|
||||
is Timeline.UserList,
|
||||
-> {
|
||||
ComposeActivityIntent(this, ComposeOptions())
|
||||
}
|
||||
|
@ -150,7 +150,7 @@ class StatusListActivity : BottomSheetActivity(), AppBarLayoutHost, ActionButton
|
|||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
val tag = hashtag
|
||||
if (timelineKind is TimelineKind.Tag && tag != null) {
|
||||
if (timeline is Timeline.Hashtags && tag != null) {
|
||||
lifecycleScope.launch {
|
||||
mastodonApi.tag(tag).fold(
|
||||
{ tagEntity ->
|
||||
|
|
|
@ -46,8 +46,8 @@ import app.pachli.core.common.util.unsafeLazy
|
|||
import app.pachli.core.data.repository.Lists
|
||||
import app.pachli.core.data.repository.ListsRepository
|
||||
import app.pachli.core.data.repository.ListsRepository.Companion.compareByListTitle
|
||||
import app.pachli.core.database.model.TabData
|
||||
import app.pachli.core.designsystem.R as DR
|
||||
import app.pachli.core.model.Timeline
|
||||
import app.pachli.core.navigation.ListActivityIntent
|
||||
import app.pachli.core.network.model.MastoList
|
||||
import app.pachli.core.network.retrofit.MastodonApi
|
||||
|
@ -119,7 +119,7 @@ class TabPreferenceActivity : BaseActivity(), ItemInteractionListener {
|
|||
MaterialDividerItemDecoration(this, MaterialDividerItemDecoration.VERTICAL),
|
||||
)
|
||||
|
||||
addTabAdapter = TabAdapter(listOf(TabViewData.from(TabData.Direct)), true, this)
|
||||
addTabAdapter = TabAdapter(listOf(TabViewData.from(Timeline.Conversations)), true, this)
|
||||
binding.addTabRecyclerView.adapter = addTabAdapter
|
||||
binding.addTabRecyclerView.layoutManager = LinearLayoutManager(this)
|
||||
|
||||
|
@ -182,12 +182,12 @@ class TabPreferenceActivity : BaseActivity(), ItemInteractionListener {
|
|||
override fun onTabAdded(tab: TabViewData) {
|
||||
toggleFab(false)
|
||||
|
||||
if (tab.tabData is TabData.Hashtag) {
|
||||
if (tab.timeline is Timeline.Hashtags) {
|
||||
showAddHashtagDialog()
|
||||
return
|
||||
}
|
||||
|
||||
if (tab.tabData is TabData.UserList) {
|
||||
if (tab.timeline is Timeline.UserList) {
|
||||
showSelectListDialog()
|
||||
return
|
||||
}
|
||||
|
@ -205,14 +205,14 @@ class TabPreferenceActivity : BaseActivity(), ItemInteractionListener {
|
|||
saveTabs()
|
||||
}
|
||||
|
||||
override fun onActionChipClicked(tabData: TabData.Hashtag, tabPosition: Int) {
|
||||
showAddHashtagDialog(tabData, tabPosition)
|
||||
override fun onActionChipClicked(timelineHashtags: Timeline.Hashtags, tabPosition: Int) {
|
||||
showAddHashtagDialog(timelineHashtags, tabPosition)
|
||||
}
|
||||
|
||||
override fun onChipClicked(tabData: TabData.Hashtag, tabPosition: Int, chipPosition: Int) {
|
||||
override fun onChipClicked(timeline: Timeline.Hashtags, tabPosition: Int, chipPosition: Int) {
|
||||
currentTabs[tabPosition] = currentTabs[tabPosition].copy(
|
||||
tabData = tabData.copy(
|
||||
tags = tabData.tags.filterIndexed { i, _ -> i != chipPosition },
|
||||
timeline = timeline.copy(
|
||||
tags = timeline.tags.filterIndexed { i, _ -> i != chipPosition },
|
||||
),
|
||||
)
|
||||
saveTabs()
|
||||
|
@ -238,7 +238,7 @@ class TabPreferenceActivity : BaseActivity(), ItemInteractionListener {
|
|||
onFabDismissedCallback.isEnabled = expand
|
||||
}
|
||||
|
||||
private fun showAddHashtagDialog(tabData: TabData.Hashtag? = null, tabPosition: Int = 0) {
|
||||
private fun showAddHashtagDialog(timeline: Timeline.Hashtags? = null, tabPosition: Int = 0) {
|
||||
val frameLayout = FrameLayout(this)
|
||||
val padding = Utils.dpToPx(this, 8)
|
||||
frameLayout.updatePadding(left = padding, right = padding)
|
||||
|
@ -254,13 +254,13 @@ class TabPreferenceActivity : BaseActivity(), ItemInteractionListener {
|
|||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setPositiveButton(R.string.action_save) { _, _ ->
|
||||
val input = editText.text.toString().trim()
|
||||
if (tabData == null) {
|
||||
val newTab = TabViewData.from(TabData.Hashtag(listOf(input)))
|
||||
if (timeline == null) {
|
||||
val newTab = TabViewData.from(Timeline.Hashtags(listOf(input)))
|
||||
currentTabs.add(newTab)
|
||||
currentTabsAdapter.notifyItemInserted(currentTabs.size - 1)
|
||||
} else {
|
||||
currentTabs[tabPosition] = currentTabs[tabPosition].copy(
|
||||
tabData = tabData.copy(tags = tabData.tags + input),
|
||||
timeline = timeline.copy(tags = timeline.tags + input),
|
||||
)
|
||||
|
||||
currentTabsAdapter.notifyItemChanged(tabPosition)
|
||||
|
@ -298,7 +298,7 @@ class TabPreferenceActivity : BaseActivity(), ItemInteractionListener {
|
|||
.setView(selectListBinding.root)
|
||||
.setAdapter(adapter) { _, position ->
|
||||
adapter.getItem(position)?.let { item ->
|
||||
val newTab = TabViewData.from(TabData.UserList(item.id, item.title))
|
||||
val newTab = TabViewData.from(Timeline.UserList(item.id, item.title))
|
||||
currentTabs.add(newTab)
|
||||
currentTabsAdapter.notifyItemInserted(currentTabs.size - 1)
|
||||
updateAvailableTabs()
|
||||
|
@ -349,45 +349,45 @@ class TabPreferenceActivity : BaseActivity(), ItemInteractionListener {
|
|||
private fun updateAvailableTabs() {
|
||||
val addableTabs: MutableList<TabViewData> = mutableListOf()
|
||||
|
||||
val homeTab = TabViewData.from(TabData.Home)
|
||||
val homeTab = TabViewData.from(Timeline.Home)
|
||||
if (!currentTabs.contains(homeTab)) {
|
||||
addableTabs.add(homeTab)
|
||||
}
|
||||
val notificationTab = TabViewData.from(TabData.Notifications)
|
||||
val notificationTab = TabViewData.from(Timeline.Notifications)
|
||||
if (!currentTabs.contains(notificationTab)) {
|
||||
addableTabs.add(notificationTab)
|
||||
}
|
||||
val localTab = TabViewData.from(TabData.Local)
|
||||
val localTab = TabViewData.from(Timeline.PublicLocal)
|
||||
if (!currentTabs.contains(localTab)) {
|
||||
addableTabs.add(localTab)
|
||||
}
|
||||
val federatedTab = TabViewData.from(TabData.Federated)
|
||||
val federatedTab = TabViewData.from(Timeline.PublicFederated)
|
||||
if (!currentTabs.contains(federatedTab)) {
|
||||
addableTabs.add(federatedTab)
|
||||
}
|
||||
val directMessagesTab = TabViewData.from(TabData.Direct)
|
||||
val directMessagesTab = TabViewData.from(Timeline.Conversations)
|
||||
if (!currentTabs.contains(directMessagesTab)) {
|
||||
addableTabs.add(directMessagesTab)
|
||||
}
|
||||
val trendingTagsTab = TabViewData.from(TabData.TrendingTags)
|
||||
val trendingTagsTab = TabViewData.from(Timeline.TrendingHashtags)
|
||||
if (!currentTabs.contains(trendingTagsTab)) {
|
||||
addableTabs.add(trendingTagsTab)
|
||||
}
|
||||
val trendingLinksTab = TabViewData.from(TabData.TrendingLinks)
|
||||
val trendingLinksTab = TabViewData.from(Timeline.TrendingLinks)
|
||||
if (!currentTabs.contains(trendingLinksTab)) {
|
||||
addableTabs.add(trendingLinksTab)
|
||||
}
|
||||
val trendingStatusesTab = TabViewData.from(TabData.TrendingStatuses)
|
||||
val trendingStatusesTab = TabViewData.from(Timeline.TrendingStatuses)
|
||||
if (!currentTabs.contains(trendingStatusesTab)) {
|
||||
addableTabs.add(trendingStatusesTab)
|
||||
}
|
||||
val bookmarksTab = TabViewData.from(TabData.Bookmarks)
|
||||
val bookmarksTab = TabViewData.from(Timeline.Bookmarks)
|
||||
if (!currentTabs.contains(trendingTagsTab)) {
|
||||
addableTabs.add(bookmarksTab)
|
||||
}
|
||||
|
||||
addableTabs.add(TabViewData.from(TabData.Hashtag(emptyList())))
|
||||
addableTabs.add(TabViewData.from(TabData.UserList("", "")))
|
||||
addableTabs.add(TabViewData.from(Timeline.Hashtags(emptyList())))
|
||||
addableTabs.add(TabViewData.from(Timeline.UserList("", "")))
|
||||
|
||||
addTabAdapter.updateData(addableTabs)
|
||||
|
||||
|
@ -405,7 +405,7 @@ class TabPreferenceActivity : BaseActivity(), ItemInteractionListener {
|
|||
private fun saveTabs() {
|
||||
accountManager.activeAccount?.let {
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
it.tabPreferences = currentTabs.map { it.tabData }
|
||||
it.tabPreferences = currentTabs.map { it.timeline }
|
||||
accountManager.saveAccount(it)
|
||||
}
|
||||
}
|
||||
|
@ -416,7 +416,7 @@ class TabPreferenceActivity : BaseActivity(), ItemInteractionListener {
|
|||
super.onPause()
|
||||
if (tabsChanged) {
|
||||
lifecycleScope.launch {
|
||||
eventHub.dispatch(MainTabsChangedEvent(currentTabs.map { it.tabData }))
|
||||
eventHub.dispatch(MainTabsChangedEvent(currentTabs.map { it.timeline }))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,20 +26,20 @@ import app.pachli.components.notifications.NotificationsFragment
|
|||
import app.pachli.components.timeline.TimelineFragment
|
||||
import app.pachli.components.trending.TrendingLinksFragment
|
||||
import app.pachli.components.trending.TrendingTagsFragment
|
||||
import app.pachli.core.database.model.TabData
|
||||
import app.pachli.core.network.model.TimelineKind
|
||||
import app.pachli.core.model.Timeline
|
||||
|
||||
/**
|
||||
* Wrap a [TabData] with additional information to display a tab with that data.
|
||||
* Wrap a [Timeline] with additional information to display a tab with that
|
||||
* timeline.
|
||||
*
|
||||
* @param tabData wrapped [TabData]
|
||||
* @param timeline wrapped [Timeline]
|
||||
* @param text text to use for this tab when displayed in lists
|
||||
* @param icon icon to use when displaying the tab
|
||||
* @param fragment [Fragment] to display the tab's contents
|
||||
* @param title title to display in the action bar if this tab is active
|
||||
*/
|
||||
data class TabViewData(
|
||||
val tabData: TabData,
|
||||
val timeline: Timeline,
|
||||
@StringRes val text: Int,
|
||||
@DrawableRes val icon: Int,
|
||||
val fragment: () -> Fragment,
|
||||
|
@ -51,70 +51,70 @@ data class TabViewData(
|
|||
|
||||
other as TabViewData
|
||||
|
||||
if (tabData != other.tabData) return false
|
||||
if (timeline != other.timeline) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode() = tabData.hashCode()
|
||||
override fun hashCode() = timeline.hashCode()
|
||||
|
||||
companion object {
|
||||
fun from(tabData: TabData) = when (tabData) {
|
||||
TabData.Home -> TabViewData(
|
||||
tabData = tabData,
|
||||
fun from(timeline: Timeline) = when (timeline) {
|
||||
Timeline.Home -> TabViewData(
|
||||
timeline = timeline,
|
||||
text = R.string.title_home,
|
||||
icon = R.drawable.ic_home_24dp,
|
||||
fragment = { TimelineFragment.newInstance(TimelineKind.Home) },
|
||||
fragment = { TimelineFragment.newInstance(timeline) },
|
||||
)
|
||||
TabData.Notifications -> TabViewData(
|
||||
tabData = tabData,
|
||||
Timeline.Notifications -> TabViewData(
|
||||
timeline = timeline,
|
||||
text = R.string.title_notifications,
|
||||
icon = R.drawable.ic_notifications_24dp,
|
||||
fragment = { NotificationsFragment.newInstance() },
|
||||
)
|
||||
TabData.Local -> TabViewData(
|
||||
tabData = tabData,
|
||||
Timeline.PublicLocal -> TabViewData(
|
||||
timeline = timeline,
|
||||
text = R.string.title_public_local,
|
||||
icon = R.drawable.ic_local_24dp,
|
||||
fragment = { TimelineFragment.newInstance(TimelineKind.PublicLocal) },
|
||||
fragment = { TimelineFragment.newInstance(timeline) },
|
||||
)
|
||||
TabData.Federated -> TabViewData(
|
||||
tabData = tabData,
|
||||
Timeline.PublicFederated -> TabViewData(
|
||||
timeline = timeline,
|
||||
text = R.string.title_public_federated,
|
||||
icon = R.drawable.ic_public_24dp,
|
||||
fragment = { TimelineFragment.newInstance(TimelineKind.PublicFederated) },
|
||||
fragment = { TimelineFragment.newInstance(timeline) },
|
||||
)
|
||||
TabData.Direct -> TabViewData(
|
||||
tabData = tabData,
|
||||
Timeline.Conversations -> TabViewData(
|
||||
timeline = timeline,
|
||||
text = R.string.title_direct_messages,
|
||||
icon = R.drawable.ic_reblog_direct_24dp,
|
||||
fragment = { ConversationsFragment.newInstance() },
|
||||
)
|
||||
TabData.TrendingTags -> TabViewData(
|
||||
tabData = tabData,
|
||||
Timeline.TrendingHashtags -> TabViewData(
|
||||
timeline = timeline,
|
||||
text = R.string.title_public_trending_hashtags,
|
||||
icon = R.drawable.ic_trending_up_24px,
|
||||
fragment = { TrendingTagsFragment.newInstance() },
|
||||
)
|
||||
TabData.TrendingLinks -> TabViewData(
|
||||
tabData = tabData,
|
||||
Timeline.TrendingLinks -> TabViewData(
|
||||
timeline = timeline,
|
||||
text = R.string.title_public_trending_links,
|
||||
icon = R.drawable.ic_trending_up_24px,
|
||||
fragment = { TrendingLinksFragment.newInstance() },
|
||||
)
|
||||
TabData.TrendingStatuses -> TabViewData(
|
||||
tabData = tabData,
|
||||
Timeline.TrendingStatuses -> TabViewData(
|
||||
timeline = timeline,
|
||||
text = R.string.title_public_trending_statuses,
|
||||
icon = R.drawable.ic_trending_up_24px,
|
||||
fragment = { TimelineFragment.newInstance(TimelineKind.TrendingStatuses) },
|
||||
fragment = { TimelineFragment.newInstance(timeline) },
|
||||
)
|
||||
is TabData.Hashtag -> TabViewData(
|
||||
tabData = tabData,
|
||||
is Timeline.Hashtags -> TabViewData(
|
||||
timeline = timeline,
|
||||
text = R.string.hashtags,
|
||||
icon = R.drawable.ic_hashtag,
|
||||
fragment = { TimelineFragment.newInstance(TimelineKind.Tag(tabData.tags)) },
|
||||
fragment = { TimelineFragment.newInstance(timeline) },
|
||||
title = { context ->
|
||||
tabData.tags.joinToString(separator = " ") {
|
||||
timeline.tags.joinToString(separator = " ") {
|
||||
context.getString(
|
||||
R.string.title_tag,
|
||||
it,
|
||||
|
@ -122,24 +122,23 @@ data class TabViewData(
|
|||
}
|
||||
},
|
||||
)
|
||||
is TabData.UserList -> TabViewData(
|
||||
tabData = tabData,
|
||||
is Timeline.UserList -> TabViewData(
|
||||
timeline = timeline,
|
||||
text = R.string.list,
|
||||
icon = app.pachli.core.ui.R.drawable.ic_list,
|
||||
fragment = {
|
||||
TimelineFragment.newInstance(
|
||||
TimelineKind.UserList(tabData.listId, tabData.title),
|
||||
)
|
||||
},
|
||||
title = { tabData.title },
|
||||
fragment = { TimelineFragment.newInstance(timeline) },
|
||||
title = { timeline.title },
|
||||
)
|
||||
TabData.Bookmarks -> TabViewData(
|
||||
tabData = tabData,
|
||||
Timeline.Bookmarks -> TabViewData(
|
||||
timeline = timeline,
|
||||
text = R.string.title_bookmarks,
|
||||
icon = R.drawable.ic_bookmark_active_24dp,
|
||||
fragment = { TimelineFragment.newInstance(TimelineKind.Bookmarks) },
|
||||
fragment = { TimelineFragment.newInstance(timeline) },
|
||||
)
|
||||
else -> throw IllegalArgumentException("unknown tab type: $tabData")
|
||||
Timeline.Favourites -> throw IllegalArgumentException("can't add to tab: $timeline")
|
||||
is Timeline.User.Pinned -> throw IllegalArgumentException("can't add to tab: $timeline")
|
||||
is Timeline.User.Posts -> throw IllegalArgumentException("can't add to tab: $timeline")
|
||||
is Timeline.User.Replies -> throw IllegalArgumentException("can't add to tab: $timeline")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,8 +26,8 @@ import app.pachli.R
|
|||
import app.pachli.TabViewData
|
||||
import app.pachli.core.common.extensions.hide
|
||||
import app.pachli.core.common.extensions.show
|
||||
import app.pachli.core.database.model.TabData
|
||||
import app.pachli.core.designsystem.R as DR
|
||||
import app.pachli.core.model.Timeline
|
||||
import app.pachli.core.ui.BindingHolder
|
||||
import app.pachli.databinding.ItemTabPreferenceBinding
|
||||
import app.pachli.databinding.ItemTabPreferenceSmallBinding
|
||||
|
@ -39,8 +39,8 @@ interface ItemInteractionListener {
|
|||
fun onTabRemoved(position: Int)
|
||||
fun onStartDelete(viewHolder: RecyclerView.ViewHolder)
|
||||
fun onStartDrag(viewHolder: RecyclerView.ViewHolder)
|
||||
fun onActionChipClicked(tabData: TabData.Hashtag, tabPosition: Int)
|
||||
fun onChipClicked(tabData: TabData.Hashtag, tabPosition: Int, chipPosition: Int)
|
||||
fun onActionChipClicked(timelineHashtags: Timeline.Hashtags, tabPosition: Int)
|
||||
fun onChipClicked(timelineHashtags: Timeline.Hashtags, tabPosition: Int, chipPosition: Int)
|
||||
}
|
||||
|
||||
class TabAdapter(
|
||||
|
@ -81,8 +81,8 @@ class TabAdapter(
|
|||
} else {
|
||||
val binding = holder.binding as ItemTabPreferenceBinding
|
||||
|
||||
if (tab.tabData is TabData.UserList) {
|
||||
binding.textView.text = tab.tabData.title
|
||||
if (tab.timeline is Timeline.UserList) {
|
||||
binding.textView.text = tab.timeline.title
|
||||
} else {
|
||||
binding.textView.setText(tab.text)
|
||||
}
|
||||
|
@ -107,7 +107,7 @@ class TabAdapter(
|
|||
(if (removeButtonEnabled) android.R.attr.textColorTertiary else DR.attr.textColorDisabled),
|
||||
)
|
||||
|
||||
if (tab.tabData is TabData.Hashtag) {
|
||||
if (tab.timeline is Timeline.Hashtags) {
|
||||
binding.chipGroup.show()
|
||||
|
||||
/*
|
||||
|
@ -115,7 +115,7 @@ class TabAdapter(
|
|||
* The other dynamic chips are inserted in front of the actionChip.
|
||||
* This code tries to reuse already added chips to reduce the number of Views created.
|
||||
*/
|
||||
tab.tabData.tags.forEachIndexed { i, arg ->
|
||||
tab.timeline.tags.forEachIndexed { i, arg ->
|
||||
|
||||
val chip = binding.chipGroup.getChildAt(i).takeUnless { it.id == R.id.actionChip } as Chip?
|
||||
?: Chip(context).apply {
|
||||
|
@ -126,23 +126,23 @@ class TabAdapter(
|
|||
|
||||
chip.text = arg
|
||||
|
||||
if (tab.tabData.tags.size <= 1) {
|
||||
if (tab.timeline.tags.size <= 1) {
|
||||
chip.isCloseIconVisible = false
|
||||
chip.setOnClickListener(null)
|
||||
} else {
|
||||
chip.isCloseIconVisible = true
|
||||
chip.setOnClickListener {
|
||||
listener.onChipClicked(tab.tabData, holder.bindingAdapterPosition, i)
|
||||
listener.onChipClicked(tab.timeline, holder.bindingAdapterPosition, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (binding.chipGroup.size - 1 > tab.tabData.tags.size) {
|
||||
binding.chipGroup.removeViewAt(tab.tabData.tags.size)
|
||||
while (binding.chipGroup.size - 1 > tab.timeline.tags.size) {
|
||||
binding.chipGroup.removeViewAt(tab.timeline.tags.size)
|
||||
}
|
||||
|
||||
binding.actionChip.setOnClickListener {
|
||||
listener.onActionChipClicked(tab.tabData, holder.bindingAdapterPosition)
|
||||
listener.onActionChipClicked(tab.timeline, holder.bindingAdapterPosition)
|
||||
}
|
||||
} else {
|
||||
binding.chipGroup.hide()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package app.pachli.appstore
|
||||
|
||||
import app.pachli.core.database.model.TabData
|
||||
import app.pachli.core.model.Timeline
|
||||
import app.pachli.core.network.model.Account
|
||||
import app.pachli.core.network.model.FilterContext
|
||||
import app.pachli.core.network.model.Poll
|
||||
|
@ -22,7 +22,7 @@ data object StatusScheduledEvent : Event
|
|||
data class StatusEditedEvent(val originalId: String, val status: Status) : Event
|
||||
data class ProfileEditedEvent(val newProfileData: Account) : Event
|
||||
data class FilterChangedEvent(val filterContext: FilterContext) : Event
|
||||
data class MainTabsChangedEvent(val newTabs: List<TabData>) : Event
|
||||
data class MainTabsChangedEvent(val newTabs: List<Timeline>) : Event
|
||||
data class PollVoteEvent(val statusId: String, val poll: Poll) : Event
|
||||
data class DomainMuteEvent(val instance: String) : Event
|
||||
data class AnnouncementReadEvent(val announcementId: String) : Event
|
||||
|
|
|
@ -22,7 +22,7 @@ import app.pachli.components.account.media.AccountMediaFragment
|
|||
import app.pachli.components.timeline.TimelineFragment
|
||||
import app.pachli.core.activity.CustomFragmentStateAdapter
|
||||
import app.pachli.core.activity.RefreshableFragment
|
||||
import app.pachli.core.network.model.TimelineKind
|
||||
import app.pachli.core.model.Timeline
|
||||
|
||||
class AccountPagerAdapter(
|
||||
activity: FragmentActivity,
|
||||
|
@ -33,9 +33,9 @@ class AccountPagerAdapter(
|
|||
|
||||
override fun createFragment(position: Int): Fragment {
|
||||
return when (position) {
|
||||
0 -> TimelineFragment.newInstance(TimelineKind.User.Posts(accountId), false)
|
||||
1 -> TimelineFragment.newInstance(TimelineKind.User.Replies(accountId), false)
|
||||
2 -> TimelineFragment.newInstance(TimelineKind.User.Pinned(accountId), false)
|
||||
0 -> TimelineFragment.newInstance(Timeline.User.Posts(accountId), false)
|
||||
1 -> TimelineFragment.newInstance(Timeline.User.Replies(accountId), false)
|
||||
2 -> TimelineFragment.newInstance(Timeline.User.Pinned(accountId), false)
|
||||
3 -> AccountMediaFragment.newInstance(accountId)
|
||||
else -> throw AssertionError("Page $position is out of AccountPagerAdapter bounds")
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ import app.pachli.core.database.model.StatusViewDataEntity
|
|||
import app.pachli.core.database.model.TimelineStatusWithAccount
|
||||
import app.pachli.core.database.model.TranslatedStatusEntity
|
||||
import app.pachli.core.database.model.TranslationState
|
||||
import app.pachli.core.network.model.TimelineKind
|
||||
import app.pachli.core.model.Timeline
|
||||
import app.pachli.core.network.model.Translation
|
||||
import app.pachli.core.network.retrofit.MastodonApi
|
||||
import app.pachli.util.EmptyPagingSource
|
||||
|
@ -76,7 +76,7 @@ class CachedTimelineRepository @Inject constructor(
|
|||
/** @return flow of Mastodon [TimelineStatusWithAccount], loaded in [pageSize] increments */
|
||||
@OptIn(ExperimentalPagingApi::class, ExperimentalCoroutinesApi::class)
|
||||
fun getStatusStream(
|
||||
kind: TimelineKind,
|
||||
kind: Timeline,
|
||||
pageSize: Int = PAGE_SIZE,
|
||||
initialKey: String? = null,
|
||||
): Flow<PagingData<TimelineStatusWithAccount>> {
|
||||
|
|
|
@ -27,8 +27,8 @@ import app.pachli.components.timeline.viewmodel.NetworkTimelinePagingSource
|
|||
import app.pachli.components.timeline.viewmodel.NetworkTimelineRemoteMediator
|
||||
import app.pachli.components.timeline.viewmodel.PageCache
|
||||
import app.pachli.core.accounts.AccountManager
|
||||
import app.pachli.core.model.Timeline
|
||||
import app.pachli.core.network.model.Status
|
||||
import app.pachli.core.network.model.TimelineKind
|
||||
import app.pachli.core.network.retrofit.MastodonApi
|
||||
import app.pachli.util.getDomain
|
||||
import javax.inject.Inject
|
||||
|
@ -81,7 +81,7 @@ class NetworkTimelineRepository @Inject constructor(
|
|||
@OptIn(ExperimentalPagingApi::class)
|
||||
fun getStatusStream(
|
||||
viewModelScope: CoroutineScope,
|
||||
kind: TimelineKind,
|
||||
kind: Timeline,
|
||||
pageSize: Int = PAGE_SIZE,
|
||||
initialKey: String? = null,
|
||||
): Flow<PagingData<Status>> {
|
||||
|
|
|
@ -56,11 +56,11 @@ import app.pachli.core.common.extensions.show
|
|||
import app.pachli.core.common.extensions.viewBinding
|
||||
import app.pachli.core.common.extensions.visible
|
||||
import app.pachli.core.database.model.TranslationState
|
||||
import app.pachli.core.model.Timeline
|
||||
import app.pachli.core.navigation.AccountListActivityIntent
|
||||
import app.pachli.core.navigation.AttachmentViewData
|
||||
import app.pachli.core.network.model.Poll
|
||||
import app.pachli.core.network.model.Status
|
||||
import app.pachli.core.network.model.TimelineKind
|
||||
import app.pachli.core.ui.BackgroundMessage
|
||||
import app.pachli.core.ui.extensions.getErrorString
|
||||
import app.pachli.databinding.FragmentTimelineBinding
|
||||
|
@ -112,11 +112,11 @@ class TimelineFragment :
|
|||
// If the navigation library was being used this would happen automatically, so this
|
||||
// workaround can be removed when that change happens.
|
||||
private val viewModel: TimelineViewModel by lazy {
|
||||
if (timelineKind == TimelineKind.Home) {
|
||||
if (timeline == Timeline.Home) {
|
||||
viewModels<CachedTimelineViewModel>(
|
||||
extrasProducer = {
|
||||
MutableCreationExtras(defaultViewModelCreationExtras).apply {
|
||||
set(DEFAULT_ARGS_KEY, TimelineViewModel.creationExtras(timelineKind))
|
||||
set(DEFAULT_ARGS_KEY, TimelineViewModel.creationExtras(timeline))
|
||||
}
|
||||
},
|
||||
).value
|
||||
|
@ -124,7 +124,7 @@ class TimelineFragment :
|
|||
viewModels<NetworkTimelineViewModel>(
|
||||
extrasProducer = {
|
||||
MutableCreationExtras(defaultViewModelCreationExtras).apply {
|
||||
set(DEFAULT_ARGS_KEY, TimelineViewModel.creationExtras(timelineKind))
|
||||
set(DEFAULT_ARGS_KEY, TimelineViewModel.creationExtras(timeline))
|
||||
}
|
||||
},
|
||||
).value
|
||||
|
@ -133,7 +133,7 @@ class TimelineFragment :
|
|||
|
||||
private val binding by viewBinding(FragmentTimelineBinding::bind)
|
||||
|
||||
private lateinit var timelineKind: TimelineKind
|
||||
private lateinit var timeline: Timeline
|
||||
|
||||
private lateinit var adapter: TimelinePagingAdapter
|
||||
|
||||
|
@ -154,7 +154,7 @@ class TimelineFragment :
|
|||
|
||||
val arguments = requireArguments()
|
||||
|
||||
timelineKind = arguments.getParcelable(KIND_ARG)!!
|
||||
timeline = arguments.getParcelable(KIND_ARG)!!
|
||||
|
||||
isSwipeToRefreshEnabled = arguments.getBoolean(ARG_ENABLE_SWIPE_TO_REFRESH, true)
|
||||
|
||||
|
@ -466,7 +466,7 @@ class TimelineFragment :
|
|||
PresentationState.PRESENTED -> {
|
||||
if (adapter.itemCount == 0) {
|
||||
binding.statusView.setup(BackgroundMessage.Empty())
|
||||
if (timelineKind == TimelineKind.Home) {
|
||||
if (timeline == Timeline.Home) {
|
||||
binding.statusView.showHelp(R.string.help_empty_home)
|
||||
}
|
||||
binding.statusView.show()
|
||||
|
@ -642,7 +642,7 @@ class TimelineFragment :
|
|||
}
|
||||
|
||||
// Can only translate the home timeline at the moment
|
||||
override fun canTranslate() = timelineKind == TimelineKind.Home
|
||||
override fun canTranslate() = timeline == Timeline.Home
|
||||
|
||||
override fun onTranslate(statusViewData: StatusViewData) {
|
||||
viewModel.accept(StatusAction.Translate(statusViewData))
|
||||
|
@ -672,10 +672,10 @@ class TimelineFragment :
|
|||
}
|
||||
|
||||
override fun onViewTag(tag: String) {
|
||||
val timelineKind = viewModel.timelineKind
|
||||
val timelineKind = viewModel.timeline
|
||||
|
||||
// If already viewing a tag page, then ignore any request to view that tag again.
|
||||
if (timelineKind is TimelineKind.Tag && timelineKind.tags.contains(tag)) {
|
||||
if ((timelineKind as? Timeline.Hashtags)?.tags?.contains(tag) == true) {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -683,10 +683,10 @@ class TimelineFragment :
|
|||
}
|
||||
|
||||
override fun onViewAccount(id: String) {
|
||||
val timelineKind = viewModel.timelineKind
|
||||
val timelineKind = viewModel.timeline
|
||||
|
||||
// Ignore request to view the account page we're currently viewing
|
||||
if (timelineKind is TimelineKind.User && timelineKind.id == id) {
|
||||
if (timelineKind is Timeline.User && timelineKind.id == id) {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -703,21 +703,25 @@ class TimelineFragment :
|
|||
* that have been made to it.
|
||||
*/
|
||||
private fun handleStatusSentOrEdit(status: Status) {
|
||||
when (timelineKind) {
|
||||
is TimelineKind.User.Pinned -> return
|
||||
when (timeline) {
|
||||
is Timeline.User.Pinned -> return
|
||||
|
||||
is TimelineKind.Home,
|
||||
is TimelineKind.PublicFederated,
|
||||
is TimelineKind.PublicLocal,
|
||||
is Timeline.Home,
|
||||
is Timeline.PublicFederated,
|
||||
is Timeline.PublicLocal,
|
||||
-> adapter.refresh()
|
||||
is TimelineKind.User -> if (status.account.id == (timelineKind as TimelineKind.User).id) {
|
||||
is Timeline.User -> if (status.account.id == (timeline as Timeline.User).id) {
|
||||
adapter.refresh()
|
||||
}
|
||||
is TimelineKind.Bookmarks,
|
||||
is TimelineKind.Favourites,
|
||||
is TimelineKind.Tag,
|
||||
is TimelineKind.TrendingStatuses,
|
||||
is TimelineKind.UserList,
|
||||
is Timeline.Bookmarks,
|
||||
is Timeline.Favourites,
|
||||
is Timeline.Hashtags,
|
||||
is Timeline.TrendingStatuses,
|
||||
is Timeline.UserList,
|
||||
is Timeline.Conversations,
|
||||
Timeline.Notifications,
|
||||
Timeline.TrendingHashtags,
|
||||
Timeline.TrendingLinks,
|
||||
-> return
|
||||
}
|
||||
}
|
||||
|
@ -727,10 +731,10 @@ class TimelineFragment :
|
|||
}
|
||||
|
||||
private fun actionButtonPresent(): Boolean {
|
||||
return viewModel.timelineKind !is TimelineKind.Tag &&
|
||||
viewModel.timelineKind !is TimelineKind.Favourites &&
|
||||
viewModel.timelineKind !is TimelineKind.Bookmarks &&
|
||||
viewModel.timelineKind !is TimelineKind.TrendingStatuses &&
|
||||
return viewModel.timeline !is Timeline.Hashtags &&
|
||||
viewModel.timeline !is Timeline.Favourites &&
|
||||
viewModel.timeline !is Timeline.Bookmarks &&
|
||||
viewModel.timeline !is Timeline.TrendingStatuses &&
|
||||
activity is ActionButtonActivity
|
||||
}
|
||||
|
||||
|
@ -771,12 +775,12 @@ class TimelineFragment :
|
|||
private const val ARG_ENABLE_SWIPE_TO_REFRESH = "enableSwipeToRefresh"
|
||||
|
||||
fun newInstance(
|
||||
timelineKind: TimelineKind,
|
||||
timeline: Timeline,
|
||||
enableSwipeToRefresh: Boolean = true,
|
||||
): TimelineFragment {
|
||||
val fragment = TimelineFragment()
|
||||
val arguments = Bundle(2)
|
||||
arguments.putParcelable(KIND_ARG, timelineKind)
|
||||
arguments.putParcelable(KIND_ARG, timeline)
|
||||
arguments.putBoolean(ARG_ENABLE_SWIPE_TO_REFRESH, enableSwipeToRefresh)
|
||||
fragment.arguments = arguments
|
||||
return fragment
|
||||
|
|
|
@ -82,12 +82,12 @@ class CachedTimelineViewModel @Inject constructor(
|
|||
}.cachedIn(viewModelScope)
|
||||
}
|
||||
|
||||
/** @return Flow of statuses that make up the timeline of [timelineKind] */
|
||||
/** @return Flow of statuses that make up the timeline of [timeline] */
|
||||
private fun getStatuses(
|
||||
initialKey: String? = null,
|
||||
): Flow<PagingData<StatusViewData>> {
|
||||
Timber.d("getStatuses: kind: %s, initialKey: %s", timelineKind, initialKey)
|
||||
return repository.getStatusStream(kind = timelineKind, initialKey = initialKey)
|
||||
Timber.d("getStatuses: kind: %s, initialKey: %s", timeline, initialKey)
|
||||
return repository.getStatusStream(kind = timeline, initialKey = initialKey)
|
||||
.map { pagingData ->
|
||||
pagingData
|
||||
.map {
|
||||
|
|
|
@ -24,8 +24,8 @@ import androidx.paging.PagingState
|
|||
import androidx.paging.RemoteMediator
|
||||
import app.pachli.BuildConfig
|
||||
import app.pachli.core.accounts.AccountManager
|
||||
import app.pachli.core.model.Timeline
|
||||
import app.pachli.core.network.model.Status
|
||||
import app.pachli.core.network.model.TimelineKind
|
||||
import app.pachli.core.network.retrofit.MastodonApi
|
||||
import java.io.IOException
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
@ -41,7 +41,7 @@ class NetworkTimelineRemoteMediator(
|
|||
accountManager: AccountManager,
|
||||
private val factory: InvalidatingPagingSourceFactory<String, Status>,
|
||||
private val pageCache: PageCache,
|
||||
private val timelineKind: TimelineKind,
|
||||
private val timeline: Timeline,
|
||||
) : RemoteMediator<String, Status>() {
|
||||
|
||||
private val activeAccount = accountManager.activeAccount!!
|
||||
|
@ -116,7 +116,7 @@ class NetworkTimelineRemoteMediator(
|
|||
Timber.d(
|
||||
" Page %s complete for %s, now got %d pages",
|
||||
loadType,
|
||||
timelineKind,
|
||||
timeline,
|
||||
pageCache.size,
|
||||
)
|
||||
pageCache.debug()
|
||||
|
@ -132,7 +132,7 @@ class NetworkTimelineRemoteMediator(
|
|||
}
|
||||
}
|
||||
|
||||
@Throws(IOException::class, HttpException::class)
|
||||
@Throws(IOException::class, HttpException::class, IllegalStateException::class)
|
||||
private suspend fun fetchStatusPageByKind(loadType: LoadType, key: String?, loadSize: Int): Response<List<Status>> {
|
||||
val (maxId, minId) = when (loadType) {
|
||||
// When refreshing fetch a page of statuses that are immediately *newer* than the key
|
||||
|
@ -144,20 +144,20 @@ class NetworkTimelineRemoteMediator(
|
|||
LoadType.PREPEND -> Pair(null, key)
|
||||
}
|
||||
|
||||
return when (timelineKind) {
|
||||
TimelineKind.Bookmarks -> api.bookmarks(maxId = maxId, minId = minId, limit = loadSize)
|
||||
TimelineKind.Favourites -> api.favourites(maxId = maxId, minId = minId, limit = loadSize)
|
||||
TimelineKind.Home -> api.homeTimeline(maxId = maxId, minId = minId, limit = loadSize)
|
||||
TimelineKind.PublicFederated -> api.publicTimeline(local = false, maxId = maxId, minId = minId, limit = loadSize)
|
||||
TimelineKind.PublicLocal -> api.publicTimeline(local = true, maxId = maxId, minId = minId, limit = loadSize)
|
||||
TimelineKind.TrendingStatuses -> api.trendingStatuses()
|
||||
is TimelineKind.Tag -> {
|
||||
val firstHashtag = timelineKind.tags.first()
|
||||
val additionalHashtags = timelineKind.tags.subList(1, timelineKind.tags.size)
|
||||
return when (timeline) {
|
||||
Timeline.Bookmarks -> api.bookmarks(maxId = maxId, minId = minId, limit = loadSize)
|
||||
Timeline.Favourites -> api.favourites(maxId = maxId, minId = minId, limit = loadSize)
|
||||
Timeline.Home -> api.homeTimeline(maxId = maxId, minId = minId, limit = loadSize)
|
||||
Timeline.PublicFederated -> api.publicTimeline(local = false, maxId = maxId, minId = minId, limit = loadSize)
|
||||
Timeline.PublicLocal -> api.publicTimeline(local = true, maxId = maxId, minId = minId, limit = loadSize)
|
||||
Timeline.TrendingStatuses -> api.trendingStatuses()
|
||||
is Timeline.Hashtags -> {
|
||||
val firstHashtag = timeline.tags.first()
|
||||
val additionalHashtags = timeline.tags.subList(1, timeline.tags.size)
|
||||
api.hashtagTimeline(firstHashtag, additionalHashtags, null, maxId = maxId, minId = minId, limit = loadSize)
|
||||
}
|
||||
is TimelineKind.User.Pinned -> api.accountStatuses(
|
||||
timelineKind.id,
|
||||
is Timeline.User.Pinned -> api.accountStatuses(
|
||||
timeline.id,
|
||||
maxId = maxId,
|
||||
minId = minId,
|
||||
limit = loadSize,
|
||||
|
@ -165,8 +165,8 @@ class NetworkTimelineRemoteMediator(
|
|||
onlyMedia = null,
|
||||
pinned = true,
|
||||
)
|
||||
is TimelineKind.User.Posts -> api.accountStatuses(
|
||||
timelineKind.id,
|
||||
is Timeline.User.Posts -> api.accountStatuses(
|
||||
timeline.id,
|
||||
maxId = maxId,
|
||||
minId = minId,
|
||||
limit = loadSize,
|
||||
|
@ -174,8 +174,8 @@ class NetworkTimelineRemoteMediator(
|
|||
onlyMedia = null,
|
||||
pinned = null,
|
||||
)
|
||||
is TimelineKind.User.Replies -> api.accountStatuses(
|
||||
timelineKind.id,
|
||||
is Timeline.User.Replies -> api.accountStatuses(
|
||||
timeline.id,
|
||||
maxId = maxId,
|
||||
minId = minId,
|
||||
limit = loadSize,
|
||||
|
@ -183,12 +183,13 @@ class NetworkTimelineRemoteMediator(
|
|||
onlyMedia = null,
|
||||
pinned = null,
|
||||
)
|
||||
is TimelineKind.UserList -> api.listTimeline(
|
||||
timelineKind.id,
|
||||
is Timeline.UserList -> api.listTimeline(
|
||||
timeline.listId,
|
||||
maxId = maxId,
|
||||
minId = minId,
|
||||
limit = loadSize,
|
||||
)
|
||||
else -> throw IllegalStateException("NetworkTimelineRemoteMediator does not support $timeline")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,12 +80,12 @@ class NetworkTimelineViewModel @Inject constructor(
|
|||
}.cachedIn(viewModelScope)
|
||||
}
|
||||
|
||||
/** @return Flow of statuses that make up the timeline of [timelineKind] */
|
||||
/** @return Flow of statuses that make up the timeline of [timeline] */
|
||||
private fun getStatuses(
|
||||
initialKey: String? = null,
|
||||
): Flow<PagingData<StatusViewData>> {
|
||||
Timber.d("getStatuses: kind: %s, initialKey: %s", timelineKind, initialKey)
|
||||
return repository.getStatusStream(viewModelScope, kind = timelineKind, initialKey = initialKey)
|
||||
Timber.d("getStatuses: kind: %s, initialKey: %s", timeline, initialKey)
|
||||
return repository.getStatusStream(viewModelScope, kind = timeline, initialKey = initialKey)
|
||||
.map { pagingData ->
|
||||
pagingData.map {
|
||||
modifiedViewData[it.id] ?: StatusViewData.from(
|
||||
|
|
|
@ -44,11 +44,11 @@ import app.pachli.appstore.UnfollowEvent
|
|||
import app.pachli.components.timeline.FilterKind
|
||||
import app.pachli.components.timeline.FiltersRepository
|
||||
import app.pachli.core.accounts.AccountManager
|
||||
import app.pachli.core.model.Timeline
|
||||
import app.pachli.core.network.model.Filter
|
||||
import app.pachli.core.network.model.FilterContext
|
||||
import app.pachli.core.network.model.Poll
|
||||
import app.pachli.core.network.model.Status
|
||||
import app.pachli.core.network.model.TimelineKind
|
||||
import app.pachli.core.preferences.PrefKeys
|
||||
import app.pachli.core.preferences.SharedPreferencesRepository
|
||||
import app.pachli.network.FilterModel
|
||||
|
@ -303,7 +303,7 @@ abstract class TimelineViewModel(
|
|||
viewModelScope.launch { uiAction.emit(action) }
|
||||
}
|
||||
|
||||
val timelineKind: TimelineKind = savedStateHandle.get<TimelineKind>(TIMELINE_KIND_TAG)!!
|
||||
val timeline: Timeline = savedStateHandle.get<Timeline>(TIMELINE_TAG)!!
|
||||
|
||||
private var filterRemoveReplies = false
|
||||
private var filterRemoveReblogs = false
|
||||
|
@ -396,7 +396,7 @@ abstract class TimelineViewModel(
|
|||
initialValue = UiState(showFabWhileScrolling = !sharedPreferencesRepository.getBoolean(PrefKeys.FAB_HIDE, false)),
|
||||
)
|
||||
|
||||
if (timelineKind is TimelineKind.Home) {
|
||||
if (timeline is Timeline.Home) {
|
||||
// Note the variable is "true if filter" but the underlying preference/settings text is "true if show"
|
||||
filterRemoveReplies =
|
||||
!sharedPreferencesRepository.getBoolean(PrefKeys.TAB_FILTER_HOME_REPLIES, true)
|
||||
|
@ -407,7 +407,7 @@ abstract class TimelineViewModel(
|
|||
}
|
||||
|
||||
// Save the visible status ID (if it's the home timeline)
|
||||
if (timelineKind == TimelineKind.Home) {
|
||||
if (timeline == Timeline.Home) {
|
||||
viewModelScope.launch {
|
||||
uiAction
|
||||
.filterIsInstance<InfallibleUiAction.SaveVisibleId>()
|
||||
|
@ -426,7 +426,7 @@ abstract class TimelineViewModel(
|
|||
uiAction
|
||||
.filterIsInstance<InfallibleUiAction.LoadNewest>()
|
||||
.collectLatest {
|
||||
if (timelineKind == TimelineKind.Home) {
|
||||
if (timeline == Timeline.Home) {
|
||||
activeAccount.lastVisibleHomeTimelineStatusId = null
|
||||
accountManager.saveAccount(activeAccount)
|
||||
}
|
||||
|
@ -449,7 +449,7 @@ abstract class TimelineViewModel(
|
|||
}
|
||||
|
||||
fun getInitialKey(): String? {
|
||||
if (timelineKind != TimelineKind.Home) {
|
||||
if (timeline != Timeline.Home) {
|
||||
return null
|
||||
}
|
||||
|
||||
|
@ -521,7 +521,7 @@ abstract class TimelineViewModel(
|
|||
/** Updates the current set of filters if filter-related preferences change */
|
||||
private fun updateFiltersFromPreferences() = eventHub.events
|
||||
.filterIsInstance<FilterChangedEvent>()
|
||||
.filter { filterContextMatchesKind(timelineKind, listOf(it.filterContext)) }
|
||||
.filter { filterContextMatchesKind(timeline, listOf(it.filterContext)) }
|
||||
.map {
|
||||
getFilters()
|
||||
Timber.d("Reload because FilterChangedEvent")
|
||||
|
@ -534,10 +534,11 @@ abstract class TimelineViewModel(
|
|||
viewModelScope.launch {
|
||||
Timber.d("getFilters()")
|
||||
try {
|
||||
val filterContext = FilterContext.from(timelineKind)
|
||||
filterModel = when (val filters = filtersRepository.getFilters()) {
|
||||
is FilterKind.V1 -> FilterModel(filterContext, filters.filters)
|
||||
is FilterKind.V2 -> FilterModel(filterContext)
|
||||
FilterContext.from(timeline)?.let { filterContext ->
|
||||
filterModel = when (val filters = filtersRepository.getFilters()) {
|
||||
is FilterKind.V1 -> FilterModel(filterContext, filters.filters)
|
||||
is FilterKind.V2 -> FilterModel(filterContext)
|
||||
}
|
||||
}
|
||||
} catch (throwable: Throwable) {
|
||||
Timber.d(throwable, "updateFilter(): Error fetching filters")
|
||||
|
@ -552,7 +553,7 @@ abstract class TimelineViewModel(
|
|||
PrefKeys.TAB_FILTER_HOME_REPLIES -> {
|
||||
val filter = sharedPreferencesRepository.getBoolean(PrefKeys.TAB_FILTER_HOME_REPLIES, true)
|
||||
val oldRemoveReplies = filterRemoveReplies
|
||||
filterRemoveReplies = timelineKind is TimelineKind.Home && !filter
|
||||
filterRemoveReplies = timeline is Timeline.Home && !filter
|
||||
if (oldRemoveReplies != filterRemoveReplies) {
|
||||
Timber.d("Reload because TAB_FILTER_HOME_REPLIES changed")
|
||||
reloadKeepingReadingPosition()
|
||||
|
@ -561,7 +562,7 @@ abstract class TimelineViewModel(
|
|||
PrefKeys.TAB_FILTER_HOME_BOOSTS -> {
|
||||
val filter = sharedPreferencesRepository.getBoolean(PrefKeys.TAB_FILTER_HOME_BOOSTS, true)
|
||||
val oldRemoveReblogs = filterRemoveReblogs
|
||||
filterRemoveReblogs = timelineKind is TimelineKind.Home && !filter
|
||||
filterRemoveReblogs = timeline is Timeline.Home && !filter
|
||||
if (oldRemoveReblogs != filterRemoveReblogs) {
|
||||
Timber.d("Reload because TAB_FILTER_HOME_BOOSTS changed")
|
||||
reloadKeepingReadingPosition()
|
||||
|
@ -570,7 +571,7 @@ abstract class TimelineViewModel(
|
|||
PrefKeys.TAB_SHOW_HOME_SELF_BOOSTS -> {
|
||||
val filter = sharedPreferencesRepository.getBoolean(PrefKeys.TAB_SHOW_HOME_SELF_BOOSTS, true)
|
||||
val oldRemoveSelfReblogs = filterRemoveSelfReblogs
|
||||
filterRemoveSelfReblogs = timelineKind is TimelineKind.Home && !filter
|
||||
filterRemoveSelfReblogs = timeline is Timeline.Home && !filter
|
||||
if (oldRemoveSelfReblogs != filterRemoveSelfReblogs) {
|
||||
Timber.d("Reload because TAB_SHOW_SOME_SELF_BOOSTS changed")
|
||||
reloadKeepingReadingPosition()
|
||||
|
@ -590,31 +591,31 @@ abstract class TimelineViewModel(
|
|||
reloadKeepingReadingPosition()
|
||||
}
|
||||
is UnfollowEvent -> {
|
||||
if (timelineKind is TimelineKind.Home) {
|
||||
if (timeline is Timeline.Home) {
|
||||
val id = event.accountId
|
||||
removeAllByAccountId(id)
|
||||
}
|
||||
}
|
||||
is BlockEvent -> {
|
||||
if (timelineKind !is TimelineKind.User) {
|
||||
if (timeline !is Timeline.User) {
|
||||
val id = event.accountId
|
||||
removeAllByAccountId(id)
|
||||
}
|
||||
}
|
||||
is MuteEvent -> {
|
||||
if (timelineKind !is TimelineKind.User) {
|
||||
if (timeline !is Timeline.User) {
|
||||
val id = event.accountId
|
||||
removeAllByAccountId(id)
|
||||
}
|
||||
}
|
||||
is DomainMuteEvent -> {
|
||||
if (timelineKind !is TimelineKind.User) {
|
||||
if (timeline !is Timeline.User) {
|
||||
val instance = event.instance
|
||||
removeAllByInstance(instance)
|
||||
}
|
||||
}
|
||||
is StatusDeletedEvent -> {
|
||||
if (timelineKind !is TimelineKind.User) {
|
||||
if (timeline !is Timeline.User) {
|
||||
removeStatusWithId(event.statusId)
|
||||
}
|
||||
}
|
||||
|
@ -626,18 +627,18 @@ abstract class TimelineViewModel(
|
|||
|
||||
/** Tag for the timelineKind in `savedStateHandle` */
|
||||
@VisibleForTesting(VisibleForTesting.PRIVATE)
|
||||
const val TIMELINE_KIND_TAG = "timelineKind"
|
||||
const val TIMELINE_TAG = "timeline"
|
||||
|
||||
/** Create extras for this view model */
|
||||
fun creationExtras(timelineKind: TimelineKind) = bundleOf(
|
||||
TIMELINE_KIND_TAG to timelineKind,
|
||||
fun creationExtras(timeline: Timeline) = bundleOf(
|
||||
TIMELINE_TAG to timeline,
|
||||
)
|
||||
|
||||
fun filterContextMatchesKind(
|
||||
timelineKind: TimelineKind,
|
||||
timeline: Timeline,
|
||||
filterContext: List<FilterContext>,
|
||||
): Boolean {
|
||||
return filterContext.contains(FilterContext.from(timelineKind))
|
||||
return filterContext.contains(FilterContext.from(timeline))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ import app.pachli.R
|
|||
import app.pachli.components.timeline.TimelineFragment
|
||||
import app.pachli.core.activity.BottomSheetActivity
|
||||
import app.pachli.core.common.extensions.viewBinding
|
||||
import app.pachli.core.network.model.TimelineKind
|
||||
import app.pachli.core.model.Timeline
|
||||
import app.pachli.core.ui.extensions.reduceSwipeSensitivity
|
||||
import app.pachli.databinding.ActivityTrendingBinding
|
||||
import app.pachli.interfaces.AppBarLayoutHost
|
||||
|
@ -93,7 +93,7 @@ class TrendingFragmentAdapter(val activity: FragmentActivity) : FragmentStateAda
|
|||
return when (position) {
|
||||
0 -> TrendingTagsFragment.newInstance()
|
||||
1 -> TrendingLinksFragment.newInstance()
|
||||
2 -> TimelineFragment.newInstance(TimelineKind.TrendingStatuses)
|
||||
2 -> TimelineFragment.newInstance(Timeline.TrendingStatuses)
|
||||
else -> throw IllegalStateException()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,8 +31,8 @@ import app.pachli.components.notifications.createNotificationChannelsForAccount
|
|||
import app.pachli.components.notifications.makeNotification
|
||||
import app.pachli.core.accounts.AccountManager
|
||||
import app.pachli.core.database.model.AccountEntity
|
||||
import app.pachli.core.database.model.TabData
|
||||
import app.pachli.core.database.model.defaultTabs
|
||||
import app.pachli.core.model.Timeline
|
||||
import app.pachli.core.navigation.AccountListActivityIntent
|
||||
import app.pachli.core.network.model.Account
|
||||
import app.pachli.core.network.model.Notification
|
||||
|
@ -154,7 +154,7 @@ class MainActivityTest {
|
|||
rule.launch(intent)
|
||||
rule.getScenario().onActivity {
|
||||
val currentTab = it.findViewById<ViewPager2>(R.id.viewPager).currentItem
|
||||
val notificationTab = defaultTabs().indexOfFirst { it is TabData.Notifications }
|
||||
val notificationTab = defaultTabs().indexOfFirst { it is Timeline.Notifications }
|
||||
assertEquals(currentTab, notificationTab)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,8 +24,8 @@ import app.pachli.appstore.EventHub
|
|||
import app.pachli.components.timeline.viewmodel.CachedTimelineViewModel
|
||||
import app.pachli.components.timeline.viewmodel.TimelineViewModel
|
||||
import app.pachli.core.accounts.AccountManager
|
||||
import app.pachli.core.model.Timeline
|
||||
import app.pachli.core.network.model.Account
|
||||
import app.pachli.core.network.model.TimelineKind
|
||||
import app.pachli.core.network.model.nodeinfo.UnvalidatedJrd
|
||||
import app.pachli.core.network.model.nodeinfo.UnvalidatedNodeInfo
|
||||
import app.pachli.core.network.retrofit.MastodonApi
|
||||
|
@ -155,7 +155,7 @@ abstract class CachedTimelineViewModelTestBase {
|
|||
timelineCases = mock()
|
||||
|
||||
viewModel = CachedTimelineViewModel(
|
||||
SavedStateHandle(mapOf(TimelineViewModel.TIMELINE_KIND_TAG to TimelineKind.Home)),
|
||||
SavedStateHandle(mapOf(TimelineViewModel.TIMELINE_TAG to Timeline.Home)),
|
||||
cachedTimelineRepository,
|
||||
timelineCases,
|
||||
eventHub,
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
package app.pachli.components.timeline
|
||||
|
||||
import app.pachli.components.timeline.viewmodel.InfallibleUiAction
|
||||
import app.pachli.core.network.model.TimelineKind
|
||||
import app.pachli.core.model.Timeline
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import kotlinx.coroutines.test.runTest
|
||||
|
@ -32,7 +32,7 @@ class CachedTimelineViewModelTestVisibleId : CachedTimelineViewModelTestBase() {
|
|||
// Given
|
||||
assertThat(accountManager.activeAccount?.lastVisibleHomeTimelineStatusId)
|
||||
.isNull()
|
||||
assertThat(viewModel.timelineKind).isEqualTo(TimelineKind.Home)
|
||||
assertThat(viewModel.timeline).isEqualTo(Timeline.Home)
|
||||
|
||||
// When
|
||||
viewModel.accept(InfallibleUiAction.SaveVisibleId("1234"))
|
||||
|
|
|
@ -30,8 +30,8 @@ import app.pachli.components.timeline.viewmodel.Page
|
|||
import app.pachli.components.timeline.viewmodel.PageCache
|
||||
import app.pachli.core.accounts.AccountManager
|
||||
import app.pachli.core.database.model.AccountEntity
|
||||
import app.pachli.core.model.Timeline
|
||||
import app.pachli.core.network.model.Status
|
||||
import app.pachli.core.network.model.TimelineKind
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import okhttp3.Headers
|
||||
|
@ -79,7 +79,7 @@ class NetworkTimelineRemoteMediatorTest {
|
|||
accountManager = accountManager,
|
||||
factory = pagingSourceFactory,
|
||||
pageCache = PageCache(),
|
||||
timelineKind = TimelineKind.Home,
|
||||
timeline = Timeline.Home,
|
||||
)
|
||||
|
||||
// When
|
||||
|
@ -101,7 +101,7 @@ class NetworkTimelineRemoteMediatorTest {
|
|||
accountManager,
|
||||
factory = pagingSourceFactory,
|
||||
pageCache = PageCache(),
|
||||
timelineKind = TimelineKind.Home,
|
||||
timeline = Timeline.Home,
|
||||
)
|
||||
|
||||
// When
|
||||
|
@ -131,7 +131,7 @@ class NetworkTimelineRemoteMediatorTest {
|
|||
accountManager = accountManager,
|
||||
factory = pagingSourceFactory,
|
||||
pageCache = pages,
|
||||
timelineKind = TimelineKind.Home,
|
||||
timeline = Timeline.Home,
|
||||
)
|
||||
|
||||
val state = state(
|
||||
|
@ -194,7 +194,7 @@ class NetworkTimelineRemoteMediatorTest {
|
|||
accountManager = accountManager,
|
||||
factory = pagingSourceFactory,
|
||||
pageCache = pages,
|
||||
timelineKind = TimelineKind.Home,
|
||||
timeline = Timeline.Home,
|
||||
)
|
||||
|
||||
val state = state(
|
||||
|
@ -264,7 +264,7 @@ class NetworkTimelineRemoteMediatorTest {
|
|||
accountManager = accountManager,
|
||||
factory = pagingSourceFactory,
|
||||
pageCache = pages,
|
||||
timelineKind = TimelineKind.Home,
|
||||
timeline = Timeline.Home,
|
||||
)
|
||||
|
||||
val state = state(
|
||||
|
|
|
@ -23,8 +23,8 @@ import app.pachli.appstore.EventHub
|
|||
import app.pachli.components.timeline.viewmodel.NetworkTimelineViewModel
|
||||
import app.pachli.components.timeline.viewmodel.TimelineViewModel
|
||||
import app.pachli.core.accounts.AccountManager
|
||||
import app.pachli.core.model.Timeline
|
||||
import app.pachli.core.network.model.Account
|
||||
import app.pachli.core.network.model.TimelineKind
|
||||
import app.pachli.core.network.model.nodeinfo.UnvalidatedJrd
|
||||
import app.pachli.core.network.model.nodeinfo.UnvalidatedNodeInfo
|
||||
import app.pachli.core.network.retrofit.MastodonApi
|
||||
|
@ -145,7 +145,7 @@ abstract class NetworkTimelineViewModelTestBase {
|
|||
timelineCases = mock()
|
||||
|
||||
viewModel = NetworkTimelineViewModel(
|
||||
SavedStateHandle(mapOf(TimelineViewModel.TIMELINE_KIND_TAG to TimelineKind.Bookmarks)),
|
||||
SavedStateHandle(mapOf(TimelineViewModel.TIMELINE_TAG to Timeline.Bookmarks)),
|
||||
networkTimelineRepository,
|
||||
timelineCases,
|
||||
eventHub,
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
package app.pachli.components.timeline
|
||||
|
||||
import app.pachli.components.timeline.viewmodel.InfallibleUiAction
|
||||
import app.pachli.core.network.model.TimelineKind
|
||||
import app.pachli.core.model.Timeline
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import kotlinx.coroutines.test.runTest
|
||||
|
@ -32,8 +32,8 @@ class NetworkTimelineViewModelTestVisibleId : NetworkTimelineViewModelTestBase()
|
|||
// Given
|
||||
assertThat(accountManager.activeAccount?.lastVisibleHomeTimelineStatusId)
|
||||
.isNull()
|
||||
assertThat(viewModel.timelineKind)
|
||||
.isNotEqualTo(TimelineKind.Home)
|
||||
assertThat(viewModel.timeline)
|
||||
.isNotEqualTo(Timeline.Home)
|
||||
|
||||
// When
|
||||
viewModel.accept(InfallibleUiAction.SaveVisibleId("1234"))
|
||||
|
|
|
@ -32,6 +32,7 @@ dependencies {
|
|||
implementation(projects.core.accounts)
|
||||
implementation(projects.core.common)
|
||||
implementation(projects.core.database)
|
||||
implementation(projects.core.model)
|
||||
implementation(projects.core.network)
|
||||
|
||||
testImplementation(libs.bundles.mockito)
|
||||
|
|
|
@ -24,7 +24,7 @@ import app.pachli.core.data.repository.ListsError.Delete
|
|||
import app.pachli.core.data.repository.ListsError.GetListsWithAccount
|
||||
import app.pachli.core.data.repository.ListsError.Retrieve
|
||||
import app.pachli.core.data.repository.ListsError.Update
|
||||
import app.pachli.core.database.model.TabData
|
||||
import app.pachli.core.model.Timeline
|
||||
import app.pachli.core.network.model.MastoList
|
||||
import app.pachli.core.network.model.TimelineAccount
|
||||
import app.pachli.core.network.model.UserListRepliesPolicy
|
||||
|
@ -89,7 +89,7 @@ class NetworkListsRepository @Inject constructor(
|
|||
var changed = false
|
||||
val newTabPreferences = buildList {
|
||||
for (oldPref in oldTabPreferences) {
|
||||
if (oldPref !is TabData.UserList) {
|
||||
if (oldPref !is Timeline.UserList) {
|
||||
add(oldPref)
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ android {
|
|||
|
||||
dependencies {
|
||||
implementation(projects.core.common)
|
||||
implementation(projects.core.model)
|
||||
implementation(projects.core.network)
|
||||
implementation(projects.core.preferences)
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ import androidx.room.ProvidedTypeConverter
|
|||
import androidx.room.TypeConverter
|
||||
import app.pachli.core.database.model.ConversationAccountEntity
|
||||
import app.pachli.core.database.model.DraftAttachment
|
||||
import app.pachli.core.database.model.TabData
|
||||
import app.pachli.core.model.Timeline
|
||||
import app.pachli.core.network.model.Attachment
|
||||
import app.pachli.core.network.model.Emoji
|
||||
import app.pachli.core.network.model.FilterResult
|
||||
|
@ -66,13 +66,13 @@ class Converters @Inject constructor(
|
|||
}
|
||||
|
||||
@TypeConverter
|
||||
fun stringToTabData(str: String?): List<TabData>? {
|
||||
fun stringToTimeline(str: String?): List<Timeline>? {
|
||||
str ?: return null
|
||||
|
||||
// Two possible storage formats. Newer (from Pachli 2.4.0) is polymorphic
|
||||
// JSON, and the first character will be a '['
|
||||
if (str.startsWith('[')) {
|
||||
return moshi.adapter<List<TabData>>().fromJson(str)
|
||||
return moshi.adapter<List<Timeline>>().fromJson(str)
|
||||
}
|
||||
|
||||
// Older is string of ';' delimited tuples, one per tab.
|
||||
|
@ -87,27 +87,27 @@ class Converters @Inject constructor(
|
|||
val arguments = data.drop(1).map { s -> URLDecoder.decode(s, "UTF-8") }
|
||||
|
||||
when (kind) {
|
||||
"Home" -> TabData.Home
|
||||
"Notifications" -> TabData.Notifications
|
||||
"Local" -> TabData.Local
|
||||
"Federated" -> TabData.Federated
|
||||
"Direct" -> TabData.Direct
|
||||
"Home" -> Timeline.Home
|
||||
"Notifications" -> Timeline.Notifications
|
||||
"Local" -> Timeline.PublicLocal
|
||||
"Federated" -> Timeline.PublicFederated
|
||||
"Direct" -> Timeline.Conversations
|
||||
// Work around for https://github.com/pachli/pachli-android/issues/329
|
||||
// when the Trending... kinds may have been serialised without the '_'
|
||||
"TrendingTags", "Trending_Tags" -> TabData.TrendingTags
|
||||
"TrendingLinks", "Trending_Links" -> TabData.TrendingLinks
|
||||
"TrendingStatuses", "Trending_Statuses" -> TabData.TrendingStatuses
|
||||
"Hashtag" -> TabData.Hashtag(arguments)
|
||||
"List" -> TabData.UserList(arguments[0], arguments[1])
|
||||
"Bookmarks" -> TabData.Bookmarks
|
||||
"TrendingTags", "Trending_Tags" -> Timeline.TrendingHashtags
|
||||
"TrendingLinks", "Trending_Links" -> Timeline.TrendingLinks
|
||||
"TrendingStatuses", "Trending_Statuses" -> Timeline.TrendingStatuses
|
||||
"Hashtag" -> Timeline.Hashtags(arguments)
|
||||
"List" -> Timeline.UserList(arguments[0], arguments[1])
|
||||
"Bookmarks" -> Timeline.Bookmarks
|
||||
else -> throw IllegalStateException("Unrecognised tab kind: $kind")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun tabDataToString(tabData: List<TabData>?): String? {
|
||||
return moshi.adapter<List<TabData>>().toJson(tabData)
|
||||
fun timelineToString(timelines: List<Timeline>?): String? {
|
||||
return moshi.adapter<List<Timeline>>().toJson(timelines)
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
|
|
|
@ -23,6 +23,7 @@ import androidx.room.Index
|
|||
import androidx.room.PrimaryKey
|
||||
import androidx.room.TypeConverters
|
||||
import app.pachli.core.database.Converters
|
||||
import app.pachli.core.model.Timeline
|
||||
import app.pachli.core.network.model.Emoji
|
||||
import app.pachli.core.network.model.Status
|
||||
|
||||
|
@ -92,7 +93,7 @@ data class AccountEntity(
|
|||
@ColumnInfo(defaultValue = "0")
|
||||
var notificationMarkerId: String = "0",
|
||||
var emojis: List<Emoji> = emptyList(),
|
||||
var tabPreferences: List<TabData> = defaultTabs(),
|
||||
var tabPreferences: List<Timeline> = defaultTabs(),
|
||||
var notificationsFilter: String = "[\"follow_request\"]",
|
||||
// Scope cannot be changed without re-login, so store it in case
|
||||
// the scope needs to be changed in the future
|
||||
|
@ -147,3 +148,10 @@ data class AccountEntity(
|
|||
return result
|
||||
}
|
||||
}
|
||||
|
||||
fun defaultTabs() = listOf(
|
||||
Timeline.Home,
|
||||
Timeline.Notifications,
|
||||
Timeline.PublicLocal,
|
||||
Timeline.Conversations,
|
||||
)
|
||||
|
|
|
@ -1,70 +0,0 @@
|
|||
/*
|
||||
* Copyright 2023 Pachli Association
|
||||
*
|
||||
* This file is a part of Pachli.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Pachli 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 Pachli; if not,
|
||||
* see <http://www.gnu.org/licenses>.
|
||||
*/
|
||||
|
||||
package app.pachli.core.database.model
|
||||
|
||||
import com.squareup.moshi.JsonClass
|
||||
import dev.zacsweers.moshix.sealed.annotations.TypeLabel
|
||||
|
||||
@JsonClass(generateAdapter = true, generator = "sealed:kind")
|
||||
sealed interface TabData {
|
||||
@TypeLabel("home")
|
||||
data object Home : TabData
|
||||
|
||||
@TypeLabel("notifications")
|
||||
data object Notifications : TabData
|
||||
|
||||
@TypeLabel("local")
|
||||
data object Local : TabData
|
||||
|
||||
@TypeLabel("federated")
|
||||
data object Federated : TabData
|
||||
|
||||
@TypeLabel("direct")
|
||||
data object Direct : TabData
|
||||
|
||||
@TypeLabel("trending_tags")
|
||||
data object TrendingTags : TabData
|
||||
|
||||
@TypeLabel("trending_links")
|
||||
data object TrendingLinks : TabData
|
||||
|
||||
@TypeLabel("trending_statuses")
|
||||
data object TrendingStatuses : TabData
|
||||
|
||||
/**
|
||||
* @property tags List of one or more hashtags (without the leading '#')
|
||||
* to show in the tab.
|
||||
*/
|
||||
@TypeLabel("hashtag")
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Hashtag(val tags: List<String>) : TabData
|
||||
|
||||
@TypeLabel("list")
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class UserList(val listId: String, val title: String) : TabData
|
||||
|
||||
@TypeLabel("bookmarks")
|
||||
data object Bookmarks : TabData
|
||||
}
|
||||
|
||||
fun defaultTabs() = listOf(
|
||||
TabData.Home,
|
||||
TabData.Notifications,
|
||||
TabData.Local,
|
||||
TabData.Direct,
|
||||
)
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright 2024 Pachli Association
|
||||
*
|
||||
* This file is a part of Pachli.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Pachli 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 Pachli; if not,
|
||||
* see <http://www.gnu.org/licenses>.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.pachli.android.library)
|
||||
alias(libs.plugins.pachli.android.hilt)
|
||||
alias(libs.plugins.kotlin.parcelize)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "app.pachli.core.model"
|
||||
|
||||
defaultConfig {
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.moshi)
|
||||
implementation(libs.moshi.adapters)
|
||||
ksp(libs.moshi.codegen)
|
||||
|
||||
implementation(libs.moshix.sealed.runtime)
|
||||
ksp(libs.moshix.sealed.codegen)
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<issues format="6" by="lint 8.3.1" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.1)" variant="all" version="8.3.1">
|
||||
|
||||
</issues>
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Copyright 2024 Pachli Association
|
||||
*
|
||||
* This file is a part of Pachli.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Pachli 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 Pachli; if not,
|
||||
* see <http://www.gnu.org/licenses>.
|
||||
*/
|
||||
|
||||
package app.pachli.core.model
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import dev.zacsweers.moshix.sealed.annotations.NestedSealed
|
||||
import dev.zacsweers.moshix.sealed.annotations.TypeLabel
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
/** A timeline's type. Hold's data necessary to display that timeline. */
|
||||
@Parcelize
|
||||
@JsonClass(generateAdapter = true, generator = "sealed:kind")
|
||||
sealed interface Timeline : Parcelable {
|
||||
@Parcelize
|
||||
@TypeLabel("home")
|
||||
data object Home : Timeline
|
||||
|
||||
@Parcelize
|
||||
@TypeLabel("notifications")
|
||||
data object Notifications : Timeline
|
||||
|
||||
/** Federated timeline */
|
||||
@Parcelize
|
||||
@TypeLabel("federated")
|
||||
data object PublicFederated : Timeline
|
||||
|
||||
/** Local timeline of the user's server */
|
||||
@Parcelize
|
||||
@TypeLabel("local")
|
||||
data object PublicLocal : Timeline
|
||||
|
||||
// TODO: LOCAL_REMOTE
|
||||
|
||||
@Parcelize
|
||||
@TypeLabel("hashtag")
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Hashtags(val tags: List<String>) : Timeline
|
||||
|
||||
@Parcelize
|
||||
@TypeLabel("direct")
|
||||
data object Conversations : Timeline
|
||||
|
||||
/** Any timeline showing statuses from a single user */
|
||||
@Parcelize
|
||||
@NestedSealed
|
||||
sealed interface User : Timeline {
|
||||
val id: String
|
||||
|
||||
/** Timeline showing just a user's statuses (no replies) */
|
||||
@Parcelize
|
||||
@TypeLabel("userPosts")
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Posts(override val id: String) : User
|
||||
|
||||
/** Timeline showing a user's pinned statuses */
|
||||
@Parcelize
|
||||
@TypeLabel("userPinned")
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Pinned(override val id: String) : User
|
||||
|
||||
/** Timeline showing a user's top-level statuses and replies they have made */
|
||||
@Parcelize
|
||||
@TypeLabel("userReplies")
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Replies(override val id: String) : User
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
@TypeLabel("favourites")
|
||||
data object Favourites : Timeline
|
||||
|
||||
@Parcelize
|
||||
@TypeLabel("bookmarks")
|
||||
data object Bookmarks : Timeline
|
||||
|
||||
@Parcelize
|
||||
@TypeLabel("list")
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class UserList(
|
||||
@Json(name = "listId")
|
||||
val listId: String,
|
||||
@Json(name = "title")
|
||||
val title: String,
|
||||
) : Timeline
|
||||
|
||||
@Parcelize
|
||||
@TypeLabel("trending_tags")
|
||||
data object TrendingHashtags : Timeline
|
||||
|
||||
@Parcelize
|
||||
@TypeLabel("trending_links")
|
||||
data object TrendingLinks : Timeline
|
||||
|
||||
@Parcelize
|
||||
@TypeLabel("trending_statuses")
|
||||
data object TrendingStatuses : Timeline
|
||||
|
||||
// TODO: DRAFTS
|
||||
|
||||
// TODO: SCHEDULED
|
||||
}
|
|
@ -31,6 +31,7 @@ android {
|
|||
|
||||
dependencies {
|
||||
implementation(projects.core.database) // For DraftAttachment, used in ComposeOptions
|
||||
implementation(projects.core.model)
|
||||
implementation(projects.core.network) // For Attachment, used in AttachmentViewData
|
||||
|
||||
implementation(libs.androidx.core.ktx) // IntentCompat
|
||||
|
|
|
@ -22,6 +22,7 @@ import android.content.Intent
|
|||
import android.os.Parcelable
|
||||
import androidx.core.content.IntentCompat
|
||||
import app.pachli.core.database.model.DraftAttachment
|
||||
import app.pachli.core.model.Timeline
|
||||
import app.pachli.core.navigation.LoginActivityIntent.LoginMode
|
||||
import app.pachli.core.navigation.StatusListActivityIntent.Companion.bookmarks
|
||||
import app.pachli.core.navigation.StatusListActivityIntent.Companion.favourites
|
||||
|
@ -32,7 +33,6 @@ import app.pachli.core.network.model.Filter
|
|||
import app.pachli.core.network.model.NewPoll
|
||||
import app.pachli.core.network.model.Notification
|
||||
import app.pachli.core.network.model.Status
|
||||
import app.pachli.core.network.model.TimelineKind
|
||||
import com.gaelmarhic.quadrant.QuadrantConstants
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
|
@ -402,7 +402,7 @@ class StatusListActivityIntent private constructor(context: Context) : Intent()
|
|||
* @param context
|
||||
*/
|
||||
fun bookmarks(context: Context) = StatusListActivityIntent(context).apply {
|
||||
putExtra(EXTRA_KIND, TimelineKind.Bookmarks)
|
||||
putExtra(EXTRA_KIND, Timeline.Bookmarks)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -411,7 +411,7 @@ class StatusListActivityIntent private constructor(context: Context) : Intent()
|
|||
* @param context
|
||||
*/
|
||||
fun favourites(context: Context) = StatusListActivityIntent(context).apply {
|
||||
putExtra(EXTRA_KIND, TimelineKind.Favourites)
|
||||
putExtra(EXTRA_KIND, Timeline.Favourites)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -421,7 +421,7 @@ class StatusListActivityIntent private constructor(context: Context) : Intent()
|
|||
* @param hashtag The hashtag to show, without the leading "`#`"
|
||||
*/
|
||||
fun hashtag(context: Context, hashtag: String) = StatusListActivityIntent(context).apply {
|
||||
putExtra(EXTRA_KIND, TimelineKind.Tag(listOf(hashtag)))
|
||||
putExtra(EXTRA_KIND, Timeline.Hashtags(listOf(hashtag)))
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -432,11 +432,11 @@ class StatusListActivityIntent private constructor(context: Context) : Intent()
|
|||
* @param title The title to display
|
||||
*/
|
||||
fun list(context: Context, listId: String, title: String) = StatusListActivityIntent(context).apply {
|
||||
putExtra(EXTRA_KIND, TimelineKind.UserList(listId, title))
|
||||
putExtra(EXTRA_KIND, Timeline.UserList(listId, title))
|
||||
}
|
||||
|
||||
/** @return The [TimelineKind] to show */
|
||||
fun getKind(intent: Intent) = IntentCompat.getParcelableExtra(intent, EXTRA_KIND, TimelineKind::class.java)!!
|
||||
fun getKind(intent: Intent) = IntentCompat.getParcelableExtra(intent, EXTRA_KIND, Timeline::class.java)!!
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ android {
|
|||
|
||||
dependencies {
|
||||
implementation(projects.core.common)
|
||||
implementation(projects.core.model)
|
||||
implementation(projects.core.preferences)
|
||||
|
||||
implementation(libs.moshi)
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
package app.pachli.core.network.model
|
||||
|
||||
import app.pachli.core.model.Timeline
|
||||
import app.pachli.core.network.json.Default
|
||||
import app.pachli.core.network.json.HasDefault
|
||||
import com.squareup.moshi.Json
|
||||
|
@ -51,18 +52,28 @@ enum class FilterContext {
|
|||
/** Filter applies when viewing a profile */
|
||||
@Json(name = "account")
|
||||
ACCOUNT,
|
||||
|
||||
;
|
||||
|
||||
companion object {
|
||||
fun from(kind: TimelineKind): FilterContext = when (kind) {
|
||||
is TimelineKind.Home, is TimelineKind.UserList -> HOME
|
||||
is TimelineKind.PublicFederated,
|
||||
is TimelineKind.PublicLocal,
|
||||
is TimelineKind.Tag,
|
||||
is TimelineKind.Favourites,
|
||||
/**
|
||||
* @return The filter context for [timeline], or null if filters are not applied
|
||||
* to this timeline.
|
||||
*/
|
||||
fun from(timeline: Timeline): FilterContext? = when (timeline) {
|
||||
is Timeline.Home, is Timeline.UserList -> HOME
|
||||
is Timeline.User -> ACCOUNT
|
||||
Timeline.Notifications -> NOTIFICATIONS
|
||||
Timeline.Bookmarks,
|
||||
Timeline.Favourites,
|
||||
Timeline.PublicFederated,
|
||||
Timeline.PublicLocal,
|
||||
is Timeline.Hashtags,
|
||||
Timeline.TrendingStatuses,
|
||||
Timeline.TrendingHashtags,
|
||||
Timeline.TrendingLinks,
|
||||
-> PUBLIC
|
||||
is TimelineKind.User -> ACCOUNT
|
||||
else -> PUBLIC
|
||||
Timeline.Conversations -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,67 +0,0 @@
|
|||
/*
|
||||
* Copyright 2023 Pachli Association
|
||||
*
|
||||
* This file is a part of Pachli.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Pachli 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 Pachli; if not,
|
||||
* see <http://www.gnu.org/licenses>.
|
||||
*/
|
||||
|
||||
package app.pachli.core.network.model
|
||||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
/** A timeline's type. Hold's data necessary to display that timeline. */
|
||||
@Parcelize
|
||||
sealed interface TimelineKind : Parcelable {
|
||||
@Parcelize
|
||||
data object Home : TimelineKind
|
||||
|
||||
@Parcelize
|
||||
data object PublicFederated : TimelineKind
|
||||
|
||||
@Parcelize
|
||||
data object PublicLocal : TimelineKind
|
||||
|
||||
@Parcelize
|
||||
data class Tag(val tags: List<String>) : TimelineKind
|
||||
|
||||
/** Any timeline showing statuses from a single user */
|
||||
@Parcelize
|
||||
sealed interface User : TimelineKind {
|
||||
val id: String
|
||||
|
||||
/** Timeline showing just the user's statuses (no replies) */
|
||||
@Parcelize
|
||||
data class Posts(override val id: String) : User
|
||||
|
||||
/** Timeline showing the user's pinned statuses */
|
||||
@Parcelize
|
||||
data class Pinned(override val id: String) : User
|
||||
|
||||
/** Timeline showing the user's top-level statuses and replies they have made */
|
||||
@Parcelize
|
||||
data class Replies(override val id: String) : User
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
data object Favourites : TimelineKind
|
||||
|
||||
@Parcelize
|
||||
data object Bookmarks : TimelineKind
|
||||
|
||||
@Parcelize
|
||||
data class UserList(val id: String, val title: String) : TimelineKind
|
||||
|
||||
@Parcelize
|
||||
data object TrendingStatuses : TimelineKind
|
||||
}
|
|
@ -57,6 +57,7 @@ include(":core:common")
|
|||
include(":core:data")
|
||||
include(":core:database")
|
||||
include(":core:designsystem")
|
||||
include(":core:model")
|
||||
include(":core:preferences")
|
||||
include(":core:navigation")
|
||||
include(":core:network")
|
||||
|
|
Loading…
Reference in New Issue