ContactChatViewController.swift 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903
  1. //
  2. // ContactChatViewController.swift
  3. // Qmera
  4. //
  5. // Created by Yayan Dwi on 22/09/21.
  6. //
  7. import UIKit
  8. import FMDB
  9. import NotificationBannerSwift
  10. class ContactChatViewController: UITableViewController {
  11. deinit {
  12. print(#function, ">>>> TADAA")
  13. NotificationCenter.default.removeObserver(self)
  14. }
  15. var isChooser: ((String, String) -> ())?
  16. var isAdmin: Bool = false
  17. var chats: [Chat] = []
  18. var contacts: [User] = []
  19. var groups: [Group] = []
  20. var groupMap: [String:Int] = [:]
  21. var searchController: UISearchController!
  22. var segment: UISegmentedControl!
  23. var fillteredData: [Any] = []
  24. var isSearchBarEmpty: Bool {
  25. return searchController.searchBar.text?.isEmpty ?? true
  26. }
  27. var isFilltering: Bool {
  28. return searchController.isActive && !isSearchBarEmpty
  29. }
  30. func filterContentForSearchText(_ searchText: String) {
  31. switch segment.selectedSegmentIndex {
  32. case 1:
  33. fillteredData = self.contacts.filter { $0.fullName.lowercased().contains(searchText.lowercased()) }
  34. case 2:
  35. fillteredData = self.groups.filter { $0.name.lowercased().contains(searchText.lowercased()) }
  36. default:
  37. fillteredData = self.chats.filter { $0.name.lowercased().contains(searchText.lowercased()) || $0.messageText.lowercased().contains(searchText.lowercased()) }
  38. }
  39. tableView.reloadData()
  40. }
  41. override func viewDidLoad() {
  42. super.viewDidLoad()
  43. let me = UserDefaults.standard.string(forKey: "me")!
  44. Database.shared.database?.inTransaction({ fmdb, rollback in
  45. if let cursor = Database.shared.getRecords(fmdb: fmdb, query: "select FIRST_NAME, LAST_NAME, IMAGE_ID, USER_TYPE from BUDDY where F_PIN = '\(me)'"), cursor.next() {
  46. isAdmin = cursor.string(forColumnIndex: 3) == "23" || cursor.string(forColumnIndex: 3) == "24"
  47. cursor.close()
  48. }
  49. })
  50. title = "Start Conversation".localized()
  51. let attributes = [NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 16.0), NSAttributedString.Key.foregroundColor: UIColor.white]
  52. self.navigationController?.navigationBar.titleTextAttributes = attributes
  53. // navigationController?.navigationBar.prefersLargeTitles = true
  54. navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Cancel".localized(), style: .plain, target: self, action: #selector(cancel(sender:)))
  55. var childrenMenu : [UIAction] = [
  56. UIAction(title: "Create Group".localized(), image: UIImage(systemName: "person.and.person"), handler: {[weak self](_) in
  57. let controller = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "createGroupNav") as! UINavigationController
  58. let vc = controller.topViewController as! GroupCreateViewController
  59. vc.isDismiss = { id in
  60. self?.groupMap.removeAll()
  61. let controller = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "groupDetailView") as! GroupDetailViewController
  62. controller.data = id
  63. self?.navigationController?.show(controller, sender: nil)
  64. }
  65. self?.navigationController?.present(controller, animated: true, completion: nil)
  66. }),
  67. UIAction(title: "Add Friends".localized(), image: UIImage(systemName: "person.badge.plus"), handler: {[weak self](_) in
  68. let controller = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "addFriendNav") as! UINavigationController
  69. if let vc = controller.viewControllers.first as? AddFriendTableViewController {
  70. vc.isDismiss = {
  71. self?.getContacts {
  72. DispatchQueue.main.async {
  73. self?.tableView.reloadData()
  74. }
  75. }
  76. }
  77. }
  78. self?.navigationController?.present(controller, animated: true, completion: nil)
  79. }),
  80. // UIAction(title: "Configure Email", image: UIImage(systemName: "mail"), handler: {[weak self](_) in
  81. //
  82. // }),
  83. UIAction(title: "Favorite Messages".localized(), image: UIImage(systemName: "star"), handler: {[weak self](_) in
  84. let editorStaredVC = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "staredVC") as! EditorStarMessages
  85. self?.navigationController?.show(editorStaredVC, sender: nil)
  86. }),
  87. ]
  88. //debug only
  89. // isAdmin = true
  90. if(isAdmin){
  91. childrenMenu.append(UIAction(title: "Broadcast Message".localized(), image: UIImage(systemName: "envelope.open"), handler: {[weak self](_) in
  92. let controller = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "broadcastNav")
  93. self?.navigationController?.present(controller, animated: true, completion: nil)
  94. }))
  95. childrenMenu.append(UIAction(title: "Live Streaming".localized(), image: UIImage(systemName: "video.bubble.left"), handler: {[weak self](_) in
  96. let navigationController = UINavigationController(rootViewController: QmeraCreateStreamingViewController())
  97. navigationController.modalPresentationStyle = .custom
  98. navigationController.navigationBar.tintColor = .white
  99. navigationController.navigationBar.barTintColor = .mainColor
  100. navigationController.navigationBar.isTranslucent = false
  101. let textAttributes = [NSAttributedString.Key.foregroundColor:UIColor.white]
  102. navigationController.navigationBar.titleTextAttributes = textAttributes
  103. navigationController.view.backgroundColor = .mainColor
  104. self?.navigationController?.present(navigationController, animated: true, completion: nil)
  105. }))
  106. }
  107. let menu = UIMenu(title: "", children: childrenMenu)
  108. navigationItem.rightBarButtonItem = UIBarButtonItem(systemItem: .add, primaryAction: .none, menu: menu)
  109. searchController = UISearchController(searchResultsController: nil)
  110. searchController.delegate = self
  111. searchController.searchResultsUpdater = self
  112. searchController.searchBar.autocapitalizationType = .none
  113. searchController.searchBar.delegate = self
  114. searchController.searchBar.barTintColor = .secondaryColor
  115. searchController.searchBar.searchTextField.backgroundColor = .secondaryColor
  116. searchController.obscuresBackgroundDuringPresentation = false
  117. navigationItem.searchController = searchController
  118. navigationItem.hidesSearchBarWhenScrolling = true
  119. definesPresentationContext = true
  120. segment = UISegmentedControl(items: ["Chats".localized(), "Contacts".localized(), "Groups".localized()])
  121. segment.sizeToFit()
  122. segment.selectedSegmentIndex = 0
  123. segment.addTarget(self, action: #selector(segmentChanged(sender:)), for: .valueChanged)
  124. segment.setTitleTextAttributes([NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 12.0)], for: .normal)
  125. NotificationCenter.default.addObserver(self, selector: #selector(onReceiveMessage(notification:)), name: NSNotification.Name(rawValue: "onReceiveChat"), object: nil)
  126. NotificationCenter.default.addObserver(self, selector: #selector(onReload(notification:)), name: NSNotification.Name(rawValue: "onMember"), object: nil)
  127. NotificationCenter.default.addObserver(self, selector: #selector(onReload(notification:)), name: NSNotification.Name(rawValue: "onUpdatePersonInfo"), object: nil)
  128. tableView.tableHeaderView = segment
  129. tableView.tableFooterView = UIView()
  130. pullBuddy()
  131. }
  132. override func viewWillAppear(_ animated: Bool) {
  133. groupMap.removeAll()
  134. getData()
  135. }
  136. @objc func onReload(notification: NSNotification) {
  137. let data:[AnyHashable : Any] = notification.userInfo!
  138. if data["member"] as? String == UserDefaults.standard.string(forKey: "me") {
  139. DispatchQueue.main.async {
  140. self.getData()
  141. }
  142. } else if data["state"] as? Int == 99 {
  143. DispatchQueue.main.async {
  144. self.getData()
  145. }
  146. }
  147. }
  148. @objc func onReceiveMessage(notification: NSNotification) {
  149. DispatchQueue.main.async {
  150. self.getData()
  151. }
  152. }
  153. @objc func add(sender: Any) {
  154. }
  155. @objc func cancel(sender: Any) {
  156. navigationController?.dismiss(animated: true, completion: nil)
  157. }
  158. @objc func segmentChanged(sender: Any) {
  159. filterContentForSearchText(searchController.searchBar.text!)
  160. if segment.selectedSegmentIndex == 2 {
  161. self.getGroups { g1 in
  162. self.getOpenGroups(listGroups: g1, completion: { g in
  163. self.groups.removeAll()
  164. self.groups.append(contentsOf: g1)
  165. for og in g {
  166. if self.groups.first(where: { $0.id == og.id }) == nil {
  167. self.groups.append(og)
  168. }
  169. }
  170. DispatchQueue.main.async {
  171. self.tableView.reloadData()
  172. }
  173. })
  174. }
  175. }
  176. }
  177. // MARK: - Data source
  178. func getData() {
  179. self.chats.removeAll()
  180. self.contacts.removeAll()
  181. getChats {
  182. self.getContacts {
  183. self.getGroups { g1 in
  184. self.getOpenGroups(listGroups: g1, completion: { g in
  185. self.groups.removeAll()
  186. self.groups.append(contentsOf: g1)
  187. for og in g {
  188. if self.groups.first(where: { $0.id == og.id }) == nil {
  189. self.groups.append(og)
  190. }
  191. }
  192. DispatchQueue.main.async {
  193. self.tableView.reloadData()
  194. }
  195. })
  196. }
  197. }
  198. }
  199. }
  200. func getChats(completion: @escaping ()->()) {
  201. self.chats.removeAll()
  202. DispatchQueue.global().async {
  203. let chatData = Chat.getData()
  204. if !self.chats.contains(where: {$0.messageId == chatData.first?.messageId ?? ""}) {
  205. self.chats.append(contentsOf: chatData)
  206. }
  207. completion()
  208. }
  209. }
  210. private func getContacts(completion: @escaping ()->()) {
  211. self.contacts.removeAll()
  212. DispatchQueue.global().async {
  213. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  214. if let cursorData = Database.shared.getRecords(fmdb: fmdb, query: "SELECT f_pin, first_name, last_name, image_id, official_account, user_type FROM BUDDY where f_pin <> '\(UserDefaults.standard.string(forKey: "me")!)' order by 5 desc, 2 collate nocase asc") {
  215. while cursorData.next() {
  216. let user = User(pin: cursorData.string(forColumnIndex: 0) ?? "",
  217. firstName: cursorData.string(forColumnIndex: 1) ?? "",
  218. lastName: cursorData.string(forColumnIndex: 2) ?? "",
  219. thumb: cursorData.string(forColumnIndex: 3) ?? "",
  220. userType: cursorData.string(forColumnIndex: 5) ?? "")
  221. if (user.firstName + " " + user.lastName).trimmingCharacters(in: .whitespaces) == "USR\(user.pin)" {
  222. continue
  223. }
  224. user.official = cursorData.string(forColumnIndex: 4) ?? ""
  225. if !self.contacts.contains(where: {$0.pin == user.pin}) {
  226. self.contacts.append(user)
  227. }
  228. }
  229. cursorData.close()
  230. }
  231. completion()
  232. })
  233. }
  234. }
  235. private func getGroupRecursive(fmdb: FMDatabase, id: String = "", parent: String = "") -> [Group] {
  236. var data: [Group] = []
  237. var 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.is_education from GROUPZ g where "
  238. if id.isEmpty {
  239. query += "g.parent = '\(parent)'"
  240. } else {
  241. query += "g.group_id = '\(id)'"
  242. }
  243. if let cursor = Database.shared.getRecords(fmdb: fmdb, query: query) {
  244. while cursor.next() {
  245. let group = Group(
  246. id: cursor.string(forColumnIndex: 0) ?? "",
  247. name: cursor.string(forColumnIndex: 1) ?? "",
  248. profile: cursor.string(forColumnIndex: 2) ?? "",
  249. quote: cursor.string(forColumnIndex: 3) ?? "",
  250. by: cursor.string(forColumnIndex: 4) ?? "",
  251. date: cursor.string(forColumnIndex: 5) ?? "",
  252. parent: cursor.string(forColumnIndex: 6) ?? "",
  253. chatId: "",
  254. groupType: cursor.string(forColumnIndex: 7) ?? "",
  255. isOpen: cursor.string(forColumnIndex: 8) ?? "",
  256. official: cursor.string(forColumnIndex: 9) ?? "",
  257. isEducation: cursor.string(forColumnIndex: 10) ?? "")
  258. if group.chatId.isEmpty {
  259. let lounge = Group(id: group.id, name: "Lounge".localized(), profile: "", quote: group.quote, by: group.by, date: group.date, parent: group.id, chatId: group.chatId, groupType: group.groupType, isOpen: group.isOpen, official: group.official, isEducation: group.isEducation, isLounge: true)
  260. group.childs.append(lounge)
  261. }
  262. if let topicCursor = Database.shared.getRecords(fmdb: fmdb, query: "select chat_id, title, thumb from DISCUSSION_FORUM where group_id = '\(group.id)'") {
  263. while topicCursor.next() {
  264. let topic = Group(id: group.id,
  265. name: topicCursor.string(forColumnIndex: 1) ?? "",
  266. profile: topicCursor.string(forColumnIndex: 2) ?? "",
  267. quote: group.quote,
  268. by: group.by,
  269. date: group.date,
  270. parent: group.id,
  271. chatId: topicCursor.string(forColumnIndex: 0) ?? "",
  272. groupType: group.groupType,
  273. isOpen: group.isOpen,
  274. official: group.official,
  275. isEducation: group.isEducation)
  276. group.childs.append(topic)
  277. }
  278. topicCursor.close()
  279. }
  280. if !group.id.isEmpty {
  281. if group.official == "1" {
  282. let idMe = UserDefaults.standard.string(forKey: "me") as String?
  283. if let cursorUser = Database.shared.getRecords(fmdb: fmdb, query: "SELECT user_type FROM BUDDY where f_pin='\(idMe!)'"), cursorUser.next() {
  284. // if cursorUser.string(forColumnIndex: 0) == "23" || cursorUser.string(forColumnIndex: 0) == "24" {
  285. // group.childs.append(contentsOf: getGroupRecursive(fmdb: fmdb, parent: group.id))
  286. // }
  287. group.childs.append(contentsOf: getGroupRecursive(fmdb: fmdb, parent: group.id))
  288. cursorUser.close()
  289. }
  290. } else if group.official != "1"{
  291. group.childs.append(contentsOf: getGroupRecursive(fmdb: fmdb, parent: group.id))
  292. }
  293. group.childs = group.childs.sorted(by: { $0.name < $1.name })
  294. let dataLounge = group.childs.filter({$0.name == "Lounge".localized()})
  295. group.childs = group.childs.filter({ $0.name != "Lounge".localized() })
  296. group.childs.insert(contentsOf: dataLounge, at: 0)
  297. }
  298. data.append(group)
  299. }
  300. cursor.close()
  301. }
  302. return data
  303. }
  304. private func getOpenGroups(listGroups: [Group], completion: @escaping ([Group]) -> ()) {
  305. if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getOpenGroups(p_account: "1,2,3,5,6,7", offset: "0", search: "")) {
  306. var dataGroups: [Group] = []
  307. if (response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99") == "00") {
  308. let data = response.getBody(key: CoreMessage_TMessageKey.DATA)
  309. if let json = try! JSONSerialization.jsonObject(with: data.data(using: String.Encoding.utf8)!, options: []) as? [[String: Any?]] {
  310. for dataJson in json {
  311. let group = Group(
  312. id: dataJson[CoreMessage_TMessageKey.GROUP_ID] as? String ?? "",
  313. name: dataJson[CoreMessage_TMessageKey.GROUP_NAME] as? String ?? "",
  314. profile: dataJson[CoreMessage_TMessageKey.THUMB_ID] as? String ?? "",
  315. quote: dataJson[CoreMessage_TMessageKey.QUOTE] as? String ?? "",
  316. by: dataJson[CoreMessage_TMessageKey.BLOCK] as? String ?? "",
  317. date: "",
  318. parent: "",
  319. chatId: "",
  320. groupType: "NOTJOINED",
  321. isOpen: dataJson[CoreMessage_TMessageKey.IS_OPEN] as? String ?? "",
  322. official: "0",
  323. isEducation: "")
  324. dataGroups.append(group)
  325. }
  326. }
  327. } else {
  328. DispatchQueue.main.async {
  329. self.groups.removeAll()
  330. self.groups.append(contentsOf: listGroups)
  331. self.tableView.reloadData()
  332. }
  333. }
  334. completion(dataGroups)
  335. }
  336. }
  337. private func getGroups(id: String = "", parent: String = "", completion: @escaping ([Group]) -> ()) {
  338. DispatchQueue.global().async {
  339. Database.shared.database?.inTransaction({ fmdb, rollback in
  340. completion(self.getGroupRecursive(fmdb: fmdb, id: id, parent: parent))
  341. })
  342. }
  343. }
  344. private func pullBuddy() {
  345. if let me = UserDefaults.standard.string(forKey: "me") {
  346. DispatchQueue.global().async {
  347. let _ = Nexilis.write(message: CoreMessage_TMessageBank.getBatchBuddiesInfos(p_f_pin: me, last_update: 0))
  348. }
  349. }
  350. }
  351. private func joinOpenGroup(groupId: String, flagMember: String = "0", completion: @escaping (Bool) -> ()) {
  352. DispatchQueue.global().async {
  353. var result: Bool = false
  354. let idMe = UserDefaults.standard.string(forKey: "me") as String?
  355. if let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getAddGroupMember(p_group_id: groupId, p_member_pin: idMe!, p_position: "0")), response.isOk() {
  356. result = true
  357. }
  358. completion(result)
  359. }
  360. }
  361. }
  362. // MARK: - Table view data source
  363. extension ContactChatViewController {
  364. override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
  365. switch segment.selectedSegmentIndex {
  366. case 0:
  367. let data: Chat
  368. if isFilltering {
  369. data = fillteredData[indexPath.row] as! Chat
  370. } else {
  371. data = chats[indexPath.row]
  372. }
  373. if let chooser = isChooser {
  374. if data.pin == "-999"{
  375. return
  376. }
  377. chooser(data.messageScope, data.pin)
  378. dismiss(animated: true, completion: nil)
  379. return
  380. }
  381. let user = User.getData(pin: data.pin)
  382. if user != nil || data.pin == "-999" {
  383. let editorPersonalVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorPersonalVC") as! EditorPersonal
  384. editorPersonalVC.hidesBottomBarWhenPushed = true
  385. editorPersonalVC.unique_l_pin = data.pin
  386. navigationController?.show(editorPersonalVC, sender: nil)
  387. } else {
  388. groupMap.removeAll()
  389. let editorGroupVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorGroupVC") as! EditorGroup
  390. editorGroupVC.hidesBottomBarWhenPushed = true
  391. editorGroupVC.unique_l_pin = data.pin
  392. navigationController?.show(editorGroupVC, sender: nil)
  393. }
  394. case 1:
  395. let data: User
  396. if isFilltering {
  397. data = fillteredData[indexPath.row] as! User
  398. } else {
  399. data = contacts[indexPath.row]
  400. }
  401. if let chooser = isChooser {
  402. chooser("3", data.pin)
  403. dismiss(animated: true, completion: nil)
  404. return
  405. }
  406. let editorPersonalVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorPersonalVC") as! EditorPersonal
  407. editorPersonalVC.hidesBottomBarWhenPushed = true
  408. editorPersonalVC.unique_l_pin = data.pin
  409. navigationController?.show(editorPersonalVC, sender: nil)
  410. case 2:
  411. let group: Group
  412. if isFilltering {
  413. if indexPath.row == 0 {
  414. group = fillteredData[indexPath.section] as! Group
  415. } else {
  416. group = (fillteredData[indexPath.section] as! Group).childs[indexPath.row - 1]
  417. }
  418. } else {
  419. if indexPath.row == 0 {
  420. group = groups[indexPath.section]
  421. } else {
  422. group = groups[indexPath.section].childs[indexPath.row - 1]
  423. }
  424. }
  425. group.isSelected = !group.isSelected
  426. if !group.isSelected{
  427. var sects = 0
  428. var sect = indexPath.section
  429. var id = group.id
  430. if let e = groupMap[id] {
  431. var loooop = true
  432. repeat {
  433. let c = sect + 1
  434. if isFilltering {
  435. if let o = self.fillteredData[c] as? Group {
  436. if o.parent == id {
  437. sects = sects + 1
  438. sect = c
  439. id = o.id
  440. (self.fillteredData[c] as! Group).isSelected = false
  441. self.groupMap.removeValue(forKey: (self.fillteredData[c] as! Group).id)
  442. }
  443. else {
  444. loooop = false
  445. }
  446. }
  447. }
  448. else {
  449. if self.groups[c].parent == id {
  450. sects = sects + 1
  451. sect = c
  452. id = self.groups[c].id
  453. self.groups[c].isSelected = false
  454. self.groupMap.removeValue(forKey: self.groups[c].id)
  455. }
  456. else {
  457. loooop = false
  458. }
  459. }
  460. } while(loooop)
  461. }
  462. for i in stride(from: sects, to: 0, by: -1){
  463. if isFilltering {
  464. self.fillteredData.remove(at: indexPath.section + i)
  465. }
  466. else {
  467. self.groups.remove(at: indexPath.section + i)
  468. }
  469. }
  470. groupMap.removeValue(forKey: group.id)
  471. }
  472. if group.groupType == "NOTJOINED" {
  473. let alert = UIAlertController(title: "Do you want to join this group?".localized(), message: "Groups : \(group.name)\nMembers: \(group.by)".localized(), preferredStyle: .alert)
  474. alert.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel, handler: nil))
  475. alert.addAction(UIAlertAction(title: "Join".localized(), style: .default, handler: {(_) in
  476. self.joinOpenGroup(groupId: group.id, completion: { result in
  477. if result {
  478. DispatchQueue.main.async {
  479. self.groupMap.removeAll()
  480. let editorGroupVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorGroupVC") as! EditorGroup
  481. editorGroupVC.hidesBottomBarWhenPushed = true
  482. editorGroupVC.unique_l_pin = group.id
  483. self.navigationController?.show(editorGroupVC, sender: nil)
  484. }
  485. }
  486. })
  487. }))
  488. self.present(alert, animated: true, completion: nil)
  489. return
  490. }
  491. if group.childs.count == 0 {
  492. let groupId = group.chatId.isEmpty ? group.id : group.chatId
  493. if let chooser = isChooser {
  494. chooser("4", groupId)
  495. dismiss(animated: true, completion: nil)
  496. return
  497. }
  498. self.groupMap.removeAll()
  499. let editorGroupVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorGroupVC") as! EditorGroup
  500. editorGroupVC.hidesBottomBarWhenPushed = true
  501. editorGroupVC.unique_l_pin = groupId
  502. navigationController?.show(editorGroupVC, sender: nil)
  503. } else {
  504. if indexPath.row == 0 {
  505. tableView.reloadData()
  506. } else {
  507. getGroups(id: group.id) { g in
  508. DispatchQueue.main.async {
  509. print("index path section: \(indexPath.section)")
  510. print("index path row: \(indexPath.row)")
  511. // print("index path item: \(indexPath.item)")
  512. if self.isFilltering {
  513. // self.fillteredData.remove(at: indexPath.section)
  514. if self.fillteredData[indexPath.section] is Group {
  515. self.groupMap[(self.fillteredData[indexPath.section] as! Group).id] = 1
  516. self.fillteredData.insert(contentsOf: g, at: indexPath.section + 1)
  517. }
  518. } else {
  519. // self.groups.remove(at: indexPath.section)
  520. self.groupMap[self.groups[indexPath.section].id] = 1
  521. self.groups.insert(contentsOf: g, at: indexPath.section + 1)
  522. }
  523. print("groupMap: \(self.groupMap)")
  524. tableView.reloadData()
  525. }
  526. }
  527. }
  528. }
  529. default:
  530. let data = contacts[indexPath.row]
  531. let editorPersonalVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorPersonalVC") as! EditorPersonal
  532. editorPersonalVC.hidesBottomBarWhenPushed = true
  533. editorPersonalVC.unique_l_pin = data.pin
  534. navigationController?.show(editorPersonalVC, sender: nil)
  535. }
  536. }
  537. }
  538. extension ContactChatViewController {
  539. override func numberOfSections(in tableView: UITableView) -> Int {
  540. if isFilltering {
  541. if segment.selectedSegmentIndex == 2 {
  542. return fillteredData.count
  543. }
  544. return 1
  545. } else {
  546. if segment.selectedSegmentIndex == 2 {
  547. return groups.count
  548. }
  549. return 1
  550. }
  551. }
  552. override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  553. var value = 0
  554. if isFilltering {
  555. if segment.selectedSegmentIndex == 2, let groups = fillteredData as? [Group] {
  556. let group = groups[section]
  557. if group.isSelected {
  558. if let g = groupMap[group.id] {
  559. value = 1
  560. }
  561. else {
  562. value = group.childs.count + 1
  563. }
  564. } else {
  565. value = 1
  566. }
  567. }
  568. return fillteredData.count
  569. }
  570. switch segment.selectedSegmentIndex {
  571. case 0:
  572. value = chats.count
  573. case 1:
  574. value = contacts.count
  575. case 2:
  576. let group = groups[section]
  577. if group.isSelected {
  578. if let g = groupMap[group.id] {
  579. value = 1
  580. }
  581. else {
  582. value = group.childs.count + 1
  583. }
  584. } else {
  585. value = 1
  586. }
  587. default:
  588. value = chats.count
  589. }
  590. return value
  591. }
  592. override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  593. var cell: UITableViewCell!
  594. switch segment.selectedSegmentIndex {
  595. case 0:
  596. cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifierChat", for: indexPath)
  597. cell.separatorInset.left = 60.0
  598. let content = cell.contentView
  599. if content.subviews.count > 0 {
  600. content.subviews.forEach { $0.removeFromSuperview() }
  601. }
  602. let data: Chat
  603. if isFilltering {
  604. data = fillteredData[indexPath.row] as! Chat
  605. } else {
  606. data = chats[indexPath.row]
  607. }
  608. let imageView = UIImageView()
  609. content.addSubview(imageView)
  610. imageView.translatesAutoresizingMaskIntoConstraints = false
  611. NSLayoutConstraint.activate([
  612. imageView.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: 10.0),
  613. imageView.topAnchor.constraint(equalTo: content.topAnchor, constant: 10.0),
  614. imageView.bottomAnchor.constraint(equalTo: content.bottomAnchor, constant: -20.0),
  615. imageView.widthAnchor.constraint(equalToConstant: 40.0),
  616. imageView.heightAnchor.constraint(equalToConstant: 40.0)
  617. ])
  618. if data.profile.isEmpty && data.pin != "-999" {
  619. let user = User.getData(pin: data.pin)
  620. if user != nil {
  621. imageView.image = UIImage(named: "Profile---Purple", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
  622. } else {
  623. imageView.image = UIImage(named: "Conversation---Purple", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
  624. }
  625. } else {
  626. getImage(name: data.profile, placeholderImage: UIImage(named: data.pin == "-999" ? "pb_button" : "Conversation---Purple", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), isCircle: true, tableView: tableView, indexPath: indexPath, completion: { result, isDownloaded, image in
  627. imageView.image = image
  628. })
  629. }
  630. let titleView = UILabel()
  631. content.addSubview(titleView)
  632. titleView.translatesAutoresizingMaskIntoConstraints = false
  633. NSLayoutConstraint.activate([
  634. titleView.leadingAnchor.constraint(equalTo: imageView.trailingAnchor, constant: 10.0),
  635. titleView.topAnchor.constraint(equalTo: content.topAnchor, constant: 10.0),
  636. titleView.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -40.0),
  637. ])
  638. titleView.text = data.name
  639. titleView.font = UIFont.systemFont(ofSize: 14, weight: .medium)
  640. let messageView = UILabel()
  641. content.addSubview(messageView)
  642. messageView.translatesAutoresizingMaskIntoConstraints = false
  643. NSLayoutConstraint.activate([
  644. messageView.leadingAnchor.constraint(equalTo: imageView.trailingAnchor, constant: 10.0),
  645. messageView.topAnchor.constraint(equalTo: titleView.bottomAnchor, constant: 5.0),
  646. messageView.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -40.0),
  647. ])
  648. messageView.textColor = .gray
  649. let text = Utils.previewMessageText(chat: data)
  650. let idMe = UserDefaults.standard.string(forKey: "me") as String?
  651. if let attributeText = text as? NSAttributedString {
  652. if data.fpin == idMe {
  653. let stringMessage = NSMutableAttributedString(string: "")
  654. let imageStatus = NSTextAttachment()
  655. if (data.status == "1" || data.status == "2" ) {
  656. imageStatus.image = UIImage(named: "checklist", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(UIColor.lightGray)
  657. } else if (data.status == "3") {
  658. imageStatus.image = UIImage(named: "double-checklist", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(UIColor.lightGray)
  659. } else if (data.status == "8") {
  660. imageStatus.image = UIImage(named: "message_status_ack", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withRenderingMode(.alwaysOriginal)
  661. } else {
  662. imageStatus.image = UIImage(named: "double-checklist", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(UIColor.systemBlue)
  663. }
  664. imageStatus.bounds = CGRect(x: 0, y: 0, width: 15, height: 15)
  665. let imageStatusString = NSAttributedString(attachment: imageStatus)
  666. stringMessage.append(imageStatusString)
  667. stringMessage.append(NSAttributedString(string: " "))
  668. stringMessage.append(attributeText)
  669. messageView.attributedText = stringMessage
  670. } else {
  671. messageView.attributedText = attributeText
  672. }
  673. } else if let stringText = text as? String {
  674. if data.fpin == idMe {
  675. let stringMessage = NSMutableAttributedString(string: "")
  676. let imageStatus = NSTextAttachment()
  677. if (data.status == "1" || data.status == "2" ) {
  678. imageStatus.image = UIImage(named: "checklist", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(UIColor.lightGray)
  679. } else if (data.status == "3") {
  680. imageStatus.image = UIImage(named: "double-checklist", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(UIColor.lightGray)
  681. } else if (data.status == "8") {
  682. imageStatus.image = UIImage(named: "message_status_ack", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withRenderingMode(.alwaysOriginal)
  683. } else {
  684. imageStatus.image = UIImage(named: "double-checklist", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(UIColor.systemBlue)
  685. }
  686. imageStatus.bounds = CGRect(x: 0, y: 0, width: 15, height: 15)
  687. let imageStatusString = NSAttributedString(attachment: imageStatus)
  688. stringMessage.append(imageStatusString)
  689. stringMessage.append(NSAttributedString(string: " "))
  690. stringMessage.append(NSAttributedString(string: stringText))
  691. messageView.attributedText = stringMessage
  692. } else {
  693. messageView.text = stringText
  694. }
  695. }
  696. messageView.numberOfLines = 2
  697. if data.counter != "0" {
  698. let viewCounter = UIView()
  699. content.addSubview(viewCounter)
  700. viewCounter.translatesAutoresizingMaskIntoConstraints = false
  701. NSLayoutConstraint.activate([
  702. viewCounter.centerYAnchor.constraint(equalTo: content.centerYAnchor),
  703. viewCounter.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -20),
  704. viewCounter.widthAnchor.constraint(greaterThanOrEqualToConstant: 20),
  705. viewCounter.heightAnchor.constraint(equalToConstant: 20)
  706. ])
  707. viewCounter.backgroundColor = .systemRed
  708. viewCounter.layer.cornerRadius = 10
  709. viewCounter.clipsToBounds = true
  710. viewCounter.layer.borderWidth = 0.5
  711. viewCounter.layer.borderColor = UIColor.secondaryColor.cgColor
  712. let labelCounter = UILabel()
  713. viewCounter.addSubview(labelCounter)
  714. labelCounter.translatesAutoresizingMaskIntoConstraints = false
  715. NSLayoutConstraint.activate([
  716. labelCounter.centerYAnchor.constraint(equalTo: viewCounter.centerYAnchor),
  717. labelCounter.leadingAnchor.constraint(equalTo: viewCounter.leadingAnchor, constant: 2),
  718. labelCounter.trailingAnchor.constraint(equalTo: viewCounter.trailingAnchor, constant: -2),
  719. ])
  720. labelCounter.font = UIFont.systemFont(ofSize: 11)
  721. if Int(data.counter)! > 99 {
  722. labelCounter.text = "99+"
  723. } else {
  724. labelCounter.text = data.counter
  725. }
  726. labelCounter.textColor = .secondaryColor
  727. labelCounter.textAlignment = .center
  728. }
  729. case 1:
  730. cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifierContact", for: indexPath)
  731. var content = cell.defaultContentConfiguration()
  732. let data: User
  733. if isFilltering {
  734. data = fillteredData[indexPath.row] as! User
  735. } else {
  736. data = contacts[indexPath.row]
  737. }
  738. content.imageProperties.maximumSize = CGSize(width: 40, height: 40)
  739. getImage(name: data.thumb, placeholderImage: UIImage(named: "Profile---Purple", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), isCircle: true, tableView: tableView, indexPath: indexPath, completion: { result, isDownloaded, image in
  740. content.image = image
  741. })
  742. if (data.official == "1") {
  743. content.attributedText = self.set(image: UIImage(named: "ic_official_flag", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, with: " \(data.fullName)", size: 15, y: -4)
  744. }
  745. else if data.userType == "23" {
  746. content.attributedText = self.set(image: UIImage(named: "ic_internal", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, with: " \(data.fullName)", size: 15, y: -4)
  747. } else if data.userType == "24" {
  748. content.attributedText = self.set(image: UIImage(named: "pb_call_center", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, with: " \(data.fullName)", size: 15, y: -4)
  749. }
  750. else {
  751. content.text = data.fullName
  752. }
  753. content.textProperties.font = UIFont.systemFont(ofSize: 14)
  754. cell.contentConfiguration = content
  755. case 2:
  756. cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifierGroup", for: indexPath)
  757. var content = cell.defaultContentConfiguration()
  758. content.textProperties.font = UIFont.systemFont(ofSize: 14)
  759. let group: Group
  760. if isFilltering {
  761. if indexPath.row == 0 {
  762. group = fillteredData[indexPath.section] as! Group
  763. } else {
  764. group = (fillteredData[indexPath.section] as! Group).childs[indexPath.row - 1]
  765. }
  766. } else {
  767. if indexPath.row == 0 {
  768. group = groups[indexPath.section]
  769. } else {
  770. group = groups[indexPath.section].childs[indexPath.row - 1]
  771. }
  772. }
  773. if group.official == "1" && group.parent == "" {
  774. content.attributedText = self.set(image: UIImage(named: "ic_official_flag", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, with: " \(group.name)", size: 15, y: -4)
  775. }
  776. else if group.isOpen == "1" && group.parent == "" {
  777. if self.traitCollection.userInterfaceStyle == .dark {
  778. content.attributedText = self.set(image: UIImage(systemName: "globe")!.withTintColor(.white), with: " \(group.name)", size: 15, y: -4)
  779. } else {
  780. content.attributedText = self.set(image: UIImage(systemName: "globe")!, with: " \(group.name)", size: 15, y: -4)
  781. }
  782. } else if group.parent == "" {
  783. if self.traitCollection.userInterfaceStyle == .dark {
  784. content.attributedText = self.set(image: UIImage(systemName: "lock.fill")!.withTintColor(.white), with: " \(group.name)", size: 15, y: -4)
  785. } else {
  786. content.attributedText = self.set(image: UIImage(systemName: "lock.fill")!, with: " \(group.name)", size: 15, y: -4)
  787. }
  788. } else {
  789. content.text = group.name
  790. }
  791. if group.childs.count > 0 {
  792. let iconName = (group.isSelected) ? "chevron.up.circle" : "chevron.down.circle"
  793. let imageView = UIImageView(image: UIImage(systemName: iconName))
  794. imageView.tintColor = .black
  795. cell.accessoryView = imageView
  796. }
  797. else {
  798. cell.accessoryView = nil
  799. cell.accessoryType = .none
  800. }
  801. content.imageProperties.maximumSize = CGSize(width: 40, height: 40)
  802. getImage(name: group.profile, placeholderImage: UIImage(named: "Conversation---Purple", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), isCircle: true, tableView: tableView, indexPath: indexPath) { result, isDownloaded, image in
  803. content.image = image
  804. }
  805. cell.contentConfiguration = content
  806. default:
  807. cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifierContact", for: indexPath)
  808. var content = cell.defaultContentConfiguration()
  809. content.text = ""
  810. cell.contentConfiguration = content
  811. }
  812. return cell
  813. }
  814. override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
  815. return 70
  816. }
  817. }
  818. extension ContactChatViewController: UISearchControllerDelegate, UISearchBarDelegate, UISearchResultsUpdating {
  819. func updateSearchResults(for searchController: UISearchController) {
  820. filterContentForSearchText(searchController.searchBar.text!)
  821. }
  822. func set(image: UIImage, with text: String, size: CGFloat, y: CGFloat) -> NSAttributedString {
  823. let attachment = NSTextAttachment()
  824. attachment.image = image
  825. attachment.bounds = CGRect(x: 0, y: y, width: size, height: size)
  826. let attachmentStr = NSAttributedString(attachment: attachment)
  827. let mutableAttributedString = NSMutableAttributedString()
  828. mutableAttributedString.append(attachmentStr)
  829. let textString = NSAttributedString(string: text)
  830. mutableAttributedString.append(textString)
  831. return mutableAttributedString
  832. }
  833. }