new: extract WebView feature module
This commit is contained in:
@@ -1,119 +0,0 @@
|
||||
package bzh.ajaury.chombev.web.ui
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.interop.UIKitView
|
||||
import kotlinx.cinterop.ExperimentalForeignApi
|
||||
import kotlinx.cinterop.ObjCSignatureOverride
|
||||
import platform.CoreGraphics.CGRectZero
|
||||
import platform.Foundation.NSError
|
||||
import platform.Foundation.NSURL
|
||||
import platform.Foundation.NSURLRequest
|
||||
import platform.UIKit.UIUserInterfaceStyle
|
||||
import platform.UIKit.UIUserInterfaceStyle.UIUserInterfaceStyleDark
|
||||
import platform.UIKit.UIUserInterfaceStyle.UIUserInterfaceStyleLight
|
||||
import platform.WebKit.WKNavigation
|
||||
import platform.WebKit.WKNavigationDelegateProtocol
|
||||
import platform.WebKit.WKWebView
|
||||
import platform.WebKit.WKWebViewConfiguration
|
||||
import platform.darwin.NSObject
|
||||
|
||||
@OptIn(ExperimentalForeignApi::class)
|
||||
@Composable
|
||||
actual fun PlatformWebView(
|
||||
url: String,
|
||||
offlineHtml: String?,
|
||||
darkTheme: Boolean,
|
||||
onLoadingStateChange: (Boolean) -> Unit,
|
||||
modifier: Modifier,
|
||||
) {
|
||||
// WKWebView holds its navigationDelegate weakly, so keep a strong reference across recompositions.
|
||||
val navigationDelegate = remember { WebViewNavigationDelegate() }
|
||||
navigationDelegate.onLoadingStateChange = onLoadingStateChange
|
||||
navigationDelegate.offlineHtml = offlineHtml
|
||||
navigationDelegate.baseUrl = url
|
||||
|
||||
UIKitView(
|
||||
modifier = modifier,
|
||||
factory = {
|
||||
WKWebView(
|
||||
frame = CGRectZero.readValue(),
|
||||
configuration = WKWebViewConfiguration(),
|
||||
).apply {
|
||||
navigationDelegate = navigationDelegate
|
||||
setOverrideUserInterfaceStyle(darkTheme.toInterfaceStyle())
|
||||
NSURL.URLWithString(url)?.let { nsUrl ->
|
||||
loadRequest(NSURLRequest(uRL = nsUrl))
|
||||
}
|
||||
}
|
||||
},
|
||||
// Re-apply on recomposition so a light/dark switch takes effect without reloading the page.
|
||||
update = { webView ->
|
||||
webView.setOverrideUserInterfaceStyle(darkTheme.toInterfaceStyle())
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Forwards WKWebView load lifecycle events to [onLoadingStateChange] so the UI can show a loader
|
||||
* while a page is loading and hide it once loading finishes.
|
||||
*
|
||||
* When a navigation fails, it swaps in [offlineHtml] (loaded against [baseUrl] so links resolve) so
|
||||
* the page still shows content offline. [showingOffline] guards against re-entering the fallback.
|
||||
*/
|
||||
private class WebViewNavigationDelegate :
|
||||
NSObject(),
|
||||
WKNavigationDelegateProtocol {
|
||||
var onLoadingStateChange: (Boolean) -> Unit = {}
|
||||
var offlineHtml: String? = null
|
||||
var baseUrl: String = ""
|
||||
|
||||
private var showingOffline = false
|
||||
|
||||
override fun webView(
|
||||
webView: WKWebView,
|
||||
didStartProvisionalNavigation: WKNavigation?,
|
||||
) {
|
||||
onLoadingStateChange(true)
|
||||
}
|
||||
|
||||
// Shares the (WKWebView, WKNavigation?) Kotlin signature with didStartProvisionalNavigation above.
|
||||
@ObjCSignatureOverride
|
||||
override fun webView(
|
||||
webView: WKWebView,
|
||||
didFinishNavigation: WKNavigation?,
|
||||
) {
|
||||
onLoadingStateChange(false)
|
||||
}
|
||||
|
||||
override fun webView(
|
||||
webView: WKWebView,
|
||||
didFailNavigation: WKNavigation?,
|
||||
withError: NSError,
|
||||
) {
|
||||
handleFailure(webView)
|
||||
}
|
||||
|
||||
// Shares the (WKWebView, WKNavigation?, NSError) Kotlin signature with didFailNavigation above.
|
||||
@ObjCSignatureOverride
|
||||
override fun webView(
|
||||
webView: WKWebView,
|
||||
didFailProvisionalNavigation: WKNavigation?,
|
||||
withError: NSError,
|
||||
) {
|
||||
handleFailure(webView)
|
||||
}
|
||||
|
||||
private fun handleFailure(webView: WKWebView) {
|
||||
val html = offlineHtml
|
||||
if (html != null && !showingOffline) {
|
||||
showingOffline = true
|
||||
webView.loadHTMLString(string = html, baseURL = NSURL.URLWithString(baseUrl))
|
||||
} else {
|
||||
onLoadingStateChange(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Boolean.toInterfaceStyle(): UIUserInterfaceStyle = if (this) UIUserInterfaceStyleDark else UIUserInterfaceStyleLight
|
||||
Reference in New Issue
Block a user