ContactChatViewController.swift 89 KB

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