7 Commits

Author SHA1 Message Date
neon443
5f52b423db changes to pending notif checker
add migration, if events dont have notifs
fix runtime error about bg threads by using await MainActor.run
add cancelallnotifs
bump version
2025-05-09 14:09:06 +01:00
neon443
7af96834d6 ok now notifs seem doen
neew event -> notif
edited event- cancel old, new notif
completed events- cancel notif
moved notif granting to settings struct
add: getNotifs, checkPendingNotifs, checkNotif, scheduleEventNotif, getDateComponents, cancelNotif
fix scheduleNotif
migration next
2025-05-07 10:33:35 +01:00
neon443
17ef4f1478 mroe reorg 2025-05-06 22:49:35 +01:00
neon443
785dda0e2b reorg -- long overdue lol 2025-05-06 21:49:21 +01:00
neon443
5f5702d958 ssdlkfjaslkf 2025-05-06 21:05:28 +01:00
neon443
b88bc11b36 yayy notifs!!
made the about app icon better
2025-05-06 21:03:53 +01:00
neon443
f8f5cdde26 symbolpicker presentationdetents 2025-05-06 18:32:59 +01:00
41 changed files with 611 additions and 133 deletions

View File

@@ -12,6 +12,6 @@ TEAM_ID = 8JGND254B7
BUNDLE_ID = com.neon443.NearFuture BUNDLE_ID = com.neon443.NearFuture
BUNDLE_ID_WIDGETS = com.neon443.NearFuture.widgets BUNDLE_ID_WIDGETS = com.neon443.NearFuture.widgets
GROUP_ID = group.NearFuture GROUP_ID = group.NearFuture
VERSION = 3.3.1 VERSION = 4.0.0
NAME = Near Future NAME = Near Future
BUILD_NUMBER = 0 BUILD_NUMBER = 0

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -3,27 +3,30 @@
archiveVersion = 1; archiveVersion = 1;
classes = { classes = {
}; };
objectVersion = 70; objectVersion = 56;
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
A9111B1B2DCA549600D4F793 /* NearFutureIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = A9111B1A2DCA549600D4F793 /* NearFutureIcon.png */; };
A9111B1C2DCA549600D4F793 /* NearFutureIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = A9111B1A2DCA549600D4F793 /* NearFutureIcon.png */; };
A920C2882D24011400E4F9B1 /* NearFutureApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = A920C2872D24011400E4F9B1 /* NearFutureApp.swift */; }; A920C2882D24011400E4F9B1 /* NearFutureApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = A920C2872D24011400E4F9B1 /* NearFutureApp.swift */; };
A920C28C2D24011400E4F9B1 /* Item.swift in Sources */ = {isa = PBXBuildFile; fileRef = A920C28B2D24011400E4F9B1 /* Item.swift */; }; A920C28C2D24011400E4F9B1 /* Item.swift in Sources */ = {isa = PBXBuildFile; fileRef = A920C28B2D24011400E4F9B1 /* Item.swift */; };
A920C28E2D24011A00E4F9B1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A920C28D2D24011A00E4F9B1 /* Assets.xcassets */; }; A920C28E2D24011A00E4F9B1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A920C28D2D24011A00E4F9B1 /* Assets.xcassets */; };
A920C2922D24011A00E4F9B1 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A920C2912D24011A00E4F9B1 /* Preview Assets.xcassets */; }; A920C2922D24011A00E4F9B1 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A920C2912D24011A00E4F9B1 /* Preview Assets.xcassets */; };
A920C2B82D2401A300E4F9B1 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A920C2B42D2401A100E4F9B1 /* SettingsView.swift */; };
A920C2BB2D2401A400E4F9B1 /* AddEventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A920C2B72D2401A300E4F9B1 /* AddEventView.swift */; };
A920C2BE2D24021A00E4F9B1 /* SFSymbolsPicker in Frameworks */ = {isa = PBXBuildFile; productRef = A920C2BD2D24021A00E4F9B1 /* SFSymbolsPicker */; }; A920C2BE2D24021A00E4F9B1 /* SFSymbolsPicker in Frameworks */ = {isa = PBXBuildFile; productRef = A920C2BD2D24021A00E4F9B1 /* SFSymbolsPicker */; };
A920C2C12D2403CA00E4F9B1 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A920C2C02D2403CA00E4F9B1 /* ContentView.swift */; }; A949F8322DCAAA8A0064DCA0 /* NearFutureIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = A949F8312DCAAA8A0064DCA0 /* NearFutureIcon.png */; };
A93BC0942D2B18A3002E8BBD /* StatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A93BC0932D2B18A3002E8BBD /* StatsView.swift */; }; A949F84B2DCAABE00064DCA0 /* ArchiveView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949F83A2DCAABE00064DCA0 /* ArchiveView.swift */; };
A973B26C2DC551310028F8A2 /* ImportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A973B26B2DC551310028F8A2 /* ImportView.swift */; }; A949F84C2DCAABE00064DCA0 /* AddEventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949F83C2DCAABE00064DCA0 /* AddEventView.swift */; };
A973B2702DC552EB0028F8A2 /* ExportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A973B26F2DC552EB0028F8A2 /* ExportView.swift */; }; A949F84D2DCAABE00064DCA0 /* EditEventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949F83D2DCAABE00064DCA0 /* EditEventView.swift */; };
A973B2712DC553050028F8A2 /* ExportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A973B26F2DC552EB0028F8A2 /* ExportView.swift */; }; A949F84E2DCAABE00064DCA0 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949F83F2DCAABE00064DCA0 /* ContentView.swift */; };
A977CC922DBBB48000DED8C0 /* ArchiveView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A977CC912DBBB48000DED8C0 /* ArchiveView.swift */; }; A949F84F2DCAABE00064DCA0 /* EventListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949F8402DCAABE00064DCA0 /* EventListView.swift */; };
A977CC9A2DBD74FE00DED8C0 /* HelpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A977CC992DBD74FE00DED8C0 /* HelpView.swift */; }; A949F8502DCAABE00064DCA0 /* HelpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949F8412DCAABE00064DCA0 /* HelpView.swift */; };
A979F57F2D26B1300094C0B3 /* EditEventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A979F57E2D26B1300094C0B3 /* EditEventView.swift */; }; A949F8512DCAABE00064DCA0 /* ExportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949F8432DCAABE00064DCA0 /* ExportView.swift */; };
A949F8522DCAABE00064DCA0 /* iCloudSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949F8442DCAABE00064DCA0 /* iCloudSettingsView.swift */; };
A949F8532DCAABE00064DCA0 /* ImportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949F8452DCAABE00064DCA0 /* ImportView.swift */; };
A949F8542DCAABE00064DCA0 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949F8462DCAABE00064DCA0 /* SettingsView.swift */; };
A949F8552DCAABE00064DCA0 /* StatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949F8482DCAABE00064DCA0 /* StatsView.swift */; };
A949F8562DCAABE00064DCA0 /* ExportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949F8432DCAABE00064DCA0 /* ExportView.swift */; };
A949F8592DCAAD670064DCA0 /* NearFutureTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949F8572DCAAD670064DCA0 /* NearFutureTests.swift */; };
A949F85F2DCABB420064DCA0 /* Buttons.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949F85D2DCABB420064DCA0 /* Buttons.swift */; };
A979F6052D270AF00094C0B3 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A979F6042D270AF00094C0B3 /* WidgetKit.framework */; }; A979F6052D270AF00094C0B3 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A979F6042D270AF00094C0B3 /* WidgetKit.framework */; };
A979F6072D270AF00094C0B3 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A979F6062D270AF00094C0B3 /* SwiftUI.framework */; }; A979F6072D270AF00094C0B3 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A979F6062D270AF00094C0B3 /* SwiftUI.framework */; };
A979F60A2D270AF00094C0B3 /* NearFutureWidgetsBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = A979F6092D270AF00094C0B3 /* NearFutureWidgetsBundle.swift */; }; A979F60A2D270AF00094C0B3 /* NearFutureWidgetsBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = A979F6092D270AF00094C0B3 /* NearFutureWidgetsBundle.swift */; };
@@ -31,8 +34,6 @@
A979F6102D270AF90094C0B3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A979F60F2D270AF80094C0B3 /* Assets.xcassets */; }; A979F6102D270AF90094C0B3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A979F60F2D270AF80094C0B3 /* Assets.xcassets */; };
A979F6142D270AF90094C0B3 /* NearFutureWidgetsExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = A979F6022D270AF00094C0B3 /* NearFutureWidgetsExtension.appex */; platformFilter = ios; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; A979F6142D270AF90094C0B3 /* NearFutureWidgetsExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = A979F6022D270AF00094C0B3 /* NearFutureWidgetsExtension.appex */; platformFilter = ios; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
A979F6182D2714310094C0B3 /* Item.swift in Sources */ = {isa = PBXBuildFile; fileRef = A920C28B2D24011400E4F9B1 /* Item.swift */; }; A979F6182D2714310094C0B3 /* Item.swift in Sources */ = {isa = PBXBuildFile; fileRef = A920C28B2D24011400E4F9B1 /* Item.swift */; };
A985104E2DB256430013D5FF /* iCloudSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A985104D2DB256430013D5FF /* iCloudSettingsView.swift */; };
A98510502DB263F00013D5FF /* EventListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A985104F2DB263F00013D5FF /* EventListView.swift */; };
A9FC7EEA2D2823920020D75B /* NearFutureWidgets.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9FC7EE92D28238A0020D75B /* NearFutureWidgets.swift */; }; A9FC7EEA2D2823920020D75B /* NearFutureWidgets.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9FC7EE92D28238A0020D75B /* NearFutureWidgets.swift */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
@@ -69,22 +70,29 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
A90FDE222DC0D4310012790C /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = "<group>"; }; A90FDE222DC0D4310012790C /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = "<group>"; };
A9111B1A2DCA549600D4F793 /* NearFutureIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = NearFutureIcon.png; path = NearFuture/Assets.xcassets/AppIcon.appiconset/NearFutureIcon.png; sourceTree = SOURCE_ROOT; };
A920C2842D24011400E4F9B1 /* NearFuture.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NearFuture.app; sourceTree = BUILT_PRODUCTS_DIR; }; 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 = "<group>"; }; A920C2872D24011400E4F9B1 /* NearFutureApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearFutureApp.swift; sourceTree = "<group>"; };
A920C28B2D24011400E4F9B1 /* Item.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Item.swift; sourceTree = "<group>"; }; A920C28B2D24011400E4F9B1 /* Item.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Item.swift; sourceTree = "<group>"; };
A920C28D2D24011A00E4F9B1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; A920C28D2D24011A00E4F9B1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
A920C28F2D24011A00E4F9B1 /* NearFuture.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NearFuture.entitlements; sourceTree = "<group>"; }; A920C28F2D24011A00E4F9B1 /* NearFuture.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NearFuture.entitlements; sourceTree = "<group>"; };
A920C2912D24011A00E4F9B1 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; }; A920C2912D24011A00E4F9B1 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
A920C2B42D2401A100E4F9B1 /* SettingsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; }; A949F82E2DCAAA640064DCA0 /* NearFutureIcon.pxd */ = {isa = PBXFileReference; lastKnownFileType = file; path = NearFutureIcon.pxd; sourceTree = "<group>"; };
A920C2B72D2401A300E4F9B1 /* AddEventView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddEventView.swift; sourceTree = "<group>"; }; A949F82F2DCAAA640064DCA0 /* NearFutureIconDark.pxd */ = {isa = PBXFileReference; lastKnownFileType = file; path = NearFutureIconDark.pxd; sourceTree = "<group>"; };
A920C2C02D2403CA00E4F9B1 /* ContentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; }; A949F8302DCAAA640064DCA0 /* NearFutureIconTint.pxd */ = {isa = PBXFileReference; lastKnownFileType = file; path = NearFutureIconTint.pxd; sourceTree = "<group>"; };
A93BC0932D2B18A3002E8BBD /* StatsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsView.swift; sourceTree = "<group>"; }; A949F8312DCAAA8A0064DCA0 /* NearFutureIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = NearFutureIcon.png; path = Assets.xcassets/AppIcon.appiconset/NearFutureIcon.png; sourceTree = "<group>"; };
A973B26B2DC551310028F8A2 /* ImportView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportView.swift; sourceTree = "<group>"; }; A949F83A2DCAABE00064DCA0 /* ArchiveView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArchiveView.swift; sourceTree = "<group>"; };
A973B26F2DC552EB0028F8A2 /* ExportView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExportView.swift; sourceTree = "<group>"; }; A949F83C2DCAABE00064DCA0 /* AddEventView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddEventView.swift; sourceTree = "<group>"; };
A977CC912DBBB48000DED8C0 /* ArchiveView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArchiveView.swift; sourceTree = "<group>"; }; A949F83D2DCAABE00064DCA0 /* EditEventView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditEventView.swift; sourceTree = "<group>"; };
A977CC992DBD74FE00DED8C0 /* HelpView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HelpView.swift; sourceTree = "<group>"; }; A949F83F2DCAABE00064DCA0 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
A979F57E2D26B1300094C0B3 /* EditEventView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditEventView.swift; sourceTree = "<group>"; }; A949F8402DCAABE00064DCA0 /* EventListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventListView.swift; sourceTree = "<group>"; };
A949F8412DCAABE00064DCA0 /* HelpView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HelpView.swift; sourceTree = "<group>"; };
A949F8432DCAABE00064DCA0 /* ExportView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExportView.swift; sourceTree = "<group>"; };
A949F8442DCAABE00064DCA0 /* iCloudSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iCloudSettingsView.swift; sourceTree = "<group>"; };
A949F8452DCAABE00064DCA0 /* ImportView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportView.swift; sourceTree = "<group>"; };
A949F8462DCAABE00064DCA0 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
A949F8482DCAABE00064DCA0 /* StatsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsView.swift; sourceTree = "<group>"; };
A949F8572DCAAD670064DCA0 /* NearFutureTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearFutureTests.swift; sourceTree = "<group>"; };
A949F85D2DCABB420064DCA0 /* Buttons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Buttons.swift; sourceTree = "<group>"; };
A979F58B2D2700680094C0B3 /* NearFutureWidgetsBundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearFutureWidgetsBundle.swift; sourceTree = "<group>"; }; A979F58B2D2700680094C0B3 /* NearFutureWidgetsBundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearFutureWidgetsBundle.swift; sourceTree = "<group>"; };
A979F58D2D2700680094C0B3 /* NearFutureWidgetsLiveActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearFutureWidgetsLiveActivity.swift; sourceTree = "<group>"; }; A979F58D2D2700680094C0B3 /* NearFutureWidgetsLiveActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearFutureWidgetsLiveActivity.swift; sourceTree = "<group>"; };
A979F58F2D2700680094C0B3 /* NearFutureWidgets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearFutureWidgets.swift; sourceTree = "<group>"; }; A979F58F2D2700680094C0B3 /* NearFutureWidgets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearFutureWidgets.swift; sourceTree = "<group>"; };
@@ -100,16 +108,10 @@
A979F6112D270AF90094C0B3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; A979F6112D270AF90094C0B3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
A980FC302D920097006A778F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; A980FC302D920097006A778F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
A980FC372D93FB2B006A778F /* NearFutureTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NearFutureTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; A980FC372D93FB2B006A778F /* NearFutureTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NearFutureTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
A985104D2DB256430013D5FF /* iCloudSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iCloudSettingsView.swift; sourceTree = "<group>"; };
A985104F2DB263F00013D5FF /* EventListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventListView.swift; sourceTree = "<group>"; };
A9C05E412D2805D7007DC497 /* NearFutureWidgetsExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NearFutureWidgetsExtension.entitlements; sourceTree = "<group>"; }; A9C05E412D2805D7007DC497 /* NearFutureWidgetsExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NearFutureWidgetsExtension.entitlements; sourceTree = "<group>"; };
A9FC7EE92D28238A0020D75B /* NearFutureWidgets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearFutureWidgets.swift; sourceTree = "<group>"; }; A9FC7EE92D28238A0020D75B /* NearFutureWidgets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearFutureWidgets.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFileSystemSynchronizedRootGroup section */
A980FC382D93FB2B006A778F /* NearFutureTests */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = NearFutureTests; sourceTree = "<group>"; };
/* End PBXFileSystemSynchronizedRootGroup section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
A920C2812D24011300E4F9B1 /* Frameworks */ = { A920C2812D24011300E4F9B1 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
@@ -138,22 +140,14 @@
/* End PBXFrameworksBuildPhase section */ /* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */ /* Begin PBXGroup section */
A9111B192DCA548800D4F793 /* Resources */ = {
isa = PBXGroup;
children = (
A9111B1A2DCA549600D4F793 /* NearFutureIcon.png */,
);
path = Resources;
sourceTree = "<group>";
};
A920C27B2D24011300E4F9B1 = { A920C27B2D24011300E4F9B1 = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
A90FDE222DC0D4310012790C /* Config.xcconfig */, A90FDE222DC0D4310012790C /* Config.xcconfig */,
A9111B192DCA548800D4F793 /* Resources */, A949F8002DCAA0340064DCA0 /* Resources */,
A920C2862D24011400E4F9B1 /* NearFuture */, A920C2862D24011400E4F9B1 /* NearFuture */,
A979F6082D270AF00094C0B3 /* NearFutureWidgets */, A979F6082D270AF00094C0B3 /* NearFutureWidgets */,
A980FC382D93FB2B006A778F /* NearFutureTests */, A949F8582DCAAD670064DCA0 /* NearFutureTests */,
A979F6032D270AF00094C0B3 /* Frameworks */, A979F6032D270AF00094C0B3 /* Frameworks */,
A920C2852D24011400E4F9B1 /* Products */, A920C2852D24011400E4F9B1 /* Products */,
); );
@@ -174,19 +168,8 @@
children = ( children = (
A920C2872D24011400E4F9B1 /* NearFutureApp.swift */, A920C2872D24011400E4F9B1 /* NearFutureApp.swift */,
A920C28B2D24011400E4F9B1 /* Item.swift */, A920C28B2D24011400E4F9B1 /* Item.swift */,
A920C2C02D2403CA00E4F9B1 /* ContentView.swift */, A949F84A2DCAABE00064DCA0 /* Views */,
A985104F2DB263F00013D5FF /* EventListView.swift */,
A920C2B72D2401A300E4F9B1 /* AddEventView.swift */,
A979F57E2D26B1300094C0B3 /* EditEventView.swift */,
A977CC912DBBB48000DED8C0 /* ArchiveView.swift */,
A93BC0932D2B18A3002E8BBD /* StatsView.swift */,
A977CC992DBD74FE00DED8C0 /* HelpView.swift */,
A920C2B42D2401A100E4F9B1 /* SettingsView.swift */,
A973B26B2DC551310028F8A2 /* ImportView.swift */,
A973B26F2DC552EB0028F8A2 /* ExportView.swift */,
A985104D2DB256430013D5FF /* iCloudSettingsView.swift */,
A980FC302D920097006A778F /* Info.plist */, A980FC302D920097006A778F /* Info.plist */,
A920C28D2D24011A00E4F9B1 /* Assets.xcassets */,
A920C28F2D24011A00E4F9B1 /* NearFuture.entitlements */, A920C28F2D24011A00E4F9B1 /* NearFuture.entitlements */,
A920C2902D24011A00E4F9B1 /* Preview Content */, A920C2902D24011A00E4F9B1 /* Preview Content */,
); );
@@ -202,6 +185,93 @@
path = "Preview Content"; path = "Preview Content";
sourceTree = "<group>"; sourceTree = "<group>";
}; };
A949F8002DCAA0340064DCA0 /* Resources */ = {
isa = PBXGroup;
children = (
A949F8312DCAAA8A0064DCA0 /* NearFutureIcon.png */,
A920C28D2D24011A00E4F9B1 /* Assets.xcassets */,
A949F82E2DCAAA640064DCA0 /* NearFutureIcon.pxd */,
A949F82F2DCAAA640064DCA0 /* NearFutureIconDark.pxd */,
A949F8302DCAAA640064DCA0 /* NearFutureIconTint.pxd */,
);
path = Resources;
sourceTree = "<group>";
};
A949F83B2DCAABE00064DCA0 /* Archive */ = {
isa = PBXGroup;
children = (
A949F83A2DCAABE00064DCA0 /* ArchiveView.swift */,
);
path = Archive;
sourceTree = "<group>";
};
A949F83E2DCAABE00064DCA0 /* Events */ = {
isa = PBXGroup;
children = (
A949F83C2DCAABE00064DCA0 /* AddEventView.swift */,
A949F83D2DCAABE00064DCA0 /* EditEventView.swift */,
);
path = Events;
sourceTree = "<group>";
};
A949F8422DCAABE00064DCA0 /* Home */ = {
isa = PBXGroup;
children = (
A949F83F2DCAABE00064DCA0 /* ContentView.swift */,
A949F8402DCAABE00064DCA0 /* EventListView.swift */,
A949F8412DCAABE00064DCA0 /* HelpView.swift */,
);
path = Home;
sourceTree = "<group>";
};
A949F8472DCAABE00064DCA0 /* Settings */ = {
isa = PBXGroup;
children = (
A949F8432DCAABE00064DCA0 /* ExportView.swift */,
A949F8442DCAABE00064DCA0 /* iCloudSettingsView.swift */,
A949F8452DCAABE00064DCA0 /* ImportView.swift */,
A949F8462DCAABE00064DCA0 /* SettingsView.swift */,
);
path = Settings;
sourceTree = "<group>";
};
A949F8492DCAABE00064DCA0 /* Stats */ = {
isa = PBXGroup;
children = (
A949F8482DCAABE00064DCA0 /* StatsView.swift */,
);
path = Stats;
sourceTree = "<group>";
};
A949F84A2DCAABE00064DCA0 /* Views */ = {
isa = PBXGroup;
children = (
A949F83B2DCAABE00064DCA0 /* Archive */,
A949F83E2DCAABE00064DCA0 /* Events */,
A949F8422DCAABE00064DCA0 /* Home */,
A949F8472DCAABE00064DCA0 /* Settings */,
A949F8492DCAABE00064DCA0 /* Stats */,
A949F85E2DCABB420064DCA0 /* Misc */,
);
path = Views;
sourceTree = "<group>";
};
A949F8582DCAAD670064DCA0 /* NearFutureTests */ = {
isa = PBXGroup;
children = (
A949F8572DCAAD670064DCA0 /* NearFutureTests.swift */,
);
path = NearFutureTests;
sourceTree = "<group>";
};
A949F85E2DCABB420064DCA0 /* Misc */ = {
isa = PBXGroup;
children = (
A949F85D2DCABB420064DCA0 /* Buttons.swift */,
);
path = Misc;
sourceTree = "<group>";
};
A979F58A2D2700680094C0B3 /* NearFutureWidgets */ = { A979F58A2D2700680094C0B3 /* NearFutureWidgets */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -292,9 +362,6 @@
dependencies = ( dependencies = (
A980FC3C2D93FB2B006A778F /* PBXTargetDependency */, A980FC3C2D93FB2B006A778F /* PBXTargetDependency */,
); );
fileSystemSynchronizedGroups = (
A980FC382D93FB2B006A778F /* NearFutureTests */,
);
name = NearFutureTests; name = NearFutureTests;
packageProductDependencies = ( packageProductDependencies = (
); );
@@ -353,7 +420,7 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
A920C2922D24011A00E4F9B1 /* Preview Assets.xcassets in Resources */, A920C2922D24011A00E4F9B1 /* Preview Assets.xcassets in Resources */,
A9111B1C2DCA549600D4F793 /* NearFutureIcon.png in Resources */, A949F8322DCAAA8A0064DCA0 /* NearFutureIcon.png in Resources */,
A920C28E2D24011A00E4F9B1 /* Assets.xcassets in Resources */, A920C28E2D24011A00E4F9B1 /* Assets.xcassets in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
@@ -363,7 +430,6 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
A979F6102D270AF90094C0B3 /* Assets.xcassets in Resources */, A979F6102D270AF90094C0B3 /* Assets.xcassets in Resources */,
A9111B1B2DCA549600D4F793 /* NearFutureIcon.png in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -381,19 +447,20 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
A920C2BB2D2401A400E4F9B1 /* AddEventView.swift in Sources */,
A973B26C2DC551310028F8A2 /* ImportView.swift in Sources */,
A98510502DB263F00013D5FF /* EventListView.swift in Sources */,
A920C2C12D2403CA00E4F9B1 /* ContentView.swift in Sources */,
A973B2712DC553050028F8A2 /* ExportView.swift in Sources */,
A920C2B82D2401A300E4F9B1 /* SettingsView.swift in Sources */,
A977CC9A2DBD74FE00DED8C0 /* HelpView.swift in Sources */,
A920C28C2D24011400E4F9B1 /* Item.swift in Sources */, A920C28C2D24011400E4F9B1 /* Item.swift in Sources */,
A949F84B2DCAABE00064DCA0 /* ArchiveView.swift in Sources */,
A949F84C2DCAABE00064DCA0 /* AddEventView.swift in Sources */,
A949F84D2DCAABE00064DCA0 /* EditEventView.swift in Sources */,
A949F84E2DCAABE00064DCA0 /* ContentView.swift in Sources */,
A949F84F2DCAABE00064DCA0 /* EventListView.swift in Sources */,
A949F8502DCAABE00064DCA0 /* HelpView.swift in Sources */,
A949F85F2DCABB420064DCA0 /* Buttons.swift in Sources */,
A949F8512DCAABE00064DCA0 /* ExportView.swift in Sources */,
A949F8522DCAABE00064DCA0 /* iCloudSettingsView.swift in Sources */,
A949F8532DCAABE00064DCA0 /* ImportView.swift in Sources */,
A949F8542DCAABE00064DCA0 /* SettingsView.swift in Sources */,
A949F8552DCAABE00064DCA0 /* StatsView.swift in Sources */,
A920C2882D24011400E4F9B1 /* NearFutureApp.swift in Sources */, A920C2882D24011400E4F9B1 /* NearFutureApp.swift in Sources */,
A93BC0942D2B18A3002E8BBD /* StatsView.swift in Sources */,
A977CC922DBBB48000DED8C0 /* ArchiveView.swift in Sources */,
A979F57F2D26B1300094C0B3 /* EditEventView.swift in Sources */,
A985104E2DB256430013D5FF /* iCloudSettingsView.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -412,7 +479,8 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
A973B2702DC552EB0028F8A2 /* ExportView.swift in Sources */, A949F8592DCAAD670064DCA0 /* NearFutureTests.swift in Sources */,
A949F8562DCAABE00064DCA0 /* ExportView.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };

View File

@@ -104,6 +104,7 @@
savedToolIdentifier = "" savedToolIdentifier = ""
useCustomWorkingDirectory = "NO" useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES" debugDocumentVersioning = "YES"
askForAppToLaunch = "Yes"
launchAutomaticallySubstyle = "2"> launchAutomaticallySubstyle = "2">
<BuildableProductRunnable <BuildableProductRunnable
runnableDebuggingMode = "0"> runnableDebuggingMode = "0">

View File

@@ -9,6 +9,7 @@ import Foundation
import SwiftData import SwiftData
import SwiftUI import SwiftUI
import WidgetKit import WidgetKit
import UserNotifications
//@Model //@Model
//final class Item { //final class Item {
@@ -113,6 +114,7 @@ class SettingsViewModel: ObservableObject {
showCompletedInHome: false, showCompletedInHome: false,
tint: ColorCodable(uiColor: UIColor(named: "AccentColor")!) tint: ColorCodable(uiColor: UIColor(named: "AccentColor")!)
) )
@Published var notifsGranted: Bool = false
@Published var accentChoices: [Color] = [ @Published var accentChoices: [Color] = [
Color(UIColor(named: "uiColors/red")!), Color(UIColor(named: "uiColors/red")!),
@@ -127,6 +129,12 @@ class SettingsViewModel: ObservableObject {
init(load: Bool = true) { init(load: Bool = true) {
if load { if load {
loadSettings() loadSettings()
Task {
let requestResult = await requestNotifs()
await MainActor.run {
self.notifsGranted = requestResult
}
}
} }
} }
@@ -221,6 +229,43 @@ class EventViewModel: ObservableObject {
updateSyncStatus() updateSyncStatus()
} }
func getNotifs() async -> [UNNotificationRequest] {
return await UNUserNotificationCenter.current().pendingNotificationRequests()
}
func checkPendingNotifs(_ pending: [UNNotificationRequest]) {
var eventUUIDs = events.map({$0.id.uuidString})
for req in pending {
//match the notif to an event
if let index = events.firstIndex(where: {$0.id.uuidString == req.identifier}) {
if let remove = eventUUIDs.firstIndex(where: {$0 == req.identifier}) {
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 {
cancelNotif(req.identifier)
}
} else {
cancelNotif(req.identifier)
scheduleEventNotif(events[index])
}
} else {
//cancel if the event is deleted
cancelNotif(req.identifier)
}
}
for uuid in eventUUIDs {
if let event = events.first(where: {$0.id.uuidString == uuid}) {
scheduleEventNotif(event)
}
}
}
// save to local and icloud // save to local and icloud
func saveEvents() { func saveEvents() {
let encoder = JSONEncoder() let encoder = JSONEncoder()
@@ -233,6 +278,9 @@ class EventViewModel: ObservableObject {
updateSyncStatus() updateSyncStatus()
loadEvents() loadEvents()
Task {
await checkPendingNotifs(getNotifs())
}
WidgetCenter.shared.reloadAllTimelines()//reload all widgets when saving events WidgetCenter.shared.reloadAllTimelines()//reload all widgets when saving events
objectWillChange.send() objectWillChange.send()
} }
@@ -252,6 +300,7 @@ class EventViewModel: ObservableObject {
func addEvent(newEvent: Event) { func addEvent(newEvent: Event) {
events.append(newEvent) events.append(newEvent)
scheduleEventNotif(newEvent)
saveEvents() //sync with icloud saveEvents() //sync with icloud
} }
@@ -325,6 +374,7 @@ class EventViewModel: ObservableObject {
UserDefaults.standard.removeObject(forKey: "events") UserDefaults.standard.removeObject(forKey: "events")
appGroupUserDefaults.removeObject(forKey: "events") appGroupUserDefaults.removeObject(forKey: "events")
events.removeAll() events.removeAll()
cancelAllNotifs()
updateSyncStatus() updateSyncStatus()
} }
@@ -332,6 +382,7 @@ class EventViewModel: ObservableObject {
icloudStore.removeObject(forKey: "events") icloudStore.removeObject(forKey: "events")
icloudStore.synchronize() icloudStore.synchronize()
icloudData.removeAll() icloudData.removeAll()
cancelAllNotifs()
updateSyncStatus() updateSyncStatus()
} }
@@ -347,6 +398,7 @@ class EventViewModel: ObservableObject {
} }
events.removeAll() events.removeAll()
cancelAllNotifs()
updateSyncStatus() updateSyncStatus()
} }
@@ -357,6 +409,7 @@ class EventViewModel: ObservableObject {
} }
icloudStore.synchronize() icloudStore.synchronize()
icloudData.removeAll() icloudData.removeAll()
cancelAllNotifs()
updateSyncStatus() updateSyncStatus()
} }
} }
@@ -423,3 +476,43 @@ func plu(_ inp: Int) -> String {
public enum importError: Error { public enum importError: Error {
case invalidB64 case invalidB64
} }
func requestNotifs() async -> Bool {
let result = try? await UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .carPlay, .sound])
return result ?? false
}
func scheduleNotif(title: String, sub: String, date: Date, id: String = UUID().uuidString) {
let content = UNMutableNotificationContent()
content.title = title
content.subtitle = sub
content.sound = .default
let identifier = id
let dateComponents = getDateComponents(date)
let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: false)
let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request)
}
func scheduleEventNotif(_ event: Event) {
scheduleNotif(
title: event.name,
sub: event.notes,
date: event.date,
id: event.id.uuidString
)
}
func getDateComponents(_ date: Date) -> DateComponents {
return Calendar.current.dateComponents([.year, .month, .day, .hour, .minute], from: date)
}
func cancelNotif(_ id: String) {
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [id])
}
func cancelAllNotifs() {
UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
}

View File

@@ -2,6 +2,10 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>aps-environment</key>
<string>development</string>
<key>com.apple.developer.aps-environment</key>
<string>development</string>
<key>com.apple.developer.icloud-container-identifiers</key> <key>com.apple.developer.icloud-container-identifiers</key>
<array/> <array/>
<key>com.apple.developer.ubiquity-kvstore-identifier</key> <key>com.apple.developer.ubiquity-kvstore-identifier</key>

View File

@@ -59,6 +59,7 @@ struct AddEventView: View {
title: "Choose a Symbol", title: "Choose a Symbol",
searchLabel: "Search...", searchLabel: "Search...",
autoDismiss: true) autoDismiss: true)
.presentationDetents([.medium])
.apply { .apply {
if #available(iOS 16.4, *) { if #available(iOS 16.4, *) {
$0.presentationBackground(.ultraThinMaterial) $0.presentationBackground(.ultraThinMaterial)
@@ -214,26 +215,6 @@ struct AddEventView: View {
} }
} }
struct MagicClearButton: View {
@Binding var text: String
var body: some View {
HStack {
Spacer()
Button {
text = ""
} label: {
Image(systemName: "xmark.circle.fill")
.resizable()
.scaledToFit()
.frame(width: text.isEmpty ? 0 : 25)
.symbolRenderingMode(.hierarchical)
.padding(.trailing, -5)
.animation(.spring, value: text.isEmpty)
}
.buttonStyle(.borderless)
}
}
}
#Preview { #Preview {
let vm = dummyEventViewModel() let vm = dummyEventViewModel()

View File

@@ -6,6 +6,7 @@
// //
import SwiftUI import SwiftUI
import UserNotifications
import SwiftData import SwiftData
enum Field { enum Field {
@@ -54,7 +55,7 @@ struct ContentView: View {
@FocusState private var focusedField: Field? @FocusState private var focusedField: Field?
@FocusState private var focusedTab: Tab? @FocusState private var focusedTab: Tab?
var body: some View { var body: some View {
TabView { TabView {
NavigationStack { NavigationStack {
@@ -62,17 +63,25 @@ struct ContentView: View {
backgroundGradient backgroundGradient
VStack { VStack {
ZStack { ZStack {
SearchBar(searchInput: $searchInput) TextField(
.focused($focusedField, equals: Field.Search) "\(Image(systemName: "magnifyingglass")) Search",
.onSubmit { text: $searchInput
focusedField = nil )
} .padding(.trailing, searchInput.isEmpty ? 0 : 30)
.animation(.spring, value: searchInput)
.textFieldStyle(RoundedBorderTextFieldStyle())
.submitLabel(.done)
.focused($focusedField, equals: Field.Search)
.onSubmit {
focusedField = nil
}
MagicClearButton(text: $searchInput) MagicClearButton(text: $searchInput)
.onTapGesture { .onTapGesture {
focusedField = nil focusedField = nil
} }
} }
.padding(.horizontal) .padding(.horizontal)
if filteredEvents.isEmpty && !searchInput.isEmpty { if filteredEvents.isEmpty && !searchInput.isEmpty {
HelpView(searchInput: $searchInput, focusedField: focusedField) HelpView(searchInput: $searchInput, focusedField: focusedField)
} else { } else {
@@ -161,42 +170,6 @@ struct ContentView: View {
) )
} }
struct SearchBar: View {
@Binding var searchInput: String
var body: some View {
TextField(
"\(Image(systemName: "magnifyingglass")) Search",
text: $searchInput
)
.padding(.trailing, searchInput.isEmpty ? 0 : 30)
.animation(.spring, value: searchInput)
.textFieldStyle(RoundedBorderTextFieldStyle())
.submitLabel(.done)
}
}
struct AddEventButton: View {
@Binding var showingAddEventView: Bool
var body: some View {
Button() {
showingAddEventView.toggle()
} label: {
ZStack {
Circle()
.frame(width: 33)
.foregroundStyle(.one)
Image(systemName: "plus")
.resizable()
.scaledToFit()
.frame(width: 15)
.bold()
.foregroundStyle(.two)
}
}
}
}
extension View { extension View {
var appearance: ColorScheme { var appearance: ColorScheme {
return UITraitCollection.current.userInterfaceStyle == .dark ? .dark : .light return UITraitCollection.current.userInterfaceStyle == .dark ? .dark : .light

View File

@@ -0,0 +1,169 @@
//
// EventListView.swift
// NearFuture
//
// Created by neon443 on 18/04/2025.
//
import SwiftUI
import SwiftData
struct EventListView: View {
@ObservedObject var viewModel: EventViewModel
@State var event: Event
var body: some View {
NavigationLink() {
EditEventView(
viewModel: viewModel,
event: $event
)
} label: {
ZStack {
HStack {
RoundedRectangle(cornerRadius: 5)
.frame(width: 7)
.foregroundStyle(
event.color.color.opacity(
event.complete ? 0.5 : 1
)
)
VStack(alignment: .leading) {
HStack {
Image(systemName: event.symbol)
.resizable()
.scaledToFit()
.frame(width: 20, height: 20)
.shadow(radius: 5)
.foregroundStyle(
.one.opacity(
event.complete ? 0.5 : 1
)
)
Text("\(event.name)")
.font(.headline)
.foregroundStyle(.one)
.strikethrough(event.complete)
.multilineTextAlignment(.leading)
}
if !event.notes.isEmpty {
Text(event.notes)
.font(.subheadline)
.foregroundStyle(.one.opacity(0.8))
.multilineTextAlignment(.leading)
}
Text(
event.date.formatted(
date: .long,
time: .shortened
)
)
.font(.subheadline)
.foregroundStyle(
.one.opacity(
event.complete ? 0.5 : 1
)
)
if event.recurrence != .none {
Text("Occurs \(event.recurrence.rawValue)")
.font(.subheadline)
.foregroundStyle(
.one.opacity(event.complete ? 0.5 : 1))
}
}
Spacer()
VStack {
Text("\(daysUntilEvent(event.date).long)")
.font(.subheadline)
.foregroundStyle(.one)
}
Button() {
withAnimation {
event.complete.toggle()
}
let eventToModify = viewModel.events.firstIndex() { currEvent in
currEvent.id == event.id
}
if let eventToModify = eventToModify {
viewModel.events[eventToModify] = event
viewModel.saveEvents()
}
} label: {
if event.complete {
ZStack {
Circle()
.foregroundStyle(.green)
Image(systemName: "checkmark")
.resizable()
.foregroundStyle(.white)
.scaledToFit()
.bold()
.frame(width: 15)
}
} else {
Image(systemName: "circle")
.resizable()
.scaledToFit()
.foregroundStyle(event.color.color)
}
}
.buttonStyle(.borderless)
.frame(maxWidth: 25, maxHeight: 25)
.shadow(radius: 5)
.padding(.trailing, 5)
}
.padding(.vertical, 5)
.background(.ultraThinMaterial)
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(
.one.opacity(appearance == .dark ? 0.5 : 1),
lineWidth: 1
)
)
.clipShape(
RoundedRectangle(cornerRadius: 10)
)
.fixedSize(horizontal: false, vertical: true)
}
.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")
}
}
}
}
}
#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(
viewModel: vm,
event: event
)
}
}
.padding(.horizontal, 10)
}
}

View File

@@ -0,0 +1,112 @@
//
// ArchiveHelp.swift
// NearFuture
//
// Created by neon443 on 26/04/2025.
//
import SwiftUI
enum HelpType {
case Search
case Archive
}
struct HelpView: View {
/// initialises a Search HelpView
///
init(searchInput: Binding<String>, focusedField: Field?) {
_searchInput = searchInput
self.helpType = .Search
_showAddEvent = .constant(false)
}
/// initialises an Archive HelpView
///
init(showAddEvent: Binding<Bool>) {
_showAddEvent = showAddEvent
self.helpType = .Archive
_searchInput = .constant("")
self.focusedField = nil
}
@Binding var searchInput: String
@FocusState var focusedField: Field?
@Binding var showAddEvent: Bool
var helpType: HelpType
var details: (
symbol: String,
title: String,
body: String,
buttonAction: () -> (),
buttonSymbol: String,
buttonText: String
) {
switch helpType {
case .Search:
return (
symbol: "questionmark.app.dashed",
title: "Looking for something?",
body: "Tip: The Search bar searches event names and notes.",
buttonAction: {
searchInput = ""
focusedField = nil
},
buttonSymbol: "xmark",
buttonText: "Clear Filters"
)
case .Archive:
return (
symbol: "eyes",
title: "Nothing to see here...",
body: "The Archive contains events that have been marked as complete.",
buttonAction: {
showAddEvent.toggle()
},
buttonSymbol: "plus",
buttonText: "Create an event"
)
}
}
var body: some View {
List {
ZStack {
Color(.tintColor)
.opacity(0.4)
.padding(.horizontal, -15)
.blur(radius: 5)
HStack {
Image(systemName: details.symbol)
.resizable()
.scaledToFit()
.frame(width: 30, height: 30)
.padding(.trailing)
Text(details.title)
.bold()
.font(.title2)
}
}
.listRowSeparator(.hidden)
Text(details.body)
Button() {
details.buttonAction()
} label: {
HStack {
Image(systemName: details.buttonSymbol)
.bold()
Text(details.buttonText)
}
.foregroundStyle(Color.accentColor)
}
}
.scrollContentBackground(.hidden)
}
}
#Preview {
HelpView(searchInput: .constant(""), focusedField: nil)
HelpView(showAddEvent: .constant(false))
}

View File

@@ -0,0 +1,58 @@
//
// MagicClearButton.swift
// NearFuture
//
// Created by neon443 on 06/05/2025.
//
import SwiftUI
struct MagicClearButton: View {
@Binding var text: String
var body: some View {
HStack {
Spacer()
Button {
text = ""
} label: {
Image(systemName: "xmark.circle.fill")
.resizable()
.scaledToFit()
.frame(width: text.isEmpty ? 0 : 25)
.symbolRenderingMode(.hierarchical)
.padding(.trailing, -5)
.animation(.spring, value: text.isEmpty)
}
.buttonStyle(.borderless)
}
}
}
struct AddEventButton: View {
@Binding var showingAddEventView: Bool
var body: some View {
Button() {
showingAddEventView.toggle()
} label: {
ZStack {
Circle()
.frame(width: 33)
.foregroundStyle(.one)
Image(systemName: "plus")
.resizable()
.scaledToFit()
.frame(width: 15)
.bold()
.foregroundStyle(.two)
}
}
}
}
#Preview {
MagicClearButton(text: .constant("s"))
}
#Preview {
AddEventButton(showingAddEventView: .constant(false))
}

View File

@@ -67,6 +67,25 @@ struct SettingsView: View {
} }
} }
} }
NavigationLink() {
List {
if !settingsModel.notifsGranted {
Button("Request Notifications") {
Task {
settingsModel.notifsGranted = await requestNotifs()
}
}
Text("\(Image(systemName: "xmark")) Notifications disabled for Near Future")
.foregroundStyle(.red)
} else {
Text("\(Image(systemName: "checkmark")) Notifications enabled for Near Future")
.foregroundStyle(.green)
}
}
} label: {
Image(systemName: "bell.badge.fill")
Text("Notifications")
}
NavigationLink() { NavigationLink() {
iCloudSettingsView( iCloudSettingsView(
viewModel: viewModel, viewModel: viewModel,

View File

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1.0 MiB

View File

Before

Width:  |  Height:  |  Size: 729 KiB

After

Width:  |  Height:  |  Size: 729 KiB

View File

Before

Width:  |  Height:  |  Size: 632 KiB

After

Width:  |  Height:  |  Size: 632 KiB