EditorStarMessages.swift 70 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155
  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. public class EditorStarMessages: UIViewController, UITableViewDataSource, UITableViewDelegate, UIContextMenuInteractionDelegate, QLPreviewControllerDataSource {
  13. @IBOutlet var tableChatView: UITableView!
  14. var dataMessages: [[String: Any?]] = []
  15. var dataDates: [String] = []
  16. var previewItem = NSURL()
  17. public override func viewDidLoad() {
  18. super.viewDidLoad()
  19. navigationController?.navigationBar.isTranslucent = false
  20. navigationController?.navigationBar.barTintColor = UIColor.mainColor
  21. navigationController?.navigationBar.tintColor = .white
  22. navigationController?.navigationBar.topItem?.title = ""
  23. self.title = "Star Messages".localized()
  24. let menu = UIMenu(title: "", children: [
  25. UIAction(title: "Unstar all messages".localized(), handler: {(_) in
  26. DispatchQueue.global().async {
  27. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  28. _ = Database.shared.updateAllRecord(fmdb: fmdb, table: "MESSAGE", cvalues: [
  29. "is_stared" : 0
  30. ])
  31. })
  32. }
  33. self.dataMessages.removeAll()
  34. self.tableChatView.reloadData()
  35. }),
  36. ])
  37. getData()
  38. let moreIcon = UIBarButtonItem(image: UIImage(systemName: "ellipsis"), menu: menu)
  39. navigationItem.rightBarButtonItem = moreIcon
  40. navigationItem.rightBarButtonItem?.tintColor = UIColor.secondaryColor
  41. tableChatView.delegate = self
  42. tableChatView.dataSource = self
  43. tableChatView.reloadData()
  44. let center: NotificationCenter = NotificationCenter.default
  45. center.addObserver(self, selector: #selector(onStatusChat(notification:)), name: NSNotification.Name(rawValue: "onMessageChat"), object: nil)
  46. }
  47. @objc func onStatusChat(notification: NSNotification) {
  48. DispatchQueue.main.async {
  49. let data:[AnyHashable : Any] = notification.userInfo!
  50. if let dataMessage = data["message"] as? TMessage {
  51. let chatData = dataMessage.mBodies
  52. if (chatData.keys.contains(CoreMessage_TMessageKey.MESSAGE_ID) && !(chatData[CoreMessage_TMessageKey.MESSAGE_ID]!).contains("-2,")) {
  53. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == chatData[CoreMessage_TMessageKey.MESSAGE_ID]! })
  54. if (idx != nil) {
  55. if (chatData[CoreMessage_TMessageKey.DELETE_MESSAGE_FLAG] == "1") {
  56. let section = self.dataDates.firstIndex(of: self.dataMessages[idx!]["chat_date"] as! String)
  57. let row = self.dataMessages.filter({ $0["chat_date"] as! String == self.dataMessages[idx!]["chat_date"] as! String}).firstIndex(where: { $0["message_id"] as? String == self.dataMessages[idx!]["message_id"] as? String })
  58. self.dataMessages.remove(at: idx!)
  59. if row != nil && section != nil {
  60. let indexPath = IndexPath(row: row!, section: section!)
  61. self.tableChatView.deleteRows(at: [indexPath], with: .fade)
  62. if self.dataMessages.filter({ $0["chat_date"] as! String == self.dataMessages[indexPath.row]["chat_date"] as! String }).count == 0 {
  63. self.dataDates.remove(at: indexPath.section)
  64. self.tableChatView.deleteSections(IndexSet(integer: indexPath.section), with: .fade)
  65. }
  66. }
  67. }
  68. }
  69. }
  70. else if (chatData.keys.contains("message_id")) {
  71. let idx = self.dataMessages.firstIndex(where: { "'\(String(describing: $0["message_id"] as? String))'" == chatData["message_id"]! })
  72. if (idx != nil) {
  73. if (chatData[CoreMessage_TMessageKey.DELETE_MESSAGE_FLAG] == "1") {
  74. let section = self.dataDates.firstIndex(of: self.dataMessages[idx!]["chat_date"] as! String)
  75. let row = self.dataMessages.filter({ $0["chat_date"] as! String == self.dataMessages[idx!]["chat_date"] as! String}).firstIndex(where: { $0["message_id"] as? String == self.dataMessages[idx!]["message_id"] as? String })
  76. self.dataMessages.remove(at: idx!)
  77. if row != nil && section != nil {
  78. let indexPath = IndexPath(row: row!, section: section!)
  79. self.tableChatView.deleteRows(at: [indexPath], with: .fade)
  80. if self.dataMessages.filter({ $0["chat_date"] as! String == self.dataMessages[indexPath.row]["chat_date"] as! String }).count == 0 {
  81. self.dataDates.remove(at: indexPath.section)
  82. self.tableChatView.deleteSections(IndexSet(integer: indexPath.section), with: .fade)
  83. }
  84. }
  85. }
  86. }
  87. }
  88. }
  89. }
  90. }
  91. public func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
  92. let containerView = UIView()
  93. containerView.backgroundColor = .clear
  94. let dateView = UIView()
  95. containerView.addSubview(dateView)
  96. dateView.translatesAutoresizingMaskIntoConstraints = false
  97. var topAnchor = dateView.topAnchor.constraint(equalTo: containerView.topAnchor)
  98. if section == 0 {
  99. topAnchor = dateView.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 10.0)
  100. }
  101. NSLayoutConstraint.activate([
  102. topAnchor,
  103. dateView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),
  104. dateView.centerXAnchor.constraint(equalTo: containerView.centerXAnchor),
  105. dateView.heightAnchor.constraint(equalToConstant: 30),
  106. dateView.widthAnchor.constraint(greaterThanOrEqualToConstant: 60)
  107. ])
  108. dateView.backgroundColor = .orangeColor
  109. dateView.layer.cornerRadius = 15.0
  110. dateView.clipsToBounds = true
  111. let labelDate = UILabel()
  112. dateView.addSubview(labelDate)
  113. labelDate.translatesAutoresizingMaskIntoConstraints = false
  114. NSLayoutConstraint.activate([
  115. labelDate.centerYAnchor.constraint(equalTo: dateView.centerYAnchor),
  116. labelDate.centerXAnchor.constraint(equalTo: dateView.centerXAnchor),
  117. labelDate.leadingAnchor.constraint(equalTo: dateView.leadingAnchor, constant: 10),
  118. labelDate.trailingAnchor.constraint(equalTo: dateView.trailingAnchor, constant: -10),
  119. ])
  120. labelDate.textAlignment = .center
  121. labelDate.textColor = .secondaryColor
  122. labelDate.font = UIFont.systemFont(ofSize: 12, weight: .medium)
  123. labelDate.text = dataDates[section]
  124. return containerView
  125. }
  126. public func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
  127. if section == 0 {
  128. return 40
  129. }
  130. return 30
  131. }
  132. public func numberOfSections(in tableView: UITableView) -> Int {
  133. dataDates.count
  134. }
  135. public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  136. let count = dataMessages.filter({ $0["chat_date"] as! String == dataDates[section] }).count
  137. return count
  138. }
  139. public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  140. let idMe = UserDefaults.standard.string(forKey: "me") as String?
  141. let dataMessages = dataMessages.filter({$0["chat_date"] as! String == dataDates[indexPath.section]})
  142. let cellMessage = UITableViewCell()
  143. cellMessage.backgroundColor = .clear
  144. cellMessage.selectionStyle = .none
  145. let profileMessage = UIImageView()
  146. profileMessage.frame.size = CGSize(width: 35, height: 35)
  147. cellMessage.contentView.addSubview(profileMessage)
  148. profileMessage.translatesAutoresizingMaskIntoConstraints = false
  149. let containerMessage = UIView()
  150. let interaction = UIContextMenuInteraction(delegate: self)
  151. containerMessage.addInteraction(interaction)
  152. containerMessage.isUserInteractionEnabled = true
  153. cellMessage.contentView.addSubview(containerMessage)
  154. containerMessage.translatesAutoresizingMaskIntoConstraints = false
  155. let timeMessage = UILabel()
  156. cellMessage.contentView.addSubview(timeMessage)
  157. timeMessage.translatesAutoresizingMaskIntoConstraints = false
  158. timeMessage.bottomAnchor.constraint(equalTo: cellMessage.contentView.bottomAnchor, constant: -5).isActive = true
  159. let messageText = UILabel()
  160. containerMessage.addSubview(messageText)
  161. messageText.translatesAutoresizingMaskIntoConstraints = false
  162. let topMarginText = messageText.topAnchor.constraint(equalTo: containerMessage.topAnchor, constant: 32)
  163. let dataProfile = getDataProfile(f_pin: dataMessages[indexPath.row]["f_pin"] as! String)
  164. let statusMessage = UIImageView()
  165. if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  166. profileMessage.topAnchor.constraint(equalTo: cellMessage.contentView.topAnchor, constant: 5).isActive = true
  167. profileMessage.trailingAnchor.constraint(equalTo: cellMessage.contentView.trailingAnchor, constant: -15).isActive = true
  168. profileMessage.heightAnchor.constraint(equalToConstant: 37).isActive = true
  169. profileMessage.widthAnchor.constraint(equalToConstant: 35).isActive = true
  170. profileMessage.circle()
  171. profileMessage.clipsToBounds = true
  172. profileMessage.backgroundColor = .lightGray
  173. profileMessage.image = UIImage(systemName: "person")
  174. profileMessage.tintColor = .white
  175. profileMessage.contentMode = .scaleAspectFit
  176. let pictureImage = dataProfile["image_id"]
  177. if (pictureImage != "" && pictureImage != nil) {
  178. profileMessage.setImage(name: pictureImage!)
  179. profileMessage.contentMode = .scaleAspectFill
  180. }
  181. containerMessage.topAnchor.constraint(equalTo: cellMessage.contentView.topAnchor, constant: 5).isActive = true
  182. containerMessage.leadingAnchor.constraint(greaterThanOrEqualTo: cellMessage.contentView.leadingAnchor, constant: 80).isActive = true
  183. containerMessage.bottomAnchor.constraint(equalTo: cellMessage.contentView.bottomAnchor, constant: -5).isActive = true
  184. containerMessage.trailingAnchor.constraint(equalTo: profileMessage.leadingAnchor, constant: -5).isActive = true
  185. containerMessage.widthAnchor.constraint(greaterThanOrEqualToConstant: 46).isActive = true
  186. if (dataMessages[indexPath.row]["attachment_flag"] as? String == "11" && dataMessages[indexPath.row]["reff_id"]as? String == "") {
  187. containerMessage.backgroundColor = .clear
  188. } else {
  189. containerMessage.backgroundColor = .mainColor
  190. }
  191. containerMessage.layer.cornerRadius = 10.0
  192. containerMessage.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner, .layerMinXMinYCorner]
  193. containerMessage.clipsToBounds = true
  194. timeMessage.trailingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: -8).isActive = true
  195. cellMessage.contentView.addSubview(statusMessage)
  196. statusMessage.translatesAutoresizingMaskIntoConstraints = false
  197. statusMessage.bottomAnchor.constraint(equalTo: timeMessage.topAnchor).isActive = true
  198. statusMessage.trailingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: -8).isActive = true
  199. statusMessage.widthAnchor.constraint(equalToConstant: 15).isActive = true
  200. statusMessage.heightAnchor.constraint(equalToConstant: 15).isActive = true
  201. if (dataMessages[indexPath.row]["status"]! as! String == "1" || dataMessages[indexPath.row]["status"]! as! String == "2" ) {
  202. statusMessage.image = UIImage(named: "checklist", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(UIColor.lightGray)
  203. } else if (dataMessages[indexPath.row]["status"]! as! String == "3") {
  204. statusMessage.image = UIImage(named: "double-checklist", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(UIColor.lightGray)
  205. } else {
  206. statusMessage.image = UIImage(named: "double-checklist", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(UIColor.systemBlue)
  207. }
  208. let nameSender = UILabel()
  209. containerMessage.addSubview(nameSender)
  210. nameSender.translatesAutoresizingMaskIntoConstraints = false
  211. nameSender.topAnchor.constraint(equalTo: containerMessage.topAnchor, constant: 15).isActive = true
  212. nameSender.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15).isActive = true
  213. nameSender.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
  214. nameSender.font = UIFont.systemFont(ofSize: 12).bold
  215. nameSender.text = dataProfile["name"]
  216. nameSender.textAlignment = .right
  217. if (dataMessages[indexPath.row]["attachment_flag"] as? String == "11" && dataMessages[indexPath.row]["reff_id"]as? String == "") {
  218. containerMessage.backgroundColor = .clear
  219. nameSender.textColor = .mainColor
  220. } else {
  221. containerMessage.backgroundColor = .mainColor
  222. nameSender.textColor = .white
  223. }
  224. } else {
  225. profileMessage.topAnchor.constraint(equalTo: cellMessage.contentView.topAnchor, constant: 5).isActive = true
  226. profileMessage.leadingAnchor.constraint(equalTo: cellMessage.contentView.leadingAnchor, constant: 15).isActive = true
  227. profileMessage.heightAnchor.constraint(equalToConstant: 37).isActive = true
  228. profileMessage.widthAnchor.constraint(equalToConstant: 35).isActive = true
  229. profileMessage.circle()
  230. profileMessage.clipsToBounds = true
  231. profileMessage.backgroundColor = .lightGray
  232. profileMessage.image = UIImage(systemName: "person")
  233. profileMessage.tintColor = .white
  234. profileMessage.contentMode = .scaleAspectFit
  235. let pictureImage = dataProfile["image_id"]
  236. if dataMessages[indexPath.row]["f_pin"] as! String == "-999" {
  237. profileMessage.image = UIImage(named: "pb_button", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
  238. profileMessage.contentMode = .scaleAspectFit
  239. }
  240. if (pictureImage != "" && pictureImage != nil) {
  241. profileMessage.setImage(name: pictureImage!)
  242. profileMessage.contentMode = .scaleAspectFill
  243. }
  244. containerMessage.topAnchor.constraint(equalTo: cellMessage.contentView.topAnchor, constant: 5).isActive = true
  245. containerMessage.leadingAnchor.constraint(equalTo: profileMessage.trailingAnchor, constant: 5).isActive = true
  246. containerMessage.bottomAnchor.constraint(equalTo: cellMessage.contentView.bottomAnchor, constant: -5).isActive = true
  247. containerMessage.trailingAnchor.constraint(lessThanOrEqualTo: cellMessage.contentView.trailingAnchor, constant: -80).isActive = true
  248. containerMessage.widthAnchor.constraint(greaterThanOrEqualToConstant: 46).isActive = true
  249. if (dataMessages[indexPath.row]["attachment_flag"] as? String == "11" && dataMessages[indexPath.row]["reff_id"]as? String == "") {
  250. containerMessage.backgroundColor = .clear
  251. } else {
  252. containerMessage.backgroundColor = .grayColor
  253. }
  254. containerMessage.layer.cornerRadius = 10.0
  255. containerMessage.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMinYCorner, .layerMaxXMaxYCorner]
  256. containerMessage.clipsToBounds = true
  257. timeMessage.leadingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: 8).isActive = true
  258. let nameSender = UILabel()
  259. containerMessage.addSubview(nameSender)
  260. nameSender.translatesAutoresizingMaskIntoConstraints = false
  261. nameSender.topAnchor.constraint(equalTo: containerMessage.topAnchor, constant: 15).isActive = true
  262. nameSender.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15).isActive = true
  263. nameSender.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
  264. nameSender.font = UIFont.systemFont(ofSize: 12).bold
  265. nameSender.text = dataProfile["name"]
  266. nameSender.textAlignment = .left
  267. nameSender.textColor = .mainColor
  268. }
  269. if (dataMessages[indexPath.row]["is_stared"] as? String == "1") {
  270. let imageStared = UIImageView()
  271. cellMessage.contentView.addSubview(imageStared)
  272. imageStared.translatesAutoresizingMaskIntoConstraints = false
  273. if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  274. imageStared.bottomAnchor.constraint(equalTo: statusMessage.topAnchor).isActive = true
  275. imageStared.trailingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: -8).isActive = true
  276. } else {
  277. imageStared.bottomAnchor.constraint(equalTo: timeMessage.topAnchor).isActive = true
  278. imageStared.leadingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: 8).isActive = true
  279. }
  280. imageStared.widthAnchor.constraint(equalToConstant: 15).isActive = true
  281. imageStared.heightAnchor.constraint(equalToConstant: 15).isActive = true
  282. imageStared.image = UIImage(systemName: "star.fill")
  283. imageStared.backgroundColor = .clear
  284. imageStared.tintColor = .systemYellow
  285. }
  286. messageText.numberOfLines = 0
  287. messageText.lineBreakMode = .byWordWrapping
  288. containerMessage.addSubview(messageText)
  289. topMarginText.isActive = true
  290. if dataMessages[indexPath.row]["attachment_flag"] as! String == "27" || dataMessages[indexPath.row]["attachment_flag"] as! String == "26" {
  291. messageText.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 85).isActive = true
  292. let imageLS = UIImageView()
  293. containerMessage.addSubview(imageLS)
  294. imageLS.translatesAutoresizingMaskIntoConstraints = false
  295. NSLayoutConstraint.activate([
  296. imageLS.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15.0),
  297. imageLS.trailingAnchor.constraint(equalTo: messageText.leadingAnchor, constant: -10.0),
  298. imageLS.centerYAnchor.constraint(equalTo: containerMessage.centerYAnchor),
  299. imageLS.heightAnchor.constraint(equalToConstant: 60.0)
  300. ])
  301. if dataMessages[indexPath.row]["attachment_flag"] as! String == "26" {
  302. imageLS.image = UIImage(named: "pb_seminar_wpr", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
  303. } else if dataMessages[indexPath.row]["attachment_flag"] as! String == "27" {
  304. imageLS.image = UIImage(named: "pb_live_tv", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
  305. }
  306. } else {
  307. messageText.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15).isActive = true
  308. }
  309. messageText.bottomAnchor.constraint(equalTo: containerMessage.bottomAnchor, constant: -15).isActive = true
  310. messageText.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
  311. var textChat = (dataMessages[indexPath.row]["message_text"])! as? String
  312. if (dataMessages[indexPath.row]["lock"] != nil && (dataMessages[indexPath.row]["lock"])! as? String == "1") {
  313. if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  314. textChat = "🚫 _"+"You were deleted this message".localized()+"_"
  315. } else {
  316. textChat = "🚫 _"+"This message was deleted".localized()+"_"
  317. }
  318. }
  319. if let attachmentFlag = dataMessages[indexPath.row]["attachment_flag"], let attachmentFlag = attachmentFlag as? String {
  320. if attachmentFlag == "27" || attachmentFlag == "26", let data = textChat { // live streaming
  321. if let json = try! JSONSerialization.jsonObject(with: data.data(using: String.Encoding.utf8)!, options: []) as? [String: Any] {
  322. Database().database?.inTransaction({ fmdb, rollback in
  323. let title = json["title"] as! String
  324. let description = json["description"] as! String
  325. let start = json["time"] as! Int64
  326. let by = json["by"] as! String
  327. var type = "*Live Streaming*"
  328. if attachmentFlag == "26" {
  329. type = "*Seminar*"
  330. }
  331. if let c = Database().getRecords(fmdb: fmdb, query: "select first_name || ' ' || last_name from BUDDY where f_pin = '\(by)'"), c.next() {
  332. let name = c.string(forColumnIndex: 0)!
  333. messageText.attributedText = "\(type) \nTitle: \(title) \nDescription: \(description) \nStart: \(Date(milliseconds: start).format(dateFormat: "dd/MM/yyyy HH:mm")) \nBroadcaster: \(name)".richText()
  334. c.close()
  335. } else {
  336. messageText.attributedText = ("\(type) \nTitle: \(title) \nDescription: \(description) \nStart: \(Date(milliseconds: start).format(dateFormat: "dd/MM/yyyy HH:mm")) \nBroadcaster: " + "Unknown".localized()).richText()
  337. }
  338. })
  339. }
  340. } else if attachmentFlag == "11" {
  341. messageText.text = ""
  342. topMarginText.constant = topMarginText.constant + 100
  343. let imageSticker = UIImageView()
  344. containerMessage.addSubview(imageSticker)
  345. imageSticker.translatesAutoresizingMaskIntoConstraints = false
  346. imageSticker.topAnchor.constraint(equalTo: containerMessage.topAnchor, constant: 27.0).isActive = true
  347. imageSticker.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor).isActive = true
  348. imageSticker.bottomAnchor.constraint(equalTo: messageText.topAnchor, constant: -5).isActive = true
  349. imageSticker.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor).isActive = true
  350. imageSticker.widthAnchor.constraint(equalToConstant: 80).isActive = true
  351. imageSticker.image = UIImage(named: (textChat?.components(separatedBy: "/")[1])!, in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
  352. imageSticker.contentMode = .scaleAspectFit
  353. }
  354. else {
  355. messageText.attributedText = textChat!.richText()
  356. }
  357. } else {
  358. messageText.attributedText = textChat!.richText()
  359. }
  360. messageText.font = UIFont.systemFont(ofSize: 12)
  361. if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  362. messageText.textColor = .white
  363. } else {
  364. messageText.textColor = .black
  365. }
  366. let stringDate = (dataMessages[indexPath.row]["server_date"] as! String)
  367. let date = Date(milliseconds: Int64(stringDate)!)
  368. let formatter = DateFormatter()
  369. formatter.dateFormat = "HH:mm"
  370. formatter.locale = NSLocale(localeIdentifier: "id") as Locale?
  371. timeMessage.text = formatter.string(from: date as Date)
  372. timeMessage.font = UIFont.systemFont(ofSize: 10, weight: .medium)
  373. timeMessage.textColor = .lightGray
  374. let thumbChat = dataMessages[indexPath.row]["thumb_id"] as! String
  375. let imageChat = dataMessages[indexPath.row]["image_id"] as! String
  376. let videoChat = dataMessages[indexPath.row]["video_id"] as! String
  377. let fileChat = dataMessages[indexPath.row]["file_id"] as! String
  378. let imageThumb = UIImageView()
  379. let containerViewFile = UIView()
  380. if (thumbChat != "") {
  381. topMarginText.constant = topMarginText.constant + 205
  382. containerMessage.addSubview(imageThumb)
  383. imageThumb.translatesAutoresizingMaskIntoConstraints = false
  384. imageThumb.topAnchor.constraint(equalTo: containerMessage.topAnchor, constant: 32).isActive = true
  385. imageThumb.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15).isActive = true
  386. imageThumb.bottomAnchor.constraint(equalTo: messageText.topAnchor, constant: -5).isActive = true
  387. imageThumb.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
  388. imageThumb.widthAnchor.constraint(equalToConstant: self.view.frame.size.width * 0.6).isActive = true
  389. imageThumb.layer.cornerRadius = 5.0
  390. imageThumb.clipsToBounds = true
  391. imageThumb.contentMode = .scaleAspectFill
  392. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  393. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  394. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  395. if let dirPath = paths.first {
  396. let thumbURL = URL(fileURLWithPath: dirPath).appendingPathComponent(thumbChat)
  397. let image = UIImage(contentsOfFile: thumbURL.path)
  398. imageThumb.image = image
  399. let videoURL = URL(fileURLWithPath: dirPath).appendingPathComponent(videoChat)
  400. let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(imageChat)
  401. if !FileManager.default.fileExists(atPath: imageURL.path) || !FileManager.default.fileExists(atPath: videoURL.path){
  402. let blurEffect = UIBlurEffect(style: UIBlurEffect.Style.light)
  403. let blurEffectView = UIVisualEffectView(effect: blurEffect)
  404. blurEffectView.frame = CGRect(x: 0, y: 0, width: imageThumb.frame.size.width, height: imageThumb.frame.size.height)
  405. blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
  406. let imageDownload = UIImageView(image: UIImage(systemName: "arrow.down.circle.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 50, weight: .bold, scale: .default)))
  407. imageThumb.addSubview(blurEffectView)
  408. imageThumb.addSubview(imageDownload)
  409. imageDownload.tintColor = .black.withAlphaComponent(0.3)
  410. imageDownload.translatesAutoresizingMaskIntoConstraints = false
  411. imageDownload.centerXAnchor.constraint(equalTo: imageThumb.centerXAnchor).isActive = true
  412. imageDownload.centerYAnchor.constraint(equalTo: imageThumb.centerYAnchor).isActive = true
  413. }
  414. }
  415. if (videoChat != "") {
  416. 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))
  417. imagePlay.circle()
  418. imageThumb.addSubview(imagePlay)
  419. imagePlay.backgroundColor = .black.withAlphaComponent(0.3)
  420. imagePlay.translatesAutoresizingMaskIntoConstraints = false
  421. imagePlay.centerXAnchor.constraint(equalTo: imageThumb.centerXAnchor).isActive = true
  422. imagePlay.centerYAnchor.constraint(equalTo: imageThumb.centerYAnchor).isActive = true
  423. }
  424. if (dataMessages[indexPath.row]["progress"] as! Double != 100.0 && dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  425. let container = UIView()
  426. imageThumb.addSubview(container)
  427. container.translatesAutoresizingMaskIntoConstraints = false
  428. container.bottomAnchor.constraint(equalTo: imageThumb.bottomAnchor, constant: -10).isActive = true
  429. container.leadingAnchor.constraint(equalTo: imageThumb.leadingAnchor, constant: 10).isActive = true
  430. container.widthAnchor.constraint(equalToConstant: 30).isActive = true
  431. container.heightAnchor.constraint(equalToConstant: 30).isActive = true
  432. let circlePath = UIBezierPath(arcCenter: CGPoint(x: 10, y: 20), radius: 15, startAngle: -(.pi / 2), endAngle: .pi * 2, clockwise: true)
  433. let trackShape = CAShapeLayer()
  434. trackShape.path = circlePath.cgPath
  435. trackShape.fillColor = UIColor.black.withAlphaComponent(0.3).cgColor
  436. trackShape.lineWidth = 3
  437. trackShape.strokeColor = UIColor.mainColor.withAlphaComponent(0.3).cgColor
  438. container.backgroundColor = .clear
  439. container.layer.addSublayer(trackShape)
  440. let shapeLoading = CAShapeLayer()
  441. shapeLoading.path = circlePath.cgPath
  442. shapeLoading.fillColor = UIColor.clear.cgColor
  443. shapeLoading.lineWidth = 3
  444. shapeLoading.strokeEnd = 0
  445. shapeLoading.strokeColor = UIColor.mainColor.cgColor
  446. container.layer.addSublayer(shapeLoading)
  447. let imageupload = UIImageView(image: UIImage(systemName: "arrow.up", withConfiguration: UIImage.SymbolConfiguration(pointSize: 10, weight: .bold, scale: .default)))
  448. imageupload.tintColor = .white
  449. container.addSubview(imageupload)
  450. imageupload.translatesAutoresizingMaskIntoConstraints = false
  451. imageupload.bottomAnchor.constraint(equalTo: imageThumb.bottomAnchor, constant: -10).isActive = true
  452. imageupload.leadingAnchor.constraint(equalTo: imageThumb.leadingAnchor, constant: 10).isActive = true
  453. imageupload.widthAnchor.constraint(equalToConstant: 20).isActive = true
  454. imageupload.heightAnchor.constraint(equalToConstant: 20).isActive = true
  455. }
  456. let objectTap = ObjectGesture(target: self, action: #selector(contentMessageTapped(_:)))
  457. imageThumb.isUserInteractionEnabled = true
  458. imageThumb.addGestureRecognizer(objectTap)
  459. objectTap.image_id = imageChat
  460. objectTap.video_id = videoChat
  461. objectTap.imageView = imageThumb
  462. objectTap.indexPath = indexPath
  463. }
  464. if (fileChat != "") {
  465. topMarginText.constant = topMarginText.constant + 55
  466. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  467. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  468. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  469. let arrExtFile = (textChat?.components(separatedBy: "|")[0])?.split(separator: ".")
  470. let finalExtFile = arrExtFile![arrExtFile!.count - 1]
  471. if let dirPath = paths.first {
  472. let fileURL = URL(fileURLWithPath: dirPath).appendingPathComponent(fileChat)
  473. if let dataFile = try? Data(contentsOf: fileURL) {
  474. var sizeOfFile = Int(dataFile.count / 1000000)
  475. if (sizeOfFile < 1) {
  476. sizeOfFile = Int(dataFile.count / 1000)
  477. if (finalExtFile.count > 4) {
  478. messageText.text = "\(sizeOfFile) kB \u{2022} TXT"
  479. }else {
  480. messageText.text = "\(sizeOfFile) kB \u{2022} \(finalExtFile.uppercased())"
  481. }
  482. } else {
  483. if (finalExtFile.count > 4) {
  484. messageText.text = "\(sizeOfFile) MB \u{2022} TXT"
  485. }else {
  486. messageText.text = "\(sizeOfFile) MB \u{2022} \(finalExtFile.uppercased())"
  487. }
  488. }
  489. } else {
  490. messageText.text = ""
  491. }
  492. }
  493. containerMessage.addSubview(containerViewFile)
  494. containerViewFile.translatesAutoresizingMaskIntoConstraints = false
  495. containerViewFile.topAnchor.constraint(equalTo: containerMessage.topAnchor, constant: 32).isActive = true
  496. containerViewFile.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15).isActive = true
  497. containerViewFile.bottomAnchor.constraint(equalTo:messageText.topAnchor, constant: -5).isActive = true
  498. containerViewFile.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
  499. containerViewFile.heightAnchor.constraint(equalToConstant: 50).isActive = true
  500. containerViewFile.backgroundColor = .black.withAlphaComponent(0.2)
  501. containerViewFile.layer.cornerRadius = 5.0
  502. containerViewFile.clipsToBounds = true
  503. let imageFile = UIImageView(image: UIImage(systemName: "doc.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 30, weight: .bold, scale: .default)))
  504. containerViewFile.addSubview(imageFile)
  505. let nameFile = UILabel()
  506. containerViewFile.addSubview(nameFile)
  507. imageFile.translatesAutoresizingMaskIntoConstraints = false
  508. imageFile.leadingAnchor.constraint(equalTo: containerViewFile.leadingAnchor, constant: 5).isActive = true
  509. imageFile.trailingAnchor.constraint(equalTo: nameFile.leadingAnchor, constant: -5).isActive = true
  510. imageFile.centerYAnchor.constraint(equalTo: containerViewFile.centerYAnchor).isActive = true
  511. imageFile.widthAnchor.constraint(equalToConstant: 30).isActive = true
  512. imageFile.heightAnchor.constraint(equalToConstant: 30).isActive = true
  513. imageFile.tintColor = .docColor
  514. nameFile.translatesAutoresizingMaskIntoConstraints = false
  515. nameFile.centerYAnchor.constraint(equalTo: containerViewFile.centerYAnchor).isActive = true
  516. nameFile.widthAnchor.constraint(lessThanOrEqualToConstant: 200).isActive = true
  517. nameFile.font = UIFont.systemFont(ofSize: 12, weight: .medium)
  518. nameFile.textColor = .white
  519. nameFile.text = textChat?.components(separatedBy: "|")[0]
  520. if (dataMessages[indexPath.row]["progress"] as! Double != 100.0) {
  521. let containerLoading = UIView()
  522. containerViewFile.addSubview(containerLoading)
  523. containerLoading.translatesAutoresizingMaskIntoConstraints = false
  524. containerLoading.centerYAnchor.constraint(equalTo: containerViewFile.centerYAnchor).isActive = true
  525. containerLoading.leadingAnchor.constraint(equalTo: nameFile.trailingAnchor, constant: 5).isActive = true
  526. containerLoading.trailingAnchor.constraint(equalTo: containerViewFile.trailingAnchor, constant: -5).isActive = true
  527. containerLoading.widthAnchor.constraint(equalToConstant: 30).isActive = true
  528. containerLoading.heightAnchor.constraint(equalToConstant: 30).isActive = true
  529. let circlePath = UIBezierPath(arcCenter: CGPoint(x: 15, y: 15), radius: 10, startAngle: -(.pi / 2), endAngle: .pi * 2, clockwise: true)
  530. let trackShape = CAShapeLayer()
  531. trackShape.path = circlePath.cgPath
  532. trackShape.fillColor = UIColor.clear.cgColor
  533. trackShape.lineWidth = 5
  534. trackShape.strokeColor = UIColor.mainColor.withAlphaComponent(0.3).cgColor
  535. containerLoading.layer.addSublayer(trackShape)
  536. let shapeLoading = CAShapeLayer()
  537. shapeLoading.path = circlePath.cgPath
  538. shapeLoading.fillColor = UIColor.clear.cgColor
  539. shapeLoading.lineWidth = 3
  540. shapeLoading.strokeEnd = 0
  541. shapeLoading.strokeColor = UIColor.mainColor.cgColor
  542. containerLoading.layer.addSublayer(shapeLoading)
  543. let imageupload = UIImageView(image: UIImage(systemName: "arrow.up", withConfiguration: UIImage.SymbolConfiguration(pointSize: 10, weight: .bold, scale: .default)))
  544. imageupload.tintColor = .white
  545. containerLoading.addSubview(imageupload)
  546. imageupload.translatesAutoresizingMaskIntoConstraints = false
  547. imageupload.centerYAnchor.constraint(equalTo: containerLoading.centerYAnchor).isActive = true
  548. imageupload.centerXAnchor.constraint(equalTo: containerLoading.centerXAnchor).isActive = true
  549. } else {
  550. nameFile.trailingAnchor.constraint(equalTo: containerViewFile.trailingAnchor, constant: -5).isActive = true
  551. }
  552. let objectTap = ObjectGesture(target: self, action: #selector(contentMessageTapped(_:)))
  553. containerViewFile.addGestureRecognizer(objectTap)
  554. objectTap.containerFile = containerViewFile
  555. objectTap.labelFile = nameFile
  556. objectTap.file_id = fileChat
  557. objectTap.indexPath = indexPath
  558. }
  559. return cellMessage
  560. }
  561. func getData() {
  562. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  563. 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") {
  564. while cursorData.next() {
  565. var row: [String: Any?] = [:]
  566. row["message_id"] = cursorData.string(forColumnIndex: 0)
  567. row["f_pin"] = cursorData.string(forColumnIndex: 1)
  568. row["l_pin"] = cursorData.string(forColumnIndex: 2)
  569. row["message_scope_id"] = cursorData.string(forColumnIndex: 3)
  570. row["server_date"] = cursorData.string(forColumnIndex: 4)
  571. row["status"] = cursorData.string(forColumnIndex: 5)
  572. row["message_text"] = cursorData.string(forColumnIndex: 6)
  573. row["audio_id"] = cursorData.string(forColumnIndex: 7)
  574. row["video_id"] = cursorData.string(forColumnIndex: 8)
  575. row["image_id"] = cursorData.string(forColumnIndex: 9)
  576. row["thumb_id"] = cursorData.string(forColumnIndex: 10)
  577. row["read_receipts"] = cursorData.int(forColumnIndex: 11)
  578. row["chat_id"] = cursorData.string(forColumnIndex: 12)
  579. row["file_id"] = cursorData.string(forColumnIndex: 13)
  580. row["attachment_flag"] = cursorData.string(forColumnIndex: 14)
  581. row["reff_id"] = cursorData.string(forColumnIndex: 15)
  582. row["lock"] = cursorData.string(forColumnIndex: 16)
  583. row["is_stared"] = cursorData.string(forColumnIndex: 17)
  584. row["blog_id"] = cursorData.string(forColumnIndex: 18)
  585. if let cursorStatus = Database.shared.getRecords(fmdb: fmdb, query: "SELECT status FROM MESSAGE_STATUS WHERE message_id='\(row["message_id"] as! String)'") {
  586. while cursorStatus.next() {
  587. row["status"] = cursorStatus.string(forColumnIndex: 0)
  588. }
  589. cursorStatus.close()
  590. }
  591. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  592. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  593. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  594. if let dirPath = paths.first {
  595. let videoURL = URL(fileURLWithPath: dirPath).appendingPathComponent(row["video_id"] as! String)
  596. let fileURL = URL(fileURLWithPath: dirPath).appendingPathComponent(row["file_id"] as! String)
  597. if ((row["video_id"] as! String) != "") {
  598. if FileManager.default.fileExists(atPath: videoURL.path){
  599. row["progress"] = 100.0
  600. } else {
  601. row["progress"] = 0.0
  602. }
  603. } else {
  604. if FileManager.default.fileExists(atPath: fileURL.path){
  605. row["progress"] = 100.0
  606. } else {
  607. row["progress"] = 0.0
  608. }
  609. }
  610. }
  611. row["chat_date"] = chatDate(stringDate: row["server_date"] as! String, messageId: row["message_id"] as! String)
  612. dataMessages.append(row)
  613. }
  614. cursorData.close()
  615. }
  616. })
  617. }
  618. private func chatDate(stringDate: String, messageId: String) -> String {
  619. let date = Date(milliseconds: Int64(stringDate)!)
  620. let calendar = Calendar.current
  621. if (calendar.isDateInToday(date)) {
  622. if !dataDates.contains("Today".localized()){
  623. dataDates.append("Today".localized())
  624. }
  625. return "Today".localized()
  626. } else {
  627. let startOfNow = calendar.startOfDay(for: Date())
  628. let startOfTimeStamp = calendar.startOfDay(for: date)
  629. let components = calendar.dateComponents([.day], from: startOfNow, to: startOfTimeStamp)
  630. let day = -(components.day!)
  631. if day == 1{
  632. if !dataDates.contains("Yesterday".localized()){
  633. dataDates.append("Yesterday".localized())
  634. }
  635. return "Yesterday".localized()
  636. } else if day < 7 {
  637. if !dataDates.contains("\(day) " + "days".localized() + " " + "ago".localized()){
  638. dataDates.append("\(day) " + "days".localized() + " " + "ago".localized())
  639. }
  640. return "\(day) " + "days".localized() + " " + "ago".localized()
  641. } else {
  642. let formatter = DateFormatter()
  643. formatter.dateFormat = "dd MMMM yyyy"
  644. formatter.locale = NSLocale(localeIdentifier: "id") as Locale?
  645. let stringFormat = formatter.string(from: date as Date)
  646. if !dataDates.contains(stringFormat){
  647. dataDates.append(stringFormat)
  648. }
  649. return stringFormat
  650. }
  651. }
  652. }
  653. @objc func contentMessageTapped(_ sender: ObjectGesture) {
  654. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  655. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  656. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  657. if (sender.image_id != "") {
  658. if let dirPath = paths.first {
  659. let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(sender.image_id)
  660. if FileManager.default.fileExists(atPath: imageURL.path) {
  661. let image = UIImage(contentsOfFile: imageURL.path)
  662. let previewImageVC = PreviewAttachmentImageVideo(nibName: "PreviewAttachmentImageVideo", bundle: Bundle.resourceBundle(for: Nexilis.self))
  663. previewImageVC.image = image
  664. previewImageVC.isHiddenTextField = true
  665. previewImageVC.modalPresentationStyle = .custom
  666. previewImageVC.modalTransitionStyle = .crossDissolve
  667. self.present(previewImageVC, animated: true, completion: nil)
  668. } else {
  669. for view in sender.imageView.subviews {
  670. if view is UIImageView {
  671. view.removeFromSuperview()
  672. }
  673. }
  674. let activityIndicator = UIActivityIndicatorView(style: .large)
  675. activityIndicator.color = .mainColor
  676. activityIndicator.hidesWhenStopped = true
  677. activityIndicator.center = CGPoint(x:sender.imageView.frame.width/2,
  678. y: sender.imageView.frame.height/2)
  679. activityIndicator.startAnimating()
  680. sender.imageView.addSubview(activityIndicator)
  681. Download().start(forKey: sender.image_id) { (name, progress) in
  682. guard progress == 100 else {
  683. return
  684. }
  685. let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(sender.image_id)
  686. let image = UIImage(contentsOfFile: imageURL.path)
  687. let save = UserDefaults.standard.bool(forKey: "saveToGallery")
  688. if save {
  689. UIImageWriteToSavedPhotosAlbum(image!, nil, nil, nil)
  690. }
  691. DispatchQueue.main.async {
  692. activityIndicator.stopAnimating()
  693. self.tableChatView.reloadData()
  694. }
  695. }
  696. }
  697. }
  698. } else if (sender.video_id != "") {
  699. if let dirPath = paths.first {
  700. let videoURL = URL(fileURLWithPath: dirPath).appendingPathComponent(sender.video_id)
  701. if FileManager.default.fileExists(atPath: videoURL.path) {
  702. let player = AVPlayer(url: videoURL as URL)
  703. let playerVC = AVPlayerViewController()
  704. playerVC.modalPresentationStyle = .custom
  705. playerVC.player = player
  706. self.present(playerVC, animated: true, completion: nil)
  707. } else {
  708. for view in sender.imageView.subviews {
  709. if view is UIImageView {
  710. view.removeFromSuperview()
  711. }
  712. }
  713. let container = UIView()
  714. sender.imageView.addSubview(container)
  715. container.translatesAutoresizingMaskIntoConstraints = false
  716. container.centerXAnchor.constraint(equalTo: sender.imageView.centerXAnchor).isActive = true
  717. container.centerYAnchor.constraint(equalTo: sender.imageView.centerYAnchor).isActive = true
  718. container.widthAnchor.constraint(equalToConstant: 50).isActive = true
  719. container.heightAnchor.constraint(equalToConstant: 50).isActive = true
  720. let circlePath = UIBezierPath(arcCenter: CGPoint(x: 25, y: 25), radius: 20, startAngle: -(.pi / 2), endAngle: .pi * 2, clockwise: true)
  721. let trackShape = CAShapeLayer()
  722. trackShape.path = circlePath.cgPath
  723. trackShape.fillColor = UIColor.clear.cgColor
  724. trackShape.lineWidth = 10
  725. trackShape.strokeColor = UIColor.mainColor.withAlphaComponent(0.3).cgColor
  726. container.backgroundColor = .clear
  727. container.layer.addSublayer(trackShape)
  728. let shapeLoading = CAShapeLayer()
  729. shapeLoading.path = circlePath.cgPath
  730. shapeLoading.fillColor = UIColor.clear.cgColor
  731. shapeLoading.lineWidth = 10
  732. shapeLoading.strokeEnd = 0
  733. shapeLoading.strokeColor = UIColor.mainColor.cgColor
  734. container.layer.addSublayer(shapeLoading)
  735. let imageDownload = UIImageView(image: UIImage(systemName: "arrow.down", withConfiguration: UIImage.SymbolConfiguration(pointSize: 10, weight: .bold, scale: .default)))
  736. imageDownload.tintColor = .white
  737. container.addSubview(imageDownload)
  738. imageDownload.translatesAutoresizingMaskIntoConstraints = false
  739. imageDownload.centerXAnchor.constraint(equalTo: sender.imageView.centerXAnchor).isActive = true
  740. imageDownload.centerYAnchor.constraint(equalTo: sender.imageView.centerYAnchor).isActive = true
  741. imageDownload.widthAnchor.constraint(equalToConstant: 30).isActive = true
  742. imageDownload.heightAnchor.constraint(equalToConstant: 30).isActive = true
  743. Download().start(forKey: sender.video_id) { (name, progress) in
  744. DispatchQueue.main.async {
  745. guard progress == 100 else {
  746. shapeLoading.strokeEnd = CGFloat(progress / 100)
  747. return
  748. }
  749. let save = UserDefaults.standard.bool(forKey: "saveToGallery")
  750. if save {
  751. let videoURL = URL(fileURLWithPath: dirPath).appendingPathComponent(sender.video_id)
  752. PHPhotoLibrary.shared().performChanges({
  753. PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: videoURL)
  754. }) { saved, error in
  755. }
  756. }
  757. let idx = self.dataMessages.firstIndex(where: { $0["video_id"] as! String == sender.video_id})
  758. if idx != nil {
  759. self.dataMessages[idx!]["progress"] = progress
  760. self.tableChatView.reloadRows(at: [sender.indexPath], with: .none)
  761. }
  762. }
  763. }
  764. }
  765. }
  766. } else if (sender.file_id != "") {
  767. if let dirPath = paths.first {
  768. let fileURL = URL(fileURLWithPath: dirPath).appendingPathComponent(sender.file_id)
  769. if FileManager.default.fileExists(atPath: fileURL.path) {
  770. self.previewItem = fileURL as NSURL
  771. let previewController = QLPreviewController()
  772. let rightBarButton = UIBarButtonItem()
  773. previewController.navigationItem.rightBarButtonItem = rightBarButton
  774. previewController.dataSource = self
  775. previewController.modalPresentationStyle = .custom
  776. self.show(previewController, sender: nil)
  777. } else {
  778. for view in sender.containerFile.subviews {
  779. if !(view is UIImageView) && !(view is UILabel) {
  780. view.removeFromSuperview()
  781. }
  782. }
  783. let containerLoading = UIView()
  784. sender.containerFile.addSubview(containerLoading)
  785. containerLoading.translatesAutoresizingMaskIntoConstraints = false
  786. containerLoading.centerYAnchor.constraint(equalTo: sender.containerFile.centerYAnchor).isActive = true
  787. containerLoading.leadingAnchor.constraint(equalTo: sender.labelFile.trailingAnchor, constant: 5).isActive = true
  788. containerLoading.trailingAnchor.constraint(equalTo: sender.containerFile.trailingAnchor, constant: -5).isActive = true
  789. containerLoading.widthAnchor.constraint(equalToConstant: 30).isActive = true
  790. containerLoading.heightAnchor.constraint(equalToConstant: 30).isActive = true
  791. let circlePath = UIBezierPath(arcCenter: CGPoint(x: 15, y: 15), radius: 10, startAngle: -(.pi / 2), endAngle: .pi * 2, clockwise: true)
  792. let trackShape = CAShapeLayer()
  793. trackShape.path = circlePath.cgPath
  794. trackShape.fillColor = UIColor.clear.cgColor
  795. trackShape.lineWidth = 5
  796. trackShape.strokeColor = UIColor.mainColor.withAlphaComponent(0.3).cgColor
  797. containerLoading.layer.addSublayer(trackShape)
  798. let shapeLoading = CAShapeLayer()
  799. shapeLoading.path = circlePath.cgPath
  800. shapeLoading.fillColor = UIColor.clear.cgColor
  801. shapeLoading.lineWidth = 3
  802. shapeLoading.strokeEnd = 0
  803. shapeLoading.strokeColor = UIColor.mainColor.cgColor
  804. containerLoading.layer.addSublayer(shapeLoading)
  805. let imageupload = UIImageView(image: UIImage(systemName: "arrow.down", withConfiguration: UIImage.SymbolConfiguration(pointSize: 10, weight: .bold, scale: .default)))
  806. imageupload.tintColor = .white
  807. containerLoading.addSubview(imageupload)
  808. imageupload.translatesAutoresizingMaskIntoConstraints = false
  809. imageupload.centerYAnchor.constraint(equalTo: containerLoading.centerYAnchor).isActive = true
  810. imageupload.centerXAnchor.constraint(equalTo: containerLoading.centerXAnchor).isActive = true
  811. Download().start(forKey: sender.file_id) { (name, progress) in
  812. DispatchQueue.main.async {
  813. guard progress == 100 else {
  814. shapeLoading.strokeEnd = CGFloat(progress / 100)
  815. return
  816. }
  817. let idx = self.dataMessages.firstIndex(where: { $0["file_id"] as! String == sender.file_id})
  818. if idx != nil {
  819. self.dataMessages[idx!]["progress"] = progress
  820. self.tableChatView.reloadRows(at: [sender.indexPath], with: .none)
  821. }
  822. }
  823. }
  824. }
  825. }
  826. } else {
  827. DispatchQueue.main.async {
  828. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as! String == sender.message_id})
  829. if idx == nil {
  830. return
  831. }
  832. let section = self.dataDates.firstIndex(of: self.dataMessages[idx!]["chat_date"] as! String)
  833. if section == nil {
  834. return
  835. }
  836. 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})
  837. if row == nil {
  838. return
  839. }
  840. let indexPath = IndexPath(row: row!, section: section!)
  841. self.tableChatView.scrollToRow(at: indexPath, at: .middle, animated: true)
  842. DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
  843. if let cell = self.tableChatView.cellForRow(at: indexPath) {
  844. let containerMessage = cell.contentView.subviews[0]
  845. let idMe = UserDefaults.standard.string(forKey: "me") as String?
  846. if (self.dataMessages[idx!]["f_pin"] as? String == idMe) {
  847. containerMessage.backgroundColor = .mainColor.withAlphaComponent(0.3)
  848. DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
  849. if (self.dataMessages[idx!]["attachment_flag"] as? String == "11") {
  850. containerMessage.backgroundColor = .clear
  851. } else {
  852. containerMessage.backgroundColor = .mainColor
  853. }
  854. }
  855. } else {
  856. containerMessage.backgroundColor = .grayColor.withAlphaComponent(0.3)
  857. DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
  858. if (self.dataMessages[idx!]["attachment_flag"] as? String == "11") {
  859. containerMessage.backgroundColor = .clear
  860. } else {
  861. containerMessage.backgroundColor = .grayColor
  862. }
  863. }
  864. }
  865. }
  866. }
  867. }
  868. }
  869. }
  870. func getDataProfile(f_pin: String) -> [String: String]{
  871. var data: [String: String] = [:]
  872. Database().database?.inTransaction({ fmdb, rollback in
  873. if let c = Database().getRecords(fmdb: fmdb, query: "select first_name || ' ' || last_name, image_id from BUDDY where f_pin = '\(f_pin)'"), c.next() {
  874. data["name"] = c.string(forColumnIndex: 0)!.trimmingCharacters(in: .whitespacesAndNewlines)
  875. data["image_id"] = c.string(forColumnIndex: 1)!
  876. c.close()
  877. }
  878. else if f_pin == "-999" {
  879. data["name"] = "Bot".localized()
  880. data["image_id"] = "pb_powered"
  881. }
  882. else {
  883. data["name"] = "Unknown".localized()
  884. data["image_id"] = ""
  885. }
  886. })
  887. return data
  888. }
  889. private func getDataProfileFromMessageId(message_id: String) -> [String: String]{
  890. var data: [String: String] = [:]
  891. Database().database?.inTransaction({ fmdb, rollback in
  892. if let c = Database().getRecords(fmdb: fmdb, query: "select f_display_name from MESSAGE where message_id = '\(message_id)'"), c.next() {
  893. data["name"] = c.string(forColumnIndex: 0)!
  894. c.close()
  895. } else {
  896. data["name"] = "Unknown".localized()
  897. data["image_id"] = ""
  898. }
  899. })
  900. return data
  901. }
  902. public func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
  903. let indexPath = self.tableChatView.indexPathForRow(at: interaction.view!.convert(location, to: self.tableChatView))
  904. let dataMessages = self.dataMessages.filter({ $0["chat_date"] as! String == dataDates[indexPath!.section]})
  905. let star = UIAction(title: "Unstar".localized(), image: UIImage(systemName: "star.fill"), handler: {(_) in
  906. DispatchQueue.global().async {
  907. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  908. _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE", cvalues: [
  909. "is_stared" : 0
  910. ], _where: "message_id = '\(dataMessages[indexPath!.row]["message_id"] as! String)'")
  911. })
  912. }
  913. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as! String == dataMessages[indexPath!.row]["message_id"] as! String})
  914. if idx != nil{
  915. self.dataMessages[idx!]["is_stared"] = "0"
  916. }
  917. self.dataMessages.remove(at: idx!)
  918. self.tableChatView.deleteRows(at: [indexPath!], with: .fade)
  919. if self.dataMessages.filter({ $0["chat_date"] as! String == dataMessages[indexPath!.row]["chat_date"] as! String }).count == 0 {
  920. self.dataDates.remove(at: indexPath!.section)
  921. self.tableChatView.deleteSections(IndexSet(integer: indexPath!.section), with: .fade)
  922. }
  923. })
  924. let forward = UIAction(title: "Forward".localized(), image: UIImage(systemName: "arrowshape.turn.up.right.fill"), handler: {(_) in
  925. let navigationController = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "contactChatNav") as! UINavigationController
  926. navigationController.modalPresentationStyle = .custom
  927. if let controller = navigationController.viewControllers.first as? ContactChatViewController {
  928. controller.isChooser = { [weak self] scope, pin in
  929. if scope == "3" {
  930. let editorPersonalVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorPersonalVC") as! EditorPersonal
  931. editorPersonalVC.unique_l_pin = pin
  932. editorPersonalVC.dataMessageForward = [dataMessages[indexPath!.row]]
  933. self?.navigationController?.replaceAllViewController(with: editorPersonalVC, animated: true)
  934. } else {
  935. let editorGroupVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorGroupVC") as! EditorGroup
  936. editorGroupVC.unique_l_pin = pin
  937. editorGroupVC.dataMessageForward = [dataMessages[indexPath!.row]]
  938. self?.navigationController?.replaceAllViewController(with: editorGroupVC, animated: true)
  939. }
  940. }
  941. }
  942. self.present(navigationController, animated: true, completion: nil)
  943. })
  944. let copy = UIAction(title: "Copy".localized(), image: UIImage(systemName: "doc.on.doc.fill"), handler: {(_) in
  945. if (dataMessages[indexPath!.row]["attachment_flag"] as! String == "0") {
  946. DispatchQueue.main.async {
  947. var text = ""
  948. let stringDate = (dataMessages[indexPath!.row]["server_date"] as! String)
  949. let date = Date(milliseconds: Int64(stringDate)!)
  950. let formatterDate = DateFormatter()
  951. let formatterTime = DateFormatter()
  952. formatterDate.dateFormat = "dd/MM/yy"
  953. formatterDate.locale = NSLocale(localeIdentifier: "id") as Locale?
  954. formatterTime.dateFormat = "HH:mm"
  955. formatterTime.locale = NSLocale(localeIdentifier: "id") as Locale?
  956. let dataProfile = self.getDataProfileFromMessageId(message_id: dataMessages[indexPath!.row]["message_id"] as! String)
  957. if text.isEmpty {
  958. text = "*[\(formatterDate.string(from: date as Date)) \(formatterTime.string(from: date as Date))] \(dataProfile["name"]!):*\n\(dataMessages[indexPath!.row]["message_text"] as! String)"
  959. } else {
  960. 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)"
  961. }
  962. text = text + "\n\n\nchat powered by Nexilis"
  963. DispatchQueue.main.async {
  964. UIPasteboard.general.string = text
  965. self.showToast(message: "Text coppied to clipboard".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
  966. }
  967. }
  968. } else {
  969. DispatchQueue.main.async {
  970. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  971. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  972. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  973. if let dirPath = paths.first {
  974. let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(dataMessages[indexPath!.row]["image_id"] as! String)
  975. if FileManager.default.fileExists(atPath: imageURL.path) {
  976. let image = UIImage(contentsOfFile: imageURL.path)
  977. UIPasteboard.general.image = image
  978. self.showToast(message: "Image coppied to clipboard".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
  979. }
  980. }
  981. }
  982. }
  983. })
  984. var children: [UIMenuElement] = [star, forward, copy]
  985. // let copyOption = self.copyOption(indexPath: indexPath!)
  986. if self.dataMessages[indexPath!.row]["f_pin"] as! String == "-999" {
  987. children = [star]
  988. } 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" {
  989. children = [star, forward]
  990. }
  991. return UIContextMenuConfiguration(identifier: nil,
  992. previewProvider: nil) { _ in
  993. UIMenu(title: "", children: children)
  994. }
  995. }
  996. private func copyOption(indexPath: IndexPath) -> UIMenu {
  997. let ratingButtonTitles = ["Text".localized(), "Image".localized()]
  998. let dataMessages = self.dataMessages.filter({ $0["chat_date"] as! String == dataDates[indexPath.section]})
  999. let copyActions = ratingButtonTitles
  1000. .enumerated()
  1001. .map { index, title in
  1002. return UIAction(
  1003. title: title,
  1004. identifier: nil,
  1005. handler: {(_) in if (index == 0) {
  1006. DispatchQueue.main.async {
  1007. UIPasteboard.general.string = dataMessages[indexPath.row]["message_text"] as? String
  1008. self.showToast(message: "Text coppied to clipboard".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
  1009. }
  1010. } else {
  1011. DispatchQueue.main.async {
  1012. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  1013. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  1014. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  1015. if let dirPath = paths.first {
  1016. let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(dataMessages[indexPath.row]["image_id"] as! String)
  1017. if FileManager.default.fileExists(atPath: imageURL.path) {
  1018. let image = UIImage(contentsOfFile: imageURL.path)
  1019. UIPasteboard.general.image = image
  1020. self.showToast(message: "Image coppied to clipboard".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
  1021. }
  1022. }
  1023. }
  1024. }})
  1025. }
  1026. return UIMenu(
  1027. title: "Copy".localized(),
  1028. image: UIImage(systemName: "doc.on.doc.fill"),
  1029. children: copyActions)
  1030. }
  1031. @objc private func cancelDocumentPreview(sender: navigationQLPreviewDocument) {
  1032. sender.navigation.dismiss(animated: true, completion: nil)
  1033. }
  1034. @objc func segmentedControlValueChanged(_ sender: segmentedControllerObject) {
  1035. switch sender.selectedSegmentIndex {
  1036. case 0:
  1037. sender.navigation.viewControllers[0].children[1].view.isHidden = true
  1038. break;
  1039. case 1:
  1040. sender.navigation.viewControllers[0].children[1].view.isHidden = false
  1041. break;
  1042. default:
  1043. break;
  1044. }
  1045. }
  1046. public func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
  1047. 1
  1048. }
  1049. public func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
  1050. return self.previewItem as QLPreviewItem
  1051. }
  1052. public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
  1053. let message = dataMessages[indexPath.row]
  1054. if let attachmentFlag = message["attachment_flag"], let attachmentFlag = attachmentFlag as? String {
  1055. if attachmentFlag == "27" {
  1056. let streamingController = QmeraCreateStreamingViewController()
  1057. streamingController.isJoin = true
  1058. if let messageText = message["message_text"],
  1059. let messageText = messageText as? String,
  1060. var json = try! JSONSerialization.jsonObject(with: messageText.data(using: String.Encoding.utf8)!, options: []) as? [String: Any] {
  1061. if json["blog"] == nil {
  1062. json["blog"] = message["blog_id"] ?? nil
  1063. }
  1064. streamingController.data = json
  1065. }
  1066. let streamingNav = UINavigationController(rootViewController: streamingController)
  1067. streamingNav.modalPresentationStyle = .custom
  1068. streamingNav.navigationBar.barTintColor = .mainColor
  1069. streamingNav.navigationBar.tintColor = .white
  1070. let textAttributes = [NSAttributedString.Key.foregroundColor:UIColor.white]
  1071. streamingNav.navigationBar.titleTextAttributes = textAttributes
  1072. streamingNav.navigationBar.isTranslucent = false
  1073. navigationController?.present(streamingNav, animated: true, completion: nil)
  1074. }
  1075. }
  1076. }
  1077. }