// // ContactManager.swift // Padel Tournament // // Created by Razmig Sarkissian on 19/09/2023. // import Foundation import SwiftUI import MessageUI import LeStorage enum ContactManagerError: LocalizedError { case mailFailed case mailNotSent //no network no error case messageFailed case messageNotSent //no network no error case calendarAccessDenied case calendarEventSaveFailed case noCalendarAvailable case uncalledTeams([TeamRegistration]) var localizedDescription: String { switch self { case .mailFailed: return "Le mail n'a pas été envoyé" case .mailNotSent: return "Le mail est dans la boîte d'envoi de l'app Mail. Vérifiez son état dans l'app Mail avant d'essayer de le renvoyer." case .messageFailed: return "Le SMS n'a pas été envoyé" case .messageNotSent: return "Le SMS n'a pas été envoyé" case .uncalledTeams(let array): let verb = array.count > 1 ? "peuvent" : "peut" return "Attention, \(array.count) équipe\(array.count.pluralSuffix) ne \(verb) pas être contacté par la méthode choisie" case .calendarAccessDenied: return "Padel Club n'a pas accès à votre calendrier" case .calendarEventSaveFailed: return "Padel Club n'a pas réussi à sauver ce tournoi dans votre calendrier" case .noCalendarAvailable: return "Padel Club n'a pas réussi à trouver un calendrier pour y inscrire ce tournoi" } } static func getNetworkErrorMessage(sentError: ContactManagerError?, networkMonitorConnected: Bool) -> String { var errors: [String] = [] if networkMonitorConnected == false { errors.append("L'appareil n'est pas connecté à internet.") } if let sentError { errors.append(sentError.localizedDescription) } return errors.joined(separator: "\n") } } enum ContactType: Identifiable { case mail(date: Date?, recipients: [String]?, bccRecipients: [String]?, body: String?, subject: String?, tournamentBuild: TournamentBuild?) case message(date: Date?, recipients: [String]?, body: String?, tournamentBuild: TournamentBuild?) var id: Int { switch self { case .message: return 0 case .mail: return 1 } } } extension ContactType { static let defaultCustomMessage: String = """ Il est conseillé de vous présenter 10 minutes avant de jouer.\n\nMerci de me confirmer votre présence avec votre nom et de prévenir votre partenaire. """ static let defaultAvailablePaymentMethods: String = "Règlement possible par chèque ou espèces." static func callingCustomMessage(source: String? = nil, tournament: Tournament?, startDate: Date?, roundLabel: String) -> String { let tournamentCustomMessage = source ?? DataStore.shared.user.summonsMessageBody ?? defaultCustomMessage let clubName = tournament?.clubName ?? "" var text = tournamentCustomMessage let date = startDate ?? tournament?.startDate ?? Date() if let tournament { text = text.replacingOccurrences(of: "#titre", with: tournament.tournamentTitle(.short)) text = text.replacingOccurrences(of: "#prix", with: tournament.entryFeeMessage) } text = text.replacingOccurrences(of: "#club", with: clubName) text = text.replacingOccurrences(of: "#manche", with: roundLabel.lowercased()) text = text.replacingOccurrences(of: "#jour", with: "\(date.formatted(Date.FormatStyle().weekday(.wide).day().month(.wide)))") text = text.replacingOccurrences(of: "#horaire", with: "\(date.formatted(Date.FormatStyle().hour().minute()))") let signature = DataStore.shared.user.summonsMessageSignature ?? DataStore.shared.user.defaultSignature() text = text.replacingOccurrences(of: "#signature", with: signature) return text } static func callingMessage(tournament: Tournament?, startDate: Date?, roundLabel: String, matchFormat: MatchFormat?, reSummon: Bool = false) -> String { let useFullCustomMessage = DataStore.shared.user.summonsUseFullCustomMessage if useFullCustomMessage { return callingCustomMessage(tournament: tournament, startDate: startDate, roundLabel: roundLabel) } let date = startDate ?? tournament?.startDate ?? Date() let clubName = tournament?.clubName ?? "" let message = DataStore.shared.user.summonsMessageBody ?? defaultCustomMessage let signature = DataStore.shared.user.summonsMessageSignature ?? DataStore.shared.user.defaultSignature() let localizedCalled = "convoqué" + (tournament?.tournamentCategory == .women ? "e" : "") + "s" var entryFeeMessage: String? { (DataStore.shared.user.summonsDisplayEntryFee) ? tournament?.entryFeeMessage : nil } var computedMessage: String { [entryFeeMessage, message].compacted().map { $0.trimmedMultiline }.joined(separator: "\n\n") } let intro = reSummon ? "Suite à des forfaits, vous êtes finalement" : "Vous êtes" if let tournament { return "Bonjour,\n\n\(intro) \(localizedCalled) pour jouer en \(roundLabel.lowercased()) du \(tournament.tournamentTitle(.short)) au \(clubName) le \(date.formatted(Date.FormatStyle().weekday(.wide).day().month(.wide))) à \(date.formatted(Date.FormatStyle().hour().minute())).\n\n" + computedMessage + "\n\n\(signature)" } else { return "Bonjour,\n\n\(intro) \(localizedCalled) \(roundLabel) au \(clubName) le \(date.formatted(Date.FormatStyle().weekday(.wide).day().month(.wide))) à \(date.formatted(Date.FormatStyle().hour().minute())).\n\nMerci de confirmer en répondant à ce message et de prévenir votre partenaire !\n\n\(signature)" } } } struct MessageComposeView: UIViewControllerRepresentable { typealias Completion = (_ result: MessageComposeResult) -> Void static var canSendText: Bool { MFMessageComposeViewController.canSendText() } let recipients: [String]? let body: String? let completion: Completion? func makeUIViewController(context: Context) -> UIViewController { guard Self.canSendText else { let errorView = ContentUnavailableView("Aucun compte de messagerie", systemImage: "xmark", description: Text("Aucun compte de messagerie n'est configuré sur cet appareil.")) return UIHostingController(rootView: errorView) } let controller = MFMessageComposeViewController() controller.messageComposeDelegate = context.coordinator controller.recipients = recipients controller.body = body return controller } func updateUIViewController(_ uiViewController: UIViewController, context: Context) {} func makeCoordinator() -> Coordinator { Coordinator(completion: self.completion) } class Coordinator: NSObject, MFMessageComposeViewControllerDelegate { private let completion: Completion? public init(completion: Completion?) { self.completion = completion } public func messageComposeViewController(_ controller: MFMessageComposeViewController, didFinishWith result: MessageComposeResult) { controller.dismiss(animated: true, completion: { self.completion?(result) }) } } } struct MailComposeView: UIViewControllerRepresentable { typealias Completion = (_ result: MFMailComposeResult) -> Void static var canSendMail: Bool { if let mailURL = URL(string: "mailto:?to=jap@padelclub.com") { let mailConfigured = UIApplication.shared.canOpenURL(mailURL) return mailConfigured && MFMailComposeViewController.canSendMail() } else { return MFMailComposeViewController.canSendMail() } } let recipients: [String]? let bccRecipients: [String]? let body: String? let subject: String? var attachmentURL: URL? let completion: Completion? func makeUIViewController(context: Context) -> UIViewController { guard Self.canSendMail else { let errorView = ContentUnavailableView("Aucun compte mail", systemImage: "xmark", description: Text("Aucun compte mail n'est configuré sur cet appareil.")) return UIHostingController(rootView: errorView) } let controller = MFMailComposeViewController() controller.mailComposeDelegate = context.coordinator controller.setToRecipients(recipients) controller.setBccRecipients(bccRecipients) if let attachmentURL { do { let attachmentData = try Data(contentsOf: attachmentURL) controller.addAttachmentData(attachmentData, mimeType: "application/zip", fileName: "backup.zip") } catch { print("Could not attach file: \(error)") } } if let body { controller.setMessageBody(body, isHTML: false) } if let subject { controller.setSubject(subject) } return controller } func updateUIViewController(_ uiViewController: UIViewController, context: Context) {} func makeCoordinator() -> Coordinator { Coordinator(completion: self.completion) } class Coordinator: NSObject, MFMailComposeViewControllerDelegate { private let completion: Completion? public init(completion: Completion?) { self.completion = completion } public func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) { controller.dismiss(animated: true, completion: { self.completion?(result) }) } } }