Procházet zdrojové kódy

Add a back button for in-app Reddit browsing so users can return to the previous page.

Co-authored-by: Cursor <cursoragent@cursor.com>
AhtashamShahzad1 před 1 měsícem
rodič
revize
504fa4cb25

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

@@ -12,6 +12,8 @@ final class FrontPageViewModel {
     var isPaywallPresented = false
     var authErrorMessage: String?
     private(set) var redditReloadTrigger = 0
+    private(set) var redditGoBackTrigger = 0
+    var redditCanGoBack = false
 
     static let settingsItem = SidebarNavItem(
         title: "Settings",
@@ -35,6 +37,15 @@ final class FrontPageViewModel {
         authErrorMessage = nil
     }
 
+    func goBackInReddit() {
+        guard redditCanGoBack else { return }
+        redditGoBackTrigger += 1
+    }
+
+    func updateRedditCanGoBack(_ canGoBack: Bool) {
+        redditCanGoBack = canGoBack
+    }
+
     func openSettings() {
         selectedNavItem = nil
         isRedditActive = false

+ 34 - 0
Reddit App/Views/Components/RedditWebBackBar.swift

@@ -0,0 +1,34 @@
+import SwiftUI
+
+struct RedditWebBackBar: View {
+    let canGoBack: Bool
+    let onBack: () -> Void
+
+    var body: some View {
+        HStack(spacing: 0) {
+            Button(action: onBack) {
+                HStack(spacing: 5) {
+                    Image(systemName: "chevron.left")
+                        .font(.system(size: 12, weight: .semibold))
+                    Text("Back")
+                        .font(.system(size: 13, weight: .medium))
+                }
+                .foregroundStyle(AppTheme.textPrimary)
+                .padding(.horizontal, 14)
+                .padding(.vertical, 9)
+            }
+            .buttonStyle(AppPlainButtonStyle())
+            .disabled(!canGoBack)
+            .opacity(canGoBack ? 1 : 0.4)
+
+            Spacer(minLength: 0)
+        }
+        .frame(maxWidth: .infinity)
+        .background(AppTheme.panelBackground)
+        .overlay(alignment: .bottom) {
+            Rectangle()
+                .fill(AppTheme.border)
+                .frame(height: 1)
+        }
+    }
+}

+ 25 - 14
Reddit App/Views/FrontPageView.swift

@@ -66,21 +66,32 @@ struct FrontPageView: View {
 
     @ViewBuilder
     private var mainContent: some View {
-        ZStack {
-            RedditWebView(
-                url: viewModel.redditLoadURL,
-                reloadTrigger: viewModel.redditReloadTrigger,
-                onAuthError: { viewModel.showAuthError($0) }
-            )
-            .id("reddit-webview")
-            .frame(maxWidth: .infinity, maxHeight: .infinity)
-            .allowsHitTesting(viewModel.isRedditActive)
-            .accessibilityHidden(!viewModel.isRedditActive)
+        VStack(spacing: 0) {
+            if viewModel.isRedditActive, viewModel.redditCanGoBack {
+                RedditWebBackBar(
+                    canGoBack: true,
+                    onBack: { viewModel.goBackInReddit() }
+                )
+            }
 
-            if viewModel.isSettingsActive {
-                SettingsView(viewModel: settingsViewModel)
-            } else if !viewModel.isRedditActive, let item = viewModel.selectedNavItem {
-                toolContent(for: item)
+            ZStack {
+                RedditWebView(
+                    url: viewModel.redditLoadURL,
+                    reloadTrigger: viewModel.redditReloadTrigger,
+                    goBackTrigger: viewModel.redditGoBackTrigger,
+                    onCanGoBackChange: { viewModel.updateRedditCanGoBack($0) },
+                    onAuthError: { viewModel.showAuthError($0) }
+                )
+                .id("reddit-webview")
+                .frame(maxWidth: .infinity, maxHeight: .infinity)
+                .allowsHitTesting(viewModel.isRedditActive)
+                .accessibilityHidden(!viewModel.isRedditActive)
+
+                if viewModel.isSettingsActive {
+                    SettingsView(viewModel: settingsViewModel)
+                } else if !viewModel.isRedditActive, let item = viewModel.selectedNavItem {
+                    toolContent(for: item)
+                }
             }
         }
         .background(AppTheme.background)

+ 35 - 2
Reddit App/Views/RedditWebView.swift

@@ -5,10 +5,15 @@ import WebKit
 struct RedditWebView: NSViewRepresentable {
     let url: URL
     let reloadTrigger: Int
+    var goBackTrigger: Int = 0
+    var onCanGoBackChange: (Bool) -> Void = { _ in }
     var onAuthError: (String) -> Void = { _ in }
 
     func makeCoordinator() -> Coordinator {
-        Coordinator(onAuthError: onAuthError)
+        Coordinator(
+            onCanGoBackChange: onCanGoBackChange,
+            onAuthError: onAuthError
+        )
     }
 
     func makeNSView(context: Context) -> RedditWebViewContainer {
@@ -17,11 +22,21 @@ struct RedditWebView: NSViewRepresentable {
         container.webView.uiDelegate = context.coordinator
         context.coordinator.container = container
         context.coordinator.lastReloadTrigger = reloadTrigger
+        context.coordinator.startObservingNavigation(in: container.webView)
         container.load(url: url)
         return container
     }
 
     func updateNSView(_ container: RedditWebViewContainer, context: Context) {
+        context.coordinator.onCanGoBackChange = onCanGoBackChange
+
+        if context.coordinator.lastGoBackTrigger != goBackTrigger {
+            context.coordinator.lastGoBackTrigger = goBackTrigger
+            if container.webView.canGoBack {
+                container.webView.goBack()
+            }
+        }
+
         let urlChanged = context.coordinator.lastLoadedURL != url
         let triggerChanged = context.coordinator.lastReloadTrigger != reloadTrigger
         guard urlChanged || triggerChanged else { return }
@@ -34,15 +49,33 @@ struct RedditWebView: NSViewRepresentable {
     final class Coordinator: NSObject, WKNavigationDelegate, WKUIDelegate {
         weak var container: RedditWebViewContainer?
         var lastReloadTrigger = -1
+        var lastGoBackTrigger = 0
         var lastLoadedURL: URL?
         private var prefillWorkItem: DispatchWorkItem?
+        private var canGoBackObservation: NSKeyValueObservation?
+        var onCanGoBackChange: (Bool) -> Void
         private let onAuthError: (String) -> Void
 
-        init(onAuthError: @escaping (String) -> Void) {
+        init(
+            onCanGoBackChange: @escaping (Bool) -> Void,
+            onAuthError: @escaping (String) -> Void
+        ) {
+            self.onCanGoBackChange = onCanGoBackChange
             self.onAuthError = onAuthError
         }
 
+        func startObservingNavigation(in webView: WKWebView) {
+            guard canGoBackObservation == nil else { return }
+
+            canGoBackObservation = webView.observe(\.canGoBack, options: [.initial, .new]) { [weak self] webView, _ in
+                DispatchQueue.main.async {
+                    self?.onCanGoBackChange(webView.canGoBack)
+                }
+            }
+        }
+
         func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
+            startObservingNavigation(in: webView)
             webView.evaluateJavaScript(RedditPostBoundaryStyle.injectionScript, completionHandler: nil)
             webView.evaluateJavaScript(RedditHomeChromeStyle.injectionScript, completionHandler: nil)
             webView.evaluateJavaScript(RedditAuthChromeStyle.injectionScript, completionHandler: nil)