RTSDA-iOS/Views/BulletinView.swift
2025-02-03 16:15:57 -05:00

193 lines
7.8 KiB
Swift

@preconcurrency import SwiftUI
@preconcurrency import WebKit
struct BulletinView: View {
@State private var isLoading = true
@Environment(\.dismiss) private var dismiss
var body: some View {
WebViewWithRefresh(url: URL(string: "https://rtsda.updates.church")!, isLoading: $isLoading)
.navigationTitle("Bulletin")
.navigationBarTitleDisplayMode(.inline)
.background(Color(uiColor: .systemBackground))
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
if isLoading {
ProgressView()
.progressViewStyle(.circular)
}
}
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: {
dismiss()
}) {
Image(systemName: "xmark")
.foregroundColor(.primary)
}
}
}
}
}
struct WebViewWithRefresh: UIViewRepresentable {
let url: URL
@Binding var isLoading: Bool
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIView(context: Context) -> WKWebView {
let configuration = WKWebViewConfiguration()
let webView = WKWebView(frame: .zero, configuration: configuration)
webView.navigationDelegate = context.coordinator
// Add swipe gesture recognizer
let swipeGesture = UISwipeGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.handleSwipe(_:)))
swipeGesture.direction = .right
webView.addGestureRecognizer(swipeGesture)
// Configure refresh control with custom appearance
let refreshControl = UIRefreshControl()
refreshControl.addTarget(context.coordinator,
action: #selector(Coordinator.handleRefresh),
for: .valueChanged)
// Set the refresh control's background color
refreshControl.backgroundColor = .clear
webView.scrollView.refreshControl = refreshControl
// Set background colors
webView.isOpaque = false
webView.backgroundColor = .clear
webView.scrollView.backgroundColor = .clear
let request = URLRequest(url: url)
webView.load(request)
return webView
}
func updateUIView(_ webView: WKWebView, context: Context) {
// No updates needed
}
class Coordinator: NSObject, WKNavigationDelegate {
var parent: WebViewWithRefresh
init(_ parent: WebViewWithRefresh) {
self.parent = parent
}
@objc func handleSwipe(_ gesture: UISwipeGestureRecognizer) {
print("🔄 Swipe detected")
if gesture.state == .ended {
if let webView = gesture.view as? WKWebView {
print("📱 Attempting to trigger back action")
// JavaScript to click the Chakra UI back button
let script = """
function findBackButton() {
// Common back button selectors
var selectors = [
'button[aria-label="Go back"]',
'button.chakra-button[aria-label*="back"]',
'button.chakra-button svg[aria-label*="back"]',
'button.chakra-button span svg[aria-hidden="true"]',
'button svg[data-icon="arrow-left"]',
'button.chakra-button svg',
'button.chakra-button'
];
for (var i = 0; i < selectors.length; i++) {
var buttons = document.querySelectorAll(selectors[i]);
for (var j = 0; j < buttons.length; j++) {
var button = buttons[j];
// Check if it looks like a back button
if (button.textContent.toLowerCase().includes('back') ||
button.getAttribute('aria-label')?.toLowerCase().includes('back') ||
button.innerHTML.toLowerCase().includes('back')) {
console.log('Found back button:', button.outerHTML);
return button;
}
}
}
console.log('No back button found');
return null;
}
var backButton = findBackButton();
if (backButton) {
backButton.click();
true;
} else {
false;
}
"""
webView.evaluateJavaScript(script) { result, error in
if let error = error {
print("❌ JavaScript error: \(error.localizedDescription)")
} else if let success = result as? Bool {
print(success ? "✅ Back button clicked" : "❌ No back button found")
}
}
}
}
}
@objc func handleRefresh(sender: UIRefreshControl) {
parent.isLoading = true
if let webView = sender.superview?.superview as? WKWebView {
// Clear all website data
WKWebsiteDataStore.default().removeData(
ofTypes: [WKWebsiteDataTypeDiskCache, WKWebsiteDataTypeMemoryCache],
modifiedSince: Date(timeIntervalSince1970: 0)
) {
DispatchQueue.main.async {
// Create a fresh request
if let url = webView.url {
let request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalAndRemoteCacheData)
webView.load(request)
}
}
}
}
}
// Navigation delegate methods
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
parent.isLoading = true
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
parent.isLoading = false
webView.scrollView.refreshControl?.endRefreshing()
}
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
parent.isLoading = false
webView.scrollView.refreshControl?.endRefreshing()
}
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
parent.isLoading = false
webView.scrollView.refreshControl?.endRefreshing()
}
// Handle back navigation
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
if navigationAction.navigationType == .backForward {
decisionHandler(.cancel)
parent.isLoading = false
return
}
decisionHandler(.allow)
}
}
}
#Preview {
NavigationStack {
BulletinView()
}
}