import Combine import SafariServices import SwiftUI import WebKit public final class WebAppWrapperViewModel: ObservableObject { public enum Action { case shareHighlight(highlightID: String) } public var subscriptions = Set() public let performActionSubject = PassthroughSubject() let webViewURLRequest: URLRequest let baseURL: URL let rawAuthCookie: String? @Published var sendIncreaseFontSignal: Bool = false @Published var sendDecreaseFontSignal: Bool = false public init(webViewURLRequest: URLRequest, baseURL: URL, rawAuthCookie: String?) { self.webViewURLRequest = webViewURLRequest self.rawAuthCookie = rawAuthCookie self.baseURL = baseURL } } public struct WebAppWrapperView: View { struct SafariWebLink: Identifiable { let id: UUID let url: URL } @ObservedObject private var viewModel: WebAppWrapperViewModel @State var showHighlightAnnotationModal = false @State private var annotation = String() @State var annotationSaveTransactionID: UUID? @State var safariWebLink: SafariWebLink? public init(viewModel: WebAppWrapperViewModel) { self.viewModel = viewModel } public var body: some View { let webAppView = WebAppView( request: viewModel.webViewURLRequest, baseURL: viewModel.baseURL, rawAuthCookie: viewModel.rawAuthCookie, openLinkAction: { #if os(macOS) NSWorkspace.shared.open($0) #elseif os(iOS) safariWebLink = SafariWebLink(id: UUID(), url: $0) #endif }, webViewActionHandler: webViewActionHandler, annotation: $annotation, annotationSaveTransactionID: $annotationSaveTransactionID, sendIncreaseFontSignal: $viewModel.sendIncreaseFontSignal, sendDecreaseFontSignal: $viewModel.sendDecreaseFontSignal ) return VStack { webAppView } .sheet(item: $safariWebLink) { SafariView(url: $0.url) } .sheet(isPresented: $showHighlightAnnotationModal) { HighlightAnnotationSheet( annotation: $annotation, onSave: { annotationSaveTransactionID = UUID() showHighlightAnnotationModal = false }, onCancel: { showHighlightAnnotationModal = false } ) } } func webViewActionHandler(message: WKScriptMessage) { if message.name == WebViewAction.highlightAction.rawValue { handleHighlightAction(message: message) } } private func handleHighlightAction(message: WKScriptMessage) { guard let messageBody = message.body as? [String: String] else { return } guard let actionID = messageBody["actionID"] else { return } switch actionID { case "share": if let highlightId = messageBody["highlightID"] { viewModel.performActionSubject.send(.shareHighlight(highlightID: highlightId)) } case "annotate": annotation = messageBody["annotation"] ?? "" showHighlightAnnotationModal = true default: break } } } #if os(iOS) struct SafariView: UIViewControllerRepresentable { let url: URL func makeUIViewController(context _: UIViewControllerRepresentableContext) -> SFSafariViewController { SFSafariViewController(url: url) } // swiftlint:disable:next line_length func updateUIViewController(_: SFSafariViewController, context _: UIViewControllerRepresentableContext) {} } #elseif os(macOS) struct SafariView: View { let url: URL var body: some View { Color.clear } } #endif