mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-22 01:22:46 +02:00
1b74f87b35
Replace ReCaptcha navigation delegate with a WKUserContentController. Move callback property closures onto the MainActor. Show a loading indicator whilst waiting for the authentication service to start. Move nextUncompletedStage into FlowResult. Handle text field actions during authentication. Remove scroll view tweaks in server selection screen following EMS banner removal.
139 lines
5.1 KiB
Swift
139 lines
5.1 KiB
Swift
//
|
|
// Copyright 2022 New Vector Ltd
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
|
|
import SwiftUI
|
|
import WebKit
|
|
|
|
struct AuthenticationRecaptchaWebView: UIViewRepresentable {
|
|
|
|
// MARK: - Properties
|
|
|
|
// MARK: Public
|
|
|
|
/// The `siteKey` string to pass to the ReCaptcha widget.
|
|
let siteKey: String
|
|
/// The homeserver's URL, used so ReCaptcha can validate where the request is coming from.
|
|
let homeserverURL: URL
|
|
|
|
/// A binding to boolean that controls whether or not a loading spinner should be shown.
|
|
@Binding var isLoading: Bool
|
|
|
|
/// The completion called when the ReCaptcha was successful. The response string
|
|
/// is passed into the closure as the only argument.
|
|
let completion: (String) -> Void
|
|
|
|
// MARK: Private
|
|
|
|
@Environment(\.theme) private var theme
|
|
|
|
// MARK: - Setup
|
|
|
|
func makeUIView(context: Context) -> WKWebView {
|
|
let userContentController = WKUserContentController()
|
|
userContentController.add(context.coordinator, name: "recaptcha")
|
|
|
|
let configuration = WKWebViewConfiguration()
|
|
configuration.userContentController = userContentController
|
|
|
|
let webView = WKWebView(frame: .zero, configuration: configuration)
|
|
webView.navigationDelegate = context.coordinator
|
|
|
|
#if DEBUG
|
|
// Use a randomised user agent to encourage the ReCaptcha to show a challenge.
|
|
webView.customUserAgent = "Show Me The Traffic Lights \(Float.random(in: 1...100))"
|
|
#endif
|
|
|
|
return webView
|
|
}
|
|
|
|
func updateUIView(_ webView: WKWebView, context: Context) {
|
|
let recaptchaTheme: Coordinator.ReCaptchaTheme = theme.isDark ? .dark : .light
|
|
webView.loadHTMLString(context.coordinator.htmlString(with: siteKey, using: recaptchaTheme), baseURL: homeserverURL)
|
|
}
|
|
|
|
func makeCoordinator() -> Coordinator {
|
|
let coordinator = Coordinator(isLoading: $isLoading)
|
|
coordinator.completion = completion
|
|
return coordinator
|
|
}
|
|
|
|
// MARK: - Coordinator
|
|
|
|
class Coordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler {
|
|
/// The theme used to render the ReCaptcha
|
|
enum ReCaptchaTheme: String { case light, dark }
|
|
|
|
/// A binding to boolean that controls whether or not a loading spinner should be shown.
|
|
@Binding var isLoading: Bool
|
|
|
|
/// The completion called when the ReCaptcha was successful. The response string
|
|
/// is passed into the closure as the only argument.
|
|
var completion: ((String) -> Void)?
|
|
|
|
init(isLoading: Binding<Bool>) {
|
|
self._isLoading = isLoading
|
|
}
|
|
|
|
/// Generates the HTML page to show for the given `siteKey` and `theme`.
|
|
func htmlString(with siteKey: String, using theme: ReCaptchaTheme) -> String {
|
|
"""
|
|
<html>
|
|
<head>
|
|
<meta name='viewport' content='initial-scale=1.0' />
|
|
<style>@media (prefers-color-scheme: dark) { body { background-color: #15191E; } }</style>
|
|
<script type="text/javascript">
|
|
var verifyCallback = function(response) {
|
|
window.webkit.messageHandlers.recaptcha.postMessage(response);
|
|
alert('Testing 1234');
|
|
};
|
|
var onloadCallback = function() {
|
|
grecaptcha.render('recaptcha_widget', {
|
|
'sitekey' : '\(siteKey)',
|
|
'callback': verifyCallback,
|
|
'theme': '\(theme.rawValue)'
|
|
});
|
|
};
|
|
</script>
|
|
</head>
|
|
<body style="margin: 16px;">
|
|
<div id="recaptcha_widget"></div>
|
|
<script src="https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit" async defer>
|
|
</script>
|
|
</body>
|
|
</html>
|
|
"""
|
|
}
|
|
|
|
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
|
|
isLoading = true
|
|
}
|
|
|
|
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
|
|
isLoading = false
|
|
}
|
|
|
|
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
|
isLoading = false
|
|
}
|
|
|
|
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
|
|
guard let response = message.body as? String else { return }
|
|
completion?(response)
|
|
}
|
|
}
|
|
}
|
|
|