Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save bajubullet/db1ee64e6772d2e73dd3f2c59a595b1f to your computer and use it in GitHub Desktop.
Save bajubullet/db1ee64e6772d2e73dd3f2c59a595b1f to your computer and use it in GitHub Desktop.
import SwiftUI
struct Event: Identifiable {
let id = UUID()
let name: String
let date: Date
}
struct ContentView: View {
@State private var events: [Event] = []
@State private var showingAddEvent = false
@State private var lastDeletedEvent: (event: Event, index: Int)? = nil
@State private var showUndo: Bool = false
var body: some View {
NavigationView {
ZStack {
if events.isEmpty {
VStack(spacing: 16) {
Image(systemName: "calendar.badge.exclamationmark")
.font(.system(size: 50))
.foregroundColor(.gray)
Text("No countdowns yet")
.font(.headline)
.foregroundColor(.secondary)
Text("Tap + to add your first event.")
.font(.subheadline)
.foregroundColor(.secondary)
}
.multilineTextAlignment(.center)
.padding()
} else {
List {
ForEach(events) { event in
CountdownCard(event: event)
.listRowInsets(EdgeInsets())
.padding(.vertical, 8)
}
.onDelete(perform: deleteEvent)
}
.listStyle(PlainListStyle())
}
}
.navigationTitle("Countdowns")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: {
showingAddEvent = true
}) {
Image(systemName: "plus")
}
}
}
.sheet(isPresented: $showingAddEvent) {
AddEventView { newEvent in
events.append(newEvent)
}
}
}
.overlay(
Group {
if showUndo, let last = lastDeletedEvent {
VStack {
Spacer()
HStack {
Text("Event deleted")
.foregroundColor(.white)
Spacer()
Button("Undo") {
events.insert(last.event, at: last.index)
lastDeletedEvent = nil
showUndo = false
}
.foregroundColor(.yellow)
}
.padding()
.background(Color.black.opacity(0.9))
.cornerRadius(10)
.padding(.horizontal)
.transition(.move(edge: .bottom))
}
.animation(.easeInOut, value: showUndo)
}
}
)
}
private func deleteEvent(at offsets: IndexSet) {
if let index = offsets.first {
let deleted = events[index]
lastDeletedEvent = (deleted, index)
events.remove(atOffsets: offsets)
showUndo = true
// Auto-hide undo after 5 seconds
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
if showUndo {
lastDeletedEvent = nil
showUndo = false
}
}
}
}
}
struct CountdownCard: View {
let event: Event
var daysLeft: Int {
let cal = Calendar.current
let start = cal.startOfDay(for: Date())
let end = cal.startOfDay(for: event.date)
let components = cal.dateComponents([.day], from: start, to: end)
return components.day ?? 0
}
var body: some View {
VStack(alignment: .leading, spacing: 8) {
Text(event.name)
.font(.headline)
Text("\(daysLeft) days left")
.font(.title)
.fontWeight(.bold)
.foregroundColor(.blue)
}
.padding()
.frame(maxWidth: .infinity, alignment: .leading)
.background(Color(.secondarySystemBackground))
.cornerRadius(12)
.shadow(radius: 4)
}
}
struct AddEventView: View {
@Environment(\.dismiss) var dismiss
@State private var name: String = ""
@State private var date: Date = Date()
var onSave: (Event) -> Void
var body: some View {
NavigationView {
Form {
Section(header: Text("Event Details")) {
TextField("Event Name", text: $name)
DatePicker("Date", selection: $date, displayedComponents: .date)
}
}
.navigationTitle("Add Event")
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Cancel") {
dismiss()
}
}
ToolbarItem(placement: .confirmationAction) {
Button("Save") {
let newEvent = Event(name: name, date: date)
onSave(newEvent)
dismiss()
}
.disabled(name.trimmingCharacters(in: .whitespaces).isEmpty)
}
}
}
}
}
#Preview {
ContentView()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment