Files
yattee/Yattee/Views/Onboarding/OnboardingSheetView.swift
Arkadiusz Fal 08c544d161 Fix tvOS onboarding background transparency and button styling
Add opaque black background to onboarding on tvOS to prevent Home
screen content from leaking through the fullScreenCover. Replace
toolbar Skip button with plain overlay button to avoid blurred
material style, and add tvOS card button style with default focus
on Continue button.
2026-02-16 19:41:22 +01:00

142 lines
3.9 KiB
Swift

//
// OnboardingSheetView.swift
// Yattee
//
// Container view for the onboarding flow with TabView and page dots.
//
import SwiftUI
struct OnboardingSheetView: View {
@Environment(\.dismiss) private var dismiss
@Environment(\.appEnvironment) private var appEnvironment
@State private var currentPage = 0
@State private var legacyItems: [LegacyImportItem]?
@State private var hasCheckedLegacyData = false
private var totalPages: Int {
hasLegacyData ? 4 : 3
}
private var hasLegacyData: Bool {
legacyItems?.isEmpty == false
}
private var settingsManager: SettingsManager? {
appEnvironment?.settingsManager
}
private var legacyMigrationService: LegacyDataMigrationService? {
appEnvironment?.legacyMigrationService
}
var body: some View {
TabView(selection: $currentPage) {
OnboardingTitleScreen(onContinue: advanceToNextPage)
.tag(0)
// Cloud screen is now always page 1 (before migration)
// This ensures iCloud instances are synced before migration runs,
// so isDuplicate() correctly detects duplicates against iCloud-synced instances
OnboardingCloudScreen(onContinue: advanceToNextPage)
.tag(1)
// Migration screen is now page 2 (when present)
if hasLegacyData, let binding = Binding($legacyItems) {
OnboardingMigrationScreen(
onContinue: advanceToNextPage,
onSkip: advanceToNextPage,
items: binding
)
.tag(2)
}
OnboardingSourcesScreen(
onGoToSources: goToSettings,
onClose: completeOnboarding
)
.tag(hasLegacyData ? 3 : 2)
}
#if os(tvOS)
.tabViewStyle(.page)
#elseif os(iOS)
.tabViewStyle(.page(indexDisplayMode: .never))
#endif
#if os(tvOS)
.background(Color.black.ignoresSafeArea())
.overlay(alignment: .topLeading) {
Button(String(localized: "onboarding.skip")) {
completeOnboarding()
}
.buttonStyle(.plain)
.foregroundStyle(.secondary)
.padding(40)
}
#endif
.interactiveDismissDisabled()
#if !os(tvOS)
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button(String(localized: "onboarding.skip")) {
completeOnboarding()
}
}
}
#endif
.task {
guard !hasCheckedLegacyData else { return }
hasCheckedLegacyData = true
legacyItems = legacyMigrationService?.parseLegacyData()
}
}
// MARK: - Navigation
private func advanceToNextPage() {
withAnimation {
if currentPage < totalPages - 1 {
currentPage += 1
} else {
completeOnboarding()
}
}
}
private func completeOnboarding() {
settingsManager?.onboardingCompleted = true
dismiss()
}
private func goToSettings() {
settingsManager?.onboardingCompleted = true
dismiss()
// Navigate to settings after dismiss completes
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
NotificationCenter.default.post(name: .showSettings, object: nil)
}
}
}
// MARK: - Notification
extension Notification.Name {
static let showOnboarding = Notification.Name("showOnboarding")
static let showSettings = Notification.Name("showSettings")
static let showOpenLinkSheet = Notification.Name("showOpenLinkSheet")
}
// MARK: - Preview
#Preview {
@Previewable @State var sheetPresented: Bool = true
VStack {
}.sheet(isPresented: $sheetPresented) {
OnboardingSheetView()
.appEnvironment(.preview)
}
}