Various fixes

multistore
Laurent 1 year ago
parent 64b6ad40ec
commit 246541a9ec
  1. 4
      PadelClub/Views/Calling/CallView.swift
  2. 97
      PadelClub/Views/Calling/Components/MenuWarningView.swift
  3. 160
      PadelClub/Views/Match/MatchDetailView.swift
  4. 10
      PadelClub/Views/Subscription/Guard.swift
  5. 27
      PadelClub/Views/Subscription/SubscriptionView.swift
  6. 2
      PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift
  7. 38
      PadelClub/Views/User/LoginView.swift

@ -172,12 +172,14 @@ struct CallView: View {
})
.sheet(isPresented: self.$showUserCreationView, content: {
NavigationStack {
LoginView { _ in
LoginView(reason: LoginReason.loginRequiredForFeature) { _ in
self.showUserCreationView = false
self._payTournamentAndExecute {
self._summon(byMessage: self.summonParamByMessage,
reSummon: self.summonParamByMessage)
}
}
}
})
}

@ -6,8 +6,10 @@
//
import SwiftUI
import LeStorage
struct MenuWarningView: View {
let tournament: Tournament
let teams: [TeamRegistration]
var date: Date?
var message: String?
@ -16,6 +18,11 @@ struct MenuWarningView: View {
@Binding var contactType: ContactType?
@State var showSubscriptionView: Bool = false
@State var showUserCreationView: Bool = false
@State var savedContactType: ContactType? = nil
private func _getUmpireMail() -> [String]? {
if let umpireMail {
return [umpireMail]
@ -23,7 +30,40 @@ struct MenuWarningView: View {
return nil
}
// TODO: Guard
var body: some View {
Menu {
if let team = teams.first, teams.count == 1 {
_teamActionView(team)
} else {
Menu("Tout le monde") {
let players = teams.flatMap({ $0.players() })
_actionView(players: players, privateMode: true)
}
Divider()
ForEach(teams) { team in
_teamView(team)
}
}
} label: {
Text("Prévenir")
.underline()
}
.sheet(isPresented: self.$showSubscriptionView, content: {
NavigationStack {
SubscriptionView(isPresented: self.$showSubscriptionView, showLackOfPlanMessage: true)
}
})
.sheet(isPresented: self.$showUserCreationView, content: {
NavigationStack {
LoginView(reason: LoginReason.loginRequiredForFeature) { _ in
self.showUserCreationView = false
self._tryToContact()
}
}
})
}
@ViewBuilder
private func _actionView(players: [PlayerRegistration], privateMode: Bool = false) -> some View {
if players.count == 1, let player = players.first, let number = player.phoneNumber?.replacingOccurrences(of: " ", with: ""), let url = URL(string: "tel:\(number)") {
@ -47,32 +87,21 @@ struct MenuWarningView: View {
}
Button("Message") {
contactType = .message(date: date, recipients: players.compactMap({ $0.phoneNumber }), body: message, tournamentBuild: nil)
self._contactByMessage(players: players, privateMode: privateMode)
}
Button("Mail") {
contactType = .mail(date: date, recipients: privateMode ? _getUmpireMail() : players.compactMap({ $0.email }), bccRecipients: privateMode ? players.compactMap({ $0.email }) : nil, body: message, subject: subject, tournamentBuild: nil)
self._contactByMail(players: players, privateMode: privateMode)
}
}
var body: some View {
Menu {
if let team = teams.first, teams.count == 1 {
_teamActionView(team)
} else {
Menu("Tout le monde") {
let players = teams.flatMap({ $0.players() })
_actionView(players: players, privateMode: true)
}
Divider()
ForEach(teams) { team in
_teamView(team)
}
}
} label: {
Text("Prévenir")
.underline()
fileprivate func _contactByMessage(players: [PlayerRegistration], privateMode: Bool) {
self.savedContactType = .message(date: date, recipients: players.compactMap({ $0.phoneNumber }), body: message, tournamentBuild: nil)
self._tryToContact()
}
fileprivate func _contactByMail(players: [PlayerRegistration], privateMode: Bool) {
self.savedContactType = .mail(date: date, recipients: privateMode ? _getUmpireMail() : players.compactMap({ $0.email }), bccRecipients: privateMode ? players.compactMap({ $0.email }) : nil, body: message, subject: subject, tournamentBuild: nil)
self._tryToContact()
}
func _playerView(_ player: PlayerRegistration) -> some View {
@ -103,6 +132,32 @@ struct MenuWarningView: View {
_playerView(player)
}
}
fileprivate func _tryToContact() {
self._verifyUser {
self._payTournamentAndExecute {
self.contactType = self.savedContactType
}
}
}
fileprivate func _verifyUser(_ handler: () -> ()) {
if Store.main.userId != nil {
handler()
} else {
self.showUserCreationView = true
}
}
fileprivate func _payTournamentAndExecute(_ handler: () -> ()) {
do {
try tournament.payIfNecessary()
handler()
} catch {
self.showSubscriptionView = true
}
}
}
//#Preview {

@ -27,7 +27,10 @@ struct MatchDetailView: View {
@State private var showDetails: Bool = false
@State private var contactType: ContactType? = nil
@State private var sentError: ContactManagerError? = nil
@State private var showSubscriptionView: Bool = false
// @State private var showSubscriptionView: Bool = false
@State var showSubscriptionView: Bool = false
@State var showUserCreationView: Bool = false
var messageSentFailed: Binding<Bool> {
Binding {
@ -65,52 +68,6 @@ struct MatchDetailView: View {
}
}
var quickLookHeader: some View {
Section {
HStack {
Menu {
Button("Non défini") {
match.removeCourt()
save()
}
if let tournament = match.currentTournament() {
ForEach(0..<tournament.courtCount, id: \.self) { courtIndex in
Button(tournament.courtName(atIndex: courtIndex)) {
match.setCourt(courtIndex)
save()
}
}
}
} label: {
VStack(alignment: .leading) {
Text("terrain").font(.footnote).foregroundStyle(.secondary)
if let courtName = match.courtName() {
Text(courtName)
.foregroundStyle(Color.master)
.underline()
} else {
Text("Choisir")
.foregroundStyle(Color.master)
.underline()
}
}
}
Spacer()
MatchDateView(match: match, showPrefix: true)
}
.font(.title)
.buttonStyle(.plain)
} footer: {
// if match.hasWalkoutTeam() == false {
// if let weatherData = match.weatherData {
// HStack {
// WeatherView(weatherData: weatherData)
// }
// }
// }
}
}
var body: some View {
List {
if match.hasWalkoutTeam() == false {
@ -130,7 +87,7 @@ struct MatchDetailView: View {
showDetails = true
}
Spacer()
MenuWarningView(teams: match.teams(), message: match.matchWarningMessage(), umpireMail: dataStore.user.email, subject: match.matchWarningSubject(), contactType: $contactType)
MenuWarningView(tournament: self.match.currentTournament()!, teams: match.teams(), message: match.matchWarningMessage(), umpireMail: dataStore.user.email, subject: match.matchWarningSubject(), contactType: $contactType)
.buttonStyle(.borderless)
}
}
@ -139,21 +96,12 @@ struct MatchDetailView: View {
if match.isReady() {
Section {
RowButtonView("Saisir les résultats", systemImage: "list.clipboard") {
do {
if let tournament = self.match.currentTournament() {
try tournament.payIfNecessary()
scoreType = .edition
} else {
self.showSubscriptionView = true
}
} catch {
self.showSubscriptionView = true
}
self._editScores()
}
}
}
let players = match.teams().flatMap { $0.players() }
let players = self.match.teams().flatMap { $0.players() }
let unpaid = players.filter({ $0.hasPaid() == false })
if unpaid.isEmpty == false {
@ -186,6 +134,14 @@ struct MatchDetailView: View {
SubscriptionView(isPresented: self.$showSubscriptionView, showLackOfPlanMessage: true)
}
})
.sheet(isPresented: self.$showUserCreationView, content: {
NavigationStack {
LoginView(reason: LoginReason.loginRequiredForFeature) { _ in
self.showUserCreationView = false
self._editScores()
}
}
})
.sheet(item: $scoreType, onDismiss: {
if match.hasEnded() {
dismiss()
@ -239,7 +195,6 @@ struct MatchDetailView: View {
Group {
switch contactType {
case .message(_, let recipients, let body, _):
if Guard.main.paymentForNewTournament() != nil {
MessageComposeView(recipients: recipients, body: body) { result in
switch result {
case .cancelled:
@ -254,13 +209,7 @@ struct MatchDetailView: View {
break
}
}
} else {
NavigationStack {
SubscriptionView(isPresented: self.$showSubscriptionView, showLackOfPlanMessage: true)
}
}
case .mail(_, let recipients, let bccRecipients, let body, let subject, _):
if Guard.main.paymentForNewTournament() != nil {
MailComposeView(recipients: recipients, bccRecipients: bccRecipients, body: body, subject: subject) { result in
switch result {
case .cancelled, .saved:
@ -277,11 +226,6 @@ struct MatchDetailView: View {
break
}
}
} else {
NavigationStack {
SubscriptionView(isPresented: self.$showSubscriptionView, showLackOfPlanMessage: true)
}
}
}
}
.tint(.master)
@ -326,6 +270,52 @@ struct MatchDetailView: View {
}
var quickLookHeader: some View {
Section {
HStack {
Menu {
Button("Non défini") {
match.removeCourt()
save()
}
if let tournament = match.currentTournament() {
ForEach(0..<tournament.courtCount, id: \.self) { courtIndex in
Button(tournament.courtName(atIndex: courtIndex)) {
match.setCourt(courtIndex)
save()
}
}
}
} label: {
VStack(alignment: .leading) {
Text("terrain").font(.footnote).foregroundStyle(.secondary)
if let courtName = match.courtName() {
Text(courtName)
.foregroundStyle(Color.master)
.underline()
} else {
Text("Choisir")
.foregroundStyle(Color.master)
.underline()
}
}
}
Spacer()
MatchDateView(match: match, showPrefix: true)
}
.font(.title)
.buttonStyle(.plain)
} footer: {
// if match.hasWalkoutTeam() == false {
// if let weatherData = match.weatherData {
// HStack {
// WeatherView(weatherData: weatherData)
// }
// }
// }
}
}
enum ScoreType: Int, Identifiable, Hashable {
var id: Int {
self.rawValue
@ -442,6 +432,34 @@ struct MatchDetailView: View {
}
}
fileprivate func _editScores() {
self._verifyUser {
self._payTournamentAndExecute {
self.scoreType = .edition
}
}
}
fileprivate func _verifyUser(_ handler: () -> ()) {
if Store.main.userId != nil {
handler()
} else {
self.showUserCreationView = true
}
}
fileprivate func _payTournamentAndExecute(_ handler: () -> ()) {
guard let tournament = self.match.currentTournament() else { fatalError("missing tournament") }
do {
try tournament.payIfNecessary()
handler()
} catch {
self.showSubscriptionView = true
}
}
private func save() {
do {
try dataStore.matches.addOrUpdate(instance: match)

@ -140,14 +140,14 @@ import LeStorage
}
var currentPlan: StoreItem? {
return .monthlyUnlimited
// return .monthlyUnlimited
// #if DEBUG
// return .monthlyUnlimited
// #else
// if let currentBestPlan = self.currentBestPlan, let plan = StoreItem(rawValue: currentBestPlan.productID) {
// return plan
// }
// return nil
if let currentBestPlan = self.currentBestPlan, let plan = StoreItem(rawValue: currentBestPlan.productID) {
return plan
}
return nil
// #endif
}

@ -126,7 +126,13 @@ struct SubscriptionView: View {
var body: some View {
VStack {
VStack(alignment: .leading) {
Text("Abonnements")
.font(.system(size: 36.0))
.fontWeight(.bold)
.padding(.horizontal)
.foregroundStyle(.white)
if self.showLoginView {
LoginView { _ in
self.showLoginView = false
@ -172,6 +178,8 @@ struct SubscriptionView: View {
}
.listStyle(.grouped)
.scrollContentBackground(.hidden)
}
}
.background(.logoBackground)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
@ -180,10 +188,17 @@ struct SubscriptionView: View {
}.isLoading(self.isRestoring)
}
}
}
}
.preferredColorScheme(.dark)
.navigationTitle("Abonnements")
// .toolbar {
// ToolbarItem(placement: .principal) {
// VStack(spacing: -4.0) {
// Text("Abonnements").font(.headline).foregroundStyle(.white)
// }
// }
//
// }.navigationBarTitleDisplayMode(.inline)
// .preferredColorScheme(.dark)
// .navigationTitle("Abonnements")
}
@ -248,7 +263,7 @@ fileprivate struct ProductsSectionView: View {
}
}
} header: {
Text("Sélectionnez une offre")
Text("Sélectionnez une offre").foregroundStyle(Color(white: 0.8))
}
}

@ -1118,7 +1118,7 @@ struct InscriptionManagerView: View {
private func _teamMenuOptionView(_ team: TeamRegistration) -> some View {
Menu {
Section {
MenuWarningView(teams: [team], contactType: $contactType)
MenuWarningView(tournament: team.tournamentObject()!, teams: [team], contactType: $contactType)
//Divider()
Button("Copier") {
let pasteboard = UIPasteboard.general

@ -8,6 +8,32 @@
import SwiftUI
import LeStorage
enum LoginReason {
case loginRequiredForFeature
var title: String {
switch self {
case .loginRequiredForFeature:
return "Compte requis"
}
}
var systemImage: String {
switch self {
case .loginRequiredForFeature:
return "person.crop.circle.fill"
}
}
var description: String {
switch self {
case .loginRequiredForFeature:
return "Pour accéder à cette fonctionnalité, veuillez-vous connecter ou créer un compte"
}
}
}
typealias Credentials = (username: String, password: String)
struct LoginView: View {
@ -27,6 +53,8 @@ struct LoginView: View {
@State var errorText: String? = nil
@FocusState var focusedField: UserLogin?
var reason: LoginReason? = nil
var showEmailValidationMessage: Bool {
credentials != nil
}
@ -64,6 +92,16 @@ struct LoginView: View {
}
}
if let reason {
Section {
ContentUnavailableView {
Label(reason.title, systemImage: reason.systemImage)
} description: {
Text(reason.description)
}
}
}
Section {
TextField("Nom d'utilisateur", text: self.$username)
.autocorrectionDisabled(true)

Loading…
Cancel
Save