You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
PadelClub/PadelClub/Views/Tournament/Screen/PrintSettingsView.swift

314 lines
11 KiB

//
// PrintSettingsView.swift
// Padel Tournament
//
// Created by Razmig Sarkissian on 23/10/2023.
//
import SwiftUI
import WebKit
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.displayRank, label: {
Text("Afficher le classement du joueur")
})
Toggle(isOn: $generator.includeBracket, label: {
Text("Tableau")
})
// Toggle(isOn: $generator.includeLoserBracket, label: {
// Text("Tableau des matchs de classements")
// })
if tournament.groupStages().isEmpty == false {
Toggle(isOn: $generator.includeGroupStage, label: {
Text("Poules")
})
}
}
if generator.includeBracket {
Section {
Picker(selection: $generator.zoomLevel) {
Text("1 page").tag(nil as Optional<CGFloat>)
Text("50%").tag(2.0 as Optional<CGFloat>)
Text("100%").tag(1.0 as Optional<CGFloat>)
} 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 && generator.includeLoserBracket == 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)
// .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(headName: generator.displayHeads, withRank: generator.displayRank, withScore: false)) {
// Text("Poule")
// }
// }
// } header: {
// Text("Partager le code source HTML")
// }
// } label: {
// Label("Options", systemImage: "ellipsis.circle")
// }
// }
// }
.sheet(isPresented: $presentShareView) {
if let pdfURL = generator.pdfURL {
ShareSheet(urls: [pdfURL])
}
}
}
@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(headName: generator.displayHeads, withRank: generator.displayRank, withScore: false), 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(headName: generator.displayHeads, withRank: generator.displayRank, withScore: false)
} else if let round {
html = generator.generateLoserBracketHtml(upperRound: round)
} else {
html = generator.generateHtml()
}
}
}
}
}
}