// // PrintSettingsView.swift // Padel Tournament // // Created by Razmig Sarkissian on 23/10/2023. // import SwiftUI import WebKit import PadelClubData struct PrintSettingsView: View { let tournament: Tournament @StateObject var generator: HtmlGenerator @State private var presentShareView: Bool = false @State private var prepareGroupStage: Bool = false @State private var generationId: UUID = UUID() @State private var generationGroupStageId: UUID = UUID() @State private var generating: Bool = false init(tournament: Tournament) { self.tournament = tournament _generator = StateObject(wrappedValue: HtmlGenerator(tournament: tournament)) } var body: some View { List { Section { // Toggle(isOn: $generator.displayHeads, label: { // Text("Afficher les têtes de séries") // } Toggle(isOn: $generator.displayPlannedDate, label: { Text("Afficher la date plannifiée") }) Toggle(isOn: $generator.displayTeamIndex, label: { Text("Afficher le poids et le rang de l'équipe") }) Toggle(isOn: $generator.displayRank, label: { Text("Afficher le classement du joueur") }) Toggle(isOn: $generator.displayScore, label: { Text("Afficher le score") Text("Affiche le score des matchs terminés") }) Toggle(isOn: $generator.includeBracket, label: { Text("Tableau") }) .onChange(of: generator.includeBracket) { oldValue, newValue in if newValue == false { generator.includeLoserBracket = newValue } } Toggle(isOn: $generator.includeLoserBracket, label: { Text("Tableau des matchs de classements") }) .disabled(generator.includeBracket == false) if tournament.groupStages().isEmpty == false { Toggle(isOn: $generator.includeGroupStage, label: { Text("Poules") }) } } Section { Picker(selection: $generator.zoomLevel) { Text("1 page").tag(nil as Optional) Text("50%").tag(2.0 as Optional) Text("100%").tag(1.0 as Optional) } label: { Text("Zoom") } .onChange(of: generator.zoomLevel) { if generator.zoomLevel == nil { generator.landscape = false } } if generator.zoomLevel != nil { Toggle(isOn: $generator.landscape, label: { Text("Format paysage") }) } HStack { Text("Nombre de page A4 à imprimer") Spacer() Text(generator.estimatedPageCount.formatted()) } } header: { Text("Tableau principal") } if generating == false { RowButtonView("Générer le PDF", systemImage: "printer") { await MainActor.run() { self.generating = true } generator.preparePDF { result in switch result { case .success(true): if generator.includeGroupStage && generator.groupStageIsReady == false && tournament.groupStages().isEmpty == false { self.prepareGroupStage = true self.generationGroupStageId = UUID() } else { self.presentShareView = true self.generating = false } case .success(false): print("didn't save pdf") break case .failure(let error): print(error) break } } self.prepareGroupStage = false self.generationId = UUID() } .disabled(generator.includeBracket == false && generator.includeGroupStage == false) } else { LabeledContent { ProgressView() } label: { Text("Préparation du PDF") } .id(generationId) } Section { NavigationLink { WebViewPreview() .environmentObject(generator) } label: { Text("Aperçu du tableau") } ForEach(tournament.rounds()) { round in if round.index > 0 { NavigationLink { WebViewPreview(round: round) .environmentObject(generator) } label: { Text("Aperçu \(round.correspondingLoserRoundTitle())") } } } ForEach(tournament.groupStages()) { groupStage in NavigationLink { WebViewPreview(groupStage: groupStage) .environmentObject(generator) } label: { Text("Aperçu de la \(groupStage.groupStageTitle())") } } } } .background { if generating { _backgroundGenerationWebView() _backgroundGroupStageWebView() } } .navigationTitle("Imprimer") .toolbarBackground(.visible, for: .navigationBar) .navigationBarTitleDisplayMode(.inline) .sheet(isPresented: $presentShareView) { if let pdfURL = generator.pdfURL { ShareSheet(urls: [pdfURL]) } } #if DEBUG .toolbar { ToolbarItem(placement: .topBarTrailing) { Menu { Section { ShareLink(item: generator.generateHtml()) { Text("Tableau") } if let groupStage = tournament.groupStages().first { ShareLink(item: HtmlService.groupstage(groupStage: groupStage).html(options: generator.options)) { Text("Poule") } } if let round = tournament.rounds().first { ShareLink(item: generator.generateLoserBracketHtml(upperRound: round)) { Text("Classement") } } } header: { Text("Partager le code source HTML") } } label: { LabelOptions() } } } #endif } @ViewBuilder private func _backgroundGenerationWebView() -> some View { WebView(htmlRawData: generator.generateHtml(), loadStatusChanged: { loaded, error, webView in if let error { print("preparePDF", error) } else if loaded == false { generator.generateWebView(webView: webView) } else { print("preparePDF", "is loading") } }) .opacity(0) .id(generationId) } private func _backgroundGroupStageWebView() -> some View { Group { if prepareGroupStage { ForEach(tournament.groupStages()) { groupStage in WebView(htmlRawData: HtmlService.groupstage(groupStage: groupStage).html(options: generator.options), loadStatusChanged: { loaded, error, webView in if let error { print("preparePDF", error) } else if loaded == false { generator.generateGroupStage(webView: webView) } else { print("preparePDF", "is loading") } }).opacity(0) } } } .id(generationGroupStageId) } } // MARK: Share Sheet struct ShareSheet: UIViewControllerRepresentable{ var urls: [Any] func makeUIViewController(context: Context) -> UIActivityViewController { let controller = UIActivityViewController(activityItems: urls, applicationActivities: nil) return controller } func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) { } } struct WebView: UIViewRepresentable { var htmlRawData: String? = nil var url: URL? = nil var loadStatusChanged: ((Bool, Error?, WKWebView) -> Void)? = nil func makeCoordinator() -> WebView.Coordinator { Coordinator(self) } func makeUIView(context: Context) -> WKWebView { let view = WKWebView() view.navigationDelegate = context.coordinator if let htmlRawData { view.loadHTMLString(htmlRawData, baseURL: nil) } if let url { view.loadFileURL(url, allowingReadAccessTo: url) } return view } func updateUIView(_ uiView: WKWebView, context: Context) { // you can access environment via context.environment here // Note that this method will be called A LOT } class Coordinator: NSObject, WKNavigationDelegate { let parent: WebView init(_ parent: WebView) { self.parent = parent } func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) { parent.loadStatusChanged?(true, nil, webView) } func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { parent.loadStatusChanged?(false, nil, webView) } func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) { parent.loadStatusChanged?(false, error, webView) } } } struct WebViewPreview: View { @EnvironmentObject var generator: HtmlGenerator let groupStage: GroupStage? let round: Round? @State private var html: String? init(groupStage: GroupStage? = nil, round: Round? = nil) { self.round = round self.groupStage = groupStage } var body: some View { Group { if let html { WebView(htmlRawData: html, loadStatusChanged: { loaded, error, webView in }) } else { ProgressView() .onAppear { if let groupStage { html = HtmlService.groupstage(groupStage: groupStage).html(options: generator.options) } else if let round { html = generator.generateLoserBracketHtml(upperRound: round) } else { html = generator.generateHtml() } } } } } }