diff --git a/Sources/AllInApp/AllIn/Assets.xcassets/PinkFlame.imageset/Contents.json b/Sources/AllInApp/AllIn/Assets.xcassets/PinkFlame.imageset/Contents.json new file mode 100644 index 0000000..f5dfe82 --- /dev/null +++ b/Sources/AllInApp/AllIn/Assets.xcassets/PinkFlame.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "pinkFlame.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/AllInApp/AllIn/Assets.xcassets/PinkFlame.imageset/pinkFlame.png b/Sources/AllInApp/AllIn/Assets.xcassets/PinkFlame.imageset/pinkFlame.png new file mode 100644 index 0000000..c6accb4 Binary files /dev/null and b/Sources/AllInApp/AllIn/Assets.xcassets/PinkFlame.imageset/pinkFlame.png differ diff --git a/Sources/AllInApp/AllIn/Assets.xcassets/blueFlame.imageset/Contents.json b/Sources/AllInApp/AllIn/Assets.xcassets/blueFlame.imageset/Contents.json new file mode 100644 index 0000000..4b172b9 --- /dev/null +++ b/Sources/AllInApp/AllIn/Assets.xcassets/blueFlame.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "blueFlame.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/AllInApp/AllIn/Assets.xcassets/blueFlame.imageset/blueFlame.png b/Sources/AllInApp/AllIn/Assets.xcassets/blueFlame.imageset/blueFlame.png new file mode 100644 index 0000000..ff9d70c Binary files /dev/null and b/Sources/AllInApp/AllIn/Assets.xcassets/blueFlame.imageset/blueFlame.png differ diff --git a/Sources/AllInApp/AllIn/Components/BetLineLoading.swift b/Sources/AllInApp/AllIn/Components/BetLineLoading.swift index bd3ff71..ec71365 100644 --- a/Sources/AllInApp/AllIn/Components/BetLineLoading.swift +++ b/Sources/AllInApp/AllIn/Components/BetLineLoading.swift @@ -11,12 +11,12 @@ import Model struct BetLineLoading: View { @State var showInfos: Bool = false - var participations: [Participation] + var bet: BetDetail var value: CGFloat { - let totalParticipations = participations.count - let numberOfYes = participations.filter { $0.answer.uppercased() == "YES" }.count - let numberOfNo = participations.filter { $0.answer.uppercased() == "NO" }.count + let totalParticipations = bet.participations.count + let numberOfYes = bet.participations.filter { $0.answer.uppercased() == "YES" }.count + let numberOfNo = bet.participations.filter { $0.answer.uppercased() == "NO" }.count if(numberOfNo == 0 && numberOfYes == 0){ return 0.5 } @@ -25,144 +25,22 @@ struct BetLineLoading: View { } var yesParticipations: [Participation] { - participations.filter { $0.answer.uppercased() == "YES" } + bet.participations.filter { $0.answer.uppercased() == "YES" } } var noParticipations: [Participation] { - participations.filter { $0.answer.uppercased() == "NO" } + bet.participations.filter { $0.answer.uppercased() == "NO" } } var body: some View { - VStack(alignment: .leading, spacing: 0) { - HStack(spacing: 5) { - Text("OUI") - .font(.system(size: 25)) - .fontWeight(.bold) - .foregroundColor(AllInColors.blue200) - Spacer() - Text("NON") - .font(.system(size: 25)) - .fontWeight(.bold) - .foregroundColor(AllInColors.pink100) + switch bet.bet { + case is BinaryBet: + BinaryBetLine(bet: bet) + case is CustomBet: + CustomBetLine(bet: bet) + default: + BinaryBetLine(bet: bet) } - - GeometryReader { geometry in - ZStack(alignment: .leading) { - HStack{ - Spacer() - Rectangle() - .frame(width: min(CGFloat(1-self.value) * geometry.size.width, geometry.size.width), height: 17) - .foregroundStyle(AllInColors.PinkBetGradiant).cornerRadius(999) - } - - HStack(spacing: 0) { - Rectangle() - .frame(width: min(CGFloat(self.value) * geometry.size.width, geometry.size.width), height: 17) - .foregroundStyle(AllInColors.BlueBetGradiant) - .cornerRadius(999) - Image("loadingHeartIcon") - .resizable() - .frame(width: 29, height: 32) - .padding(.leading, -10) - } - - } - .padding(.bottom, 5) - } - .frame(height: 40) - - HStack { - Spacer() - Text("bet_status_details_drawer") - .textStyle(weight: .medium, color: AllInColors.primaryTextColor, size: 10) - Image(showInfos ? "chevronUpIcon" : "chevronDownIcon") - .resizable() - .frame(width: 10, height: 7) - .scaledToFill() - } - .onTapGesture { - withAnimation { - showInfos.toggle() - } - } - .padding(.bottom, 5) - .padding(.trailing, 5) - - if showInfos { - VStack(spacing: 1) { - HStack(spacing: 5) { - Image("blueAllCoinIcon") - .resizable() - .frame(width:12, height: 12) - Text(yesParticipations.reduce(0, {x,y in x + y.stake}).description) - .font(.system(size: 15)) - .fontWeight(.bold) - .foregroundColor(AllInColors.blue200) - Spacer() - Text(noParticipations.reduce(0, {x,y in x + y.stake}).description) - .font(.system(size: 15)) - .fontWeight(.bold) - .foregroundColor(AllInColors.pink100) - Image("pinkAllCoinIcon") - .resizable() - .frame(width: 12, height: 12) - } - HStack(spacing: 5){ - Image("bluePersonIcon") - .resizable() - .frame(width: 12, height: 12) - Text(yesParticipations.count.description) - .font(.system(size: 15)) - .fontWeight(.bold) - .foregroundColor(AllInColors.blue200) - Spacer() - Text(noParticipations.count.description) - .font(.system(size: 15)) - .fontWeight(.bold) - .foregroundColor(AllInColors.pink100) - Image("pinkPersonIcon") - .resizable() - .frame(width: 12, height: 12) - - } - HStack(spacing: 5){ - Image("blueBadgeIcon") - .resizable() - .frame(width: 12, height: 12) - Text(yesParticipations.max(by: { $0.stake < $1.stake })?.stake.description ?? "0") - .font(.system(size: 15)) - .fontWeight(.bold) - .foregroundColor(AllInColors.blue200) - Spacer() - Text(noParticipations.max(by: { $0.stake < $1.stake })?.stake.description ?? "0") - .font(.system(size: 15)) - .fontWeight(.bold) - .foregroundColor(AllInColors.pink100) - Image("pinkBadgeIcon") - .resizable() - .frame(width: 12, height: 12) - - } - HStack(spacing: 5){ - Image("blueTrophyIcon") - .resizable() - .frame(width: 12, height: 12) - Text("1.2") - .font(.system(size: 15)) - .fontWeight(.bold) - .foregroundColor(AllInColors.blue200) - Spacer() - Text("1.2") - .font(.system(size: 15)) - .fontWeight(.bold) - .foregroundColor(AllInColors.pink100) - Image("pinkTrophyIcon") - .resizable() - .frame(width:12, height: 12) - } - } - } - } } } diff --git a/Sources/AllInApp/AllIn/Components/BinaryBetLine.swift b/Sources/AllInApp/AllIn/Components/BinaryBetLine.swift new file mode 100644 index 0000000..68fb4ab --- /dev/null +++ b/Sources/AllInApp/AllIn/Components/BinaryBetLine.swift @@ -0,0 +1,168 @@ +// +// BinaryBetLine.swift +// AllIn +// +// Created by Lucas Delanier on 10/06/2024. +// + + +import SwiftUI +import Model + +struct BinaryBetLine: View { + + @State var showInfos: Bool = false + var bet: BetDetail + + var value: CGFloat { + let totalParticipations = bet.participations.count + let numberOfYes = bet.participations.filter { $0.answer.uppercased() == "YES" }.count + let numberOfNo = bet.participations.filter { $0.answer.uppercased() == "NO" }.count + if(numberOfNo == 0 && numberOfYes == 0){ + return 0.5 + } + + return totalParticipations > 0 ? CGFloat(numberOfYes) / CGFloat(totalParticipations) : 0.0 + } + + var yesParticipations: [Participation] { + bet.participations.filter { $0.answer.uppercased() == "YES" } + } + + var noParticipations: [Participation] { + bet.participations.filter { $0.answer.uppercased() == "NO" } + } + + var body: some View { + VStack(alignment: .leading, spacing: 0) { + HStack(spacing: 5) { + Text("OUI") + .font(.system(size: 25)) + .fontWeight(.bold) + .foregroundColor(AllInColors.blue200) + Spacer() + Text("NON") + .font(.system(size: 25)) + .fontWeight(.bold) + .foregroundColor(AllInColors.pink100) + } + + GeometryReader { geometry in + ZStack(alignment: .leading) { + HStack{ + Spacer() + Rectangle() + .frame(width: min(CGFloat(1-self.value) * geometry.size.width, geometry.size.width), height: 17) + .foregroundStyle(AllInColors.PinkBetGradiant).cornerRadius(999) + } + + HStack(spacing: 0) { + Rectangle() + .frame(width: min(CGFloat(self.value) * geometry.size.width, geometry.size.width), height: 17) + .foregroundStyle(AllInColors.BlueBetGradiant) + .cornerRadius(999) + Image("loadingHeartIcon") + .resizable() + .frame(width: 29, height: 32) + .padding(.leading, -10) + } + + } + .padding(.bottom, 5) + } + .frame(height: 40) + + HStack { + Spacer() + Text("bet_status_details_drawer") + .textStyle(weight: .medium, color: AllInColors.primaryTextColor, size: 10) + Image(showInfos ? "chevronUpIcon" : "chevronDownIcon") + .resizable() + .frame(width: 10, height: 7) + .scaledToFill() + } + .onTapGesture { + withAnimation { + showInfos.toggle() + } + } + .padding(.bottom, 5) + .padding(.trailing, 5) + + if showInfos { + VStack(spacing: 1) { + HStack(spacing: 5) { + Image("blueAllCoinIcon") + .resizable() + .frame(width:12, height: 12) + Text(yesParticipations.reduce(0, {x,y in x + y.stake}).description) + .font(.system(size: 15)) + .fontWeight(.bold) + .foregroundColor(AllInColors.blue200) + Spacer() + Text(noParticipations.reduce(0, {x,y in x + y.stake}).description) + .font(.system(size: 15)) + .fontWeight(.bold) + .foregroundColor(AllInColors.pink100) + Image("pinkAllCoinIcon") + .resizable() + .frame(width: 12, height: 12) + } + HStack(spacing: 5){ + Image("bluePersonIcon") + .resizable() + .frame(width: 12, height: 12) + Text(yesParticipations.count.description) + .font(.system(size: 15)) + .fontWeight(.bold) + .foregroundColor(AllInColors.blue200) + Spacer() + Text(noParticipations.count.description) + .font(.system(size: 15)) + .fontWeight(.bold) + .foregroundColor(AllInColors.pink100) + Image("pinkPersonIcon") + .resizable() + .frame(width: 12, height: 12) + + } + HStack(spacing: 5){ + Image("blueBadgeIcon") + .resizable() + .frame(width: 12, height: 12) + Text(yesParticipations.max(by: { $0.stake < $1.stake })?.stake.description ?? "0") + .font(.system(size: 15)) + .fontWeight(.bold) + .foregroundColor(AllInColors.blue200) + Spacer() + Text(noParticipations.max(by: { $0.stake < $1.stake })?.stake.description ?? "0") + .font(.system(size: 15)) + .fontWeight(.bold) + .foregroundColor(AllInColors.pink100) + Image("pinkBadgeIcon") + .resizable() + .frame(width: 12, height: 12) + + } + HStack(spacing: 5){ + Image("blueTrophyIcon") + .resizable() + .frame(width: 12, height: 12) + Text("1.2") + .font(.system(size: 15)) + .fontWeight(.bold) + .foregroundColor(AllInColors.blue200) + Spacer() + Text("1.2") + .font(.system(size: 15)) + .fontWeight(.bold) + .foregroundColor(AllInColors.pink100) + Image("pinkTrophyIcon") + .resizable() + .frame(width:12, height: 12) + } + } + } + } + } +} diff --git a/Sources/AllInApp/AllIn/Components/ChoiceFinalAnswerCell.swift b/Sources/AllInApp/AllIn/Components/ChoiceFinalAnswerCell.swift index 5665ea7..58e2831 100644 --- a/Sources/AllInApp/AllIn/Components/ChoiceFinalAnswerCell.swift +++ b/Sources/AllInApp/AllIn/Components/ChoiceFinalAnswerCell.swift @@ -14,23 +14,30 @@ struct ChoiceFinalAnswerCell: View { var selected = false var answer: AnswerDetail var rawColor = AllInColors.blueAccentColor - var body: some View { - ZStack{ - HStack{ + ZStack { + HStack { Spacer() Text(answer.response) - .textStyle(weight: .bold, color: selected ? .white :rawColor, size: 40).padding(.vertical, 10) + .textStyle(weight: .bold, color: selected ? .white : rawColor, size: 40) + .padding(.vertical, 10) Spacer() } - HStack{ + HStack { Spacer() - OddCapsule(backgroundColor: selected ? .white : AllInColors.purpleAccentColor, foregroundColor: selected ? AllInColors.purpleAccentColor : .white ,odd: answer.odds ).padding(.trailing,20).scaleEffect(0.9) + OddCapsule( + backgroundColor: selected ? .white : AllInColors.purpleAccentColor, + foregroundColor: selected ? AllInColors.purpleAccentColor : .white, + odd: answer.odds + ) + .padding(.trailing, 20) + .scaleEffect(0.9) } - }.background(selected ? AllInColors.purpleAccentColor : .white).cornerRadius(17).scaleEffect(selected ? 1.02 : 1).animation( - .easeInOut(duration: 0.3), - value: selected - ) + } + .background(selected ? AllInColors.purpleAccentColor : .white) + .cornerRadius(17) + .scaleEffect(selected ? 1.02 : 1) + .animation(.easeInOut(duration: 0.3), value: selected) } } diff --git a/Sources/AllInApp/AllIn/Components/CustomBetLine.swift b/Sources/AllInApp/AllIn/Components/CustomBetLine.swift new file mode 100644 index 0000000..e452c5e --- /dev/null +++ b/Sources/AllInApp/AllIn/Components/CustomBetLine.swift @@ -0,0 +1,141 @@ +// +// CustomBetLine.swift +// AllIn +// +// Created by Lucas Delanier on 10/06/2024. +// + +import SwiftUI +import Model + +struct CustomBetLine: View { + + @State var showInfos: [String: Bool] = [:] + var bet: BetDetail + + var participationsForAnswer: [String: [Participation]] { + Dictionary(grouping: bet.participations, by: { $0.answer.uppercased() }) + } + + var sortedAnswers: [AnswerDetail] { + bet.answers.sorted { $0.totalParticipants > $1.totalParticipants } + } + + func getTextStyle(for answer: AnswerDetail) -> Font.Weight { + return answer == sortedAnswers.first ? .bold : .light + } + + func getColor(for answer: AnswerDetail) -> Color { + return answer == sortedAnswers.first ? AllInColors.pinkAccentColor : AllInColors.blueAccentColor + } + + func getGradiant(for answer: AnswerDetail) -> LinearGradient { + return answer == sortedAnswers.first ? AllInColors.PinkBetGradiant : AllInColors.BlueBetLineGradiant + } + + func getFlameImage(for answer: AnswerDetail) -> String { + return answer == sortedAnswers.first ? "PinkFlame" : "BlueFlame" + } + + var body: some View { + VStack(alignment: .leading, spacing: 0) { + ForEach(sortedAnswers, id: \.response) { answer in + let participations = participationsForAnswer[answer.response.uppercased()] ?? [] + let totalParticipations = bet.participations.count + let percentage = totalParticipations > 0 ? CGFloat(participations.count) / CGFloat(totalParticipations) : 0.0 + + VStack { + HStack { + Text(answer.response) + .font(.system(size: 15)) + .fontWeight(getTextStyle(for: answer)) + .foregroundColor(getColor(for: answer)) + Spacer() + } + + GeometryReader { geometry in + ZStack(alignment: .leading) { + HStack(spacing: 0) { + Rectangle() + .frame(width: min(percentage * geometry.size.width, geometry.size.width), height: 17) + .foregroundStyle(getGradiant(for: answer)) + .cornerRadius(999, corners: [.topLeft, .bottomLeft]) + Image(getFlameImage(for: answer)) + .resizable() + .frame(width: 29, height: 32) + .padding(.leading, -6) + Text("\(Int(percentage * 100))%") + .font(.system(size: 15)) + .fontWeight(.bold) + .foregroundStyle(getColor(for: answer)) + .padding(.leading, 2) + } + } + .padding(.bottom, 5) + } + .frame(height: 40) + + HStack { + Spacer() + Text("bet_status_details_drawer") + .textStyle(weight: .medium, color: AllInColors.primaryTextColor, size: 10) + Image(showInfos[answer.response] ?? false ? "chevronUpIcon" : "chevronDownIcon") + .resizable() + .frame(width: 10, height: 7) + .scaledToFill() + } + .onTapGesture { + withAnimation { + showInfos[answer.response, default: false].toggle() + } + } + .padding(.trailing, 5) + + if showInfos[answer.response] ?? false { + HStack{ + VStack(alignment: .leading,spacing: 1) { + HStack(spacing: 5) { + Image(answer == sortedAnswers.first ? "pinkAllCoinIcon" : "blueAllCoinIcon") + .resizable() + .frame(width: 12, height: 12) + Text(participations.reduce(0, { x, y in x + y.stake }).description) + .font(.system(size: 15)) + .fontWeight(.bold) + .foregroundColor(getColor(for: answer)) + } + HStack(spacing: 5) { + Image(answer == sortedAnswers.first ? "pinkPersonIcon" : "bluePersonIcon") + .resizable() + .frame(width: 12, height: 12) + Text(participations.count.description) + .font(.system(size: 15)) + .fontWeight(.bold) + .foregroundColor(getColor(for: answer)) + } + HStack(spacing: 5) { + Image(answer == sortedAnswers.first ? "pinkBadgeIcon" : "blueBadgeIcon") + .resizable() + .frame(width: 12, height: 12) + Text(participations.max(by: { $0.stake < $1.stake })?.stake.description ?? "0") + .font(.system(size: 15)) + .fontWeight(.bold) + .foregroundColor(getColor(for: answer)) + } + HStack(spacing: 5) { + Image(answer == sortedAnswers.first ? "pinkTrophyIcon" : "blueTrophyIcon") + .resizable() + .frame(width: 12, height: 12) + Text(String(format: "%.2f", answer.odds)) + .font(.system(size: 15)) + .fontWeight(.bold) + .foregroundColor(getColor(for: answer)) + } + } + Spacer() + } + } + } + } + } + } +} diff --git a/Sources/AllInApp/AllIn/Components/DropDownAnswerMenu.swift b/Sources/AllInApp/AllIn/Components/DropDownAnswerMenu.swift index 7825627..f0c39a7 100644 --- a/Sources/AllInApp/AllIn/Components/DropDownAnswerMenu.swift +++ b/Sources/AllInApp/AllIn/Components/DropDownAnswerMenu.swift @@ -1,12 +1,3 @@ -// -// DropDownAnswerMenu.swift -// AllIn -// -// Created by Lucas Delanier on 16/01/2024. -// - -import SwiftUI - // // DropDownMenu.swift // AllIn @@ -15,20 +6,21 @@ import SwiftUI // import SwiftUI +import Model struct DropDownAnswerMenu: View { @State var expand = false - @Binding var selectedOption: Int - var options: [(Int, String, Float)] + @Binding var selectedAnswer: AnswerDetail + var answers: [AnswerDetail] var body: some View { VStack(spacing: 0, content: { - Button(action: { self.expand.toggle() }) { + Button(action: { withTransaction(Transaction(animation: nil)) { self.expand.toggle() } }) { HStack{ - Text(options[selectedOption].1.description) + Text(selectedAnswer.response) .textStyle(weight: .bold, color: AllInColors.blueAccentColor, size: 20) - Text(options[selectedOption].2.description) + Text(selectedAnswer.odds.description) .textStyle(weight: .bold, color: AllInColors.lightPurpleColor, size: 10) Spacer() @@ -43,19 +35,21 @@ struct DropDownAnswerMenu: View { .foregroundColor(AllInColors.delimiterGrey) .padding(.bottom, 18) VStack(spacing: 0) { - ForEach(0.. Void)? + @Binding var selectedAnswer: AnswerDetail + @Binding var mise: String + var phrase: String + var answers: [AnswerDetail] + var participationAddedCallback: () -> Void + var checkAndSetError: () -> Bool + var possibleGain: Int { - if let stake = Float(mise), let selectedOption = options.first(where: { $0.0 == self.selectedOption }) { - return Int(round(stake * selectedOption.2)) + if let stake = Float(mise) { + return Int(round(stake * selectedAnswer.odds)) } else { return 0 } } - init(answer: Binding, mise: Binding, description: String, participationAddedCallback: (() -> Void)? = nil) { - self._selectedOption = answer - self._mise = mise - self.description = description - self.participationAddedCallback = participationAddedCallback - } - - let options: [(Int, String, Float)] = [ - (0, "OUI", 1.2), - (1, "NON", 3.3), - ] - var body: some View { GeometryReader { geometry in VStack(alignment: .leading){ @@ -56,12 +48,12 @@ struct ParticipationModal: View { } .padding(.leading, 15) VStack(alignment: .leading){ - Text(description) + Text(phrase) .font(.system(size: 13)) .foregroundColor(AllInColors.primaryTextColor) .fontWeight(.light) - DropDownAnswerMenu(selectedOption: $selectedOption, options: options) + DropDownAnswerMenu(selectedAnswer: $selectedAnswer, answers: answers) TextField("", text: $mise, prompt: Text("generic_stake") .foregroundColor(AllInColors.lightGrey300Color) @@ -104,7 +96,7 @@ struct ParticipationModal: View { .padding(.top, 10) .padding(.bottom, 0) Button { - participationAddedCallback?() + participationAddedCallback() } label: { Text("Miser") .font(.system(size: 23)) @@ -125,16 +117,4 @@ struct ParticipationModal: View { .background(AllInColors.underComponentBackgroundColor) } } - - func checkAndSetError() -> Bool { - if let stake = Int(mise) { - if stake <= AppStateContainer.shared.user?.nbCoins ?? 0 && stake > 0 { - return false - } else { - return true - } - } else { - return true - } - } } diff --git a/Sources/AllInApp/AllIn/Ressources/Colors.swift b/Sources/AllInApp/AllIn/Ressources/Colors.swift index 99506b3..b1801c5 100644 --- a/Sources/AllInApp/AllIn/Ressources/Colors.swift +++ b/Sources/AllInApp/AllIn/Ressources/Colors.swift @@ -77,6 +77,12 @@ struct AllInColors { endPoint: .trailing ) + static let BlueBetLineGradiant = LinearGradient( + gradient: Gradient(colors: [AllInColors.blue200, AllInColors.blueAccentColor]), + startPoint: .leading, + endPoint: .trailing + ) + static let PinkBetGradiant = LinearGradient( gradient: Gradient(colors: [AllInColors.pink100, AllInColors.pink200]), startPoint: .leading, diff --git a/Sources/AllInApp/AllIn/Ressources/en.lproj/Localizable.strings b/Sources/AllInApp/AllIn/Ressources/en.lproj/Localizable.strings index 09abd58..d95e2d9 100644 --- a/Sources/AllInApp/AllIn/Ressources/en.lproj/Localizable.strings +++ b/Sources/AllInApp/AllIn/Ressources/en.lproj/Localizable.strings @@ -187,3 +187,8 @@ "empty_bets_title"= "No bet matches your search"; "empty_friends_title" = "You don't have any friends yet"; "empty_friends_explain" = "Add them from this screen"; + +/// Error Messages + +"error_title" = "Error: Failed to Load Content."; +"error_message" = "The content you are trying to load could not be retrieved. Please check your internet connection and try again"; diff --git a/Sources/AllInApp/AllIn/Ressources/fr.lproj/Localizable.strings b/Sources/AllInApp/AllIn/Ressources/fr.lproj/Localizable.strings index 9934153..e781668 100644 --- a/Sources/AllInApp/AllIn/Ressources/fr.lproj/Localizable.strings +++ b/Sources/AllInApp/AllIn/Ressources/fr.lproj/Localizable.strings @@ -186,3 +186,8 @@ "empty_bets_title"= "Aucun Bet ne correspond à votre recherche"; "empty_friends_title" = "Vous n’avez pas encore d’amis"; "empty_friends_explain" = "Ajoutez les depuis cet écran"; + +/// Error Messages + +"error_title" = "Erreur : Échec du chargement du contenu."; +"error_message" = "Le contenu que vous essayez de charger n'a pas pu être récupéré. Veuillez vérifier votre connexion internet et réessayer."; diff --git a/Sources/AllInApp/AllIn/ViewModels/CreationBetViewModel.swift b/Sources/AllInApp/AllIn/ViewModels/CreationBetViewModel.swift index cba9c44..568f759 100644 --- a/Sources/AllInApp/AllIn/ViewModels/CreationBetViewModel.swift +++ b/Sources/AllInApp/AllIn/ViewModels/CreationBetViewModel.swift @@ -23,19 +23,36 @@ class CreationBetViewModel: ObservableObject { @Published var endRegisterDate = Date() @Published var endBetDate = Date() @Published var betAdded = false - @Published var selectedOption = 0 + @Published var selectedTypeBet = 0 { + didSet { + values.removeAll() + groupedItems.removeAll() + response = "" + } + } + @Published var values: [String] = [] @Published var invited: Set = [] @Published var themeFieldError: String? @Published var descriptionFieldError: String? @Published var endRegisterDateFieldError: String? @Published var endBetDateFieldError: String? + @Published var responsesFieldError: String? @Published var errorMessage: String? @Published var showErrorMessage = false @Published var friends: [User] = [] + 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")) + ] + + @Published var response = "" + @Published var groupedItems: [[String]] = [[String]] () + init() { getFriends() } @@ -50,14 +67,14 @@ class CreationBetViewModel: ObservableObject { func create() { - guard checkAndSetError(forTheme: true, forDescription: true, forEndRegisterDate: true, forEndBetDate: true) else { + guard checkAndSetError(forTheme: true, forDescription: true, forEndRegisterDate: true, forEndBetDate: true, forResponse: true) else { return } resetAllFieldErrors() if let user = AppStateContainer.shared.user { - manager.addBet(bet: toBet(theme: theme, description: description, endRegister: endRegisterDate, endBet: endBetDate, isPrivate: isPrivate, status: .inProgress, creator: user.username, invited: Array(invited), type: selectedOption)) { statusCode in + manager.addBet(bet: toBet(theme: theme, description: description, endRegister: endRegisterDate, endBet: endBetDate, isPrivate: isPrivate, status: .inProgress, creator: user.username, invited: Array(invited), type: selectedTypeBet)) { statusCode in print(statusCode) switch statusCode { case 201: @@ -72,12 +89,13 @@ class CreationBetViewModel: ObservableObject { } } - func checkAndSetError(forTheme checkTheme: Bool, forDescription checkDescription: Bool, forEndRegisterDate checkEndRegisterDate: Bool, forEndBetDate checkEndBetDate: Bool) -> Bool { + func checkAndSetError(forTheme checkTheme: Bool, forDescription checkDescription: Bool, forEndRegisterDate checkEndRegisterDate: Bool, forEndBetDate checkEndBetDate: Bool, forResponse checkResponse: Bool) -> Bool { var newThemeFieldError: String? var newDescriptionFieldError: String? var newEndRegisterDateFieldError: String? var newEndBetDateFieldError: String? + var newResponsesFieldError: String? var hasError = false @@ -105,6 +123,19 @@ class CreationBetViewModel: ObservableObject { hasError = true } + if checkResponse, selectedTypeBet == 2 { + if values.count < 2 { + newResponsesFieldError = "Il doit y'avoir 2 réponses minimum" + hasError = true + } else { + let uniqueValues = Set(values) + if uniqueValues.count != values.count { + newResponsesFieldError = "Les réponses doivent être uniques" + hasError = true + } + } + } + if !hasError { // No error return true @@ -115,6 +146,7 @@ class CreationBetViewModel: ObservableObject { descriptionFieldError = newDescriptionFieldError endRegisterDateFieldError = newEndRegisterDateFieldError endBetDateFieldError = newEndBetDateFieldError + responsesFieldError = newResponsesFieldError } return false } @@ -125,6 +157,7 @@ class CreationBetViewModel: ObservableObject { descriptionFieldError = nil endRegisterDateFieldError = nil endBetDateFieldError = nil + responsesFieldError = nil } } @@ -140,7 +173,11 @@ class CreationBetViewModel: ObservableObject { case 1: return MatchBet(theme: theme, phrase: description, endRegisterDate: endRegister, endBetDate: endBet, isPrivate: isPrivate, status: status, invited: invited, author: creator, nameTeam1: "", nameTeam2: "") case 2: - return CustomBet(theme: theme, phrase: description, endRegisterDate: endRegister, endBetDate: endBet, isPrivate: isPrivate, status: status, invited: invited, author: creator) + var bet = CustomBet(theme: theme, phrase: description, endRegisterDate: endRegister, endBetDate: endBet, isPrivate: isPrivate, status: status, invited: invited, author: creator) + for answer in values { + bet.addCustomResponse(answer) + } + return bet default: return BinaryBet(theme: theme, phrase: description, endRegisterDate: endRegister, endBetDate: endBet, isPrivate: isPrivate, status: status, invited: invited, author: creator) } diff --git a/Sources/AllInApp/AllIn/ViewModels/DetailsViewModel.swift b/Sources/AllInApp/AllIn/ViewModels/DetailsViewModel.swift index ec1472b..913a165 100644 --- a/Sources/AllInApp/AllIn/ViewModels/DetailsViewModel.swift +++ b/Sources/AllInApp/AllIn/ViewModels/DetailsViewModel.swift @@ -15,9 +15,9 @@ class DetailsViewModel: ObservableObject { @Inject var manager: Manager var id: String - @Published var answer = 0 + @Published var selectedAnswer = AnswerDetail(response: "", totalStakes: 0, totalParticipants: 0, highestStake: 0, odds: 0) @Published var mise: String = "" - + @Published var betDetail: BetDetail? init(id: String) { @@ -28,28 +28,22 @@ class DetailsViewModel: ObservableObject { func getItem(withId id: String) { manager.getBet(withId: id) { bet in DispatchQueue.main.async { - for par in bet.participations { - print(par.id) - } self.betDetail = bet + if let firstAnswer = bet.answers.first { + self.selectedAnswer = firstAnswer + } } } } func addParticipate() { if let stake = Int(mise) { - var rep: String = "" - if answer == 0 { - rep = "Yes" - } else { - rep = "No" - } - manager.addParticipation(withId: id, withAnswer: rep, andStake: stake) { statusCode in + manager.addParticipation(withId: id, withAnswer: selectedAnswer.response, andStake: stake) { statusCode in switch statusCode { case 201: AppStateContainer.shared.user?.nbCoins -= stake WidgetCenter.shared.reloadAllTimelines() - + self.getItem(withId: self.id) default: break @@ -57,10 +51,20 @@ class DetailsViewModel: ObservableObject { } } mise = "" - answer = 0 + if let firstAnswer = betDetail!.answers.first { + self.selectedAnswer = firstAnswer + } } - func checkAndSetError() { - + func checkAndSetError() -> Bool { + if let stake = Int(mise) { + if stake <= AppStateContainer.shared.user?.nbCoins ?? 0 && stake > 0 { + return false + } else { + return true + } + } else { + return true + } } } diff --git a/Sources/AllInApp/AllIn/Views/CreationBetView.swift b/Sources/AllInApp/AllIn/Views/CreationBetView.swift index 4d033c5..d84a969 100644 --- a/Sources/AllInApp/AllIn/Views/CreationBetView.swift +++ b/Sources/AllInApp/AllIn/Views/CreationBetView.swift @@ -28,17 +28,6 @@ struct CreationBetView: View { }() 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]] () @@ -46,7 +35,7 @@ struct CreationBetView: View { var width: CGFloat = 0 var dynamicWidthLimit: CGFloat - for value in values { + for value in viewModel.values { let label = UILabel() label.text = value label.sizeToFit() @@ -65,7 +54,7 @@ struct CreationBetView: View { } updatedGroupedItems.append(tempItems) - groupedItems = updatedGroupedItems + viewModel.groupedItems = updatedGroupedItems } var body: some View { @@ -108,6 +97,7 @@ struct CreationBetView: View { .foregroundColor(AllInColors.lightGrey300Color) .font(.system(size: 14)) .fontWeight(.light)) + .autocorrectionDisabled(true) .padding() .background( RoundedRectangle(cornerRadius: 9) @@ -151,6 +141,7 @@ struct CreationBetView: View { .foregroundColor(AllInColors.lightGrey300Color) .font(.system(size: 14)) .fontWeight(.light), axis: .vertical) + .autocorrectionDisabled(true) .lineLimit(4, reservesSpace: true) .padding() .background( @@ -360,14 +351,14 @@ struct CreationBetView: View { VStack(spacing: 5) { VStack() { - DropDownMenu(selectedOption: $viewModel.selectedOption, options: options) + DropDownMenu(selectedOption: $viewModel.selectedTypeBet, options: viewModel.options) } .padding([.bottom], 15) .frame(width: 340) Group { - switch viewModel.selectedOption { + switch viewModel.selectedTypeBet { case 0: Text("bet_creation_yes_no_bottom_text_1") .textStyle(weight: .bold, color: AllInColors.veryLightPurpleColor, size: 13) @@ -385,12 +376,18 @@ struct CreationBetView: View { .textStyle(weight: .bold, color: AllInColors.veryLightPurpleColor, size: 13) .padding(.bottom, 15) + if let responseError = $viewModel.responsesFieldError.wrappedValue { + Text(responseError) + .textStyle(weight: .bold, color: .red, size: 10) + } + VStack(spacing: 5) { HStack(spacing: 0) { - TextField("", text: $response, prompt: Text("bet_creation_response_title") + TextField("", text: $viewModel.response, prompt: Text("bet_creation_response_title") .foregroundColor(AllInColors.lightGrey200Color) .font(.system(size: 16)) .fontWeight(.medium)) + .autocorrectionDisabled(true) .padding() .background( Rectangle() @@ -400,17 +397,17 @@ struct CreationBetView: View { ) .frame(width: 250, height: 38) .foregroundColor(.black) - .onChange(of: response) { newValue in + .onChange(of: viewModel.response) { newValue in if newValue.count > 20 { - response = String(newValue.prefix(20)) + viewModel.response = String(newValue.prefix(20)) } } Button(action: { - if !response.isEmpty && values.count < 5 { - values.append(response) + if !viewModel.response.isEmpty && viewModel.values.count < 5 { + viewModel.values.append(viewModel.response) updateGroupedItems() - response = "" + viewModel.response = "" } }) { Text("generic_add") @@ -423,12 +420,12 @@ struct CreationBetView: View { } HStack { Spacer() - Text(String(localized: "bet_creation_max_answers \(5 - values.count)")) + Text(String(localized: "bet_creation_max_answers \(5 - viewModel.values.count)")) .textStyle(weight: .regular, color: AllInColors.primaryTextColor, size: 12) } VStack(spacing: 10) { - ForEach(groupedItems, id: \.self) { items in + ForEach(viewModel.groupedItems, id: \.self) { items in HStack { ForEach(items, id: \.self) { text in HStack { @@ -436,8 +433,8 @@ struct CreationBetView: View { .foregroundColor(.white) .lineLimit(1) Button(action: { - if let index = values.firstIndex(of: text) { - values.remove(at: index) + if let index = viewModel.values.firstIndex(of: text) { + viewModel.values.remove(at: index) updateGroupedItems() } }) { diff --git a/Sources/AllInApp/AllIn/Views/DetailsView.swift b/Sources/AllInApp/AllIn/Views/DetailsView.swift index 09bcb2b..3472e83 100644 --- a/Sources/AllInApp/AllIn/Views/DetailsView.swift +++ b/Sources/AllInApp/AllIn/Views/DetailsView.swift @@ -57,98 +57,116 @@ struct DetailsView: View { } .padding(.horizontal, 15) .background(StatusValues.1) - - VStack(spacing: 0) { - VStack(alignment: .leading, spacing: 5) { - HStack(spacing: 3) { - Spacer() - Text("bet_proposed_by_format") - .font(.system(size: 10)) - .foregroundColor(AllInColors.grey800Color) - Text((viewModel.betDetail?.bet.author ?? "Unknown").capitalized) - .font(.system(size: 10)) - .fontWeight(.semibold) - .foregroundColor(AllInColors.primaryTextColor) - - } - Text(viewModel.betDetail?.bet.theme ?? "Not loaded") - .font(.system(size: 15)) - .foregroundColor(AllInColors.grey800Color) - Text(viewModel.betDetail?.bet.phrase ?? "Not loaded") - .font(.system(size: 20)) - .fontWeight(.bold) - .padding(.bottom, 10) - HStack { - HStack { + if viewModel.betDetail != nil{ + ScrollView { + VStack(alignment: .leading, spacing: 5) { + HStack(spacing: 3) { Spacer() - Text("bet_starting") - .font(.system(size: 15)) + Text("bet_proposed_by_format") + .font(.system(size: 10)) .foregroundColor(AllInColors.grey800Color) + Text((viewModel.betDetail?.bet.author ?? "Unknown").capitalized) + .font(.system(size: 10)) + .fontWeight(.semibold) + .foregroundColor(AllInColors.primaryTextColor) + } - .frame(width: 105) - .padding(.trailing, 10) - TextCapsule(date: viewModel.betDetail?.bet.endRegisterDate ?? Date()) - Spacer() - - }.padding(.bottom, 10) - HStack { + Text(viewModel.betDetail?.bet.theme ?? "Not loaded") + .font(.system(size: 15)) + .foregroundColor(AllInColors.grey800Color) + Text(viewModel.betDetail?.bet.phrase ?? "Not loaded") + .font(.system(size: 20)) + .fontWeight(.bold) + .padding(.bottom, 10) HStack { + HStack { + Spacer() + Text("bet_starting") + .font(.system(size: 15)) + .foregroundColor(AllInColors.grey800Color) + } + .frame(width: 105) + .padding(.trailing, 10) + TextCapsule(date: viewModel.betDetail?.bet.endRegisterDate ?? Date()) + Spacer() + + }.padding(.bottom, 10) + HStack { + HStack { + Spacer() + Text("bet_ends") + .font(.system(size: 15)) + .foregroundColor(AllInColors.grey800Color) + } + .frame(width: 105) + .padding(.trailing, 10) + TextCapsule(date: viewModel.betDetail?.bet.endBetDate ?? Date()) Spacer() - Text("bet_ends") - .font(.system(size: 15)) - .foregroundColor(AllInColors.grey800Color) } - .frame(width: 105) - .padding(.trailing, 10) - TextCapsule(date: viewModel.betDetail?.bet.endBetDate ?? Date()) - Spacer() } - } - .padding(.all, 15) - .padding(.vertical, 10) - .background(AllInColors.componentBackgroundColor) - .cornerRadius(20, corners: [.topLeft,.topRight]).padding(.bottom,0) - - if isFinished { - ResultBanner(finalAnswer: (viewModel.betDetail?.wonParticipation)!, odds: (viewModel.betDetail?.odds)!) - } - VStack(alignment: .leading, spacing: 5) { - BetLineLoading(participations: viewModel.betDetail?.participations ?? []) - .padding(.vertical,15) - Text("bet_status_participants_list") - .font(.system(size: 18)) - .foregroundStyle(AllInColors.grey100Color) - .fontWeight(.bold) - .padding(.bottom, 10) - ScrollView(showsIndicators: false) { + .padding(.all, 15) + .padding(.top, 6) + .border(width: 1, edges: [.bottom], color: AllInColors.delimiterGrey) + .background(AllInColors.componentBackgroundColor) + .cornerRadius(20, corners: [.topLeft,.topRight]) + + if isFinished { + ResultBanner(finalAnswer: (viewModel.betDetail?.wonParticipation)!, odds: (viewModel.betDetail?.odds)!) + } + VStack(alignment: .leading, spacing: 0) { + if let bet = viewModel.betDetail{ + BetLineLoading(bet: bet).padding(.vertical, 20) + + } + Text("bet_status_participants_list") + .font(.system(size: 18)) + .foregroundStyle(AllInColors.grey100Color) + .fontWeight(.bold) + .padding(.bottom, 10) + ForEach(viewModel.betDetail?.participations ?? []) { participation in - // TODO UserInfo(username: participation.username, picture: nil , value: participation.stake).padding(.horizontal, 10) } + .padding(.bottom) + + Spacer() } - .padding(.bottom, geometry.safeAreaInsets.bottom + 28) + .padding([.trailing,.leading], 15) - Spacer() } - .padding([.bottom,.trailing,.leading], 15) + .frame(maxWidth: .infinity, maxHeight: (geometry.size.height + geometry.safeAreaInsets.bottom) - 50) .background(AllInColors.underComponentBackgroundColor) - .border(width: 1, edges: [.top], color: AllInColors.delimiterGrey) - Spacer() - + .cornerRadius(15, corners: [.topLeft, .topRight]) + } + else{ + ScrollView { + HStack(alignment: .center){ + Spacer() + Image(systemName: "exclamationmark.triangle.fill").font(.system(size: 45)) + VStack(alignment:.leading){ + Text("error_title").font(.system(size: 10)) + Text("error_message").font(.system(size: 10)) + } + Spacer() + }.opacity(0.6) + .padding(.vertical, 20) + + } + .frame(maxWidth: .infinity, maxHeight: (geometry.size.height + geometry.safeAreaInsets.bottom) - 50) + .background(AllInColors.underComponentBackgroundColor) + .cornerRadius(15, corners: [.topLeft, .topRight]) } - .frame(maxWidth: .infinity, maxHeight: (geometry.size.height + geometry.safeAreaInsets.bottom) - 50) - .background(AllInColors.componentBackgroundColor) - .cornerRadius(15, corners: [.topLeft, .topRight]) ParticipateButton(isOpen: $isModalPresented, isParticapatedOpen: $isModalParticipated, bet: viewModel.betDetail?.bet) .padding(.bottom, geometry.safeAreaInsets.bottom + 5) .padding(.horizontal, 10) } .sheet(isPresented: $isModalParticipated) { - ParticipationModal(answer: $viewModel.answer, mise: $viewModel.mise, description: viewModel.betDetail?.bet.phrase ?? "Not loaded", participationAddedCallback: { + ParticipationModal(selectedAnswer: $viewModel.selectedAnswer, mise: $viewModel.mise, phrase: viewModel.betDetail?.bet.phrase ?? "", answers: viewModel.betDetail?.answers ?? [], participationAddedCallback: { viewModel.addParticipate() isModalParticipated.toggle() - }) + }, + checkAndSetError: viewModel.checkAndSetError) .presentationDetents([.fraction(0.55)]) } .edgesIgnoringSafeArea(.bottom) diff --git a/Sources/AllInApp/AllInApp.xcodeproj/project.pbxproj b/Sources/AllInApp/AllInApp.xcodeproj/project.pbxproj index 09d030d..aaa866b 100644 --- a/Sources/AllInApp/AllInApp.xcodeproj/project.pbxproj +++ b/Sources/AllInApp/AllInApp.xcodeproj/project.pbxproj @@ -9,6 +9,8 @@ /* Begin PBXBuildFile section */ 120919182B56D0AE00D0FA29 /* ParticipationModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 120919172B56D0AE00D0FA29 /* ParticipationModal.swift */; }; 1209191A2B56DC6C00D0FA29 /* DropDownAnswerMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 120919192B56DC6C00D0FA29 /* DropDownAnswerMenu.swift */; }; + 12228A212C1760D2008524F4 /* BinaryBetLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12228A202C1760D2008524F4 /* BinaryBetLine.swift */; }; + 12228A232C176117008524F4 /* CustomBetLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12228A222C176117008524F4 /* CustomBetLine.swift */; }; 123225D92B67B46100D30BB3 /* BetEndingValidationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123225D82B67B46100D30BB3 /* BetEndingValidationView.swift */; }; 123225DB2B67E41400D30BB3 /* ChoiceFinalAnswerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123225DA2B67E41400D30BB3 /* ChoiceFinalAnswerCell.swift */; }; 123590B42B51792000F7AEBD /* DetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123590B32B51792000F7AEBD /* DetailsView.swift */; }; @@ -149,6 +151,8 @@ 120919192B56DC6C00D0FA29 /* DropDownAnswerMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropDownAnswerMenu.swift; sourceTree = ""; }; 122278B72B4BDE1100E632AA /* DependencyInjection.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DependencyInjection.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 122278B92B4BDE9500E632AA /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Package.swift; path = ../Model/Package.swift; sourceTree = ""; }; + 12228A202C1760D2008524F4 /* BinaryBetLine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BinaryBetLine.swift; sourceTree = ""; }; + 12228A222C176117008524F4 /* CustomBetLine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomBetLine.swift; sourceTree = ""; }; 123225D82B67B46100D30BB3 /* BetEndingValidationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BetEndingValidationView.swift; sourceTree = ""; }; 123225DA2B67E41400D30BB3 /* ChoiceFinalAnswerCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChoiceFinalAnswerCell.swift; sourceTree = ""; }; 123590B32B51792000F7AEBD /* DetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailsView.swift; sourceTree = ""; }; @@ -444,6 +448,8 @@ 129D051C2B6E7FF0003D3E08 /* OddCapsule.swift */, 12A9E4932C07132600AB8677 /* EmptyInfo.swift */, 123F31DA2C0F26E8009B6D65 /* userPicture.swift */, + 12228A202C1760D2008524F4 /* BinaryBetLine.swift */, + 12228A222C176117008524F4 /* CustomBetLine.swift */, ); path = Components; sourceTree = ""; @@ -675,6 +681,7 @@ files = ( EC6B96CC2B24B7E500FC1C58 /* IAuthService.swift in Sources */, ECB26A172B4073F100FE06B3 /* CreationBetViewModel.swift in Sources */, + 12228A232C176117008524F4 /* CustomBetLine.swift in Sources */, EC3077092B24CF7F0060E34D /* Colors.swift in Sources */, EC6B969E2B24B4CC00FC1C58 /* ContentView.swift in Sources */, EC89F7BD2B250D66003821CE /* LoginView.swift in Sources */, @@ -721,6 +728,7 @@ EC01937E2B25C52E005D81E6 /* TopBar.swift in Sources */, ECA9D1CB2B2DA2320076E0EC /* DropDownFriends.swift in Sources */, 123590B82B5541BA00F7AEBD /* ParticipateButton.swift in Sources */, + 12228A212C1760D2008524F4 /* BinaryBetLine.swift in Sources */, EC01FCC32B56650400BB2390 /* DetailsViewModel.swift in Sources */, ECB26A1B2B40746C00FE06B3 /* FriendsViewModel.swift in Sources */, ECB7BC682B2F1ADF002A6654 /* LoginViewModel.swift in Sources */, diff --git a/Sources/Api/Sources/Api/Factory/FactoryApiBet.swift b/Sources/Api/Sources/Api/Factory/FactoryApiBet.swift index 67811b7..3a7019c 100644 --- a/Sources/Api/Sources/Api/Factory/FactoryApiBet.swift +++ b/Sources/Api/Sources/Api/Factory/FactoryApiBet.swift @@ -24,7 +24,7 @@ public class FactoryApiBet: FactoryBet { "endRegistration": formatZonedDateTime(dateTime: bet.endRegisterDate), "endBet": formatZonedDateTime(dateTime: bet.endBetDate), "isPrivate": String(bet.isPrivate), - "response": ["Yes","No"], + "response": bet.getResponses(), "userInvited": bet.invited, "type": betTypeString(fromType: String(describing: type(of: bet))) ] diff --git a/Sources/Api/Sources/Api/UserApiManager.swift b/Sources/Api/Sources/Api/UserApiManager.swift index 5669526..4e6a347 100644 --- a/Sources/Api/Sources/Api/UserApiManager.swift +++ b/Sources/Api/Sources/Api/UserApiManager.swift @@ -67,7 +67,6 @@ public struct UserApiManager: UserDataManager { print ("ALLIN : get bets won") do { if let httpResponse = response as? HTTPURLResponse, let jsonArray = try JSONSerialization.jsonObject(with: data, options: []) as? [[String: Any]] { - print(jsonArray) for json in jsonArray { if let bet = FactoryApiBet().toBetResultDetail(from: json) { print(bet) diff --git a/Sources/Model/Sources/Model/AnswerDetail.swift b/Sources/Model/Sources/Model/AnswerDetail.swift index fb0baef..5b56cdd 100644 --- a/Sources/Model/Sources/Model/AnswerDetail.swift +++ b/Sources/Model/Sources/Model/AnswerDetail.swift @@ -8,7 +8,7 @@ import Foundation /// A class representing detailed information about a specific answer option for a bet. -public class AnswerDetail: ObservableObject, Identifiable, Codable { +public class AnswerDetail: ObservableObject, Codable, Equatable, Identifiable, Hashable { /// The response or outcome associated with this answer. public private(set) var response: String @@ -41,4 +41,19 @@ public class AnswerDetail: ObservableObject, Identifiable, Codable { self.odds = odds } + public static func == (lhs: AnswerDetail, rhs: AnswerDetail) -> Bool { + return lhs.response == rhs.response && + lhs.totalStakes == rhs.totalStakes && + lhs.totalParticipants == rhs.totalParticipants && + lhs.highestStake == rhs.highestStake && + lhs.odds == rhs.odds + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(response) + hasher.combine(totalStakes) + hasher.combine(totalParticipants) + hasher.combine(highestStake) + hasher.combine(odds) + } } diff --git a/Sources/Model/Sources/Model/Bet.swift b/Sources/Model/Sources/Model/Bet.swift index ba016a6..28369f3 100644 --- a/Sources/Model/Sources/Model/Bet.swift +++ b/Sources/Model/Sources/Model/Bet.swift @@ -8,7 +8,7 @@ import Foundation /// A class representing a betting entity, including details about the bet theme, participants, and deadlines. -public class Bet: ObservableObject, Identifiable, Codable { +public class Bet: ObservableObject, Codable { /// The id for the bet. public private(set) var id: String @@ -112,4 +112,11 @@ public class Bet: ObservableObject, Identifiable, Codable { self.author = author } + /// Function that returns an empty list of responses + /// + /// - Returns: An empty list of responses + public func getResponses() -> [String] { + return [] + } + } diff --git a/Sources/Model/Sources/Model/CustomBet.swift b/Sources/Model/Sources/Model/CustomBet.swift index e057643..9660ac6 100644 --- a/Sources/Model/Sources/Model/CustomBet.swift +++ b/Sources/Model/Sources/Model/CustomBet.swift @@ -10,4 +10,21 @@ import Foundation /// A subclass of Bet that represents a custom bet, allowing users to define their own parameters and rules. public class CustomBet: Bet { + /// List of custom responses + private var customResponses: [String] = [] + + /// Override the getResponses function to return custom responses + /// + /// - Returns: A list of custom responses + public override func getResponses() -> [String] { + return customResponses + } + + /// Add a custom response + /// + /// - Parameter response: The custom response to add + public func addCustomResponse(_ response: String) { + customResponses.append(response) + } + }