diff --git a/Config.xcconfig b/Config.xcconfig
index 7118d9a..b3278ca 100644
--- a/Config.xcconfig
+++ b/Config.xcconfig
@@ -12,6 +12,6 @@ TEAM_ID = 8JGND254B7
BUNDLE_ID = com.neon443.NearFuture
BUNDLE_ID_WIDGETS = com.neon443.NearFuture.widgets
GROUP_ID = group.NearFuture
-VERSION = 4.4.0
+VERSION = 5
NAME = Near Future
BUILD_NUMBER = 1
diff --git a/MacNearFuture/AboutView.swift b/MacNearFuture/AboutView.swift
new file mode 100644
index 0000000..1b3fc9f
--- /dev/null
+++ b/MacNearFuture/AboutView.swift
@@ -0,0 +1,40 @@
+//
+// AboutView.swift
+// MacNearFuture
+//
+// Created by neon443 on 28/05/2025.
+//
+
+import SwiftUI
+
+struct AboutView: View {
+ var body: some View {
+ VStack(alignment: .center) {
+ Image(nsImage: #imageLiteral(resourceName: "NearFutureIcon.png"))
+ .resizable()
+ .scaledToFit()
+ .frame(width: 100)
+ .clipShape(RoundedRectangle(cornerRadius: 25))
+ Text("Near Future")
+ .bold()
+ .monospaced()
+ .font(.title)
+ Text("Version " + getVersion() + " (\(getBuildID()))")
+ .padding(.bottom)
+ Text("© 2024-2025 neon443, Inc")
+ .padding(.bottom)
+ Link("Developer Website", destination: URL(string: "https://neon443.xyz")!)
+ }
+ .padding()
+ .padding()
+ .containerBackground(.ultraThinMaterial, for: .window)
+ .toolbar(removing: .title)
+ .toolbarBackground(.hidden, for: .windowToolbar)
+ .windowMinimizeBehavior(.disabled)
+ .windowFullScreenBehavior(.disabled)
+ }
+}
+
+#Preview {
+ AboutView()
+}
diff --git a/MacNearFuture/MacNearFuture.entitlements b/MacNearFuture/MacNearFuture.entitlements
new file mode 100644
index 0000000..69112c6
--- /dev/null
+++ b/MacNearFuture/MacNearFuture.entitlements
@@ -0,0 +1,18 @@
+
+
+
+
+ com.apple.developer.icloud-container-identifiers
+
+ com.apple.developer.ubiquity-kvstore-identifier
+ $(TeamIdentifierPrefix)$(CFBundleIdentifier)
+ com.apple.security.app-sandbox
+
+ com.apple.security.application-groups
+
+ group.NearFuture
+
+ com.apple.security.files.user-selected.read-only
+
+
+
diff --git a/MacNearFuture/MacNearFutureApp.swift b/MacNearFuture/MacNearFutureApp.swift
new file mode 100644
index 0000000..434bfa1
--- /dev/null
+++ b/MacNearFuture/MacNearFutureApp.swift
@@ -0,0 +1,72 @@
+//
+// MacNearFutureApp.swift
+// MacNearFuture
+//
+// Created by neon443 on 21/05/2025.
+//
+
+import Foundation
+import SwiftUI
+
+@main
+struct NearFutureApp: App {
+ @Environment(\.openWindow) var openWindow
+ @StateObject var viewModel: EventViewModel = EventViewModel()
+ @StateObject var settingsModel: SettingsViewModel = SettingsViewModel()
+
+ var body: some Scene {
+ WindowGroup {
+ ContentView(
+ viewModel: viewModel,
+ settingsModel: settingsModel
+ )
+ }
+ .defaultSize(width: 550, height: 650)
+ .commands {
+ CommandGroup(replacing: CommandGroupPlacement.appInfo) {
+ Button("About Near Future") {
+ openWindow(id: "about")
+ }
+ }
+ NearFutureCommands()
+ }
+
+ WindowGroup("Edit Event", for: Event.ID.self) { $eventID in
+ if viewModel.events.first(where: {$0.id == eventID}) == nil {
+ AddEventView(
+ viewModel: viewModel
+ )
+ } else {
+ EditEventView(
+ viewModel: viewModel,
+ event: Binding(
+ get: {
+ viewModel.events.first(where: {$0.id == eventID}) ?? viewModel.template
+ },
+ set: { newValue in
+ viewModel.editEvent(newValue)
+ }
+ )
+ )
+ }
+ }
+ .defaultSize(width: 480, height: 550)
+ .windowIdealSize(.fitToContent)
+ .restorationBehavior(.disabled)
+
+ Window("About Near Future", id: "about") {
+ AboutView()
+ }
+ .windowBackgroundDragBehavior(.enabled)
+ .windowResizability(.contentSize)
+ .restorationBehavior(.disabled)
+ .defaultPosition(UnitPoint.center)
+
+ Settings {
+ SettingsView(
+ viewModel: viewModel,
+ settingsModel: settingsModel
+ )
+ }
+ }
+}
diff --git a/MacNearFuture/NFCommands.swift b/MacNearFuture/NFCommands.swift
new file mode 100644
index 0000000..29c6083
--- /dev/null
+++ b/MacNearFuture/NFCommands.swift
@@ -0,0 +1,17 @@
+//
+// NFCommands.swift
+// MacNearFuture
+//
+// Created by neon443 on 21/05/2025.
+//
+
+import Foundation
+import SwiftUI
+
+struct NearFutureCommands: Commands {
+ var body: some Commands {
+ CommandGroup(after: CommandGroupPlacement.appInfo) {
+// Text("hi")
+ }
+ }
+}
diff --git a/MacNearFuture/Views/ArchiveView.swift b/MacNearFuture/Views/ArchiveView.swift
new file mode 100644
index 0000000..ac0af6c
--- /dev/null
+++ b/MacNearFuture/Views/ArchiveView.swift
@@ -0,0 +1,33 @@
+//
+// ArchiveView.swift
+// MacNearFuture
+//
+// Created by neon443 on 28/05/2025.
+//
+
+import SwiftUI
+
+struct ArchiveView: View {
+ @StateObject var viewModel: EventViewModel
+ @StateObject var settingsModel: SettingsViewModel
+
+ var filteredEvents: [Event] {
+ return viewModel.events.filter { $0.complete }
+ }
+
+ var body: some View {
+ ScrollView {
+ ForEach(filteredEvents) { event in
+ EventListView(viewModel: viewModel, event: event)
+ }
+ }
+ .scrollContentBackground(.hidden)
+ }
+}
+
+#Preview {
+ ArchiveView(
+ viewModel: dummyEventViewModel(),
+ settingsModel: dummySettingsViewModel()
+ )
+}
diff --git a/MacNearFuture/Views/ContentViewMac.swift b/MacNearFuture/Views/ContentViewMac.swift
new file mode 100644
index 0000000..f4e9684
--- /dev/null
+++ b/MacNearFuture/Views/ContentViewMac.swift
@@ -0,0 +1,88 @@
+//
+// ContentView.swift
+// MacNearFuture
+//
+// Created by neon443 on 21/05/2025.
+//
+
+import SwiftUI
+
+struct ContentView: View {
+ @StateObject var viewModel: EventViewModel
+ @StateObject var settingsModel: SettingsViewModel
+
+ @State private var showAddEventView: Bool = false
+ @State private var symbolSearchInput: String = ""
+
+ var body: some View {
+ NavigationSplitView {
+ List {
+ NavigationLink {
+ HomeView(
+ viewModel: viewModel,
+ settingsModel: settingsModel
+ )
+ } label: {
+ Image(systemName: "house")
+ Text("Home")
+ }
+ NavigationLink {
+ ArchiveView(
+ viewModel: viewModel,
+ settingsModel: settingsModel
+ )
+ } label: {
+ Image(systemName: "tray.full")
+ Text("Archive")
+ }
+ NavigationLink {
+ SymbolsPicker(
+ selection: .constant(""),
+ browsing: true
+ )
+ } label: {
+ Image(systemName: "star.circle")
+ Text("Symbols")
+ }
+ NavigationLink {
+ SettingsView(
+ viewModel: viewModel,
+ settingsModel: settingsModel
+ )
+ } label: {
+ Image(systemName: "gear")
+ Text("Settings")
+ }
+ }
+ } detail: {
+ Text("Welcome to Near Future")
+ }
+ .tint(settingsModel.settings.tint.color)
+ .frame(minWidth: 450, minHeight: 550)
+ .containerBackground(.regularMaterial, for: .window)
+ .sheet(isPresented: $settingsModel.settings.showWhatsNew) {
+ WhatsNewView(settingsModel: settingsModel)
+ .presentationSizing(.form)
+ }
+ .sheet(isPresented: $showAddEventView) {
+ AddEventView(
+ viewModel: viewModel
+ )
+ .presentationSizing(.page)
+ }
+ .toolbar {
+ Button() {
+ showAddEventView.toggle()
+ } label: {
+ Label("New", systemImage: "plus")
+ }
+ }
+ }
+}
+
+#Preview {
+ ContentView(
+ viewModel: dummyEventViewModel(),
+ settingsModel: dummySettingsViewModel()
+ )
+}
diff --git a/MacNearFuture/Views/HomeView.swift b/MacNearFuture/Views/HomeView.swift
new file mode 100644
index 0000000..f21b7c5
--- /dev/null
+++ b/MacNearFuture/Views/HomeView.swift
@@ -0,0 +1,50 @@
+//
+// HomeView.swift
+// MacNearFuture
+//
+// Created by neon443 on 28/05/2025.
+//
+
+import SwiftUI
+
+struct HomeView: View {
+ @StateObject var viewModel: EventViewModel
+ @StateObject var settingsModel: SettingsViewModel
+
+ @State private var searchInput: String = ""
+
+ var filteredEvents: [Event] {
+ if searchInput.isEmpty {
+ if settingsModel.settings.showCompletedInHome {
+ return viewModel.events
+ } else {
+ return viewModel.events.filter() { !$0.complete }
+ }
+ } else {
+ return viewModel.events.filter {
+ $0.name.localizedCaseInsensitiveContains(searchInput) ||
+ $0.notes.localizedCaseInsensitiveContains(searchInput)
+ }
+ }
+ }
+
+ var body: some View {
+ ScrollView {
+ ForEach(viewModel.events) { event in
+ if filteredEvents.contains(event) {
+ EventListView(viewModel: viewModel, event: event)
+ .id(event)
+ }
+ }
+ }
+ .searchable(text: $searchInput)
+ .scrollContentBackground(.hidden)
+ }
+}
+
+#Preview {
+ HomeView(
+ viewModel: dummyEventViewModel(),
+ settingsModel: dummySettingsViewModel()
+ )
+}
diff --git a/MacNearFuture/Views/SettingsView.swift b/MacNearFuture/Views/SettingsView.swift
new file mode 100644
index 0000000..34ad279
--- /dev/null
+++ b/MacNearFuture/Views/SettingsView.swift
@@ -0,0 +1,155 @@
+//
+// SettingsView.swift
+// NearFuture
+//
+// Created by neon443 on 13/06/2025.
+//
+
+import SwiftUI
+
+struct SettingsView: View {
+ @ObservedObject var viewModel: EventViewModel
+ @ObservedObject var settingsModel: SettingsViewModel
+
+ @State private var importStr: String = ""
+
+ func changeIcon(to toIcon: String) {
+ 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()
+ }
+ }
+
+ var body: some View {
+ NavigationStack {
+ List {
+ ScrollView(.horizontal) {
+ HStack {
+ ForEach(settingsModel.accentChoices, id: \.self) { choice in
+ let color = Color(nsColor: NSColor(named: "uiColors/\(choice)")!)
+ ZStack {
+ Button() {
+ settingsModel.changeTint(to: choice)
+ changeIcon(to: choice)
+ } label: {
+ Circle()
+ .foregroundStyle(color)
+ .frame(width: 30)
+ }
+ .buttonStyle(.plain)
+ if ColorCodable(color) == settingsModel.settings.tint {
+ let needContrast: Bool = ColorCodable(color) == settingsModel.settings.tint
+ Circle()
+ .foregroundStyle(needContrast ? .two : .one)
+ .frame(width: 10)
+ }
+ }
+ }
+ }
+ }
+ Button("Show What's New") {
+ settingsModel.settings.showWhatsNew = true
+ }
+ Toggle("Show completed Events in Home", isOn: $settingsModel.settings.showCompletedInHome)
+ .onChange(of: settingsModel.settings.showCompletedInHome) { _ in
+ settingsModel.saveSettings()
+ }
+ NavigationLink() {
+ List {
+ if !settingsModel.notifsGranted {
+ Text("\(Image(systemName: "xmark")) Notifications disabled for Near Future")
+ .foregroundStyle(.red)
+ Button("Request Notifications") {
+ Task.detached {
+ let requestNotifsResult = await requestNotifs()
+ await MainActor.run {
+ settingsModel.notifsGranted = requestNotifsResult
+ }
+ }
+ }
+ } else {
+ Text("\(Image(systemName: "checkmark")) Notifications enabled for Near Future")
+ .foregroundStyle(.green)
+ }
+ }
+ } label: {
+ Image(systemName: "bell.badge.fill")
+ Text("Notifications")
+ }
+ NavigationLink() {
+ iCloudSettingsView(
+ viewModel: viewModel,
+ settingsModel: settingsModel
+ )
+ } label: {
+ HStack {
+ Image(systemName: "icloud.fill")
+ Text("iCloud")
+ Spacer()
+ Circle()
+ .frame(width: 20, height: 20)
+ .foregroundStyle(viewModel.iCloudStatusColor)
+ }
+ }
+ .onAppear {
+ viewModel.sync()
+ viewModel.updateiCStatus()
+ }
+ NavigationLink() {
+ ImportView(viewModel: viewModel, importStr: $importStr)
+ } label: {
+ Label("Import Events", systemImage: "tray.and.arrow.down.fill")
+ .foregroundStyle(.one)
+ }
+ NavigationLink() {
+ ExportView(viewModel: viewModel)
+ } label: {
+ Label("Export Events", systemImage: "square.and.arrow.up")
+ .foregroundStyle(.one)
+ }
+
+ Text("Tip")
+ .font(.subheadline)
+ Text("Near Future has Widgets!")
+
+ Text("Danger Zone")
+ .foregroundStyle(.red)
+ .font(.subheadline)
+ Button("Delete local data", role: .destructive) {
+ viewModel.dangerClearLocalData()
+ }
+ Button("Delete iCloud data", role: .destructive) {
+ viewModel.dangerCleariCloudData()
+ }
+ Button("Delete all data", role: .destructive) {
+ viewModel.dangerClearLocalData()
+ viewModel.dangerCleariCloudData()
+ }
+
+ Text("Debug")
+ .foregroundStyle(.red)
+ .font(.subheadline)
+ Button("Reset UserDefaults", role: .destructive) {
+ viewModel.dangerResetLocalData()
+ }
+ Button("Reset iCloud", role: .destructive) {
+ viewModel.dangerResetiCloud()
+ }
+
+ // AboutView()
+
+ .modifier(navigationInlineLarge())
+ .scrollContentBackground(.hidden)
+ }
+ }
+ }
+}
+
+#Preview {
+ SettingsView(
+ viewModel: dummyEventViewModel(),
+ settingsModel: dummySettingsViewModel()
+ )
+}
diff --git a/NearFuture.xcodeproj/project.pbxproj b/NearFuture.xcodeproj/project.pbxproj
index 402ded8..0a3c59a 100644
--- a/NearFuture.xcodeproj/project.pbxproj
+++ b/NearFuture.xcodeproj/project.pbxproj
@@ -7,14 +7,44 @@
objects = {
/* Begin PBXBuildFile section */
+ A90D49382DDE0FAF00781124 /* ContentViewMac.swift in Sources */ = {isa = PBXBuildFile; fileRef = A90D49332DDE0FAF00781124 /* ContentViewMac.swift */; };
+ A90D493D2DDE10B200781124 /* NearFutureIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = A949F8312DCAAA8A0064DCA0 /* NearFutureIcon.png */; };
+ A90D493E2DDE10CF00781124 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A920C28D2D24011A00E4F9B1 /* Assets.xcassets */; };
+ A90D49422DDE114100781124 /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = A920C28B2D24011400E4F9B1 /* Events.swift */; };
+ A90D49442DDE1C7600781124 /* Tints.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A90D49432DDE1C1100781124 /* Tints.xcassets */; };
+ A90D49452DDE1C7600781124 /* Tints.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A90D49432DDE1C1100781124 /* Tints.xcassets */; };
+ A90D49462DDE1C7A00781124 /* Tints.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A90D49432DDE1C1100781124 /* Tints.xcassets */; };
+ A90D49522DDE2D0000781124 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A90D49512DDE2D0000781124 /* Extensions.swift */; };
+ A90D49532DDE2D0000781124 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A90D49512DDE2D0000781124 /* Extensions.swift */; };
+ A90D495B2DDE2EDB00781124 /* MacNearFutureApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = A90D495A2DDE2EDB00781124 /* MacNearFutureApp.swift */; };
+ A90D495E2DDE3C7400781124 /* NFCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = A90D495D2DDE3C7400781124 /* NFCommands.swift */; };
+ A90D495F2DDE3C7400781124 /* NFCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = A90D495D2DDE3C7400781124 /* NFCommands.swift */; };
+ A90D49612DDE626300781124 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = A90D49602DDE626300781124 /* Settings.swift */; };
+ A90D49622DDE626300781124 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = A90D49602DDE626300781124 /* Settings.swift */; };
+ A90D49632DDE626300781124 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = A90D49602DDE626300781124 /* Settings.swift */; };
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 */; };
+ A91EF80E2DFC9A0C00B8463D /* WhatsNewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A914FA4C2DD2768900856265 /* WhatsNewView.swift */; };
+ A91EF8102DFCB66C00B8463D /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A91EF80F2DFCB66C00B8463D /* SettingsView.swift */; };
+ A91EF8132DFCC87D00B8463D /* EditEventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949F83D2DCAABE00064DCA0 /* EditEventView.swift */; };
+ A91EF8142DFCC87D00B8463D /* AddEventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949F83C2DCAABE00064DCA0 /* AddEventView.swift */; };
+ A91EF8182DFD77BF00B8463D /* SymbolsLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = A91EF8172DFD77BF00B8463D /* SymbolsLoader.swift */; };
+ A91EF8192DFD77BF00B8463D /* SymbolsLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = A91EF8172DFD77BF00B8463D /* SymbolsLoader.swift */; };
+ A91EF81A2DFD77BF00B8463D /* SymbolsLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = A91EF8172DFD77BF00B8463D /* SymbolsLoader.swift */; };
+ A91EF81C2DFD796600B8463D /* SymbolsPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = A91EF81B2DFD796600B8463D /* SymbolsPicker.swift */; };
+ A91EF81D2DFD796600B8463D /* SymbolsPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = A91EF81B2DFD796600B8463D /* SymbolsPicker.swift */; };
+ A91EF81E2DFD796600B8463D /* SymbolsPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = A91EF81B2DFD796600B8463D /* SymbolsPicker.swift */; };
A920C2882D24011400E4F9B1 /* NearFutureApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = A920C2872D24011400E4F9B1 /* NearFutureApp.swift */; };
- A920C28C2D24011400E4F9B1 /* Item.swift in Sources */ = {isa = PBXBuildFile; fileRef = A920C28B2D24011400E4F9B1 /* Item.swift */; };
+ A920C28C2D24011400E4F9B1 /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = A920C28B2D24011400E4F9B1 /* Events.swift */; };
A920C28E2D24011A00E4F9B1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A920C28D2D24011A00E4F9B1 /* Assets.xcassets */; };
A920C2922D24011A00E4F9B1 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A920C2912D24011A00E4F9B1 /* Preview Assets.xcassets */; };
- A920C2BE2D24021A00E4F9B1 /* SFSymbolsPicker in Frameworks */ = {isa = PBXBuildFile; productRef = A920C2BD2D24021A00E4F9B1 /* SFSymbolsPicker */; };
A949F8322DCAAA8A0064DCA0 /* NearFutureIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = A949F8312DCAAA8A0064DCA0 /* NearFutureIcon.png */; };
A949F84B2DCAABE00064DCA0 /* ArchiveView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949F83A2DCAABE00064DCA0 /* ArchiveView.swift */; };
A949F84C2DCAABE00064DCA0 /* AddEventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949F83C2DCAABE00064DCA0 /* AddEventView.swift */; };
@@ -27,16 +57,25 @@
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 */; };
- A979F6072D270AF00094C0B3 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A979F6062D270AF00094C0B3 /* SwiftUI.framework */; };
+ A95E9ED32DFC703200ED655F /* iCloudSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949F8442DCAABE00064DCA0 /* iCloudSettingsView.swift */; };
+ A95E9ED82DFC742B00ED655F /* AccentIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = A95E9ED72DFC742B00ED655F /* AccentIcon.swift */; };
+ A95E9ED92DFC742B00ED655F /* AccentIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = A95E9ED72DFC742B00ED655F /* AccentIcon.swift */; };
+ A95E9EDA2DFC742B00ED655F /* AccentIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = A95E9ED72DFC742B00ED655F /* AccentIcon.swift */; };
+ A95E9EE42DFC77D400ED655F /* ImportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949F8452DCAABE00064DCA0 /* ImportView.swift */; };
+ A95E9EE52DFC77E200ED655F /* ExportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949F8432DCAABE00064DCA0 /* ExportView.swift */; };
+ A96609E72DFD800000DBFA78 /* HelpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949F8412DCAABE00064DCA0 /* HelpView.swift */; };
A979F60A2D270AF00094C0B3 /* NearFutureWidgetsBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = A979F6092D270AF00094C0B3 /* NearFutureWidgetsBundle.swift */; };
A979F60C2D270AF00094C0B3 /* NearFutureWidgetsLiveActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = A979F60B2D270AF00094C0B3 /* NearFutureWidgetsLiveActivity.swift */; };
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, ); }; };
- A979F6182D2714310094C0B3 /* Item.swift in Sources */ = {isa = PBXBuildFile; fileRef = A920C28B2D24011400E4F9B1 /* Item.swift */; };
+ A979F6182D2714310094C0B3 /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = A920C28B2D24011400E4F9B1 /* Events.swift */; };
+ A98C20CE2DE7308E0008D61C /* ArchiveView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A98C20CD2DE7308E0008D61C /* ArchiveView.swift */; };
+ A98C20D02DE731BD0008D61C /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A98C20CF2DE731BD0008D61C /* HomeView.swift */; };
+ A98C20D42DE7339E0008D61C /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A98C20D32DE7339E0008D61C /* AboutView.swift */; };
+ A9BAC6882DFF242300EC8E44 /* CompleteEventButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9BAC6872DFF238100EC8E44 /* CompleteEventButton.swift */; };
+ A9BAC6892DFF242300EC8E44 /* CompleteEventButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9BAC6872DFF238100EC8E44 /* CompleteEventButton.swift */; };
+ A9D1C34D2DFE10FA00703C2D /* EventListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949F8402DCAABE00064DCA0 /* EventListView.swift */; };
A9FC7EEA2D2823920020D75B /* NearFutureWidgets.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9FC7EE92D28238A0020D75B /* NearFutureWidgets.swift */; };
/* End PBXBuildFile section */
@@ -48,12 +87,12 @@
remoteGlobalIDString = A979F6012D270AF00094C0B3;
remoteInfo = NearFutureWidgetsExtension;
};
- A980FC3B2D93FB2B006A778F /* PBXContainerItemProxy */ = {
+ A98C20D12DE732B10008D61C /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = A920C27C2D24011300E4F9B1 /* Project object */;
proxyType = 1;
- remoteGlobalIDString = A920C2832D24011300E4F9B1;
- remoteInfo = NearFuture;
+ remoteGlobalIDString = A979F6012D270AF00094C0B3;
+ remoteInfo = NearFutureWidgetsExtension;
};
/* End PBXContainerItemProxy section */
@@ -72,15 +111,29 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
+ A90D491F2DDE08E400781124 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; };
+ A90D49262DDE0FA400781124 /* Near Future.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Near Future.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+ A90D49332DDE0FAF00781124 /* ContentViewMac.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentViewMac.swift; sourceTree = ""; };
+ A90D49342DDE0FAF00781124 /* MacNearFuture.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = MacNearFuture.entitlements; sourceTree = ""; };
+ A90D49432DDE1C1100781124 /* Tints.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Tints.xcassets; sourceTree = ""; };
+ A90D49512DDE2D0000781124 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; };
+ A90D495A2DDE2EDB00781124 /* MacNearFutureApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = MacNearFutureApp.swift; path = MacNearFuture/MacNearFutureApp.swift; sourceTree = SOURCE_ROOT; };
+ A90D495D2DDE3C7400781124 /* NFCommands.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NFCommands.swift; sourceTree = ""; };
+ A90D49602DDE626300781124 /* Settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = ""; };
+ A90D49652DDE658100781124 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
A90FDE222DC0D4310012790C /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = ""; };
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 = ""; };
+ A91EF80F2DFCB66C00B8463D /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; };
+ A91EF8172DFD77BF00B8463D /* SymbolsLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SymbolsLoader.swift; sourceTree = ""; };
+ A91EF81B2DFD796600B8463D /* SymbolsPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SymbolsPicker.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 /* Item.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Item.swift; sourceTree = ""; };
+ A920C28B2D24011400E4F9B1 /* Events.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Events.swift; sourceTree = ""; };
A920C28D2D24011A00E4F9B1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
- A920C28F2D24011A00E4F9B1 /* NearFuture.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NearFuture.entitlements; sourceTree = ""; };
A920C2912D24011A00E4F9B1 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
A949F82E2DCAAA640064DCA0 /* NearFutureIcon.pxd */ = {isa = PBXFileReference; lastKnownFileType = file; path = NearFutureIcon.pxd; sourceTree = ""; };
A949F82F2DCAAA640064DCA0 /* NearFutureIconDark.pxd */ = {isa = PBXFileReference; lastKnownFileType = file; path = NearFutureIconDark.pxd; sourceTree = ""; };
@@ -97,46 +150,35 @@
A949F8452DCAABE00064DCA0 /* ImportView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportView.swift; sourceTree = ""; };
A949F8462DCAABE00064DCA0 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; };
A949F8482DCAABE00064DCA0 /* StatsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsView.swift; sourceTree = ""; };
- A949F8572DCAAD670064DCA0 /* NearFutureTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearFutureTests.swift; sourceTree = ""; };
A949F85D2DCABB420064DCA0 /* Buttons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Buttons.swift; sourceTree = ""; };
- A979F58B2D2700680094C0B3 /* NearFutureWidgetsBundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearFutureWidgetsBundle.swift; sourceTree = ""; };
- A979F58D2D2700680094C0B3 /* NearFutureWidgetsLiveActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearFutureWidgetsLiveActivity.swift; sourceTree = ""; };
- A979F58F2D2700680094C0B3 /* NearFutureWidgets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearFutureWidgets.swift; sourceTree = ""; };
- A979F5912D2700680094C0B3 /* AppIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppIntent.swift; sourceTree = ""; };
- A979F5932D27006D0094C0B3 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
- A979F5952D27006D0094C0B3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ A95E9ED72DFC742B00ED655F /* AccentIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccentIcon.swift; sourceTree = ""; };
A979F6022D270AF00094C0B3 /* NearFutureWidgetsExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NearFutureWidgetsExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
- A979F6042D270AF00094C0B3 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; };
- A979F6062D270AF00094C0B3 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; };
A979F6092D270AF00094C0B3 /* NearFutureWidgetsBundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearFutureWidgetsBundle.swift; sourceTree = ""; };
A979F60B2D270AF00094C0B3 /* NearFutureWidgetsLiveActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearFutureWidgetsLiveActivity.swift; sourceTree = ""; };
A979F60F2D270AF80094C0B3 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
- A979F6112D270AF90094C0B3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
- A980FC302D920097006A778F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
- A980FC372D93FB2B006A778F /* NearFutureTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NearFutureTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
- A9C05E412D2805D7007DC497 /* NearFutureWidgetsExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NearFutureWidgetsExtension.entitlements; sourceTree = ""; };
+ A98C20CD2DE7308E0008D61C /* ArchiveView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArchiveView.swift; sourceTree = ""; };
+ A98C20CF2DE731BD0008D61C /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = ""; };
+ A98C20D32DE7339E0008D61C /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = ""; };
+ A9BAC6872DFF238100EC8E44 /* CompleteEventButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompleteEventButton.swift; sourceTree = ""; };
A9FC7EE92D28238A0020D75B /* NearFutureWidgets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearFutureWidgets.swift; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
+ A90D49232DDE0FA400781124 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
A920C2812D24011300E4F9B1 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- A920C2BE2D24021A00E4F9B1 /* SFSymbolsPicker in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
A979F5FF2D270AF00094C0B3 /* Frameworks */ = {
- isa = PBXFrameworksBuildPhase;
- buildActionMask = 2147483647;
- files = (
- A979F6072D270AF00094C0B3 /* SwiftUI.framework in Frameworks */,
- A979F6052D270AF00094C0B3 /* WidgetKit.framework in Frameworks */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
- A980FC342D93FB2B006A778F /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -146,15 +188,80 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
+ A90D49202DDE0A3B00781124 /* Model */ = {
+ isa = PBXGroup;
+ children = (
+ A920C28B2D24011400E4F9B1 /* Events.swift */,
+ A90D49602DDE626300781124 /* Settings.swift */,
+ A91EF8062DFC8B8B00B8463D /* ColorCodable.swift */,
+ A95E9ED72DFC742B00ED655F /* AccentIcon.swift */,
+ A91EF8162DFD77A500B8463D /* SymbolsPicker */,
+ );
+ path = Model;
+ sourceTree = "";
+ };
+ A90D49362DDE0FAF00781124 /* MacNearFuture */ = {
+ isa = PBXGroup;
+ children = (
+ A90D495A2DDE2EDB00781124 /* MacNearFutureApp.swift */,
+ A98C20D32DE7339E0008D61C /* AboutView.swift */,
+ A90D495D2DDE3C7400781124 /* NFCommands.swift */,
+ A90D493F2DDE10EC00781124 /* Views */,
+ A90D49342DDE0FAF00781124 /* MacNearFuture.entitlements */,
+ );
+ path = MacNearFuture;
+ sourceTree = "";
+ };
+ A90D493F2DDE10EC00781124 /* Views */ = {
+ isa = PBXGroup;
+ children = (
+ A90D49332DDE0FAF00781124 /* ContentViewMac.swift */,
+ A98C20CF2DE731BD0008D61C /* HomeView.swift */,
+ A98C20CD2DE7308E0008D61C /* ArchiveView.swift */,
+ A91EF80F2DFCB66C00B8463D /* SettingsView.swift */,
+ );
+ path = Views;
+ sourceTree = "";
+ };
+ A90D49412DDE112700781124 /* Shared */ = {
+ isa = PBXGroup;
+ children = (
+ A920C2872D24011400E4F9B1 /* NearFutureApp.swift */,
+ A90D49512DDE2D0000781124 /* Extensions.swift */,
+ A90D49202DDE0A3B00781124 /* Model */,
+ A91EF80A2DFC910000B8463D /* ViewModifiers.swift */,
+ A9BAC6872DFF238100EC8E44 /* CompleteEventButton.swift */,
+ );
+ path = Shared;
+ sourceTree = "";
+ };
+ A90D49542DDE2D5800781124 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+ A91EF8162DFD77A500B8463D /* SymbolsPicker */ = {
+ isa = PBXGroup;
+ children = (
+ A91EF8172DFD77BF00B8463D /* SymbolsLoader.swift */,
+ A91EF81B2DFD796600B8463D /* SymbolsPicker.swift */,
+ );
+ path = SymbolsPicker;
+ sourceTree = "";
+ };
A920C27B2D24011300E4F9B1 = {
isa = PBXGroup;
children = (
+ A90D491F2DDE08E400781124 /* README.md */,
A90FDE222DC0D4310012790C /* Config.xcconfig */,
- A949F8002DCAA0340064DCA0 /* Resources */,
+ A90D49412DDE112700781124 /* Shared */,
A920C2862D24011400E4F9B1 /* NearFuture */,
+ A90D49362DDE0FAF00781124 /* MacNearFuture */,
+ A949F8002DCAA0340064DCA0 /* Resources */,
A979F6082D270AF00094C0B3 /* NearFutureWidgets */,
- A949F8582DCAAD670064DCA0 /* NearFutureTests */,
- A979F6032D270AF00094C0B3 /* Frameworks */,
+ A90D49542DDE2D5800781124 /* Frameworks */,
A920C2852D24011400E4F9B1 /* Products */,
);
sourceTree = "";
@@ -164,7 +271,7 @@
children = (
A920C2842D24011400E4F9B1 /* NearFuture.app */,
A979F6022D270AF00094C0B3 /* NearFutureWidgetsExtension.appex */,
- A980FC372D93FB2B006A778F /* NearFutureTests.xctest */,
+ A90D49262DDE0FA400781124 /* Near Future.app */,
);
name = Products;
sourceTree = "";
@@ -172,11 +279,7 @@
A920C2862D24011400E4F9B1 /* NearFuture */ = {
isa = PBXGroup;
children = (
- A920C2872D24011400E4F9B1 /* NearFutureApp.swift */,
- A920C28B2D24011400E4F9B1 /* Item.swift */,
A949F84A2DCAABE00064DCA0 /* Views */,
- A980FC302D920097006A778F /* Info.plist */,
- A920C28F2D24011A00E4F9B1 /* NearFuture.entitlements */,
A920C2902D24011A00E4F9B1 /* Preview Content */,
);
path = NearFuture;
@@ -185,7 +288,6 @@
A920C2902D24011A00E4F9B1 /* Preview Content */ = {
isa = PBXGroup;
children = (
- A979F58A2D2700680094C0B3 /* NearFutureWidgets */,
A920C2912D24011A00E4F9B1 /* Preview Assets.xcassets */,
);
path = "Preview Content";
@@ -196,6 +298,7 @@
children = (
A949F8312DCAAA8A0064DCA0 /* NearFutureIcon.png */,
A920C28D2D24011A00E4F9B1 /* Assets.xcassets */,
+ A90D49432DDE1C1100781124 /* Tints.xcassets */,
A949F82E2DCAAA640064DCA0 /* NearFutureIcon.pxd */,
A949F82F2DCAAA640064DCA0 /* NearFutureIconDark.pxd */,
A949F8302DCAAA640064DCA0 /* NearFutureIconTint.pxd */,
@@ -223,10 +326,9 @@
A949F8422DCAABE00064DCA0 /* Home */ = {
isa = PBXGroup;
children = (
- A949F83F2DCAABE00064DCA0 /* ContentView.swift */,
+ A914FA4A2DD26C0F00856265 /* HomeView.swift */,
A949F8402DCAABE00064DCA0 /* EventListView.swift */,
A949F8412DCAABE00064DCA0 /* HelpView.swift */,
- A914FA4A2DD26C0F00856265 /* HomeView.swift */,
);
path = Home;
sourceTree = "";
@@ -254,6 +356,7 @@
A949F84A2DCAABE00064DCA0 /* Views */ = {
isa = PBXGroup;
children = (
+ A949F83F2DCAABE00064DCA0 /* ContentView.swift */,
A949F8422DCAABE00064DCA0 /* Home */,
A949F83E2DCAABE00064DCA0 /* Events */,
A949F83B2DCAABE00064DCA0 /* Archive */,
@@ -264,14 +367,6 @@
path = Views;
sourceTree = "";
};
- A949F8582DCAAD670064DCA0 /* NearFutureTests */ = {
- isa = PBXGroup;
- children = (
- A949F8572DCAAD670064DCA0 /* NearFutureTests.swift */,
- );
- path = NearFutureTests;
- sourceTree = "";
- };
A949F85E2DCABB420064DCA0 /* Misc */ = {
isa = PBXGroup;
children = (
@@ -281,28 +376,6 @@
path = Misc;
sourceTree = "";
};
- A979F58A2D2700680094C0B3 /* NearFutureWidgets */ = {
- isa = PBXGroup;
- children = (
- A979F58B2D2700680094C0B3 /* NearFutureWidgetsBundle.swift */,
- A979F58D2D2700680094C0B3 /* NearFutureWidgetsLiveActivity.swift */,
- A979F58F2D2700680094C0B3 /* NearFutureWidgets.swift */,
- A979F5912D2700680094C0B3 /* AppIntent.swift */,
- A979F5932D27006D0094C0B3 /* Assets.xcassets */,
- A979F5952D27006D0094C0B3 /* Info.plist */,
- );
- path = NearFutureWidgets;
- sourceTree = "";
- };
- A979F6032D270AF00094C0B3 /* Frameworks */ = {
- isa = PBXGroup;
- children = (
- A979F6042D270AF00094C0B3 /* WidgetKit.framework */,
- A979F6062D270AF00094C0B3 /* SwiftUI.framework */,
- );
- name = Frameworks;
- sourceTree = "";
- };
A979F6082D270AF00094C0B3 /* NearFutureWidgets */ = {
isa = PBXGroup;
children = (
@@ -310,8 +383,7 @@
A979F6092D270AF00094C0B3 /* NearFutureWidgetsBundle.swift */,
A979F60B2D270AF00094C0B3 /* NearFutureWidgetsLiveActivity.swift */,
A979F60F2D270AF80094C0B3 /* Assets.xcassets */,
- A979F6112D270AF90094C0B3 /* Info.plist */,
- A9C05E412D2805D7007DC497 /* NearFutureWidgetsExtension.entitlements */,
+ A90D49652DDE658100781124 /* Info.plist */,
);
path = NearFutureWidgets;
sourceTree = "";
@@ -319,6 +391,26 @@
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
+ A90D49252DDE0FA400781124 /* MacNearFuture */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = A90D492F2DDE0FA600781124 /* Build configuration list for PBXNativeTarget "MacNearFuture" */;
+ buildPhases = (
+ A90D49222DDE0FA400781124 /* Sources */,
+ A90D49232DDE0FA400781124 /* Frameworks */,
+ A90D49242DDE0FA400781124 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ A98C20D22DE732B10008D61C /* PBXTargetDependency */,
+ );
+ name = MacNearFuture;
+ packageProductDependencies = (
+ );
+ productName = MacNearFuture;
+ productReference = A90D49262DDE0FA400781124 /* Near Future.app */;
+ productType = "com.apple.product-type.application";
+ };
A920C2832D24011300E4F9B1 /* NearFuture */ = {
isa = PBXNativeTarget;
buildConfigurationList = A920C2AB2D24011B00E4F9B1 /* Build configuration list for PBXNativeTarget "NearFuture" */;
@@ -335,7 +427,6 @@
);
name = NearFuture;
packageProductDependencies = (
- A920C2BD2D24021A00E4F9B1 /* SFSymbolsPicker */,
);
productName = NearFuture;
productReference = A920C2842D24011400E4F9B1 /* NearFuture.app */;
@@ -358,26 +449,6 @@
productReference = A979F6022D270AF00094C0B3 /* NearFutureWidgetsExtension.appex */;
productType = "com.apple.product-type.app-extension";
};
- A980FC362D93FB2B006A778F /* NearFutureTests */ = {
- isa = PBXNativeTarget;
- buildConfigurationList = A980FC3D2D93FB2B006A778F /* Build configuration list for PBXNativeTarget "NearFutureTests" */;
- buildPhases = (
- A980FC332D93FB2B006A778F /* Sources */,
- A980FC342D93FB2B006A778F /* Frameworks */,
- A980FC352D93FB2B006A778F /* Resources */,
- );
- buildRules = (
- );
- dependencies = (
- A980FC3C2D93FB2B006A778F /* PBXTargetDependency */,
- );
- name = NearFutureTests;
- packageProductDependencies = (
- );
- productName = NearFutureTests;
- productReference = A980FC372D93FB2B006A778F /* NearFutureTests.xctest */;
- productType = "com.apple.product-type.bundle.unit-test";
- };
/* End PBXNativeTarget section */
/* Begin PBXProject section */
@@ -385,19 +456,18 @@
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = 1;
- LastSwiftUpdateCheck = 1620;
+ LastSwiftUpdateCheck = 1640;
LastUpgradeCheck = 1630;
TargetAttributes = {
+ A90D49252DDE0FA400781124 = {
+ CreatedOnToolsVersion = 16.4;
+ };
A920C2832D24011300E4F9B1 = {
CreatedOnToolsVersion = 15.4;
};
A979F6012D270AF00094C0B3 = {
CreatedOnToolsVersion = 15.4;
};
- A980FC362D93FB2B006A778F = {
- CreatedOnToolsVersion = 16.2;
- TestTargetID = A920C2832D24011300E4F9B1;
- };
};
};
buildConfigurationList = A920C27F2D24011300E4F9B1 /* Build configuration list for PBXProject "NearFuture" */;
@@ -410,24 +480,34 @@
);
mainGroup = A920C27B2D24011300E4F9B1;
packageReferences = (
- A920C2BC2D24021900E4F9B1 /* XCRemoteSwiftPackageReference "SFSymbolsPicker" */,
);
productRefGroup = A920C2852D24011400E4F9B1 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
A920C2832D24011300E4F9B1 /* NearFuture */,
+ A90D49252DDE0FA400781124 /* MacNearFuture */,
A979F6012D270AF00094C0B3 /* NearFutureWidgetsExtension */,
- A980FC362D93FB2B006A778F /* NearFutureTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
+ A90D49242DDE0FA400781124 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ A90D49452DDE1C7600781124 /* Tints.xcassets in Resources */,
+ A90D493E2DDE10CF00781124 /* Assets.xcassets in Resources */,
+ A90D493D2DDE10B200781124 /* NearFutureIcon.png in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
A920C2822D24011300E4F9B1 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ A90D49442DDE1C7600781124 /* Tints.xcassets in Resources */,
A920C2922D24011A00E4F9B1 /* Preview Assets.xcassets in Resources */,
A949F8322DCAAA8A0064DCA0 /* NearFutureIcon.png in Resources */,
A920C28E2D24011A00E4F9B1 /* Assets.xcassets in Resources */,
@@ -438,40 +518,74 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ A90D49462DDE1C7A00781124 /* Tints.xcassets in Resources */,
A979F6102D270AF90094C0B3 /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
- A980FC352D93FB2B006A778F /* Resources */ = {
- isa = PBXResourcesBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
+ A90D49222DDE0FA400781124 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ A91EF80E2DFC9A0C00B8463D /* WhatsNewView.swift in Sources */,
+ A91EF8192DFD77BF00B8463D /* SymbolsLoader.swift in Sources */,
+ A95E9EE42DFC77D400ED655F /* ImportView.swift in Sources */,
+ A9BAC6892DFF242300EC8E44 /* CompleteEventButton.swift in Sources */,
+ A91EF80C2DFC910000B8463D /* ViewModifiers.swift in Sources */,
+ A95E9ED92DFC742B00ED655F /* AccentIcon.swift in Sources */,
+ A91EF8102DFCB66C00B8463D /* SettingsView.swift in Sources */,
+ A90D495E2DDE3C7400781124 /* NFCommands.swift in Sources */,
+ A95E9EE52DFC77E200ED655F /* ExportView.swift in Sources */,
+ A91EF8132DFCC87D00B8463D /* EditEventView.swift in Sources */,
+ A91EF8142DFCC87D00B8463D /* AddEventView.swift in Sources */,
+ A9D1C34D2DFE10FA00703C2D /* EventListView.swift in Sources */,
+ A98C20D42DE7339E0008D61C /* AboutView.swift in Sources */,
+ A98C20CE2DE7308E0008D61C /* ArchiveView.swift in Sources */,
+ A98C20D02DE731BD0008D61C /* HomeView.swift in Sources */,
+ A90D495B2DDE2EDB00781124 /* MacNearFutureApp.swift in Sources */,
+ A91EF8082DFC8B8B00B8463D /* ColorCodable.swift in Sources */,
+ A90D49522DDE2D0000781124 /* Extensions.swift in Sources */,
+ A91EF81D2DFD796600B8463D /* SymbolsPicker.swift in Sources */,
+ A90D49422DDE114100781124 /* Events.swift in Sources */,
+ A90D49382DDE0FAF00781124 /* ContentViewMac.swift in Sources */,
+ A90D49622DDE626300781124 /* Settings.swift in Sources */,
+ A95E9ED32DFC703200ED655F /* iCloudSettingsView.swift in Sources */,
+ A96609E72DFD800000DBFA78 /* HelpView.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
A920C2802D24011300E4F9B1 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- A920C28C2D24011400E4F9B1 /* Item.swift in Sources */,
+ A920C28C2D24011400E4F9B1 /* Events.swift in Sources */,
A949F84B2DCAABE00064DCA0 /* ArchiveView.swift in Sources */,
A914FA4F2DD276D200856265 /* AboutView.swift in Sources */,
A949F84C2DCAABE00064DCA0 /* AddEventView.swift in Sources */,
A914FA4B2DD26C6800856265 /* HomeView.swift in Sources */,
A949F84D2DCAABE00064DCA0 /* EditEventView.swift in Sources */,
A949F84E2DCAABE00064DCA0 /* ContentView.swift in Sources */,
+ 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 */,
A949F8522DCAABE00064DCA0 /* iCloudSettingsView.swift in Sources */,
+ A9BAC6882DFF242300EC8E44 /* CompleteEventButton.swift in Sources */,
A949F8532DCAABE00064DCA0 /* ImportView.swift in Sources */,
A949F8542DCAABE00064DCA0 /* SettingsView.swift in Sources */,
+ A91EF81E2DFD796600B8463D /* SymbolsPicker.swift in Sources */,
A949F8552DCAABE00064DCA0 /* StatsView.swift in Sources */,
+ A91EF81A2DFD77BF00B8463D /* SymbolsLoader.swift in Sources */,
A920C2882D24011400E4F9B1 /* NearFutureApp.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -480,19 +594,16 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- A979F6182D2714310094C0B3 /* Item.swift in Sources */,
+ A979F6182D2714310094C0B3 /* Events.swift in Sources */,
+ A91EF81C2DFD796600B8463D /* SymbolsPicker.swift in Sources */,
A979F60A2D270AF00094C0B3 /* NearFutureWidgetsBundle.swift in Sources */,
+ A95E9EDA2DFC742B00ED655F /* AccentIcon.swift in Sources */,
+ A91EF80D2DFC910000B8463D /* ViewModifiers.swift in Sources */,
+ A91EF8182DFD77BF00B8463D /* SymbolsLoader.swift in Sources */,
+ A91EF8072DFC8B8B00B8463D /* ColorCodable.swift in Sources */,
A9FC7EEA2D2823920020D75B /* NearFutureWidgets.swift in Sources */,
A979F60C2D270AF00094C0B3 /* NearFutureWidgetsLiveActivity.swift in Sources */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
- A980FC332D93FB2B006A778F /* Sources */ = {
- isa = PBXSourcesBuildPhase;
- buildActionMask = 2147483647;
- files = (
- A949F8592DCAAD670064DCA0 /* NearFutureTests.swift in Sources */,
- A949F8562DCAABE00064DCA0 /* ExportView.swift in Sources */,
+ A90D49632DDE626300781124 /* Settings.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -505,14 +616,76 @@
target = A979F6012D270AF00094C0B3 /* NearFutureWidgetsExtension */;
targetProxy = A979F6122D270AF90094C0B3 /* PBXContainerItemProxy */;
};
- A980FC3C2D93FB2B006A778F /* PBXTargetDependency */ = {
+ A98C20D22DE732B10008D61C /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
- target = A920C2832D24011300E4F9B1 /* NearFuture */;
- targetProxy = A980FC3B2D93FB2B006A778F /* PBXContainerItemProxy */;
+ target = A979F6012D270AF00094C0B3 /* NearFutureWidgetsExtension */;
+ targetProxy = A98C20D12DE732B10008D61C /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
+ A90D49302DDE0FA600781124 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = A90FDE222DC0D4310012790C /* Config.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = "bloo blue green pink purple red yellow";
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CODE_SIGN_ENTITLEMENTS = MacNearFuture/MacNearFuture.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ CURRENT_PROJECT_VERSION = "$(BUILD_NUMBER)";
+ DEVELOPMENT_TEAM = 8JGND254B7;
+ ENABLE_HARDENED_RUNTIME = YES;
+ ENABLE_PREVIEWS = YES;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ MACOSX_DEPLOYMENT_TARGET = 15;
+ MARKETING_VERSION = "$(VERSION)";
+ PRODUCT_BUNDLE_IDENTIFIER = com.neon443.NearFuture;
+ PRODUCT_NAME = "Near Future";
+ REGISTER_APP_GROUPS = YES;
+ SDKROOT = macosx;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ };
+ name = Debug;
+ };
+ A90D49312DDE0FA600781124 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = A90FDE222DC0D4310012790C /* Config.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = "bloo blue green pink purple red yellow";
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CODE_SIGN_ENTITLEMENTS = MacNearFuture/MacNearFuture.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ CURRENT_PROJECT_VERSION = "$(BUILD_NUMBER)";
+ DEVELOPMENT_TEAM = 8JGND254B7;
+ ENABLE_HARDENED_RUNTIME = YES;
+ ENABLE_PREVIEWS = YES;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ MACOSX_DEPLOYMENT_TARGET = 15;
+ MARKETING_VERSION = "$(VERSION)";
+ PRODUCT_BUNDLE_IDENTIFIER = com.neon443.NearFuture;
+ PRODUCT_NAME = "Near Future";
+ REGISTER_APP_GROUPS = YES;
+ SDKROOT = macosx;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ };
+ name = Release;
+ };
A920C2A92D24011B00E4F9B1 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -660,8 +833,7 @@
ENABLE_HARDENED_RUNTIME = NO;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
- INFOPLIST_FILE = NearFuture/Info.plist;
- INFOPLIST_KEY_CFBundleDisplayName = "$(NAME)";
+ INFOPLIST_KEY_CFBundleDisplayName = "Near Future";
INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO;
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES;
@@ -687,8 +859,8 @@
REGISTER_APP_GROUPS = YES;
SDKROOT = auto;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
- SUPPORTS_MACCATALYST = YES;
- SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
+ SUPPORTS_MACCATALYST = NO;
+ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
@@ -713,8 +885,7 @@
ENABLE_HARDENED_RUNTIME = NO;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
- INFOPLIST_FILE = NearFuture/Info.plist;
- INFOPLIST_KEY_CFBundleDisplayName = "$(NAME)";
+ INFOPLIST_KEY_CFBundleDisplayName = "Near Future";
INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO;
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES;
@@ -737,8 +908,8 @@
REGISTER_APP_GROUPS = YES;
SDKROOT = auto;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
- SUPPORTS_MACCATALYST = YES;
- SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
+ SUPPORTS_MACCATALYST = NO;
+ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
@@ -758,7 +929,7 @@
CURRENT_PROJECT_VERSION = "$(BUILD_NUMBER)";
DEVELOPMENT_TEAM = "$(TEAM_ID)";
GENERATE_INFOPLIST_FILE = YES;
- INFOPLIST_FILE = NearFutureWidgets/Info.plist;
+ INFOPLIST_FILE = "$(SRCROOT)/NearFutureWidgets/Info.plist";
INFOPLIST_KEY_CFBundleDisplayName = NearFutureWidgets;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 17;
@@ -778,8 +949,9 @@
REGISTER_APP_GROUPS = YES;
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
- SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
- SUPPORTS_MACCATALYST = YES;
+ SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
+ SUPPORTS_MACCATALYST = NO;
+ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@@ -799,7 +971,7 @@
CURRENT_PROJECT_VERSION = "$(BUILD_NUMBER)";
DEVELOPMENT_TEAM = "$(TEAM_ID)";
GENERATE_INFOPLIST_FILE = YES;
- INFOPLIST_FILE = NearFutureWidgets/Info.plist;
+ INFOPLIST_FILE = "$(SRCROOT)/NearFutureWidgets/Info.plist";
INFOPLIST_KEY_CFBundleDisplayName = NearFutureWidgets;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 17;
@@ -815,8 +987,9 @@
REGISTER_APP_GROUPS = YES;
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
- SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
- SUPPORTS_MACCATALYST = YES;
+ SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
+ SUPPORTS_MACCATALYST = NO;
+ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@@ -825,61 +998,18 @@
};
name = Release;
};
- A980FC3E2D93FB2B006A778F /* Debug */ = {
- isa = XCBuildConfiguration;
- baseConfigurationReference = A90FDE222DC0D4310012790C /* Config.xcconfig */;
- buildSettings = {
- BUNDLE_LOADER = "$(TEST_HOST)";
- CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 1;
- GENERATE_INFOPLIST_FILE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 16;
- MACOSX_DEPLOYMENT_TARGET = 13;
- MARKETING_VERSION = 1.0;
- PRODUCT_BUNDLE_IDENTIFIER = dev.neon443.NearFutureTests;
- PRODUCT_NAME = "$(TARGET_NAME)";
- SDKROOT = auto;
- SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
- SUPPORTS_MACCATALYST = NO;
- SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
- SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = YES;
- SWIFT_EMIT_LOC_STRINGS = NO;
- SWIFT_VERSION = 5.0;
- TARGETED_DEVICE_FAMILY = "1,2";
- TEST_HOST = "$(BUILT_PRODUCTS_DIR)/NearFuture.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/NearFuture";
- XROS_DEPLOYMENT_TARGET = 1;
- };
- name = Debug;
- };
- A980FC3F2D93FB2B006A778F /* Release */ = {
- isa = XCBuildConfiguration;
- baseConfigurationReference = A90FDE222DC0D4310012790C /* Config.xcconfig */;
- buildSettings = {
- BUNDLE_LOADER = "$(TEST_HOST)";
- CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 1;
- GENERATE_INFOPLIST_FILE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 16;
- MACOSX_DEPLOYMENT_TARGET = 13;
- MARKETING_VERSION = 1.0;
- PRODUCT_BUNDLE_IDENTIFIER = dev.neon443.NearFutureTests;
- PRODUCT_NAME = "$(TARGET_NAME)";
- SDKROOT = auto;
- SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
- SUPPORTS_MACCATALYST = NO;
- SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
- SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = YES;
- SWIFT_EMIT_LOC_STRINGS = NO;
- SWIFT_VERSION = 5.0;
- TARGETED_DEVICE_FAMILY = "1,2";
- TEST_HOST = "$(BUILT_PRODUCTS_DIR)/NearFuture.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/NearFuture";
- XROS_DEPLOYMENT_TARGET = 1;
- };
- name = Release;
- };
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
+ A90D492F2DDE0FA600781124 /* Build configuration list for PBXNativeTarget "MacNearFuture" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ A90D49302DDE0FA600781124 /* Debug */,
+ A90D49312DDE0FA600781124 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
A920C27F2D24011300E4F9B1 /* Build configuration list for PBXProject "NearFuture" */ = {
isa = XCConfigurationList;
buildConfigurations = (
@@ -907,35 +1037,7 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
- A980FC3D2D93FB2B006A778F /* Build configuration list for PBXNativeTarget "NearFutureTests" */ = {
- isa = XCConfigurationList;
- buildConfigurations = (
- A980FC3E2D93FB2B006A778F /* Debug */,
- A980FC3F2D93FB2B006A778F /* Release */,
- );
- defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Release;
- };
/* End XCConfigurationList section */
-
-/* Begin XCRemoteSwiftPackageReference section */
- A920C2BC2D24021900E4F9B1 /* XCRemoteSwiftPackageReference "SFSymbolsPicker" */ = {
- isa = XCRemoteSwiftPackageReference;
- repositoryURL = "https://github.com/alessiorubicini/SFSymbolsPicker";
- requirement = {
- kind = upToNextMajorVersion;
- minimumVersion = 1.0.6;
- };
- };
-/* End XCRemoteSwiftPackageReference section */
-
-/* Begin XCSwiftPackageProductDependency section */
- A920C2BD2D24021A00E4F9B1 /* SFSymbolsPicker */ = {
- isa = XCSwiftPackageProductDependency;
- package = A920C2BC2D24021900E4F9B1 /* XCRemoteSwiftPackageReference "SFSymbolsPicker" */;
- productName = SFSymbolsPicker;
- };
-/* End XCSwiftPackageProductDependency section */
};
rootObject = A920C27C2D24011300E4F9B1 /* Project object */;
}
diff --git a/NearFuture.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/NearFuture.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
deleted file mode 100644
index b5805b7..0000000
--- a/NearFuture.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "originHash" : "19df39f99b22f4ef95b73ed292ffb0c8d7694dd4c9db2b96ea73b091b7b1a026",
- "pins" : [
- {
- "identity" : "sfsymbolspicker",
- "kind" : "remoteSourceControl",
- "location" : "https://github.com/alessiorubicini/SFSymbolsPicker",
- "state" : {
- "revision" : "73c909b8a7fc77a30dd04208e33f759f8b52c4c8",
- "version" : "1.0.6"
- }
- }
- ],
- "version" : 3
-}
diff --git a/NearFuture.xcodeproj/xcshareddata/xcschemes/MacNearFuture.xcscheme b/NearFuture.xcodeproj/xcshareddata/xcschemes/MacNearFuture.xcscheme
new file mode 100644
index 0000000..d4dc62b
--- /dev/null
+++ b/NearFuture.xcodeproj/xcshareddata/xcschemes/MacNearFuture.xcscheme
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/NearFuture.xcodeproj/xcuserdata/neon443.xcuserdatad/xcschemes/xcschememanagement.plist b/NearFuture.xcodeproj/xcuserdata/neon443.xcuserdatad/xcschemes/xcschememanagement.plist
index 3d4f3de..87d566a 100644
--- a/NearFuture.xcodeproj/xcuserdata/neon443.xcuserdatad/xcschemes/xcschememanagement.plist
+++ b/NearFuture.xcodeproj/xcuserdata/neon443.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -4,19 +4,29 @@
SchemeUserState
- NearFuture.xcscheme_^#shared#^_
+ MacNearFuture.xcscheme_^#shared#^_
orderHint
1
- NearFutureWidgetsExtension.xcscheme_^#shared#^_
+ NearFuture.xcscheme_^#shared#^_
orderHint
0
+ NearFutureWidgetsExtension.xcscheme_^#shared#^_
+
+ orderHint
+ 2
+
SuppressBuildableAutocreation
+ A90D49252DDE0FA400781124
+
+ primary
+
+
A920C2832D24011300E4F9B1
primary
diff --git a/NearFuture/Home/EventListView.swift b/NearFuture/Home/EventListView.swift
deleted file mode 100644
index c79174d..0000000
--- a/NearFuture/Home/EventListView.swift
+++ /dev/null
@@ -1,170 +0,0 @@
-//
-// 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)
- }
- .transition(.opacity)
- .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)
- }
-}
diff --git a/NearFuture/Info.plist b/NearFuture/Info.plist
deleted file mode 100644
index dfce773..0000000
--- a/NearFuture/Info.plist
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
- AppIntents
-
- com.neon443.NearFuture.CompleteEvent
-
-
-
diff --git a/NearFuture/Misc/HelpView.swift b/NearFuture/Misc/HelpView.swift
deleted file mode 100644
index 24d4909..0000000
--- a/NearFuture/Misc/HelpView.swift
+++ /dev/null
@@ -1,112 +0,0 @@
-//
-// 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, focusedField: Field?) {
- _searchInput = searchInput
- self.helpType = .Search
- _showAddEvent = .constant(false)
- }
-
- /// initialises an Archive HelpView
- ///
- init(showAddEvent: Binding) {
- _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))
-}
diff --git a/NearFuture/NearFuture.entitlements b/NearFuture/NearFuture.entitlements
index 057d0cc..82eaccb 100644
--- a/NearFuture/NearFuture.entitlements
+++ b/NearFuture/NearFuture.entitlements
@@ -2,10 +2,6 @@
- aps-environment
- development
- com.apple.developer.aps-environment
- development
com.apple.developer.icloud-container-identifiers
com.apple.developer.ubiquity-kvstore-identifier
diff --git a/NearFuture/NearFutureApp.swift b/NearFuture/NearFutureApp.swift
deleted file mode 100644
index 96ae80f..0000000
--- a/NearFuture/NearFutureApp.swift
+++ /dev/null
@@ -1,36 +0,0 @@
-//
-// NearFutureApp.swift
-// NearFuture
-//
-// Created by neon443 on 24/12/2024.
-//
-
-import SwiftUI
-import SwiftData
-
-@main
-struct NearFutureApp: App {
-// var sharedModelContainer: ModelContainer = {
-// let schema = Schema([
-// Item.self,
-// ])
-// let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)
-//
-// do {
-// return try ModelContainer(for: schema, configurations: [modelConfiguration])
-// } catch {
-// fatalError("Could not create ModelContainer: \(error)")
-// }
-// }()
- @StateObject var settingsModel: SettingsViewModel = SettingsViewModel()
- var body: some Scene {
- WindowGroup {
- ContentView(
- viewModel: EventViewModel(),
- settingsModel: settingsModel
- )
- .tint(settingsModel.settings.tint.color)
- }
-// .modelContainer(sharedModelContainer)
- }
-}
diff --git a/NearFuture/Preview Content/NearFutureWidgets/AppIntent.swift b/NearFuture/Preview Content/NearFutureWidgets/AppIntent.swift
deleted file mode 100644
index 21e1c5b..0000000
--- a/NearFuture/Preview Content/NearFutureWidgets/AppIntent.swift
+++ /dev/null
@@ -1,18 +0,0 @@
-//
-// AppIntent.swift
-// NearFutureWidgets
-//
-// Created by neon443 on 02/01/2025.
-//
-
-import WidgetKit
-import AppIntents
-
-struct ConfigurationAppIntent: WidgetConfigurationIntent {
- static var title: LocalizedStringResource = "Configuration"
- static var description = IntentDescription("This is an example widget.")
-
- // An example configurable parameter.
- @Parameter(title: "Favorite Emoji", default: "😃")
- var favoriteEmoji: String
-}
diff --git a/NearFuture/Preview Content/NearFutureWidgets/Assets.xcassets/AccentColor.colorset/Contents.json b/NearFuture/Preview Content/NearFutureWidgets/Assets.xcassets/AccentColor.colorset/Contents.json
deleted file mode 100644
index eb87897..0000000
--- a/NearFuture/Preview Content/NearFutureWidgets/Assets.xcassets/AccentColor.colorset/Contents.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "colors" : [
- {
- "idiom" : "universal"
- }
- ],
- "info" : {
- "author" : "xcode",
- "version" : 1
- }
-}
diff --git a/NearFuture/Preview Content/NearFutureWidgets/Assets.xcassets/AppIcon.appiconset/Contents.json b/NearFuture/Preview Content/NearFutureWidgets/Assets.xcassets/AppIcon.appiconset/Contents.json
deleted file mode 100644
index 13613e3..0000000
--- a/NearFuture/Preview Content/NearFutureWidgets/Assets.xcassets/AppIcon.appiconset/Contents.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "images" : [
- {
- "idiom" : "universal",
- "platform" : "ios",
- "size" : "1024x1024"
- }
- ],
- "info" : {
- "author" : "xcode",
- "version" : 1
- }
-}
diff --git a/NearFuture/Preview Content/NearFutureWidgets/Assets.xcassets/WidgetBackground.colorset/Contents.json b/NearFuture/Preview Content/NearFutureWidgets/Assets.xcassets/WidgetBackground.colorset/Contents.json
deleted file mode 100644
index eb87897..0000000
--- a/NearFuture/Preview Content/NearFutureWidgets/Assets.xcassets/WidgetBackground.colorset/Contents.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "colors" : [
- {
- "idiom" : "universal"
- }
- ],
- "info" : {
- "author" : "xcode",
- "version" : 1
- }
-}
diff --git a/NearFuture/Preview Content/NearFutureWidgets/Info.plist b/NearFuture/Preview Content/NearFutureWidgets/Info.plist
deleted file mode 100644
index 0f118fb..0000000
--- a/NearFuture/Preview Content/NearFutureWidgets/Info.plist
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
- NSExtension
-
- NSExtensionPointIdentifier
- com.apple.widgetkit-extension
-
-
-
diff --git a/NearFuture/Preview Content/NearFutureWidgets/NearFutureWidgets.swift b/NearFuture/Preview Content/NearFutureWidgets/NearFutureWidgets.swift
deleted file mode 100644
index 9bb45d4..0000000
--- a/NearFuture/Preview Content/NearFutureWidgets/NearFutureWidgets.swift
+++ /dev/null
@@ -1,84 +0,0 @@
-//
-// NearFutureWidgets.swift
-// NearFutureWidgets
-//
-// Created by neon443 on 02/01/2025.
-//
-
-import WidgetKit
-import SwiftUI
-
-struct Provider: AppIntentTimelineProvider {
- func placeholder(in context: Context) -> SimpleEntry {
- SimpleEntry(date: Date(), configuration: ConfigurationAppIntent())
- }
-
- func snapshot(for configuration: ConfigurationAppIntent, in context: Context) async -> SimpleEntry {
- SimpleEntry(date: Date(), configuration: configuration)
- }
-
- func timeline(for configuration: ConfigurationAppIntent, in context: Context) async -> Timeline {
- var entries: [SimpleEntry] = []
-
- // Generate a timeline consisting of five entries an hour apart, starting from the current date.
- let currentDate = Date()
- for hourOffset in 0 ..< 5 {
- let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)!
- let entry = SimpleEntry(date: entryDate, configuration: configuration)
- entries.append(entry)
- }
-
- return Timeline(entries: entries, policy: .atEnd)
- }
-}
-
-struct SimpleEntry: TimelineEntry {
- let date: Date
- let configuration: ConfigurationAppIntent
-}
-
-struct NearFutureWidgetsEntryView : View {
- var entry: Provider.Entry
-
- var body: some View {
- VStack {
- Text("Time:")
- Text(entry.date, style: .time)
-
- Text("Favorite Emoji:")
- Text(entry.configuration.favoriteEmoji)
- }
- }
-}
-
-struct NearFutureWidgets: Widget {
- let kind: String = "NearFutureWidgets"
-
- var body: some WidgetConfiguration {
- AppIntentConfiguration(kind: kind, intent: ConfigurationAppIntent.self, provider: Provider()) { entry in
- NearFutureWidgetsEntryView(entry: entry)
- .containerBackground(.fill.tertiary, for: .widget)
- }
- }
-}
-
-extension ConfigurationAppIntent {
- fileprivate static var smiley: ConfigurationAppIntent {
- let intent = ConfigurationAppIntent()
- intent.favoriteEmoji = "😀"
- return intent
- }
-
- fileprivate static var starEyes: ConfigurationAppIntent {
- let intent = ConfigurationAppIntent()
- intent.favoriteEmoji = "🤩"
- return intent
- }
-}
-
-#Preview(as: .systemSmall) {
- NearFutureWidgets()
-} timeline: {
- SimpleEntry(date: .now, configuration: .smiley)
- SimpleEntry(date: .now, configuration: .starEyes)
-}
diff --git a/NearFuture/Preview Content/NearFutureWidgets/NearFutureWidgetsBundle.swift b/NearFuture/Preview Content/NearFutureWidgets/NearFutureWidgetsBundle.swift
deleted file mode 100644
index 31a0562..0000000
--- a/NearFuture/Preview Content/NearFutureWidgets/NearFutureWidgetsBundle.swift
+++ /dev/null
@@ -1,17 +0,0 @@
-//
-// NearFutureWidgetsBundle.swift
-// NearFutureWidgets
-//
-// Created by neon443 on 02/01/2025.
-//
-
-import WidgetKit
-import SwiftUI
-
-@main
-struct NearFutureWidgetsBundle: WidgetBundle {
- var body: some Widget {
- NearFutureWidgets()
- NearFutureWidgetsLiveActivity()
- }
-}
diff --git a/NearFuture/Preview Content/NearFutureWidgets/NearFutureWidgetsLiveActivity.swift b/NearFuture/Preview Content/NearFutureWidgets/NearFutureWidgetsLiveActivity.swift
deleted file mode 100644
index 3eef6ad..0000000
--- a/NearFuture/Preview Content/NearFutureWidgets/NearFutureWidgetsLiveActivity.swift
+++ /dev/null
@@ -1,80 +0,0 @@
-//
-// NearFutureWidgetsLiveActivity.swift
-// NearFutureWidgets
-//
-// Created by neon443 on 02/01/2025.
-//
-
-import ActivityKit
-import WidgetKit
-import SwiftUI
-
-struct NearFutureWidgetsAttributes: ActivityAttributes {
- public struct ContentState: Codable, Hashable {
- // Dynamic stateful properties about your activity go here!
- var emoji: String
- }
-
- // Fixed non-changing properties about your activity go here!
- var name: String
-}
-
-struct NearFutureWidgetsLiveActivity: Widget {
- var body: some WidgetConfiguration {
- ActivityConfiguration(for: NearFutureWidgetsAttributes.self) { context in
- // Lock screen/banner UI goes here
- VStack {
- Text("Hello \(context.state.emoji)")
- }
- .activityBackgroundTint(Color.cyan)
- .activitySystemActionForegroundColor(Color.black)
-
- } dynamicIsland: { context in
- DynamicIsland {
- // Expanded UI goes here. Compose the expanded UI through
- // various regions, like leading/trailing/center/bottom
- DynamicIslandExpandedRegion(.leading) {
- Text("Leading")
- }
- DynamicIslandExpandedRegion(.trailing) {
- Text("Trailing")
- }
- DynamicIslandExpandedRegion(.bottom) {
- Text("Bottom \(context.state.emoji)")
- // more content
- }
- } compactLeading: {
- Text("L")
- } compactTrailing: {
- Text("T \(context.state.emoji)")
- } minimal: {
- Text(context.state.emoji)
- }
- .widgetURL(URL(string: "http://www.apple.com"))
- .keylineTint(Color.red)
- }
- }
-}
-
-extension NearFutureWidgetsAttributes {
- fileprivate static var preview: NearFutureWidgetsAttributes {
- NearFutureWidgetsAttributes(name: "World")
- }
-}
-
-extension NearFutureWidgetsAttributes.ContentState {
- fileprivate static var smiley: NearFutureWidgetsAttributes.ContentState {
- NearFutureWidgetsAttributes.ContentState(emoji: "😀")
- }
-
- fileprivate static var starEyes: NearFutureWidgetsAttributes.ContentState {
- NearFutureWidgetsAttributes.ContentState(emoji: "🤩")
- }
-}
-
-#Preview("Notification", as: .content, using: NearFutureWidgetsAttributes.preview) {
- NearFutureWidgetsLiveActivity()
-} contentStates: {
- NearFutureWidgetsAttributes.ContentState.smiley
- NearFutureWidgetsAttributes.ContentState.starEyes
-}
diff --git a/NearFuture/Views/Archive/ArchiveView.swift b/NearFuture/Views/Archive/ArchiveView.swift
index 70966fb..8f0fae4 100644
--- a/NearFuture/Views/Archive/ArchiveView.swift
+++ b/NearFuture/Views/Archive/ArchiveView.swift
@@ -11,7 +11,8 @@ struct ArchiveView: View {
@ObservedObject var viewModel: EventViewModel
@State var showAddEvent: Bool = false
var filteredEvents: [Event] {
- return viewModel.events.filter() {$0.complete}
+ let filteredEvents = viewModel.events.filter({$0.complete})
+ return filteredEvents.reversed()
}
var body: some View {
NavigationStack {
@@ -22,42 +23,40 @@ struct ArchiveView: View {
} else {
ScrollView {
ForEach(filteredEvents) { event in
- EventListView(viewModel: viewModel, event: event)
- .transition(.moveAndFadeReversed)
- .id(event.complete)
+ NavigationLink() {
+ EditEventView(
+ viewModel: viewModel,
+ event: Binding(
+ get: { event },
+ set: { newValue in
+ viewModel.editEvent(newValue)
+ }
+ )
+ )
+ } label: {
+ EventListView(viewModel: viewModel, event: event)
+ .id(event.complete)
+ }
+ .transition(.moveAndFadeReversed)
}
.padding(.horizontal)
}
.animation(.default, value: filteredEvents)
}
}
+ .transition(.opacity)
.scrollContentBackground(.hidden)
.toolbar {
- ToolbarItem(placement: .topBarTrailing) {
+ ToolbarItem(placement: .primaryAction) {
AddEventButton(showingAddEventView: $showAddEvent)
}
}
.navigationTitle("Archive")
- .apply {
- if #available(iOS 17, *) {
- $0.toolbarTitleDisplayMode(.inlineLarge)
- } else {
- $0.navigationBarTitleDisplayMode(.inline)
- }
- }
+ .modifier(navigationInlineLarge())
}
.sheet(isPresented: $showAddEvent) {
AddEventView(
- viewModel: viewModel,
- eventName: $viewModel.editableTemplate.name,
- eventComplete: $viewModel.editableTemplate.complete,
- eventCompleteDesc: $viewModel.editableTemplate.completeDesc,
- eventSymbol: $viewModel.editableTemplate.symbol,
- eventColor: $viewModel.editableTemplate.color.colorBind,
- eventNotes: $viewModel.editableTemplate.notes,
- eventDate: $viewModel.editableTemplate.date,
- eventRecurrence: $viewModel.editableTemplate.recurrence,
- adding: true
+ viewModel: viewModel
)
}
}
diff --git a/NearFuture/Views/Home/ContentView.swift b/NearFuture/Views/ContentView.swift
similarity index 53%
rename from NearFuture/Views/Home/ContentView.swift
rename to NearFuture/Views/ContentView.swift
index 122e94b..a0c3e5a 100644
--- a/NearFuture/Views/Home/ContentView.swift
+++ b/NearFuture/Views/ContentView.swift
@@ -9,12 +9,10 @@ import SwiftUI
import UserNotifications
import SwiftData
-enum Field {
- case Search
-}
enum Tab {
case home
case archive
+ case symbols
case stats
case settings
}
@@ -22,21 +20,30 @@ 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")
- }
- .tag(Tab.home)
+ .tabItem {
+ Label("Home", systemImage: "house")
+ }
+ .tag(Tab.home)
ArchiveView(viewModel: viewModel)
.tabItem() {
Label("Archive", systemImage: "tray.full")
}
.tag(Tab.archive)
+ SymbolsPicker(
+ selection: .constant(""),
+ browsing: true
+ )
+ .tabItem {
+ Label("Symbols", systemImage: "star.circle")
+ }
+ .tag(Tab.symbols)
StatsView(viewModel: viewModel)
+// SymbolsPickerStoryboardUIViewRepresentable()
.tabItem {
Label("Statistics", systemImage: "chart.pie")
}
@@ -47,11 +54,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)
}
@@ -64,33 +67,3 @@ struct ContentView: View {
settingsModel: dummySettingsViewModel()
)
}
-
-extension View {
- var backgroundGradient: some View {
- return LinearGradient(
- gradient: Gradient(colors: [.bgTop, .two]),
- startPoint: .top,
- endPoint: .bottom
- )
- .ignoresSafeArea(.all)
- }
-
- func apply(@ViewBuilder _ block: (Self) -> V) -> V { block(self) }
-}
-
-extension AnyTransition {
- static var moveAndFade: AnyTransition {
- .asymmetric(
- insertion: .move(edge: .leading),
- removal: .move(edge: .trailing)
- )
- .combined(with: .opacity)
- }
- static var moveAndFadeReversed: AnyTransition {
- .asymmetric(
- insertion: .move(edge: .trailing),
- removal: .move(edge: .leading)
- )
- .combined(with: .opacity)
- }
-}
diff --git a/NearFuture/Views/Events/AddEventView.swift b/NearFuture/Views/Events/AddEventView.swift
index 76f14c8..f51f8f4 100644
--- a/NearFuture/Views/Events/AddEventView.swift
+++ b/NearFuture/Views/Events/AddEventView.swift
@@ -6,23 +6,15 @@
//
import SwiftUI
-import SFSymbolsPicker
struct AddEventView: View {
@ObservedObject var viewModel: EventViewModel
- @Binding var eventName: String
- @Binding var eventComplete: Bool
- @Binding var eventCompleteDesc: String
- @Binding var eventSymbol: String
- @Binding var eventColor: Color
- @Binding var eventNotes: String
- @Binding var eventDate: Date
- @Binding var eventRecurrence: Event.RecurrenceType
+ @State var event: Event = dummyEventViewModel().template
- @State var adding: Bool
+ @State var adding: Bool = true
@State var showNeedsNameAlert: Bool = false
- @State var isSymbolPickerPresented = false
+ @State var isSymbolPickerPresented: Bool = false
@State private var bye: Bool = false
@@ -33,13 +25,21 @@ struct AddEventView: View {
@Environment(\.dismiss) var dismiss
+ var isMac: Bool {
+ if #available(iOS 1, *) {
+ return false
+ } else {
+ return true
+ }
+ }
+
var body: some View {
ZStack {
if !adding {
backgroundGradient
}
NavigationStack {
- Form {
+ List {
Section(
header:
Text("Event Details")
@@ -51,66 +51,52 @@ struct AddEventView: View {
Button() {
isSymbolPickerPresented.toggle()
} label: {
- Image(systemName: eventSymbol)
+ Image(systemName: event.symbol)
.resizable()
.scaledToFit()
.frame(width: 20, height: 20)
- .foregroundStyle(eventColor)
+ .foregroundStyle(event.color.color)
}
.frame(width: 20)
.buttonStyle(.borderless)
.sheet(isPresented: $isSymbolPickerPresented) {
SymbolsPicker(
- selection: $eventSymbol,
- title: "Choose a Symbol",
- searchLabel: "Search...",
- autoDismiss: true)
+ selection: $event.symbol
+ )
.presentationDetents([.medium])
- .apply {
- if #available(iOS 16.4, *) {
- $0.presentationBackground(.ultraThinMaterial)
- }
- }
- }
- ColorPicker("", selection: $eventColor, supportsOpacity: false)
- .fixedSize()
- ZStack {
- TextField("Event Name", text: $eventName)
- .textFieldStyle(RoundedBorderTextFieldStyle())
- .padding(.trailing, eventName.isEmpty ? 0 : 30)
- .animation(.spring, value: eventName)
- .focused($focusedField, equals: Field.Name)
- .submitLabel(.next)
- .onSubmit {
- focusedField = .Notes
- }
- MagicClearButton(text: $eventName)
+ .modifier(presentationSizeForm())
}
+ TextField("Event Name", text: $event.name)
+ .textFieldStyle(.roundedBorder)
}
// dscription
ZStack {
- TextField("Event Notes", text: $eventNotes)
+ TextField("Event Notes", text: $event.notes)
.textFieldStyle(RoundedBorderTextFieldStyle())
- .padding(.trailing, eventNotes.isEmpty ? 0 : 30)
- .animation(.spring, value: eventNotes)
+ .padding(.trailing, event.notes.isEmpty ? 0 : 30)
+ .animation(.spring, value: event.notes)
.focused($focusedField, equals: Field.Notes)
.submitLabel(.done)
.onSubmit {
focusedField = nil
}
- MagicClearButton(text: $eventNotes)
}
+ ColorPicker("Event Color", selection: $event.color.colorBind)
// date picker
HStack {
Spacer()
- DatePicker("", selection: $eventDate, displayedComponents: .date)
- .datePickerStyle(WheelDatePickerStyle())
+ DatePicker("", selection: $event.date, displayedComponents: .date)
+ #if os(iOS)
+ .datePickerStyle(.wheel)
+ #else
+ .datePickerStyle(.graphical)
+ #endif
Spacer()
Button() {
- eventDate = Date()
+ event.date = Date()
} label: {
Image(systemName: "arrow.uturn.left")
.resizable()
@@ -122,12 +108,15 @@ struct AddEventView: View {
DatePicker(
"",
- selection: $eventDate,
+ selection: $event.date,
displayedComponents: .hourAndMinute
)
+ #if os(macOS)
+ .datePickerStyle(.stepperField)
+ #endif
// re-ocurrence Picker
- Picker("Recurrence", selection: $eventRecurrence) {
+ Picker("Recurrence", selection: $event.recurrence) {
ForEach(Event.RecurrenceType.allCases, id: \.self) { recurrence in
Text(recurrence.rawValue.capitalized)
}
@@ -135,61 +124,44 @@ struct AddEventView: View {
.pickerStyle(SegmentedPickerStyle())
Text(
describeOccurrence(
- date: eventDate,
- recurrence: eventRecurrence
+ date: event.date,
+ recurrence: event.recurrence
)
)
}
}
- .scrollContentBackground(.hidden)
.navigationTitle("\(adding ? "Add Event" : "")")
- .navigationBarTitleDisplayMode(.inline)
+ .modifier(navigationInlineLarge())
.toolbar {
- ToolbarItem(placement: .topBarLeading) {
+ ToolbarItem(placement: .cancellationAction) {
if adding {
Button() {
resetAddEventView()
dismiss()
} label: {
- Image(systemName: "xmark.circle.fill")
- .symbolRenderingMode(.hierarchical)
+ Image(systemName: "xmark")
.resizable()
.scaledToFit()
- .frame(width: 30)
+ .tint(.one)
}
}
}
- ToolbarItem(placement: .topBarTrailing) {
+ ToolbarItem() {
if adding {
Button {
viewModel.addEvent(
- newEvent: Event(
- name: eventName,
- complete: eventComplete,
- completeDesc: eventCompleteDesc,
- symbol: eventSymbol,
- color: ColorCodable(eventColor),
- notes: eventNotes,
- date: eventDate,
- recurrence: eventRecurrence
- )
+ newEvent: event
)
bye.toggle()
resetAddEventView()
} label: {
- Text("Save")
- .font(.headline)
- .cornerRadius(10)
- .buttonStyle(BorderedProminentButtonStyle())
+ Label("Save", systemImage: "checkmark")
}
- .apply {
- if #available(iOS 17, *) {
- $0.sensoryFeedback(.success, trigger: bye)
- }
- }
- .disabled(eventName.isEmpty)
+ .tint(.accent)
+ .modifier(hapticSuccess(trigger: bye))
+ .disabled(event.name.isEmpty)
.onTapGesture {
- if eventName.isEmpty {
+ if event.name.isEmpty {
showNeedsNameAlert.toggle()
}
}
@@ -201,36 +173,29 @@ struct AddEventView: View {
} message: {
Text("Give your Event a name before saving.")
}
- if eventName.isEmpty {
- HStack {
- Image(systemName: "exclamationmark")
- .foregroundStyle(.red)
- Text("Give your event a name.")
- }
+ }
+ }
+ ToolbarItem(placement: .confirmationAction) {
+ if !adding {
+ Button() {
+ viewModel.editEvent(event)
+ dismiss()
+ } label: {
+ Label("Done", systemImage: "checkmark")
}
+ .disabled(event.name == "")
}
}
}
+ .navigationTitle("Editing \(event.name) - Ne")
}
+ .scrollContentBackground(isMac ? .automatic : .hidden)
.presentationDragIndicator(.visible)
- .scrollContentBackground(.hidden)
- }
- .apply {
- if #available(iOS 16.4, *) {
- $0.presentationBackground(.ultraThinMaterial)
- }
}
}
func resetAddEventView() {
//reset addeventView
- eventName = viewModel.template.name
- eventComplete = viewModel.template.complete
- eventCompleteDesc = viewModel.template.completeDesc
- eventSymbol = viewModel.template.symbol
- eventColor = randomColor()
- eventNotes = viewModel.template.notes
- eventDate = viewModel.template.date
- eventRecurrence = viewModel.template.recurrence
+ event = viewModel.template
dismiss()
}
@@ -243,14 +208,6 @@ struct AddEventView: View {
.sheet(isPresented: .constant(true)) {
AddEventView(
viewModel: vm,
- eventName: .constant(vm.template.notes),
- eventComplete: .constant(vm.template.complete),
- eventCompleteDesc: .constant(vm.template.completeDesc),
- eventSymbol: .constant(vm.template.symbol),
- eventColor: .constant(vm.template.color.color),
- eventNotes: .constant(vm.template.notes),
- eventDate: .constant(vm.template.date),
- eventRecurrence: .constant(vm.template.recurrence),
adding: true
)
}
diff --git a/NearFuture/Views/Events/EditEventView.swift b/NearFuture/Views/Events/EditEventView.swift
index 0cea627..6962920 100644
--- a/NearFuture/Views/Events/EditEventView.swift
+++ b/NearFuture/Views/Events/EditEventView.swift
@@ -12,44 +12,13 @@ struct EditEventView: View {
@ObservedObject var viewModel: EventViewModel
@Binding var event: Event
- fileprivate func saveEdits() {
- //if there is an event in vM.events with the id of the event we r editing,
- //firstindex - loops through the arr and finds first element where that events id matches editing event's id
- if let index = viewModel.events.firstIndex(where: { xEvent in
- xEvent.id == event.id
- }) {
- viewModel.events[index] = event
- }
- viewModel.saveEvents()
-
- dismiss()
- }
-
var body: some View {
AddEventView(
viewModel: viewModel,
- eventName: $event.name,
- eventComplete: $event.complete,
- eventCompleteDesc: $event.completeDesc,
- eventSymbol: $event.symbol,
- eventColor: $event.color.colorBind,
- eventNotes: $event.notes,
- eventDate: $event.date,
- eventRecurrence: $event.recurrence,
+ event: event,
adding: false //bc we editing existing event
)
.navigationTitle("Edit Event")
- .toolbar {
- ToolbarItem(placement: .topBarTrailing) {
- Button() {
- saveEdits()
- } label: {
- Text("Done")
- .bold()
- }
- .disabled(event.name == "")
- }
- }
}
}
diff --git a/NearFuture/Views/Home/EventListView.swift b/NearFuture/Views/Home/EventListView.swift
index 32c393c..5ec6a8c 100644
--- a/NearFuture/Views/Home/EventListView.swift
+++ b/NearFuture/Views/Home/EventListView.swift
@@ -12,141 +12,185 @@ struct EventListView: View {
@ObservedObject var viewModel: EventViewModel
@State var event: Event
+ @Environment(\.openWindow) var openWindow
+
+ @State var hovering: Bool = false
+
+#if canImport(AppKit)
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
- )
+ ZStack {
+ Color.black.opacity(hovering ? 0.5 : 0.0)
+ 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
- )
+ )
+ 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
)
+ Text("\(event.name)")
+ .bold()
+ .foregroundStyle(.one)
+ .strikethrough(event.complete)
+ .multilineTextAlignment(.leading)
+ }
+ if !event.notes.isEmpty {
+ Text(event.notes)
+ .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)")
+ .multilineTextAlignment(.trailing)
+ .foregroundStyle(event.date.timeIntervalSinceNow < 0 ? .red : .one)
+ }
+ CompleteEventButton(
+ viewModel: viewModel,
+ event: $event
+ )
+ }
+ .fixedSize(horizontal: false, vertical: true)
+ }
+ .onHover { isHovering in
+ withAnimation {
+ hovering.toggle()
+ }
+ }
+ .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 {
+ 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
)
)
- 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(event.date.timeIntervalSinceNow < 0 ? .red : .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)
- .apply {
- if #available(iOS 17, *) {
- $0.sensoryFeedback(.success, trigger: event.complete)
- }
- }
+ Text("\(event.name)")
+ .font(.headline)
+ .foregroundStyle(.one)
+ .strikethrough(event.complete)
+ .multilineTextAlignment(.leading)
}
- .transition(.opacity)
- .padding(.vertical, 5)
- .background(.ultraThinMaterial)
- .overlay(
- RoundedRectangle(cornerRadius: 10)
- .stroke(
- .one.opacity(0.5),
- lineWidth: 1
- )
+ if !event.notes.isEmpty {
+ Text(event.notes)
+ .font(.subheadline)
+ .foregroundStyle(.one.opacity(0.8))
+ .multilineTextAlignment(.leading)
+ }
+ Text(
+ event.date.formatted(
+ date: .long,
+ time: .shortened
+ )
)
- .clipShape(
- RoundedRectangle(cornerRadius: 10)
+ .font(.subheadline)
+ .foregroundStyle(
+ .one.opacity(
+ event.complete ? 0.5 : 1
+ )
)
- .fixedSize(horizontal: false, vertical: true)
+ if event.recurrence != .none {
+ Text("Occurs \(event.recurrence.rawValue)")
+ .font(.subheadline)
+ .foregroundStyle(
+ .one.opacity(event.complete ? 0.5 : 1))
+ }
}
- .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")
+ Spacer()
+ VStack {
+ Text("\(daysUntilEvent(event.date).long)")
+ .font(.subheadline)
+ .foregroundStyle(event.date.timeIntervalSinceNow < 0 ? .red : .one)
+ .multilineTextAlignment(.trailing)
+ }
+ CompleteEventButton(
+ viewModel: viewModel,
+ event: $event
+ )
+ }
+ .padding(.vertical, 5)
+ .overlay(
+ RoundedRectangle(cornerRadius: 15)
+ .stroke(.one.opacity(0.5), lineWidth: 1)
+ )
+ .clipShape(RoundedRectangle(cornerRadius: 15))
+ .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")
}
}
}
+#endif
}
#Preview("EventListView") {
diff --git a/NearFuture/Views/Home/HelpView.swift b/NearFuture/Views/Home/HelpView.swift
index 24d4909..3958e8d 100644
--- a/NearFuture/Views/Home/HelpView.swift
+++ b/NearFuture/Views/Home/HelpView.swift
@@ -13,6 +13,10 @@ enum HelpType {
case Archive
}
+enum Field {
+ case Search
+}
+
struct HelpView: View {
/// initialises a Search HelpView
///
@@ -74,7 +78,7 @@ struct HelpView: View {
var body: some View {
List {
ZStack {
- Color(.tintColor)
+ Color(.accent)
.opacity(0.4)
.padding(.horizontal, -15)
.blur(radius: 5)
diff --git a/NearFuture/Views/Home/HomeView.swift b/NearFuture/Views/Home/HomeView.swift
index 56741cf..8f77c9e 100644
--- a/NearFuture/Views/Home/HomeView.swift
+++ b/NearFuture/Views/Home/HomeView.swift
@@ -5,20 +5,14 @@
// Created by neon443 on 12/05/2025.
//
-import SwiftUI;import AppIntents
+import SwiftUI
+import AppIntents
struct HomeView: View {
@ObservedObject var viewModel: EventViewModel
@ObservedObject var settingsModel: SettingsViewModel
- @State private var eventName = ""
- @State private var eventComplete = false
- @State private var eventCompleteDesc = ""
- @State private var eventSymbol = "star"
- @State private var eventColor: Color = randomColor()
- @State private var eventNotes = ""
- @State private var eventDate = Date()
- @State private var eventRecurrence: Event.RecurrenceType = .none
- @State private var showingAddEventView = false
+
+ @State private var showingAddEventView: Bool = false
@State private var searchInput: String = ""
@Environment(\.colorScheme) var appearance
var darkMode: Bool {
@@ -47,36 +41,30 @@ struct HomeView: View {
ZStack {
backgroundGradient
VStack {
- ZStack {
- TextField(
- "\(Image(systemName: "magnifyingglass")) Search",
- text: $searchInput
- )
- .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)
- .onTapGesture {
- focusedField = nil
- }
- }
- .padding(.horizontal)
-
if filteredEvents.isEmpty && !searchInput.isEmpty {
HelpView(searchInput: $searchInput, focusedField: focusedField)
} else {
ScrollView {
- ForEach(filteredEvents) { event in
- EventListView(viewModel: viewModel, event: event)
+// LazyVStack {
+ ForEach(filteredEvents) { event in
+ NavigationLink() {
+ EditEventView(
+ viewModel: viewModel,
+ event: Binding(
+ get: { event },
+ set: { newValue in
+ viewModel.editEvent(newValue)
+ }
+ )
+ )
+ } label: {
+ EventListView(viewModel: viewModel, event: event)
+ .id(event.complete)
+ }
.transition(.moveAndFade)
- .id(event.complete)
- }
- .padding(.horizontal)
+ }
+ .padding(.horizontal)
+// }
if filteredEvents.isEmpty {
HelpView(
searchInput: $searchInput,
@@ -88,30 +76,16 @@ struct HomeView: View {
.animation(.default, value: filteredEvents)
}
}
+ .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,
- eventName: $eventName,
- eventComplete: $eventComplete,
- eventCompleteDesc: $eventCompleteDesc,
- eventSymbol: $eventSymbol,
- eventColor: $eventColor,
- eventNotes: $eventNotes,
- eventDate: $eventDate,
- eventRecurrence: $eventRecurrence,
- adding: true //adding event
+ viewModel: viewModel
)
}
.toolbar {
- ToolbarItem(placement: .topBarTrailing) {
+ ToolbarItem(placement: .primaryAction) {
AddEventButton(showingAddEventView: $showingAddEventView)
}
}
diff --git a/NearFuture/Views/Misc/Buttons.swift b/NearFuture/Views/Misc/Buttons.swift
index 48a447b..313a631 100644
--- a/NearFuture/Views/Misc/Buttons.swift
+++ b/NearFuture/Views/Misc/Buttons.swift
@@ -34,17 +34,12 @@ struct AddEventButton: View {
Button() {
showingAddEventView.toggle()
} label: {
- ZStack {
- Circle()
- .frame(width: 33)
- .foregroundStyle(.one)
- Image(systemName: "plus")
- .resizable()
- .scaledToFit()
- .frame(width: 15)
- .bold()
- .foregroundStyle(.two)
- }
+ Image(systemName: "plus")
+ .resizable()
+ .scaledToFit()
+ .frame(width: 15)
+ .bold()
+ .foregroundStyle(.one)
}
}
}
diff --git a/NearFuture/Views/Settings/ExportView.swift b/NearFuture/Views/Settings/ExportView.swift
index ef30633..9577b41 100644
--- a/NearFuture/Views/Settings/ExportView.swift
+++ b/NearFuture/Views/Settings/ExportView.swift
@@ -12,7 +12,11 @@ struct ExportView: View {
var body: some View {
List {
Button() {
+ #if canImport(UIKit)
UIPasteboard.general.string = viewModel.exportEvents()
+ #else
+ NSPasteboard.general.setString(viewModel.exportEvents(), forType: .string)
+ #endif
} label: {
Label("Copy Events", systemImage: "document.on.clipboard")
}
diff --git a/NearFuture/Views/Settings/ImportView.swift b/NearFuture/Views/Settings/ImportView.swift
index 6694076..b6cda9d 100644
--- a/NearFuture/Views/Settings/ImportView.swift
+++ b/NearFuture/Views/Settings/ImportView.swift
@@ -15,47 +15,117 @@ struct ImportView: View {
@State private var text: String = "Ready..."
@State private var fgColor: Color = .yellow
- var body: some View {
- List {
- Section("Status") {
- Label(text, systemImage: image)
- .contentTransition(.numericText())
- .foregroundStyle(fgColor)
+ @State private var showAlert: Bool = false
+
+ @State private var replaceCurrentEvents: Bool = false
+
+ fileprivate func importEvents() {
+ do throws {
+ try viewModel.importEvents(importStr, replace: replaceCurrentEvents)
+ withAnimation {
+ image = "checkmark.circle.fill"
+ text = "Complete"
+ fgColor = .green
}
- TextField("", text: $importStr)
- Button() {
- do throws {
- try viewModel.importEvents(importStr)
- withAnimation {
- image = "checkmark.circle.fill"
- text = "Complete"
- fgColor = .green
- }
- } catch importError.invalidB64 {
- withAnimation {
- image = "xmark.app.fill"
- text = "Invalid base64 input."
- fgColor = .red
- }
- } catch {
- withAnimation {
- image = "xmark.app.fill"
- text = error.localizedDescription
- fgColor = .red
- }
- }
- } label: {
- Label("Import", systemImage: "tray.and.arrow.down.fill")
+ } catch importError.invalidB64 {
+ withAnimation {
+ image = "xmark.app.fill"
+ text = "Invalid base64 input."
+ fgColor = .red
}
- .disabled(importStr.isEmpty)
- .onAppear() {
- importStr = ""
- image = "clock.fill"
- text = "Ready..."
- fgColor = .yellow
+ } catch {
+ withAnimation {
+ image = "xmark.app.fill"
+ text = error.localizedDescription
+ fgColor = .red
}
}
-
+ }
+
+ var body: some View {
+ ZStack(alignment: .center) {
+ List {
+ Section("Status") {
+ Label(text, systemImage: image)
+ .contentTransition(.numericText())
+ .foregroundStyle(fgColor)
+ }
+ TextField("", text: $importStr)
+ Button() {
+ withAnimation {
+ showAlert.toggle()
+ }
+ } label: {
+ Label("Import", systemImage: "tray.and.arrow.down.fill")
+ }
+ .disabled(importStr.isEmpty)
+ .onAppear() {
+ importStr = ""
+ image = "clock.fill"
+ text = "Ready..."
+ 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()
+ }
+ .frame(maxWidth: 250, maxHeight: 250)
+ }
+ .opacity(showAlert ? 1 : 0)
+ }
}
}
diff --git a/NearFuture/Views/Settings/SettingsView.swift b/NearFuture/Views/Settings/SettingsView.swift
index 9c3fcd7..e6a5fba 100644
--- a/NearFuture/Views/Settings/SettingsView.swift
+++ b/NearFuture/Views/Settings/SettingsView.swift
@@ -10,48 +10,21 @@ import SwiftUI
struct SettingsView: View {
@ObservedObject var viewModel: EventViewModel
@ObservedObject var settingsModel: SettingsViewModel
-
- @State private var hasUbiquitous: Bool = false
- @State private var lastSyncWasSuccessful: Bool = false
- @State private var lastSyncWasNormalAgo: Bool = false
- @State private var localCountEqualToiCloud: Bool = false
- @State private var icloudCountEqualToLocal: Bool = false
+
@State private var importStr: String = ""
- func updateStatus() {
- let vm = viewModel
- hasUbiquitous = vm.hasUbiquitousKeyValueStore()
- lastSyncWasSuccessful = vm.syncStatus.contains("Success")
- lastSyncWasNormalAgo = vm.lastSync?.timeIntervalSinceNow.isNormal ?? false
- localCountEqualToiCloud = vm.localEventCount == vm.icloudEventCount
- icloudCountEqualToLocal = vm.icloudEventCount == vm.localEventCount
- }
-
- var iCloudStatusColor: Color {
- let allTrue = hasUbiquitous && lastSyncWasSuccessful && lastSyncWasNormalAgo && localCountEqualToiCloud && icloudCountEqualToLocal
- let someTrue = hasUbiquitous || lastSyncWasSuccessful || lastSyncWasNormalAgo || localCountEqualToiCloud || icloudCountEqualToLocal
-
- if allTrue {
- return .green
- } else if someTrue {
- return .orange
- } else {
- return .red
- }
- }
-
- func changeIcon(to: String) {
+ func changeIcon(to toIcon: String) {
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)
}
}
@@ -74,6 +47,7 @@ struct SettingsView: View {
.foregroundStyle(color)
.frame(width: 30)
}
+ .buttonStyle(.plain)
if ColorCodable(color) == settingsModel.settings.tint {
let needContrast: Bool = ColorCodable(color) == settingsModel.settings.tint
Circle()
@@ -94,13 +68,16 @@ 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)
+ Button("Request Notifications") {
+ Task.detached {
+ let requestNotifsResult = await requestNotifs()
+ await MainActor.run {
+ settingsModel.notifsGranted = requestNotifsResult
+ }
+ }
+ }
} else {
Text("\(Image(systemName: "checkmark")) Notifications enabled for Near Future")
.foregroundStyle(.green)
@@ -113,13 +90,7 @@ struct SettingsView: View {
NavigationLink() {
iCloudSettingsView(
viewModel: viewModel,
- settingsModel: settingsModel,
- hasUbiquitous: $hasUbiquitous,
- lastSyncWasSuccessful: $lastSyncWasSuccessful,
- lastSyncWasNormalAgo: $lastSyncWasNormalAgo,
- localCountEqualToiCloud: $localCountEqualToiCloud,
- icloudCountEqualToLocal: $icloudCountEqualToLocal,
- updateStatus: updateStatus
+ settingsModel: settingsModel
)
} label: {
HStack {
@@ -128,12 +99,12 @@ struct SettingsView: View {
Spacer()
Circle()
.frame(width: 20, height: 20)
- .foregroundStyle(iCloudStatusColor)
+ .foregroundStyle(viewModel.iCloudStatusColor)
}
}
.onAppear {
viewModel.sync()
- updateStatus()
+ viewModel.updateiCStatus()
}
NavigationLink() {
ImportView(viewModel: viewModel, importStr: $importStr)
@@ -177,15 +148,9 @@ struct SettingsView: View {
}
}
}
- .scrollContentBackground(.hidden)
.navigationTitle("Settings")
- .apply {
- if #available(iOS 17, *) {
- $0.toolbarTitleDisplayMode(.inlineLarge)
- } else {
- $0.navigationBarTitleDisplayMode(.inline)
- }
- }
+ .modifier(navigationInlineLarge())
+ .scrollContentBackground(.hidden)
}
}
}
diff --git a/NearFuture/Views/Settings/WhatsNewView.swift b/NearFuture/Views/Settings/WhatsNewView.swift
index efa8350..ca709d1 100644
--- a/NearFuture/Views/Settings/WhatsNewView.swift
+++ b/NearFuture/Views/Settings/WhatsNewView.swift
@@ -16,9 +16,14 @@ struct WhatsNewView: View {
var title: String
var subtitle: String
}
- @State var bye = false
+ @State var bye: Bool = false
var whatsNewChunks: [WhatsNewChunk] {
return [
+ WhatsNewChunk(
+ symbol: "desktopcomputer",
+ title: "Mac Native App",
+ subtitle: "New Mac native app (Intel too!)"
+ ),
WhatsNewChunk(
symbol: "iphone.radiowaves.left.and.right",
title: "Haptic Feedback",
@@ -53,23 +58,22 @@ struct WhatsNewView: View {
}
var body: some View {
NavigationStack {
- List {
- VStack {
- Text("What's New")
- .font(.largeTitle)
- .bold()
- AboutView()
- Divider()
- VStack(alignment: .leading) {
- ForEach(whatsNewChunks) { new in
- WhatsNewChunkView(
- symbol: new.symbol,
- title: new.title,
- subtitle: new.subtitle
- )
- }
+ ScrollView {
+ Text("What's New")
+ .font(.largeTitle)
+ .bold()
+ .padding(.vertical)
+ VStack(alignment: .leading) {
+ ForEach(whatsNewChunks) { new in
+ WhatsNewChunkView(
+ symbol: new.symbol,
+ title: new.title,
+ subtitle: new.subtitle
+ )
}
+ Spacer()
}
+ .padding(.horizontal, 10)
}
Button() {
bye.toggle()
@@ -79,24 +83,14 @@ struct WhatsNewView: View {
.font(.headline)
.frame(height: 40)
.bold()
- .frame(maxWidth: .infinity)
- }
- .buttonStyle(BorderedProminentButtonStyle())
- .clipShape(RoundedRectangle(cornerRadius: 15))
- .padding().padding()
- .apply {
- if #available(iOS 17, *) {
- $0.sensoryFeedback(.impact(weight: .heavy, intensity: 1), trigger: bye)
- }
+// .frame(maxWidth: .infinity)
}
+ .foregroundStyle(.orange)
+ .modifier(glassButton())
+ .modifier(hapticHeavy(trigger: bye))
}
.scrollContentBackground(.hidden)
.presentationDragIndicator(.visible)
- .apply {
- if #available(iOS 16.4, *) {
- $0.presentationBackground(.ultraThinMaterial)
- }
- }
.onDisappear {
settingsModel.settings.prevAppVersion = getVersion()+getBuildID()
settingsModel.saveSettings()
@@ -122,7 +116,7 @@ struct WhatsNewChunkView: View {
.resizable()
.scaledToFit()
.frame(width: 30, height: 30)
- .foregroundStyle(Color.accentColor)
+ .foregroundStyle(Color.orange)
.padding(.trailing, 15)
VStack(alignment: .leading) {
Text(title)
diff --git a/NearFuture/Views/Settings/iCloudSettingsView.swift b/NearFuture/Views/Settings/iCloudSettingsView.swift
index 973e803..72cb561 100644
--- a/NearFuture/Views/Settings/iCloudSettingsView.swift
+++ b/NearFuture/Views/Settings/iCloudSettingsView.swift
@@ -13,13 +13,13 @@ struct iCloudSettingsView: View {
@State var showPushAlert: Bool = false
@State var showPullAlert: Bool = false
- @Binding var hasUbiquitous: Bool
- @Binding var lastSyncWasSuccessful: Bool
- @Binding var lastSyncWasNormalAgo: Bool
- @Binding var localCountEqualToiCloud: Bool
- @Binding var icloudCountEqualToLocal: Bool
-
- var updateStatus: () -> Void
+// @Binding var hasUbiquitous: Bool
+// @Binding var lastSyncWasSuccessful: Bool
+// @Binding var lastSyncWasNormalAgo: Bool
+// @Binding var localCountEqualToiCloud: Bool
+// @Binding var icloudCountEqualToLocal: Bool
+//
+// var updateStatus: () -> Void
var body: some View {
ZStack {
@@ -55,7 +55,7 @@ struct iCloudSettingsView: View {
Button("OK", role: .destructive) {
viewModel.replaceiCloudWithLocalData()
viewModel.sync()
- updateStatus()
+ viewModel.updateiCStatus()
}
Button("Cancel", role: .cancel) {}
} message: {
@@ -64,7 +64,7 @@ struct iCloudSettingsView: View {
Button() {
viewModel.sync()
- updateStatus()
+ viewModel.updateiCStatus()
} label: {
Image(systemName: "arrow.triangle.2.circlepath")
.resizable()
@@ -87,7 +87,7 @@ struct iCloudSettingsView: View {
Button("OK", role: .destructive) {
viewModel.replaceLocalWithiCloudData()
viewModel.sync()
- updateStatus()
+ viewModel.updateiCStatus()
}
Button("Cancel", role: .cancel) {}
} message: {
@@ -112,23 +112,23 @@ struct iCloudSettingsView: View {
.listRowSeparator(.hidden)
.onAppear {
viewModel.sync()
- updateStatus()
+ viewModel.updateiCStatus()
}
HStack {
Circle()
.frame(width: 20, height: 20)
- .foregroundStyle(hasUbiquitous ? .green : .red)
+ .foregroundStyle(viewModel.hasUbiquitous ? .green : .red)
Text("iCloud")
Spacer()
- Text("\(hasUbiquitous ? "" : "Not ")Working")
+ Text("\(viewModel.hasUbiquitous ? "" : "Not ")Working")
.bold()
}
HStack {
Circle()
.frame(width: 20, height: 20)
- .foregroundStyle(lastSyncWasSuccessful ? .green : .red)
+ .foregroundStyle(viewModel.lastSyncWasSuccessful ? .green : .red)
Text("Sync Status")
Spacer()
Text("\(viewModel.syncStatus)")
@@ -138,7 +138,7 @@ struct iCloudSettingsView: View {
HStack {
Circle()
.frame(width: 20, height: 20)
- .foregroundStyle(lastSyncWasNormalAgo ? .green : .red)
+ .foregroundStyle(viewModel.lastSyncWasNormalAgo ? .green : .red)
Text("Last Sync")
Spacer()
Text("\(viewModel.lastSync?.formatted(date: .long, time: .standard) ?? "Never")")
@@ -148,7 +148,7 @@ struct iCloudSettingsView: View {
HStack {
Circle()
.frame(width: 20, height: 20)
- .foregroundStyle(localCountEqualToiCloud ? .green : .red)
+ .foregroundStyle(viewModel.localCountEqualToiCloud ? .green : .red)
Text("Local Events")
Spacer()
Text("\(viewModel.localEventCount)")
@@ -158,7 +158,7 @@ struct iCloudSettingsView: View {
HStack {
Circle()
.frame(width: 20, height: 20)
- .foregroundStyle(icloudCountEqualToLocal ? .green : .red)
+ .foregroundStyle(viewModel.icloudCountEqualToLocal ? .green : .red)
Text("Events in iCloud")
Spacer()
Text("\(viewModel.icloudEventCount)")
@@ -172,11 +172,10 @@ struct iCloudSettingsView: View {
}
.refreshable {
viewModel.sync()
- updateStatus()
+ viewModel.updateiCStatus()
}
.scrollContentBackground(.hidden)
.navigationTitle("iCloud")
- .navigationBarTitleDisplayMode(.inline)
}
}
}
@@ -184,12 +183,6 @@ struct iCloudSettingsView: View {
#Preview("iCloudSettingsView") {
iCloudSettingsView(
viewModel: dummyEventViewModel(),
- settingsModel: dummySettingsViewModel(),
- hasUbiquitous: .constant(true),
- lastSyncWasSuccessful: .constant(true),
- lastSyncWasNormalAgo: .constant(true),
- localCountEqualToiCloud: .constant(true),
- icloudCountEqualToLocal: .constant(true),
- updateStatus: {}
+ settingsModel: dummySettingsViewModel()
)
}
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/NearFutureTests/NearFutureTests.swift b/NearFutureTests/NearFutureTests.swift
deleted file mode 100644
index 06165fc..0000000
--- a/NearFutureTests/NearFutureTests.swift
+++ /dev/null
@@ -1,16 +0,0 @@
-//
-// NearFutureTests.swift
-// NearFutureTests
-//
-// Created by neon443 on 26/03/2025.
-//
-
-import Testing
-
-struct NearFutureTests {
-
- @Test func example() async throws {
- // Write your test here and use APIs like `#expect(...)` to check expected conditions.
- }
-
-}
diff --git a/README.md b/README.md
index 0048fa8..99f2c47 100644
--- a/README.md
+++ b/README.md
@@ -29,17 +29,18 @@
- [x] Event colors
- [x] Recurrence
- [x] Search
-- [ ] Notifications
+- [x] Notifications
+- [ ] Mac App
- [ ] Apple Watch App
- [x] Home Screen Widgets
- [ ] Lock Screen Widgets
- [ ] Later Box
- [ ] Sort by
- [ ] Reorder Events
-- [ ] Archive
+- [x] Archive
- [ ] Collaboration
- [ ] Autocomplete tasks
-- [ ] Settings
+- [x] Settings
## Features
- **Event Creation**: Create events with a name, description, date, recurrence, and an icon
diff --git a/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json b/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json
index 1e9333b..6f663e8 100644
--- a/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json
+++ b/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -31,54 +31,70 @@
"size" : "1024x1024"
},
{
+ "filename" : "MacNearFutureIcon16.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "16x16"
},
{
+ "filename" : "MacNearFutureIcon32.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "16x16"
},
{
+ "filename" : "MacNearFutureIcon32.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "32x32"
},
{
+ "filename" : "MacNearFutureIcon64.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "32x32"
},
{
+ "filename" : "MacNearFutureIcon128.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "128x128"
},
{
+ "filename" : "MacNearFutureIcon256.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "128x128"
},
{
+ "filename" : "MacNearFutureIcon256.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "256x256"
},
{
+ "filename" : "MacNearFutureIcon512.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "256x256"
},
{
+ "filename" : "MacNearFutureIcon512.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "512x512"
},
{
+ "filename" : "MacNearFutureIcon.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "512x512"
+ },
+ {
+ "filename" : "NearFutureIcon.png",
+ "idiom" : "universal",
+ "platform" : "watchos",
+ "size" : "1024x1024"
}
],
"info" : {
diff --git a/Resources/Assets.xcassets/AppIcon.appiconset/MacNearFutureIcon.png b/Resources/Assets.xcassets/AppIcon.appiconset/MacNearFutureIcon.png
new file mode 100644
index 0000000..e479390
Binary files /dev/null and b/Resources/Assets.xcassets/AppIcon.appiconset/MacNearFutureIcon.png differ
diff --git a/Resources/Assets.xcassets/AppIcon.appiconset/MacNearFutureIcon128.png b/Resources/Assets.xcassets/AppIcon.appiconset/MacNearFutureIcon128.png
new file mode 100644
index 0000000..07930d2
Binary files /dev/null and b/Resources/Assets.xcassets/AppIcon.appiconset/MacNearFutureIcon128.png differ
diff --git a/Resources/Assets.xcassets/AppIcon.appiconset/MacNearFutureIcon16.png b/Resources/Assets.xcassets/AppIcon.appiconset/MacNearFutureIcon16.png
new file mode 100644
index 0000000..18566cc
Binary files /dev/null and b/Resources/Assets.xcassets/AppIcon.appiconset/MacNearFutureIcon16.png differ
diff --git a/Resources/Assets.xcassets/AppIcon.appiconset/MacNearFutureIcon256.png b/Resources/Assets.xcassets/AppIcon.appiconset/MacNearFutureIcon256.png
new file mode 100644
index 0000000..0cf18ea
Binary files /dev/null and b/Resources/Assets.xcassets/AppIcon.appiconset/MacNearFutureIcon256.png differ
diff --git a/Resources/Assets.xcassets/AppIcon.appiconset/MacNearFutureIcon32.png b/Resources/Assets.xcassets/AppIcon.appiconset/MacNearFutureIcon32.png
new file mode 100644
index 0000000..40baa3e
Binary files /dev/null and b/Resources/Assets.xcassets/AppIcon.appiconset/MacNearFutureIcon32.png differ
diff --git a/Resources/Assets.xcassets/AppIcon.appiconset/MacNearFutureIcon512.png b/Resources/Assets.xcassets/AppIcon.appiconset/MacNearFutureIcon512.png
new file mode 100644
index 0000000..6bab581
Binary files /dev/null and b/Resources/Assets.xcassets/AppIcon.appiconset/MacNearFutureIcon512.png differ
diff --git a/Resources/Assets.xcassets/AppIcon.appiconset/MacNearFutureIcon64.png b/Resources/Assets.xcassets/AppIcon.appiconset/MacNearFutureIcon64.png
new file mode 100644
index 0000000..95b823f
Binary files /dev/null and b/Resources/Assets.xcassets/AppIcon.appiconset/MacNearFutureIcon64.png differ
diff --git a/Resources/Assets.xcassets/bloo.appiconset/Contents.json b/Resources/Assets.xcassets/bloo.appiconset/Contents.json
index 5e8bb49..ee8f596 100644
--- a/Resources/Assets.xcassets/bloo.appiconset/Contents.json
+++ b/Resources/Assets.xcassets/bloo.appiconset/Contents.json
@@ -29,6 +29,12 @@
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
+ },
+ {
+ "filename" : "bloo.png",
+ "idiom" : "universal",
+ "platform" : "watchos",
+ "size" : "1024x1024"
}
],
"info" : {
diff --git a/Resources/Assets.xcassets/blue.appiconset/Contents.json b/Resources/Assets.xcassets/blue.appiconset/Contents.json
index 685a600..5a7fd5f 100644
--- a/Resources/Assets.xcassets/blue.appiconset/Contents.json
+++ b/Resources/Assets.xcassets/blue.appiconset/Contents.json
@@ -29,6 +29,12 @@
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
+ },
+ {
+ "filename" : "blue.png",
+ "idiom" : "universal",
+ "platform" : "watchos",
+ "size" : "1024x1024"
}
],
"info" : {
diff --git a/Resources/Assets.xcassets/green.appiconset/Contents.json b/Resources/Assets.xcassets/green.appiconset/Contents.json
index 81b9d10..a14471a 100644
--- a/Resources/Assets.xcassets/green.appiconset/Contents.json
+++ b/Resources/Assets.xcassets/green.appiconset/Contents.json
@@ -29,6 +29,12 @@
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
+ },
+ {
+ "filename" : "green.png",
+ "idiom" : "universal",
+ "platform" : "watchos",
+ "size" : "1024x1024"
}
],
"info" : {
diff --git a/Resources/Assets.xcassets/pink.appiconset/Contents.json b/Resources/Assets.xcassets/pink.appiconset/Contents.json
index 15df7dc..3093a8a 100644
--- a/Resources/Assets.xcassets/pink.appiconset/Contents.json
+++ b/Resources/Assets.xcassets/pink.appiconset/Contents.json
@@ -29,6 +29,12 @@
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
+ },
+ {
+ "filename" : "pink.png",
+ "idiom" : "universal",
+ "platform" : "watchos",
+ "size" : "1024x1024"
}
],
"info" : {
diff --git a/Resources/Assets.xcassets/purple.appiconset/Contents.json b/Resources/Assets.xcassets/purple.appiconset/Contents.json
index f75afde..d6161f4 100644
--- a/Resources/Assets.xcassets/purple.appiconset/Contents.json
+++ b/Resources/Assets.xcassets/purple.appiconset/Contents.json
@@ -29,6 +29,12 @@
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
+ },
+ {
+ "filename" : "purple.png",
+ "idiom" : "universal",
+ "platform" : "watchos",
+ "size" : "1024x1024"
}
],
"info" : {
diff --git a/Resources/Assets.xcassets/red.appiconset/Contents.json b/Resources/Assets.xcassets/red.appiconset/Contents.json
index eb3ee18..a33e2c5 100644
--- a/Resources/Assets.xcassets/red.appiconset/Contents.json
+++ b/Resources/Assets.xcassets/red.appiconset/Contents.json
@@ -29,6 +29,12 @@
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
+ },
+ {
+ "filename" : "red.png",
+ "idiom" : "universal",
+ "platform" : "watchos",
+ "size" : "1024x1024"
}
],
"info" : {
diff --git a/Resources/Assets.xcassets/yellow.appiconset/Contents.json b/Resources/Assets.xcassets/yellow.appiconset/Contents.json
index ae6c02e..87f4f9c 100644
--- a/Resources/Assets.xcassets/yellow.appiconset/Contents.json
+++ b/Resources/Assets.xcassets/yellow.appiconset/Contents.json
@@ -29,6 +29,12 @@
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
+ },
+ {
+ "filename" : "yellow.png",
+ "idiom" : "universal",
+ "platform" : "watchos",
+ "size" : "1024x1024"
}
],
"info" : {
diff --git a/Resources/MacNearFutureIcon.pxd b/Resources/MacNearFutureIcon.pxd
new file mode 100644
index 0000000..01b0d17
Binary files /dev/null and b/Resources/MacNearFutureIcon.pxd differ
diff --git a/Resources/NearFuture.icon/Assets/Oval copy.png b/Resources/NearFuture.icon/Assets/Oval copy.png
new file mode 100644
index 0000000..f377ee4
Binary files /dev/null and b/Resources/NearFuture.icon/Assets/Oval copy.png differ
diff --git a/Resources/NearFuture.icon/Assets/Rounded Rectangle.png b/Resources/NearFuture.icon/Assets/Rounded Rectangle.png
new file mode 100644
index 0000000..7dadcb0
Binary files /dev/null and b/Resources/NearFuture.icon/Assets/Rounded Rectangle.png differ
diff --git a/Resources/NearFuture.icon/icon.json b/Resources/NearFuture.icon/icon.json
new file mode 100644
index 0000000..8a60fd5
--- /dev/null
+++ b/Resources/NearFuture.icon/icon.json
@@ -0,0 +1,50 @@
+{
+ "fill" : {
+ "linear-gradient" : [
+ "display-p3:0.12307,0.73184,0.43944,1.00000",
+ "display-p3:0.12142,0.28022,0.58006,1.00000"
+ ]
+ },
+ "groups" : [
+ {
+ "layers" : [
+ {
+ "fill" : {
+ "automatic-gradient" : "display-p3:1.00000,0.30347,0.00000,1.00000"
+ },
+ "image-name" : "Rounded Rectangle.png",
+ "name" : "Rounded Rectangle",
+ "position" : {
+ "scale" : 1,
+ "translation-in-points" : [
+ -466.2265625,
+ 37.9921875
+ ]
+ }
+ },
+ {
+ "fill" : {
+ "automatic-gradient" : "display-p3:0.96611,0.36599,0.00000,1.00000"
+ },
+ "hidden" : false,
+ "image-name" : "Oval copy.png",
+ "name" : "Oval copy"
+ }
+ ],
+ "shadow" : {
+ "kind" : "neutral",
+ "opacity" : 0.5
+ },
+ "translucency" : {
+ "enabled" : true,
+ "value" : 0.5
+ }
+ }
+ ],
+ "supported-platforms" : {
+ "circles" : [
+ "watchOS"
+ ],
+ "squares" : "shared"
+ }
+}
\ No newline at end of file
diff --git a/NearFuture/Preview Content/NearFutureWidgets/Assets.xcassets/Contents.json b/Resources/Tints.xcassets/Contents.json
similarity index 100%
rename from NearFuture/Preview Content/NearFutureWidgets/Assets.xcassets/Contents.json
rename to Resources/Tints.xcassets/Contents.json
diff --git a/Resources/Assets.xcassets/one.colorset/Contents.json b/Resources/Tints.xcassets/one.colorset/Contents.json
similarity index 100%
rename from Resources/Assets.xcassets/one.colorset/Contents.json
rename to Resources/Tints.xcassets/one.colorset/Contents.json
diff --git a/Resources/Assets.xcassets/two.colorset/Contents.json b/Resources/Tints.xcassets/two.colorset/Contents.json
similarity index 100%
rename from Resources/Assets.xcassets/two.colorset/Contents.json
rename to Resources/Tints.xcassets/two.colorset/Contents.json
diff --git a/Resources/Assets.xcassets/uiColors/Contents.json b/Resources/Tints.xcassets/uiColors/Contents.json
similarity index 100%
rename from Resources/Assets.xcassets/uiColors/Contents.json
rename to Resources/Tints.xcassets/uiColors/Contents.json
diff --git a/Resources/Assets.xcassets/uiColors/bloo.colorset/Contents.json b/Resources/Tints.xcassets/uiColors/bloo.colorset/Contents.json
similarity index 100%
rename from Resources/Assets.xcassets/uiColors/bloo.colorset/Contents.json
rename to Resources/Tints.xcassets/uiColors/bloo.colorset/Contents.json
diff --git a/Resources/Assets.xcassets/uiColors/blue.colorset/Contents.json b/Resources/Tints.xcassets/uiColors/blue.colorset/Contents.json
similarity index 100%
rename from Resources/Assets.xcassets/uiColors/blue.colorset/Contents.json
rename to Resources/Tints.xcassets/uiColors/blue.colorset/Contents.json
diff --git a/Resources/Assets.xcassets/uiColors/green.colorset/Contents.json b/Resources/Tints.xcassets/uiColors/green.colorset/Contents.json
similarity index 100%
rename from Resources/Assets.xcassets/uiColors/green.colorset/Contents.json
rename to Resources/Tints.xcassets/uiColors/green.colorset/Contents.json
diff --git a/Resources/Assets.xcassets/uiColors/orange.colorset/Contents.json b/Resources/Tints.xcassets/uiColors/orange.colorset/Contents.json
similarity index 100%
rename from Resources/Assets.xcassets/uiColors/orange.colorset/Contents.json
rename to Resources/Tints.xcassets/uiColors/orange.colorset/Contents.json
diff --git a/Resources/Assets.xcassets/uiColors/pink.colorset/Contents.json b/Resources/Tints.xcassets/uiColors/pink.colorset/Contents.json
similarity index 100%
rename from Resources/Assets.xcassets/uiColors/pink.colorset/Contents.json
rename to Resources/Tints.xcassets/uiColors/pink.colorset/Contents.json
diff --git a/Resources/Assets.xcassets/uiColors/purple.colorset/Contents.json b/Resources/Tints.xcassets/uiColors/purple.colorset/Contents.json
similarity index 100%
rename from Resources/Assets.xcassets/uiColors/purple.colorset/Contents.json
rename to Resources/Tints.xcassets/uiColors/purple.colorset/Contents.json
diff --git a/Resources/Assets.xcassets/uiColors/red.colorset/Contents.json b/Resources/Tints.xcassets/uiColors/red.colorset/Contents.json
similarity index 100%
rename from Resources/Assets.xcassets/uiColors/red.colorset/Contents.json
rename to Resources/Tints.xcassets/uiColors/red.colorset/Contents.json
diff --git a/Resources/Assets.xcassets/uiColors/yellow.colorset/Contents.json b/Resources/Tints.xcassets/uiColors/yellow.colorset/Contents.json
similarity index 100%
rename from Resources/Assets.xcassets/uiColors/yellow.colorset/Contents.json
rename to Resources/Tints.xcassets/uiColors/yellow.colorset/Contents.json
diff --git a/Shared/CompleteEventButton.swift b/Shared/CompleteEventButton.swift
new file mode 100644
index 0000000..985fc00
--- /dev/null
+++ b/Shared/CompleteEventButton.swift
@@ -0,0 +1,129 @@
+//
+// CompleteEventButton.swift
+// NearFuture
+//
+// Created by neon443 on 15/06/2025.
+//
+
+import SwiftUI
+
+struct CompleteEventButton: View {
+ @ObservedObject var viewModel: EventViewModel
+ @Binding var event: Event
+
+ @State var timer: Timer?
+ @State var largeTick: Bool = false
+ @State var completeInProgress: Bool = false
+ @State var completeStartTime: Date = .now
+ @State var progress: Double = 0
+ private let completeDuration: TimeInterval = 3.0
+
+ var isMac: Bool {
+#if canImport(AppKit)
+ return true
+#else
+ return false
+#endif
+ }
+
+ func startCompleting() {
+#if canImport(UIKit)
+ UIImpactFeedbackGenerator(style: .heavy).impactOccurred()
+#endif
+ withAnimation { completeInProgress = true }
+ 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)
+#if canImport(UIKit)
+ UINotificationFeedbackGenerator().notificationOccurred(.success)
+#endif
+ timer.invalidate()
+ progress = 0
+ }
+ }
+ RunLoop.main.add(timer!, forMode: .common)
+ }
+
+ var body: some View {
+ Group {
+ if completeInProgress {
+ ZStack {
+ CircularProgressView(progress: $progress)
+ Image(systemName: "xmark")
+ .bold()
+ }
+ .onTapGesture {
+ withAnimation { completeInProgress = false }
+ }
+ } else {
+ Image(systemName: event.complete ? "checkmark.circle.fill" : "circle")
+ .resizable().scaledToFit()
+ .foregroundStyle(event.complete ? .green : event.color.color)
+ .bold()
+ .onTapGesture {
+ startCompleting()
+ }
+ .onHover() { hovering in
+ withAnimation {
+ largeTick.toggle()
+ }
+ }
+ .scaleEffect(largeTick ? 1.5 : 1)
+ }
+ }
+ .frame(maxWidth: isMac ? 20 : 30)
+ .shadow(color: .one.opacity(0.2), radius: 2.5)
+ .padding(.trailing, isMac ? 15 : 5)
+ .transition(.scale)
+ .animation(.spring, value: completeInProgress)
+ .animation(
+ .spring(response: 0.2, dampingFraction: 0.75, blendDuration: 2),
+ value: largeTick
+ )
+ }
+}
+
+struct CircularProgressView: View {
+ @Binding var progress: Double
+ var body: some View {
+ ZStack {
+ Circle()
+ .stroke(
+ .two,
+ lineWidth: 5
+ )
+ Circle()
+ .trim(from: 0, to: progress)
+ .stroke(
+ .one,
+ lineWidth: 5
+ // style: StrokeStyle(
+ // lineWidth: 5,
+ // lineCap: .round
+ // )
+ )
+ .rotationEffect(.degrees(-90))
+ }
+ }
+}
+
+#Preview {
+ CompleteEventButton(
+ viewModel: dummyEventViewModel(),
+ event: .constant(dummyEventViewModel().example)
+ )
+ .scaleEffect(5)
+}
+
+#Preview {
+ CircularProgressView(progress: .constant(0.5))
+}
diff --git a/Shared/Extensions.swift b/Shared/Extensions.swift
new file mode 100644
index 0000000..049e4c4
--- /dev/null
+++ b/Shared/Extensions.swift
@@ -0,0 +1,37 @@
+//
+// Extension.swift
+// NearFuture
+//
+// Created by neon443 on 21/05/2025.
+//
+
+import Foundation
+import SwiftUI
+
+extension View {
+ var backgroundGradient: some View {
+ return LinearGradient(
+ gradient: Gradient(colors: [.bgTop, .two]),
+ startPoint: .top,
+ endPoint: .bottom
+ )
+ .ignoresSafeArea(.all)
+ }
+}
+
+extension AnyTransition {
+ static var moveAndFade: AnyTransition {
+ .asymmetric(
+ insertion: .move(edge: .leading),
+ removal: .move(edge: .trailing)
+ )
+ .combined(with: .opacity)
+ }
+ static var moveAndFadeReversed: AnyTransition {
+ .asymmetric(
+ insertion: .move(edge: .trailing),
+ removal: .move(edge: .leading)
+ )
+ .combined(with: .opacity)
+ }
+}
diff --git a/Shared/Model/AccentIcon.swift b/Shared/Model/AccentIcon.swift
new file mode 100644
index 0000000..6ec13c4
--- /dev/null
+++ b/Shared/Model/AccentIcon.swift
@@ -0,0 +1,47 @@
+//
+// AccentIcon.swift
+// NearFuture
+//
+// Created by neon443 on 13/06/2025.
+//
+
+import Foundation
+import SwiftUI
+#if canImport(AppKit)
+import AppKit
+#endif
+
+
+class AccentIcon {
+#if canImport(UIKit)
+ var icon: UIImage
+#elseif canImport(AppKit)
+ var icon: NSImage
+#endif
+ var color: Color
+ var name: String
+
+ init(_ colorName: String) {
+#if canImport(UIKit)
+ self.icon = UIImage(named: "AppIcon")!
+ self.color = Color(uiColor: UIColor(named: "uiColors/\(colorName)")!)
+#elseif canImport(AppKit)
+ self.icon = NSImage(imageLiteralResourceName: "AppIcon")
+ self.color = Color(nsColor: NSColor(named: "uiColors/\(colorName)")!)
+#endif
+
+ self.name = colorName
+
+ if colorName != "orange" {
+ setSelfIcon(to: colorName)
+ }
+ }
+
+ func setSelfIcon(to name: String) {
+#if canImport(UIKit)
+ self.icon = UIImage(named: name)!
+#elseif canImport(AppKit)
+ self.icon = NSImage(imageLiteralResourceName: name)
+#endif
+ }
+}
diff --git a/Shared/Model/ColorCodable.swift b/Shared/Model/ColorCodable.swift
new file mode 100644
index 0000000..0f58bd5
--- /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, Hashable {
+ 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/NearFuture/Item.swift b/Shared/Model/Events.swift
similarity index 76%
rename from NearFuture/Item.swift
rename to Shared/Model/Events.swift
index 19b2d99..8b708fc 100644
--- a/NearFuture/Item.swift
+++ b/Shared/Model/Events.swift
@@ -11,6 +11,9 @@ import SwiftUI
import WidgetKit
import UserNotifications
import AppIntents
+#if canImport(AppKit)
+import AppKit
+#endif
//@Model
//final class Item {
@@ -21,7 +24,7 @@ import AppIntents
// }
//}
-struct Event: Identifiable, Codable, Equatable, Animatable {
+struct Event: Identifiable, Codable, Equatable, Animatable, Hashable {
var id = UUID()
var name: String
var complete: Bool
@@ -37,52 +40,6 @@ struct Event: Identifiable, Codable, Equatable, Animatable {
}
}
-struct ColorCodable: Codable, Equatable {
- init(_ color: Color) {
- let uiColor = UIColor(color)
- var r: CGFloat = 0, g: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 1.0
- uiColor.getRed(&r, green: &g, blue: &b, alpha: &a)
-
- self.red = Double(r)
- self.green = Double(g)
- self.blue = Double(b)
- }
- 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.red = Double(r)
- self.green = Double(g)
- self.blue = Double(b)
- }
- 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())
@@ -93,115 +50,19 @@ func daysUntilEvent(_ eventDate: Date) -> (long: String, short: String) {
if days < 0 {
//past
return (
- "\(-days) day\(plu(days)) ago",
+ "\(-days)\nday\(plu(days)) ago",
"\(days)d"
)
} else {
//future
return (
- "\(days) day\(plu(days))",
+ "\(days)\nday\(plu(days))",
"\(days)d"
)
}
}
-struct Settings: Codable, Equatable {
- var showCompletedInHome: Bool
- var tint: ColorCodable
- var showWhatsNew: Bool
- var prevAppVersion: String
-}
-
-struct AccentIcon {
- var icon: UIImage
- var color: Color
- var name: String
- init(_ colorName: String) {
- if colorName == "orange" {
- self.icon = UIImage(named: "AppIcon")!
- } else {
- self.icon = UIImage(named: colorName)!
- }
- self.color = Color(uiColor: UIColor(named: "uiColors/\(colorName)")!)
- self.name = colorName
- }
-}
-
-class SettingsViewModel: ObservableObject {
- @Published var settings: Settings = Settings(
- showCompletedInHome: false,
- tint: ColorCodable(uiColor: UIColor(named: "AccentColor")!),
- showWhatsNew: true,
- prevAppVersion: getVersion()+getBuildID()
- )
- @Published var notifsGranted: Bool = false
-
- @Published var colorChoices: [AccentIcon] = []
-
- let accentChoices: [String] = [
- "red",
- "orange",
- "yellow",
- "green",
- "blue",
- "bloo",
- "purple",
- "pink"
- ]
-
- @Published var device: (sf: String, label: String)
-
- init(load: Bool = true) {
- self.device = getDevice()
- if load {
- loadSettings()
- Task {
- let requestResult = await requestNotifs()
- await MainActor.run {
- self.notifsGranted = requestResult
- }
- }
- }
- }
-
- func changeTint(to: String) {
- if let uicolor = UIColor(named: "uiColors/\(to)") {
- self.settings.tint = ColorCodable(uiColor: uicolor)
- saveSettings()
- }
- }
-
- let appGroupSettingsStore = UserDefaults(suiteName: "group.NearFuture") ?? UserDefaults.standard
- let icSettStore = NSUbiquitousKeyValueStore.default
-
- func loadSettings() {
- let decoder = JSONDecoder()
- if let icSettings = icSettStore.data(forKey: "settings") {
- if let decodedSetts = try? decoder.decode(Settings.self, from: icSettings) {
- self.settings = decodedSetts
- }
- } else if let savedData = appGroupSettingsStore.data(forKey: "settings") {
- if let decodedSetts = try? decoder.decode(Settings.self, from: savedData) {
- self.settings = decodedSetts
- }
- }
- if self.settings.prevAppVersion != getVersion()+getBuildID() {
- self.settings.showWhatsNew = true
- }
- }
-
- func saveSettings() {
- let encoder = JSONEncoder()
- if let encoded = try? encoder.encode(settings) {
- appGroupSettingsStore.set(encoded, forKey: "settings")
- icSettStore.set(encoded, forKey: "settings")
- icSettStore.synchronize()
- loadSettings()
- }
- }
-}
-
-class EventViewModel: ObservableObject {
+class EventViewModel: ObservableObject, @unchecked Sendable {
@Published var events: [Event] = []
@Published var icloudData: [Event] = []
@@ -232,11 +93,31 @@ class EventViewModel: ObservableObject {
@Published var localEventCount: Int = 0
@Published var syncStatus: String = "Not Synced"
+ @Published var hasUbiquitous: Bool = false
+ @Published var lastSyncWasSuccessful: Bool = false
+ @Published var lastSyncWasNormalAgo: Bool = false
+ @Published var localCountEqualToiCloud: Bool = false
+ @Published var icloudCountEqualToLocal: Bool = false
+
+ var iCloudStatusColor: Color {
+ let allTrue = hasUbiquitous && lastSyncWasSuccessful && lastSyncWasNormalAgo && localCountEqualToiCloud && icloudCountEqualToLocal
+ let someTrue = hasUbiquitous || lastSyncWasSuccessful || lastSyncWasNormalAgo || localCountEqualToiCloud || icloudCountEqualToLocal
+
+ if allTrue {
+ return .green
+ } else if someTrue {
+ return .orange
+ } else {
+ return .red
+ }
+ }
+
init(load: Bool = true) {
self.editableTemplate = template
if load {
loadEvents()
}
+// AudioServicesPlaySystemSound(kSystemSoundID_Vibrate)
}
//appgroup or regular userdefaults
@@ -315,8 +196,8 @@ class EventViewModel: ObservableObject {
updateSyncStatus()
loadEvents()
- Task {
- await checkPendingNotifs(getNotifs())
+ Task.detached {
+ await self.checkPendingNotifs(self.getNotifs())
}
WidgetCenter.shared.reloadAllTimelines()//reload all widgets when saving events
objectWillChange.send()
@@ -341,6 +222,24 @@ class EventViewModel: ObservableObject {
saveEvents() //sync with icloud
}
+ func editEvent(_ editedEvent: Event) {
+ if let index = events.firstIndex(where: { editedEvent.id == $0.id }) {
+ self.events[index] = editedEvent
+ saveEvents()
+ }
+ }
+
+ func completeEvent(_ event: inout Event) {
+ withAnimation { event.complete.toggle() }
+ let eventToModify = self.events.firstIndex() { currEvent in
+ currEvent.id == event.id
+ }
+ if let eventToModify = eventToModify {
+ self.events[eventToModify] = event
+ self.saveEvents()
+ }
+ }
+
func removeEvent(at index: IndexSet) {
events.remove(atOffsets: index)
saveEvents() //sync local and icl
@@ -392,20 +291,32 @@ class EventViewModel: ObservableObject {
return ""
}
- func importEvents(_ imported: String) throws {
+ func importEvents(_ imported: String, replace: Bool) throws {
guard let data = Data(base64Encoded: imported) else {
throw importError.invalidB64
}
let decoder = JSONDecoder()
do {
let decoded = try decoder.decode([Event].self, from: data)
- self.events = decoded
+ if replace {
+ self.events = decoded
+ } else {
+ self.events = self.events + decoded
+ }
saveEvents()
} catch {
throw error
}
}
+ func updateiCStatus() {
+ hasUbiquitous = hasUbiquitousKeyValueStore()
+ lastSyncWasSuccessful = syncStatus.contains("Success")
+ lastSyncWasNormalAgo = lastSync?.timeIntervalSinceNow.isNormal ?? false
+ localCountEqualToiCloud = localEventCount == icloudEventCount
+ icloudCountEqualToLocal = icloudEventCount == localEventCount
+ }
+
//MARK: Danger Zone
func dangerClearLocalData() {
UserDefaults.standard.removeObject(forKey: "events")
@@ -451,7 +362,7 @@ class EventViewModel: ObservableObject {
}
}
-class dummyEventViewModel: EventViewModel {
+class dummyEventViewModel: EventViewModel, @unchecked Sendable{
var template2: Event
override init(load: Bool = false) {
self.template2 = Event(
@@ -580,6 +491,7 @@ func getBuildID() -> String {
}
func getDevice() -> (sf: String, label: String) {
+ #if canImport(UIKit)
let asi = ProcessInfo().isiOSAppOnMac
let model = UIDevice().model
if asi {
@@ -590,6 +502,10 @@ func getDevice() -> (sf: String, label: String) {
return (sf: model.lowercased(), label: model)
}
return (sf: "iphone", label: "iPhone")
+ #elseif canImport(AppKit)
+
+ return (sf: "desktopcomputer", label: "Mac")
+ #endif
}
extension Event: AppEntity {
@@ -630,8 +546,7 @@ struct CompleteEvent: AppIntent {
func perform() async throws -> some IntentResult {
print("s")
- var viewModel = EventViewModel()
- var eventss = viewModel.events
+ let viewModel = EventViewModel()
print("hip")
guard let eventUUID = UUID(uuidString: eventID) else {
print(":sdklfajk")
diff --git a/Shared/Model/Settings.swift b/Shared/Model/Settings.swift
new file mode 100644
index 0000000..a01d61d
--- /dev/null
+++ b/Shared/Model/Settings.swift
@@ -0,0 +1,95 @@
+//
+// Settings.swift
+// MacNearFuture
+//
+// Created by neon443 on 21/05/2025.
+//
+
+import Foundation
+import SwiftUI
+#if canImport(AppKit)
+import AppKit
+#endif
+
+struct NFSettings: Codable, Equatable {
+ var showCompletedInHome: Bool = false
+ var tint: ColorCodable = ColorCodable(.accentColor)
+ var showWhatsNew: Bool = true
+ var prevAppVersion: String = getVersion()+getBuildID()
+}
+
+class SettingsViewModel: ObservableObject {
+ @Published var settings: NFSettings = NFSettings()
+
+ @Published var notifsGranted: Bool = false
+
+ @Published var colorChoices: [AccentIcon] = []
+
+ let accentChoices: [String] = [
+ "red",
+ "orange",
+ "yellow",
+ "green",
+ "blue",
+ "bloo",
+ "purple",
+ "pink"
+ ]
+
+ @Published var device: (sf: String, label: String)
+
+ init(load: Bool = true) {
+ self.device = getDevice()
+ if load {
+ loadSettings()
+ Task.detached {
+ let requestResult = await requestNotifs()
+ await MainActor.run {
+ self.notifsGranted = requestResult
+ }
+ }
+ }
+ }
+
+ func changeTint(to: String) {
+#if canImport(UIKit)
+ if let uicolor = UIColor(named: "uiColors/\(to)") {
+ self.settings.tint = ColorCodable(uiColor: uicolor)
+ saveSettings()
+ }
+#elseif canImport(AppKit)
+ if let nscolor = NSColor(named: "uiColors/\(to)") {
+ self.settings.tint = ColorCodable(nsColor: nscolor)
+ }
+#endif
+ }
+
+ let appGroupSettingsStore = UserDefaults(suiteName: "group.NearFuture") ?? UserDefaults.standard
+ let icSettStore = NSUbiquitousKeyValueStore.default
+
+ func loadSettings() {
+ let decoder = JSONDecoder()
+ if let icSettings = icSettStore.data(forKey: "settings") {
+ if let decodedSetts = try? decoder.decode(NFSettings.self, from: icSettings) {
+ self.settings = decodedSetts
+ }
+ } else if let savedData = appGroupSettingsStore.data(forKey: "settings") {
+ if let decodedSetts = try? decoder.decode(NFSettings.self, from: savedData) {
+ self.settings = decodedSetts
+ }
+ }
+ if self.settings.prevAppVersion != getVersion()+getBuildID() {
+ self.settings.showWhatsNew = true
+ }
+ }
+
+ func saveSettings() {
+ let encoder = JSONEncoder()
+ if let encoded = try? encoder.encode(settings) {
+ appGroupSettingsStore.set(encoded, forKey: "settings")
+ icSettStore.set(encoded, forKey: "settings")
+ icSettStore.synchronize()
+ loadSettings()
+ }
+ }
+}
diff --git a/Shared/Model/SymbolsPicker/SymbolsLoader.swift b/Shared/Model/SymbolsPicker/SymbolsLoader.swift
new file mode 100644
index 0000000..abff58c
--- /dev/null
+++ b/Shared/Model/SymbolsPicker/SymbolsLoader.swift
@@ -0,0 +1,36 @@
+//
+// SymbolsLoader.swift
+// NearFuture
+//
+// Created by neon443 on 14/06/2025.
+//
+
+import Foundation
+
+class SymbolsLoader: ObservableObject {
+ @Published var allSymbols: [String] = []
+
+ init() {
+ self.allSymbols = getAllSymbols()
+ }
+
+ func getSymbols(_ searched: String) -> [String] {
+ if searched.isEmpty {
+ return allSymbols
+ } else {
+ return allSymbols.filter() { $0.localizedCaseInsensitiveContains(searched) }
+ }
+ }
+
+ func getAllSymbols() -> [String] {
+ var allSymbols = [String]()
+ if let bundle = Bundle(identifier: "com.apple.CoreGlyphs"),
+ let resPath = bundle.path(forResource: "name_availability", ofType: "plist"),
+ let plist = try? NSDictionary(contentsOf: URL(fileURLWithPath: resPath), error: ()),
+ let plistSymbols = plist["symbols"] as? [String: String]
+ {
+ allSymbols = Array(plistSymbols.keys)
+ }
+ return allSymbols
+ }
+}
diff --git a/Shared/Model/SymbolsPicker/SymbolsPicker.swift b/Shared/Model/SymbolsPicker/SymbolsPicker.swift
new file mode 100644
index 0000000..92f988d
--- /dev/null
+++ b/Shared/Model/SymbolsPicker/SymbolsPicker.swift
@@ -0,0 +1,89 @@
+//
+// SymbolsPicker.swift
+// NearFuture
+//
+// Created by neon443 on 14/06/2025.
+//
+
+import SwiftUI
+
+struct SymbolsPicker: View {
+ @StateObject private var symbolsLoader = SymbolsLoader()
+ @Binding var selection: String
+
+ @FocusState var searchfocuesd: Bool
+
+ @State var searchInput: String = ""
+ @State var browsing: Bool = false
+ @Environment(\.dismiss) var dismiss
+
+ var symbols: [String] {
+ return symbolsLoader.getSymbols(searchInput)
+ }
+
+ private func gridLayout(forWidth geoSizeWidth: CGFloat) -> [GridItem] {
+ let gridItem = GridItem(.fixed(80), spacing: 20, alignment: .center)
+ let columns = Int(geoSizeWidth/100.rounded(.up))
+ return Array(repeating: gridItem, count: columns)
+ }
+
+ var body: some View {
+ NavigationStack {
+ GeometryReader { geo in
+ ScrollView {
+ if symbols.isEmpty {
+ HStack {
+ Image(systemName: "magnifyingglass")
+ .resizable().scaledToFit()
+ .frame(width: 30)
+ Text("You look lost")
+ .font(.title)
+ .bold()
+ }
+ .padding()
+ Text("The symbol picker search only works with exact matches, try a different search term.")
+ }
+ LazyVGrid(columns: gridLayout(forWidth: geo.size.width)) {
+ ForEach(symbols, id: \.self) { symbol in
+ Button() {
+ selection = symbol
+ searchInput = ""
+ dismiss()
+ } label: {
+ VStack {
+ Image(systemName: symbol)
+ .resizable()
+ .scaledToFit()
+ .symbolRenderingMode(.palette)
+ .foregroundStyle(.blue, .gray, .black)
+ Text(symbol)
+ .truncationMode(.middle)
+ .font(.footnote)
+ }
+ }
+ .frame(maxWidth: 80, maxHeight: 80)
+ .buttonStyle(.plain)
+ }
+ }
+ }
+ }
+ .searchable(text: $searchInput)
+ .toolbar {
+ ToolbarItem(placement: .cancellationAction) {
+ if !browsing {
+ Button() {
+ searchInput = ""
+ dismiss()
+ } label: {
+ Label("Cancel", systemImage: "xmark")
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+#Preview {
+ SymbolsPicker(selection: .constant(""))
+}
diff --git a/Shared/NearFutureApp.swift b/Shared/NearFutureApp.swift
new file mode 100644
index 0000000..e01dbbb
--- /dev/null
+++ b/Shared/NearFutureApp.swift
@@ -0,0 +1,23 @@
+//
+// NearFutureApp.swift
+// NearFuture
+//
+// Created by neon443 on 24/12/2024.
+//
+
+import SwiftUI
+import SwiftData
+
+@main
+struct NearFutureApp: App {
+ @StateObject var settingsModel: SettingsViewModel = SettingsViewModel()
+ var body: some Scene {
+ WindowGroup {
+ ContentView(
+ viewModel: EventViewModel(),
+ settingsModel: settingsModel
+ )
+ .tint(settingsModel.settings.tint.color)
+ }
+ }
+}
diff --git a/Shared/ViewModifiers.swift b/Shared/ViewModifiers.swift
new file mode 100644
index 0000000..caa1ae9
--- /dev/null
+++ b/Shared/ViewModifiers.swift
@@ -0,0 +1,76 @@
+//
+// 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 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
+ }
+ }
+}
+
+struct navigationInlineLarge: ViewModifier {
+ func body(content: Content) -> some View {
+#if os(macOS)
+ content
+ .toolbarTitleDisplayMode(.inlineLarge)
+#else
+ content
+ .navigationBarTitleDisplayMode(.inline)
+#endif
+ }
+}
+
+struct presentationSizeForm: ViewModifier {
+ func body(content: Content) -> some View {
+ if #available(iOS 18, macOS 15, *) {
+ content.presentationSizing(.form)
+ } else {
+ content
+ }
+ }
+}