Group authors, avatars everywhere, QR scan/share, and iOS-parity Messages bar
- Session.Line carries `author` (record always persisted it; the read side never surfaced it) — group bubbles now wear the WRITER's name and picture instead of the group's initial for every message. - Story cards use the author's real picture (your own from myAvatar). - avatarShared marker stamps the peer's device set, not just the blob id: a bare marker outlived their reinstall and they saw initials for ever. Old markers mismatch once and self-heal with one resend. - QR: AddressCard embeds ?u=<handle> so a same-relay scan resolves through the ordinary username lookup, and the Home + Messages bars gain a scanner — WebRTC's own Camera2 capture rendered to a SurfaceViewRenderer with each frame's Y plane offered to zxing:core (the one new dependency; decode only, no second camera framework). parseScannedAddress mirrors iOS ContactCard.decode's refusals. - Messages screen matches iOS: bare title with scan + add-contact actions, New group beside the Groups heading (GroupsSection's own label, always drawn now, carries it), New chat beside CHATS with a contact picker, and the duplicated GROUPS label is gone. - Story composer gains a per-story audience: Everyone / Only… / All except… chips with a member checklist, seeded from the account setting, never written back to it. postStory takes the override. - AddContactForm accepts a prefill so a scan lands in the field — looked up, then added, never auto-added. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
91ab857107
commit
3b6bdb43a3
@@ -309,9 +309,20 @@ class Conversations(
|
||||
*/
|
||||
fun shareAvatarIfNeeded(toUserId: Long) {
|
||||
val ref = vault.myAvatar() ?: return
|
||||
if (vault.avatarSharedWith(toUserId) == ref.id) return
|
||||
// 🔴 The marker records WHICH DEVICES were told, not just which
|
||||
// picture. A bare ref.id marker outlived the peer's reinstall: their
|
||||
// new install held no avatar, this side said "already shared", and
|
||||
// they saw initials for ever — the same staleness class as the
|
||||
// delivery key. The directory endpoint is free (no prekey popped).
|
||||
val devices = runCatching { relay.listDevices(toUserId) }.getOrNull() ?: return
|
||||
val ids = (0 until devices.length())
|
||||
.map { devices.getJSONObject(it).optString("device_id") }
|
||||
.sorted().joinToString(",")
|
||||
if (ids.isEmpty()) return
|
||||
val stamp = "${ref.id}|$ids"
|
||||
if (vault.avatarSharedWith(toUserId) == stamp) return
|
||||
runCatching { sendPlaintext(toUserId, Content.avatar(ref)) }
|
||||
.onSuccess { vault.markAvatarShared(toUserId, ref.id) }
|
||||
.onSuccess { vault.markAvatarShared(toUserId, stamp) }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user