LongTextViewController.swift 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. //
  2. // Copyright (c) 2019 Open Whisper Systems. All rights reserved.
  3. //
  4. import Foundation
  5. import SignalServiceKit
  6. import SignalMessaging
  7. @objc
  8. public protocol LongTextViewDelegate {
  9. @objc
  10. func longTextViewMessageWasDeleted(_ longTextViewController: LongTextViewController)
  11. }
  12. @objc
  13. public class LongTextViewController: OWSViewController {
  14. // MARK: - Dependencies
  15. var uiDatabaseConnection: YapDatabaseConnection {
  16. return OWSPrimaryStorage.shared().uiDatabaseConnection
  17. }
  18. // MARK: - Properties
  19. @objc
  20. weak var delegate: LongTextViewDelegate?
  21. let viewItem: ConversationViewItem
  22. var messageTextView: UITextView!
  23. var displayableText: DisplayableText? {
  24. return viewItem.displayableBodyText
  25. }
  26. var fullText: String {
  27. return displayableText?.fullText ?? ""
  28. }
  29. // MARK: Initializers
  30. @available(*, unavailable, message:"use other constructor instead.")
  31. public required init?(coder aDecoder: NSCoder) {
  32. notImplemented()
  33. }
  34. @objc
  35. public required init(viewItem: ConversationViewItem) {
  36. self.viewItem = viewItem
  37. super.init(nibName: nil, bundle: nil)
  38. }
  39. // MARK: View Lifecycle
  40. public override func viewDidLoad() {
  41. super.viewDidLoad()
  42. self.navigationItem.title = NSLocalizedString("LONG_TEXT_VIEW_TITLE",
  43. comment: "Title for the 'long text message' view.")
  44. createViews()
  45. self.messageTextView.contentOffset = CGPoint(x: 0, y: self.messageTextView.contentInset.top)
  46. NotificationCenter.default.addObserver(self,
  47. selector: #selector(uiDatabaseDidUpdate),
  48. name: .OWSUIDatabaseConnectionDidUpdate,
  49. object: OWSPrimaryStorage.shared().dbNotificationObject)
  50. }
  51. // MARK: - DB
  52. @objc internal func uiDatabaseDidUpdate(notification: NSNotification) {
  53. AssertIsOnMainThread()
  54. guard let notifications = notification.userInfo?[OWSUIDatabaseConnectionNotificationsKey] as? [Notification] else {
  55. owsFailDebug("notifications was unexpectedly nil")
  56. return
  57. }
  58. guard let uniqueId = self.viewItem.interaction.uniqueId else {
  59. Logger.error("Message is missing uniqueId.")
  60. return
  61. }
  62. guard self.uiDatabaseConnection.hasChange(forKey: uniqueId,
  63. inCollection: TSInteraction.collection(),
  64. in: notifications) else {
  65. Logger.debug("No relevant changes.")
  66. return
  67. }
  68. do {
  69. try uiDatabaseConnection.read { transaction in
  70. guard TSInteraction.fetch(uniqueId: uniqueId, transaction: transaction) != nil else {
  71. Logger.error("Message was deleted")
  72. throw LongTextViewError.messageWasDeleted
  73. }
  74. }
  75. } catch LongTextViewError.messageWasDeleted {
  76. DispatchQueue.main.async {
  77. self.delegate?.longTextViewMessageWasDeleted(self)
  78. }
  79. } catch {
  80. owsFailDebug("unexpected error: \(error)")
  81. }
  82. }
  83. enum LongTextViewError: Error {
  84. case messageWasDeleted
  85. }
  86. // MARK: - Create Views
  87. private func createViews() {
  88. view.backgroundColor = Theme.backgroundColor
  89. let messageTextView = OWSTextView()
  90. self.messageTextView = messageTextView
  91. messageTextView.font = UIFont.ows_dynamicTypeBody
  92. messageTextView.backgroundColor = Theme.backgroundColor
  93. messageTextView.isOpaque = true
  94. messageTextView.isEditable = false
  95. messageTextView.isSelectable = true
  96. messageTextView.isScrollEnabled = true
  97. messageTextView.showsHorizontalScrollIndicator = false
  98. messageTextView.showsVerticalScrollIndicator = true
  99. messageTextView.isUserInteractionEnabled = true
  100. messageTextView.textColor = Theme.primaryColor
  101. if let displayableText = displayableText {
  102. messageTextView.text = fullText
  103. messageTextView.ensureShouldLinkifyText(displayableText.shouldAllowLinkification)
  104. } else {
  105. owsFailDebug("displayableText was unexpectedly nil")
  106. messageTextView.text = ""
  107. }
  108. // RADAR #18669
  109. // https://github.com/lionheart/openradar-mirror/issues/18669
  110. //
  111. // UITextView’s linkTextAttributes property has type [String : Any]! but should be [NSAttributedStringKey : Any]! in Swift 4.
  112. let linkTextAttributes: [String: Any] = [
  113. NSAttributedStringKey.foregroundColor.rawValue: Theme.primaryColor,
  114. NSAttributedStringKey.underlineColor.rawValue: Theme.primaryColor,
  115. NSAttributedStringKey.underlineStyle.rawValue: NSUnderlineStyle.styleSingle.rawValue
  116. ]
  117. messageTextView.linkTextAttributes = linkTextAttributes
  118. view.addSubview(messageTextView)
  119. messageTextView.autoPinEdge(toSuperviewEdge: .top)
  120. messageTextView.autoPinEdge(toSuperviewEdge: .leading)
  121. messageTextView.autoPinEdge(toSuperviewEdge: .trailing)
  122. messageTextView.textContainerInset = UIEdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16)
  123. let footer = UIToolbar()
  124. view.addSubview(footer)
  125. footer.autoPinWidthToSuperview()
  126. footer.autoPinEdge(.top, to: .bottom, of: messageTextView)
  127. footer.autoPin(toBottomLayoutGuideOf: self, withInset: 0)
  128. footer.items = [
  129. UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil),
  130. UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(shareButtonPressed)),
  131. UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
  132. ]
  133. }
  134. // MARK: - Actions
  135. @objc func shareButtonPressed() {
  136. AttachmentSharing.showShareUI(forText: fullText)
  137. }
  138. }