Jelajahi Sumber

Show full-screen loading and separate results page for punctuation checks.

Align Punctuation Checker with the updated flow by presenting a dedicated loading screen, moving corrected output to a separate results view with back navigation, and making long corrected text scrollable.

Co-authored-by: Cursor <cursoragent@cursor.com>
AhtashamShahzad1 1 bulan lalu
induk
melakukan
466585fd90
1 mengubah file dengan 102 tambahan dan 89 penghapusan
  1. 102 89
      gramora/Views/PunctuationCheckerView.swift

+ 102 - 89
gramora/Views/PunctuationCheckerView.swift

@@ -3,17 +3,32 @@ import SwiftUI
 struct PunctuationCheckerView: View {
     @StateObject private var viewModel = PunctuationCheckerViewModel()
 
-    private var showsOutputLayout: Bool {
-        !viewModel.correctedText.isEmpty || viewModel.isChecking
-    }
-
     var body: some View {
         GeometryReader { proxy in
             let dynamicHorizontalPadding = min(max(proxy.size.width * 0.045, AppTheme.contentPadding), 52)
             let dynamicBottomPadding = min(max(proxy.size.height * 0.04, 20), 40)
 
             Group {
-                if !showsOutputLayout {
+                if viewModel.isChecking {
+                    loadingLayout(
+                        horizontalPadding: dynamicHorizontalPadding,
+                        topPadding: AppTheme.brandLabelTopInset,
+                        bottomPadding: dynamicBottomPadding
+                    )
+                } else if viewModel.hasResults {
+                    VStack(alignment: .leading, spacing: 0) {
+                        header
+                            .padding(.bottom, 20)
+
+                        resultsLayout
+                            .frame(maxWidth: .infinity, maxHeight: .infinity)
+                    }
+                    .frame(maxWidth: 1200, maxHeight: .infinity, alignment: .topLeading)
+                    .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
+                    .padding(.horizontal, dynamicHorizontalPadding)
+                    .padding(.top, AppTheme.brandLabelTopInset)
+                    .padding(.bottom, dynamicBottomPadding)
+                } else {
                     VStack(alignment: .leading, spacing: 0) {
                         header
                             .padding(.bottom, 24)
@@ -39,40 +54,6 @@ struct PunctuationCheckerView: View {
                     .padding(.horizontal, dynamicHorizontalPadding)
                     .padding(.top, AppTheme.brandLabelTopInset)
                     .padding(.bottom, dynamicBottomPadding)
-                } else {
-                    VStack(alignment: .leading, spacing: 0) {
-                        header
-                            .padding(.bottom, 24)
-
-                        ScrollView(.vertical, showsIndicators: false) {
-                            VStack(alignment: .leading, spacing: 0) {
-                                contentCard
-                                    .padding(.bottom, 24)
-
-                                outputCard
-                                    .padding(.bottom, 24)
-
-                                if let errorMessage = viewModel.errorMessage {
-                                    Text(errorMessage)
-                                        .font(.system(size: 14))
-                                        .foregroundStyle(.red)
-                                        .padding(.bottom, 12)
-                                }
-
-                                GradientButton(title: viewModel.isChecking ? "Checking..." : "Check Punctuation") {
-                                    viewModel.checkPunctuation()
-                                }
-                                .disabled(!viewModel.canCheck)
-                            }
-                            .padding(.bottom, dynamicBottomPadding)
-                        }
-                        .frame(maxWidth: .infinity, maxHeight: .infinity)
-                    }
-                    .frame(maxWidth: 1200, maxHeight: .infinity, alignment: .topLeading)
-                    .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
-                    .padding(.horizontal, dynamicHorizontalPadding)
-                    .padding(.top, AppTheme.brandLabelTopInset)
-                    .padding(.bottom, dynamicBottomPadding)
                 }
             }
         }
@@ -81,43 +62,74 @@ struct PunctuationCheckerView: View {
         }
     }
 
-    private var header: some View {
-        HStack(alignment: .top) {
-            VStack(alignment: .leading, spacing: 8) {
-                Text("Punctuation Checker")
-                    .font(.system(size: 30, weight: .bold))
-                    .foregroundStyle(AppTheme.textPrimary)
+    private func loadingLayout(
+        horizontalPadding: CGFloat,
+        topPadding: CGFloat,
+        bottomPadding: CGFloat
+    ) -> some View {
+        VStack(spacing: 14) {
+            ProgressView()
+                .controlSize(.regular)
+
+            Text("Loading...")
+                .font(.system(size: 16, weight: .semibold))
+                .foregroundStyle(AppTheme.textPrimary)
+
+            Text("Checking punctuation and applying corrections")
+                .font(.system(size: 14))
+                .foregroundStyle(AppTheme.textSecondary)
+        }
+        .frame(maxWidth: 1200, maxHeight: .infinity, alignment: .center)
+        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
+        .padding(.horizontal, horizontalPadding)
+        .padding(.top, topPadding)
+        .padding(.bottom, bottomPadding)
+    }
 
-                HStack(spacing: 0) {
-                    Text("Perfect your ")
-                        .foregroundStyle(AppTheme.textSecondary)
-                    Text("punctuation")
-                        .fontWeight(.semibold)
-                        .foregroundStyle(AppTheme.teal)
-                    Text(". Write with ")
-                        .foregroundStyle(AppTheme.textSecondary)
-                    Text("clarity")
-                        .fontWeight(.semibold)
-                        .foregroundStyle(AppTheme.teal)
-                    Text(".")
-                        .foregroundStyle(AppTheme.textSecondary)
+    private var header: some View {
+        VStack(alignment: .leading, spacing: 16) {
+            if viewModel.hasResults {
+                IconButton(iconName: "arrow.left") {
+                    viewModel.resetResults()
                 }
-                .font(.system(size: 15))
+                .accessibilityLabel("Go back")
             }
 
-            Spacer()
+            HStack(alignment: .top) {
+                VStack(alignment: .leading, spacing: 8) {
+                    Text("Punctuation Checker")
+                        .font(.system(size: 30, weight: .bold))
+                        .foregroundStyle(AppTheme.textPrimary)
+
+                    HStack(spacing: 0) {
+                        Text("Perfect your ")
+                            .foregroundStyle(AppTheme.textSecondary)
+                        Text("punctuation")
+                            .fontWeight(.semibold)
+                            .foregroundStyle(AppTheme.teal)
+                        Text(". Write with ")
+                            .foregroundStyle(AppTheme.textSecondary)
+                        Text("clarity")
+                            .fontWeight(.semibold)
+                            .foregroundStyle(AppTheme.teal)
+                        Text(".")
+                            .foregroundStyle(AppTheme.textSecondary)
+                    }
+                    .font(.system(size: 15))
+                }
 
-            IconButton {
-                SettingsGearIcon()
-            } action: {}
-            .accessibilityLabel("Settings")
+                Spacer()
+
+                IconButton {
+                    SettingsGearIcon()
+                } action: {}
+                .accessibilityLabel("Settings")
+            }
         }
     }
 
     private var contentCard: some View {
-        let compactEditor = showsOutputLayout
-
-        return VStack(alignment: .leading, spacing: AppTheme.contentCardSpacing) {
+        VStack(alignment: .leading, spacing: AppTheme.contentCardSpacing) {
             YourContentHeader()
 
             VStack(alignment: .leading, spacing: 6) {
@@ -134,7 +146,7 @@ struct PunctuationCheckerView: View {
                     .foregroundStyle(AppTheme.textMuted)
                     .frame(maxWidth: .infinity, alignment: .trailing)
             }
-            .scrollSafeEditorSectionFrame(compact: compactEditor)
+            .scrollSafeEditorSectionFrame(compact: false)
 
             HStack(spacing: 8) {
                 ToolbarButton(title: "Paste Text", iconName: "doc.on.clipboard") {
@@ -153,7 +165,7 @@ struct PunctuationCheckerView: View {
             }
         }
         .inputContentCardPadding()
-        .scrollSafeContentCardFrame(compact: compactEditor)
+        .scrollSafeContentCardFrame(compact: false)
         .background(AppTheme.cardBackground)
         .clipShape(RoundedRectangle(cornerRadius: AppTheme.cardCornerRadius))
         .overlay(
@@ -172,10 +184,14 @@ struct PunctuationCheckerView: View {
         )
         .font(.system(size: 14))
         .foregroundStyle(AppTheme.textPrimary)
-        .scrollSafeTextEditorFrame(compact: showsOutputLayout)
+        .scrollSafeTextEditorFrame(compact: false)
+    }
+
+    private var resultsLayout: some View {
+        correctedTextCard
     }
 
-    private var outputCard: some View {
+    private var correctedTextCard: some View {
         VStack(alignment: .leading, spacing: 12) {
             HStack {
                 Text("Corrected Text")
@@ -191,25 +207,21 @@ struct PunctuationCheckerView: View {
                 .opacity(viewModel.correctedText.isEmpty ? 0.55 : 1)
             }
 
-            Group {
-                if viewModel.isChecking && viewModel.correctedText.isEmpty {
-                    Text("Checking punctuation...")
-                        .font(.system(size: 14))
-                        .foregroundStyle(AppTheme.textMuted)
-                } else {
-                    Text(viewModel.correctedText)
-                        .font(.system(size: 14))
-                        .foregroundStyle(AppTheme.textPrimary)
-                }
+            ScrollView(.vertical, showsIndicators: true) {
+                Text(viewModel.correctedText)
+                    .font(.system(size: 14))
+                    .foregroundStyle(AppTheme.textPrimary)
+                    .frame(maxWidth: .infinity, alignment: .leading)
+                    .textSelection(.enabled)
+                    .padding(16)
             }
-                .frame(maxWidth: .infinity, alignment: .leading)
-                .padding(16)
-                .background(AppTheme.tealLight.opacity(0.35))
-                .clipShape(RoundedRectangle(cornerRadius: AppTheme.inputCornerRadius))
-                .overlay(
-                    RoundedRectangle(cornerRadius: AppTheme.inputCornerRadius)
-                        .stroke(AppTheme.teal.opacity(0.2), lineWidth: 1)
-                )
+            .frame(maxHeight: .infinity, alignment: .top)
+            .background(AppTheme.tealLight.opacity(0.35))
+            .clipShape(RoundedRectangle(cornerRadius: AppTheme.inputCornerRadius))
+            .overlay(
+                RoundedRectangle(cornerRadius: AppTheme.inputCornerRadius)
+                    .stroke(AppTheme.teal.opacity(0.2), lineWidth: 1)
+            )
         }
         .padding(AppTheme.contentCardPadding)
         .background(AppTheme.cardBackground)
@@ -219,5 +231,6 @@ struct PunctuationCheckerView: View {
                 .stroke(AppTheme.border, lineWidth: 1)
         )
         .shadow(color: AppTheme.cardShadow, radius: 16, y: 6)
+        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
     }
 }