ContactPagerViewModel.swift 75 lignes · 2634 octets
import Combine
import Foundation

// Port of ui/contact/ContactPagerViewModel.kt — ordered contacts for the
// pager (same order as the carnet for the given sort) + duplicate counts.
// Adds delete and notes-save actions surfaced by the iOS fiche.

@MainActor
final class ContactPagerViewModel: ObservableObject {
    @Published private(set) var contacts: [CrmContactEntity] = []
    @Published private(set) var duplicateCountById: [Int64: Int] = [:]

    let initialContactId: Int64
    let sort: ContactSort

    private let repository: ContactRepository
    private var cancellables: Set<AnyCancellable> = []

    init(
        initialContactId: Int64,
        sort: ContactSort,
        repository: ContactRepository = ContactRepository(
            dao: Card2vcfDatabase.shared.crmContactDao,
            images: ContactImageStore()
        )
    ) {
        self.initialContactId = initialContactId
        self.sort = sort
        self.repository = repository
        NotificationCenter.default.publisher(for: .card2vcfDatabaseDidChange)
            .receive(on: RunLoop.main)
            .sink { [weak self] _ in Task { await self?.refresh() } }
            .store(in: &cancellables)
    }

    func refresh() async {
        do {
            let list = try await repository.fetchAll()
            let exclusions = (try? await repository.getExclusions()) ?? []
            let clusters = DuplicateDetector.clusters(contacts: list, exclusions: exclusions)
            contacts = AlphabetIndex.group(contacts: list, sort: sort).flatMap { $0.contacts }
            duplicateCountById = Dictionary(
                uniqueKeysWithValues: list.map {
                    ($0.id, DuplicateDetector.duplicateCount($0.id, clusters: clusters))
                }
            )
        } catch {
            contacts = []
            duplicateCountById = [:]
        }
    }

    func initialPageIndex(_ list: [CrmContactEntity]) -> Int {
        let idx = list.firstIndex { $0.id == initialContactId }
        return idx ?? 0
    }

    /// Inline notes editor on the fiche (Android edited notes in the edit form).
    func saveNotes(contactId: Int64, notes: String) async {
        guard let entity = try? await repository.getById(contactId) else { return }
        var card = entity.toCard()
        card.note = notes.nilIfBlank
        try? await repository.updateFromCard(
            id: contactId,
            card: card,
            preserveNotesIfBlankDraft: false
        )
        await refresh()
    }

    func delete(contactId: Int64) async {
        try? await repository.delete(contactId)
        await refresh()
    }
}