add server info screen, connection stats, and emoji picker
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:
1 parent
a886917dc8
commit
0317277d46
12 files changed
+1081
-24
No files matched your search
@@ -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[
|
||||
|
||||
Reference in new issue
Block a user