diff --git a/MacNearFuture/Views/EventListViewMac.swift b/MacNearFuture/Views/EventListViewMac.swift index a8ac999..7d86eb0 100644 --- a/MacNearFuture/Views/EventListViewMac.swift +++ b/MacNearFuture/Views/EventListViewMac.swift @@ -118,11 +118,6 @@ struct EventListView: View { .spring(response: 0.2, dampingFraction: 0.75, blendDuration: 2), value: largeTick ) - .apply { - if #available(iOS 17, *) { - $0.sensoryFeedback(.success, trigger: event.complete) - } - } } .transition(.opacity) .fixedSize(horizontal: false, vertical: true) diff --git a/MacNearFuture/Views/Events/AddEventView.swift b/MacNearFuture/Views/Events/AddEventView.swift index ad61533..fe79361 100644 --- a/MacNearFuture/Views/Events/AddEventView.swift +++ b/MacNearFuture/Views/Events/AddEventView.swift @@ -143,11 +143,7 @@ struct AddEventView: View { .buttonStyle(BorderedProminentButtonStyle()) } .tint(.accent) - .apply { - if #available(iOS 17, *) { - $0.sensoryFeedback(.success, trigger: bye) - } - } + .modifier(hapticSuccess(trigger: bye)) .disabled(event.name.isEmpty) .onTapGesture { if event.name.isEmpty { diff --git a/NearFuture.xcodeproj/project.pbxproj b/NearFuture.xcodeproj/project.pbxproj index 7d9fec1..78145fe 100644 --- a/NearFuture.xcodeproj/project.pbxproj +++ b/NearFuture.xcodeproj/project.pbxproj @@ -26,6 +26,12 @@ A914FA4B2DD26C6800856265 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A914FA4A2DD26C0F00856265 /* HomeView.swift */; }; A914FA4D2DD2768900856265 /* WhatsNewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A914FA4C2DD2768900856265 /* WhatsNewView.swift */; }; A914FA4F2DD276D200856265 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A914FA4E2DD276D200856265 /* AboutView.swift */; }; + A91EF8072DFC8B8B00B8463D /* ColorCodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = A91EF8062DFC8B8B00B8463D /* ColorCodable.swift */; }; + A91EF8082DFC8B8B00B8463D /* ColorCodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = A91EF8062DFC8B8B00B8463D /* ColorCodable.swift */; }; + A91EF8092DFC8B8B00B8463D /* ColorCodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = A91EF8062DFC8B8B00B8463D /* ColorCodable.swift */; }; + A91EF80B2DFC910000B8463D /* ViewModifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = A91EF80A2DFC910000B8463D /* ViewModifiers.swift */; }; + A91EF80C2DFC910000B8463D /* ViewModifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = A91EF80A2DFC910000B8463D /* ViewModifiers.swift */; }; + A91EF80D2DFC910000B8463D /* ViewModifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = A91EF80A2DFC910000B8463D /* ViewModifiers.swift */; }; A920C2882D24011400E4F9B1 /* NearFutureApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = A920C2872D24011400E4F9B1 /* NearFutureApp.swift */; }; A920C28C2D24011400E4F9B1 /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = A920C28B2D24011400E4F9B1 /* Events.swift */; }; A920C28E2D24011A00E4F9B1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A920C28D2D24011A00E4F9B1 /* Assets.xcassets */; }; @@ -111,6 +117,8 @@ A914FA4A2DD26C0F00856265 /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = ""; }; A914FA4C2DD2768900856265 /* WhatsNewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = WhatsNewView.swift; path = NearFuture/Views/Settings/WhatsNewView.swift; sourceTree = SOURCE_ROOT; }; A914FA4E2DD276D200856265 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AboutView.swift; path = NearFuture/Views/Misc/AboutView.swift; sourceTree = SOURCE_ROOT; }; + A91EF8062DFC8B8B00B8463D /* ColorCodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorCodable.swift; sourceTree = ""; }; + A91EF80A2DFC910000B8463D /* ViewModifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModifiers.swift; sourceTree = ""; }; A920C2842D24011400E4F9B1 /* NearFuture.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NearFuture.app; sourceTree = BUILT_PRODUCTS_DIR; }; A920C2872D24011400E4F9B1 /* NearFutureApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearFutureApp.swift; sourceTree = ""; }; A920C28B2D24011400E4F9B1 /* Events.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Events.swift; sourceTree = ""; }; @@ -178,6 +186,7 @@ children = ( A920C28B2D24011400E4F9B1 /* Events.swift */, A90D49602DDE626300781124 /* Settings.swift */, + A91EF8062DFC8B8B00B8463D /* ColorCodable.swift */, A95E9ED72DFC742B00ED655F /* AccentIcon.swift */, ); path = Model; @@ -213,6 +222,7 @@ A920C2872D24011400E4F9B1 /* NearFutureApp.swift */, A90D49512DDE2D0000781124 /* Extensions.swift */, A90D49202DDE0A3B00781124 /* Model */, + A91EF80A2DFC910000B8463D /* ViewModifiers.swift */, ); path = Shared; sourceTree = ""; @@ -519,6 +529,7 @@ A95E9EE32DFC775300ED655F /* SettingsView.swift in Sources */, A95E9EE42DFC77D400ED655F /* ImportView.swift in Sources */, A98C20CB2DE730740008D61C /* EventListViewMac.swift in Sources */, + A91EF80C2DFC910000B8463D /* ViewModifiers.swift in Sources */, A95E9ED92DFC742B00ED655F /* AccentIcon.swift in Sources */, A98C20CC2DE730740008D61C /* EditEventView.swift in Sources */, A90D495E2DDE3C7400781124 /* NFCommands.swift in Sources */, @@ -528,6 +539,7 @@ A98C20D02DE731BD0008D61C /* HomeView.swift in Sources */, A90D495B2DDE2EDB00781124 /* MacNearFutureApp.swift in Sources */, A9B78B942DF9F3CF00647399 /* AddEventView.swift in Sources */, + A91EF8082DFC8B8B00B8463D /* ColorCodable.swift in Sources */, A90D49522DDE2D0000781124 /* Extensions.swift in Sources */, A90D49422DDE114100781124 /* Events.swift in Sources */, A90D49382DDE0FAF00781124 /* ContentViewMac.swift in Sources */, @@ -550,9 +562,11 @@ A90D49612DDE626300781124 /* Settings.swift in Sources */, A90D495F2DDE3C7400781124 /* NFCommands.swift in Sources */, A949F84F2DCAABE00064DCA0 /* EventListView.swift in Sources */, + A91EF8092DFC8B8B00B8463D /* ColorCodable.swift in Sources */, A949F8502DCAABE00064DCA0 /* HelpView.swift in Sources */, A949F85F2DCABB420064DCA0 /* Buttons.swift in Sources */, A914FA4D2DD2768900856265 /* WhatsNewView.swift in Sources */, + A91EF80B2DFC910000B8463D /* ViewModifiers.swift in Sources */, A949F8512DCAABE00064DCA0 /* ExportView.swift in Sources */, A95E9ED82DFC742B00ED655F /* AccentIcon.swift in Sources */, A90D49532DDE2D0000781124 /* Extensions.swift in Sources */, @@ -571,6 +585,8 @@ A979F6182D2714310094C0B3 /* Events.swift in Sources */, A979F60A2D270AF00094C0B3 /* NearFutureWidgetsBundle.swift in Sources */, A95E9EDA2DFC742B00ED655F /* AccentIcon.swift in Sources */, + A91EF80D2DFC910000B8463D /* ViewModifiers.swift in Sources */, + A91EF8072DFC8B8B00B8463D /* ColorCodable.swift in Sources */, A9FC7EEA2D2823920020D75B /* NearFutureWidgets.swift in Sources */, A979F60C2D270AF00094C0B3 /* NearFutureWidgetsLiveActivity.swift in Sources */, A90D49632DDE626300781124 /* Settings.swift in Sources */, diff --git a/NearFuture/Views/Archive/ArchiveView.swift b/NearFuture/Views/Archive/ArchiveView.swift index cccb9f8..0c752f7 100644 --- a/NearFuture/Views/Archive/ArchiveView.swift +++ b/NearFuture/Views/Archive/ArchiveView.swift @@ -39,13 +39,7 @@ struct ArchiveView: View { } } .navigationTitle("Archive") - .apply { - if #available(iOS 17, *) { - $0.toolbarTitleDisplayMode(.inlineLarge) - } else { - $0.navigationBarTitleDisplayMode(.inline) - } - } + .modifier(navigationInlineLarge()) } .sheet(isPresented: $showAddEvent) { AddEventView( diff --git a/NearFuture/Views/ContentView.swift b/NearFuture/Views/ContentView.swift index 324a7bb..4c11370 100644 --- a/NearFuture/Views/ContentView.swift +++ b/NearFuture/Views/ContentView.swift @@ -22,10 +22,10 @@ enum Tab { struct ContentView: View { @StateObject var viewModel: EventViewModel @StateObject var settingsModel: SettingsViewModel - @State var selection: Tab = .home + @State var tabSelection: Tab = .home var body: some View { - TabView(selection: $selection) { + TabView(selection: $tabSelection) { HomeView(viewModel: viewModel, settingsModel: settingsModel) .tabItem { Label("Home", systemImage: "house") @@ -47,11 +47,7 @@ struct ContentView: View { } .tag(Tab.settings) } - .apply { - if #available(iOS 17, *) { - $0.sensoryFeedback(.impact(weight: .heavy, intensity: 1), trigger: selection) - } - } + .modifier(hapticHeavy(trigger: tabSelection)) .sheet(isPresented: $settingsModel.settings.showWhatsNew) { WhatsNewView(settingsModel: settingsModel) } diff --git a/NearFuture/Views/Events/AddEventView.swift b/NearFuture/Views/Events/AddEventView.swift index 2a04244..3471ec5 100644 --- a/NearFuture/Views/Events/AddEventView.swift +++ b/NearFuture/Views/Events/AddEventView.swift @@ -149,11 +149,7 @@ struct AddEventView: View { .buttonStyle(BorderedProminentButtonStyle()) } .tint(.accent) - .apply { - if #available(iOS 17, *) { - $0.sensoryFeedback(.success, trigger: bye) - } - } + .modifier(hapticSuccess(trigger: bye)) .disabled(event.name.isEmpty) .onTapGesture { if event.name.isEmpty { diff --git a/NearFuture/Views/Home/EventListView.swift b/NearFuture/Views/Home/EventListView.swift index aaab596..e7003e8 100644 --- a/NearFuture/Views/Home/EventListView.swift +++ b/NearFuture/Views/Home/EventListView.swift @@ -112,11 +112,7 @@ struct EventListView: View { .frame(maxWidth: 25, maxHeight: 25) .shadow(radius: 5) .padding(.trailing, 5) - .apply { - if #available(iOS 17, *) { - $0.sensoryFeedback(.success, trigger: event.complete) - } - } + .modifier(hapticSuccess(trigger: event.complete)) } .transition(.opacity) .padding(.vertical, 5) diff --git a/NearFuture/Views/Home/HomeView.swift b/NearFuture/Views/Home/HomeView.swift index 10ced96..d105350 100644 --- a/NearFuture/Views/Home/HomeView.swift +++ b/NearFuture/Views/Home/HomeView.swift @@ -67,13 +67,7 @@ struct HomeView: View { } .searchable(text: $searchInput) .navigationTitle("Near Future") - .apply { - if #available(iOS 17, *) { - $0.toolbarTitleDisplayMode(.inlineLarge) - } else { - $0.navigationBarTitleDisplayMode(.inline) - } - } + .modifier(navigationInlineLarge()) .sheet(isPresented: $showingAddEventView) { AddEventView( viewModel: viewModel, diff --git a/NearFuture/Views/Settings/SettingsView.swift b/NearFuture/Views/Settings/SettingsView.swift index d8c09a7..0bd94e4 100644 --- a/NearFuture/Views/Settings/SettingsView.swift +++ b/NearFuture/Views/Settings/SettingsView.swift @@ -13,28 +13,37 @@ struct SettingsView: View { @State private var importStr: String = "" - func changeIcon(to: String) { + func changeIcon(to toIcon: String) { #if canImport(UIKit) guard UIApplication.shared.supportsAlternateIcons else { print("doesnt tsupport alternate icons") return } - guard to != "orange" else { + guard toIcon != "orange" else { UIApplication.shared.setAlternateIconName(nil) { error in print(error as Any) } return } - UIApplication.shared.setAlternateIconName(to) { error in + UIApplication.shared.setAlternateIconName(toIcon) { error in print(error as Any) } + #else + if let nsimage = NSImage(named: toIcon) { + let nsImageView = NSImageView(image: nsimage) + nsImageView.frame = NSRect(x: 0, y: 0, width: 128, height: 128) + NSApplication.shared.dockTile.contentView = nsImageView + NSApplication.shared.dockTile.display() + } #endif } var body: some View { NavigationStack { ZStack { + #if os(iOS) backgroundGradient + #endif List { ScrollView(.horizontal) { HStack { @@ -153,17 +162,9 @@ struct SettingsView: View { } } } - .scrollContentBackground(.hidden) .navigationTitle("Settings") - .apply { - #if canImport(UIKit) - if #available(iOS 17, *) { - $0.toolbarTitleDisplayMode(.inlineLarge) - } else { - $0.navigationBarTitleDisplayMode(.inline) - } - #endif - } + .modifier(navigationInlineLarge()) + .scrollContentBackground(.hidden) } } } diff --git a/NearFuture/Views/Settings/WhatsNewView.swift b/NearFuture/Views/Settings/WhatsNewView.swift index 68fee39..44f438a 100644 --- a/NearFuture/Views/Settings/WhatsNewView.swift +++ b/NearFuture/Views/Settings/WhatsNewView.swift @@ -84,11 +84,7 @@ struct WhatsNewView: View { .buttonStyle(BorderedProminentButtonStyle()) .clipShape(RoundedRectangle(cornerRadius: 15)) .padding().padding() - .apply { - if #available(iOS 17, *) { - $0.sensoryFeedback(.impact(weight: .heavy, intensity: 1), trigger: bye) - } - } + .modifier(hapticHeavy(trigger: bye)) } .scrollContentBackground(.hidden) .presentationDragIndicator(.visible) diff --git a/NearFuture/Views/Stats/StatsView.swift b/NearFuture/Views/Stats/StatsView.swift index 588e121..eae317e 100644 --- a/NearFuture/Views/Stats/StatsView.swift +++ b/NearFuture/Views/Stats/StatsView.swift @@ -58,13 +58,7 @@ struct StatsView: View { } .scrollContentBackground(.hidden) .navigationTitle("Statistics") - .apply { - if #available(iOS 17, *) { - $0.toolbarTitleDisplayMode(.inlineLarge) - } else { - $0.navigationBarTitleDisplayMode(.inline) - } - } + .modifier(navigationInlineLarge()) } } } diff --git a/Shared/Extensions.swift b/Shared/Extensions.swift index 5120315..049e4c4 100644 --- a/Shared/Extensions.swift +++ b/Shared/Extensions.swift @@ -17,8 +17,6 @@ extension View { ) .ignoresSafeArea(.all) } - - func apply(@ViewBuilder _ block: (Self) -> V) -> V { block(self) } } extension AnyTransition { diff --git a/Shared/Model/ColorCodable.swift b/Shared/Model/ColorCodable.swift new file mode 100644 index 0000000..62bf696 --- /dev/null +++ b/Shared/Model/ColorCodable.swift @@ -0,0 +1,83 @@ +// +// ColorCodable.swift +// NearFuture +// +// Created by neon443 on 13/06/2025. +// + +import Foundation +import SwiftUI +#if canImport(UIKit) +import UIKit +#else +import AppKit +#endif + +struct ColorCodable: Codable, Equatable { + init(_ color: Color) { + var r: CGFloat = 0, g: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 1 + +#if canImport(UIKit) + let uiColor = UIColor(color) + uiColor.getRed(&r, green: &g, blue: &b, alpha: &a) +#elseif canImport(AppKit) + let nscolor = NSColor(color).usingColorSpace(.deviceRGB) + nscolor!.getRed(&r, green: &g, blue: &b, alpha: &a) +#endif + + self = ColorCodable( + red: r, + green: g, + blue: b + ) + } +#if canImport(UIKit) + init(uiColor: UIColor) { + var r: CGFloat = 0, g: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 1.0 + uiColor.getRed(&r, green: &g, blue: &b, alpha: &a) + self = ColorCodable( + red: r, + green: g, + blue: b + ) + } +#elseif canImport(AppKit) + init(nsColor: NSColor) { + var r: CGFloat = 0, g: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 1.0 + let nsColor = nsColor.usingColorSpace(.deviceRGB) + nsColor!.getRed(&r, green: &g, blue: &b, alpha: &a) + self = ColorCodable( + red: r, + green: g, + blue: b + ) + } +#endif + init(red: Double, green: Double, blue: Double) { + self.red = red + self.green = green + self.blue = blue + } + + var red: Double + var green: Double + var blue: Double + + var color: Color { + Color(red: red, green: green, blue: blue) + } + var colorBind: Color { + get { + return Color( + red: red, + green: green, + blue: blue + ) + } set { + let cc = ColorCodable(newValue) + self.red = cc.red + self.green = cc.green + self.blue = cc.blue + } + } +} diff --git a/Shared/Model/Events.swift b/Shared/Model/Events.swift index 35849b3..5da37f5 100644 --- a/Shared/Model/Events.swift +++ b/Shared/Model/Events.swift @@ -40,75 +40,6 @@ struct Event: Identifiable, Codable, Equatable, Animatable { } } -struct ColorCodable: Codable, Equatable { - init(_ color: Color) { - var r: CGFloat = 0, g: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 1 - -#if canImport(UIKit) - let uiColor = UIColor(color) - uiColor.getRed(&r, green: &g, blue: &b, alpha: &a) -#elseif canImport(AppKit) - let nscolor = NSColor(color).usingColorSpace(.deviceRGB) - nscolor!.getRed(&r, green: &g, blue: &b, alpha: &a) -#endif - - self = ColorCodable( - red: r, - green: g, - blue: b - ) - } -#if canImport(UIKit) - init(uiColor: UIColor) { - var r: CGFloat = 0, g: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 1.0 - uiColor.getRed(&r, green: &g, blue: &b, alpha: &a) - self = ColorCodable( - red: r, - green: g, - blue: b - ) - } -#elseif canImport(AppKit) - init(nsColor: NSColor) { - var r: CGFloat = 0, g: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 1.0 - let nsColor = nsColor.usingColorSpace(.deviceRGB) - nsColor!.getRed(&r, green: &g, blue: &b, alpha: &a) - self = ColorCodable( - red: r, - green: g, - blue: b - ) - } -#endif - init(red: Double, green: Double, blue: Double) { - self.red = red - self.green = green - self.blue = blue - } - - var red: Double - var green: Double - var blue: Double - - var color: Color { - Color(red: red, green: green, blue: blue) - } - var colorBind: Color { - get { - return Color( - red: red, - green: green, - blue: blue - ) - } set { - let cc = ColorCodable(newValue) - self.red = cc.red - self.green = cc.green - self.blue = cc.blue - } - } -} - func daysUntilEvent(_ eventDate: Date) -> (long: String, short: String) { let calendar = Calendar.current let startOfDayNow = calendar.startOfDay(for: Date()) diff --git a/Shared/ViewModifiers.swift b/Shared/ViewModifiers.swift new file mode 100644 index 0000000..71d0f70 --- /dev/null +++ b/Shared/ViewModifiers.swift @@ -0,0 +1,54 @@ +// +// ViewModifiers.swift +// NearFuture +// +// Created by neon443 on 13/06/2025. +// + +import Foundation +import SwiftUI + +struct hapticHeavy: ViewModifier { + var trigger: any Equatable + + init(trigger: any Equatable) { + self.trigger = trigger + } + + func body(content: Content) -> some View { + if #available(iOS 17, *) { + content + .sensoryFeedback(.impact(weight: .heavy, intensity: 1), trigger: trigger) + } else { + content + } + } +} + +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 + } + } +} + +struct navigationInlineLarge: ViewModifier { + func body(content: Content) -> some View { +#if os(macOS) + content + .toolbarTitleDisplayMode(.inlineLarge) +#else + content + .navigationBarTitleDisplayMode(.inline) +#endif + } +}