SecondTabViewController.swift 54 KB

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