Functions
Resolves and conditionally applies a queue-stage status without racing a terminal ACK/NAK update.
Reaction equivalent of applyOutgoingQueueStatus, restricted to one unambiguous outgoing row.
Atomically applies an SFPP delivery-status transition to every packet and reaction matching packetId + address (from/to). Preserves the no-downgrade invariant: an item already SFPP_CONFIRMED is not regressed to SFPP_ROUTING. All updates land in one transaction so a packet and its reactions cannot end up in inconsistent delivery states.
Atomically applies an SFPP delivery-status transition to the single packet and single reaction matching hash (8-byte prefix). Same no-downgrade invariant as applySFPPStatus. Both updates land in one transaction.
Populates Packet.messageText for historical text packets that predate the FTS5 schema (v39) so they become searchable. The text is decoded in Kotlin from each packet's DataPacket.text; it cannot be read with a SQL json_extract(data, '$.text') because DataPacket.text is a computed property that is never serialized into the stored JSON (the payload is persisted as bytes). Returns the number of rows updated; the caller rebuilds the FTS index via rebuildFtsIndex when this is greater than zero.
Atomically claims one stable packet row for sending. A returned QUEUED packet means this call performed the QUEUED -> ENROUTE transition and owns the send; any other returned status means another path already handled it.
Legacy claim used only by pre-upgrade WorkManager jobs whose mesh packet ID still resolves to one row.
Atomically deletes all messages (and their reactions) for the given uuidList, chunking internally to stay under SQLite's bind-parameter limit. The entire batch is all-or-nothing: a failure rolls back every chunk.
Inserts a new packet row and returns its auto-generated stable database UUID.
Insert a packet copied from another DB. Pass uuid = 0 so a fresh auto-generated id is assigned.
One-time migration: Remap all message DataPacket.channel indices to new mapping using PSK after a channel reorder. For each Packet (with port_num = 1), finds the old PSK then sets the channel index to the matching newSettings index. Skips if PSKs do not match or are missing.
Resolves only an unambiguous row matching the identity available on an outgoing protobuf packet. Mesh packet IDs are sender-scoped, so ID-only lookup can select an inbound packet or an unrelated command with the same ID.
Rolls back a failed send only while the exact claimed row is still ENROUTE, preserving any racing ACK update.
Persists an on-device translation and switches the message to display it in one write.
Stamps routingError on a sent packet only while it is still MessageStatus.ENROUTE. The read and the write share one transaction so an ACK/NAK that resolves the packet concurrently is never overwritten by a send-ack timeout that sampled the row before it landed.
Atomically stamps routingError on a sent reaction only while it is still MessageStatus.ENROUTE.
Atomically updates the last-read message pointer for contact, preserving the monotonic-timestamp guard: if lastReadTimestamp is not newer than the stored value, the method is a no-op. Creates the contact-settings row if absent. Uses INSERT OR IGNORE + conditional UPDATE instead of a read-then-@Upsert so the existing row's unrelated columns (muteUntil, filteringDisabled) are preserved without a Kotlin-side read-modify-write.
Conditional UPDATE that only writes newer timestamps. Callers must ensure the contact-settings row exists (e.g. via insertContactSettingsIgnore) before calling this inside the same transaction.
Updates the unique outgoing row, preferring the only candidate already at status when duplicates exist.
Atomically finds a packet by identity key (id + from + to) and updates its data, optionally stamping routingError when it is non-negative. Mirrors the existing updateMessageStatus / updateMessageId pattern.
Atomically finds reactions by replacement's packetId + userId + emoji and updates every ownership-scoped copy, borrowing myNodeNum from each existing row. No-op if no match is found.