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.
467 lines
19 KiB
467 lines
19 KiB
//
|
|
// BroadcastView.swift
|
|
// PadelClub
|
|
//
|
|
// Created by Razmig Sarkissian on 01/05/2024.
|
|
//
|
|
|
|
import SwiftUI
|
|
import CoreImage.CIFilterBuiltins
|
|
import LeStorage
|
|
import TipKit
|
|
import PadelClubData
|
|
|
|
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 = .info
|
|
|
|
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("Voir le 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 shareURL = tournament.shareURL(.info) {
|
|
Section {
|
|
Link(destination: shareURL) {
|
|
Text(shareURL.absoluteString)
|
|
}
|
|
} header: {
|
|
Text("Page d'information")
|
|
} footer: {
|
|
HStack {
|
|
CopyPasteButtonView(pasteValue: shareURL.absoluteString)
|
|
Spacer()
|
|
ShareLink(item: shareURL) {
|
|
Label("Partager", systemImage: "square.and.arrow.up")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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("Visible sur Padel Club", isOn: Binding(
|
|
get: { !tournament.isPrivate },
|
|
set: { tournament.isPrivate = !$0 }
|
|
))
|
|
|
|
Toggle(isOn: $tournament.hideTeamsWeight) {
|
|
Text("Masquer les poids des équipes")
|
|
}
|
|
}
|
|
|
|
Section {
|
|
let links : [PageLink] = [.info, .teams, .summons, .groupStages, .matches, .rankings, .broadcast, .clubBroadcast]
|
|
Picker(selection: $pageLink) {
|
|
ForEach(links) { pageLink in
|
|
Text(pageLink.localizedLabel()).tag(pageLink)
|
|
}
|
|
} label: {
|
|
Text("Page à partager")
|
|
}
|
|
.pickerStyle(.menu)
|
|
actionForURL(title: "Partager la page '" + pageLink.localizedLabel() + "'", url: tournament.shareURL(pageLink))
|
|
} header: {
|
|
Text("Lien du tournoi à partager")
|
|
}
|
|
|
|
if tournament.isPrivate == false {
|
|
|
|
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")
|
|
}
|
|
}
|
|
} header: {
|
|
Text("Information sur le tournoi")
|
|
} footer: {
|
|
if Date() < tournament.publishedTournamentDate() || tournament.isTournamentPublished() == false {
|
|
HStack {
|
|
Spacer()
|
|
FooterButtonView(tournament.publishTournament ? "masquer sur le site" : "publier maintenant") {
|
|
tournament.publishTournament.toggle()
|
|
_save()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Section {
|
|
LabeledContent {
|
|
if tournament.isProgPublished() {
|
|
Image(systemName:"checkmark").foregroundStyle(.green)
|
|
} else {
|
|
Text(tournament.publishedProgDate().formatted())
|
|
}
|
|
} label: {
|
|
if tournament.isProgPublished() {
|
|
Text("Publiée")
|
|
} else {
|
|
Text("Publication prévue")
|
|
}
|
|
}
|
|
|
|
Toggle(isOn: $tournament.showTeamsInProg) {
|
|
Text("Afficher les équipes sur la prog")
|
|
}
|
|
|
|
} header: {
|
|
Text("Programmation tournoi")
|
|
} footer: {
|
|
if Date() < tournament.publishedProgDate() || tournament.isProgPublished() == false {
|
|
HStack {
|
|
Spacer()
|
|
FooterButtonView(tournament.publishProg ? "masquer sur le site" : "publier maintenant") {
|
|
tournament.publishProg.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")
|
|
}
|
|
}
|
|
} 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
|
|
}
|
|
}
|
|
.toolbar(content: {
|
|
if StoreCenter.main.userId != nil, tournament.club() != nil {
|
|
ToolbarItem(placement: .topBarTrailing) {
|
|
Menu {
|
|
#if DEBUG
|
|
Section {
|
|
actionForURL(title: "La Boutique", url: URLs.main.url.appending(path: "shop"))
|
|
} header: {
|
|
Text("Lien de la boutique")
|
|
}
|
|
#endif
|
|
|
|
Section {
|
|
let club = tournament.club()
|
|
actionForURL(title: (club == nil) ? "Aucun club indiqué pour ce tournoi" : club!.clubTitle(), description: "Page du club", url: club?.shareURL())
|
|
if let event = tournament.eventObject() {
|
|
actionForURL(title: event.eventTitle(), description: "Page de l'évémement", url: event.shareURL())
|
|
}
|
|
actionForURL(title: "Padel Club", url: URLs.main.url)
|
|
} header: {
|
|
Text("Autres liens")
|
|
}
|
|
} label: {
|
|
Label("Partager les liens", systemImage: "square.and.arrow.up")
|
|
}
|
|
}
|
|
}
|
|
})
|
|
.headerProminence(.increased)
|
|
.navigationTitle("Publication")
|
|
.ifAvailableiOS26 {
|
|
if #available(iOS 26.0, *) {
|
|
$0.navigationSubtitle(tournament.tournamentTitle())
|
|
}
|
|
}
|
|
.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, tournament.showTeamsInProg]) {
|
|
_save()
|
|
}
|
|
}
|
|
|
|
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()
|
|
//}
|
|
|