Skip to content

feat: simplify onboarding flow and UX improvements#72

Merged
hubo1989 merged 6 commits into
mainfrom
Easeofuse
Mar 26, 2026
Merged

feat: simplify onboarding flow and UX improvements#72
hubo1989 merged 6 commits into
mainfrom
Easeofuse

Conversation

@hubo1989
Copy link
Copy Markdown
Owner

@hubo1989 hubo1989 commented Mar 26, 2026

Summary

  • Simplify first-launch onboarding from 4 steps to 3 steps (remove configuration step)
  • Add permission purpose descriptions, skip option with deferred reminders, and menu bar hint on completion page
  • Defer Sparkle updater initialization during onboarding to avoid UI conflicts
  • Fix PreviewWindow timer callback data race that blocked Release builds

Changes

  • Onboarding flow: 3 steps (Welcome → Permissions → Complete), configuration moved to Settings
  • Permission UX: Add purpose subtitles, "Skip for Now" button, timeout hint after 30s polling
  • Complete page: Add menu bar location hint, conditional warning for skipped permissions
  • Sparkle: Skip updater init during first launch, initialize after onboarding completes
  • Bug fix: Move PreviewWindow timer counter/imageSizeTimer to class properties to fix @MainActor isolation race

Test plan

  • Clear UserDefaults ScreenTranslate.onboardingCompleted, verify 3-step flow
  • Verify permission purpose subtitles display correctly
  • Verify "Skip for Now" button works and completion page shows permission warning
  • Verify Sparkle update check does NOT appear during first launch
  • Verify Sparkle update check works normally after onboarding
  • Verify no data race warnings in Release build
  • Verify VLM provider switching sets correct defaults (with GLM OCR support)

Summary by CodeRabbit

发布说明

  • 新功能

    • 入门完成页新增菜单栏使用提示与未授予权限警告
    • 新增“暂时跳过权限”操作并持久记录用户选择
  • 改进

    • 入门流程简化为 3 步,移除了配置/安装相关步骤
    • 权限步骤增强:每项权限显示副标题与超时提示,支持跳过
    • 启动时更新检查延后至入门完成后再初始化
    • 调整入门窗口尺寸与预览窗口的图像尺寸检测逻辑
  • 本地化

    • 更新并新增多项入门相关文案与翻译文本

Hubert added 4 commits March 26, 2026 13:36
Reduce onboarding from 4 steps to 3 by removing the engine configuration
step (PaddleOCR/MTranServer setup moved to Settings). Add permission
purpose descriptions, skip-for-now option with timeout hint, and menubar
location guidance on completion page.
Move lastCounter and timer references to class properties to avoid
sending non-Sendable values across isolation boundaries.
- Fix button calling both requestAction and openSettingsAction (caused
  wrong permission dialog to appear)
- Increase window height from 520 to 580 to fit all content
- Defer Keychain access on launch to avoid prompt during onboarding
- Add note about removing and re-adding app for permission changes
…aults

- Skip Sparkle updater initialization during first launch to avoid
  overlapping with onboarding window; initialize after onboarding completes
- Add GLM OCR provider support after rebasing onto latest main
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 26, 2026

Warning

Rate limit exceeded

@hubo1989 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 9 minutes and 13 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f8c45ed9-ece4-41c8-bca6-5022522f9c14

📥 Commits

Reviewing files that changed from the base of the PR and between 0eaa366 and 33a1cbf.

📒 Files selected for processing (2)
  • ScreenTranslate/Features/Onboarding/OnboardingViewModel.swift
  • ScreenTranslate/Models/AppSettings.swift
📝 Walkthrough

Walkthrough

将引导流程从4步简化为3步(移除配置),新增“跳过权限”路径并记录;延迟 Sparkle 更新器初始化直到引导完成或被取消;移除 PaddleOCR/翻译测试相关功能并更新本地化与 UI 布局/文案。

Changes

Cohort / File(s) Summary
App 启动与更新器
ScreenTranslate/App/AppDelegate.swift
延迟初始化 updaterController:仅在 settings.onboardingCompleted 为 true 时访问;未完成时注册 .onboardingCompleted.onboardingDismissed 通知,新增 initializeUpdaterIfNeeded() 处理器。
引导核心逻辑
ScreenTranslate/Features/Onboarding/OnboardingViewModel.swift
totalSteps 从 4 -> 3,移除配置/翻译测试/PaddleOCR 相关状态与方法;新增 hasSkippedPermissionspermissionCheckTimedOutskipPermissions(),新增 .onboardingDismissed 通知。
引导视图与交互
ScreenTranslate/Features/Onboarding/.../OnboardingPermissionsStepView.swift, OnboardingCompleteStepView.swift, OnboardingComponents.swift, OnboardingView.swift
权限行新增 subtitle,未授予时“授予”按钮仅调用 requestAction();权限页加入超时提示与“Skip for Now”回调 (onSkip);完成页新增 hasSkippedPermissions 展示警告与菜单栏提示;窗口高度由 620 -> 580。
移除配置步骤 UI
ScreenTranslate/Features/Onboarding/OnboardingConfigurationStepView.swift
完全删除配置步骤及 PaddleOCR 安装/测试 UI(~199 行移除)。
窗口与通知
ScreenTranslate/Features/Onboarding/OnboardingWindowController.swift
窗口高度调整为 580;在窗口关闭且未完成引导时发送 .onboardingDismissed 通知。
设置与持久化
ScreenTranslate/Models/AppSettings.swift, ScreenTranslate/Features/Settings/SettingsViewModel.swift
新增 userSkippedPermissions persisted 标志;初始化不再同步从 Keychain 读取 PaddleOCR API key(改为空串);vlmProvider setter 中捕获 oldValue
预览行为
ScreenTranslate/Features/Preview/PreviewWindow.swift
增加 imageSizeTimerlastImageSizeCounter 持久字段,定时器回调使用实例字段比对并更新计数以触发尺寸调整。
本地化
ScreenTranslate/Resources/*/Localizable.strings
移除配置与 PaddleOCR 相关多条键;新增权限超时提示、权限子标题、完成页菜单栏提示与权限未授予警告等键(英文与简体中文更新)。

Sequence Diagram(s)

sequenceDiagram
    participant User as User
    participant App as AppDelegate
    participant Onboarding as OnboardingViewModel
    participant Window as OnboardingWindowController
    participant NC as NotificationCenter

    User->>App: 启动应用
    App->>Onboarding: 显示 Onboarding(onboardingCompleted=false)
    App--xOnboarding: 不初始化 updaterController
    App->>NC: 注册 .onboardingCompleted / .onboardingDismissed 观察者

    User->>Window: 交互权限页面
    alt 用户跳过权限
        User->>Onboarding: 调用 onSkip() / skipPermissions()
        Onboarding->>Onboarding: hasSkippedPermissions = true
        Onboarding->>NC: 发布 .onboardingCompleted
    else 用户完成或授予权限
        User->>Onboarding: 完成授权 -> completeOnboarding()
        Onboarding->>NC: 发布 .onboardingCompleted
    end

    NC->>App: 收到 .onboardingCompleted / .onboardingDismissed
    App->>App: initializeUpdaterIfNeeded()
    App->>App: 访问 updaterController -> Sparkle 初始化
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 我是一只小兔子,跳着改引导,
三步走代替四步,配置已散场。
权限可暂时跳过,更新器后启航,
文案与提示换新装,兔兔为你鼓掌! 🎉

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: simplifying the onboarding flow from 4 to 3 steps and improving UX, which aligns with the substantial refactoring of the onboarding system.
Docstring Coverage ✅ Passed Docstring coverage is 86.67% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch Easeofuse

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
ScreenTranslate/Features/Preview/PreviewWindow.swift (1)

103-113: ⚠️ Potential issue | 🟠 Major

重复定时器未失效会导致后台持续轮询。

Line 103 创建了重复 Timer,但当前没有对应 invalidate()。这会在窗口关闭后继续触发回调(或在重复调用观察逻辑时并存多个定时器),带来不必要的 CPU 唤醒和资源泄漏风险。

建议修复
 `@MainActor`
 private func observeImageSizeChanges() {
+    imageSizeTimer?.invalidate()
+    imageSizeTimer = nil
     lastImageSizeCounter = viewModel.imageSizeChangeCounter

     imageSizeTimer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [weak self] _ in
         Task { `@MainActor` [weak self] in
             guard let self = self else { return }

             let currentCounter = self.viewModel.imageSizeChangeCounter
             if currentCounter != self.lastImageSizeCounter {
                 self.lastImageSizeCounter = currentCounter
                 self.resizeToFitImage()
             }
         }
     }
 }
+
+deinit {
+    imageSizeTimer?.invalidate()
+    imageSizeTimer = nil
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ScreenTranslate/Features/Preview/PreviewWindow.swift` around lines 103 - 113,
The repeating Timer assigned to imageSizeTimer keeps firing after the window is
closed or when multiple observers start because it's never invalidated; update
the lifecycle to (1) avoid creating duplicate timers by invalidating any
existing imageSizeTimer before scheduling a new one, and (2) invalidate and nil
out imageSizeTimer when the preview window is closed or the owner is
deinitialized (e.g., in windowWillClose/deinit or an appropriate cleanup method)
so resizeToFitImage and the imageSizeChangeCounter polling stop running and
resources are released.
🧹 Nitpick comments (2)
ScreenTranslate/Features/Settings/SettingsViewModel.swift (1)

353-353: 移除未使用的 oldValue 捕获。

这里先读取了旧值,但后续未使用,容易误导读者以为存在前后值比较逻辑。建议直接删除该行,保持 setter 语义清晰。

♻️ 建议修改
-            let oldValue = settings.vlmProvider
             settings.vlmProvider = newValue
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ScreenTranslate/Features/Settings/SettingsViewModel.swift` at line 353,
Remove the unused capture of the previous value by deleting the line that
declares oldValue (let oldValue = settings.vlmProvider) in SettingsViewModel's
setter for vlmProvider; ensure no other code depends on oldValue and keep the
setter logic using settings.vlmProvider directly to avoid misleading readers
about a before/after comparison.
ScreenTranslate/Features/Onboarding/OnboardingComponents.swift (1)

77-83: OnboardingPermissionRow 的 API 和实际行为保持一致。

openSettingsAction 现在已经不会被调用了,但 OnboardingPermissionsStepView.swift 的 Line 38-54 仍然要为它传 closure。要么删掉这个参数和调用端,要么在 UI 上恢复一个明确的“打开设置”入口;保留失效回调会误导后续调用者。

Also applies to: 112-117

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ScreenTranslate/Features/Onboarding/OnboardingComponents.swift` around lines
77 - 83, The OnboardingPermissionRow's API exposes openSettingsAction but the UI
no longer invokes it; update code to align behavior by either removing the
openSettingsAction parameter from the OnboardingPermissionRow struct and all
call sites (e.g., where OnboardingPermissionsStepView passes a closure at lines
referenced) or reintroducing a visible "Open Settings" control in
OnboardingPermissionRow that calls openSettingsAction; locate the symbol
OnboardingPermissionRow and the usage in OnboardingPermissionsStepView and
either (A) delete the openSettingsAction property and remove the passed
closures, or (B) add a clear button/tap target in the row UI that invokes
openSettingsAction so the API is functional.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@ScreenTranslate/App/AppDelegate.swift`:
- Around line 71-74: The code skips initializing Sparkle/updater by only
creating updaterController when settings.onboardingCompleted is true, which
misses the case where the user manually closes the onboarding window; modify the
onboarding-close path to call initializeUpdaterIfNeeded() (or create
updaterController) and register the checkForUpdates observer when onboarding is
dismissed. Concretely, update the OnboardingWindowController's close/completion
handler (referenced around OnboardingWindowController line 60-68 and 96-103) to
invoke AppDelegate.initializeUpdaterIfNeeded() or to access
AppDelegate.updaterController so the same initialization and observer
registration that occurs when settings.onboardingCompleted == true also runs
when onboarding is closed early; also mirror this fix for the alternate branch
referenced at lines 85-91 so both code paths initialize Sparkle consistently.

In `@ScreenTranslate/Features/Onboarding/OnboardingPermissionsStepView.swift`:
- Around line 82-88: The Skip button currently calls onSkip() even when
canGoNext == true, causing OnboardingViewModel.skipPermissions() to incorrectly
set hasSkippedPermissions/userSkippedPermissions to true; update the UI to
disable or hide the Skip Button when canGoNext is true (i.e., wrap the Button
with a conditional or set .disabled(canGoNext)) or add a guard inside
OnboardingViewModel.skipPermissions() to no-op when canGoNext (or both) so skip
cannot be applied after all permissions are granted.

In `@ScreenTranslate/Features/Onboarding/OnboardingViewModel.swift`:
- Around line 109-113: The skipPermissions() implementation currently calls
completeOnboarding() and bypasses the Complete step and its UI flag; change
skipPermissions() to instead mark hasSkippedPermissions = true and advance the
onboarding flow to the final step (e.g., set the current step/state to the
completion step rather than calling completeOnboarding()), so that
OnboardingCompleteStepView.can read hasSkippedPermissions and show the
skipped-permissions warning; keep completeOnboarding() as the actual
finalization action invoked by the Start button handler in the completion view.

In `@ScreenTranslate/Models/AppSettings.swift`:
- Around line 556-558: The paddleOCRCloudAPIKey is being set to an empty string
which permanently prevents loading the saved Keychain value; instead make this
value lazily loaded (not pre-initialized to "") so existing saved keys are
returned when first needed. Change the property paddleOCRCloudAPIKey to be
nil/optional or a lazy/computed property that fetches the key from the Keychain
on first access (or provide a loadPaddleOCRCloudAPIKey() helper that reads
Keychain without triggering UI until invoked), and ensure callers in
SettingsViewModel and PaddleOCRVLMProvider read via that lazy getter or call the
loader before sending requests so old users get their saved API key after
restart.

---

Outside diff comments:
In `@ScreenTranslate/Features/Preview/PreviewWindow.swift`:
- Around line 103-113: The repeating Timer assigned to imageSizeTimer keeps
firing after the window is closed or when multiple observers start because it's
never invalidated; update the lifecycle to (1) avoid creating duplicate timers
by invalidating any existing imageSizeTimer before scheduling a new one, and (2)
invalidate and nil out imageSizeTimer when the preview window is closed or the
owner is deinitialized (e.g., in windowWillClose/deinit or an appropriate
cleanup method) so resizeToFitImage and the imageSizeChangeCounter polling stop
running and resources are released.

---

Nitpick comments:
In `@ScreenTranslate/Features/Onboarding/OnboardingComponents.swift`:
- Around line 77-83: The OnboardingPermissionRow's API exposes
openSettingsAction but the UI no longer invokes it; update code to align
behavior by either removing the openSettingsAction parameter from the
OnboardingPermissionRow struct and all call sites (e.g., where
OnboardingPermissionsStepView passes a closure at lines referenced) or
reintroducing a visible "Open Settings" control in OnboardingPermissionRow that
calls openSettingsAction; locate the symbol OnboardingPermissionRow and the
usage in OnboardingPermissionsStepView and either (A) delete the
openSettingsAction property and remove the passed closures, or (B) add a clear
button/tap target in the row UI that invokes openSettingsAction so the API is
functional.

In `@ScreenTranslate/Features/Settings/SettingsViewModel.swift`:
- Line 353: Remove the unused capture of the previous value by deleting the line
that declares oldValue (let oldValue = settings.vlmProvider) in
SettingsViewModel's setter for vlmProvider; ensure no other code depends on
oldValue and keep the setter logic using settings.vlmProvider directly to avoid
misleading readers about a before/after comparison.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e5899511-555f-4421-8edc-2fc061f465f2

📥 Commits

Reviewing files that changed from the base of the PR and between 40e49a5 and 7512300.

📒 Files selected for processing (13)
  • ScreenTranslate/App/AppDelegate.swift
  • ScreenTranslate/Features/Onboarding/OnboardingCompleteStepView.swift
  • ScreenTranslate/Features/Onboarding/OnboardingComponents.swift
  • ScreenTranslate/Features/Onboarding/OnboardingConfigurationStepView.swift
  • ScreenTranslate/Features/Onboarding/OnboardingPermissionsStepView.swift
  • ScreenTranslate/Features/Onboarding/OnboardingView.swift
  • ScreenTranslate/Features/Onboarding/OnboardingViewModel.swift
  • ScreenTranslate/Features/Onboarding/OnboardingWindowController.swift
  • ScreenTranslate/Features/Preview/PreviewWindow.swift
  • ScreenTranslate/Features/Settings/SettingsViewModel.swift
  • ScreenTranslate/Models/AppSettings.swift
  • ScreenTranslate/Resources/en.lproj/Localizable.strings
  • ScreenTranslate/Resources/zh-Hans.lproj/Localizable.strings
💤 Files with no reviewable changes (1)
  • ScreenTranslate/Features/Onboarding/OnboardingConfigurationStepView.swift

Comment thread ScreenTranslate/App/AppDelegate.swift
Comment thread ScreenTranslate/Features/Onboarding/OnboardingViewModel.swift Outdated
Comment on lines +556 to +558
// Load PaddleOCR cloud API key from Keychain (secure storage)
paddleOCRCloudAPIKey = Self.loadPaddleOCRAPIKeyFromKeychain()
// Defer keychain access to avoid triggering UI on first launch
paddleOCRCloudAPIKey = ""
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

不要把已保存的 PaddleOCR Cloud API Key 永久丢成空串。

这里为了避免首启触发 Keychain UI 把 paddleOCRCloudAPIKey 直接初始化成 "",但这个文件里已经没有任何后续回填路径了。SettingsViewModel.swift 的 Line 996-1001 和 PaddleOCRVLMProvider.swift 的 Line 75-85 都直接读取这个属性发请求,所以老用户重启后云端 PaddleOCR 会一直拿不到已保存密钥,直到手动重新输入。请改成“延迟加载”,不要变成“不再加载”。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ScreenTranslate/Models/AppSettings.swift` around lines 556 - 558, The
paddleOCRCloudAPIKey is being set to an empty string which permanently prevents
loading the saved Keychain value; instead make this value lazily loaded (not
pre-initialized to "") so existing saved keys are returned when first needed.
Change the property paddleOCRCloudAPIKey to be nil/optional or a lazy/computed
property that fetches the key from the Keychain on first access (or provide a
loadPaddleOCRCloudAPIKey() helper that reads Keychain without triggering UI
until invoked), and ensure callers in SettingsViewModel and PaddleOCRVLMProvider
read via that lazy getter or call the loader before sending requests so old
users get their saved API key after restart.

- skipPermissions() now navigates to Complete step instead of skipping it
- Disable skip button when all permissions are already granted
- Initialize Sparkle updater when onboarding window is dismissed without completing
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@ScreenTranslate/Features/Onboarding/OnboardingViewModel.swift`:
- Around line 29-30: The hasSkippedPermissions flag is never cleared, causing
sessions where the user skips then later grants permissions to still be treated
as skipped; update the ViewModel so that when the Permissions step reports
success (the method handling permission grant confirmation in
OnboardingViewModel / the Permissions step completion handler) you reset
hasSkippedPermissions = false, and additionally add a guard in the logic that
decides whether the session is skipped (e.g., the code used by
goToPreviousStep(), the Complete step warning and the completion logic
referenced around the existing hasSkippedPermissions checks) to verify
currentMissingPermissions is empty (or call a helper like
hasMissingPermissions()) instead of relying solely on the boolean; ensure the
Complete-page warning and finalization path consult this runtime permission
check so granting permissions after a skip clears the skipped state.
- Around line 244-247: The code currently only writes
settings.userSkippedPermissions = true when hasSkippedPermissions is true, so
the persisted flag never gets cleared; in OnboardingViewModel where you post
.onboardingCompleted, replace the conditional with an explicit write of the
final state (e.g. set settings.userSkippedPermissions = hasSkippedPermissions)
so the persisted value reflects the actual outcome (skipped or not) before
posting the onboardingCompleted notification.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 214553a6-fbd4-40b6-854d-b218d58bfa9a

📥 Commits

Reviewing files that changed from the base of the PR and between 7512300 and 0eaa366.

📒 Files selected for processing (4)
  • ScreenTranslate/App/AppDelegate.swift
  • ScreenTranslate/Features/Onboarding/OnboardingPermissionsStepView.swift
  • ScreenTranslate/Features/Onboarding/OnboardingViewModel.swift
  • ScreenTranslate/Features/Onboarding/OnboardingWindowController.swift
🚧 Files skipped from review as they are similar to previous changes (2)
  • ScreenTranslate/Features/Onboarding/OnboardingWindowController.swift
  • ScreenTranslate/Features/Onboarding/OnboardingPermissionsStepView.swift

Comment on lines +29 to +30
/// Whether user has skipped the permissions step
var hasSkippedPermissions = false
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

hasSkippedPermissions 会“粘住”,回退后补齐权限仍会被当成 skipped。

这里一旦置成 true 就没有回退路径了;而 goToPreviousStep() 允许用户从 Complete 回到 Permissions。用户如果先点 Skip、再返回补齐两个权限,Complete 页 warning 仍会显示,后面的完成逻辑也会把这次会话当成 skipped。建议在权限页成功通过后清掉这个标志,并在 ViewModel 里也 guard 一下“当前仍有缺失权限”,别只靠按钮禁用维持不变量。

Also applies to: 109-115

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ScreenTranslate/Features/Onboarding/OnboardingViewModel.swift` around lines
29 - 30, The hasSkippedPermissions flag is never cleared, causing sessions where
the user skips then later grants permissions to still be treated as skipped;
update the ViewModel so that when the Permissions step reports success (the
method handling permission grant confirmation in OnboardingViewModel / the
Permissions step completion handler) you reset hasSkippedPermissions = false,
and additionally add a guard in the logic that decides whether the session is
skipped (e.g., the code used by goToPreviousStep(), the Complete step warning
and the completion logic referenced around the existing hasSkippedPermissions
checks) to verify currentMissingPermissions is empty (or call a helper like
hasMissingPermissions()) instead of relying solely on the boolean; ensure the
Complete-page warning and finalization path consult this runtime permission
check so granting permissions after a skip clears the skipped state.

Comment thread ScreenTranslate/Features/Onboarding/OnboardingViewModel.swift Outdated
- Restore PaddleOCR cloud API key loading from Keychain on init to
  avoid losing saved credentials for existing users
- Clear hasSkippedPermissions flag when all permissions are granted
  during polling, preventing stale skipped state after user goes back
- Check actual permission state in completeOnboarding() before
  persisting userSkippedPermissions to UserDefaults
@hubo1989
Copy link
Copy Markdown
Owner Author

Replied to CodeRabbit review feedback

All issues have been addressed:

Already fixed in commit 0eaa366:

  • Skip button visible when permissions granted → Added .disabled(canGoNext)
  • Skip bypasses Complete stepskipPermissions() now navigates to Complete step

Fixed in commit 33a1cbf:

  • PaddleOCR keychain key lost → Restored loadPaddleOCRAPIKeyFromKeychain() on init. SecItemCopyMatching for reading should not trigger UI prompts.
  • hasSkippedPermissions sticky flag → Clears the flag in permission polling loop when both permissions become granted
  • Persisted skipped state always truecompleteOnboarding() now checks actual permission state before persisting

@hubo1989 hubo1989 merged commit 69a6b33 into main Mar 26, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant