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

@ -203,35 +203,31 @@ open class SavedLoginsStorageController(
loginsFragmentStore.dispatch(LoginsAction.DuplicateLogin(dupe)) loginsFragmentStore.dispatch(LoginsAction.DuplicateLogin(dupe))
} }
fun fetchLoginDetails(loginId: String) { fun fetchLoginDetails(loginId: String) = lifecycleScope.launch(ioDispatcher) {
var deferredLogin: Deferred<List<Login>>? = null val fetchedLogin = passwordsStorage.get(loginId)
val fetchLoginJob = lifecycleScope.launch(ioDispatcher) {
deferredLogin = async {
passwordsStorage.list()
}
val fetchedLoginList = deferredLogin?.await()
fetchedLoginList?.let {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
val login = fetchedLoginList.filter { if (fetchedLogin != null) {
it.guid == loginId
}.first()
loginsFragmentStore.dispatch( loginsFragmentStore.dispatch(
LoginsAction.UpdateCurrentLogin( LoginsAction.UpdateCurrentLogin(
login.mapToSavedLogin() fetchedLogin.mapToSavedLogin()
) )
) )
} } else {
} navController.popBackStack()
}
fetchLoginJob.invokeOnCompletion {
if (it is CancellationException) {
deferredLogin?.cancel()
} }
} }
} }
fun handleLoadAndMapLogins() { 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 var deferredLogins: Deferred<List<Login>>? = null
val fetchLoginsJob = lifecycleScope.launch(ioDispatcher) { val fetchLoginsJob = lifecycleScope.launch(ioDispatcher) {
deferredLogins = async { deferredLogins = async {

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

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

Loading…
Cancel
Save