Просмотр исходного кода

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 месяц назад
Родитель
Сommit
ed52287fa1

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

@@ -8,6 +8,7 @@ final class RedditOAuthPanelController: NSObject {
     private var panel: NSPanel?
     private var panel: NSPanel?
     private var webView: WKWebView?
     private var webView: WKWebView?
     private var onComplete: (() -> Void)?
     private var onComplete: (() -> Void)?
+    private var onAuthError: ((String) -> Void)?
 
 
     private override init() {
     private override init() {
         super.init()
         super.init()
@@ -15,11 +16,13 @@ final class RedditOAuthPanelController: NSObject {
 
 
     func completeSignIn(
     func completeSignIn(
         callbackURL: URL,
         callbackURL: URL,
+        onAuthError: @escaping (String) -> Void,
         onFinished: @escaping () -> Void
         onFinished: @escaping () -> Void
     ) {
     ) {
         dismiss()
         dismiss()
 
 
-        onComplete = onFinished
+        self.onComplete = onFinished
+        self.onAuthError = onAuthError
 
 
         let panel = NSPanel(
         let panel = NSPanel(
             contentRect: NSRect(x: 0, y: 0, width: 520, height: 640),
             contentRect: NSRect(x: 0, y: 0, width: 520, height: 640),
@@ -51,6 +54,7 @@ final class RedditOAuthPanelController: NSObject {
         panel = nil
         panel = nil
         webView = nil
         webView = nil
         onComplete = nil
         onComplete = nil
+        onAuthError = nil
     }
     }
 
 
     private func finish() {
     private func finish() {
@@ -66,14 +70,6 @@ extension RedditOAuthPanelController: WKNavigationDelegate {
 
 
         if shouldFinish(for: url) {
         if shouldFinish(for: url) {
             finish()
             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 {
                 switch result {
                 case .success(let callbackURL):
                 case .success(let callbackURL):
                     webView.load(URLRequest(url: callbackURL))
                     webView.load(URLRequest(url: callbackURL))
-                case .failure:
+                case .failure(let error):
+                    self.onAuthError?(error.localizedDescription)
                     self.finish()
                     self.finish()
                 }
                 }
             }
             }

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

@@ -1,27 +1,11 @@
 import Foundation
 import Foundation
 
 
 enum RedditHomeChromeStyle {
 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 = """
     static let injectionScript = """
     (function() {
     (function() {
       const STYLE_ID = 'reddora-home-chrome-style';
       const STYLE_ID = 'reddora-home-chrome-style';
       if (document.getElementById(STYLE_ID)) return;
       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",
             "accounts.google.com",
             "appleid.apple.com",
             "appleid.apple.com",
             "idmsa.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)") }) {
         if oauthHosts.contains(where: { host == $0 || host.hasSuffix(".\($0)") }) {
@@ -23,6 +31,15 @@ enum RedditWebAuthHelper {
                 || path.contains("gsi")
                 || 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
         return false
     }
     }
 
 

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

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

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

@@ -44,6 +44,24 @@ struct FrontPageView: View {
         }
         }
         .frame(minWidth: AppWindow.minWidth, minHeight: AppWindow.minHeight)
         .frame(minWidth: AppWindow.minWidth, minHeight: AppWindow.minHeight)
         .background(AppTheme.background)
         .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
     @ViewBuilder
@@ -51,7 +69,8 @@ struct FrontPageView: View {
         ZStack {
         ZStack {
             RedditWebView(
             RedditWebView(
                 url: viewModel.redditLoadURL,
                 url: viewModel.redditLoadURL,
-                reloadTrigger: viewModel.redditReloadTrigger
+                reloadTrigger: viewModel.redditReloadTrigger,
+                onAuthError: { viewModel.showAuthError($0) }
             )
             )
             .id("reddit-webview")
             .id("reddit-webview")
             .frame(maxWidth: .infinity, maxHeight: .infinity)
             .frame(maxWidth: .infinity, maxHeight: .infinity)
@@ -71,7 +90,9 @@ struct FrontPageView: View {
     private func toolContent(for item: SidebarNavItem) -> some View {
     private func toolContent(for item: SidebarNavItem) -> some View {
         switch item.iconKind {
         switch item.iconKind {
         case .postGenerator:
         case .postGenerator:
-            PostGeneratorView(viewModel: postGeneratorViewModel)
+            PostGeneratorView(viewModel: postGeneratorViewModel) { url in
+                viewModel.openReddit(at: url)
+            }
         case .titleOptimizer:
         case .titleOptimizer:
             TitleOptimizerView(viewModel: titleOptimizerViewModel)
             TitleOptimizerView(viewModel: titleOptimizerViewModel)
         case .commentWriter:
         case .commentWriter:

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

@@ -5,6 +5,7 @@ import UniformTypeIdentifiers
 struct PostGeneratorView: View {
 struct PostGeneratorView: View {
     @Environment(\.requirePremiumAccess) private var requirePremiumAccess
     @Environment(\.requirePremiumAccess) private var requirePremiumAccess
     @Bindable var viewModel: PostGeneratorViewModel
     @Bindable var viewModel: PostGeneratorViewModel
+    var onPostToReddit: (URL) -> Void = { _ in }
 
 
     private enum Layout {
     private enum Layout {
         static let horizontalPadding: CGFloat = 24
         static let horizontalPadding: CGFloat = 24
@@ -141,6 +142,15 @@ struct PostGeneratorView: View {
             .buttonStyle(AppSecondaryButtonStyle())
             .buttonStyle(AppSecondaryButtonStyle())
             .disabled(!viewModel.canExport)
             .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 {
             Button {
                 guard requirePremiumAccess() else { return }
                 guard requirePremiumAccess() else { return }
                 Task { await viewModel.generatePost() }
                 Task { await viewModel.generatePost() }
@@ -658,6 +668,20 @@ struct PostGeneratorView: View {
                     )
                     )
 
 
                     previewMetadata
                     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)
                 .frame(maxWidth: 560)
 
 

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

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