47e77d4ba508

fix: open populated phones directly at pairing (v2.7.13)

time
2026-07-28 21:22 UTC · 2 months ago
machine
koh2
agent
gpt-5
session
normal mesh pairing scan
parent
7c32509a6aa3
4 files app/build.gradle+2 −2app/src/main/java/com/peachpasswords/ui/pairing/AdoptVaultScreen.kt+1 −19app/src/main/java/com/peachpasswords/ui/pairing/AdoptVaultViewModel.kt+6 −17app/src/test/java/com/peachpasswords/ui/pairing/AdoptVaultSafetyTest.kt+8 −0
app/build.gradle+2 −2
@@ -16,8 +16,8 @@1616         applicationId "com.peachpasswords"1717         minSdk 261818         targetSdk 3519-        versionCode 24013920-        versionName "2.7.12"19+        versionCode 24014020+        versionName "2.7.13"2121 2222         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"2323         vectorDrawables {

view file

app/src/main/java/com/peachpasswords/ui/pairing/AdoptVaultScreen.kt+1 −19
@@ -73,7 +73,7 @@7373             verticalArrangement = Arrangement.Center,7474         ) {7575             Text(76-                if (vm.joining) "Re-pair a device" else "I already have Peach",76+                if (vm.joining) "Pair a device" else "I already have Peach",7777                 color = peachColors.T0,7878                 fontFamily = PeachType.Display,7979                 fontSize = 26.sp,@@ -96,24 +96,6 @@9696             }9797 9898             when (val current = state) {99-                AdoptVaultUiState.Disclosure -> {100-                    Body(101-                        "This device already has a vault. Scanning a QR from a second browser, " +102-                            "tablet, or phone is allowed as long as it holds the same vault " +103-                            "(same recovery words).",104-                    )105-                    Spacer(Modifier.height(PeachSpacing.md))106-                    Body(107-                        "You'll see the same short code on both screens. The codes must match " +108-                            "before anything is shared — that's what proves the other device is " +109-                            "really the one you pointed the camera at, not a relay or middleman.",110-                    )111-                    Spacer(Modifier.height(PeachSpacing.lg))112-                    PillButton("Continue", LocalPeachAccent.current, filled = true) {113-                        vm.acceptDisclosure()114-                    }115-                }116-11799                 AdoptVaultUiState.Password -> {118100                     Body(119101                         "Choose the password this phone will use. It never leaves this device; " +

view file

app/src/main/java/com/peachpasswords/ui/pairing/AdoptVaultViewModel.kt+6 −17
@@ -42,12 +42,6 @@4242 4343 sealed interface AdoptVaultUiState {4444     data object Password : AdoptVaultUiState45-    /**46-     * Existing-vault mode only. Pairing is allowed only when the incoming root47-     * key identifies this same vault; Peach refuses to combine populated48-     * vaults because separately imported copies can otherwise nearly double.49-     */50-    data object Disclosure : AdoptVaultUiState5145     data object Pair : AdoptVaultUiState5246     data object Connecting : AdoptVaultUiState5347     /**@@ -83,7 +77,7 @@8377     val joining: Boolean = vault.hasVault()8478 8579     private val _state = MutableStateFlow<AdoptVaultUiState>(86-        if (joining) AdoptVaultUiState.Disclosure else AdoptVaultUiState.Password,80+        initialAdoptVaultState(joining),8781     )8882     val state: StateFlow<AdoptVaultUiState> = _state.asStateFlow()8983 @@ -101,13 +95,6 @@10195         return true10296     }10397 104-    /** The user has read what joining does to this device's recovery words. */105-    fun acceptDisclosure() {106-        if (_state.value is AdoptVaultUiState.Disclosure) {107-            _state.value = AdoptVaultUiState.Pair108-        }109-    }110-11198     fun adopt(invitation: String) {11299         if (_state.value !is AdoptVaultUiState.Pair) return113100         val parsed = parsePairingInvitation(invitation)@@ -239,8 +226,7 @@239226         confirmation?.complete(false)240227         confirmation = null241228         clearPassword()242-        _state.value =243-            if (joining) AdoptVaultUiState.Disclosure else AdoptVaultUiState.Password229+        _state.value = initialAdoptVaultState(joining)244230     }245231 246232     private fun receiverDeps(): AdoptionReceiverDeps =@@ -365,6 +351,9 @@365351     }366352 }367353 354+internal fun initialAdoptVaultState(hasVault: Boolean): AdoptVaultUiState =355+    if (hasVault) AdoptVaultUiState.Pair else AdoptVaultUiState.Password356+368357 /**369358  * Pairing is not a merge tool. A populated destination may re-pair only when370359  * both sides already derive the same vault id. Different ids must stop before@@ -379,4 +368,4 @@379368         throw IllegalStateException("JOIN_POPULATED_VAULT_BLOCKED")380369     }381370     return localVaultId382-}\ No newline at end of file371+}

view file

app/src/test/java/com/peachpasswords/ui/pairing/AdoptVaultSafetyTest.kt+8 −0
@@ -5,6 +5,14 @@55 import org.junit.Test66 77 class AdoptVaultSafetyTest {8+    @Test fun populated_vault_starts_at_pairing_without_a_repair_disclosure() {9+        assertEquals(AdoptVaultUiState.Pair, initialAdoptVaultState(hasVault = true))10+    }11+12+    @Test fun empty_install_still_collects_a_local_password_first() {13+        assertEquals(AdoptVaultUiState.Password, initialAdoptVaultState(hasVault = false))14+    }15+816     @Test fun same_vault_identity_may_repair_without_mutation() {917         val id = "ab".repeat(32)1018 

view file