fix: synchronize read state across web and native clients

Track readable content revisions and durable read cursors in the hub. Acknowledge only rendered foreground tails, persist offline read operations, and preserve newer manual unread choices across devices.

Migrate existing history to a read baseline and cover pagination receipts with shared native conformance fixtures.
This commit is contained in:
weishu
2026-11-01 09:52:51 +08:00
parent ee4ac238cb
commit 5ef4304a4a
139 changed files with 2352 additions and 1053 deletions
@@ -10,6 +10,7 @@ import Foundation
/// `content` degrades to `.null` instead of throwing.
public struct DecryptedMessage: Codable, Equatable, Sendable {
/// Server uuid. Optimistic rows use the client `localId` until echoed.
public var contentRevision: Int?
public var id: String
/// Per-session insert counter; `null` on optimistic rows.
public var seq: Int?
@@ -32,6 +33,7 @@ public struct DecryptedMessage: Codable, Equatable, Sendable {
public var deliveryState: String?
public init(
contentRevision: Int? = nil,
id: String,
seq: Int? = nil,
localId: String? = nil,
@@ -41,6 +43,7 @@ public struct DecryptedMessage: Codable, Equatable, Sendable {
scheduledAt: Int? = nil,
deliveryState: String? = nil
) {
self.contentRevision = contentRevision
self.id = id
self.seq = seq
self.localId = localId
@@ -53,6 +56,7 @@ public struct DecryptedMessage: Codable, Equatable, Sendable {
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
contentRevision = try container.decodeIfPresent(Int.self, forKey: .contentRevision)
id = try container.decode(String.self, forKey: .id)
seq = try container.decodeIfPresent(Int.self, forKey: .seq)
localId = try container.decodeIfPresent(String.self, forKey: .localId)