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