SecondTabViewController.swift 56 KB

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