Initial commit
This commit is contained in:
112
apple-client/Sources/App/EmbeddedWebAppView.swift
Normal file
112
apple-client/Sources/App/EmbeddedWebAppView.swift
Normal file
@@ -0,0 +1,112 @@
|
||||
import SwiftUI
|
||||
import WebKit
|
||||
|
||||
struct EmbeddedWebAppView: View {
|
||||
@Bindable var settings: SettingsStore
|
||||
|
||||
var body: some View {
|
||||
PlatformWebView(script: settings.injectionScript, reloadToken: settings.webStateVersion)
|
||||
.ignoresSafeArea()
|
||||
}
|
||||
}
|
||||
|
||||
#if os(macOS)
|
||||
struct PlatformWebView: NSViewRepresentable {
|
||||
let script: String
|
||||
let reloadToken: UUID
|
||||
|
||||
func makeCoordinator() -> Coordinator {
|
||||
Coordinator()
|
||||
}
|
||||
|
||||
func makeNSView(context: Context) -> WKWebView {
|
||||
context.coordinator.makeWebView(script: script)
|
||||
}
|
||||
|
||||
func updateNSView(_ webView: WKWebView, context: Context) {
|
||||
context.coordinator.update(webView: webView, script: script, reloadToken: reloadToken)
|
||||
}
|
||||
}
|
||||
#else
|
||||
struct PlatformWebView: UIViewRepresentable {
|
||||
let script: String
|
||||
let reloadToken: UUID
|
||||
|
||||
func makeCoordinator() -> Coordinator {
|
||||
Coordinator()
|
||||
}
|
||||
|
||||
func makeUIView(context: Context) -> WKWebView {
|
||||
context.coordinator.makeWebView(script: script)
|
||||
}
|
||||
|
||||
func updateUIView(_ webView: WKWebView, context: Context) {
|
||||
context.coordinator.update(webView: webView, script: script, reloadToken: reloadToken)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
final class Coordinator: NSObject, WKNavigationDelegate {
|
||||
private var lastReloadToken: UUID?
|
||||
private var pendingRefreshTask: Task<Void, Never>?
|
||||
|
||||
func makeWebView(script: String) -> WKWebView {
|
||||
let configuration = WKWebViewConfiguration()
|
||||
let contentController = WKUserContentController()
|
||||
contentController.addUserScript(
|
||||
WKUserScript(source: script, injectionTime: .atDocumentStart, forMainFrameOnly: true)
|
||||
)
|
||||
configuration.userContentController = contentController
|
||||
|
||||
let webView = WKWebView(frame: .zero, configuration: configuration)
|
||||
webView.navigationDelegate = self
|
||||
#if os(macOS)
|
||||
webView.setValue(false, forKey: "drawsBackground")
|
||||
#endif
|
||||
loadIndex(into: webView)
|
||||
return webView
|
||||
}
|
||||
|
||||
func update(webView: WKWebView, script: String, reloadToken: UUID) {
|
||||
guard lastReloadToken != reloadToken else {
|
||||
return
|
||||
}
|
||||
|
||||
lastReloadToken = reloadToken
|
||||
pendingRefreshTask?.cancel()
|
||||
pendingRefreshTask = Task { @MainActor [weak webView] in
|
||||
await Task.yield()
|
||||
|
||||
guard !Task.isCancelled, let webView else {
|
||||
return
|
||||
}
|
||||
|
||||
webView.evaluateJavaScript(script) { _, _ in
|
||||
DispatchQueue.main.async {
|
||||
webView.reload()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func loadIndex(into webView: WKWebView) {
|
||||
let indexURL =
|
||||
Bundle.main.url(forResource: "index", withExtension: "html", subdirectory: "WebApp/browser")
|
||||
?? Bundle.main.url(forResource: "index", withExtension: "html", subdirectory: "WebApp")
|
||||
|
||||
guard let indexURL else {
|
||||
let fallbackHTML = """
|
||||
<html>
|
||||
<body style="font-family:-apple-system;padding:32px;background:#08111d;color:#eff3ff">
|
||||
<h2>Embedded chat bundle not found</h2>
|
||||
<p>The Apple app expects the Angular client to be built into the bundled <code>WebApp</code> folder.</p>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
webView.loadHTMLString(fallbackHTML, baseURL: nil)
|
||||
return
|
||||
}
|
||||
|
||||
webView.loadFileURL(indexURL, allowingReadAccessTo: indexURL.deletingLastPathComponent())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user