From 3f210740917ea61503bf4a70356ac25d87f7f614 Mon Sep 17 00:00:00 2001 From: neon443 <69979447+neon443@users.noreply.github.com> Date: Fri, 13 Jun 2025 17:15:23 +0100 Subject: [PATCH] moved icloud ui functions into viewModel - idk why it was in the view added settings for mac fix addeventview export view pasteboard for mac move accent icon to new file Archive view reversed - new to old --- MacNearFuture/Views/ContentViewMac.swift | 9 + NearFuture.xcodeproj/project.pbxproj | 16 ++ NearFuture/Views/Archive/ArchiveView.swift | 3 +- NearFuture/Views/Events/AddEventView.swift | 171 +++++++++--------- NearFuture/Views/Settings/ExportView.swift | 4 + NearFuture/Views/Settings/SettingsView.swift | 53 ++---- .../Views/Settings/iCloudSettingsView.swift | 45 ++--- README.md | 9 +- Shared/Model/AccentIcon.swift | 47 +++++ Shared/Model/Events.swift | 29 ++- Shared/Model/Settings.swift | 34 ---- 11 files changed, 227 insertions(+), 193 deletions(-) create mode 100644 Shared/Model/AccentIcon.swift diff --git a/MacNearFuture/Views/ContentViewMac.swift b/MacNearFuture/Views/ContentViewMac.swift index 83b84a3..8ef8a8e 100644 --- a/MacNearFuture/Views/ContentViewMac.swift +++ b/MacNearFuture/Views/ContentViewMac.swift @@ -32,6 +32,15 @@ struct ContentView: View { Image(systemName: "tray.full") Text("Archive") } + NavigationLink { + SettingsView( + viewModel: viewModel, + settingsModel: settingsModel + ) + } label: { + Image(systemName: "gear") + Text("Settings") + } } } detail: { diff --git a/NearFuture.xcodeproj/project.pbxproj b/NearFuture.xcodeproj/project.pbxproj index eb20a9b..7d9fec1 100644 --- a/NearFuture.xcodeproj/project.pbxproj +++ b/NearFuture.xcodeproj/project.pbxproj @@ -44,6 +44,13 @@ A949F8542DCAABE00064DCA0 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949F8462DCAABE00064DCA0 /* SettingsView.swift */; }; A949F8552DCAABE00064DCA0 /* StatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949F8482DCAABE00064DCA0 /* StatsView.swift */; }; A949F85F2DCABB420064DCA0 /* Buttons.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949F85D2DCABB420064DCA0 /* Buttons.swift */; }; + 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 */; }; + A95E9EE32DFC775300ED655F /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949F8462DCAABE00064DCA0 /* SettingsView.swift */; }; + A95E9EE42DFC77D400ED655F /* ImportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949F8452DCAABE00064DCA0 /* ImportView.swift */; }; + A95E9EE52DFC77E200ED655F /* ExportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949F8432DCAABE00064DCA0 /* ExportView.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 */; }; @@ -125,6 +132,7 @@ A949F8462DCAABE00064DCA0 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; }; A949F8482DCAABE00064DCA0 /* StatsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsView.swift; sourceTree = ""; }; A949F85D2DCABB420064DCA0 /* Buttons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Buttons.swift; 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; }; A979F6092D270AF00094C0B3 /* NearFutureWidgetsBundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearFutureWidgetsBundle.swift; sourceTree = ""; }; A979F60B2D270AF00094C0B3 /* NearFutureWidgetsLiveActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearFutureWidgetsLiveActivity.swift; sourceTree = ""; }; @@ -170,6 +178,7 @@ children = ( A920C28B2D24011400E4F9B1 /* Events.swift */, A90D49602DDE626300781124 /* Settings.swift */, + A95E9ED72DFC742B00ED655F /* AccentIcon.swift */, ); path = Model; sourceTree = ""; @@ -507,9 +516,13 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + A95E9EE32DFC775300ED655F /* SettingsView.swift in Sources */, + A95E9EE42DFC77D400ED655F /* ImportView.swift in Sources */, A98C20CB2DE730740008D61C /* EventListViewMac.swift in Sources */, + A95E9ED92DFC742B00ED655F /* AccentIcon.swift in Sources */, A98C20CC2DE730740008D61C /* EditEventView.swift in Sources */, A90D495E2DDE3C7400781124 /* NFCommands.swift in Sources */, + A95E9EE52DFC77E200ED655F /* ExportView.swift in Sources */, A98C20D42DE7339E0008D61C /* AboutView.swift in Sources */, A98C20CE2DE7308E0008D61C /* ArchiveView.swift in Sources */, A98C20D02DE731BD0008D61C /* HomeView.swift in Sources */, @@ -519,6 +532,7 @@ A90D49422DDE114100781124 /* Events.swift in Sources */, A90D49382DDE0FAF00781124 /* ContentViewMac.swift in Sources */, A90D49622DDE626300781124 /* Settings.swift in Sources */, + A95E9ED32DFC703200ED655F /* iCloudSettingsView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -540,6 +554,7 @@ A949F85F2DCABB420064DCA0 /* Buttons.swift in Sources */, A914FA4D2DD2768900856265 /* WhatsNewView.swift in Sources */, A949F8512DCAABE00064DCA0 /* ExportView.swift in Sources */, + A95E9ED82DFC742B00ED655F /* AccentIcon.swift in Sources */, A90D49532DDE2D0000781124 /* Extensions.swift in Sources */, A949F8522DCAABE00064DCA0 /* iCloudSettingsView.swift in Sources */, A949F8532DCAABE00064DCA0 /* ImportView.swift in Sources */, @@ -555,6 +570,7 @@ files = ( A979F6182D2714310094C0B3 /* Events.swift in Sources */, A979F60A2D270AF00094C0B3 /* NearFutureWidgetsBundle.swift in Sources */, + A95E9EDA2DFC742B00ED655F /* AccentIcon.swift in Sources */, A9FC7EEA2D2823920020D75B /* NearFutureWidgets.swift in Sources */, A979F60C2D270AF00094C0B3 /* NearFutureWidgetsLiveActivity.swift in Sources */, A90D49632DDE626300781124 /* Settings.swift in Sources */, diff --git a/NearFuture/Views/Archive/ArchiveView.swift b/NearFuture/Views/Archive/ArchiveView.swift index 0ec1f9b..cccb9f8 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 { diff --git a/NearFuture/Views/Events/AddEventView.swift b/NearFuture/Views/Events/AddEventView.swift index 6904860..2a04244 100644 --- a/NearFuture/Views/Events/AddEventView.swift +++ b/NearFuture/Views/Events/AddEventView.swift @@ -31,90 +31,92 @@ struct AddEventView: View { if !adding { backgroundGradient } - List { - Section( - header: - Text("Event Details") - .font(.headline) - .foregroundColor(.accentColor) - ) { - // name & symbol - HStack(spacing: 5) { - Button() { - isSymbolPickerPresented.toggle() - } label: { - Image(systemName: event.symbol) - .resizable() - .scaledToFit() - .frame(width: 20, height: 20) - .foregroundStyle(event.color.color) - } - .frame(width: 20) - .buttonStyle(.borderless) - .sheet(isPresented: $isSymbolPickerPresented) { - SymbolsPicker( - selection: $event.symbol, - title: "Choose a Symbol", - searchLabel: "Search...", - autoDismiss: true) - .presentationDetents([.medium]) - } - } - - // dscription - ZStack { - TextField("Event Notes", text: $event.notes) - .textFieldStyle(RoundedBorderTextFieldStyle()) - .padding(.trailing, event.notes.isEmpty ? 0 : 30) - .animation(.spring, value: event.notes) - .focused($focusedField, equals: Field.Notes) - .submitLabel(.done) - .onSubmit { - focusedField = nil + NavigationStack { + List { + Section( + header: + Text("Event Details") + .font(.headline) + .foregroundColor(.accentColor) + ) { + // name & symbol + HStack(spacing: 5) { + Button() { + isSymbolPickerPresented.toggle() + } label: { + Image(systemName: event.symbol) + .resizable() + .scaledToFit() + .frame(width: 20, height: 20) + .foregroundStyle(event.color.color) } - // MagicClearButton(text: $eventNotes) - } - - - // date picker - HStack { - Spacer() - DatePicker("", selection: $event.date, displayedComponents: .date) - .datePickerStyle(.wheel) - Spacer() - Button() { - event.date = Date() - } label: { - Image(systemName: "arrow.uturn.left") - .resizable() - .scaledToFit() + .frame(width: 20) + .buttonStyle(.borderless) + .sheet(isPresented: $isSymbolPickerPresented) { + SymbolsPicker( + selection: $event.symbol, + title: "Choose a Symbol", + searchLabel: "Search...", + autoDismiss: true) + .presentationDetents([.medium]) + } + TextField("Event Name", text: $event.name) + .textFieldStyle(.roundedBorder) } - .buttonStyle(BorderlessButtonStyle()) - .frame(width: 20) - } - - DatePicker( - "", - selection: $event.date, - displayedComponents: .hourAndMinute - ) - .datePickerStyle(.wheel) - - // re-ocurrence Picker - Picker("Recurrence", selection: $event.recurrence) { - ForEach(Event.RecurrenceType.allCases, id: \.self) { recurrence in - Text(recurrence.rawValue.capitalized) + + // dscription + ZStack { + TextField("Event Notes", text: $event.notes) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .padding(.trailing, event.notes.isEmpty ? 0 : 30) + .animation(.spring, value: event.notes) + .focused($focusedField, equals: Field.Notes) + .submitLabel(.done) + .onSubmit { + focusedField = nil + } + // MagicClearButton(text: $eventNotes) } - } - .pickerStyle(SegmentedPickerStyle()) - Text( - describeOccurrence( - date: event.date, - recurrence: event.recurrence + + + // date picker + HStack { + Spacer() + DatePicker("", selection: $event.date, displayedComponents: .date) + .datePickerStyle(.wheel) + Spacer() + Button() { + event.date = Date() + } label: { + Image(systemName: "arrow.uturn.left") + .resizable() + .scaledToFit() + } + .buttonStyle(BorderlessButtonStyle()) + .frame(width: 20) + } + + DatePicker( + "", + selection: $event.date, + displayedComponents: .hourAndMinute ) - ) + + // re-ocurrence Picker + Picker("Recurrence", selection: $event.recurrence) { + ForEach(Event.RecurrenceType.allCases, id: \.self) { recurrence in + Text(recurrence.rawValue.capitalized) + } + } + .pickerStyle(SegmentedPickerStyle()) + Text( + describeOccurrence( + date: event.date, + recurrence: event.recurrence + ) + ) + } } - .scrollContentBackground(.hidden) .navigationTitle("\(adding ? "Add Event" : "")") .navigationBarTitleDisplayMode(.inline) .toolbar { @@ -128,10 +130,11 @@ struct AddEventView: View { .resizable() .scaledToFit() .frame(width: 30) + .tint(.one) } } } - ToolbarItem(placement: .topBarTrailing) { + ToolbarItem() { if adding { Button { viewModel.addEvent( @@ -165,18 +168,12 @@ struct AddEventView: View { } message: { Text("Give your Event a name before saving.") } - if event.name.isEmpty { - HStack { - Image(systemName: "exclamationmark") - .foregroundStyle(.red) - Text("Give your event a name.") - } - } } } } } .scrollContentBackground(.hidden) + .scrollContentBackground(.hidden) .presentationDragIndicator(.visible) } } 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/SettingsView.swift b/NearFuture/Views/Settings/SettingsView.swift index fb90138..d8c09a7 100644 --- a/NearFuture/Views/Settings/SettingsView.swift +++ b/NearFuture/Views/Settings/SettingsView.swift @@ -10,37 +10,11 @@ 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) { + #if canImport(UIKit) guard UIApplication.shared.supportsAlternateIcons else { print("doesnt tsupport alternate icons") return @@ -54,6 +28,7 @@ struct SettingsView: View { UIApplication.shared.setAlternateIconName(to) { error in print(error as Any) } + #endif } var body: some View { @@ -64,7 +39,11 @@ struct SettingsView: View { ScrollView(.horizontal) { HStack { ForEach(settingsModel.accentChoices, id: \.self) { choice in + #if canImport(UIKit) let color = Color(uiColor: UIColor(named: "uiColors/\(choice)")!) + #else + let color = Color(nsColor: NSColor(named: "uiColors/\(choice)")!) + #endif ZStack { Button() { settingsModel.changeTint(to: choice) @@ -94,6 +73,8 @@ struct SettingsView: View { 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() @@ -102,8 +83,6 @@ struct SettingsView: View { } } } - Text("\(Image(systemName: "xmark")) Notifications disabled for Near Future") - .foregroundStyle(.red) } else { Text("\(Image(systemName: "checkmark")) Notifications enabled for Near Future") .foregroundStyle(.green) @@ -116,13 +95,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 { @@ -131,12 +104,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) @@ -183,11 +156,13 @@ struct SettingsView: View { .scrollContentBackground(.hidden) .navigationTitle("Settings") .apply { + #if canImport(UIKit) if #available(iOS 17, *) { $0.toolbarTitleDisplayMode(.inlineLarge) } else { $0.navigationBarTitleDisplayMode(.inline) } + #endif } } } 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/README.md b/README.md index ad370e1..e503274 100644 --- a/README.md +++ b/README.md @@ -11,17 +11,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 @@ -53,4 +54,4 @@ Contributions are welcome! Just follow these steps: ## Used Tools/Frameworks - Swift & SwiftUI by Apple -- **SFSymbolsPicker** by [alessiorubicini/SFSymbolsPickerForSwiftUI]. \ No newline at end of file +- **SFSymbolsPicker** by [alessiorubicini/SFSymbolsPickerForSwiftUI]. 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/Events.swift b/Shared/Model/Events.swift index 72f45d9..35849b3 100644 --- a/Shared/Model/Events.swift +++ b/Shared/Model/Events.swift @@ -11,10 +11,8 @@ import SwiftUI import WidgetKit import UserNotifications import AppIntents -import AudioToolbox #if canImport(AppKit) import AppKit -import IOKit #endif //@Model @@ -164,6 +162,25 @@ class EventViewModel: ObservableObject, @unchecked Sendable { @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 { @@ -343,6 +360,14 @@ class EventViewModel: ObservableObject, @unchecked Sendable { } } + 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") diff --git a/Shared/Model/Settings.swift b/Shared/Model/Settings.swift index 58c14b2..a01d61d 100644 --- a/Shared/Model/Settings.swift +++ b/Shared/Model/Settings.swift @@ -18,40 +18,6 @@ struct NFSettings: Codable, Equatable { var prevAppVersion: String = getVersion()+getBuildID() } -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 - } -} - class SettingsViewModel: ObservableObject { @Published var settings: NFSettings = NFSettings()