From 901d54aff2a3baf3283151b4d6cac6c03f29a96b Mon Sep 17 00:00:00 2001 From: lissine Date: Tue, 12 Aug 2025 04:51:56 +0100 Subject: [PATCH 1/9] Remove manual constant imports from Objective-C to Swift MLConstants.h is part of monalxmpp, which is globally imported in our Swift source files. And as of commit 12c4d23ed52b93, MLConstants.h has public visibility. Thus we don't need to manually import Objc constants anymore. --- Monal/Classes/HelperTools.h | 26 -------------------------- Monal/Classes/HelperTools.m | 31 ------------------------------- Monal/Classes/SwiftHelpers.swift | 27 --------------------------- 3 files changed, 84 deletions(-) diff --git a/Monal/Classes/HelperTools.h b/Monal/Classes/HelperTools.h index 5de15faa35..1329798b4e 100644 --- a/Monal/Classes/HelperTools.h +++ b/Monal/Classes/HelperTools.h @@ -46,31 +46,6 @@ typedef NS_ENUM(NSUInteger, MLVersionType) { MLVersionTypeLog, }; -typedef NS_ENUM(NSUInteger, MLDefinedIdentifier) { - MLDefinedIdentifier_kAppGroup, - MLDefinedIdentifier_kMonalOpenURL, - MLDefinedIdentifier_kBackgroundProcessingTask, - MLDefinedIdentifier_kBackgroundRefreshingTask, - MLDefinedIdentifier_kMonalKeychainName, - MLDefinedIdentifier_kMucTypeGroup, - MLDefinedIdentifier_kMucTypeChannel, - MLDefinedIdentifier_kMucRoleModerator, - MLDefinedIdentifier_kMucRoleNone, - MLDefinedIdentifier_kMucRoleParticipant, - MLDefinedIdentifier_kMucRoleVisitor, - MLDefinedIdentifier_kMucAffiliationOwner, - MLDefinedIdentifier_kMucAffiliationAdmin, - MLDefinedIdentifier_kMucAffiliationMember, - MLDefinedIdentifier_kMucAffiliationOutcast, - MLDefinedIdentifier_kMucAffiliationNone, - MLDefinedIdentifier_kMucActionShowProfile, - MLDefinedIdentifier_kMucActionReinvite, - MLDefinedIdentifier_SHORT_PING, - MLDefinedIdentifier_LONG_PING, - MLDefinedIdentifier_MUC_PING, - MLDefinedIdentifier_BGFETCH_DEFAULT_INTERVAL, -}; - typedef NS_ENUM(NSUInteger, MLRunLoopIdentifier) { MLRunLoopIdentifierNetwork, MLRunLoopIdentifierTimer, @@ -126,7 +101,6 @@ void swizzle(Class c, SEL orig, SEL new); +(AnyPromise*) renderUIImageFromSVGURL:(NSURL* _Nullable) url; +(AnyPromise*) renderUIImageFromSVGData:(NSData* _Nullable) data; +(void) busyWaitForOperationQueue:(NSOperationQueue*) queue; -+(id) getObjcDefinedValue:(MLDefinedIdentifier) identifier; +(NSRunLoop*) getExtraRunloopWithIdentifier:(MLRunLoopIdentifier) identifier; +(NSError* _Nullable) hardLinkOrCopyFile:(NSString*) from to:(NSString*) to; +(NSString*) getQueueThreadLabelFor:(DDLogMessage*) logMessage; diff --git a/Monal/Classes/HelperTools.m b/Monal/Classes/HelperTools.m index c536ce0a5d..dcec25b8bd 100644 --- a/Monal/Classes/HelperTools.m +++ b/Monal/Classes/HelperTools.m @@ -760,37 +760,6 @@ +(void) busyWaitForOperationQueue:(NSOperationQueue*) queue DDLogWarn(@"busyWaitFor:%@ --> busyWaitCounter=%d, waitTime=%f", queue.name, busyWaitCounter, waitTime); } -+(id) getObjcDefinedValue:(MLDefinedIdentifier) identifier -{ - switch(identifier) - { - case MLDefinedIdentifier_kAppGroup: return kAppGroup; break; - case MLDefinedIdentifier_kMonalOpenURL: return kMonalOpenURL; break; - case MLDefinedIdentifier_kBackgroundProcessingTask: return kBackgroundProcessingTask; break; - case MLDefinedIdentifier_kBackgroundRefreshingTask: return kBackgroundRefreshingTask; break; - case MLDefinedIdentifier_kMonalKeychainName: return kMonalKeychainName; break; - case MLDefinedIdentifier_kMucTypeGroup: return kMucTypeGroup; break; - case MLDefinedIdentifier_kMucRoleModerator: return kMucRoleModerator; break; - case MLDefinedIdentifier_kMucRoleNone: return kMucRoleNone; break; - case MLDefinedIdentifier_kMucRoleParticipant: return kMucRoleParticipant; break; - case MLDefinedIdentifier_kMucRoleVisitor: return kMucRoleVisitor; break; - case MLDefinedIdentifier_kMucAffiliationOwner: return kMucAffiliationOwner; break; - case MLDefinedIdentifier_kMucAffiliationAdmin: return kMucAffiliationAdmin; break; - case MLDefinedIdentifier_kMucAffiliationMember: return kMucAffiliationMember; break; - case MLDefinedIdentifier_kMucAffiliationOutcast: return kMucAffiliationOutcast; break; - case MLDefinedIdentifier_kMucAffiliationNone: return kMucAffiliationNone; break; - case MLDefinedIdentifier_kMucActionShowProfile: return kMucActionShowProfile; break; - case MLDefinedIdentifier_kMucActionReinvite: return kMucActionReinvite; break; - case MLDefinedIdentifier_kMucTypeChannel: return kMucTypeChannel; break; - case MLDefinedIdentifier_SHORT_PING: return @(SHORT_PING); break; - case MLDefinedIdentifier_LONG_PING: return @(LONG_PING); break; - case MLDefinedIdentifier_MUC_PING: return @(MUC_PING); break; - case MLDefinedIdentifier_BGFETCH_DEFAULT_INTERVAL: return @(BGFETCH_DEFAULT_INTERVAL); break; - default: - unreachable(@"unknown MLDefinedIdentifier!"); - } -} - +(NSRunLoop*) getExtraRunloopWithIdentifier:(MLRunLoopIdentifier) identifier { static NSMutableDictionary* runloops = nil; diff --git a/Monal/Classes/SwiftHelpers.swift b/Monal/Classes/SwiftHelpers.swift index 739902f63f..929160de26 100644 --- a/Monal/Classes/SwiftHelpers.swift +++ b/Monal/Classes/SwiftHelpers.swift @@ -18,33 +18,6 @@ import Combine import SwiftUI import SVGView -//import some defines in MLConstants.h into swift -let kAppGroup = HelperTools.getObjcDefinedValue(.kAppGroup) -let kMonalOpenURL = HelperTools.getObjcDefinedValue(.kMonalOpenURL) -let kBackgroundProcessingTask = HelperTools.getObjcDefinedValue(.kBackgroundProcessingTask) -let kBackgroundRefreshingTask = HelperTools.getObjcDefinedValue(.kBackgroundRefreshingTask) -let kMonalKeychainName = HelperTools.getObjcDefinedValue(.kMonalKeychainName) -let kMucTypeGroup = HelperTools.getObjcDefinedValue(.kMucTypeGroup) -let kMucTypeChannel = HelperTools.getObjcDefinedValue(.kMucTypeChannel) - -let kMucRoleModerator = HelperTools.getObjcDefinedValue(.kMucRoleModerator) -let kMucRoleNone = HelperTools.getObjcDefinedValue(.kMucRoleNone) -let kMucRoleParticipant = HelperTools.getObjcDefinedValue(.kMucRoleParticipant) -let kMucRoleVisitor = HelperTools.getObjcDefinedValue(.kMucRoleVisitor) - -let kMucAffiliationOwner = HelperTools.getObjcDefinedValue(.kMucAffiliationOwner) -let kMucAffiliationAdmin = HelperTools.getObjcDefinedValue(.kMucAffiliationAdmin) -let kMucAffiliationMember = HelperTools.getObjcDefinedValue(.kMucAffiliationMember) -let kMucAffiliationOutcast = HelperTools.getObjcDefinedValue(.kMucAffiliationOutcast) -let kMucAffiliationNone = HelperTools.getObjcDefinedValue(.kMucAffiliationNone) -let kMucActionShowProfile = HelperTools.getObjcDefinedValue(.kMucActionShowProfile) -let kMucActionReinvite = HelperTools.getObjcDefinedValue(.kMucActionReinvite) - -let SHORT_PING = HelperTools.getObjcDefinedValue(.SHORT_PING) -let LONG_PING = HelperTools.getObjcDefinedValue(.LONG_PING) -let MUC_PING = HelperTools.getObjcDefinedValue(.MUC_PING) -let BGFETCH_DEFAULT_INTERVAL = HelperTools.getObjcDefinedValue(.BGFETCH_DEFAULT_INTERVAL) - public typealias monal_timer_block_t = @convention(block) (MLDelayableTimer?) -> Void; public typealias monal_void_block_t = @convention(block) () -> Void; public typealias monal_id_block_t = @convention(block) (AnyObject?) -> Void; From 32030de66b91f74865e5f3fb1859dc62fe2f84e7 Mon Sep 17 00:00:00 2001 From: lissine Date: Wed, 7 May 2025 11:46:08 +0100 Subject: [PATCH 2/9] Use constants for notification names in Swift Using constants allows the compiler to catch potential typos. --- Monal/Classes/BlockedUsers.swift | 4 ++-- Monal/Classes/ChannelMemberList.swift | 2 +- Monal/Classes/ChatView.swift | 6 +++--- Monal/Classes/ContactDetails.swift | 2 +- Monal/Classes/ContactRequestsMenu.swift | 4 ++-- Monal/Classes/ContactResources.swift | 6 +++--- Monal/Classes/ContactsView.swift | 4 ++-- Monal/Classes/MemberList.swift | 2 +- Monal/Classes/OmemoKeysView.swift | 4 ++-- Monal/Classes/PasswordMigration.swift | 2 +- Monal/Classes/Quicksy_RegisterAccount.swift | 10 +++++----- Monal/Classes/RegisterAccount.swift | 4 ++-- Monal/Classes/WelcomeLogIn.swift | 10 +++++----- 13 files changed, 30 insertions(+), 30 deletions(-) diff --git a/Monal/Classes/BlockedUsers.swift b/Monal/Classes/BlockedUsers.swift index e05da1a6a3..de632eb008 100644 --- a/Monal/Classes/BlockedUsers.swift +++ b/Monal/Classes/BlockedUsers.swift @@ -51,7 +51,7 @@ struct BlockedUsers: View { showBlockingUnsupportedPlaceholder = blockingUnsupported reloadBlocksFromDB() } - .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("kMonalAccountDiscoDone")).receive(on: RunLoop.main)) { notification in + .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name(kMonalAccountDiscoDone)).receive(on: RunLoop.main)) { notification in guard let notificationAccountID = notification.userInfo?["accountID"] as? NSNumber, notificationAccountID.intValue == xmppAccount.accountID.intValue else { return @@ -62,7 +62,7 @@ struct BlockedUsers: View { reloadBlocksFromDB() hideLoadingOverlay(overlay) } - .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("kMonalBlockListRefresh")).receive(on: RunLoop.main)) { notification in + .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name(kMonalBlockListRefresh)).receive(on: RunLoop.main)) { notification in guard let notificationAccountID = notification.userInfo?["accountID"] as? NSNumber, notificationAccountID.intValue == xmppAccount.accountID.intValue else { return diff --git a/Monal/Classes/ChannelMemberList.swift b/Monal/Classes/ChannelMemberList.swift index 3229edbf2e..1e2362b4be 100644 --- a/Monal/Classes/ChannelMemberList.swift +++ b/Monal/Classes/ChannelMemberList.swift @@ -59,7 +59,7 @@ struct ChannelMemberList: View { .onAppear { updateParticipantList() } - .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("kMonalMucParticipantsAndMembersUpdated")).receive(on: RunLoop.main)) { notification in + .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name(kMonalMucParticipantsAndMembersUpdated)).receive(on: RunLoop.main)) { notification in if let xmppAccount = notification.object as? xmpp, let contact = notification.userInfo?["contact"] as? MLContact { DDLogVerbose("Got muc participants/members update from account \(xmppAccount)...") if contact == channel { diff --git a/Monal/Classes/ChatView.swift b/Monal/Classes/ChatView.swift index 951e1c331f..f9db5fe7eb 100644 --- a/Monal/Classes/ChatView.swift +++ b/Monal/Classes/ChatView.swift @@ -386,7 +386,7 @@ struct ChatView: View { MLNotificationManager.sharedInstance().currentContact = nil } } - .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("kMonalOmemoFetchingStateUpdate")).receive(on: RunLoop.main)) { notification in + .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name(kMonalOmemoFetchingStateUpdate)).receive(on: RunLoop.main)) { notification in if let xmppAccount = notification.object as? xmpp, let notificationJid = notification.userInfo?["jid"] as? String { if xmppAccount.accountID == contact.accountID && notificationJid == contact.contactJid { DDLogDebug("Got omemo fetching update: \(contact) --> \(String(describing:notification.userInfo))") @@ -398,7 +398,7 @@ struct ChatView: View { } } } - .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("kMonalNewMessageNotice")).receive(on: RunLoop.main)) { notification in + .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name(kMonalNewMessageNotice)).receive(on: RunLoop.main)) { notification in DDLogVerbose("chat view got new message notice \(String(describing:notification.userInfo))") guard let message = notification.userInfo?["message"] as? MLMessage else { @@ -412,7 +412,7 @@ struct ChatView: View { } ChatViewHelpers.refreshCounter(for: self.contact.obj) } - .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("kMonalRefresh")).receive(on: RunLoop.main)) { notification in + .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name(kMonalRefresh)).receive(on: RunLoop.main)) { notification in ChatViewHelpers.refreshCounter(for: self.contact.obj) } } diff --git a/Monal/Classes/ContactDetails.swift b/Monal/Classes/ContactDetails.swift index 795b686305..330dad1d47 100644 --- a/Monal/Classes/ContactDetails.swift +++ b/Monal/Classes/ContactDetails.swift @@ -676,7 +676,7 @@ struct ContactDetails: View { .onAppear { self.updateRoleAndAffiliation() } - .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("kMonalMucParticipantsAndMembersUpdated")).receive(on: RunLoop.main)) { notification in + .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name(kMonalMucParticipantsAndMembersUpdated)).receive(on: RunLoop.main)) { notification in if let xmppAccount = notification.object as? xmpp, let notificationContact = notification.userInfo?["contact"] as? MLContact { DDLogVerbose("Got muc participants/members update from account \(xmppAccount)...") if notificationContact == contact { diff --git a/Monal/Classes/ContactRequestsMenu.swift b/Monal/Classes/ContactRequestsMenu.swift index a362e35eb7..fdf0f4a0c7 100644 --- a/Monal/Classes/ContactRequestsMenu.swift +++ b/Monal/Classes/ContactRequestsMenu.swift @@ -112,10 +112,10 @@ struct ContactRequestsMenu: View { } } } - .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("kMonalContactRefresh")).receive(on: RunLoop.main)) { notification in + .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name(kMonalContactRefresh)).receive(on: RunLoop.main)) { notification in updateRequests() } - .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("kMonalContactRemoved")).receive(on: RunLoop.main)) { notification in + .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name(kMonalContactRemoved)).receive(on: RunLoop.main)) { notification in updateRequests() } .onAppear { diff --git a/Monal/Classes/ContactResources.swift b/Monal/Classes/ContactResources.swift index 5b51d44d20..d67f672f45 100644 --- a/Monal/Classes/ContactResources.swift +++ b/Monal/Classes/ContactResources.swift @@ -88,7 +88,7 @@ struct ContactResources: View { } } } - .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("kMonalXmppUserSoftWareVersionRefresh")).receive(on: RunLoop.main)) { notification in + .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name(kMonalXmppUserSoftWareVersionRefresh)).receive(on: RunLoop.main)) { notification in if let xmppAccount = notification.object as? xmpp, let softwareInfo = notification.userInfo?["versionInfo"] as? MLContactSoftwareVersionInfo { DDLogVerbose("Got software version info from account \(xmppAccount)...") if softwareInfo.fromJid == contact.obj.contactJid && xmppAccount.accountID == contact.obj.accountID { @@ -99,7 +99,7 @@ struct ContactResources: View { } } } - .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("kMonalNewPresenceNotice")).receive(on: RunLoop.main)) { notification in + .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name(kMonalNewPresenceNotice)).receive(on: RunLoop.main)) { notification in if let xmppAccount = notification.object as? xmpp, let jid = notification.userInfo?["jid"] as? String, let resource = notification.userInfo?["resource"] as? String, let available = notification.userInfo?["available"] as? NSNumber { DDLogVerbose("Got presence update from account \(xmppAccount)...") if jid == contact.obj.contactJid && xmppAccount.accountID == contact.obj.accountID { @@ -118,7 +118,7 @@ struct ContactResources: View { } } } - .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("kMonalLastInteractionUpdatedNotice")).receive(on: RunLoop.main)) { notification in + .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name(kMonalLastInteractionUpdatedNotice)).receive(on: RunLoop.main)) { notification in if let xmppAccount = notification.object as? xmpp, let jid = notification.userInfo?["jid"] as? String, let resource = notification.userInfo?["resource"] as? String, notification.userInfo?["lastInteraction"] as? NSDate != nil { DDLogVerbose("Got lastInteraction update from account \(xmppAccount)...") if jid == contact.obj.contactJid && xmppAccount.accountID == contact.obj.accountID { diff --git a/Monal/Classes/ContactsView.swift b/Monal/Classes/ContactsView.swift index 7480e516d1..cdac614f0b 100644 --- a/Monal/Classes/ContactsView.swift +++ b/Monal/Classes/ContactsView.swift @@ -200,10 +200,10 @@ class Contacts: ObservableObject { self.contacts = Set(DataLayer.sharedInstance().contactList()) self.requestCount = DataLayer.sharedInstance().allContactRequests().count subscriptions = [ - NotificationCenter.default.publisher(for: NSNotification.Name("kMonalContactRefresh")) + NotificationCenter.default.publisher(for: NSNotification.Name(kMonalContactRefresh)) .receive(on: DispatchQueue.main) .sink() { _ in self.refreshContacts() }, - NotificationCenter.default.publisher(for: NSNotification.Name("kMonalContactRemoved")) + NotificationCenter.default.publisher(for: NSNotification.Name(kMonalContactRemoved)) .receive(on: DispatchQueue.main) .sink() { _ in self.refreshContacts() }, ] diff --git a/Monal/Classes/MemberList.swift b/Monal/Classes/MemberList.swift index c7f6a62ba2..e8a4d89a69 100644 --- a/Monal/Classes/MemberList.swift +++ b/Monal/Classes/MemberList.swift @@ -340,7 +340,7 @@ struct MemberList: View { .onAppear { updateMemberlist() } - .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("kMonalMucParticipantsAndMembersUpdated")).receive(on: RunLoop.main)) { notification in + .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name(kMonalMucParticipantsAndMembersUpdated)).receive(on: RunLoop.main)) { notification in if let xmppAccount = notification.object as? xmpp, let contact = notification.userInfo?["contact"] as? MLContact { DDLogVerbose("Got muc participants/members update from account \(xmppAccount)...") //only trigger update if we are either in a group type muc or have admin/owner priviledges diff --git a/Monal/Classes/OmemoKeysView.swift b/Monal/Classes/OmemoKeysView.swift index 7b457a8e9b..741bb9b150 100644 --- a/Monal/Classes/OmemoKeysView.swift +++ b/Monal/Classes/OmemoKeysView.swift @@ -453,10 +453,10 @@ class OmemoKeysForChat: ObservableObject { self.viewContact = viewContact self.contacts = OmemoKeysForChat.knownDevices(viewContact: self.viewContact) subscriptions = [ - NotificationCenter.default.publisher(for: NSNotification.Name("kMonalOmemoStateUpdated")) + NotificationCenter.default.publisher(for: NSNotification.Name(kMonalOmemoStateUpdated)) .receive(on: DispatchQueue.main) .sink() { _ in self.updateContactDevices() }, - NotificationCenter.default.publisher(for: NSNotification.Name("kMonalMucParticipantsAndMembersUpdated")) + NotificationCenter.default.publisher(for: NSNotification.Name(kMonalMucParticipantsAndMembersUpdated)) .receive(on: DispatchQueue.main) .sink() { _ in self.updateContactDevices() }, ] diff --git a/Monal/Classes/PasswordMigration.swift b/Monal/Classes/PasswordMigration.swift index 0986a66ca4..cde5856853 100644 --- a/Monal/Classes/PasswordMigration.swift +++ b/Monal/Classes/PasswordMigration.swift @@ -116,7 +116,7 @@ struct PasswordMigration: View { DataLayer.sharedInstance().updateAccoun(with:dic) } } - NotificationCenter.default.post(name:Notification.Name("kMonalRefresh"), object:nil); + NotificationCenter.default.post(name:Notification.Name(kMonalRefresh), object:nil); self.delegate.dismiss() }, label: { Text("Done") diff --git a/Monal/Classes/Quicksy_RegisterAccount.swift b/Monal/Classes/Quicksy_RegisterAccount.swift index 9e84c6c77c..97043a8674 100644 --- a/Monal/Classes/Quicksy_RegisterAccount.swift +++ b/Monal/Classes/Quicksy_RegisterAccount.swift @@ -447,7 +447,7 @@ struct Quicksy_RegisterAccount: View { }) .padding() .addLoadingOverlay(overlay) - .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("kXMPPError")).receive(on: RunLoop.main)) { notification in + .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name(kXMPPError)).receive(on: RunLoop.main)) { notification in if(self.errorObserverEnabled == false) { return } @@ -463,7 +463,7 @@ struct Quicksy_RegisterAccount: View { } } } - .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("kMLResourceBoundNotice")).receive(on: RunLoop.main)) { notification in + .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name(kMLResourceBoundNotice)).receive(on: RunLoop.main)) { notification in if let xmppAccount = notification.object as? xmpp, let newAccountID : NSNumber = self.newAccountID { if(xmppAccount.accountID.intValue == newAccountID.intValue) { DispatchQueue.main.async { @@ -474,7 +474,7 @@ struct Quicksy_RegisterAccount: View { } } } - .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("kMonalUpdateBundleFetchStatus")).receive(on: RunLoop.main)) { notification in + .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name(kMonalUpdateBundleFetchStatus)).receive(on: RunLoop.main)) { notification in if let notificationAccountID = notification.userInfo?["accountID"] as? NSNumber, let completed = notification.userInfo?["completed"] as? NSNumber, let all = notification.userInfo?["all"] as? NSNumber, let newAccountID : NSNumber = self.newAccountID { if(notificationAccountID.intValue == newAccountID.intValue) { isLoadingOmemoBundles = true @@ -486,7 +486,7 @@ struct Quicksy_RegisterAccount: View { } } } - .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("kMonalFinishedOmemoBundleFetch")).receive(on: RunLoop.main)) { notification in + .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name(kMonalFinishedOmemoBundleFetch)).receive(on: RunLoop.main)) { notification in if let notificationAccountID = notification.userInfo?["accountID"] as? NSNumber, let newAccountID : NSNumber = self.newAccountID { if(notificationAccountID.intValue == newAccountID.intValue && isLoadingOmemoBundles) { DispatchQueue.main.async { @@ -496,7 +496,7 @@ struct Quicksy_RegisterAccount: View { } } } - .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("kMonalFinishedCatchup")).receive(on: RunLoop.main)) { notification in + .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name(kMonalFinishedCatchup)).receive(on: RunLoop.main)) { notification in if let xmppAccount = notification.object as? xmpp, let newAccountID : NSNumber = self.newAccountID { if(xmppAccount.accountID.intValue == newAccountID.intValue && !isLoadingOmemoBundles) { DispatchQueue.main.async { diff --git a/Monal/Classes/RegisterAccount.swift b/Monal/Classes/RegisterAccount.swift index d90e161a75..b963e0c09b 100644 --- a/Monal/Classes/RegisterAccount.swift +++ b/Monal/Classes/RegisterAccount.swift @@ -452,7 +452,7 @@ struct RegisterAccount: View { } .addLoadingOverlay(overlay) .navigationBarTitle(Text("Register"), displayMode:.large) - .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("kXMPPError")).receive(on: RunLoop.main)) { notification in + .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name(kXMPPError)).receive(on: RunLoop.main)) { notification in DDLogDebug("Got xmpp error") if(self.errorObserverEnabled == false) { return @@ -473,7 +473,7 @@ struct RegisterAccount: View { } } } - .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("kMLResourceBoundNotice")).receive(on: RunLoop.main)) { notification in + .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name(kMLResourceBoundNotice)).receive(on: RunLoop.main)) { notification in if(self.registerComplete == true) { return } diff --git a/Monal/Classes/WelcomeLogIn.swift b/Monal/Classes/WelcomeLogIn.swift index a77f6a5743..c98edba744 100644 --- a/Monal/Classes/WelcomeLogIn.swift +++ b/Monal/Classes/WelcomeLogIn.swift @@ -339,7 +339,7 @@ struct WelcomeLogIn: View { .navigationTitle(advancedMode ? Text("Add Account (advanced)") : Text("Welcome")) .navigationBarTitleDisplayMode(advancedMode ? .inline : .large) .onDisappear {UITableView.appearance().tableHeaderView = nil} //why that?? - .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("kXMPPError")).receive(on: RunLoop.main)) { notification in + .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name(kXMPPError)).receive(on: RunLoop.main)) { notification in if(self.errorObserverEnabled == false) { return } @@ -355,7 +355,7 @@ struct WelcomeLogIn: View { } } } - .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("kMLResourceBoundNotice")).receive(on: RunLoop.main)) { notification in + .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name(kMLResourceBoundNotice)).receive(on: RunLoop.main)) { notification in if let xmppAccount = notification.object as? xmpp, let newAccountID : NSNumber = self.newAccountID { if(xmppAccount.accountID.intValue == newAccountID.intValue) { DispatchQueue.main.async { @@ -366,7 +366,7 @@ struct WelcomeLogIn: View { } } } - .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("kMonalUpdateBundleFetchStatus")).receive(on: RunLoop.main)) { notification in + .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name(kMonalUpdateBundleFetchStatus)).receive(on: RunLoop.main)) { notification in if let notificationAccountID = notification.userInfo?["accountID"] as? NSNumber, let completed = notification.userInfo?["completed"] as? NSNumber, let all = notification.userInfo?["all"] as? NSNumber, let newAccountID : NSNumber = self.newAccountID { if(notificationAccountID.intValue == newAccountID.intValue) { isLoadingOmemoBundles = true @@ -378,7 +378,7 @@ struct WelcomeLogIn: View { } } } - .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("kMonalFinishedOmemoBundleFetch")).receive(on: RunLoop.main)) { notification in + .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name(kMonalFinishedOmemoBundleFetch)).receive(on: RunLoop.main)) { notification in if let notificationAccountID = notification.userInfo?["accountID"] as? NSNumber, let newAccountID : NSNumber = self.newAccountID { if(notificationAccountID.intValue == newAccountID.intValue && isLoadingOmemoBundles) { DispatchQueue.main.async { @@ -388,7 +388,7 @@ struct WelcomeLogIn: View { } } } - .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("kMonalFinishedCatchup")).receive(on: RunLoop.main)) { notification in + .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name(kMonalFinishedCatchup)).receive(on: RunLoop.main)) { notification in if let xmppAccount = notification.object as? xmpp, let newAccountID : NSNumber = self.newAccountID { if(xmppAccount.accountID.intValue == newAccountID.intValue && !isLoadingOmemoBundles) { DispatchQueue.main.async { From c7f74502a86c109fa2b1bdf53b333b2b4c70f706 Mon Sep 17 00:00:00 2001 From: lissine Date: Wed, 7 May 2025 06:43:26 +0100 Subject: [PATCH 3/9] Use a separate notification for message updates Use kMonalUpdatedMessageNotice for message corrections and MUC message reflections, instead of using kMonalNewMessageNotice. --- Monal/Classes/ActiveChatsViewController.m | 1 + Monal/Classes/MLConstants.h | 1 + Monal/Classes/MLContact.m | 1 + Monal/Classes/MLMessageProcessor.m | 31 ++++++++++++++++------- Monal/Classes/MLNotificationManager.m | 1 + Monal/Classes/chatViewController.m | 1 + 6 files changed, 27 insertions(+), 9 deletions(-) diff --git a/Monal/Classes/ActiveChatsViewController.m b/Monal/Classes/ActiveChatsViewController.m index 202eae8949..2a2f139601 100755 --- a/Monal/Classes/ActiveChatsViewController.m +++ b/Monal/Classes/ActiveChatsViewController.m @@ -333,6 +333,7 @@ -(void) viewDidLoad [nc addObserver:self selector:@selector(handleRefreshDisplayNotification:) name:kMonalMessageFiletransferUpdateNotice object:nil]; [nc addObserver:self selector:@selector(refreshContact:) name:kMonalContactRefresh object:nil]; [nc addObserver:self selector:@selector(handleNewMessage:) name:kMonalNewMessageNotice object:nil]; + [nc addObserver:self selector:@selector(handleNewMessage:) name:kMonalUpdatedMessageNotice object:nil]; [nc addObserver:self selector:@selector(handleNewMessage:) name:kMonalDeletedMessageNotice object:nil]; [nc addObserver:self selector:@selector(messageSent:) name:kMLMessageSentToContact object:nil]; [nc addObserver:self selector:@selector(handleDeviceRotation) name:UIDeviceOrientationDidChangeNotification object:nil]; diff --git a/Monal/Classes/MLConstants.h b/Monal/Classes/MLConstants.h index ab595530b0..0cb27be8ae 100644 --- a/Monal/Classes/MLConstants.h +++ b/Monal/Classes/MLConstants.h @@ -151,6 +151,7 @@ static inline NSString* _Nonnull LocalizationNotNeeded(NSString* _Nonnull s) #define kMonalFrozen @"kMonalFrozen" #define kMonalUnfrozen @"kMonalUnfrozen" #define kMonalNewMessageNotice @"kMonalNewMessageNotice" +#define kMonalUpdatedMessageNotice @"kMonalUpdatedMessageNotice" #define kMonalMucSubjectChanged @"kMonalMucSubjectChanged" #define kMonalDeletedMessageNotice @"kMonalDeletedMessageNotice" #define kMonalDisplayedMessagesNotice @"kMonalDisplayedMessagesNotice" diff --git a/Monal/Classes/MLContact.m b/Monal/Classes/MLContact.m index b382ba925d..ab35a8895e 100644 --- a/Monal/Classes/MLContact.m +++ b/Monal/Classes/MLContact.m @@ -274,6 +274,7 @@ -(instancetype) init [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleContactRefresh:) name:kMonalContactRemoved object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleMucSubjectChange:) name:kMonalMucSubjectChanged object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateUnreadCount) name:kMonalNewMessageNotice object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateUnreadCount) name:kMonalUpdatedMessageNotice object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateUnreadCount) name:kMonalDeletedMessageNotice object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateUnreadCount) name:kMLMessageSentToContact object:nil]; return self; diff --git a/Monal/Classes/MLMessageProcessor.m b/Monal/Classes/MLMessageProcessor.m index c9410bb043..fd1ff21832 100644 --- a/Monal/Classes/MLMessageProcessor.m +++ b/Monal/Classes/MLMessageProcessor.m @@ -698,13 +698,25 @@ +(MLMessage* _Nullable) processMessage:(XMPPMessage*) messageNode andOuterMessag [[DataLayer sharedInstance] addActiveBuddies:buddyName forAccount:account.accountID]; - DDLogInfo(@"Sending out kMonalNewMessageNotice notification for historyId %@", historyId); - [[MLNotificationQueue currentQueue] postNotificationName:kMonalNewMessageNotice object:account userInfo:@{ - @"message": message, - @"showAlert": @(showAlert), - @"contact": possiblyUnknownContact, - @"LMCReplaced": @(LMCReplaced), - }]; + if (LMCReplaced) + { + DDLogInfo(@"Sending out kMonalUpdatedMessageNotice notification for historyId %@", historyId); + [[MLNotificationQueue currentQueue] postNotificationName:kMonalUpdatedMessageNotice object:account userInfo:@{ + @"message": message, + @"showAlert": @(showAlert), + @"contact": possiblyUnknownContact, + @"LMCReplaced": @YES, + }]; + } + else + { + DDLogInfo(@"Sending out kMonalNewMessageNotice notification for historyId %@", historyId); + [[MLNotificationQueue currentQueue] postNotificationName:kMonalNewMessageNotice object:account userInfo:@{ + @"message": message, + @"showAlert": @(showAlert), + @"contact": possiblyUnknownContact, + }]; + } //try to automatically determine content type of filetransfers if(messageType == kMessageTypeFiletransfer && [[HelperTools defaultsDB] boolForKey:@"AutodownloadFiletransfers"]) @@ -721,11 +733,12 @@ +(MLMessage* _Nullable) processMessage:(XMPPMessage*) messageNode andOuterMessag { message = [[DataLayer sharedInstance] messageForHistoryID:historyId]; DDLogDebug(@"Managed to update stanzaid of message (or stanzaid already known): %@", message); - DDLogInfo(@"Sending out kMonalNewMessageNotice notification for historyId %@", historyId); - [[MLNotificationQueue currentQueue] postNotificationName:kMonalNewMessageNotice object:account userInfo:@{ + DDLogInfo(@"Sending out kMonalUpdatedMessageNotice notification for historyId %@", historyId); + [[MLNotificationQueue currentQueue] postNotificationName:kMonalUpdatedMessageNotice object:account userInfo:@{ @"message": message, @"showAlert": @(NO), @"contact": possiblyUnknownContact, + @"LMCReplaced": @NO, }]; } } diff --git a/Monal/Classes/MLNotificationManager.m b/Monal/Classes/MLNotificationManager.m index 50bf9a95f6..dcb70b1188 100644 --- a/Monal/Classes/MLNotificationManager.m +++ b/Monal/Classes/MLNotificationManager.m @@ -50,6 +50,7 @@ -(id) init { self = [super init]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNewMessage:) name:kMonalNewMessageNotice object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNewMessage:) name:kMonalUpdatedMessageNotice object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleFiletransferUpdate:) name:kMonalMessageFiletransferUpdateNotice object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleDeletedMessage:) name:kMonalDeletedMessageNotice object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleDisplayedMessages:) name:kMonalDisplayedMessagesNotice object:nil]; diff --git a/Monal/Classes/chatViewController.m b/Monal/Classes/chatViewController.m index e10cbf8c30..a25a1329f9 100644 --- a/Monal/Classes/chatViewController.m +++ b/Monal/Classes/chatViewController.m @@ -777,6 +777,7 @@ -(void) viewWillAppear:(BOOL)animated NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; [nc addObserver:self selector:@selector(handleNewMessage:) name:kMonalNewMessageNotice object:nil]; + [nc addObserver:self selector:@selector(handleNewMessage:) name:kMonalUpdatedMessageNotice object:nil]; [nc addObserver:self selector:@selector(handleDeletedMessage:) name:kMonalDeletedMessageNotice object:nil]; [nc addObserver:self selector:@selector(handleSentMessage:) name:kMonalSentMessageNotice object:nil]; [nc addObserver:self selector:@selector(handleMessageError:) name:kMonalMessageErrorNotice object:nil]; From 8aeb238e6f9096cbdde8b9abc62d634287431d92 Mon Sep 17 00:00:00 2001 From: lissine Date: Tue, 12 Aug 2025 09:46:00 +0100 Subject: [PATCH 4/9] Make MLMessage a singleton over the historyID --- Monal/Classes/DataLayer.h | 1 - Monal/Classes/DataLayer.m | 28 ++---- Monal/Classes/MLFiletransfer.m | 4 +- Monal/Classes/MLIQProcessor.m | 3 +- Monal/Classes/MLMessage.h | 1 + Monal/Classes/MLMessage.m | 118 ++++++++++++++++++-------- Monal/Classes/MLMessageProcessor.m | 8 +- Monal/Classes/MLNotificationManager.m | 2 +- Monal/Classes/MLOMEMO.m | 2 +- Monal/Classes/MLXMPPManager.m | 2 +- Monal/Classes/chatViewController.m | 12 ++- 11 files changed, 106 insertions(+), 75 deletions(-) diff --git a/Monal/Classes/DataLayer.h b/Monal/Classes/DataLayer.h index a368c66189..5abf208cb1 100644 --- a/Monal/Classes/DataLayer.h +++ b/Monal/Classes/DataLayer.h @@ -179,7 +179,6 @@ extern NSString* const kMessageTypeFiletransfer; returns messages with the provided local id number */ -(NSArray*) messagesForHistoryIDs:(NSArray*) historyIDs; --(MLMessage* _Nullable) messageForHistoryID:(NSNumber* _Nullable) historyID; -(NSNumber*) getSmallestHistoryId; -(NSNumber*) getBiggestHistoryId; diff --git a/Monal/Classes/DataLayer.m b/Monal/Classes/DataLayer.m index 25bd6034ba..67f86f5655 100644 --- a/Monal/Classes/DataLayer.m +++ b/Monal/Classes/DataLayer.m @@ -283,7 +283,7 @@ -(BOOL) removeAccount:(NSNumber*) accountID // delete transfered files from local device NSArray* messageHistoryIDs = [self.db executeScalarReader:@"SELECT message_history_id FROM message_history WHERE messageType=? AND account_id=?;" andArguments:@[kMessageTypeFiletransfer, accountID]]; for(NSNumber* historyId in messageHistoryIDs) - [MLFiletransfer deleteFileForMessage:[self messageForHistoryID:historyId]]; + [MLFiletransfer deleteFileForMessage:[MLMessage createMessageFromHistoryID:historyId]]; // delete account and all entries with the same account_id (CASCADE DELETE) BOOL accountDeleted = [self.db executeNonQuery:@"DELETE FROM account WHERE account_id=?;" andArguments:@[accountID]]; @@ -1243,16 +1243,6 @@ -(NSString*) getMucTypeOfRoom:(NSString*) room andAccount:(NSNumber*) accountID }]; } --(MLMessage*) messageForHistoryID:(NSNumber*) historyID -{ - if(historyID == nil) - return nil; - NSArray* result = [self messagesForHistoryIDs:@[historyID]]; - if(![result count]) - return nil; - return result[0]; -} - -(NSNumber*) getSmallestHistoryId { return [self.db idReadTransaction:^{ @@ -1475,7 +1465,7 @@ -(void) clearMessages:(NSNumber*) accountID [self.db executeNonQuery:@"PRAGMA secure_delete=on;"]; NSArray* messageHistoryIDs = [self.db executeScalarReader:@"SELECT message_history_id FROM message_history WHERE messageType=? AND account_id=?;" andArguments:@[kMessageTypeFiletransfer, accountID]]; for(NSNumber* historyId in messageHistoryIDs) - [MLFiletransfer deleteFileForMessage:[self messageForHistoryID:historyId]]; + [MLFiletransfer deleteFileForMessage:[MLMessage createMessageFromHistoryID:historyId]]; [self.db executeNonQuery:@"DELETE FROM message_history WHERE account_id=?;" andArguments:@[accountID]]; [self.db executeNonQuery:@"DELETE FROM activechats WHERE account_id=?;" andArguments:@[accountID]]; @@ -1489,7 +1479,7 @@ -(void) clearMessagesWithBuddy:(NSString*) buddy onAccount:(NSNumber*) accountID [self.db executeNonQuery:@"PRAGMA secure_delete=on;"]; NSArray* messageHistoryIDs = [self.db executeScalarReader:@"SELECT message_history_id FROM message_history WHERE messageType=? AND account_id=? AND buddy_name=?;" andArguments:@[kMessageTypeFiletransfer, accountID, buddy]]; for(NSNumber* historyId in messageHistoryIDs) - [MLFiletransfer deleteFileForMessage:[self messageForHistoryID:historyId]]; + [MLFiletransfer deleteFileForMessage:[MLMessage createMessageFromHistoryID:historyId]]; [self.db executeNonQuery:@"DELETE FROM message_history WHERE account_id=? AND buddy_name=?;" andArguments:@[accountID, buddy]]; //better UX without deleting the active chat @@ -1510,7 +1500,7 @@ -(NSNumber*) autoDeleteMessagesAfterInterval:(NSTimeInterval) interval //if they are filetransfers and delete those files NSArray* messageHistoryIDs = [self.db executeScalarReader:@"SELECT message_history_id FROM message_history WHERE (inbound=0 OR unread=0) AND timestamp"]) { DDLogError(@"Moderating message %@ returned an error: %@", msg, [iqNode findFirst:@"error"]); diff --git a/Monal/Classes/MLMessage.h b/Monal/Classes/MLMessage.h index 49ccde79fe..7eb5db091c 100644 --- a/Monal/Classes/MLMessage.h +++ b/Monal/Classes/MLMessage.h @@ -17,6 +17,7 @@ NS_ASSUME_NONNULL_BEGIN */ @interface MLMessage : NSObject ++(MLMessage* _Nullable) createMessageFromHistoryID:(NSNumber* _Nullable) historyID; +(BOOL) supportsSecureCoding; @property (readonly) NSString* id; //for Identifiable protocol diff --git a/Monal/Classes/MLMessage.m b/Monal/Classes/MLMessage.m index dabd9b7ada..82cdde9b6d 100644 --- a/Monal/Classes/MLMessage.m +++ b/Monal/Classes/MLMessage.m @@ -9,52 +9,96 @@ #import #import #import +#import #import +static NSMutableDictionary* _singletonCache; + @implementation MLMessage { MLContact* _contact; } ++(void) initialize +{ + _singletonCache = [NSMutableDictionary new]; +} + ++(MLMessage*) createMessageFromHistoryID:(NSNumber*) historyID +{ + if(historyID == nil) + return nil; + NSArray* result = [[DataLayer sharedInstance] messagesForHistoryIDs:@[historyID]]; + if(![result count]) + return nil; + return result[0]; +} + +(MLMessage*) messageFromDictionary:(NSDictionary*) dic { - MLMessage* message = [MLMessage new]; - message.accountID = [dic objectForKey:@"account_id"]; - - message.buddyName = [dic objectForKey:@"buddy_name"]; - message.inbound = [(NSNumber*)[dic objectForKey:@"inbound"] boolValue]; - message.actualFrom = [dic objectForKey:@"af"]; - message.messageText = [dic objectForKey:@"message"]; - message.isMuc = [(NSNumber*)[dic objectForKey:@"Muc"] boolValue]; - - message.messageId = [dic objectForKey:@"messageid"]; - message.stanzaId = [dic objectForKey:@"stanzaid"]; - message.messageDBId = [dic objectForKey:@"message_history_id"]; - message.timestamp = [dic objectForKey:@"thetime"]; - message.messageType = [dic objectForKey:@"messageType"]; - message.mucType = [dic objectForKey:@"muc_type"]; - message.participantJid = [dic objectForKey:@"participant_jid"]; - - message.hasBeenDisplayed = [(NSNumber*)[dic objectForKey:@"displayed"] boolValue]; - message.hasBeenReceived = [(NSNumber*)[dic objectForKey:@"received"] boolValue]; - message.hasBeenSent = [(NSNumber*)[dic objectForKey:@"sent"] boolValue]; - message.encrypted = [(NSNumber*)[dic objectForKey:@"encrypted"] boolValue]; - - message.unread = [(NSNumber*)[dic objectForKey:@"unread"] boolValue]; - message.displayMarkerWanted = [(NSNumber*)[dic objectForKey:@"displayMarkerWanted"] boolValue]; - - message.previewText = [dic objectForKey:@"previewText"]; - message.previewImage = [NSURL URLWithString:[dic objectForKey:@"previewImage"]]; - - message.errorType = [dic objectForKey:@"errorType"]; - message.errorReason = [dic objectForKey:@"errorReason"]; - - message.filetransferMimeType = [dic objectForKey:@"filetransferMimeType"]; - message.filetransferSize = [dic objectForKey:@"filetransferSize"]; - - message.retracted = [(NSNumber*)[dic objectForKey:@"retracted"] boolValue]; - - return message; + // Draft messages don't have a historyID and shouldn't be cached + if ([[dic objectForKey:@"messageType"] isEqualToString:kMessageTypeMessageDraft]) + { + MLMessage* message = [MLMessage new]; + // Fill only the properties useful for a draft message + // The timestamp is used when rendering MLContactCell, among other things + message.messageText = [dic objectForKey:@"message"]; + message.messageType = [dic objectForKey:@"messageType"]; + message.timestamp = [dic objectForKey:@"thetime"]; + return message; + } + + NSNumber* cacheKey = [dic objectForKey:@"message_history_id"]; + MLAssert(cacheKey != nil, @"A non-draft message can't have a nil historyID!"); + @synchronized(_singletonCache) { + if(_singletonCache[cacheKey] != nil) + { + MLMessage* obj = ((WeakContainer*)_singletonCache[cacheKey]).obj; + if(obj != nil) + return obj; + else + [_singletonCache removeObjectForKey:cacheKey]; + } + + MLMessage* message = [MLMessage new]; + message.accountID = [dic objectForKey:@"account_id"]; + + message.buddyName = [dic objectForKey:@"buddy_name"]; + message.inbound = [(NSNumber*)[dic objectForKey:@"inbound"] boolValue]; + message.actualFrom = [dic objectForKey:@"af"]; + message.messageText = [dic objectForKey:@"message"]; + message.isMuc = [(NSNumber*)[dic objectForKey:@"Muc"] boolValue]; + + message.messageId = [dic objectForKey:@"messageid"]; + message.stanzaId = [dic objectForKey:@"stanzaid"]; + message.messageDBId = [dic objectForKey:@"message_history_id"]; + message.timestamp = [dic objectForKey:@"thetime"]; + message.messageType = [dic objectForKey:@"messageType"]; + message.mucType = [dic objectForKey:@"muc_type"]; + message.participantJid = [dic objectForKey:@"participant_jid"]; + + message.hasBeenDisplayed = [(NSNumber*)[dic objectForKey:@"displayed"] boolValue]; + message.hasBeenReceived = [(NSNumber*)[dic objectForKey:@"received"] boolValue]; + message.hasBeenSent = [(NSNumber*)[dic objectForKey:@"sent"] boolValue]; + message.encrypted = [(NSNumber*)[dic objectForKey:@"encrypted"] boolValue]; + + message.unread = [(NSNumber*)[dic objectForKey:@"unread"] boolValue]; + message.displayMarkerWanted = [(NSNumber*)[dic objectForKey:@"displayMarkerWanted"] boolValue]; + + message.previewText = [dic objectForKey:@"previewText"]; + message.previewImage = [NSURL URLWithString:[dic objectForKey:@"previewImage"]]; + + message.errorType = [dic objectForKey:@"errorType"]; + message.errorReason = [dic objectForKey:@"errorReason"]; + + message.filetransferMimeType = [dic objectForKey:@"filetransferMimeType"]; + message.filetransferSize = [dic objectForKey:@"filetransferSize"]; + + message.retracted = [(NSNumber*)[dic objectForKey:@"retracted"] boolValue]; + + _singletonCache[cacheKey] = [[WeakContainer alloc] initWithObj:message]; + return message; + } } +(BOOL) supportsSecureCoding diff --git a/Monal/Classes/MLMessageProcessor.m b/Monal/Classes/MLMessageProcessor.m index fd1ff21832..0007af5485 100644 --- a/Monal/Classes/MLMessageProcessor.m +++ b/Monal/Classes/MLMessageProcessor.m @@ -483,7 +483,7 @@ +(MLMessage* _Nullable) processMessage:(XMPPMessage*) messageNode andOuterMessag //update ui DDLogInfo(@"Sending out kMonalDeletedMessageNotice notification for historyId %@", historyIdToRetract); [[MLNotificationQueue currentQueue] postNotificationName:kMonalDeletedMessageNotice object:account userInfo:@{ - @"message": [[[DataLayer sharedInstance] messagesForHistoryIDs:@[historyIdToRetract]] firstObject], + @"message": [MLMessage createMessageFromHistoryID:historyIdToRetract], @"contact": possiblyUnknownContact, }]; @@ -532,7 +532,7 @@ +(MLMessage* _Nullable) processMessage:(XMPPMessage*) messageNode andOuterMessag //update ui DDLogInfo(@"Sending out kMonalDeletedMessageNotice notification for historyId %@", historyIdToRetract); [[MLNotificationQueue currentQueue] postNotificationName:kMonalDeletedMessageNotice object:account userInfo:@{ - @"message": [[[DataLayer sharedInstance] messagesForHistoryIDs:@[historyIdToRetract]] firstObject], + @"message": [MLMessage createMessageFromHistoryID:historyIdToRetract], @"historyId": historyIdToRetract, @"contact": possiblyUnknownContact, }]; @@ -642,7 +642,7 @@ +(MLMessage* _Nullable) processMessage:(XMPPMessage*) messageNode andOuterMessag ]; } - message = [[DataLayer sharedInstance] messageForHistoryID:historyId]; + message = [MLMessage createMessageFromHistoryID:historyId]; if(message != nil && historyId != nil) //check historyId to make static analyzer happy { //send receive markers if requested, but DON'T do so for MLhistory messages (and don't do so for channel type mucs) @@ -731,7 +731,7 @@ +(MLMessage* _Nullable) processMessage:(XMPPMessage*) messageNode andOuterMessag NSNumber* historyId = [[DataLayer sharedInstance] hasMessageForStanzaId:stanzaid orMessageID:messageId withInboundDir:inbound occupantId:occupantId andJid:buddyName onAccount:account.accountID]; if(historyId != nil) { - message = [[DataLayer sharedInstance] messageForHistoryID:historyId]; + message = [MLMessage createMessageFromHistoryID:historyId]; DDLogDebug(@"Managed to update stanzaid of message (or stanzaid already known): %@", message); DDLogInfo(@"Sending out kMonalUpdatedMessageNotice notification for historyId %@", historyId); [[MLNotificationQueue currentQueue] postNotificationName:kMonalUpdatedMessageNotice object:account userInfo:@{ diff --git a/Monal/Classes/MLNotificationManager.m b/Monal/Classes/MLNotificationManager.m index dcb70b1188..db12c050a6 100644 --- a/Monal/Classes/MLNotificationManager.m +++ b/Monal/Classes/MLNotificationManager.m @@ -614,7 +614,7 @@ -(void) showModernNotificationForMessage:(MLMessage*) message withSound:(BOOL) s -(void) donateInteractionForOutgoingDBId:(NSNumber*) messageDBId { - MLMessage* message = [[DataLayer sharedInstance] messageForHistoryID:messageDBId]; + MLMessage* message = [MLMessage createMessageFromHistoryID:messageDBId]; INSendMessageIntent* intent = [self makeIntentForMessage:message usingText:@"dummyText" andAudioAttachment:nil direction:INInteractionDirectionOutgoing]; INInteraction* interaction = [[INInteraction alloc] initWithIntent:intent response:nil]; interaction.direction = INInteractionDirectionOutgoing; diff --git a/Monal/Classes/MLOMEMO.m b/Monal/Classes/MLOMEMO.m index 7f4840ac16..5c77484297 100644 --- a/Monal/Classes/MLOMEMO.m +++ b/Monal/Classes/MLOMEMO.m @@ -444,7 +444,7 @@ -(void) postOMEMOMessageForUser:(NSString*) jid withMessage:(NSString*) omemoMes NSString* newMessageID = [[NSUUID UUID] UUIDString]; NSNumber* historyId = [[DataLayer sharedInstance] addMessageHistoryTo:jid forAccount:self.account.accountID withMessage:omemoMessage actuallyFrom:jid withId:newMessageID encrypted:NO messageType:kMessageTypeStatus mimeType:nil size:nil]; - MLMessage* message = [[DataLayer sharedInstance] messageForHistoryID:historyId]; + MLMessage* message = [MLMessage createMessageFromHistoryID:historyId]; MLContact* contact = [MLContact createContactFromJid:jid andAccountID:self.account.accountID]; [[MLNotificationQueue currentQueue] postNotificationName:kMonalNewMessageNotice object:self.account userInfo:@{ @"message": message, diff --git a/Monal/Classes/MLXMPPManager.m b/Monal/Classes/MLXMPPManager.m index 19b217eb7d..75e96d63ac 100644 --- a/Monal/Classes/MLXMPPManager.m +++ b/Monal/Classes/MLXMPPManager.m @@ -711,7 +711,7 @@ -(MLMessage*) sendMessageAndAddToHistory:(NSString*) message havingType:(NSStrin DDLogVerbose(@"Message added to history with id %ld", (long)[messageDBId intValue]); // MLMessage object that will be returned by the method - MLMessage* newMLMessage = [[DataLayer sharedInstance] messageForHistoryID:messageDBId]; + MLMessage* newMLMessage = [MLMessage createMessageFromHistoryID:messageDBId]; if (!newMLMessage) { DDLogError(@"Could not find message for history ID %@!", messageDBId); diff --git a/Monal/Classes/chatViewController.m b/Monal/Classes/chatViewController.m index a25a1329f9..91cce37323 100644 --- a/Monal/Classes/chatViewController.m +++ b/Monal/Classes/chatViewController.m @@ -1629,13 +1629,12 @@ -(MLMessage* _Nullable) addMessageto:(NSString *)to withMessage:(nonnull NSStrin if(messageDBId != nil) { DDLogVerbose(@"added message"); - NSArray* msgList = [[DataLayer sharedInstance] messagesForHistoryIDs:@[messageDBId]]; - if(![msgList count]) + MLMessage* messageObj = [MLMessage createMessageFromHistoryID:messageDBId]; + if(!messageObj) { DDLogError(@"Could not find msg for history ID %@!", messageDBId); return nil; } - MLMessage* messageObj = msgList[0]; [self tempfreezeAutoloading]; @@ -1982,13 +1981,12 @@ -(NSString*) formattedTimeStampWithSource:(NSDate *) sourceDate -(void) retry:(id) sender { NSInteger msgHistoryID = ((UIButton*) sender).tag; - NSArray* msgArray = [[DataLayer sharedInstance] messagesForHistoryIDs:@[[NSNumber numberWithInteger:msgHistoryID]]]; - if(![msgArray count]) + MLMessage* msg = [MLMessage createMessageFromHistoryID:@(msgHistoryID)]; + if(!msg) { DDLogError(@"Called retry for non existing message with history id %ld", (long)msgHistoryID); return; } - MLMessage* msg = msgArray[0]; DDLogDebug(@"Called retry for message with history id %ld: %@", (long)msgHistoryID, msg); UIAlertController* alert = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Retry sending message?", @"") message:[NSString stringWithFormat:NSLocalizedString(@"This message failed to send (%@): %@", @""), msg.errorType, msg.errorReason] preferredStyle:UIAlertControllerStyleActionSheet]; @@ -2513,7 +2511,7 @@ -(UISwipeActionsConfiguration*) tableView:(UITableView*) tableView trailingSwipe { [self.xmppAccount retractMessage:message]; [[DataLayer sharedInstance] retractMessageHistory:message.messageDBId]; - [message updateWithMessage:[[[DataLayer sharedInstance] messagesForHistoryIDs:@[message.messageDBId]] firstObject]]; + [message updateWithMessage:[MLMessage createMessageFromHistoryID:message.messageDBId]]; //update table entry [self->_messageTable beginUpdates]; From 2a9d7b47ab92390d6ed7943c29c177714d937db3 Mon Sep 17 00:00:00 2001 From: lissine Date: Tue, 12 Aug 2025 09:46:16 +0100 Subject: [PATCH 5/9] Remove unused fields from the SQL query returning message drafts --- Monal/Classes/DataLayer.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Monal/Classes/DataLayer.m b/Monal/Classes/DataLayer.m index 67f86f5655..064c1e1f87 100644 --- a/Monal/Classes/DataLayer.m +++ b/Monal/Classes/DataLayer.m @@ -1705,7 +1705,7 @@ -(MLMessage*) lastMessageForContact:(NSString*) contact forAccount:(NSNumber*) a return [self.db idReadTransaction:^{ //return message draft (if any) - NSString* query = @"SELECT bl.messageDraft AS message, ac.lastMessageTime AS thetime, 'MessageDraft' AS messageType, '' AS af, '' AS filetransferMimeType, 0 AS filetransferSize, bl.Muc, bl.muc_type, bl.buddy_name FROM buddylist AS bl INNER JOIN activechats AS ac ON bl.account_id = ac.account_id AND bl.buddy_name = ac.buddy_name WHERE ac.account_id=? AND ac.buddy_name=? AND messageDraft IS NOT NULL AND messageDraft != '';"; + NSString* query = @"SELECT bl.messageDraft AS message, ac.lastMessageTime AS thetime, 'MessageDraft' AS messageType FROM buddylist AS bl INNER JOIN activechats AS ac ON bl.account_id = ac.account_id AND bl.buddy_name = ac.buddy_name WHERE ac.account_id=? AND ac.buddy_name=? AND messageDraft IS NOT NULL AND messageDraft != '';"; NSArray* params = @[accountID, contact]; NSArray* results = [self.db executeReader:query andArguments:params]; if([results count]) From d7dd92861f447e3d1d7f99375a10947807a63330 Mon Sep 17 00:00:00 2001 From: lissine Date: Thu, 15 May 2025 23:43:31 +0100 Subject: [PATCH 6/9] MLMessage: Listen for notifications and update own properties Also, delete MLMessage:updateWithMessage: as it's no longer used --- Monal/Classes/MLFiletransfer.m | 25 +++-- Monal/Classes/MLMessage.h | 1 - Monal/Classes/MLMessage.m | 155 ++++++++++++++++++++++++----- Monal/Classes/MLMessageProcessor.m | 2 + Monal/Classes/chatViewController.m | 49 +-------- 5 files changed, 148 insertions(+), 84 deletions(-) diff --git a/Monal/Classes/MLFiletransfer.m b/Monal/Classes/MLFiletransfer.m index 319412a55a..95a86a41aa 100644 --- a/Monal/Classes/MLFiletransfer.m +++ b/Monal/Classes/MLFiletransfer.m @@ -116,12 +116,14 @@ +(void) checkMimeTypeAndSizeForHistoryID:(NSNumber*) historyId //update db with content type and size [[DataLayer sharedInstance] setMessageHistoryId:historyId filetransferMimeType:mimeType filetransferSize:contentLength]; - //send out update notification (and update used MLMessage object directly instead of reloading it from db after updating the db) - msg.filetransferMimeType = mimeType; - msg.filetransferSize = contentLength; + //send out update notification xmpp* account = [[MLXMPPManager sharedInstance] getEnabledAccountForID:msg.accountID]; if(account != nil) //don't send out update notices for already deleted accounts - [[MLNotificationQueue currentQueue] postNotificationName:kMonalMessageFiletransferUpdateNotice object:account userInfo:@{@"message": msg}]; + [[MLNotificationQueue currentQueue] postNotificationName:kMonalMessageFiletransferUpdateNotice object:account userInfo:@{ + @"message": msg, + @"mimeType": mimeType, + @"filetransferSize": contentLength + }]; else return; //abort here without autodownloading if account was already deleted @@ -274,21 +276,22 @@ +(void) downloadFileForHistoryID:(NSNumber*) historyId andForceDownload:(BOOL) f [HelperTools configureFileProtectionFor:cacheFile]; } - //update MLMessage object with mime type and size - NSNumber* filetransferSize = @([[_fileManager attributesOfItemAtPath:cacheFile error:nil] fileSize]); - msg.filetransferMimeType = mimeType; - msg.filetransferSize = filetransferSize; - //hardlink cache file if possible [self hardlinkFileForMessage:msg]; + NSNumber* filetransferSize = @([[_fileManager attributesOfItemAtPath:cacheFile error:nil] fileSize]); DDLogDebug(@"Updating db and sending out kMonalMessageFiletransferUpdateNotice"); //update db with content type and size [[DataLayer sharedInstance] setMessageHistoryId:historyId filetransferMimeType:mimeType filetransferSize:filetransferSize]; - //send out update notification (using our directly update MLMessage object instead of reloading it from db after updating the db) + //send out update notification xmpp* account = [[MLXMPPManager sharedInstance] getEnabledAccountForID:msg.accountID]; if(account != nil) //don't send out update notices for already deleted accounts - [[MLNotificationQueue currentQueue] postNotificationName:kMonalMessageFiletransferUpdateNotice object:account userInfo:@{@"message": msg}]; + [[MLNotificationQueue currentQueue] postNotificationName:kMonalMessageFiletransferUpdateNotice object:account userInfo:@{ + @"message": msg, + @"mimeType": mimeType, + @"filetransferSize": filetransferSize + }]; + else [_fileManager removeItemAtPath:cacheFile error:nil]; diff --git a/Monal/Classes/MLMessage.h b/Monal/Classes/MLMessage.h index 7eb5db091c..95bb9c8c52 100644 --- a/Monal/Classes/MLMessage.h +++ b/Monal/Classes/MLMessage.h @@ -126,7 +126,6 @@ The of the message in the DB , should be int */ +(MLMessage*) messageFromDictionary:(NSDictionary*) dic; --(void) updateWithMessage:(MLMessage*) msg; @property (nonatomic, readonly) MLContact* contact; -(BOOL) isEqualToContact:(MLContact*) contact; diff --git a/Monal/Classes/MLMessage.m b/Monal/Classes/MLMessage.m index 82cdde9b6d..389a8c77f4 100644 --- a/Monal/Classes/MLMessage.m +++ b/Monal/Classes/MLMessage.m @@ -11,6 +11,7 @@ #import #import #import +#import "XMPPMessage.h" static NSMutableDictionary* _singletonCache; @@ -168,34 +169,134 @@ -(instancetype) initWithCoder:(NSCoder*) coder return self; } --(void) updateWithMessage:(MLMessage*) msg +-(instancetype) init { - self.accountID = msg.accountID; - self.buddyName = msg.buddyName; - self.inbound = msg.inbound; - self.actualFrom = msg.actualFrom; - self.messageText = msg.messageText; - self.isMuc = msg.isMuc; - self.messageId = msg.messageId; - self.stanzaId = msg.stanzaId; - self.messageDBId = msg.messageDBId; - self.timestamp = msg.timestamp; - self.messageType = msg.messageType; - self.mucType = msg.mucType; - self.participantJid = msg.participantJid; - self.hasBeenDisplayed = msg.hasBeenDisplayed; - self.hasBeenReceived = msg.hasBeenReceived; - self.hasBeenSent = msg.hasBeenSent; - self.encrypted = msg.encrypted; - self.unread = msg.unread; - self.displayMarkerWanted = msg.displayMarkerWanted; - self.previewText = msg.previewText; - self.previewImage = msg.previewImage; - self.errorType = msg.errorType; - self.errorReason = msg.errorReason; - self.filetransferMimeType = msg.filetransferMimeType; - self.filetransferSize = msg.filetransferSize; - self.retracted = msg.retracted; + self = [super init]; + //watch for all sorts of changes and update our singleton dynamically + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleMessageUpdate:) name:kMonalUpdatedMessageNotice object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleMessageDeletion:) name:kMonalDeletedMessageNotice object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleFiletransferUpdate:) name:kMonalMessageFiletransferUpdateNotice object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleMessageSent:) name:kMonalSentMessageNotice object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleMessageReceived:) name:kMonalMessageReceivedNotice object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleMessageDisplayed:) name:kMonalMessageDisplayedNotice object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleMessageError:) name:kMonalMessageErrorNotice object:nil]; + return self; +} + +-(void) dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +-(void) handleMessageUpdate:(NSNotification*) notification +{ + NSDictionary* data = notification.userInfo; + MLMessage* message = data[@"message"]; + MLAssert(message != nil, @"Notification without message"); + if(self.messageDBId.integerValue != message.messageDBId.integerValue) + return; //ignore updates of other messages + NSNumber* LMCReplaced = data[@"LMCReplaced"]; + MLAssert(LMCReplaced != nil, @"Message update notification without LMCReplaced object"); + if(LMCReplaced.boolValue) + { + // Message correction + NSString* correctedText = data[@"correctedText"]; + MLAssert(correctedText != nil, @"Message correction notification without the corrected text"); + self.messageText = correctedText; + } + else + { + // MUC reflection + NSString* stanzaId = data[@"stanzaId"]; + MLAssert(stanzaId != nil, @"MUC reflection notification without the new stanzaId"); + self.stanzaId = stanzaId; + } +} + +// Handle message retraction and moderation +-(void) handleMessageDeletion:(NSNotification*) notification +{ + NSDictionary* data = notification.userInfo; + MLMessage* message = data[@"message"]; + MLAssert(message != nil, @"Notification without message"); + if(self.messageDBId.integerValue != message.messageDBId.integerValue) + return; //ignore deletions of other messages + + self.messageText = @""; + self.messageType = kMessageTypeText; + self.filetransferMimeType = @""; + self.filetransferSize = @0; + self.retracted = YES; +} + +-(void) handleFiletransferUpdate:(NSNotification*) notification +{ + NSDictionary* data = notification.userInfo; + MLMessage* message = data[@"message"]; + MLAssert(message != nil, @"Notification without message"); + if(self.messageDBId.integerValue != message.messageDBId.integerValue) + return; //ignore filetransfer updates of other messages + + NSString* mimeType = data[@"mimeType"]; + NSNumber* filetransferSize = data[@"filetransferSize"]; + MLAssert(mimeType != nil && filetransferSize != nil, @"Notification without mimeType and/or filetransferSize"); + self.filetransferMimeType = mimeType; + self.filetransferSize = filetransferSize; +} + +-(void) handleMessageSent:(NSNotification*) notification +{ + NSDictionary* data = notification.userInfo; + XMPPMessage* messageNode = data[@"message"]; + MLAssert(messageNode != nil, @"Notification without message node"); + if([messageNode.id isEqualToString:self.messageId]) + self.hasBeenSent = YES; +} + +-(void) handleMessageReceived:(NSNotification*) notification +{ + NSDictionary* data = notification.userInfo; + NSString* messageId = data[kMessageId]; + NSString* jid = data[@"jid"]; + MLAssert(messageId != nil && jid != nil, @"Notification without jid and/or messageId"); + if([messageId isEqualToString:self.messageId] && [jid isEqualToString:self.buddyName]) + { + self.hasBeenSent = YES; + self.hasBeenReceived = YES; + } +} + +-(void) handleMessageDisplayed:(NSNotification*) notification +{ + NSDictionary* data = notification.userInfo; + MLMessage* message = data[@"message"]; + MLAssert(message != nil, @"Notification without message"); + if(self.messageDBId.integerValue != message.messageDBId.integerValue) + return; //ignore displayed notices of other messages + + self.hasBeenSent = YES; + self.hasBeenReceived = YES; + self.hasBeenDisplayed = YES; +} + +-(void) handleMessageError:(NSNotification*) notification +{ + NSDictionary* data = notification.userInfo; + NSString* jid = data[@"jid"]; + NSString* messageId = data[kMessageId]; + NSString* errorType = data[@"errorType"]; + NSString* errorReason = data[@"errorReason"]; + MLAssert(jid != nil && messageId != nil && errorType != nil && errorReason != nil, @"Notification is missing data"); + + if([messageId isEqualToString:self.messageId] && [jid isEqualToString:self.buddyName]) + { + //we don't want to show errors if the message has been received at least once + if(!self.hasBeenReceived) + { + self.errorType = errorType; + self.errorReason = errorReason; + } + } } -(NSString*) contactDisplayName diff --git a/Monal/Classes/MLMessageProcessor.m b/Monal/Classes/MLMessageProcessor.m index 0007af5485..bb18bb0f51 100644 --- a/Monal/Classes/MLMessageProcessor.m +++ b/Monal/Classes/MLMessageProcessor.m @@ -706,6 +706,7 @@ +(MLMessage* _Nullable) processMessage:(XMPPMessage*) messageNode andOuterMessag @"showAlert": @(showAlert), @"contact": possiblyUnknownContact, @"LMCReplaced": @YES, + @"correctedText": body, }]; } else @@ -739,6 +740,7 @@ +(MLMessage* _Nullable) processMessage:(XMPPMessage*) messageNode andOuterMessag @"showAlert": @(NO), @"contact": possiblyUnknownContact, @"LMCReplaced": @NO, + @"stanzaId": stanzaid, }]; } } diff --git a/Monal/Classes/chatViewController.m b/Monal/Classes/chatViewController.m index 91cce37323..6e629f33f5 100644 --- a/Monal/Classes/chatViewController.m +++ b/Monal/Classes/chatViewController.m @@ -1698,9 +1698,6 @@ -(void) handleNewMessage:(NSNotification *)notification MLMessage* msgInList = [self.messageList objectAtIndex:(msgIdx - 1)]; if([msgInList.messageDBId intValue] == [message.messageDBId intValue]) { - //update message in our list - [msgInList updateWithMessage:message]; - //update table entry NSIndexPath* indexPath = [NSIndexPath indexPathForRow:(msgIdx - 1) inSection:messagesSection]; dispatch_async(dispatch_get_main_queue(), ^{ @@ -1755,9 +1752,6 @@ -(void) handleDeletedMessage:(NSNotification*) notification MLMessage* msgInList = [self.messageList objectAtIndex:(msgIdx - 1)]; if([msgInList.messageDBId intValue] == [msg.messageDBId intValue]) { - //update message in our list - [msgInList updateWithMessage:msg]; - //update table entry NSIndexPath* indexPath = [NSIndexPath indexPathForRow:(msgIdx - 1) inSection:messagesSection]; dispatch_async(dispatch_get_main_queue(), ^{ @@ -1779,33 +1773,6 @@ -(void) updateMsgState:(NSString *) messageId withEvent:(size_t) event withOptDi MLMessage* msg = [self.messageList objectAtIndex:(msgIdx - 1)]; if([msg.messageId isEqualToString:messageId]) { - // Set correct flags - if(event == msgSent) { - DDLogVerbose(@"got msgSent event for messageid: %@", messageId); - msg.hasBeenSent = YES; - } else if(event == msgRecevied) { - DDLogVerbose(@"got msgRecevied event for messageid: %@", messageId); - msg.hasBeenSent = YES; - msg.hasBeenReceived = YES; - } else if(event == msgDisplayed) { - DDLogVerbose(@"got msgDisplayed event for messageid: %@", messageId); - msg.hasBeenSent = YES; - msg.hasBeenReceived = YES; - msg.hasBeenDisplayed = YES; - } else if(event == msgErrorAfterSent) { - DDLogVerbose(@"got msgErrorAfterSent event for messageid: %@", messageId); - //we don't want to show errors if the message has been received at least once - if(!msg.hasBeenReceived) - { - msg.errorType = [dic objectForKey:@"errorType"]; - msg.errorReason = [dic objectForKey:@"errorReason"]; - - //ping muc to self-heal cases where we aren't joined anymore without noticing it - if(self.contact.isMuc) - [self.xmppAccount.mucProcessor ping:self.contact.contactJid]; - } - } - indexPath = [NSIndexPath indexPathForRow:(msgIdx - 1) inSection:messagesSection]; //update table entry @@ -1862,9 +1829,6 @@ -(void) handleFiletransferMessageUpdate:(NSNotification*) notification MLMessage* msgInList = [self.messageList objectAtIndex:(msgIdx - 1)]; if([msgInList.messageDBId intValue] == [msg.messageDBId intValue]) { - //update message in our list (this will copy filetransferMimeType and filetransferSize fields) - [msgInList updateWithMessage:msg]; - //update table entry indexPath = [NSIndexPath indexPathForRow:(msgIdx - 1) inSection:messagesSection]; dispatch_async(dispatch_get_main_queue(), ^{ @@ -2511,15 +2475,10 @@ -(UISwipeActionsConfiguration*) tableView:(UITableView*) tableView trailingSwipe { [self.xmppAccount retractMessage:message]; [[DataLayer sharedInstance] retractMessageHistory:message.messageDBId]; - [message updateWithMessage:[MLMessage createMessageFromHistoryID:message.messageDBId]]; - - //update table entry - [self->_messageTable beginUpdates]; - [self->_messageTable reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; - [self->_messageTable endUpdates]; - - //update active chats if necessary - [[MLNotificationQueue currentQueue] postNotificationName:kMonalContactRefresh object:self.xmppAccount userInfo:@{@"contact": self.contact}]; + [[MLNotificationQueue currentQueue] postNotificationName:kMonalDeletedMessageNotice object:self.xmppAccount userInfo:@{ + @"message": message, + @"contact": self.contact + }]; } else { From e9957f7cf4acc366338dbf98f90650faddc46a3a Mon Sep 17 00:00:00 2001 From: lissine Date: Sat, 9 Aug 2025 22:40:04 +0100 Subject: [PATCH 7/9] Bump SwiftPM dependencies --- .../xcshareddata/swiftpm/Package.resolved | 50 ++++++------------- 1 file changed, 16 insertions(+), 34 deletions(-) diff --git a/Monal/Monal.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Monal/Monal.xcworkspace/xcshareddata/swiftpm/Package.resolved index b49ff1c024..23484658b9 100644 --- a/Monal/Monal.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Monal/Monal.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -6,8 +6,8 @@ "repositoryURL": "https://github.com/exyte/ActivityIndicatorView", "state": { "branch": null, - "revision": "9970fd0bb7a05dad0b6566ae1f56937716686b24", - "version": "1.1.1" + "revision": "36140867802ae4a1d2b11490bcbbefe058001d14", + "version": "1.2.1" } }, { @@ -15,8 +15,8 @@ "repositoryURL": "https://github.com/cocoalumberjack/cocoalumberjack", "state": { "branch": null, - "revision": "4b8714a7fb84d42393314ce897127b3939885ec3", - "version": "3.8.5" + "revision": "a9ed4b6f9bdedce7d77046f43adfb8ce1fd54114", + "version": "3.9.0" } }, { @@ -24,35 +24,26 @@ "repositoryURL": "https://github.com/monal-im/ExyteChat", "state": { "branch": "main", - "revision": "b52d2cb406add00fdc482930122d0a64bb833469", + "revision": "120f7d960e67d36f7f411a03cca9ad5344c26021", "version": null } }, - { - "package": "FloatingButton", - "repositoryURL": "https://github.com/exyte/FloatingButton", - "state": { - "branch": null, - "revision": "cf77c2f124df1423d90a9a1985e9b9ccfa4b9b3e", - "version": "1.3.0" - } - }, { "package": "FrameUp", "repositoryURL": "https://github.com/ryanlintott/FrameUp", "state": { "branch": null, - "revision": "3b46ddcfc02a85ed060bb0c9e7ab14a7f90f415a", - "version": "0.9.7" + "revision": "0fccfdc93a01c8555cec86bf2c64caee30c23704", + "version": "0.9.11" } }, { "package": "ExyteMediaPicker", - "repositoryURL": "https://github.com/exyte/MediaPicker.git", + "repositoryURL": "https://github.com/monal-im/MediaPicker.git", "state": { - "branch": null, - "revision": "88769b1b69c2b5e5fa5b65522c08bc7b667a6cb8", - "version": "2.2.3" + "branch": "main", + "revision": "81ec10c7f9cd32d296fdc42c376c0f252925c719", + "version": null } }, { @@ -69,8 +60,8 @@ "repositoryURL": "https://github.com/apple/swift-collections.git", "state": { "branch": null, - "revision": "671108c96644956dddcd89dd59c203dcdb36cec7", - "version": "1.1.4" + "revision": "8c0c0a8b49e080e54e5e328cc552821ff07cd341", + "version": "1.2.1" } }, { @@ -78,17 +69,8 @@ "repositoryURL": "https://github.com/apple/swift-log", "state": { "branch": null, - "revision": "96a2f8a0fa41e9e09af4585e2724c4e825410b91", - "version": "1.6.2" - } - }, - { - "package": "swiftui-introspect", - "repositoryURL": "https://github.com/siteline/swiftui-introspect", - "state": { - "branch": null, - "revision": "807f73ce09a9b9723f12385e592b4e0aaebd3336", - "version": "1.3.0" + "revision": "ce592ae52f982c847a4efc0dd881cc9eb32d29f2", + "version": "1.6.4" } }, { @@ -114,7 +96,7 @@ "repositoryURL": "https://github.com/Stasel/WebRTC", "state": { "branch": "latest", - "revision": "b85669f32ffb3f48ce3a8f18ad828c6f559a8a0c", + "revision": "820d216010b6bb5695139852ec6740edd6f790ff", "version": null } } From 383672472c4bc5ac3b90bc44239171e96fbd7b98 Mon Sep 17 00:00:00 2001 From: lissine Date: Wed, 23 Jul 2025 18:24:32 +0100 Subject: [PATCH 8/9] Extend the default ExyteChat MessageView with an MLMessage @StateObject Thanks to ObservableKVOWrapper, and to MLMessage being a model class, this gives us automatic view updates when an MLMessage object changes, while still using the default MessageView. --- Monal/Classes/ChatView.swift | 43 +++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/Monal/Classes/ChatView.swift b/Monal/Classes/ChatView.swift index f9db5fe7eb..bdb93ee7d5 100644 --- a/Monal/Classes/ChatView.swift +++ b/Monal/Classes/ChatView.swift @@ -200,7 +200,9 @@ struct ChatView: View { guard let newMLMessage = MLXMPPManager.sharedInstance().sendMessageAndAddToHistory(message: draft.text, havingType: kMessageTypeText, toContact: self.contact.obj, isEncrypted: self.contact.isEncrypted, uploadInfo: nil) else { return } - messages.append(ChatViewMessage(newMLMessage)) + messages.append(ChatViewMessage(ObservableKVOWrapper(newMLMessage))) + } messageBuilder: { message, viewModel, positionInUserGroup, positionInMessagesSection, positionInCommentsGroup, showContextMenuClosure, messageActionClosure, showAttachmentClosure in + MessageView(message: (message as! ChatViewMessage).message, viewModel: viewModel, positionInUserGroup: positionInUserGroup, positionInMessagesSection: positionInMessagesSection) } .showNetworkConnectionProblem(false) // .enableLoadMore(pageSize: 3) { message in @@ -350,7 +352,7 @@ struct ChatView: View { if messages.isEmpty { let dbMessages = DataLayer.sharedInstance().messages(forContact:contact.contactJid, forAccount:contact.accountID) as! [MLMessage] for msg in dbMessages { - messages.append(ChatViewMessage(msg)) + messages.append(ChatViewMessage(ObservableKVOWrapper(msg))) } } ChatViewHelpers.refreshCounter(for: self.contact.obj) @@ -408,7 +410,7 @@ struct ChatView: View { if message.isEqual(to: self.contact.obj) { // Do not insert based on delay timestamp because that // would make it possible to fake history entries. - messages.append(ChatViewMessage(message)) + messages.append(ChatViewMessage(ObservableKVOWrapper(message))) } ChatViewHelpers.refreshCounter(for: self.contact.obj) } @@ -419,9 +421,9 @@ struct ChatView: View { } class ChatViewMessage: ExyteChat.Message { - @Published public var message: MLMessage - - init(_ message: MLMessage) { + let message: ObservableKVOWrapper + + init(_ message: ObservableKVOWrapper) { self.message = message let user = ExyteChat.User(id: message.senderID, name: message.contactDisplayName, avatarURL: nil, isCurrentUser: !message.inbound) super.init(id: message.id, user: user, createdAt: message.timestamp, text: message.messageText) @@ -481,3 +483,32 @@ public extension ExyteChat.MessageView { // } } }*/ +struct MessageView: View { + @StateObject var message: ObservableKVOWrapper + @ObservedObject var viewModel: ExyteChat.ChatViewModel + let positionInUserGroup: PositionInUserGroup + let positionInMessagesSection: PositionInMessagesSection + init(message: ObservableKVOWrapper, viewModel: ChatViewModel, positionInUserGroup: PositionInUserGroup, positionInMessagesSection: PositionInMessagesSection) { + _message = StateObject(wrappedValue: message) + self.viewModel = viewModel + self.positionInUserGroup = positionInUserGroup + self.positionInMessagesSection = positionInMessagesSection + } + var body: some View { + ExyteChat.MessageView( + viewModel: viewModel, + message: ChatViewMessage(message), + positionInUserGroup: positionInUserGroup, + positionInMessagesSection: positionInMessagesSection, + chatType: .conversation, + avatarSize: 32, + tapAvatarClosure: nil, + messageStyler: AttributedString.init, + shouldShowLinkPreview: { _ in true }, + isDisplayingMessageMenu: false, + showMessageTimeView: true, + messageLinkPreviewLimit: 8, + font: UIFontMetrics.default.scaledFont(for: UIFont.systemFont(ofSize: 15)) + ) + } +} From f1bb841002e4223e5b7f52af7344e27b5966939c Mon Sep 17 00:00:00 2001 From: lissine Date: Sun, 10 Aug 2025 02:39:01 +0100 Subject: [PATCH 9/9] New ChatView: Display "This message got retracted" for retracted messages --- Monal/Classes/ChatView.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Monal/Classes/ChatView.swift b/Monal/Classes/ChatView.swift index bdb93ee7d5..667b0aa3c9 100644 --- a/Monal/Classes/ChatView.swift +++ b/Monal/Classes/ChatView.swift @@ -425,8 +425,9 @@ class ChatViewMessage: ExyteChat.Message { init(_ message: ObservableKVOWrapper) { self.message = message + let messageText = message.retracted ? NSLocalizedString("This message got retracted", comment: "") : message.messageText let user = ExyteChat.User(id: message.senderID, name: message.contactDisplayName, avatarURL: nil, isCurrentUser: !message.inbound) - super.init(id: message.id, user: user, createdAt: message.timestamp, text: message.messageText) + super.init(id: message.id, user: user, createdAt: message.timestamp, text: messageText) } }