ShareViewController.swift 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. import UIKit
  2. import Social
  3. import DcCore
  4. import MobileCoreServices
  5. import Intents
  6. import SDWebImageWebPCoder
  7. import SDWebImage
  8. class ShareViewController: SLComposeServiceViewController {
  9. class SimpleLogger: Logger {
  10. func verbose(_ message: String) {
  11. print("ShareViewController", "verbose", message)
  12. }
  13. func debug(_ message: String) {
  14. print("ShareViewController", "debug", message)
  15. }
  16. func info(_ message: String) {
  17. print("ShareViewController", "info", message)
  18. }
  19. func warning(_ message: String) {
  20. print("ShareViewController", "warning", message)
  21. }
  22. func error(_ message: String) {
  23. print("ShareViewController", "error", message)
  24. }
  25. }
  26. let logger = SimpleLogger()
  27. let dcContext = DcContext.shared
  28. var selectedChatId: Int?
  29. var selectedChat: DcChat?
  30. let dbHelper = DatabaseHelper()
  31. var shareAttachment: ShareAttachment?
  32. var isAccountConfigured: Bool = true
  33. lazy var preview: SDAnimatedImageView? = {
  34. let imageView = SDAnimatedImageView(frame: .zero)
  35. imageView.clipsToBounds = true
  36. imageView.shouldGroupAccessibilityChildren = true
  37. imageView.isAccessibilityElement = false
  38. imageView.contentMode = .scaleAspectFit
  39. imageView.constraintHeightTo(96).isActive = true
  40. imageView.constraintWidthTo(96).isActive = true
  41. return imageView
  42. }()
  43. override func viewDidLoad() {
  44. super.viewDidLoad()
  45. setupNavigationBar()
  46. // workaround for iOS13 bug
  47. if #available(iOS 13.0, *) {
  48. _ = NotificationCenter.default.addObserver(forName: UIResponder.keyboardDidShowNotification, object: nil, queue: .main) { (_) in
  49. if let layoutContainerView = self.view.subviews.last {
  50. layoutContainerView.frame.size.height += 10
  51. }
  52. }
  53. }
  54. placeholder = String.localized("chat_input_placeholder")
  55. let webPCoder = SDImageWebPCoder.shared
  56. SDImageCodersManager.shared.addCoder(webPCoder)
  57. }
  58. override func presentationAnimationDidFinish() {
  59. if dbHelper.currentDatabaseLocation == dbHelper.sharedDbFile {
  60. dcContext.logger = self.logger
  61. dcContext.openDatabase(dbFile: dbHelper.sharedDbFile)
  62. isAccountConfigured = dcContext.isConfigured()
  63. if isAccountConfigured {
  64. if #available(iOSApplicationExtension 13.0, *) {
  65. if let intent = self.extensionContext?.intent as? INSendMessageIntent, let chatId = Int(intent.conversationIdentifier ?? "") {
  66. selectedChatId = chatId
  67. }
  68. }
  69. if selectedChatId == nil {
  70. selectedChatId = dcContext.getChatIdByContactId(contactId: Int(DC_CONTACT_ID_SELF))
  71. }
  72. if let chatId = selectedChatId {
  73. selectedChat = dcContext.getChat(chatId: chatId)
  74. }
  75. DispatchQueue.global(qos: .background).async {
  76. self.shareAttachment = ShareAttachment(dcContext: self.dcContext, inputItems: self.extensionContext?.inputItems, delegate: self)
  77. }
  78. }
  79. reloadConfigurationItems()
  80. validateContent()
  81. } else {
  82. cancel()
  83. }
  84. }
  85. override func loadPreviewView() -> UIView! {
  86. return preview
  87. }
  88. override func isContentValid() -> Bool {
  89. // Do validation of contentText and/or NSExtensionContext attachments here
  90. return isAccountConfigured && (!(contentText?.isEmpty ?? true) || !(self.shareAttachment?.isEmpty ?? true))
  91. }
  92. private func setupNavigationBar() {
  93. guard let item = navigationController?.navigationBar.items?.first else { return }
  94. let button = UIBarButtonItem(
  95. title: String.localized("menu_send"),
  96. style: .done,
  97. target: self,
  98. action: #selector(appendPostTapped))
  99. item.rightBarButtonItem? = button
  100. }
  101. /// Invoked when the user wants to post.
  102. @objc
  103. private func appendPostTapped() {
  104. if let chatId = self.selectedChatId {
  105. guard var messages = shareAttachment?.messages else { return }
  106. if !self.contentText.isEmpty {
  107. if messages.count == 1 {
  108. messages[0].text?.append(self.contentText)
  109. } else {
  110. let message = DcMsg(viewType: DC_MSG_TEXT)
  111. message.text = self.contentText
  112. messages.insert(message, at: 0)
  113. }
  114. }
  115. let chatListController = SendingController(chatId: chatId, dcMsgs: messages, dcContext: dcContext)
  116. chatListController.delegate = self
  117. self.pushConfigurationViewController(chatListController)
  118. }
  119. }
  120. func quit() {
  121. if dbHelper.currentDatabaseLocation == dbHelper.sharedDbFile {
  122. dcContext.closeDatabase()
  123. }
  124. // Inform the host that we're done, so it un-blocks its UI.
  125. self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
  126. }
  127. override func configurationItems() -> [Any]! {
  128. let item = SLComposeSheetConfigurationItem()
  129. if isAccountConfigured {
  130. logger.debug("configurationItems")
  131. // To add configuration options via table cells at the bottom of the sheet, return an array of SLComposeSheetConfigurationItem here.
  132. item?.title = String.localized("forward_to")
  133. item?.value = selectedChat?.name
  134. logger.debug("configurationItems chat name: \(String(describing: selectedChat?.name))")
  135. item?.tapHandler = {
  136. let chatListController = ChatListController(dcContext: self.dcContext, chatListDelegate: self)
  137. self.pushConfigurationViewController(chatListController)
  138. }
  139. } else {
  140. item?.title = String.localized("share_account_not_configured")
  141. }
  142. return [item as Any]
  143. }
  144. override func didSelectCancel() {
  145. quit()
  146. }
  147. }
  148. extension ShareViewController: ChatListDelegate {
  149. func onChatSelected(chatId: Int) {
  150. selectedChatId = chatId
  151. selectedChat = dcContext.getChat(chatId: chatId)
  152. reloadConfigurationItems()
  153. popConfigurationViewController()
  154. }
  155. }
  156. extension ShareViewController: SendingControllerDelegate {
  157. func onSendingAttemptFinished() {
  158. DispatchQueue.main.async {
  159. self.popConfigurationViewController()
  160. UserDefaults.shared?.set(true, forKey: UserDefaults.hasExtensionAttemptedToSend)
  161. self.quit()
  162. }
  163. }
  164. }
  165. extension ShareViewController: ShareAttachmentDelegate {
  166. func onUrlShared(url: URL) {
  167. DispatchQueue.main.async {
  168. if var contentText = self.contentText, !contentText.isEmpty {
  169. contentText.append("\n\(url.absoluteString)")
  170. self.textView.text = contentText
  171. } else {
  172. self.textView.text = "\(url.absoluteString)"
  173. }
  174. }
  175. }
  176. func onAttachmentChanged() {
  177. DispatchQueue.main.async {
  178. self.validateContent()
  179. }
  180. }
  181. func onThumbnailChanged() {
  182. DispatchQueue.main.async {
  183. if let preview = self.preview {
  184. preview.image = self.shareAttachment?.thumbnail ?? nil
  185. }
  186. }
  187. }
  188. }