diff --git a/NearFuture/Home/EventListView.swift b/NearFuture/Home/EventListView.swift index 49d576c..c79174d 100644 --- a/NearFuture/Home/EventListView.swift +++ b/NearFuture/Home/EventListView.swift @@ -126,6 +126,7 @@ struct EventListView: View { ) .fixedSize(horizontal: false, vertical: true) } + .transition(.opacity) .contextMenu() { Button(role: .destructive) { let eventToModify = viewModel.events.firstIndex() { currEvent in diff --git a/NearFuture/Item.swift b/NearFuture/Item.swift index a6862c5..4e2fc48 100644 --- a/NearFuture/Item.swift +++ b/NearFuture/Item.swift @@ -20,7 +20,7 @@ import UserNotifications // } //} -struct Event: Identifiable, Codable { +struct Event: Identifiable, Codable, Equatable { var id = UUID() var name: String var complete: Bool @@ -415,9 +415,20 @@ class EventViewModel: ObservableObject { } class dummyEventViewModel: EventViewModel { + var template2: Event override init(load: Bool = false) { + self.template2 = Event( + name: "template2", + complete: false, + completeDesc: "", + symbol: "hammer", + color: ColorCodable(randomColor()), + notes: "notes", + date: Date(), + recurrence: .none + ) super.init(load: false) - self.events = [self.example, self.template, self.example, self.template] + self.events = [self.example, self.template, self.template2] self.events[0].complete.toggle() } } diff --git a/NearFuture/Views/Archive/ArchiveView.swift b/NearFuture/Views/Archive/ArchiveView.swift index e367fd7..0ec074f 100644 --- a/NearFuture/Views/Archive/ArchiveView.swift +++ b/NearFuture/Views/Archive/ArchiveView.swift @@ -22,11 +22,12 @@ struct ArchiveView: View { ForEach(viewModel.events.filter({$0.complete})) { event in EventListView(viewModel: viewModel, event: event) } - .padding(.horizontal) - .id(hey) - .onReceive(viewModel.objectWillChange) { - hey = UUID() - } + } + .transition(.opacity) + .padding(.horizontal) + .id(hey) + .onReceive(viewModel.objectWillChange) { + hey = UUID() } } } diff --git a/NearFuture/Views/Events/AddEventView.swift b/NearFuture/Views/Events/AddEventView.swift index d047c2b..b69ea54 100644 --- a/NearFuture/Views/Events/AddEventView.swift +++ b/NearFuture/Views/Events/AddEventView.swift @@ -24,6 +24,8 @@ struct AddEventView: View { @State var showNeedsNameAlert: Bool = false @State var isSymbolPickerPresented = false + @State private var bye: Bool = false + @FocusState private var focusedField: Field? private enum Field { case Name, Notes @@ -32,168 +34,177 @@ struct AddEventView: View { @Environment(\.dismiss) var dismiss var body: some View { - NavigationStack { - Form { - Section( - header: - Text("Event Details") - .font(.headline) - .foregroundColor(.accentColor) - ) { - // name & symbol - HStack(spacing: 5) { - Button() { - isSymbolPickerPresented.toggle() - } label: { - Image(systemName: eventSymbol) - .resizable() - .scaledToFit() - .frame(width: 20, height: 20) - .foregroundStyle(eventColor) - } - .frame(width: 20) - .buttonStyle(.borderless) - .sheet(isPresented: $isSymbolPickerPresented) { - SymbolsPicker( - selection: $eventSymbol, - title: "Choose a Symbol", - searchLabel: "Search...", - autoDismiss: true) - .presentationDetents([.medium]) - .apply { - if #available(iOS 16.4, *) { - $0.presentationBackground(.ultraThinMaterial) + ZStack { + backgroundGradient + NavigationStack { + Form { + Section( + header: + Text("Event Details") + .font(.headline) + .foregroundColor(.accentColor) + ) { + // name & symbol + HStack(spacing: 5) { + Button() { + isSymbolPickerPresented.toggle() + } label: { + Image(systemName: eventSymbol) + .resizable() + .scaledToFit() + .frame(width: 20, height: 20) + .foregroundStyle(eventColor) + } + .frame(width: 20) + .buttonStyle(.borderless) + .sheet(isPresented: $isSymbolPickerPresented) { + SymbolsPicker( + selection: $eventSymbol, + title: "Choose a Symbol", + searchLabel: "Search...", + autoDismiss: true) + .presentationDetents([.medium]) + .apply { + if #available(iOS 16.4, *) { + $0.presentationBackground(.ultraThinMaterial) + } } } + ColorPicker("", selection: $eventColor, supportsOpacity: false) + .fixedSize() + ZStack { + TextField("Event Name", text: $eventName) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .padding(.trailing, eventName.isEmpty ? 0 : 30) + .animation(.spring, value: eventName) + .focused($focusedField, equals: Field.Name) + .submitLabel(.next) + .onSubmit { + focusedField = .Notes + } + MagicClearButton(text: $eventName) + } } - ColorPicker("", selection: $eventColor, supportsOpacity: false) - .fixedSize() + + // dscription ZStack { - TextField("Event Name", text: $eventName) + TextField("Event Notes", text: $eventNotes) .textFieldStyle(RoundedBorderTextFieldStyle()) - .padding(.trailing, eventName.isEmpty ? 0 : 30) - .animation(.spring, value: eventName) - .focused($focusedField, equals: Field.Name) - .submitLabel(.next) + .padding(.trailing, eventNotes.isEmpty ? 0 : 30) + .animation(.spring, value: eventNotes) + .focused($focusedField, equals: Field.Notes) + .submitLabel(.done) .onSubmit { - focusedField = .Notes + focusedField = nil } - MagicClearButton(text: $eventName) + MagicClearButton(text: $eventNotes) } - } - - // dscription - ZStack { - TextField("Event Notes", text: $eventNotes) - .textFieldStyle(RoundedBorderTextFieldStyle()) - .padding(.trailing, eventNotes.isEmpty ? 0 : 30) - .animation(.spring, value: eventNotes) - .focused($focusedField, equals: Field.Notes) - .submitLabel(.done) - .onSubmit { - focusedField = nil + + + // date picker + HStack { + Spacer() + DatePicker("", selection: $eventDate, displayedComponents: .date) + .datePickerStyle(WheelDatePickerStyle()) + Spacer() + Button() { + eventDate = Date() + } label: { + Image(systemName: "arrow.uturn.left") + .resizable() + .scaledToFit() } - MagicClearButton(text: $eventNotes) - } - - - // date picker - HStack { - Spacer() - DatePicker("", selection: $eventDate, displayedComponents: .date) - .datePickerStyle(WheelDatePickerStyle()) - Spacer() - Button() { - eventDate = Date() - } label: { - Image(systemName: "arrow.uturn.left") - .resizable() - .scaledToFit() + .buttonStyle(BorderlessButtonStyle()) + .frame(width: 20) } - .buttonStyle(BorderlessButtonStyle()) - .frame(width: 20) - } - + DatePicker( "", selection: $eventDate, displayedComponents: .hourAndMinute ) - - // re-ocurrence Picker - Picker("Recurrence", selection: $eventRecurrence) { - ForEach(Event.RecurrenceType.allCases, id: \.self) { recurrence in - Text(recurrence.rawValue.capitalized) + + // re-ocurrence Picker + Picker("Recurrence", selection: $eventRecurrence) { + ForEach(Event.RecurrenceType.allCases, id: \.self) { recurrence in + Text(recurrence.rawValue.capitalized) + } } - } - .pickerStyle(SegmentedPickerStyle()) - Text( - describeOccurrence( - date: eventDate, - recurrence: eventRecurrence - ) - ) - } - } - .scrollContentBackground(.hidden) - .navigationTitle("\(adding ? "Add Event" : "")") - .navigationBarTitleDisplayMode(.inline) - .toolbar { - ToolbarItem(placement: .topBarLeading) { - if adding { - Button() { - resetAddEventView() - dismiss() - } label: { - Image(systemName: "xmark.circle.fill") - .symbolRenderingMode(.hierarchical) - .resizable() - .scaledToFit() - .frame(width: 30) - } - } - } - ToolbarItem(placement: .topBarTrailing) { - if adding { - Button { - viewModel.addEvent( - newEvent: Event( - name: eventName, - complete: eventComplete, - completeDesc: eventCompleteDesc, - symbol: eventSymbol, - color: ColorCodable(eventColor), - notes: eventNotes, - date: eventDate, - recurrence: eventRecurrence - ) + .pickerStyle(SegmentedPickerStyle()) + Text( + describeOccurrence( + date: eventDate, + recurrence: eventRecurrence ) - resetAddEventView() - } label: { - Text("Save") - .font(.headline) - .cornerRadius(10) - .buttonStyle(BorderedProminentButtonStyle()) + ) + } + } + .scrollContentBackground(.hidden) + .navigationTitle("\(adding ? "Add Event" : "")") + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .topBarLeading) { + if adding { + Button() { + resetAddEventView() + dismiss() + } label: { + Image(systemName: "xmark.circle.fill") + .symbolRenderingMode(.hierarchical) + .resizable() + .scaledToFit() + .frame(width: 30) + } } - .disabled(eventName.isEmpty) - .onTapGesture { + } + ToolbarItem(placement: .topBarTrailing) { + if adding { + Button { + viewModel.addEvent( + newEvent: Event( + name: eventName, + complete: eventComplete, + completeDesc: eventCompleteDesc, + symbol: eventSymbol, + color: ColorCodable(eventColor), + notes: eventNotes, + date: eventDate, + recurrence: eventRecurrence + ) + ) + bye.toggle() + resetAddEventView() + } label: { + Text("Save") + .font(.headline) + .cornerRadius(10) + .buttonStyle(BorderedProminentButtonStyle()) + } + .apply { + if #available(iOS 17, *) { + $0.sensoryFeedback(.success, trigger: bye) + } + } + .disabled(eventName.isEmpty) + .onTapGesture { + if eventName.isEmpty { + showNeedsNameAlert.toggle() + } + } + .alert("Missing Name", isPresented: $showNeedsNameAlert) { + Button("OK", role: .cancel) { + showNeedsNameAlert.toggle() + focusedField = .Name + } + } message: { + Text("Give your Event a name before saving.") + } if eventName.isEmpty { - showNeedsNameAlert.toggle() - } - } - .alert("Missing Name", isPresented: $showNeedsNameAlert) { - Button("OK", role: .cancel) { - showNeedsNameAlert.toggle() - focusedField = .Name - } - } message: { - Text("Give your Event a name before saving.") - } - if eventName.isEmpty { - HStack { - Image(systemName: "exclamationmark") - .foregroundStyle(.red) - Text("Give your event a name.") + HStack { + Image(systemName: "exclamationmark") + .foregroundStyle(.red) + Text("Give your event a name.") + } } } } diff --git a/NearFuture/Views/Home/ContentView.swift b/NearFuture/Views/Home/ContentView.swift index c5e9d8a..1affb75 100644 --- a/NearFuture/Views/Home/ContentView.swift +++ b/NearFuture/Views/Home/ContentView.swift @@ -13,11 +13,16 @@ enum Field { case Search } enum Tab { - case Home - case Archive - case Statistics - case Settings - case Gradient + case home + case archive + case stats + case settings +} +enum FilterCategory { + case Future + case Past + case Complete + case Incomplete } struct ContentView: View { @@ -36,7 +41,11 @@ struct ContentView: View { @State private var searchInput: String = "" var filteredEvents: [Event] { if searchInput.isEmpty { - return viewModel.events.filter() {!$0.complete} + if settingsModel.settings.showCompletedInHome { + return viewModel.events + } else { + return viewModel.events.filter() {!$0.complete} + } } else { return viewModel.events.filter { $0.name.localizedCaseInsensitiveContains(searchInput) || @@ -44,17 +53,9 @@ struct ContentView: View { } } } - - var noEvents: Bool { - if viewModel.events.count == 0 { - return true - } else { - return false - } - } + @State private var focusedTab: Tab = .home @FocusState private var focusedField: Field? - @FocusState private var focusedTab: Tab? var body: some View { TabView { @@ -85,17 +86,28 @@ struct ContentView: View { if filteredEvents.isEmpty && !searchInput.isEmpty { HelpView(searchInput: $searchInput, focusedField: focusedField) } else { + Button("hiiiiiiiiiiiiiii") { + withAnimation() { + settingsModel.settings.showCompletedInHome.toggle() + } + } + Button("sort") { + withAnimation() { + viewModel.events.sort() { $0.date < $1.date } + } + } ScrollView { ForEach(filteredEvents) { event in EventListView(viewModel: viewModel, event: event) } - .onDelete(perform: viewModel.removeEvent) + .animation(.default, value: filteredEvents) + .transition(.opacity) .id(hey) .onReceive(viewModel.objectWillChange) { hey = UUID() } .padding(.horizontal) - if /*!searchInput.isEmpty && */filteredEvents.isEmpty { + if filteredEvents.isEmpty { HelpView( searchInput: $searchInput, focusedField: focusedField @@ -143,22 +155,22 @@ struct ContentView: View { .tabItem { Label("Home", systemImage: "house") } - .focused($focusedTab, equals: Tab.Home) + .tag(Tab.home) ArchiveView(viewModel: viewModel) .tabItem() { Label("Archive", systemImage: "tray.full") } - .focused($focusedTab, equals: Tab.Archive) + .tag(Tab.archive) StatsView(viewModel: viewModel) .tabItem { Label("Statistics", systemImage: "chart.pie") } - .focused($focusedTab, equals: Tab.Statistics) + .tag(Tab.stats) SettingsView(viewModel: viewModel, settingsModel: settingsModel) .tabItem { Label("Settings", systemImage: "gear") } - .focused($focusedTab, equals: Tab.Settings) + .tag(Tab.settings) } } } diff --git a/NearFuture/Views/Home/EventListView.swift b/NearFuture/Views/Home/EventListView.swift index 49d576c..3fb1182 100644 --- a/NearFuture/Views/Home/EventListView.swift +++ b/NearFuture/Views/Home/EventListView.swift @@ -75,7 +75,7 @@ struct EventListView: View { VStack { Text("\(daysUntilEvent(event.date).long)") .font(.subheadline) - .foregroundStyle(.one) + .foregroundStyle(event.date.timeIntervalSinceNow < 0 ? .red : .one) } Button() { withAnimation { @@ -112,6 +112,7 @@ struct EventListView: View { .shadow(radius: 5) .padding(.trailing, 5) } + .transition(.opacity) .padding(.vertical, 5) .background(.ultraThinMaterial) .overlay( diff --git a/Resources/NearFutureIcon.pxd b/Resources/NearFutureIcon.pxd index d7e64af..4bbf16b 100644 Binary files a/Resources/NearFutureIcon.pxd and b/Resources/NearFutureIcon.pxd differ