SecondTabViewController.swift 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967
  1. //
  2. // SecondTabViewController.swift
  3. // AppBuilder
  4. //
  5. // Created by Kevin Maulana on 30/03/22.
  6. //
  7. import UIKit
  8. import FMDB
  9. import NexilisLite
  10. import Speech
  11. class SecondTabViewController: UIViewController, UIScrollViewDelegate, UIGestureRecognizerDelegate {
  12. var isChooser: ((String, String) -> ())?
  13. var isAdmin: Bool = false
  14. var chats: [Chat] = []
  15. var contacts: [User] = []
  16. var groups: [Group] = []
  17. var cancelSearchButton = UIBarButtonItem()
  18. var menuItem = UIBarButtonItem()
  19. var voiceItem = UIBarButtonItem()
  20. var childrenMenu = [UIAction]()
  21. var groupMap: [String:Int] = [:]
  22. lazy var searchController: UISearchController = {
  23. var searchController = UISearchController(searchResultsController: nil)
  24. searchController.delegate = self
  25. searchController.searchResultsUpdater = self
  26. searchController.searchBar.autocapitalizationType = .none
  27. searchController.searchBar.delegate = self
  28. searchController.searchBar.barTintColor = .secondaryColor
  29. searchController.searchBar.searchTextField.backgroundColor = .secondaryColor
  30. searchController.obscuresBackgroundDuringPresentation = false
  31. return searchController
  32. }()
  33. lazy var segment: UISegmentedControl = {
  34. var segment = UISegmentedControl(items: ["Chats".localized(), "Groups".localized()])
  35. segment.sizeToFit()
  36. segment.selectedSegmentIndex = 0
  37. segment.addTarget(self, action: #selector(segmentChanged(sender:)), for: .valueChanged)
  38. return segment
  39. }()
  40. var fillteredData: [Any] = []
  41. var isSearchBarEmpty: Bool {
  42. return searchController.searchBar.text?.isEmpty ?? true
  43. }
  44. var isFilltering: Bool {
  45. return searchController.isActive && !isSearchBarEmpty
  46. }
  47. @IBOutlet var tableView: UITableView!
  48. let speechRecognizer = SFSpeechRecognizer()
  49. var recognitionRequest : SFSpeechAudioBufferRecognitionRequest?
  50. var recognitionTask : SFSpeechRecognitionTask?
  51. let audioEngine = AVAudioEngine()
  52. func filterContentForSearchText(_ searchText: String) {
  53. switch segment.selectedSegmentIndex {
  54. case 1:
  55. fillteredData = self.groups.filter { $0.name.lowercased().contains(searchText.lowercased()) }
  56. default:
  57. fillteredData = self.chats.filter { $0.name.lowercased().contains(searchText.lowercased()) || $0.messageText.lowercased().contains(searchText.lowercased()) }
  58. }
  59. tableView.reloadData()
  60. }
  61. override func viewDidLoad() {
  62. super.viewDidLoad()
  63. SFSpeechRecognizer.requestAuthorization({ authStatus in
  64. var isButtonEnabled = false
  65. OperationQueue.main.addOperation {
  66. switch authStatus {
  67. case .authorized:
  68. isButtonEnabled = true
  69. print("User allowed access to speech recognition")
  70. case .denied:
  71. isButtonEnabled = false
  72. print("User denied access to speech recognition")
  73. case .restricted:
  74. isButtonEnabled = false
  75. print("Speech recognition restricted on this device")
  76. case .notDetermined:
  77. isButtonEnabled = false
  78. print("Speech recognition not yet authorized")
  79. @unknown default:
  80. print("Speech recognition not yet authorized")
  81. }
  82. }
  83. })
  84. let me = UserDefaults.standard.string(forKey: "me")!
  85. Database.shared.database?.inTransaction({ fmdb, rollback in
  86. 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() {
  87. isAdmin = cursor.string(forColumnIndex: 3) == "23" || cursor.string(forColumnIndex: 3) == "24"
  88. cursor.close()
  89. }
  90. })
  91. cancelSearchButton = UIBarButtonItem(title: "Cancel".localized(), style: .plain, target: self, action: #selector(cancel(sender:)))
  92. var childrenMenu : [UIAction] = []
  93. if(isAdmin){
  94. childrenMenu.append(UIAction(title: "Broadcast Message", image: UIImage(systemName: "envelope.open"), handler: {[weak self](_) in
  95. let controller = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "broadcastNav")
  96. self?.navigationController?.present(controller, animated: true, completion: nil)
  97. }))
  98. }
  99. menuItem = UIBarButtonItem(image: UIImage(systemName: "square.and.pencil"), style: .plain, target: self, action: #selector(startConversation))
  100. voiceItem = UIBarButtonItem(image: UIImage(systemName: "mic.fill"), style: .plain, target: self, action: #selector(recordAudio))
  101. tabBarController?.navigationItem.leftBarButtonItem = voiceItem
  102. tabBarController?.navigationItem.rightBarButtonItem = menuItem
  103. tabBarController?.navigationItem.searchController = searchController
  104. definesPresentationContext = true
  105. NotificationCenter.default.addObserver(self, selector: #selector(onReceiveMessage(notification:)), name: NSNotification.Name(rawValue: "onReceiveChat"), object: nil)
  106. NotificationCenter.default.addObserver(self, selector: #selector(onReload(notification:)), name: NSNotification.Name(rawValue: "onMember"), object: nil)
  107. tableView.tableHeaderView = segment
  108. pullBuddy()
  109. let cpaasMode = PrefsUtil.getCpaasMode()
  110. let isBurger = cpaasMode == PrefsUtil.CPAAS_MODE_BURGER
  111. navigationController?.setNavigationBarHidden(!isBurger, animated: false)
  112. let tapGesture = UITapGestureRecognizer(target: self, action: #selector(collapseDocked))
  113. tapGesture.cancelsTouchesInView = false
  114. tapGesture.delegate = self
  115. self.view.addGestureRecognizer(tapGesture)
  116. }
  117. @objc func collapseDocked() {
  118. if ViewController.isExpandButton {
  119. ViewController.expandButton()
  120. }
  121. }
  122. @objc func startConversation(){
  123. let navigationController = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "contactChatNav") as! UINavigationController
  124. navigationController.modalPresentationStyle = .fullScreen
  125. navigationController.navigationBar.tintColor = .white
  126. navigationController.navigationBar.barTintColor = .mainColor
  127. navigationController.navigationBar.isTranslucent = false
  128. let textAttributes = [NSAttributedString.Key.foregroundColor:UIColor.white]
  129. navigationController.navigationBar.titleTextAttributes = textAttributes
  130. navigationController.view.backgroundColor = .mainColor
  131. self.navigationController?.present(navigationController, animated: true, completion: nil)
  132. }
  133. @objc func recordAudio(){
  134. if audioEngine.isRunning {
  135. self.audioEngine.stop()
  136. self.recognitionRequest?.endAudio()
  137. voiceItem.image = UIImage(systemName: "mic.fill")
  138. searchController.searchBar.isUserInteractionEnabled = true
  139. searchController.automaticallyShowsCancelButton = true
  140. searchController.hidesNavigationBarDuringPresentation = true
  141. } else {
  142. print("start recording")
  143. self.startRecording()
  144. voiceItem.image = UIImage(systemName: "mic")
  145. searchController.searchBar.isUserInteractionEnabled = false
  146. }
  147. }
  148. func startRecording() {
  149. // Clear all previous session data and cancel task
  150. if recognitionTask != nil {
  151. recognitionTask?.cancel()
  152. recognitionTask = nil
  153. }
  154. // Create instance of audio session to record voice
  155. let audioSession = AVAudioSession.sharedInstance()
  156. do {
  157. try audioSession.setCategory(AVAudioSession.Category.record, mode: AVAudioSession.Mode.measurement, options: AVAudioSession.CategoryOptions.defaultToSpeaker)
  158. try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
  159. } catch {
  160. print("audioSession properties weren't set because of an error.")
  161. }
  162. self.recognitionRequest = SFSpeechAudioBufferRecognitionRequest()
  163. let inputNode = audioEngine.inputNode
  164. guard let recognitionRequest = recognitionRequest else {
  165. fatalError("Unable to create an SFSpeechAudioBufferRecognitionRequest object")
  166. }
  167. recognitionRequest.shouldReportPartialResults = true
  168. self.recognitionTask = speechRecognizer?.recognitionTask(with: recognitionRequest, resultHandler: { (result, error) in
  169. var isFinal = false
  170. if result != nil {
  171. self.searchController.searchBar.searchTextField.text = result?.bestTranscription.formattedString
  172. self.updateSearchResults(for: self.searchController)
  173. isFinal = (result?.isFinal)!
  174. }
  175. if error != nil || isFinal {
  176. self.audioEngine.stop()
  177. inputNode.removeTap(onBus: 0)
  178. self.recognitionRequest = nil
  179. self.recognitionTask = nil
  180. // self.btnStart.isEnabled = true
  181. }
  182. })
  183. let recordingFormat = inputNode.outputFormat(forBus: 0)
  184. inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { (buffer, when) in
  185. self.recognitionRequest?.append(buffer)
  186. }
  187. self.audioEngine.prepare()
  188. do {
  189. try self.audioEngine.start()
  190. } catch {
  191. print("audioEngine couldn't start because of an error.")
  192. }
  193. //
  194. // self.lblText.text = "Say something, I'm listening!"
  195. }
  196. override func viewWillAppear(_ animated: Bool) {
  197. // tabBarController?.navigationItem.leftBarButtonItem = cancelSearchButton
  198. self.navigationController?.navigationBar.topItem?.title = Bundle.main.displayName
  199. self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.black]
  200. let cpaasMode = PrefsUtil.getCpaasMode()
  201. let isBurger = cpaasMode == PrefsUtil.CPAAS_MODE_BURGER
  202. navigationController?.navigationBar.backgroundColor = .clear
  203. navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
  204. navigationController?.navigationBar.shadowImage = UIImage()
  205. navigationController?.navigationBar.isTranslucent = true
  206. navigationController?.setNavigationBarHidden(false, animated: false)
  207. navigationController?.navigationBar.tintColor = .black
  208. tabBarController?.navigationItem.leftBarButtonItem = voiceItem
  209. tabBarController?.navigationItem.searchController = searchController
  210. tabBarController?.navigationItem.rightBarButtonItem = menuItem
  211. let randomInt = Int.random(in: 1..<10)
  212. backgroundImage.image = UIImage(named: "pb_lbackground_\(randomInt)")
  213. getData()
  214. searchController.searchBar.placeholder = "Search chats & messages".localized()
  215. searchController.searchBar.setValue("Cancel".localized(), forKey: "cancelButtonText")
  216. if segment.numberOfSegments == 2 {
  217. segment.setTitle("Chats".localized(), forSegmentAt: 0)
  218. segment.setTitle("Groups".localized(), forSegmentAt: 1)
  219. }
  220. }
  221. override func viewWillDisappear(_ animated: Bool) {
  222. tabBarController?.navigationItem.leftBarButtonItem = nil
  223. tabBarController?.navigationItem.searchController = nil
  224. tabBarController?.navigationItem.rightBarButtonItem = nil
  225. if ViewController.isExpandButton {
  226. ViewController.expandButton()
  227. }
  228. }
  229. @objc func onReload(notification: NSNotification) {
  230. let data:[AnyHashable : Any] = notification.userInfo!
  231. if data["member"] as! String == UserDefaults.standard.string(forKey: "me")! {
  232. DispatchQueue.main.async {
  233. self.getData()
  234. }
  235. }
  236. }
  237. @objc func onReceiveMessage(notification: NSNotification) {
  238. let data:[AnyHashable : Any] = notification.userInfo!
  239. guard let dataMessage = data["message"] as? TMessage else {
  240. return
  241. }
  242. let isUser = User.getData(pin: dataMessage.getBody(key: CoreMessage_TMessageKey.L_PIN)) != nil
  243. let chatId = dataMessage.getBody(key: CoreMessage_TMessageKey.CHAT_ID, default_value: "").isEmpty ? dataMessage.getBody(key: CoreMessage_TMessageKey.L_PIN) : dataMessage.getBody(key: CoreMessage_TMessageKey.CHAT_ID, default_value: "")
  244. let pin = isUser ? dataMessage.getBody(key: CoreMessage_TMessageKey.F_PIN) : chatId
  245. let messageId = dataMessage.getBody(key: CoreMessage_TMessageKey.MESSAGE_ID)
  246. if let index = chats.firstIndex(of: Chat(pin: pin)) {
  247. guard let chat = Chat.getData(messageId: messageId).first else {
  248. return
  249. }
  250. DispatchQueue.main.async {
  251. if self.segment.selectedSegmentIndex == 0 {
  252. self.tableView.beginUpdates()
  253. self.chats.remove(at: index)
  254. self.tableView.deleteRows(at: [IndexPath(row: index, section: 0)], with: .none)
  255. }
  256. self.chats.insert(chat, at: 0)
  257. if self.segment.selectedSegmentIndex == 0 {
  258. self.tableView.insertRows(at: [IndexPath(row: 0, section: 0)], with: .none)
  259. self.tableView.endUpdates()
  260. }
  261. }
  262. } else {
  263. guard let chat = Chat.getData(messageId: messageId).first else {
  264. return
  265. }
  266. DispatchQueue.main.async {
  267. if self.segment.selectedSegmentIndex == 0 {
  268. self.tableView.beginUpdates()
  269. }
  270. self.chats.insert(chat, at: 0)
  271. if self.segment.selectedSegmentIndex == 0 {
  272. self.tableView.insertRows(at: [IndexPath(row: 0, section: 0)], with: .none)
  273. self.tableView.endUpdates()
  274. }
  275. }
  276. }
  277. }
  278. @objc func add(sender: Any) {
  279. }
  280. @objc func cancel(sender: Any) {
  281. navigationController?.dismiss(animated: true, completion: nil)
  282. }
  283. @objc func segmentChanged(sender: Any) {
  284. filterContentForSearchText(searchController.searchBar.text!)
  285. }
  286. // MARK: - Data source
  287. func getData() {
  288. getChats {
  289. self.getContacts {
  290. self.getGroups { g1 in
  291. DispatchQueue.global().async {
  292. self.getOpenGroups(listGroups: g1, completion: { g in
  293. DispatchQueue.main.async {
  294. self.groups.removeAll()
  295. self.groups.append(contentsOf: g1)
  296. for og in g {
  297. if self.groups.first(where: { $0.id == og.id }) == nil {
  298. self.groups.append(og)
  299. }
  300. }
  301. DispatchQueue.main.async {
  302. self.tableView.reloadData()
  303. }
  304. }
  305. })
  306. }
  307. }
  308. }
  309. }
  310. }
  311. func getChats(completion: @escaping ()->()) {
  312. DispatchQueue.global().async {
  313. self.chats.removeAll()
  314. self.chats.append(contentsOf: Chat.getData())
  315. completion()
  316. }
  317. }
  318. private func getContacts(completion: @escaping ()->()) {
  319. DispatchQueue.global().async {
  320. self.contacts.removeAll()
  321. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  322. do {
  323. 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") {
  324. while cursorData.next() {
  325. let user = User(pin: cursorData.string(forColumnIndex: 0) ?? "",
  326. firstName: cursorData.string(forColumnIndex: 1) ?? "",
  327. lastName: cursorData.string(forColumnIndex: 2) ?? "",
  328. thumb: cursorData.string(forColumnIndex: 3) ?? "",
  329. userType: cursorData.string(forColumnIndex: 5) ?? "")
  330. if (user.firstName + " " + user.lastName).trimmingCharacters(in: .whitespaces) == "USR\(user.pin)" {
  331. continue
  332. }
  333. user.official = cursorData.string(forColumnIndex: 4) ?? ""
  334. self.contacts.append(user)
  335. }
  336. cursorData.close()
  337. }
  338. } catch {
  339. rollback.pointee = true
  340. print(error)
  341. }
  342. completion()
  343. })
  344. }
  345. }
  346. private func getGroupRecursive(fmdb: FMDatabase, id: String = "", parent: String = "") -> [Group] {
  347. var data: [Group] = []
  348. 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 from GROUPZ g where "
  349. if id.isEmpty {
  350. query += "g.parent = '\(parent)'"
  351. } else {
  352. query += "g.group_id = '\(id)'"
  353. }
  354. if let cursor = Database.shared.getRecords(fmdb: fmdb, query: query) {
  355. while cursor.next() {
  356. let group = Group(
  357. id: cursor.string(forColumnIndex: 0) ?? "",
  358. name: cursor.string(forColumnIndex: 1) ?? "",
  359. profile: cursor.string(forColumnIndex: 2) ?? "",
  360. quote: cursor.string(forColumnIndex: 3) ?? "",
  361. by: cursor.string(forColumnIndex: 4) ?? "",
  362. date: cursor.string(forColumnIndex: 5) ?? "",
  363. parent: cursor.string(forColumnIndex: 6) ?? "",
  364. chatId: "",
  365. groupType: cursor.string(forColumnIndex: 7) ?? "",
  366. isOpen: cursor.string(forColumnIndex: 8) ?? "",
  367. official: cursor.string(forColumnIndex: 9) ?? "",
  368. isEducation: cursor.string(forColumnIndex: 10) ?? "")
  369. if group.chatId.isEmpty {
  370. 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)
  371. group.childs.append(lounge)
  372. }
  373. if let topicCursor = Database.shared.getRecords(fmdb: fmdb, query: "select chat_id, title, thumb from DISCUSSION_FORUM where group_id = '\(group.id)'") {
  374. while topicCursor.next() {
  375. let topic = Group(id: group.id,
  376. name: topicCursor.string(forColumnIndex: 1) ?? "",
  377. profile: topicCursor.string(forColumnIndex: 2) ?? "",
  378. quote: group.quote,
  379. by: group.by,
  380. date: group.date,
  381. parent: group.id,
  382. chatId: topicCursor.string(forColumnIndex: 0) ?? "",
  383. groupType: group.groupType,
  384. isOpen: group.isOpen,
  385. official: group.official,
  386. isEducation: group.isEducation)
  387. group.childs.append(topic)
  388. }
  389. topicCursor.close()
  390. }
  391. if !group.id.isEmpty {
  392. if group.official == "1" {
  393. let idMe = UserDefaults.standard.string(forKey: "me") as String?
  394. if let cursorUser = Database.shared.getRecords(fmdb: fmdb, query: "SELECT user_type FROM BUDDY where f_pin='\(idMe!)'"), cursorUser.next() {
  395. group.childs.append(contentsOf: getGroupRecursive(fmdb: fmdb, parent: group.id))
  396. cursorUser.close()
  397. }
  398. } else if group.official != "1"{
  399. group.childs.append(contentsOf: getGroupRecursive(fmdb: fmdb, parent: group.id))
  400. }
  401. }
  402. data.append(group)
  403. }
  404. cursor.close()
  405. }
  406. return data
  407. }
  408. private func getOpenGroups(listGroups: [Group], completion: @escaping ([Group]) -> ()) {
  409. if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getOpenGroups(p_account: "1,2,3,5,6,7", offset: "0", search: "")) {
  410. var dataGroups: [Group] = []
  411. if (response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99") == "00") {
  412. let data = response.getBody(key: CoreMessage_TMessageKey.DATA)
  413. if let json = try! JSONSerialization.jsonObject(with: data.data(using: String.Encoding.utf8)!, options: []) as? [[String: Any?]] {
  414. for dataJson in json {
  415. let group = Group(
  416. id: dataJson[CoreMessage_TMessageKey.GROUP_ID] as? String ?? "",
  417. name: dataJson[CoreMessage_TMessageKey.GROUP_NAME] as? String ?? "",
  418. profile: dataJson[CoreMessage_TMessageKey.THUMB_ID] as? String ?? "",
  419. quote: dataJson[CoreMessage_TMessageKey.QUOTE] as? String ?? "",
  420. by: dataJson[CoreMessage_TMessageKey.BLOCK] as? String ?? "",
  421. date: "",
  422. parent: "",
  423. chatId: "",
  424. groupType: "NOTJOINED",
  425. isOpen: dataJson[CoreMessage_TMessageKey.IS_OPEN] as? String ?? "",
  426. official: "0",
  427. isEducation: "")
  428. dataGroups.append(group)
  429. }
  430. }
  431. } else {
  432. DispatchQueue.main.async {
  433. self.groups.removeAll()
  434. self.groups.append(contentsOf: listGroups)
  435. self.tableView.reloadData()
  436. }
  437. }
  438. completion(dataGroups)
  439. }
  440. }
  441. private func getGroups(id: String = "", parent: String = "", completion: @escaping ([Group]) -> ()) {
  442. DispatchQueue.global().async {
  443. Database.shared.database?.inTransaction({ fmdb, rollback in
  444. completion(self.getGroupRecursive(fmdb: fmdb, id: id, parent: parent))
  445. })
  446. }
  447. }
  448. private func pullBuddy() {
  449. if let me = UserDefaults.standard.string(forKey: "me") {
  450. DispatchQueue.global().async {
  451. let _ = Nexilis.write(message: CoreMessage_TMessageBank.getBatchBuddiesInfos(p_f_pin: me, last_update: 0))
  452. }
  453. }
  454. }
  455. private func joinOpenGroup(groupId: String, flagMember: String = "0", completion: @escaping (Bool) -> ()) {
  456. DispatchQueue.global().async {
  457. var result: Bool = false
  458. let idMe = UserDefaults.standard.string(forKey: "me") as String?
  459. if let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getAddGroupMember(p_group_id: groupId, p_member_pin: idMe!, p_position: "0")), response.isOk() {
  460. result = true
  461. }
  462. completion(result)
  463. }
  464. }
  465. @IBOutlet weak var backgroundImage: UIImageView!
  466. /*
  467. // MARK: - Navigation
  468. // In a storyboard-based application, you will often want to do a little preparation before navigation
  469. override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
  470. // Get the new view controller using segue.destination.
  471. // Pass the selected object to the new view controller.
  472. }
  473. */
  474. }
  475. // MARK: - Table view data source
  476. extension SecondTabViewController: UITableViewDelegate, UITableViewDataSource {
  477. func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
  478. switch segment.selectedSegmentIndex {
  479. case 0:
  480. let data: Chat
  481. if isFilltering {
  482. data = fillteredData[indexPath.row] as! Chat
  483. } else {
  484. data = chats[indexPath.row]
  485. }
  486. if let chooser = isChooser {
  487. if data.pin == "-999"{
  488. return
  489. }
  490. chooser(data.messageScope, data.pin)
  491. dismiss(animated: true, completion: nil)
  492. return
  493. }
  494. let user = User.getData(pin: data.pin)
  495. if user != nil || data.pin == "-999" {
  496. let editorPersonalVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorPersonalVC") as! EditorPersonal
  497. editorPersonalVC.hidesBottomBarWhenPushed = true
  498. editorPersonalVC.unique_l_pin = data.pin
  499. navigationController?.show(editorPersonalVC, sender: nil)
  500. } else {
  501. let editorGroupVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorGroupVC") as! EditorGroup
  502. editorGroupVC.hidesBottomBarWhenPushed = true
  503. editorGroupVC.unique_l_pin = data.pin
  504. navigationController?.show(editorGroupVC, sender: nil)
  505. }
  506. case 1:
  507. let group: Group
  508. if isFilltering {
  509. if indexPath.row == 0 {
  510. group = fillteredData[indexPath.section] as! Group
  511. } else {
  512. group = (fillteredData[indexPath.section] as! Group).childs[indexPath.row - 1]
  513. }
  514. } else {
  515. if indexPath.row == 0 {
  516. group = groups[indexPath.section]
  517. } else {
  518. group = groups[indexPath.section].childs[indexPath.row - 1]
  519. }
  520. }
  521. group.isSelected = !group.isSelected
  522. if !group.isSelected{
  523. var sects = 0
  524. var sect = indexPath.section
  525. var id = group.id
  526. if let e = groupMap[id] {
  527. var loooop = true
  528. repeat {
  529. let c = sect + 1
  530. if isFilltering {
  531. if let o = self.fillteredData[c] as? Group {
  532. if o.parent == id {
  533. sects = sects + 1
  534. sect = c
  535. id = o.id
  536. (self.fillteredData[c] as! Group).isSelected = false
  537. self.groupMap.removeValue(forKey: (self.fillteredData[c] as! Group).id)
  538. }
  539. else {
  540. loooop = false
  541. }
  542. }
  543. }
  544. else {
  545. if self.groups[c].parent == id {
  546. sects = sects + 1
  547. sect = c
  548. id = self.groups[c].id
  549. self.groups[c].isSelected = false
  550. self.groupMap.removeValue(forKey: self.groups[c].id)
  551. }
  552. else {
  553. loooop = false
  554. }
  555. }
  556. } while(loooop)
  557. }
  558. for i in stride(from: sects, to: 0, by: -1){
  559. if isFilltering {
  560. self.fillteredData.remove(at: indexPath.section + i)
  561. }
  562. else {
  563. self.groups.remove(at: indexPath.section + i)
  564. }
  565. }
  566. groupMap.removeValue(forKey: group.id)
  567. }
  568. if group.groupType == "NOTJOINED" {
  569. let alert = UIAlertController(title: "Do you want to join this group?".localized(), message: "Groups : \(group.name)\nMembers: \(group.by)".localized(), preferredStyle: .alert)
  570. alert.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel, handler: nil))
  571. alert.addAction(UIAlertAction(title: "Join".localized(), style: .default, handler: {(_) in
  572. self.joinOpenGroup(groupId: group.id, completion: { result in
  573. if result {
  574. DispatchQueue.main.async {
  575. self.groupMap.removeAll()
  576. let editorGroupVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorGroupVC") as! EditorGroup
  577. editorGroupVC.hidesBottomBarWhenPushed = true
  578. editorGroupVC.unique_l_pin = group.id
  579. self.navigationController?.show(editorGroupVC, sender: nil)
  580. }
  581. }
  582. })
  583. }))
  584. self.present(alert, animated: true, completion: nil)
  585. return
  586. }
  587. if group.childs.count == 0 {
  588. let groupId = group.chatId.isEmpty ? group.id : group.chatId
  589. if let chooser = isChooser {
  590. chooser("4", groupId)
  591. dismiss(animated: true, completion: nil)
  592. return
  593. }
  594. self.groupMap.removeAll()
  595. let editorGroupVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorGroupVC") as! EditorGroup
  596. editorGroupVC.hidesBottomBarWhenPushed = true
  597. editorGroupVC.unique_l_pin = groupId
  598. navigationController?.show(editorGroupVC, sender: nil)
  599. } else {
  600. if indexPath.row == 0 {
  601. tableView.reloadData()
  602. } else {
  603. getGroups(id: group.id) { g in
  604. DispatchQueue.main.async {
  605. print("index path section: \(indexPath.section)")
  606. print("index path row: \(indexPath.row)")
  607. // print("index path item: \(indexPath.item)")
  608. if self.isFilltering {
  609. // self.fillteredData.remove(at: indexPath.section)
  610. if self.fillteredData[indexPath.section] is Group {
  611. self.groupMap[(self.fillteredData[indexPath.section] as! Group).id] = 1
  612. self.fillteredData.insert(contentsOf: g, at: indexPath.section + 1)
  613. }
  614. } else {
  615. // self.groups.remove(at: indexPath.section)
  616. self.groupMap[self.groups[indexPath.section].id] = 1
  617. self.groups.insert(contentsOf: g, at: indexPath.section + 1)
  618. }
  619. print("groupMap: \(self.groupMap)")
  620. tableView.reloadData()
  621. }
  622. }
  623. }
  624. }
  625. default:
  626. let data = contacts[indexPath.row]
  627. let editorPersonalVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorPersonalVC") as! EditorPersonal
  628. editorPersonalVC.hidesBottomBarWhenPushed = true
  629. editorPersonalVC.unique_l_pin = data.pin
  630. navigationController?.show(editorPersonalVC, sender: nil)
  631. }
  632. }
  633. func numberOfSections(in tableView: UITableView) -> Int {
  634. if isFilltering {
  635. if segment.selectedSegmentIndex == 1 {
  636. return fillteredData.count
  637. }
  638. return 1
  639. } else {
  640. if segment.selectedSegmentIndex == 1 {
  641. return groups.count
  642. }
  643. return 1
  644. }
  645. }
  646. func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  647. var value = 0
  648. if isFilltering {
  649. if segment.selectedSegmentIndex == 2, let groups = fillteredData as? [Group] {
  650. let group = groups[section]
  651. if group.isSelected {
  652. if let g = groupMap[group.id] {
  653. value = 1
  654. }
  655. else {
  656. value = group.childs.count + 1
  657. }
  658. } else {
  659. value = 1
  660. }
  661. }
  662. return fillteredData.count
  663. }
  664. switch segment.selectedSegmentIndex {
  665. case 0:
  666. value = chats.count
  667. case 1:
  668. let group = groups[section]
  669. if group.isSelected {
  670. if let g = groupMap[group.id] {
  671. value = 1
  672. }
  673. else {
  674. value = group.childs.count + 1
  675. }
  676. } else {
  677. value = 1
  678. }
  679. default:
  680. value = chats.count
  681. }
  682. return value
  683. }
  684. func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  685. var cell: UITableViewCell!
  686. switch segment.selectedSegmentIndex {
  687. case 0:
  688. cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifierChat", for: indexPath)
  689. cell.separatorInset.left = 60.0
  690. let content = cell.contentView
  691. if content.subviews.count > 0 {
  692. content.subviews.forEach { $0.removeFromSuperview() }
  693. }
  694. let data: Chat
  695. if isFilltering {
  696. data = fillteredData[indexPath.row] as! Chat
  697. } else {
  698. data = chats[indexPath.row]
  699. }
  700. let imageView = UIImageView()
  701. content.addSubview(imageView)
  702. imageView.translatesAutoresizingMaskIntoConstraints = false
  703. NSLayoutConstraint.activate([
  704. imageView.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: 10.0),
  705. imageView.topAnchor.constraint(equalTo: content.topAnchor, constant: 10.0),
  706. imageView.bottomAnchor.constraint(equalTo: content.bottomAnchor, constant: -20.0),
  707. imageView.widthAnchor.constraint(equalToConstant: 40.0),
  708. imageView.heightAnchor.constraint(equalToConstant: 40.0)
  709. ])
  710. if data.profile.isEmpty && data.pin != "-999" {
  711. let user = User.getData(pin: data.pin)
  712. if user != nil {
  713. imageView.image = UIImage(named: "Profile---Purple", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
  714. } else {
  715. imageView.image = UIImage(named: "Conversation---Purple", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
  716. }
  717. } else {
  718. 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
  719. imageView.image = image
  720. })
  721. }
  722. let titleView = UILabel()
  723. content.addSubview(titleView)
  724. titleView.translatesAutoresizingMaskIntoConstraints = false
  725. NSLayoutConstraint.activate([
  726. titleView.leadingAnchor.constraint(equalTo: imageView.trailingAnchor, constant: 10.0),
  727. titleView.topAnchor.constraint(equalTo: content.topAnchor, constant: 10.0),
  728. titleView.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -40.0),
  729. ])
  730. titleView.text = data.name
  731. titleView.font = UIFont.systemFont(ofSize: 14, weight: .medium)
  732. let messageView = UILabel()
  733. content.addSubview(messageView)
  734. messageView.translatesAutoresizingMaskIntoConstraints = false
  735. NSLayoutConstraint.activate([
  736. messageView.leadingAnchor.constraint(equalTo: imageView.trailingAnchor, constant: 10.0),
  737. messageView.topAnchor.constraint(equalTo: titleView.bottomAnchor, constant: 5.0),
  738. messageView.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -40.0),
  739. ])
  740. messageView.textColor = .gray
  741. let text = Utils.previewMessageText(chat: data)
  742. if let attributeText = text as? NSAttributedString {
  743. messageView.attributedText = attributeText
  744. } else if let stringText = text as? String {
  745. messageView.text = stringText
  746. }
  747. messageView.font = UIFont.systemFont(ofSize: 12)
  748. messageView.numberOfLines = 2
  749. if data.counter != "0" {
  750. let viewCounter = UIView()
  751. content.addSubview(viewCounter)
  752. viewCounter.translatesAutoresizingMaskIntoConstraints = false
  753. NSLayoutConstraint.activate([
  754. viewCounter.centerYAnchor.constraint(equalTo: content.centerYAnchor),
  755. viewCounter.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -20),
  756. viewCounter.widthAnchor.constraint(greaterThanOrEqualToConstant: 20),
  757. viewCounter.heightAnchor.constraint(equalToConstant: 20)
  758. ])
  759. viewCounter.backgroundColor = .systemRed
  760. viewCounter.layer.cornerRadius = 10
  761. viewCounter.clipsToBounds = true
  762. viewCounter.layer.borderWidth = 0.5
  763. viewCounter.layer.borderColor = UIColor.secondaryColor.cgColor
  764. let labelCounter = UILabel()
  765. viewCounter.addSubview(labelCounter)
  766. labelCounter.translatesAutoresizingMaskIntoConstraints = false
  767. NSLayoutConstraint.activate([
  768. labelCounter.centerYAnchor.constraint(equalTo: viewCounter.centerYAnchor),
  769. labelCounter.leadingAnchor.constraint(equalTo: viewCounter.leadingAnchor, constant: 2),
  770. labelCounter.trailingAnchor.constraint(equalTo: viewCounter.trailingAnchor, constant: -2),
  771. ])
  772. labelCounter.font = UIFont.systemFont(ofSize: 11)
  773. if Int(data.counter)! > 99 {
  774. labelCounter.text = "99+"
  775. } else {
  776. labelCounter.text = data.counter
  777. }
  778. labelCounter.textColor = .secondaryColor
  779. labelCounter.textAlignment = .center
  780. }
  781. case 1:
  782. cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifierGroup", for: indexPath)
  783. var content = cell.defaultContentConfiguration()
  784. content.textProperties.font = UIFont.systemFont(ofSize: 14)
  785. let group: Group
  786. if isFilltering {
  787. if indexPath.row == 0 {
  788. group = fillteredData[indexPath.section] as! Group
  789. } else {
  790. group = (fillteredData[indexPath.section] as! Group).childs[indexPath.row - 1]
  791. }
  792. } else {
  793. if indexPath.row == 0 {
  794. group = groups[indexPath.section]
  795. } else {
  796. group = groups[indexPath.section].childs[indexPath.row - 1]
  797. }
  798. }
  799. if group.official == "1" && group.parent == "" {
  800. 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)
  801. }
  802. else if group.isOpen == "1" && group.parent == "" {
  803. if self.traitCollection.userInterfaceStyle == .dark {
  804. content.attributedText = self.set(image: UIImage(systemName: "globe")!.withTintColor(.white), with: " \(group.name)", size: 15, y: -4)
  805. } else {
  806. content.attributedText = self.set(image: UIImage(systemName: "globe")!, with: " \(group.name)", size: 15, y: -4)
  807. }
  808. } else if group.parent == "" {
  809. if self.traitCollection.userInterfaceStyle == .dark {
  810. content.attributedText = self.set(image: UIImage(systemName: "lock.fill")!.withTintColor(.white), with: " \(group.name)", size: 15, y: -4)
  811. } else {
  812. content.attributedText = self.set(image: UIImage(systemName: "lock.fill")!, with: " \(group.name)", size: 15, y: -4)
  813. }
  814. } else {
  815. content.text = group.name
  816. }
  817. if group.childs.count > 0 {
  818. let iconName = (group.isSelected) ? "chevron.up.circle" : "chevron.down.circle"
  819. let imageView = UIImageView(image: UIImage(systemName: iconName))
  820. imageView.tintColor = .black
  821. cell.accessoryView = imageView
  822. }
  823. else {
  824. cell.accessoryView = nil
  825. cell.accessoryType = .none
  826. }
  827. content.imageProperties.maximumSize = CGSize(width: 40, height: 40)
  828. 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
  829. content.image = image
  830. }
  831. cell.contentConfiguration = content
  832. default:
  833. cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifierContact", for: indexPath)
  834. var content = cell.defaultContentConfiguration()
  835. content.text = ""
  836. cell.contentConfiguration = content
  837. }
  838. cell.backgroundColor = .clear
  839. return cell
  840. }
  841. func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
  842. return 70.0
  843. }
  844. }
  845. extension SecondTabViewController: UISearchControllerDelegate, UISearchBarDelegate, UISearchResultsUpdating {
  846. func updateSearchResults(for searchController: UISearchController) {
  847. filterContentForSearchText(searchController.searchBar.text!)
  848. }
  849. func searchBarBookmarkButtonClicked(_ searchBar: UISearchBar) {
  850. recordAudio()
  851. }
  852. func set(image: UIImage, with text: String, size: CGFloat, y: CGFloat) -> NSAttributedString {
  853. let attachment = NSTextAttachment()
  854. attachment.image = image
  855. attachment.bounds = CGRect(x: 0, y: y, width: size, height: size)
  856. let attachmentStr = NSAttributedString(attachment: attachment)
  857. let mutableAttributedString = NSMutableAttributedString()
  858. mutableAttributedString.append(attachmentStr)
  859. let textString = NSAttributedString(string: text)
  860. mutableAttributedString.append(textString)
  861. return mutableAttributedString
  862. }
  863. }