diff --git a/NearFuture.xcodeproj/project.xcworkspace/xcuserdata/neon443.xcuserdatad/UserInterfaceState.xcuserstate b/NearFuture.xcodeproj/project.xcworkspace/xcuserdata/neon443.xcuserdatad/UserInterfaceState.xcuserstate index 4621179..52cbcf8 100644 Binary files a/NearFuture.xcodeproj/project.xcworkspace/xcuserdata/neon443.xcuserdatad/UserInterfaceState.xcuserstate and b/NearFuture.xcodeproj/project.xcworkspace/xcuserdata/neon443.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/NearFuture.xcodeproj/xcuserdata/neon443.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/NearFuture.xcodeproj/xcuserdata/neon443.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index cba1885..1a02d79 100644 --- a/NearFuture.xcodeproj/xcuserdata/neon443.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/NearFuture.xcodeproj/xcuserdata/neon443.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -3,651 +3,4 @@ uuid = "81691EC5-53AC-4338-9E2E-C1A8F23D20E5" type = "1" version = "2.0"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/NearFuture/AddEventView.swift b/NearFuture/AddEventView.swift index 4fac805..519f68c 100644 --- a/NearFuture/AddEventView.swift +++ b/NearFuture/AddEventView.swift @@ -12,6 +12,8 @@ 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 eventDescription: String @@ -123,6 +125,8 @@ struct AddEventView: View { Button { viewModel.addEvent( name: eventName, + complete: eventComplete, + completedDesc: eventCompleteDesc, symbol: eventSymbol, color: ColorCodable(eventColor), description: eventDescription, @@ -140,7 +144,7 @@ struct AddEventView: View { .disabled(eventName.isEmpty) if eventName.isEmpty { HStack { - Image(systemName: "exclamationmark.circle") + Image(systemName: "exclamationmark") .foregroundStyle(.red) Text("Give your event a name.") } @@ -207,6 +211,8 @@ struct MagicClearButton: View { AddEventView( viewModel: EventViewModel(), eventName: .constant("Birthday"), + eventComplete: .constant(false), + eventCompleteDesc: .constant(""), eventSymbol: .constant("star"), eventColor: .constant(Color.red), eventDescription: .constant("A very special day"), diff --git a/NearFuture/ContentView.swift b/NearFuture/ContentView.swift index c6b8ef2..32943d0 100644 --- a/NearFuture/ContentView.swift +++ b/NearFuture/ContentView.swift @@ -15,6 +15,8 @@ enum Field { struct ContentView: View { @StateObject private var viewModel = EventViewModel() @State private var eventName = "" + @State private var eventComplete = false + @State private var eventCompleteDesc = "" @State private var eventSymbol = "star" @State private var eventColor: Color = [ Color.red, @@ -119,6 +121,8 @@ struct ContentView: View { AddEventView( viewModel: viewModel, eventName: $eventName, + eventComplete: $eventComplete, + eventCompleteDesc: $eventCompleteDesc, eventSymbol: $eventSymbol, eventColor: $eventColor, eventDescription: $eventDescription, @@ -159,6 +163,7 @@ struct ContentView: View { struct EventListView: View { @StateObject var viewModel: EventViewModel @State var event: Event + @State var opaity: Double = 1 var body: some View { NavigationLink() { @@ -170,18 +175,33 @@ struct EventListView: View { HStack { RoundedRectangle(cornerRadius: 5) .frame(width: 5) - .foregroundStyle(event.color.color) + .foregroundStyle( + event.color.color.opacity( + event.complete ? 0.5 : 1 + ) + ) .padding(.leading, -10) .padding(.vertical, 5) + .animation(.spring, value: event.complete) VStack(alignment: .leading) { HStack { Image(systemName: event.symbol) .resizable() .scaledToFit() .frame(width: 20, height: 20) - .foregroundStyle(event.color.color) + .foregroundStyle( + event.color.color.opacity( + event.complete ? 0.5 : 1 + ) + ) + .animation(.spring, value: event.complete) Text("\(event.name)") .font(.headline) + .strikethrough(event.complete) +// .foregroundStyle( +// event.complete ? .gray : .primary +// ) + .animation(.spring, value: event.complete) } if !event.description.isEmpty { Text(event.description) @@ -195,18 +215,63 @@ struct EventListView: View { ) ) .font(.subheadline) - .foregroundColor(event.color.color) + .foregroundStyle( + event.color.color.opacity( + event.complete ? 0.5 : 1 + ) + ) + .animation(.spring, value: event.complete) if event.recurrence != .none { - Text("Recurring: \(event.recurrence.rawValue.capitalized)") + Text("Recurs \(event.recurrence.rawValue)") .font(.subheadline) + .foregroundStyle( + .primary.opacity( + event.complete ? 0.5 : 1 + ) + ) + .animation(.spring, value: event.complete) } } Spacer() - Text("\(daysUntilEvent(event.date, short: false))") - .font(.subheadline) - .foregroundColor(event.color.color) + VStack { + Text("\(daysUntilEvent(event.date, short: false))") + .font(.subheadline) + .foregroundStyle( + event.color.color.opacity( + event.complete ? 0.5 : 1 + ) + ) + .animation(.spring, value: event.complete) + } + Button() { + withAnimation(.spring) { + event.complete.toggle() + opaity = 0.5 + } + viewModel.saveEvents() + } label: { + if event.complete { + ZStack { + Circle() + .foregroundStyle(.green) + Image(systemName: "checkmark") + .resizable() + .foregroundStyle(.white) + .scaledToFit() + .frame(width: 15) + } + } else { + Image(systemName: "circle") + .resizable() + .scaledToFit() + .foregroundStyle(event.color.color) + } + } + .buttonStyle(.borderless) + .frame(maxWidth: 25, maxHeight: 25) + .animation(.spring, value: event.complete) } } } @@ -241,3 +306,20 @@ struct SearchHelp: View { #Preview { ContentView() } + +#Preview("EventListView") { + EventListView( + viewModel: EventViewModel(), + event: Event( + name: "event", + complete: false, + completeDesc: "dofajiof", + symbol: "star", + color: ColorCodable(.orange), + description: "lksdjfakdflkasjlkjl", + date: Date(), + time: true, + recurrence: .daily + ) + ) +} diff --git a/NearFuture/EditEventView.swift b/NearFuture/EditEventView.swift index 0a2c89d..757ec4b 100644 --- a/NearFuture/EditEventView.swift +++ b/NearFuture/EditEventView.swift @@ -13,6 +13,8 @@ struct EditEventView: View { @Binding var event: Event @State private var eventName: String + @State private var eventComplete: Bool + @State private var eventCompleteDesc: String @State private var eventSymbol: String @State private var eventColor: Color @State private var eventDescription: String @@ -24,6 +26,8 @@ struct EditEventView: View { self.viewModel = viewModel _event = event _eventName = State(initialValue: event.wrappedValue.name) + _eventComplete = State(initialValue: event.wrappedValue.complete) + _eventCompleteDesc = State(initialValue: event.wrappedValue.completeDesc) _eventSymbol = State(initialValue: event.wrappedValue.symbol) _eventColor = State(initialValue: event.wrappedValue.color.color) _eventDescription = State(initialValue: event.wrappedValue.description) @@ -32,11 +36,33 @@ struct EditEventView: View { _eventRecurrence = State(initialValue: event.wrappedValue.recurrence) } + fileprivate func saveEdits() { + event.name = eventName + event.symbol = eventSymbol + event.color = ColorCodable(eventColor) + event.description = eventDescription + event.date = eventDate + event.recurrence = eventRecurrence + + //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 { // NavigationStack { AddEventView( viewModel: viewModel, eventName: $eventName, + eventComplete: $eventComplete, + eventCompleteDesc: $eventCompleteDesc, eventSymbol: $eventSymbol, eventColor: $eventColor, eventDescription: $eventDescription, @@ -49,26 +75,11 @@ struct EditEventView: View { .toolbar { ToolbarItem(placement: .topBarTrailing) { Button() { - event.name = eventName - event.symbol = eventSymbol - event.color = ColorCodable(eventColor) - event.description = eventDescription - event.date = eventDate - event.recurrence = eventRecurrence - - //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() + saveEdits() } label: { Text("Done") } + .disabled(eventName == "") } } // } @@ -81,6 +92,8 @@ struct EditEventView: View { event: .constant( Event( name: "Birthday", + complete: false, + completeDesc: "", symbol: "gear", color: ColorCodable(.red), description: "an event", diff --git a/NearFuture/Item.swift b/NearFuture/Item.swift index 2771edf..b1bbeba 100644 --- a/NearFuture/Item.swift +++ b/NearFuture/Item.swift @@ -22,6 +22,8 @@ import WidgetKit struct Event: Identifiable, Codable { var id = UUID() var name: String + var complete: Bool + var completeDesc: String var symbol: String var color: ColorCodable var description: String @@ -65,7 +67,7 @@ struct ColorCodable: Codable { } } -func daysUntilEvent(_ eventDate: Date, short: Bool) -> String { +func daysUntilEvent(_ eventDate: Date, short: Bool, sepLines: Bool = false) -> String { let calendar = Calendar.current let currentDate = Date() let components = calendar.dateComponents([.day], from: currentDate, to: eventDate) @@ -74,7 +76,7 @@ func daysUntilEvent(_ eventDate: Date, short: Bool) -> String { if short { return "\(days)d" } else { - return "\(-days) day\(-days == 1 ? "" : "s") ago" + return "\(-days)\(sepLines ? "\n" : " ")day\(-days == 1 ? "" : "s") ago" } } guard days != 0 else { @@ -83,7 +85,7 @@ func daysUntilEvent(_ eventDate: Date, short: Bool) -> String { if short { return "\(days)d" } else { - return "\(days) day\(days == 1 ? "" : "s")" + return "\(days)\(sepLines ? "\n" : " ")day\(days == 1 ? "" : "s")" } } @@ -156,6 +158,8 @@ class EventViewModel: ObservableObject { func addEvent( name: String, + complete: Bool, + completedDesc: String, symbol: String, color: ColorCodable, description: String, @@ -165,6 +169,8 @@ class EventViewModel: ObservableObject { ) { let newEvent = Event( name: name, + complete: complete, + completeDesc: completedDesc, symbol: symbol, color: color, description: description, diff --git a/NearFuture/SettingsView.swift b/NearFuture/SettingsView.swift index b726e14..ba0128c 100644 --- a/NearFuture/SettingsView.swift +++ b/NearFuture/SettingsView.swift @@ -109,6 +109,10 @@ struct SettingsView: View { Text("Import events") } + Section("Tip") { + Text("Near Future has Widgets!") + } + Section("Danger Zone") { Button("Delete local data", role: .destructive) { viewModel.dangerClearLocalData() diff --git a/NearFutureWidgets/NearFutureWidgets.swift b/NearFutureWidgets/NearFutureWidgets.swift index 13e77ab..fd87c48 100644 --- a/NearFutureWidgets/NearFutureWidgets.swift +++ b/NearFutureWidgets/NearFutureWidgets.swift @@ -50,7 +50,7 @@ struct EventWidgetProvider: TimelineProvider { struct EventWidgetView: View { var entry: EventWidgetEntry @Environment(\.widgetFamily) var widgetFamily - var showedEvents: Int { + var showedEventsNum: Int { switch widgetFamily { case .systemSmall: return 3 @@ -63,74 +63,100 @@ struct EventWidgetView: View { } } + let bgGradient = LinearGradient( + gradient: Gradient( + colors: [ + .black, + .gray.opacity(0.2) + ] + ), + startPoint: .bottom, + endPoint: .top + ) + var body: some View { let isLarge = widgetFamily == .systemLarge let events = entry.events - VStack { - Text("Upcoming Events") - .font(.subheadline) - .padding(.top, -12) -// .padding(.bottom, -5) - - ForEach(events.prefix(showedEvents), id: \.id) { event in - HStack { - RoundedRectangle(cornerRadius: 5) - .frame(width: 5) - .frame(maxHeight: isLarge ? 50 : 30) - .foregroundStyle(event.color.color) - .padding(.leading, -18) - .padding(.vertical, 2) - VStack(alignment: .leading) { + ZStack { + bgGradient + .padding(.top, 4) + .padding(.horizontal, -50) + .padding(.bottom, -100) + VStack { + Text("In the Near Future...") + .foregroundStyle(.white) + .font(.caption) + .bold() + .padding(.top, -12) + + ForEach(events.prefix(showedEventsNum), id: \.id) { event in + if !event.complete { HStack { - Image(systemName: event.symbol) - .resizable() - .scaledToFit() - .frame(width: 15, height: 15) + RoundedRectangle(cornerRadius: 5) + .frame(width: 5) + .frame(maxHeight: isLarge ? 50 : 30) .foregroundStyle(event.color.color) - Text("\(event.name)") - .font(.footnote) - .padding(.leading, -5) - } - - if isLarge { - if !event.description.isEmpty { - Text(event.description) - .font(.caption2) - .foregroundColor(.gray) - .padding(.top, -5) + .padding(.leading, -18) + .padding(.vertical, 2) + VStack(alignment: .leading) { + HStack { + Image(systemName: event.symbol) + .resizable() + .scaledToFit() + .frame(width: 15, height: 15) + .foregroundStyle(event.color.color) + Text("\(event.name)") + .foregroundStyle(.white) + .font(.footnote) + .padding(.leading, -5) + } + + if isLarge { + if !event.description.isEmpty { + Text(event.description) + .font(.caption2) + .foregroundColor(.gray) + .padding(.top, -5) + } + Text(event.date.formatted(date: .long, time: .omitted)) + .font(.caption2) + .foregroundColor(event.color.color) + .padding(.top, -5) + } + if event.recurrence != .none { + Text("\(event.recurrence.rawValue.capitalized)") + .foregroundStyle(.white) + .font(.caption2) + .padding(.top, -5) + } } - Text(event.date.formatted(date: .long, time: .omitted)) - .font(.caption2) + .padding(.leading, -15) + + Spacer() + + //short days till if not large widget + Text(daysUntilEvent(event.date, short: !isLarge, sepLines: true)) + .font(.caption) + .multilineTextAlignment(.trailing) .foregroundColor(event.color.color) - .padding(.top, -5) - } - if event.recurrence != .none { - Text("\(event.recurrence.rawValue.capitalized)") - .font(.caption2) - .padding(.top, -5) + .padding(.trailing, -12) } + } else { + /*@START_MENU_TOKEN@*/EmptyView()/*@END_MENU_TOKEN@*/ } - .padding(.leading, -15) - - Spacer() - - Text(daysUntilEvent(event.date, short: !isLarge)) - .font(.caption) - .foregroundColor(event.color.color) - .padding(.trailing, -12) + } + Spacer() + if showedEventsNum < events.count { + let xMoreEvents = events.count - showedEventsNum + Text("+\(xMoreEvents) more event\(xMoreEvents == 1 ? "" : "s")") + .font(.caption2) +// .foregroundStyle(.gray) + .padding(.top, -5) + .padding(.bottom, -15) } } - Spacer() - if showedEvents < events.count { - let xMoreEvents = events.count - showedEvents - Text("+\(xMoreEvents) more event\(xMoreEvents == 1 ? "" : "s")") - .font(.caption2) - .foregroundStyle(.gray) - .padding(.top, -5) - .padding(.bottom, -15) - } + .containerBackground(Color.widgetBackground, for: .widget) } - .containerBackground(Color.widgetBackground, for: .widget) } } @@ -138,6 +164,8 @@ struct Widget_Previews: PreviewProvider { static var events = [ Event( name: "Event Name", + complete: false, + completeDesc: "", symbol: "gear", color: ColorCodable(.blue), description: "Event description", @@ -146,36 +174,33 @@ struct Widget_Previews: PreviewProvider { recurrence: .yearly ), Event( - name: "A Day", + name: "distant past", + complete: false, + completeDesc: "", symbol: "star", color: ColorCodable(.orange), description: "description", + date: Date.distantPast, + time: false, + recurrence: .daily + ), + Event( + name: "event", + complete: false, + completeDesc: "", + symbol: "star", + color: ColorCodable(.purple), + description: "description", date: Date(), time: false, recurrence: .daily ), Event( - name: "A Day", + name: "An event", + complete: false, + completeDesc: "", symbol: "star", - color: ColorCodable(.orange), - description: "description", - date: Date(), - time: false, - recurrence: .daily - ), - Event( - name: "A Day", - symbol: "star", - color: ColorCodable(.orange), - description: "description", - date: Date(), - time: false, - recurrence: .daily - ), - Event( - name: "A Day", - symbol: "star", - color: ColorCodable(.orange), + color: ColorCodable(.green), description: "description", date: Date(), time: false, @@ -183,8 +208,10 @@ struct Widget_Previews: PreviewProvider { ), Event( name: "time event", - symbol: "", - color: ColorCodable(.blue), + complete: true, + completeDesc: "", + symbol: "clock", + color: ColorCodable(.brown), description: "an event with a time", date: Date(), time: true,