The export carries the choices somebody made, not only their messages
Following the SPEC 16.3 rule the same sweep just wrote down. The export held threads, groups, calls, stories, the roster and the group keys, and left behind the story audience, muted authors, the story archive, drafts, polls in flight and the 8.9c presence tokens. Restoring on a new phone lost every one of them silently, which looks exactly like a person who never set them. They travel under the names the iPhone already writes, so a backup moves between the two phones rather than only between two Android ones. The presence tokens travel as the two per-contact maps alone, never the delivery record around them, which holds this account's own key and 16.1 excludes. Import merges into gaps rather than overwriting: a draft typed on THIS phone is the newer thought, and a presence token this device already holds is the live one the relay knows the digest of, so an older copy from a file would make this account invisible to that contact until they issued another. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
fd3a52b35c
commit
f9463d8ba6
@@ -140,6 +140,27 @@ object BackupPayload {
|
||||
root.put("storyReactions", it)
|
||||
}
|
||||
|
||||
// §16.3. State somebody set on purpose, under the iPhone's names.
|
||||
internal.optJSONObject("storyAudience")?.let { root.put("storyAudience", it) }
|
||||
internal.optJSONObject("storyMutes")?.optJSONArray("ids")?.takeIf { it.length() > 0 }?.let {
|
||||
root.put("mutedStoryAuthors", it)
|
||||
}
|
||||
internal.optJSONObject("storyArchive")?.optJSONArray("items")?.let { items ->
|
||||
val out = JSONArray()
|
||||
for (i in 0 until items.length()) {
|
||||
items.optJSONObject(i)?.let { out.put(storyToShared(it)) }
|
||||
}
|
||||
if (out.length() > 0) root.put("storyArchive", out)
|
||||
}
|
||||
internal.optJSONObject("drafts")?.takeIf { it.length() > 0 }?.let { root.put("drafts", it) }
|
||||
internal.optJSONObject("polls")?.takeIf { it.length() > 0 }?.let { root.put("polls", it) }
|
||||
internal.optJSONObject("presenceTokensIssued")?.takeIf { it.length() > 0 }?.let {
|
||||
root.put("presenceTokensIssued", it)
|
||||
}
|
||||
internal.optJSONObject("presenceTokensHeld")?.takeIf { it.length() > 0 }?.let {
|
||||
root.put("presenceTokensHeld", it)
|
||||
}
|
||||
|
||||
return root
|
||||
}
|
||||
|
||||
@@ -310,6 +331,27 @@ object BackupPayload {
|
||||
out.put("storyReactions", it)
|
||||
}
|
||||
|
||||
shared.optJSONObject("storyAudience")?.let { out.put("storyAudience", it) }
|
||||
shared.optJSONArray("mutedStoryAuthors")?.takeIf { it.length() > 0 }?.let {
|
||||
out.put("storyMutes", JSONObject().put("ids", it))
|
||||
}
|
||||
shared.optJSONArray("storyArchive")?.takeIf { it.length() > 0 }?.let { items ->
|
||||
val restored = JSONArray()
|
||||
for (i in 0 until items.length()) {
|
||||
val s = items.optJSONObject(i) ?: continue
|
||||
restored.put(storyFromShared(s, s.optLong("author", 0L)))
|
||||
}
|
||||
if (restored.length() > 0) out.put("storyArchive", JSONObject().put("items", restored))
|
||||
}
|
||||
shared.optJSONObject("drafts")?.takeIf { it.length() > 0 }?.let { out.put("drafts", it) }
|
||||
shared.optJSONObject("polls")?.takeIf { it.length() > 0 }?.let { out.put("polls", it) }
|
||||
shared.optJSONObject("presenceTokensIssued")?.takeIf { it.length() > 0 }?.let {
|
||||
out.put("presenceTokensIssued", it)
|
||||
}
|
||||
shared.optJSONObject("presenceTokensHeld")?.takeIf { it.length() > 0 }?.let {
|
||||
out.put("presenceTokensHeld", it)
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user