add server info screen, connection stats, and emoji picker
iOS CI / Build and Test SwiftUI App (push) Canceled after 0s
Build Unsigned iOS and macOS Apps / Build Unsigned iOS IPA (push) Canceled after 0s
Build Unsigned iOS and macOS Apps / Build macOS ZIP (push) Canceled after 0s

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.
This commit is contained in:
wt committed 2026-08-18 21:44:27 +07:00
1 parent a886917dc8
commit 0317277d46
12 files changed
+1081 -24

No files matched your search

+29
View File
@@ -41,6 +41,7 @@ final class AppModel: ObservableObject {
@Published var selectedConversationID: String?
@Published private(set) var isOMEMOReady = false
@Published private(set) var ownFingerprint: String?
@Published private(set) var serverInformation: ServerInformation?
@Published private(set) var isArchiveSyncing = false
@Published private(set) var isLoadingOlderHistory = false
@Published private(set) var hasMoreOlderHistory = true
@@ -1621,6 +1622,15 @@ final class AppModel: ObservableObject {
objectWillChange.send()
}
func refreshServerInformation() async {
do {
serverInformation = try await xmpp.fetchServerInformation()
} catch {
// The screen shows its own error state; keep any previously loaded
// snapshot rather than clearing it on a transient refresh failure.
}
}
func clearError() {
errorMessage = nil
}
@@ -2521,6 +2531,25 @@ final class AppModel: ObservableObject {
merged.security = previous.security
merged.encryptionFingerprint = previous.encryptionFingerprint
}
if message.security == .decryptionFailed,
previous.security != .decryptionFailed,
!previous.body.isEmpty
{
// Our own outgoing message was rendered optimistically with the
// text the user typed. Its server echo (carbon or MAM) cannot
// be decrypted back (no encrypt-to-self key, or a consumed
// session), so keep the original content instead of replacing
// it with a "failed to decrypt" placeholder.
merged.body = previous.body
merged.security = previous.security
merged.encryptionFingerprint = previous.encryptionFingerprint
merged.kind = previous.kind
merged.remoteAttachmentURL = previous.remoteAttachmentURL
merged.localFilename = previous.localFilename
merged.mimeType = previous.mimeType
merged.duration = previous.duration
merged.byteCount = previous.byteCount
}
messages[index] = merged
if let stanzaID = merged.stanzaID {
messageIndexByStanzaKey[