fix: correct link highlighting, update app store validation
This commit is contained in:
@@ -9,6 +9,12 @@ import Foundation
|
|||||||
|
|
||||||
enum AppStoreDetector {
|
enum AppStoreDetector {
|
||||||
/// Returns true if the app was downloaded from the Mac App Store
|
/// Returns true if the app was downloaded from the Mac App Store
|
||||||
|
///
|
||||||
|
/// Uses a heuristic approach that checks for the presence of a valid App Store receipt.
|
||||||
|
/// This is sufficient for distinguishing App Store builds from direct distribution.
|
||||||
|
///
|
||||||
|
/// Note: This does not perform full cryptographic validation of the receipt signature, its
|
||||||
|
/// only used to determine if we should show the 'buy me a coffee' link.
|
||||||
static var isAppStoreVersion: Bool {
|
static var isAppStoreVersion: Bool {
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
return false
|
return false
|
||||||
@@ -17,12 +23,30 @@ enum AppStoreDetector {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if receipt exists and is in the expected App Store location
|
guard FileManager.default.fileExists(atPath: receiptURL.path) else {
|
||||||
let fileManager = FileManager.default
|
return false
|
||||||
let receiptExists = fileManager.fileExists(atPath: receiptURL.path)
|
}
|
||||||
let isInMASReceipt = receiptURL.path.contains("_MASReceipt")
|
|
||||||
|
|
||||||
return receiptExists && isInMASReceipt
|
guard let receiptData = try? Data(contentsOf: receiptURL),
|
||||||
|
receiptData.count > 2
|
||||||
|
else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify receipt has PKCS#7 signature format (starts with ASN.1 SEQUENCE tag)
|
||||||
|
let bytes = [UInt8](receiptData.prefix(2))
|
||||||
|
return bytes[0] == 0x30 && bytes[1] == 0x82
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if the app is running in TestFlight
|
||||||
|
///
|
||||||
|
/// TestFlight builds have a receipt named "sandboxReceipt" instead of "receipt"
|
||||||
|
static var isTestFlight: Bool {
|
||||||
|
#if DEBUG
|
||||||
|
return false
|
||||||
|
#else
|
||||||
|
return Bundle.main.appStoreReceiptURL?.lastPathComponent == "sandboxReceipt"
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
// Created by Mike Freno on 1/7/26.
|
// Created by Mike Freno on 1/7/26.
|
||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
import AppKit
|
import AppKit
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
struct PostureSetupView: View {
|
struct PostureSetupView: View {
|
||||||
@Binding var enabled: Bool
|
@Binding var enabled: Bool
|
||||||
@@ -33,9 +33,11 @@ struct PostureSetupView: View {
|
|||||||
VStack(spacing: 30) {
|
VStack(spacing: 30) {
|
||||||
HStack(spacing: 12) {
|
HStack(spacing: 12) {
|
||||||
Button(action: {
|
Button(action: {
|
||||||
|
// Using properly URL-encoded text fragment
|
||||||
|
// Points to key findings about sitting posture and behavior relationship with LBP
|
||||||
if let url = URL(
|
if let url = URL(
|
||||||
string:
|
string:
|
||||||
"https://pubmed.ncbi.nlm.nih.gov/40111906/#:~:text=For studies exploring sitting posture, seven found a relationship with LBP. Regarding studies on sitting behavior, only one showed no relationship between LBP prevalence, while twelve indicated a relationship."
|
"https://pubmed.ncbi.nlm.nih.gov/40111906/#:~:text=For%20studies%20exploring%20sitting%20posture%2C%20seven%20found%20a%20relationship%20with%20LBP.%20Regarding%20studies%20on%20sitting%20behavior%2C%20only%20one%20showed%20no%20relationship%20between%20LBP%20prevalence"
|
||||||
) {
|
) {
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
UIApplication.shared.open(url)
|
UIApplication.shared.open(url)
|
||||||
@@ -96,7 +98,7 @@ struct PostureSetupView: View {
|
|||||||
.font(.caption)
|
.font(.caption)
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Preview button
|
// Preview button
|
||||||
Button(action: {
|
Button(action: {
|
||||||
showPreviewWindow()
|
showPreviewWindow()
|
||||||
@@ -120,34 +122,34 @@ struct PostureSetupView: View {
|
|||||||
.padding()
|
.padding()
|
||||||
.background(.clear)
|
.background(.clear)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func showPreviewWindow() {
|
private func showPreviewWindow() {
|
||||||
guard let screen = NSScreen.main else { return }
|
guard let screen = NSScreen.main else { return }
|
||||||
|
|
||||||
let window = NSWindow(
|
let window = NSWindow(
|
||||||
contentRect: screen.frame,
|
contentRect: screen.frame,
|
||||||
styleMask: [.borderless, .fullSizeContentView],
|
styleMask: [.borderless, .fullSizeContentView],
|
||||||
backing: .buffered,
|
backing: .buffered,
|
||||||
defer: false
|
defer: false
|
||||||
)
|
)
|
||||||
|
|
||||||
window.level = .floating
|
window.level = .floating
|
||||||
window.isOpaque = false
|
window.isOpaque = false
|
||||||
window.backgroundColor = .clear
|
window.backgroundColor = .clear
|
||||||
window.collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary]
|
window.collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary]
|
||||||
window.acceptsMouseMovedEvents = true
|
window.acceptsMouseMovedEvents = true
|
||||||
|
|
||||||
let contentView = PostureReminderView(sizePercentage: 10.0) { [weak window] in
|
let contentView = PostureReminderView(sizePercentage: 10.0) { [weak window] in
|
||||||
window?.close()
|
window?.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
window.contentView = NSHostingView(rootView: contentView)
|
window.contentView = NSHostingView(rootView: contentView)
|
||||||
window.makeFirstResponder(window.contentView)
|
window.makeFirstResponder(window.contentView)
|
||||||
|
|
||||||
let windowController = NSWindowController(window: window)
|
let windowController = NSWindowController(window: window)
|
||||||
windowController.showWindow(nil)
|
windowController.showWindow(nil)
|
||||||
window.makeKeyAndOrderFront(nil)
|
window.makeKeyAndOrderFront(nil)
|
||||||
|
|
||||||
previewWindowController = windowController
|
previewWindowController = windowController
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,9 +9,21 @@
|
|||||||
import Testing
|
import Testing
|
||||||
|
|
||||||
struct AppStoreDetectorTests {
|
struct AppStoreDetectorTests {
|
||||||
|
|
||||||
@Test func isAppStoreVersionReturnsFalseInDebug() {
|
@Test func isAppStoreVersionReturnsFalseInDebug() {
|
||||||
// In test/debug builds, should always return false
|
// In test/debug builds, should always return false
|
||||||
#expect(AppStoreDetector.isAppStoreVersion == false)
|
#expect(AppStoreDetector.isAppStoreVersion == false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test func isTestFlightReturnsFalseInDebug() {
|
||||||
|
// In test/debug builds, should always return false
|
||||||
|
#expect(AppStoreDetector.isTestFlight == false)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test func receiptValidationHandlesMissingReceipt() {
|
||||||
|
// When there's no receipt (development build), should return false
|
||||||
|
// This is implicitly tested by isAppStoreVersionReturnsFalseInDebug
|
||||||
|
// but we're documenting the expected behavior
|
||||||
|
#expect(AppStoreDetector.isAppStoreVersion == false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user