fix: Update tabs when lists are renamed or deleted

If the user has tabs containing one or more lists, and any of those
lists are renamed or deleted then the change should be reflected in the
tabs.

To do that:

`MainActivity`:

- Re-create tabs whenever lists are loaded and there's a list in a tab
- Compare lists-in-tabs by the ID of the list when restoring the user's
  tab, so that a list rename doesn't lose their position.

`NetworkListsRepository`:

- Update the user's tab preferences whenever lists are loaded, removing
  tabs that contain lists that have been deleted, and updating the
  list's title for lists that have been renamed.

Fixes #192
This commit is contained in:
Nik Clayton 2024-03-12 18:45:09 +01:00
parent a973f67ac8
commit 7da4bc090b
2 changed files with 78 additions and 5 deletions

View File

@ -363,10 +363,18 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider {
} }
lifecycleScope.launch { lifecycleScope.launch {
listsRepository.lists.collect { listsRepository.lists.collect { result ->
it.onSuccess { refreshMainDrawerItems(addSearchButton = hideTopToolbar) } result.onSuccess { lists ->
// Update the list of lists in the main drawer
refreshMainDrawerItems(addSearchButton = hideTopToolbar)
it.onFailure { // Any lists in tabs might have changed titles, update those
if (lists is Lists.Loaded && tabAdapter.tabs.any { it.tabData is TabData.UserList }) {
setupTabs(false)
}
}
result.onFailure {
Snackbar.make(binding.root, R.string.error_list_load, Snackbar.LENGTH_INDEFINITE) Snackbar.make(binding.root, R.string.error_list_load, Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.action_retry) { listsRepository.refresh() } .setAction(R.string.action_retry) { listsRepository.refresh() }
.show() .show()
@ -875,11 +883,20 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider {
// Selected tab is either // Selected tab is either
// - Notification tab (if appropriate) // - Notification tab (if appropriate)
// - The previously selected tab (if it hasn't been removed) // - The previously selected tab (if it hasn't been removed)
// - 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.tabData is TabData.Notifications }
} else { } else {
previousTab?.let { tabs.indexOfFirst { it == previousTab } } previousTab?.let {
tabs.indexOfFirst {
if (it.tabData is TabData.UserList && previousTab.tabData is TabData.UserList) {
it.tabData.listId == previousTab.tabData.listId
} else {
it == previousTab
}
}
}
}.takeIf { it != -1 } ?: 0 }.takeIf { it != -1 } ?: 0
binding.viewPager.setCurrentItem(position, false) binding.viewPager.setCurrentItem(position, false)

View File

@ -24,6 +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.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.retrofit.MastodonApi import app.pachli.core.network.retrofit.MastodonApi
@ -60,12 +61,67 @@ class NetworkListsRepository @Inject constructor(
_lists.value = Ok(Lists.Loading) _lists.value = Ok(Lists.Loading)
_lists.value = api.getLists() _lists.value = api.getLists()
.mapBoth( .mapBoth(
{ Ok(Lists.Loaded(it.body)) }, {
updateTabPreferences(it.body.associateBy { it.id })
Ok(Lists.Loaded(it.body))
},
{ Err(Retrieve(it)) }, { Err(Retrieve(it)) },
) )
} }
} }
/**
* Updates the user's tab preferences when lists are loaded.
*
* The user may have added one or more lists to tabs. If they have then:
*
* - A list-in-a-tab might have been deleted
* - A list-in-a-tab might have been renamed
*
* Handle both of those scenarios.
*
* @param lists Map of listId -> [MastoList]
*/
private fun updateTabPreferences(lists: Map<String, MastoList>) {
val account = accountManager.activeAccount ?: return
val oldTabPreferences = account.tabPreferences
var changed = false
val newTabPreferences = buildList {
for (oldPref in oldTabPreferences) {
if (oldPref !is TabData.UserList) {
add(oldPref)
continue
}
// List has been deleted? Don't add this pref,
// record there's been a change, and move on to the
// next one.
if (oldPref.listId !in lists) {
changed = true
continue
}
// Title changed? Update the title in the pref and
// add it.
if (oldPref.title != lists[oldPref.listId]?.title) {
changed = true
add(
oldPref.copy(
title = lists[oldPref.listId]?.title!!,
),
)
continue
}
add(oldPref)
}
}
if (changed) {
account.tabPreferences = newTabPreferences
accountManager.saveAccount(account)
}
}
override suspend fun createList(title: String, exclusive: Boolean): Result<MastoList, Create> = binding { override suspend fun createList(title: String, exclusive: Boolean): Result<MastoList, Create> = binding {
externalScope.async { externalScope.async {
api.createList(title, exclusive).mapError { Create(it) }.bind().run { api.createList(title, exclusive).mapError { Create(it) }.bind().run {