GroupDetailViewController.swift 51 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949
  1. //
  2. // GroupViewController.swift
  3. // Qmera
  4. //
  5. // Created by Yayan Dwi on 24/09/21.
  6. //
  7. import UIKit
  8. import NotificationBannerSwift
  9. import nuSDKService
  10. class GroupDetailViewController: UITableViewController {
  11. enum Flag {
  12. case edit
  13. case view
  14. }
  15. var flag: Flag = .edit
  16. var data: String = ""
  17. static let SUBGROUP_LEVEL_LIMIT = 5
  18. private var group: Group?
  19. private var isAdmin: Bool = false
  20. private var imageVideoPicker : ImageVideoPicker!
  21. private var tempImage: UIImage?
  22. private let imageAdmin = UIImageView()
  23. private let imageAddContact = UIImageView()
  24. private enum Section {
  25. case profile
  26. case description
  27. case access
  28. case topic
  29. case detail
  30. case member
  31. case exit
  32. }
  33. private var sections: [Section] = [
  34. .profile,
  35. .description,
  36. .access,
  37. .topic,
  38. .detail
  39. ]
  40. var checkReadMessage: (() -> ())?
  41. private let idSubGroup = Date().currentTimeMillis().toHex()
  42. override func viewDidLoad() {
  43. super.viewDidLoad()
  44. imageVideoPicker = ImageVideoPicker(presentationController: self, delegate: self)
  45. let textAttributes = [NSAttributedString.Key.foregroundColor:UIColor.white]
  46. navigationController?.navigationBar.titleTextAttributes = textAttributes
  47. navigationController?.navigationBar.topItem?.backButtonTitle = ""
  48. reload()
  49. let center: NotificationCenter = NotificationCenter.default
  50. center.addObserver(self, selector: #selector(updateData(notification:)), name: NSNotification.Name(rawValue: "onGroup"), object: nil)
  51. center.addObserver(self, selector: #selector(updateData(notification:)), name: NSNotification.Name(rawValue: "onTopic"), object: nil)
  52. center.addObserver(self, selector: #selector(updateData(notification:)), name: NSNotification.Name(rawValue: "onMember"), object: nil)
  53. }
  54. override func viewWillDisappear(_ animated: Bool) {
  55. if self.isMovingFromParent {
  56. self.checkReadMessage?()
  57. }
  58. }
  59. var alert2: UIAlertController?
  60. var textFields = [UITextField]()
  61. // MARK: - Data source
  62. func reload() {
  63. getData { group in
  64. self.group = group
  65. if let myData = self.group?.members.first(where: { member in
  66. return member.pin == UserDefaults.standard.string(forKey: "me")!
  67. }) {
  68. if myData.position == "1" {
  69. self.isAdmin = true
  70. } else {
  71. self.isAdmin = false
  72. }
  73. }
  74. if self.sections.count == 5, group.groupType != "1" {
  75. self.sections.append(.member)
  76. self.sections.append(.exit)
  77. }
  78. DispatchQueue.main.async {
  79. self.title = group.name
  80. self.tableView.reloadData()
  81. if group.official == "1" && !group.parent.isEmpty {
  82. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  83. if let cursorImage = Database.shared.getRecords(fmdb: fmdb, query: "SELECT image_id FROM GROUPZ where group_type = 1 AND official = 1"), cursorImage.next() {
  84. self.group?.profile = cursorImage.string(forColumnIndex: 0)!
  85. cursorImage.close()
  86. }
  87. })
  88. }
  89. if self.isAdmin && group.official != "1" {
  90. var children : [UIAction] = []
  91. if Int(group.level)! <= GroupDetailViewController.SUBGROUP_LEVEL_LIMIT {
  92. children.append(UIAction(title: "Add Sub Group".localized(), handler: {(_) in
  93. self.createSubGroup()
  94. }))
  95. }
  96. children.append(UIAction(title: "Change Name Group".localized(), handler: {(_) in
  97. self.edit()
  98. }))
  99. let menu = UIMenu(title: "", children: children)
  100. let moreIcon = UIBarButtonItem(image: UIImage(systemName: "ellipsis"), menu: menu)
  101. self.navigationItem.rightBarButtonItem = moreIcon
  102. }
  103. }
  104. }
  105. }
  106. func createSubGroup() {
  107. self.alert2 = UIAlertController(title: "Create Sub Group".localized(), message: nil, preferredStyle: .alert)
  108. self.textFields.removeAll()
  109. self.alert2?.addTextField{ (texfield) in
  110. texfield.placeholder = "Group's Name"
  111. texfield.addTarget(self, action: #selector(self.alertTextFieldDidChange(_:)), for: UIControl.Event.editingChanged)
  112. }
  113. let submitAction = UIAlertAction(title: "Create".localized(), style: .default, handler: { (action) -> Void in
  114. let textField = self.alert2?.textFields![0]
  115. if !CheckConnection.isConnectedToNetwork() || API.nGetCLXConnState() == 0 {
  116. let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  117. imageView.tintColor = .white
  118. let banner = FloatingNotificationBanner(title: "Check your connection".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
  119. banner.show()
  120. return
  121. }
  122. var level = self.group!.level
  123. if level.isEmpty || level == "-1"{
  124. level = "2"
  125. } else {
  126. level = "\(Int(level)! + 1)"
  127. }
  128. print("level: \(level)")
  129. DispatchQueue.main.async {
  130. if let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getCreateSubGroup(group_id: self.idSubGroup, group_name: textField!.text!, parent_id: self.group!.id, level: level)) {
  131. if response.isOk() {
  132. let controller = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "groupDetailView") as! GroupDetailViewController
  133. controller.data = self.idSubGroup
  134. self.navigationController?.show(controller, sender: nil)
  135. self.navigationController?.viewControllers.removeSubrange(1...(self.navigationController?.viewControllers.count)! - 2)
  136. } else {
  137. let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  138. imageView.tintColor = .white
  139. let banner = FloatingNotificationBanner(title: "Unable to access servers".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
  140. banner.show()
  141. }
  142. }
  143. }
  144. })
  145. self.alert2?.addAction(submitAction)
  146. self.alert2?.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel, handler: nil))
  147. self.present(self.alert2!, animated: true, completion: nil)
  148. }
  149. func edit() {
  150. let controller = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "groupNameView") as! GroupNameViewController
  151. controller.data = self.data
  152. controller.name = group?.name
  153. controller.isDismiss = {
  154. self.reload()
  155. }
  156. navigationController?.present(UINavigationController(rootViewController: controller), animated: true, completion: nil)
  157. }
  158. @objc func updateData(notification: NSNotification) {
  159. let data:[AnyHashable : Any] = notification.userInfo!
  160. if data["code"] as! String == "A008" && data["member"] as! String == UserDefaults.standard.string(forKey: "me")! {
  161. DispatchQueue.main.async {
  162. self.navigationController?.popViewController(animated: true)
  163. }
  164. } else if data["f_pin"] as! String != UserDefaults.standard.string(forKey: "me")! || data["code"] as! String == "BD" {
  165. DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
  166. self.reload()
  167. }
  168. }
  169. }
  170. private func getData(completion: @escaping (Group) -> ()) {
  171. DispatchQueue.global().async {
  172. let query = "select g.group_id, g.f_name, g.image_id, g.quote, g.created_by, g.created_date, g.parent, g.group_type, g.is_open, g.official, g.level from GROUPZ g where g.group_id = '\(self.data)'"
  173. Database.shared.database?.inTransaction({ fmdb, rollback in
  174. if let cursor = Database.shared.getRecords(fmdb: fmdb, query: query), cursor.next() {
  175. let group = Group(id: cursor.string(forColumnIndex: 0) ?? "",
  176. name: cursor.string(forColumnIndex: 1) ?? "",
  177. profile: cursor.string(forColumnIndex: 2) ?? "",
  178. quote: cursor.string(forColumnIndex: 3) ?? "",
  179. by: cursor.string(forColumnIndex: 4) ?? "",
  180. date: cursor.string(forColumnIndex: 5) ?? "",
  181. parent: cursor.string(forColumnIndex: 6) ?? "",
  182. groupType: cursor.string(forColumnIndex: 7) ?? "",
  183. isOpen: cursor.string(forColumnIndex: 8) ?? "",
  184. official: cursor.string(forColumnIndex: 9) ?? "",
  185. level: cursor.string(forColumnIndex: 10) ?? "")
  186. cursor.close()
  187. group.topics.append(Topic(chatId: "", title: "Lounge".localized(), thumb: ""))
  188. if let cursorTopic = Database.shared.getRecords(fmdb: fmdb, query: "select chat_id, title, thumb from DISCUSSION_FORUM where group_id = '\(self.data)'") {
  189. while cursorTopic.next() {
  190. let topic = Topic(chatId: cursorTopic.string(forColumnIndex: 0) ?? "",
  191. title: cursorTopic.string(forColumnIndex: 1) ?? "",
  192. thumb: cursorTopic.string(forColumnIndex: 2) ?? "")
  193. group.topics.append(topic)
  194. }
  195. cursorTopic.close()
  196. }
  197. if let cursorMember = Database.shared.getRecords(fmdb: fmdb, query: "select f_pin, first_name, last_name, thumb_id, position from GROUPZ_MEMBER where group_id = '\(self.data)' order by 2 asc") {
  198. while cursorMember.next() {
  199. let member = Member(pin: cursorMember.string(forColumnIndex: 0) ?? "",
  200. firstName: cursorMember.string(forColumnIndex: 1) ?? "",
  201. lastName: cursorMember.string(forColumnIndex: 2) ?? "",
  202. thumb: cursorMember.string(forColumnIndex: 3) ?? "",
  203. position: cursorMember.string(forColumnIndex: 4) ?? "")
  204. if let cursorUser = Database.shared.getRecords(fmdb: fmdb, query: "SELECT user_type, official_account FROM BUDDY where f_pin='\(member.pin)'"), cursorUser.next() {
  205. member.userType = cursorUser.string(forColumnIndex: 0)
  206. member.official = cursorUser.string(forColumnIndex: 1)
  207. cursorUser.close()
  208. }
  209. group.members.append(member)
  210. }
  211. cursorMember.close()
  212. }
  213. completion(group)
  214. }
  215. })
  216. }
  217. }
  218. // MARK: - Cell selected
  219. override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
  220. tableView.deselectRow(at: indexPath, animated: false)
  221. switch sections[indexPath.section] {
  222. case .profile:
  223. if isAdmin {
  224. let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
  225. alert.addAction(UIAlertAction(title: "Take Photo".localized(), style: .default, handler: { action in
  226. self.imageVideoPicker.present(source: .imageCamera)
  227. }))
  228. alert.addAction(UIAlertAction(title: "Choose Photo".localized(), style: .default, handler: { action in
  229. self.imageVideoPicker.present(source: .imageAlbum)
  230. }))
  231. alert.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel, handler: { action in
  232. }))
  233. navigationController?.present(alert, animated: true)
  234. }
  235. case .description:
  236. if let g = group, isAdmin {
  237. let controller = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "groupDescView") as! GroupDescViewController
  238. controller.data = g.id
  239. controller.quote = g.quote
  240. controller.isDismiss = {
  241. self.reload()
  242. }
  243. let navController = UINavigationController(rootViewController: controller)
  244. navigationController?.present(navController, animated: true)
  245. }
  246. case .access:
  247. if let g = group, isAdmin, g.official != "1" {
  248. var currentAccess = g.isOpen
  249. if currentAccess.isEmpty || currentAccess == "0" {
  250. currentAccess = "1"
  251. } else {
  252. currentAccess = "0"
  253. }
  254. self.changeOpenGroup(open: currentAccess) { result in
  255. if result {
  256. DispatchQueue.main.async {
  257. g.isOpen = currentAccess
  258. tableView.reloadRows(at: [indexPath], with: .none)
  259. }
  260. }
  261. }
  262. }
  263. case .topic:
  264. if let g = group, isAdmin, indexPath.row == 0 {
  265. let controller = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "groupTopicView") as! GroupTopicViewController
  266. controller.data = g.id
  267. controller.isDismiss = {
  268. self.reload()
  269. }
  270. let navController = UINavigationController(rootViewController: controller)
  271. navigationController?.present(navController, animated: true)
  272. } else if let g = group, isAdmin {
  273. let topic = g.topics[indexPath.row - 1]
  274. if topic.chatId.isEmpty {
  275. if let controller = self.previousViewController as? EditorGroup {
  276. if controller.dataTopic["chat_id"] as! String == topic.chatId {
  277. self.navigationController?.popViewController(animated: true)
  278. return
  279. }
  280. controller.unique_l_pin = g.id
  281. controller.loadData()
  282. self.navigationController?.popViewController(animated: true)
  283. } else {
  284. self.navigationController?.popViewController(animated: true)
  285. let editorGroupVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorGroupVC") as! EditorGroup
  286. editorGroupVC.hidesBottomBarWhenPushed = true
  287. editorGroupVC.unique_l_pin = g.id
  288. self.navigationController?.viewControllers.first?.show(editorGroupVC, sender: nil)
  289. }
  290. return
  291. }
  292. let alert = UIAlertController(title: nil, message: topic.title, preferredStyle: .actionSheet)
  293. alert.addAction(UIAlertAction(title: "Enter Topic", style: .default, handler: { action in
  294. if let controller = self.previousViewController as? EditorGroup {
  295. if controller.dataTopic["chat_id"] as! String == topic.chatId {
  296. self.navigationController?.popViewController(animated: true)
  297. return
  298. }
  299. controller.unique_l_pin = topic.chatId
  300. controller.loadData()
  301. self.navigationController?.popViewController(animated: true)
  302. } else {
  303. self.navigationController?.popViewController(animated: true)
  304. let editorGroupVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorGroupVC") as! EditorGroup
  305. editorGroupVC.hidesBottomBarWhenPushed = true
  306. editorGroupVC.unique_l_pin = topic.chatId
  307. self.navigationController?.viewControllers.first?.show(editorGroupVC, sender: nil)
  308. }
  309. }))
  310. alert.addAction(UIAlertAction(title: "Rename Topic", style: .default, handler: { action in
  311. self.alert2 = UIAlertController(title: "Change Topic's Name".localized(), message: nil, preferredStyle: .alert)
  312. self.textFields.removeAll()
  313. self.alert2?.addTextField{ (texfield) in
  314. texfield.text = topic.title
  315. texfield.placeholder = "Topic's Name"
  316. texfield.addTarget(self, action: #selector(self.alertTextFieldDidChange(_:)), for: UIControl.Event.editingChanged)
  317. }
  318. let submitAction = UIAlertAction(title: "Rename".localized(), style: .default, handler: { (action) -> Void in
  319. let textField = self.alert2?.textFields![0]
  320. if !CheckConnection.isConnectedToNetwork() || API.nGetCLXConnState() == 0 {
  321. let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  322. imageView.tintColor = .white
  323. let banner = FloatingNotificationBanner(title: "Check your connection".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
  324. banner.show()
  325. return
  326. }
  327. if textField!.text! == topic.title {
  328. let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  329. imageView.tintColor = .white
  330. let banner = FloatingNotificationBanner(title: "Topic name has been used. Enter another topic name".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
  331. banner.show()
  332. return
  333. }
  334. if textField!.text! == "Lounge" || textField!.text! == "Beranda" {
  335. let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  336. imageView.tintColor = .white
  337. let banner = FloatingNotificationBanner(title: "Topic already registered".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
  338. banner.show()
  339. return
  340. }
  341. DispatchQueue.main.async {
  342. if let g = self.group, let _ = Nexilis.write(message: CoreMessage_TMessageBank.getUpdateChat(p_chat_id: topic.chatId, p_f_pin: g.id, p_title: textField!.text!, p_anonym: "", p_image: topic.thumb)) {
  343. let imageView = UIImageView(image: UIImage(systemName: "checkmark.circle.fill"))
  344. imageView.tintColor = .white
  345. let banner = FloatingNotificationBanner(title: "Successfully changed".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .success, colors: nil, iconPosition: .center)
  346. banner.show()
  347. }
  348. }
  349. })
  350. self.alert2?.addAction(submitAction)
  351. self.alert2?.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel, handler: nil))
  352. self.present(self.alert2!, animated: true, completion: nil)
  353. }))
  354. alert.addAction(UIAlertAction(title: "Remove Topic".localized(), style: .destructive, handler: { action in
  355. let message = "Remove \(topic.title) from the \"\(g.name)\" group?"
  356. let notif = UIAlertController(title: nil, message: message, preferredStyle: .actionSheet)
  357. notif.addAction(UIAlertAction(title: "Remove".localized(), style: .destructive, handler: { notifAction in
  358. self.removeTopic(chatId: topic.chatId) { result in
  359. if result, let index = g.topics.firstIndex(of: topic) {
  360. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  361. _ = Database.shared.deleteRecord(fmdb: fmdb, table: "DISCUSSION_FORUM", _where: "chat_id = '\(topic.chatId)'")
  362. _ = Database.shared.deleteRecord(fmdb: fmdb, table: "MESSAGE", _where: "chat_id='\(topic.chatId)'")
  363. _ = Database.shared.deleteRecord(fmdb: fmdb, table: "MESSAGE_SUMMARY", _where: "l_pin='\(topic.chatId)'")
  364. })
  365. var data: [AnyHashable : Any] = [:]
  366. data["code"] = CoreMessage_TMessageCode.DELETE_CHAT
  367. data["f_pin"] = UserDefaults.standard.string(forKey: "me")!
  368. data["topicId"] = topic.chatId
  369. NotificationCenter.default.post(name: NSNotification.Name(rawValue: "onTopic"), object: nil, userInfo: data)
  370. DispatchQueue.main.async {
  371. tableView.beginUpdates()
  372. tableView.deleteRows(at: [indexPath], with: .none)
  373. g.topics.remove(at: index)
  374. tableView.endUpdates()
  375. }
  376. }
  377. }
  378. }))
  379. notif.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel, handler: { notifAction in
  380. }))
  381. self.navigationController?.present(notif, animated: true, completion: nil)
  382. }))
  383. alert.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel, handler: { action in
  384. }))
  385. navigationController?.present(alert, animated: true)
  386. } else if let g = group {
  387. let topic = g.topics[indexPath.row]
  388. if topic.chatId.isEmpty {
  389. if let controller = self.previousViewController as? EditorGroup {
  390. if controller.dataTopic["chat_id"] as! String == topic.chatId {
  391. self.navigationController?.popViewController(animated: true)
  392. return
  393. }
  394. controller.unique_l_pin = g.id
  395. controller.loadData()
  396. self.navigationController?.popViewController(animated: true)
  397. } else {
  398. self.navigationController?.popViewController(animated: true)
  399. let editorGroupVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorGroupVC") as! EditorGroup
  400. editorGroupVC.hidesBottomBarWhenPushed = true
  401. editorGroupVC.unique_l_pin = g.id
  402. self.navigationController?.viewControllers.first?.show(editorGroupVC, sender: nil)
  403. }
  404. return
  405. }
  406. else if let controller = self.previousViewController as? EditorGroup {
  407. if controller.dataTopic["chat_id"] as! String == topic.chatId {
  408. self.navigationController?.popViewController(animated: true)
  409. return
  410. }
  411. controller.unique_l_pin = topic.chatId
  412. controller.loadData()
  413. self.navigationController?.popViewController(animated: true)
  414. } else {
  415. self.navigationController?.popViewController(animated: true)
  416. let editorGroupVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorGroupVC") as! EditorGroup
  417. editorGroupVC.hidesBottomBarWhenPushed = true
  418. editorGroupVC.unique_l_pin = topic.chatId
  419. self.navigationController?.viewControllers.first?.show(editorGroupVC, sender: nil)
  420. }
  421. }
  422. case .member:
  423. if let g = group, isAdmin, indexPath.row == 0 {
  424. let controller = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "groupMemberView") as! GroupMemberViewController
  425. controller.group = g
  426. if g.official == "1" && g.name == "Customer Service" {
  427. controller.isContactCenterInvite = true
  428. }
  429. controller.isDismiss = {
  430. self.reload()
  431. }
  432. let navController = UINavigationController(rootViewController: controller)
  433. navigationController?.present(navController, animated: true)
  434. } else if let g = group, isAdmin {
  435. let member = g.members[indexPath.row - 1]
  436. if member.pin != UserDefaults.standard.string(forKey: "me")! {
  437. if member.pin == g.by {
  438. let data = User.getData(pin: member.pin)
  439. let controller = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "profileView") as! ProfileViewController
  440. controller.flag = data == nil ? .invite : .friend
  441. controller.user = member
  442. controller.name = member.fullName
  443. controller.data = member.pin
  444. controller.picture = member.thumb
  445. self.navigationController?.show(controller, sender: nil)
  446. return
  447. }
  448. let alert = UIAlertController(title: nil, message: "\(member.firstName) \(member.lastName)", preferredStyle: .actionSheet)
  449. alert.addAction(UIAlertAction(title: "Info".localized(), style: .default, handler: { action in
  450. let data = User.getData(pin: member.pin)
  451. let controller = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "profileView") as! ProfileViewController
  452. controller.flag = data == nil ? .invite : .friend
  453. controller.user = member
  454. controller.name = member.fullName
  455. controller.data = member.pin
  456. controller.picture = member.thumb
  457. controller.isDismiss = {
  458. self.reload()
  459. }
  460. self.navigationController?.show(controller, sender: nil)
  461. }))
  462. alert.addAction(UIAlertAction(title: member.position == "0" ? "Make Group Admin".localized() : "Remove Group Admin".localized(), style: member.position == "0" ? .default : .destructive, handler: { action in
  463. let message = "\(member.position == "0" ? "Make" : "Remove") \(member.fullName) from the \"\(g.name)\" group Admin?"
  464. let notif = UIAlertController(title: nil, message: message, preferredStyle: .actionSheet)
  465. notif.addAction(UIAlertAction(title: member.position == "0" ? "Make".localized() : "Remove".localized(), style: member.position == "0" ? .default : .destructive, handler: { notifAction in
  466. self.changePosition(pin: member.pin, isAdmin: member.position == "0") { result in
  467. if result {
  468. DispatchQueue.main.async {
  469. tableView.beginUpdates()
  470. member.position = member.position == "1" ? "0" : "1"
  471. tableView.reloadRows(at: [indexPath], with: .none)
  472. tableView.endUpdates()
  473. }
  474. }
  475. }
  476. }))
  477. notif.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel, handler: { notifAction in
  478. }))
  479. self.navigationController?.present(notif, animated: true, completion: nil)
  480. }))
  481. alert.addAction(UIAlertAction(title: "Remove From Group".localized(), style: .destructive, handler: { action in
  482. let message = "Remove \(member.fullName) from the \"\(g.name)\" group?"
  483. let notif = UIAlertController(title: nil, message: message, preferredStyle: .actionSheet)
  484. notif.addAction(UIAlertAction(title: "Remove".localized(), style: .destructive, handler: { notifAction in
  485. self.exitGroup(pin: member.pin) { result in
  486. if result, let index = g.members.firstIndex(of: member) {
  487. DispatchQueue.main.async {
  488. tableView.beginUpdates()
  489. tableView.deleteRows(at: [indexPath], with: .none)
  490. g.members.remove(at: index)
  491. tableView.endUpdates()
  492. }
  493. }
  494. }
  495. }))
  496. notif.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel, handler: { notifAction in
  497. }))
  498. self.navigationController?.present(notif, animated: true, completion: nil)
  499. }))
  500. alert.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel, handler: { action in
  501. }))
  502. navigationController?.present(alert, animated: true)
  503. }
  504. } else if let g = group {
  505. let member = g.members[indexPath.row]
  506. if member.pin == UserDefaults.standard.string(forKey: "me") {
  507. // skip self profile
  508. return
  509. }
  510. let data = User.getData(pin: member.pin)
  511. let controller = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "profileView") as! ProfileViewController
  512. controller.flag = data == nil ? .invite : .friend
  513. controller.user = member
  514. controller.name = member.fullName
  515. controller.data = member.pin
  516. controller.picture = member.thumb
  517. controller.isDismiss = {
  518. self.reload()
  519. }
  520. self.navigationController?.show(controller, sender: nil)
  521. }
  522. case .exit:
  523. if let g = group {
  524. let idMe = UserDefaults.standard.string(forKey: "me") as String?
  525. let admins = g.members.filter { member in
  526. return member.position == "1"
  527. }
  528. var isDeleted = false
  529. if admins.count == 1 {
  530. if admins.first?.pin == idMe {
  531. isDeleted = true
  532. }
  533. }
  534. let message: String
  535. if isDeleted {
  536. message = "Are you sure want to delete the \"\(g.name)\" group?".localized()
  537. } else {
  538. message = "Are you sure want to exit the \"\(g.name)\" group?".localized()
  539. }
  540. let alert = UIAlertController(title: nil, message: message, preferredStyle: .actionSheet)
  541. alert.addAction(UIAlertAction(title: isDeleted ? "Delete Group".localized() : "Exit Group".localized(), style: .destructive, handler: { action in
  542. self.exitGroup(pin: isDeleted ? "ALL": UserDefaults.standard.string(forKey: "me")!) { result in
  543. if result {
  544. DispatchQueue.main.async {
  545. self.navigationController?.popToRootViewController(animated: true)
  546. }
  547. }
  548. }
  549. }))
  550. alert.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel, handler: { action in
  551. }))
  552. navigationController?.present(alert, animated: true)
  553. }
  554. default:
  555. print("No handler..")
  556. }
  557. }
  558. @objc func alertTextFieldDidChange(_ sender: UITextField) {
  559. if(!textFields.isEmpty){
  560. alert2?.actions[0].isEnabled = textFields[0].text!.trimmingCharacters(in: .whitespaces).count > 0
  561. }
  562. else {
  563. alert2?.actions[0].isEnabled = sender.text!.trimmingCharacters(in: .whitespaces).count > 0
  564. }
  565. }
  566. private func removeTopic(chatId: String, completion: @escaping (Bool) -> ()) {
  567. DispatchQueue.global().async {
  568. var result: Bool = false
  569. if let g = self.group, let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getDeleteChat(chat_id: chatId, f_pin: g.id)), response.isOk() {
  570. result = true
  571. }
  572. completion(result)
  573. }
  574. }
  575. private func changePosition(pin: String, isAdmin: Bool = true, completion: @escaping (Bool) -> ()) {
  576. DispatchQueue.global().async {
  577. var result: Bool = false
  578. if let g = self.group, let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getChangeGroupMemberPosition(p_group_id: g.id, p_pin: pin, p_position: isAdmin ? "1" : "0")), response.isOk() {
  579. result = true
  580. }
  581. completion(result)
  582. }
  583. }
  584. private func exitGroup(pin: String, completion: @escaping (Bool) -> ()) {
  585. DispatchQueue.global().async {
  586. var result: Bool = false
  587. if let g = self.group, let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getExitGroup(p_group_id: g.id, p_pin: pin)), response.isOk() {
  588. result = true
  589. }
  590. completion(result)
  591. }
  592. }
  593. private func changeOpenGroup(open: String, completion: @escaping (Bool) -> ()) {
  594. DispatchQueue.global().async {
  595. var result: Bool = false
  596. if let g = self.group, let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getChangeGroupInfo(p_group_id: g.id, p_open: open)), response.isOk() {
  597. result = true
  598. }
  599. completion(result)
  600. }
  601. }
  602. private func checkIsFriend(pin: String) -> Bool {
  603. var isFriend = true
  604. Database.shared.database?.inTransaction({ fmdb, rollback in
  605. if let cursor = Database.shared.getRecords(fmdb: fmdb, query: "select f_pin from BUDDY where f_pin = '\(pin)'"), cursor.next() {
  606. cursor.close()
  607. } else {
  608. isFriend = false
  609. }
  610. })
  611. return isFriend
  612. }
  613. // MARK: - Table view data source
  614. override func numberOfSections(in tableView: UITableView) -> Int {
  615. return sections.count
  616. }
  617. override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  618. guard let g = group else {
  619. return 1
  620. }
  621. switch sections[section] {
  622. case .topic:
  623. return isAdmin ? g.topics.count + 1 : g.topics.count
  624. case .detail:
  625. return 3
  626. case .member:
  627. return isAdmin ? g.members.count + 1 : g.members.count
  628. default:
  629. return 1
  630. }
  631. }
  632. override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
  633. switch sections[section] {
  634. case .description:
  635. return "Description".localized()
  636. case .topic:
  637. return "Topic".localized()
  638. case .detail:
  639. return "Detail".localized()
  640. case .member:
  641. return "Member".localized()
  642. default:
  643. return nil
  644. }
  645. }
  646. override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  647. switch sections[indexPath.section] {
  648. case .profile:
  649. let cell = tableView.dequeueReusableCell(withIdentifier: "profileCell", for: indexPath) as! ProfileCell
  650. cell.cover.image = UIImage(named: "Sofa", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
  651. guard let g = group else {
  652. return cell
  653. }
  654. if let image = tempImage {
  655. cell.profile.image = image
  656. } else {
  657. getImage(name: g.profile, placeholderImage: UIImage(systemName: "person.2.circle.fill"), tableView: tableView, indexPath: indexPath) { result, isDownloaded, image in
  658. cell.profile.image = image
  659. }
  660. }
  661. return cell
  662. case .description:
  663. let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
  664. cell.accessoryView = nil
  665. cell.accessoryType = .disclosureIndicator
  666. var content = cell.defaultContentConfiguration()
  667. if let g = group {
  668. content.text = g.quote.isEmpty ? "No description".localized() : g.quote
  669. }
  670. cell.contentConfiguration = content
  671. cell.selectionStyle = .default
  672. return cell
  673. case .access:
  674. let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
  675. var content = cell.defaultContentConfiguration()
  676. if let g = group {
  677. if g.isOpen.isEmpty || g.isOpen == "0" {
  678. content.text = "Private".localized()
  679. content.secondaryText = "Only members can access this group".localized()
  680. } else if g.isOpen == "1" {
  681. content.text = "Public".localized()
  682. content.secondaryText = "All user can access this group".localized()
  683. }
  684. if isAdmin && group?.official != "1" {
  685. let changeOpen = UISwitch()
  686. changeOpen.isUserInteractionEnabled = false
  687. if g.isOpen == "1" {
  688. changeOpen.setOn(true, animated: true)
  689. } else {
  690. changeOpen.setOn(false, animated: true)
  691. }
  692. cell.accessoryView = changeOpen
  693. }
  694. }
  695. content.textProperties.color = .mainColor
  696. cell.contentConfiguration = content
  697. cell.accessoryType = .none
  698. return cell
  699. case .topic:
  700. let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
  701. cell.accessoryView = nil
  702. cell.accessoryType = .none
  703. var content = cell.defaultContentConfiguration()
  704. if let g = group {
  705. if indexPath.row == 0, isAdmin {
  706. content.image = UIImage(systemName: "plus.circle")
  707. content.imageProperties.tintColor = .mainColor
  708. content.text = "Add topic".localized()
  709. cell.accessoryType = .disclosureIndicator
  710. cell.selectionStyle = .default
  711. } else {
  712. let topic = g.topics[isAdmin ? indexPath.row - 1 : indexPath.row]
  713. getImage(name: topic.thumb, placeholderImage: UIImage(systemName: "message.fill"), isCircle: true, tableView: tableView, indexPath: indexPath) { result, isDownloaded, image in
  714. content.image = image
  715. if !result {
  716. content.imageProperties.tintColor = .mainColor
  717. }
  718. }
  719. content.text = topic.title
  720. content.secondaryText = topic.description
  721. cell.selectionStyle = isAdmin ? .default : .none
  722. }
  723. }
  724. cell.contentConfiguration = content
  725. return cell
  726. case .detail:
  727. let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
  728. cell.accessoryView = nil
  729. cell.accessoryType = .none
  730. cell.selectionStyle = .none
  731. var content = cell.defaultContentConfiguration()
  732. content.prefersSideBySideTextAndSecondaryText = true
  733. if let g = group {
  734. switch indexPath.row {
  735. case 1:
  736. content.text = "Crated date".localized()
  737. content.secondaryText = Date(milliseconds: Int64(g.date)!).format(dateFormat: "dd, MMM yyyy")
  738. case 2:
  739. content.text = "Members".localized()
  740. content.secondaryText = String(g.members.count)
  741. default:
  742. content.text = "Crated by".localized()
  743. Database.shared.database?.inTransaction({ fmdb, rollback in
  744. if let cursor = Database.shared.getRecords(fmdb: fmdb, query: "select first_name || ' ' || ifnull(last_name, '') from BUDDY where f_pin = '\(g.by)'") {
  745. if cursor.next() {
  746. content.secondaryText = cursor.string(forColumnIndex: 0) ?? "Unknown".localized()
  747. }
  748. cursor.close()
  749. }
  750. })
  751. }
  752. }
  753. cell.contentConfiguration = content
  754. return cell
  755. case .member:
  756. let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
  757. cell.selectionStyle = isAdmin ? .default : .none
  758. cell.accessoryView = nil
  759. var content = cell.defaultContentConfiguration()
  760. content.prefersSideBySideTextAndSecondaryText = true
  761. if let g = group {
  762. if indexPath.row == 0, isAdmin {
  763. content.image = UIImage(systemName: "plus.circle")
  764. content.imageProperties.tintColor = .mainColor
  765. content.text = "Add member".localized()
  766. cell.accessoryType = .disclosureIndicator
  767. } else {
  768. let member = g.members[isAdmin ? indexPath.row - 1 : indexPath.row]
  769. content.imageProperties.maximumSize = CGSize(width: 20, height: 20)
  770. getImage(name: member.thumb, placeholderImage: UIImage(systemName: "person.fill"), isCircle: true, tableView: tableView, indexPath: indexPath) { result, isDownloaded, image in
  771. content.image = image
  772. if !result {
  773. content.imageProperties.tintColor = .mainColor
  774. }
  775. }
  776. if member.userType == "23" || member.official == "1" {
  777. content.attributedText = self.set(image: UIImage(named: "ic_internal", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, with: " " + (member.firstName + " " + member.lastName).trimmingCharacters(in: .whitespaces), size: 15, y: 0)
  778. } else if member.userType == "24" {
  779. content.attributedText = self.set(image: UIImage(named: "pb_call_center", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, with: " " + (member.firstName + " " + member.lastName).trimmingCharacters(in: .whitespaces), size: 15, y: 0)
  780. } else {
  781. content.text = (member.firstName + " " + member.lastName).trimmingCharacters(in: .whitespaces)
  782. }
  783. if !checkIsFriend(pin: member.pin) {
  784. if member.position == "1" {
  785. content.secondaryAttributedText = self.set(image: UIImage(named: "pb_twsn_group_admin_11", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, image2: UIImage(named: "pb_add_contact", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, with: "", size: 20, y: 0, moreImage: true)
  786. } else {
  787. content.secondaryAttributedText = self.set(image: UIImage(named: "pb_add_contact", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, with: "", size: 20, y: 0)
  788. }
  789. } else {
  790. content.secondaryAttributedText = member.position == "1" ? self.set(image: UIImage(named: "pb_twsn_group_admin_11", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, with: "", size: 20, y: 0) : NSAttributedString(string: "")
  791. }
  792. cell.accessoryType = .none
  793. }
  794. }
  795. cell.contentConfiguration = content
  796. return cell
  797. case .exit:
  798. let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
  799. cell.accessoryView = nil
  800. cell.accessoryType = .none
  801. var content = cell.defaultContentConfiguration()
  802. content.text = "Exit Group".localized()
  803. content.textProperties.color = .red
  804. cell.contentConfiguration = content
  805. cell.selectionStyle = .default
  806. return cell
  807. }
  808. }
  809. override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
  810. switch sections[indexPath.section] {
  811. case .profile:
  812. return 200
  813. default:
  814. return UITableView.automaticDimension
  815. }
  816. }
  817. }
  818. // MARK: - Cell
  819. class ProfileCell: UITableViewCell {
  820. @IBOutlet weak var cover: UIImageView!
  821. @IBOutlet weak var profile: UIImageView!
  822. override func layoutSubviews() {
  823. super.layoutSubviews()
  824. profile.circle()
  825. }
  826. }
  827. // MARK: - Extension
  828. extension GroupDetailViewController: ImageVideoPickerDelegate {
  829. func didSelect(imagevideo: Any?) {
  830. if let info = imagevideo as? [UIImagePickerController.InfoKey: Any], let cell = tableView.cellForRow(at: IndexPath(row: 0, section: 0)) as? ProfileCell, let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
  831. guard let g = group else {
  832. return
  833. }
  834. self.tempImage = image
  835. cell.profile.image = self.tempImage
  836. DispatchQueue.global().async {
  837. let resize = image.resize(target: CGSize(width: 800, height: 600))
  838. let documentDir = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
  839. let fileDir = documentDir.appendingPathComponent("THUMB_\(g.id)\(Date().currentTimeMillis().toHex())")
  840. if !FileManager.default.fileExists(atPath: fileDir.path), let data = resize.jpegData(compressionQuality: 0.8) {
  841. try! data.write(to: fileDir)
  842. Network().upload(name: fileDir.lastPathComponent) { result, progress in
  843. guard result, progress == 100 else {
  844. return
  845. }
  846. if let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getChangeGroupInfo(p_group_id: g.id, p_thumb_id: fileDir.lastPathComponent)), response.isOk() {
  847. Database.shared.database?.inTransaction({ fmdb, rollback in
  848. _ = Database.shared.updateRecord(fmdb: fmdb, table: "GROUPZ", cvalues: ["image_id": fileDir.lastPathComponent], _where: "group_id = '\(g.id)'")
  849. })
  850. }
  851. }
  852. }
  853. }
  854. }
  855. }
  856. func set(image: UIImage, image2: UIImage = UIImage(), with text: String, size: CGFloat, y: CGFloat, moreImage: Bool = false) -> NSAttributedString {
  857. let attachment = NSTextAttachment()
  858. let attachment2 = NSTextAttachment()
  859. attachment.image = image
  860. attachment.bounds = CGRect(x: 0, y: y, width: size, height: size)
  861. attachment2.image = image2
  862. attachment2.bounds = CGRect(x: 0, y: y, width: size, height: size)
  863. let attachmentStr = NSAttributedString(attachment: attachment)
  864. let attachmentStr2 = NSAttributedString(attachment: attachment2)
  865. let mutableAttributedString = NSMutableAttributedString()
  866. if moreImage {
  867. mutableAttributedString.append(attachmentStr2)
  868. }
  869. mutableAttributedString.append(attachmentStr)
  870. let textString = NSAttributedString(string: text)
  871. mutableAttributedString.append(textString)
  872. return mutableAttributedString
  873. }
  874. }