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/BroadcastView.swift

414 lines
17 KiB

//
// BroadcastView.swift
// PadelClub
//
// Created by Razmig Sarkissian on 01/05/2024.
//
import SwiftUI
import CoreImage.CIFilterBuiltins
import LeStorage
import TipKit
extension String : Identifiable {
public var id: String { self }
}
struct BroadcastView: View {
@EnvironmentObject var dataStore: DataStore
@Environment(Tournament.self) var tournament: Tournament
@Environment(NavigationViewModel.self) var navigation: NavigationViewModel
let context = CIContext()
let filter = CIFilter.qrCodeGenerator()
@State private var urlToShow: String?
@State private var tvMode: Bool = false
@State private var pageLink: PageLink = .matches
let createAccountTip = CreateAccountTip()
let tournamentPublishingTip = TournamentPublishingTip()
let tournamentTVBroadcastTip = TournamentTVBroadcastTip()
var body: some View {
@Bindable var tournament = tournament
List {
if StoreCenter.main.userId == nil {
Section {
ContentUnavailableView {
Label("Créer votre compte Padel Club", systemImage: "person.bust")
} description: {
let message = "Un compte est nécessaire pour publier le tournoi sur [Padel Club](\(URLs.main.rawValue)) et profiter de toutes les pages du site, comme le mode TV pour transformer l'expérience de vos tournois !"
Text(.init(message))
} actions: {
RowButtonView("Créer votre compte") {
navigation.selectedTab = .umpire
}
RowButtonView("Jeter un oeil au site Padel Club") {
UIApplication.shared.open(URLs.main.url)
}
}
}
} else if tournament.club() == nil {
Section {
ContentUnavailableView {
Text("Aucun club associé à votre tournoi")
} description: {
Text("Vous avez besoin d'associer un club à votre tournoi pour pouvoir publier celui-ci sur Padel Club.")
} actions: {
NavigationLink {
ClubsView() { club in
if let event = tournament.eventObject() {
event.club = club.id
do {
try dataStore.events.addOrUpdate(instance: event)
} catch {
Logger.error(error)
}
}
}
} label: {
Text("Choisir un club")
}
}
}
} else {
Section {
TipView(tournamentPublishingTip) { action in
UIApplication.shared.open(URLs.main.url)
}
.tipStyle(tint: nil)
}
Section {
TipView(tournamentTVBroadcastTip)
.tipStyle(tint: nil)
}
if let url = tournament.shareURL(.clubBroadcast) {
Section {
Link(destination: url) {
Text(url.absoluteString)
}
.contextMenu {
Button("Copier") {
let pasteboard = UIPasteboard.general
pasteboard.string = url.absoluteString
}
}
} header: {
Text("Lien pour la diffusion TV")
} footer: {
Text("Lien à mettre sur une smart tv ou ordinateur dans le club house par exemple ! Disponible même si le tournoi est privée.")
}
}
Section {
Toggle(isOn: $tournament.isPrivate) {
Text("Tournoi privé")
if (tournament.isPrivate && Guard.main.purchasedTransactions.isEmpty) {
Text("Vous devez disposer d'une offre pour rendre publique ce tournoi.")
.foregroundStyle(.logoRed)
}
}
.disabled(_disablePrivateToggle())
} footer: {
let verb : String = tournament.isPrivate ? "est" : "sera"
let footerString = " Le tournoi \(verb) masqué sur le site [Padel Club](\(URLs.main.rawValue))"
Text(.init(footerString))
}
if tournament.isPrivate == false {
Section {
let links : [PageLink] = [.teams, .summons, .groupStages, .matches, .rankings, .broadcast, .clubBroadcast]
Picker(selection: $pageLink) {
ForEach(links) { pageLink in
Text(pageLink.localizedLabel()).tag(pageLink)
}
} label: {
Text("Choisir la page à partager")
}
.pickerStyle(.menu)
actionForURL(title: "Partager la page '" + pageLink.localizedLabel() + "'", url: tournament.shareURL(pageLink))
} header: {
Text("Lien du tournoi à partager")
}
Section {
let club = tournament.club()
actionForURL(title: (club == nil) ? "Aucun club indiqué pour ce tournoi" : club!.clubTitle(), description: "Page du club", url: club?.shareURL())
actionForURL(title: "Padel Club", url: URLs.main.url)
} header: {
Text("Autres liens")
}
Section {
LabeledContent {
if tournament.isTournamentPublished() {
Image(systemName:"checkmark").foregroundStyle(.green)
} else {
Text(tournament.publishedTournamentDate().formatted())
}
} label: {
if tournament.isTournamentPublished() {
Text("Publiée")
} else {
Text("Publication prévue")
}
}
if tournament.canBePublished() == false {
Text("Pour être visible automatiquement, le tournoi doit avoir été créé il y a 24h, avoir une structure et au moins 4 inscriptions.")
}
} header: {
Text("Information sur le tournoi")
} footer: {
if Date() < tournament.publishedTournamentDate() || tournament.canBePublished() == false {
HStack {
Spacer()
FooterButtonView(tournament.publishTournament ? "masquer sur le site" : "publier maintenant") {
tournament.publishTournament.toggle()
_save()
}
}
}
}
Section {
LabeledContent {
if tournament.areTeamsPublished() {
Image(systemName:"checkmark").foregroundStyle(.green)
} else {
Text(tournament.publishedTeamsDate().formatted())
}
} label: {
if tournament.areTeamsPublished() {
Text("Publiée")
} else {
Text("Publication prévue")
}
}
Toggle(isOn: $tournament.hideTeamsWeight) {
Text("Masquer les poids des équipes")
}
} header: {
Text("Liste des équipes")
} footer: {
if Date() < tournament.publishedTeamsDate() {
HStack {
Spacer()
FooterButtonView(tournament.publishTeams ? "masquer sur le site" : "publier maintenant") {
tournament.publishTeams.toggle()
_save()
}
}
}
}
Section {
LabeledContent {
if tournament.areSummonsPublished() {
Image(systemName:"checkmark").foregroundStyle(.green)
} else {
Text(tournament.publishedTeamsDate().formatted())
}
} label: {
if tournament.areSummonsPublished() {
Text("Publiées")
} else {
Text("Publication prévue")
}
}
} header: {
Text("Convocations")
} footer: {
if Date() < tournament.publishedTeamsDate() {
HStack {
Spacer()
FooterButtonView(tournament.publishSummons ? "masquer sur le site" : "publier maintenant") {
tournament.publishSummons.toggle()
_save()
}
}
}
}
if let publishedGroupStagesDate = tournament.publishedGroupStagesDate() {
Section {
let areGroupStagesPublished = tournament.areGroupStagesPublished()
LabeledContent {
if areGroupStagesPublished {
Image(systemName:"checkmark").foregroundStyle(.green)
} else {
Text(publishedGroupStagesDate.formatted())
}
} label: {
if areGroupStagesPublished {
Text("Publiées")
} else {
Text("Publication prévue")
}
}
} header: {
Text("Poules")
} footer: {
if Date() < publishedGroupStagesDate {
HStack {
Spacer()
FooterButtonView(tournament.publishGroupStages ? "masquer sur le site" : "publier maintenant") {
tournament.publishGroupStages.toggle()
_save()
}
}
}
}
}
if let publishedBracketsDate = tournament.publishedBracketsDate() {
Section {
let areBracketsPublished = tournament.areBracketsPublished()
LabeledContent {
if areBracketsPublished {
Image(systemName:"checkmark").foregroundStyle(.green)
} else {
Text(publishedBracketsDate.formatted())
}
} label: {
if areBracketsPublished {
Text("Publié")
} else {
Text("Publication prévue")
}
}
} header: {
Text("Tableau")
} footer: {
if Date() < publishedBracketsDate {
HStack {
Spacer()
FooterButtonView(tournament.publishBrackets ? "masquer sur le site" : "publier maintenant") {
tournament.publishBrackets.toggle()
_save()
}
}
}
}
}
}
//todo waitinglist & info
}
}
.headerProminence(.increased)
.navigationTitle("Publication")
.navigationBarTitleDisplayMode(.inline)
.toolbarBackground(.visible, for: .navigationBar)
.sheet(item: $urlToShow) { urlToShow in
Image(uiImage: generateQRCode(from: urlToShow))
.interpolation(.none)
.resizable()
.scaledToFit()
.frame(width: 300, height: 300)
.onAppear {
UIPasteboard.general.string = urlToShow
}
}
.onChange(of: [tournament.hideTeamsWeight, tournament.isPrivate]) {
_save()
}
}
private func _disablePrivateToggle() -> Bool {
#if DEBUG
return false
#else
return (tournament.isPrivate && Guard.main.purchasedTransactions.isEmpty)
#endif
}
private func _save() {
do {
if [tournament.publishTeams, tournament.publishSummons, tournament.publishBrackets, tournament.publishGroupStages].anySatisfy({ $0 == true }) {
tournament.publishTournament = true
}
if tournament.publishTournament == false {
tournament.publishTeams = false
tournament.publishSummons = false
tournament.publishBrackets = false
tournament.publishGroupStages = false
}
try dataStore.tournaments.addOrUpdate(instance: tournament)
} catch {
Logger.error(error)
}
}
private func generateQRCode(from string: String) -> UIImage {
filter.message = Data(string.utf8)
if let outputImage = filter.outputImage {
if let cgimg = context.createCGImage(outputImage, from: outputImage.extent) {
return UIImage(cgImage: cgimg)
}
}
return UIImage(systemName: "xmark.circle") ?? UIImage()
}
@ViewBuilder
func actionForURL(title: String, description: String? = nil, url: URL?, removeSource: Bool = false) -> some View {
if let url {
Menu {
Button {
UIApplication.shared.open(url)
} label: {
Label("Voir", systemImage: "safari")
}
Button {
urlToShow = url.absoluteString
} label: {
Label("QRCode", systemImage: "qrcode")
}
ShareLink(item: url) {
Label("Partager le lien", systemImage: "link")
}
} label: {
LabeledContent {
Image(systemName: "square.and.arrow.up")
.foregroundColor(.master)
} label: {
Text(title)
.foregroundColor(.primary)
if let description {
Text(description)
.foregroundColor(.secondary)
}
}
}
.buttonStyle(.plain)
} else {
LabeledContent {
Image(systemName: "xmark").foregroundColor(.logoYellow)
} label: {
Text(title)
if let description {
Text(description)
}
}
}
}
}
//#Preview {
// BroadcastView()
//}