ContactChatViewController.swift 78 KB

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