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

513 lines
25 KiB

//
// 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] = []
let options: [(Int, String, String)] = [
(0, "questionMarkIcon", String(localized: "bet_type_binary")),
(1, "footballIcon", String(localized: "bet_type_match")),
(2, "paintbrushIcon", String(localized: "bet_type_custom"))
]
@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("bet_creation_theme")
.textStyle(weight: .bold, color: AllInColors.primaryTextColor, size: 17)
Image("questionMarkGreyIcon")
.resizable()
.frame(width: 14, height: 14)
.onTapGesture {
showTitlePopover.toggle()
}
.allInPopover(isPresented: $showTitlePopover, paddingHorizontal: 20) {
String(localized: "bet_creation_theme_tooltip")
}
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("bet_creation_theme_placeholder")
.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("bet_creation_bet_phrase")
.textStyle(weight: .bold, color: AllInColors.primaryTextColor, size: 17)
Image("questionMarkGreyIcon")
.resizable()
.frame(width: 14, height: 14)
.allInPopover(isPresented: $showDescriptionPopover, paddingHorizontal: 10) {
String(localized: "bet_creation_phrase_tooltip")
}
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("bet_creation_bet_phrase_placeholder")
.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("bet_creation_end_registration_date")
.textStyle(weight: .bold, color: AllInColors.primaryTextColor, size: 17)
Image("questionMarkGreyIcon")
.resizable()
.frame(width: 14, height: 14)
.allInPopover(isPresented: $showRegistrationEndDatePopover) {
String(localized: "bet_creation_register_tooltip")
}
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("bet_creation_end_bet_date")
.textStyle(weight: .bold, color: AllInColors.primaryTextColor, size: 17)
Image("questionMarkGreyIcon")
.resizable()
.frame(width: 14, height: 14)
.allInPopover(isPresented: $showBetEndDatePopover) {
String(localized: "bet_creation_bet_end_tooltip")
}
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("bet_creation_bet_privacy")
.textStyle(weight: .bold, color: AllInColors.primaryTextColor, size: 17)
Image("questionMarkGreyIcon")
.resizable()
.frame(width: 14, height: 14)
.allInPopover(isPresented: $showConfidentialityPopover, paddingHorizontal: 15) {
String(localized: "bet_creation_privacy_tooltip")
}
Spacer()
}
.padding(.leading, 10)
HStack(spacing: 5) {
ConfidentialityButton(image: "globe", text: String(localized: "bet_public"), selected: viewModel.isPublic)
.onTapGesture {
viewModel.isPublic = true
}
.padding(.trailing, 5)
ConfidentialityButton(image: "lock", text: String(localized: "bet_private"), 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("bet_creation_private_bottom_text_1")
.textStyle(weight: .bold, color: AllInColors.veryLightPurpleColor, size: 13)
.multilineTextAlignment(.center)
Spacer()
}
HStack() {
Spacer()
Text("bet_creation_private_bottom_text_2")
.textStyle(weight: .bold, color: AllInColors.veryLightPurpleColor, size: 13)
.multilineTextAlignment(.center)
Spacer()
}
} else {
HStack() {
Spacer()
Text("bet_creation_public_bottom_text_1")
.textStyle(weight: .bold, color: AllInColors.veryLightPurpleColor, size: 13)
.multilineTextAlignment(.center)
Spacer()
}
HStack() {
Spacer()
Text("bet_creation_public_bottom_text_2")
.textStyle(weight: .bold, color: AllInColors.veryLightPurpleColor, size: 13)
.multilineTextAlignment(.center)
Spacer()
}
}
HStack() {
Spacer()
Text("bet_creation_bottom_text_3")
.textStyle(weight: .bold, color: AllInColors.veryLightPurpleColor, size: 13)
.padding(.leading, 35)
.multilineTextAlignment(.center)
Spacer()
}
}
Spacer()
HStack() {
Spacer()
Button(action: {
viewModel.create()
}) {
Text("bet_creation_publish")
.font(.system(size: 24))
.fontWeight(.bold)
.overlay {
AllInColors.primaryGradient.frame(width: 200)
.mask(
Text("bet_creation_publish")
.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: $viewModel.selectedOption, options: options)
}
.padding([.bottom], 15)
.frame(width: 340)
Group {
switch viewModel.selectedOption {
case 0:
Text("bet_creation_yes_no_bottom_text_1")
.textStyle(weight: .bold, color: AllInColors.veryLightPurpleColor, size: 13)
.padding([.leading, .trailing], 20)
Text("bet_creation_yes_no_bottom_text_2")
.textStyle(weight: .bold, color: AllInColors.veryLightPurpleColor, size: 13)
case 2:
Text("bet_creation_custom_bottom_text_1")
.textStyle(weight: .bold, color: AllInColors.veryLightPurpleColor, size: 13)
.padding(.leading, 13)
Text("bet_creation_custom_bottom_text_2")
.textStyle(weight: .bold, color: AllInColors.veryLightPurpleColor, size: 13)
.padding(.bottom, 15)
VStack(spacing: 5) {
HStack(spacing: 0) {
TextField("", text: $response, prompt: Text("bet_creation_response_title")
.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("generic_add")
.foregroundColor(.white)
}
.frame(width: 95, height: 40)
.background(AllInColors.lightPurpleColor)
.cornerRadius(10, corners: [.bottomRight, .topRight])
.cornerRadius(2, corners: [.bottomLeft, .topLeft])
}
HStack {
Spacer()
Text(String(localized: "bet_creation_max_answers \(5 - values.count)"))
.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("generic_in_waiting")
}
}
Spacer()
}
.padding([.leading, .trailing], 30)
.padding(.top, 50)
.tag(1)
}
.overlay(
HStack {
Button(action: {
selectedTab = 0
}) {
Text("bet_creation_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("bet_creation_answer")
.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()
}
.alert(isPresented: $viewModel.showErrorMessage) {
Alert(title: Text("bet_creation_error"), message: Text(viewModel.errorMessage ?? ""), dismissButton: .default(Text("generic_ok")))
}
.edgesIgnoringSafeArea(.bottom)
.background(AllInColors.backgroundColor)
}
}
struct CreationBetView_Previews: PreviewProvider {
static var previews: some View {
CreationBetView(showMenu: .constant(false))
.preferredColorScheme(.dark)
}
}