diff --git a/Session.xcodeproj/project.pbxproj b/Session.xcodeproj/project.pbxproj index e4268b9f0..0eb91a319 100644 --- a/Session.xcodeproj/project.pbxproj +++ b/Session.xcodeproj/project.pbxproj @@ -217,6 +217,7 @@ B8041AA725C90927003C2166 /* TypingIndicatorCellV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8041AA625C90927003C2166 /* TypingIndicatorCellV2.swift */; }; B80A579F23DFF1F300876683 /* NewClosedGroupVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B80A579E23DFF1F300876683 /* NewClosedGroupVC.swift */; }; B821494625D4D6FF009C0F2A /* URLModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B821494525D4D6FF009C0F2A /* URLModal.swift */; }; + B821494F25D4E163009C0F2A /* BodyTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B821494E25D4E163009C0F2A /* BodyTextView.swift */; }; B8269D2925C7A4B400488AB4 /* InputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8269D2825C7A4B400488AB4 /* InputView.swift */; }; B8269D3325C7A8C600488AB4 /* InputViewButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8269D3225C7A8C600488AB4 /* InputViewButton.swift */; }; B8269D3D25C7B34D00488AB4 /* InputTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8269D3C25C7B34D00488AB4 /* InputTextView.swift */; }; @@ -1263,6 +1264,7 @@ B8041AA625C90927003C2166 /* TypingIndicatorCellV2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypingIndicatorCellV2.swift; sourceTree = ""; }; B80A579E23DFF1F300876683 /* NewClosedGroupVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewClosedGroupVC.swift; sourceTree = ""; }; B821494525D4D6FF009C0F2A /* URLModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLModal.swift; sourceTree = ""; }; + B821494E25D4E163009C0F2A /* BodyTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BodyTextView.swift; sourceTree = ""; }; B8269D2825C7A4B400488AB4 /* InputView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputView.swift; sourceTree = ""; }; B8269D3225C7A8C600488AB4 /* InputViewButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputViewButton.swift; sourceTree = ""; }; B8269D3C25C7B34D00488AB4 /* InputTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputTextView.swift; sourceTree = ""; }; @@ -2236,6 +2238,7 @@ children = ( B897621B25D201F7004F83B2 /* ScrollToBottomButton.swift */, B821494525D4D6FF009C0F2A /* URLModal.swift */, + B821494E25D4E163009C0F2A /* BodyTextView.swift */, ); path = "Views & Modals"; sourceTree = ""; @@ -5121,6 +5124,7 @@ B88847BC23E10BC6009836D2 /* GroupMembersVC.swift in Sources */, B85357BF23A1AE0800AAF6CD /* SeedReminderView.swift in Sources */, B83786802586D296003CE78E /* KeyPairMigrationSheet.swift in Sources */, + B821494F25D4E163009C0F2A /* BodyTextView.swift in Sources */, C35E8AAE2485E51D00ACB629 /* IP2Country.swift in Sources */, B835249B25C3AB650089A44F /* VisibleMessageCell.swift in Sources */, 340FC8AE204DAC8D007AEB0F /* OWSSoundSettingsViewController.m in Sources */, diff --git a/Session/Conversations V2/ConversationVC.swift b/Session/Conversations V2/ConversationVC.swift index 3a3dee2eb..4856daeec 100644 --- a/Session/Conversations V2/ConversationVC.swift +++ b/Session/Conversations V2/ConversationVC.swift @@ -6,7 +6,7 @@ // • Blocking // • Subtitle // • Resending failed messages -// • Linkification +// • Tapping links in link previews // • Link previews // • Animation glitch when leaving conversation (probably because vc is resigning first responder) // • Timestamps diff --git a/Session/Conversations V2/Message Cells/Content Views/LinkView.swift b/Session/Conversations V2/Message Cells/Content Views/LinkView.swift index c152e9a8c..74d5dff80 100644 --- a/Session/Conversations V2/Message Cells/Content Views/LinkView.swift +++ b/Session/Conversations V2/Message Cells/Content Views/LinkView.swift @@ -2,7 +2,7 @@ final class LinkView : UIView { private let viewItem: ConversationViewItem private let maxWidth: CGFloat - private let delegate: UITextViewDelegate + private let delegate: UITextViewDelegate & BodyTextViewDelegate private var textColor: UIColor { let isOutgoing = (viewItem.interaction.interactionType() == .outgoingMessage) @@ -14,7 +14,7 @@ final class LinkView : UIView { private static let imageSize: CGFloat = 100 - init(for viewItem: ConversationViewItem, maxWidth: CGFloat, delegate: UITextViewDelegate) { + init(for viewItem: ConversationViewItem, maxWidth: CGFloat, delegate: UITextViewDelegate & BodyTextViewDelegate) { self.viewItem = viewItem self.maxWidth = maxWidth self.delegate = delegate @@ -86,4 +86,8 @@ final class LinkView : UIView { addSubview(vStackView) vStackView.pin(to: self) } + + func handleLongPress() { + + } } diff --git a/Session/Conversations V2/Message Cells/VisibleMessageCell.swift b/Session/Conversations V2/Message Cells/VisibleMessageCell.swift index 1e16675f6..62e1d747b 100644 --- a/Session/Conversations V2/Message Cells/VisibleMessageCell.swift +++ b/Session/Conversations V2/Message Cells/VisibleMessageCell.swift @@ -1,5 +1,5 @@ -final class VisibleMessageCell : MessageCell, UITextViewDelegate { +final class VisibleMessageCell : MessageCell, UITextViewDelegate, BodyTextViewDelegate { private var unloadContent: (() -> Void)? var albumView: MediaAlbumView? var bodyTextView: UITextView? @@ -317,7 +317,7 @@ final class VisibleMessageCell : MessageCell, UITextViewDelegate { return super.hitTest(point, with: event) } - @objc private func handleLongPress() { + @objc func handleLongPress() { guard let viewItem = viewItem else { return } delegate?.handleViewItemLongPressed(viewItem) } @@ -426,10 +426,10 @@ final class VisibleMessageCell : MessageCell, UITextViewDelegate { return isGroupThread && viewItem.shouldShowSenderProfilePicture && senderSessionID != nil } - static func getBodyTextView(for viewItem: ConversationViewItem, with availableWidth: CGFloat, textColor: UIColor, delegate: UITextViewDelegate) -> UITextView { + static func getBodyTextView(for viewItem: ConversationViewItem, with availableWidth: CGFloat, textColor: UIColor, delegate: UITextViewDelegate & BodyTextViewDelegate) -> UITextView { guard let message = viewItem.interaction as? TSMessage else { preconditionFailure() } let isOutgoing = (message.interactionType() == .outgoingMessage) - let result = UITextView() + let result = BodyTextView(snDelegate: delegate) result.isEditable = false let attributes: [NSAttributedString.Key:Any] = [ .foregroundColor : textColor, diff --git a/Session/Conversations V2/Views & Modals/BodyTextView.swift b/Session/Conversations V2/Views & Modals/BodyTextView.swift new file mode 100644 index 000000000..3048db56d --- /dev/null +++ b/Session/Conversations V2/Views & Modals/BodyTextView.swift @@ -0,0 +1,49 @@ + +// Requirements: +// • Links should show up properly and be tappable. +// • Text should * not * be selectable. +// • The long press interaction that shows the context menu should still work. + +final class BodyTextView : UITextView { + private let snDelegate: BodyTextViewDelegate + + override var selectedTextRange: UITextRange? { + get { return nil } + set { } + } + + init(snDelegate: BodyTextViewDelegate) { + self.snDelegate = snDelegate + super.init(frame: CGRect.zero, textContainer: nil) + setUpGestureRecognizers() + } + + override init(frame: CGRect, textContainer: NSTextContainer?) { + preconditionFailure("Use init(snDelegate:) instead.") + } + + required init?(coder: NSCoder) { + preconditionFailure("Use init(snDelegate:) instead.") + } + + private func setUpGestureRecognizers() { + let longPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress)) + addGestureRecognizer(longPressGestureRecognizer) + let doubleTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleDoubleTap)) + doubleTapGestureRecognizer.numberOfTapsRequired = 2 + addGestureRecognizer(doubleTapGestureRecognizer) + } + + @objc private func handleLongPress() { + snDelegate.handleLongPress() + } + + @objc private func handleDoubleTap() { + // Do nothing + } +} + +protocol BodyTextViewDelegate { + + func handleLongPress() +}