FirstTabViewController.swift 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. //
  2. // FirstTabViewController.swift
  3. // AppBuilder
  4. //
  5. // Created by Kevin Maulana on 29/03/22.
  6. //
  7. import UIKit
  8. import WebKit
  9. import NexilisLite
  10. import Speech
  11. class FirstTabViewController: UIViewController, UIScrollViewDelegate, UIGestureRecognizerDelegate, WKScriptMessageHandler {
  12. @IBOutlet weak var webView: WKWebView!
  13. var address = ""
  14. private var lastContentOffset: CGFloat = 0
  15. var isAllowSpeech = false
  16. let speechRecognizer = SFSpeechRecognizer(locale: Locale(identifier: "id"))
  17. var recognitionRequest : SFSpeechAudioBufferRecognitionRequest?
  18. var recognitionTask : SFSpeechRecognitionTask?
  19. let audioEngine = AVAudioEngine()
  20. var alertController = UIAlertController()
  21. var dateRefresh: Date?
  22. public static var forceRefresh = false
  23. override func viewDidLoad() {
  24. super.viewDidLoad()
  25. let cpaasMode = PrefsUtil.getCpaasMode()
  26. let isBurger = cpaasMode == PrefsUtil.CPAAS_MODE_BURGER
  27. // navigationController?.navigationBar.backgroundColor = .white
  28. navigationController?.setNavigationBarHidden(!isBurger, animated: false)
  29. let tapGesture = UITapGestureRecognizer(target: self, action: #selector(collapseDocked))
  30. tapGesture.cancelsTouchesInView = false
  31. tapGesture.delegate = self
  32. webView.scrollView.addGestureRecognizer(tapGesture)
  33. let refreshControl = UIRefreshControl()
  34. refreshControl.addTarget(self, action: #selector(reloadWebView(_:)), for: .valueChanged)
  35. webView.scrollView.addSubview(refreshControl)
  36. webView.scrollView.delegate = self
  37. webView.navigationDelegate = self
  38. webView.allowsBackForwardNavigationGestures = true
  39. let contentController = self.webView.configuration.userContentController
  40. contentController.add(self, name: "checkProfile")
  41. contentController.add(self, name: "setIsProductModalOpen")
  42. contentController.add(self, name: "toggleVoiceSearch")
  43. contentController.add(self, name: "blockUser")
  44. contentController.add(self, name: "showAlert")
  45. contentController.add(self, name: "closeProfile")
  46. contentController.add(self, name: "tabShowHide")
  47. let source: String = "var meta = document.createElement('meta');" +
  48. "meta.name = 'viewport';" +
  49. "meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no';" +
  50. "var head = document.getElementsByTagName('head')[0];" +
  51. "head.appendChild(meta);" +
  52. "$('#header-layout').find('.col-8').removeClass('col-8').addClass('col');" +
  53. "$('#header-layout').find('.col-4').removeClass('col-4').addClass('col');"
  54. let script: WKUserScript = WKUserScript(source: source, injectionTime: .atDocumentEnd, forMainFrameOnly: false)
  55. contentController.addUserScript(script)
  56. NotificationCenter.default.addObserver(self, selector: #selector(onShowAC(notification:)), name: NSNotification.Name(rawValue: "onShowAC"), object: nil)
  57. }
  58. override func viewWillAppear(_ animated: Bool) {
  59. let me = UserDefaults.standard.string(forKey: "me")
  60. var myURL : URL?
  61. let lang = UserDefaults.standard.string(forKey: "i18n_language")
  62. var intLang = 0
  63. if lang == "id" {
  64. intLang = 1
  65. }
  66. switch(ViewController.sURL){
  67. case "0":
  68. address = "\(PrefsUtil.getURLBase() ?? "https://qmera.io/")nexilis/pages/tab1-main-only?f_pin=\(me ?? "")&lang=\(intLang)"
  69. myURL = URL(string: address)
  70. case "1":
  71. address = "\(PrefsUtil.getURLBase() ?? "https://qmera.io/")nexilis/pages/tab3-main-only?f_pin=\(me ?? "")&lang=\(intLang)"
  72. myURL = URL(string: address)
  73. case "2":
  74. address = "\(PrefsUtil.getURLBase() ?? "https://qmera.io/")nexilis/pages/tab1-main?f_pin=\(me ?? "")&lang=\(intLang)"
  75. myURL = URL(string: address)
  76. case "3":
  77. address = "\(PrefsUtil.getURLBase() ?? "https://qmera.io/")nexilis/pages/tab3-commerce?f_pin=\(me ?? "")&lang=\(intLang)"
  78. myURL = URL(string: address)
  79. case "4":
  80. address = "\(PrefsUtil.getURLBase() ?? "https://qmera.io/")nexilis/pages/tab1-video?f_pin=\(me ?? "")&lang=\(intLang)"
  81. myURL = URL(string: address)
  82. default:
  83. if(!ViewController.sURL.isEmpty){
  84. if(ViewController.sURL.lowercased().contains("https://") || ViewController.sURL.lowercased().contains("http://")){
  85. address = ViewController.sURL
  86. myURL = URL(string: address)
  87. }
  88. else {
  89. address = "https://\(ViewController.sURL)"
  90. myURL = URL(string: address)
  91. }
  92. }
  93. }
  94. if let u = myURL {
  95. if dateRefresh == nil || Int(Date().timeIntervalSince(dateRefresh!)) >= 60 || FirstTabViewController.forceRefresh {
  96. let myRequest = URLRequest(url: u)
  97. webView.load(myRequest)
  98. }
  99. dateRefresh = Date()
  100. FirstTabViewController.forceRefresh = false
  101. self.webView.evaluateJavaScript("if(resumeAll){resumeAll();}")
  102. }
  103. self.navigationController?.navigationBar.topItem?.title = Bundle.main.displayName
  104. let cpaasMode = PrefsUtil.getCpaasMode()
  105. let isBurger = cpaasMode == PrefsUtil.CPAAS_MODE_BURGER
  106. navigationController?.setNavigationBarHidden(!isBurger, animated: false)
  107. }
  108. @objc func onShowAC(notification: NSNotification) {
  109. self.webView.evaluateJavaScript("{if(pauseAll){pauseAll();}}")
  110. view.endEditing(true)
  111. resignFirstResponder()
  112. }
  113. override func viewWillDisappear(_ animated: Bool) {
  114. self.webView.evaluateJavaScript("{if(pauseAll){pauseAll();}}")
  115. }
  116. func scrollViewDidScroll(_ scrollView: UIScrollView) {
  117. if (self.lastContentOffset > scrollView.contentOffset.y && scrollView.contentOffset.y < (scrollView.contentSize.height - scrollView.frame.size.height)) {
  118. showTabBar();
  119. }
  120. else if (self.lastContentOffset != 0 && self.lastContentOffset < scrollView.contentOffset.y && self.lastContentOffset >= 0) {
  121. hideTabBar();
  122. }
  123. self.lastContentOffset = scrollView.contentOffset.y
  124. self.collapseDocked()
  125. }
  126. @objc func collapseDocked() {
  127. if ViewController.isExpandButton {
  128. ViewController.expandButton()
  129. }
  130. }
  131. @objc func reloadWebView(_ sender: UIRefreshControl) {
  132. webView.reload()
  133. sender.endRefreshing()
  134. }
  135. func hideTabBar() {
  136. var viewController = UIApplication.shared.windows.first!.rootViewController
  137. if !(viewController is ViewController) {
  138. viewController = self.parent
  139. }
  140. if ViewController.middleButton.isDescendant(of: viewController!.view) {
  141. DispatchQueue.main.async {
  142. ViewController.hideDockedButton()
  143. if let viewController = viewController as? ViewController {
  144. viewController.tabBar.isHidden = true
  145. }
  146. ViewController.removeMiddleButton()
  147. }
  148. }
  149. }
  150. func showTabBar() {
  151. if(ViewController.alwaysHideButton){
  152. return
  153. }
  154. var viewController = UIApplication.shared.windows.first!.rootViewController
  155. if !(viewController is ViewController) {
  156. viewController = self.parent
  157. }
  158. if ViewController.middleButton.isHidden {
  159. ViewController.isExpandButton = false
  160. if let viewController = viewController as? ViewController {
  161. viewController.tabBar.isHidden = false
  162. ViewController.middleButton.isHidden = false
  163. }
  164. }
  165. }
  166. func scrollViewWillBeginZooming(_ scrollView: UIScrollView, with view: UIView?) {
  167. scrollView.pinchGestureRecognizer?.isEnabled = false
  168. }
  169. func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
  170. if message.name == "checkProfile" {
  171. guard let dict = message.body as? [String: AnyObject],
  172. let param1 = dict["param1"] as? String,
  173. let param2 = dict["param2"] as? String else {
  174. return
  175. }
  176. if ViewController.checkIsChangePerson() {
  177. if param2 == "like" {
  178. self.webView.evaluateJavaScript("likeProduct('\(param1)',true);")
  179. } else if param2 == "comment" {
  180. self.webView.evaluateJavaScript("openComment('\(param1.split(separator: "|")[0])',\(param1.split(separator: "|")[1]),true);")
  181. } else if param2 == "report_user" {
  182. self.webView.evaluateJavaScript("reportUser('\(param1)',true);")
  183. } else if param2 == "report_content" {
  184. self.webView.evaluateJavaScript("reportContent('\(param1.split(separator: "|")[0])','\(param1.split(separator: "|")[1])',true);")
  185. } else if param2 == "block_user" {
  186. self.webView.evaluateJavaScript("blockUser('\(param1)',true);")
  187. } else if param2 == "follow_user" {
  188. self.webView.evaluateJavaScript("followUser('\(param1.split(separator: "|")[0])',\(param1.split(separator: "|")[1]),true);")
  189. } else if param2 == "homepage" || param2 == "gif" {
  190. self.webView.evaluateJavaScript("window.location.href = '\(param1)';")
  191. } else {
  192. self.webView.evaluateJavaScript("openNewPost(true);")
  193. }
  194. } else {
  195. self.webView.evaluateJavaScript("{if(pauseAll){pauseAll();}}")
  196. }
  197. } else if message.name == "setIsProductModalOpen" {
  198. guard let dict = message.body as? [String: AnyObject],
  199. let param1 = dict["param1"] as? Bool else {
  200. return
  201. }
  202. if param1 {
  203. } else {
  204. }
  205. } else if message.name == "toggleVoiceSearch" {
  206. if !isAllowSpeech {
  207. setupSpeech()
  208. } else {
  209. runVoice()
  210. }
  211. } else if message.name == "blockUser" {
  212. guard let dict = message.body as? [String: AnyObject],
  213. let param1 = dict["param1"] as? String,
  214. let param2 = dict["param2"] as? Bool else {
  215. return
  216. }
  217. if param2 {
  218. DispatchQueue.global().async {
  219. if let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getBlock(l_pin: param1)) {
  220. if response.isOk() {
  221. DispatchQueue.main.async {
  222. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  223. _ = Database.shared.updateRecord(fmdb: fmdb, table: "BUDDY", cvalues: [
  224. "ex_block" : "1"
  225. ], _where: "f_pin = '\(param1)'")
  226. })
  227. }
  228. }
  229. }
  230. }
  231. } else {
  232. DispatchQueue.global().async {
  233. if let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getUnBlock(l_pin: param1)) {
  234. if response.isOk() {
  235. DispatchQueue.main.async {
  236. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  237. _ = Database.shared.updateRecord(fmdb: fmdb, table: "BUDDY", cvalues: [
  238. "ex_block" : "0"
  239. ], _where: "f_pin = '\(param1)'")
  240. })
  241. }
  242. }
  243. }
  244. }
  245. }
  246. } else if message.name == "showAlert" {
  247. guard let dict = message.body as? [String: AnyObject],
  248. let param1 = dict["param1"] as? String else {
  249. return
  250. }
  251. showToast(message: param1, controller: self.tabBarController!)
  252. } else if message.name == "blockUser" {
  253. guard let dict = message.body as? [String: AnyObject],
  254. let param1 = dict["param1"] as? String,
  255. let param2 = dict["param2"] as? Bool else {
  256. return
  257. }
  258. if param2 {
  259. DispatchQueue.global().async {
  260. if let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getBlock(l_pin: param1)) {
  261. if response.isOk() {
  262. DispatchQueue.main.async {
  263. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  264. _ = Database.shared.updateRecord(fmdb: fmdb, table: "BUDDY", cvalues: [
  265. "ex_block" : "1"
  266. ], _where: "f_pin = '\(param1)'")
  267. })
  268. }
  269. }
  270. }
  271. }
  272. } else {
  273. DispatchQueue.global().async {
  274. if let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getUnBlock(l_pin: param1)) {
  275. if response.isOk() {
  276. DispatchQueue.main.async {
  277. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  278. _ = Database.shared.updateRecord(fmdb: fmdb, table: "BUDDY", cvalues: [
  279. "ex_block" : "0"
  280. ], _where: "f_pin = '\(param1)'")
  281. })
  282. }
  283. }
  284. }
  285. }
  286. }
  287. } else if message.name == "tabShowHide" {
  288. guard let dict = message.body as? [String: AnyObject],
  289. let param1 = dict["param1"] as? Bool else {
  290. return
  291. }
  292. if param1 {
  293. ViewController.alwaysHideButton = false
  294. showTabBar()
  295. } else {
  296. ViewController.alwaysHideButton = true
  297. hideTabBar()
  298. }
  299. }
  300. }
  301. func setupSpeech() {
  302. self.speechRecognizer?.delegate = self
  303. SFSpeechRecognizer.requestAuthorization { (authStatus) in
  304. var isButtonEnabled = false
  305. switch authStatus {
  306. case .authorized:
  307. isButtonEnabled = true
  308. case .denied:
  309. isButtonEnabled = false
  310. print("User denied access to speech recognition")
  311. case .restricted:
  312. isButtonEnabled = false
  313. print("Speech recognition restricted on this device")
  314. case .notDetermined:
  315. isButtonEnabled = false
  316. print("Speech recognition not yet authorized")
  317. @unknown default:
  318. isButtonEnabled = false
  319. }
  320. OperationQueue.main.addOperation() {
  321. self.isAllowSpeech = isButtonEnabled
  322. if isButtonEnabled {
  323. UserDefaults.standard.set(isButtonEnabled, forKey: "allowSpeech")
  324. self.runVoice()
  325. }
  326. }
  327. }
  328. }
  329. func runVoice() {
  330. if !audioEngine.isRunning {
  331. alertController = UIAlertController(title: "Start Recording".localized(), message: "Say something, I'm listening!".localized(), preferredStyle: .alert)
  332. self.present(alertController, animated: true)
  333. self.webView.evaluateJavaScript("toggleVoiceButton(true)")
  334. self.startRecording()
  335. }
  336. }
  337. func startRecording() {
  338. // Clear all previous session data and cancel task
  339. if recognitionTask != nil {
  340. recognitionTask?.cancel()
  341. recognitionTask = nil
  342. }
  343. // Create instance of audio session to record voice
  344. let audioSession = AVAudioSession.sharedInstance()
  345. do {
  346. try audioSession.setCategory(AVAudioSession.Category.record, mode: .default, options: [])
  347. try audioSession.setMode(AVAudioSession.Mode.measurement)
  348. try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
  349. } catch {
  350. print("audioSession properties weren't set because of an error.")
  351. }
  352. self.recognitionRequest = SFSpeechAudioBufferRecognitionRequest()
  353. let inputNode = audioEngine.inputNode
  354. guard let recognitionRequest = recognitionRequest else {
  355. fatalError("Unable to create an SFSpeechAudioBufferRecognitionRequest object")
  356. }
  357. recognitionRequest.shouldReportPartialResults = true
  358. self.recognitionTask = speechRecognizer?.recognitionTask(with: recognitionRequest, resultHandler: { (result, error) in
  359. var isFinal = false
  360. var text = ""
  361. if result != nil {
  362. text = result?.bestTranscription.formattedString ?? ""
  363. isFinal = (result?.isFinal)!
  364. self.alertController.dismiss(animated: true)
  365. self.audioEngine.stop()
  366. self.recognitionRequest?.endAudio()
  367. } else {
  368. self.alertController.dismiss(animated: true)
  369. }
  370. if error != nil || isFinal {
  371. if error == nil {
  372. self.webView.evaluateJavaScript("toggleVoiceButton(false)")
  373. self.webView.evaluateJavaScript("submitVoiceSearch('\(text)')")
  374. } else {
  375. self.audioEngine.stop()
  376. self.recognitionRequest?.endAudio()
  377. }
  378. inputNode.removeTap(onBus: 0)
  379. self.recognitionRequest = nil
  380. self.recognitionTask = nil
  381. self.isAllowSpeech = true
  382. }
  383. })
  384. let recordingFormat = inputNode.outputFormat(forBus: 0)
  385. inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { (buffer, when) in
  386. self.recognitionRequest?.append(buffer)
  387. }
  388. self.audioEngine.prepare()
  389. do {
  390. try self.audioEngine.start()
  391. } catch {
  392. print("audioEngine couldn't start because of an error.")
  393. }
  394. }
  395. }
  396. extension FirstTabViewController: SFSpeechRecognizerDelegate {
  397. func speechRecognizer(_ speechRecognizer: SFSpeechRecognizer, availabilityDidChange available: Bool) {
  398. if available {
  399. self.isAllowSpeech = true
  400. } else {
  401. self.isAllowSpeech = false
  402. }
  403. }
  404. }
  405. extension FirstTabViewController: WKUIDelegate, WKNavigationDelegate {
  406. func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
  407. if let urlStr = navigationAction.request.url?.absoluteString {
  408. print("url: \(urlStr)")
  409. collapseDocked()
  410. if urlStr.contains("nexilis/pages/tab1-main-only") || urlStr.contains("nexilis/pages/tab3-main-only") || urlStr.contains("nexilis/pages/tab1-main") || urlStr.contains("nexilis/pages/tab3-commerce") {
  411. ViewController.alwaysHideButton = false
  412. showTabBar()
  413. }
  414. }
  415. decisionHandler(.allow)
  416. }
  417. }