From 7fbff450a4d05e4ad70b315e155d7e25f3e1d030 Mon Sep 17 00:00:00 2001 From: Kieran Klukas Date: Fri, 27 Mar 2026 21:32:05 -0700 Subject: [PATCH] feat: add onboarding dialog --- overpass/Services/StoreManager.swift | 10 ++- overpass/Views/ConfettiView.swift | 72 +++++++++++++++++ overpass/Views/OnboardingView.swift | 89 +++++++++++++++++++++ overpass/Views/SettingsView.swift | 9 ++- overpass/overpass.xcodeproj/project.pbxproj | 8 ++ overpass/overpassApp.swift | 16 +++- 6 files changed, 201 insertions(+), 3 deletions(-) create mode 100644 overpass/Views/ConfettiView.swift create mode 100644 overpass/Views/OnboardingView.swift diff --git a/overpass/Services/StoreManager.swift b/overpass/Services/StoreManager.swift index 1f68480..3f6d991 100644 --- a/overpass/Services/StoreManager.swift +++ b/overpass/Services/StoreManager.swift @@ -8,13 +8,15 @@ final class StoreManager { static let shared = StoreManager() private(set) var isUnlocked = false + private(set) var justPurchased = false + var isAppActive = false private(set) var purchaseDate: Date? private(set) var product: Product? private(set) var isPurchasing = false private let productId = "sh.dunkirk.overpass.unlock" private let firstLaunchKeychainKey = "sh.dunkirk.overpass.first_launch" - static let trialDuration: TimeInterval = 7 * 24 * 60 * 60 + static let trialDuration: TimeInterval = 15 * 24 * 60 * 60 var trialStartDate: Date? { guard let stored = KeychainService.load(forKey: firstLaunchKeychainKey), @@ -59,6 +61,10 @@ final class StoreManager { } else { isUnlocked = true purchaseDate = tx.purchaseDate + if isAppActive { + justPurchased = true + Task { try? await Task.sleep(for: .seconds(4)); justPurchased = false } + } } await tx.finish() } @@ -88,6 +94,8 @@ final class StoreManager { if case .success(let verification) = result, case .verified(_) = verification { isUnlocked = true + justPurchased = true + Task { try? await Task.sleep(for: .seconds(4)); justPurchased = false } } } diff --git a/overpass/Views/ConfettiView.swift b/overpass/Views/ConfettiView.swift new file mode 100644 index 0000000..ab0b2fc --- /dev/null +++ b/overpass/Views/ConfettiView.swift @@ -0,0 +1,72 @@ +import SwiftUI + +struct ConfettiView: View { + struct Particle: Identifiable { + let id = UUID() + let x: CGFloat + let color: Color + let width: CGFloat + let height: CGFloat + let delay: Double + let duration: Double + let targetRotation: Double + let drift: CGFloat + } + + private static let colors: [Color] = [.red, .orange, .yellow, .green, .blue, .purple, .pink, .cyan] + + private let particles: [Particle] = (0..<90).map { _ in + Particle( + x: CGFloat.random(in: 0...1), + color: colors[Int.random(in: 0..