Add option to dismiss Affiliate promotion

This commit is contained in:
Beingpax 2025-12-06 08:18:35 +05:45
parent d4fb2125e6
commit ab9d6ad830
3 changed files with 61 additions and 42 deletions

View File

@ -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)";

View File

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

View File

@ -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)