Jelajahi Sumber

Restore Reddit login controls and improve sign-in and post publishing flows.

Show Log In and user menu in the embedded WebView, surface OAuth failures to the user, broaden provider routing, and let Post Generator open Reddit submit with prefilled draft content.

Co-authored-by: Cursor <cursoragent@cursor.com>
AhtashamShahzad1 1 bulan lalu
induk
melakukan
ed52287fa1

+ 7 - 10
Reddit App/Managers/RedditOAuthPanelController.swift

@@ -8,6 +8,7 @@ final class RedditOAuthPanelController: NSObject {
     private var panel: NSPanel?
     private var webView: WKWebView?
     private var onComplete: (() -> Void)?
+    private var onAuthError: ((String) -> Void)?
 
     private override init() {
         super.init()
@@ -15,11 +16,13 @@ final class RedditOAuthPanelController: NSObject {
 
     func completeSignIn(
         callbackURL: URL,
+        onAuthError: @escaping (String) -> Void,
         onFinished: @escaping () -> Void
     ) {
         dismiss()
 
-        onComplete = onFinished
+        self.onComplete = onFinished
+        self.onAuthError = onAuthError
 
         let panel = NSPanel(
             contentRect: NSRect(x: 0, y: 0, width: 520, height: 640),
@@ -51,6 +54,7 @@ final class RedditOAuthPanelController: NSObject {
         panel = nil
         webView = nil
         onComplete = nil
+        onAuthError = nil
     }
 
     private func finish() {
@@ -66,14 +70,6 @@ extension RedditOAuthPanelController: WKNavigationDelegate {
 
         if shouldFinish(for: url) {
             finish()
-            return
-        }
-
-        if url.host?.lowercased() == "accounts.reddit.com" {
-            DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { [weak self] in
-                guard let self, self.panel != nil else { return }
-                self.finish()
-            }
         }
     }
 
@@ -97,7 +93,8 @@ extension RedditOAuthPanelController: WKNavigationDelegate {
                 switch result {
                 case .success(let callbackURL):
                     webView.load(URLRequest(url: callbackURL))
-                case .failure:
+                case .failure(let error):
+                    self.onAuthError?(error.localizedDescription)
                     self.finish()
                 }
             }

+ 1 - 17
Reddit App/Utilities/RedditHomeChromeStyle.swift

@@ -1,27 +1,11 @@
 import Foundation
 
 enum RedditHomeChromeStyle {
-    /// Hides Reddit's header Log In button and user-menu trigger in the embedded WebView.
+    /// Reserved for optional Reddit chrome tweaks. Login and user-menu controls are left visible.
     static let injectionScript = """
     (function() {
       const STYLE_ID = 'reddora-home-chrome-style';
       if (document.getElementById(STYLE_ID)) return;
-
-      const style = document.createElement('style');
-      style.id = STYLE_ID;
-      style.textContent = `
-        #login-button,
-        faceplate-tracker:has(#login-button),
-        #expand-user-drawer-button,
-        activate-feature:has(#expand-user-drawer-button),
-        rpl-tooltip:has(#expand-user-drawer-button) {
-          display: none !important;
-          visibility: hidden !important;
-          pointer-events: none !important;
-        }
-      `;
-
-      (document.head || document.documentElement).appendChild(style);
     })();
     """
 }

+ 17 - 0
Reddit App/Utilities/RedditWebAuthHelper.swift

@@ -10,6 +10,14 @@ enum RedditWebAuthHelper {
             "accounts.google.com",
             "appleid.apple.com",
             "idmsa.apple.com",
+            "login.microsoftonline.com",
+            "login.live.com",
+            "account.live.com",
+            "login.microsoft.com",
+            "www.facebook.com",
+            "m.facebook.com",
+            "facebook.com",
+            "auth0.com",
         ]
 
         if oauthHosts.contains(where: { host == $0 || host.hasSuffix(".\($0)") }) {
@@ -23,6 +31,15 @@ enum RedditWebAuthHelper {
                 || path.contains("gsi")
         }
 
+        if host == "facebook.com" || host.hasSuffix(".facebook.com") {
+            let path = url.path.lowercased()
+            return path.contains("oauth") || path.contains("login") || path.contains("dialog")
+        }
+
+        if host.hasSuffix(".auth0.com") {
+            return true
+        }
+
         return false
     }
 

+ 9 - 0
Reddit App/ViewModels/FrontPageViewModel.swift

@@ -10,6 +10,7 @@ final class FrontPageViewModel {
     var isRedditActive = true
     var isSettingsActive = false
     var isPaywallPresented = false
+    var authErrorMessage: String?
     private(set) var redditReloadTrigger = 0
 
     static let settingsItem = SidebarNavItem(
@@ -26,6 +27,14 @@ final class FrontPageViewModel {
         redditReloadTrigger += 1
     }
 
+    func showAuthError(_ message: String) {
+        authErrorMessage = message
+    }
+
+    func clearAuthError() {
+        authErrorMessage = nil
+    }
+
     func openSettings() {
         selectedNavItem = nil
         isRedditActive = false

+ 18 - 0
Reddit App/ViewModels/PostGeneratorViewModel.swift

@@ -140,6 +140,24 @@ final class PostGeneratorViewModel {
         }
     }
 
+    func preparePostToReddit() throws -> URL {
+        try PostDraftValidator.validateForExport(draft)
+        RedditSubmitPrefillManager.setPending(from: draft)
+        return try RedditSubmitURLBuilder.submitURL(for: draft)
+    }
+
+    func postToReddit(openURL: (URL) -> Void) {
+        do {
+            let url = try preparePostToReddit()
+            errorMessage = nil
+            successMessage = "Opening Reddit submit page…"
+            openURL(url)
+        } catch {
+            errorMessage = error.localizedDescription
+            successMessage = nil
+        }
+    }
+
     func resetDraft() {
         releaseImageAccess()
         draft = PostDraft()

+ 23 - 2
Reddit App/Views/FrontPageView.swift

@@ -44,6 +44,24 @@ struct FrontPageView: View {
         }
         .frame(minWidth: AppWindow.minWidth, minHeight: AppWindow.minHeight)
         .background(AppTheme.background)
+        .alert("Sign-in issue", isPresented: authErrorBinding) {
+            Button("OK") {
+                viewModel.clearAuthError()
+            }
+        } message: {
+            Text(viewModel.authErrorMessage ?? "")
+        }
+    }
+
+    private var authErrorBinding: Binding<Bool> {
+        Binding(
+            get: { viewModel.authErrorMessage != nil },
+            set: { isPresented in
+                if !isPresented {
+                    viewModel.clearAuthError()
+                }
+            }
+        )
     }
 
     @ViewBuilder
@@ -51,7 +69,8 @@ struct FrontPageView: View {
         ZStack {
             RedditWebView(
                 url: viewModel.redditLoadURL,
-                reloadTrigger: viewModel.redditReloadTrigger
+                reloadTrigger: viewModel.redditReloadTrigger,
+                onAuthError: { viewModel.showAuthError($0) }
             )
             .id("reddit-webview")
             .frame(maxWidth: .infinity, maxHeight: .infinity)
@@ -71,7 +90,9 @@ struct FrontPageView: View {
     private func toolContent(for item: SidebarNavItem) -> some View {
         switch item.iconKind {
         case .postGenerator:
-            PostGeneratorView(viewModel: postGeneratorViewModel)
+            PostGeneratorView(viewModel: postGeneratorViewModel) { url in
+                viewModel.openReddit(at: url)
+            }
         case .titleOptimizer:
             TitleOptimizerView(viewModel: titleOptimizerViewModel)
         case .commentWriter:

+ 24 - 0
Reddit App/Views/PostGeneratorView.swift

@@ -5,6 +5,7 @@ import UniformTypeIdentifiers
 struct PostGeneratorView: View {
     @Environment(\.requirePremiumAccess) private var requirePremiumAccess
     @Bindable var viewModel: PostGeneratorViewModel
+    var onPostToReddit: (URL) -> Void = { _ in }
 
     private enum Layout {
         static let horizontalPadding: CGFloat = 24
@@ -141,6 +142,15 @@ struct PostGeneratorView: View {
             .buttonStyle(AppSecondaryButtonStyle())
             .disabled(!viewModel.canExport)
 
+            Button {
+                viewModel.postToReddit(openURL: onPostToReddit)
+            } label: {
+                Label("Post to Reddit", systemImage: "arrow.up.right.square")
+                    .font(.system(size: 11, weight: .medium))
+            }
+            .buttonStyle(AppSecondaryButtonStyle())
+            .disabled(!viewModel.canExport)
+
             Button {
                 guard requirePremiumAccess() else { return }
                 Task { await viewModel.generatePost() }
@@ -658,6 +668,20 @@ struct PostGeneratorView: View {
                     )
 
                     previewMetadata
+
+                    Button {
+                        viewModel.postToReddit(openURL: onPostToReddit)
+                    } label: {
+                        Label("Post to Reddit", systemImage: "arrow.up.right.square")
+                            .font(.system(size: 12, weight: .semibold))
+                            .foregroundStyle(.white)
+                            .frame(maxWidth: .infinity)
+                            .padding(.vertical, 10)
+                            .background(viewModel.canExport ? AppTheme.accentPurple : AppTheme.accentPurple.opacity(0.4))
+                            .clipShape(RoundedRectangle(cornerRadius: 8))
+                    }
+                    .buttonStyle(AppPrimaryButtonStyle())
+                    .disabled(!viewModel.canExport)
                 }
                 .frame(maxWidth: 560)
 

+ 13 - 4
Reddit App/Views/RedditWebView.swift

@@ -5,9 +5,10 @@ import WebKit
 struct RedditWebView: NSViewRepresentable {
     let url: URL
     let reloadTrigger: Int
+    var onAuthError: (String) -> Void = { _ in }
 
     func makeCoordinator() -> Coordinator {
-        Coordinator()
+        Coordinator(onAuthError: onAuthError)
     }
 
     func makeNSView(context: Context) -> RedditWebViewContainer {
@@ -35,6 +36,11 @@ struct RedditWebView: NSViewRepresentable {
         var lastReloadTrigger = -1
         var lastLoadedURL: URL?
         private var prefillWorkItem: DispatchWorkItem?
+        private let onAuthError: (String) -> Void
+
+        init(onAuthError: @escaping (String) -> Void) {
+            self.onAuthError = onAuthError
+        }
 
         func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
             webView.evaluateJavaScript(RedditPostBoundaryStyle.injectionScript, completionHandler: nil)
@@ -159,13 +165,16 @@ struct RedditWebView: NSViewRepresentable {
 
                 switch result {
                 case .success(let callbackURL):
-                    RedditOAuthPanelController.shared.completeSignIn(callbackURL: callbackURL) {
+                    RedditOAuthPanelController.shared.completeSignIn(
+                        callbackURL: callbackURL,
+                        onAuthError: onAuthError
+                    ) {
                         container.load(url: RedditWebAuthHelper.redditHomeURL)
                     }
                 case .failure(let error as OAuthError) where error == .cancelled:
                     break
-                case .failure:
-                    break
+                case .failure(let error):
+                    onAuthError(error.localizedDescription)
                 }
             }
         }