ContactChatViewController.swift 70 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355
  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. import Toast_Swift
  11. class ContactChatViewController: UITableViewController {
  12. deinit {
  13. //print(#function, ">>>> TADAA")
  14. NotificationCenter.default.removeObserver(self)
  15. }
  16. var isChooser: ((String, String) -> ())?
  17. var isAdmin: Bool = false
  18. var chats: [Chat] = []
  19. var contacts: [User] = []
  20. var groups: [Group] = []
  21. var groupMap: [String:Int] = [:]
  22. var searchController: UISearchController!
  23. var segment: UISegmentedControl!
  24. var fillteredData: [Any] = []
  25. var isSearchBarEmpty: Bool {
  26. return searchController.searchBar.text!.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
  27. }
  28. var isFilltering: Bool {
  29. return !isSearchBarEmpty
  30. }
  31. var noData = false
  32. var noUCList = false
  33. func filterContentForSearchText(_ searchText: String) {
  34. func filterContact() {
  35. Utils.inTabChats = false
  36. fillteredData = self.contacts.filter { $0.fullName.lowercased().contains(searchText.lowercased()) }
  37. }
  38. func filterGroup() {
  39. Utils.inTabChats = false
  40. fillteredData = self.groups.filter { $0.name.lowercased().contains(searchText.lowercased()) }
  41. }
  42. if !searchText.isEmpty {
  43. if segment.numberOfSegments == 3 {
  44. switch segment.selectedSegmentIndex {
  45. case 1:
  46. Utils.inTabChats = false
  47. fillteredData = self.contacts.filter { $0.fullName.lowercased().contains(searchText.lowercased()) }
  48. case 2:
  49. Utils.inTabChats = false
  50. fillteredData = self.groups.filter { $0.name.lowercased().contains(searchText.lowercased()) }
  51. default:
  52. Utils.inTabChats = true
  53. fillteredData = self.chats.filter { $0.name.lowercased().contains(searchText.lowercased()) || $0.messageText.lowercased().contains(searchText.lowercased()) }
  54. }
  55. } else {
  56. switch segment.selectedSegmentIndex {
  57. case 1:
  58. filterGroup()
  59. default:
  60. filterContact()
  61. }
  62. }
  63. }
  64. tableView.reloadData()
  65. }
  66. override func viewDidLoad() {
  67. super.viewDidLoad()
  68. let me = UserDefaults.standard.string(forKey: "me")!
  69. Database.shared.database?.inTransaction({ fmdb, rollback in
  70. 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() {
  71. isAdmin = cursor.string(forColumnIndex: 3) == "23" || cursor.string(forColumnIndex: 3) == "24"
  72. cursor.close()
  73. }
  74. })
  75. // navigationController?.navigationBar.prefersLargeTitles = true
  76. navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Cancel".localized(), style: .plain, target: self, action: #selector(cancel(sender:)))
  77. let childrenMenu : [UIAction] = [
  78. UIAction(title: "Create Group".localized(), image: UIImage(systemName: "person.and.person"), handler: {[weak self](_) in
  79. let controller = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "createGroupNav") as! UINavigationController
  80. let vc = controller.topViewController as! GroupCreateViewController
  81. vc.isDismiss = { id in
  82. self?.groupMap.removeAll()
  83. let controller = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "groupDetailView") as! GroupDetailViewController
  84. controller.data = id
  85. self?.navigationController?.show(controller, sender: nil)
  86. }
  87. self?.navigationController?.present(controller, animated: true, completion: nil)
  88. }),
  89. UIAction(title: "Add Friends".localized(), image: UIImage(systemName: "person.badge.plus"), handler: {[weak self](_) in
  90. self?.addFriend(sender: UIBarButtonItem())
  91. }),
  92. // UIAction(title: "Configure Email", image: UIImage(systemName: "mail"), handler: {[weak self](_) in
  93. //
  94. // }),
  95. UIAction(title: "Favorite Messages".localized(), image: UIImage(systemName: "star"), handler: {[weak self](_) in
  96. let editorStaredVC = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "staredVC") as! EditorStarMessages
  97. self?.navigationController?.show(editorStaredVC, sender: nil)
  98. }),
  99. ]
  100. //debug only
  101. // isAdmin = true
  102. // if(isAdmin){
  103. // childrenMenu.append(UIAction(title: "Broadcast Message".localized(), image: UIImage(systemName: "envelope.open"), handler: {[weak self](_) in
  104. // let controller = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "broadcastNav")
  105. // controller.modalPresentationStyle = .fullScreen
  106. // self?.navigationController?.present(controller, animated: true, completion: nil)
  107. // }))
  108. // childrenMenu.append(UIAction(title: "Live Streaming".localized(), image: UIImage(systemName: "video.bubble.left"), handler: {[weak self](_) in
  109. // let navigationController = UINavigationController(rootViewController: QmeraCreateStreamingViewController())
  110. // navigationController.modalPresentationStyle = .fullScreen
  111. // navigationController.navigationBar.tintColor = .white
  112. // navigationController.navigationBar.barTintColor = .mainColor
  113. // navigationController.navigationBar.isTranslucent = false
  114. // navigationController.navigationBar.overrideUserInterfaceStyle = .dark
  115. // navigationController.navigationBar.barStyle = .black
  116. // let cancelButtonAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.foregroundColor: UIColor.white]
  117. // UIBarButtonItem.appearance().setTitleTextAttributes(cancelButtonAttributes, for: .normal)
  118. // let textAttributes = [NSAttributedString.Key.foregroundColor:UIColor.white]
  119. // navigationController.navigationBar.titleTextAttributes = textAttributes
  120. // navigationController.view.backgroundColor = .mainColor
  121. // self?.navigationController?.present(navigationController, animated: true, completion: nil)
  122. // }))
  123. // }
  124. if noUCList {
  125. let buttonAddFriend = UIBarButtonItem(image: UIImage(systemName: "person.badge.plus", withConfiguration: UIImage.SymbolConfiguration(pointSize: 18, weight: .regular, scale: .default))?.withTintColor(.white), style: .plain, target: self, action: #selector(addFriend(sender:)))
  126. navigationItem.rightBarButtonItem = buttonAddFriend
  127. } else {
  128. let menu = UIMenu(title: "", children: childrenMenu)
  129. navigationItem.rightBarButtonItem = UIBarButtonItem(title: nil, image: UIImage(systemName: "ellipsis"), primaryAction: .none, menu: menu)
  130. }
  131. searchController = UISearchController(searchResultsController: nil)
  132. searchController.delegate = self
  133. searchController.searchResultsUpdater = self
  134. searchController.searchBar.autocapitalizationType = .none
  135. searchController.searchBar.delegate = self
  136. searchController.searchBar.barTintColor = .secondaryColor
  137. searchController.searchBar.searchTextField.backgroundColor = .secondaryColor
  138. searchController.obscuresBackgroundDuringPresentation = false
  139. searchController.searchBar.setMagnifyingGlassColorTo(color: .mainColor)
  140. searchController.searchBar.tintColor = .mainColor
  141. searchController.searchBar.searchTextField.attributedPlaceholder = NSAttributedString(string: "Search".localized(), attributes: [NSAttributedString.Key.foregroundColor: UIColor.gray, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 16)])
  142. navigationItem.searchController = searchController
  143. navigationItem.hidesSearchBarWhenScrolling = false
  144. definesPresentationContext = true
  145. var dataSegment = ["Chats".localized(), "Contacts".localized(), "Groups".localized()]
  146. if noUCList{
  147. dataSegment = ["Contacts".localized(), "Groups".localized()]
  148. }
  149. segment = UISegmentedControl(items: dataSegment)
  150. segment.sizeToFit()
  151. segment.selectedSegmentIndex = 0
  152. segment.addTarget(self, action: #selector(segmentChanged(sender:)), for: .valueChanged)
  153. segment.setTitleTextAttributes([NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 12.0)], for: .normal)
  154. Utils.inTabChats = true
  155. NotificationCenter.default.addObserver(self, selector: #selector(onStatusChat(notification:)), name: NSNotification.Name(rawValue: Nexilis.listenerStatusChat), object: nil)
  156. NotificationCenter.default.addObserver(self, selector: #selector(onReceiveMessage(notification:)), name: NSNotification.Name(rawValue: Nexilis.listenerReceiveChat), object: nil)
  157. NotificationCenter.default.addObserver(self, selector: #selector(onReload(notification:)), name: NSNotification.Name(rawValue: "onMember"), object: nil)
  158. NotificationCenter.default.addObserver(self, selector: #selector(onReload(notification:)), name: NSNotification.Name(rawValue: "onUpdatePersonInfo"), object: nil)
  159. tableView.tableHeaderView = segment
  160. tableView.tableFooterView = UIView()
  161. pullBuddy()
  162. getData()
  163. }
  164. override func viewWillAppear(_ animated: Bool) {
  165. let attributes = [NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 16.0), NSAttributedString.Key.foregroundColor: UIColor.white]
  166. let navBarAppearance = UINavigationBarAppearance()
  167. navBarAppearance.configureWithOpaqueBackground()
  168. navBarAppearance.backgroundColor = UIColor.mainColor
  169. navBarAppearance.titleTextAttributes = attributes
  170. navigationController?.navigationBar.standardAppearance = navBarAppearance
  171. navigationController?.navigationBar.scrollEdgeAppearance = navBarAppearance
  172. // removeAllData()
  173. // getData()
  174. }
  175. override func viewWillDisappear(_ animated: Bool) {
  176. Utils.inTabChats = false
  177. }
  178. override func viewDidAppear(_ animated: Bool) {
  179. if isChooser != nil {
  180. self.navigationController?.navigationBar.topItem?.title = "Forward Messages".localized()
  181. self.navigationController?.navigationBar.setNeedsLayout()
  182. } else if noUCList{
  183. self.navigationController?.navigationBar.topItem?.title = "Start Chat".localized()
  184. self.navigationController?.navigationBar.setNeedsLayout()
  185. } else {
  186. self.navigationController?.navigationBar.topItem?.title = "Start Conversation".localized()
  187. self.navigationController?.navigationBar.setNeedsLayout()
  188. }
  189. DispatchQueue.global().async {
  190. self.getOpenGroups(listGroups: self.groups, completion: { g in
  191. DispatchQueue.main.async {
  192. for og in g {
  193. if self.groups.first(where: { $0.id == og.id }) == nil {
  194. self.groups.append(og)
  195. }
  196. }
  197. DispatchQueue.main.async {
  198. self.tableView.reloadData()
  199. }
  200. }
  201. })
  202. }
  203. if segment.selectedSegmentIndex == 0 {
  204. Utils.inTabChats = true
  205. }
  206. }
  207. @objc func addFriend(sender: UIBarButtonItem) {
  208. let controller = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "addFriendNav") as! UINavigationController
  209. if let vc = controller.viewControllers.first as? AddFriendTableViewController {
  210. vc.isDismiss = {
  211. self.getContacts {
  212. DispatchQueue.main.async {
  213. self.tableView.reloadData()
  214. }
  215. }
  216. }
  217. }
  218. navigationController?.present(controller, animated: true, completion: nil)
  219. }
  220. // func removeAllData() {
  221. // groups.removeAll()
  222. // groupMap.removeAll()
  223. // chats.removeAll()
  224. // tableView.reloadData()
  225. // }
  226. @objc func onReload(notification: NSNotification) {
  227. let data:[AnyHashable : Any] = notification.userInfo!
  228. if data["member"] as? String == UserDefaults.standard.string(forKey: "me") {
  229. DispatchQueue.main.async {
  230. self.getData()
  231. }
  232. } else if data["state"] as? Int == 99 {
  233. //print("MASUK 99")
  234. DispatchQueue.main.async {
  235. self.getData()
  236. }
  237. }
  238. }
  239. @objc func onReceiveMessage(notification: NSNotification) {
  240. DispatchQueue.main.async { [self] in
  241. let data:[AnyHashable : Any] = notification.userInfo!
  242. if let dataMessage = data["message"] as? TMessage {
  243. let chatData = dataMessage.mBodies
  244. if chatData[CoreMessage_TMessageKey.IS_CALL_CENTER] == nil || chatData[CoreMessage_TMessageKey.IS_CALL_CENTER] == "0" {
  245. var indexChat: Int?
  246. if chatData[CoreMessage_TMessageKey.MESSAGE_SCOPE_ID] == "3" && chatData[CoreMessage_TMessageKey.F_PIN] != User.getMyPin() {
  247. indexChat = chats.firstIndex(where: { $0.fpin == chatData[CoreMessage_TMessageKey.F_PIN] })
  248. } else if chatData[CoreMessage_TMessageKey.MESSAGE_SCOPE_ID] == "4" && chatData[CoreMessage_TMessageKey.F_PIN] != User.getMyPin() {
  249. indexChat = chats.firstIndex(where: { (chatData[CoreMessage_TMessageKey.CHAT_ID] ?? "").isEmpty ? $0.pin == chatData[CoreMessage_TMessageKey.L_PIN] : $0.pin == chatData[CoreMessage_TMessageKey.CHAT_ID] })
  250. }
  251. let newChat = Chat.getData(messageId: chatData[CoreMessage_TMessageKey.MESSAGE_ID] ?? "")
  252. if newChat.count > 0 {
  253. if indexChat != nil {
  254. chats.remove(at: indexChat!)
  255. chats.insert(newChat[0], at: 0)
  256. let indexPathToMove = IndexPath(row: indexChat!, section: 0)
  257. let indexPathNewPosition = IndexPath(row: 0, section: 0)
  258. tableView.performBatchUpdates({
  259. tableView.moveRow(at: indexPathToMove, to: indexPathNewPosition)
  260. }, completion: nil)
  261. tableView.beginUpdates()
  262. tableView.reloadRows(at: [IndexPath(row: 0, section: 0)], with: .none)
  263. tableView.endUpdates()
  264. } else {
  265. chats.insert(newChat[0], at: 0)
  266. tableView.reloadData()
  267. }
  268. }
  269. }
  270. }
  271. }
  272. }
  273. @objc func onStatusChat(notification: NSNotification) {
  274. DispatchQueue.main.async { [self] in
  275. let data:[AnyHashable : Any] = notification.userInfo!
  276. if let dataMessage = data["message"] as? TMessage {
  277. let indexChat = chats.firstIndex(where: { (dataMessage.getBody(key: CoreMessage_TMessageKey.MESSAGE_ID)).contains(",") ? $0.messageId == (dataMessage.getBody(key: CoreMessage_TMessageKey.MESSAGE_ID)).components(separatedBy: ",")[1] : $0.messageId == dataMessage.getBody(key: CoreMessage_TMessageKey.MESSAGE_ID) })
  278. if indexChat != nil {
  279. tableView.beginUpdates()
  280. tableView.reloadRows(at: [IndexPath(row: indexChat!, section: 0)], with: .none)
  281. tableView.endUpdates()
  282. }
  283. }
  284. }
  285. }
  286. @objc func add(sender: Any) {
  287. }
  288. @objc func cancel(sender: Any) {
  289. navigationController?.dismiss(animated: true, completion: nil)
  290. }
  291. @objc func segmentChanged(sender: Any) {
  292. filterContentForSearchText(searchController.searchBar.text!)
  293. }
  294. // MARK: - Data source
  295. func getData() {
  296. getChats {
  297. self.getContacts {
  298. self.getGroups { g1 in
  299. self.groupMap.removeAll()
  300. self.groups = g1
  301. DispatchQueue.main.async {
  302. self.tableView.reloadData()
  303. }
  304. }
  305. }
  306. }
  307. }
  308. func getChats(completion: @escaping ()->()) {
  309. DispatchQueue.global().async {
  310. self.chats = Chat.getData()
  311. completion()
  312. }
  313. }
  314. private func getContacts(completion: @escaping ()->()) {
  315. self.contacts.removeAll()
  316. let gptUser = User(pin: "-997",
  317. firstName: "GPT SmartBot",
  318. lastName: "",
  319. thumb: "",
  320. userType: "0",
  321. official: "1")
  322. contacts.append(gptUser)
  323. DispatchQueue.global().async {
  324. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  325. 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") {
  326. while cursorData.next() {
  327. let user = User(pin: cursorData.string(forColumnIndex: 0) ?? "",
  328. firstName: cursorData.string(forColumnIndex: 1) ?? "",
  329. lastName: cursorData.string(forColumnIndex: 2) ?? "",
  330. thumb: cursorData.string(forColumnIndex: 3) ?? "",
  331. userType: cursorData.string(forColumnIndex: 5) ?? "")
  332. if (user.firstName + " " + user.lastName).trimmingCharacters(in: .whitespaces) == "USR\(user.pin)" {
  333. continue
  334. }
  335. user.official = cursorData.string(forColumnIndex: 4) ?? ""
  336. if !self.contacts.contains(where: {$0.pin == user.pin}) {
  337. self.contacts.append(user)
  338. }
  339. }
  340. cursorData.close()
  341. }
  342. completion()
  343. })
  344. }
  345. }
  346. private func getGroupRecursive(fmdb: FMDatabase, id: String = "", parent: String = "") -> [Group] {
  347. var data: [Group] = []
  348. 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, g.level from GROUPZ g where "
  349. if id.isEmpty {
  350. query += "g.parent = '\(parent)'"
  351. } else {
  352. query += "g.group_id = '\(id)'"
  353. }
  354. query += "order by 10 desc"
  355. if let cursor = Database.shared.getRecords(fmdb: fmdb, query: query) {
  356. while cursor.next() {
  357. let group = Group(
  358. id: cursor.string(forColumnIndex: 0) ?? "",
  359. name: cursor.string(forColumnIndex: 1) ?? "",
  360. profile: cursor.string(forColumnIndex: 2) ?? "",
  361. quote: cursor.string(forColumnIndex: 3) ?? "",
  362. by: cursor.string(forColumnIndex: 4) ?? "",
  363. date: cursor.string(forColumnIndex: 5) ?? "",
  364. parent: cursor.string(forColumnIndex: 6) ?? "",
  365. chatId: "",
  366. groupType: cursor.string(forColumnIndex: 7) ?? "",
  367. isOpen: cursor.string(forColumnIndex: 8) ?? "",
  368. official: cursor.string(forColumnIndex: 9) ?? "",
  369. isEducation: cursor.string(forColumnIndex: 10) ?? "",
  370. level: cursor.string(forColumnIndex: 11) ?? "")
  371. if group.chatId.isEmpty {
  372. 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, level: group.level != "-1" ? group.level : "2")
  373. group.childs.append(lounge)
  374. }
  375. if let topicCursor = Database.shared.getRecords(fmdb: fmdb, query: "select chat_id, title, thumb from DISCUSSION_FORUM where group_id = '\(group.id)'") {
  376. while topicCursor.next() {
  377. let topic = Group(id: group.id,
  378. name: topicCursor.string(forColumnIndex: 1) ?? "",
  379. profile: topicCursor.string(forColumnIndex: 2) ?? "",
  380. quote: group.quote,
  381. by: group.by,
  382. date: group.date,
  383. parent: group.id,
  384. chatId: topicCursor.string(forColumnIndex: 0) ?? "",
  385. groupType: group.groupType,
  386. isOpen: group.isOpen,
  387. official: group.official,
  388. isEducation: group.isEducation,
  389. level: group.level != "-1" ? group.level : "2")
  390. group.childs.append(topic)
  391. }
  392. topicCursor.close()
  393. }
  394. if !group.id.isEmpty {
  395. // if group.official == "1" {
  396. // let idMe = UserDefaults.standard.string(forKey: "me") as String?
  397. // if let cursorUser = Database.shared.getRecords(fmdb: fmdb, query: "SELECT user_type FROM BUDDY where f_pin='\(idMe!)'"), cursorUser.next() {
  398. //// if cursorUser.string(forColumnIndex: 0) == "23" || cursorUser.string(forColumnIndex: 0) == "24" {
  399. //// group.childs.append(contentsOf: getGroupRecursive(fmdb: fmdb, parent: group.id))
  400. //// }
  401. // group.childs.append(contentsOf: getGroupRecursive(fmdb: fmdb, parent: group.id))
  402. // cursorUser.close()
  403. // }
  404. // } else if group.official != "1"{
  405. // group.childs.append(contentsOf: getGroupRecursive(fmdb: fmdb, parent: group.id))
  406. // }
  407. group.childs.append(contentsOf: getGroupRecursive(fmdb: fmdb, parent: group.id))
  408. // group.childs = group.childs.sorted(by: { $0.name < $1.name })
  409. // let dataLounge = group.childs.filter({$0.name == "Lounge".localized()})
  410. // group.childs = group.childs.filter({ $0.name != "Lounge".localized() })
  411. // group.childs.insert(contentsOf: dataLounge, at: 0)
  412. }
  413. data.append(group)
  414. }
  415. cursor.close()
  416. }
  417. return data
  418. }
  419. private func getOpenGroups(listGroups: [Group], completion: @escaping ([Group]) -> ()) {
  420. if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getOpenGroups(p_account: "1,2,3,5,6,7", offset: "0", search: "")) {
  421. var dataGroups: [Group] = []
  422. if (response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99") == "00") {
  423. let data = response.getBody(key: CoreMessage_TMessageKey.DATA)
  424. if let json = try! JSONSerialization.jsonObject(with: data.data(using: String.Encoding.utf8)!, options: []) as? [[String: Any?]] {
  425. for dataJson in json {
  426. let group = Group(
  427. id: dataJson[CoreMessage_TMessageKey.GROUP_ID] as? String ?? "",
  428. name: dataJson[CoreMessage_TMessageKey.GROUP_NAME] as? String ?? "",
  429. profile: dataJson[CoreMessage_TMessageKey.THUMB_ID] as? String ?? "",
  430. quote: dataJson[CoreMessage_TMessageKey.QUOTE] as? String ?? "",
  431. by: dataJson[CoreMessage_TMessageKey.BLOCK] as? String ?? "",
  432. date: "",
  433. parent: "",
  434. chatId: "",
  435. groupType: "NOTJOINED",
  436. isOpen: dataJson[CoreMessage_TMessageKey.IS_OPEN] as? String ?? "",
  437. official: "0",
  438. isEducation: "")
  439. dataGroups.append(group)
  440. }
  441. }
  442. } else {
  443. DispatchQueue.main.async {
  444. self.groups.removeAll()
  445. self.groups.append(contentsOf: listGroups)
  446. self.tableView.reloadData()
  447. }
  448. }
  449. completion(dataGroups)
  450. }
  451. }
  452. private func getGroups(id: String = "", parent: String = "", completion: @escaping ([Group]) -> ()) {
  453. DispatchQueue.global().async {
  454. Database.shared.database?.inTransaction({ fmdb, rollback in
  455. completion(self.getGroupRecursive(fmdb: fmdb, id: id, parent: parent))
  456. })
  457. }
  458. }
  459. private func pullBuddy() {
  460. if let me = UserDefaults.standard.string(forKey: "me") {
  461. DispatchQueue.global().async {
  462. let _ = Nexilis.write(message: CoreMessage_TMessageBank.getBatchBuddiesInfos(p_f_pin: me, last_update: 0))
  463. }
  464. }
  465. }
  466. private func joinOpenGroup(groupId: String, flagMember: String = "0", completion: @escaping (Bool) -> ()) {
  467. DispatchQueue.global().async {
  468. var result: Bool = false
  469. let idMe = UserDefaults.standard.string(forKey: "me") as String?
  470. if let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getAddGroupMember(p_group_id: groupId, p_member_pin: idMe!, p_position: "0")), response.isOk() {
  471. result = true
  472. }
  473. completion(result)
  474. }
  475. }
  476. }
  477. // MARK: - Table view data source
  478. extension ContactChatViewController {
  479. override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
  480. if noData {
  481. return
  482. }
  483. tableView.deselectRow(at: indexPath, animated: true)
  484. func selectOnContact() {
  485. let data: User
  486. if isFilltering {
  487. data = fillteredData[indexPath.row] as! User
  488. } else {
  489. data = contacts[indexPath.row]
  490. }
  491. if data.pin == "-997" {
  492. APIS.openSmartChatbot()
  493. return
  494. }
  495. if let chooser = isChooser {
  496. var exblock = User.getDataCanNil(pin: data.pin)?.ex_block
  497. exblock = exblock == nil ? "0" : exblock!.isEmpty ? "0" : exblock!
  498. if exblock != "0" {
  499. if exblock == "1" {
  500. self.view.makeToast("You blocked this user".localized())
  501. } else {
  502. self.view.makeToast("You have been blocked by this user".localized())
  503. }
  504. return
  505. }
  506. chooser("3", data.pin)
  507. dismiss(animated: true, completion: nil)
  508. return
  509. }
  510. let editorPersonalVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorPersonalVC") as! EditorPersonal
  511. editorPersonalVC.hidesBottomBarWhenPushed = true
  512. editorPersonalVC.unique_l_pin = data.pin
  513. navigationController?.show(editorPersonalVC, sender: nil)
  514. }
  515. if segment.numberOfSegments == 3 {
  516. switch segment.selectedSegmentIndex {
  517. case 0:
  518. let data: Chat
  519. if isFilltering {
  520. data = fillteredData[indexPath.row] as! Chat
  521. } else {
  522. data = chats[indexPath.row]
  523. }
  524. if let chooser = isChooser {
  525. var exblock = User.getDataCanNil(pin: data.pin)?.ex_block
  526. exblock = exblock == nil ? "0" : exblock!.isEmpty ? "0" : exblock!
  527. if exblock != "0" {
  528. if exblock == "1" {
  529. self.view.makeToast("You blocked this user".localized())
  530. } else {
  531. self.view.makeToast("You have been blocked by this user".localized())
  532. }
  533. return
  534. }
  535. if data.pin == "-999"{
  536. return
  537. }
  538. chooser(data.messageScope, data.pin)
  539. dismiss(animated: true, completion: nil)
  540. return
  541. }
  542. if data.messageScope == "3" {
  543. let editorPersonalVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorPersonalVC") as! EditorPersonal
  544. editorPersonalVC.hidesBottomBarWhenPushed = true
  545. editorPersonalVC.unique_l_pin = data.pin
  546. navigationController?.show(editorPersonalVC, sender: nil)
  547. } else {
  548. groupMap.removeAll()
  549. let editorGroupVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorGroupVC") as! EditorGroup
  550. editorGroupVC.hidesBottomBarWhenPushed = true
  551. editorGroupVC.unique_l_pin = data.pin
  552. navigationController?.show(editorGroupVC, sender: nil)
  553. }
  554. case 1:
  555. selectOnContact()
  556. case 2:
  557. expandCollapseGroup(tableView: tableView, indexPath: indexPath)
  558. default:
  559. let data = contacts[indexPath.row]
  560. let editorPersonalVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorPersonalVC") as! EditorPersonal
  561. editorPersonalVC.hidesBottomBarWhenPushed = true
  562. editorPersonalVC.unique_l_pin = data.pin
  563. navigationController?.show(editorPersonalVC, sender: nil)
  564. }
  565. } else {
  566. switch segment.selectedSegmentIndex {
  567. case 0:
  568. selectOnContact()
  569. case 1:
  570. expandCollapseGroup(tableView: tableView, indexPath: indexPath)
  571. default:
  572. let data = contacts[indexPath.row]
  573. let editorPersonalVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorPersonalVC") as! EditorPersonal
  574. editorPersonalVC.hidesBottomBarWhenPushed = true
  575. editorPersonalVC.unique_l_pin = data.pin
  576. navigationController?.show(editorPersonalVC, sender: nil)
  577. }
  578. }
  579. }
  580. func expandCollapseGroup(tableView: UITableView, indexPath: IndexPath) {
  581. let group: Group
  582. if isFilltering {
  583. if indexPath.row == 0 {
  584. group = fillteredData[indexPath.section] as! Group
  585. } else {
  586. group = (fillteredData[indexPath.section] as! Group).childs[indexPath.row - 1]
  587. }
  588. } else {
  589. if indexPath.row == 0 {
  590. group = groups[indexPath.section]
  591. } else {
  592. group = groups[indexPath.section].childs[indexPath.row - 1]
  593. }
  594. }
  595. group.isSelected = !group.isSelected
  596. if !group.isSelected{
  597. var sects = 0
  598. var sect = indexPath.section
  599. var id = group.id
  600. if let _ = groupMap[id] {
  601. var loooop = true
  602. repeat {
  603. let c = sect + 1
  604. if isFilltering {
  605. if let o = self.fillteredData[c] as? Group {
  606. if o.parent == id {
  607. sects = sects + 1
  608. sect = c
  609. id = o.id
  610. (self.fillteredData[c] as! Group).isSelected = false
  611. self.groupMap.removeValue(forKey: (self.fillteredData[c] as! Group).id)
  612. }
  613. else {
  614. loooop = false
  615. }
  616. }
  617. }
  618. else {
  619. if c < self.groups.count && self.groups[c].parent == id {
  620. sects = sects + 1
  621. sect = c
  622. id = self.groups[c].id
  623. self.groups[c].isSelected = false
  624. self.groupMap.removeValue(forKey: self.groups[c].id)
  625. }
  626. else {
  627. loooop = false
  628. }
  629. }
  630. } while(loooop)
  631. }
  632. for i in stride(from: sects, to: 0, by: -1){
  633. if isFilltering {
  634. self.fillteredData.remove(at: indexPath.section + i)
  635. }
  636. else {
  637. self.groups.remove(at: indexPath.section + i)
  638. }
  639. }
  640. groupMap.removeValue(forKey: group.id)
  641. }
  642. if group.groupType == "NOTJOINED" {
  643. let alert = LibAlertController(title: "Do you want to join this group?".localized(), message: "Groups : \(group.name)\nMembers: \(group.by)".localized(), preferredStyle: .alert)
  644. alert.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel, handler: nil))
  645. alert.addAction(UIAlertAction(title: "Join".localized(), style: .default, handler: {(_) in
  646. self.joinOpenGroup(groupId: group.id, completion: { result in
  647. if result {
  648. DispatchQueue.main.async {
  649. self.groupMap.removeAll()
  650. let editorGroupVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorGroupVC") as! EditorGroup
  651. editorGroupVC.hidesBottomBarWhenPushed = true
  652. editorGroupVC.unique_l_pin = group.id
  653. self.navigationController?.show(editorGroupVC, sender: nil)
  654. }
  655. }
  656. })
  657. }))
  658. self.present(alert, animated: true, completion: nil)
  659. return
  660. }
  661. if group.childs.count == 0 {
  662. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  663. let idMe = UserDefaults.standard.string(forKey: "me") as String?
  664. if let cursorMember = Database.shared.getRecords(fmdb: fmdb, query: "select f_pin from GROUPZ_MEMBER where group_id = '\(group.id)' and f_pin = '\(idMe!)'"), cursorMember.next() {
  665. let groupId = group.chatId.isEmpty ? group.id : group.chatId
  666. if let chooser = isChooser {
  667. chooser("4", groupId)
  668. dismiss(animated: true, completion: nil)
  669. return
  670. }
  671. self.groupMap.removeAll()
  672. let editorGroupVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorGroupVC") as! EditorGroup
  673. editorGroupVC.hidesBottomBarWhenPushed = true
  674. editorGroupVC.unique_l_pin = groupId
  675. navigationController?.show(editorGroupVC, sender: nil)
  676. cursorMember.close()
  677. } else {
  678. self.view.makeToast("You are not a member of this group".localized(), duration: 0.5)
  679. }
  680. })
  681. } else {
  682. if indexPath.row == 0 {
  683. tableView.reloadData()
  684. } else {
  685. getGroups(id: group.id) { g in
  686. DispatchQueue.main.async {
  687. //print("index path section: \(indexPath.section)")
  688. //print("index path row: \(indexPath.row)")
  689. //print("index path item: \(indexPath.item)")
  690. if self.isFilltering {
  691. // self.fillteredData.remove(at: indexPath.section)
  692. if self.fillteredData[indexPath.section] is Group {
  693. self.groupMap[(self.fillteredData[indexPath.section] as! Group).id] = 1
  694. self.fillteredData.insert(contentsOf: g, at: indexPath.section + 1)
  695. }
  696. } else {
  697. // self.groups.remove(at: indexPath.section)
  698. self.groupMap[self.groups[indexPath.section].id] = 1
  699. self.groups.insert(contentsOf: g, at: indexPath.section + 1)
  700. }
  701. //print("groupMap: \(self.groupMap)")
  702. tableView.reloadData()
  703. self.expandCollapseGroup(tableView: tableView, indexPath: IndexPath(row: 0, section: indexPath.section + 1))
  704. }
  705. }
  706. }
  707. }
  708. }
  709. }
  710. extension ContactChatViewController {
  711. override func numberOfSections(in tableView: UITableView) -> Int {
  712. if isFilltering {
  713. if ((segment.numberOfSegments == 3 && segment.selectedSegmentIndex == 2) || (segment.numberOfSegments < 3 && segment.selectedSegmentIndex == 1)) {
  714. return fillteredData.count
  715. }
  716. return 1
  717. } else {
  718. if ((segment.numberOfSegments == 3 && segment.selectedSegmentIndex == 2) || (segment.numberOfSegments < 3 && segment.selectedSegmentIndex == 1)) {
  719. return groups.count
  720. }
  721. return 1
  722. }
  723. }
  724. override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  725. var value = 0
  726. if isFilltering {
  727. func filterGroup(groups: [Group]) {
  728. let group = groups[section]
  729. if group.isSelected {
  730. if let _ = groupMap[group.id] {
  731. value = 1
  732. }
  733. else {
  734. value = group.childs.count + 1
  735. }
  736. } else {
  737. value = 1
  738. }
  739. }
  740. if ((segment.numberOfSegments == 3 && segment.selectedSegmentIndex == 2) || (segment.numberOfSegments < 3 && segment.selectedSegmentIndex == 1)), let groups = fillteredData as? [Group] {
  741. filterGroup(groups: groups)
  742. } else {
  743. value = fillteredData.count
  744. }
  745. return value
  746. }
  747. if segment.numberOfSegments == 3 {
  748. switch segment.selectedSegmentIndex {
  749. case 0:
  750. value = chats.count
  751. case 1:
  752. value = contacts.count
  753. case 2:
  754. let group = groups[section]
  755. if group.isSelected {
  756. if let _ = groupMap[group.id] {
  757. value = 1
  758. }
  759. else {
  760. value = group.childs.count + 1
  761. }
  762. } else {
  763. value = 1
  764. }
  765. default:
  766. value = chats.count
  767. }
  768. } else {
  769. switch segment.selectedSegmentIndex {
  770. case 0:
  771. value = contacts.count
  772. case 1:
  773. let group = groups[section]
  774. if group.isSelected {
  775. if let _ = groupMap[group.id] {
  776. value = 1
  777. }
  778. else {
  779. value = group.childs.count + 1
  780. }
  781. } else {
  782. value = 1
  783. }
  784. default:
  785. value = chats.count
  786. }
  787. }
  788. if value == 0 {
  789. noData = true
  790. value = 1
  791. tableView.separatorStyle = .none
  792. } else {
  793. noData = false
  794. tableView.separatorStyle = .singleLine
  795. }
  796. return value
  797. }
  798. override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  799. var cell: UITableViewCell!
  800. switch segment.selectedSegmentIndex {
  801. case 0:
  802. if segment.numberOfSegments < 3 {
  803. cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifierContact", for: indexPath)
  804. var content = cell.defaultContentConfiguration()
  805. let data: User
  806. if isFilltering {
  807. data = fillteredData[indexPath.row] as! User
  808. } else {
  809. if indexPath.row > contacts.count - 1 {
  810. return cell
  811. }
  812. data = contacts[indexPath.row]
  813. }
  814. content.imageProperties.maximumSize = CGSize(width: 40, height: 40)
  815. if data.pin == "-997" {
  816. content.image = UIImage(named: "pb_gpt_bot", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
  817. }
  818. else {
  819. 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
  820. content.image = image
  821. })
  822. }
  823. if User.isOfficial(official_account: data.official ?? "") || User.isOfficialRegular(official_account: data.official ?? "") {
  824. 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, colorText: UIColor.officialColor)
  825. } else if User.isVerified(official_account: data.official ?? "") {
  826. content.attributedText = self.set(image: UIImage(named: "ic_verified", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, with: " \(data.fullName)", size: 15, y: -4, colorText: UIColor.verifiedColor)
  827. }
  828. else if User.isInternal(userType: data.userType ?? "") {
  829. content.attributedText = self.set(image: UIImage(named: "ic_internal", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, with: " \(data.fullName)", size: 15, y: -4, colorText: UIColor.internalColor)
  830. } else if User.isCallCenter(userType: data.userType ?? "") {
  831. // let dataCategory = CategoryCC.getDataFromServiceId(service_id: data.ex_offmp!)
  832. // if dataCategory != nil {
  833. // content.attributedText = self.set(image: UIImage(named: "pb_call_center", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, with: " \(data.fullName) (\(dataCategory!.service_name))", size: 15, y: -4, colorText: UIColor.ccColor)
  834. // } else {
  835. 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, colorText: UIColor.ccColor)
  836. // }
  837. } else {
  838. content.text = data.fullName
  839. }
  840. content.textProperties.font = UIFont.systemFont(ofSize: 14)
  841. cell.contentConfiguration = content
  842. } else {
  843. cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifierChat", for: indexPath)
  844. let content = cell.contentView
  845. if content.subviews.count > 0 {
  846. content.subviews.forEach { $0.removeFromSuperview() }
  847. }
  848. if noData {
  849. let labelNochat = UILabel()
  850. labelNochat.text = "There are no conversations".localized()
  851. labelNochat.font = .systemFont(ofSize: 13)
  852. labelNochat.textColor = .black
  853. content.addSubview(labelNochat)
  854. labelNochat.anchor(centerX: content.centerXAnchor, centerY: content.centerYAnchor)
  855. cell.backgroundColor = .clear
  856. cell.selectionStyle = .none
  857. return cell
  858. }
  859. let data: Chat
  860. if isFilltering {
  861. data = fillteredData[indexPath.row] as! Chat
  862. } else {
  863. if chats.count == 0 {
  864. let labelNochat = UILabel()
  865. labelNochat.text = "There are no conversations".localized()
  866. labelNochat.font = .systemFont(ofSize: 13)
  867. labelNochat.textColor = .black
  868. content.addSubview(labelNochat)
  869. labelNochat.anchor(centerX: content.centerXAnchor, centerY: content.centerYAnchor)
  870. cell.backgroundColor = .clear
  871. cell.selectionStyle = .none
  872. return cell
  873. }
  874. data = chats[indexPath.row]
  875. }
  876. let imageView = UIImageView()
  877. content.addSubview(imageView)
  878. imageView.translatesAutoresizingMaskIntoConstraints = false
  879. NSLayoutConstraint.activate([
  880. imageView.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: 10.0),
  881. imageView.topAnchor.constraint(equalTo: content.topAnchor, constant: 10.0),
  882. imageView.bottomAnchor.constraint(equalTo: content.bottomAnchor, constant: -10.0),
  883. imageView.widthAnchor.constraint(equalToConstant: 55.0),
  884. imageView.heightAnchor.constraint(equalToConstant: 55.0)
  885. ])
  886. if data.profile.isEmpty && data.pin != "-999" {
  887. if data.messageScope == "3" {
  888. imageView.image = UIImage(named: "Profile---Purple", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
  889. } else {
  890. imageView.image = UIImage(named: "Conversation---Purple", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
  891. }
  892. } else {
  893. if !Utils.getIconDock().isEmpty {
  894. let dataImage = try? Data(contentsOf: URL(string: Utils.getUrlDock()!)!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
  895. if dataImage != nil {
  896. getImage(name: data.profile, placeholderImage: UIImage(data: dataImage!), isCircle: true, tableView: tableView, indexPath: indexPath, completion: { result, isDownloaded, image in
  897. imageView.image = image
  898. })
  899. }
  900. } else {
  901. getImage(name: data.profile, placeholderImage: UIImage(named: data.pin == "-999" ? "pb_button" : data.messageScope == "3" ? "Profile---Purple" : "Conversation---Purple", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), isCircle: true, tableView: tableView, indexPath: indexPath, completion: { result, isDownloaded, image in
  902. imageView.image = image
  903. })
  904. }
  905. }
  906. let titleView = UILabel()
  907. content.addSubview(titleView)
  908. titleView.translatesAutoresizingMaskIntoConstraints = false
  909. NSLayoutConstraint.activate([
  910. titleView.leadingAnchor.constraint(equalTo: imageView.trailingAnchor, constant: 10.0),
  911. titleView.topAnchor.constraint(equalTo: content.topAnchor, constant: 10.0),
  912. titleView.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -40.0),
  913. ])
  914. titleView.text = data.name
  915. titleView.font = UIFont.systemFont(ofSize: 14, weight: .medium)
  916. let messageView = UILabel()
  917. content.addSubview(messageView)
  918. messageView.translatesAutoresizingMaskIntoConstraints = false
  919. NSLayoutConstraint.activate([
  920. messageView.leadingAnchor.constraint(equalTo: imageView.trailingAnchor, constant: 10.0),
  921. messageView.topAnchor.constraint(equalTo: titleView.bottomAnchor, constant: 5.0),
  922. messageView.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -40.0),
  923. ])
  924. messageView.textColor = .gray
  925. if data.messageText.contains("■") {
  926. data.messageText = data.messageText.components(separatedBy: "■")[0]
  927. data.messageText = data.messageText.trimmingCharacters(in: .whitespacesAndNewlines)
  928. }
  929. let text = Utils.previewMessageText(chat: data)
  930. let idMe = UserDefaults.standard.string(forKey: "me") as String?
  931. if let attributeText = text as? NSMutableAttributedString {
  932. let stringMessage = NSMutableAttributedString(string: "")
  933. if data.fpin == idMe {
  934. if data.lock == "1" {
  935. if data.messageScope == "4" {
  936. stringMessage.append(NSAttributedString(string: "You".localized() + ": ", attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 12, weight: .medium)]))
  937. }
  938. stringMessage.append(("🚫 _"+"You were deleted this message".localized()+"_").richText())
  939. } else {
  940. let imageStatus = NSTextAttachment()
  941. let status = getRealStatus(messageId: data.messageId)
  942. if (status == "1" || status == "2" ) {
  943. imageStatus.image = UIImage(named: "checklist", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(UIColor.lightGray)
  944. } else if (status == "3") {
  945. imageStatus.image = UIImage(named: "double-checklist", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(UIColor.lightGray)
  946. } else if (status == "8") {
  947. imageStatus.image = UIImage(named: "message_status_ack", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withRenderingMode(.alwaysOriginal)
  948. } else {
  949. imageStatus.image = UIImage(named: "double-checklist", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(UIColor.systemBlue)
  950. }
  951. imageStatus.bounds = CGRect(x: 0, y: -2, width: 15, height: 15)
  952. let imageStatusString = NSAttributedString(attachment: imageStatus)
  953. stringMessage.append(imageStatusString)
  954. stringMessage.append(NSAttributedString(string: " "))
  955. if data.messageScope == "4" {
  956. stringMessage.append(NSAttributedString(string: "You".localized() + ": ", attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 12, weight: .medium)]))
  957. }
  958. stringMessage.append(attributeText)
  959. }
  960. } else {
  961. if data.messageScope == "4" {
  962. stringMessage.append(NSAttributedString(string: User.getData(pin: data.fpin)!.firstName + ": ", attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 12, weight: .medium)]))
  963. }
  964. if data.lock == "1" {
  965. stringMessage.append(("🚫 _"+"This message was deleted".localized()+"_").richText())
  966. } else {
  967. stringMessage.append(attributeText)
  968. }
  969. }
  970. messageView.attributedText = stringMessage
  971. }
  972. messageView.numberOfLines = 2
  973. let timeView = UILabel()
  974. content.addSubview(timeView)
  975. timeView.translatesAutoresizingMaskIntoConstraints = false
  976. NSLayoutConstraint.activate([
  977. timeView.topAnchor.constraint(equalTo: content.topAnchor, constant: 10.0),
  978. timeView.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -20.0),
  979. ])
  980. timeView.textColor = .gray
  981. timeView.font = UIFont.systemFont(ofSize: 14)
  982. let date = Date(milliseconds: Int64(data.serverDate)!)
  983. let calendar = Calendar.current
  984. if (calendar.isDateInToday(date)) {
  985. let formatter = DateFormatter()
  986. formatter.dateFormat = "HH:mm"
  987. formatter.locale = NSLocale(localeIdentifier: "id") as Locale?
  988. timeView.text = formatter.string(from: date as Date)
  989. } else {
  990. let startOfNow = calendar.startOfDay(for: Date())
  991. let startOfTimeStamp = calendar.startOfDay(for: date)
  992. let components = calendar.dateComponents([.day], from: startOfNow, to: startOfTimeStamp)
  993. let day = -(components.day!)
  994. if day == 1 {
  995. timeView.text = "Yesterday".localized()
  996. } else {
  997. if day < 7 {
  998. let formatter = DateFormatter()
  999. formatter.dateFormat = "EEEE"
  1000. let lang = UserDefaults.standard.string(forKey: "i18n_language")
  1001. if lang == "id" {
  1002. formatter.locale = NSLocale(localeIdentifier: "id") as Locale?
  1003. }
  1004. timeView.text = formatter.string(from: date)
  1005. } else {
  1006. let formatter = DateFormatter()
  1007. formatter.dateFormat = "M/dd/yy"
  1008. formatter.locale = NSLocale(localeIdentifier: "id") as Locale?
  1009. let stringFormat = formatter.string(from: date as Date)
  1010. timeView.text = stringFormat
  1011. }
  1012. }
  1013. }
  1014. if data.counter != "0" {
  1015. timeView.textColor = .systemRed
  1016. let viewCounter = UIView()
  1017. content.addSubview(viewCounter)
  1018. viewCounter.translatesAutoresizingMaskIntoConstraints = false
  1019. NSLayoutConstraint.activate([
  1020. viewCounter.topAnchor.constraint(equalTo: timeView.bottomAnchor, constant: 5.0),
  1021. viewCounter.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -20),
  1022. viewCounter.widthAnchor.constraint(greaterThanOrEqualToConstant: 20),
  1023. viewCounter.heightAnchor.constraint(equalToConstant: 20)
  1024. ])
  1025. viewCounter.backgroundColor = .systemRed
  1026. viewCounter.layer.cornerRadius = 10
  1027. viewCounter.clipsToBounds = true
  1028. viewCounter.layer.borderWidth = 0.5
  1029. viewCounter.layer.borderColor = UIColor.secondaryColor.cgColor
  1030. let labelCounter = UILabel()
  1031. viewCounter.addSubview(labelCounter)
  1032. labelCounter.translatesAutoresizingMaskIntoConstraints = false
  1033. NSLayoutConstraint.activate([
  1034. labelCounter.centerYAnchor.constraint(equalTo: viewCounter.centerYAnchor),
  1035. labelCounter.leadingAnchor.constraint(equalTo: viewCounter.leadingAnchor, constant: 2),
  1036. labelCounter.trailingAnchor.constraint(equalTo: viewCounter.trailingAnchor, constant: -2),
  1037. ])
  1038. labelCounter.font = UIFont.systemFont(ofSize: 11)
  1039. if Int(data.counter)! > 99 {
  1040. labelCounter.text = "99+"
  1041. } else {
  1042. labelCounter.text = data.counter
  1043. }
  1044. labelCounter.textColor = .secondaryColor
  1045. labelCounter.textAlignment = .center
  1046. }
  1047. }
  1048. case 1:
  1049. if segment.numberOfSegments < 3 {
  1050. cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifierGroup", for: indexPath)
  1051. var content = cell.defaultContentConfiguration()
  1052. content.textProperties.font = UIFont.systemFont(ofSize: 14)
  1053. let group: Group
  1054. if isFilltering {
  1055. if indexPath.row == 0 {
  1056. group = fillteredData[indexPath.section] as! Group
  1057. } else {
  1058. if (fillteredData[indexPath.section] as! Group).childs.count > 0 {
  1059. group = (fillteredData[indexPath.section] as! Group).childs[indexPath.row - 1]
  1060. } else {
  1061. return cell
  1062. }
  1063. }
  1064. } else {
  1065. if indexPath.row == 0 {
  1066. if indexPath.section > (groups.count - 1) {
  1067. return cell
  1068. }
  1069. group = groups[indexPath.section]
  1070. } else {
  1071. group = groups[indexPath.section].childs[indexPath.row - 1]
  1072. }
  1073. }
  1074. if group.official == "1" && group.parent == "" {
  1075. 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)
  1076. }
  1077. else if group.isOpen == "1" && group.parent == "" {
  1078. if self.traitCollection.userInterfaceStyle == .dark {
  1079. content.attributedText = self.set(image: UIImage(systemName: "globe")!.withTintColor(.white), with: " \(group.name)", size: 15, y: -4)
  1080. } else {
  1081. content.attributedText = self.set(image: UIImage(systemName: "globe")!, with: " \(group.name)", size: 15, y: -4)
  1082. }
  1083. } else if group.parent == "" {
  1084. if self.traitCollection.userInterfaceStyle == .dark {
  1085. content.attributedText = self.set(image: UIImage(systemName: "lock.fill")!.withTintColor(.white), with: " \(group.name)", size: 15, y: -4)
  1086. } else {
  1087. content.attributedText = self.set(image: UIImage(systemName: "lock.fill")!, with: " \(group.name)", size: 15, y: -4)
  1088. }
  1089. } else {
  1090. content.text = group.name
  1091. }
  1092. if group.childs.count > 0 {
  1093. let iconName = (group.isSelected) ? "chevron.up.circle" : "chevron.down.circle"
  1094. let imageView = UIImageView(image: UIImage(systemName: iconName))
  1095. imageView.tintColor = .black
  1096. cell.accessoryView = imageView
  1097. }
  1098. else {
  1099. cell.accessoryView = nil
  1100. cell.accessoryType = .none
  1101. }
  1102. content.imageProperties.maximumSize = CGSize(width: 40, height: 40)
  1103. 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
  1104. content.image = image
  1105. }
  1106. cell.contentConfiguration = content
  1107. if !group.level.isEmpty {
  1108. if group.level != "-1" && Int(group.level)! < 7 {
  1109. cell.contentView.layoutMargins = .init(top: 0.0, left: CGFloat(25 * Int(group.level)!), bottom: 0.0, right: 0)
  1110. } else if Int(group.level)! > 6 {
  1111. cell.contentView.layoutMargins = .init(top: 0.0, left: CGFloat(25 * (Int(group.level)! - 6)), bottom: 0.0, right: 0)
  1112. }
  1113. }
  1114. } else {
  1115. cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifierContact", for: indexPath)
  1116. var content = cell.defaultContentConfiguration()
  1117. let data: User
  1118. if isFilltering {
  1119. data = fillteredData[indexPath.row] as! User
  1120. } else {
  1121. if indexPath.row > contacts.count - 1 {
  1122. return cell
  1123. }
  1124. data = contacts[indexPath.row]
  1125. }
  1126. content.imageProperties.maximumSize = CGSize(width: 40, height: 40)
  1127. 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
  1128. content.image = image
  1129. })
  1130. if User.isOfficial(official_account: data.official ?? "") || User.isOfficialRegular(official_account: data.official ?? "") {
  1131. 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, colorText: UIColor.officialColor)
  1132. } else if User.isVerified(official_account: data.official ?? "") {
  1133. content.attributedText = self.set(image: UIImage(named: "ic_verified", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, with: " \(data.fullName)", size: 15, y: -4, colorText: UIColor.verifiedColor)
  1134. }
  1135. else if User.isInternal(userType: data.userType ?? "") {
  1136. content.attributedText = self.set(image: UIImage(named: "ic_internal", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, with: " \(data.fullName)", size: 15, y: -4, colorText: UIColor.internalColor)
  1137. } else if User.isCallCenter(userType: data.userType ?? "") {
  1138. // let dataCategory = CategoryCC.getDataFromServiceId(service_id: data.ex_offmp!)
  1139. // if dataCategory != nil {
  1140. // content.attributedText = self.set(image: UIImage(named: "pb_call_center", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, with: " \(data.fullName) (\(dataCategory!.service_name))", size: 15, y: -4, colorText: UIColor.ccColor)
  1141. // } else {
  1142. 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, colorText: UIColor.ccColor)
  1143. // }
  1144. } else {
  1145. content.text = data.fullName
  1146. }
  1147. content.textProperties.font = UIFont.systemFont(ofSize: 14)
  1148. cell.contentConfiguration = content
  1149. }
  1150. case 2:
  1151. cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifierGroup", for: indexPath)
  1152. var content = cell.defaultContentConfiguration()
  1153. content.textProperties.font = UIFont.systemFont(ofSize: 14)
  1154. let group: Group
  1155. if isFilltering {
  1156. if indexPath.row == 0 {
  1157. group = fillteredData[indexPath.section] as! Group
  1158. } else {
  1159. if (fillteredData[indexPath.section] as! Group).childs.count > 0 {
  1160. group = (fillteredData[indexPath.section] as! Group).childs[indexPath.row - 1]
  1161. } else {
  1162. return cell
  1163. }
  1164. }
  1165. } else {
  1166. if indexPath.row == 0 {
  1167. if indexPath.section > (groups.count - 1) {
  1168. return cell
  1169. }
  1170. group = groups[indexPath.section]
  1171. } else {
  1172. group = groups[indexPath.section].childs[indexPath.row - 1]
  1173. }
  1174. }
  1175. if group.official == "1" && group.parent == "" {
  1176. 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)
  1177. }
  1178. else if group.isOpen == "1" && group.parent == "" {
  1179. if self.traitCollection.userInterfaceStyle == .dark {
  1180. content.attributedText = self.set(image: UIImage(systemName: "globe")!.withTintColor(.white), with: " \(group.name)", size: 15, y: -4)
  1181. } else {
  1182. content.attributedText = self.set(image: UIImage(systemName: "globe")!, with: " \(group.name)", size: 15, y: -4)
  1183. }
  1184. } else if group.parent == "" {
  1185. if self.traitCollection.userInterfaceStyle == .dark {
  1186. content.attributedText = self.set(image: UIImage(systemName: "lock.fill")!.withTintColor(.white), with: " \(group.name)", size: 15, y: -4)
  1187. } else {
  1188. content.attributedText = self.set(image: UIImage(systemName: "lock.fill")!, with: " \(group.name)", size: 15, y: -4)
  1189. }
  1190. } else {
  1191. content.text = group.name
  1192. }
  1193. if group.childs.count > 0 {
  1194. let iconName = (group.isSelected) ? "chevron.up.circle" : "chevron.down.circle"
  1195. let imageView = UIImageView(image: UIImage(systemName: iconName))
  1196. imageView.tintColor = .black
  1197. cell.accessoryView = imageView
  1198. }
  1199. else {
  1200. cell.accessoryView = nil
  1201. cell.accessoryType = .none
  1202. }
  1203. content.imageProperties.maximumSize = CGSize(width: 40, height: 40)
  1204. 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
  1205. content.image = image
  1206. }
  1207. cell.contentConfiguration = content
  1208. if !group.level.isEmpty {
  1209. if group.level != "-1" && Int(group.level)! < 7 {
  1210. cell.contentView.layoutMargins = .init(top: 0.0, left: CGFloat(25 * Int(group.level)!), bottom: 0.0, right: 0)
  1211. } else if Int(group.level)! > 6 {
  1212. cell.contentView.layoutMargins = .init(top: 0.0, left: CGFloat(25 * (Int(group.level)! - 6)), bottom: 0.0, right: 0)
  1213. }
  1214. }
  1215. default:
  1216. cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifierContact", for: indexPath)
  1217. var content = cell.defaultContentConfiguration()
  1218. content.text = ""
  1219. cell.contentConfiguration = content
  1220. }
  1221. cell.backgroundColor = .clear
  1222. cell.separatorInset = UIEdgeInsets(top: 0, left: 60.0, bottom: 0, right: 0)
  1223. return cell
  1224. }
  1225. override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
  1226. return 75.0
  1227. }
  1228. private func getRealStatus(messageId: String) -> String {
  1229. var status = "1"
  1230. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  1231. if let cursorStatus = Database.shared.getRecords(fmdb: fmdb, query: "SELECT status, f_pin FROM MESSAGE_STATUS WHERE message_id='\(messageId)'") {
  1232. var listStatus: [Int] = []
  1233. while cursorStatus.next() {
  1234. listStatus.append(Int(cursorStatus.string(forColumnIndex: 0)!)!)
  1235. }
  1236. cursorStatus.close()
  1237. status = "\(listStatus.min() ?? 2)"
  1238. }
  1239. })
  1240. return status
  1241. }
  1242. }
  1243. extension ContactChatViewController: UISearchControllerDelegate, UISearchBarDelegate, UISearchResultsUpdating {
  1244. func updateSearchResults(for searchController: UISearchController) {
  1245. filterContentForSearchText(searchController.searchBar.text!.trimmingCharacters(in: .whitespacesAndNewlines))
  1246. }
  1247. func set(image: UIImage, with text: String, size: CGFloat, y: CGFloat, colorText: UIColor = UIColor.black) -> NSAttributedString {
  1248. let attachment = NSTextAttachment()
  1249. attachment.image = image
  1250. attachment.bounds = CGRect(x: 0, y: y, width: size, height: size)
  1251. let attachmentStr = NSAttributedString(attachment: attachment)
  1252. let mutableAttributedString = NSMutableAttributedString()
  1253. mutableAttributedString.append(attachmentStr)
  1254. let attributedStringColor = [NSAttributedString.Key.foregroundColor : colorText]
  1255. let textString = NSAttributedString(string: text, attributes: attributedStringColor)
  1256. mutableAttributedString.append(textString)
  1257. return mutableAttributedString
  1258. }
  1259. func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
  1260. searchBar.showsCancelButton = true
  1261. let cBtn = searchBar.value(forKey: "cancelButton") as! UIButton
  1262. cBtn.setTitle("Cancel".localized(), for: .normal)
  1263. }
  1264. func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
  1265. searchBar.showsCancelButton = false
  1266. }
  1267. }