mirror of
https://github.com/neon443/NearFuture.git
synced 2026-03-11 06:49:12 +00:00
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
This commit is contained in:
@@ -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: {
|
||||
|
||||
|
||||
@@ -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 = "<group>"; };
|
||||
A949F8482DCAABE00064DCA0 /* StatsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsView.swift; sourceTree = "<group>"; };
|
||||
A949F85D2DCABB420064DCA0 /* Buttons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Buttons.swift; sourceTree = "<group>"; };
|
||||
A95E9ED72DFC742B00ED655F /* AccentIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccentIcon.swift; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
A979F60B2D270AF00094C0B3 /* NearFutureWidgetsLiveActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearFutureWidgetsLiveActivity.swift; sourceTree = "<group>"; };
|
||||
@@ -170,6 +178,7 @@
|
||||
children = (
|
||||
A920C28B2D24011400E4F9B1 /* Events.swift */,
|
||||
A90D49602DDE626300781124 /* Settings.swift */,
|
||||
A95E9ED72DFC742B00ED655F /* AccentIcon.swift */,
|
||||
);
|
||||
path = Model;
|
||||
sourceTree = "<group>";
|
||||
@@ -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 */,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -11,36 +11,10 @@ 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
47
Shared/Model/AccentIcon.swift
Normal file
47
Shared/Model/AccentIcon.swift
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user