|
|
//
|
|
|
// CreateBetView.swift
|
|
|
// AllIn
|
|
|
//
|
|
|
// Created by Emre on 29/09/2023.
|
|
|
//
|
|
|
|
|
|
import SwiftUI
|
|
|
|
|
|
struct CreationBetView: View {
|
|
|
|
|
|
@StateObject private var viewModel = CreationBetViewModel()
|
|
|
@Binding var showMenu: Bool
|
|
|
@State private var selectedTab = 0
|
|
|
|
|
|
// Popovers
|
|
|
@State private var showTitlePopover: Bool = false
|
|
|
@State private var showDescriptionPopover: Bool = false
|
|
|
@State private var showRegistrationEndDatePopover: Bool = false
|
|
|
@State private var showBetEndDatePopover: Bool = false
|
|
|
@State private var showConfidentialityPopover: Bool = false
|
|
|
|
|
|
let dateRange: ClosedRange<Date> = {
|
|
|
let calendar = Calendar.current
|
|
|
let startDate = Date()
|
|
|
let endDate = calendar.date(byAdding: .year, value: 10, to: startDate)!
|
|
|
return startDate ... endDate
|
|
|
}()
|
|
|
let screenWidth = UIScreen.main.bounds.width
|
|
|
|
|
|
@State private var response = ""
|
|
|
@State private var values: [String] = []
|
|
|
|
|
|
@State private var selectedOption = 0
|
|
|
let options: [(Int, String, String)] = [
|
|
|
(0, "questionMarkIcon", "Oui / Non"),
|
|
|
(1, "footballIcon", "Pari sportif"),
|
|
|
(2, "paintbrushIcon", "Réponses personnalisées")
|
|
|
]
|
|
|
|
|
|
@State var groupedItems: [[String]] = [[String]] ()
|
|
|
|
|
|
private func updateGroupedItems() {
|
|
|
|
|
|
var updatedGroupedItems: [[String]] = [[String]] ()
|
|
|
var tempItems: [String] = [String] ()
|
|
|
var width: CGFloat = 0
|
|
|
var dynamicWidthLimit: CGFloat
|
|
|
|
|
|
for value in values {
|
|
|
let label = UILabel()
|
|
|
label.text = value
|
|
|
label.sizeToFit()
|
|
|
dynamicWidthLimit = CGFloat(tempItems.count) * 105.0
|
|
|
|
|
|
let labelWidth = label.frame.size.width
|
|
|
if (width + labelWidth + dynamicWidthLimit) < screenWidth {
|
|
|
width += labelWidth
|
|
|
tempItems.append(value)
|
|
|
} else {
|
|
|
width = labelWidth
|
|
|
updatedGroupedItems.append(tempItems)
|
|
|
tempItems.removeAll()
|
|
|
tempItems.append(value)
|
|
|
}
|
|
|
}
|
|
|
updatedGroupedItems.append(tempItems)
|
|
|
|
|
|
groupedItems = updatedGroupedItems
|
|
|
}
|
|
|
|
|
|
var body: some View {
|
|
|
VStack(alignment: .center, spacing: 0) {
|
|
|
NavigationLink(destination: MainView(page: "Bet").navigationBarBackButtonHidden(true), isActive: $viewModel.betAdded) {
|
|
|
EmptyView()
|
|
|
}
|
|
|
TopBar(showMenu: self.$showMenu)
|
|
|
TabView(selection: $selectedTab) {
|
|
|
|
|
|
// First Page
|
|
|
ScrollView(showsIndicators: false) {
|
|
|
VStack(spacing: 5) {
|
|
|
VStack() {
|
|
|
HStack(spacing: 5) {
|
|
|
Text("Thème")
|
|
|
.textStyle(weight: .bold, color: AllInColors.primaryTextColor, size: 17)
|
|
|
|
|
|
Image("questionMarkGreyIcon")
|
|
|
.resizable()
|
|
|
.frame(width: 14, height: 14)
|
|
|
.onTapGesture {
|
|
|
showTitlePopover.toggle()
|
|
|
}
|
|
|
.allInPopover(isPresented: $showTitlePopover, paddingHorizontal: 20) {
|
|
|
"Généralement un nom commun décrivant le thème global du pari pour servir de référence."
|
|
|
}
|
|
|
|
|
|
Spacer()
|
|
|
}
|
|
|
.frame(width: 340)
|
|
|
.padding(.leading, 10)
|
|
|
|
|
|
VStack {
|
|
|
if let themeError = $viewModel.themeFieldError.wrappedValue {
|
|
|
Text(themeError)
|
|
|
.textStyle(weight: .bold, color: .red, size: 10)
|
|
|
}
|
|
|
TextField("", text: $viewModel.theme, prompt: Text("Études, sport, soirée...")
|
|
|
.foregroundColor(AllInColors.lightGrey300Color)
|
|
|
.font(.system(size: 14))
|
|
|
.fontWeight(.light))
|
|
|
.padding()
|
|
|
.background(
|
|
|
RoundedRectangle(cornerRadius: 9)
|
|
|
.fill(AllInColors.componentBackgroundColor)
|
|
|
.frame(height: 40)
|
|
|
)
|
|
|
.frame(width: 350, height: 40)
|
|
|
.foregroundColor(AllInColors.primaryTextColor)
|
|
|
.overlay(
|
|
|
RoundedRectangle(cornerRadius: 10, style: .continuous)
|
|
|
.stroke(AllInColors.delimiterGrey, lineWidth: 1)
|
|
|
)
|
|
|
.padding(.bottom, 5)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
HStack(spacing: 5) {
|
|
|
Text("Phrase du BET")
|
|
|
.textStyle(weight: .bold, color: AllInColors.primaryTextColor, size: 17)
|
|
|
|
|
|
Image("questionMarkGreyIcon")
|
|
|
.resizable()
|
|
|
.frame(width: 14, height: 14)
|
|
|
.allInPopover(isPresented: $showDescriptionPopover, paddingHorizontal: 10) {
|
|
|
"Court descriptif du pari, souvent une question ouverte ou fermée."
|
|
|
}
|
|
|
|
|
|
Spacer()
|
|
|
}
|
|
|
.frame(width: 340)
|
|
|
.padding(.leading, 10)
|
|
|
|
|
|
VStack {
|
|
|
if let descriptionError = $viewModel.descriptionFieldError.wrappedValue {
|
|
|
Text(descriptionError)
|
|
|
.textStyle(weight: .bold, color: .red, size: 10)
|
|
|
}
|
|
|
TextField("", text: $viewModel.description, prompt: Text("David sera absent Lundi matin en cours ?")
|
|
|
.foregroundColor(AllInColors.lightGrey300Color)
|
|
|
.font(.system(size: 14))
|
|
|
.fontWeight(.light), axis: .vertical)
|
|
|
.lineLimit(4, reservesSpace: true)
|
|
|
.padding()
|
|
|
.background(
|
|
|
RoundedRectangle(cornerRadius: 9)
|
|
|
.fill(AllInColors.componentBackgroundColor)
|
|
|
.frame(height: 110)
|
|
|
)
|
|
|
.frame(width: 350, height: 110)
|
|
|
.foregroundColor(AllInColors.primaryTextColor)
|
|
|
.overlay(
|
|
|
RoundedRectangle(cornerRadius: 10, style: .continuous)
|
|
|
.stroke(AllInColors.delimiterGrey, lineWidth: 1)
|
|
|
)
|
|
|
.padding(.bottom, 30)
|
|
|
}
|
|
|
|
|
|
HStack(spacing: 5) {
|
|
|
Text("Date de fin des inscriptions")
|
|
|
.textStyle(weight: .bold, color: AllInColors.primaryTextColor, size: 17)
|
|
|
Image("questionMarkGreyIcon")
|
|
|
.resizable()
|
|
|
.frame(width: 14, height: 14)
|
|
|
.allInPopover(isPresented: $showRegistrationEndDatePopover) {
|
|
|
"Date de fin avant laquelle les joueurs peuvent s'inscrire en pariant leurs Allcoins."
|
|
|
}
|
|
|
|
|
|
Spacer()
|
|
|
}
|
|
|
.frame(width: 340)
|
|
|
.padding(.leading, 10)
|
|
|
|
|
|
VStack {
|
|
|
if let endRegisterError = $viewModel.endRegisterDateFieldError.wrappedValue {
|
|
|
Text(endRegisterError)
|
|
|
.textStyle(weight: .bold, color: .red, size: 10)
|
|
|
}
|
|
|
HStack(spacing: 5) {
|
|
|
DatePicker(
|
|
|
"",
|
|
|
selection: $viewModel.endRegisterDate,
|
|
|
in: dateRange,
|
|
|
displayedComponents: [.date, .hourAndMinute]
|
|
|
)
|
|
|
.accentColor(AllInColors.lightPurpleColor)
|
|
|
.labelsHidden()
|
|
|
.padding(.bottom, 10)
|
|
|
Spacer()
|
|
|
}
|
|
|
.frame(width: 340)
|
|
|
}
|
|
|
|
|
|
VStack(alignment: .leading, spacing: 5) {
|
|
|
VStack() {
|
|
|
HStack(spacing: 5) {
|
|
|
Text("Date de fin du BET")
|
|
|
.textStyle(weight: .bold, color: AllInColors.primaryTextColor, size: 17)
|
|
|
|
|
|
Image("questionMarkGreyIcon")
|
|
|
.resizable()
|
|
|
.frame(width: 14, height: 14)
|
|
|
.allInPopover(isPresented: $showBetEndDatePopover) {
|
|
|
"Date des résultats où seront redistribués les Allcoins aux vainqueurs."
|
|
|
}
|
|
|
|
|
|
Spacer()
|
|
|
}
|
|
|
.padding(.leading, 10)
|
|
|
}
|
|
|
|
|
|
VStack {
|
|
|
if let endBetError = $viewModel.endBetDateFieldError.wrappedValue {
|
|
|
Text(endBetError)
|
|
|
.textStyle(weight: .bold, color: .red, size: 10)
|
|
|
}
|
|
|
HStack(spacing: 5) {
|
|
|
DatePicker(
|
|
|
"",
|
|
|
selection: $viewModel.endBetDate,
|
|
|
in: dateRange,
|
|
|
displayedComponents: [.date, .hourAndMinute]
|
|
|
)
|
|
|
.accentColor(AllInColors.lightPurpleColor)
|
|
|
.labelsHidden()
|
|
|
.padding(.bottom, 40)
|
|
|
Spacer()
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
.frame(width: 340)
|
|
|
|
|
|
VStack {
|
|
|
HStack(spacing: 5) {
|
|
|
Text("Confidentialité du BET")
|
|
|
.textStyle(weight: .bold, color: AllInColors.primaryTextColor, size: 17)
|
|
|
Image("questionMarkGreyIcon")
|
|
|
.resizable()
|
|
|
.frame(width: 14, height: 14)
|
|
|
.allInPopover(isPresented: $showConfidentialityPopover, paddingHorizontal: 15) {
|
|
|
"Option permettant d'ouvrir ou non le pari à des inconnus."
|
|
|
}
|
|
|
|
|
|
Spacer()
|
|
|
}
|
|
|
.padding(.leading, 10)
|
|
|
|
|
|
HStack(spacing: 5) {
|
|
|
ConfidentialityButton(image: "globe", text: "Public", selected: viewModel.isPublic)
|
|
|
.onTapGesture {
|
|
|
viewModel.isPublic = true
|
|
|
}
|
|
|
.padding(.trailing, 5)
|
|
|
|
|
|
ConfidentialityButton(image: "lock", text: "Privé", selected: !viewModel.isPublic)
|
|
|
.onTapGesture {
|
|
|
viewModel.isPublic = false
|
|
|
}
|
|
|
Spacer()
|
|
|
}
|
|
|
}
|
|
|
.frame(width: 340)
|
|
|
.padding(.bottom, 10)
|
|
|
|
|
|
|
|
|
VStack(spacing: 10) {
|
|
|
|
|
|
if !self.viewModel.isPublic {
|
|
|
DropDownFriends()
|
|
|
.padding(.bottom, 30)
|
|
|
}
|
|
|
|
|
|
HStack() {
|
|
|
Spacer()
|
|
|
Text("Votre BET sera visible par tous les utilisateurs.")
|
|
|
.textStyle(weight: .bold, color: AllInColors.veryLightPurpleColor, size: 13)
|
|
|
.multilineTextAlignment(.center)
|
|
|
Spacer()
|
|
|
}
|
|
|
HStack() {
|
|
|
Spacer()
|
|
|
Text("Tout le monde pourra rejoindre le BET.")
|
|
|
.textStyle(weight: .bold, color: AllInColors.veryLightPurpleColor, size: 13)
|
|
|
.multilineTextAlignment(.center)
|
|
|
Spacer()
|
|
|
}
|
|
|
HStack() {
|
|
|
Spacer()
|
|
|
Text("Vous pourrez inviter des amis à tout moment pendant la période d’inscription.")
|
|
|
.textStyle(weight: .bold, color: AllInColors.veryLightPurpleColor, size: 13)
|
|
|
.padding(.leading, 35)
|
|
|
.multilineTextAlignment(.center)
|
|
|
Spacer()
|
|
|
}
|
|
|
}
|
|
|
Spacer()
|
|
|
HStack() {
|
|
|
Spacer()
|
|
|
Button(action: {
|
|
|
viewModel.create()
|
|
|
}) {
|
|
|
Text("Publier le bet")
|
|
|
.font(.system(size: 24))
|
|
|
.fontWeight(.bold)
|
|
|
.overlay {
|
|
|
AllInColors.primaryGradient.frame(width: 150)
|
|
|
.mask(
|
|
|
Text("Publier le bet")
|
|
|
.font(.system(size: 24))
|
|
|
.fontWeight(.bold)
|
|
|
.frame(maxWidth: .infinity)
|
|
|
)
|
|
|
}
|
|
|
}
|
|
|
.frame(width: 335, height: 60)
|
|
|
.background(AllInColors.componentBackgroundColor)
|
|
|
.cornerRadius(12)
|
|
|
.overlay(
|
|
|
RoundedRectangle(cornerRadius: 10, style: .continuous)
|
|
|
.stroke(AllInColors.delimiterGrey, lineWidth: 1)
|
|
|
)
|
|
|
Spacer()
|
|
|
}
|
|
|
}
|
|
|
.padding([.leading, .trailing, .bottom], 30)
|
|
|
.padding(.top, 50)
|
|
|
}
|
|
|
.tag(0)
|
|
|
|
|
|
// Second Page
|
|
|
VStack(spacing: 5) {
|
|
|
|
|
|
VStack() {
|
|
|
DropDownMenu(selectedOption: $selectedOption, options: options)
|
|
|
}
|
|
|
.padding([.bottom], 15)
|
|
|
.frame(width: 340)
|
|
|
|
|
|
|
|
|
Group {
|
|
|
switch selectedOption {
|
|
|
case 0:
|
|
|
Text("Les utilisateurs devront répondre au pari avec OUI ou NON.")
|
|
|
.textStyle(weight: .bold, color: AllInColors.veryLightPurpleColor, size: 13)
|
|
|
.padding([.leading, .trailing], 20)
|
|
|
|
|
|
Text("Aucune autre réponse ne sera acceptée.")
|
|
|
.textStyle(weight: .bold, color: AllInColors.veryLightPurpleColor, size: 13)
|
|
|
|
|
|
case 2:
|
|
|
Text("Vous allez renseigner les différentes réponses disponibles dans ce pari.")
|
|
|
.textStyle(weight: .bold, color: AllInColors.veryLightPurpleColor, size: 13)
|
|
|
.padding(.leading, 13)
|
|
|
|
|
|
Text("Faites attention a etre claire et éviter toutes incertitudes")
|
|
|
.textStyle(weight: .bold, color: AllInColors.veryLightPurpleColor, size: 13)
|
|
|
.padding(.bottom, 15)
|
|
|
|
|
|
VStack(spacing: 5) {
|
|
|
HStack(spacing: 0) {
|
|
|
TextField("", text: $response, prompt: Text("Intitulé de réponse")
|
|
|
.foregroundColor(AllInColors.lightGrey200Color)
|
|
|
.font(.system(size: 16))
|
|
|
.fontWeight(.medium))
|
|
|
.padding()
|
|
|
.background(
|
|
|
Rectangle()
|
|
|
.fill(Color.white)
|
|
|
.cornerRadius(9, corners: [.topLeft, .bottomLeft])
|
|
|
.frame(height: 38)
|
|
|
)
|
|
|
.frame(width: 250, height: 38)
|
|
|
.foregroundColor(.black)
|
|
|
.onChange(of: response) { newValue in
|
|
|
if newValue.count > 20 {
|
|
|
response = String(newValue.prefix(20))
|
|
|
}
|
|
|
}
|
|
|
|
|
|
Button(action: {
|
|
|
if !response.isEmpty && values.count < 5 {
|
|
|
values.append(response)
|
|
|
updateGroupedItems()
|
|
|
response = ""
|
|
|
}
|
|
|
}) {
|
|
|
Text("Ajouter")
|
|
|
.foregroundColor(.white)
|
|
|
}
|
|
|
.frame(width: 95, height: 40)
|
|
|
.background(AllInColors.lightPurpleColor)
|
|
|
.cornerRadius(10, corners: [.bottomRight, .topRight])
|
|
|
.cornerRadius(2, corners: [.bottomLeft, .topLeft])
|
|
|
}
|
|
|
HStack {
|
|
|
Spacer()
|
|
|
Text("encore \(5 - values.count) max.")
|
|
|
.textStyle(weight: .regular, color: AllInColors.primaryTextColor, size: 12)
|
|
|
|
|
|
}
|
|
|
VStack(spacing: 10) {
|
|
|
ForEach(groupedItems, id: \.self) { items in
|
|
|
HStack {
|
|
|
ForEach(items, id: \.self) { text in
|
|
|
HStack {
|
|
|
Text(text)
|
|
|
.foregroundColor(.white)
|
|
|
.lineLimit(1)
|
|
|
Button(action: {
|
|
|
if let index = values.firstIndex(of: text) {
|
|
|
values.remove(at: index)
|
|
|
updateGroupedItems()
|
|
|
}
|
|
|
}) {
|
|
|
Image("crossIcon")
|
|
|
.resizable()
|
|
|
.frame(width: 15, height: 15)
|
|
|
.foregroundColor(.white)
|
|
|
}
|
|
|
}
|
|
|
.padding(5)
|
|
|
.padding([.leading, .trailing], 5)
|
|
|
.background(AllInColors.lightPurpleColor)
|
|
|
.cornerRadius(16)
|
|
|
}
|
|
|
Spacer()
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
default:
|
|
|
Text("En attente")
|
|
|
}
|
|
|
}
|
|
|
Spacer()
|
|
|
}
|
|
|
.padding([.leading, .trailing], 30)
|
|
|
.padding(.top, 50)
|
|
|
.tag(1)
|
|
|
}
|
|
|
.overlay(
|
|
|
HStack {
|
|
|
Button(action: {
|
|
|
selectedTab = 0
|
|
|
}) {
|
|
|
Text("Question")
|
|
|
.font(.system(size: 16))
|
|
|
.padding()
|
|
|
.fontWeight(selectedTab == 0 ? .bold : .semibold)
|
|
|
.foregroundColor(selectedTab == 0 ? AllInColors.primaryTextColor : .gray)
|
|
|
.offset(y: 0)
|
|
|
}
|
|
|
Button(action: {
|
|
|
selectedTab = 1
|
|
|
}) {
|
|
|
Text("Réponses")
|
|
|
.font(.system(size: 16))
|
|
|
.padding()
|
|
|
.fontWeight(selectedTab == 1 ? .bold : .semibold)
|
|
|
.foregroundColor(selectedTab == 1 ? AllInColors.primaryTextColor : .gray)
|
|
|
.offset(y: 0)
|
|
|
}
|
|
|
}
|
|
|
, alignment: .top)
|
|
|
.tabViewStyle(PageTabViewStyle())
|
|
|
}
|
|
|
.onTapGesture {
|
|
|
hideKeyboard()
|
|
|
}
|
|
|
.edgesIgnoringSafeArea(.bottom)
|
|
|
.background(AllInColors.backgroundColor)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
struct CreationBetView_Previews: PreviewProvider {
|
|
|
static var previews: some View {
|
|
|
CreationBetView(showMenu: .constant(false))
|
|
|
.preferredColorScheme(.dark)
|
|
|
}
|
|
|
}
|