GroupDetailViewController.swift 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963
  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".localized()
  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. Nexilis.showLoader()
  255. self.changeOpenGroup(open: currentAccess) { result in
  256. if result {
  257. DispatchQueue.main.async {
  258. g.isOpen = currentAccess
  259. tableView.reloadRows(at: [indexPath], with: .none)
  260. Nexilis.hideLoader()
  261. }
  262. } else {
  263. DispatchQueue.main.async {
  264. Nexilis.hideLoader()
  265. let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  266. imageView.tintColor = .white
  267. 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)
  268. banner.show()
  269. }
  270. }
  271. }
  272. }
  273. case .topic:
  274. if let g = group, isAdmin, indexPath.row == 0 {
  275. let controller = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "groupTopicView") as! GroupTopicViewController
  276. controller.data = g.id
  277. controller.isDismiss = {
  278. self.reload()
  279. }
  280. let navController = UINavigationController(rootViewController: controller)
  281. navigationController?.present(navController, animated: true)
  282. } else if let g = group, isAdmin {
  283. let topic = g.topics[indexPath.row - 1]
  284. if topic.chatId.isEmpty {
  285. if let controller = self.previousViewController as? EditorGroup {
  286. if controller.dataTopic["chat_id"] as! String == topic.chatId {
  287. self.navigationController?.popViewController(animated: true)
  288. return
  289. }
  290. controller.unique_l_pin = g.id
  291. controller.loadData()
  292. self.navigationController?.popViewController(animated: true)
  293. } else {
  294. self.navigationController?.popViewController(animated: true)
  295. let editorGroupVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorGroupVC") as! EditorGroup
  296. editorGroupVC.hidesBottomBarWhenPushed = true
  297. editorGroupVC.unique_l_pin = g.id
  298. self.navigationController?.viewControllers.first?.show(editorGroupVC, sender: nil)
  299. }
  300. return
  301. }
  302. let alert = UIAlertController(title: nil, message: topic.title, preferredStyle: .actionSheet)
  303. alert.addAction(UIAlertAction(title: "Enter Topic", style: .default, handler: { action in
  304. if let controller = self.previousViewController as? EditorGroup {
  305. if controller.dataTopic["chat_id"] as! String == topic.chatId {
  306. self.navigationController?.popViewController(animated: true)
  307. return
  308. }
  309. controller.unique_l_pin = topic.chatId
  310. controller.loadData()
  311. self.navigationController?.popViewController(animated: true)
  312. } else {
  313. self.navigationController?.popViewController(animated: true)
  314. let editorGroupVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorGroupVC") as! EditorGroup
  315. editorGroupVC.hidesBottomBarWhenPushed = true
  316. editorGroupVC.unique_l_pin = topic.chatId
  317. self.navigationController?.viewControllers.first?.show(editorGroupVC, sender: nil)
  318. }
  319. }))
  320. alert.addAction(UIAlertAction(title: "Rename Topic", style: .default, handler: { action in
  321. self.alert2 = UIAlertController(title: "Change Topic's Name".localized(), message: nil, preferredStyle: .alert)
  322. self.textFields.removeAll()
  323. self.alert2?.addTextField{ (texfield) in
  324. texfield.text = topic.title
  325. texfield.placeholder = "Topic's Name"
  326. texfield.addTarget(self, action: #selector(self.alertTextFieldDidChange(_:)), for: UIControl.Event.editingChanged)
  327. }
  328. let submitAction = UIAlertAction(title: "Rename".localized(), style: .default, handler: { (action) -> Void in
  329. let textField = self.alert2?.textFields![0]
  330. if !CheckConnection.isConnectedToNetwork() || API.nGetCLXConnState() == 0 {
  331. let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  332. imageView.tintColor = .white
  333. 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)
  334. banner.show()
  335. return
  336. }
  337. if textField!.text! == topic.title {
  338. let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  339. imageView.tintColor = .white
  340. 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)
  341. banner.show()
  342. return
  343. }
  344. if textField!.text! == "Lounge" || textField!.text! == "Beranda" {
  345. let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  346. imageView.tintColor = .white
  347. 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)
  348. banner.show()
  349. return
  350. }
  351. DispatchQueue.main.async {
  352. 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)) {
  353. let imageView = UIImageView(image: UIImage(systemName: "checkmark.circle.fill"))
  354. imageView.tintColor = .white
  355. 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)
  356. banner.show()
  357. }
  358. }
  359. })
  360. self.alert2?.addAction(submitAction)
  361. self.alert2?.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel, handler: nil))
  362. self.present(self.alert2!, animated: true, completion: nil)
  363. }))
  364. alert.addAction(UIAlertAction(title: "Remove Topic".localized(), style: .destructive, handler: { action in
  365. let message = "Remove \(topic.title) from the \"\(g.name)\" group?"
  366. let notif = UIAlertController(title: nil, message: message, preferredStyle: .actionSheet)
  367. notif.addAction(UIAlertAction(title: "Remove".localized(), style: .destructive, handler: { notifAction in
  368. self.removeTopic(chatId: topic.chatId) { result in
  369. if result, let index = g.topics.firstIndex(of: topic) {
  370. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  371. _ = Database.shared.deleteRecord(fmdb: fmdb, table: "DISCUSSION_FORUM", _where: "chat_id = '\(topic.chatId)'")
  372. _ = Database.shared.deleteRecord(fmdb: fmdb, table: "MESSAGE", _where: "chat_id='\(topic.chatId)'")
  373. _ = Database.shared.deleteRecord(fmdb: fmdb, table: "MESSAGE_SUMMARY", _where: "l_pin='\(topic.chatId)'")
  374. })
  375. var data: [AnyHashable : Any] = [:]
  376. data["code"] = CoreMessage_TMessageCode.DELETE_CHAT
  377. data["f_pin"] = UserDefaults.standard.string(forKey: "me")!
  378. data["topicId"] = topic.chatId
  379. NotificationCenter.default.post(name: NSNotification.Name(rawValue: "onTopic"), object: nil, userInfo: data)
  380. DispatchQueue.main.async {
  381. tableView.beginUpdates()
  382. tableView.deleteRows(at: [indexPath], with: .none)
  383. g.topics.remove(at: index)
  384. tableView.endUpdates()
  385. }
  386. }
  387. }
  388. }))
  389. notif.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel, handler: { notifAction in
  390. }))
  391. self.navigationController?.present(notif, animated: true, completion: nil)
  392. }))
  393. alert.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel, handler: { action in
  394. }))
  395. navigationController?.present(alert, animated: true)
  396. } else if let g = group {
  397. let topic = g.topics[indexPath.row]
  398. if topic.chatId.isEmpty {
  399. if let controller = self.previousViewController as? EditorGroup {
  400. if controller.dataTopic["chat_id"] as! String == topic.chatId {
  401. self.navigationController?.popViewController(animated: true)
  402. return
  403. }
  404. controller.unique_l_pin = g.id
  405. controller.loadData()
  406. self.navigationController?.popViewController(animated: true)
  407. } else {
  408. self.navigationController?.popViewController(animated: true)
  409. let editorGroupVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorGroupVC") as! EditorGroup
  410. editorGroupVC.hidesBottomBarWhenPushed = true
  411. editorGroupVC.unique_l_pin = g.id
  412. self.navigationController?.viewControllers.first?.show(editorGroupVC, sender: nil)
  413. }
  414. return
  415. }
  416. else if let controller = self.previousViewController as? EditorGroup {
  417. if controller.dataTopic["chat_id"] as! String == topic.chatId {
  418. self.navigationController?.popViewController(animated: true)
  419. return
  420. }
  421. controller.unique_l_pin = topic.chatId
  422. controller.loadData()
  423. self.navigationController?.popViewController(animated: true)
  424. } else {
  425. self.navigationController?.popViewController(animated: true)
  426. let editorGroupVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorGroupVC") as! EditorGroup
  427. editorGroupVC.hidesBottomBarWhenPushed = true
  428. editorGroupVC.unique_l_pin = topic.chatId
  429. self.navigationController?.viewControllers.first?.show(editorGroupVC, sender: nil)
  430. }
  431. }
  432. case .member:
  433. if let g = group, isAdmin, indexPath.row == 0 {
  434. let controller = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "groupMemberView") as! GroupMemberViewController
  435. controller.group = g
  436. if g.official == "1" && g.name == "Customer Service" {
  437. controller.isContactCenterInvite = true
  438. }
  439. controller.isDismiss = {
  440. self.reload()
  441. }
  442. let navController = UINavigationController(rootViewController: controller)
  443. navigationController?.present(navController, animated: true)
  444. } else if let g = group, isAdmin {
  445. let member = g.members[indexPath.row - 1]
  446. if member.pin != UserDefaults.standard.string(forKey: "me")! {
  447. if member.pin == g.by {
  448. let data = User.getData(pin: member.pin)
  449. let controller = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "profileView") as! ProfileViewController
  450. controller.flag = data == nil ? .invite : .friend
  451. controller.user = member
  452. controller.name = member.fullName
  453. controller.data = member.pin
  454. controller.picture = member.thumb
  455. self.navigationController?.show(controller, sender: nil)
  456. return
  457. }
  458. let alert = UIAlertController(title: nil, message: "\(member.firstName) \(member.lastName)", preferredStyle: .actionSheet)
  459. alert.addAction(UIAlertAction(title: "Info".localized(), style: .default, handler: { action in
  460. let data = User.getData(pin: member.pin)
  461. let controller = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "profileView") as! ProfileViewController
  462. controller.flag = data == nil ? .invite : .friend
  463. controller.user = member
  464. controller.name = member.fullName
  465. controller.data = member.pin
  466. controller.picture = member.thumb
  467. controller.isDismiss = {
  468. self.reload()
  469. }
  470. self.navigationController?.show(controller, sender: nil)
  471. }))
  472. alert.addAction(UIAlertAction(title: member.position == "0" ? "Make Group Admin".localized() : "Remove Group Admin".localized(), style: member.position == "0" ? .default : .destructive, handler: { action in
  473. let message = "\(member.position == "0" ? "Make" : "Remove") \(member.fullName) from the \"\(g.name)\" group Admin?"
  474. let notif = UIAlertController(title: nil, message: message, preferredStyle: .actionSheet)
  475. notif.addAction(UIAlertAction(title: member.position == "0" ? "Make".localized() : "Remove".localized(), style: member.position == "0" ? .default : .destructive, handler: { notifAction in
  476. self.changePosition(pin: member.pin, isAdmin: member.position == "0") { result in
  477. if result {
  478. DispatchQueue.main.async {
  479. tableView.beginUpdates()
  480. member.position = member.position == "1" ? "0" : "1"
  481. tableView.reloadRows(at: [indexPath], with: .none)
  482. tableView.endUpdates()
  483. }
  484. }
  485. }
  486. }))
  487. notif.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel, handler: { notifAction in
  488. }))
  489. self.navigationController?.present(notif, animated: true, completion: nil)
  490. }))
  491. alert.addAction(UIAlertAction(title: "Remove From Group".localized(), style: .destructive, handler: { action in
  492. let message = "Remove \(member.fullName) from the \"\(g.name)\" group?"
  493. let notif = UIAlertController(title: nil, message: message, preferredStyle: .actionSheet)
  494. notif.addAction(UIAlertAction(title: "Remove".localized(), style: .destructive, handler: { notifAction in
  495. self.exitGroup(pin: member.pin) { result in
  496. if result, let index = g.members.firstIndex(of: member) {
  497. DispatchQueue.main.async {
  498. tableView.beginUpdates()
  499. tableView.deleteRows(at: [indexPath], with: .none)
  500. g.members.remove(at: index)
  501. tableView.endUpdates()
  502. }
  503. }
  504. }
  505. }))
  506. notif.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel, handler: { notifAction in
  507. }))
  508. self.navigationController?.present(notif, animated: true, completion: nil)
  509. }))
  510. alert.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel, handler: { action in
  511. }))
  512. navigationController?.present(alert, animated: true)
  513. }
  514. } else if let g = group {
  515. let member = g.members[indexPath.row]
  516. if member.pin == UserDefaults.standard.string(forKey: "me") {
  517. // skip self profile
  518. return
  519. }
  520. let data = User.getData(pin: member.pin)
  521. let controller = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "profileView") as! ProfileViewController
  522. controller.flag = data == nil ? .invite : .friend
  523. controller.user = member
  524. controller.name = member.fullName
  525. controller.data = member.pin
  526. controller.picture = member.thumb
  527. controller.isDismiss = {
  528. self.reload()
  529. }
  530. self.navigationController?.show(controller, sender: nil)
  531. }
  532. case .exit:
  533. if let g = group {
  534. let idMe = UserDefaults.standard.string(forKey: "me") as String?
  535. let admins = g.members.filter { member in
  536. return member.position == "1"
  537. }
  538. var isDeleted = false
  539. if admins.count == 1 {
  540. if admins.first?.pin == idMe {
  541. isDeleted = true
  542. }
  543. }
  544. let message: String
  545. if isDeleted {
  546. message = "Are you sure want to delete the group?".localized()
  547. // message = "Are you sure want to delete the \"\(g.name)\" group?".localized()
  548. } else {
  549. message = "Are you sure want to exit the group?".localized()
  550. // message = "Are you sure want to exit the \"\(g.name)\" group?".localized()
  551. }
  552. let alert = UIAlertController(title: nil, message: message, preferredStyle: .actionSheet)
  553. alert.addAction(UIAlertAction(title: isDeleted ? "Delete Group".localized() : "Exit Group".localized(), style: .destructive, handler: { action in
  554. self.exitGroup(pin: isDeleted ? "ALL": UserDefaults.standard.string(forKey: "me")!) { result in
  555. if result {
  556. DispatchQueue.main.async {
  557. self.navigationController?.popToRootViewController(animated: true)
  558. }
  559. }
  560. }
  561. }))
  562. alert.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel, handler: { action in
  563. }))
  564. navigationController?.present(alert, animated: true)
  565. }
  566. default:
  567. print("No handler..")
  568. }
  569. }
  570. @objc func alertTextFieldDidChange(_ sender: UITextField) {
  571. if(!textFields.isEmpty){
  572. alert2?.actions[0].isEnabled = textFields[0].text!.trimmingCharacters(in: .whitespaces).count > 0
  573. }
  574. else {
  575. alert2?.actions[0].isEnabled = sender.text!.trimmingCharacters(in: .whitespaces).count > 0
  576. }
  577. }
  578. private func removeTopic(chatId: String, completion: @escaping (Bool) -> ()) {
  579. DispatchQueue.global().async {
  580. var result: Bool = false
  581. if let g = self.group, let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getDeleteChat(chat_id: chatId, f_pin: g.id)), response.isOk() {
  582. result = true
  583. }
  584. completion(result)
  585. }
  586. }
  587. private func changePosition(pin: String, isAdmin: Bool = true, completion: @escaping (Bool) -> ()) {
  588. DispatchQueue.global().async {
  589. var result: Bool = false
  590. 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() {
  591. result = true
  592. }
  593. completion(result)
  594. }
  595. }
  596. private func exitGroup(pin: String, completion: @escaping (Bool) -> ()) {
  597. DispatchQueue.global().async {
  598. var result: Bool = false
  599. if let g = self.group, let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getExitGroup(p_group_id: g.id, p_pin: pin)), response.isOk() {
  600. result = true
  601. }
  602. completion(result)
  603. }
  604. }
  605. private func changeOpenGroup(open: String, completion: @escaping (Bool) -> ()) {
  606. DispatchQueue.global().async {
  607. var result: Bool = false
  608. if let g = self.group, let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getChangeGroupInfo(p_group_id: g.id, p_open: open)), response.isOk() {
  609. result = true
  610. } else {
  611. result = false
  612. }
  613. completion(result)
  614. }
  615. }
  616. private func checkIsFriend(pin: String) -> Bool {
  617. var isFriend = true
  618. Database.shared.database?.inTransaction({ fmdb, rollback in
  619. if let cursor = Database.shared.getRecords(fmdb: fmdb, query: "select f_pin from BUDDY where f_pin = '\(pin)'"), cursor.next() {
  620. cursor.close()
  621. } else {
  622. isFriend = false
  623. }
  624. })
  625. return isFriend
  626. }
  627. // MARK: - Table view data source
  628. override func numberOfSections(in tableView: UITableView) -> Int {
  629. return sections.count
  630. }
  631. override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  632. guard let g = group else {
  633. return 1
  634. }
  635. switch sections[section] {
  636. case .topic:
  637. return isAdmin ? g.topics.count + 1 : g.topics.count
  638. case .detail:
  639. return 3
  640. case .member:
  641. return isAdmin ? g.members.count + 1 : g.members.count
  642. default:
  643. return 1
  644. }
  645. }
  646. override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
  647. switch sections[section] {
  648. case .description:
  649. return "Description".localized()
  650. case .topic:
  651. return "Topic".localized()
  652. case .detail:
  653. return "Detail".localized()
  654. case .member:
  655. return "Member".localized()
  656. default:
  657. return nil
  658. }
  659. }
  660. override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  661. switch sections[indexPath.section] {
  662. case .profile:
  663. let cell = tableView.dequeueReusableCell(withIdentifier: "profileCell", for: indexPath) as! ProfileCell
  664. cell.cover.image = UIImage(named: "Sofa", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
  665. guard let g = group else {
  666. return cell
  667. }
  668. if let image = tempImage {
  669. cell.profile.image = image
  670. } else {
  671. getImage(name: g.profile, placeholderImage: UIImage(systemName: "person.2.circle.fill"), tableView: tableView, indexPath: indexPath) { result, isDownloaded, image in
  672. cell.profile.image = image
  673. }
  674. }
  675. return cell
  676. case .description:
  677. let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
  678. cell.accessoryView = nil
  679. cell.accessoryType = .disclosureIndicator
  680. var content = cell.defaultContentConfiguration()
  681. if let g = group {
  682. content.text = g.quote.isEmpty ? "No description".localized() : g.quote
  683. }
  684. cell.contentConfiguration = content
  685. cell.selectionStyle = .default
  686. return cell
  687. case .access:
  688. let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
  689. var content = cell.defaultContentConfiguration()
  690. if let g = group {
  691. if g.isOpen.isEmpty || g.isOpen == "0" {
  692. content.text = "Private".localized()
  693. content.secondaryText = "Only members can be access this group".localized()
  694. } else if g.isOpen == "1" {
  695. content.text = "Public".localized()
  696. content.secondaryText = "All user can be access this group".localized()
  697. }
  698. if isAdmin && group?.official != "1" {
  699. let changeOpen = UISwitch()
  700. changeOpen.isUserInteractionEnabled = false
  701. if g.isOpen == "1" {
  702. changeOpen.setOn(true, animated: true)
  703. } else {
  704. changeOpen.setOn(false, animated: true)
  705. }
  706. cell.accessoryView = changeOpen
  707. }
  708. }
  709. content.textProperties.color = .mainColor
  710. cell.contentConfiguration = content
  711. cell.accessoryType = .none
  712. return cell
  713. case .topic:
  714. let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
  715. cell.accessoryView = nil
  716. cell.accessoryType = .none
  717. var content = cell.defaultContentConfiguration()
  718. if let g = group {
  719. if indexPath.row == 0, isAdmin {
  720. content.image = UIImage(systemName: "plus.circle")
  721. content.imageProperties.tintColor = .mainColor
  722. content.text = "Add Topic".localized()
  723. cell.accessoryType = .disclosureIndicator
  724. cell.selectionStyle = .default
  725. } else {
  726. let topic = g.topics[isAdmin ? indexPath.row - 1 : indexPath.row]
  727. getImage(name: topic.thumb, placeholderImage: UIImage(systemName: "message.fill"), isCircle: true, tableView: tableView, indexPath: indexPath) { result, isDownloaded, image in
  728. content.image = image
  729. if !result {
  730. content.imageProperties.tintColor = .mainColor
  731. }
  732. }
  733. content.text = topic.title
  734. content.secondaryText = topic.description
  735. cell.selectionStyle = isAdmin ? .default : .none
  736. }
  737. }
  738. cell.contentConfiguration = content
  739. return cell
  740. case .detail:
  741. let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
  742. cell.accessoryView = nil
  743. cell.accessoryType = .none
  744. cell.selectionStyle = .none
  745. var content = cell.defaultContentConfiguration()
  746. content.prefersSideBySideTextAndSecondaryText = true
  747. if let g = group {
  748. switch indexPath.row {
  749. case 1:
  750. content.text = "Created date".localized()
  751. content.secondaryText = Date(milliseconds: Int64(g.date)!).format(dateFormat: "dd, MMM yyyy")
  752. case 2:
  753. content.text = "Members".localized()
  754. content.secondaryText = String(g.members.count)
  755. default:
  756. content.text = "Created by".localized()
  757. Database.shared.database?.inTransaction({ fmdb, rollback in
  758. if let cursor = Database.shared.getRecords(fmdb: fmdb, query: "select first_name || ' ' || ifnull(last_name, '') from BUDDY where f_pin = '\(g.by)'") {
  759. if cursor.next() {
  760. content.secondaryText = cursor.string(forColumnIndex: 0) ?? "Unknown".localized()
  761. }
  762. cursor.close()
  763. }
  764. })
  765. }
  766. }
  767. cell.contentConfiguration = content
  768. return cell
  769. case .member:
  770. let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
  771. cell.selectionStyle = isAdmin ? .default : .none
  772. cell.accessoryView = nil
  773. var content = cell.defaultContentConfiguration()
  774. content.prefersSideBySideTextAndSecondaryText = true
  775. if let g = group {
  776. if indexPath.row == 0, isAdmin {
  777. content.image = UIImage(systemName: "plus.circle")
  778. content.imageProperties.tintColor = .mainColor
  779. content.text = "Add Member".localized()
  780. cell.accessoryType = .disclosureIndicator
  781. } else {
  782. let member = g.members[isAdmin ? indexPath.row - 1 : indexPath.row]
  783. content.imageProperties.maximumSize = CGSize(width: 20, height: 20)
  784. getImage(name: member.thumb, placeholderImage: UIImage(systemName: "person.fill"), isCircle: true, tableView: tableView, indexPath: indexPath) { result, isDownloaded, image in
  785. content.image = image
  786. if !result {
  787. content.imageProperties.tintColor = .mainColor
  788. }
  789. }
  790. if member.userType == "23" || member.official == "1" {
  791. 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)
  792. } else if member.userType == "24" {
  793. 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)
  794. } else {
  795. content.text = (member.firstName + " " + member.lastName).trimmingCharacters(in: .whitespaces)
  796. }
  797. if !checkIsFriend(pin: member.pin) {
  798. if member.position == "1" {
  799. 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)
  800. } else {
  801. content.secondaryAttributedText = self.set(image: UIImage(named: "pb_add_contact", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, with: "", size: 20, y: 0)
  802. }
  803. } else {
  804. 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: "")
  805. }
  806. cell.accessoryType = .none
  807. }
  808. }
  809. cell.contentConfiguration = content
  810. return cell
  811. case .exit:
  812. let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
  813. cell.accessoryView = nil
  814. cell.accessoryType = .none
  815. var content = cell.defaultContentConfiguration()
  816. content.text = "Exit Group".localized()
  817. content.textProperties.color = .red
  818. cell.contentConfiguration = content
  819. cell.selectionStyle = .default
  820. return cell
  821. }
  822. }
  823. override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
  824. switch sections[indexPath.section] {
  825. case .profile:
  826. return 200
  827. default:
  828. return UITableView.automaticDimension
  829. }
  830. }
  831. }
  832. // MARK: - Cell
  833. class ProfileCell: UITableViewCell {
  834. @IBOutlet weak var cover: UIImageView!
  835. @IBOutlet weak var profile: UIImageView!
  836. override func layoutSubviews() {
  837. super.layoutSubviews()
  838. profile.circle()
  839. }
  840. }
  841. // MARK: - Extension
  842. extension GroupDetailViewController: ImageVideoPickerDelegate {
  843. func didSelect(imagevideo: Any?) {
  844. 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 {
  845. guard let g = group else {
  846. return
  847. }
  848. self.tempImage = image
  849. cell.profile.image = self.tempImage
  850. DispatchQueue.global().async {
  851. let resize = image.resize(target: CGSize(width: 800, height: 600))
  852. let documentDir = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
  853. let fileDir = documentDir.appendingPathComponent("THUMB_\(g.id)\(Date().currentTimeMillis().toHex())")
  854. if !FileManager.default.fileExists(atPath: fileDir.path), let data = resize.jpegData(compressionQuality: 0.8) {
  855. try! data.write(to: fileDir)
  856. Network().upload(name: fileDir.lastPathComponent) { result, progress in
  857. guard result, progress == 100 else {
  858. return
  859. }
  860. if let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getChangeGroupInfo(p_group_id: g.id, p_thumb_id: fileDir.lastPathComponent)), response.isOk() {
  861. Database.shared.database?.inTransaction({ fmdb, rollback in
  862. _ = Database.shared.updateRecord(fmdb: fmdb, table: "GROUPZ", cvalues: ["image_id": fileDir.lastPathComponent], _where: "group_id = '\(g.id)'")
  863. })
  864. }
  865. }
  866. }
  867. }
  868. }
  869. }
  870. func set(image: UIImage, image2: UIImage = UIImage(), with text: String, size: CGFloat, y: CGFloat, moreImage: Bool = false) -> NSAttributedString {
  871. let attachment = NSTextAttachment()
  872. let attachment2 = NSTextAttachment()
  873. attachment.image = image
  874. attachment.bounds = CGRect(x: 0, y: y, width: size, height: size)
  875. attachment2.image = image2
  876. attachment2.bounds = CGRect(x: 0, y: y, width: size, height: size)
  877. let attachmentStr = NSAttributedString(attachment: attachment)
  878. let attachmentStr2 = NSAttributedString(attachment: attachment2)
  879. let mutableAttributedString = NSMutableAttributedString()
  880. if moreImage {
  881. mutableAttributedString.append(attachmentStr2)
  882. }
  883. mutableAttributedString.append(attachmentStr)
  884. let textString = NSAttributedString(string: text)
  885. mutableAttributedString.append(textString)
  886. return mutableAttributedString
  887. }
  888. }