[PM-9927] Sort order update (#3625)

This commit is contained in:
Dave Severns 2024-07-24 16:17:58 -04:00 committed by GitHub
parent da3d834a91
commit 8ffd14c2fb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 133 additions and 16 deletions

View file

@ -0,0 +1,38 @@
package com.x8bit.bitwarden.data.platform.util
import java.util.Locale
/**
* Compare two characters, where a special character is considered with higher precedence over
* letters and numbers. If both characters are a letter or a digit use the default
* [Char.compareTo].
*/
private fun compareCharsSpecialCharsWithPrecedence(c1: Char, c2: Char): Int {
return when {
c1.isLetterOrDigit() && !c2.isLetterOrDigit() -> 1
!c1.isLetterOrDigit() && c2.isLetterOrDigit() -> -1
else -> c1.compareTo(c2)
}
}
/**
* String [Comparator] where the characters are compared giving precedence to
* special characters.
*/
object CompareStringSpecialCharWithPrecedence : Comparator<String> {
override fun compare(str1: String, str2: String): Int {
val uppercaseStr1 = str1.uppercase(Locale.getDefault())
val uppercaseStr2 = str2.uppercase(Locale.getDefault())
val minLength = minOf(uppercaseStr1.length, uppercaseStr2.length)
for (i in 0 until minLength) {
val char1 = uppercaseStr1[i]
val char2 = uppercaseStr2[i]
val compareResult = compareCharsSpecialCharsWithPrecedence(char1, char2)
if (compareResult != 0) {
return compareResult
}
}
// If all compared chars are the same give precedence to the shorter String.
return uppercaseStr1.length - uppercaseStr2.length
}
}

View file

@ -1085,7 +1085,7 @@ class VaultRepositoryImpl(
sendList = it.toEncryptedSdkSendList(),
)
.fold(
onSuccess = { sends -> DataState.Loaded(sends) },
onSuccess = { sends -> DataState.Loaded(sends.sortAlphabetically()) },
onFailure = { throwable -> DataState.Error(throwable) },
)
}

View file

@ -18,6 +18,7 @@ import com.bitwarden.vault.PasswordHistory
import com.bitwarden.vault.SecureNote
import com.bitwarden.vault.SecureNoteType
import com.bitwarden.vault.UriMatchType
import com.x8bit.bitwarden.data.platform.util.CompareStringSpecialCharWithPrecedence
import com.x8bit.bitwarden.data.vault.datasource.network.model.AttachmentJsonRequest
import com.x8bit.bitwarden.data.vault.datasource.network.model.CipherJsonRequest
import com.x8bit.bitwarden.data.vault.datasource.network.model.CipherRepromptTypeJson
@ -29,7 +30,6 @@ import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
import com.x8bit.bitwarden.data.vault.datasource.network.model.UriMatchTypeJson
import java.time.ZoneOffset
import java.time.ZonedDateTime
import java.util.Locale
/**
* Converts a Bitwarden SDK [Cipher] object to a corresponding
@ -553,8 +553,14 @@ fun FieldTypeJson.toSdkFieldType(): FieldType =
}
/**
* Sorts the data in alphabetical order by name.
* Sorts the data in alphabetical order by name. Using lexicographical sorting but giving
* precedence to special characters over letters and digits.
*/
@JvmName("toAlphabeticallySortedCipherList")
fun List<CipherView>.sortAlphabetically(): List<CipherView> =
this.sortedBy { it.name.uppercase(Locale.getDefault()) }
fun List<CipherView>.sortAlphabetically(): List<CipherView> {
return this.sortedWith(
comparator = { cipher1, cipher2 ->
CompareStringSpecialCharWithPrecedence.compare(cipher1.name, cipher2.name)
},
)
}

View file

@ -2,8 +2,8 @@ package com.x8bit.bitwarden.data.vault.repository.util
import com.bitwarden.vault.Collection
import com.bitwarden.vault.CollectionView
import com.x8bit.bitwarden.data.platform.util.CompareStringSpecialCharWithPrecedence
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
import java.util.Locale
/**
* Converts a [SyncResponseJson.Collection] object to a corresponding Bitwarden SDK [Collection]
@ -30,5 +30,10 @@ fun List<SyncResponseJson.Collection>.toEncryptedSdkCollectionList(): List<Colle
* Sorts the data in alphabetical order by name.
*/
@JvmName("toAlphabeticallySortedCollectionList")
fun List<CollectionView>.sortAlphabetically(): List<CollectionView> =
this.sortedBy { it.name.uppercase(Locale.getDefault()) }
fun List<CollectionView>.sortAlphabetically(): List<CollectionView> {
return this.sortedWith(
comparator = { collection1, collection2 ->
CompareStringSpecialCharWithPrecedence.compare(collection1.name, collection2.name)
},
)
}

View file

@ -2,9 +2,9 @@ package com.x8bit.bitwarden.data.vault.repository.util
import com.bitwarden.vault.Folder
import com.bitwarden.vault.FolderView
import com.x8bit.bitwarden.data.platform.util.CompareStringSpecialCharWithPrecedence
import com.x8bit.bitwarden.data.vault.datasource.network.model.FolderJsonRequest
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
import java.util.Locale
/**
* Converts a list of [SyncResponseJson.Folder] objects to a list of corresponding
@ -24,16 +24,21 @@ fun SyncResponseJson.Folder.toEncryptedSdkFolder(): Folder =
revisionDate = revisionDate.toInstant(),
)
/**
* Sorts the data in alphabetical order by name.
*/
@JvmName("toAlphabeticallySortedFolderList")
fun List<FolderView>.sortAlphabetically(): List<FolderView> =
this.sortedBy { it.name.uppercase(Locale.getDefault()) }
/**
* Converts a Bitwarden SDK [Folder] objects to a corresponding
* [SyncResponseJson.Folder] object.
*/
fun Folder.toEncryptedNetworkFolder(): FolderJsonRequest =
FolderJsonRequest(name = name)
/**
* Sorts the data in alphabetical order by name.
*/
@JvmName("toAlphabeticallySortedFolderList")
fun List<FolderView>.sortAlphabetically(): List<FolderView> {
return this.sortedWith(
comparator = { folder1, folder2 ->
CompareStringSpecialCharWithPrecedence.compare(folder1.name, folder2.name)
},
)
}

View file

@ -4,6 +4,8 @@ import com.bitwarden.send.Send
import com.bitwarden.send.SendFile
import com.bitwarden.send.SendText
import com.bitwarden.send.SendType
import com.bitwarden.send.SendView
import com.x8bit.bitwarden.data.platform.util.CompareStringSpecialCharWithPrecedence
import com.x8bit.bitwarden.data.vault.datasource.network.model.SendJsonRequest
import com.x8bit.bitwarden.data.vault.datasource.network.model.SendTypeJson
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
@ -123,3 +125,15 @@ private fun SendTypeJson.toSdkSendType(): SendType =
SendTypeJson.TEXT -> SendType.TEXT
SendTypeJson.FILE -> SendType.FILE
}
/**
* Sorts the data in alphabetical order by name.
*/
@JvmName("toAlphabeticallySortedSendList")
fun List<SendView>.sortAlphabetically(): List<SendView> {
return this.sortedWith(
comparator = { send1, send2 ->
CompareStringSpecialCharWithPrecedence.compare(send1.name, send2.name)
},
)
}

View file

@ -304,12 +304,23 @@ class VaultSdkCipherExtensionsTest {
createMockCipherView(1).copy(name = "c"),
createMockCipherView(1).copy(name = "B"),
createMockCipherView(1).copy(name = "z"),
createMockCipherView(1).copy(name = "8"),
createMockCipherView(1).copy(name = "7"),
createMockCipherView(1).copy(name = "_"),
createMockCipherView(1).copy(name = "A"),
createMockCipherView(1).copy(name = "D"),
createMockCipherView(1).copy(name = "AbA"),
createMockCipherView(1).copy(name = "aAb"),
)
val expected = listOf(
createMockCipherView(1).copy(name = "_"),
createMockCipherView(1).copy(name = "7"),
createMockCipherView(1).copy(name = "8"),
createMockCipherView(1).copy(name = "A"),
createMockCipherView(1).copy(name = "aAb"),
createMockCipherView(1).copy(name = "AbA"),
createMockCipherView(1).copy(name = "B"),
createMockCipherView(1).copy(name = "c"),
createMockCipherView(1).copy(name = "D"),

View file

@ -65,11 +65,15 @@ class VaultSdkCollectionExtensionsTest {
createMockCollectionView(1).copy(name = "c"),
createMockCollectionView(1).copy(name = "B"),
createMockCollectionView(1).copy(name = "z"),
createMockCollectionView(1).copy(name = "4"),
createMockCollectionView(1).copy(name = "A"),
createMockCollectionView(1).copy(name = "#"),
createMockCollectionView(1).copy(name = "D"),
)
val expected = listOf(
createMockCollectionView(1).copy(name = "#"),
createMockCollectionView(1).copy(name = "4"),
createMockCollectionView(1).copy(name = "A"),
createMockCollectionView(1).copy(name = "B"),
createMockCollectionView(1).copy(name = "c"),

View file

@ -41,12 +41,16 @@ class VaultSdkFolderExtensionsTest {
val list = listOf(
createMockFolderView(1).copy(name = "c"),
createMockFolderView(1).copy(name = "D"),
createMockFolderView(1).copy(name = "_"),
createMockFolderView(1).copy(name = "4"),
createMockFolderView(1).copy(name = "B"),
createMockFolderView(1).copy(name = "A"),
createMockFolderView(1).copy(name = "z"),
)
val expected = listOf(
createMockFolderView(1).copy(name = "_"),
createMockFolderView(1).copy(name = "4"),
createMockFolderView(1).copy(name = "A"),
createMockFolderView(1).copy(name = "B"),
createMockFolderView(1).copy(name = "c"),

View file

@ -2,6 +2,7 @@ package com.x8bit.bitwarden.data.vault.repository.util
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockSend
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockSendJsonRequest
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSendView
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkSend
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
@ -50,4 +51,33 @@ class VaultSdkSendExtensionsTest {
sdkSends,
)
}
@Suppress("MaxLineLength")
@Test
fun `toSortAlphabetically should sort SendView by name`() {
val list = listOf(
createMockSendView(1).copy(name = "c"),
createMockSendView(1).copy(name = "B"),
createMockSendView(1).copy(name = "z"),
createMockSendView(1).copy(name = "4"),
createMockSendView(1).copy(name = "A"),
createMockSendView(1).copy(name = "#"),
createMockSendView(1).copy(name = "D"),
)
val expected = listOf(
createMockSendView(1).copy(name = "#"),
createMockSendView(1).copy(name = "4"),
createMockSendView(1).copy(name = "A"),
createMockSendView(1).copy(name = "B"),
createMockSendView(1).copy(name = "c"),
createMockSendView(1).copy(name = "D"),
createMockSendView(1).copy(name = "z"),
)
assertEquals(
expected,
list.sortAlphabetically(),
)
}
}