GroupChatDetailViewController.swift 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. import UIKit
  2. class GroupChatDetailViewController: UIViewController {
  3. private let sectionConfig = 0
  4. private let sectionMembers = 1
  5. private let sectionLeaveGroup = 2
  6. private let sectionMembersRowAddMember = 0
  7. private let sectionMembersRowJoinQR = 1
  8. private var currentUser: DcContact? {
  9. return groupMembers.filter { $0.email == DcConfig.addr }.first
  10. }
  11. weak var coordinator: GroupChatDetailCoordinator?
  12. fileprivate var chat: DcChat
  13. var chatDetailTable: UITableView = {
  14. let table = UITableView(frame: .zero, style: .grouped)
  15. table.bounces = false
  16. table.register(UITableViewCell.self, forCellReuseIdentifier: "tableCell")
  17. table.register(ActionCell.self, forCellReuseIdentifier: "actionCell")
  18. table.register(ContactCell.self, forCellReuseIdentifier: "contactCell")
  19. return table
  20. }()
  21. init(chatId: Int) {
  22. chat = DcChat(id: chatId)
  23. super.init(nibName: nil, bundle: nil)
  24. setupSubviews()
  25. }
  26. required init?(coder _: NSCoder) {
  27. fatalError("init(coder:) has not been implemented")
  28. }
  29. private func setupSubviews() {
  30. view.addSubview(chatDetailTable)
  31. chatDetailTable.translatesAutoresizingMaskIntoConstraints = false
  32. chatDetailTable.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
  33. chatDetailTable.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
  34. chatDetailTable.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
  35. chatDetailTable.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
  36. }
  37. private func showNotificationSetup() {
  38. let notificationSetupAlert = UIAlertController(title: "Notifications Setup is not implemented yet",
  39. message: "But you get an idea where this is going",
  40. preferredStyle: .actionSheet)
  41. let cancelAction = UIAlertAction(title: String.localized("cancel"), style: .cancel, handler: nil)
  42. notificationSetupAlert.addAction(cancelAction)
  43. present(notificationSetupAlert, animated: true, completion: nil)
  44. }
  45. private lazy var editBarButtonItem: UIBarButtonItem = {
  46. UIBarButtonItem(title: String.localized("global_menu_edit_desktop"), style: .plain, target: self, action: #selector(editButtonPressed))
  47. }()
  48. private var groupMembers: [DcContact] = []
  49. private let staticCellCountMemberSection = 2
  50. override func viewDidLoad() {
  51. super.viewDidLoad()
  52. title = String.localized("tab_group")
  53. chatDetailTable.delegate = self
  54. chatDetailTable.dataSource = self
  55. navigationItem.rightBarButtonItem = editBarButtonItem
  56. }
  57. override func viewWillAppear(_ animated: Bool) {
  58. super.viewWillAppear(animated)
  59. updateGroupMembers()
  60. chatDetailTable.reloadData() // to display updates
  61. editBarButtonItem.isEnabled = currentUser != nil
  62. }
  63. private func updateGroupMembers() {
  64. let ids = chat.contactIds
  65. groupMembers = ids.map { DcContact(id: $0) }
  66. chatDetailTable.reloadData()
  67. }
  68. @objc func editButtonPressed() {
  69. coordinator?.showGroupChatEdit(chat: chat)
  70. }
  71. private func leaveGroup() {
  72. if let userId = currentUser?.id {
  73. let alert = UIAlertController(title: String.localized("ask_leave_group"), message: nil, preferredStyle: .actionSheet)
  74. alert.addAction(UIAlertAction(title: String.localized("menu_leave_group"), style: .destructive, handler: { _ in
  75. dc_remove_contact_from_chat(mailboxPointer, UInt32(self.chat.id), UInt32(userId))
  76. self.editBarButtonItem.isEnabled = false
  77. self.updateGroupMembers()
  78. }))
  79. alert.addAction(UIAlertAction(title: String.localized("cancel"), style: .cancel, handler: nil))
  80. present(alert, animated: true, completion: nil)
  81. }
  82. }
  83. }
  84. extension GroupChatDetailViewController: UITableViewDelegate, UITableViewDataSource {
  85. func tableView(_: UITableView, titleForHeaderInSection section: Int) -> String? {
  86. if section == 1 {
  87. return String.localized("tab_members")
  88. }
  89. return nil
  90. }
  91. func tableView(_: UITableView, viewForHeaderInSection section: Int) -> UIView? {
  92. if section == sectionConfig {
  93. let header = ContactDetailHeader()
  94. header.updateDetails(title: chat.name, subtitle: String.localizedStringWithFormat(NSLocalizedString("n_members", comment: ""), chat.contactIds.count))
  95. if let img = chat.profileImage {
  96. header.setImage(img)
  97. } else {
  98. header.setBackupImage(name: chat.name, color: chat.color)
  99. }
  100. header.setVerified(isVerified: chat.isVerified)
  101. return header
  102. } else {
  103. return nil
  104. }
  105. }
  106. func numberOfSections(in _: UITableView) -> Int {
  107. /*
  108. section 0: config
  109. section 1: members
  110. section 2: leave group (optional - if user already left group this option will be hidden)
  111. */
  112. if currentUser == nil {
  113. return 2
  114. }
  115. return 3
  116. }
  117. func tableView(_: UITableView, numberOfRowsInSection section: Int) -> Int {
  118. switch section {
  119. case sectionConfig:
  120. return 1
  121. case sectionMembers:
  122. return groupMembers.count + staticCellCountMemberSection
  123. case sectionLeaveGroup:
  124. return 1
  125. default:
  126. return 0
  127. }
  128. }
  129. func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  130. let section = indexPath.section
  131. let row = indexPath.row
  132. switch section {
  133. case sectionConfig:
  134. let cell = tableView.dequeueReusableCell(withIdentifier: "tableCell", for: indexPath)
  135. cell.textLabel?.text = String.localized("pref_notifications")
  136. cell.selectionStyle = .none
  137. return cell
  138. case sectionMembers:
  139. switch row {
  140. case sectionMembersRowAddMember:
  141. let cell = tableView.dequeueReusableCell(withIdentifier: "actionCell", for: indexPath)
  142. if let actionCell = cell as? ActionCell {
  143. actionCell.actionTitle = String.localized("group_add_members")
  144. actionCell.actionColor = UIColor.systemBlue
  145. }
  146. return cell
  147. case sectionMembersRowJoinQR:
  148. let cell = tableView.dequeueReusableCell(withIdentifier: "actionCell", for: indexPath)
  149. if let actionCell = cell as? ActionCell {
  150. actionCell.actionTitle = String.localized("qrshow_join_group_title")
  151. actionCell.actionColor = UIColor.systemBlue
  152. }
  153. return cell
  154. default:
  155. let cell = tableView.dequeueReusableCell(withIdentifier: "contactCell", for: indexPath)
  156. if let contactCell = cell as? ContactCell {
  157. let contact = groupMembers[row - staticCellCountMemberSection]
  158. let displayName = contact.displayName
  159. contactCell.nameLabel.text = displayName
  160. contactCell.emailLabel.text = contact.email
  161. contactCell.initialsLabel.text = Utils.getInitials(inputName: displayName)
  162. contactCell.setColor(contact.color)
  163. contactCell.setVerified(isVerified: chat.isVerified)
  164. }
  165. return cell
  166. }
  167. case sectionLeaveGroup:
  168. let cell = tableView.dequeueReusableCell(withIdentifier: "actionCell", for: indexPath)
  169. if let actionCell = cell as? ActionCell {
  170. actionCell.actionTitle = String.localized("menu_leave_group")
  171. actionCell.actionColor = UIColor.red
  172. }
  173. return cell
  174. default:
  175. return UITableViewCell(frame: .zero)
  176. }
  177. }
  178. func tableView(_: UITableView, didSelectRowAt indexPath: IndexPath) {
  179. let section = indexPath.section
  180. let row = indexPath.row
  181. if section == sectionConfig {
  182. showNotificationSetup()
  183. } else if section == sectionMembers {
  184. if row == sectionMembersRowAddMember {
  185. coordinator?.showAddGroupMember(chatId: chat.id)
  186. } else if row == sectionMembersRowJoinQR {
  187. coordinator?.showQrCodeInvite(chatId: chat.id)
  188. } else {
  189. let contact = getGroupMember(at: row)
  190. coordinator?.showContactDetail(of: contact.id)
  191. }
  192. // ignore for now - in Telegram tapping a contactCell leads into ContactDetail
  193. } else if section == sectionLeaveGroup {
  194. leaveGroup()
  195. }
  196. }
  197. func tableView(_: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
  198. let section = indexPath.section
  199. let row = indexPath.row
  200. if let currentUser = currentUser {
  201. if section == sectionMembers, row >= staticCellCountMemberSection, groupMembers[row - staticCellCountMemberSection].id != currentUser.id {
  202. return true
  203. }
  204. }
  205. return false
  206. }
  207. func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
  208. let section = indexPath.section
  209. let row = indexPath.row
  210. // assigning swipe by delete to members (except for current user)
  211. if section == sectionMembers, row >= staticCellCountMemberSection, groupMembers[row - staticCellCountMemberSection].id != currentUser?.id {
  212. let delete = UITableViewRowAction(style: .destructive, title: String.localized("remove_desktop")) { [unowned self] _, indexPath in
  213. let contact = self.getGroupMember(at: row)
  214. let title = String.localizedStringWithFormat(String.localized("ask_remove_members"), contact.nameNAddr)
  215. let alert = UIAlertController(title: title, message: nil, preferredStyle: .actionSheet)
  216. alert.addAction(UIAlertAction(title: String.localized("remove_desktop"), style: .destructive, handler: { _ in
  217. let success = dc_remove_contact_from_chat(mailboxPointer, UInt32(self.chat.id), UInt32(contact.id))
  218. if success == 1 {
  219. self.groupMembers.remove(at: row - self.staticCellCountMemberSection)
  220. tableView.deleteRows(at: [indexPath], with: .fade)
  221. tableView.reloadData()
  222. }
  223. }))
  224. alert.addAction(UIAlertAction(title: String.localized("cancel"), style: .cancel, handler: nil))
  225. self.present(alert, animated: true, completion: nil)
  226. }
  227. delete.backgroundColor = UIColor.red
  228. return [delete]
  229. } else {
  230. return nil
  231. }
  232. }
  233. func getGroupMember(at row: Int) -> DcContact {
  234. let memberId = self.groupMembers[row - self.staticCellCountMemberSection].id
  235. return DcContact(id: memberId)
  236. }
  237. }