ac25ca315a1b
refactor: homogenize entry edit options (v2.14.52)
- time
- 2026-08-15 00:12 UTC · 2 months ago
- machine
- koh2
- parent
- 5cf4b795ab7d
↓ peach-android-ac25ca31.tar.gzatlas at this save
files changed5 files
VERSION+1 −1app/src/main/java/com/peachpasswords/ui/vault/AdditionalUrlsEditor.kt+7 −3app/src/main/java/com/peachpasswords/ui/vault/EntryEditScreen.kt+49 −43app/src/main/java/com/peachpasswords/ui/vault/PasswordHistorySection.kt+2 −1app/src/test/java/com/peachpasswords/ui/vault/EntryEditScreenTest.kt+2 −3app/src/main/java/com/peachpasswords/ui/vault/AdditionalUrlsEditor.kt+7 −3
@@ -27,6 +27,7 @@2727 import com.peachpasswords.ui.theme.LocalPeachAccent2828 import com.peachpasswords.ui.theme.peachColors2929 import com.peachpasswords.ui.theme.PeachType30+import com.peachpasswords.ui.theme.PeachSpacing3031 3132 /** E3 — hard cap on additional URLs, matching the spec. */3233 const val MAX_ADDITIONAL_URLS = 5@@ -46,9 +47,12 @@4647 onChange: (Int, String) -> Unit,4748 onAdd: () -> Unit,4849 onRemove: (Int) -> Unit,50+ embedded: Boolean = false,4951 ) {5052 val accent = LocalPeachAccent.current51- Column(Modifier.fillMaxWidth()) {53+ Column(54+ Modifier.fillMaxWidth().padding(horizontal = if (embedded) PeachSpacing.lg else 0.dp),55+ ) {5256 urls.forEachIndexed { index, value ->5357 // Label numbering starts at 2 so it reads as a continuation of the5458 // primary URL field (which is conceptually "URL 1").@@ -105,7 +109,7 @@105109 .clickable(onClick = onAdd)106110 .padding(vertical = 4.dp),107111 )108- Spacer(Modifier.height(14.dp))112+ Spacer(Modifier.height(if (embedded) 8.dp else 14.dp))109113 } else {110114 Text(111115 "Max $MAX_ADDITIONAL_URLS additional URLs",@@ -113,7 +117,7 @@113117 fontFamily = PeachType.Mono,114118 fontSize = 9.sp,115119 )116- Spacer(Modifier.height(14.dp))120+ Spacer(Modifier.height(if (embedded) 8.dp else 14.dp))117121 }118122 }119123 }app/src/main/java/com/peachpasswords/ui/vault/EntryEditScreen.kt+49 −43
@@ -109,7 +109,7 @@109109 Modifier.fillMaxSize().verticalScroll(rememberScrollState()),110110 ) {111111 PeachSegmentedControl(112- segments = entryEditTypeSegments(vm.isNew),112+ segments = entryEditTypeSegments(),113113 selectedKey = f.type,114114 onSelected = vm::setType,115115 )@@ -119,6 +119,13 @@119119 if (f.type == "login") {120120 PeachFieldStack {121121 Field("url", f.url, vm::setUrl, joined = true, divider = true)122+ AdditionalUrlsEditor(123+ urls = f.additionalUrls,124+ onChange = vm::setAdditionalUrl,125+ onAdd = vm::addAdditionalUrl,126+ onRemove = vm::removeAdditionalUrl,127+ embedded = true,128+ )122129 IdentitySuggestField(123130 "username", f.username, usernameSuggestions, vm::setUsername,124131 joined = true,@@ -137,34 +144,42 @@137144 onToggleShow = vm::toggleShowPassword,138145 onToggleGenerator = vm::toggleGenerator,139146 joined = true,147+ divider = true,140148 )149+ GeneratorDrawer(150+ open = f.generatorOpen,151+ length = f.genLength,152+ upper = f.genUpper, lower = f.genLower, numbers = f.genNumbers, symbols = f.genSymbols,153+ password = f.password,154+ copied = f.genCopied,155+ onLength = vm::setGenLength,156+ onUpper = vm::setGenUpper, onLower = vm::setGenLower,157+ onNumbers = vm::setGenNumbers, onSymbols = vm::setGenSymbols,158+ onGenerate = vm::generate,159+ onCopy = vm::copyGenerated,160+ onUse = vm::useGeneratedPassword,161+ embedded = true,162+ )163+ PasswordHistorySection(164+ history = f.history,165+ expanded = f.showHistory,166+ onToggle = vm::toggleHistory,167+ onRestore = vm::restorePassword,168+ onCopy = vm::copyHistoryPassword,169+ embedded = true,170+ )141171 }142- GeneratorDrawer(143- open = f.generatorOpen,144- length = f.genLength,145- upper = f.genUpper, lower = f.genLower, numbers = f.genNumbers, symbols = f.genSymbols,146- password = f.password,147- copied = f.genCopied,148- onLength = vm::setGenLength,149- onUpper = vm::setGenUpper, onLower = vm::setGenLower,150- onNumbers = vm::setGenNumbers, onSymbols = vm::setGenSymbols,151- onGenerate = vm::generate,152- onCopy = vm::copyGenerated,153- onUse = vm::useGeneratedPassword,172+ OptionalLoginAddLinks(173+ showTotpLink = shouldShowAddTotpLink(f.type, f.showTotp, f.totp),174+ showNotesLink = shouldShowLoginNotesLink(175+ isNew = vm.isNew,176+ type = f.type,177+ revealed = f.showNotes,178+ notes = f.notes,179+ ),180+ onAddTotp = vm::revealTotp,181+ onAddNotes = vm::revealNotes,154182 )155- AdditionalUrlsEditor(156- urls = f.additionalUrls,157- onChange = vm::setAdditionalUrl,158- onAdd = vm::addAdditionalUrl,159- onRemove = vm::removeAdditionalUrl,160- )161- PasswordHistorySection(162- history = f.history,163- expanded = f.showHistory,164- onToggle = vm::toggleHistory,165- onRestore = vm::restorePassword,166- onCopy = vm::copyHistoryPassword,167- )168183 if (shouldShowLoginTotp(f.type, f.showTotp, f.totp)) {169184 Field("2FA secret (otpauth:// or base32)", f.totp, vm::setTotp)170185 }@@ -190,17 +205,6 @@190205 Field("notes", f.notes, vm::setNotes, singleLine = false)191206 }192207 if (f.type == "login") {193- OptionalLoginAddLinks(194- showTotpLink = shouldShowAddTotpLink(f.type, f.showTotp, f.totp),195- showNotesLink = shouldShowLoginNotesLink(196- isNew = vm.isNew,197- type = f.type,198- revealed = f.showNotes,199- notes = f.notes,200- ),201- onAddTotp = vm::revealTotp,202- onAddNotes = vm::revealNotes,203- )204208 CanaryEditSection(205209 entryId = id ?: "new",206210 modifiedAt = f.canary?.lastPinged ?: 0L,@@ -274,11 +278,10 @@274278 else -> "new entry"275279 }276280 277-internal fun entryEditTypeSegments(isNew: Boolean): List<PeachSegment> = buildList {278- add(PeachSegment("login", "login"))279- if (!isNew) add(PeachSegment("note", "note"))280- add(PeachSegment("card", "card"))281-}281+internal fun entryEditTypeSegments(): List<PeachSegment> = listOf(282+ PeachSegment("login", "login"),283+ PeachSegment("card", "card"),284+)282285 283286 internal fun entryEditPrimaryActionLabel(isNew: Boolean, saving: Boolean): String = when {284287 saving -> "saving..."@@ -371,6 +374,7 @@371374 onToggleShow: () -> Unit,372375 onToggleGenerator: () -> Unit,373376 joined: Boolean = false,377+ divider: Boolean = false,374378 ) {375379 Field(376380 label = "password",@@ -378,6 +382,7 @@378382 onChange = onChange,379383 transformation = if (show) VisualTransformation.None else PasswordVisualTransformation(),380384 joined = joined,385+ divider = divider,381386 trailing = {382387 Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {383388 PasswordAction(if (show) "hide" else "show", onToggleShow)@@ -418,6 +423,7 @@418423 onGenerate: () -> Unit,419424 onCopy: () -> Unit,420425 onUse: () -> Unit,426+ embedded: Boolean = false,421427 ) {422428 val height by animateDpAsState(423429 targetValue = if (open) 326.dp else 0.dp,@@ -441,7 +447,7 @@441447 onUse = onUse,442448 )443449 }444- if (open) Spacer(Modifier.height(14.dp))450+ if (open) Spacer(Modifier.height(if (embedded) 8.dp else 14.dp))445451 }446452 447453 /**app/src/main/java/com/peachpasswords/ui/vault/PasswordHistorySection.kt+2 −1
@@ -50,8 +50,9 @@5050 onToggle: () -> Unit,5151 onRestore: (String) -> Unit,5252 onCopy: (String) -> Unit,53+ embedded: Boolean = false,5354 ) {54- Column(Modifier.fillMaxWidth().padding(bottom = 14.dp)) {55+ Column(Modifier.fillMaxWidth().padding(bottom = if (embedded) 8.dp else 14.dp)) {5556 PasswordHistoryToggle(count = history.size, expanded = expanded, onToggle = onToggle)5657 if (expanded) {5758 Spacer(Modifier.height(8.dp))app/src/test/java/com/peachpasswords/ui/vault/EntryEditScreenTest.kt+2 −3
@@ -37,9 +37,8 @@3737 3838 // ── New Entry creates logins/cards; Notes mode creates notes ─────────────3939 40- @Test fun new_entry_type_picker_excludes_notes() {41- assertEquals(listOf("login", "card"), entryEditTypeSegments(isNew = true).map { it.key })42- assertEquals(listOf("login", "note", "card"), entryEditTypeSegments(isNew = false).map { it.key })40+ @Test fun entry_type_picker_excludes_notes_for_new_and_existing_entries() {41+ assertEquals(listOf("login", "card"), entryEditTypeSegments().map { it.key })4342 }4443 4544 // ── Ghost watermark fades in as the title is typed ────────────────────────