Add local iOS release pipeline, fix shortcuts, and improve invoice UX.

Enable App Store builds without EAS, iOS 18 App Intents plugins, and signing
fixes for distribution export. Add mobile invoice PDF preview, compact line
items, and more reliable shortcut deep-link handling.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-23 01:08:20 -04:00
parent 06bc91ac13
commit 355b14faef
35 changed files with 1915 additions and 502 deletions
@@ -0,0 +1,9 @@
import SwiftUI
@available(iOS 18.0, *)
enum BeenVoiceIntentHelpers {
@MainActor
static func openDeepLink(_ url: URL) {
EnvironmentValues().openURL(url)
}
}
+31 -30
View File
@@ -1,36 +1,37 @@
import AppIntents
@available(iOS 16.0, *)
@available(iOS 18.0, *)
struct BeenVoiceShortcuts: AppShortcutsProvider {
@AppShortcutsBuilder
static var appShortcuts: [AppShortcut] {
[
AppShortcut(
intent: ClockInIntent(),
phrases: [
"Clock in with \(.applicationName)",
"Start timer in \(.applicationName)",
],
shortTitle: "Clock In",
systemImageName: "play.circle.fill"
),
AppShortcut(
intent: ClockOutIntent(),
phrases: [
"Clock out in \(.applicationName)",
"Stop timer in \(.applicationName)",
],
shortTitle: "Clock Out",
systemImageName: "stop.circle.fill"
),
AppShortcut(
intent: OpenTimerIntent(),
phrases: [
"Open time clock in \(.applicationName)",
"Open timer in \(.applicationName)",
],
shortTitle: "Time Clock",
systemImageName: "timer"
),
]
AppShortcut(
intent: ClockInIntent(),
phrases: [
"Clock in with \(.applicationName)",
"Start timer in \(.applicationName)",
"Start tracking time in \(.applicationName)",
],
shortTitle: "Clock In",
systemImageName: "play.circle.fill"
)
AppShortcut(
intent: ClockOutIntent(),
phrases: [
"Clock out in \(.applicationName)",
"Stop timer in \(.applicationName)",
"Stop tracking time in \(.applicationName)",
],
shortTitle: "Clock Out",
systemImageName: "stop.circle.fill"
)
AppShortcut(
intent: OpenTimerIntent(),
phrases: [
"Open time clock in \(.applicationName)",
"Open timer in \(.applicationName)",
],
shortTitle: "Time Clock",
systemImageName: "timer"
)
}
}
+4 -6
View File
@@ -1,15 +1,15 @@
import AppIntents
import UIKit
@available(iOS 16.0, *)
@available(iOS 18.0, *)
struct ClockInIntent: AppIntent {
static var title: LocalizedStringResource = "Clock In"
static var description = IntentDescription("Start the beenvoice time clock with your last client.")
static var openAppWhenRun: Bool = false
static var openAppWhenRun: Bool = true
@Parameter(title: "Title")
var title: String?
@MainActor
func perform() async throws -> some IntentResult {
var components = URLComponents()
components.scheme = "beenvoice"
@@ -24,9 +24,7 @@ struct ClockInIntent: AppIntent {
return .result()
}
await MainActor.run {
UIApplication.shared.open(url)
}
BeenVoiceIntentHelpers.openDeepLink(url)
return .result()
}
+4 -6
View File
@@ -1,20 +1,18 @@
import AppIntents
import UIKit
@available(iOS 16.0, *)
@available(iOS 18.0, *)
struct ClockOutIntent: AppIntent {
static var title: LocalizedStringResource = "Clock Out"
static var description = IntentDescription("Stop the running beenvoice timer and save your time.")
static var openAppWhenRun: Bool = false
static var openAppWhenRun: Bool = true
@MainActor
func perform() async throws -> some IntentResult {
guard let url = URL(string: "beenvoice://shortcuts/clock-out") else {
return .result()
}
await MainActor.run {
UIApplication.shared.open(url)
}
BeenVoiceIntentHelpers.openDeepLink(url)
return .result()
}
+4 -6
View File
@@ -1,20 +1,18 @@
import AppIntents
import UIKit
@available(iOS 16.0, *)
@available(iOS 18.0, *)
struct OpenTimerIntent: AppIntent {
static var title: LocalizedStringResource = "Open Time Clock"
static var description = IntentDescription("Open the beenvoice time clock.")
static var openAppWhenRun: Bool = false
static var openAppWhenRun: Bool = true
@MainActor
func perform() async throws -> some IntentResult {
guard let url = URL(string: "beenvoice://timer") else {
return .result()
}
await MainActor.run {
UIApplication.shared.open(url)
}
BeenVoiceIntentHelpers.openDeepLink(url)
return .result()
}