SecondTabViewController.swift 55 KB

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