CreateSeminarViewController.swift 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  1. //
  2. // CreateSeminarViewController.swift
  3. // NexilisLite
  4. //
  5. // Created by Maronakins on 31/05/23.
  6. //
  7. import UIKit
  8. import NotificationBannerSwift
  9. import AVFoundation
  10. public class CreateSeminarViewController: UITableViewController {
  11. var isJoin = false
  12. var startTime = 0
  13. var data: [String: Any] = [:]
  14. private enum Section {
  15. case title
  16. case start
  17. case participants
  18. }
  19. private var sections: [Section] = [
  20. .title,
  21. .start,
  22. .participants
  23. ]
  24. private var users: [User] = [] {
  25. didSet {
  26. DispatchQueue.main.async { [weak self] in
  27. self?.tableView.reloadData()
  28. }
  29. }
  30. }
  31. private let cellIdentifier = "reuseIdentifier"
  32. lazy var table: UITableView = {
  33. let tableView = UITableView(frame: CGRect.zero, style: .grouped)
  34. tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellIdentifier)
  35. return tableView
  36. }()
  37. lazy var titleView: UITextField = {
  38. let textField = UITextField()
  39. textField.borderStyle = .none
  40. textField.placeholder = "Title".localized()
  41. return textField
  42. }()
  43. lazy var startView: UITextField = {
  44. let textField = UITextField()
  45. textField.placeholder = "Start".localized()
  46. textField.borderStyle = .none
  47. return textField
  48. }()
  49. let datePicker: UIDatePicker = {
  50. let picker = UIDatePicker()
  51. picker.datePickerMode = .dateAndTime // Allows both date and time selection
  52. picker.preferredDatePickerStyle = .wheels // Optional: change the picker style
  53. return picker
  54. }()
  55. deinit {
  56. //print(#function, ">>>> TADAA1")
  57. NotificationCenter.default.removeObserver(self)
  58. }
  59. public override func viewDidLoad() {
  60. super.viewDidLoad()
  61. title = "Video Conference Room".localized()
  62. let attributes = [NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 16.0), NSAttributedString.Key.foregroundColor: UIColor.white]
  63. let navBarAppearance = UINavigationBarAppearance()
  64. navBarAppearance.configureWithOpaqueBackground()
  65. navBarAppearance.backgroundColor = self.traitCollection.userInterfaceStyle == .dark ? .blackDarkMode : UIColor.mainColor
  66. navBarAppearance.titleTextAttributes = attributes
  67. navigationController?.navigationBar.standardAppearance = navBarAppearance
  68. navigationController?.navigationBar.scrollEdgeAppearance = navBarAppearance
  69. navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Cancel".localized(), style: .plain, target: self, action: #selector(didTapCancel(sender:)))
  70. navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Start".localized(), style: .plain, target: self, action: #selector(didTapRight(sender:)))
  71. let tapGesture = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
  72. tapGesture.cancelsTouchesInView = false
  73. table.addGestureRecognizer(tapGesture)
  74. if isJoin {
  75. navigationItem.rightBarButtonItem?.title = "Join".localized()
  76. titleView.isEnabled = false
  77. if let a = data["title"] as? String {
  78. titleView.isUserInteractionEnabled = false
  79. titleView.text = a
  80. }
  81. if let b = data["start"] as? String {
  82. startView.isUserInteractionEnabled = false
  83. startView.text = b
  84. }
  85. } else if !isJoin && !data.isEmpty {
  86. navigationItem.rightBarButtonItem?.title = "Start".localized()
  87. titleView.isEnabled = false
  88. if let a = data["title"] as? String {
  89. titleView.text = a
  90. }
  91. if let b = data["start"] as? String {
  92. startView.text = b
  93. }
  94. }
  95. tableView = table
  96. }
  97. private func configureDatePicker() {
  98. // Set the date picker as the input view for the text field
  99. startView.inputView = datePicker
  100. // Create a toolbar with a "Done" button
  101. let toolbar = UIToolbar()
  102. toolbar.backgroundColor = self.traitCollection.userInterfaceStyle == .dark ? .blackDarkMode : UIColor.mainColor
  103. toolbar.barTintColor = self.traitCollection.userInterfaceStyle == .dark ? .blackDarkMode : UIColor.mainColor
  104. toolbar.sizeToFit()
  105. let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(doneButtonTapped))
  106. toolbar.setItems([doneButton], animated: true)
  107. // Set the toolbar as the accessory view for the text field
  108. startView.inputAccessoryView = toolbar
  109. }
  110. @objc private func doneButtonTapped() {
  111. // Format the selected date and time
  112. let formatter = DateFormatter()
  113. formatter.dateStyle = .medium
  114. formatter.timeStyle = .short
  115. // Update the text field with the formatted date and time
  116. startTime = datePicker.date.currentTimeMillis()
  117. startView.text = formatter.string(from: datePicker.date)
  118. // Dismiss the date picker
  119. view.endEditing(true)
  120. }
  121. @objc func dismissKeyboard() {
  122. titleView.resignFirstResponder()
  123. }
  124. @objc func didTapCancel(sender: AnyObject) {
  125. navigationController?.dismiss(animated: true, completion: nil)
  126. }
  127. @objc func didTapRight(sender: Any?) {
  128. let controller = VideoConferenceViewController()
  129. controller.isInisiator = !isJoin
  130. // TODO: let controller = SeminarViewController()
  131. // TODO: controller.isLive = !isJoin
  132. if isJoin {
  133. // guard let by = data["by"] as? String else {
  134. // return
  135. // }
  136. if let dataBlog = data["blog"] as? String {
  137. controller.roomId = dataBlog
  138. }
  139. // if dataBlog != nil {
  140. // if let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.joinVCallConference(blog_id: dataBlog!)) {
  141. // if response.getBody(key: CoreMessage_TMessageKey.ERRCOD) != "00" {
  142. // let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  143. // imageView.tintColor = .white
  144. // let banner = FloatingNotificationBanner(title: "Conference call session hasn\'t started yet".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
  145. // banner.show()
  146. // return
  147. // }
  148. // } else {
  149. // let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  150. // imageView.tintColor = .white
  151. // let banner = FloatingNotificationBanner(title: "No Network. Please try again.".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
  152. // banner.show()
  153. // return
  154. // }
  155. // }
  156. // TODO: controller.data = by
  157. // TODO: controller.streamingData = data
  158. } else {
  159. let goAudioCall = Nexilis.checkMicPermission()
  160. if !goAudioCall {
  161. let alert = LibAlertController(title: "Attention!".localized(), message: "Please allow microphone permission in your settings".localized(), preferredStyle: .alert)
  162. alert.addAction(UIAlertAction(title: "OK".localized(), style: UIAlertAction.Style.default, handler: { _ in
  163. if let url = URL(string: UIApplication.openSettingsURLString), UIApplication.shared.canOpenURL(url) {
  164. UIApplication.shared.open(url, options: [:], completionHandler: nil)
  165. }
  166. }))
  167. if UIApplication.shared.visibleViewController?.navigationController != nil {
  168. UIApplication.shared.visibleViewController?.navigationController?.present(alert, animated: true, completion: nil)
  169. } else {
  170. UIApplication.shared.visibleViewController?.present(alert, animated: true, completion: nil)
  171. }
  172. return
  173. }
  174. var permissionCheck = -1
  175. if AVCaptureDevice.authorizationStatus(for: .video) == .authorized {
  176. permissionCheck = 1
  177. } else if AVCaptureDevice.authorizationStatus(for: .video) == .denied {
  178. permissionCheck = 0
  179. } else {
  180. AVCaptureDevice.requestAccess(for: .video, completionHandler: { (granted: Bool) -> Void in
  181. if granted == true {
  182. permissionCheck = 1
  183. } else {
  184. permissionCheck = 0
  185. }
  186. })
  187. }
  188. while permissionCheck == -1 {
  189. sleep(1)
  190. }
  191. if permissionCheck == 0 {
  192. let alert = LibAlertController(title: "Attention!".localized(), message: "Please allow camera permission in your settings".localized(), preferredStyle: .alert)
  193. alert.addAction(UIAlertAction(title: "OK".localized(), style: UIAlertAction.Style.default, handler: { _ in
  194. if let url = URL(string: UIApplication.openSettingsURLString), UIApplication.shared.canOpenURL(url) {
  195. UIApplication.shared.open(url, options: [:], completionHandler: nil)
  196. }
  197. }))
  198. if UIApplication.shared.visibleViewController?.navigationController != nil {
  199. UIApplication.shared.visibleViewController?.navigationController?.present(alert, animated: true, completion: nil)
  200. } else {
  201. UIApplication.shared.visibleViewController?.present(alert, animated: true, completion: nil)
  202. }
  203. return
  204. }
  205. var data: [String: Any] = [:]
  206. if !isJoin && !self.data.isEmpty {
  207. data = self.data
  208. } else if self.data.isEmpty {
  209. guard let conferenceTitle = titleView.text else {
  210. return
  211. }
  212. let id = "CR\(Date().currentTimeMillis().toHex())"
  213. data["title"] = conferenceTitle
  214. // TODO: data["start"]
  215. data["by"] = User.getMyPin() ?? ""
  216. data["time"] = Date().currentTimeMillis()
  217. data["blog"] = id
  218. if conferenceTitle.trimmingCharacters(in: .whitespaces).isEmpty {
  219. let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  220. imageView.tintColor = .white
  221. let banner = FloatingNotificationBanner(title: "Video Conference title can't be empty".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
  222. banner.show()
  223. return
  224. }
  225. //
  226. // var type: String = "0" // Friend
  227. let members: [String] = users.map{ $0.pin }
  228. // switch chooser[0].id {
  229. // case 0:
  230. // type = "7"
  231. // case 1:
  232. // type = "8"
  233. // case 2:
  234. // type = "3"
  235. // case 3:
  236. // type = "6"
  237. // case 4:
  238. // type = "5"
  239. // default:
  240. // type = "0"
  241. // }
  242. //
  243. data["members"] = members
  244. // var notif: String = "0"
  245. // switch chooser[1].id {
  246. // case 0:
  247. // notif = "1"
  248. // default:
  249. // notif = "2"
  250. // }
  251. // data["broadcast_type"] = notif
  252. guard let json = String(data: try! JSONSerialization.data(withJSONObject: data, options: []), encoding: String.Encoding.utf8) else {
  253. return
  254. }
  255. if let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.createVCallConference(blog_id: data["blog"] as! String, data: json)) {
  256. if response.getBody(key: CoreMessage_TMessageKey.ERRCOD) != "00" {
  257. let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  258. imageView.tintColor = .white
  259. let banner = FloatingNotificationBanner(title: "Server Busy. Please try again.".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
  260. banner.show()
  261. return
  262. }
  263. } else {
  264. let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  265. imageView.tintColor = .white
  266. let banner = FloatingNotificationBanner(title: "No Network. Please try again.".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
  267. banner.show()
  268. return
  269. }
  270. // if let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.startVCallConference(blog_id: data["blog"] as! String, time: data["time"] as! String), timeout: 30 * 1000) {
  271. // if response.getBody(key: CoreMessage_TMessageKey.ERRCOD) != "00" {
  272. // let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  273. // imageView.tintColor = .white
  274. // let banner = FloatingNotificationBanner(title: "Server Busy. Please try again.".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
  275. // banner.show()
  276. // return
  277. // }
  278. // } else {
  279. // let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  280. // imageView.tintColor = .white
  281. // let banner = FloatingNotificationBanner(title: "No Network. Please try again.".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
  282. // banner.show()
  283. // return
  284. // }
  285. // Nexilis.saveMessageBot(textMessage: json, blog_id: data["blog"] as? String ?? "", attachment_type: "26")
  286. NotificationCenter.default.post(name: NSNotification.Name(rawValue: "reloadTabChats"), object: nil, userInfo: nil)
  287. }
  288. // TODO: controller.data = User.getMyPin()!
  289. // TODO: controller.streamingData = data
  290. controller.roomId = data["blog"] as! String
  291. }
  292. // TODO: navigationController?.show(controller, sender: nil)
  293. navigationController?.show(controller, sender: nil)
  294. // navigationController?.dismiss(animated: true, completion: nil)
  295. }
  296. // MARK: - Table view data source
  297. public override func numberOfSections(in tableView: UITableView) -> Int {
  298. if isJoin {
  299. return sections.count - 1
  300. }
  301. return sections.count
  302. }
  303. public override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  304. switch sections[section] {
  305. case .participants:
  306. return users.count + 1
  307. default:
  308. return 1
  309. }
  310. }
  311. public override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
  312. return 44
  313. // switch sections[indexPath.section] {
  314. // case .description:
  315. // return 100
  316. // default:
  317. // return 44
  318. // }
  319. }
  320. public override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
  321. if isJoin, sections[indexPath.section] == .participants {
  322. return nil
  323. }
  324. return indexPath
  325. }
  326. public override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
  327. tableView.deselectRow(at: indexPath, animated: true)
  328. switch sections[indexPath.section] {
  329. case .start:
  330. if !isJoin {
  331. sections.append(.participants)
  332. if users.count != 0{
  333. users.removeAll()
  334. }
  335. }
  336. // if indexPath.row == 0 {
  337. // if isJoin || (!isJoin && !data.isEmpty){
  338. // return
  339. // }
  340. // let chooser = chooser[indexPath.row]
  341. // let controller = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "audienceView") as AudienceViewController
  342. // controller.selected = chooser.value
  343. // controller.isDismiss = { [weak self] index in
  344. // chooser.id = index
  345. // chooser.value = AudienceViewController().data[index]
  346. // guard let sec = self?.sections else {
  347. // return
  348. // }
  349. // if sec.count > 4 {
  350. // self?.sections.removeLast()
  351. // }
  352. // if chooser.value == "Group".localized() {
  353. // self?.sections.append(.groups)
  354. // if self?.users.count != 0 {
  355. // self?.users.removeAll()
  356. // }
  357. // } else if chooser.value == "User".localized() {
  358. // self?.sections.append(.users)
  359. // if self?.groups.count != 0{
  360. // self?.groups.removeAll()
  361. // }
  362. // } else {
  363. // if self?.users.count != 0 {
  364. // self?.users.removeAll()
  365. // } else if self?.groups.count != 0{
  366. // self?.groups.removeAll()
  367. // }
  368. // }
  369. // DispatchQueue.main.async {
  370. // tableView.reloadData()
  371. // }
  372. // }
  373. // navigationController?.show(controller, sender: nil)
  374. // }
  375. case .participants:
  376. if indexPath.row == 0 && !isJoin {
  377. let controller = QmeraUserChooserViewController()
  378. controller.ignored.append(contentsOf: users)
  379. controller.isDismiss = { users in
  380. self.users.append(contentsOf: users)
  381. }
  382. navigationController?.show(controller, sender: nil)
  383. }
  384. default:
  385. return
  386. }
  387. }
  388. public override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  389. let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)
  390. cell.contentView.subviews.forEach{ NSLayoutConstraint.deactivate($0.constraints); $0.removeFromSuperview() }
  391. cell.contentConfiguration = nil
  392. cell.accessoryType = .none
  393. cell.selectionStyle = .none
  394. switch sections[indexPath.section] {
  395. case .start:
  396. // var content = cell.defaultContentConfiguration()
  397. // let data = chooser[indexPath.row]
  398. // content.text = data.title
  399. // content.secondaryText = data.value
  400. // content.textProperties.font = UIFont.systemFont(ofSize: 14)
  401. // content.secondaryTextProperties.color = .systemGray
  402. // content.secondaryTextProperties.font = UIFont.systemFont(ofSize: 14)
  403. // content.prefersSideBySideTextAndSecondaryText = true
  404. // cell.contentConfiguration = content
  405. // cell.accessoryType = .disclosureIndicator
  406. // cell.selectionStyle = .default
  407. cell.contentView.addSubview(startView)
  408. startView.anchor(top: cell.topAnchor, left: cell.leftAnchor, bottom: cell.bottomAnchor, right: cell.rightAnchor, paddingLeft: 20, paddingRight: 20)
  409. configureDatePicker()
  410. case .title:
  411. cell.contentView.addSubview(titleView)
  412. titleView.anchor(top: cell.topAnchor, left: cell.leftAnchor, bottom: cell.bottomAnchor, right: cell.rightAnchor, paddingLeft: 20, paddingRight: 20)
  413. case .participants:
  414. var content = cell.defaultContentConfiguration()
  415. if indexPath.row == 0 {
  416. content.image = UIImage(systemName: "plus.circle.fill")
  417. content.imageProperties.tintColor = .mainColor
  418. content.text = "Add user".localized()
  419. content.textProperties.font = UIFont.systemFont(ofSize: 14)
  420. cell.accessoryType = .disclosureIndicator
  421. cell.selectionStyle = .default
  422. } else {
  423. let data = users[indexPath.row - 1]
  424. getImage(name: data.thumb, placeholderImage: UIImage(named: "Profile---Purple", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), isCircle: true, tableView: tableView, indexPath: indexPath) { result, isDownloaded, image in
  425. content.image = image
  426. }
  427. content.text = data.fullName
  428. content.textProperties.font = UIFont.systemFont(ofSize: 14)
  429. }
  430. cell.contentConfiguration = content
  431. }
  432. return cell
  433. }
  434. public override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
  435. switch sections[indexPath.section] {
  436. case .participants:
  437. if (editingStyle == .delete) {
  438. users.remove(at: indexPath.row - 1)
  439. tableView.deleteRows(at: [indexPath], with: .automatic)
  440. }
  441. default:
  442. return
  443. }
  444. }
  445. public override func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
  446. if indexPath.section < 4 {
  447. return .none
  448. } else if indexPath.section == 4 && indexPath.row == 0 {
  449. return .none
  450. }
  451. return .delete
  452. }
  453. @IBAction func result(unwind segue: UIStoryboardSegue) {
  454. }
  455. }
  456. private class Chooser {
  457. let title: String
  458. var id: Int = 0
  459. var value: String?
  460. init(title: String, id: Int = 0, value: String?) {
  461. self.title = title
  462. self.id = id
  463. self.value = value
  464. }
  465. }
  466. extension CreateSeminarViewController: UITextViewDelegate {
  467. public func textViewDidBeginEditing(_ textView: UITextView) {
  468. if textView.textColor == UIColor.lightGray {
  469. textView.text = nil
  470. textView.textColor = UIColor.black
  471. }
  472. }
  473. public func textViewDidEndEditing(_ textView: UITextView) {
  474. if textView.text.isEmpty {
  475. textView.text = "Title".localized()
  476. textView.textColor = UIColor.lightGray
  477. }
  478. }
  479. }