|
@@ -24,6 +24,7 @@ struct RedditWebView: NSViewRepresentable {
|
|
|
context.coordinator.container = container
|
|
context.coordinator.container = container
|
|
|
context.coordinator.lastReloadTrigger = reloadTrigger
|
|
context.coordinator.lastReloadTrigger = reloadTrigger
|
|
|
context.coordinator.lastAppliedDarkMode = isDarkMode
|
|
context.coordinator.lastAppliedDarkMode = isDarkMode
|
|
|
|
|
+ context.coordinator.installLoginBridge()
|
|
|
context.coordinator.startObservingNavigation(in: container.webView)
|
|
context.coordinator.startObservingNavigation(in: container.webView)
|
|
|
container.applyUnderPageBackground(isDarkMode: isDarkMode)
|
|
container.applyUnderPageBackground(isDarkMode: isDarkMode)
|
|
|
container.load(url: url)
|
|
container.load(url: url)
|
|
@@ -32,6 +33,7 @@ struct RedditWebView: NSViewRepresentable {
|
|
|
|
|
|
|
|
func updateNSView(_ container: RedditWebViewContainer, context: Context) {
|
|
func updateNSView(_ container: RedditWebViewContainer, context: Context) {
|
|
|
context.coordinator.onCanGoBackChange = onCanGoBackChange
|
|
context.coordinator.onCanGoBackChange = onCanGoBackChange
|
|
|
|
|
+ context.coordinator.installLoginBridge()
|
|
|
|
|
|
|
|
if context.coordinator.lastAppliedDarkMode != isDarkMode {
|
|
if context.coordinator.lastAppliedDarkMode != isDarkMode {
|
|
|
context.coordinator.lastAppliedDarkMode = isDarkMode
|
|
context.coordinator.lastAppliedDarkMode = isDarkMode
|
|
@@ -95,12 +97,21 @@ struct RedditWebView: NSViewRepresentable {
|
|
|
onCanGoBackChange(webView.canGoBack && !isOnHome)
|
|
onCanGoBackChange(webView.canGoBack && !isOnHome)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ func installLoginBridge() {
|
|
|
|
|
+ RedditLoginScriptBridge.shared.onLoginRequested = { [weak self] in
|
|
|
|
|
+ self?.beginCredentialLogin()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
|
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
|
|
startObservingNavigation(in: webView)
|
|
startObservingNavigation(in: webView)
|
|
|
reportBackButtonVisibility(for: webView)
|
|
reportBackButtonVisibility(for: webView)
|
|
|
webView.evaluateJavaScript(RedditPostBoundaryStyle.injectionScript, completionHandler: nil)
|
|
webView.evaluateJavaScript(RedditPostBoundaryStyle.injectionScript, completionHandler: nil)
|
|
|
webView.evaluateJavaScript(RedditHomeChromeStyle.injectionScript, completionHandler: nil)
|
|
webView.evaluateJavaScript(RedditHomeChromeStyle.injectionScript, completionHandler: nil)
|
|
|
webView.evaluateJavaScript(RedditAuthChromeStyle.injectionScript, completionHandler: nil)
|
|
webView.evaluateJavaScript(RedditAuthChromeStyle.injectionScript, completionHandler: nil)
|
|
|
|
|
+ if let url = webView.url, !RedditWebAuthHelper.isCredentialLoginPage(url) {
|
|
|
|
|
+ webView.evaluateJavaScript(RedditLoginBridgeStyle.injectionScript, completionHandler: nil)
|
|
|
|
|
+ }
|
|
|
scheduleSubmitPrefillIfNeeded(on: webView)
|
|
scheduleSubmitPrefillIfNeeded(on: webView)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -171,14 +182,30 @@ struct RedditWebView: NSViewRepresentable {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if RedditWebAuthHelper.isOAuthProviderURL(url) {
|
|
if RedditWebAuthHelper.isOAuthProviderURL(url) {
|
|
|
- // Always route OAuth through ASWebAuthenticationSession. Reddit's login
|
|
|
|
|
- // modal runs in a subframe; letting WebKit handle Google/Apple SSO there
|
|
|
|
|
- // triggers SOAuthorizationCoordinator subframe errors and breaks sign-in.
|
|
|
|
|
decisionHandler(.cancel)
|
|
decisionHandler(.cancel)
|
|
|
beginOAuthSignIn(url: url)
|
|
beginOAuthSignIn(url: url)
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ let isSubframe = navigationAction.targetFrame.map { !$0.isMainFrame } ?? false
|
|
|
|
|
+ if isSubframe,
|
|
|
|
|
+ let mainURL = webView.url,
|
|
|
|
|
+ RedditWebAuthHelper.isCredentialLoginPage(mainURL),
|
|
|
|
|
+ RedditWebAuthHelper.isAccountsRedditHost(url) {
|
|
|
|
|
+ decisionHandler(.allow)
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if RedditWebAuthHelper.isAuthFlowURL(url), isSubframe {
|
|
|
|
|
+ if RedditWebAuthHelper.shouldRedirectAuthSubframe(mainFrameURL: webView.url) {
|
|
|
|
|
+ decisionHandler(.cancel)
|
|
|
|
|
+ beginCredentialLogin()
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ decisionHandler(.allow)
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
if navigationAction.targetFrame == nil {
|
|
if navigationAction.targetFrame == nil {
|
|
|
webView.load(URLRequest(url: url))
|
|
webView.load(URLRequest(url: url))
|
|
|
decisionHandler(.cancel)
|
|
decisionHandler(.cancel)
|
|
@@ -212,6 +239,22 @@ struct RedditWebView: NSViewRepresentable {
|
|
|
return nil
|
|
return nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private func beginCredentialLogin() {
|
|
|
|
|
+ guard let container else { return }
|
|
|
|
|
+
|
|
|
|
|
+ if let url = container.webView.url, RedditWebAuthHelper.isCredentialLoginPage(url) {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ RedditOAuthPanelController.shared.dismiss()
|
|
|
|
|
+
|
|
|
|
|
+ container.webView.evaluateJavaScript(
|
|
|
|
|
+ RedditLoginBridgeStyle.closeAuthModalScript,
|
|
|
|
|
+ completionHandler: nil
|
|
|
|
|
+ )
|
|
|
|
|
+ container.load(url: RedditWebAuthHelper.redditLoginURL)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private func beginOAuthSignIn(url: URL) {
|
|
private func beginOAuthSignIn(url: URL) {
|
|
|
let presentationWindow = container?.window
|
|
let presentationWindow = container?.window
|
|
|
|
|
|
|
@@ -223,12 +266,8 @@ struct RedditWebView: NSViewRepresentable {
|
|
|
|
|
|
|
|
switch result {
|
|
switch result {
|
|
|
case .success(let callbackURL):
|
|
case .success(let callbackURL):
|
|
|
- RedditOAuthPanelController.shared.completeSignIn(
|
|
|
|
|
- callbackURL: callbackURL,
|
|
|
|
|
- onAuthError: onAuthError
|
|
|
|
|
- ) {
|
|
|
|
|
- container.load(url: RedditWebAuthHelper.redditHomeURL)
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ RedditOAuthPanelController.shared.dismiss()
|
|
|
|
|
+ container.load(url: callbackURL)
|
|
|
case .failure(let error as OAuthError) where error == .cancelled:
|
|
case .failure(let error as OAuthError) where error == .cancelled:
|
|
|
break
|
|
break
|
|
|
case .failure(let error):
|
|
case .failure(let error):
|