diff --git a/Config.xcconfig b/Config.xcconfig index b5617b1..2135b30 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 = 3.1.1 +VERSION = 3.2.1 NAME = Near Future BUILD_NUMBER = 5 diff --git a/NearFuture/AddEventView.swift b/NearFuture/AddEventView.swift index bb7dae8..d2aa17d 100644 --- a/NearFuture/AddEventView.swift +++ b/NearFuture/AddEventView.swift @@ -94,8 +94,10 @@ struct AddEventView: View { // date picker HStack { + Spacer() DatePicker("", selection: $eventDate, displayedComponents: .date) .datePickerStyle(WheelDatePickerStyle()) + Spacer() Button() { eventDate = Date() } label: { diff --git a/NearFuture/ArchiveView.swift b/NearFuture/ArchiveView.swift index 6a30b3f..ca062f9 100644 --- a/NearFuture/ArchiveView.swift +++ b/NearFuture/ArchiveView.swift @@ -36,6 +36,14 @@ struct ArchiveView: View { AddEventButton(showingAddEventView: $showAddEvent) } } + .navigationTitle("Archive") + .apply { + if #available(iOS 17, *) { + $0.toolbarTitleDisplayMode(.inlineLarge) + } else { + $0.navigationBarTitleDisplayMode(.inline) + } + } } .sheet(isPresented: $showAddEvent) { AddEventView( diff --git a/NearFuture/ContentView.swift b/NearFuture/ContentView.swift index 1c1fb93..15f5cc4 100644 --- a/NearFuture/ContentView.swift +++ b/NearFuture/ContentView.swift @@ -34,7 +34,7 @@ struct ContentView: View { @State private var searchInput: String = "" var filteredEvents: [Event] { if searchInput.isEmpty { - return viewModel.events + return viewModel.events.filter() {!$0.complete} } else { return viewModel.events.filter { $0.name.localizedCaseInsensitiveContains(searchInput) || @@ -86,14 +86,23 @@ struct ContentView: View { } .padding(.horizontal) if /*!searchInput.isEmpty && */filteredEvents.isEmpty { - HelpView(searchInput: $searchInput, focusedField: focusedField) + HelpView( + searchInput: $searchInput, + focusedField: focusedField + ) } Spacer() } } } .navigationTitle("Near Future") - .navigationBarTitleDisplayMode(.inline) + .apply { + if #available(iOS 17, *) { + $0.toolbarTitleDisplayMode(.inlineLarge) + } else { + $0.navigationBarTitleDisplayMode(.inline) + } + } .sheet(isPresented: $showingAddEventView) { AddEventView( viewModel: viewModel, @@ -193,3 +202,7 @@ extension View { .ignoresSafeArea(.all) } } + +extension View { + func apply(@ViewBuilder _ block: (Self) -> V) -> V { block(self) } +} diff --git a/NearFuture/EventListView.swift b/NearFuture/EventListView.swift index 2083efa..49d576c 100644 --- a/NearFuture/EventListView.swift +++ b/NearFuture/EventListView.swift @@ -44,11 +44,13 @@ struct EventListView: View { .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( diff --git a/NearFuture/Item.swift b/NearFuture/Item.swift index 9412792..757a227 100644 --- a/NearFuture/Item.swift +++ b/NearFuture/Item.swift @@ -76,30 +76,24 @@ struct ColorCodable: Codable, Equatable { func daysUntilEvent(_ eventDate: Date) -> (long: String, short: String) { let calendar = Calendar.current - let now = Date() - - let isToday = calendar.isDate(now, inSameDayAs: eventDate) - let components = calendar.dateComponents([.second, .day], from: now, to: eventDate) - - guard !isToday else { return ("Today", "Today") } - let secsComponents = eventDate.timeIntervalSinceNow - guard let daysCompontents = components.day else { return ("N/A", "N/A") } - let secs = Double(secsComponents) - var days = 0 - var long = "" - var short = "" - if secs < 0 { + let startOfDayNow = calendar.startOfDay(for: Date()) + let startOfDayEvent = calendar.startOfDay(for: eventDate) + let components = calendar.dateComponents([.day], from: startOfDayNow, to: startOfDayEvent) + guard let days = components.day else { return ("N/A", "N/A") } + guard days != 0 else { return ("Today", "Today") } + if days < 0 { //past - days = Int(floor(secs/86400)) - long = "\(-days) day\(plu(days)) ago" - short = "\(days)d" + return ( + "\(-days) day\(plu(days)) ago", + "\(days)d" + ) } else { //future - days = Int(ceil(secs/86400)) - long = "\(days) day\(plu(days))" - short = "\(days)d" + return ( + "\(days) day\(plu(days))", + "\(days)d" + ) } - return (long, short) } class EventViewModel: ObservableObject { diff --git a/NearFuture/SettingsView.swift b/NearFuture/SettingsView.swift index 348d647..896ff72 100644 --- a/NearFuture/SettingsView.swift +++ b/NearFuture/SettingsView.swift @@ -108,7 +108,13 @@ struct SettingsView: View { } .scrollContentBackground(.hidden) .navigationTitle("Settings") - .navigationBarTitleDisplayMode(.inline) + .apply { + if #available(iOS 17, *) { + $0.toolbarTitleDisplayMode(.inlineLarge) + } else { + $0.navigationBarTitleDisplayMode(.inline) + } + } } } } diff --git a/NearFuture/StatsView.swift b/NearFuture/StatsView.swift index b95627c..fce5cc3 100644 --- a/NearFuture/StatsView.swift +++ b/NearFuture/StatsView.swift @@ -58,7 +58,13 @@ struct StatsView: View { } .scrollContentBackground(.hidden) .navigationTitle("Statistics") - .navigationBarTitleDisplayMode(.inline) + .apply { + if #available(iOS 17, *) { + $0.toolbarTitleDisplayMode(.inlineLarge) + } else { + $0.navigationBarTitleDisplayMode(.inline) + } + } } } } diff --git a/NearFuture/iCloudSettingsView.swift b/NearFuture/iCloudSettingsView.swift index 4b924cd..4f016a3 100644 --- a/NearFuture/iCloudSettingsView.swift +++ b/NearFuture/iCloudSettingsView.swift @@ -37,145 +37,155 @@ struct iCloudSettingsView: View { ZStack { backgroundGradient List { - HStack { - Spacer() - VStack { - ZStack { - Image(systemName: "icloud") - .resizable() - .scaledToFit() - .frame(width: 75, height: 55) - .symbolRenderingMode(.multicolor) - Text("\(viewModel.icloudEventCount)") - .font(.title2) - .monospaced() - .bold() - } - Text("iCloud") - HStack { - Button(role: .destructive) { - showPushAlert.toggle() - } label: { - Image(systemName: "arrow.up") + Section { + HStack { + Spacer() + VStack { + ZStack { + Image(systemName: "icloud") .resizable() .scaledToFit() - .frame(width: 30, height: 40) + .frame(width: 75, height: 55) + .symbolRenderingMode(.multicolor) + Text("\(viewModel.icloudEventCount)") + .font(.title2) + .monospaced() + .bold() } - .buttonStyle(BorderedButtonStyle()) - .alert("Warning", isPresented: $showPushAlert) { - Button("OK", role: .destructive) { - viewModel.replaceiCloudWithLocalData() + Text("iCloud") + HStack { + Button(role: .destructive) { + showPushAlert.toggle() + } label: { + Image(systemName: "arrow.up") + .resizable() + .scaledToFit() + .frame(width: 30, height: 40) + } + .buttonStyle(BorderedButtonStyle()) + .alert("Warning", isPresented: $showPushAlert) { + Button("OK", role: .destructive) { + viewModel.replaceiCloudWithLocalData() + viewModel.sync() + updateStatus() + } + Button("Cancel", role: .cancel) {} + } message: { + Text("This will replace Events stored in iCloud with Events stored locally.") + } + + Button() { viewModel.sync() updateStatus() + } label: { + Image(systemName: "arrow.triangle.2.circlepath") + .resizable() + .scaledToFit() + .frame(width: 30, height: 40) + .foregroundStyle(Color.accentColor) + } + .buttonStyle(BorderedButtonStyle()) + + Button(role: .destructive) { + showPullAlert.toggle() + } label: { + Image(systemName: "arrow.down") + .resizable() + .scaledToFit() + .frame(width: 30, height: 40) + } + .buttonStyle(BorderedButtonStyle()) + .alert("Warning", isPresented: $showPullAlert) { + Button("OK", role: .destructive) { + viewModel.replaceLocalWithiCloudData() + viewModel.sync() + updateStatus() + } + Button("Cancel", role: .cancel) {} + } message: { + Text("This will replace Events stored locally with Events stored in iCloud.") } - Button("Cancel", role: .cancel) {} - } message: { - Text("This will replace Events stored in iCloud with Events stored locally.") } - - Button() { - viewModel.sync() - updateStatus() - } label: { - Image(systemName: "arrow.triangle.2.circlepath") + ZStack { + Image(systemName: device.sf) .resizable() .scaledToFit() - .frame(width: 30, height: 40) - .foregroundStyle(Color.accentColor) - } - .buttonStyle(BorderedButtonStyle()) - - Button(role: .destructive) { - showPullAlert.toggle() - } label: { - Image(systemName: "arrow.down") - .resizable() - .scaledToFit() - .frame(width: 30, height: 40) - } - .buttonStyle(BorderedButtonStyle()) - .alert("Warning", isPresented: $showPullAlert) { - Button("OK", role: .destructive) { - viewModel.replaceLocalWithiCloudData() - viewModel.sync() - updateStatus() - } - Button("Cancel", role: .cancel) {} - } message: { - Text("This will replace Events stored locally with Events stored in iCloud.") + .frame(width: 75, height: 75) + .symbolRenderingMode(.monochrome) + Text("\(viewModel.localEventCount)") + .font(.title2) + .monospaced() + .bold() } + Text(device.label) } - ZStack { - Image(systemName: device.sf) - .resizable() - .scaledToFit() - .frame(width: 75, height: 75) - .symbolRenderingMode(.monochrome) - Text("\(viewModel.localEventCount)") - .font(.title2) - .monospaced() - .bold() - } - Text(device.label) + Spacer() } - Spacer() - } - .listRowSeparator(.hidden) - .onAppear { - viewModel.sync() - updateStatus() - } - - HStack { - Circle() - .frame(width: 20, height: 20) - .foregroundStyle(hasUbiquitous ? .green : .red) - Text("iCloud") - Spacer() - Text("\(hasUbiquitous ? "" : "Not ")Working") - .bold() - } - - HStack { - Circle() - .frame(width: 20, height: 20) - .foregroundStyle(lastSyncWasSuccessful ? .green : .red) + .listRowSeparator(.hidden) + .onAppear { + viewModel.sync() + updateStatus() + } + + HStack { + Circle() + .frame(width: 20, height: 20) + .foregroundStyle(hasUbiquitous ? .green : .red) + Text("iCloud") + Spacer() + Text("\(hasUbiquitous ? "" : "Not ")Working") + .bold() + } + + HStack { + Circle() + .frame(width: 20, height: 20) + .foregroundStyle(lastSyncWasSuccessful ? .green : .red) + Text("Sync Status") + Spacer() + Text("\(viewModel.syncStatus)") + .bold() + } + + HStack { + Circle() + .frame(width: 20, height: 20) + .foregroundStyle(lastSyncWasNormalAgo ? .green : .red) + Text("Last Sync") + Spacer() + Text("\(viewModel.lastSync?.formatted(date: .long, time: .standard) ?? "Never")") + .bold() + } + + HStack { + Circle() + .frame(width: 20, height: 20) + .foregroundStyle(localCountEqualToiCloud ? .green : .red) + Text("Local Events") + Spacer() + Text("\(viewModel.localEventCount)") + .bold() + } + + HStack { + Circle() + .frame(width: 20, height: 20) + .foregroundStyle(icloudCountEqualToLocal ? .green : .red) + Text("Events in iCloud") + Spacer() + Text("\(viewModel.icloudEventCount)") + .bold() + } + } header: { Text("Sync Status") - Spacer() - Text("\(viewModel.syncStatus)") - .bold() - } - - HStack { - Circle() - .frame(width: 20, height: 20) - .foregroundStyle(lastSyncWasNormalAgo ? .green : .red) - Text("Last Sync") - Spacer() - Text("\(viewModel.lastSync?.formatted() ?? "Never")") - .bold() - } - - HStack { - Circle() - .frame(width: 20, height: 20) - .foregroundStyle(localCountEqualToiCloud ? .green : .red) - Text("Local Events") - Spacer() - Text("\(viewModel.localEventCount)") - .bold() - } - - HStack { - Circle() - .frame(width: 20, height: 20) - .foregroundStyle(icloudCountEqualToLocal ? .green : .red) - Text("Events in iCloud") - Spacer() - Text("\(viewModel.icloudEventCount)") - .bold() + } footer: { + Text("Pull to sync\nOr use the arrows to force push/pull") } } + .refreshable { + viewModel.sync() + updateStatus() + } .scrollContentBackground(.hidden) .navigationTitle("iCloud") .navigationBarTitleDisplayMode(.inline)