From 025dbf22cdf2ea930356101fc31714c34e53b415 Mon Sep 17 00:00:00 2001 From: neon443 <69979447+neon443@users.noreply.github.com> Date: Mon, 12 May 2025 17:26:37 +0100 Subject: [PATCH] tring to ad animations --- NearFuture/Home/EventListView.swift | 1 + NearFuture/Item.swift | 15 +- NearFuture/Views/Archive/ArchiveView.swift | 11 +- NearFuture/Views/Events/AddEventView.swift | 297 +++++++++++---------- NearFuture/Views/Home/ContentView.swift | 54 ++-- NearFuture/Views/Home/EventListView.swift | 3 +- Resources/NearFutureIcon.pxd | Bin 175160 -> 175158 bytes 7 files changed, 209 insertions(+), 172 deletions(-) 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 d7e64af73e7cf3f4ec71d4edf56858096907bb39..4bbf16b1e624dfdf674dd4e70c1662ee371e61b4 100644 GIT binary patch delta 3363 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