SecondTabViewController.swift 116 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266
  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. import QuickLook
  12. class SecondTabViewController: UIViewController, UIScrollViewDelegate, UIGestureRecognizerDelegate, UITextFieldDelegate, UICollectionViewDelegate, UICollectionViewDataSource, QLPreviewControllerDataSource {
  13. var isChooser: ((String, String) -> ())?
  14. var isAdmin: Bool = false
  15. var chats: [Chat] = []
  16. var groups: [Group] = []
  17. var cancelSearchButton = UIBarButtonItem()
  18. var menuItem = UIBarButtonItem()
  19. var menuBroadcast = UIBarButtonItem()
  20. var voiceItem = UIBarButtonItem()
  21. var childrenMenu = [UIAction]()
  22. var groupMap: [String:Int] = [:]
  23. var isAllowSpeech = false
  24. var alertController = LibAlertController()
  25. var noData = false
  26. let textViewSearch = UITextField()
  27. let imageVoiceSb = UIImageView()
  28. let imageNewChatSb = UIImageView()
  29. let imageClearSearch = UIImageView()
  30. @IBOutlet weak var viewToolbar: UIView!
  31. @IBOutlet weak var heightToolbar: NSLayoutConstraint!
  32. var viewCategorySearch: UIScrollView!
  33. var leftTVSearch: NSLayoutConstraint!
  34. var viewCatInTV: UIView!
  35. var imageViewSearch: UIImageView!
  36. var gridImage: UICollectionView!
  37. var previewItem: NSURL?
  38. var audioPlayer: AVAudioPlayer?
  39. let UNREAD_TAG = 10
  40. let PHOTOS_TAG = 11
  41. let DOCUMENTS_TAG = 12
  42. let LINKS_TAG = 13
  43. let VIDEOS_TAG = 14
  44. let GIFS_TAG = 15
  45. let AUDIOS_TAG = 16
  46. var selectedTag = 0
  47. // override var preferredStatusBarStyle: UIStatusBarStyle {
  48. // return self.traitCollection.userInterfaceStyle == .dark ? .default : .lightContent // Change this to .default for black text color
  49. // }
  50. lazy var searchController: UISearchController = {
  51. var searchController = UISearchController(searchResultsController: nil)
  52. searchController.delegate = self
  53. searchController.searchResultsUpdater = self
  54. searchController.searchBar.autocapitalizationType = .none
  55. searchController.searchBar.delegate = self
  56. // searchController.searchBar.setMagnifyingGlassColorTo(color: self.traitCollection.userInterfaceStyle == .dark ? .white : .black)
  57. // searchController.searchBar.updateHeight(height: 30, radius: 15, borderColor: UIColor.clear.cgColor)
  58. searchController.searchBar.setImage(UIImage(), for: .search, state: .normal)
  59. searchController.searchBar.setPositionAdjustment(UIOffset(horizontal: 10, vertical: 0), for: .search)
  60. searchController.searchBar.setCustomBackgroundImage(image: UIImage(named: self.traitCollection.userInterfaceStyle == .dark ? "nx_search_bar_dark" : "nx_search_bar", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!)
  61. searchController.obscuresBackgroundDuringPresentation = false
  62. searchController.searchBar.searchTextField.attributedPlaceholder = NSAttributedString(string: "Search...".localized(), attributes: [NSAttributedString.Key.foregroundColor: UIColor.gray, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 11)])
  63. return searchController
  64. }()
  65. lazy var segment: UISegmentedControl = {
  66. var segment = UISegmentedControl(items: ["Chats".localized(), "Forums".localized()])
  67. segment.sizeToFit()
  68. segment.selectedSegmentIndex = 0
  69. segment.addTarget(self, action: #selector(segmentChanged(sender:)), for: .valueChanged)
  70. segment.setTitleTextAttributes([NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 12.0)], for: .normal)
  71. return segment
  72. }()
  73. var fillteredData: [Any] = []
  74. var isSearchBarEmpty: Bool {
  75. return searchController.searchBar.text!.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
  76. }
  77. var isFiltering: Bool {
  78. return !textViewSearch.text!.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
  79. // return !isSearchBarEmpty
  80. }
  81. @IBOutlet var tableView: UITableView!
  82. var speechRecognizer = SFSpeechRecognizer(locale: Locale(identifier: "id"))
  83. var recognitionRequest : SFSpeechAudioBufferRecognitionRequest?
  84. var recognitionTask : SFSpeechRecognitionTask?
  85. let audioEngine = AVAudioEngine()
  86. func filterContentForSearchText(_ searchText: String) {
  87. if !searchText.isEmpty {
  88. switch segment.selectedSegmentIndex {
  89. case 1:
  90. fillteredData = self.groups.filter { $0.name.lowercased().contains(searchText.lowercased()) }
  91. default:
  92. if selectedTag == 0 {
  93. fillteredData = self.chats.filter { $0.name.lowercased().contains(searchText.lowercased()) || $0.messageText.lowercased().contains(searchText.lowercased()) }
  94. } else {
  95. switch(selectedTag) {
  96. case UNREAD_TAG :
  97. fillteredData = self.chats.filter { $0.counter != "0" }
  98. break
  99. case PHOTOS_TAG, VIDEOS_TAG :
  100. fillteredData = Chat.getData(isImage: selectedTag == PHOTOS_TAG, isVideo: selectedTag == VIDEOS_TAG)
  101. if fillteredData.count > 0 {
  102. if gridImage != nil && gridImage.isDescendant(of: self.view) {
  103. gridImage.removeFromSuperview()
  104. }
  105. let width = self.view.frame.width / 3 - 2
  106. let cellSize = CGSize(width:width, height:width)
  107. let layout = UICollectionViewFlowLayout()
  108. layout.scrollDirection = .vertical
  109. layout.itemSize = cellSize
  110. layout.sectionInset = UIEdgeInsets(top: 1, left: 1, bottom: 1, right: 1)
  111. layout.minimumLineSpacing = 1.0
  112. layout.minimumInteritemSpacing = 1.0
  113. gridImage = UICollectionView(frame: .zero, collectionViewLayout: layout)
  114. self.view.addSubview(gridImage)
  115. gridImage.anchor(top: tableView.topAnchor, left: tableView.leftAnchor, bottom: tableView.bottomAnchor, right: tableView.rightAnchor)
  116. gridImage.backgroundColor = .clear
  117. gridImage.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "gridCell")
  118. gridImage.delegate = self
  119. gridImage.dataSource = self
  120. tableView.isHidden = true
  121. }
  122. break
  123. case DOCUMENTS_TAG :
  124. fillteredData = Chat.getData(isDoc: true)
  125. break
  126. case GIFS_TAG :
  127. break
  128. case LINKS_TAG :
  129. fillteredData = Chat.getData(isLink: true)
  130. break
  131. case AUDIOS_TAG :
  132. fillteredData = Chat.getData(isAudio: true)
  133. break
  134. default:
  135. break
  136. }
  137. }
  138. }
  139. }
  140. tableView.reloadData()
  141. }
  142. override func viewDidLoad() {
  143. super.viewDidLoad()
  144. let me = User.getMyPin()!
  145. Database.shared.database?.inTransaction({ fmdb, rollback in
  146. 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() {
  147. isAdmin = cursor.string(forColumnIndex: 3) == "23" || cursor.string(forColumnIndex: 3) == "24"
  148. cursor.close()
  149. }
  150. })
  151. // var childrenMenu : [UIAction] = []
  152. //
  153. // if(isAdmin){
  154. // childrenMenu.append(UIAction(title: "Broadcast Message".localized(), image: UIImage(systemName: "envelope.open"), handler: {[weak self](_) in
  155. // let controller = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "broadcastNav")
  156. // self?.navigationController?.present(controller, animated: true, completion: nil)
  157. // }))
  158. // }
  159. // let startConvIcon = resizeImage(image: self.traitCollection.userInterfaceStyle == .dark ? UIImage(systemName: "square.and.pencil")!.withTintColor(.white) : UIImage(systemName: "square.and.pencil")!, targetSize: CGSize(width: 25, height: 25))
  160. // let viewStartConv = UIButton(frame: CGRect(x: 0, y: 0, width: startConvIcon.size.width, height: startConvIcon.size.height))
  161. // viewStartConv.setImage(startConvIcon, for: .normal)
  162. // viewStartConv.addTarget(self, action: #selector(startConversation), for: .touchUpInside)
  163. // let brodcastIcon = resizeImage(image: self.traitCollection.userInterfaceStyle == .dark ? UIImage(named: "ic_broadcast")!.withTintColor(.white) : UIImage(named: "ic_broadcast")!, targetSize: CGSize(width: 25, height: 25))
  164. // let viewbrodcast = UIButton(frame: CGRect(x: 0, y: 0, width: brodcastIcon.size.width, height: brodcastIcon.size.height))
  165. // viewbrodcast.setImage(brodcastIcon, for: .normal)
  166. // viewbrodcast.addTarget(self, action: #selector(openBroadcast), for: .touchUpInside)
  167. //
  168. // menuItem = UIBarButtonItem(customView: viewStartConv)
  169. // menuBroadcast = UIBarButtonItem(customView: viewbrodcast)
  170. // menuItem = UIBarButtonItem(image: UIImage(systemName: "square.and.pencil"), style: .plain, target: self, action: #selector(startConversation))
  171. // menuBroadcast = UIBarButtonItem(image: UIImage(systemName: "info.bubble"), style: .plain, target: self, action: #selector(openBroadcast))
  172. // voiceItem = UIBarButtonItem(image: UIImage(systemName: "mic.fill"), style: .plain, target: self, action: #selector(recordAudio))
  173. definesPresentationContext = true
  174. NotificationCenter.default.addObserver(self, selector: #selector(onStatusChat(notification:)), name: NSNotification.Name(rawValue: Nexilis.listenerStatusChat), object: nil)
  175. NotificationCenter.default.addObserver(self, selector: #selector(onReceiveMessage(notification:)), name: NSNotification.Name(rawValue: Nexilis.listenerReceiveChat), object: nil)
  176. NotificationCenter.default.addObserver(self, selector: #selector(onReload(notification:)), name: NSNotification.Name(rawValue: "onMember"), object: nil)
  177. NotificationCenter.default.addObserver(self, selector: #selector(onReload(notification:)), name: NSNotification.Name(rawValue: "onUpdatePersonInfo"), object: nil)
  178. NotificationCenter.default.addObserver(self, selector: #selector(onReloadTab(notification:)), name: NSNotification.Name(rawValue: "reloadTabChats"), object: nil)
  179. NotificationCenter.default.addObserver(self, selector: #selector(onReloadTab(notification:)), name: NSNotification.Name(rawValue: "onTopic"), object: nil)
  180. imageViewSearch = UIImageView(image: UIImage(named: self.traitCollection.userInterfaceStyle == .dark ? "nx_search_bar_dark" : "nx_search_bar", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!)
  181. imageViewSearch.contentMode = .scaleToFill
  182. viewToolbar.addSubview(imageViewSearch)
  183. imageViewSearch.anchor(top: viewToolbar.topAnchor, left: viewToolbar.leftAnchor, right: viewToolbar.rightAnchor, paddingTop: 5, paddingLeft: 10, paddingRight: 10, height: 35)
  184. imageViewSearch.isUserInteractionEnabled = true
  185. viewCategorySearch = UIScrollView()
  186. viewCategorySearch.showsHorizontalScrollIndicator = false
  187. viewToolbar.addSubview(viewCategorySearch)
  188. viewCategorySearch.anchor(top: imageViewSearch.bottomAnchor, left: viewToolbar.leftAnchor, right: viewToolbar.rightAnchor, paddingLeft: 10, paddingRight: 10, height: 40)
  189. viewCategorySearch.isHidden = true
  190. let groupViewCat = UIStackView()
  191. viewCategorySearch.addSubview(groupViewCat)
  192. groupViewCat.anchor(left: viewCategorySearch.leftAnchor, right: viewCategorySearch.rightAnchor, centerY: viewCategorySearch.centerYAnchor, height: 30)
  193. groupViewCat.axis = .horizontal
  194. groupViewCat.spacing = 10
  195. for i in 0..<7 {
  196. var widthView: CGFloat = 105
  197. var iconCat = UIImage(systemName: "bubble.right")
  198. var textCat = "Unread".localized()
  199. var tag = UNREAD_TAG
  200. if i == 1 {
  201. widthView = 100
  202. iconCat = UIImage(systemName: "photo")
  203. textCat = "Photos".localized()
  204. tag = PHOTOS_TAG
  205. } else if i == 2 {
  206. widthView = 130
  207. iconCat = UIImage(systemName: "doc")
  208. textCat = "Documents".localized()
  209. tag = DOCUMENTS_TAG
  210. } else if i == 3 {
  211. widthView = 80
  212. iconCat = UIImage(systemName: "link")
  213. textCat = "Links".localized()
  214. tag = LINKS_TAG
  215. } else if i == 4 {
  216. widthView = 100
  217. iconCat = UIImage(systemName: "video")
  218. textCat = "Videos".localized()
  219. tag = VIDEOS_TAG
  220. } else if i == 5 {
  221. widthView = 80
  222. iconCat = UIImage(systemName: "photo.on.rectangle")
  223. textCat = "GIFs".localized()
  224. tag = GIFS_TAG
  225. } else if i == 6 {
  226. widthView = 80
  227. iconCat = UIImage(systemName: "music.note")
  228. textCat = "Audio".localized()
  229. tag = AUDIOS_TAG
  230. }
  231. let viewCat = UIView(frame: CGRect(x: 0, y: 0, width: widthView, height: 30))
  232. groupViewCat.addArrangedSubview(viewCat)
  233. viewCat.anchor(width: widthView, height: 30)
  234. viewCat.layer.cornerRadius = 15
  235. viewCat.layer.borderColor = UIColor.gray.cgColor
  236. viewCat.layer.borderWidth = 0.5
  237. viewCat.backgroundColor = .white
  238. viewCat.isUserInteractionEnabled = true
  239. viewCat.tag = tag
  240. viewCat.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(onTapCategory(_:))))
  241. let imageIcon = UIImageView()
  242. imageIcon.image = iconCat
  243. imageIcon.tintColor = .black
  244. viewCat.addSubview(imageIcon)
  245. imageIcon.anchor(left: viewCat.leftAnchor, paddingLeft: 10, centerY: viewCat.centerYAnchor)
  246. let imageText = UILabel()
  247. imageText.text = textCat
  248. viewCat.addSubview(imageText)
  249. imageText.anchor(left: imageIcon.rightAnchor, paddingLeft: 5, centerY: viewCat.centerYAnchor)
  250. imageText.font = .systemFont(ofSize: 15)
  251. }
  252. let imageSetting = UIImageView(image: UIImage(named: "nx_setting_sb", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!)
  253. imageSetting.contentMode = .scaleToFill
  254. imageViewSearch.addSubview(imageSetting)
  255. imageSetting.anchor(right: imageViewSearch.rightAnchor, paddingRight: 15, centerY: imageViewSearch.centerYAnchor, width: 20, height: 20)
  256. imageSetting.isUserInteractionEnabled = true
  257. imageSetting.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(settingTapped)))
  258. imageVoiceSb.image = UIImage(named: "nx_mic", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!
  259. imageVoiceSb.contentMode = .scaleAspectFit
  260. imageViewSearch.addSubview(imageVoiceSb)
  261. imageVoiceSb.anchor(right: imageSetting.leftAnchor, paddingRight: 10, centerY: imageViewSearch.centerYAnchor, width: 20, height: 20)
  262. imageVoiceSb.isUserInteractionEnabled = true
  263. imageVoiceSb.isHidden = true
  264. imageVoiceSb.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(recordAudio)))
  265. imageClearSearch.image = UIImage(systemName: "xmark")
  266. imageClearSearch.contentMode = .scaleAspectFit
  267. imageClearSearch.tintColor = .gray
  268. imageViewSearch.addSubview(imageClearSearch)
  269. imageClearSearch.anchor(right: imageVoiceSb.leftAnchor, paddingRight: 5, centerY: imageViewSearch.centerYAnchor, width: 20, height: 20)
  270. imageClearSearch.isUserInteractionEnabled = true
  271. imageClearSearch.isHidden = true
  272. imageClearSearch.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(clearSearch)))
  273. textViewSearch.placeholder = "Search...".localized()
  274. textViewSearch.isUserInteractionEnabled = true
  275. imageViewSearch.addSubview(textViewSearch)
  276. textViewSearch.font = .systemFont(ofSize: 11)
  277. textViewSearch.anchor(top: imageViewSearch.topAnchor, bottom: imageViewSearch.bottomAnchor, right: imageViewSearch.rightAnchor, paddingTop: 5, paddingBottom: 5, paddingRight: 90)
  278. leftTVSearch = textViewSearch.leftAnchor.constraint(equalTo: imageViewSearch.leftAnchor, constant: 20.0)
  279. NSLayoutConstraint.activate([
  280. leftTVSearch
  281. ])
  282. textViewSearch.delegate = self
  283. tableView.tableHeaderView = segment
  284. tableView.tableFooterView = UIView()
  285. if PrefsUtil.getCpaasMode() == PrefsUtil.CPAAS_MODE_DOCKED {
  286. tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 80, right: 0)
  287. }
  288. pullBuddy()
  289. navigationController?.setNavigationBarHidden(true, animated: false)
  290. let tapGesture = UITapGestureRecognizer(target: self, action: #selector(collapseDocked))
  291. tapGesture.cancelsTouchesInView = false
  292. tapGesture.delegate = self
  293. self.view.addGestureRecognizer(tapGesture)
  294. }
  295. @objc func collapseDocked() {
  296. if ViewController.isExpandButton {
  297. ViewController.expandButton()
  298. }
  299. if textViewSearch.isFirstResponder {
  300. textViewSearch.resignFirstResponder()
  301. }
  302. }
  303. @objc func openBroadcast() {
  304. let controller = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "broadcastNav")
  305. controller.modalPresentationStyle = .fullScreen
  306. self.navigationController?.present(controller, animated: true, completion: nil)
  307. }
  308. @objc func startConversation(){
  309. let navigationController = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "contactChatNav") as! UINavigationController
  310. navigationController.view.backgroundColor = self.traitCollection.userInterfaceStyle == .dark ? .black : .white
  311. Utils.addBackground(view: navigationController.view)
  312. navigationController.modalPresentationStyle = .fullScreen
  313. navigationController.navigationBar.tintColor = .white
  314. navigationController.navigationBar.barTintColor = self.traitCollection.userInterfaceStyle == .dark ? .blackDarkMode : .mainColor
  315. navigationController.navigationBar.isTranslucent = false
  316. let cancelButtonAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.foregroundColor: UIColor.white, NSAttributedString.Key.font : UIFont.systemFont(ofSize: 16)]
  317. UIBarButtonItem.appearance().setTitleTextAttributes(cancelButtonAttributes, for: .normal)
  318. let textAttributes = [NSAttributedString.Key.foregroundColor:UIColor.white]
  319. navigationController.navigationBar.titleTextAttributes = textAttributes
  320. navigationController.navigationBar.overrideUserInterfaceStyle = .dark
  321. navigationController.navigationBar.barStyle = .black
  322. self.navigationController?.present(navigationController, animated: true, completion: nil)
  323. }
  324. @objc func recordAudio(){
  325. if !isAllowSpeech {
  326. setupSpeech()
  327. } else {
  328. runVoice()
  329. }
  330. }
  331. @objc func clearSearch(){
  332. selectedTag = 0
  333. if gridImage != nil && gridImage.isDescendant(of: self.view) {
  334. gridImage.removeFromSuperview()
  335. tableView.isHidden = false
  336. }
  337. textViewSearch.text = ""
  338. imageClearSearch.isHidden = true
  339. if !textViewSearch.isFirstResponder {
  340. viewCategorySearch.isHidden = true
  341. imageVoiceSb.isHidden = true
  342. heightToolbar.constant = 45
  343. filterContentForSearchText("")
  344. } else if heightToolbar.constant == 45 {
  345. viewCategorySearch.isHidden = false
  346. heightToolbar.constant = 85
  347. }
  348. if leftTVSearch.constant != 20 {
  349. viewCatInTV.removeFromSuperview()
  350. leftTVSearch.constant = 20
  351. }
  352. }
  353. func setupSpeech() {
  354. self.speechRecognizer?.delegate = self
  355. SFSpeechRecognizer.requestAuthorization { (authStatus) in
  356. var isButtonEnabled = false
  357. switch authStatus {
  358. case .authorized:
  359. isButtonEnabled = true
  360. case .denied:
  361. isButtonEnabled = false
  362. //print("User denied access to speech recognition")
  363. case .restricted:
  364. isButtonEnabled = false
  365. //print("Speech recognition restricted on this device")
  366. case .notDetermined:
  367. isButtonEnabled = false
  368. //print("Speech recognition not yet authorized")
  369. @unknown default:
  370. isButtonEnabled = false
  371. }
  372. OperationQueue.main.addOperation() {
  373. self.isAllowSpeech = isButtonEnabled
  374. if isButtonEnabled {
  375. SecureUserDefaults.shared.set(isButtonEnabled, forKey: "allowSpeech")
  376. self.runVoice()
  377. }
  378. }
  379. }
  380. }
  381. func startRecording() {
  382. // Clear all previous session data and cancel task
  383. if recognitionTask != nil {
  384. recognitionTask?.cancel()
  385. recognitionTask = nil
  386. }
  387. // Create instance of audio session to record voice
  388. let audioSession = AVAudioSession.sharedInstance()
  389. do {
  390. try audioSession.setCategory(AVAudioSession.Category.record, mode: AVAudioSession.Mode.measurement, options: AVAudioSession.CategoryOptions.defaultToSpeaker)
  391. try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
  392. } catch {
  393. //print("audioSession properties weren't set because of an error.")
  394. }
  395. self.recognitionRequest = SFSpeechAudioBufferRecognitionRequest()
  396. let inputNode = audioEngine.inputNode
  397. guard let recognitionRequest = recognitionRequest else {
  398. fatalError("Unable to create an SFSpeechAudioBufferRecognitionRequest object")
  399. }
  400. recognitionRequest.shouldReportPartialResults = true
  401. self.recognitionTask = speechRecognizer?.recognitionTask(with: recognitionRequest, resultHandler: { (result, error) in
  402. var isFinal = false
  403. if result != nil {
  404. self.alertController.dismiss(animated: true)
  405. self.audioEngine.stop()
  406. self.recognitionRequest?.endAudio()
  407. isFinal = (result?.isFinal)!
  408. }
  409. if error != nil || isFinal {
  410. if error == nil {
  411. let textRes = result!.bestTranscription.formattedString
  412. self.textViewSearch.text = textRes
  413. self.filterContentForSearchText(textRes)
  414. // self.searchController.searchBar.searchTextField.text = result!.bestTranscription.formattedString
  415. // self.updateSearchResults(for: self.searchController)
  416. } else {
  417. self.audioEngine.stop()
  418. self.recognitionRequest?.endAudio()
  419. }
  420. self.voiceItem.image = UIImage(systemName: "mic.fill")
  421. inputNode.removeTap(onBus: 0)
  422. self.recognitionRequest = nil
  423. self.recognitionTask = nil
  424. }
  425. })
  426. let recordingFormat = inputNode.outputFormat(forBus: 0)
  427. inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { (buffer, when) in
  428. self.recognitionRequest?.append(buffer)
  429. }
  430. self.audioEngine.prepare()
  431. do {
  432. try self.audioEngine.start()
  433. } catch {
  434. //print("audioEngine couldn't start because of an error.")
  435. }
  436. }
  437. func runVoice() {
  438. if !audioEngine.isRunning {
  439. self.voiceItem.image = UIImage(systemName: "mic")
  440. alertController = LibAlertController(title: "Start Recording".localized(), message: "Say something, I'm listening!".localized(), preferredStyle: .alert)
  441. self.present(alertController, animated: true)
  442. self.startRecording()
  443. }
  444. }
  445. override func viewDidAppear(_ animated: Bool) {
  446. self.navigationController?.navigationBar.topItem?.title = "Chats".localized() + " & " + "Forums".localized()
  447. self.navigationController?.navigationBar.setNeedsLayout()
  448. DispatchQueue.main.asyncAfter(deadline: .now() + 0.2, execute: {
  449. var viewController = UIApplication.shared.windows.first!.rootViewController
  450. if !(viewController is ViewController) {
  451. viewController = self.parent
  452. }
  453. if ViewController.middleButton.isHidden {
  454. ViewController.isExpandButton = false
  455. if let viewController = viewController as? ViewController {
  456. if viewController.tabBar.isHidden {
  457. viewController.tabBar.isHidden = false
  458. ViewController.alwaysHideButton = false
  459. ViewController.middleButton.isHidden = false
  460. }
  461. }
  462. } else if PrefsUtil.getCpaasMode() != PrefsUtil.CPAAS_MODE_DOCKED {
  463. DispatchQueue.main.async {
  464. if let viewController = viewController as? ViewController {
  465. if viewController.tabBar.isHidden {
  466. ViewController.alwaysHideButton = false
  467. viewController.tabBar.isHidden = false
  468. }
  469. }
  470. }
  471. }
  472. })
  473. getData()
  474. DispatchQueue.global().async {
  475. self.getOpenGroups(listGroups: self.groups, completion: { g in
  476. DispatchQueue.main.async {
  477. for og in g {
  478. if self.groups.first(where: { $0.id == og.id }) == nil {
  479. self.groups.append(og)
  480. }
  481. }
  482. self.groups.sort { (a, b) -> Bool in
  483. if Int(a.official) == 1 {
  484. return true
  485. } else if Int(b.official) == 1 {
  486. return false
  487. } else {
  488. return Int(a.official) ?? 0 > Int(b.official) ?? 0
  489. }
  490. }
  491. DispatchQueue.main.async {
  492. self.tableView.reloadData()
  493. }
  494. }
  495. })
  496. }
  497. }
  498. override func viewWillAppear(_ animated: Bool) {
  499. // tabBarController?.navigationItem.leftBarButtonItem = cancelSearchButton
  500. let cancelButtonAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.foregroundColor: self.traitCollection.userInterfaceStyle == .dark ? .white : UIColor.black, NSAttributedString.Key.font : UIFont.systemFont(ofSize: 16)]
  501. UIBarButtonItem.appearance().setTitleTextAttributes(cancelButtonAttributes, for: .normal)
  502. let attributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.foregroundColor: self.traitCollection.userInterfaceStyle == .dark ? .white : UIColor.black, NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 16)]
  503. let navBarAppearance = UINavigationBarAppearance()
  504. navBarAppearance.configureWithTransparentBackground()
  505. navBarAppearance.titleTextAttributes = attributes
  506. navigationController?.navigationBar.standardAppearance = navBarAppearance
  507. navigationController?.navigationBar.scrollEdgeAppearance = navBarAppearance
  508. navigationController?.navigationBar.backgroundColor = .clear
  509. navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
  510. navigationController?.navigationBar.shadowImage = UIImage()
  511. navigationController?.navigationBar.isTranslucent = true
  512. navigationController?.setNavigationBarHidden(true, animated: false)
  513. navigationController?.navigationBar.tintColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .black
  514. navigationController?.navigationBar.overrideUserInterfaceStyle = self.traitCollection.userInterfaceStyle == .dark ? .dark : .light
  515. navigationController?.navigationBar.barStyle = .default
  516. // tabBarController?.navigationItem.leftBarButtonItem = voiceItem
  517. // let myData = User.getData(pin: User.getMyPin())
  518. // if User.isOfficial(official_account: myData?.official ?? "") || User.isOfficialRegular(official_account: myData?.official ?? "") || User.isInternal(userType: myData?.userType ?? "") {
  519. // let fixedSpace = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
  520. // fixedSpace.width = 15.0
  521. // tabBarController?.navigationItem.rightBarButtonItems = [menuBroadcast, fixedSpace, menuItem]
  522. // } else {
  523. // tabBarController?.navigationItem.rightBarButtonItem = menuItem
  524. // }
  525. // tabBarController?.navigationItem.searchController = searchController
  526. let lang: String = SecureUserDefaults.shared.value(forKey: "i18n_language") ?? "en"
  527. speechRecognizer = SFSpeechRecognizer(locale: Locale(identifier: lang ?? "en"))
  528. backgroundImage.backgroundColor = self.traitCollection.userInterfaceStyle == .dark ? .black : .white
  529. DispatchQueue.global().async {
  530. DispatchQueue.main.async {
  531. let listBg = PrefsUtil.getBackgroundLight().isEmpty && PrefsUtil.getBackgroundDark().isEmpty ? PrefsUtil.getBackground() :
  532. self.traitCollection.userInterfaceStyle == .dark ? PrefsUtil.getBackgroundDark() : PrefsUtil.getBackgroundLight()
  533. if listBg.isEmpty {
  534. return
  535. }
  536. var bgChoosen = ""
  537. let arrayBg = listBg.split(separator: ",")
  538. bgChoosen = String(arrayBg[Int.random(in: 0..<arrayBg.count)])
  539. let urlString = PrefsUtil.getURLBase() + "get_file_from_path?img=" + bgChoosen
  540. if let cachedImage = ImageCache.shared.image(forKey: urlString) {
  541. DispatchQueue.main.async() { [self] in
  542. backgroundImage.image = cachedImage
  543. }
  544. return
  545. }
  546. Utils.fetchDataWithCookiesAndUserAgent(from: URL(string: urlString)!) { data, response, error in
  547. guard let data = data, error == nil else { return }
  548. // always update the UI from the main thread
  549. DispatchQueue.main.async() { [self] in
  550. if UIImage(data: data) != nil {
  551. backgroundImage.image = UIImage(data: data)!
  552. ImageCache.shared.save(image: UIImage(data: data)!, forKey: urlString)
  553. }
  554. }
  555. }
  556. }
  557. }
  558. if segment.numberOfSegments == 2 {
  559. segment.setTitle("Chats".localized(), forSegmentAt: 0)
  560. segment.setTitle("Forums".localized(), forSegmentAt: 1)
  561. }
  562. if segment.selectedSegmentIndex == 0 {
  563. Utils.inTabChats = true
  564. // searchController.searchBar.placeholder = "Search chats & messages".localized()
  565. // searchController.searchBar.searchTextField.attributedPlaceholder = NSAttributedString(string: "Search chats & messages".localized(), attributes: [NSAttributedString.Key.foregroundColor: UIColor.gray, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 16)])
  566. } else {
  567. // searchController.searchBar.placeholder = "Search groups name".localized()
  568. // searchController.searchBar.searchTextField.attributedPlaceholder = NSAttributedString(string: "Search groups name".localized(), attributes: [NSAttributedString.Key.foregroundColor: UIColor.gray, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 16)])
  569. }
  570. // removeAllData()
  571. // getData()
  572. }
  573. // func removeAllData() {
  574. // groups.removeAll()
  575. // groupMap.removeAll()
  576. // chats.removeAll()
  577. // tableView.reloadData()
  578. // }
  579. func textFieldDidChangeSelection(_ textField: UITextField) {
  580. if textField == textViewSearch {
  581. var textSerch = textField.text ?? ""
  582. textSerch = textSerch.trimmingCharacters(in: .whitespacesAndNewlines)
  583. if !textSerch.isEmpty && imageClearSearch.isHidden {
  584. imageClearSearch.isHidden = false
  585. } else if textSerch.isEmpty && !imageClearSearch.isHidden {
  586. imageClearSearch.isHidden = true
  587. }
  588. if textField.text?.count == 1 && leftTVSearch.constant != 20 {
  589. selectedTag = 0
  590. if gridImage != nil && gridImage.isDescendant(of: self.view) {
  591. gridImage.removeFromSuperview()
  592. tableView.isHidden = false
  593. }
  594. textField.text = ""
  595. viewCatInTV.removeFromSuperview()
  596. leftTVSearch.constant = 20
  597. }
  598. filterContentForSearchText(textSerch)
  599. }
  600. if viewCatInTV != nil && viewCatInTV.isDescendant(of: imageViewSearch) {
  601. viewCategorySearch.isHidden = true
  602. heightToolbar.constant = 45
  603. } else if (viewCatInTV == nil || !viewCatInTV.isDescendant(of: imageViewSearch)) && segment.selectedSegmentIndex == 0 {
  604. viewCategorySearch.isHidden = false
  605. heightToolbar.constant = 85
  606. }
  607. }
  608. func textFieldDidBeginEditing(_ textField: UITextField) {
  609. if textField == textViewSearch {
  610. if segment.selectedSegmentIndex == 0 && (viewCatInTV == nil || !viewCatInTV.isDescendant(of: imageViewSearch)) {
  611. heightToolbar.constant = 85
  612. viewCategorySearch.isHidden = false
  613. imageVoiceSb.isHidden = false
  614. }
  615. }
  616. }
  617. func textFieldDidEndEditing(_ textField: UITextField) {
  618. if textField == textViewSearch {
  619. if let text = textField.text {
  620. if text.isEmpty {
  621. viewCategorySearch.isHidden = true
  622. imageVoiceSb.isHidden = true
  623. heightToolbar.constant = 45
  624. }
  625. }
  626. }
  627. }
  628. override func viewWillDisappear(_ animated: Bool) {
  629. tabBarController?.navigationItem.leftBarButtonItem = nil
  630. tabBarController?.navigationItem.searchController = nil
  631. tabBarController?.navigationItem.rightBarButtonItem = nil
  632. tabBarController?.navigationItem.rightBarButtonItems = nil
  633. tabBarController?.navigationItem.titleView = nil
  634. Utils.inTabChats = false
  635. if ViewController.isExpandButton {
  636. ViewController.expandButton()
  637. }
  638. }
  639. @objc func settingTapped() {
  640. // imageVoiceSb.isHidden = !imageVoiceSb.isHidden
  641. // imageNewChatSb.isHidden = !imageNewChatSb.isHidden
  642. startConversation()
  643. }
  644. @objc func onTapCategory(_ sender: UITapGestureRecognizer) {
  645. if let tappedView = sender.view {
  646. if viewCatInTV != nil && viewCatInTV.isDescendant(of: imageViewSearch) {
  647. viewCatInTV.removeFromSuperview()
  648. }
  649. var width1: CGFloat = 67
  650. var width2: CGFloat = 80
  651. viewCatInTV = UIView()
  652. imageViewSearch.addSubview(viewCatInTV)
  653. var iconCat = UIImage(systemName: "bubble.right")
  654. var textCat = "Unread".localized()
  655. selectedTag = tappedView.tag
  656. if tappedView.tag == PHOTOS_TAG {
  657. iconCat = UIImage(systemName: "photo")
  658. textCat = "Photos".localized()
  659. } else if tappedView.tag == DOCUMENTS_TAG {
  660. iconCat = UIImage(systemName: "doc")
  661. textCat = "Documents".localized()
  662. width1 = 97
  663. width2 = 110
  664. } else if tappedView.tag == LINKS_TAG {
  665. iconCat = UIImage(systemName: "link")
  666. textCat = "Links".localized()
  667. width1 = 57
  668. width2 = 70
  669. } else if tappedView.tag == VIDEOS_TAG {
  670. iconCat = UIImage(systemName: "video")
  671. textCat = "Videos".localized()
  672. } else if tappedView.tag == GIFS_TAG {
  673. iconCat = UIImage(systemName: "photo.on.rectangle")
  674. textCat = "GIFs".localized()
  675. width1 = 52
  676. width2 = 65
  677. } else if tappedView.tag == AUDIOS_TAG {
  678. iconCat = UIImage(systemName: "music.note")
  679. textCat = "Audio".localized()
  680. width1 = 62
  681. width2 = 75
  682. }
  683. viewCatInTV.frame = CGRect(x: 0, y: 0, width: width1, height: 30)
  684. viewCatInTV.anchor(left: imageViewSearch.leftAnchor, paddingLeft: 20, centerY: imageViewSearch.centerYAnchor, width: width1, height: 25)
  685. viewCatInTV.backgroundColor = .white
  686. viewCatInTV.layer.cornerRadius = 8
  687. viewCatInTV.clipsToBounds = true
  688. viewCatInTV.backgroundColor = .black
  689. textViewSearch.text = "~ "
  690. leftTVSearch.constant = width2
  691. let imageIcon = UIImageView()
  692. imageIcon.image = iconCat
  693. imageIcon.tintColor = .white
  694. viewCatInTV.addSubview(imageIcon)
  695. imageIcon.anchor(left: viewCatInTV.leftAnchor, paddingLeft: 5, centerY: viewCatInTV.centerYAnchor, width: 12, height: 12)
  696. let imageText = UILabel()
  697. imageText.text = textCat
  698. viewCatInTV.addSubview(imageText)
  699. imageText.textColor = .white
  700. imageText.anchor(left: imageIcon.rightAnchor, paddingLeft: 3, centerY: viewCatInTV.centerYAnchor, height: 12)
  701. imageText.font = .systemFont(ofSize: 12)
  702. }
  703. }
  704. @objc func onReloadTab(notification: NSNotification) {
  705. DispatchQueue.main.async {
  706. self.getData()
  707. }
  708. }
  709. @objc func onReload(notification: NSNotification) {
  710. let data:[AnyHashable : Any] = notification.userInfo!
  711. if data["member"] as? String == User.getMyPin()! {
  712. DispatchQueue.main.async {
  713. self.getData()
  714. }
  715. } else if data["state"] as? Int == 99 {
  716. DispatchQueue.main.async {
  717. self.getData()
  718. }
  719. }
  720. }
  721. @objc func onReceiveMessage(notification: NSNotification) {
  722. DispatchQueue.main.async { [self] in
  723. let data:[AnyHashable : Any] = notification.userInfo!
  724. if let dataMessage = data["message"] as? TMessage {
  725. let chatData = dataMessage.mBodies
  726. if chatData[CoreMessage_TMessageKey.IS_CALL_CENTER] == nil || chatData[CoreMessage_TMessageKey.IS_CALL_CENTER] == "0" {
  727. var indexChat: Int?
  728. if chatData[CoreMessage_TMessageKey.MESSAGE_SCOPE_ID] == "3" {
  729. let f_pin = chatData[CoreMessage_TMessageKey.F_PIN] != User.getMyPin() ? chatData[CoreMessage_TMessageKey.F_PIN] : chatData[CoreMessage_TMessageKey.L_PIN]
  730. indexChat = chats.firstIndex(where: { $0.pin == f_pin })
  731. } else if chatData[CoreMessage_TMessageKey.MESSAGE_SCOPE_ID] == "4" && chatData[CoreMessage_TMessageKey.F_PIN] != User.getMyPin() {
  732. indexChat = chats.firstIndex(where: { (chatData[CoreMessage_TMessageKey.CHAT_ID] ?? "").isEmpty ? $0.pin == chatData[CoreMessage_TMessageKey.L_PIN] : $0.pin == chatData[CoreMessage_TMessageKey.CHAT_ID] })
  733. }
  734. let newChat = Chat.getData(messageId: chatData[CoreMessage_TMessageKey.MESSAGE_ID] ?? "")
  735. if newChat.count > 0 {
  736. if indexChat != nil {
  737. chats.remove(at: indexChat!)
  738. chats.insert(newChat[0], at: 0)
  739. if segment.selectedSegmentIndex == 0 {
  740. let indexPathToMove = IndexPath(row: indexChat!, section: 0)
  741. let indexPathNewPosition = IndexPath(row: 0, section: 0)
  742. tableView.performBatchUpdates({
  743. tableView.moveRow(at: indexPathToMove, to: indexPathNewPosition)
  744. }, completion: nil)
  745. tableView.beginUpdates()
  746. tableView.reloadRows(at: [IndexPath(row: 0, section: 0)], with: .none)
  747. tableView.endUpdates()
  748. } else {
  749. tableView.reloadData()
  750. }
  751. } else {
  752. chats.insert(newChat[0], at: 0)
  753. tableView.reloadData()
  754. }
  755. }
  756. }
  757. }
  758. }
  759. }
  760. @objc func onStatusChat(notification: NSNotification) {
  761. DispatchQueue.main.async { [self] in
  762. let data:[AnyHashable : Any] = notification.userInfo!
  763. if let dataMessage = data["message"] as? TMessage {
  764. var idMessage = dataMessage.getBody(key: CoreMessage_TMessageKey.MESSAGE_ID)
  765. if dataMessage.mBodies["message_id"] != nil {
  766. idMessage = dataMessage.getBody(key: "message_id")
  767. if idMessage.contains("'") {
  768. idMessage = idMessage.replacingOccurrences(of: "'", with: "")
  769. }
  770. }
  771. if idMessage.contains(",") {
  772. let listString = dataMessage.getBody(key: CoreMessage_TMessageKey.MESSAGE_ID).components(separatedBy: ",")
  773. idMessage = listString[listString.count - 1]
  774. }
  775. let indexChat = chats.firstIndex(where: { $0.messageId == idMessage })
  776. if indexChat != nil {
  777. if dataMessage.getBody(key: CoreMessage_TMessageKey.DELETE_MESSAGE_FLAG) == "1" {
  778. chats[indexChat!].lock = "1"
  779. }
  780. if segment.selectedSegmentIndex == 0 {
  781. tableView.beginUpdates()
  782. tableView.reloadRows(at: [IndexPath(row: indexChat!, section: 0)], with: .none)
  783. tableView.endUpdates()
  784. } else {
  785. tableView.reloadData()
  786. }
  787. }
  788. }
  789. }
  790. }
  791. @objc func segmentChanged(sender: Any) {
  792. switch segment.selectedSegmentIndex {
  793. case 1:
  794. Utils.inTabChats = false
  795. // searchController.searchBar.placeholder = "Search groups name".localized()
  796. // searchController.searchBar.searchTextField.attributedPlaceholder = NSAttributedString(string: "Search groups name".localized(), attributes: [NSAttributedString.Key.foregroundColor: UIColor.gray, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 16)])
  797. default:
  798. Utils.inTabChats = true
  799. // searchController.searchBar.placeholder = "Search chats & messages".localized()
  800. // searchController.searchBar.searchTextField.attributedPlaceholder = NSAttributedString(string: "Search chats & messages".localized(), attributes: [NSAttributedString.Key.foregroundColor: UIColor.gray, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 16)])
  801. }
  802. // filterContentForSearchText(searchController.searchBar.text!)
  803. filterContentForSearchText(self.textViewSearch.text ?? "")
  804. }
  805. // MARK: - Data source
  806. func getData() {
  807. getChats {
  808. self.getGroups { g1 in
  809. self.groupMap.removeAll()
  810. self.groups = g1
  811. self.groups.sort { (a, b) -> Bool in
  812. if Int(a.official) == 1 {
  813. return true
  814. } else if Int(b.official) == 1 {
  815. return false
  816. } else {
  817. return Int(a.official) ?? 0 > Int(b.official) ?? 0
  818. }
  819. }
  820. DispatchQueue.main.async {
  821. self.tableView.reloadData()
  822. }
  823. }
  824. }
  825. }
  826. func getChats(completion: @escaping ()->()) {
  827. DispatchQueue.global().async {
  828. self.chats = Chat.getData()
  829. completion()
  830. }
  831. }
  832. private func getGroupRecursive(fmdb: FMDatabase, id: String = "", parent: String = "") -> [Group] {
  833. var data: [Group] = []
  834. 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, g.chat_modifier from GROUPZ g where "
  835. if id.isEmpty {
  836. query += "g.parent = '\(parent)'"
  837. } else {
  838. query += "g.group_id = '\(id)'"
  839. }
  840. query += "order by 12 asc, 13 asc, 2 asc"
  841. if let cursor = Database.shared.getRecords(fmdb: fmdb, query: query) {
  842. while cursor.next() {
  843. let group = Group(
  844. id: cursor.string(forColumnIndex: 0) ?? "",
  845. name: cursor.string(forColumnIndex: 1) ?? "",
  846. profile: cursor.string(forColumnIndex: 2) ?? "",
  847. quote: cursor.string(forColumnIndex: 3) ?? "",
  848. by: cursor.string(forColumnIndex: 4) ?? "",
  849. date: cursor.string(forColumnIndex: 5) ?? "",
  850. parent: cursor.string(forColumnIndex: 6) ?? "",
  851. chatId: "",
  852. groupType: cursor.string(forColumnIndex: 7) ?? "",
  853. isOpen: cursor.string(forColumnIndex: 8) ?? "",
  854. official: cursor.string(forColumnIndex: 9) ?? "",
  855. isEducation: cursor.string(forColumnIndex: 10) ?? "",
  856. level: cursor.string(forColumnIndex: 11) ?? "")
  857. if group.chatId.isEmpty {
  858. 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")
  859. group.childs.append(lounge)
  860. }
  861. if let topicCursor = Database.shared.getRecords(fmdb: fmdb, query: "select chat_id, title, thumb from DISCUSSION_FORUM where group_id = '\(group.id)'") {
  862. while topicCursor.next() {
  863. let topic = Group(id: group.id,
  864. name: topicCursor.string(forColumnIndex: 1) ?? "",
  865. profile: topicCursor.string(forColumnIndex: 2) ?? "",
  866. quote: group.quote,
  867. by: group.by,
  868. date: group.date,
  869. parent: group.id,
  870. chatId: topicCursor.string(forColumnIndex: 0) ?? "",
  871. groupType: group.groupType,
  872. isOpen: group.isOpen,
  873. official: group.official,
  874. isEducation: group.isEducation,
  875. level: group.level != "-1" ? group.level : "2")
  876. group.childs.append(topic)
  877. }
  878. topicCursor.close()
  879. }
  880. if !group.id.isEmpty {
  881. // if group.official == "1" {
  882. // let idMe = User.getMyPin() as String?
  883. // if let cursorUser = Database.shared.getRecords(fmdb: fmdb, query: "SELECT user_type FROM BUDDY where f_pin='\(idMe!)'"), cursorUser.next() {
  884. // group.childs.append(contentsOf: getGroupRecursive(fmdb: fmdb, parent: group.id))
  885. // cursorUser.close()
  886. // }
  887. // } else if group.official != "1"{
  888. // group.childs.append(contentsOf: getGroupRecursive(fmdb: fmdb, parent: group.id))
  889. // }
  890. group.childs.append(contentsOf: getGroupRecursive(fmdb: fmdb, parent: group.id))
  891. // group.childs = group.childs.sorted(by: { $0.name < $1.name })
  892. // let dataLounge = group.childs.filter({$0.name == "Lounge".localized()})
  893. // group.childs = group.childs.filter({ $0.name != "Lounge".localized() })
  894. // group.childs.insert(contentsOf: dataLounge, at: 0)
  895. }
  896. data.append(group)
  897. }
  898. cursor.close()
  899. }
  900. return data
  901. }
  902. private func getOpenGroups(listGroups: [Group], completion: @escaping ([Group]) -> ()) {
  903. if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getOpenGroups(p_account: "1,2,3,6,5,7", offset: "0", search: "")) {
  904. var dataGroups: [Group] = []
  905. if (response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99") == "00") {
  906. let data = response.getBody(key: CoreMessage_TMessageKey.DATA)
  907. if let json = try! JSONSerialization.jsonObject(with: data.data(using: String.Encoding.utf8)!, options: []) as? [[String: Any?]] {
  908. for dataJson in json {
  909. let group = Group(
  910. id: dataJson[CoreMessage_TMessageKey.GROUP_ID] as? String ?? "",
  911. name: dataJson[CoreMessage_TMessageKey.GROUP_NAME] as? String ?? "",
  912. profile: dataJson[CoreMessage_TMessageKey.THUMB_ID] as? String ?? "",
  913. quote: dataJson[CoreMessage_TMessageKey.QUOTE] as? String ?? "",
  914. by: dataJson[CoreMessage_TMessageKey.BLOCK] as? String ?? "",
  915. date: "",
  916. parent: "",
  917. chatId: "",
  918. groupType: "NOTJOINED",
  919. isOpen: dataJson[CoreMessage_TMessageKey.IS_OPEN] as? String ?? "",
  920. official: "0",
  921. isEducation: "")
  922. dataGroups.append(group)
  923. }
  924. }
  925. }
  926. completion(dataGroups)
  927. }
  928. }
  929. private func getGroups(id: String = "", parent: String = "", completion: @escaping ([Group]) -> ()) {
  930. DispatchQueue.global().async {
  931. Database.shared.database?.inTransaction({ fmdb, rollback in
  932. completion(self.getGroupRecursive(fmdb: fmdb, id: id, parent: parent))
  933. })
  934. }
  935. }
  936. private func pullBuddy() {
  937. if let me = User.getMyPin() {
  938. DispatchQueue.global().async {
  939. let _ = Nexilis.write(message: CoreMessage_TMessageBank.getBatchBuddiesInfos(p_f_pin: me, last_update: 0))
  940. }
  941. }
  942. }
  943. private func joinOpenGroup(groupId: String, flagMember: String = "0", completion: @escaping (Bool) -> ()) {
  944. DispatchQueue.global().async {
  945. var result: Bool = false
  946. let idMe = User.getMyPin() as String?
  947. if let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getAddGroupMember(p_group_id: groupId, p_member_pin: idMe!, p_position: "0")), response.isOk() {
  948. result = true
  949. }
  950. completion(result)
  951. }
  952. }
  953. @IBOutlet weak var backgroundImage: UIImageView!
  954. /*
  955. // MARK: - Navigation
  956. // In a storyboard-based application, you will often want to do a little preparation before navigation
  957. override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
  958. // Get the new view controller using segue.destination.
  959. // Pass the selected object to the new view controller.
  960. }
  961. */
  962. }
  963. // MARK: - Table view data source
  964. extension SecondTabViewController: UITableViewDelegate, UITableViewDataSource {
  965. func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
  966. if noData {
  967. return
  968. }
  969. tableView.deselectRow(at: indexPath, animated: true)
  970. switch segment.selectedSegmentIndex {
  971. case 0:
  972. let data: Chat
  973. if isFiltering {
  974. data = fillteredData[indexPath.row] as! Chat
  975. } else {
  976. data = chats[indexPath.row]
  977. }
  978. if let chooser = isChooser {
  979. if data.pin == "-999"{
  980. return
  981. }
  982. chooser(data.messageScope, data.pin)
  983. dismiss(animated: true, completion: nil)
  984. return
  985. }
  986. if data.messageScope == "3" {
  987. let editorPersonalVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorPersonalVC") as! EditorPersonal
  988. editorPersonalVC.hidesBottomBarWhenPushed = true
  989. editorPersonalVC.unique_l_pin = data.pin
  990. navigationController?.show(editorPersonalVC, sender: nil)
  991. } else {
  992. let editorGroupVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorGroupVC") as! EditorGroup
  993. editorGroupVC.hidesBottomBarWhenPushed = true
  994. editorGroupVC.unique_l_pin = data.pin
  995. navigationController?.show(editorGroupVC, sender: nil)
  996. }
  997. case 1:
  998. expandCollapseGroup(tableView: tableView, indexPath: indexPath)
  999. default:
  1000. expandCollapseGroup(tableView: tableView, indexPath: indexPath)
  1001. }
  1002. }
  1003. func expandCollapseGroup(tableView: UITableView, indexPath: IndexPath) {
  1004. let group: Group
  1005. if isFiltering {
  1006. if indexPath.row == 0 {
  1007. group = fillteredData[indexPath.section] as! Group
  1008. } else {
  1009. if (fillteredData[indexPath.section] as! Group).childs.count > 0 {
  1010. group = (fillteredData[indexPath.section] as! Group).childs[indexPath.row - 1]
  1011. } else {
  1012. return
  1013. }
  1014. }
  1015. } else {
  1016. if indexPath.row == 0 {
  1017. group = groups[indexPath.section]
  1018. } else {
  1019. group = groups[indexPath.section].childs[indexPath.row - 1]
  1020. }
  1021. }
  1022. if (checkOverrideAction(groupHolder: group)) {
  1023. return;
  1024. }
  1025. group.isSelected = !group.isSelected
  1026. if !group.isSelected{
  1027. var sects = 0
  1028. var sect = indexPath.section
  1029. var id = group.id
  1030. if let _ = groupMap[id] {
  1031. var loooop = true
  1032. repeat {
  1033. let c = sect + 1
  1034. if isFiltering {
  1035. if let o = self.fillteredData[c] as? Group {
  1036. if o.parent == id {
  1037. sects = sects + 1
  1038. sect = c
  1039. id = o.id
  1040. (self.fillteredData[c] as! Group).isSelected = false
  1041. self.groupMap.removeValue(forKey: (self.fillteredData[c] as! Group).id)
  1042. }
  1043. else {
  1044. loooop = false
  1045. }
  1046. }
  1047. }
  1048. else {
  1049. if c < self.groups.count && self.groups[c].parent == id {
  1050. sects = sects + 1
  1051. sect = c
  1052. id = self.groups[c].id
  1053. self.groups[c].isSelected = false
  1054. self.groupMap.removeValue(forKey: self.groups[c].id)
  1055. }
  1056. else {
  1057. loooop = false
  1058. }
  1059. }
  1060. } while(loooop)
  1061. }
  1062. for i in stride(from: sects, to: 0, by: -1){
  1063. if isFiltering {
  1064. self.fillteredData.remove(at: indexPath.section + i)
  1065. }
  1066. else {
  1067. self.groups.remove(at: indexPath.section + i)
  1068. }
  1069. }
  1070. groupMap.removeValue(forKey: group.id)
  1071. }
  1072. if group.groupType == "NOTJOINED" {
  1073. let alert = LibAlertController(title: "Do you want to join this group?".localized(), message: "Groups : \(group.name)\nMembers: \(group.by)".localized(), preferredStyle: .alert)
  1074. alert.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel, handler: nil))
  1075. alert.addAction(UIAlertAction(title: "Join".localized(), style: .default, handler: {(_) in
  1076. self.joinOpenGroup(groupId: group.id, completion: { result in
  1077. if result {
  1078. DispatchQueue.main.async {
  1079. self.groupMap.removeAll()
  1080. let editorGroupVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorGroupVC") as! EditorGroup
  1081. editorGroupVC.hidesBottomBarWhenPushed = true
  1082. editorGroupVC.unique_l_pin = group.id
  1083. self.navigationController?.show(editorGroupVC, sender: nil)
  1084. }
  1085. }
  1086. })
  1087. }))
  1088. self.present(alert, animated: true, completion: nil)
  1089. return
  1090. }
  1091. if group.childs.count == 0 {
  1092. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  1093. let idMe = User.getMyPin() as String?
  1094. 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() {
  1095. let groupId = group.chatId.isEmpty ? group.id : group.chatId
  1096. if let chooser = isChooser {
  1097. chooser("4", groupId)
  1098. dismiss(animated: true, completion: nil)
  1099. return
  1100. }
  1101. self.groupMap.removeAll()
  1102. let editorGroupVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorGroupVC") as! EditorGroup
  1103. editorGroupVC.hidesBottomBarWhenPushed = true
  1104. editorGroupVC.unique_l_pin = groupId
  1105. navigationController?.show(editorGroupVC, sender: nil)
  1106. cursorMember.close()
  1107. } else {
  1108. var viewController = UIApplication.shared.windows.first!.rootViewController
  1109. if !(viewController is ViewController) {
  1110. viewController = self.parent
  1111. }
  1112. if let viewController = viewController as? ViewController {
  1113. viewController.view.makeToast("You are not a member of this group".localized(), duration: 3)
  1114. }
  1115. }
  1116. })
  1117. } else {
  1118. if indexPath.row == 0 {
  1119. tableView.reloadData()
  1120. } else {
  1121. getGroups(id: group.id) { g in
  1122. DispatchQueue.main.async {
  1123. //print("index path section: \(indexPath.section)")
  1124. //print("index path row: \(indexPath.row)")
  1125. //print("index path item: \(indexPath.item)")
  1126. if self.isFiltering {
  1127. // self.fillteredData.remove(at: indexPath.section)
  1128. if self.fillteredData[indexPath.section] is Group {
  1129. self.groupMap[(self.fillteredData[indexPath.section] as! Group).id] = 1
  1130. self.fillteredData.insert(contentsOf: g, at: indexPath.section + 1)
  1131. }
  1132. } else {
  1133. // self.groups.remove(at: indexPath.section)
  1134. self.groupMap[self.groups[indexPath.section].id] = 1
  1135. self.groups.insert(contentsOf: g, at: indexPath.section + 1)
  1136. }
  1137. //print("groupMap: \(self.groupMap)")
  1138. tableView.reloadData()
  1139. self.expandCollapseGroup(tableView: tableView, indexPath: IndexPath(row: 0, section: indexPath.section + 1))
  1140. }
  1141. }
  1142. }
  1143. }
  1144. }
  1145. func checkOverrideAction(groupHolder: Group) -> Bool {
  1146. if groupHolder.isLounge {
  1147. return false
  1148. }
  1149. let groupId = groupHolder.chatId.isEmpty ? groupHolder.id : groupHolder.chatId
  1150. switch (groupId){
  1151. case "18d1c6cffb70215af7b49" //bpkh konsultasi
  1152. , "18d1c6e37a20215af7b49"
  1153. , "18d1c6f852d0215af7b49"
  1154. , "18d1c6ff83a0215af7b49"
  1155. , "18d1c705e970215af7b49"
  1156. , "18d30db3bde0230d00c15" //ina konsultasi bot
  1157. , "18d30e64ce30230d00c15"
  1158. , "18d30e9b6d80230d00c15"
  1159. , "18d30ee00610230d00c15"
  1160. , "18d30f02f850230d00c15":
  1161. APIS.openSmartChatbot();
  1162. return true;
  1163. case "18d30daa4540230d00c15" //ina cc
  1164. , "18d30e59a950230d00c15"
  1165. , "18d30e9292b0230d00c15"
  1166. , "18d30ed8e250230d00c15"
  1167. , "18d30efa66c0230d00c15"
  1168. , "18d35b220540215af7b49" //bpkhcc
  1169. , "18d35b2f5ee0215af7b49"
  1170. , "18d35b356530215af7b49"
  1171. , "18d35b411510215af7b49"
  1172. , "18d35b46ae90215af7b49":
  1173. APIS.openContactCenter();
  1174. return true;
  1175. case "18d1c6d9f330215af7b49": //bpkh haji
  1176. Nexilis.openUrl(url: Utils.decrypt(str: "6]tov!l_opgn=hgz?ykmgv?yoro3kt?uo>yoro3kt??@yvzzn"))
  1177. return true;
  1178. case "18d1c6eefd40215af7b49": //bpkh bpjs
  1179. Nexilis.openUrl(url: Utils.decrypt(str: "1>ojq`g@tkqc.cbu:tfhbq:tjmjyfo:pj/tjmjyfo::;tquui"))
  1180. return true;
  1181. case "18d30e711c20230d00c15": //ina bpjs
  1182. Nexilis.openUrl(url: Utils.decrypt(str: "6]tov!l_ypvh=hgz?ykmgv?yoro3kt?sui>ykrgyomojrkyz??@yvzzn"))
  1183. return true;
  1184. case "18d30e47ae60230d00c15": //ina KTP, KK, SKL
  1185. Nexilis.openUrl(url: Utils.decrypt(str: "1>ojq`g@qul.cbu:tfhbq:tjmjyfo:npd/tfmbtjhjemftu::;tquui"))
  1186. return true;
  1187. case "18d30eb2e910230d00c15": //SIM, SKKB, SKBN
  1188. Nexilis.openUrl(url: Utils.decrypt(str: "4[rmt}j]qmw;fex=wiket=wmpm1ir=qsg<wipewmkmhpiwx==>wtxxl"))
  1189. return true;
  1190. case "18da1c0200f0215af7b49": //BPKH index BMI
  1191. Nexilis.openUrl(url: Utils.decrypt(str: "4[rmt}j]mqf}1ihrm=mqf=wmpm1ir=sm<wmpm1ir==>wtxxl"))
  1192. return true;
  1193. default:
  1194. break;
  1195. }
  1196. return false
  1197. }
  1198. func numberOfSections(in tableView: UITableView) -> Int {
  1199. if isFiltering {
  1200. if segment.selectedSegmentIndex == 1 {
  1201. return fillteredData.count
  1202. }
  1203. return 1
  1204. } else {
  1205. if segment.selectedSegmentIndex == 1 {
  1206. return groups.count
  1207. }
  1208. return 1
  1209. }
  1210. }
  1211. func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  1212. var value = 0
  1213. if isFiltering {
  1214. if segment.selectedSegmentIndex == 1, let groups = fillteredData as? [Group] {
  1215. let group = groups[section]
  1216. if group.isSelected {
  1217. if let _ = groupMap[group.id] {
  1218. value = 1
  1219. }
  1220. else {
  1221. value = group.childs.count + 1
  1222. }
  1223. } else {
  1224. value = 1
  1225. }
  1226. } else {
  1227. value = fillteredData.count
  1228. }
  1229. if value == 0 {
  1230. value = 1
  1231. tableView.separatorStyle = .none
  1232. } else {
  1233. tableView.separatorStyle = .singleLine
  1234. }
  1235. return value
  1236. }
  1237. switch segment.selectedSegmentIndex {
  1238. case 0:
  1239. value = chats.count
  1240. case 1:
  1241. let group = groups[section]
  1242. if group.isSelected {
  1243. if let _ = groupMap[group.id] {
  1244. value = 1
  1245. }
  1246. else {
  1247. value = group.childs.count + 1
  1248. }
  1249. } else {
  1250. value = 1
  1251. }
  1252. default:
  1253. value = chats.count
  1254. }
  1255. if value == 0 {
  1256. noData = true
  1257. value = 1
  1258. tableView.separatorStyle = .none
  1259. } else {
  1260. noData = false
  1261. tableView.separatorStyle = .singleLine
  1262. }
  1263. return value
  1264. }
  1265. func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  1266. var cell: UITableViewCell!
  1267. switch segment.selectedSegmentIndex {
  1268. case 0:
  1269. cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifierChat", for: indexPath)
  1270. let content = cell.contentView
  1271. if content.subviews.count > 0 {
  1272. content.subviews.forEach { $0.removeFromSuperview() }
  1273. }
  1274. if noData || (isFiltering && fillteredData.count == 0) {
  1275. let labelNochat = UILabel()
  1276. labelNochat.text = isFiltering ? "No Result".localized() : "There are no conversations".localized()
  1277. labelNochat.font = .systemFont(ofSize: 13)
  1278. labelNochat.textColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .black
  1279. content.addSubview(labelNochat)
  1280. labelNochat.anchor(centerX: content.centerXAnchor, centerY: content.centerYAnchor)
  1281. cell.backgroundColor = .clear
  1282. cell.selectionStyle = .none
  1283. return cell
  1284. }
  1285. let data: Chat
  1286. if isFiltering {
  1287. data = fillteredData[indexPath.row] as! Chat
  1288. } else {
  1289. if chats.count == 0 {
  1290. let labelNochat = UILabel()
  1291. labelNochat.text = "There are no conversations".localized()
  1292. labelNochat.font = .systemFont(ofSize: 13)
  1293. labelNochat.textColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .black
  1294. content.addSubview(labelNochat)
  1295. labelNochat.anchor(centerX: content.centerXAnchor, centerY: content.centerYAnchor)
  1296. cell.backgroundColor = .clear
  1297. cell.selectionStyle = .none
  1298. return cell
  1299. }
  1300. data = chats[indexPath.row]
  1301. }
  1302. if selectedTag != UNREAD_TAG && selectedTag != 0 {
  1303. let title = UILabel()
  1304. let subtitle = UILabel()
  1305. title.textColor = .black
  1306. subtitle.textColor = .gray
  1307. title.font = .systemFont(ofSize: 16, weight: .medium)
  1308. subtitle.font = .systemFont(ofSize: 14)
  1309. content.addSubview(title)
  1310. content.addSubview(subtitle)
  1311. title.anchor(top: content.topAnchor, left: content.leftAnchor, paddingTop: 10, paddingLeft: 20)
  1312. subtitle.anchor(top: title.bottomAnchor, left: content.leftAnchor, right: content.rightAnchor, paddingLeft: 20, paddingRight: 20)
  1313. subtitle.numberOfLines = 2
  1314. title.text = data.name
  1315. let imageArrowRight = UIImageView(image: UIImage(systemName: "chevron.right"))
  1316. content.addSubview(imageArrowRight)
  1317. imageArrowRight.tintColor = .gray
  1318. imageArrowRight.anchor(top: content.topAnchor, right: content.rightAnchor, paddingTop: 10, paddingRight: 20)
  1319. let timeView = UILabel()
  1320. content.addSubview(timeView)
  1321. timeView.anchor(top: content.topAnchor, right: imageArrowRight.leftAnchor, paddingTop: 10, paddingRight: 20)
  1322. timeView.textColor = .gray
  1323. timeView.font = UIFont.systemFont(ofSize: 16)
  1324. let date = Date(milliseconds: Int64(data.serverDate)!)
  1325. let calendar = Calendar.current
  1326. if (calendar.isDateInToday(date)) {
  1327. let formatter = DateFormatter()
  1328. formatter.dateFormat = "HH:mm"
  1329. formatter.locale = NSLocale(localeIdentifier: "id") as Locale?
  1330. timeView.text = formatter.string(from: date as Date)
  1331. } else {
  1332. let startOfNow = calendar.startOfDay(for: Date())
  1333. let startOfTimeStamp = calendar.startOfDay(for: date)
  1334. let components = calendar.dateComponents([.day], from: startOfNow, to: startOfTimeStamp)
  1335. let day = -(components.day!)
  1336. if day == 1 {
  1337. timeView.text = "Yesterday".localized()
  1338. } else {
  1339. if day < 7 {
  1340. let formatter = DateFormatter()
  1341. formatter.dateFormat = "EEEE"
  1342. let lang: String = SecureUserDefaults.shared.value(forKey: "i18n_language") ?? "en"
  1343. if lang == "id" {
  1344. formatter.locale = NSLocale(localeIdentifier: "id") as Locale?
  1345. }
  1346. timeView.text = formatter.string(from: date)
  1347. } else {
  1348. let formatter = DateFormatter()
  1349. formatter.dateFormat = "M/dd/yy"
  1350. formatter.locale = NSLocale(localeIdentifier: "id") as Locale?
  1351. let stringFormat = formatter.string(from: date as Date)
  1352. timeView.text = stringFormat
  1353. }
  1354. }
  1355. }
  1356. cell.separatorInset = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 0)
  1357. let container = UIView()
  1358. content.addSubview(container)
  1359. container.anchor(top: subtitle.bottomAnchor, left: content.leftAnchor, right: content.rightAnchor, paddingTop: 5, paddingLeft: 20, paddingRight: 20, height: selectedTag == LINKS_TAG ? 75 : 60)
  1360. container.backgroundColor = .lightGray.withAlphaComponent(0.3)
  1361. container.layer.cornerRadius = 15
  1362. container.clipsToBounds = true
  1363. container.isUserInteractionEnabled = true
  1364. if selectedTag == DOCUMENTS_TAG {
  1365. subtitle.text = "📄 " + "Document".localized()
  1366. let imageFile = UIImageView(image: UIImage(systemName: "doc.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 45)))
  1367. container.addSubview(imageFile)
  1368. imageFile.tintColor = .black
  1369. imageFile.anchor(top: container.topAnchor, left: container.leftAnchor, bottom: container.bottomAnchor, paddingTop: 5, paddingLeft: 5, paddingBottom: 5, width: 45)
  1370. let nameFile = UILabel()
  1371. container.addSubview(nameFile)
  1372. nameFile.font = .systemFont(ofSize: 12, weight: .medium)
  1373. nameFile.textColor = .black
  1374. nameFile.numberOfLines = 2
  1375. nameFile.anchor(top: container.topAnchor, left: imageFile.rightAnchor, right: container.rightAnchor, paddingTop: 5, paddingLeft: 10, paddingRight: 5)
  1376. nameFile.text = data.messageText.components(separatedBy: "|")[0]
  1377. let fileSub = UILabel()
  1378. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  1379. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  1380. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  1381. let arrExtFile = (data.messageText.components(separatedBy: "|")[0]).split(separator: ".")
  1382. let finalExtFile = arrExtFile[arrExtFile.count - 1]
  1383. if let dirPath = paths.first {
  1384. let fileURL = URL(fileURLWithPath: dirPath).appendingPathComponent(data.file)
  1385. if FileManager.default.fileExists(atPath: fileURL.path) {
  1386. if let dataFile = try? Data(contentsOf: fileURL) {
  1387. var sizeOfFile = Int(dataFile.count / 1000000)
  1388. if (sizeOfFile < 1) {
  1389. sizeOfFile = Int(dataFile.count / 1000)
  1390. if (finalExtFile.count > 4) {
  1391. fileSub.text = "\(sizeOfFile) kB \u{2022} TXT"
  1392. }else {
  1393. fileSub.text = "\(sizeOfFile) kB \u{2022} \(finalExtFile.uppercased())"
  1394. }
  1395. } else {
  1396. if (finalExtFile.count > 4) {
  1397. fileSub.text = "\(sizeOfFile) MB \u{2022} TXT"
  1398. }else {
  1399. fileSub.text = "\(sizeOfFile) MB \u{2022} \(finalExtFile.uppercased())"
  1400. }
  1401. }
  1402. } else {
  1403. fileSub.text = ""
  1404. }
  1405. }
  1406. else if FileEncryption.shared.isSecureExists(filename: data.file) {
  1407. if let dataFile = try? FileEncryption.shared.readSecure(filename: data.file) {
  1408. var sizeOfFile = Int(dataFile.count / 1000000)
  1409. if (sizeOfFile < 1) {
  1410. sizeOfFile = Int(dataFile.count / 1000)
  1411. if (finalExtFile.count > 4) {
  1412. fileSub.text = "\(sizeOfFile) kB \u{2022} TXT"
  1413. }else {
  1414. fileSub.text = "\(sizeOfFile) kB \u{2022} \(finalExtFile.uppercased())"
  1415. }
  1416. } else {
  1417. if (finalExtFile.count > 4) {
  1418. fileSub.text = "\(sizeOfFile) MB \u{2022} TXT"
  1419. }else {
  1420. fileSub.text = "\(sizeOfFile) MB \u{2022} \(finalExtFile.uppercased())"
  1421. }
  1422. }
  1423. } else {
  1424. fileSub.text = ""
  1425. }
  1426. }
  1427. container.addSubview(fileSub)
  1428. fileSub.anchor(top: nameFile.bottomAnchor, left: imageFile.rightAnchor, bottom: container.bottomAnchor, paddingLeft: 10, paddingBottom: 5)
  1429. fileSub.font = .systemFont(ofSize: 10)
  1430. fileSub.textColor = .gray
  1431. let objectTap = ObjectGesture(target: self, action: #selector(onContSearch(_:)))
  1432. objectTap.file_id = data.file
  1433. container.addGestureRecognizer(objectTap)
  1434. }
  1435. } else if selectedTag == LINKS_TAG {
  1436. var text = ""
  1437. var txtData = data.messageText
  1438. if txtData.contains("■"){
  1439. txtData = txtData.components(separatedBy: "■")[0]
  1440. txtData = txtData.trimmingCharacters(in: .whitespacesAndNewlines)
  1441. }
  1442. let listTextSplitBreak = txtData.components(separatedBy: "\n")
  1443. let indexFirstLinkSplitBreak = listTextSplitBreak.firstIndex(where: { $0.contains("www.") || $0.contains("http://") || $0.contains("https://") })
  1444. if indexFirstLinkSplitBreak != nil {
  1445. let listTextSplitSpace = listTextSplitBreak[indexFirstLinkSplitBreak!].components(separatedBy: " ")
  1446. let indexFirstLinkSplitSpace = listTextSplitSpace.firstIndex(where: { ($0.starts(with: "www.") && $0.components(separatedBy: ".").count > 2) || ($0.starts(with: "http://") && $0.components(separatedBy: ".").count > 1) || ($0.starts(with: "https://") && $0.components(separatedBy: ".").count > 1) })
  1447. if indexFirstLinkSplitSpace != nil {
  1448. text = listTextSplitSpace[indexFirstLinkSplitSpace!]
  1449. }
  1450. }
  1451. var dataURL = ""
  1452. subtitle.text = txtData
  1453. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  1454. do {
  1455. if let cursor = Database.shared.getRecords(fmdb: fmdb, query: "select data_link from LINK_PREVIEW where link='\(text)'"), cursor.next() {
  1456. if let data = cursor.string(forColumnIndex: 0) {
  1457. dataURL = data
  1458. }
  1459. cursor.close()
  1460. }
  1461. } catch {
  1462. rollback.pointee = true
  1463. print("Access database error: \(error.localizedDescription)")
  1464. }
  1465. })
  1466. var title = ""
  1467. var description = ""
  1468. var imageUrl: String?
  1469. var link = text
  1470. let objectTap = ObjectGesture(target: self, action: #selector(onContSearch(_:)))
  1471. objectTap.message_id = link
  1472. container.addGestureRecognizer(objectTap)
  1473. let imagePreview = UIImageView()
  1474. container.addSubview(imagePreview)
  1475. imagePreview.translatesAutoresizingMaskIntoConstraints = false
  1476. imagePreview.leadingAnchor.constraint(equalTo: container.leadingAnchor).isActive = true
  1477. imagePreview.bottomAnchor.constraint(equalTo: container.bottomAnchor).isActive = true
  1478. imagePreview.topAnchor.constraint(equalTo: container.topAnchor).isActive = true
  1479. imagePreview.widthAnchor.constraint(equalToConstant: 80.0).isActive = true
  1480. imagePreview.image = UIImage(systemName: "link", withConfiguration: UIImage.SymbolConfiguration(pointSize: 45))
  1481. imagePreview.contentMode = .center
  1482. imagePreview.clipsToBounds = true
  1483. imagePreview.tintColor = .black
  1484. imagePreview.backgroundColor = .gray.withAlphaComponent(0.3)
  1485. let titlePreview = UILabel()
  1486. container.addSubview(titlePreview)
  1487. titlePreview.translatesAutoresizingMaskIntoConstraints = false
  1488. titlePreview.leadingAnchor.constraint(equalTo: imagePreview.trailingAnchor, constant: 5.0).isActive = true
  1489. titlePreview.topAnchor.constraint(equalTo: container.topAnchor, constant: 10.0).isActive = true
  1490. titlePreview.trailingAnchor.constraint(equalTo: container.trailingAnchor, constant: -5.0).isActive = true
  1491. titlePreview.text = title
  1492. titlePreview.font = UIFont.systemFont(ofSize: 14.0, weight: .bold)
  1493. titlePreview.textColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .black
  1494. let descPreview = UILabel()
  1495. container.addSubview(descPreview)
  1496. descPreview.translatesAutoresizingMaskIntoConstraints = false
  1497. descPreview.leadingAnchor.constraint(equalTo: imagePreview.trailingAnchor, constant: 5.0).isActive = true
  1498. descPreview.topAnchor.constraint(equalTo: titlePreview.bottomAnchor).isActive = true
  1499. descPreview.trailingAnchor.constraint(equalTo: container.trailingAnchor, constant: -5.0).isActive = true
  1500. descPreview.text = description
  1501. descPreview.font = UIFont.systemFont(ofSize: 12.0)
  1502. descPreview.textColor = .gray
  1503. descPreview.numberOfLines = 1
  1504. let linkPreview = UILabel()
  1505. container.addSubview(linkPreview)
  1506. linkPreview.translatesAutoresizingMaskIntoConstraints = false
  1507. linkPreview.leadingAnchor.constraint(equalTo: imagePreview.trailingAnchor, constant: 5.0).isActive = true
  1508. linkPreview.trailingAnchor.constraint(equalTo: container.trailingAnchor, constant: -5.0).isActive = true
  1509. linkPreview.font = UIFont.systemFont(ofSize: 10.0)
  1510. linkPreview.textColor = .gray
  1511. linkPreview.numberOfLines = 1
  1512. if !dataURL.isEmpty {
  1513. if let data = try! JSONSerialization.jsonObject(with: dataURL.data(using: String.Encoding.utf8)!, options: []) as? [String: Any] {
  1514. title = data["title"] as! String
  1515. description = data["description"] as! String
  1516. imageUrl = data["imageUrl"] as? String
  1517. link = data["link"] as! String
  1518. if imageUrl != nil {
  1519. imagePreview.loadImageAsync(with: imageUrl)
  1520. imagePreview.contentMode = .scaleToFill
  1521. imagePreview.clipsToBounds = true
  1522. }
  1523. titlePreview.text = title
  1524. descPreview.text = description
  1525. linkPreview.text = link
  1526. linkPreview.topAnchor.constraint(equalTo: descPreview.bottomAnchor, constant: 8.0).isActive = true
  1527. }
  1528. } else {
  1529. linkPreview.text = link
  1530. linkPreview.centerYAnchor.constraint(equalTo: container.centerYAnchor).isActive = true
  1531. }
  1532. } else if selectedTag == AUDIOS_TAG {
  1533. subtitle.text = "♫ " + "Audio".localized()
  1534. let imageAudio = UIImageView()
  1535. imageAudio.image = UIImage(systemName: "music.note", withConfiguration: UIImage.SymbolConfiguration(pointSize: 35))
  1536. container.addSubview(imageAudio)
  1537. imageAudio.anchor(left: container.leftAnchor, paddingLeft: 10, centerY: container.centerYAnchor)
  1538. imageAudio.tintColor = .black
  1539. let nameAudio = UILabel()
  1540. container.addSubview(nameAudio)
  1541. nameAudio.anchor(left: imageAudio.rightAnchor, right: container.rightAnchor, paddingLeft: 10, paddingRight: 10, centerY: container.centerYAnchor)
  1542. nameAudio.numberOfLines = 2
  1543. nameAudio.text = data.messageText.components(separatedBy: "|")[0]
  1544. nameAudio.font = .systemFont(ofSize: 16, weight: .medium)
  1545. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  1546. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  1547. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  1548. if let dirPath = paths.first {
  1549. let audioURL = URL(fileURLWithPath: dirPath).appendingPathComponent(data.audio)
  1550. if !FileManager.default.fileExists(atPath: audioURL.path) && !FileEncryption.shared.isSecureExists(filename: data.audio) {
  1551. Download().startHTTP(forKey: data.audio, isImage: false) { (name, progress) in
  1552. guard progress == 100 else {
  1553. return
  1554. }
  1555. tableView.reloadRows(at: [indexPath], with: .none)
  1556. }
  1557. } else {
  1558. let objectTap = ObjectGesture(target: self, action: #selector(onContSearch(_:)))
  1559. objectTap.audio_id = data.audio
  1560. container.addGestureRecognizer(objectTap)
  1561. }
  1562. }
  1563. }
  1564. return cell
  1565. }
  1566. let imageView = UIImageView()
  1567. content.addSubview(imageView)
  1568. imageView.translatesAutoresizingMaskIntoConstraints = false
  1569. NSLayoutConstraint.activate([
  1570. imageView.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: 10.0),
  1571. imageView.topAnchor.constraint(equalTo: content.topAnchor, constant: 10.0),
  1572. imageView.bottomAnchor.constraint(equalTo: content.bottomAnchor, constant: -10.0),
  1573. imageView.widthAnchor.constraint(equalToConstant: 55.0),
  1574. imageView.heightAnchor.constraint(equalToConstant: 55.0)
  1575. ])
  1576. if data.profile.isEmpty && data.pin != "-999" {
  1577. if data.messageScope == "3" {
  1578. imageView.image = UIImage(named: "Profile---Purple", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
  1579. } else {
  1580. imageView.image = UIImage(named: "Conversation---Purple", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
  1581. }
  1582. } else {
  1583. 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
  1584. imageView.image = image
  1585. })
  1586. }
  1587. let titleView = UILabel()
  1588. content.addSubview(titleView)
  1589. titleView.translatesAutoresizingMaskIntoConstraints = false
  1590. NSLayoutConstraint.activate([
  1591. titleView.leadingAnchor.constraint(equalTo: imageView.trailingAnchor, constant: 10.0),
  1592. titleView.topAnchor.constraint(equalTo: content.topAnchor, constant: 10.0),
  1593. titleView.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -40.0),
  1594. ])
  1595. titleView.text = data.name
  1596. titleView.font = UIFont.systemFont(ofSize: 14, weight: .medium)
  1597. let messageView = UILabel()
  1598. content.addSubview(messageView)
  1599. messageView.translatesAutoresizingMaskIntoConstraints = false
  1600. NSLayoutConstraint.activate([
  1601. messageView.leadingAnchor.constraint(equalTo: imageView.trailingAnchor, constant: 10.0),
  1602. messageView.topAnchor.constraint(equalTo: titleView.bottomAnchor, constant: 5.0),
  1603. messageView.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -40.0),
  1604. ])
  1605. messageView.textColor = .gray
  1606. if data.messageText.contains("■") {
  1607. data.messageText = data.messageText.components(separatedBy: "■")[0]
  1608. data.messageText = data.messageText.trimmingCharacters(in: .whitespacesAndNewlines)
  1609. }
  1610. let text = Utils.previewMessageText(chat: data)
  1611. let idMe = User.getMyPin() as String?
  1612. if let attributeText = text as? NSMutableAttributedString {
  1613. let stringMessage = NSMutableAttributedString(string: "")
  1614. if data.fpin == idMe {
  1615. if data.lock == "1" {
  1616. if data.messageScope == "4" {
  1617. stringMessage.append(NSAttributedString(string: "You".localized() + ": ", attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 12, weight: .medium)]))
  1618. }
  1619. stringMessage.append(("🚫 _"+"You were deleted this message".localized()+"_").richText())
  1620. } else {
  1621. let imageStatus = NSTextAttachment()
  1622. let status = getRealStatus(messageId: data.messageId)
  1623. if status == "0" {
  1624. imageStatus.image = UIImage(systemName: "xmark.circle")!.withTintColor(UIColor.red, renderingMode: .alwaysOriginal)
  1625. } else if status == "1" {
  1626. imageStatus.image = UIImage(systemName: "clock.arrow.circlepath")!.withTintColor(UIColor.lightGray, renderingMode: .alwaysOriginal)
  1627. } else if status == "2" {
  1628. imageStatus.image = UIImage(named: "checklist", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(UIColor.lightGray)
  1629. } else if (status == "3") {
  1630. imageStatus.image = UIImage(named: "double-checklist", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(UIColor.lightGray)
  1631. } else if (status == "8") {
  1632. imageStatus.image = UIImage(named: "message_status_ack", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withRenderingMode(.alwaysOriginal)
  1633. } else {
  1634. imageStatus.image = UIImage(named: "double-checklist", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(UIColor.systemBlue)
  1635. }
  1636. imageStatus.bounds = CGRect(x: 0, y: -2, width: 15, height: 15)
  1637. let imageStatusString = NSAttributedString(attachment: imageStatus)
  1638. stringMessage.append(imageStatusString)
  1639. stringMessage.append(NSAttributedString(string: " "))
  1640. if data.messageScope == "4" {
  1641. stringMessage.append(NSAttributedString(string: "You".localized() + ": ", attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 12, weight: .medium)]))
  1642. }
  1643. stringMessage.append(attributeText)
  1644. }
  1645. } else {
  1646. if data.messageScope == "4" {
  1647. stringMessage.append(NSAttributedString(string: User.getData(pin: data.fpin)!.firstName + ": ", attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 12, weight: .medium)]))
  1648. }
  1649. if data.lock == "1" {
  1650. stringMessage.append(("🚫 _"+"This message was deleted".localized()+"_").richText())
  1651. } else {
  1652. stringMessage.append(attributeText)
  1653. }
  1654. }
  1655. messageView.attributedText = stringMessage
  1656. }
  1657. messageView.numberOfLines = 2
  1658. let timeView = UILabel()
  1659. content.addSubview(timeView)
  1660. timeView.translatesAutoresizingMaskIntoConstraints = false
  1661. NSLayoutConstraint.activate([
  1662. timeView.topAnchor.constraint(equalTo: content.topAnchor, constant: 10.0),
  1663. timeView.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -20.0),
  1664. ])
  1665. timeView.textColor = .gray
  1666. timeView.font = UIFont.systemFont(ofSize: 14)
  1667. let date = Date(milliseconds: Int64(data.serverDate)!)
  1668. let calendar = Calendar.current
  1669. if (calendar.isDateInToday(date)) {
  1670. let formatter = DateFormatter()
  1671. formatter.dateFormat = "HH:mm"
  1672. formatter.locale = NSLocale(localeIdentifier: "id") as Locale?
  1673. timeView.text = formatter.string(from: date as Date)
  1674. } else {
  1675. let startOfNow = calendar.startOfDay(for: Date())
  1676. let startOfTimeStamp = calendar.startOfDay(for: date)
  1677. let components = calendar.dateComponents([.day], from: startOfNow, to: startOfTimeStamp)
  1678. let day = -(components.day!)
  1679. if day == 1 {
  1680. timeView.text = "Yesterday".localized()
  1681. } else {
  1682. if day < 7 {
  1683. let formatter = DateFormatter()
  1684. formatter.dateFormat = "EEEE"
  1685. let lang: String = SecureUserDefaults.shared.value(forKey: "i18n_language") ?? "en"
  1686. if lang == "id" {
  1687. formatter.locale = NSLocale(localeIdentifier: "id") as Locale?
  1688. }
  1689. timeView.text = formatter.string(from: date)
  1690. } else {
  1691. let formatter = DateFormatter()
  1692. formatter.dateFormat = "M/dd/yy"
  1693. formatter.locale = NSLocale(localeIdentifier: "id") as Locale?
  1694. let stringFormat = formatter.string(from: date as Date)
  1695. timeView.text = stringFormat
  1696. }
  1697. }
  1698. }
  1699. if data.counter != "0" {
  1700. timeView.textColor = .systemRed
  1701. let viewCounter = UIView()
  1702. content.addSubview(viewCounter)
  1703. viewCounter.translatesAutoresizingMaskIntoConstraints = false
  1704. NSLayoutConstraint.activate([
  1705. viewCounter.topAnchor.constraint(equalTo: timeView.bottomAnchor, constant: 5.0),
  1706. viewCounter.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -20),
  1707. viewCounter.widthAnchor.constraint(greaterThanOrEqualToConstant: 20),
  1708. viewCounter.heightAnchor.constraint(equalToConstant: 20)
  1709. ])
  1710. viewCounter.backgroundColor = .systemRed
  1711. viewCounter.layer.cornerRadius = 10
  1712. viewCounter.clipsToBounds = true
  1713. viewCounter.layer.borderWidth = 0.5
  1714. viewCounter.layer.borderColor = UIColor.secondaryColor.cgColor
  1715. let labelCounter = UILabel()
  1716. viewCounter.addSubview(labelCounter)
  1717. labelCounter.translatesAutoresizingMaskIntoConstraints = false
  1718. NSLayoutConstraint.activate([
  1719. labelCounter.centerYAnchor.constraint(equalTo: viewCounter.centerYAnchor),
  1720. labelCounter.leadingAnchor.constraint(equalTo: viewCounter.leadingAnchor, constant: 2),
  1721. labelCounter.trailingAnchor.constraint(equalTo: viewCounter.trailingAnchor, constant: -2),
  1722. ])
  1723. labelCounter.font = UIFont.systemFont(ofSize: 11)
  1724. if Int(data.counter)! > 99 {
  1725. labelCounter.text = "99+"
  1726. } else {
  1727. labelCounter.text = data.counter
  1728. }
  1729. labelCounter.textColor = .secondaryColor
  1730. labelCounter.textAlignment = .center
  1731. }
  1732. case 1:
  1733. cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifierGroup", for: indexPath)
  1734. var content = cell.defaultContentConfiguration()
  1735. content.textProperties.font = UIFont.systemFont(ofSize: 14)
  1736. let group: Group
  1737. if isFiltering {
  1738. if indexPath.row == 0 {
  1739. group = fillteredData[indexPath.section] as! Group
  1740. } else {
  1741. if (fillteredData[indexPath.section] as! Group).childs.count > 0 {
  1742. group = (fillteredData[indexPath.section] as! Group).childs[indexPath.row - 1]
  1743. } else {
  1744. return cell
  1745. }
  1746. }
  1747. } else {
  1748. if indexPath.row == 0 {
  1749. if indexPath.section > (groups.count - 1) {
  1750. return cell
  1751. }
  1752. group = groups[indexPath.section]
  1753. } else {
  1754. group = groups[indexPath.section].childs[indexPath.row - 1]
  1755. }
  1756. }
  1757. if group.official == "1" && group.parent == "" {
  1758. 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, colorText: self.traitCollection.userInterfaceStyle == .dark ? .white : .black)
  1759. }
  1760. else if group.isOpen == "1" && group.parent == "" {
  1761. if self.traitCollection.userInterfaceStyle == .dark {
  1762. content.attributedText = self.set(image: UIImage(systemName: "globe")!.withTintColor(.white), with: " \(group.name)", size: 15, y: -4, colorText: self.traitCollection.userInterfaceStyle == .dark ? .white : .black)
  1763. } else {
  1764. content.attributedText = self.set(image: UIImage(systemName: "globe")!, with: " \(group.name)", size: 15, y: -4, colorText: self.traitCollection.userInterfaceStyle == .dark ? .white : .black)
  1765. }
  1766. } else if group.parent == "" {
  1767. if self.traitCollection.userInterfaceStyle == .dark {
  1768. content.attributedText = self.set(image: UIImage(systemName: "lock.fill")!.withTintColor(.white), with: " \(group.name)", size: 15, y: -4, colorText: self.traitCollection.userInterfaceStyle == .dark ? .white : .black)
  1769. } else {
  1770. content.attributedText = self.set(image: UIImage(systemName: "lock.fill")!, with: " \(group.name)", size: 15, y: -4, colorText: self.traitCollection.userInterfaceStyle == .dark ? .white : .black)
  1771. }
  1772. } else {
  1773. content.text = group.name
  1774. }
  1775. if group.childs.count > 0 {
  1776. let iconName = (group.isSelected) ? "chevron.up.circle" : "chevron.down.circle"
  1777. let imageView = UIImageView(image: UIImage(systemName: iconName))
  1778. imageView.tintColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .black
  1779. cell.accessoryView = imageView
  1780. }
  1781. else {
  1782. cell.accessoryView = nil
  1783. cell.accessoryType = .none
  1784. }
  1785. content.imageProperties.maximumSize = CGSize(width: 40, height: 40)
  1786. 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
  1787. content.image = image
  1788. }
  1789. cell.contentConfiguration = content
  1790. if !group.level.isEmpty {
  1791. if group.level != "-1" && Int(group.level)! < 7 {
  1792. cell.contentView.layoutMargins = .init(top: 0.0, left: CGFloat(25 * Int(group.level)!), bottom: 0.0, right: 0)
  1793. } else if Int(group.level)! > 6 {
  1794. cell.contentView.layoutMargins = .init(top: 0.0, left: CGFloat(25 * (Int(group.level)! - 6)), bottom: 0.0, right: 0)
  1795. }
  1796. }
  1797. default:
  1798. cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifierContact", for: indexPath)
  1799. var content = cell.defaultContentConfiguration()
  1800. content.text = ""
  1801. cell.contentConfiguration = content
  1802. }
  1803. cell.backgroundColor = .clear
  1804. cell.separatorInset = UIEdgeInsets(top: 0, left: 60.0, bottom: 0, right: 0)
  1805. return cell
  1806. }
  1807. @objc func onContSearch(_ sender: ObjectGesture) {
  1808. if selectedTag == PHOTOS_TAG {
  1809. } else if selectedTag == VIDEOS_TAG {
  1810. } else if selectedTag == DOCUMENTS_TAG {
  1811. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  1812. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  1813. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  1814. if let dirPath = paths.first {
  1815. let fileURL = URL(fileURLWithPath: dirPath).appendingPathComponent(sender.file_id)
  1816. if FileManager.default.fileExists(atPath: fileURL.path) {
  1817. self.previewItem = fileURL as NSURL
  1818. let previewController = QLPreviewController()
  1819. let rightBarButton = UIBarButtonItem()
  1820. previewController.navigationItem.rightBarButtonItem = rightBarButton
  1821. previewController.dataSource = self
  1822. previewController.modalPresentationStyle = .custom
  1823. self.present(previewController, animated: true)
  1824. } else if FileEncryption.shared.isSecureExists(filename: sender.file_id) {
  1825. do {
  1826. if let docData = try FileEncryption.shared.readSecure(filename: sender.file_id) {
  1827. let cachesDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
  1828. let tempPath = cachesDirectory.appendingPathComponent(sender.file_id)
  1829. try docData.write(to: tempPath)
  1830. self.previewItem = tempPath as NSURL
  1831. let previewController = QLPreviewController()
  1832. let rightBarButton = UIBarButtonItem()
  1833. previewController.navigationItem.rightBarButtonItem = rightBarButton
  1834. previewController.dataSource = self
  1835. previewController.modalPresentationStyle = .custom
  1836. self.present(previewController,animated: true)
  1837. }
  1838. }
  1839. catch {
  1840. }
  1841. }
  1842. }
  1843. } else if selectedTag == LINKS_TAG {
  1844. var stringURl = sender.message_id
  1845. if stringURl.lowercased().starts(with: "www.") {
  1846. stringURl = "https://" + stringURl.replacingOccurrences(of: "www.", with: "")
  1847. }
  1848. guard let url = URL(string: stringURl) else { return }
  1849. UIApplication.shared.open(url)
  1850. } else if selectedTag == AUDIOS_TAG {
  1851. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  1852. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  1853. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  1854. if let dirPath = paths.first {
  1855. let audioURL = URL(fileURLWithPath: dirPath).appendingPathComponent(sender.audio_id)
  1856. if FileManager.default.fileExists(atPath: audioURL.path) {
  1857. do {
  1858. if audioPlayer == nil || audioPlayer?.url != audioURL {
  1859. audioPlayer = try AVAudioPlayer(contentsOf: audioURL)
  1860. audioPlayer?.prepareToPlay()
  1861. audioPlayer?.play()
  1862. } else if audioPlayer!.isPlaying {
  1863. audioPlayer?.pause()
  1864. } else {
  1865. audioPlayer?.play()
  1866. }
  1867. } catch {
  1868. }
  1869. } else if FileEncryption.shared.isSecureExists(filename: sender.audio_id) {
  1870. do {
  1871. if let audioData = try FileEncryption.shared.readSecure(filename: sender.audio_id) {
  1872. let cachesDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
  1873. let tempPath = cachesDirectory.appendingPathComponent(sender.audio_id)
  1874. try audioData.write(to: tempPath)
  1875. if audioPlayer == nil || audioPlayer?.url != tempPath {
  1876. audioPlayer = try AVAudioPlayer(contentsOf: tempPath)
  1877. audioPlayer?.prepareToPlay()
  1878. audioPlayer?.play()
  1879. } else if audioPlayer!.isPlaying {
  1880. audioPlayer?.pause()
  1881. } else {
  1882. audioPlayer?.play()
  1883. }
  1884. }
  1885. } catch {
  1886. }
  1887. }
  1888. }
  1889. }
  1890. }
  1891. func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
  1892. if fillteredData.count > 0 && selectedTag != UNREAD_TAG && selectedTag != 0 {
  1893. if selectedTag == LINKS_TAG {
  1894. return 160.0
  1895. }
  1896. return 130.0
  1897. }
  1898. return 75.0
  1899. }
  1900. private func getRealStatus(messageId: String) -> String {
  1901. var status = "1"
  1902. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  1903. if let cursorStatus = Database.shared.getRecords(fmdb: fmdb, query: "SELECT status, f_pin FROM MESSAGE_STATUS WHERE message_id='\(messageId)'") {
  1904. var listStatus: [Int] = []
  1905. while cursorStatus.next() {
  1906. listStatus.append(Int(cursorStatus.string(forColumnIndex: 0)!)!)
  1907. }
  1908. cursorStatus.close()
  1909. status = "\(listStatus.min() ?? 2)"
  1910. }
  1911. })
  1912. return status
  1913. }
  1914. func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
  1915. return fillteredData.count
  1916. }
  1917. func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
  1918. let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "gridCell", for: indexPath)
  1919. if cell.contentView.subviews.count > 0 {
  1920. cell.contentView.subviews.forEach { $0.removeFromSuperview() }
  1921. }
  1922. let data = fillteredData[indexPath.row] as! Chat
  1923. let thumb = data.thumb
  1924. let imgData = data.image
  1925. let vidData = data.video
  1926. let image = UIImageView()
  1927. cell.contentView.addSubview(image)
  1928. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  1929. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  1930. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  1931. if let dirPath = paths.first {
  1932. let thumbURL = URL(fileURLWithPath: dirPath).appendingPathComponent(thumb)
  1933. let imageT : UIImage? = {
  1934. if let img = Nexilis.imageCache.object(forKey: thumb as NSString) {
  1935. return img
  1936. }
  1937. else if let img = UIImage(contentsOfFile: thumbURL.path)?.resize(target: CGSize(width: 500, height: 500)) {
  1938. Nexilis.imageCache.setObject(img, forKey: thumb as NSString)
  1939. return img
  1940. }
  1941. return nil
  1942. }()
  1943. if imageT != nil {
  1944. image.image = imageT
  1945. }
  1946. let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(imgData)
  1947. let videoURL = URL(fileURLWithPath: dirPath).appendingPathComponent(vidData)
  1948. if (!FileManager.default.fileExists(atPath: imageURL.path) && !FileEncryption.shared.isSecureExists(filename: imageURL.lastPathComponent)) || (!FileManager.default.fileExists(atPath: videoURL.path) && !FileEncryption.shared.isSecureExists(filename: videoURL.lastPathComponent)) {
  1949. let blurEffect = UIBlurEffect(style: UIBlurEffect.Style.light)
  1950. let blurEffectView = UIVisualEffectView(effect: blurEffect)
  1951. blurEffectView.frame = CGRect(x: 0, y: 0, width: image.frame.size.width, height: image.frame.size.height)
  1952. blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
  1953. image.addSubview(blurEffectView)
  1954. if !imgData.isEmpty {
  1955. let imageDownload = UIImageView(image: UIImage(systemName: "arrow.down.circle.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 50, weight: .bold, scale: .default)))
  1956. image.addSubview(imageDownload)
  1957. imageDownload.tintColor = .black.withAlphaComponent(0.3)
  1958. imageDownload.translatesAutoresizingMaskIntoConstraints = false
  1959. imageDownload.centerXAnchor.constraint(equalTo: image.centerXAnchor).isActive = true
  1960. imageDownload.centerYAnchor.constraint(equalTo: image.centerYAnchor).isActive = true
  1961. }
  1962. }
  1963. if (!vidData.isEmpty) {
  1964. let imagePlay = UIImageView(image: UIImage(systemName: "play.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .bold, scale: .default))?.imageWithInsets(insets: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10))?.withTintColor(.white))
  1965. imagePlay.circle()
  1966. image.addSubview(imagePlay)
  1967. imagePlay.backgroundColor = .black.withAlphaComponent(0.3)
  1968. imagePlay.translatesAutoresizingMaskIntoConstraints = false
  1969. imagePlay.centerXAnchor.constraint(equalTo: image.centerXAnchor).isActive = true
  1970. imagePlay.centerYAnchor.constraint(equalTo: image.centerYAnchor).isActive = true
  1971. }
  1972. }
  1973. image.contentMode = .scaleAspectFill
  1974. image.clipsToBounds = true
  1975. image.anchor(top: cell.contentView.topAnchor, left: cell.contentView.leftAnchor, bottom: cell.contentView.bottomAnchor, right: cell.contentView.rightAnchor)
  1976. return cell
  1977. }
  1978. func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
  1979. let data = fillteredData[indexPath.row] as! Chat
  1980. let imgData = data.image
  1981. let vidData = data.video
  1982. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  1983. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  1984. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  1985. if let dirPath = paths.first {
  1986. if selectedTag == PHOTOS_TAG {
  1987. let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(imgData)
  1988. if FileManager.default.fileExists(atPath: imageURL.path) {
  1989. let image = UIImage(contentsOfFile: imageURL.path) ?? UIImage()
  1990. APIS.openImageNexilis(image: image)
  1991. } else if FileEncryption.shared.isSecureExists(filename: imgData) {
  1992. do {
  1993. let data = try FileEncryption.shared.readSecure(filename: imgData)
  1994. let image = UIImage(data: data!) ?? UIImage()
  1995. APIS.openImageNexilis(image: image)
  1996. }
  1997. catch {
  1998. }
  1999. } else {
  2000. Download().startHTTP(forKey: imgData) { (name, progress) in
  2001. guard progress == 100 else {
  2002. return
  2003. }
  2004. DispatchQueue.main.async {
  2005. collectionView.reloadItems(at: [indexPath])
  2006. }
  2007. }
  2008. }
  2009. } else {
  2010. let videoURL = URL(fileURLWithPath: dirPath).appendingPathComponent(vidData)
  2011. if FileManager.default.fileExists(atPath: videoURL.path) {
  2012. APIS.openVideoNexilis(videoURL: videoURL)
  2013. } else if FileEncryption.shared.isSecureExists(filename: vidData) {
  2014. do {
  2015. if let secureData = try FileEncryption.shared.readSecure(filename: vidData) {
  2016. let cachesDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
  2017. let tempPath = cachesDirectory.appendingPathComponent(vidData)
  2018. try secureData.write(to: tempPath)
  2019. APIS.openVideoNexilis(videoURL: tempPath)
  2020. }
  2021. } catch {
  2022. }
  2023. } else {
  2024. Download().startHTTP(forKey: vidData, isImage: false) { (name, progress) in
  2025. guard progress == 100 else {
  2026. return
  2027. }
  2028. DispatchQueue.main.async {
  2029. collectionView.reloadItems(at: [indexPath])
  2030. }
  2031. }
  2032. }
  2033. }
  2034. }
  2035. }
  2036. func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
  2037. return self.previewItem != nil ? 1 : 0
  2038. }
  2039. func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> any QLPreviewItem {
  2040. return self.previewItem!
  2041. }
  2042. }
  2043. extension SecondTabViewController: UISearchControllerDelegate, UISearchBarDelegate, UISearchResultsUpdating {
  2044. func updateSearchResults(for searchController: UISearchController) {
  2045. filterContentForSearchText(searchController.searchBar.text!.trimmingCharacters(in: .whitespacesAndNewlines))
  2046. }
  2047. func searchBarBookmarkButtonClicked(_ searchBar: UISearchBar) {
  2048. recordAudio()
  2049. }
  2050. func set(image: UIImage, with text: String, size: CGFloat, y: CGFloat, colorText: UIColor = UIColor.black) -> NSAttributedString {
  2051. let attachment = NSTextAttachment()
  2052. attachment.image = image
  2053. attachment.bounds = CGRect(x: 0, y: y, width: size, height: size)
  2054. let attachmentStr = NSAttributedString(attachment: attachment)
  2055. let mutableAttributedString = NSMutableAttributedString()
  2056. mutableAttributedString.append(attachmentStr)
  2057. let attributedStringColor = [NSAttributedString.Key.foregroundColor : colorText]
  2058. let textString = NSAttributedString(string: text, attributes: attributedStringColor)
  2059. mutableAttributedString.append(textString)
  2060. return mutableAttributedString
  2061. }
  2062. func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
  2063. searchBar.showsCancelButton = true
  2064. let cBtn = searchBar.value(forKey: "cancelButton") as! UIButton
  2065. cBtn.setTitle("Cancel".localized(), for: .normal)
  2066. }
  2067. func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
  2068. searchBar.showsCancelButton = false
  2069. }
  2070. }
  2071. extension SecondTabViewController: SFSpeechRecognizerDelegate {
  2072. func speechRecognizer(_ speechRecognizer: SFSpeechRecognizer, availabilityDidChange available: Bool) {
  2073. if available {
  2074. self.isAllowSpeech = true
  2075. } else {
  2076. self.isAllowSpeech = false
  2077. }
  2078. }
  2079. }