From 2109f1a93268237343346efedd77b8d0697dc7db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Sat, 30 Nov 2024 20:32:05 +0100 Subject: [PATCH 01/12] Don't add update messages to the table view MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- NextcloudTalk/BaseChatViewController.swift | 15 ++++++--------- NextcloudTalk/ChatViewController.swift | 19 ++++++++++++------- NextcloudTalk/SystemMessageTableViewCell.h | 1 - NextcloudTalk/SystemMessageTableViewCell.m | 6 +----- .../Chat/UnitBaseChatViewControllerTest.swift | 11 ----------- 5 files changed, 19 insertions(+), 33 deletions(-) diff --git a/NextcloudTalk/BaseChatViewController.swift b/NextcloudTalk/BaseChatViewController.swift index e028eface..51cabf970 100644 --- a/NextcloudTalk/BaseChatViewController.swift +++ b/NextcloudTalk/BaseChatViewController.swift @@ -271,7 +271,6 @@ import SwiftUI self.tableView?.register(UINib(nibName: "BaseChatTableViewCell", bundle: nil), forCellReuseIdentifier: pollGroupedMessageCellIdentifier) self.tableView?.register(SystemMessageTableViewCell.self, forCellReuseIdentifier: SystemMessageCellIdentifier) - self.tableView?.register(SystemMessageTableViewCell.self, forCellReuseIdentifier: InvisibleSystemMessageCellIdentifier) self.tableView?.register(MessageSeparatorTableViewCell.self, forCellReuseIdentifier: MessageSeparatorTableViewCell.identifier) let newMessagesButtonText = NSLocalizedString("↓ New messages", comment: "") @@ -2171,6 +2170,10 @@ import SwiftUI private func internalAppendMessages(messages: [NCChatMessage], inDictionary dictionary: inout [Date: [NCChatMessage]]) { for newMessage in messages { + // Skip any update message, as that would still trigger some operations on the UITableView. + // Processing of update messages still happens when receiving new messages, so safe to skip here + guard !newMessage.isUpdateMessage else { continue } + let newMessageDate = Date(timeIntervalSince1970: TimeInterval(newMessage.timestamp)) let keyDate = self.getKeyForDate(date: newMessageDate, inDictionary: dictionary) @@ -2749,12 +2752,6 @@ import SwiftUI return cell } - if message.isUpdateMessage, - let cell = self.tableView?.dequeueReusableCell(withIdentifier: InvisibleSystemMessageCellIdentifier) as? SystemMessageTableViewCell { - - return cell - } - if message.isSystemMessage, let cell = self.tableView?.dequeueReusableCell(withIdentifier: SystemMessageCellIdentifier) as? SystemMessageTableViewCell { @@ -2874,8 +2871,8 @@ import SwiftUI return size.height } - // Update messages (the ones that notify about an update in one message, they should not be displayed) - if message.message.isEmpty || message.isUpdateMessage || (message.isCollapsed && message.collapsedBy != nil) { + // Empty or collapsed system messages should not be displayed + if message.message.isEmpty || (message.isCollapsed && message.collapsedBy != nil) { return 0.0 } diff --git a/NextcloudTalk/ChatViewController.swift b/NextcloudTalk/ChatViewController.swift index fae280f00..56a6db921 100644 --- a/NextcloudTalk/ChatViewController.swift +++ b/NextcloudTalk/ChatViewController.swift @@ -1031,6 +1031,18 @@ import SwiftyAttributes self.appendMessages(messages: messages) for newMessage in messages { + // Update messages might trigger an reload of another cell, but are not part of the tableView itself + if newMessage.isUpdateMessage { + if let parentMessage = newMessage.parent, let parentPath = self.indexPath(for: parentMessage) { + if parentPath.section < tableView.numberOfSections, parentPath.row < tableView.numberOfRows(inSection: parentPath.section) { + // We received an update message to a message which is already part of our current data, therefore we need to reload it + reloadIndexPaths.insert(parentPath) + } + } + + continue + } + // If we don't get an indexPath here, something is wrong with our appendMessages function let indexPath = self.indexPath(for: newMessage)! @@ -1047,13 +1059,6 @@ import SwiftyAttributes insertIndexPaths.insert(indexPath) } - if newMessage.isUpdateMessage, let parentMessage = newMessage.parent, let parentPath = self.indexPath(for: parentMessage) { - if parentPath.section < tableView.numberOfSections, parentPath.row < tableView.numberOfRows(inSection: parentPath.section) { - // We received an update message to a message which is already part of our current data, therefore we need to reload it - reloadIndexPaths.insert(parentPath) - } - } - if let collapsedByMessage = newMessage.collapsedBy, let collapsedPath = self.indexPath(for: collapsedByMessage) { if collapsedPath.section < tableView.numberOfSections, collapsedPath.row < tableView.numberOfRows(inSection: collapsedPath.section) { // The current message is collapsed, so we need to make sure that the collapsedBy message is reloaded diff --git a/NextcloudTalk/SystemMessageTableViewCell.h b/NextcloudTalk/SystemMessageTableViewCell.h index 1f6e29092..0a222c714 100644 --- a/NextcloudTalk/SystemMessageTableViewCell.h +++ b/NextcloudTalk/SystemMessageTableViewCell.h @@ -12,7 +12,6 @@ static CGFloat kSystemMessageCellMinimumHeight = 30.0; static NSString *SystemMessageCellIdentifier = @"SystemMessageCellIdentifier"; -static NSString *InvisibleSystemMessageCellIdentifier = @"InvisibleSystemMessageCellIdentifier"; @protocol SystemMessageTableViewCellDelegate diff --git a/NextcloudTalk/SystemMessageTableViewCell.m b/NextcloudTalk/SystemMessageTableViewCell.m index 6da2fe5b2..45fd22b20 100644 --- a/NextcloudTalk/SystemMessageTableViewCell.m +++ b/NextcloudTalk/SystemMessageTableViewCell.m @@ -27,10 +27,6 @@ - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSStr - (void)configureSubviews { - if ([self.reuseIdentifier isEqualToString:InvisibleSystemMessageCellIdentifier]) { - return; - } - [self.contentView addSubview:self.dateLabel]; [self.contentView addSubview:self.bodyTextView]; [self.contentView addSubview:self.collapseButton]; @@ -58,7 +54,7 @@ - (void)prepareForReuse [super prepareForReuse]; if (!self.didCreateSubviews) { - [self configureSubviews]; + return; } self.selectionStyle = UITableViewCellSelectionStyleNone; diff --git a/NextcloudTalkTests/Unit/Chat/UnitBaseChatViewControllerTest.swift b/NextcloudTalkTests/Unit/Chat/UnitBaseChatViewControllerTest.swift index f0bfeb8c5..18f913435 100644 --- a/NextcloudTalkTests/Unit/Chat/UnitBaseChatViewControllerTest.swift +++ b/NextcloudTalkTests/Unit/Chat/UnitBaseChatViewControllerTest.swift @@ -42,17 +42,6 @@ final class UnitBaseChatViewControllerTest: TestBaseRealm { testMessage = NCChatMessage(dictionary: [:], andAccountId: UnitBaseChatViewControllerTest.fakeAccountId) } - func testInvisibleCellHeight() throws { - // Empty message should have a height of 0 - testMessage.message = "" - XCTAssertEqual(baseController.getCellHeight(for: testMessage, with: 300), 0.0) - - // Test update message - testMessage.message = "System Message" - testMessage.systemMessage = "message_deleted" - XCTAssertEqual(baseController.getCellHeight(for: testMessage, with: 300), 0.0) - } - func testSystemMessageCellHeight() throws { testMessage.message = "System Message" testMessage.systemMessage = "test_system_message" From 962b8052179d294049297efc32e78ff77ece3fcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Sun, 1 Dec 2024 00:50:49 +0100 Subject: [PATCH 02/12] Cache parsed markdown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- NextcloudTalk/NCChatMessage.m | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/NextcloudTalk/NCChatMessage.m b/NextcloudTalk/NCChatMessage.m index 449b1c1f9..998f186d0 100644 --- a/NextcloudTalk/NCChatMessage.m +++ b/NextcloudTalk/NCChatMessage.m @@ -39,6 +39,7 @@ @interface NCChatMessage () NSString *_urlDetected; BOOL _referenceDataDone; NSDictionary *_referenceData; + NSMutableAttributedString *_parsedMarkdownForChat; } @end @@ -398,6 +399,10 @@ - (NSMutableAttributedString *)parsedMarkdown - (NSMutableAttributedString *)parsedMarkdownForChat { + if (_parsedMarkdownForChat) { + return _parsedMarkdownForChat; + } + // In some circumstances we want/need to hide the message in the chat, but still want to show it in other parts like the conversation list if ([self getDeckCardUrlForReferenceProvider]) { return nil; @@ -413,7 +418,9 @@ - (NSMutableAttributedString *)parsedMarkdownForChat return parsedMessage; } - return [SwiftMarkdownObjCBridge parseMarkdownWithMarkdownString:parsedMessage]; + _parsedMarkdownForChat = [SwiftMarkdownObjCBridge parseMarkdownWithMarkdownString:parsedMessage]; + + return _parsedMarkdownForChat; } - (NSMutableArray *)temporaryReactions From bd33e9441d31775096327cf5dcd5f528eb4ad57c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Sun, 1 Dec 2024 00:51:51 +0100 Subject: [PATCH 03/12] Simplify prepareForReuse MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- .../BaseChatTableViewCell+Message.swift | 4 -- .../BaseChatTableViewCell+Poll.swift | 4 -- NextcloudTalk/BaseChatTableViewCell.swift | 37 +++++++++++-------- NextcloudTalk/BaseChatTableViewCell.xib | 14 ++----- 4 files changed, 24 insertions(+), 35 deletions(-) diff --git a/NextcloudTalk/BaseChatTableViewCell+Message.swift b/NextcloudTalk/BaseChatTableViewCell+Message.swift index 5faa46f11..b625c9af4 100644 --- a/NextcloudTalk/BaseChatTableViewCell+Message.swift +++ b/NextcloudTalk/BaseChatTableViewCell+Message.swift @@ -26,8 +26,4 @@ extension BaseChatTableViewCell { messageTextView.attributedText = message.parsedMarkdownForChat() } - - func prepareForReuseMessageCell() { - self.messageTextView?.text = "" - } } diff --git a/NextcloudTalk/BaseChatTableViewCell+Poll.swift b/NextcloudTalk/BaseChatTableViewCell+Poll.swift index 33ba899fd..68c7b6159 100644 --- a/NextcloudTalk/BaseChatTableViewCell+Poll.swift +++ b/NextcloudTalk/BaseChatTableViewCell+Poll.swift @@ -37,10 +37,6 @@ extension BaseChatTableViewCell { pollMessageView.pollTitleTextView.text = message.parsedMessage().string } - func prepareForReusePollCell() { - self.pollMessageView?.pollTitleTextView.text = "" - } - @objc func pollViewTapped() { guard let poll = message?.poll else { return diff --git a/NextcloudTalk/BaseChatTableViewCell.swift b/NextcloudTalk/BaseChatTableViewCell.swift index cfbf3eeb3..bc4e735c2 100644 --- a/NextcloudTalk/BaseChatTableViewCell.swift +++ b/NextcloudTalk/BaseChatTableViewCell.swift @@ -131,24 +131,11 @@ class BaseChatTableViewCell: UITableViewCell, AudioPlayerViewDelegate, Reactions self.quotedMessageView?.avatarView.cancelCurrentRequest() self.quotedMessageView?.avatarView.image = nil - self.titleLabel.text = "" - self.dateLabel.text = "" - - self.headerPart.isHidden = false - self.quotePart.isHidden = true - self.referencePart.isHidden = true - self.reactionPart.isHidden = true - - self.statusView.isHidden = false - self.statusView.subviews.forEach { $0.removeFromSuperview() } - self.referenceView?.prepareForReuse() - self.prepareForReuseMessageCell() self.prepareForReuseFileCell() self.prepareForReuseLocationCell() self.prepareForReuseAudioCell() - self.prepareForReusePollCell() if let replyGestureRecognizer { self.removeGestureRecognizer(replyGestureRecognizer) @@ -208,13 +195,15 @@ class BaseChatTableViewCell: UITableViewCell, AudioPlayerViewDelegate, Reactions self.quotedMessageView?.actorLabel.attributedText = parent.actor.attributedDisplayName self.quotedMessageView?.highlighted = parent.isMessage(from: account.userId) self.quotedMessageView?.avatarView.setActorAvatar(forMessage: parent) + } else { + self.hideQuotePart() } if message.isGroupMessage, message.parent == nil { self.headerPart.isHidden = true } - // When `setDeliveryState` is not called, we still need to make sure the placeholder view is removed + // Make sure the status view is empty, when no delivery state should be set self.statusView.subviews.forEach { $0.removeFromSuperview() } if message.isDeleting { @@ -238,6 +227,8 @@ class BaseChatTableViewCell: UITableViewCell, AudioPlayerViewDelegate, Reactions if !reactionsArray.isEmpty { self.showReactionsPart() self.reactionView?.updateReactions(reactions: reactionsArray) + } else { + self.hideReactionsPart() } if message.containsURL() { @@ -257,6 +248,8 @@ class BaseChatTableViewCell: UITableViewCell, AudioPlayerViewDelegate, Reactions self.referenceView?.update(for: referenceData, and: url) } } + } else { + self.hideReferencePart() } if message.isReplyable, !message.isDeleting { @@ -283,6 +276,8 @@ class BaseChatTableViewCell: UITableViewCell, AudioPlayerViewDelegate, Reactions if message.isDeletedMessage { self.statusView.isHidden = true self.messageTextView?.textColor = .tertiaryLabel + } else { + self.statusView.isHidden = false } NotificationCenter.default.addObserver(self, selector: #selector(didChangeIsDownloading(notification:)), name: NSNotification.Name.NCChatFileControllerDidChangeIsDownloading, object: nil) @@ -320,8 +315,6 @@ class BaseChatTableViewCell: UITableViewCell, AudioPlayerViewDelegate, Reactions } func setDeliveryState(to deliveryState: ChatMessageDeliveryState) { - self.statusView.subviews.forEach { $0.removeFromSuperview() } - if deliveryState == .sending || deliveryState == .deleting { let activityIndicator = MDCActivityIndicator(frame: .init(x: 0, y: 0, width: 20, height: 20)) @@ -398,6 +391,10 @@ class BaseChatTableViewCell: UITableViewCell, AudioPlayerViewDelegate, Reactions } } + func hideQuotePart() { + self.quotePart.isHidden = true + } + @objc func quoteTapped(_ sender: UITapGestureRecognizer?) { if let parent = self.message?.parent { self.delegate?.cellWantsToScroll(to: parent) @@ -426,6 +423,10 @@ class BaseChatTableViewCell: UITableViewCell, AudioPlayerViewDelegate, Reactions } } + func hideReferencePart() { + self.referencePart.isHidden = true + } + // MARK: - ReactionsPart func showReactionsPart() { @@ -452,6 +453,10 @@ class BaseChatTableViewCell: UITableViewCell, AudioPlayerViewDelegate, Reactions } } + func hideReactionsPart() { + self.reactionPart.isHidden = true + } + // MARK: - ReactionsView Delegate func didSelectReaction(reaction: NCChatReaction) { diff --git a/NextcloudTalk/BaseChatTableViewCell.xib b/NextcloudTalk/BaseChatTableViewCell.xib index 41f41f612..0ab6555cc 100644 --- a/NextcloudTalk/BaseChatTableViewCell.xib +++ b/NextcloudTalk/BaseChatTableViewCell.xib @@ -1,9 +1,9 @@ - + - + @@ -70,16 +70,8 @@ - + - - - - - - - - From 432dddc51ea1679b8ab89dab87b54ec958da071a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Sun, 1 Dec 2024 11:42:42 +0100 Subject: [PATCH 04/12] Add AvatarProtocol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- NextcloudTalk.xcodeproj/project.pbxproj | 8 +++++++- NextcloudTalk/AvatarButton.swift | 5 ++--- NextcloudTalk/AvatarImageView.swift | 5 ++--- NextcloudTalk/AvatarProtocol.swift | 18 ++++++++++++++++++ NextcloudTalk/AvatarView.swift | 10 +++++++--- 5 files changed, 36 insertions(+), 10 deletions(-) create mode 100644 NextcloudTalk/AvatarProtocol.swift diff --git a/NextcloudTalk.xcodeproj/project.pbxproj b/NextcloudTalk.xcodeproj/project.pbxproj index 6a1cdb4f9..82f198eed 100644 --- a/NextcloudTalk.xcodeproj/project.pbxproj +++ b/NextcloudTalk.xcodeproj/project.pbxproj @@ -67,6 +67,8 @@ 1F205C532CEF91C500AAA673 /* UserAbsence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F205C4F2CEF903000AAA673 /* UserAbsence.swift */; }; 1F205C552CEFA01200AAA673 /* OutOfOfficeView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1F205C542CEFA01200AAA673 /* OutOfOfficeView.xib */; }; 1F205C572CEFA01900AAA673 /* OutOfOfficeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F205C562CEFA01900AAA673 /* OutOfOfficeView.swift */; }; + 1F205D412CFC6DD300AAA673 /* AvatarProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F205D402CFC6DCF00AAA673 /* AvatarProtocol.swift */; }; + 1F205D442CFC70AD00AAA673 /* AvatarProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F205D402CFC6DCF00AAA673 /* AvatarProtocol.swift */; }; 1F24B5A228E0648600654457 /* ReferenceGithubView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F24B5A128E0648600654457 /* ReferenceGithubView.swift */; }; 1F24B5A428E0649200654457 /* ReferenceGithubView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1F24B5A328E0649200654457 /* ReferenceGithubView.xib */; }; 1F35F8E22AEEBAF900044BDA /* InputbarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F5A24322ADA77DA009939FE /* InputbarViewController.swift */; }; @@ -691,6 +693,7 @@ 1F205C4F2CEF903000AAA673 /* UserAbsence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAbsence.swift; sourceTree = ""; }; 1F205C542CEFA01200AAA673 /* OutOfOfficeView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = OutOfOfficeView.xib; sourceTree = ""; }; 1F205C562CEFA01900AAA673 /* OutOfOfficeView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutOfOfficeView.swift; sourceTree = ""; }; + 1F205D402CFC6DCF00AAA673 /* AvatarProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvatarProtocol.swift; sourceTree = ""; }; 1F21A0622C77863500ED8C0C /* nb-NO */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "nb-NO"; path = "nb-NO.lproj/InfoPlist.strings"; sourceTree = ""; }; 1F21A0632C77863500ED8C0C /* nb-NO */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "nb-NO"; path = "nb-NO.lproj/Localizable.strings"; sourceTree = ""; }; 1F21A0642C77863500ED8C0C /* nb-NO */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "nb-NO"; path = "nb-NO.lproj/Localizable.stringsdict"; sourceTree = ""; }; @@ -1683,9 +1686,10 @@ 1F1C0D8829AFB89900D17C6D /* VLCKitVideoViewController.swift */, 1F1C0D8629AFB88800D17C6D /* VLCKitVideoViewController.xib */, 1F90DA0329E9A28E00E81E3D /* AvatarManager.swift */, + 1F205D402CFC6DCF00AAA673 /* AvatarProtocol.swift */, + 1FDCC3EF29ECB4CE00DEB39B /* AvatarButton.swift */, 1FDCC3D329EBF6E700DEB39B /* AvatarImageView.swift */, 2CF338E02CED388B0029CACC /* AvatarView.swift */, - 1FDCC3EF29ECB4CE00DEB39B /* AvatarButton.swift */, 1F3C41A229EDF05700F58435 /* AvatarEditView.swift */, 1F3C41A429EDF0B800F58435 /* AvatarEditView.xib */, 2CB997C32A052449003C41AC /* EmojiAvatarPickerViewController.swift */, @@ -2860,6 +2864,7 @@ 1FEDE3C6257D439500853F79 /* NCChatFileController.m in Sources */, 1FEDE3CE257D43AB00853F79 /* NCMessageFileParameter.m in Sources */, 2C4446DD2658158000DF1DBC /* NCChatBlock.m in Sources */, + 1F205D412CFC6DD300AAA673 /* AvatarProtocol.swift in Sources */, 1FDCC3F029ECB4CE00DEB39B /* AvatarButton.swift in Sources */, 2C06BF6720AC647A0031EB46 /* DateHeaderView.m in Sources */, 2CB6ACDA2641483800D3D641 /* NCMessageLocationParameter.m in Sources */, @@ -3144,6 +3149,7 @@ 2C4446F9265D5A0700DF1DBC /* NotificationCenterNotifications.m in Sources */, 1F35F8ED2AEEBC1600044BDA /* UIView+SLKAdditions.m in Sources */, 1FDFC94F2BA50B9100670DF4 /* UIFontExtension.swift in Sources */, + 1F205D442CFC70AD00AAA673 /* AvatarProtocol.swift in Sources */, 1F35F8EA2AEEBC0E00044BDA /* SLKTextViewController.m in Sources */, 2C6955132B0CE1A20070F6E1 /* NCUtils.swift in Sources */, 1F8AAC342C518B8A004DA20A /* SignalingSettings.swift in Sources */, diff --git a/NextcloudTalk/AvatarButton.swift b/NextcloudTalk/AvatarButton.swift index 65033f45a..b6ab5ea84 100644 --- a/NextcloudTalk/AvatarButton.swift +++ b/NextcloudTalk/AvatarButton.swift @@ -6,7 +6,7 @@ import UIKit import SDWebImage -@objcMembers class AvatarButton: UIButton { +@objcMembers class AvatarButton: UIButton, AvatarProtocol { private var currentRequest: SDWebImageCombinedOperation? @@ -63,8 +63,7 @@ import SDWebImage // MARK: - User avatars - public func setActorAvatar(forMessage message: NCChatMessage) { - guard let account = message.account else { return } + public func setActorAvatar(forMessage message: NCChatMessage, withAccount account: TalkAccount) { self.setActorAvatar(forId: message.actorId, withType: message.actorType, withDisplayName: message.actorDisplayName, withRoomToken: message.token, using: account) } diff --git a/NextcloudTalk/AvatarImageView.swift b/NextcloudTalk/AvatarImageView.swift index 0ad5de89b..8cd77809b 100644 --- a/NextcloudTalk/AvatarImageView.swift +++ b/NextcloudTalk/AvatarImageView.swift @@ -6,7 +6,7 @@ import UIKit import SDWebImage -@objcMembers class AvatarImageView: UIImageView { +@objcMembers class AvatarImageView: UIImageView, AvatarProtocol { private var currentRequest: SDWebImageCombinedOperation? @@ -60,8 +60,7 @@ import SDWebImage // MARK: - User avatars - public func setActorAvatar(forMessage message: NCChatMessage) { - guard let account = message.account else { return } + public func setActorAvatar(forMessage message: NCChatMessage, withAccount account: TalkAccount) { self.setActorAvatar(forId: message.actorId, withType: message.actorType, withDisplayName: message.actorDisplayName, withRoomToken: message.token, using: account) } diff --git a/NextcloudTalk/AvatarProtocol.swift b/NextcloudTalk/AvatarProtocol.swift new file mode 100644 index 000000000..bfd72cdad --- /dev/null +++ b/NextcloudTalk/AvatarProtocol.swift @@ -0,0 +1,18 @@ +// +// SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors +// SPDX-License-Identifier: GPL-3.0-or-later +// + +protocol AvatarProtocol { + + func cancelCurrentRequest() + + // MARK: - Conversation avatars + func setAvatar(for room: NCRoom) + func setGroupAvatar() + + // MARK: - User avatars + func setActorAvatar(forMessage message: NCChatMessage, withAccount account: TalkAccount) + func setActorAvatar(forId actorId: String?, withType actorType: String?, withDisplayName actorDisplayName: String?, withRoomToken roomToken: String?, using account: TalkAccount) + +} diff --git a/NextcloudTalk/AvatarView.swift b/NextcloudTalk/AvatarView.swift index 33891e26f..dd18aa881 100644 --- a/NextcloudTalk/AvatarView.swift +++ b/NextcloudTalk/AvatarView.swift @@ -6,7 +6,7 @@ import UIKit import SDWebImage -@objcMembers class AvatarView: UIView { +@objcMembers class AvatarView: UIView, AvatarProtocol { private let userStatusSizePercentage = 0.38 @@ -92,6 +92,10 @@ import SDWebImage userStatusLabel.text = nil } + func cancelCurrentRequest() { + self.avatarImageView.cancelCurrentRequest() + } + // MARK: - Conversation avatars public func setAvatar(for room: NCRoom) { @@ -108,8 +112,8 @@ import SDWebImage // MARK: - User avatars - public func setActorAvatar(forMessage message: NCChatMessage) { - self.avatarImageView.setActorAvatar(forMessage: message) + public func setActorAvatar(forMessage message: NCChatMessage, withAccount account: TalkAccount) { + self.avatarImageView.setActorAvatar(forMessage: message, withAccount: account) } public func setActorAvatar(forId actorId: String?, withType actorType: String?, withDisplayName actorDisplayName: String?, withRoomToken roomToken: String?, using account: TalkAccount) { From 7dca2d6d047b0d91f9575811ad30e03463d1f799 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Sun, 1 Dec 2024 11:44:00 +0100 Subject: [PATCH 05/12] Require an account for the chat views MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- NextcloudTalk/BaseChatTableViewCell.swift | 11 +++++------ NextcloudTalk/BaseChatViewController.swift | 18 +++++++++--------- NextcloudTalk/CallViewController.swift | 3 ++- NextcloudTalk/ChatViewController.swift | 4 ++-- NextcloudTalk/InputbarViewController.swift | 7 +++++-- NextcloudTalk/NCRoomsManager.m | 6 +++--- NextcloudTalk/ReplyMessageView.m | 7 ++++++- .../RoomSharedItemsTableViewController.swift | 2 +- NextcloudTalk/RoomsTableViewController.m | 8 +++++++- .../ShareConfirmationViewController.swift | 4 +--- 10 files changed, 41 insertions(+), 29 deletions(-) diff --git a/NextcloudTalk/BaseChatTableViewCell.swift b/NextcloudTalk/BaseChatTableViewCell.swift index bc4e735c2..dde4e2c4a 100644 --- a/NextcloudTalk/BaseChatTableViewCell.swift +++ b/NextcloudTalk/BaseChatTableViewCell.swift @@ -77,6 +77,7 @@ class BaseChatTableViewCell: UITableViewCell, AudioPlayerViewDelegate, Reactions public var message: NCChatMessage? public var room: NCRoom? + public var account: TalkAccount? internal var quotedMessageView: QuotedMessageView? internal var reactionView: ReactionsView? @@ -144,11 +145,12 @@ class BaseChatTableViewCell: UITableViewCell, AudioPlayerViewDelegate, Reactions } // swiftlint:disable:next cyclomatic_complexity - public func setup(for message: NCChatMessage, inRoom room: NCRoom) { + public func setup(for message: NCChatMessage, inRoom room: NCRoom, withAccount account: TalkAccount) { self.message = message self.room = room + self.account = account - self.avatarButton.setActorAvatar(forMessage: message) + self.avatarButton.setActorAvatar(forMessage: message, withAccount: account) self.avatarButton.menu = self.getDeferredUserMenu() self.avatarButton.showsMenuAsPrimaryAction = true @@ -176,9 +178,6 @@ class BaseChatTableViewCell: UITableViewCell, AudioPlayerViewDelegate, Reactions self.titleLabel.attributedText = titleLabel - guard let account = message.account - else { return } - let shouldShowDeliveryStatus = NCDatabaseManager.sharedInstance().roomHasTalkCapability(kCapabilityChatReadStatus, for: room) var shouldShowReadStatus = false @@ -194,7 +193,7 @@ class BaseChatTableViewCell: UITableViewCell, AudioPlayerViewDelegate, Reactions self.quotedMessageView?.messageLabel.text = quoteString self.quotedMessageView?.actorLabel.attributedText = parent.actor.attributedDisplayName self.quotedMessageView?.highlighted = parent.isMessage(from: account.userId) - self.quotedMessageView?.avatarView.setActorAvatar(forMessage: parent) + self.quotedMessageView?.avatarView.setActorAvatar(forMessage: parent, withAccount: account) } else { self.hideQuotePart() } diff --git a/NextcloudTalk/BaseChatViewController.swift b/NextcloudTalk/BaseChatViewController.swift index 51cabf970..b179a2aeb 100644 --- a/NextcloudTalk/BaseChatViewController.swift +++ b/NextcloudTalk/BaseChatViewController.swift @@ -192,8 +192,8 @@ import SwiftUI // MARK: - Init/Deinit - public init?(for room: NCRoom) { - super.init(for: room, tableViewStyle: .plain) + public init?(forRoom room: NCRoom, withAccount account: TalkAccount) { + super.init(forRoom: room, withAccount: account, tableViewStyle: .plain) self.hidesBottomBarWhenPushed = true self.tableView?.estimatedRowHeight = 0 @@ -209,8 +209,8 @@ import SwiftUI // Not using an optional here, because it is not available from ObjC // Pass "0" as highlightMessageId to not highlight a message - public convenience init?(for room: NCRoom, withMessage messages: [NCChatMessage], withHighlightId highlightMessageId: Int) { - self.init(for: room) + public convenience init?(forRoom room: NCRoom, withAccount account: TalkAccount, withMessage messages: [NCChatMessage], withHighlightId highlightMessageId: Int) { + self.init(forRoom: room, withAccount: account) // When we pass in a fixed number of messages, we hide the inputbar by default self.textInputbar.isHidden = true @@ -2765,7 +2765,7 @@ import SwiftUI if let cell = self.tableView?.dequeueReusableCell(withIdentifier: cellIdentifier) as? BaseChatTableViewCell { cell.delegate = self - cell.setup(for: message, inRoom: self.room) + cell.setup(for: message, inRoom: self.room, withAccount: self.account) if let playerAudioFileStatus = self.playerAudioFileStatus, let voiceMessagesPlayer = self.voiceMessagesPlayer { @@ -2788,7 +2788,7 @@ import SwiftUI if let cell = self.tableView?.dequeueReusableCell(withIdentifier: cellIdentifier) as? BaseChatTableViewCell { cell.delegate = self - cell.setup(for: message, inRoom: self.room) + cell.setup(for: message, inRoom: self.room, withAccount: self.account) return cell } @@ -2799,7 +2799,7 @@ import SwiftUI if let cell = self.tableView?.dequeueReusableCell(withIdentifier: cellIdentifier) as? BaseChatTableViewCell { cell.delegate = self - cell.setup(for: message, inRoom: self.room) + cell.setup(for: message, inRoom: self.room, withAccount: self.account) return cell } @@ -2810,7 +2810,7 @@ import SwiftUI if let cell = self.tableView?.dequeueReusableCell(withIdentifier: cellIdentifier) as? BaseChatTableViewCell { cell.delegate = self - cell.setup(for: message, inRoom: self.room) + cell.setup(for: message, inRoom: self.room, withAccount: self.account) return cell } @@ -2826,7 +2826,7 @@ import SwiftUI if let cell = self.tableView?.dequeueReusableCell(withIdentifier: cellIdentifier) as? BaseChatTableViewCell { cell.delegate = self - cell.setup(for: message, inRoom: self.room) + cell.setup(for: message, inRoom: self.room, withAccount: self.account) return cell } diff --git a/NextcloudTalk/CallViewController.swift b/NextcloudTalk/CallViewController.swift index aa649ba7c..9efd5bf9d 100644 --- a/NextcloudTalk/CallViewController.swift +++ b/NextcloudTalk/CallViewController.swift @@ -1907,7 +1907,8 @@ class CallViewController: UIViewController, func showChat() { if chatNavigationController == nil { guard let room = NCDatabaseManager.sharedInstance().room(withToken: room.token, forAccountId: room.accountId), - let chatViewController = ChatViewController(for: room) + let account = room.account, + let chatViewController = ChatViewController(forRoom: room, withAccount: account) else { return } chatViewController.presentedInCall = true diff --git a/NextcloudTalk/ChatViewController.swift b/NextcloudTalk/ChatViewController.swift index 56a6db921..01f26f0b6 100644 --- a/NextcloudTalk/ChatViewController.swift +++ b/NextcloudTalk/ChatViewController.swift @@ -120,10 +120,10 @@ import SwiftyAttributes private var messageExpirationTimer: Timer? - public override init?(for room: NCRoom) { + public override init?(forRoom room: NCRoom, withAccount account: TalkAccount) { self.chatController = NCChatController(for: room) - super.init(for: room) + super.init(forRoom: room, withAccount: account) NotificationCenter.default.addObserver(self, selector: #selector(didUpdateRoom(notification:)), name: NSNotification.Name.NCRoomsManagerDidUpdateRoom, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(didJoinRoom(notification:)), name: NSNotification.Name.NCRoomsManagerDidJoinRoom, object: nil) diff --git a/NextcloudTalk/InputbarViewController.swift b/NextcloudTalk/InputbarViewController.swift index 5594ff82f..6e03a28e0 100644 --- a/NextcloudTalk/InputbarViewController.swift +++ b/NextcloudTalk/InputbarViewController.swift @@ -12,6 +12,7 @@ import UIKit // MARK: - Public var public var room: NCRoom + public var account: TalkAccount // MARK: - Internal var internal var titleView: NCChatTitleView? @@ -19,16 +20,18 @@ import UIKit internal var mentionsDict: [String: NCMessageParameter] = [:] internal var contentView: UIView? - public init?(for room: NCRoom, tableViewStyle style: UITableView.Style) { + public init?(forRoom room: NCRoom, withAccount account: TalkAccount, tableViewStyle style: UITableView.Style) { self.room = room + self.account = account super.init(tableViewStyle: style) self.commonInit() } - public init?(for room: NCRoom, withView view: UIView) { + public init?(forRoom room: NCRoom, withAccount account: TalkAccount, withView view: UIView) { self.room = room + self.account = account self.contentView = view super.init(tableViewStyle: .plain) diff --git a/NextcloudTalk/NCRoomsManager.m b/NextcloudTalk/NCRoomsManager.m index 0ac4448e4..72632bb0f 100644 --- a/NextcloudTalk/NCRoomsManager.m +++ b/NextcloudTalk/NCRoomsManager.m @@ -352,11 +352,11 @@ - (void)startChatInRoom:(NCRoom *)room NSLog(@"Not starting chat due to in a call."); return; } - + + TalkAccount *activeAccount = [[NCDatabaseManager sharedInstance] activeAccount]; NCRoomController *roomController = [_activeRooms objectForKey:room.token]; if (!roomController) { // Workaround until external signaling supports multi-room - TalkAccount *activeAccount = [[NCDatabaseManager sharedInstance] activeAccount]; NCExternalSignalingController *extSignalingController = [[NCSettingsController sharedInstance] externalSignalingControllerForAccountId:activeAccount.accountId]; if (extSignalingController) { NSString *currentRoom = extSignalingController.currentRoom; @@ -374,7 +374,7 @@ - (void)startChatInRoom:(NCRoom *)room //[[NCRoomsManager sharedInstance].chatViewController leaveChat]; NSLog(@"Creating new chat view controller."); - _chatViewController = [[ChatViewController alloc] initFor:room]; + _chatViewController = [[ChatViewController alloc] initForRoom:room withAccount:activeAccount]; if (_highlightMessageDict && [[_highlightMessageDict objectForKey:@"token"] isEqualToString:room.token]) { _chatViewController.highlightMessageId = [[_highlightMessageDict objectForKey:@"messageId"] integerValue]; _highlightMessageDict = nil; diff --git a/NextcloudTalk/ReplyMessageView.m b/NextcloudTalk/ReplyMessageView.m index e3644216a..6428d54b1 100644 --- a/NextcloudTalk/ReplyMessageView.m +++ b/NextcloudTalk/ReplyMessageView.m @@ -156,7 +156,12 @@ - (void)presentReplyViewWithMessage:(NCChatMessage *)message withUserId:(NSStrin self.quotedMessageView.actorLabel.text = ([message.actorDisplayName isEqualToString:@""]) ? NSLocalizedString(@"Guest", nil) : message.actorDisplayName; self.quotedMessageView.messageLabel.text = message.parsedMarkdownForChat.string; self.quotedMessageView.highlighted = [message isMessageFrom:userId]; - [self.quotedMessageView.avatarView setActorAvatarForMessage:message]; + + TalkAccount *account = message.account; + if (account) { + [self.quotedMessageView.avatarView setActorAvatarForMessage:message withAccount:account]; + } + [self.cancelButton setHidden:NO]; // Reset button size to 44 in case it was hidden before diff --git a/NextcloudTalk/RoomSharedItemsTableViewController.swift b/NextcloudTalk/RoomSharedItemsTableViewController.swift index dbcde20b0..70aa095d8 100644 --- a/NextcloudTalk/RoomSharedItemsTableViewController.swift +++ b/NextcloudTalk/RoomSharedItemsTableViewController.swift @@ -405,7 +405,7 @@ import QuickLook return UIContextMenuConfiguration(identifier: indexPath as NSCopying, previewProvider: { // Init the BaseChatViewController without message to directly show a preview - if let chatViewController = ContextChatViewController(for: self.room, withMessage: [], withHighlightId: 0) { + if let account = self.room.account, let chatViewController = ContextChatViewController(forRoom: self.room, withAccount: account, withMessage: [], withHighlightId: 0) { self.previewChatViewController = chatViewController // Fetch the context of the message and update the BaseChatViewController diff --git a/NextcloudTalk/RoomsTableViewController.m b/NextcloudTalk/RoomsTableViewController.m index 1a5a6f568..4e2e39920 100644 --- a/NextcloudTalk/RoomsTableViewController.m +++ b/NextcloudTalk/RoomsTableViewController.m @@ -1283,7 +1283,13 @@ - (void)presentSelectedMessageInChat:(NKSearchEntry *)message - (void)presentContextChatInRoom:(NCRoom *)room forMessageId:(NSInteger)messageId { - ContextChatViewController *contextChatViewController = [[ContextChatViewController alloc] initFor:room withMessage:@[] withHighlightId:0]; + TalkAccount *account = room.account; + + if (!account) { + return; + } + + ContextChatViewController *contextChatViewController = [[ContextChatViewController alloc] initForRoom:room withAccount:account withMessage:@[] withHighlightId:0]; contextChatViewController.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(closeContextChat)]; NCChatController *chatController = [[NCChatController alloc] initForRoom:room]; diff --git a/ShareExtension/ShareConfirmationViewController.swift b/ShareExtension/ShareConfirmationViewController.swift index 5a87a5307..9b6ff8d16 100644 --- a/ShareExtension/ShareConfirmationViewController.swift +++ b/ShareExtension/ShareConfirmationViewController.swift @@ -30,7 +30,6 @@ import MBProgressHUD // MARK: - Public var - public var account: TalkAccount public var isModal: Bool = false public var forwardingMessage: Bool = false @@ -220,10 +219,9 @@ import MBProgressHUD // MARK: - Init. public init?(room: NCRoom, account: TalkAccount, serverCapabilities: ServerCapabilities) { - self.account = account self.serverCapabilities = serverCapabilities - super.init(for: room, withView: self.shareContentView) + super.init(forRoom: room, withAccount: account, withView: self.shareContentView) self.shareContentView.addSubview(self.toLabelView) NSLayoutConstraint.activate([ From 57d0fd0e11c77e19b34e5dbb46abc2fe991ee935 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Sun, 1 Dec 2024 11:54:35 +0100 Subject: [PATCH 06/12] Use TalkAccount from current viewController MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- NextcloudTalk/BaseChatViewController.swift | 89 ++++++++-------------- NextcloudTalk/ChatViewController.swift | 31 ++++---- NextcloudTalk/InputbarViewController.swift | 4 +- 3 files changed, 45 insertions(+), 79 deletions(-) diff --git a/NextcloudTalk/BaseChatViewController.swift b/NextcloudTalk/BaseChatViewController.swift index b179a2aeb..0a4e460b0 100644 --- a/NextcloudTalk/BaseChatViewController.swift +++ b/NextcloudTalk/BaseChatViewController.swift @@ -501,13 +501,11 @@ import SwiftUI // MARK: - Temporary messages internal func createTemporaryMessage(message: String, replyTo parentMessage: NCChatMessage?, messageParameters: String, silently: Bool) -> NCChatMessage? { - guard let account = self.room.account else { return nil } - let temporaryMessage = NCChatMessage() - temporaryMessage.accountId = account.accountId - temporaryMessage.actorDisplayName = account.userDisplayName - temporaryMessage.actorId = account.userId + temporaryMessage.accountId = self.account.accountId + temporaryMessage.actorDisplayName = self.account.userDisplayName + temporaryMessage.actorId = self.account.userId temporaryMessage.actorType = "users" temporaryMessage.timestamp = Int(Date().timeIntervalSince1970) temporaryMessage.token = room.token @@ -879,14 +877,12 @@ import SwiftUI } func showReplyView(for message: NCChatMessage) { - guard let account = message.account else { return } - let isAtBottom = self.shouldScrollOnNewMessages() if let replyProxyView = self.replyProxyView as? ReplyMessageView { self.replyMessageView = replyProxyView - replyProxyView.presentReply(with: message, withUserId: account.userId) + replyProxyView.presentReply(with: message, withUserId: self.account.userId) self.presentKeyboard(true) // Make sure we're really at the bottom after showing the replyMessageView @@ -958,13 +954,11 @@ import SwiftUI } func didPressNoteToSelf(for message: NCChatMessage) { - guard let account = message.account else { return } - - NCAPIController.sharedInstance().getNoteToSelfRoom(forAccount: account) { roomDict, error in - if error == nil, let room = NCRoom(dictionary: roomDict, andAccountId: account.accountId) { + NCAPIController.sharedInstance().getNoteToSelfRoom(forAccount: self.account) { roomDict, error in + if error == nil, let room = NCRoom(dictionary: roomDict, andAccountId: self.account.accountId) { if message.isObjectShare { - NCAPIController.sharedInstance().shareRichObject(message.richObjectFromObjectShare, inRoom: room.token, for: account) { error in + NCAPIController.sharedInstance().shareRichObject(message.richObjectFromObjectShare, inRoom: room.token, for: self.account) { error in if error == nil { NotificationPresenter.shared().present(text: NSLocalizedString("Added note to self", comment: ""), dismissAfterDelay: 5.0, includedStyle: .success) } else { @@ -972,7 +966,7 @@ import SwiftUI } } } else { - NCAPIController.sharedInstance().sendChatMessage(message.parsedMessage().string, toRoom: room.token, displayName: nil, replyTo: -1, referenceId: nil, silently: false, for: account) { error in + NCAPIController.sharedInstance().sendChatMessage(message.parsedMessage().string, toRoom: room.token, displayName: nil, replyTo: -1, referenceId: nil, silently: false, for: self.account) { error in if error == nil { NotificationPresenter.shared().present(text: NSLocalizedString("Added note to self", comment: ""), dismissAfterDelay: 5.0, includedStyle: .success) } else { @@ -1108,9 +1102,7 @@ import SwiftUI self.updateMessage(withMessageId: deletingMessage.messageId, updatedMessage: deletingMessage) } - guard let account = message.account else { return } - - NCAPIController.sharedInstance().deleteChatMessage(inRoom: self.room.token, withMessageId: message.messageId, for: account) { messageDict, error, statusCode in + NCAPIController.sharedInstance().deleteChatMessage(inRoom: self.room.token, withMessageId: message.messageId, for: self.account) { messageDict, error, statusCode in if error == nil, let messageDict, let parent = messageDict["parent"] as? [AnyHashable: Any] { @@ -1121,7 +1113,7 @@ import SwiftUI NotificationPresenter.shared().present(text: NSLocalizedString("Message deleted successfully", comment: ""), dismissAfterDelay: 5.0, includedStyle: .success) } - if let deleteMessage = NCChatMessage(dictionary: parent, andAccountId: account.accountId) { + if let deleteMessage = NCChatMessage(dictionary: parent, andAccountId: self.account.accountId) { self.updateMessage(withMessageId: deleteMessage.messageId, updatedMessage: deleteMessage) } } else if error != nil { @@ -1349,10 +1341,8 @@ import SwiftUI } internal func createShareConfirmationViewController() -> (shareConfirmationVC: ShareConfirmationViewController, navController: NCNavigationController)? { - guard let account = room.account else { return nil } - - let serverCapabilities = NCDatabaseManager.sharedInstance().serverCapabilities(forAccountId: account.accountId) - let shareConfirmationVC = ShareConfirmationViewController(room: self.room, account: account, serverCapabilities: serverCapabilities!)! + let serverCapabilities = NCDatabaseManager.sharedInstance().serverCapabilities(forAccountId: self.account.accountId) + let shareConfirmationVC = ShareConfirmationViewController(room: self.room, account: self.account, serverCapabilities: serverCapabilities!)! shareConfirmationVC.delegate = self shareConfirmationVC.isModal = true let navigationController = NCNavigationController(rootViewController: shareConfirmationVC) @@ -1482,11 +1472,9 @@ import SwiftUI // MARK: - ShareLocationViewController Delegate public func shareLocationViewController(_ viewController: ShareLocationViewController, didSelectLocationWithLatitude latitude: Double, longitude: Double, andName name: String) { - guard let account = room.account else { return } - let richObject = GeoLocationRichObject(latitude: latitude, longitude: longitude, name: name) - NCAPIController.sharedInstance().shareRichObject(richObject.richObjectDictionary(), inRoom: self.room.token, for: account) { error in + NCAPIController.sharedInstance().shareRichObject(richObject.richObjectDictionary(), inRoom: self.room.token, for: self.account) { error in if let error { print("Error sharing rich object: \(error)") } @@ -1498,8 +1486,7 @@ import SwiftUI // MARK: - CNContactPickerViewController Delegate public func contactPicker(_ picker: CNContactPickerViewController, didSelect contact: CNContact) { - guard let account = room.account, - let vCardData = try? CNContactVCardSerialization.data(with: [contact]) + guard let vCardData = try? CNContactVCardSerialization.data(with: [contact]) else { return } var vcString = String(data: vCardData, encoding: .utf8) @@ -1519,7 +1506,7 @@ import SwiftUI let url = URL(fileURLWithPath: filePath) let contactFileName = "\(contact.identifier).vcf" - NCAPIController.sharedInstance().uniqueNameForFileUpload(withName: contactFileName, originalName: true, for: account) { fileServerURL, fileServerPath, _, _ in + NCAPIController.sharedInstance().uniqueNameForFileUpload(withName: contactFileName, originalName: true, for: self.account) { fileServerURL, fileServerPath, _, _ in if let fileServerURL, let fileServerPath { self.uploadFileAtPath(localPath: url.path, withFileServerURL: fileServerURL, andFileServerPath: fileServerPath, withMetaData: nil) } else { @@ -1695,8 +1682,6 @@ import SwiftUI } func shareVoiceMessage() { - guard let account = room.account else { return } - let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd HH-mm-ss" let dateString = dateFormatter.string(from: Date()) @@ -1719,7 +1704,7 @@ import SwiftUI audioFileName += ".mp3" - NCAPIController.sharedInstance().uniqueNameForFileUpload(withName: audioFileName, originalName: true, for: account, withCompletionBlock: { fileServerURL, fileServerPath, _, _ in + NCAPIController.sharedInstance().uniqueNameForFileUpload(withName: audioFileName, originalName: true, for: self.account, withCompletionBlock: { fileServerURL, fileServerPath, _, _ in if let fileServerURL, let fileServerPath, let recorder = self.recorder { let talkMetaData: [String: String] = ["messageType": "voice-message"] self.uploadFileAtPath(localPath: recorder.url.path, withFileServerURL: fileServerURL, andFileServerPath: fileServerPath, withMetaData: talkMetaData) @@ -1730,9 +1715,7 @@ import SwiftUI } func uploadFileAtPath(localPath: String, withFileServerURL fileServerURL: String, andFileServerPath fileServerPath: String, withMetaData talkMetaData: [String: String]?) { - guard let account = room.account else { return } - - NCAPIController.sharedInstance().setupNCCommunication(for: account) + NCAPIController.sharedInstance().setupNCCommunication(for: self.account) NextcloudKit.shared.upload(serverUrlFileName: fileServerURL, fileNameLocalPath: localPath, taskHandler: { _ in NSLog("Upload task") @@ -1742,13 +1725,13 @@ import SwiftUI NSLog("Upload completed with error code: %ld", error.errorCode) if error.errorCode == 0 { - NCAPIController.sharedInstance().shareFileOrFolder(for: account, atPath: fileServerPath, toRoom: self.room.token, talkMetaData: talkMetaData, withCompletionBlock: { error in + NCAPIController.sharedInstance().shareFileOrFolder(for: self.account, atPath: fileServerPath, toRoom: self.room.token, talkMetaData: talkMetaData, withCompletionBlock: { error in if error != nil { NSLog("Failed to share voice message") } }) } else if error.errorCode == 404 || error.errorCode == 409 { - NCAPIController.sharedInstance().checkOrCreateAttachmentFolder(for: account, withCompletionBlock: { created, _ in + NCAPIController.sharedInstance().checkOrCreateAttachmentFolder(for: self.account, withCompletionBlock: { created, _ in if created { self.uploadFileAtPath(localPath: localPath, withFileServerURL: fileServerURL, andFileServerPath: fileServerPath, withMetaData: talkMetaData) } else { @@ -2307,21 +2290,19 @@ import SwiftUI collapseByMessage.collapsedMessages.add(newMessage.messageId as NSNumber) collapseByMessage.isCollapsed = true - guard let account = room.account else { return } - var isUser0Self = false var isUser1Self = false if let userDict = collapseByMessage.messageParameters["user"] as? [String: Any] { - isUser0Self = userDict["id"] as? String == account.userId && userDict["type"] as? String == "user" + isUser0Self = userDict["id"] as? String == self.account.userId && userDict["type"] as? String == "user" } if let userDict = newMessage.messageParameters["user"] as? [String: Any] { - isUser1Self = userDict["id"] as? String == account.userId && userDict["type"] as? String == "user" + isUser1Self = userDict["id"] as? String == self.account.userId && userDict["type"] as? String == "user" } - let isActor0Self = collapseByMessage.actorId == account.userId && collapseByMessage.actorType == "users" - let isActor1Self = newMessage.actorId == account.userId && newMessage.actorType == "users" + let isActor0Self = collapseByMessage.actorId == self.account.userId && collapseByMessage.actorType == "users" + let isActor1Self = newMessage.actorId == self.account.userId && newMessage.actorType == "users" let isActor0Admin = collapseByMessage.actorId == "cli" && collapseByMessage.actorType == "guests" collapseByMessage.collapsedIncludesUserSelf = isUser0Self || isUser1Self @@ -2530,18 +2511,16 @@ import SwiftUI // MARK: - Reactions func addReaction(reaction: String, to message: NCChatMessage) { - if message.reactionsArray().contains(where: {$0.reaction == reaction && $0.userReacted }) { + if message.reactionsArray().contains(where: { $0.reaction == reaction && $0.userReacted }) { // We can't add reaction twice return } - guard let account = message.account else { return } - self.setTemporaryReaction(reaction: reaction, withState: .adding, toMessage: message) - NCDatabaseManager.sharedInstance().increaseEmojiUsage(forEmoji: reaction, forAccount: account.accountId) + NCDatabaseManager.sharedInstance().increaseEmojiUsage(forEmoji: reaction, forAccount: self.account.accountId) - NCAPIController.sharedInstance().addReaction(reaction, toMessage: message.messageId, inRoom: self.room.token, for: account) { _, error, _ in + NCAPIController.sharedInstance().addReaction(reaction, toMessage: message.messageId, inRoom: self.room.token, for: self.account) { _, error, _ in if error != nil { NotificationPresenter.shared().present(text: NSLocalizedString("An error occurred while adding a reaction to a message", comment: ""), dismissAfterDelay: 5.0, includedStyle: .error) self.removeTemporaryReaction(reaction: reaction, forMessageId: message.messageId) @@ -2550,11 +2529,9 @@ import SwiftUI } func removeReaction(reaction: String, from message: NCChatMessage) { - guard let account = message.account else { return } - self.setTemporaryReaction(reaction: reaction, withState: .removing, toMessage: message) - NCAPIController.sharedInstance().removeReaction(reaction, fromMessage: message.messageId, inRoom: self.room.token, for: account) { _, error, _ in + NCAPIController.sharedInstance().removeReaction(reaction, fromMessage: message.messageId, inRoom: self.room.token, for: self.account) { _, error, _ in if error != nil { NotificationPresenter.shared().present(text: NSLocalizedString("An error occurred while removing a reaction from a message", comment: ""), dismissAfterDelay: 5.0, includedStyle: .error) self.removeTemporaryReaction(reaction: reaction, forMessageId: message.messageId) @@ -2621,9 +2598,7 @@ import SwiftUI reactionsVC.room = self.room self.presentWithNavigation(reactionsVC, animated: true) - guard let account = message.account else { return } - - NCAPIController.sharedInstance().getReactions(nil, fromMessage: message.messageId, inRoom: self.room.token, for: account) { reactionsDict, error, _ in + NCAPIController.sharedInstance().getReactions(nil, fromMessage: message.messageId, inRoom: self.room.token, for: self.account) { reactionsDict, error, _ in if error == nil, let reactions = reactionsDict as? [String: [[String: AnyObject]]] { @@ -3470,9 +3445,9 @@ import SwiftUI pollVC.room = self.room self.presentWithNavigation(pollVC, animated: true) - guard let pollId = Int(poll.parameterId), let account = room.account else { return } + guard let pollId = Int(poll.parameterId) else { return } - NCAPIController.sharedInstance().getPollWithId(pollId, inRoom: self.room.token, for: account) { poll, error, _ in + NCAPIController.sharedInstance().getPollWithId(pollId, inRoom: self.room.token, for: self.account) { poll, error, _ in if error == nil, let poll { pollVC.updatePoll(poll: poll) } @@ -3482,9 +3457,7 @@ import SwiftUI // MARK: - PollCreationViewControllerDelegate func pollCreationViewControllerWantsToCreatePoll(pollCreationViewController: PollCreationViewController, question: String, options: [String], resultMode: NCPollResultMode, maxVotes: Int) { - guard let account = room.account else { return } - - NCAPIController.sharedInstance().createPoll(withQuestion: question, options: options, resultMode: resultMode, maxVotes: maxVotes, inRoom: self.room.token, for: account) { _, error, _ in + NCAPIController.sharedInstance().createPoll(withQuestion: question, options: options, resultMode: resultMode, maxVotes: maxVotes, inRoom: self.room.token, for: self.account) { _, error, _ in if error != nil { pollCreationViewController.showCreationError() } else { diff --git a/NextcloudTalk/ChatViewController.swift b/NextcloudTalk/ChatViewController.swift index 01f26f0b6..8e0940738 100644 --- a/NextcloudTalk/ChatViewController.swift +++ b/NextcloudTalk/ChatViewController.swift @@ -1081,10 +1081,8 @@ import SwiftyAttributes } } completion: { _ in - guard let account = self.room.account else { return } - // Remove unread messages separator when user writes a message - if messages.containsMessage(forUserId: account.userId) { + if messages.containsMessage(forUserId: self.account.userId) { self.removeUnreadMessagesSeparator() } @@ -1092,7 +1090,7 @@ import SwiftyAttributes // Otherwise we would scroll whenever a unread message separator is available if addedUnreadMessageSeparator, let indexPathUnreadMessageSeparator = self.indexPathForUnreadMessageSeparator() { tableView.scrollToRow(at: indexPathUnreadMessageSeparator, at: .middle, animated: true) - } else if (shouldScrollOnNewMessages || messages.containsMessage(forUserId: account.userId)), let lastIndexPath = self.getLastRealMessage()?.indexPath { + } else if (shouldScrollOnNewMessages || messages.containsMessage(forUserId: self.account.userId)), let lastIndexPath = self.getLastRealMessage()?.indexPath { tableView.scrollToRow(at: lastIndexPath, at: .none, animated: true) } else if self.firstUnreadMessage == nil, newMessagesContainVisibleMessages, let firstNewMessage = messages.first { // This check is needed since several calls to receiveMessages API might be needed @@ -1264,9 +1262,8 @@ import SwiftyAttributes guard serverSupportsConversationPermissions else { return } // Retrieve the information about ourselves - guard let account = room.account, - let userDict = notification.userInfo?["users"] as? [[String: String]], - let appUserDict = userDict.first(where: { $0["userId"] == account.userId }) + guard let userDict = notification.userInfo?["users"] as? [[String: String]], + let appUserDict = userDict.first(where: { $0["userId"] == self.account.userId }) else { return } // Check if we still have the same permissions @@ -1292,15 +1289,14 @@ import SwiftyAttributes // Don't show a typing indicator for ourselves or if typing indicator setting is disabled // Workaround: TypingPrivacy should be checked locally, not from the remote server, use serverCapabilities for now // TODO: Remove workaround for federated typing indicators. - guard let account = room.account, - let serverCapabilities = NCDatabaseManager.sharedInstance().serverCapabilities(forAccountId: self.room.accountId) + guard let serverCapabilities = NCDatabaseManager.sharedInstance().serverCapabilities(forAccountId: self.room.accountId) else { return } let userId = notification.userInfo?["userId"] as? String let isFederated = notification.userInfo?["isFederated"] as? Bool ?? false // Since our own userId can exist on other servers, only suppress the notification if it's not federated - if (userId == account.userId && !isFederated) || serverCapabilities.typingPrivacy { + if (userId == self.account.userId && !isFederated) || serverCapabilities.typingPrivacy { return } @@ -1428,7 +1424,7 @@ import SwiftyAttributes // MARK: - Editing support public override func didCommitTextEditing(_ sender: Any) { - if let editingMessage, let account = self.room.account { + if let editingMessage { let messageParametersJSONString = NCMessageParameter.messageParametersJSONString(from: self.mentionsDict) ?? "" editingMessage.message = self.replaceMentionsDisplayNamesWithMentionsKeysInMessage(message: self.textView.text, parameters: messageParametersJSONString) editingMessage.messageParametersJSONString = messageParametersJSONString @@ -1441,7 +1437,7 @@ import SwiftyAttributes guard let messageDict, let parent = messageDict["parent"] as? [AnyHashable: Any], - let updatedMessage = NCChatMessage(dictionary: parent, andAccountId: account.accountId) + let updatedMessage = NCChatMessage(dictionary: parent, andAccountId: self.account.accountId) else { return } self.updateMessage(withMessageId: editingMessage.messageId, updatedMessage: updatedMessage) @@ -1764,8 +1760,7 @@ import SwiftyAttributes } } - guard let message = self.message(for: indexPath), - let account = message.account + guard let message = self.message(for: indexPath) else { return nil } if message.isSystemMessage || message.isDeletedMessage || @@ -1817,7 +1812,7 @@ import SwiftyAttributes } // Reply-privately option (only to other users and not in one-to-one) - if self.isMessageReplyable(message: message), self.room.type != .oneToOne, message.actorType == "users", message.actorId != account.userId { + if self.isMessageReplyable(message: message), self.room.type != .oneToOne, message.actorType == "users", message.actorId != self.account.userId { actions.append(UIAction(title: NSLocalizedString("Reply privately", comment: ""), image: .init(systemName: "person")) { _ in self.didPressReplyPrivately(for: message) }) @@ -1892,7 +1887,7 @@ import SwiftyAttributes }) // Translate - if !self.offlineMode, NCDatabaseManager.sharedInstance().hasAvailableTranslations(forAccountId: account.accountId) { + if !self.offlineMode, NCDatabaseManager.sharedInstance().hasAvailableTranslations(forAccountId: self.account.accountId) { actions.append(UIAction(title: NSLocalizedString("Translate", comment: ""), image: .init(systemName: "character.book.closed")) { _ in self.didPressTranslate(for: message) }) @@ -1917,14 +1912,14 @@ import SwiftyAttributes var destructiveMenuActions: [UIMenuElement] = [] // Edit option - if message.isEditable(for: account, in: self.room) && hasChatPermissions { + if message.isEditable(for: self.account, in: self.room) && hasChatPermissions { destructiveMenuActions.append(UIAction(title: NSLocalizedString("Edit", comment: "Edit a message or room participants"), image: .init(systemName: "pencil")) { _ in self.didPressEdit(for: message) }) } // Delete option - if message.sendingFailed || message.isOfflineMessage || (message.isDeletable(for: account, in: self.room) && hasChatPermissions) { + if message.sendingFailed || message.isOfflineMessage || (message.isDeletable(for: self.account, in: self.room) && hasChatPermissions) { destructiveMenuActions.append(UIAction(title: NSLocalizedString("Delete", comment: ""), image: .init(systemName: "trash"), attributes: .destructive) { _ in self.didPressDelete(for: message) }) diff --git a/NextcloudTalk/InputbarViewController.swift b/NextcloudTalk/InputbarViewController.swift index 6e03a28e0..af591dae0 100644 --- a/NextcloudTalk/InputbarViewController.swift +++ b/NextcloudTalk/InputbarViewController.swift @@ -312,9 +312,7 @@ import UIKit if suggestion.id == "all" { cell.avatarButton.setAvatar(for: self.room) } else { - if let account = room.account { - cell.avatarButton.setActorAvatar(forId: suggestion.id, withType: suggestion.source, withDisplayName: suggestion.label, withRoomToken: self.room.token, using: account) - } + cell.avatarButton.setActorAvatar(forId: suggestion.id, withType: suggestion.source, withDisplayName: suggestion.label, withRoomToken: self.room.token, using: self.account) } cell.accessibilityIdentifier = AutoCompletionCellIdentifier From 387b93b9f61d2312809de7ed6b6954d9285ff302 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Sun, 1 Dec 2024 12:38:21 +0100 Subject: [PATCH 07/12] Cache authHeaders and requestModifiers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- NextcloudTalk/NCAPIController.h | 1 + NextcloudTalk/NCAPIController.m | 41 ++++++++++++++++++++++++++-- NextcloudTalk/NCSettingsController.m | 1 + 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/NextcloudTalk/NCAPIController.h b/NextcloudTalk/NCAPIController.h index a35f97f92..0c15cd76d 100644 --- a/NextcloudTalk/NCAPIController.h +++ b/NextcloudTalk/NCAPIController.h @@ -128,6 +128,7 @@ extern NSInteger const kReceivedChatMessagesLimit; + (instancetype)sharedInstance; - (void)createAPISessionManagerForAccount:(TalkAccount *)account; +- (void)removeAPISessionManagerForAccount:(TalkAccount *)account; - (void)setupNCCommunicationForAccount:(TalkAccount *)account; - (NSInteger)conversationAPIVersionForAccount:(TalkAccount *)account; - (NSInteger)callAPIVersionForAccount:(TalkAccount *)account; diff --git a/NextcloudTalk/NCAPIController.m b/NextcloudTalk/NCAPIController.m index e136dfcb8..17cc1f3ce 100644 --- a/NextcloudTalk/NCAPIController.m +++ b/NextcloudTalk/NCAPIController.m @@ -33,6 +33,8 @@ @interface NCAPIController () @property (nonatomic, strong) NCAPISessionManager *defaultAPISessionManager; +@property (nonatomic, strong) NSCache *authTokenCache; +@property (nonatomic, strong) NSCache *requestModifierCache; @end @@ -52,6 +54,9 @@ - (id)init { self = [super init]; if (self) { + self.authTokenCache = [[NSCache alloc] init]; + self.requestModifierCache = [[NSCache alloc] init]; + [self initSessionManagers]; [self initImageDownloaders]; } @@ -75,6 +80,10 @@ - (void)initSessionManagers - (void)createAPISessionManagerForAccount:(TalkAccount *)account { + // Make sure there are no old entries in our caches when we create APISessionManagers + [self.authTokenCache removeObjectForKey:account.accountId]; + [self.requestModifierCache removeObjectForKey:account.accountId]; + NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedCookieStorageForGroupContainerIdentifier:account.accountId]; configuration.HTTPCookieStorage = cookieStorage; @@ -93,6 +102,14 @@ - (void)createAPISessionManagerForAccount:(TalkAccount *)account [_longPollingApiSessionManagers setObject:apiSessionManager forKey:account.accountId]; } +- (void)removeAPISessionManagerForAccount:(TalkAccount *)account +{ + [self.authTokenCache removeObjectForKey:account.accountId]; + [self.requestModifierCache removeObjectForKey:account.accountId]; + [self.apiSessionManagers removeObjectForKey:account.accountId]; + [self.longPollingApiSessionManagers removeObjectForKey:account.accountId]; +} + - (void)setupNCCommunicationForAccount:(TalkAccount *)account { ServerCapabilities *serverCapabilities = [[NCDatabaseManager sharedInstance] serverCapabilitiesForAccountId:account.accountId]; @@ -141,19 +158,37 @@ - (void)initImageDownloaders - (NSString *)authHeaderForAccount:(TalkAccount *)account { + NSString *cachedHeader = [self.authTokenCache objectForKey:account.accountId]; + + if (cachedHeader) { + return cachedHeader; + } + NSString *userTokenString = [NSString stringWithFormat:@"%@:%@", account.user, [[NCKeyChainController sharedInstance] tokenForAccountId:account.accountId]]; NSData *data = [userTokenString dataUsingEncoding:NSUTF8StringEncoding]; NSString *base64Encoded = [data base64EncodedStringWithOptions:0]; - - return [[NSString alloc]initWithFormat:@"Basic %@",base64Encoded]; + + NSString *authHeader = [[NSString alloc] initWithFormat:@"Basic %@",base64Encoded]; + [self.authTokenCache setObject:authHeader forKey:account.accountId]; + + return authHeader; } - (SDWebImageDownloaderRequestModifier *)getRequestModifierForAccount:(TalkAccount *)account { + SDWebImageDownloaderRequestModifier *cachedModifier = [self.requestModifierCache objectForKey:account.accountId]; + + if (cachedModifier) { + return cachedModifier; + } + NSMutableDictionary *headerDictionary = [[NSMutableDictionary alloc] init]; [headerDictionary setObject:[self authHeaderForAccount:account] forKey:@"Authorization"]; - return [[SDWebImageDownloaderRequestModifier alloc] initWithHeaders:headerDictionary]; + SDWebImageDownloaderRequestModifier *requestModifier = [[SDWebImageDownloaderRequestModifier alloc] initWithHeaders:headerDictionary]; + [self.requestModifierCache setObject:requestModifier forKey:account.accountId]; + + return requestModifier; } - (NSInteger)conversationAPIVersionForAccount:(TalkAccount *)account diff --git a/NextcloudTalk/NCSettingsController.m b/NextcloudTalk/NCSettingsController.m index 2ca6af636..8ae7e6ef8 100644 --- a/NextcloudTalk/NCSettingsController.m +++ b/NextcloudTalk/NCSettingsController.m @@ -435,6 +435,7 @@ - (void)logoutAccountWithAccountId:(NSString *)accountId withCompletionBlock:(Lo NCExternalSignalingController *extSignalingController = [self externalSignalingControllerForAccountId:removingAccount.accountId]; [extSignalingController disconnect]; [[NCAPIController sharedInstance] removeProfileImageForAccount:removingAccount]; + [[NCAPIController sharedInstance] removeAPISessionManagerForAccount:removingAccount]; [[NCDatabaseManager sharedInstance] removeAccountWithAccountId:removingAccount.accountId]; [[[NCChatFileController alloc] init] deleteDownloadDirectoryForAccount:removingAccount]; [[[NCRoomsManager sharedInstance] chatViewController] leaveChat]; From 569880694ffd1b65f831bc3a4d22a00951f0cd44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Sun, 1 Dec 2024 12:53:44 +0100 Subject: [PATCH 08/12] Don't recreate regular expression in each message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- NextcloudTalk/NCChatMessage.m | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/NextcloudTalk/NCChatMessage.m b/NextcloudTalk/NCChatMessage.m index 998f186d0..f43a9d4a6 100644 --- a/NextcloudTalk/NCChatMessage.m +++ b/NextcloudTalk/NCChatMessage.m @@ -297,7 +297,12 @@ - (NSMutableAttributedString *)parsedMessage NSString *parsedMessage = originalMessage; NSError *error = nil; - NSRegularExpression *parameterRegex = [NSRegularExpression regularExpressionWithPattern:@"\\{([^}]+)\\}" options:NSRegularExpressionCaseInsensitive error:&error]; + static NSRegularExpression *parameterRegex; + + if (!parameterRegex) { + parameterRegex = [NSRegularExpression regularExpressionWithPattern:@"\\{([^}]+)\\}" options:NSRegularExpressionCaseInsensitive error:&error]; + } + NSArray *matches = [parameterRegex matchesInString:originalMessage options:0 range:NSMakeRange(0, [originalMessage length])]; From 7990af21f429a9e6dd9b000d5539c117701119f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Sun, 1 Dec 2024 12:54:37 +0100 Subject: [PATCH 09/12] Don't recreate non-changing colors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- NextcloudTalk/NCAppBranding.m | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/NextcloudTalk/NCAppBranding.m b/NextcloudTalk/NCAppBranding.m index 4ee64ecd0..5f859c4e8 100644 --- a/NextcloudTalk/NCAppBranding.m +++ b/NextcloudTalk/NCAppBranding.m @@ -160,7 +160,13 @@ + (UIColor *)avatarPlaceholderColor + (UIColor *)chatForegroundColor { - return [self getDynamicColor:[UIColor darkGrayColor] withDarkMode:[UIColor labelColor]]; + static UIColor *color; + + if (!color) { + color = [self getDynamicColor:[UIColor darkGrayColor] withDarkMode:[UIColor labelColor]]; + } + + return color; } + (UIStatusBarStyle)statusBarStyleForBrandColor From 19e88bb7ec18995c02568050a478fc57bf57f95a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Sun, 1 Dec 2024 13:03:19 +0100 Subject: [PATCH 10/12] Adjust tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- .../Unit/Chat/UnitBaseChatTableViewCellTest.swift | 4 ++-- .../Unit/Chat/UnitBaseChatViewControllerTest.swift | 5 +++-- .../Unit/Chat/UnitChatViewControllerTest.swift | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/NextcloudTalkTests/Unit/Chat/UnitBaseChatTableViewCellTest.swift b/NextcloudTalkTests/Unit/Chat/UnitBaseChatTableViewCellTest.swift index c14690d4b..5407c5a1f 100644 --- a/NextcloudTalkTests/Unit/Chat/UnitBaseChatTableViewCellTest.swift +++ b/NextcloudTalkTests/Unit/Chat/UnitBaseChatTableViewCellTest.swift @@ -59,10 +59,10 @@ final class UnitBaseChatTableViewCellTest: TestBaseRealm { } let deckCell: BaseChatTableViewCell = .fromNib() - deckCell.setup(for: deckMessage, inRoom: room) + deckCell.setup(for: deckMessage, inRoom: room, withAccount: activeAccount) let quoteCell: BaseChatTableViewCell = .fromNib() - quoteCell.setup(for: quoteMessage, inRoom: room) + quoteCell.setup(for: quoteMessage, inRoom: room, withAccount: activeAccount) } } diff --git a/NextcloudTalkTests/Unit/Chat/UnitBaseChatViewControllerTest.swift b/NextcloudTalkTests/Unit/Chat/UnitBaseChatViewControllerTest.swift index 18f913435..f5241d84e 100644 --- a/NextcloudTalkTests/Unit/Chat/UnitBaseChatViewControllerTest.swift +++ b/NextcloudTalkTests/Unit/Chat/UnitBaseChatViewControllerTest.swift @@ -38,8 +38,9 @@ final class UnitBaseChatViewControllerTest: TestBaseRealm { override func setUpWithError() throws { try super.setUpWithError() - baseController = BaseChatViewController(for: NCRoom())! - testMessage = NCChatMessage(dictionary: [:], andAccountId: UnitBaseChatViewControllerTest.fakeAccountId) + let activeAccount = NCDatabaseManager.sharedInstance().activeAccount() + baseController = BaseChatViewController(forRoom: NCRoom(), withAccount: activeAccount)! + testMessage = NCChatMessage(dictionary: [:], andAccountId: activeAccount.accountId) } func testSystemMessageCellHeight() throws { diff --git a/NextcloudTalkTests/Unit/Chat/UnitChatViewControllerTest.swift b/NextcloudTalkTests/Unit/Chat/UnitChatViewControllerTest.swift index 35101c76c..4ef0b6216 100644 --- a/NextcloudTalkTests/Unit/Chat/UnitChatViewControllerTest.swift +++ b/NextcloudTalkTests/Unit/Chat/UnitChatViewControllerTest.swift @@ -51,7 +51,7 @@ final class UnitChatViewControllerTest: TestBaseRealm { XCTAssertEqual(NCChatMessage.allObjects().count, 2) - let chatViewController = ChatViewController(for: room)! + let chatViewController = ChatViewController(forRoom: room, withAccount: activeAccount)! let messageArray = [expMessage1, expMessage2].map { NCChatMessage(value: $0) } chatViewController.appendMessages(messages: messageArray) From 2fb59ae24b59d275c7b9c712a374b8019a78caf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Sun, 1 Dec 2024 20:35:49 +0100 Subject: [PATCH 11/12] Fix header in grouped messages on reuse MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- NextcloudTalk/BaseChatTableViewCell.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NextcloudTalk/BaseChatTableViewCell.swift b/NextcloudTalk/BaseChatTableViewCell.swift index dde4e2c4a..adaef45f1 100644 --- a/NextcloudTalk/BaseChatTableViewCell.swift +++ b/NextcloudTalk/BaseChatTableViewCell.swift @@ -200,6 +200,8 @@ class BaseChatTableViewCell: UITableViewCell, AudioPlayerViewDelegate, Reactions if message.isGroupMessage, message.parent == nil { self.headerPart.isHidden = true + } else { + self.headerPart.isHidden = false } // Make sure the status view is empty, when no delivery state should be set From 5c17fd38a7b9692ca54547b3514c8ddd089a9292 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Tue, 3 Dec 2024 22:49:18 +0100 Subject: [PATCH 12/12] Revert part of the reuse simplification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- NextcloudTalk/BaseChatTableViewCell.swift | 25 +++++------------------ 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/NextcloudTalk/BaseChatTableViewCell.swift b/NextcloudTalk/BaseChatTableViewCell.swift index adaef45f1..ee3467315 100644 --- a/NextcloudTalk/BaseChatTableViewCell.swift +++ b/NextcloudTalk/BaseChatTableViewCell.swift @@ -132,6 +132,11 @@ class BaseChatTableViewCell: UITableViewCell, AudioPlayerViewDelegate, Reactions self.quotedMessageView?.avatarView.cancelCurrentRequest() self.quotedMessageView?.avatarView.image = nil + self.headerPart.isHidden = false + self.quotePart.isHidden = true + self.referencePart.isHidden = true + self.reactionPart.isHidden = true + self.referenceView?.prepareForReuse() self.prepareForReuseFileCell() @@ -194,14 +199,10 @@ class BaseChatTableViewCell: UITableViewCell, AudioPlayerViewDelegate, Reactions self.quotedMessageView?.actorLabel.attributedText = parent.actor.attributedDisplayName self.quotedMessageView?.highlighted = parent.isMessage(from: account.userId) self.quotedMessageView?.avatarView.setActorAvatar(forMessage: parent, withAccount: account) - } else { - self.hideQuotePart() } if message.isGroupMessage, message.parent == nil { self.headerPart.isHidden = true - } else { - self.headerPart.isHidden = false } // Make sure the status view is empty, when no delivery state should be set @@ -228,8 +229,6 @@ class BaseChatTableViewCell: UITableViewCell, AudioPlayerViewDelegate, Reactions if !reactionsArray.isEmpty { self.showReactionsPart() self.reactionView?.updateReactions(reactions: reactionsArray) - } else { - self.hideReactionsPart() } if message.containsURL() { @@ -249,8 +248,6 @@ class BaseChatTableViewCell: UITableViewCell, AudioPlayerViewDelegate, Reactions self.referenceView?.update(for: referenceData, and: url) } } - } else { - self.hideReferencePart() } if message.isReplyable, !message.isDeleting { @@ -392,10 +389,6 @@ class BaseChatTableViewCell: UITableViewCell, AudioPlayerViewDelegate, Reactions } } - func hideQuotePart() { - self.quotePart.isHidden = true - } - @objc func quoteTapped(_ sender: UITapGestureRecognizer?) { if let parent = self.message?.parent { self.delegate?.cellWantsToScroll(to: parent) @@ -424,10 +417,6 @@ class BaseChatTableViewCell: UITableViewCell, AudioPlayerViewDelegate, Reactions } } - func hideReferencePart() { - self.referencePart.isHidden = true - } - // MARK: - ReactionsPart func showReactionsPart() { @@ -454,10 +443,6 @@ class BaseChatTableViewCell: UITableViewCell, AudioPlayerViewDelegate, Reactions } } - func hideReactionsPart() { - self.reactionPart.isHidden = true - } - // MARK: - ReactionsView Delegate func didSelectReaction(reaction: NCChatReaction) {