diff --git a/EmployeeDirectory-Observed/ContentView.swift b/EmployeeDirectory-Observed/ContentView.swift index ab1e4c9..3ea8c96 100644 --- a/EmployeeDirectory-Observed/ContentView.swift +++ b/EmployeeDirectory-Observed/ContentView.swift @@ -22,6 +22,7 @@ struct ContentView: View { public struct EmployeeDirectoryList: View { @State public var viewModel: EmployeesViewModel @State public var path = NavigationPath() + init(viewModel: EmployeesViewModel? = nil) { _viewModel = .init(wrappedValue: viewModel ?? .init(service: EmployeeService())) } @@ -33,7 +34,7 @@ public struct EmployeeDirectoryList: View { let employeeViewModel = EmployeeViewModel(employee: employee) HStack (spacing: 10) { ProfileImageView(urlString: employeeViewModel.smallPhoto, size: 51) - VStack(alignment: .leading) { + VStack(alignment: .leading, spacing: 5) { Text(employeeViewModel.fullName) .font(.headline) @@ -54,10 +55,33 @@ public struct EmployeeDirectoryList: View { .font(.caption) } } - }.onTapGesture { + } + .onTapGesture { path.append(employee) } + .swipeActions(edge: .leading) { + Button("Pin") { + print("\(employee.firstName) pinned") + } + .tint(.orange) + } + .swipeActions(edge: .leading) { + Button("Flagged") { + print("\(employee.firstName) flagged") + } + .tint(.yellow) + } + .swipeActions { + Button(role: .destructive) { + print("\(employee.firstName) deleted") + } label: { + Label("Delete", systemImage: "trash") + } + } } + .onMove(perform: moveEmployee) + .onDelete(perform: deleteEmployees) + if viewModel.hasNextPage { ProgressView() .frame(maxWidth: .infinity, alignment: .center) @@ -76,8 +100,26 @@ public struct EmployeeDirectoryList: View { await viewModel.loadNextPage() } } + .toolbar { + EditButton() + } } } + + // Move Function (Reordering Rows) + private func moveEmployee(from source: IndexSet, to destination: Int) { + viewModel.employees.move(fromOffsets: source, toOffset: destination) + } + + // Delete Function (Swipe to Delete) + private func deleteEmployees(at offsets: IndexSet) { + viewModel.employees.remove(atOffsets: offsets) + } + + // Single Delete Function (For Swipe Actions) + private func deleteEmployee(_ employee: Employee) { + viewModel.employees.removeAll { $0.id == employee.id } + } } public struct EmployeeDetailView: View { @@ -99,13 +141,23 @@ public struct EmployeeDetailView: View { } Label("Email: \(viewModel.emailAddress)", systemImage: "envelope.fill") - .foregroundColor(.gray) + .foregroundColor(viewModel.emailAddressURL != nil ? .blue : .gray) .font(.caption) + .onTapGesture { + if let url = viewModel.emailAddressURL { + UIApplication.shared.open(url) + } + } if let phoneNumber = viewModel.phoneNumber { Label("Call: \(phoneNumber)", systemImage: "phone.fill") - .foregroundColor(.gray) + .foregroundColor(viewModel.phoneNumberURL != nil ? .blue : .gray) .font(.caption) + .onTapGesture { + if let url = viewModel.phoneNumberURL { + UIApplication.shared.open(url) + } + } } Spacer() @@ -180,6 +232,8 @@ public class EmployeeViewModel { public private(set) var biography: String? public private(set) var smallPhoto: String? public private(set) var largePhoto: String? + public private(set) var emailAddressURL: URL? + public private(set) var phoneNumberURL: URL? // MARK: - Initializer public init(employee: Employee) { @@ -191,6 +245,14 @@ public class EmployeeViewModel { biography = employee.biography smallPhoto = employee.photoURLSmall largePhoto = employee.photoURLLarge + + if let url = URL(string: "mailto:\(emailAddress)"), UIApplication.shared.canOpenURL(url) { + emailAddressURL = url + } + + if let phoneNumber, let url = URL(string: "tel:\(phoneNumber)"), UIApplication.shared.canOpenURL(url) { + phoneNumberURL = url + } } }