BitchatMessage.deliveryStatus was Optional, with nil implicitly meaning
'no tracking' for public messages. Every consumer had to branch on the
absent case, ranking needed an optional-aware helper, and the UI treated
nil as an invisible state (#644).
Model delivery as a total state machine instead:
- New DeliveryStatus.notSentYet: created but not yet handed to any
transport. Public messages initialize to it; private messages keep
their historical .sending default.
- BitchatMessage.deliveryStatus becomes non-optional. Archives written
while the field was optional decode with the absent key mapped to
.notSentYet. The wire format is untouched (toBinaryPayload never
carried the field).
- deliveryStatusRank drops its optional parameter; .notSentYet ranks
below .failed, preserving the existing dedup preference order.
- Conversation.shouldSkipStatusUpdate treats a write back to .notSentYet
as a downgrade and skips it.
- The status indicator renders exactly as before: .notSentYet draws
nothing in message rows (the state nil used to represent), and
DeliveryStatusView gains a glyph and description for it only so the
view stays total.
Tests: initialization defaults, legacy-archive decoding, round-trip,
the extended rank order, and the new downgrade rule.
Fixes#644