A contact who reinstalled could never be messaged again
🔴 **The chat header said "keys unchanged since you met" while every send was refused with "identity keys changed".** Found by driving two real apps: the iOS side relaunched with -FNMSGResetState, which is a genuinely new device with new keys, and Android correctly refused to send to it. Correct — and then there was no way out. `Trust.state()` could return UNKNOWN, VERIFIED or PINNED and nothing else. CHANGED was computed inside `record()` while a send was in flight and thrown away, because `Vault.Trust` had nowhere to remember it. So: - the header kept reporting the OLD pinned keys as unchanged, - the "Accept new keys" button, which only renders for CHANGED, never appeared, - `acceptIdentityChange()` was unreachable from the UI, - and the contact stayed unmessageable for ever, with the app insisting nothing was wrong. That is precisely what a tester does on day one: reinstall, and discover their friend can no longer reach them. `Vault.Trust` now carries `changed`, `record()` persists the detection without adopting the offered keys and drops the verified tick (whatever was compared in person was compared against the old key), `state()` reports it, and accepting clears it. Keys that match again also clear it, so a transient wrong answer does not strand somebody behind a banner for ever. Verified live on the emulator: the banner appears with its full text, Accept new keys is reachable, and the header returns to "keys unchanged" afterwards. Four tests in TrustTest; three of them fail against the old `state()`. --- 🔴 **An expired session told the user their passphrase was wrong.** Hit immediately afterwards: a send failed with "That handle or passphrase isn't right", ten hours after a successful sign-in, because AccountsClient mapped EVERY 401 and 403 to BadCredentials. The natural reading is that the password stopped working or was changed by someone else, and the natural response is to change a password that was never wrong. Only `sessions` and `accounts` actually present a passphrase, so only those can report one as wrong. Everything else now raises SessionExpired, which the app states as what it is. Two tests cover both directions. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
3c66d3e9bd
commit
cda64d8c2e
@@ -17,6 +17,8 @@
|
||||
|
||||
<application
|
||||
android:allowBackup="false"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.FlamenetMessenger">
|
||||
|
||||
@@ -1165,6 +1165,9 @@ class MessengerViewModel(app: Application) : AndroidViewModel(app) {
|
||||
*/
|
||||
private fun describe(failure: Throwable): String = when (failure) {
|
||||
is AccountsClient.Failure.BadCredentials -> "That handle or passphrase isn't right."
|
||||
is AccountsClient.Failure.SessionExpired ->
|
||||
"Your session expired. Sign out and back in to keep going."
|
||||
|
||||
is AccountsClient.Failure.HandleTaken -> "That handle is already taken."
|
||||
is AccountsClient.Failure.RateLimited -> "Too many attempts. Wait a few minutes."
|
||||
is AccountsClient.Failure.Transport -> failure.message.orEmpty()
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
The messenger's mark: a speech bubble with a flame knocked out of it.
|
||||
|
||||
⚠️ Deliberately NOT the bare flame that Flamenet Files uses. Two apps from
|
||||
the same shop with the same icon is a support problem the first time a tester
|
||||
has both installed.
|
||||
|
||||
The content sits inside the adaptive-icon safe zone: a 100-unit drawing
|
||||
scaled to 66 and centred in the 108 viewport, so nothing important is lost
|
||||
to a circular or squircle mask.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<group android:scaleX="0.66" android:scaleY="0.66"
|
||||
android:translateX="21" android:translateY="21">
|
||||
<path
|
||||
android:fillColor="#FF5A1F"
|
||||
android:pathData="M22,20 H78 A14,14 0 0 1 92,34 V62 A14,14 0 0 1 78,76 H46 L30,91 V76 H22 A14,14 0 0 1 8,62 V34 A14,14 0 0 1 22,20 Z" />
|
||||
<path
|
||||
android:fillColor="#121A2E"
|
||||
android:pathData="M50,30 C56,40 62,45 62,54 C62,63.5 57,69.5 50,69.5 C43,69.5 38,63.5 38,54 C38,47 43,43 46,36 C47,43 50,44 50,30 Z" />
|
||||
</group>
|
||||
</vector>
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
⚠️ Themed icons need a SILHOUETTE, not the colour artwork. Pointing
|
||||
<monochrome> at the full-colour foreground makes the launcher tint artwork
|
||||
that already has its own colours, which is why the icon came back wearing a
|
||||
pale ring. One shape, one colour, and the launcher recolours it.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<group android:scaleX="0.66" android:scaleY="0.66"
|
||||
android:translateX="21" android:translateY="21">
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M22,20 H78 A14,14 0 0 1 92,34 V62 A14,14 0 0 1 78,76 H46 L30,91 V76 H22 A14,14 0 0 1 8,62 V34 A14,14 0 0 1 22,20 Z" />
|
||||
</group>
|
||||
</vector>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<monochrome android:drawable="@drawable/ic_launcher_monochrome" />
|
||||
</adaptive-icon>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<monochrome android:drawable="@drawable/ic_launcher_monochrome" />
|
||||
</adaptive-icon>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- The site's panel navy (CSS var: panel), so the icon shares the app palette. -->
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#121A2E</color>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user