123 lines
3.9 KiB
Swift
123 lines
3.9 KiB
Swift
import Foundation
|
|
import AppKit
|
|
|
|
@MainActor
|
|
class LicenseViewModel: ObservableObject {
|
|
enum LicenseState: Equatable {
|
|
case trial(daysRemaining: Int)
|
|
case trialExpired
|
|
case licensed
|
|
}
|
|
|
|
@Published private(set) var licenseState: LicenseState = .trial(daysRemaining: 7) // Default to trial
|
|
@Published var licenseKey: String = ""
|
|
@Published var isValidating = false
|
|
@Published var validationMessage: String?
|
|
|
|
private let trialPeriodDays = 7
|
|
private let polarService = PolarService()
|
|
private let userDefaults = UserDefaults.standard
|
|
|
|
init() {
|
|
checkLicenseState()
|
|
}
|
|
|
|
func startTrial() {
|
|
// Only set trial start date if it hasn't been set before
|
|
if userDefaults.trialStartDate == nil {
|
|
userDefaults.trialStartDate = Date()
|
|
licenseState = .trial(daysRemaining: trialPeriodDays)
|
|
NotificationCenter.default.post(name: .licenseStatusChanged, object: nil)
|
|
}
|
|
}
|
|
|
|
private func checkLicenseState() {
|
|
// Check for existing license key
|
|
if let licenseKey = userDefaults.licenseKey {
|
|
self.licenseKey = licenseKey
|
|
licenseState = .licensed
|
|
return
|
|
}
|
|
|
|
// Check if this is first launch
|
|
let hasLaunchedBefore = userDefaults.bool(forKey: "VoiceInkHasLaunchedBefore")
|
|
if !hasLaunchedBefore {
|
|
// First launch - start trial automatically
|
|
userDefaults.set(true, forKey: "VoiceInkHasLaunchedBefore")
|
|
startTrial()
|
|
return
|
|
}
|
|
|
|
// Only check trial if not licensed and not first launch
|
|
if let trialStartDate = userDefaults.trialStartDate {
|
|
let daysSinceTrialStart = Calendar.current.dateComponents([.day], from: trialStartDate, to: Date()).day ?? 0
|
|
|
|
if daysSinceTrialStart >= trialPeriodDays {
|
|
licenseState = .trialExpired
|
|
} else {
|
|
licenseState = .trial(daysRemaining: trialPeriodDays - daysSinceTrialStart)
|
|
}
|
|
} else {
|
|
// No trial has been started yet - start it now
|
|
startTrial()
|
|
}
|
|
}
|
|
|
|
var canUseApp: Bool {
|
|
switch licenseState {
|
|
case .licensed, .trial:
|
|
return true
|
|
case .trialExpired:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func openPurchaseLink() {
|
|
if let url = URL(string: "https://tryvoiceink.com/buy") {
|
|
NSWorkspace.shared.open(url)
|
|
}
|
|
}
|
|
|
|
func validateLicense() async {
|
|
guard !licenseKey.isEmpty else {
|
|
validationMessage = "Please enter a license key"
|
|
return
|
|
}
|
|
|
|
isValidating = true
|
|
|
|
do {
|
|
let isValid = try await polarService.validateLicenseKey(licenseKey)
|
|
if isValid {
|
|
userDefaults.licenseKey = licenseKey
|
|
licenseState = .licensed
|
|
validationMessage = "License activated successfully!"
|
|
NotificationCenter.default.post(name: .licenseStatusChanged, object: nil)
|
|
} else {
|
|
validationMessage = "Invalid license key"
|
|
}
|
|
} catch {
|
|
validationMessage = "Error validating license"
|
|
}
|
|
|
|
isValidating = false
|
|
}
|
|
|
|
func removeLicense() {
|
|
// Remove both license key and trial data
|
|
userDefaults.licenseKey = nil
|
|
userDefaults.trialStartDate = nil
|
|
userDefaults.set(false, forKey: "VoiceInkHasLaunchedBefore") // Allow trial to restart
|
|
|
|
licenseState = .trial(daysRemaining: trialPeriodDays) // Reset to trial state
|
|
licenseKey = ""
|
|
validationMessage = nil
|
|
NotificationCenter.default.post(name: .licenseStatusChanged, object: nil)
|
|
checkLicenseState()
|
|
}
|
|
}
|
|
|
|
extension Notification.Name {
|
|
static let licenseStatusChanged = Notification.Name("licenseStatusChanged")
|
|
}
|