diff --git a/Session.xcodeproj/project.pbxproj b/Session.xcodeproj/project.pbxproj index eec7f7dfa..592b9855d 100644 --- a/Session.xcodeproj/project.pbxproj +++ b/Session.xcodeproj/project.pbxproj @@ -196,7 +196,6 @@ B8566C6C256F60F50045A0B9 /* OWSUserProfile.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2D1255B6DAF007E1867 /* OWSUserProfile.m */; }; B8566C7D256F62030045A0B9 /* OWSUserProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF2D3255B6DAF007E1867 /* OWSUserProfile.h */; settings = {ATTRIBUTES = (Public, ); }; }; B8569AC325CB5D2900DBA3DB /* ConversationVC+Interaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8569AC225CB5D2900DBA3DB /* ConversationVC+Interaction.swift */; }; - B8569AD325CBA13D00DBA3DB /* MediaTextOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8569AD225CBA13D00DBA3DB /* MediaTextOverlayView.swift */; }; B8569AE325CBB19A00DBA3DB /* DocumentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8569AE225CBB19A00DBA3DB /* DocumentView.swift */; }; B866CE112581C1A900535CC4 /* Sodium+Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3E7134E251C867C009649BB /* Sodium+Conversion.swift */; }; B86BD08423399ACF000F5AE3 /* Modal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08323399ACF000F5AE3 /* Modal.swift */; }; @@ -1224,7 +1223,6 @@ B8544E3223D50E4900299F14 /* SNAppearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SNAppearance.swift; sourceTree = ""; }; B8566C62256F55930045A0B9 /* OWSLinkPreview+Conversion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OWSLinkPreview+Conversion.swift"; sourceTree = ""; }; B8569AC225CB5D2900DBA3DB /* ConversationVC+Interaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ConversationVC+Interaction.swift"; sourceTree = ""; }; - B8569AD225CBA13D00DBA3DB /* MediaTextOverlayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaTextOverlayView.swift; sourceTree = ""; }; B8569AE225CBB19A00DBA3DB /* DocumentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentView.swift; sourceTree = ""; }; B86BD08323399ACF000F5AE3 /* Modal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modal.swift; sourceTree = ""; }; B86BD08523399CEF000F5AE3 /* SeedModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeedModal.swift; sourceTree = ""; }; @@ -2171,7 +2169,6 @@ isa = PBXGroup; children = ( 34A8B3502190A40E00218A25 /* MediaAlbumView.swift */, - B8569AD225CBA13D00DBA3DB /* MediaTextOverlayView.swift */, 3488F9352191CC4000E524CC /* MediaView.swift */, B8041A9425C8FA1D003C2166 /* MediaLoaderView.swift */, B8F5F71925F1B35C003BF8D4 /* MediaPlaceholderView.swift */, @@ -4941,7 +4938,6 @@ 341341EF2187467A00192D59 /* ConversationViewModel.m in Sources */, 4C21D5D8223AC60F00EF8A77 /* PhotoCapture.swift in Sources */, C331FFF32558FF0300070591 /* PathStatusView.swift in Sources */, - B8569AD325CBA13D00DBA3DB /* MediaTextOverlayView.swift in Sources */, 4CC1ECFB211A553000CC13BE /* AppUpdateNag.swift in Sources */, B848A4C5269EAAA200617031 /* UserDetailsSheet.swift in Sources */, 34B6A903218B3F63007C4606 /* TypingIndicatorView.swift in Sources */, diff --git a/Session/Conversations/ConversationVC+Interaction.swift b/Session/Conversations/ConversationVC+Interaction.swift index 48031c38d..7e3aec685 100644 --- a/Session/Conversations/ConversationVC+Interaction.swift +++ b/Session/Conversations/ConversationVC+Interaction.swift @@ -544,14 +544,7 @@ extension ConversationVC : InputViewDelegate, MessageCellDelegate, ContextMenuAc } else { guard let albumView = cell.albumView else { return } let locationInCell = gestureRecognizer.location(in: cell) - // Figure out whether the "read more" button was tapped - if let overlayView = cell.mediaTextOverlayView { - let locationInOverlayView = cell.convert(locationInCell, to: overlayView) - if let readMoreButton = overlayView.readMoreButton, readMoreButton.frame.contains(locationInOverlayView) { - return showFullText(viewItem) // HACK: This is a dirty way to do this - } - } - // Otherwise, figure out which of the media views was tapped + // Figure out which of the media views was tapped let locationInAlbumView = cell.convert(locationInCell, to: albumView) guard let mediaView = albumView.mediaView(forLocation: locationInAlbumView) else { return } if albumView.isMoreItemsView(mediaView: mediaView) && viewItem.mediaAlbumHasFailedAttachment() { @@ -590,10 +583,7 @@ extension ConversationVC : InputViewDelegate, MessageCellDelegate, ContextMenuAc navigationController!.present(shareVC, animated: true, completion: nil) } case .textOnlyMessage: - if let preview = viewItem.linkPreview, let urlAsString = preview.urlString, let url = URL(string: urlAsString) { - // Open the link preview URL - openURL(url) - } else if let reply = viewItem.quotedReply { + if let reply = viewItem.quotedReply { // Scroll to the source of the reply guard let indexPath = viewModel.ensureLoadWindowContainsQuotedReply(reply) else { return } messagesTableView.scrollToRow(at: indexPath, at: UITableView.ScrollPosition.middle, animated: true) diff --git a/Session/Conversations/Message Cells/Content Views/LinkPreviewView.swift b/Session/Conversations/Message Cells/Content Views/LinkPreviewView.swift index a9bd6f3c5..a2fe25fc5 100644 --- a/Session/Conversations/Message Cells/Content Views/LinkPreviewView.swift +++ b/Session/Conversations/Message Cells/Content Views/LinkPreviewView.swift @@ -12,6 +12,7 @@ final class LinkPreviewView : UIView { let isOutgoing = (viewItem!.interaction.interactionType() == .outgoingMessage) switch (isOutgoing, AppModeManager.shared.currentAppMode) { case (true, .dark), (false, .light): return .black + case (true, .light): return Colors.grey default: return .white } }() @@ -57,6 +58,8 @@ final class LinkPreviewView : UIView { result.addTarget(self, action: #selector(cancel), for: UIControl.Event.touchUpInside) return result }() + + var bodyTextView: UITextView? // MARK: Settings private static let loaderSize: CGFloat = 24 @@ -133,15 +136,7 @@ final class LinkPreviewView : UIView { loader.alpha = (image != nil) ? 0 : 1 if image != nil { loader.stopAnimating() } else { loader.startAnimating() } // Title - let isSent = (linkPreviewState is LinkPreviewSent) - let isOutgoing = (viewItem?.interaction.interactionType() == .outgoingMessage) - let textColor: UIColor - if isSent && isOutgoing && isLightMode { - textColor = .white - } else { - textColor = isDarkMode ? .white : .black - } - titleLabel.textColor = textColor + titleLabel.textColor = sentLinkPreviewTextColor titleLabel.text = linkPreviewState.title() // Horizontal stack view switch linkPreviewState { @@ -152,6 +147,7 @@ final class LinkPreviewView : UIView { bodyTextViewContainer.subviews.forEach { $0.removeFromSuperview() } if let viewItem = viewItem { let bodyTextView = VisibleMessageCell.getBodyTextView(for: viewItem, with: maxWidth, textColor: sentLinkPreviewTextColor, searchText: delegate.lastSearchedText, delegate: delegate) + self.bodyTextView = bodyTextView bodyTextViewContainer.addSubview(bodyTextView) bodyTextView.pin(to: bodyTextViewContainer, withInset: 12) } diff --git a/Session/Conversations/Message Cells/Content Views/MediaTextOverlayView.swift b/Session/Conversations/Message Cells/Content Views/MediaTextOverlayView.swift deleted file mode 100644 index a0c655a5f..000000000 --- a/Session/Conversations/Message Cells/Content Views/MediaTextOverlayView.swift +++ /dev/null @@ -1,74 +0,0 @@ -import UIKit - -/// Shown over a media message if it has a message body. -final class MediaTextOverlayView : UIView { - private let viewItem: ConversationViewItem - private let albumViewWidth: CGFloat - private let delegate: MessageCellDelegate - private let textColor: UIColor - var readMoreButton: UIButton? - - // MARK: Settings - private static let maxHeight: CGFloat = 88; - - // MARK: Lifecycle - init(viewItem: ConversationViewItem, albumViewWidth: CGFloat, textColor: UIColor, delegate: MessageCellDelegate) { - self.viewItem = viewItem - self.albumViewWidth = albumViewWidth - self.delegate = delegate - self.textColor = textColor - super.init(frame: CGRect.zero) - setUpViewHierarchy() - } - - override init(frame: CGRect) { - preconditionFailure("Use init(text:) instead.") - } - - required init?(coder: NSCoder) { - preconditionFailure("Use init(text:) instead.") - } - - private func setUpViewHierarchy() { - guard let message = viewItem.interaction as? TSMessage, let body = message.body, body.count > 0 else { return } - // Body label - let bodyLabel = UILabel() - bodyLabel.numberOfLines = 0 - bodyLabel.lineBreakMode = .byTruncatingTail - bodyLabel.text = given(body) { MentionUtilities.highlightMentions(in: $0, threadID: viewItem.interaction.uniqueThreadId) } - bodyLabel.textColor = self.textColor - bodyLabel.font = .systemFont(ofSize: Values.mediumFontSize) - // Content stack view - let contentStackView = UIStackView(arrangedSubviews: [ bodyLabel ]) - contentStackView.axis = .horizontal - contentStackView.spacing = Values.smallSpacing - addSubview(contentStackView) - let inset: CGFloat = 12 - contentStackView.pin(.left, to: .left, of: self, withInset: inset) - contentStackView.pin(.top, to: .top, of: self) - contentStackView.pin(.right, to: .right, of: self, withInset: -inset) - // Max height - bodyLabel.heightAnchor.constraint(lessThanOrEqualToConstant: MediaTextOverlayView.maxHeight).isActive = true - // Overflow button - let bodyLabelTargetSize = bodyLabel.sizeThatFits(CGSize(width: albumViewWidth - 2 * inset, height: .greatestFiniteMagnitude)) - if bodyLabelTargetSize.height > MediaTextOverlayView.maxHeight { - let readMoreButton = UIButton() - self.readMoreButton = readMoreButton - readMoreButton.setTitle("Read More", for: UIControl.State.normal) - readMoreButton.titleLabel!.font = .boldSystemFont(ofSize: Values.smallFontSize) - readMoreButton.setTitleColor(self.textColor, for: UIControl.State.normal) - readMoreButton.addTarget(self, action: #selector(readMore), for: UIControl.Event.touchUpInside) - addSubview(readMoreButton) - readMoreButton.pin(.left, to: .left, of: self, withInset: inset) - readMoreButton.pin(.top, to: .bottom, of: contentStackView, withInset: Values.smallSpacing) - readMoreButton.pin(.bottom, to: .bottom, of: self, withInset: -Values.smallSpacing) - } else { - contentStackView.pin(.bottom, to: .bottom, of: self, withInset: -inset) - } - } - - // MARK: Interaction - @objc private func readMore() { - delegate.showFullText(viewItem) - } -} diff --git a/Session/Conversations/Message Cells/VisibleMessageCell.swift b/Session/Conversations/Message Cells/VisibleMessageCell.swift index 114bf34b0..ee2f9658f 100644 --- a/Session/Conversations/Message Cells/VisibleMessageCell.swift +++ b/Session/Conversations/Message Cells/VisibleMessageCell.swift @@ -4,7 +4,6 @@ final class VisibleMessageCell : MessageCell, LinkPreviewViewDelegate { private var previousX: CGFloat = 0 var albumView: MediaAlbumView? var bodyTextView: UITextView? - var mediaTextOverlayView: MediaTextOverlayView? // Constraints private lazy var headerViewTopConstraint = headerView.pin(.top, to: .top, of: self, withInset: 1) private lazy var authorLabelHeightConstraint = authorLabel.set(.height, to: 0) @@ -313,7 +312,6 @@ final class VisibleMessageCell : MessageCell, LinkPreviewViewDelegate { } albumView = nil bodyTextView = nil - mediaTextOverlayView = nil let isOutgoing = (viewItem.interaction.interactionType() == .outgoingMessage) switch viewItem.messageCellType { case .textOnlyMessage: @@ -325,6 +323,7 @@ final class VisibleMessageCell : MessageCell, LinkPreviewViewDelegate { snContentView.addSubview(linkPreviewView) linkPreviewView.pin(to: snContentView) linkPreviewView.layer.mask = bubbleViewMaskLayer + self.bodyTextView = linkPreviewView.bodyTextView } else if let openGroupInvitationName = message.openGroupInvitationName, let openGroupInvitationURL = message.openGroupInvitationURL { let openGroupInvitationView = OpenGroupInvitationView(name: openGroupInvitationName, url: openGroupInvitationURL, textColor: bodyLabelTextColor, isOutgoing: isOutgoing) snContentView.addSubview(openGroupInvitationView) @@ -373,11 +372,12 @@ final class VisibleMessageCell : MessageCell, LinkPreviewViewDelegate { albumView.layer.mask = bubbleViewMaskLayer stackView.addArrangedSubview(albumView) // Body text view - if let message = viewItem.interaction as? TSMessage, let body = message.body, body.count > 0, - let delegate = delegate { // delegate should always be set at this point - let overlayView = MediaTextOverlayView(viewItem: viewItem, albumViewWidth: size.width, textColor: bodyLabelTextColor, delegate: delegate) - self.mediaTextOverlayView = overlayView - stackView.addArrangedSubview(overlayView) + if let message = viewItem.interaction as? TSMessage, let body = message.body, body.count > 0 { + let inset: CGFloat = 12 + let maxWidth = size.width - 2 * inset + let bodyTextView = VisibleMessageCell.getBodyTextView(for: viewItem, with: maxWidth, textColor: bodyLabelTextColor, searchText: delegate?.lastSearchedText, delegate: self) + self.bodyTextView = bodyTextView + stackView.addArrangedSubview(UIView(wrapping: bodyTextView, withInsets: UIEdgeInsets(top: 0, left: inset, bottom: inset, right: inset))) } unloadContent = { albumView.unloadMedia() } // Constraints diff --git a/Session/Shared/BaseVC.swift b/Session/Shared/BaseVC.swift index b2953ed36..0bccafb10 100644 --- a/Session/Shared/BaseVC.swift +++ b/Session/Shared/BaseVC.swift @@ -106,6 +106,9 @@ class BaseVC : UIViewController { } override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + if #available(iOS 13.0, *) { + SNLog("Current trait collection: \(UITraitCollection.current), previous trait collection: \(previousTraitCollection)") + } if LKAppModeUtilities.isSystemDefault { NotificationCenter.default.post(name: .appModeChanged, object: nil) } diff --git a/SessionMessagingKit/Sending & Receiving/Pollers/OpenGroupPollerV2.swift b/SessionMessagingKit/Sending & Receiving/Pollers/OpenGroupPollerV2.swift index 4c2ddbc8a..a1b3a47b3 100644 --- a/SessionMessagingKit/Sending & Receiving/Pollers/OpenGroupPollerV2.swift +++ b/SessionMessagingKit/Sending & Receiving/Pollers/OpenGroupPollerV2.swift @@ -48,6 +48,7 @@ public final class OpenGroupPollerV2 : NSObject { guard let self = self else { return } self.isPolling = false bodies.forEach { self.handleCompactPollBody($0, isBackgroundPoll: isBackgroundPoll) } + SNLog("Open group polling finished for \(self.server).") seal.fulfill(()) }.catch(on: OpenGroupAPIV2.workQueue) { error in SNLog("Open group polling failed due to error: \(error).") diff --git a/SessionNotificationServiceExtension/NSENotificationPresenter.swift b/SessionNotificationServiceExtension/NSENotificationPresenter.swift index a9aab1e44..148876540 100644 --- a/SessionNotificationServiceExtension/NSENotificationPresenter.swift +++ b/SessionNotificationServiceExtension/NSENotificationPresenter.swift @@ -13,14 +13,14 @@ public class NSENotificationPresenter: NSObject, NotificationsProtocol { // to check if this is the only message request thread (group threads can't be message requests // so just ignore those and if the user has hidden message requests then we want to show the // notification regardless of how many message requests there are) - if !thread.isGroupThread() && thread.isMessageRequest() && !CurrentAppContext().appUserDefaults()[.hasHiddenMessageRequests] { + if !thread.isGroupThread() && thread.isMessageRequest(using: transaction) && !CurrentAppContext().appUserDefaults()[.hasHiddenMessageRequests] { let threads = transaction.ext(TSThreadDatabaseViewExtensionName) as! YapDatabaseViewTransaction let numMessageRequests = threads.numberOfItems(inGroup: TSMessageRequestGroup) // Allow this to show a notification if there are no message requests (ie. this is the first one) guard numMessageRequests == 0 else { return } } - else if thread.isMessageRequest() && CurrentAppContext().appUserDefaults()[.hasHiddenMessageRequests] { + else if thread.isMessageRequest(using: transaction) && CurrentAppContext().appUserDefaults()[.hasHiddenMessageRequests] { // If there are other interactions on this thread already then don't show the notification if thread.numberOfInteractions(with: transaction) > 1 { return } @@ -37,7 +37,7 @@ public class NSENotificationPresenter: NSObject, NotificationsProtocol { } let context = Contact.context(for: thread) - let senderName = Storage.shared.getContact(with: senderPublicKey)?.displayName(for: context) ?? senderPublicKey + let senderName = Storage.shared.getContact(with: senderPublicKey, using: transaction)?.displayName(for: context) ?? senderPublicKey var notificationTitle = senderName if let group = thread as? TSGroupThread { @@ -85,7 +85,7 @@ public class NSENotificationPresenter: NSObject, NotificationsProtocol { // If it's a message request then overwrite the body to be something generic (only show a notification // when receiving a new message request if there aren't any others or the user had hidden them) - if thread.isMessageRequest() { + if thread.isMessageRequest(using: transaction) { notificationContent.title = "Session" notificationContent.body = "MESSAGE_REQUESTS_NOTIFICATION".localized() } @@ -93,8 +93,16 @@ public class NSENotificationPresenter: NSObject, NotificationsProtocol { // Add request let identifier = incomingMessage.notificationIdentifier ?? UUID().uuidString let request = UNNotificationRequest(identifier: identifier, content: notificationContent, trigger: nil) - SNLog("Add remote notification request") - UNUserNotificationCenter.current().add(request) + SNLog("Add remote notification request: \(notificationContent.body)") + let semaphore = DispatchSemaphore(value: 0) + UNUserNotificationCenter.current().add(request) { error in + if let error = error { + SNLog("Failed to add notification request due to error:\(error)") + } + semaphore.signal() + } + semaphore.wait() + SNLog("Finish adding remote notification request") } public func cancelNotification(_ identifier: String) { diff --git a/SessionNotificationServiceExtension/NotificationServiceExtension.swift b/SessionNotificationServiceExtension/NotificationServiceExtension.swift index d35de1ffe..c78342b43 100644 --- a/SessionNotificationServiceExtension/NotificationServiceExtension.swift +++ b/SessionNotificationServiceExtension/NotificationServiceExtension.swift @@ -41,7 +41,10 @@ public final class NotificationServiceExtension : UNNotificationServiceExtension let envelope = try? MessageWrapper.unwrap(data: data), let envelopeAsData = try? envelope.serializedData() else { return self.handleFailure(for: notificationContent) } - Storage.write { transaction in // Intentionally capture self + // HACK: It is important to use writeSync() here to avoid a race condition + // where the completeSilenty() is called before the local notification request + // is added to notification center. + Storage.writeSync { transaction in // Intentionally capture self do { let (message, proto) = try MessageReceiver.parse(envelopeAsData, openGroupMessageServerID: nil, using: transaction) switch message { diff --git a/SessionSnodeKit/OnionRequestAPI.swift b/SessionSnodeKit/OnionRequestAPI.swift index 61e617c72..7a03b16f0 100644 --- a/SessionSnodeKit/OnionRequestAPI.swift +++ b/SessionSnodeKit/OnionRequestAPI.swift @@ -204,7 +204,11 @@ public enum OnionRequestAPI { } else { return buildPaths(reusing: []).map2 { paths in if let snode = snode { - return paths.filter { !$0.contains(snode) }.randomElement()! + if let path = paths.filter({ !$0.contains(snode) }).randomElement() { + return path + } else { + throw Error.insufficientSnodes + } } else { return paths.randomElement()! }