Merge master into spm

This commit is contained in:
Денис Либит
2025-04-15 20:50:36 +03:00
4 changed files with 151 additions and 14 deletions

View File

@@ -34,6 +34,11 @@ open class ChatApi {
} }
} }
public func setInfo(_ token: String, params: Dictionary<String, Any>, callback: @escaping (NSDictionary?) -> Void) {
send(token, "chat/client/setInfo", params, callback: callback)
}
public func messages(_ token: String, params: Dictionary<String, Any>, callback: @escaping (NSDictionary?) -> Void) { public func messages(_ token: String, params: Dictionary<String, Any>, callback: @escaping (NSDictionary?) -> Void) {
send(token, "chat/message/getList", params, callback: callback) send(token, "chat/message/getList", params, callback: callback)
} }
@@ -53,4 +58,8 @@ open class ChatApi {
] as [String : Any] ] as [String : Any]
(ChatApi()).messages(token, params: params, callback: callback) (ChatApi()).messages(token, params: params, callback: callback)
} }
public static func setInfo(_ token: String, _ params: Dictionary<String, Any>, callback: @escaping (NSDictionary?) -> Void) {
(ChatApi()).setInfo(token, params: params, callback: callback)
}
} }

View File

@@ -10,6 +10,7 @@ import UIKit
@preconcurrency import WebKit @preconcurrency import WebKit
import AVFoundation import AVFoundation
@available(iOS 13.0, *)
open class ChatController: UIViewController, WKNavigationDelegate, WKScriptMessageHandler { open class ChatController: UIViewController, WKNavigationDelegate, WKScriptMessageHandler {
public static let event_operatorSendMessage = "operatorSendMessage" public static let event_operatorSendMessage = "operatorSendMessage"
@@ -37,6 +38,7 @@ open class ChatController: UIViewController, WKNavigationDelegate, WKScriptMessa
private var widgetUrl: String = "" private var widgetUrl: String = ""
private var widgetOrg: String = "" private var widgetOrg: String = ""
private var css: String = "" private var css: String = ""
private var alertLoading: UIAlertController?
private static func getUnreadedMessagesCallback(_ result: NSDictionary) -> NSDictionary { private static func getUnreadedMessagesCallback(_ result: NSDictionary) -> NSDictionary {
let resultWrapper = ChatApiMessagesWrapper(result) let resultWrapper = ChatApiMessagesWrapper(result)
@@ -133,6 +135,23 @@ open class ChatController: UIViewController, WKNavigationDelegate, WKScriptMessa
}) })
} }
public static func setInfoCustomDataValue(key: String, value: String, callback: @escaping (NSDictionary?) -> Void) {
DispatchQueue.global().async {
ChatApi.setInfo(
ChatConfig.getApiToken(),
[
"client": [
"id": ChatConfig.getClientId(),
"customData": [
key: value
]
],
] as [String : Any],
callback: callback
)
}
}
override public func loadView() { override public func loadView() {
let contentController = WKUserContentController() let contentController = WKUserContentController()
contentController.add(self, name: "chatInterface") contentController.add(self, name: "chatInterface")
@@ -151,9 +170,84 @@ open class ChatController: UIViewController, WKNavigationDelegate, WKScriptMessa
} }
chatView = WKWebView(frame: frame, configuration: config) chatView = WKWebView(frame: frame, configuration: config)
chatView?.navigationDelegate = self chatView?.navigationDelegate = self
view = chatView view = chatView
} }
private func getAlertLoadingActionCloseTitle() -> String {
let currentLanguage = Locale.current.languageCode
if currentLanguage == "ru" {
return "Закрыть"
}
return "Close"
}
private func showLoadingDialog() {
if alertLoading != nil {
return
}
alertLoading = UIAlertController(
title: nil,
message: " ",
preferredStyle: .alert
)
alertLoading?.addAction(UIAlertAction(title: getAlertLoadingActionCloseTitle(), style: .destructive, handler: cancelLoading))
let loadingIndicator = UIActivityIndicatorView(style: .medium)
loadingIndicator.translatesAutoresizingMaskIntoConstraints = false
loadingIndicator.startAnimating()
alertLoading?.view.addSubview(loadingIndicator)
NSLayoutConstraint.activate([
loadingIndicator.centerXAnchor.constraint(equalTo: alertLoading!.view.centerXAnchor),
loadingIndicator.bottomAnchor.constraint(equalTo: alertLoading!.view.bottomAnchor, constant: -60)
])
present(alertLoading!, animated: true)
}
private func cancelLoading(action: UIAlertAction) {
onCloseSupport()
}
private func hideLoadingDialog() {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
if self.alertLoading == nil {
return
}
self.alertLoading?.dismiss(animated: true, completion: nil)
self.alertLoading = nil
}
}
public func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
showLoadingDialog()
}
public func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
hideLoadingDialog()
showMessage(error.localizedDescription)
}
public func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: any Error) {
hideLoadingDialog()
showMessage(error.localizedDescription)
}
private func showMessage(_ message: String) {
let alert = UIAlertController(
title: nil,
message: message,
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: "OK", style: .default) { _ in
self.onCloseSupport()
})
present(alert, animated: true, completion: nil)
}
public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
didFinish = true didFinish = true
if callJs != nil && !callJs.isEmpty { if callJs != nil && !callJs.isEmpty {
@@ -162,12 +256,10 @@ open class ChatController: UIViewController, WKNavigationDelegate, WKScriptMessa
} }
callJs = nil callJs = nil
} }
// hideLoadingDialog()
} }
public func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> ()) { public func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> ()) {
// print("widgetUrl = \(self.widgetUrl)")
// print("widgetOrg = \(self.widgetOrg)")
// print("absoluteString = \(navigationAction.request.url?.absoluteString)")
if let _ = navigationAction.request.url?.host { if let _ = navigationAction.request.url?.host {
if (navigationAction.request.url?.absoluteString.contains(self.widgetOrg))! { if (navigationAction.request.url?.absoluteString.contains(self.widgetOrg))! {
decisionHandler(.allow) decisionHandler(.allow)
@@ -213,6 +305,7 @@ open class ChatController: UIViewController, WKNavigationDelegate, WKScriptMessa
} }
private func callJs(_ script: String) { private func callJs(_ script: String) {
print("callJs : \(script)")
chatView?.evaluateJavaScript(script) chatView?.evaluateJavaScript(script)
} }
@@ -387,6 +480,7 @@ open class ChatController: UIViewController, WKNavigationDelegate, WKScriptMessa
} }
public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
hideLoadingDialog()
if message.name != "chatInterface" { if message.name != "chatInterface" {
return return
} }
@@ -411,6 +505,8 @@ open class ChatController: UIViewController, WKNavigationDelegate, WKScriptMessa
switch name { switch name {
case ChatController.method_pageLoaded: case ChatController.method_pageLoaded:
injectCss(style: self.css) injectCss(style: self.css)
onChatWasOpen()
listenApplicationState()
break break
case ChatController.event_closeSupport: case ChatController.event_closeSupport:
onCloseSupport() onCloseSupport()
@@ -444,28 +540,60 @@ open class ChatController: UIViewController, WKNavigationDelegate, WKScriptMessa
onEvent(name, data!) onEvent(name, data!)
} }
private func listenApplicationState() {
NotificationCenter.default.addObserver(
self,
selector: #selector(appDidBecomeActive),
name: UIApplication.didBecomeActiveNotification,
object: nil
)
NotificationCenter.default.addObserver(
self,
selector: #selector(appWillResignActive),
name: UIApplication.willResignActiveNotification,
object: nil
)
}
@objc private func appDidBecomeActive() {
onChatWasOpen()
}
@objc private func appWillResignActive() {
onChatWasClosed()
}
open func onChatWasOpen() {
}
open func onChatWasClosed() {
}
open func onCloseSupport() { open func onCloseSupport() {
if chatView == nil { if chatView == nil {
return return
} }
chatView?.stopLoading()
callJsDestroy()
chatView = nil
dismiss(animated: true, completion: nil) dismiss(animated: true, completion: nil)
navigationController?.popViewController(animated: true) navigationController?.popViewController(animated: true)
NotificationCenter.default.removeObserver(self)
onChatWasClosed()
} }
open override func viewDidDisappear(_ animated: Bool) { open override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated) super.viewDidDisappear(animated)
onCloseSupport()
if animated && chatView != nil {
chatView?.stopLoading()
callJsDestroy()
chatView = nil
}
} }
open func onLinkPressed(url: URL) { open func onLinkPressed(url: URL) {
if UIApplication.shared.canOpenURL(url) { UIApplication.shared.open(url, options: [:], completionHandler: nil)
UIApplication.shared.open(url)
}
} }
open func playSound(_ systemSoundId: SystemSoundID) { open func playSound(_ systemSoundId: SystemSoundID) {