mirror of
https://github.com/neon443/NearFuture.git
synced 2026-03-11 06:49:12 +00:00
rerewrote daysUntillEvent, now acc can count days
inlineLarge titlebar, backwards compatible view extension competed events go to archive fix long event name and notes alignemnts can refresh icloud settings to sync
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -94,8 +94,10 @@ struct AddEventView: View {
|
||||
|
||||
// date picker
|
||||
HStack {
|
||||
Spacer()
|
||||
DatePicker("", selection: $eventDate, displayedComponents: .date)
|
||||
.datePickerStyle(WheelDatePickerStyle())
|
||||
Spacer()
|
||||
Button() {
|
||||
eventDate = Date()
|
||||
} label: {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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<V: View>(@ViewBuilder _ block: (Self) -> V) -> V { block(self) }
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user