Mobile (IOS Webview)
Pertama, pilih file HTML index-debug-standard.html dan ganti namanya menjadi index-mobile.html (Ini opsional atau Anda dapat membiarkannya saja seperti itu).
Kemudian, buka file HTML dan salin kode di bawah ini ke index situs web Anda atau masukkan file HTML. Tempel kode di dalam tag <head> Anda.
<html>
<head>
<meta charset="UTF-8">
<title>Enterprise Web Chat</title>
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1,user-scalable=no" />
<link rel='stylesheet prefetch' href='https://fonts.googleapis.com/css?family=Open+Sans'>
<link rel="stylesheet" href="css/indigo.css">
<link rel="stylesheet" href="css/widget.css">
<link rel="stylesheet" href="css/lightslider.css">
<link rel="stylesheet" href="css/intlTelInput.css">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1,user-scalable=no" />
<style type="text/css">
html {
background: url(images/bg-login.svg) no-repeat;
height: 100%;
background-size: cover;
}
</style>
<!-- <script src="https://maps.googleapis.com/maps/api/js?key=<KEY>&libraries=places"></script> -->
<script src="https://www.google.com/recaptcha/api.js"></script>
<!-- <script src="http://code.responsivevoice.org/responsivevoice.js"></script> -->
<script src='js/jquery-2.1.3.min.js'></script>
<script src='js/iframeResizer.contentWindow.js'></script>
<script src='js/iframeResizer.js'></script>
<script src='js/lightslider.js'></script>
<script src='js/lazyload.min.js'></script>
<script src="js/cryptojs/pbkdf2.js"></script>
<script src="js/cryptojs/aes.js"></script>
<script src="js/cipher/aes-util.js"></script>
<script src="js/stp.js"></script>
<script src="js/sjs.js"></script>
<script src="js/chat.js"></script>
<script src="js/fuse.js"></script>
<script src="js/speech.js"></script>
<script src="js/intlTelInput.js"></script>
<script>
$(document).ready(function() {
Chat.init({
header: 'Welcome to Our Chat',
login_sub_header: 'Please tell us about yourself',
connect_message: 'Do you have questions ? <br>Come chat with us, we are here to help you',
chat_sub_header: 'Our agent will serve you shortly',
url: 'https://<host>:<port>',
client_id: '<client_id>',
client_secret: '<client_secret>',
type_placeholder: 'Type message...',
avatar: '<bot_image>',
icon_avatar: '<icon_image>',
agent_avatar: '<agent_image>',
enable_attachment: true,
enable_voice: true,
enable_speech: true,
enable_queue: true,
enable_history: true,
compatibility_mode: false,
queue_text: "⏰NOMOR URUT: ",
enable_campaign: false,
campaign_avatar: '<campaign_image>',
campaign_title: '<campaign_title>',
campaign_text: 'Hello 👋, What do you think about our service ?',
campaign_timer: 5000,
campaign_menu: [{
"label": "<Campaign Label>",
"value": "<Campaign Payload>",
"icon": "<campaign_icon>"
}, {
"label": "<Campaign Label>",
"value": "<Campaign Payload>",
"icon": "<campaign_icon>"
}, {
"label": "<Campaign Label>",
"value": "<Campaign Payload>",
"icon": "<campaign_icon>"
}],
max_upload_message: "File size limit exceeded. Maximum filesize is [max_filesize]",
selection_topic_placeholder: "Please select a topic",
selection_topic_member: ["Product", "Service"],
selection_topic_non_member: ["Hai", "Hello"],
member_mode: false,
selection_topic_placeholder: "Please select a topic",
enable_service_hour: false,
service_hour: [{
"days": "sunday",
"startHour": "00:00",
"endHour": "23:59",
"holiday": true
},
{
"days": "monday",
"startHour": "00:00",
"endHour": "23:59",
"holiday": true
},
{
"days": "tuesday",
"startHour": "00:00",
"endHour": "15:52",
"holiday": true
},
{
"days": "wednesday",
"startHour": "00:00",
"endHour": "23:59",
"holiday": true
},
{
"days": "thursday",
"startHour": "00:00",
"endHour": "23:59",
"holiday": true
},
{
"days": "friday",
"startHour": "00:00",
"endHour": "23:59",
"holiday": true
},
{
"days": "saturday",
"startHour": "00:00",
"endHour": "23:59",
"holiday": true
}
],
channel_id_email: "25b7fe4f7bd81aa0533be4963972ac74", /* channel id email */
subject_email: "Of service hour ticket",
send_email_success_message: "Email sent successfully !<br/><br/>Thanks, we have received your question or your complaint",
process_send_email_message: "Sending Email...",
process_send_email_error_message: "Email sent failed !",
is_waiting_text_icon: true
});
});
</script>
</head>
<body>
</body>
</html>
Hapus semua file HTML tetapi pertahankan index-mobile.html sebagai gantinya.
Salin seluruh direktori live chat yang tersisa ke path yang Anda tentukan dari server hosting web Anda.
Asumsikan yang ingin Anda pilih adalah tema indigo, lalu buka index-mobile.html dan ubah kode menggunakan warna yang Anda inginkan.
<link rel="stylesheet" href="css/indigo.css">
<link rel="stylesheet" href="css/widget.css">
<link rel="stylesheet" href="css/lightslider.css">
<link rel="stylesheet" href="css/intlTelInput.css">
Jangan lupa untuk menyesuaikan resource path yang ada di index-mobile.html yang digunakan untuk memuat file seperti js, CSS, atau gambar, sesuai dengan tempat resource disimpan di server Anda.
<html>
<head>
<meta charset="UTF-8">
<title>Enterprise Web Chat</title>
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1,user-scalable=no" />
<link rel='stylesheet prefetch' href='https://fonts.googleapis.com/css?family=Open+Sans'>
<link rel="stylesheet" href="css/indigo.css">
<link rel="stylesheet" href="css/widget.css">
<link rel="stylesheet" href="css/lightslider.css">
<link rel="stylesheet" href="css/intlTelInput.css">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1,user-scalable=no" />
<style type="text/css">
html {
background: url(images/bg-login.svg) no-repeat;
height: 100%;
background-size: cover;
}
</style>
<!-- <script src="https://maps.googleapis.com/maps/api/js?key=<KEY>&libraries=places"></script> -->
<script src="https://www.google.com/recaptcha/api.js"></script>
<!-- <script src="http://code.responsivevoice.org/responsivevoice.js"></script> -->
<script src='js/jquery-2.1.3.min.js'></script>
<script src='js/iframeResizer.contentWindow.js'></script>
<script src='js/iframeResizer.js'></script>
<script src='js/lightslider.js'></script>
<script src='js/lazyload.min.js'></script>
<script src="js/cryptojs/pbkdf2.js"></script>
<script src="js/cryptojs/aes.js"></script>
<script src="js/cipher/aes-util.js"></script>
<script src="js/stp.js"></script>
<script src="js/sjs.js"></script>
<script src="js/chat.js"></script>
<script src="js/fuse.js"></script>
<script src="js/speech.js"></script>
<script src="js/intlTelInput.js"></script>
<script>
$(document).ready(function() {
Chat.init({
header: 'Welcome to Our Chat',
login_sub_header: 'Please tell us about yourself',
connect_message: 'Do you have questions ? <br>Come chat with us, we are here to help you',
chat_sub_header: 'Our agent will serve you shortly',
url: 'https://<host>:<port>',
client_id: '<client_id>',
client_secret: '<client_secret>',
type_placeholder: 'Type message...',
avatar: '<bot_image>',
icon_avatar: '<icon_image>',
agent_avatar: '<agent_image>',
enable_attachment: true,
enable_voice: true,
enable_speech: true,
enable_queue: true,
enable_history: true,
compatibility_mode: false,
queue_text: "⏰NOMOR URUT: ",
enable_campaign: false,
campaign_avatar: '<campaign_image>',
campaign_title: '<campaign_title>',
campaign_text: 'Hello 👋, What do you think about our service ?',
campaign_timer: 5000,
campaign_menu: [{
"label": "<Campaign Label>",
"value": "<Campaign Payload>",
"icon": "<campaign_icon>"
}, {
"label": "<Campaign Label>",
"value": "<Campaign Payload>",
"icon": "<campaign_icon>"
}, {
"label": "<Campaign Label>",
"value": "<Campaign Payload>",
"icon": "<campaign_icon>"
}],
max_upload_message: "File size limit exceeded. Maximum filesize is [max_filesize]",
selection_topic_placeholder: "Please select a topic",
selection_topic_member: ["Product", "Service"],
selection_topic_non_member: ["Hai", "Hello"],
member_mode: false,
selection_topic_placeholder: "Please select a topic",
enable_service_hour: false,
service_hour: [{
"days": "sunday",
"startHour": "00:00",
"endHour": "23:59",
"holiday": true
},
{
"days": "monday",
"startHour": "00:00",
"endHour": "23:59",
"holiday": true
},
{
"days": "tuesday",
"startHour": "00:00",
"endHour": "15:52",
"holiday": true
},
{
"days": "wednesday",
"startHour": "00:00",
"endHour": "23:59",
"holiday": true
},
{
"days": "thursday",
"startHour": "00:00",
"endHour": "23:59",
"holiday": true
},
{
"days": "friday",
"startHour": "00:00",
"endHour": "23:59",
"holiday": true
},
{
"days": "saturday",
"startHour": "00:00",
"endHour": "23:59",
"holiday": true
}
],
channel_id_email: "25b7fe4f7bd81aa0533be4963972ac74", /* channel id email */
subject_email: "Of service hour ticket",
send_email_success_message: "Email sent successfully !<br/><br/>Thanks, we have received your question or your complaint",
process_send_email_message: "Sending Email...",
process_send_email_error_message: "Email sent failed !",
is_waiting_text_icon: true
});
});
</script>
</head>
<body>
</body>
</html>
Selanjutnya, sesuaikan url, client_id dan properti client_secret sesuai dengan konfigurasi channel back-end Anda. Silakan merujuk kembali ke bagian How to Connect Client to Live Chat Channel untuk informasi lebih lanjut.
Di bagian sebelumnya ditujukkan untuk meletakkan source code pada situs web yang sudah tersedia, sedangkan di bagian ini kita harus membuat host live chat secara mandiri.
Di bawah ini merupakan sample code untuk mengonfigurasi tampilan web Anda yang menampilkan index-mobile.html yang sudah dihosting.
Configure Web View
Untuk mengaktifkan JavaScript dan mengonfigurasi tampilan web, Anda perlu mengatur properti yang diperlukan.
let webConfiguration = WKWebViewConfiguration()
webConfiguration.defaultWebpagePreferences.allowsContentJavaScript = true
webConfiguration.websiteDataStore = WKWebsiteDataStore.default()
let webView = WKWebView(frame: .zero, configuration: webConfiguration)
Sekarang Anda dapat memuat URL
webView.load(URLRequest(url: URL(string: "https://hostname/webchat/indexdebug-standard.html")!))
Untuk menyesuaikan setelah halaman selesai dimuat, Anda dapat menjalankan kode ini.
func webView(
_ webView: WKWebView,
didStartProvisionalNavigation navigation: WKNavigation!
) {
DispatchQueue.main.async {
self.parent.progress = 0.3
self.parent.isLoading = true
}
}
func webView(
_ webView: WKWebView,
didFinish navigation: WKNavigation!
) {
DispatchQueue.main.async {
self.parent.progress = 1.0
self.parent.isLoading = false
}
}
Enable Voice to Text (Speech Recognition)
Untuk mengaktifkan fitur ini, WebView perlu meminta izin untuk merekam audio
func webView(
_ webView: WKWebView,
requestMediaCapturePermissionFor origin: WKSecurityOrigin,
initiatedByFrame frame: WKFrameInfo,
type: WKMediaCaptureType,
decisionHandler: @escaping WKPermissionDecision
) {
if #available(iOS 17.0, *) {
AVAudioApplication.requestRecordPermission { granted in
DispatchQueue.main.async {
decisionHandler(granted ? .grant : .deny)
}
}
} else {
AVAudioSession.sharedInstance().requestRecordPermission { granted in
DispatchQueue.main.async {
decisionHandler(granted ? .grant : .deny)
}
}
}
}
Enable Attachment
Untuk mengaktifkan lampiran file, implementasikan pemilih gambar di dalam delegasi WebView.
func webView(
_ webView: WKWebView,
runJavaScriptConfirmPanelWithMessage message: String,
initiatedByFrame frame: WKFrameInfo,
completionHandler: @escaping (Bool) -> Void
) {
let picker = UIImagePickerController()
picker.delegate = self
picker.sourceType = .photoLibrary
if let windowScene = UIApplication.shared.connectedScenes
.compactMap({ $0 as? UIWindowScene })
.first,
let rootViewController = windowScene.windows.first?.rootViewController {
rootViewController.present(picker, animated: true)
}
}
Setelah sebuah gambar dipilih, URL file yang dipilih harus diproses.
func imagePickerController(
_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]
) {
picker.dismiss(animated: true, completion: nil)
if let imageURL = info[.imageURL] as? URL {
filePathCallback?([imageURL])
} else {
filePathCallback?(nil)
}
filePathCallback = nil
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
filePathCallback?(nil)
filePathCallback = nil
}
Berikut adalah contoh kode akhir jika Anda telah mengikuti langkah-langkah di atas.
import SwiftUI
@preconcurrency import WebKit
import AVFoundation
struct WebView: UIViewRepresentable {
@Binding var progress: Double
@Binding var isLoading: Bool
class Coordinator: NSObject, WKNavigationDelegate, WKUIDelegate, WKScriptMessageHandler, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
var parent: WebView
private let audioSession = AVAudioSession.sharedInstance()
private var filePathCallback: (([URL]?) -> Void)?
init(_ parent: WebView) {
self.parent = parent
}
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
DispatchQueue.main.async {
self.parent.progress = 0.3
self.parent.isLoading = true
}
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
DispatchQueue.main.async {
self.parent.progress = 1.0
self.parent.isLoading = false
}
}
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
// Handle messages from JavaScript
}
func webView(
_ webView: WKWebView,
requestMediaCapturePermissionFor origin: WKSecurityOrigin,
initiatedByFrame frame: WKFrameInfo,
type: WKMediaCaptureType,
decisionHandler: @escaping (WKPermissionDecision) -> Void
) {
if #available(iOS 17.0, *) {
AVAudioApplication.requestRecordPermission { granted in
DispatchQueue.main.async {
decisionHandler(granted ? .grant : .deny)
}
}
} else {
audioSession.requestRecordPermission { granted in
DispatchQueue.main.async {
decisionHandler(granted ? .grant : .deny)
}
}
}
}
func webView(
_ webView: WKWebView,
runJavaScriptConfirmPanelWithMessage message: String,
initiatedByFrame frame: WKFrameInfo,
completionHandler: @escaping (Bool) -> Void
) {
let picker = UIImagePickerController()
picker.delegate = self
picker.sourceType = .photoLibrary
if let windowScene = UIApplication.shared.connectedScenes
.compactMap({ $0 as? UIWindowScene })
.first,
let rootViewController = windowScene.windows.first?.rootViewController {
rootViewController.present(picker, animated: true)
}
}
func imagePickerController(
_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]
) {
picker.dismiss(animated: true, completion: nil)
if let imageURL = info[.imageURL] as? URL {
filePathCallback?([imageURL])
} else {
filePathCallback?(nil)
}
filePathCallback = nil
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
filePathCallback?(nil)
filePathCallback = nil
}
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIView(context: Context) -> WKWebView {
let webConfiguration = WKWebViewConfiguration()
webConfiguration.defaultWebpagePreferences.allowsContentJavaScript = true
webConfiguration.websiteDataStore = WKWebsiteDataStore.default()
let webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.uiDelegate = context.coordinator
webView.navigationDelegate = context.coordinator
webView.load(URLRequest(url: URL(string: "Server URL")!))
return webView
}
func updateUIView(_ uiView: WKWebView, context: Context) {}
}
struct ContentView: View {
@State private var progress: Double = 0.0
@State private var isLoading: Bool = true
var body: some View {
ZStack {
WebView(progress: $progress, isLoading: $isLoading)
if isLoading {
ProgressView(value: progress)
.progressViewStyle(LinearProgressViewStyle())
.frame(width: UIScreen.main.bounds.width * 0.4, height: 6)
.background(Color.gray.opacity(0.3))
.cornerRadius(3)
}
}
.edgesIgnoringSafeArea(.all)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Jika Anda ingin mencoba kode kami, silakan klik ini untuk memulai mencoba aplikasi kami.
Last updated
Was this helpful?