Do less work while navigating Logins views

Fetching a set of logins from the store is quite expensive. This commit
avoids doing that while navigating back and forth between the list and
detail views:

- retain processes logins state when navigating into detail view
- use the `get` storage api to obtain specific login, instead of
  `list().filter {...}`
- avoid re-sorting retained logins when navigating back into the list
  view
upstream-sync
Grisha Kruglov 3 years ago committed by mergify[bot]
parent 56b4491e81
commit 0b5b1a738a

@ -53,6 +53,7 @@ class LoginsFragmentStore(initialState: LoginsListState) :
sealed class LoginsAction : Action {
data class FilterLogins(val newText: String?) : LoginsAction()
data class UpdateLoginsList(val list: List<SavedLogin>) : LoginsAction()
object LoginsListUpToDate : LoginsAction()
data class UpdateCurrentLogin(val item: SavedLogin) : LoginsAction()
data class SortLogins(val sortingStrategy: SortingStrategy) : LoginsAction()
data class DuplicateLogin(val dupe: SavedLogin?) : LoginsAction()
@ -97,6 +98,9 @@ private fun savedLoginsStateReducer(
action: LoginsAction
): LoginsListState {
return when (action) {
is LoginsAction.LoginsListUpToDate -> {
state.copy(isLoading = false)
}
is LoginsAction.UpdateLoginsList -> {
state.copy(
isLoading = false,
@ -125,9 +129,7 @@ private fun savedLoginsStateReducer(
}
is LoginsAction.LoginSelected -> {
state.copy(
isLoading = true,
loginList = emptyList(),
filteredItems = emptyList()
isLoading = true
)
}
is LoginsAction.DuplicateLogin -> {

@ -203,35 +203,31 @@ open class SavedLoginsStorageController(
loginsFragmentStore.dispatch(LoginsAction.DuplicateLogin(dupe))
}
fun fetchLoginDetails(loginId: String) {
var deferredLogin: Deferred<List<Login>>? = null
val fetchLoginJob = lifecycleScope.launch(ioDispatcher) {
deferredLogin = async {
passwordsStorage.list()
}
val fetchedLoginList = deferredLogin?.await()
fetchedLoginList?.let {
withContext(Dispatchers.Main) {
val login = fetchedLoginList.filter {
it.guid == loginId
}.first()
loginsFragmentStore.dispatch(
LoginsAction.UpdateCurrentLogin(
login.mapToSavedLogin()
)
fun fetchLoginDetails(loginId: String) = lifecycleScope.launch(ioDispatcher) {
val fetchedLogin = passwordsStorage.get(loginId)
withContext(Dispatchers.Main) {
if (fetchedLogin != null) {
loginsFragmentStore.dispatch(
LoginsAction.UpdateCurrentLogin(
fetchedLogin.mapToSavedLogin()
)
}
}
}
fetchLoginJob.invokeOnCompletion {
if (it is CancellationException) {
deferredLogin?.cancel()
)
} else {
navController.popBackStack()
}
}
}
fun handleLoadAndMapLogins() {
// Don't touch the store if we already have the logins loaded.
// This has a slight downside of possibly being out of date with the storage if, say, Sync
// ran in the meantime, but that's fairly unlikely and the speedy UI is worth it.
if (loginsFragmentStore.state.loginList.isNotEmpty()) {
lifecycleScope.launch(Dispatchers.Main) {
loginsFragmentStore.dispatch(LoginsAction.LoginsListUpToDate)
}
return
}
var deferredLogins: Deferred<List<Login>>? = null
val fetchLoginsJob = lifecycleScope.launch(ioDispatcher) {
deferredLogins = async {

@ -167,7 +167,7 @@ class LoginsFragmentStoreTest {
store.dispatch(LoginsAction.LoginSelected(mockk())).joinBlocking()
assertTrue(store.state.isLoading)
assertTrue(store.state.loginList.isEmpty())
assertTrue(store.state.filteredItems.isEmpty())
assertTrue(store.state.loginList.isNotEmpty())
assertTrue(store.state.filteredItems.isNotEmpty())
}
}

@ -92,14 +92,14 @@ class SavedLoginsStorageControllerTest {
httpRealm = "httpRealm",
formActionOrigin = ""
)
coEvery { passwordsStorage.list() } returns listOf(login)
coEvery { passwordsStorage.get("id") } returns login
controller.fetchLoginDetails(login.guid)
val expectedLogin = login.mapToSavedLogin()
coVerify {
passwordsStorage.list()
passwordsStorage.get("id")
loginsFragmentStore.dispatch(
LoginsAction.UpdateCurrentLogin(
expectedLogin

Loading…
Cancel
Save