vOOice/VoiceInk/ViewModels/LicenseViewModel.swift
2025-02-22 11:52:41 +05:45

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")
}