- Skip older-history loads silently while the connection is down:
the trigger previously failed with "not connected", popped the
global alert over the timeline and froze scrolling until dismissed.
- Accept fast reply-swipe flicks whose end state is clearly leftward
even without an intermediate horizontal lock, while scroll-owned
touches still never activate the swipe.
- Add a seeded-chat UI test mode (launch argument), accessibility
identifiers and a LumaUITests target with timeline scroll and reply
swipe tests; guard the fix in verify.sh.
- Lock the gesture into a reply swipe once it starts clearly
horizontal (1.6 dominance, 12pt) and relax the ratio to 1.05
afterwards, so a slightly diagonal finish no longer cancels the
swipe while vertical scrolls stay untouched.
- Restyle the composer reply banner as a rounded telegram-like card
with the reply header, close button and two-line quote preview.
- Cover the lock/follow behaviour in MessageReplySwipeTests.
- Raise the swipe dominance threshold to 2.0 and add a 24pt
activation distance so diagonal timeline scrolls never move bubbles
or fire haptics, and snap the indicator back when a gesture turns
vertical mid-drag.
- Cover the stricter thresholds in MessageReplySwipeTests.
Models:
- Convert Conversation and ChatMessage to SwiftData @Model classes with
a
one-to-many relationship and cascade delete.
- Rename ChatMessage.id to clientID and drop Conversation.id in favor of
jid
(the string id would clash with PersistentIdentifier).
- Keep MessageReaction as a Codable value stored in an array attribute.
Persistence:
- Add ArchiveStore, a per-account ModelContainer/ModelContext that
replaces
the JSON ChatArchive with load/save/erase.
- Add ArchiveMetadataRecord for the durable MAM checkpoints, cursor,
locally
deleted message IDs and roster contact JIDs.
- Add LegacyArchiveImporter to import the old JSON snapshot once and
delete
the file afterwards, preserving existing history.
AppModel:
- Replace ChatArchive with ArchiveStore and persist via context.save();
insert new models and delete the replaced object on upsert merges so
SwiftData never keeps an orphaned duplicate.
Tests:
- Replace ChatArchiveTests with ArchiveStoreTests (store round-trip and
legacy import) and drop the now-obsolete Codable round-trip
assertions.
Server information (Monal-style):
- Add ServerInformation model and ServerInfoView reachable from
Settings,
showing server software (XEP-0092), disco#info identities and
features,
conference (MUC) services via disco#items, and STUN/TURN via XEP-0215.
- Add a curated XEP capability list with per-XEP success/error status
derived
from server/account disco features and connection flags.
- Detect PEP (0163) from account disco pubsub#* features, HTTP Upload
(0363)
from the main domain plus its components, and add Roster Versioning
(0237),
Pre-Authenticated Roster Subscription (0379), and SASL SCRAM Downgrade
Protection (0474, neutral because Martin does not expose SSDP).
Connection statistics:
- Add LumaConnectionStatsModule, an XmppStanzaFilter registered before
stream
management, to count sent / acknowledged / received stanzas.
- Track last login and SMACKS session timestamps, and read SASL
mechanisms
and Client State Indication from the raw stream features.
MAM / OMEMO handling:
- Prefer a plaintext <body> fallback when OMEMO decryption fails instead
of
showing "failed to decrypt".
- Keep the optimistic text of our own outgoing message when its echo
cannot
be decrypted back, instead of replacing it with a placeholder.
Emoji picker:
- Add EmojiCatalog and a cross-platform EmojiPickerView.
- Open the picker for reactions (replacing the static quick list) and
from
the composer's smiley button, inserting the chosen emoji into the
draft.