Functions
Re-keys conversations after the radio's channel set changed, applying a plan from planChannelReconciliation.
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.
Every retired conversation currently holding messages, by its channel-identity token.
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.
Marks contact unread by flipping its newest message back to unread and rewinding the read watermark past it. Both writes are needed: the badge counts read = 0 packets, while the watermark is what the message screen compares against when it later clears the conversation.
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.
Re-stamps the reactions belonging to one conversation's messages.
Unconditional watermark rewind, for marking a conversation unread again.
Rolls back a failed send only while the exact claimed row is still ENROUTE, preserving any racing ACK update.
Persists the unsent composer text for contact, creating the settings row if absent. INSERT OR IGNORE plus a targeted UPDATE, like updateLastReadMessage, so mute and filtering survive without a read-modify-write.
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.