From 376f603f29e6bc87398f2b49ec3123ba5c3833ba Mon Sep 17 00:00:00 2001 From: blackxfiied <41133734+blackxfiied@users.noreply.github.com> Date: Mon, 20 May 2024 21:14:38 +0800 Subject: [PATCH] Fix Mythic's WebView implementation (fixes #52) --- Mythic/Views/Navigation/Store.swift | 40 ++++++------------------- Mythic/Views/Navigation/Support.swift | 43 ++++++++------------------- Mythic/Views/WebView.swift | 31 +++++++------------ 3 files changed, 32 insertions(+), 82 deletions(-) diff --git a/Mythic/Views/Navigation/Store.swift b/Mythic/Views/Navigation/Store.swift index 27f015c5..e6429f02 100644 --- a/Mythic/Views/Navigation/Store.swift +++ b/Mythic/Views/Navigation/Store.swift @@ -17,28 +17,22 @@ import SwiftUI import SwordRPC struct StoreView: View { - @State private var loadingError = false - @State private var isLoading = false + @State private var loadingError: Error? + @State private var isLoading: Bool? = false @State private var canGoBack = false @State private var canGoForward = false - @State private var urlString = "https://store.epicgames.com/" + @State private var url: URL = .init(string: "https://store.epicgames.com/")! @State private var refreshAnimation: Angle = .degrees(0) var body: some View { - WebView( - loadingError: $loadingError, - canGoBack: $canGoBack, - canGoForward: $canGoForward, - isLoading: $isLoading, - urlString: urlString - ) + WebView(url: url, error: .constant(nil), isLoading: .constant(nil), canGoBack: canGoBack, canGoForward: canGoForward) .navigationTitle("Store") .task(priority: .background) { discordRPC.setPresence({ var presence: RichPresence = .init() - presence.details = "Currently browsing \(urlString)" + presence.details = "Currently browsing \(url)" presence.state = "Looking for games to purchase" presence.timestamps.start = .now presence.assets.largeImage = "macos_512x512_2x" @@ -48,7 +42,6 @@ struct StoreView: View { } .toolbar { - // FIXME: Loading view update creates view update race condition with webview /* if isLoading { @@ -68,7 +61,7 @@ struct StoreView: View { ToolbarItem(placement: .confirmationAction) { Button { if canGoBack { - urlString = "javascript:history.back();" + url = .init(string: "javascript:history.back();")! } } label: { Image(systemName: "arrow.left.circle") @@ -79,7 +72,7 @@ struct StoreView: View { ToolbarItem(placement: .confirmationAction) { Button { if canGoForward { - urlString = "javascript:history.forward();" + url = .init(string: "javascript:history.forward();")! } } label: { Image(systemName: "arrow.right.circle") @@ -89,7 +82,7 @@ struct StoreView: View { ToolbarItem(placement: .confirmationAction) { Button { - urlString = "javascript:location.reload();" + url = .init(string: "javascript:location.reload();")! withAnimation(.default) { refreshAnimation = .degrees(360) } completion: { @@ -102,27 +95,12 @@ struct StoreView: View { } ToolbarItem(placement: .confirmationAction) { Button { - if let url = URL(string: urlString) { - workspace.open(url) - } + workspace.open(url) } label: { Image(systemName: "arrow.up.forward") } } } - - .alert(isPresented: $loadingError) { // FIXME: Error pops up continuously, making Mythic unusable. - Alert( - title: Text("Error"), - message: Text("Failed to load the webpage."), - primaryButton: .default(Text("Retry")) { - _ = NotImplementedView() - }, - secondaryButton: .cancel(Text("Cancel")) { - loadingError = false - } - ) - } } } diff --git a/Mythic/Views/Navigation/Support.swift b/Mythic/Views/Navigation/Support.swift index 065e5b8a..ad1f31ad 100644 --- a/Mythic/Views/Navigation/Support.swift +++ b/Mythic/Views/Navigation/Support.swift @@ -18,45 +18,28 @@ import Shimmer import SwordRPC struct SupportView: View { - // TODO: https://arc.net/l/quote/icczlrwf - @State private var game: Game = .init(type: .local, title: "default title") - @ObservedObject var operation: GameOperation = .shared - @State private var optionalPacks: [String: String] = .init() - - @State private var discordWidgetIsLoading: Bool = false var body: some View { HStack { - Text("\(Engine.fetchLatestVersion())") VStack { - WebView( - loadingError: Binding(get: {false}, set: {_ in}), // FIXME: terrible placeholders, webview refactor soon - canGoBack: Binding(get: {false}, set: {_ in}), - canGoForward: Binding(get: {false}, set: {_ in}), - isLoading: $discordWidgetIsLoading, - urlString: "https://discord.com/widget?id=1154998702650425397&theme=dark" - ) + WebView(url: .init(string: "https://discord.com/widget?id=1154998702650425397&theme=dark")!, error: .constant(nil), isLoading: .constant(nil)) } .frame(maxWidth: .infinity, maxHeight: .infinity) .background(.background) .clipShape(.rect(cornerRadius: 10)) VStack { - WebView( - loadingError: Binding(get: {false}, set: {_ in}), - canGoBack: Binding(get: {false}, set: {_ in}), - canGoForward: Binding(get: {false}, set: {_ in}), - isLoading: $discordWidgetIsLoading, - urlString: "https://patreon.com/mythicapp" - ) - .overlay(alignment: .bottomTrailing) { - Button { - workspace.open(.init(string: "https://patreon.com/mythicapp")!) - } label: { - Image(systemName: "arrow.up.forward") - .padding(5) - } - .clipShape(.circle) - .padding() + if let patreonURL: URL = .init(string: "https://patreon.com/mythicapp") { + WebView(url: patreonURL, error: .constant(nil), isLoading: .constant(nil)) + .overlay(alignment: .bottomTrailing) { + Button { + workspace.open(patreonURL) + } label: { + Image(systemName: "arrow.up.forward") + .padding(5) + } + .clipShape(.circle) + .padding() + } } } .frame(maxWidth: .infinity, maxHeight: .infinity) diff --git a/Mythic/Views/WebView.swift b/Mythic/Views/WebView.swift index dfee306e..631c997c 100644 --- a/Mythic/Views/WebView.swift +++ b/Mythic/Views/WebView.swift @@ -19,14 +19,12 @@ import OSLog /// SwiftUI view representing a WebView. struct WebView: NSViewRepresentable { - // MARK: - Bindings - @Binding var loadingError: Bool - @Binding var canGoBack: Bool - @Binding var canGoForward: Bool - @Binding var isLoading: Bool + var url: URL - // MARK: - Variables - var urlString: String + @Binding var error: Error? + @Binding var isLoading: Bool? + var canGoBack: Bool? + var canGoForward: Bool? // MARK: - Logger /// Logger for the WebView. @@ -42,10 +40,8 @@ struct WebView: NSViewRepresentable { } func updateNSView(_ nsView: WKWebView, context: Context) { - if let url = URL(string: urlString) { - let request = URLRequest(url: url) - nsView.load(request) - } + let request = URLRequest(url: self.url) + nsView.load(request) } // MARK: Coordinator Creation @@ -68,7 +64,7 @@ struct WebView: NSViewRepresentable { func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation, withError error: Error) { DispatchQueue.main.async { [self] in parent.log.error("\(error.localizedDescription)") - parent.loadingError = true + parent.error = error } } @@ -76,10 +72,9 @@ struct WebView: NSViewRepresentable { /// Called when navigation finishes successfully. func webView(_ webView: WKWebView, didFinish navigation: WKNavigation) { DispatchQueue.main.async { [self] in + parent.isLoading = false parent.canGoBack = webView.canGoBack parent.canGoForward = webView.canGoForward - parent.isLoading = false - parent.loadingError = false } } @@ -95,11 +90,5 @@ struct WebView: NSViewRepresentable { // MARK: - Preview #Preview { - WebView( - loadingError: .constant(false), - canGoBack: .constant(false), - canGoForward: .constant(false), - isLoading: .constant(false), - urlString: "https://example.com" - ) + WebView(url: .init(string: "https://example.com")!, error: .constant(nil), isLoading: .constant(nil)) }