EditorStarMessages.swift 96 KB

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