update summon custom message screen and options

multistore
Razmig Sarkissian 2 years ago
parent 52ba7dac15
commit 116a1135d2
  1. 6
      PadelClub/Data/Tournament.swift
  2. 6
      PadelClub/Data/User.swift
  3. 18
      PadelClub/Extensions/Array+Extensions.swift
  4. 27
      PadelClub/Utils/ContactManager.swift
  5. 273
      PadelClub/Views/Calling/CallMessageCustomizationView.swift
  6. 2
      PadelClub/Views/Calling/CallView.swift

@ -943,9 +943,13 @@ class Tournament : ModelObject, Storable {
//return qualifiedTeams().count == qualifiedFromGroupStage() + groupStageAdditionalQualified
}
func paymentMethodMessage() -> String? {
DataStore.shared.user.summonsAvailablePaymentMethods ?? ContactType.defaultAvailablePaymentMethods
}
var entryFeeMessage: String {
if let entryFee {
return "Inscription: " + entryFee.formatted(.currency(code: "EUR")) + "."
return ["Inscription: " + entryFee.formatted(.currency(code: "EUR")) + " par joueur.", paymentMethodMessage()].compactMap { $0 }.joined(separator: "\n")
} else {
return "Inscription: gratuite."
}

@ -35,6 +35,7 @@ class User: ModelObject, UserBase, Storable {
var summonsMessageBody : String? = nil
var summonsMessageSignature: String? = nil
var summonsAvailablePaymentMethods: String? = nil
var summonsDisplayFormat: Bool = false
var summonsDisplayEntryFee: Bool = false
var summonsUseFullCustomMessage: Bool = false
@ -68,6 +69,10 @@ class User: ModelObject, UserBase, Storable {
return try? federalContext.fetch(fetchRequest).first
}
func defaultSignature() -> String {
return "Sportivement,\n\(firstName) \(lastName), votre JAP."
}
func hasClubs() -> Bool {
self.clubs.isEmpty == false
}
@ -106,6 +111,7 @@ class User: ModelObject, UserBase, Storable {
case _country = "country"
case _summonsMessageBody = "summonsMessageBody"
case _summonsMessageSignature = "summonsMessageSignature"
case _summonsAvailablePaymentMethods = "summonsAvailablePaymentMethods"
case _summonsDisplayFormat = "summonsDisplayFormat"
case _summonsDisplayEntryFee = "summonsDisplayEntryFee"
case _summonsUseFullCustomMessage = "summonsUseFullCustomMessage"

@ -31,3 +31,21 @@ extension Array where Element: Equatable {
}
}
}
extension Array where Element: CustomStringConvertible {
func customJoined(separator: String, lastSeparator: String) -> String {
switch count {
case 0:
return ""
case 1:
return "\(self[0])"
case 2:
return "\(self[0]) \(lastSeparator) \(self[1])"
default:
let firstPart = dropLast().map { "\($0)" }.joined(separator: ", ")
let lastPart = "\(lastSeparator) \(last!)"
return "\(firstPart) \(lastPart)"
}
}
}

@ -30,11 +30,11 @@ enum ContactType: Identifiable {
}
extension ContactType {
static let defaultCustomMessage = "Il est conseillé de vous présenter 10 minutes avant de jouer.\nMerci de confirmer en répondant à ce message et de prévenir votre partenaire."
static let defaultSignature = ""
static let defaultCustomMessage = "Il est conseillé de vous présenter 10 minutes avant de jouer.\nMerci de me confirmer votre présence avec votre nom et de prévenir votre partenaire."
static let defaultAvailablePaymentMethods = "Règlement possible par chèque ou espèce."
static func callingGroupStageCustomMessage(tournament: Tournament?, startDate: Date?, roundLabel: String) -> String {
let tournamentCustomMessage = DataStore.shared.user.summonsMessageBody ?? defaultCustomMessage
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
@ -42,45 +42,42 @@ extension ContactType {
if let tournament {
text = text.replacingOccurrences(of: "#titre", with: tournament.tournamentTitle())
text = text.replacingOccurrences(of: "#prix", with: tournament.entryFeeMessage)
}
text = text.replacingOccurrences(of: "#club", with: clubName)
text = text.replacingOccurrences(of: "#round", with: roundLabel)
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 ?? defaultSignature
let signature = DataStore.shared.user.summonsMessageSignature ?? DataStore.shared.user.defaultSignature()
text = text.replacingOccurrences(of: "#signature", with: signature)
return text
}
static func callingGroupStageMessage(tournament: Tournament?, startDate: Date?, roundLabel: String, matchFormat: MatchFormat?) -> String {
static func callingMessage(tournament: Tournament?, startDate: Date?, roundLabel: String, matchFormat: MatchFormat?) -> String {
let useFullCustomMessage = DataStore.shared.user.summonsUseFullCustomMessage
if useFullCustomMessage {
return callingGroupStageCustomMessage(tournament: tournament, startDate: startDate, roundLabel: roundLabel)
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 ?? defaultSignature
let signature = DataStore.shared.user.summonsMessageSignature ?? DataStore.shared.user.defaultSignature()
let localizedCalled = "convoqué" + (tournament?.tournamentCategory == .women ? "e" : "") + "s"
var formatMessage: String? {
(DataStore.shared.user.summonsDisplayFormat) ? matchFormat?.computedLongLabel.appending(".") : nil
}
var entryFeeMessage: String? {
(DataStore.shared.user.summonsDisplayEntryFee) ? tournament?.entryFeeMessage : nil
}
var computedMessage: String {
[formatMessage, entryFeeMessage, message].compacted().map { $0.trimmed }.joined(separator: "\n")
[entryFeeMessage, message].compacted().map { $0.trimmed }.joined(separator: "\n")
}
if let tournament {

@ -11,33 +11,37 @@ import LeStorage
struct CallMessageCustomizationView: View {
@EnvironmentObject var dataStore: DataStore
var tournament: Tournament
@FocusState private var textEditor: Bool
enum Field {
case signature
case body
case paymentMethods
case clubName
}
@FocusState var focusedField: CallMessageCustomizationView.Field?
@State private var customClubName: String = ""
@State private var customCallMessageBody: String = ""
@State private var customCallMessageSignature: String = ""
@State private var summonsAvailablePaymentMethods: String = ""
init(tournament: Tournament) {
self.tournament = tournament
_customCallMessageBody = State(wrappedValue: DataStore.shared.user.summonsMessageBody ?? "")
_customCallMessageSignature = State(wrappedValue: DataStore.shared.user.summonsMessageSignature ?? "")
_customClubName = State(wrappedValue: tournament.clubName ?? "")
_customCallMessageBody = State(wrappedValue: DataStore.shared.user.summonsMessageBody ?? (DataStore.shared.user.summonsUseFullCustomMessage ? "" : ContactType.defaultCustomMessage))
_customCallMessageSignature = State(wrappedValue: DataStore.shared.user.summonsMessageSignature ?? DataStore.shared.user.defaultSignature())
_customClubName = State(wrappedValue: tournament.clubName ?? "Lieu du tournoi")
_summonsAvailablePaymentMethods = State(wrappedValue: DataStore.shared.user.summonsAvailablePaymentMethods ?? ContactType.defaultAvailablePaymentMethods)
}
var clubName: String {
customClubName
}
var formatMessage: String? {
dataStore.user.summonsDisplayFormat ? tournament.matchFormat.computedLongLabel + "." : nil
}
var entryFeeMessage: String? {
dataStore.user.summonsDisplayEntryFee ? tournament.entryFeeMessage : nil
}
var computedMessage: String {
[formatMessage, entryFeeMessage, customCallMessageBody].compacted().map { $0.trimmed }.joined(separator: "\n")
[entryFeeMessage, customCallMessageBody].compacted().map { $0.trimmed }.joined(separator: "\n")
}
var finalMessage: String? {
@ -48,16 +52,12 @@ struct CallMessageCustomizationView: View {
var body: some View {
@Bindable var user = dataStore.user
List {
Section {
ZStack {
Text(customCallMessageBody).hidden()
.padding(.vertical, 20)
TextEditor(text: $customCallMessageBody)
.autocorrectionDisabled()
.focused($textEditor)
}
} header: {
Text("Personnalisation du message de convocation")
_renderingView()
_optionsView()
_editorView()
if user.summonsUseFullCustomMessage {
_paymentMethodsView()
}
Section {
@ -65,100 +65,54 @@ struct CallMessageCustomizationView: View {
Text(customCallMessageSignature).hidden()
TextEditor(text: $customCallMessageSignature)
.autocorrectionDisabled()
.focused($textEditor)
.focused($focusedField, equals: .signature)
}
} header: {
Text("Signature du message")
}
_clubNameView()
Section {
if user.summonsUseFullCustomMessage {
Text(self.computedFullCustomMessage())
.contextMenu {
Button("Coller dans le presse-papier") {
UIPasteboard.general.string = self.computedFullCustomMessage()
}
}
}
else if let finalMessage {
Text(finalMessage)
.contextMenu {
Button("Coller dans le presse-papier") {
UIPasteboard.general.string = finalMessage
}
}
}
} header: {
Text("Exemple")
}
Section {
LabeledContent {
Toggle(isOn: $user.summonsUseFullCustomMessage) {
} footer: {
HStack {
Spacer()
FooterButtonView("éditer") {
focusedField = .signature
}
} label: {
Text("contrôle complet du message")
}
} header: {
Text("Personnalisation complète")
} footer: {
Text("Utilisez ces balises dans votre texte : #titre, #jour, #horaire, #club, #signature")
}
_clubNameView()
}
.headerProminence(.increased)
.navigationBarTitleDisplayMode(.inline)
.toolbarBackground(.visible, for: .navigationBar)
.navigationTitle("Message de convocation")
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Menu {
Picker(selection: $user.summonsDisplayFormat) {
Text("Afficher le format").tag(true)
Text("Masquer le format").tag(false)
} label: {
}
Picker(selection: $user.summonsDisplayEntryFee) {
Text("Afficher le prix d'inscription").tag(true)
Text("Masquer le prix d'inscription").tag(false)
} label: {
}
} label: {
LabelOptions()
}
}
ToolbarItemGroup(placement: .keyboard) {
if textEditor {
if focusedField != .clubName {
Spacer()
Button {
textEditor = false
focusedField = nil
user.summonsMessageBody = customCallMessageBody
user.summonsMessageSignature = customCallMessageSignature
user.summonsAvailablePaymentMethods = summonsAvailablePaymentMethods
_save()
} label: {
Label("Fermer", systemImage: "xmark")
Text("Valider")
}
.buttonStyle(.bordered)
}
}
}
.onChange(of: user.summonsUseFullCustomMessage) {
user.summonsMessageBody = nil
if user.summonsUseFullCustomMessage == false {
user.summonsMessageBody = ContactType.defaultCustomMessage
customCallMessageBody = ContactType.defaultCustomMessage
} else {
customCallMessageBody = ""
}
_save()
}
.onChange(of: customCallMessageBody) {
user.summonsMessageBody = customCallMessageBody
_save()
}
.onChange(of: customCallMessageSignature) {
user.summonsMessageSignature = customCallMessageSignature
_save()
}
.onChange(of: user.summonsDisplayEntryFee) {
_save()
}
.onChange(of: user.summonsDisplayFormat) {
_save()
}
}
}
private func _save() {
@ -169,6 +123,68 @@ struct CallMessageCustomizationView: View {
}
}
@ViewBuilder
private func _editorView() -> some View {
@Bindable var user = dataStore.user
Section {
ZStack {
Text(customCallMessageBody).hidden()
.padding(.vertical, 20)
TextEditor(text: $customCallMessageBody)
.autocorrectionDisabled()
.focused($focusedField, equals: .body)
}
} header: {
if user.summonsUseFullCustomMessage {
Text("Message de convocation")
} else {
Text("Information supplémentaire")
}
} footer: {
if user.summonsUseFullCustomMessage {
LazyVStack(alignment: .leading) {
Text("Utilisez ces balises")
FooterButtonView("#titre") {
customCallMessageBody.append("#titre")
focusedField = .body
}
FooterButtonView("#manche") {
customCallMessageBody.append("#manche")
focusedField = .body
}
FooterButtonView("#prix") {
customCallMessageBody.append("#prix")
focusedField = .body
}
FooterButtonView("#jour") {
customCallMessageBody.append("#jour")
focusedField = .body
}
FooterButtonView("#horaire") {
customCallMessageBody.append("#horaire")
focusedField = .body
}
FooterButtonView("#club") {
customCallMessageBody.append("#club")
focusedField = .body
}
FooterButtonView("#signature") {
customCallMessageBody.append("#signature")
focusedField = .body
}
}
} else {
HStack {
Spacer()
FooterButtonView("éditer") {
focusedField = .body
}
}
}
}
}
@ViewBuilder
private func _clubNameView() -> some View {
if let eventClub = tournament.eventObject()?.clubObject() {
@ -176,6 +192,7 @@ struct CallMessageCustomizationView: View {
Section {
TextField("Nom du club", text: $customClubName)
.autocorrectionDisabled()
.focused($focusedField, equals: .clubName)
.onSubmit {
eventClub.name = customClubName
do {
@ -190,17 +207,79 @@ struct CallMessageCustomizationView: View {
} footer: {
if hasBeenCreated == false {
Text("Édition impossible, vous n'êtes pas le créateur de ce club.").foregroundStyle(.logoRed)
} else {
HStack {
Spacer()
FooterButtonView("éditer") {
focusedField = .clubName
}
}
}
}
}
}
@ViewBuilder
private func _renderingView() -> some View {
@Bindable var user = dataStore.user
Section {
if user.summonsUseFullCustomMessage {
Text(self.computedFullCustomMessage())
}
else if let finalMessage {
Text(finalMessage)
}
} header: {
Text("Rendu généré automatiquement")
}
}
@ViewBuilder
private func _optionsView() -> some View {
@Bindable var user = dataStore.user
Section {
Toggle(isOn: $user.summonsUseFullCustomMessage) {
Text("Tout personnaliser")
Text("Écrivez votre propre message.")
}
if user.summonsUseFullCustomMessage == false {
Toggle(isOn: $user.summonsDisplayEntryFee) {
Text("Afficher le prix d'inscription")
Text("et les informations sur le paiement")
}
}
} header: {
Text("Options")
}
if user.summonsDisplayEntryFee && user.summonsUseFullCustomMessage == false {
_paymentMethodsView()
}
}
@ViewBuilder
private func _paymentMethodsView() -> some View {
Section {
ZStack {
Text(summonsAvailablePaymentMethods).hidden()
.padding(.vertical, 20)
TextEditor(text: $summonsAvailablePaymentMethods)
.autocorrectionDisabled()
.focused($focusedField, equals: .paymentMethods)
}
} header: {
Text("Information sur le paiement")
} footer: {
HStack {
Spacer()
FooterButtonView("éditer") {
focusedField = .paymentMethods
}
}
}
}
func computedFullCustomMessage() -> String {
var text = customCallMessageBody.replacingOccurrences(of: "#titre", with: tournament.tournamentTitle())
text = text.replacingOccurrences(of: "#club", with: clubName)
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()))")
text = text.replacingOccurrences(of: "#signature", with: customCallMessageSignature)
return text
return ContactType.callingCustomMessage(source: customCallMessageBody, tournament: tournament, startDate: Date(), roundLabel: RoundRule.roundName(fromRoundIndex: 2))
}
}

@ -79,7 +79,7 @@ struct CallView: View {
}
var finalMessage: String {
ContactType.callingGroupStageMessage(tournament: tournament, startDate: callDate, roundLabel: roundLabel, matchFormat: matchFormat)
ContactType.callingMessage(tournament: tournament, startDate: callDate, roundLabel: roundLabel, matchFormat: matchFormat)
}
// TODO: Guard

Loading…
Cancel
Save