From 21138471622324adf7cd0c807c0a45347d91293e Mon Sep 17 00:00:00 2001 From: Artem Chepurnoy Date: Mon, 15 Jan 2024 10:16:48 +0200 Subject: [PATCH] Always sort ignoring word case --- .../common/util/StringComparatorIgnoreCase.kt | 26 +++++++++++++++++++ .../attachments/AttachmentsStateProducer.kt | 3 ++- .../folder/FolderConfirmationStateProducer.kt | 3 ++- .../OrganizationConfirmationStateProducer.kt | 5 ++-- .../emailrelay/EmailRelayListStateProducer.kt | 3 ++- .../settings/accounts/AccountListViewModel.kt | 3 ++- .../home/vault/add/AddStateProducer.kt | 3 ++- .../vault/folders/FoldersStateProducer.kt | 3 ++- .../home/vault/screen/VaultListFilter.kt | 5 ++-- .../vault/screen/VaultListStateProducer.kt | 3 ++- .../vault/screen/VaultViewStateProducer.kt | 3 ++- .../home/vault/util/changePasswordAction.kt | 5 ++-- 12 files changed, 51 insertions(+), 14 deletions(-) create mode 100644 common/src/commonMain/kotlin/com/artemchep/keyguard/common/util/StringComparatorIgnoreCase.kt diff --git a/common/src/commonMain/kotlin/com/artemchep/keyguard/common/util/StringComparatorIgnoreCase.kt b/common/src/commonMain/kotlin/com/artemchep/keyguard/common/util/StringComparatorIgnoreCase.kt new file mode 100644 index 00000000..9eafa668 --- /dev/null +++ b/common/src/commonMain/kotlin/com/artemchep/keyguard/common/util/StringComparatorIgnoreCase.kt @@ -0,0 +1,26 @@ +package com.artemchep.keyguard.common.util + +class StringComparatorIgnoreCase( + private val descending: Boolean = false, + private val getter: (T) -> String?, +) : Comparator { + override fun compare(aHolder: T, bHolder: T): Int { + val a = getter(aHolder) + val b = getter(bHolder) + + var r = internalCompare(a, b) + // Reverse the comparison result, + // if needed. + if (descending) { + r *= -1 + } + return r + } + + private fun internalCompare(a: String?, b: String?): Int { + if (a === b) return 0 + if (a == null) return -1 + if (b == null) return 1 + return a.compareTo(b, ignoreCase = true) + } +} diff --git a/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/attachments/AttachmentsStateProducer.kt b/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/attachments/AttachmentsStateProducer.kt index f85b930a..102b0d07 100644 --- a/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/attachments/AttachmentsStateProducer.kt +++ b/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/attachments/AttachmentsStateProducer.kt @@ -30,6 +30,7 @@ import com.artemchep.keyguard.common.usecase.GetFolders import com.artemchep.keyguard.common.usecase.GetOrganizations import com.artemchep.keyguard.common.usecase.GetProfiles import com.artemchep.keyguard.common.usecase.RemoveAttachment +import com.artemchep.keyguard.common.util.StringComparatorIgnoreCase import com.artemchep.keyguard.common.util.flow.foldAsList import com.artemchep.keyguard.feature.attachments.model.AttachmentItem import com.artemchep.keyguard.feature.attachments.util.createAttachmentItem @@ -239,7 +240,7 @@ fun produceAttachmentsScreenState( .map { list -> list .filterNotNull() - .sortedBy { it.item.name } + .sortedWith(StringComparatorIgnoreCase { it.item.name }) } } .shareInScreenScope() diff --git a/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/confirmation/folder/FolderConfirmationStateProducer.kt b/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/confirmation/folder/FolderConfirmationStateProducer.kt index 376e31ce..9a9c144c 100644 --- a/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/confirmation/folder/FolderConfirmationStateProducer.kt +++ b/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/confirmation/folder/FolderConfirmationStateProducer.kt @@ -9,6 +9,7 @@ import arrow.core.partially1 import com.artemchep.keyguard.common.model.Loadable import com.artemchep.keyguard.common.usecase.GetFolders import com.artemchep.keyguard.common.usecase.WindowCoroutineScope +import com.artemchep.keyguard.common.util.StringComparatorIgnoreCase import com.artemchep.keyguard.feature.auth.common.TextFieldModel2 import com.artemchep.keyguard.feature.auth.common.Validated import com.artemchep.keyguard.feature.auth.common.util.validatedTitle @@ -130,7 +131,7 @@ fun folderConfirmationState( .map { folders -> val items = folders .filter { it.accountId == args.accountId.id } - .sortedBy { it.name } + .sortedWith(StringComparatorIgnoreCase { it.name }) .map { folder -> val folderEnabled = folder.id !in args.blacklistedFolderIds val folderInfo = FolderVariant.FolderInfo.Id( diff --git a/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/confirmation/organization/OrganizationConfirmationStateProducer.kt b/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/confirmation/organization/OrganizationConfirmationStateProducer.kt index c48407c1..06baacc7 100644 --- a/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/confirmation/organization/OrganizationConfirmationStateProducer.kt +++ b/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/confirmation/organization/OrganizationConfirmationStateProducer.kt @@ -21,6 +21,7 @@ import com.artemchep.keyguard.common.usecase.GetFolders import com.artemchep.keyguard.common.usecase.GetOrganizations import com.artemchep.keyguard.common.usecase.GetProfiles import com.artemchep.keyguard.common.usecase.WindowCoroutineScope +import com.artemchep.keyguard.common.util.StringComparatorIgnoreCase import com.artemchep.keyguard.common.util.contains import com.artemchep.keyguard.feature.auth.common.TextFieldModel2 import com.artemchep.keyguard.feature.auth.common.Validated @@ -230,7 +231,7 @@ fun organizationConfirmationState( accountsFlow, ) { organizations, accounts -> organizations - .sortedBy { it.name } + .sortedWith(StringComparatorIgnoreCase { it.name }) .map { organization -> val enabled = organization.id !in args.blacklistedOrganizationIds OrganizationVariant( @@ -274,7 +275,7 @@ fun organizationConfirmationState( accountsFlow, ) { folders, accounts -> folders - .sortedBy { it.name } + .sortedWith(StringComparatorIgnoreCase { it.name }) .map { folder -> val enabled = folder.id !in args.blacklistedFolderIds FolderVariant( diff --git a/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/generator/emailrelay/EmailRelayListStateProducer.kt b/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/generator/emailrelay/EmailRelayListStateProducer.kt index 1a321c35..7860a298 100644 --- a/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/generator/emailrelay/EmailRelayListStateProducer.kt +++ b/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/generator/emailrelay/EmailRelayListStateProducer.kt @@ -15,6 +15,7 @@ import com.artemchep.keyguard.common.service.relays.api.EmailRelay import com.artemchep.keyguard.common.usecase.AddEmailRelay import com.artemchep.keyguard.common.usecase.GetEmailRelays import com.artemchep.keyguard.common.usecase.RemoveEmailRelayById +import com.artemchep.keyguard.common.util.StringComparatorIgnoreCase import com.artemchep.keyguard.common.util.flow.persistingStateIn import com.artemchep.keyguard.feature.attachments.SelectableItemState import com.artemchep.keyguard.feature.attachments.SelectableItemStateRaw @@ -200,7 +201,7 @@ fun produceEmailRelayListState( .partially1(emailRelay), ) } - .sortedBy { it.title } + .sortedWith(StringComparatorIgnoreCase { it.title }) .toImmutableList() val itemsRawFlow = getEmailRelays() diff --git a/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/settings/accounts/AccountListViewModel.kt b/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/settings/accounts/AccountListViewModel.kt index f87b2837..1211dde9 100644 --- a/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/settings/accounts/AccountListViewModel.kt +++ b/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/settings/accounts/AccountListViewModel.kt @@ -19,6 +19,7 @@ import com.artemchep.keyguard.common.usecase.QueueSyncById import com.artemchep.keyguard.common.usecase.RemoveAccountById import com.artemchep.keyguard.common.usecase.SupervisorRead import com.artemchep.keyguard.common.usecase.WindowCoroutineScope +import com.artemchep.keyguard.common.util.StringComparatorIgnoreCase import com.artemchep.keyguard.common.util.flow.foldAsList import com.artemchep.keyguard.feature.auth.AccountViewRoute import com.artemchep.keyguard.feature.confirmation.createConfirmationDialogIntent @@ -287,7 +288,7 @@ fun accountListScreenState( .foldAsList() .map { items -> items - .sortedBy { it.text } + .sortedWith(StringComparatorIgnoreCase { it.text }) } combine( diff --git a/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/vault/add/AddStateProducer.kt b/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/vault/add/AddStateProducer.kt index 72fb434d..2ec43d58 100644 --- a/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/vault/add/AddStateProducer.kt +++ b/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/vault/add/AddStateProducer.kt @@ -88,6 +88,7 @@ import com.artemchep.keyguard.common.usecase.GetOrganizations import com.artemchep.keyguard.common.usecase.GetProfiles import com.artemchep.keyguard.common.usecase.GetTotpCode import com.artemchep.keyguard.common.usecase.ShowMessage +import com.artemchep.keyguard.common.util.StringComparatorIgnoreCase import com.artemchep.keyguard.common.util.flow.EventFlow import com.artemchep.keyguard.common.util.flow.combineToList import com.artemchep.keyguard.common.util.flow.foldAsList @@ -1965,7 +1966,7 @@ private suspend fun RememberStateFlowScope.produceOwnershipFlow( } val selectedCollections = collections - .sortedBy { it.name } + .sortedWith(StringComparatorIgnoreCase { it.name }) .filter { it.id in collectionIds } val el = AddState.SaveToElement( readOnly = ro, diff --git a/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/vault/folders/FoldersStateProducer.kt b/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/vault/folders/FoldersStateProducer.kt index b384e528..1b02e3ab 100644 --- a/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/vault/folders/FoldersStateProducer.kt +++ b/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/vault/folders/FoldersStateProducer.kt @@ -19,6 +19,7 @@ import com.artemchep.keyguard.common.usecase.GetFolders import com.artemchep.keyguard.common.usecase.MergeFolderById import com.artemchep.keyguard.common.usecase.RemoveFolderById import com.artemchep.keyguard.common.usecase.RenameFolderById +import com.artemchep.keyguard.common.util.StringComparatorIgnoreCase import com.artemchep.keyguard.core.store.bitwarden.exists import com.artemchep.keyguard.feature.confirmation.ConfirmationResult import com.artemchep.keyguard.feature.confirmation.ConfirmationRoute @@ -189,7 +190,7 @@ fun foldersScreenState( translate(Res.strings.folder_action_change_name_title) }, items = folders - .sortedBy { it.name } + .sortedWith(StringComparatorIgnoreCase { it.name }) .map { folder -> ConfirmationRoute.Args.Item.StringItem( key = folder.id, diff --git a/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/vault/screen/VaultListFilter.kt b/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/vault/screen/VaultListFilter.kt index 1b1ef18a..97d14689 100644 --- a/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/vault/screen/VaultListFilter.kt +++ b/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/vault/screen/VaultListFilter.kt @@ -27,6 +27,7 @@ import com.artemchep.keyguard.common.model.DSecret import com.artemchep.keyguard.common.model.iconImageVector import com.artemchep.keyguard.common.model.titleH import com.artemchep.keyguard.common.usecase.GetFolderTree +import com.artemchep.keyguard.common.util.StringComparatorIgnoreCase import com.artemchep.keyguard.feature.home.vault.component.rememberSecretAccentColor import com.artemchep.keyguard.feature.home.vault.model.FilterItem import com.artemchep.keyguard.feature.home.vault.search.filter.FilterHolder @@ -560,7 +561,7 @@ suspend fun < title = name, ) } - .sortedBy { it.title } + .sortedWith(StringComparatorIgnoreCase { it.title }) .toList() + createCollectionFilterAction( collectionIds = setOfNull, @@ -602,7 +603,7 @@ suspend fun < title = name, ) } - .sortedBy { it.title } + .sortedWith(StringComparatorIgnoreCase { it.title }) .toList() + createOrganizationFilterAction( organizationIds = setOfNull, diff --git a/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/vault/screen/VaultListStateProducer.kt b/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/vault/screen/VaultListStateProducer.kt index 7b8bba5e..d4e2d894 100644 --- a/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/vault/screen/VaultListStateProducer.kt +++ b/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/vault/screen/VaultListStateProducer.kt @@ -60,6 +60,7 @@ import com.artemchep.keyguard.common.usecase.PasskeyTargetCheck import com.artemchep.keyguard.common.usecase.QueueSyncAll import com.artemchep.keyguard.common.usecase.RenameFolderById import com.artemchep.keyguard.common.usecase.SupervisorRead +import com.artemchep.keyguard.common.util.StringComparatorIgnoreCase import com.artemchep.keyguard.common.util.flow.EventFlow import com.artemchep.keyguard.common.util.flow.persistingStateIn import com.artemchep.keyguard.feature.attachments.AttachmentsRoute @@ -284,7 +285,7 @@ fun vaultListScreenState( icon = icon(Icons.Outlined.Edit), title = "Change folder name", items = folders - .sortedBy { it.name } + .sortedWith(StringComparatorIgnoreCase { it.name }) .map { folder -> ConfirmationRoute.Args.Item.StringItem( key = folder.id, diff --git a/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/vault/screen/VaultViewStateProducer.kt b/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/vault/screen/VaultViewStateProducer.kt index eb8bc899..2fa1a159 100644 --- a/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/vault/screen/VaultViewStateProducer.kt +++ b/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/vault/screen/VaultViewStateProducer.kt @@ -133,6 +133,7 @@ import com.artemchep.keyguard.common.usecase.RestoreCipherById import com.artemchep.keyguard.common.usecase.RetryCipher import com.artemchep.keyguard.common.usecase.TrashCipherById import com.artemchep.keyguard.common.usecase.WindowCoroutineScope +import com.artemchep.keyguard.common.util.StringComparatorIgnoreCase import com.artemchep.keyguard.common.util.flow.persistingStateIn import com.artemchep.keyguard.core.store.bitwarden.canRetry import com.artemchep.keyguard.core.store.bitwarden.expired @@ -421,7 +422,7 @@ fun vaultViewScreenState( collections .firstOrNull { it.id == collectionId && it.accountId == accountId } } - .sortedByDescending { it.name } + .sortedWith(StringComparatorIgnoreCase(descending = true) { it.name }) } .distinctUntilChanged() val organizationFlow = getOrganizations() diff --git a/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/vault/util/changePasswordAction.kt b/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/vault/util/changePasswordAction.kt index afe12c8e..33099b6c 100644 --- a/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/vault/util/changePasswordAction.kt +++ b/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/vault/util/changePasswordAction.kt @@ -36,6 +36,7 @@ import com.artemchep.keyguard.common.usecase.RePromptCipherById import com.artemchep.keyguard.common.usecase.RemoveCipherById import com.artemchep.keyguard.common.usecase.RestoreCipherById import com.artemchep.keyguard.common.usecase.TrashCipherById +import com.artemchep.keyguard.common.util.StringComparatorIgnoreCase import com.artemchep.keyguard.feature.confirmation.ConfirmationResult import com.artemchep.keyguard.feature.confirmation.ConfirmationRoute import com.artemchep.keyguard.feature.confirmation.folder.FolderConfirmationResult @@ -371,7 +372,7 @@ fun RememberStateFlowScope.cipherChangeNameAction( before?.invoke() val items = ciphers - .sortedBy { it.name } + .sortedWith(StringComparatorIgnoreCase { it.name }) .map { cipher -> ConfirmationRoute.Args.Item.StringItem( key = cipher.id, @@ -444,7 +445,7 @@ fun RememberStateFlowScope.cipherChangePasswordAction( val items = ciphers .filter { it.type == DSecret.Type.Login } - .sortedBy { it.name } + .sortedWith(StringComparatorIgnoreCase { it.name }) .map { cipher -> ConfirmationRoute.Args.Item.StringItem( key = cipher.id,