Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion ScreenTranslate/App/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,10 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
PaddleOCRChecker.checkAvailabilityAsync()

// Initialize updater controller to register notification observer
_ = updaterController
// Skip during first launch to avoid overlapping with onboarding
if settings.onboardingCompleted {
_ = updaterController
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

Logger.general.info("ScreenTranslate launched - settings loaded from: \(self.settings.saveLocation.path)")
}
Expand All @@ -78,11 +81,30 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
if !settings.onboardingCompleted {
// Show onboarding for first-time users - already @MainActor
OnboardingWindowController.shared.showOnboarding(settings: settings)

// Initialize updater after onboarding completes or is dismissed
NotificationCenter.default.addObserver(
self,
selector: #selector(initializeUpdaterIfNeeded),
name: .onboardingCompleted,
object: nil
)
NotificationCenter.default.addObserver(
self,
selector: #selector(initializeUpdaterIfNeeded),
name: .onboardingDismissed,
object: nil
)
}
// Note: Don't auto-check screen recording permission on launch
// to avoid triggering system dialog. Users can check in Settings.
}

/// Initializes the updater controller if not already done.
@objc private func initializeUpdaterIfNeeded() {
_ = updaterController
}

func applicationWillTerminate(_ notification: Notification) {
// Unregister hotkeys synchronously with timeout
// Use semaphore to ensure completion before process exits
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import SwiftUI

struct OnboardingCompleteStepView: View {
let onStart: () -> Void
let hasSkippedPermissions: Bool

var body: some View {
VStack(spacing: 24) {
Expand All @@ -23,6 +24,13 @@ struct OnboardingCompleteStepView: View {
}

VStack(alignment: .leading, spacing: 12) {
OnboardingInfoRow(
icon: "menubar.rectangle",
text: NSLocalizedString("onboarding.complete.menubar.hint", comment: "")
)

Divider()

OnboardingInfoRow(
icon: "command",
text: NSLocalizedString("onboarding.complete.shortcuts", comment: "")
Expand All @@ -38,6 +46,19 @@ struct OnboardingCompleteStepView: View {
}
.padding(.vertical, 8)

if hasSkippedPermissions {
HStack(spacing: 8) {
Image(systemName: "exclamationmark.triangle.fill")
.foregroundStyle(.orange)
Text(NSLocalizedString("onboarding.complete.permissions.warning", comment: ""))
.font(.caption)
.foregroundStyle(.secondary)
}
.padding(12)
.background(Color(nsColor: .controlBackgroundColor))
.clipShape(.rect(cornerRadius: 8))
}

Spacer()

Button {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ struct OnboardingNavigationButtons: View {
struct OnboardingPermissionRow: View {
let icon: String
let title: String
let subtitle: String
let isGranted: Bool
let requestAction: () -> Void
let openSettingsAction: () -> Void
Expand All @@ -91,6 +92,9 @@ struct OnboardingPermissionRow: View {
VStack(alignment: .leading, spacing: 4) {
Text(title)
.font(.headline)
Text(subtitle)
.font(.caption)
.foregroundStyle(.secondary)
Text(isGranted
? NSLocalizedString("onboarding.permission.granted", comment: "")
: NSLocalizedString("onboarding.permission.not.granted", comment: ""))
Expand All @@ -106,7 +110,6 @@ struct OnboardingPermissionRow: View {
.font(.title2)
} else {
Button {
openSettingsAction()
requestAction()
} label: {
Text(NSLocalizedString("onboarding.permission.grant", comment: ""))
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ struct OnboardingPermissionsStepView: View {
let canGoPrevious: Bool
let canGoNext: Bool
let isLastStep: Bool
let permissionCheckTimedOut: Bool
let onRequestScreenRecording: () -> Void
let onOpenScreenRecordingSettings: () -> Void
let onRequestAccessibility: () -> Void
let onOpenAccessibilitySettings: () -> Void
let onPrevious: () -> Void
let onNext: () -> Void
let onSkip: () -> Void

var body: some View {
VStack(spacing: 24) {
Expand All @@ -36,6 +38,7 @@ struct OnboardingPermissionsStepView: View {
OnboardingPermissionRow(
icon: "video.fill",
title: NSLocalizedString("onboarding.permission.screen.recording", comment: ""),
subtitle: NSLocalizedString("onboarding.permission.screen.recording.subtitle", comment: ""),
isGranted: hasScreenRecordingPermission,
requestAction: onRequestScreenRecording,
openSettingsAction: onOpenScreenRecordingSettings
Expand All @@ -44,25 +47,56 @@ struct OnboardingPermissionsStepView: View {
OnboardingPermissionRow(
icon: "command.square.fill",
title: NSLocalizedString("onboarding.permission.accessibility", comment: ""),
subtitle: NSLocalizedString("onboarding.permission.accessibility.subtitle", comment: ""),
isGranted: hasAccessibilityPermission,
requestAction: onRequestAccessibility,
openSettingsAction: onOpenAccessibilitySettings
)
}

if permissionCheckTimedOut {
Text(NSLocalizedString("onboarding.permissions.timeout.hint", comment: ""))
.font(.caption)
.foregroundStyle(.orange)
.multilineTextAlignment(.center)
}

Spacer()

Text(NSLocalizedString("onboarding.permissions.hint", comment: ""))
.font(.caption)
.foregroundStyle(.secondary)

OnboardingNavigationButtons(
canGoPrevious: canGoPrevious,
canGoNext: canGoNext,
isLastStep: isLastStep,
onPrevious: onPrevious,
onNext: onNext
)
HStack(spacing: 16) {
if canGoPrevious {
Button {
onPrevious()
} label: {
Text(NSLocalizedString("onboarding.back", comment: ""))
}
.buttonStyle(.bordered)
}

Spacer()

Button {
onSkip()
} label: {
Text(NSLocalizedString("onboarding.skip.permissions", comment: ""))
}
.buttonStyle(.bordered)
.tint(.secondary)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
.disabled(canGoNext)

if canGoNext && !isLastStep {
Button {
onNext()
} label: {
Text(NSLocalizedString("onboarding.continue", comment: ""))
}
.buttonStyle(.borderedProminent)
}
}
}
.padding(32)
}
Expand Down
Loading