SecondTabViewController.swift 60 KB

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