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.
Apple/Sources/AllInApp/AllIn/Views/CreationBetView.swift

491 lines
24 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

//
// 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 dinscription.")
.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)
}
}