fb79f4f138ac

fix: Match browser card add-entry fields (v2.3.14)

time
2026-06-26 01:19 UTC · 4 months ago
machine
koh2
parent
610b588ac078
8 files app/build.gradle+2 −2app/src/main/java/com/peachpasswords/ui/vault/CardDetailFields.kt+5 −2app/src/main/java/com/peachpasswords/ui/vault/CardEditFields.kt+9 −0app/src/main/java/com/peachpasswords/ui/vault/CardFieldUtils.kt+6 −5app/src/main/java/com/peachpasswords/ui/vault/EntryDetailViewModel.kt+2 −1app/src/main/java/com/peachpasswords/ui/vault/EntryEditScreen.kt+2 −0app/src/main/java/com/peachpasswords/ui/vault/EntryEditViewModel.kt+5 −0app/src/test/java/com/peachpasswords/ui/vault/EntryEditScreenTest.kt+10 −0
app/build.gradle+2 −2
@@ -16,8 +16,8 @@1616         applicationId "com.peachpasswords"1717         minSdk 261818         targetSdk 3519-        versionCode 23130020-        versionName "2.3.13"19+        versionCode 23140020+        versionName "2.3.14"2121 2222         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"2323         vectorDrawables {

view file

app/src/main/java/com/peachpasswords/ui/vault/CardDetailFields.kt+5 −2
@@ -55,7 +55,7 @@5555     onRevealCvc: () -> Unit,5656     onMaskCvc: () -> Unit,5757     onCopy: (field: String, value: String?) -> Unit,58-    onSaveCard: (cardNumber: String, cardholderName: String, expiry: String, cvc: String) -> Unit,58+    onSaveCard: (cardNickname: String, cardNumber: String, cardholderName: String, expiry: String, cvc: String) -> Unit,5959     copyButton: @Composable (copied: Boolean, onCopy: () -> Unit) -> Unit,6060 ) {6161     val accent = LocalPeachAccent.current@@ -65,6 +65,7 @@6565 6666     if (editing) {6767         var cardNumber by remember(entry.id, entry.modifiedAt) { mutableStateOf(CardFieldUtils.formatPan(entry.cardNumber.orEmpty(), network)) }68+        var cardNickname by remember(entry.id, entry.modifiedAt) { mutableStateOf(entry.cardNickname.orEmpty()) }6869         var cardholder by remember(entry.id, entry.modifiedAt) { mutableStateOf(entry.cardholderName.orEmpty()) }6970         var expiry by remember(entry.id, entry.modifiedAt) {7071             mutableStateOf(CardFieldUtils.displayExpiry(entry.expMonth, entry.expYear))@@ -73,12 +74,14 @@7374         var showCvc by remember { mutableStateOf(false) }7475         val draftNetwork = CardFieldUtils.detectNetwork(cardNumber)7576         CardEditFields(77+            cardNickname = cardNickname,7678             cardNumber = cardNumber,7779             cardholderName = cardholder,7880             expiry = expiry,7981             cvc = cvc,8082             showCvc = showCvc,8183             network = draftNetwork,84+            onCardNickname = { cardNickname = it },8285             onCardNumber = { cardNumber = it },8386             onCardholder = { cardholder = it },8487             onExpiry = { expiry = it },@@ -89,7 +92,7 @@8992             Text(9093                 "✓ save", color = accent, style = PeachType.labelMono,9194                 modifier = Modifier.clip(RoundedCornerShape(6.dp)).clickable {92-                    onSaveCard(cardNumber, cardholder, expiry, cvc)95+                    onSaveCard(cardNickname, cardNumber, cardholder, expiry, cvc)9396                     editing = false9497                 }.padding(vertical = 4.dp),9598             )

view file

app/src/main/java/com/peachpasswords/ui/vault/CardEditFields.kt+9 −0
@@ -29,18 +29,27 @@2929  */3030 @Composable3131 fun CardEditFields(32+    cardNickname: String,3233     cardNumber: String,3334     cardholderName: String,3435     expiry: String,3536     cvc: String,3637     showCvc: Boolean,3738     network: String,39+    onCardNickname: (String) -> Unit,3840     onCardNumber: (String) -> Unit,3941     onCardholder: (String) -> Unit,4042     onExpiry: (String) -> Unit,4143     onCvc: (String) -> Unit,4244     onToggleShowCvc: () -> Unit,4345 ) {46+    PeachField(47+        label = "nickname",48+        value = cardNickname,49+        onChange = onCardNickname,50+        mono = false,51+    )52+4453     // CARD NUMBER — auto-formatted, network label beside the label.4554     PeachField(4655         label = "card number",

view file

app/src/main/java/com/peachpasswords/ui/vault/CardFieldUtils.kt+6 −5
@@ -14,11 +14,12 @@1414     fun detectNetwork(rawNumber: String): String {1515         val digits = rawNumber.filter { it.isDigit() }1616         if (digits.isEmpty()) return "unknown"17-        return when (digits.first()) {18-            '4' -> "visa"19-            '5' -> "mastercard"20-            '3' -> "amex"21-            '6' -> "discover"17+        return when {18+            digits.startsWith("4") -> "visa"19+            digits.length >= 2 && digits.take(2).toIntOrNull() in 51..55 -> "mastercard"20+            digits.length >= 2 && digits.take(2).toIntOrNull() in 22..27 -> "mastercard"21+            digits.startsWith("34") || digits.startsWith("37") -> "amex"22+            digits.startsWith("6011") || digits.startsWith("65") -> "discover"2223             else -> "unknown"2324         }2425     }

view file

app/src/main/java/com/peachpasswords/ui/vault/EntryDetailViewModel.kt+2 −1
@@ -188,7 +188,7 @@188188         }189189     }190190 191-    fun updateCard(cardNumber: String, cardholderName: String, expiry: String, cvc: String) {191+    fun updateCard(cardNickname: String, cardNumber: String, cardholderName: String, expiry: String, cvc: String) {192192         val current = entry.value ?: return193193         val digits = cardNumber.filter { it.isDigit() }194194         val expDigits = expiry.filter { it.isDigit() }@@ -197,6 +197,7 @@197197             runCatching {198198                 vault.updateEntry(199199                     current.copy(200+                        cardNickname = cardNickname.ifBlank { null },200201                         cardNumber = digits.ifBlank { null },201202                         cardholderName = cardholderName.ifBlank { null },202203                         expMonth = expDigits.take(2).ifBlank { null },

view file

app/src/main/java/com/peachpasswords/ui/vault/EntryEditScreen.kt+2 −0
@@ -160,12 +160,14 @@160160                 }161161                 if (f.type == "card") {162162                     CardEditFields(163+                        cardNickname = f.cardNickname,163164                         cardNumber = f.cardNumber,164165                         cardholderName = f.cardholderName,165166                         expiry = f.expiry,166167                         cvc = f.cvc,167168                         showCvc = f.showCvc,168169                         network = f.cardNetwork,170+                        onCardNickname = vm::setCardNickname,169171                         onCardNumber = vm::setCardNumber,170172                         onCardholder = vm::setCardholderName,171173                         onExpiry = vm::setExpiry,

view file

app/src/main/java/com/peachpasswords/ui/vault/EntryEditViewModel.kt+5 −0
@@ -28,6 +28,7 @@2828     val additionalUrls: List<String> = emptyList(),2929     val totp: String = "",3030     val notes: String = "",31+    val cardNickname: String = "",3132     val cardNumber: String = "",3233     val cardholderName: String = "",3334     val expiry: String = "",        // "MM/YY"; split into expMonth/expYear on save@@ -73,6 +74,7 @@7374                 additionalUrls = it.additionalUrls.orEmpty(),7475                 totp = it.totp.orEmpty(),7576                 notes = it.notes.orEmpty(),77+                cardNickname = it.cardNickname.orEmpty(),7678                 cardNumber = CardFieldUtils.formatPan(it.cardNumber.orEmpty()),7779                 cardholderName = it.cardholderName.orEmpty(),7880                 expiry = CardFieldUtils.displayExpiry(it.expMonth, it.expYear),@@ -128,6 +130,7 @@128130     fun setNotes(v: String) = _form.update { it.copy(notes = v) }129131     fun revealTotp() = _form.update { it.copy(showTotp = true) }130132     fun revealNotes() = _form.update { it.copy(showNotes = true) }133+    fun setCardNickname(v: String) = _form.update { it.copy(cardNickname = v, error = null) }131134     fun setCardNumber(v: String) = _form.update {132135         val formatted = CardFieldUtils.formatPan(v)133136         it.copy(cardNumber = formatted, cardNetwork = CardFieldUtils.detectNetwork(formatted), error = null)@@ -213,6 +216,7 @@213216                                 .ifEmpty { null },214217                             totp = f.totp.ifBlank { null },215218                             notes = f.notes.ifBlank { null },219+                            cardNickname = f.cardNickname.ifBlank { null },216220                             cardNumber = f.cardNumber.filter { it.isDigit() }.ifBlank { null },217221                             cardholderName = f.cardholderName.ifBlank { null },218222                             expMonth = f.expiry.filter { it.isDigit() }.take(2).ifBlank { null },@@ -236,6 +240,7 @@236240                                 .ifEmpty { null },237241                             totp = f.totp.ifBlank { null },238242                             notes = f.notes.ifBlank { null },243+                            cardNickname = f.cardNickname.ifBlank { null },239244                             cardNumber = f.cardNumber.filter { it.isDigit() }.ifBlank { null },240245                             cardholderName = f.cardholderName.ifBlank { null },241246                             expMonth = f.expiry.filter { it.isDigit() }.take(2).ifBlank { null },

view file

app/src/test/java/com/peachpasswords/ui/vault/EntryEditScreenTest.kt+10 −0
@@ -100,6 +100,16 @@100100         assertEquals("3782 822463 10005", CardFieldUtils.formatPan("378282246310005"))101101     }102102 103+    @Test fun card_network_detection_matches_browser_add_view_badges() {104+        assertEquals("visa", CardFieldUtils.detectNetwork("4242424242424242"))105+        assertEquals("mastercard", CardFieldUtils.detectNetwork("5555555555554444"))106+        assertEquals("mastercard", CardFieldUtils.detectNetwork("2221000000000009"))107+        assertEquals("amex", CardFieldUtils.detectNetwork("378282246310005"))108+        assertEquals("discover", CardFieldUtils.detectNetwork("6011111111111117"))109+        assertEquals("discover", CardFieldUtils.detectNetwork("6500000000000002"))110+        assertEquals("unknown", CardFieldUtils.detectNetwork("6000000000000000"))111+    }112+103113     @Test fun new_card_expiry_auto_formats_mm_yy() {104114         assertEquals("12", CardFieldUtils.formatExpiry("12"))105115         assertEquals("12/28", CardFieldUtils.formatExpiry("1228"))

view file