From cb689a4cbc6a7d68ba852ea0ff88a67ed759297c Mon Sep 17 00:00:00 2001 From: Michael Freno Date: Sat, 10 Jan 2026 10:02:51 -0500 Subject: [PATCH] feat: conditional buy me a coffee --- Gaze/Services/AppStoreDetector.swift | 28 ++++++++++ Gaze/Views/Onboarding/BlinkSetupView.swift | 8 ++- Gaze/Views/Onboarding/LookAwaySetupView.swift | 8 ++- Gaze/Views/Onboarding/PostureSetupView.swift | 8 ++- .../Onboarding/SettingsOnboardingView.swift | 55 ++++++++++--------- .../Services/AppStoreDetectorTests.swift | 17 ++++++ 6 files changed, 88 insertions(+), 36 deletions(-) create mode 100644 Gaze/Services/AppStoreDetector.swift create mode 100644 GazeTests/Services/AppStoreDetectorTests.swift diff --git a/Gaze/Services/AppStoreDetector.swift b/Gaze/Services/AppStoreDetector.swift new file mode 100644 index 0000000..7c3090a --- /dev/null +++ b/Gaze/Services/AppStoreDetector.swift @@ -0,0 +1,28 @@ +// +// AppStoreDetector.swift +// Gaze +// +// Created by Mike Freno on 1/10/26. +// + +import Foundation + +enum AppStoreDetector { + /// Returns true if the app was downloaded from the Mac App Store + static var isAppStoreVersion: Bool { + #if DEBUG + return false + #else + guard let receiptURL = Bundle.main.appStoreReceiptURL else { + return false + } + + // Check if receipt exists and is in the expected App Store location + let fileManager = FileManager.default + let receiptExists = fileManager.fileExists(atPath: receiptURL.path) + let isInMASReceipt = receiptURL.path.contains("_MASReceipt") + + return receiptExists && isInMASReceipt + #endif + } +} diff --git a/Gaze/Views/Onboarding/BlinkSetupView.swift b/Gaze/Views/Onboarding/BlinkSetupView.swift index a73725a..98759ed 100644 --- a/Gaze/Views/Onboarding/BlinkSetupView.swift +++ b/Gaze/Views/Onboarding/BlinkSetupView.swift @@ -101,13 +101,15 @@ struct BlinkSetupView: View { Button(action: { showPreviewWindow() }) { - HStack { + HStack(spacing: 8) { Image(systemName: "eye") - .foregroundColor(.accentColor) + .foregroundColor(.white) Text("Preview Reminder") .font(.headline) + .foregroundColor(.white) } - .frame(maxWidth: .infinity, minHeight: 44, alignment: .center) + .padding(.horizontal, 16) + .padding(.vertical, 10) } .glassEffect(.regular.tint(.accentColor).interactive(), in: .rect(cornerRadius: 10)) } diff --git a/Gaze/Views/Onboarding/LookAwaySetupView.swift b/Gaze/Views/Onboarding/LookAwaySetupView.swift index fd18283..fb54fde 100644 --- a/Gaze/Views/Onboarding/LookAwaySetupView.swift +++ b/Gaze/Views/Onboarding/LookAwaySetupView.swift @@ -122,13 +122,15 @@ struct LookAwaySetupView: View { Button(action: { showPreviewWindow() }) { - HStack { + HStack(spacing: 8) { Image(systemName: "eye") - .foregroundColor(.accentColor) + .foregroundColor(.white) Text("Preview Reminder") .font(.headline) + .foregroundColor(.white) } - .frame(maxWidth: .infinity, minHeight: 44, alignment: .center) + .padding(.horizontal, 16) + .padding(.vertical, 10) } .glassEffect(.regular.tint(.accentColor).interactive(), in: .rect(cornerRadius: 10)) } diff --git a/Gaze/Views/Onboarding/PostureSetupView.swift b/Gaze/Views/Onboarding/PostureSetupView.swift index 7636f8f..7ce0e51 100644 --- a/Gaze/Views/Onboarding/PostureSetupView.swift +++ b/Gaze/Views/Onboarding/PostureSetupView.swift @@ -101,13 +101,15 @@ struct PostureSetupView: View { Button(action: { showPreviewWindow() }) { - HStack { + HStack(spacing: 8) { Image(systemName: "eye") - .foregroundColor(.accentColor) + .foregroundColor(.white) Text("Preview Reminder") .font(.headline) + .foregroundColor(.white) } - .frame(maxWidth: .infinity, minHeight: 44, alignment: .center) + .padding(.horizontal, 16) + .padding(.vertical, 10) } .glassEffect(.regular.tint(.accentColor).interactive(), in: .rect(cornerRadius: 10)) } diff --git a/Gaze/Views/Onboarding/SettingsOnboardingView.swift b/Gaze/Views/Onboarding/SettingsOnboardingView.swift index fc6f1f3..745a381 100644 --- a/Gaze/Views/Onboarding/SettingsOnboardingView.swift +++ b/Gaze/Views/Onboarding/SettingsOnboardingView.swift @@ -132,36 +132,37 @@ struct SettingsOnboardingView: View { .buttonStyle(.plain) .glassEffect(.regular.interactive(), in: .rect(cornerRadius: 10)) - // Buy Me a Coffee - Button(action: { - if let url = URL(string: "https://buymeacoffee.com/placeholder") { - NSWorkspace.shared.open(url) - } - }) { - HStack { - Image(systemName: "cup.and.saucer.fill") - .font(.title3) - .foregroundColor(.orange) - VStack(alignment: .leading, spacing: 2) { - Text("Buy Me a Coffee") - .font(.subheadline) - .fontWeight(.semibold) - Text("Support development of Gaze") - .font(.caption) - .foregroundColor(.secondary) + if !AppStoreDetector.isAppStoreVersion { + Button(action: { + if let url = URL(string: "https://buymeacoffee.com/placeholder") { + NSWorkspace.shared.open(url) } - Spacer() - Image(systemName: "arrow.up.right") - .font(.caption) + }) { + HStack { + Image(systemName: "cup.and.saucer.fill") + .font(.title3) + .foregroundColor(.orange) + VStack(alignment: .leading, spacing: 2) { + Text("Buy Me a Coffee") + .font(.subheadline) + .fontWeight(.semibold) + Text("Support development of Gaze") + .font(.caption) + .foregroundColor(.secondary) + } + Spacer() + Image(systemName: "arrow.up.right") + .font(.caption) + } + .padding() + .frame(maxWidth: .infinity) + .background(Color.orange.opacity(0.1)) + .cornerRadius(10) } - .padding() - .frame(maxWidth: .infinity) - .background(Color.orange.opacity(0.1)) - .cornerRadius(10) + .buttonStyle(.plain) + .glassEffect( + .regular.tint(.orange).interactive(), in: .rect(cornerRadius: 10)) } - .buttonStyle(.plain) - .glassEffect( - .regular.tint(.orange).interactive(), in: .rect(cornerRadius: 10)) } .padding() } diff --git a/GazeTests/Services/AppStoreDetectorTests.swift b/GazeTests/Services/AppStoreDetectorTests.swift new file mode 100644 index 0000000..213508b --- /dev/null +++ b/GazeTests/Services/AppStoreDetectorTests.swift @@ -0,0 +1,17 @@ +// +// AppStoreDetectorTests.swift +// GazeTests +// +// Created by Mike Freno on 1/10/26. +// + +@testable import Gaze +import Testing + +struct AppStoreDetectorTests { + + @Test func isAppStoreVersionReturnsFalseInDebug() { + // In test/debug builds, should always return false + #expect(AppStoreDetector.isAppStoreVersion == false) + } +}