BusinessCard/BusinessCard/State/ContactsStore.swift

156 lines
4.5 KiB
Swift

import Foundation
import Observation
import SwiftData
@Observable
@MainActor
final class ContactsStore: ContactTracking {
private let modelContext: ModelContext
private(set) var contacts: [Contact] = []
var searchQuery: String = ""
init(modelContext: ModelContext) {
self.modelContext = modelContext
fetchContacts()
}
func fetchContacts() {
let descriptor = FetchDescriptor<Contact>(
sortBy: [SortDescriptor(\.lastSharedDate, order: .reverse)]
)
do {
contacts = try modelContext.fetch(descriptor)
} catch {
contacts = []
}
}
var visibleContacts: [Contact] {
let trimmedQuery = searchQuery.trimmingCharacters(in: .whitespacesAndNewlines)
guard !trimmedQuery.isEmpty else { return contacts }
return contacts.filter { contact in
contact.name.localizedStandardContains(trimmedQuery)
|| contact.company.localizedStandardContains(trimmedQuery)
|| contact.role.localizedStandardContains(trimmedQuery)
|| contact.tags.localizedStandardContains(trimmedQuery)
|| contact.notes.localizedStandardContains(trimmedQuery)
}
}
/// Contacts with overdue follow-ups
var overdueFollowUps: [Contact] {
contacts.filter { $0.isFollowUpOverdue }
}
/// Contacts that were received (scanned from others)
var receivedCards: [Contact] {
contacts.filter { $0.isReceivedCard }
}
func recordShare(for name: String, role: String, company: String, cardLabel: String) {
// Check if contact already exists
if let existingContact = contacts.first(where: { $0.name == name && $0.company == company }) {
existingContact.lastSharedDate = .now
existingContact.cardLabel = cardLabel
} else {
let newContact = Contact(
name: name,
role: role,
company: company,
avatarSystemName: "person.crop.circle",
lastSharedDate: .now,
cardLabel: cardLabel
)
modelContext.insert(newContact)
}
saveContext()
fetchContacts()
}
/// Adds a contact from a received vCard (scanned QR code)
func addReceivedCard(vCardData: String) {
let contact = Contact.fromVCard(vCardData)
modelContext.insert(contact)
saveContext()
fetchContacts()
}
/// Creates a new contact manually
func createContact(
name: String,
role: String = "",
company: String = "",
email: String = "",
phone: String = "",
notes: String = "",
tags: String = "",
followUpDate: Date? = nil,
contactFields: [ContactField] = [],
photoData: Data? = nil
) {
let contact = Contact(
name: name,
role: role,
company: company,
cardLabel: "Manual",
notes: notes,
tags: tags,
followUpDate: followUpDate,
email: email,
phone: phone,
photoData: photoData
)
modelContext.insert(contact)
// Add contact fields if provided
if !contactFields.isEmpty {
for field in contactFields {
field.contact = contact
modelContext.insert(field)
}
contact.contactFields = contactFields
}
saveContext()
fetchContacts()
}
/// Updates a contact's notes
func updateNotes(for contact: Contact, notes: String) {
contact.notes = notes
saveContext()
}
/// Updates a contact's tags
func updateTags(for contact: Contact, tags: String) {
contact.tags = tags
saveContext()
}
/// Sets or clears a follow-up reminder
func setFollowUp(for contact: Contact, date: Date?) {
contact.followUpDate = date
saveContext()
}
func deleteContact(_ contact: Contact) {
modelContext.delete(contact)
saveContext()
fetchContacts()
}
func relativeShareDate(for contact: Contact) -> String {
contact.lastSharedDate.formatted(
.relative(presentation: .named, unitsStyle: .abbreviated)
)
}
private func saveContext() {
do {
try modelContext.save()
} catch {
// Handle error silently for now
}
}
}