24 Commits

Author SHA1 Message Date
neon443
f3501a9379 update redme 2025-07-17 09:07:31 +01:00
neon443
6f870ffa4f improve notification handling
fix crash on ocmpleting events
2025-06-25 20:20:38 +01:00
neon443
9841574d37 swift 6 migration complete 2025-06-23 20:10:54 +01:00
neon443
899304833c mb gng 2025-06-23 18:25:56 +01:00
neon443
4629d4f75f preparation for swift 6 2025-06-23 18:23:18 +01:00
neon443
be68c44ffe improvbe text field 2025-06-19 15:02:46 +01:00
neon443
3fe9077e69 rewrote haptic viewmodifiers again :cry
made textfield expand and allowed the enter button
fix crashes due to as! self
2025-06-19 13:44:00 +01:00
neon443
121dd79d54 fix crashiing on mac 2025-06-19 10:59:59 +01:00
neon443
01116c7fcb fix modifier uses
fix addeventview not compiling
2025-06-19 10:40:52 +01:00
neon443
3690a9e4d2 fix stuff for GM seed building
(cant use xcode beta to sumbit to appstore smh)
and bump version
2025-06-19 10:23:17 +01:00
neon443
c393404fec fix alert having like 2 cancel buttons 2025-06-19 09:48:07 +01:00
neon443
3ee22da036 made events update propery when changing on ios
update animations on adding and removing events
fix symbolpicker crash on fast scroll by making it not show all the symbols lol
might be ready to ship?
2025-06-19 09:37:15 +01:00
neon443
2b25ddf9b3 extract delete event logic
trying to add swipe actions again
2025-06-19 09:03:11 +01:00
neon443
01ff82181a crazy haptics on completeing events
new alert for importing events (my custom one was pretty shit)
2025-06-16 19:59:39 +01:00
neon443
b7ef7b4e19 add app type 2025-06-15 21:31:48 +01:00
neon443
7727f14ad4 Merge branch 'feat-mac' 2025-06-15 21:25:25 +01:00
Nihaal Sharma
d5580e52f5 Update README.md 2025-05-29 17:29:37 +01:00
Nihaal Sharma
84a7091e05 Update README.md 2025-05-29 17:22:53 +01:00
Nihaal Sharma
44b40894e4 Delete .github directory 2025-05-26 18:04:30 +01:00
Nihaal Sharma
e4842bd29a Update ios.yml 2025-05-26 18:02:37 +01:00
Nihaal Sharma
b378a831be Update ios.yml 2025-05-26 17:57:31 +01:00
Nihaal Sharma
2ff96a7093 Update ios.yml 2025-05-26 17:52:13 +01:00
Nihaal Sharma
4f5e31a6f3 Update ios.yml 2025-05-26 17:51:01 +01:00
Nihaal Sharma
266b27d817 Create ios.yml 2025-05-26 17:49:06 +01:00
18 changed files with 214 additions and 239 deletions

View File

@@ -12,6 +12,6 @@ TEAM_ID = 8JGND254B7
BUNDLE_ID = com.neon443.NearFuture
BUNDLE_ID_WIDGETS = com.neon443.NearFuture.widgets
GROUP_ID = group.NearFuture
VERSION = 5
VERSION = 5.0.1
NAME = Near Future
BUILD_NUMBER = 1
BUILD_NUMBER = 52

View File

@@ -19,6 +19,14 @@ struct ArchiveView: View {
ScrollView {
ForEach(filteredEvents) { event in
EventListView(viewModel: viewModel, event: event)
.contextMenu() {
Button(role: .destructive) {
viewModel.removeEvent(event)
} label: {
Label("Delete", systemImage: "trash")
.tint(.red )
}
}
}
}
.scrollContentBackground(.hidden)

View File

@@ -34,6 +34,14 @@ struct HomeView: View {
if filteredEvents.contains(event) {
EventListView(viewModel: viewModel, event: event)
.id(event)
.contextMenu() {
Button(role: .destructive) {
viewModel.removeEvent(event)
} label: {
Label("Delete", systemImage: "trash")
.tint(.red)
}
}
}
}
}

View File

@@ -639,6 +639,7 @@
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
INFOPLIST_KEY_NSHumanReadableCopyright = "";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@@ -651,7 +652,7 @@
REGISTER_APP_GROUPS = YES;
SDKROOT = macosx;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
SWIFT_VERSION = 6.0;
};
name = Debug;
};
@@ -670,6 +671,7 @@
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
INFOPLIST_KEY_NSHumanReadableCopyright = "";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@@ -682,7 +684,7 @@
REGISTER_APP_GROUPS = YES;
SDKROOT = macosx;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
SWIFT_VERSION = 6.0;
};
name = Release;
};
@@ -835,6 +837,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = "Near Future";
INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES;
"INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES;
@@ -863,7 +866,8 @@
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_VERSION = 6.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
@@ -887,6 +891,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = "Near Future";
INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES;
"INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES;
@@ -912,7 +917,8 @@
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_VERSION = 6.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;

View File

@@ -35,9 +35,17 @@ struct ArchiveView: View {
)
} label: {
EventListView(viewModel: viewModel, event: event)
.id(event.complete)
.id(event)
}
.transition(.moveAndFadeReversed)
.contextMenu() {
Button(role: .destructive) {
viewModel.removeEvent(event)
} label: {
Label("Delete", systemImage: "trash")
.tint(.red)
}
}
}
.padding(.horizontal)
}

View File

@@ -16,8 +16,6 @@ struct AddEventView: View {
@State var showNeedsNameAlert: Bool = false
@State var isSymbolPickerPresented: Bool = false
@State private var bye: Bool = false
@FocusState private var focusedField: Field?
private enum Field {
case Name, Notes
@@ -72,15 +70,15 @@ struct AddEventView: View {
// dscription
ZStack {
TextField("Event Notes", text: $event.notes)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding(.trailing, event.notes.isEmpty ? 0 : 30)
.animation(.spring, value: event.notes)
.focused($focusedField, equals: Field.Notes)
.submitLabel(.done)
.onSubmit {
focusedField = nil
if event.notes.isEmpty {
HStack {
Text("Event Notes")
.opacity(0.5)
Spacer()
}
}
TextEditor(text: $event.notes)
.lineLimit(10)
}
ColorPicker("Event Color", selection: $event.color.colorBind)
@@ -152,13 +150,14 @@ struct AddEventView: View {
viewModel.addEvent(
newEvent: event
)
bye.toggle()
resetAddEventView()
#if canImport(UIKit)
UINotificationFeedbackGenerator().notificationOccurred(.success)
#endif
} label: {
Label("Save", systemImage: "checkmark")
}
.tint(.accent)
.modifier(hapticSuccess(trigger: bye))
.disabled(event.name.isEmpty)
.onTapGesture {
if event.name.isEmpty {
@@ -180,6 +179,9 @@ struct AddEventView: View {
Button() {
viewModel.editEvent(event)
dismiss()
#if canImport(UIKit)
UINotificationFeedbackGenerator().notificationOccurred(.success)
#endif
} label: {
Label("Done", systemImage: "checkmark")
}

View File

@@ -90,19 +90,6 @@ struct EventListView: View {
.onTapGesture {
openWindow(value: event.id)
}
.contextMenu() {
Button(role: .destructive) {
let eventToModify = viewModel.events.firstIndex() { currEvent in
currEvent.id == event.id
}
if let eventToModify = eventToModify {
viewModel.events.remove(at: eventToModify)
viewModel.saveEvents()
}
} label: {
Label("Delete", systemImage: "trash")
}
}
}
#else
var body: some View {
@@ -176,15 +163,9 @@ struct EventListView: View {
)
.clipShape(RoundedRectangle(cornerRadius: 15))
.fixedSize(horizontal: false, vertical: true)
.contextMenu() {
.swipeActions(edge: .trailing, allowsFullSwipe: true) {
Button(role: .destructive) {
let eventToModify = viewModel.events.firstIndex() { currEvent in
currEvent.id == event.id
}
if let eventToModify = eventToModify {
viewModel.events.remove(at: eventToModify)
viewModel.saveEvents()
}
viewModel.removeEvent(event)
} label: {
Label("Delete", systemImage: "trash")
}
@@ -196,16 +177,6 @@ struct EventListView: View {
#Preview("EventListView") {
let vm = dummyEventViewModel()
ZStack {
Color.black
VStack {
ForEach(0..<50) { _ in
Rectangle()
.foregroundStyle(randomColor().opacity(0.5))
.padding(-10)
}
.ignoresSafeArea(.all)
.blur(radius: 5)
}
VStack {
ForEach(vm.events) { event in
EventListView(

View File

@@ -59,9 +59,17 @@ struct HomeView: View {
)
} label: {
EventListView(viewModel: viewModel, event: event)
.id(event.complete)
.id(event)
}
.transition(.moveAndFade)
.contextMenu() {
Button(role: .destructive) {
viewModel.removeEvent(event)
} label: {
Label("Delete", systemImage: "trash")
.tint(.red)
}
}
}
.padding(.horizontal)
// }

View File

@@ -66,65 +66,19 @@ struct ImportView: View {
fgColor = .yellow
}
}
.blur(radius: showAlert ? 2 : 0)
Group {
Rectangle()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.foregroundStyle(replaceCurrentEvents ? .red.opacity(0.25) : .black.opacity(0.2))
.animation(.default, value: replaceCurrentEvents)
.ignoresSafeArea()
ZStack {
Rectangle()
.clipShape(RoundedRectangle(cornerRadius: 25))
VStack(alignment: .center) {
Text("Are you sure?")
.font(.largeTitle)
.bold()
.foregroundStyle(replaceCurrentEvents ? .red : .two)
.animation(.default, value: replaceCurrentEvents)
Text("This will replace your current events!")
.lineLimit(nil)
.multilineTextAlignment(.center)
.opacity(replaceCurrentEvents ? 1 : 0)
.animation(.default, value: replaceCurrentEvents)
.foregroundStyle(.two)
Toggle("Replace Events", isOn: $replaceCurrentEvents)
.foregroundStyle(.two)
Spacer()
HStack {
Button() {
withAnimation {
showAlert.toggle()
}
importEvents()
} label: {
Text("cancel")
.font(.title2)
.bold()
}
.buttonStyle(BorderedProminentButtonStyle())
Spacer()
Button() {
withAnimation {
showAlert.toggle()
}
importEvents()
} label: {
Text("yes")
.font(.title2)
.bold()
}
.buttonStyle(BorderedProminentButtonStyle())
}
.padding()
}
.padding()
.alert("Are you sure?", isPresented: $showAlert) {
Button(role: .destructive) {
importEvents()
} label: {
Text("Replace Events")
}
Button() {
importEvents()
} label: {
Text("Add to Events")
.foregroundStyle(.one)
}
.frame(maxWidth: 250, maxHeight: 250)
}
.opacity(showAlert ? 1 : 0)
}
}
}

View File

@@ -154,25 +154,25 @@ struct EventWidgetView: View {
.foregroundColor(event.date < Date() ? .red : .primary)
.padding(.trailing, -12)
} else {
Button(
intent: CompleteEvent(
eventID: IntentParameter(
title: LocalizedStringResource(
stringLiteral: event.id.uuidString
)
)
)
) {
if event.complete {
Circle()
.frame(width: 10)
.foregroundStyle(.green)
} else {
Circle()
.frame(width: 10)
.foregroundStyle(.gray)
}
}
// Button(
// intent: CompleteEvent(
// eventID: IntentParameter(
// title: LocalizedStringResource(
// stringLiteral: event.id.uuidString
// )
// )
// )
// ) {
// if event.complete {
// Circle()
// .frame(width: 10)
// .foregroundStyle(.green)
// } else {
// Circle()
// .frame(width: 10)
// .foregroundStyle(.gray)
// }
// }
Text(daysUntilEvent(event.date).long)
.font(.caption)
.multilineTextAlignment(.trailing)

View File

@@ -1,6 +1,32 @@
# NearFuture
<div align="center">
<br/>
<p>
<img src="https://github.com/neon443/NearFuture/blob/main/Resources/Assets.xcassets/AppIcon.appiconset/NearFutureIcon.png?raw=true" title="dockphobia" alt="dockphobia icon" width="100" />
</p>
<h3>Near Future</h3>
<p>
<a href="https://apps.apple.com/us/app/near-future-event-tracker/id6744963429">
download
<img alt="GitHub Release" src="https://img.shields.io/itunes/v/6744963429">
</a>
</p>
<p>
make your Dock scared of the mouse
<br/>
<a href="https://neon443.github.io">
made by neon443
</a>
</p>
<br/>
</div>
[App Store](https://apps.apple.com/us/app/near-future-event-tracker/id6744963429)
<div align="center">
<a href="https://shipwrecked.hackclub.com/?t=ghrm" target="_blank">
<img src="https://hc-cdn.hel1.your-objectstorage.com/s/v3/739361f1d440b17fc9e2f74e49fc185d86cbec14_badge.png"
alt="This project is part of Shipwrecked, the world's first hackathon on an island!"
style="width: 25%;">
</a>
</div>
**Near Future** is a SwiftUI App to help people track upcoming events - Holidays, Trips, Birthdays, Weddings, Anniversaries.

View File

@@ -11,7 +11,7 @@ struct CompleteEventButton: View {
@ObservedObject var viewModel: EventViewModel
@Binding var event: Event
@State var timer: Timer?
@MainActor @State var timer: Timer?
@State var largeTick: Bool = false
@State var completeInProgress: Bool = false
@State var completeStartTime: Date = .now
@@ -34,20 +34,28 @@ struct CompleteEventButton: View {
completeStartTime = .now
progress = 0
timer = Timer(timeInterval: 0.01, repeats: true) { timer in
guard completeInProgress else { return }
guard timer.isValid else { return }
let elapsed = Date().timeIntervalSince(completeStartTime)
progress = min(1, elapsed)
if progress >= 1 {
withAnimation { completeInProgress = false }
viewModel.completeEvent(&event)
timer = Timer(timeInterval: 0.02, repeats: true) { timer in
DispatchQueue.main.async {
guard completeInProgress else { return }
guard let timer = self.timer else { return }
guard timer.isValid else { return }
let elapsed = Date().timeIntervalSince(completeStartTime)
progress = min(1, elapsed)
#if canImport(UIKit)
UINotificationFeedbackGenerator().notificationOccurred(.success)
UIImpactFeedbackGenerator(style: .light).impactOccurred()
#endif
timer.invalidate()
progress = 0
if progress >= 1 {
withAnimation { completeInProgress = false }
viewModel.completeEvent(&event)
#if canImport(UIKit)
DispatchQueue.main.asyncAfter(deadline: .now()+0.02) {
UINotificationFeedbackGenerator().notificationOccurred(.success)
}
#endif
timer.invalidate()
progress = 0
}
}
}
RunLoop.main.add(timer!, forMode: .common)

View File

@@ -22,14 +22,14 @@ extension View {
extension AnyTransition {
static var moveAndFade: AnyTransition {
.asymmetric(
insertion: .move(edge: .leading),
insertion: .opacity,
removal: .move(edge: .trailing)
)
.combined(with: .opacity)
}
static var moveAndFadeReversed: AnyTransition {
.asymmetric(
insertion: .move(edge: .trailing),
insertion: .opacity,
removal: .move(edge: .leading)
)
.combined(with: .opacity)

View File

@@ -160,20 +160,24 @@ class EventViewModel: ObservableObject, @unchecked Sendable {
eventUUIDs.remove(at: remove)
}
let components = getDateComponents(events[index].date)
//check the notif matches event details
if req.content.title == events[index].name,
req.content.subtitle == events[index].notes,
req.trigger == UNCalendarNotificationTrigger(dateMatching: components, repeats: false) {
//if it does, make sure the notif delets if u complete the veent
if events[index].complete {
//if it does, make sure the notif delets if u complete the veent or in the past
if events[index].complete || events[index].date > .now {
cancelNotif(req.identifier)
} else {
//dont cancel the notif
}
} else {
//reschedult it because the event details have changed
cancelNotif(req.identifier)
scheduleEventNotif(events[index])
}
} else {
//cancel if the event is deleted
//cancel notif if the event is deleted (doesnt exist/cannot be matched)
cancelNotif(req.identifier)
}
}
@@ -182,6 +186,14 @@ class EventViewModel: ObservableObject, @unchecked Sendable {
scheduleEventNotif(event)
}
}
Task {
try? await UNUserNotificationCenter.current().setBadgeCount(await getNotifs().count)
}
print(eventUUIDs.count)
print(events.count(where: {!$0.complete && $0.date < .now}))
print(events.count(where: {!$0.complete && $0.date > .now}))
print(events.count(where: {!$0.complete}))
print(events.count(where: {$0.complete}))
}
// save to local and icloud
@@ -240,8 +252,13 @@ class EventViewModel: ObservableObject, @unchecked Sendable {
}
}
func removeEvent(at index: IndexSet) {
events.remove(atOffsets: index)
func removeEvent(_ eventToRemove: Event) {
let eventToModify = self.events.firstIndex() { currEvent in
currEvent.id == eventToRemove.id
}
if let eventToModify = eventToModify {
self.events.remove(at: eventToModify)
}
saveEvents() //sync local and icl
}
@@ -490,6 +507,7 @@ func getBuildID() -> String {
return "\(build)"
}
@MainActor
func getDevice() -> (sf: String, label: String) {
#if canImport(UIKit)
let asi = ProcessInfo().isiOSAppOnMac
@@ -507,57 +525,3 @@ func getDevice() -> (sf: String, label: String) {
return (sf: "desktopcomputer", label: "Mac")
#endif
}
extension Event: AppEntity {
static let defaultQuery = EventQuery()
static var typeDisplayRepresentation: TypeDisplayRepresentation {
TypeDisplayRepresentation("skdfj")
}
var displayRepresentation: DisplayRepresentation {
DisplayRepresentation("eventsss")
}
}
struct EventQuery: EntityQuery, DynamicOptionsProvider {
typealias Entity = Event
@Dependency var vm: EventViewModel
func results() async throws -> some ResultsCollection {
return vm.events
}
// func defaultResult() async -> DefaultValue? {
// return vm.events[0]
// }
func entities(for identifiers: [Entity.ID]) async throws -> [Entity] {
return vm.events
}
func suggestedEntities() async throws -> some ResultsCollection {
return vm.events //lol cba
}
}
struct CompleteEvent: AppIntent {
static var title: LocalizedStringResource = "Complete An Event"
static var description = IntentDescription("Mark an Event as complete.")
@Parameter(title: "Event ID")
var eventID: String
func perform() async throws -> some IntentResult {
print("s")
let viewModel = EventViewModel()
print("hip")
guard let eventUUID = UUID(uuidString: eventID) else {
print(":sdklfajk")
return .result()
}
print("hii")
if let eventToModify = viewModel.events.firstIndex(where: { $0.id == eventUUID }) {
print("hiii")
viewModel.events[eventToModify].complete = true
viewModel.saveEvents()
}
return .result()
}
}

View File

@@ -18,6 +18,7 @@ struct NFSettings: Codable, Equatable {
var prevAppVersion: String = getVersion()+getBuildID()
}
@MainActor
class SettingsViewModel: ObservableObject {
@Published var settings: NFSettings = NFSettings()
@@ -36,7 +37,7 @@ class SettingsViewModel: ObservableObject {
"pink"
]
@Published var device: (sf: String, label: String)
@Published var device: (sf: String, label: String) = ("", "")
init(load: Bool = true) {
self.device = getDevice()

View File

@@ -8,7 +8,7 @@
import Foundation
class SymbolsLoader: ObservableObject {
@Published var allSymbols: [String] = []
private var allSymbols: [String] = []
init() {
self.allSymbols = getAllSymbols()
@@ -16,7 +16,7 @@ class SymbolsLoader: ObservableObject {
func getSymbols(_ searched: String) -> [String] {
if searched.isEmpty {
return allSymbols
return []
} else {
return allSymbols.filter() { $0.localizedCaseInsensitiveContains(searched) }
}

View File

@@ -31,7 +31,17 @@ struct SymbolsPicker: View {
NavigationStack {
GeometryReader { geo in
ScrollView {
if symbols.isEmpty {
if searchInput.isEmpty {
HStack {
Image(systemName: "magnifyingglass")
.resizable().scaledToFit()
.frame(width: 30)
Text("Start a Search")
.font(.title)
.bold()
}
.padding()
} else if symbols.isEmpty {
HStack {
Image(systemName: "magnifyingglass")
.resizable().scaledToFit()

View File

@@ -8,48 +8,49 @@
import Foundation
import SwiftUI
struct hapticHeavy: ViewModifier {
var trigger: any Equatable
struct hapticHeavy<T: Equatable>: ViewModifier {
var trigger: T
init(trigger: any Equatable) {
init(trigger: T) {
self.trigger = trigger
}
func body(content: Content) -> some View {
if #available(iOS 17, *) {
content
.sensoryFeedback(.impact(weight: .heavy, intensity: 1), trigger: trigger)
} else {
content
}
content
.onChange(of: trigger) { _ in
#if canImport(UIKit)
UIImpactFeedbackGenerator(style: .rigid).impactOccurred()
#endif
}
}
}
struct hapticSuccess<T: Equatable>: ViewModifier {
var trigger: T
init(trigger: T) {
self.trigger = trigger
}
func body(content: Content) -> some View {
content
.onChange(of: trigger) { _ in
#if canImport(UIKit)
UINotificationFeedbackGenerator().notificationOccurred(.success)
#endif
}
}
}
struct glassButton: ViewModifier {
func body(content: Content) -> some View {
if #available(iOS 19, macOS 16, *) {
content.buttonStyle(.glass)
} else {
content.buttonStyle(.borderedProminent)
.clipShape(RoundedRectangle(cornerRadius: 15))
.tint(.two)
}
}
}
struct hapticSuccess: ViewModifier {
var trigger: any Equatable
init(trigger: any Equatable) {
self.trigger = trigger
}
func body(content: Content) -> some View {
if #available(iOS 17, *) {
content.sensoryFeedback(.success, trigger: trigger)
} else {
content
}
#if swift(>=6.2)
content.buttonStyle(.glass)
#else
content.buttonStyle(.borderedProminent)
.clipShape(RoundedRectangle(cornerRadius: 15))
.tint(.two)
#endif
}
}