From 7da4bc090ba73120245dcd6c6f8a35f8c6eab39f Mon Sep 17 00:00:00 2001 From: Nik Clayton Date: Tue, 12 Mar 2024 18:45:09 +0100 Subject: [PATCH] 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 --- app/src/main/java/app/pachli/MainActivity.kt | 25 ++++++-- .../data/repository/NetworkListsRepository.kt | 58 ++++++++++++++++++- 2 files changed, 78 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/app/pachli/MainActivity.kt b/app/src/main/java/app/pachli/MainActivity.kt index e02b84b90..7d9ed5ad7 100644 --- a/app/src/main/java/app/pachli/MainActivity.kt +++ b/app/src/main/java/app/pachli/MainActivity.kt @@ -363,10 +363,18 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider { } lifecycleScope.launch { - listsRepository.lists.collect { - it.onSuccess { refreshMainDrawerItems(addSearchButton = hideTopToolbar) } + listsRepository.lists.collect { result -> + 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) .setAction(R.string.action_retry) { listsRepository.refresh() } .show() @@ -875,11 +883,20 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider { // Selected tab is either // - Notification tab (if appropriate) // - 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 val position = if (selectNotificationTab) { tabs.indexOfFirst { it.tabData is TabData.Notifications } } 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 binding.viewPager.setCurrentItem(position, false) diff --git a/core/data/src/main/kotlin/app/pachli/core/data/repository/NetworkListsRepository.kt b/core/data/src/main/kotlin/app/pachli/core/data/repository/NetworkListsRepository.kt index 1e28a0a62..5b6873b25 100644 --- a/core/data/src/main/kotlin/app/pachli/core/data/repository/NetworkListsRepository.kt +++ b/core/data/src/main/kotlin/app/pachli/core/data/repository/NetworkListsRepository.kt @@ -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.Retrieve 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.TimelineAccount import app.pachli.core.network.retrofit.MastodonApi @@ -60,12 +61,67 @@ class NetworkListsRepository @Inject constructor( _lists.value = Ok(Lists.Loading) _lists.value = api.getLists() .mapBoth( - { Ok(Lists.Loaded(it.body)) }, + { + updateTabPreferences(it.body.associateBy { it.id }) + Ok(Lists.Loaded(it.body)) + }, { 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) { + 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 = binding { externalScope.async { api.createList(title, exclusive).mapError { Create(it) }.bind().run {