EditorStarMessages.swift 107 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781
  1. //
  2. // EditorStarMessages.swift
  3. // Qmera
  4. //
  5. // Created by Akhmad Al Qindi Irsyam on 22/09/21.
  6. //
  7. import UIKit
  8. import AVKit
  9. import AVFoundation
  10. import QuickLook
  11. import Photos
  12. import SwiftLinkPreview
  13. import nuSDKService
  14. import NotificationBannerSwift
  15. public class EditorStarMessages: UIViewController, UITableViewDataSource, UITableViewDelegate, UIContextMenuInteractionDelegate, QLPreviewControllerDataSource, UITextViewDelegate {
  16. @IBOutlet var tableChatView: UITableView!
  17. var dataMessages: [[String: Any?]] = []
  18. var dataDates: [String] = []
  19. var previewItem = NSURL()
  20. var fromNotification = false
  21. var timerCheckLink: Timer?
  22. var showMenuContext = false
  23. var touchedSubview = UIView()
  24. var lastTouchPoint: CGPoint = .zero
  25. var downloadList: [String: IndexPath] = [:]
  26. var transitioningDelegateRef: ZoomTransitioningDelegate?
  27. func offset() -> CGFloat{
  28. guard let fontSize = Int(SecureUserDefaults.shared.value(forKey: "font_size") ?? "0") else { return 0 }
  29. return CGFloat(fontSize)
  30. }
  31. public override func viewDidLoad() {
  32. super.viewDidLoad()
  33. if fromNotification {
  34. let imageButton = UIImageView(frame: CGRect(x: -16, y: 0, width: 20, height: 44))
  35. imageButton.image = UIImage(systemName: "chevron.backward", withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .regular, scale: .default))?.withTintColor(.white)
  36. imageButton.contentMode = .left
  37. let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(didTapExit))
  38. imageButton.isUserInteractionEnabled = true
  39. imageButton.addGestureRecognizer(tapGestureRecognizer)
  40. let leftItem = UIBarButtonItem(customView: imageButton)
  41. self.navigationItem.leftBarButtonItem = leftItem
  42. }
  43. navigationController?.navigationBar.isTranslucent = false
  44. navigationController?.navigationBar.barTintColor = UIColor.mainColor
  45. navigationController?.navigationBar.tintColor = .white
  46. navigationController?.navigationBar.topItem?.title = ""
  47. self.title = "Favorite Messages".localized()
  48. let menu = UIMenu(title: "", children: [
  49. UIAction(title: "Unfavorite all messages".localized(), handler: {(_) in
  50. DispatchQueue.global().async {
  51. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  52. do {
  53. _ = Database.shared.updateAllRecord(fmdb: fmdb, table: "MESSAGE", cvalues: [
  54. "is_stared" : 0
  55. ])
  56. } catch {
  57. rollback.pointee = true
  58. print("Access database error: \(error.localizedDescription)")
  59. }
  60. })
  61. }
  62. self.dataMessages.removeAll()
  63. self.tableChatView.reloadData()
  64. }),
  65. ])
  66. getData()
  67. let moreIcon = UIBarButtonItem(image: UIImage(systemName: "ellipsis"), menu: menu)
  68. navigationItem.rightBarButtonItem = moreIcon
  69. navigationItem.rightBarButtonItem?.tintColor = UIColor.secondaryColor
  70. tableChatView.delegate = self
  71. tableChatView.dataSource = self
  72. tableChatView.reloadData()
  73. let center: NotificationCenter = NotificationCenter.default
  74. center.addObserver(self, selector: #selector(onRefreshData(notification:)), name: NSNotification.Name(rawValue: Nexilis.listenerStatusChat), object: nil)
  75. center.addObserver(self, selector: #selector(onRefreshData(notification:)), name: NSNotification.Name(rawValue: "listenerStarMessage"), object: nil)
  76. }
  77. @objc func onRefreshData(notification: NSNotification) {
  78. DispatchQueue.main.async { [self] in
  79. getData()
  80. tableChatView.reloadData()
  81. }
  82. }
  83. @objc func didTapExit() {
  84. self.dismiss(animated: true, completion: nil)
  85. }
  86. public func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
  87. let containerView = UIView()
  88. containerView.backgroundColor = .clear
  89. let dateView = UIView()
  90. containerView.addSubview(dateView)
  91. dateView.translatesAutoresizingMaskIntoConstraints = false
  92. var topAnchor = dateView.topAnchor.constraint(equalTo: containerView.topAnchor)
  93. topAnchor = dateView.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 20.0)
  94. NSLayoutConstraint.activate([
  95. topAnchor,
  96. dateView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -20.0),
  97. dateView.centerXAnchor.constraint(equalTo: containerView.centerXAnchor),
  98. dateView.heightAnchor.constraint(equalToConstant: 30),
  99. dateView.widthAnchor.constraint(greaterThanOrEqualToConstant: 60)
  100. ])
  101. dateView.backgroundColor = .orangeColor
  102. dateView.layer.cornerRadius = 15.0
  103. dateView.clipsToBounds = true
  104. let labelDate = UILabel()
  105. dateView.addSubview(labelDate)
  106. labelDate.translatesAutoresizingMaskIntoConstraints = false
  107. NSLayoutConstraint.activate([
  108. labelDate.centerYAnchor.constraint(equalTo: dateView.centerYAnchor),
  109. labelDate.centerXAnchor.constraint(equalTo: dateView.centerXAnchor),
  110. labelDate.leadingAnchor.constraint(equalTo: dateView.leadingAnchor, constant: 10),
  111. labelDate.trailingAnchor.constraint(equalTo: dateView.trailingAnchor, constant: -10),
  112. ])
  113. labelDate.textAlignment = .center
  114. labelDate.textColor = .secondaryColor
  115. labelDate.font = UIFont.systemFont(ofSize: 12 + offset(), weight: .medium)
  116. labelDate.text = dataDates[section]
  117. return containerView
  118. }
  119. public func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
  120. return 80
  121. }
  122. public func numberOfSections(in tableView: UITableView) -> Int {
  123. dataDates.count
  124. }
  125. public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  126. let count = dataMessages.filter({ $0["chat_date"] as! String == dataDates[section] }).count
  127. return count
  128. }
  129. public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  130. let idMe = User.getMyPin() as String?
  131. let dataMessages = dataMessages.filter({$0["chat_date"] as! String == dataDates[indexPath.section]})
  132. let cellMessage = UITableViewCell()
  133. cellMessage.backgroundColor = .clear
  134. cellMessage.selectionStyle = .none
  135. let profileMessage = UIImageView()
  136. profileMessage.frame.size = CGSize(width: 35, height: 35)
  137. cellMessage.contentView.addSubview(profileMessage)
  138. profileMessage.translatesAutoresizingMaskIntoConstraints = false
  139. let containerMessage = UIView()
  140. let interaction = UIContextMenuInteraction(delegate: self)
  141. containerMessage.addInteraction(interaction)
  142. containerMessage.isUserInteractionEnabled = true
  143. cellMessage.contentView.addSubview(containerMessage)
  144. containerMessage.translatesAutoresizingMaskIntoConstraints = false
  145. let timeMessage = UILabel()
  146. cellMessage.contentView.addSubview(timeMessage)
  147. timeMessage.translatesAutoresizingMaskIntoConstraints = false
  148. if ((dataMessages[indexPath.row]["read_receipts"] as? String) == "8" ||
  149. (dataMessages[indexPath.row]["credential"] as? String) == "1" ||
  150. !(dataMessages[indexPath.row][TypeDataMessage.spec_file] as? String ?? "").isEmpty) &&
  151. (dataMessages[indexPath.row]["lock"] as? String) != "2" &&
  152. (dataMessages[indexPath.row]["lock"] as? String) != "1" {
  153. timeMessage.bottomAnchor.constraint(equalTo: cellMessage.contentView.bottomAnchor, constant: -40).isActive = true
  154. } else {
  155. timeMessage.bottomAnchor.constraint(equalTo: cellMessage.contentView.bottomAnchor, constant: -5).isActive = true
  156. }
  157. let messageText = UITextView()
  158. messageText.isEditable = false
  159. messageText.isSelectable = true
  160. messageText.dataDetectorTypes = []
  161. messageText.backgroundColor = .clear
  162. messageText.isScrollEnabled = false
  163. messageText.textContainerInset = UIEdgeInsets.zero
  164. messageText.contentInset = UIEdgeInsets.zero
  165. messageText.textDragInteraction?.isEnabled = false
  166. containerMessage.addSubview(messageText)
  167. messageText.translatesAutoresizingMaskIntoConstraints = false
  168. let topMarginText = messageText.topAnchor.constraint(equalTo: containerMessage.topAnchor, constant: 32)
  169. let dataProfile = getDataProfile(f_pin: dataMessages[indexPath.row]["f_pin"] as! String)
  170. let statusMessage = UIImageView()
  171. if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  172. profileMessage.topAnchor.constraint(equalTo: cellMessage.contentView.topAnchor, constant: 5).isActive = true
  173. profileMessage.trailingAnchor.constraint(equalTo: cellMessage.contentView.trailingAnchor, constant: -15).isActive = true
  174. profileMessage.heightAnchor.constraint(equalToConstant: 37).isActive = true
  175. profileMessage.widthAnchor.constraint(equalToConstant: 35).isActive = true
  176. profileMessage.circle()
  177. profileMessage.clipsToBounds = true
  178. profileMessage.backgroundColor = .lightGray
  179. profileMessage.image = UIImage(systemName: "person")
  180. profileMessage.tintColor = .white
  181. profileMessage.contentMode = .scaleAspectFit
  182. let pictureImage = dataProfile["image_id"]
  183. if (pictureImage != "" && pictureImage != nil) {
  184. profileMessage.setImage(name: pictureImage!)
  185. profileMessage.contentMode = .scaleAspectFill
  186. }
  187. containerMessage.topAnchor.constraint(equalTo: cellMessage.contentView.topAnchor, constant: 5).isActive = true
  188. containerMessage.leadingAnchor.constraint(greaterThanOrEqualTo: cellMessage.contentView.leadingAnchor, constant: 80).isActive = true
  189. containerMessage.trailingAnchor.constraint(equalTo: profileMessage.leadingAnchor, constant: -5).isActive = true
  190. containerMessage.widthAnchor.constraint(greaterThanOrEqualToConstant: 46).isActive = true
  191. if (dataMessages[indexPath.row]["attachment_flag"] as? String == "11" && dataMessages[indexPath.row]["reff_id"]as? String == "") {
  192. containerMessage.backgroundColor = .clear
  193. } else {
  194. containerMessage.backgroundColor = .mainColor
  195. }
  196. containerMessage.layer.cornerRadius = 10.0
  197. containerMessage.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner, .layerMinXMinYCorner]
  198. containerMessage.clipsToBounds = true
  199. timeMessage.trailingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: -8).isActive = true
  200. cellMessage.contentView.addSubview(statusMessage)
  201. statusMessage.translatesAutoresizingMaskIntoConstraints = false
  202. statusMessage.bottomAnchor.constraint(equalTo: timeMessage.topAnchor).isActive = true
  203. statusMessage.trailingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: -8).isActive = true
  204. statusMessage.widthAnchor.constraint(equalToConstant: 15).isActive = true
  205. statusMessage.heightAnchor.constraint(equalToConstant: 15).isActive = true
  206. if (dataMessages[indexPath.row]["status"]! as! String == "1" || dataMessages[indexPath.row]["status"]! as! String == "2" ) {
  207. statusMessage.image = UIImage(named: "checklist", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(UIColor.lightGray)
  208. } else if (dataMessages[indexPath.row]["status"]! as! String == "3") {
  209. statusMessage.image = UIImage(named: "double-checklist", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(UIColor.lightGray)
  210. } else {
  211. statusMessage.image = UIImage(named: "double-checklist", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(UIColor.systemBlue)
  212. }
  213. let nameSender = UILabel()
  214. containerMessage.addSubview(nameSender)
  215. nameSender.translatesAutoresizingMaskIntoConstraints = false
  216. nameSender.topAnchor.constraint(equalTo: containerMessage.topAnchor, constant: 15).isActive = true
  217. nameSender.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15).isActive = true
  218. nameSender.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
  219. nameSender.font = UIFont.systemFont(ofSize: 12 + offset()).bold
  220. nameSender.text = dataProfile["name"]
  221. nameSender.textAlignment = .right
  222. if (dataMessages[indexPath.row]["attachment_flag"] as? String == "11" && dataMessages[indexPath.row]["reff_id"]as? String == "") {
  223. containerMessage.backgroundColor = .clear
  224. nameSender.textColor = .mainColor
  225. } else {
  226. containerMessage.backgroundColor = .mainColor
  227. nameSender.textColor = .white
  228. }
  229. } else {
  230. profileMessage.topAnchor.constraint(equalTo: cellMessage.contentView.topAnchor, constant: 5).isActive = true
  231. profileMessage.leadingAnchor.constraint(equalTo: cellMessage.contentView.leadingAnchor, constant: 15).isActive = true
  232. profileMessage.heightAnchor.constraint(equalToConstant: 37).isActive = true
  233. profileMessage.widthAnchor.constraint(equalToConstant: 35).isActive = true
  234. profileMessage.circle()
  235. profileMessage.clipsToBounds = true
  236. profileMessage.backgroundColor = .lightGray
  237. profileMessage.image = UIImage(systemName: "person")
  238. profileMessage.tintColor = .white
  239. profileMessage.contentMode = .scaleAspectFit
  240. let pictureImage = dataProfile["image_id"]
  241. if dataMessages[indexPath.row]["f_pin"] as! String == "-999" {
  242. if !Utils.getIconDock().isEmpty {
  243. let dataImage = try? Data(contentsOf: URL(string: Utils.getUrlDock()!)!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
  244. if dataImage != nil {
  245. profileMessage.image = UIImage(data: dataImage!)
  246. }
  247. } else {
  248. profileMessage.image = UIImage(named: "pb_button", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
  249. }
  250. profileMessage.contentMode = .scaleAspectFit
  251. }
  252. if (pictureImage != "" && pictureImage != nil) {
  253. profileMessage.setImage(name: pictureImage!)
  254. profileMessage.contentMode = .scaleAspectFill
  255. }
  256. containerMessage.topAnchor.constraint(equalTo: cellMessage.contentView.topAnchor, constant: 5).isActive = true
  257. containerMessage.leadingAnchor.constraint(equalTo: profileMessage.trailingAnchor, constant: 5).isActive = true
  258. containerMessage.trailingAnchor.constraint(lessThanOrEqualTo: cellMessage.contentView.trailingAnchor, constant: -80).isActive = true
  259. containerMessage.widthAnchor.constraint(greaterThanOrEqualToConstant: 46).isActive = true
  260. if (dataMessages[indexPath.row]["attachment_flag"] as? String == "11" && dataMessages[indexPath.row]["reff_id"]as? String == "") {
  261. containerMessage.backgroundColor = .clear
  262. } else {
  263. containerMessage.backgroundColor = .whiteBubbleColor
  264. }
  265. containerMessage.layer.cornerRadius = 10.0
  266. containerMessage.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMinYCorner, .layerMaxXMaxYCorner]
  267. containerMessage.clipsToBounds = true
  268. timeMessage.leadingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: 8).isActive = true
  269. let nameSender = UILabel()
  270. containerMessage.addSubview(nameSender)
  271. nameSender.translatesAutoresizingMaskIntoConstraints = false
  272. nameSender.topAnchor.constraint(equalTo: containerMessage.topAnchor, constant: 15).isActive = true
  273. nameSender.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15).isActive = true
  274. nameSender.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
  275. nameSender.font = UIFont.systemFont(ofSize: 12 + offset()).bold
  276. nameSender.text = dataProfile["name"]
  277. nameSender.textAlignment = .left
  278. nameSender.textColor = .mainColor
  279. }
  280. if ((dataMessages[indexPath.row]["read_receipts"] as? String) == "8" ||
  281. (dataMessages[indexPath.row]["credential"] as? String) == "1" ||
  282. !(dataMessages[indexPath.row][TypeDataMessage.spec_file] as? String ?? "").isEmpty) &&
  283. (dataMessages[indexPath.row]["lock"] as? String) != "2" &&
  284. (dataMessages[indexPath.row]["lock"] as? String) != "1" {
  285. containerMessage.bottomAnchor.constraint(equalTo: cellMessage.contentView.bottomAnchor, constant: -40).isActive = true
  286. } else {
  287. containerMessage.bottomAnchor.constraint(equalTo: cellMessage.contentView.bottomAnchor, constant: -5).isActive = true
  288. }
  289. if (dataMessages[indexPath.row]["is_stared"] as? String == "1") {
  290. let imageStared = UIImageView()
  291. cellMessage.contentView.addSubview(imageStared)
  292. imageStared.translatesAutoresizingMaskIntoConstraints = false
  293. if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  294. imageStared.bottomAnchor.constraint(equalTo: statusMessage.topAnchor).isActive = true
  295. imageStared.trailingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: -8).isActive = true
  296. } else {
  297. imageStared.bottomAnchor.constraint(equalTo: timeMessage.topAnchor).isActive = true
  298. imageStared.leadingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: 8).isActive = true
  299. }
  300. imageStared.widthAnchor.constraint(equalToConstant: 15).isActive = true
  301. imageStared.heightAnchor.constraint(equalToConstant: 15).isActive = true
  302. imageStared.image = UIImage(systemName: "star.fill")
  303. imageStared.backgroundColor = .clear
  304. imageStared.tintColor = .systemYellow
  305. }
  306. let imageAckView = UIImageView()
  307. if dataMessages[indexPath.row]["read_receipts"] as? String == "8" {
  308. var imageAck = UIImage(named: "ack_icon_gray", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withRenderingMode(.alwaysOriginal)
  309. if dataMessages[indexPath.row]["status"] as? String == "8" {
  310. imageAck = UIImage(named: "ack_icon", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withRenderingMode(.alwaysOriginal)
  311. }
  312. imageAckView.image = imageAck
  313. cellMessage.contentView.addSubview(imageAckView)
  314. imageAckView.translatesAutoresizingMaskIntoConstraints = false
  315. imageAckView.widthAnchor.constraint(equalToConstant: 30).isActive = true
  316. imageAckView.heightAnchor.constraint(equalToConstant: 30).isActive = true
  317. imageAckView.topAnchor.constraint(equalTo: containerMessage.bottomAnchor, constant: 5).isActive = true
  318. if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  319. imageAckView.trailingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 30).isActive = true
  320. } else {
  321. imageAckView.leadingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -30).isActive = true
  322. let tap = ObjectGesture(target: self, action: #selector(tapAck(_:)))
  323. tap.indexPath = indexPath
  324. imageAckView.addGestureRecognizer(tap)
  325. imageAckView.isUserInteractionEnabled = true
  326. }
  327. }
  328. if !(dataMessages[indexPath.row][TypeDataMessage.spec_file] as? String ?? "").isEmpty && (dataMessages[indexPath.row]["lock"] as? String) != "2" && (dataMessages[indexPath.row]["lock"] as? String) != "1" {
  329. let imageSpecFileView = UIImageView()
  330. let imageSpecFile = UIImage(named: "pb_ic_attach_spc", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withRenderingMode(.alwaysOriginal)
  331. imageSpecFileView.image = imageSpecFile
  332. cellMessage.contentView.addSubview(imageSpecFileView)
  333. imageSpecFileView.translatesAutoresizingMaskIntoConstraints = false
  334. imageSpecFileView.widthAnchor.constraint(equalToConstant: 30).isActive = true
  335. imageSpecFileView.heightAnchor.constraint(equalToConstant: 30).isActive = true
  336. imageSpecFileView.topAnchor.constraint(equalTo: containerMessage.bottomAnchor, constant: 5).isActive = true
  337. if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  338. if imageAckView.isDescendant(of: cellMessage.contentView) {
  339. imageSpecFileView.leadingAnchor.constraint(equalTo: imageAckView.trailingAnchor, constant: 5).isActive = true
  340. } else {
  341. imageSpecFileView.trailingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 30).isActive = true
  342. }
  343. } else {
  344. if imageAckView.isDescendant(of: cellMessage.contentView) {
  345. imageSpecFileView.trailingAnchor.constraint(equalTo: imageAckView.leadingAnchor, constant: -5).isActive = true
  346. } else {
  347. imageSpecFileView.leadingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -30).isActive = true
  348. }
  349. }
  350. }
  351. topMarginText.isActive = true
  352. if dataMessages[indexPath.row]["attachment_flag"] as! String == "27" || dataMessages[indexPath.row]["attachment_flag"] as! String == "26" {
  353. messageText.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 85).isActive = true
  354. let imageLS = UIImageView()
  355. containerMessage.addSubview(imageLS)
  356. imageLS.translatesAutoresizingMaskIntoConstraints = false
  357. NSLayoutConstraint.activate([
  358. imageLS.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15.0),
  359. imageLS.trailingAnchor.constraint(equalTo: messageText.leadingAnchor, constant: -10.0),
  360. imageLS.centerYAnchor.constraint(equalTo: containerMessage.centerYAnchor),
  361. imageLS.heightAnchor.constraint(equalToConstant: 60.0)
  362. ])
  363. if dataMessages[indexPath.row]["attachment_flag"] as! String == "26" {
  364. imageLS.image = UIImage(named: "pb_seminar_wpr", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
  365. } else if dataMessages[indexPath.row]["attachment_flag"] as! String == "27" {
  366. imageLS.image = UIImage(named: "pb_live_tv", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
  367. }
  368. } else {
  369. messageText.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15).isActive = true
  370. }
  371. messageText.bottomAnchor.constraint(equalTo: containerMessage.bottomAnchor, constant: -15).isActive = true
  372. messageText.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
  373. var textChat = (dataMessages[indexPath.row]["message_text"])! as? String
  374. if (dataMessages[indexPath.row]["lock"] != nil && (dataMessages[indexPath.row]["lock"])! as? String == "1") {
  375. if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  376. textChat = "🚫 _"+"You were deleted this message".localized()+"_"
  377. } else {
  378. textChat = "🚫 _"+"This message was deleted".localized()+"_"
  379. }
  380. }
  381. let imageSticker = UIImageView()
  382. if let attachmentFlag = dataMessages[indexPath.row]["attachment_flag"], let attachmentFlag = attachmentFlag as? String {
  383. if attachmentFlag == "27" || attachmentFlag == "26", let data = textChat { // live streaming
  384. if let json = try! JSONSerialization.jsonObject(with: data.data(using: String.Encoding.utf8)!, options: []) as? [String: Any] {
  385. Database.shared.database?.inTransaction({ fmdb, rollback in
  386. let title = json["title"] as! String
  387. let description = json["description"] as! String
  388. let start = json["time"] as! Int64
  389. let by = json["by"] as! String
  390. var type = "*Live Streaming*"
  391. if attachmentFlag == "26" {
  392. type = "*Seminar*"
  393. }
  394. if let c = Database().getRecords(fmdb: fmdb, query: "select first_name || ' ' || last_name from BUDDY where f_pin = '\(by)'"), c.next() {
  395. let name = c.string(forColumnIndex: 0)!
  396. messageText.attributedText = "\(type) \nTitle: \(title) \nDescription: \(description) \nStart: \(Date(milliseconds: start).format(dateFormat: "dd/MM/yyyy HH:mm")) \nBroadcaster: \(name)".richText()
  397. c.close()
  398. } else {
  399. messageText.attributedText = ("\(type) \nTitle: \(title) \nDescription: \(description) \nStart: \(Date(milliseconds: start).format(dateFormat: "dd/MM/yyyy HH:mm")) \nBroadcaster: " + "Unknown".localized()).richText()
  400. }
  401. })
  402. }
  403. } else if attachmentFlag == "11" {
  404. messageText.text = ""
  405. topMarginText.constant = topMarginText.constant + 100
  406. containerMessage.addSubview(imageSticker)
  407. imageSticker.translatesAutoresizingMaskIntoConstraints = false
  408. imageSticker.topAnchor.constraint(equalTo: containerMessage.topAnchor, constant: 27.0).isActive = true
  409. imageSticker.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor).isActive = true
  410. imageSticker.bottomAnchor.constraint(equalTo: messageText.topAnchor, constant: -5).isActive = true
  411. imageSticker.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor).isActive = true
  412. imageSticker.widthAnchor.constraint(equalToConstant: 80).isActive = true
  413. var imageStickerBundle = UIImage(named: (textChat!.components(separatedBy: "/")[1]), in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
  414. if imageStickerBundle == nil {
  415. imageStickerBundle = UIImage(named: (textChat!.components(separatedBy: "/")[1]), in: Bundle.resourcesMediaBundle(for: Nexilis.self), with: nil)
  416. }
  417. imageSticker.image = imageStickerBundle //resourcesMediaBundle
  418. imageSticker.contentMode = .scaleAspectFit
  419. }
  420. else {
  421. modifyText()
  422. }
  423. } else {
  424. modifyText()
  425. }
  426. messageText.font = UIFont.systemFont(ofSize: 12 + offset())
  427. func modifyText() {
  428. if !textChat!.isEmpty {
  429. if textChat!.contains("■"){
  430. textChat = textChat!.components(separatedBy: "■")[0]
  431. textChat = textChat!.trimmingCharacters(in: .whitespacesAndNewlines)
  432. }
  433. let finalAtribute = textChat!.richText()
  434. textChat = finalAtribute.string
  435. let urlPattern = "(https?://|www\\.)\\S+"
  436. if let regex = try? NSRegularExpression(pattern: urlPattern, options: []) {
  437. let matches = regex.matches(in: textChat!, options: [], range: NSRange(textChat!.startIndex..., in: textChat!))
  438. for match in matches {
  439. if let range = Range(match.range, in: textChat!) {
  440. let linkText = String(textChat![range])
  441. let nsRange = NSRange(range, in: textChat!)
  442. finalAtribute.addAttribute(.link, value: linkText, range: nsRange)
  443. finalAtribute.addAttribute(.foregroundColor, value: UIColor.blue, range: nsRange)
  444. finalAtribute.addAttribute(.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: nsRange)
  445. }
  446. }
  447. }
  448. messageText.attributedText = finalAtribute
  449. messageText.delegate = self
  450. }
  451. }
  452. if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  453. messageText.textColor = .white
  454. } else {
  455. messageText.textColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .black
  456. }
  457. let stringDate = (dataMessages[indexPath.row]["server_date"] as! String)
  458. let date = Date(milliseconds: Int64(stringDate)!)
  459. let formatter = DateFormatter()
  460. formatter.dateFormat = "HH:mm"
  461. formatter.locale = NSLocale(localeIdentifier: "id") as Locale?
  462. timeMessage.text = formatter.string(from: date as Date)
  463. timeMessage.font = UIFont.systemFont(ofSize: 10 + offset(), weight: .medium)
  464. timeMessage.textColor = .lightGray
  465. let thumbChat = dataMessages[indexPath.row]["thumb_id"] as! String
  466. let imageChat = dataMessages[indexPath.row]["image_id"] as! String
  467. let videoChat = dataMessages[indexPath.row]["video_id"] as! String
  468. let fileChat = dataMessages[indexPath.row]["file_id"] as! String
  469. let imageThumb = UIImageView()
  470. let containerViewFile = UIView()
  471. if (!thumbChat.isEmpty) {
  472. topMarginText.constant = topMarginText.constant + 205
  473. containerMessage.addSubview(imageThumb)
  474. imageThumb.translatesAutoresizingMaskIntoConstraints = false
  475. imageThumb.topAnchor.constraint(equalTo: containerMessage.topAnchor, constant: 32).isActive = true
  476. imageThumb.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15).isActive = true
  477. imageThumb.bottomAnchor.constraint(equalTo: messageText.topAnchor, constant: -5).isActive = true
  478. imageThumb.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
  479. imageThumb.widthAnchor.constraint(equalToConstant: self.view.frame.size.width * 0.6).isActive = true
  480. imageThumb.layer.cornerRadius = 5.0
  481. imageThumb.clipsToBounds = true
  482. imageThumb.contentMode = .scaleAspectFill
  483. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  484. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  485. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  486. if let dirPath = paths.first {
  487. let thumbURL = URL(fileURLWithPath: dirPath).appendingPathComponent(thumbChat)
  488. let image = UIImage(contentsOfFile: thumbURL.path)
  489. imageThumb.image = image
  490. let videoURL = URL(fileURLWithPath: dirPath).appendingPathComponent(videoChat)
  491. let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(imageChat)
  492. if !FileManager.default.fileExists(atPath: imageURL.path) && !FileManager.default.fileExists(atPath: videoURL.path) && !FileEncryption.shared.isSecureExists(filename: imageURL.lastPathComponent) && !FileEncryption.shared.isSecureExists(filename: videoURL.lastPathComponent){
  493. let blurEffect = UIBlurEffect(style: UIBlurEffect.Style.light)
  494. let blurEffectView = UIVisualEffectView(effect: blurEffect)
  495. blurEffectView.frame = CGRect(x: 0, y: 0, width: imageThumb.frame.size.width, height: imageThumb.frame.size.height)
  496. blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
  497. let imageDownload = UIImageView(image: UIImage(systemName: "arrow.down.circle.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 50, weight: .bold, scale: .default)))
  498. imageThumb.addSubview(blurEffectView)
  499. imageThumb.addSubview(imageDownload)
  500. imageDownload.tintColor = .black.withAlphaComponent(0.3)
  501. imageDownload.translatesAutoresizingMaskIntoConstraints = false
  502. imageDownload.centerXAnchor.constraint(equalTo: imageThumb.centerXAnchor).isActive = true
  503. imageDownload.centerYAnchor.constraint(equalTo: imageThumb.centerYAnchor).isActive = true
  504. }
  505. }
  506. if (videoChat != "") {
  507. 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))
  508. imagePlay.circle()
  509. imageThumb.addSubview(imagePlay)
  510. imagePlay.backgroundColor = .black.withAlphaComponent(0.3)
  511. imagePlay.translatesAutoresizingMaskIntoConstraints = false
  512. imagePlay.centerXAnchor.constraint(equalTo: imageThumb.centerXAnchor).isActive = true
  513. imagePlay.centerYAnchor.constraint(equalTo: imageThumb.centerYAnchor).isActive = true
  514. }
  515. if (dataMessages[indexPath.row]["progress"] as! Double != 100.0 && dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  516. let container = UIView()
  517. imageThumb.addSubview(container)
  518. container.translatesAutoresizingMaskIntoConstraints = false
  519. container.bottomAnchor.constraint(equalTo: imageThumb.bottomAnchor, constant: -10).isActive = true
  520. container.leadingAnchor.constraint(equalTo: imageThumb.leadingAnchor, constant: 10).isActive = true
  521. container.widthAnchor.constraint(equalToConstant: 30).isActive = true
  522. container.heightAnchor.constraint(equalToConstant: 30).isActive = true
  523. let circlePath = UIBezierPath(arcCenter: CGPoint(x: 10, y: 20), radius: 15, startAngle: -(.pi / 2), endAngle: .pi * 2, clockwise: true)
  524. let trackShape = CAShapeLayer()
  525. trackShape.path = circlePath.cgPath
  526. trackShape.fillColor = UIColor.black.withAlphaComponent(0.3).cgColor
  527. trackShape.lineWidth = 3
  528. trackShape.strokeColor = UIColor.mainColor.withAlphaComponent(0.3).cgColor
  529. container.backgroundColor = .clear
  530. container.layer.addSublayer(trackShape)
  531. let shapeLoading = CAShapeLayer()
  532. shapeLoading.path = circlePath.cgPath
  533. shapeLoading.fillColor = UIColor.clear.cgColor
  534. shapeLoading.lineWidth = 3
  535. shapeLoading.strokeEnd = 0
  536. shapeLoading.strokeColor = UIColor.mainColor.cgColor
  537. container.layer.addSublayer(shapeLoading)
  538. let imageupload = UIImageView(image: UIImage(systemName: "arrow.up", withConfiguration: UIImage.SymbolConfiguration(pointSize: 10, weight: .bold, scale: .default)))
  539. imageupload.tintColor = .white
  540. container.addSubview(imageupload)
  541. imageupload.translatesAutoresizingMaskIntoConstraints = false
  542. imageupload.bottomAnchor.constraint(equalTo: imageThumb.bottomAnchor, constant: -10).isActive = true
  543. imageupload.leadingAnchor.constraint(equalTo: imageThumb.leadingAnchor, constant: 10).isActive = true
  544. imageupload.widthAnchor.constraint(equalToConstant: 20).isActive = true
  545. imageupload.heightAnchor.constraint(equalToConstant: 20).isActive = true
  546. }
  547. let objectTap = ObjectGesture(target: self, action: #selector(contentMessageTapped(_:)))
  548. let sfs = (dataMessages[indexPath.row][TypeDataMessage.spec_file] as? String) ?? ""
  549. imageThumb.isUserInteractionEnabled = true
  550. imageThumb.addGestureRecognizer(objectTap)
  551. objectTap.image_id = imageChat
  552. objectTap.video_id = videoChat
  553. objectTap.specFile = sfs
  554. objectTap.imageView = imageThumb
  555. objectTap.indexPath = indexPath
  556. }
  557. if (!fileChat.isEmpty) {
  558. topMarginText.constant = topMarginText.constant + 55
  559. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  560. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  561. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  562. let arrExtFile = (textChat?.components(separatedBy: "|")[0])?.split(separator: ".")
  563. let finalExtFile = arrExtFile![arrExtFile!.count - 1]
  564. if let dirPath = paths.first {
  565. let fileURL = URL(fileURLWithPath: dirPath).appendingPathComponent(fileChat)
  566. if FileManager.default.fileExists(atPath: fileURL.path) {
  567. if let dataFile = try? Data(contentsOf: fileURL) {
  568. var sizeOfFile = Int(dataFile.count / 1000000)
  569. if (sizeOfFile < 1) {
  570. sizeOfFile = Int(dataFile.count / 1000)
  571. if (finalExtFile.count > 4) {
  572. messageText.text = "\(sizeOfFile) kB \u{2022} TXT"
  573. }else {
  574. messageText.text = "\(sizeOfFile) kB \u{2022} \(finalExtFile.uppercased())"
  575. }
  576. } else {
  577. if (finalExtFile.count > 4) {
  578. messageText.text = "\(sizeOfFile) MB \u{2022} TXT"
  579. }else {
  580. messageText.text = "\(sizeOfFile) MB \u{2022} \(finalExtFile.uppercased())"
  581. }
  582. }
  583. } else {
  584. messageText.text = ""
  585. }
  586. }
  587. else if FileEncryption.shared.isSecureExists(filename: fileChat) {
  588. if var dataFile = try? FileEncryption.shared.readSecure(filename: fileChat) {
  589. let dataDecrypt = FileEncryption.shared.decryptFileFromServer(data: dataFile)
  590. if dataDecrypt != nil {
  591. dataFile = dataDecrypt!
  592. }
  593. var sizeOfFile = Int(dataFile.count / 1000000)
  594. if (sizeOfFile < 1) {
  595. sizeOfFile = Int(dataFile.count / 1000)
  596. if (finalExtFile.count > 4) {
  597. messageText.text = "\(sizeOfFile) kB \u{2022} TXT"
  598. }else {
  599. messageText.text = "\(sizeOfFile) kB \u{2022} \(finalExtFile.uppercased())"
  600. }
  601. } else {
  602. if (finalExtFile.count > 4) {
  603. messageText.text = "\(sizeOfFile) MB \u{2022} TXT"
  604. }else {
  605. messageText.text = "\(sizeOfFile) MB \u{2022} \(finalExtFile.uppercased())"
  606. }
  607. }
  608. } else {
  609. messageText.text = ""
  610. }
  611. }
  612. }
  613. containerMessage.addSubview(containerViewFile)
  614. containerViewFile.translatesAutoresizingMaskIntoConstraints = false
  615. containerViewFile.topAnchor.constraint(equalTo: containerMessage.topAnchor, constant: 32).isActive = true
  616. containerViewFile.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15).isActive = true
  617. containerViewFile.bottomAnchor.constraint(equalTo:messageText.topAnchor, constant: -5).isActive = true
  618. containerViewFile.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
  619. containerViewFile.heightAnchor.constraint(equalToConstant: 50).isActive = true
  620. containerViewFile.backgroundColor = .black.withAlphaComponent(0.2)
  621. containerViewFile.layer.cornerRadius = 5.0
  622. containerViewFile.clipsToBounds = true
  623. let imageFile = UIImageView(image: UIImage(systemName: "doc.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 30, weight: .bold, scale: .default)))
  624. containerViewFile.addSubview(imageFile)
  625. let nameFile = UILabel()
  626. containerViewFile.addSubview(nameFile)
  627. imageFile.translatesAutoresizingMaskIntoConstraints = false
  628. imageFile.leadingAnchor.constraint(equalTo: containerViewFile.leadingAnchor, constant: 5).isActive = true
  629. imageFile.trailingAnchor.constraint(equalTo: nameFile.leadingAnchor, constant: -5).isActive = true
  630. imageFile.centerYAnchor.constraint(equalTo: containerViewFile.centerYAnchor).isActive = true
  631. imageFile.widthAnchor.constraint(equalToConstant: 30).isActive = true
  632. imageFile.heightAnchor.constraint(equalToConstant: 30).isActive = true
  633. imageFile.tintColor = .docColor
  634. nameFile.translatesAutoresizingMaskIntoConstraints = false
  635. nameFile.centerYAnchor.constraint(equalTo: containerViewFile.centerYAnchor).isActive = true
  636. nameFile.widthAnchor.constraint(lessThanOrEqualToConstant: 200).isActive = true
  637. nameFile.font = UIFont.systemFont(ofSize: 12 + offset(), weight: .medium)
  638. nameFile.textColor = .white
  639. nameFile.text = textChat?.components(separatedBy: "|")[0]
  640. if (dataMessages[indexPath.row]["progress"] as! Double != 100.0) {
  641. let containerLoading = UIView()
  642. containerViewFile.addSubview(containerLoading)
  643. containerLoading.translatesAutoresizingMaskIntoConstraints = false
  644. containerLoading.centerYAnchor.constraint(equalTo: containerViewFile.centerYAnchor).isActive = true
  645. containerLoading.leadingAnchor.constraint(equalTo: nameFile.trailingAnchor, constant: 5).isActive = true
  646. containerLoading.trailingAnchor.constraint(equalTo: containerViewFile.trailingAnchor, constant: -5).isActive = true
  647. containerLoading.widthAnchor.constraint(equalToConstant: 30).isActive = true
  648. containerLoading.heightAnchor.constraint(equalToConstant: 30).isActive = true
  649. let circlePath = UIBezierPath(arcCenter: CGPoint(x: 15, y: 15), radius: 10, startAngle: -(.pi / 2), endAngle: .pi * 2, clockwise: true)
  650. let trackShape = CAShapeLayer()
  651. trackShape.path = circlePath.cgPath
  652. trackShape.fillColor = UIColor.clear.cgColor
  653. trackShape.lineWidth = 5
  654. trackShape.strokeColor = UIColor.mainColor.withAlphaComponent(0.3).cgColor
  655. containerLoading.layer.addSublayer(trackShape)
  656. let shapeLoading = CAShapeLayer()
  657. shapeLoading.path = circlePath.cgPath
  658. shapeLoading.fillColor = UIColor.clear.cgColor
  659. shapeLoading.lineWidth = 3
  660. shapeLoading.strokeEnd = 0
  661. shapeLoading.strokeColor = UIColor.mainColor.cgColor
  662. containerLoading.layer.addSublayer(shapeLoading)
  663. let imageupload = UIImageView(image: UIImage(systemName: "arrow.up", withConfiguration: UIImage.SymbolConfiguration(pointSize: 10, weight: .bold, scale: .default)))
  664. imageupload.tintColor = .white
  665. containerLoading.addSubview(imageupload)
  666. imageupload.translatesAutoresizingMaskIntoConstraints = false
  667. imageupload.centerYAnchor.constraint(equalTo: containerLoading.centerYAnchor).isActive = true
  668. imageupload.centerXAnchor.constraint(equalTo: containerLoading.centerXAnchor).isActive = true
  669. } else {
  670. nameFile.trailingAnchor.constraint(equalTo: containerViewFile.trailingAnchor, constant: -5).isActive = true
  671. }
  672. let objectTap = ObjectGesture(target: self, action: #selector(contentMessageTapped(_:)))
  673. let sfs = (dataMessages[indexPath.row][TypeDataMessage.spec_file] as? String) ?? ""
  674. containerViewFile.addGestureRecognizer(objectTap)
  675. objectTap.containerFile = containerViewFile
  676. objectTap.labelFile = nameFile
  677. objectTap.file_id = fileChat
  678. objectTap.specFile = sfs
  679. objectTap.indexPath = indexPath
  680. }
  681. let containerLinkMessage = UIView()
  682. if thumbChat.isEmpty && fileChat.isEmpty && !textChat!.isEmpty {
  683. var text = ""
  684. let listTextSplitBreak = textChat!.components(separatedBy: "\n")
  685. let indexFirstLinkSplitBreak = listTextSplitBreak.firstIndex(where: { $0.contains("www.") || $0.contains("http://") || $0.contains("https://") })
  686. if indexFirstLinkSplitBreak != nil {
  687. let listTextSplitSpace = listTextSplitBreak[indexFirstLinkSplitBreak!].components(separatedBy: " ")
  688. 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) })
  689. if indexFirstLinkSplitSpace != nil {
  690. text = listTextSplitSpace[indexFirstLinkSplitSpace!]
  691. }
  692. }
  693. if !text.isEmpty {
  694. func showLink() {
  695. if let data = try! JSONSerialization.jsonObject(with: dataURL.data(using: String.Encoding.utf8)!, options: []) as? [String: Any] {
  696. let title = data["title"] as! String
  697. let description = data["description"] as! String
  698. let imageUrl = data["imageUrl"] as? String
  699. let link = data["link"] as! String
  700. topMarginText.constant = topMarginText.constant + 85
  701. containerMessage.addSubview(containerLinkMessage)
  702. containerLinkMessage.translatesAutoresizingMaskIntoConstraints = false
  703. containerLinkMessage.leadingAnchor.constraint(equalTo:containerMessage.leadingAnchor, constant: 15).isActive = true
  704. if dataMessages[indexPath.row]["attachment_flag"] as? String == "11" {
  705. containerLinkMessage.bottomAnchor.constraint(equalTo: imageSticker.topAnchor, constant: -5).isActive = true
  706. } else {
  707. containerLinkMessage.bottomAnchor.constraint(equalTo: messageText.topAnchor, constant: -5).isActive = true
  708. }
  709. containerLinkMessage.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
  710. containerLinkMessage.heightAnchor.constraint(equalToConstant: 80.0).isActive = true
  711. containerLinkMessage.backgroundColor = .gray.withAlphaComponent(0.2)
  712. let imagePreview = UIImageView()
  713. if imageUrl != nil {
  714. containerLinkMessage.addSubview(imagePreview)
  715. imagePreview.translatesAutoresizingMaskIntoConstraints = false
  716. imagePreview.leadingAnchor.constraint(equalTo: containerLinkMessage.leadingAnchor).isActive = true
  717. imagePreview.bottomAnchor.constraint(equalTo: containerLinkMessage.bottomAnchor).isActive = true
  718. imagePreview.topAnchor.constraint(equalTo: containerLinkMessage.topAnchor).isActive = true
  719. imagePreview.widthAnchor.constraint(equalToConstant: 80.0).isActive = true
  720. if !imageUrl!.starts(with: "https://") {
  721. imagePreview.loadImageAsync(with: "https://www.google.be" + imageUrl!)
  722. } else {
  723. imagePreview.loadImageAsync(with: imageUrl)
  724. }
  725. imagePreview.contentMode = .scaleToFill
  726. }
  727. let titlePreview = UILabel()
  728. containerLinkMessage.addSubview(titlePreview)
  729. titlePreview.translatesAutoresizingMaskIntoConstraints = false
  730. if imageUrl != nil {
  731. titlePreview.leadingAnchor.constraint(equalTo: imagePreview.trailingAnchor, constant: 5.0).isActive = true
  732. } else {
  733. titlePreview.leadingAnchor.constraint(equalTo: containerLinkMessage.leadingAnchor, constant: 5.0).isActive = true
  734. }
  735. titlePreview.topAnchor.constraint(equalTo: containerLinkMessage.topAnchor, constant: 25.0).isActive = true
  736. titlePreview.trailingAnchor.constraint(equalTo: containerLinkMessage.trailingAnchor, constant: -80.0).isActive = true
  737. titlePreview.text = title
  738. titlePreview.font = UIFont.systemFont(ofSize: 14.0 + offset(), weight: .bold)
  739. titlePreview.textColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .black
  740. let descPreview = UILabel()
  741. containerLinkMessage.addSubview(descPreview)
  742. descPreview.translatesAutoresizingMaskIntoConstraints = false
  743. if imageUrl != nil {
  744. descPreview.leadingAnchor.constraint(equalTo: imagePreview.trailingAnchor, constant: 5.0).isActive = true
  745. } else {
  746. descPreview.leadingAnchor.constraint(equalTo: containerLinkMessage.leadingAnchor, constant: 5.0).isActive = true
  747. }
  748. descPreview.topAnchor.constraint(equalTo: titlePreview.bottomAnchor).isActive = true
  749. descPreview.trailingAnchor.constraint(equalTo: containerLinkMessage.trailingAnchor, constant: -80.0).isActive = true
  750. descPreview.text = description
  751. descPreview.font = UIFont.systemFont(ofSize: 12.0 + offset())
  752. descPreview.textColor = .gray
  753. descPreview.numberOfLines = 1
  754. let linkPreview = UILabel()
  755. containerLinkMessage.addSubview(linkPreview)
  756. linkPreview.translatesAutoresizingMaskIntoConstraints = false
  757. if imageUrl != nil {
  758. linkPreview.leadingAnchor.constraint(equalTo: imagePreview.trailingAnchor, constant: 5.0).isActive = true
  759. } else {
  760. linkPreview.leadingAnchor.constraint(equalTo: containerLinkMessage.leadingAnchor, constant: 5.0).isActive = true
  761. }
  762. linkPreview.topAnchor.constraint(equalTo: descPreview.bottomAnchor, constant: 8.0).isActive = true
  763. linkPreview.trailingAnchor.constraint(equalTo: containerLinkMessage.trailingAnchor, constant: -80.0).isActive = true
  764. linkPreview.text = link
  765. linkPreview.font = UIFont.systemFont(ofSize: 10.0 + offset())
  766. linkPreview.textColor = .gray
  767. linkPreview.numberOfLines = 1
  768. let objectTap = ObjectGesture(target: self, action: #selector(tapMessageText(_:)))
  769. objectTap.message_id = text
  770. containerLinkMessage.addGestureRecognizer(objectTap)
  771. }
  772. }
  773. var dataURL = ""
  774. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  775. do {
  776. if let cursor = Database.shared.getRecords(fmdb: fmdb, query: "select data_link from LINK_PREVIEW where link='\(text)'"), cursor.next() {
  777. if let data = cursor.string(forColumnIndex: 0) {
  778. dataURL = data
  779. }
  780. cursor.close()
  781. }
  782. } catch {
  783. rollback.pointee = true
  784. print("Access database error: \(error.localizedDescription)")
  785. }
  786. })
  787. if dataURL.isEmpty {
  788. let urlConfig = URLSessionConfiguration.default
  789. let sessionDelegate = SelfSignedURLSessionDelegate()
  790. let session = URLSession(configuration: urlConfig, delegate: sessionDelegate, delegateQueue: nil)
  791. let slp = SwiftLinkPreview(session: session,
  792. workQueue: SwiftLinkPreview.defaultWorkQueue,
  793. responseQueue: DispatchQueue.main,
  794. cache: DisabledCache.instance)
  795. let preview = slp.preview(text,
  796. onSuccess: { result in
  797. let title = result.title ?? "No Title"
  798. let description = text.contains("google.com") ? "" : result.description
  799. let imageUrl = result.image
  800. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  801. do {
  802. var dataJson: [String: Any] = [:]
  803. dataJson["title"] = title
  804. dataJson["description"] = description
  805. dataJson["imageUrl"] = imageUrl
  806. dataJson["link"] = text
  807. guard let json = String(data: try! JSONSerialization.data(withJSONObject: dataJson, options: []), encoding: String.Encoding.utf8) else {
  808. return
  809. }
  810. _ = try Database.shared.insertRecord(fmdb: fmdb, table: "LINK_PREVIEW", cvalues: [
  811. "id" : "\(Date().currentTimeMillis().toHex())",
  812. "link" : text,
  813. "data_link" : json,
  814. "retry": 0
  815. ], replace: true)
  816. dataURL = json
  817. showLink()
  818. DispatchQueue.main.async {
  819. tableView.reloadRows(at: [indexPath], with: .none)
  820. }
  821. } catch {
  822. rollback.pointee = true
  823. print("Access database error: \(error.localizedDescription)")
  824. }
  825. })
  826. }, onError: { error in
  827. })
  828. } else {
  829. showLink()
  830. }
  831. }
  832. }
  833. return cellMessage
  834. }
  835. @objc func tapAck(_ sender: ObjectGesture) {
  836. let indexPath = sender.indexPath
  837. let dataMessages = self.dataMessages.filter({ $0["chat_date"] as? String ?? "" == dataDates[indexPath.section]})
  838. if dataMessages[indexPath.row]["status"] as? String ?? "" == "8" {
  839. return
  840. }
  841. if !CheckConnection.isConnectedToNetwork() || API.nGetCLXConnState() == 0 {
  842. let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  843. imageView.tintColor = .white
  844. let banner = FloatingNotificationBanner(title: "Check your connection".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
  845. banner.show()
  846. return
  847. }
  848. DispatchQueue.global().async {
  849. let result = Nexilis.write(message: CoreMessage_TMessageBank.getAckLocationMessage(f_pin: dataMessages[indexPath.row]["f_pin"] as? String ?? "", message_id: dataMessages[indexPath.row]["message_id"] as? String ?? "", l_pin: dataMessages[indexPath.row]["l_pin"] as? String ?? "", server_date: "\(Date().currentTimeMillis())", message_scope_id: dataMessages[indexPath.row]["message_scope_id"] as? String ?? "", longitude: "", latitude: "", description: ""))
  850. if result != nil {
  851. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  852. do {
  853. _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE", cvalues: [
  854. "status" : "8"
  855. ], _where: "message_id = '\(dataMessages[indexPath.row]["message_id"] as? String ?? "")'")
  856. } catch {
  857. rollback.pointee = true
  858. print("Access database error: \(error.localizedDescription)")
  859. }
  860. })
  861. DispatchQueue.main.async {
  862. if let index = self.dataMessages.firstIndex(where: {$0["message_id"] as? String == dataMessages[indexPath.row]["message_id"] as? String}) {
  863. self.dataMessages[index]["status"] = "8"
  864. let section = self.dataDates.firstIndex(of: self.dataMessages[index]["chat_date"] as? String ?? "")
  865. let row = self.dataMessages.filter({ $0["chat_date"] as? String ?? "" == self.dataDates[section!]}).firstIndex(where: { $0["message_id"] as? String ?? "" == self.dataMessages[index]["message_id"] as? String ?? ""})
  866. if row != nil && section != nil {
  867. self.tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
  868. }
  869. self.view.makeToast("Confirmation Success.".localized(), duration: 3)
  870. }
  871. }
  872. }
  873. }
  874. }
  875. func highlightedText(for text: String, in range: Range<String.Index>, textView: UITextView) -> NSAttributedString {
  876. let mutableAttributedString = textView.attributedText!.mutableCopy() as! NSMutableAttributedString
  877. mutableAttributedString.addAttribute(.backgroundColor, value: UIColor.lightGray.withAlphaComponent(0.5), range: NSRange(range, in: text))
  878. return mutableAttributedString
  879. }
  880. func removeHighlightedText(for text: String, in range: Range<String.Index>, textView: UITextView) -> NSAttributedString {
  881. let mutableAttributedString = textView.attributedText!.mutableCopy() as! NSMutableAttributedString
  882. mutableAttributedString.removeAttribute(.backgroundColor, range: NSRange(range, in: text))
  883. return mutableAttributedString
  884. }
  885. @objc func tapMessageText(_ sender: ObjectGesture) {
  886. var stringURl = sender.message_id
  887. if stringURl.lowercased().starts(with: "www.") {
  888. stringURl = "https://" + stringURl.replacingOccurrences(of: "www.", with: "")
  889. }
  890. if Nexilis.checkingAccess(key: "secure_browser") {
  891. APIS.openUrl(url: stringURl)
  892. } else {
  893. guard let url = URL(string: stringURl) else { return }
  894. UIApplication.shared.open(url)
  895. }
  896. }
  897. func getData() {
  898. if !dataMessages.isEmpty {
  899. dataMessages.removeAll()
  900. }
  901. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  902. do {
  903. if let cursorData = Database.shared.getRecords(fmdb: fmdb, query: "SELECT message_id, f_pin, l_pin, message_scope_id, server_date, status, message_text, audio_id, video_id, image_id, thumb_id, read_receipts, chat_id, file_id, attachment_flag, reff_id, lock, is_stared, blog_id, attachment_speciality FROM MESSAGE where is_stared=1 order by server_date asc") {
  904. while cursorData.next() {
  905. var row: [String: Any?] = [:]
  906. row["message_id"] = cursorData.string(forColumnIndex: 0)
  907. row["f_pin"] = cursorData.string(forColumnIndex: 1)
  908. row["l_pin"] = cursorData.string(forColumnIndex: 2)
  909. row["message_scope_id"] = cursorData.string(forColumnIndex: 3)
  910. row["server_date"] = cursorData.string(forColumnIndex: 4)
  911. row["status"] = cursorData.string(forColumnIndex: 5)
  912. row["message_text"] = cursorData.string(forColumnIndex: 6)
  913. row["audio_id"] = cursorData.string(forColumnIndex: 7)
  914. row["video_id"] = cursorData.string(forColumnIndex: 8)
  915. row["image_id"] = cursorData.string(forColumnIndex: 9)
  916. row["thumb_id"] = cursorData.string(forColumnIndex: 10)
  917. row["read_receipts"] = cursorData.string(forColumnIndex: 11)
  918. row["chat_id"] = cursorData.string(forColumnIndex: 12)
  919. row["file_id"] = cursorData.string(forColumnIndex: 13)
  920. row["attachment_flag"] = cursorData.string(forColumnIndex: 14)
  921. row["reff_id"] = cursorData.string(forColumnIndex: 15)
  922. row["lock"] = cursorData.string(forColumnIndex: 16)
  923. row["is_stared"] = cursorData.string(forColumnIndex: 17)
  924. row["blog_id"] = cursorData.string(forColumnIndex: 18)
  925. row[TypeDataMessage.spec_file] = cursorData.string(forColumnIndex: 19)
  926. if let cursorStatus = Database.shared.getRecords(fmdb: fmdb, query: "SELECT status FROM MESSAGE_STATUS WHERE message_id='\(row["message_id"] as! String)'") {
  927. while cursorStatus.next() {
  928. row["status"] = cursorStatus.string(forColumnIndex: 0)
  929. }
  930. cursorStatus.close()
  931. }
  932. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  933. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  934. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  935. if let dirPath = paths.first {
  936. let videoURL = URL(fileURLWithPath: dirPath).appendingPathComponent(row["video_id"] as! String)
  937. let fileURL = URL(fileURLWithPath: dirPath).appendingPathComponent(row["file_id"] as! String)
  938. if ((row["video_id"] as! String) != "") {
  939. if FileManager.default.fileExists(atPath: videoURL.path) || FileEncryption.shared.isSecureExists(filename: videoURL.lastPathComponent){
  940. row["progress"] = 100.0
  941. } else {
  942. row["progress"] = 0.0
  943. }
  944. } else {
  945. if FileManager.default.fileExists(atPath: fileURL.path) || FileEncryption.shared.isSecureExists(filename: fileURL.lastPathComponent){
  946. row["progress"] = 100.0
  947. } else {
  948. row["progress"] = 0.0
  949. }
  950. }
  951. }
  952. row["chat_date"] = chatDate(stringDate: row["server_date"] as! String, messageId: row["message_id"] as! String)
  953. dataMessages.append(row)
  954. }
  955. cursorData.close()
  956. }
  957. } catch {
  958. rollback.pointee = true
  959. print("Access database error: \(error.localizedDescription)")
  960. }
  961. })
  962. }
  963. private func chatDate(stringDate: String, messageId: String) -> String {
  964. let date = Date(milliseconds: Int64(stringDate)!)
  965. let calendar = Calendar.current
  966. if (calendar.isDateInToday(date)) {
  967. if !dataDates.contains("Today".localized()){
  968. dataDates.append("Today".localized())
  969. }
  970. return "Today".localized()
  971. } else {
  972. let startOfNow = calendar.startOfDay(for: Date())
  973. let startOfTimeStamp = calendar.startOfDay(for: date)
  974. let components = calendar.dateComponents([.day], from: startOfNow, to: startOfTimeStamp)
  975. let day = -(components.day!)
  976. if day == 1{
  977. if !dataDates.contains("Yesterday".localized()){
  978. dataDates.append("Yesterday".localized())
  979. }
  980. return "Yesterday".localized()
  981. } else if day < 7 {
  982. let formatter = DateFormatter()
  983. formatter.dateFormat = "EEEE"
  984. let lang: String = SecureUserDefaults.shared.value(forKey: "i18n_language") ?? "en"
  985. if lang == "id" {
  986. formatter.locale = NSLocale(localeIdentifier: "id") as Locale?
  987. }
  988. if !dataDates.contains(formatter.string(from: date)){
  989. dataDates.append(formatter.string(from: date))
  990. }
  991. return formatter.string(from: date)
  992. } else {
  993. let formatter = DateFormatter()
  994. formatter.dateFormat = "EE, dd MMM"
  995. let lang: String = SecureUserDefaults.shared.value(forKey: "i18n_language") ?? "en"
  996. if lang == "id" {
  997. formatter.locale = NSLocale(localeIdentifier: "id") as Locale?
  998. }
  999. let stringFormat = formatter.string(from: date as Date)
  1000. if !dataDates.contains(stringFormat){
  1001. dataDates.append(stringFormat)
  1002. }
  1003. return stringFormat
  1004. }
  1005. }
  1006. }
  1007. @objc func contentMessageTapped(_ sender: ObjectGesture) {
  1008. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  1009. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  1010. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  1011. func showMedia(data: Data? = nil, url: URL? = nil, type: Int = 0) {
  1012. let image = UIImage(data: data ?? Data())
  1013. let imageViewer = MediaViewerViewController()
  1014. if type == 0 {
  1015. imageViewer.media = .image(image ?? UIImage())
  1016. } else if type == 1 {
  1017. imageViewer.media = .video(url ?? URL(string: "")!)
  1018. } else if type == 2 {
  1019. imageViewer.media = .gif(UIImage.gifImageWithData(data ?? Data()) ?? UIImage())
  1020. }
  1021. let navigationController = UINavigationController(rootViewController: imageViewer)
  1022. navigationController.defaultStyle()
  1023. navigationController.view.backgroundColor = .clear
  1024. navigationController.modalPresentationCapturesStatusBarAppearance = true
  1025. navigationController.modalPresentationStyle = .overFullScreen
  1026. let backAction = UIAction { _ in
  1027. navigationController.dismiss(animated: true)
  1028. }
  1029. let backButton = UIBarButtonItem(title: nil, image: UIImage(systemName: "chevron.backward"), primaryAction: backAction, menu: nil)
  1030. imageViewer.navigationItem.leftBarButtonItem = backButton
  1031. if Nexilis.checkingAccess(key: "secure_folder_share") || sender.specFile.contains("download") || sender.specFile.contains("share") {
  1032. let shareAction = UIAction { _ in
  1033. var activityViewController = UIActivityViewController(activityItems: [image ?? UIImage()], applicationActivities: nil)
  1034. if type == 1 {
  1035. activityViewController = UIActivityViewController(activityItems: [url ?? URL(string: "")!], applicationActivities: nil)
  1036. }
  1037. activityViewController.popoverPresentationController?.sourceView = imageViewer.view
  1038. imageViewer.present(activityViewController, animated: true, completion: nil)
  1039. }
  1040. let shareButton = UIBarButtonItem(title: nil, image: UIImage(systemName: "square.and.arrow.up"), primaryAction: shareAction, menu: nil)
  1041. imageViewer.navigationItem.rightBarButtonItem = shareButton
  1042. }
  1043. let name = "Favorite Messages".localized()
  1044. imageViewer.title = name
  1045. let transitionDelegate = ZoomTransitioningDelegate()
  1046. transitionDelegate.originImageView = sender.imageView
  1047. navigationController.transitioningDelegate = transitionDelegate
  1048. self.transitioningDelegateRef = transitionDelegate
  1049. present(navigationController, animated: true) {
  1050. imageViewer.animateBackgroundIn()
  1051. }
  1052. }
  1053. if (sender.image_id != "") {
  1054. if let dirPath = paths.first {
  1055. let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(sender.image_id)
  1056. if FileManager.default.fileExists(atPath: imageURL.path) {
  1057. do {
  1058. showMedia(data: try Data(contentsOf: imageURL))
  1059. } catch {
  1060. }
  1061. } else if FileEncryption.shared.isSecureExists(filename: sender.image_id) {
  1062. do {
  1063. if var data = try FileEncryption.shared.readSecure(filename: sender.image_id) {
  1064. let dataDecrypt = FileEncryption.shared.decryptFileFromServer(data: data)
  1065. if dataDecrypt != nil {
  1066. data = dataDecrypt!
  1067. }
  1068. showMedia(data: data)
  1069. }
  1070. }
  1071. catch {
  1072. print("Error reading secure file")
  1073. }
  1074. } else {
  1075. for view in sender.imageView.subviews {
  1076. if view is UIImageView {
  1077. view.removeFromSuperview()
  1078. }
  1079. }
  1080. let activityIndicator = UIActivityIndicatorView(style: .large)
  1081. activityIndicator.color = .mainColor
  1082. activityIndicator.hidesWhenStopped = true
  1083. activityIndicator.center = CGPoint(x:sender.imageView.frame.width/2,
  1084. y: sender.imageView.frame.height/2)
  1085. activityIndicator.startAnimating()
  1086. sender.imageView.addSubview(activityIndicator)
  1087. Download().startHTTP(forKey: sender.image_id) { (name, progress) in
  1088. guard progress == 100 else {
  1089. return
  1090. }
  1091. DispatchQueue.main.async {
  1092. activityIndicator.stopAnimating()
  1093. self.tableChatView.reloadData()
  1094. }
  1095. }
  1096. }
  1097. }
  1098. } else if (sender.gif_id != "") {
  1099. if let dirPath = paths.first {
  1100. let gifURL = URL(fileURLWithPath: dirPath).appendingPathComponent(sender.gif_id)
  1101. if FileManager.default.fileExists(atPath: gifURL.path) {
  1102. do {
  1103. let data = try Data(contentsOf: gifURL)
  1104. showMedia(data: data, type: 2)
  1105. } catch {
  1106. }
  1107. } else if FileEncryption.shared.isSecureExists(filename: sender.gif_id) {
  1108. do {
  1109. if var secureData = try FileEncryption.shared.readSecure(filename: sender.gif_id) {
  1110. let dataDecrypt = FileEncryption.shared.decryptFileFromServer(data: secureData)
  1111. if dataDecrypt != nil {
  1112. secureData = dataDecrypt!
  1113. }
  1114. showMedia(data: secureData, type: 2)
  1115. }
  1116. } catch {
  1117. }
  1118. }
  1119. }
  1120. } else if (sender.video_id != "") {
  1121. if let dirPath = paths.first {
  1122. let videoURL = URL(fileURLWithPath: dirPath).appendingPathComponent(sender.video_id)
  1123. if FileManager.default.fileExists(atPath: videoURL.path) {
  1124. showMedia(url: videoURL, type: 1)
  1125. } else if FileEncryption.shared.isSecureExists(filename: sender.video_id) {
  1126. do {
  1127. if var secureData = try FileEncryption.shared.readSecure(filename: sender.video_id) {
  1128. let dataDecrypt = FileEncryption.shared.decryptFileFromServer(data: secureData)
  1129. if dataDecrypt != nil {
  1130. secureData = dataDecrypt!
  1131. }
  1132. let cachesDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
  1133. let tempPath = cachesDirectory.appendingPathComponent(sender.video_id)
  1134. try secureData.write(to: tempPath)
  1135. showMedia(url: tempPath, type: 1)
  1136. }
  1137. } catch {
  1138. }
  1139. } else {
  1140. for view in sender.imageView.subviews {
  1141. if view is UIImageView {
  1142. view.removeFromSuperview()
  1143. }
  1144. }
  1145. let container = UIView()
  1146. sender.imageView.addSubview(container)
  1147. container.translatesAutoresizingMaskIntoConstraints = false
  1148. container.centerXAnchor.constraint(equalTo: sender.imageView.centerXAnchor).isActive = true
  1149. container.centerYAnchor.constraint(equalTo: sender.imageView.centerYAnchor).isActive = true
  1150. container.widthAnchor.constraint(equalToConstant: 50).isActive = true
  1151. container.heightAnchor.constraint(equalToConstant: 50).isActive = true
  1152. let circlePath = UIBezierPath(arcCenter: CGPoint(x: 25, y: 25), radius: 20, startAngle: -(.pi / 2), endAngle: .pi * 2, clockwise: true)
  1153. let trackShape = CAShapeLayer()
  1154. trackShape.path = circlePath.cgPath
  1155. trackShape.fillColor = UIColor.clear.cgColor
  1156. trackShape.lineWidth = 10
  1157. trackShape.strokeColor = UIColor.mainColor.withAlphaComponent(0.3).cgColor
  1158. container.backgroundColor = .clear
  1159. container.layer.addSublayer(trackShape)
  1160. let shapeLoading = CAShapeLayer()
  1161. shapeLoading.path = circlePath.cgPath
  1162. shapeLoading.fillColor = UIColor.clear.cgColor
  1163. shapeLoading.lineWidth = 10
  1164. shapeLoading.strokeEnd = 0
  1165. shapeLoading.strokeColor = UIColor.mainColor.cgColor
  1166. container.layer.addSublayer(shapeLoading)
  1167. let imageDownload = UIImageView(image: UIImage(systemName: "arrow.down", withConfiguration: UIImage.SymbolConfiguration(pointSize: 10, weight: .bold, scale: .default)))
  1168. imageDownload.tintColor = .white
  1169. container.addSubview(imageDownload)
  1170. imageDownload.translatesAutoresizingMaskIntoConstraints = false
  1171. imageDownload.centerXAnchor.constraint(equalTo: sender.imageView.centerXAnchor).isActive = true
  1172. imageDownload.centerYAnchor.constraint(equalTo: sender.imageView.centerYAnchor).isActive = true
  1173. imageDownload.widthAnchor.constraint(equalToConstant: 30).isActive = true
  1174. imageDownload.heightAnchor.constraint(equalToConstant: 30).isActive = true
  1175. Download().startHTTP(forKey: sender.video_id) { (name, progress) in
  1176. DispatchQueue.main.async {
  1177. guard progress == 100 else {
  1178. shapeLoading.strokeEnd = CGFloat(progress / 100)
  1179. return
  1180. }
  1181. let idx = self.dataMessages.firstIndex(where: { $0["video_id"] as! String == sender.video_id})
  1182. if idx != nil {
  1183. self.dataMessages[idx!]["progress"] = progress
  1184. self.tableChatView.reloadRows(at: [sender.indexPath], with: .none)
  1185. }
  1186. }
  1187. }
  1188. }
  1189. }
  1190. } else if (sender.file_id != "") {
  1191. func showFile(urlFile: URL) {
  1192. let previewController = QLPreviewController()
  1193. previewController.dataSource = self
  1194. let vcHandleFile = UIViewController()
  1195. let nc = UINavigationController(rootViewController: vcHandleFile)
  1196. let attributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
  1197. let navBarAppearance = UINavigationBarAppearance()
  1198. nc.defaultStyle()
  1199. navBarAppearance.configureWithOpaqueBackground()
  1200. navBarAppearance.backgroundColor = self.traitCollection.userInterfaceStyle == .dark ? .blackDarkMode : UIColor.mainColor
  1201. navBarAppearance.titleTextAttributes = attributes
  1202. nc.navigationBar.standardAppearance = navBarAppearance
  1203. nc.navigationBar.scrollEdgeAppearance = navBarAppearance
  1204. let backAction = UIAction { _ in
  1205. nc.dismiss(animated: true)
  1206. }
  1207. let backButton = UIBarButtonItem(title: nil, image: UIImage(systemName: "chevron.backward"), primaryAction: backAction, menu: nil)
  1208. vcHandleFile.navigationItem.leftBarButtonItem = backButton
  1209. if Nexilis.checkingAccess(key: "secure_folder_share") || sender.specFile.contains("download") || sender.specFile.contains("share") {
  1210. let shareAction = UIAction { _ in
  1211. let fileManager = FileManager.default
  1212. let tempURL = fileManager.temporaryDirectory.appendingPathComponent(sender.labelFile.text ?? "")
  1213. do {
  1214. if !fileManager.fileExists(atPath: tempURL.path) {
  1215. try fileManager.copyItem(at: urlFile, to: tempURL)
  1216. }
  1217. let activityViewController = UIActivityViewController(activityItems: [tempURL], applicationActivities: nil)
  1218. activityViewController.popoverPresentationController?.sourceView = vcHandleFile.view
  1219. vcHandleFile.present(activityViewController, animated: true, completion: nil)
  1220. } catch {
  1221. }
  1222. }
  1223. let shareButton = UIBarButtonItem(title: nil, image: UIImage(systemName: "square.and.arrow.up"), primaryAction: shareAction, menu: nil)
  1224. vcHandleFile.navigationItem.rightBarButtonItem = shareButton
  1225. }
  1226. if let viewVc = vcHandleFile.view {
  1227. vcHandleFile.title = sender.labelFile.text
  1228. vcHandleFile.addChild(previewController)
  1229. previewController.dataSource = self
  1230. previewController.view.frame = CGRect(x: 0, y: 0, width: viewVc.bounds.size.width, height: viewVc.bounds.size.height)
  1231. previewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
  1232. viewVc.addSubview(previewController.view)
  1233. previewController.didMove(toParent: vcHandleFile)
  1234. self.present(nc, animated: true)
  1235. }
  1236. }
  1237. if let dirPath = paths.first {
  1238. let fileURL = URL(fileURLWithPath: dirPath).appendingPathComponent(sender.file_id)
  1239. if FileManager.default.fileExists(atPath: fileURL.path) {
  1240. self.previewItem = fileURL as NSURL
  1241. showFile(urlFile: fileURL)
  1242. } else if FileEncryption.shared.isSecureExists(filename: sender.file_id) {
  1243. do {
  1244. if var docData = try FileEncryption.shared.readSecure(filename: sender.file_id) {
  1245. let dataDecrypt = FileEncryption.shared.decryptFileFromServer(data: docData)
  1246. if dataDecrypt != nil {
  1247. docData = dataDecrypt!
  1248. }
  1249. let cachesDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
  1250. let tempPath = cachesDirectory.appendingPathComponent(sender.file_id)
  1251. try docData.write(to: tempPath)
  1252. self.previewItem = tempPath as NSURL
  1253. showFile(urlFile: tempPath)
  1254. }
  1255. }
  1256. catch {
  1257. }
  1258. } else {
  1259. if downloadList[sender.file_id] != nil && downloadList[sender.file_id] == sender.indexPath {
  1260. return
  1261. }
  1262. downloadList[sender.file_id] = sender.indexPath
  1263. for view in sender.containerFile.subviews {
  1264. if !(view is UIImageView) && !(view is UILabel) {
  1265. view.removeFromSuperview()
  1266. }
  1267. }
  1268. let containerLoading = UIView()
  1269. sender.containerFile.addSubview(containerLoading)
  1270. containerLoading.translatesAutoresizingMaskIntoConstraints = false
  1271. containerLoading.centerYAnchor.constraint(equalTo: sender.containerFile.centerYAnchor).isActive = true
  1272. containerLoading.leadingAnchor.constraint(equalTo: sender.labelFile.trailingAnchor, constant: 5).isActive = true
  1273. containerLoading.trailingAnchor.constraint(equalTo: sender.containerFile.trailingAnchor, constant: -5).isActive = true
  1274. containerLoading.widthAnchor.constraint(equalToConstant: 30).isActive = true
  1275. containerLoading.heightAnchor.constraint(equalToConstant: 30).isActive = true
  1276. let circlePath = UIBezierPath(arcCenter: CGPoint(x: 15, y: 15), radius: 10, startAngle: -(.pi / 2), endAngle: .pi * 2, clockwise: true)
  1277. let trackShape = CAShapeLayer()
  1278. trackShape.path = circlePath.cgPath
  1279. trackShape.fillColor = UIColor.clear.cgColor
  1280. trackShape.lineWidth = 5
  1281. trackShape.strokeColor = UIColor.mentionColor.withAlphaComponent(0.3).cgColor
  1282. containerLoading.layer.addSublayer(trackShape)
  1283. let shapeLoading = CAShapeLayer()
  1284. shapeLoading.path = circlePath.cgPath
  1285. shapeLoading.fillColor = UIColor.clear.cgColor
  1286. shapeLoading.lineWidth = 3
  1287. shapeLoading.strokeEnd = 0
  1288. shapeLoading.strokeColor = UIColor.mentionColor.cgColor
  1289. containerLoading.layer.addSublayer(shapeLoading)
  1290. let imageupload = UIImageView(image: UIImage(systemName: "arrow.down", withConfiguration: UIImage.SymbolConfiguration(pointSize: 10, weight: .bold, scale: .default)))
  1291. imageupload.tintColor = .white
  1292. containerLoading.addSubview(imageupload)
  1293. imageupload.translatesAutoresizingMaskIntoConstraints = false
  1294. imageupload.centerYAnchor.constraint(equalTo: containerLoading.centerYAnchor).isActive = true
  1295. imageupload.centerXAnchor.constraint(equalTo: containerLoading.centerXAnchor).isActive = true
  1296. Download().startHTTP(forKey: sender.file_id) { (name, progress) in
  1297. DispatchQueue.main.async {
  1298. guard progress == 100 else {
  1299. shapeLoading.strokeEnd = CGFloat(progress / 100)
  1300. return
  1301. }
  1302. let idx = self.dataMessages.firstIndex(where: { $0["file_id"] as? String ?? "" == sender.file_id})
  1303. if idx != nil {
  1304. self.dataMessages[idx!]["progress"] = progress
  1305. self.tableChatView.reloadRows(at: [sender.indexPath], with: .none)
  1306. }
  1307. }
  1308. }
  1309. }
  1310. }
  1311. } else {
  1312. DispatchQueue.main.async {
  1313. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as! String == sender.message_id})
  1314. if idx == nil {
  1315. return
  1316. }
  1317. let section = self.dataDates.firstIndex(of: self.dataMessages[idx!]["chat_date"] as! String)
  1318. if section == nil {
  1319. return
  1320. }
  1321. let row = self.dataMessages.filter({ $0["chat_date"] as! String == self.dataDates[section!]}).firstIndex(where: { $0["message_id"] as! String == self.dataMessages[idx!]["message_id"] as! String})
  1322. if row == nil {
  1323. return
  1324. }
  1325. let indexPath = IndexPath(row: row!, section: section!)
  1326. self.tableChatView.scrollToRow(at: indexPath, at: .middle, animated: true)
  1327. DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
  1328. if let cell = self.tableChatView.cellForRow(at: indexPath) {
  1329. let containerMessage = cell.contentView.subviews[0]
  1330. let idMe = User.getMyPin() as String?
  1331. if (self.dataMessages[idx!]["f_pin"] as? String == idMe) {
  1332. containerMessage.backgroundColor = .mainColor.withAlphaComponent(0.3)
  1333. DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
  1334. if (self.dataMessages[idx!]["attachment_flag"] as? String == "11") {
  1335. containerMessage.backgroundColor = .clear
  1336. } else {
  1337. containerMessage.backgroundColor = .mainColor
  1338. }
  1339. }
  1340. } else {
  1341. containerMessage.backgroundColor = .whiteBubbleColor.withAlphaComponent(0.3)
  1342. DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
  1343. if (self.dataMessages[idx!]["attachment_flag"] as? String == "11") {
  1344. containerMessage.backgroundColor = .clear
  1345. } else {
  1346. containerMessage.backgroundColor = .whiteBubbleColor
  1347. }
  1348. }
  1349. }
  1350. }
  1351. }
  1352. }
  1353. }
  1354. }
  1355. func getDataProfile(f_pin: String) -> [String: String]{
  1356. var data: [String: String] = [:]
  1357. Database.shared.database?.inTransaction({ fmdb, rollback in
  1358. if let c = Database().getRecords(fmdb: fmdb, query: "select first_name || ' ' || last_name, image_id from BUDDY where f_pin = '\(f_pin)'"), c.next() {
  1359. data["name"] = c.string(forColumnIndex: 0)!.trimmingCharacters(in: .whitespacesAndNewlines)
  1360. data["image_id"] = c.string(forColumnIndex: 1)!
  1361. c.close()
  1362. }
  1363. else if f_pin == "-999" {
  1364. data["name"] = "Bot".localized()
  1365. data["image_id"] = "pb_powered"
  1366. }
  1367. else {
  1368. data["name"] = "Unknown".localized()
  1369. data["image_id"] = ""
  1370. }
  1371. })
  1372. return data
  1373. }
  1374. private func getDataProfileFromMessageId(message_id: String) -> [String: String]{
  1375. var data: [String: String] = [:]
  1376. Database.shared.database?.inTransaction({ fmdb, rollback in
  1377. if let c = Database().getRecords(fmdb: fmdb, query: "select f_display_name from MESSAGE where message_id = '\(message_id)'"), c.next() {
  1378. data["name"] = c.string(forColumnIndex: 0)!
  1379. c.close()
  1380. } else {
  1381. data["name"] = "Unknown".localized()
  1382. data["image_id"] = ""
  1383. }
  1384. })
  1385. return data
  1386. }
  1387. public func contextMenuInteraction(_ interaction: UIContextMenuInteraction, willEndFor configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionAnimating?) {
  1388. if showMenuContext {
  1389. showMenuContext = false
  1390. interaction.view!.removeInteraction(interaction)
  1391. }
  1392. }
  1393. public func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
  1394. let indexPath = self.tableChatView.indexPathForRow(at: interaction.view!.convert(location, to: self.tableChatView))
  1395. let dataMessages = self.dataMessages.filter({ $0["chat_date"] as! String == dataDates[indexPath!.section]})
  1396. let star = UIAction(title: "Unstar".localized(), image: UIImage(systemName: "star.slash.fill"), handler: {(_) in
  1397. DispatchQueue.global().async {
  1398. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  1399. do {
  1400. _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE", cvalues: [
  1401. "is_stared" : 0
  1402. ], _where: "message_id = '\(dataMessages[indexPath!.row]["message_id"] as! String)'")
  1403. } catch {
  1404. rollback.pointee = true
  1405. print("Access database error: \(error.localizedDescription)")
  1406. }
  1407. })
  1408. }
  1409. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as! String == dataMessages[indexPath!.row]["message_id"] as! String})
  1410. if idx != nil{
  1411. self.dataMessages[idx!]["is_stared"] = "0"
  1412. }
  1413. self.dataMessages.remove(at: idx!)
  1414. self.tableChatView.deleteRows(at: [indexPath!], with: .fade)
  1415. if self.dataMessages.filter({ $0["chat_date"] as! String == dataMessages[indexPath!.row]["chat_date"] as! String }).count == 0 {
  1416. self.dataDates.remove(at: indexPath!.section)
  1417. self.tableChatView.deleteSections(IndexSet(integer: indexPath!.section), with: .fade)
  1418. }
  1419. self.tableChatView.reloadData()
  1420. })
  1421. let forward = UIAction(title: "Forward".localized(), image: UIImage(systemName: "arrowshape.turn.up.right.fill"), handler: {(_) in
  1422. let navigationController = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "contactChatNav") as! UINavigationController
  1423. Utils.addBackground(view: navigationController.view)
  1424. navigationController.modalPresentationStyle = .custom
  1425. navigationController.navigationBar.tintColor = .white
  1426. navigationController.navigationBar.barTintColor = self.traitCollection.userInterfaceStyle == .dark ? .blackDarkMode : .mainColor
  1427. navigationController.navigationBar.isTranslucent = false
  1428. navigationController.navigationBar.overrideUserInterfaceStyle = .dark
  1429. navigationController.navigationBar.barStyle = .black
  1430. let cancelButtonAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.foregroundColor: UIColor.white, NSAttributedString.Key.font : UIFont.systemFont(ofSize: 16)]
  1431. UIBarButtonItem.appearance().setTitleTextAttributes(cancelButtonAttributes, for: .normal)
  1432. let textAttributes = [NSAttributedString.Key.foregroundColor:UIColor.white]
  1433. navigationController.navigationBar.titleTextAttributes = textAttributes
  1434. if let controller = navigationController.viewControllers.first as? ContactChatViewController {
  1435. controller.isChooser = { [weak self] scope, pin in
  1436. if scope == "3" {
  1437. let editorPersonalVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorPersonalVC") as! EditorPersonal
  1438. editorPersonalVC.unique_l_pin = pin
  1439. editorPersonalVC.dataMessageForward = [dataMessages[indexPath!.row]]
  1440. self?.navigationController?.replaceAllViewController(with: editorPersonalVC, animated: true)
  1441. } else {
  1442. let editorGroupVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorGroupVC") as! EditorGroup
  1443. editorGroupVC.unique_l_pin = pin
  1444. editorGroupVC.dataMessageForward = [dataMessages[indexPath!.row]]
  1445. self?.navigationController?.replaceAllViewController(with: editorGroupVC, animated: true)
  1446. }
  1447. }
  1448. }
  1449. self.present(navigationController, animated: true, completion: nil)
  1450. })
  1451. let copy = UIAction(title: "Copy".localized(), image: UIImage(systemName: "doc.on.doc.fill"), handler: {(_) in
  1452. if (dataMessages[indexPath!.row]["attachment_flag"] as! String == "0") {
  1453. DispatchQueue.main.async {
  1454. var text = ""
  1455. let stringDate = (dataMessages[indexPath!.row]["server_date"] as! String)
  1456. let date = Date(milliseconds: Int64(stringDate)!)
  1457. let formatterDate = DateFormatter()
  1458. let formatterTime = DateFormatter()
  1459. formatterDate.dateFormat = "dd/MM/yy"
  1460. formatterDate.locale = NSLocale(localeIdentifier: "id") as Locale?
  1461. formatterTime.dateFormat = "HH:mm"
  1462. formatterTime.locale = NSLocale(localeIdentifier: "id") as Locale?
  1463. let dataProfile = self.getDataProfileFromMessageId(message_id: dataMessages[indexPath!.row]["message_id"] as! String)
  1464. if text.isEmpty {
  1465. text = "*[\(formatterDate.string(from: date as Date)) \(formatterTime.string(from: date as Date))] \(dataProfile["name"]!):*\n\(dataMessages[indexPath!.row]["message_text"] as! String)"
  1466. } else {
  1467. text = text + "\n\n*[\(formatterDate.string(from: date as Date)) \(formatterTime.string(from: date as Date))] \(dataProfile["name"]!):*\n\(dataMessages[indexPath!.row]["message_text"] as! String)"
  1468. }
  1469. text = text + "\n\n\nchat " + "Powered by Nexilis".localized()
  1470. DispatchQueue.main.async {
  1471. UIPasteboard.general.string = text
  1472. self.view.makeToast("Text coppied to clipboard".localized(), duration: 3)
  1473. }
  1474. }
  1475. } else {
  1476. DispatchQueue.main.async {
  1477. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  1478. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  1479. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  1480. if let dirPath = paths.first {
  1481. let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(dataMessages[indexPath!.row]["image_id"] as! String)
  1482. if FileManager.default.fileExists(atPath: imageURL.path) {
  1483. let image = UIImage(contentsOfFile: imageURL.path)
  1484. UIPasteboard.general.image = image
  1485. self.view.makeToast("Image coppied to clipboard".localized(), duration: 3)
  1486. } else if FileEncryption.shared.isSecureExists(filename: imageURL.lastPathComponent) {
  1487. do {
  1488. if var imageData = try FileEncryption.shared.readSecure(filename: imageURL.lastPathComponent) {
  1489. let dataDecrypt = FileEncryption.shared.decryptFileFromServer(data: imageData)
  1490. if dataDecrypt != nil {
  1491. imageData = dataDecrypt!
  1492. }
  1493. let image = UIImage(data: imageData)
  1494. UIPasteboard.general.image = image
  1495. self.view.makeToast("Image coppied to clipboard".localized(), duration: 3)
  1496. }
  1497. } catch {
  1498. }
  1499. }
  1500. }
  1501. }
  1502. }
  1503. })
  1504. var children: [UIMenuElement] = [star, copy]
  1505. if self.dataMessages[indexPath!.row]["f_pin"] as! String == "-999" || !(dataMessages[indexPath!.row]["image_id"] as! String).isEmpty || !(dataMessages[indexPath!.row]["video_id"] as! String).isEmpty || !(dataMessages[indexPath!.row]["file_id"] as! String).isEmpty || dataMessages[indexPath!.row]["attachment_flag"] as! String == "11" {
  1506. children = [star]
  1507. }
  1508. if (Nexilis.checkingAccess(key: "secure_folder_forward") || (dataMessages[indexPath!.row][TypeDataMessage.spec_file] as? String ?? "").contains("forward")) && self.dataMessages[indexPath!.row]["f_pin"] as! String != "-999" && dataMessages[indexPath!.row]["read_receipts"] as? String != "8" {
  1509. children.insert(forward, at: 1)
  1510. }
  1511. return UIContextMenuConfiguration(identifier: nil,
  1512. previewProvider: nil) { _ in
  1513. UIMenu(title: "", children: children)
  1514. }
  1515. }
  1516. private func copyOption(indexPath: IndexPath) -> UIMenu {
  1517. let ratingButtonTitles = ["Text".localized(), "Image".localized()]
  1518. let dataMessages = self.dataMessages.filter({ $0["chat_date"] as! String == dataDates[indexPath.section]})
  1519. let copyActions = ratingButtonTitles
  1520. .enumerated()
  1521. .map { index, title in
  1522. return UIAction(
  1523. title: title,
  1524. identifier: nil,
  1525. handler: {(_) in if (index == 0) {
  1526. DispatchQueue.main.async {
  1527. UIPasteboard.general.string = dataMessages[indexPath.row]["message_text"] as? String
  1528. self.view.makeToast("Text coppied to clipboard".localized(), duration: 3)
  1529. }
  1530. } else {
  1531. DispatchQueue.main.async {
  1532. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  1533. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  1534. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  1535. if let dirPath = paths.first {
  1536. let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(dataMessages[indexPath.row]["image_id"] as! String)
  1537. if FileManager.default.fileExists(atPath: imageURL.path) {
  1538. let image = UIImage(contentsOfFile: imageURL.path)
  1539. UIPasteboard.general.image = image
  1540. self.view.makeToast("Image coppied to clipboard".localized(), duration: 3)
  1541. } else if FileEncryption.shared.isSecureExists(filename: imageURL.lastPathComponent) {
  1542. do {
  1543. if var imageData = try FileEncryption.shared.readSecure(filename: imageURL.lastPathComponent) {
  1544. let dataDecrypt = FileEncryption.shared.decryptFileFromServer(data: imageData)
  1545. if dataDecrypt != nil {
  1546. imageData = dataDecrypt!
  1547. }
  1548. let image = UIImage(data: imageData)
  1549. UIPasteboard.general.image = image
  1550. self.view.makeToast("Image coppied to clipboard".localized(), duration: 3)
  1551. }
  1552. } catch {
  1553. }
  1554. }
  1555. }
  1556. }
  1557. }})
  1558. }
  1559. return UIMenu(
  1560. title: "Copy".localized(),
  1561. image: UIImage(systemName: "doc.on.doc.fill"),
  1562. children: copyActions)
  1563. }
  1564. @objc private func cancelDocumentPreview(sender: navigationQLPreviewDocument) {
  1565. sender.navigation.dismiss(animated: true, completion: nil)
  1566. }
  1567. @objc func segmentedControlValueChanged(_ sender: segmentedControllerObject) {
  1568. switch sender.selectedSegmentIndex {
  1569. case 0:
  1570. sender.navigation.viewControllers[0].children[1].view.isHidden = true
  1571. break;
  1572. case 1:
  1573. sender.navigation.viewControllers[0].children[1].view.isHidden = false
  1574. break;
  1575. default:
  1576. break;
  1577. }
  1578. }
  1579. public func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
  1580. 1
  1581. }
  1582. public func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
  1583. return self.previewItem as QLPreviewItem
  1584. }
  1585. public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
  1586. let message = dataMessages[indexPath.row]
  1587. if let attachmentFlag = message["attachment_flag"], let attachmentFlag = attachmentFlag as? String {
  1588. if attachmentFlag == "27" {
  1589. let streamingController = QmeraCreateStreamingViewController()
  1590. streamingController.isJoin = true
  1591. if let messageText = message["message_text"],
  1592. let messageText = messageText as? String,
  1593. var json = try! JSONSerialization.jsonObject(with: messageText.data(using: String.Encoding.utf8)!, options: []) as? [String: Any] {
  1594. if json["blog"] == nil {
  1595. json["blog"] = message["blog_id"] ?? nil
  1596. }
  1597. streamingController.data = json
  1598. }
  1599. let streamingNav = CustomNavigationController(rootViewController: streamingController)
  1600. streamingNav.modalPresentationStyle = .custom
  1601. streamingNav.navigationBar.barTintColor = self.traitCollection.userInterfaceStyle == .dark ? .blackDarkMode : .mainColor
  1602. streamingNav.navigationBar.tintColor = .white
  1603. let textAttributes = [NSAttributedString.Key.foregroundColor:UIColor.white]
  1604. streamingNav.navigationBar.titleTextAttributes = textAttributes
  1605. streamingNav.navigationBar.isTranslucent = false
  1606. navigationController?.present(streamingNav, animated: true, completion: nil)
  1607. }
  1608. }
  1609. if message[TypeDataMessage.message_scope_id] as? String == "3" {
  1610. var pin = message[TypeDataMessage.l_pin] as? String ?? ""
  1611. if pin == (User.getMyPin() ?? "") {
  1612. pin = message[TypeDataMessage.f_pin] as? String ?? ""
  1613. }
  1614. let editorPersonalVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorPersonalVC") as! EditorPersonal
  1615. editorPersonalVC.hidesBottomBarWhenPushed = true
  1616. editorPersonalVC.unique_l_pin = pin
  1617. editorPersonalVC.referenceMessageId = message[TypeDataMessage.message_id] as? String ?? ""
  1618. editorPersonalVC.referenceChatDate = message[TypeDataMessage.chat_date] as? String ?? ""
  1619. navigationController?.show(editorPersonalVC, sender: nil)
  1620. } else {
  1621. var pin = message[TypeDataMessage.chat_id] as? String ?? ""
  1622. if pin.isEmpty {
  1623. pin = message[TypeDataMessage.l_pin] as? String ?? ""
  1624. }
  1625. let editorGroupVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorGroupVC") as! EditorGroup
  1626. editorGroupVC.hidesBottomBarWhenPushed = true
  1627. editorGroupVC.unique_l_pin = pin
  1628. editorGroupVC.referenceMessageId = message[TypeDataMessage.message_id] as? String ?? ""
  1629. editorGroupVC.referenceChatDate = message[TypeDataMessage.chat_date] as? String ?? ""
  1630. navigationController?.show(editorGroupVC, sender: nil)
  1631. }
  1632. }
  1633. public func textView(_ textView: UITextView, shouldInteractWith URL: URL?, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
  1634. var urlString: String?
  1635. if let url = URL {
  1636. urlString = url.absoluteString
  1637. } else {
  1638. if let range = Range(characterRange, in: textView.text) {
  1639. let tappedText = String(textView.text[range])
  1640. urlString = tappedText
  1641. }
  1642. }
  1643. guard let finalURL = urlString else {
  1644. return false
  1645. }
  1646. switch interaction {
  1647. case .invokeDefaultAction:
  1648. let gesture = ObjectGesture()
  1649. gesture.message_id = finalURL
  1650. tapMessageText(gesture)
  1651. return false
  1652. case .presentActions:
  1653. UIPasteboard.general.string = finalURL
  1654. self.view.makeToast("Link Copied".localized(), duration: 3)
  1655. return false
  1656. case .preview:
  1657. return true
  1658. @unknown default:
  1659. return true
  1660. }
  1661. }
  1662. }