Add option to dismiss Affiliate promotion
This commit is contained in:
parent
d4fb2125e6
commit
ab9d6ad830
@ -469,7 +469,7 @@
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 163;
|
||||
CURRENT_PROJECT_VERSION = 164;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"VoiceInk/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = V6J6A3VWY2;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
@ -484,7 +484,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
||||
MARKETING_VERSION = 1.63;
|
||||
MARKETING_VERSION = 1.64;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.prakashjoshipax.VoiceInk;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG ENABLE_NATIVE_SPEECH_ANALYZER $(inherited)";
|
||||
@ -503,7 +503,7 @@
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 163;
|
||||
CURRENT_PROJECT_VERSION = 164;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"VoiceInk/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = V6J6A3VWY2;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
@ -518,7 +518,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
||||
MARKETING_VERSION = 1.63;
|
||||
MARKETING_VERSION = 1.64;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.prakashjoshipax.VoiceInk;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "ENABLE_NATIVE_SPEECH_ANALYZER $(inherited)";
|
||||
|
||||
@ -8,7 +8,8 @@ extension UserDefaults {
|
||||
static let audioInputMode = "audioInputMode"
|
||||
static let selectedAudioDeviceUID = "selectedAudioDeviceUID"
|
||||
static let prioritizedDevices = "prioritizedDevices"
|
||||
|
||||
static let affiliatePromotionDismissed = "VoiceInkAffiliatePromotionDismissed"
|
||||
|
||||
// Obfuscated keys for license-related data
|
||||
enum License {
|
||||
static let trialStartDate = "VoiceInkTrialStartDate"
|
||||
@ -72,4 +73,10 @@ extension UserDefaults {
|
||||
get { data(forKey: Keys.prioritizedDevices) }
|
||||
set { setValue(newValue, forKey: Keys.prioritizedDevices) }
|
||||
}
|
||||
|
||||
// MARK: - Affiliate Promotion Dismissal
|
||||
var affiliatePromotionDismissed: Bool {
|
||||
get { bool(forKey: Keys.affiliatePromotionDismissed) }
|
||||
set { setValue(newValue, forKey: Keys.affiliatePromotionDismissed) }
|
||||
}
|
||||
}
|
||||
@ -3,21 +3,22 @@ import AppKit
|
||||
|
||||
struct DashboardPromotionsSection: View {
|
||||
let licenseState: LicenseViewModel.LicenseState
|
||||
|
||||
@State private var isAffiliatePromotionDismissed: Bool = UserDefaults.standard.affiliatePromotionDismissed
|
||||
|
||||
private var shouldShowUpgradePromotion: Bool {
|
||||
switch licenseState {
|
||||
case .trial(let daysRemaining):
|
||||
return daysRemaining <= 2
|
||||
return daysRemaining <= 3
|
||||
case .trialExpired:
|
||||
return true
|
||||
case .licensed:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private var shouldShowAffiliatePromotion: Bool {
|
||||
if case .licensed = licenseState {
|
||||
return true
|
||||
return !isAffiliatePromotionDismissed
|
||||
}
|
||||
return false
|
||||
}
|
||||
@ -52,7 +53,8 @@ struct DashboardPromotionsSection: View {
|
||||
glowColor: Color(red: 0.08, green: 0.48, blue: 0.85),
|
||||
actionTitle: "Explore Affiliate",
|
||||
actionIcon: "arrow.up.right",
|
||||
action: openAffiliateProgram
|
||||
action: openAffiliateProgram,
|
||||
onDismiss: dismissAffiliatePromotion
|
||||
)
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
@ -74,6 +76,13 @@ struct DashboardPromotionsSection: View {
|
||||
NSWorkspace.shared.open(url)
|
||||
}
|
||||
}
|
||||
|
||||
private func dismissAffiliatePromotion() {
|
||||
withAnimation(.easeInOut(duration: 0.3)) {
|
||||
isAffiliatePromotionDismissed = true
|
||||
}
|
||||
UserDefaults.standard.affiliatePromotionDismissed = true
|
||||
}
|
||||
}
|
||||
|
||||
private struct DashboardPromotionCard: View {
|
||||
@ -85,6 +94,7 @@ private struct DashboardPromotionCard: View {
|
||||
let actionTitle: String
|
||||
let actionIcon: String
|
||||
let action: () -> Void
|
||||
var onDismiss: (() -> Void)? = nil
|
||||
|
||||
private static let defaultGradient: LinearGradient = LinearGradient(
|
||||
colors: [
|
||||
@ -96,8 +106,8 @@ private struct DashboardPromotionCard: View {
|
||||
)
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 14) {
|
||||
HStack(alignment: .top) {
|
||||
ZStack(alignment: .topTrailing) {
|
||||
VStack(alignment: .leading, spacing: 14) {
|
||||
Text(badge.uppercased())
|
||||
.font(.system(size: 11, weight: .heavy))
|
||||
.tracking(0.8)
|
||||
@ -107,42 +117,44 @@ private struct DashboardPromotionCard: View {
|
||||
.clipShape(Capsule())
|
||||
.foregroundColor(.white)
|
||||
|
||||
Spacer()
|
||||
Text(title)
|
||||
.font(.system(size: 20, weight: .heavy, design: .rounded))
|
||||
.foregroundColor(.white)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
|
||||
Image(systemName: accentSymbol)
|
||||
.font(.system(size: 20, weight: .bold))
|
||||
Text(message)
|
||||
.font(.system(size: 13, weight: .medium))
|
||||
.foregroundColor(.white.opacity(0.85))
|
||||
.padding(10)
|
||||
.background(.white.opacity(0.18))
|
||||
.clipShape(RoundedRectangle(cornerRadius: 14, style: .continuous))
|
||||
}
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
|
||||
Text(title)
|
||||
.font(.system(size: 20, weight: .heavy, design: .rounded))
|
||||
.foregroundColor(.white)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
|
||||
Text(message)
|
||||
.font(.system(size: 13, weight: .medium))
|
||||
.foregroundColor(.white.opacity(0.85))
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
|
||||
Button(action: action) {
|
||||
HStack(spacing: 6) {
|
||||
Text(actionTitle)
|
||||
Image(systemName: actionIcon)
|
||||
Button(action: action) {
|
||||
HStack(spacing: 6) {
|
||||
Text(actionTitle)
|
||||
Image(systemName: actionIcon)
|
||||
}
|
||||
.font(.system(size: 13, weight: .semibold))
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 9)
|
||||
.background(.white.opacity(0.22))
|
||||
.clipShape(Capsule())
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
.font(.system(size: 13, weight: .semibold))
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 9)
|
||||
.background(.white.opacity(0.22))
|
||||
.clipShape(Capsule())
|
||||
.foregroundColor(.white)
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
.padding(18)
|
||||
.frame(maxWidth: .infinity, alignment: .topLeading)
|
||||
|
||||
if let onDismiss = onDismiss {
|
||||
Button(action: onDismiss) {
|
||||
Image(systemName: "xmark.circle.fill")
|
||||
.font(.system(size: 18, weight: .medium))
|
||||
.foregroundColor(.white.opacity(0.7))
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.padding(12)
|
||||
.help("Dismiss this promotion")
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
.padding(18)
|
||||
.frame(maxWidth: .infinity, alignment: .topLeading)
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 28, style: .continuous)
|
||||
.fill(Self.defaultGradient)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user