From 0b4f5a0f537c1ba0b1a5f96d37207722b2511cbd Mon Sep 17 00:00:00 2001 From: "emre.kartal" Date: Wed, 24 Jan 2024 13:35:18 +0100 Subject: [PATCH] Add Participation functions --- .../AllInApp/AllIn/Components/BetCard.swift | 3 +- .../AllIn/Components/BetLineLoading.swift | 38 +++++----- Sources/AllInApp/AllIn/Components/Menu.swift | 15 ++++ .../AllIn/Components/ParticipateButton.swift | 29 +++----- .../AllIn/Components/ParticipationModal.swift | 73 ++++++++++++++----- .../AllInApp/AllIn/Services/AuthService.swift | 5 ++ .../AllIn/Services/IAuthService.swift | 1 + .../AllIn/ViewModels/DetailsViewModel.swift | 16 +++- .../AllInApp/AllIn/Views/DetailsView.swift | 50 ++++++++----- .../Sources/Api/Factory/FactoryApiBet.swift | 28 ++++++- Sources/Api/Sources/Api/UserApiManager.swift | 23 ++++++ Sources/Model/Sources/Model/Manager.swift | 4 + .../Model/Sources/Model/Participation.swift | 6 +- .../Model/Sources/Model/UserDataManager.swift | 1 + Sources/StubLib/Sources/StubLib/Stub.swift | 8 +- 15 files changed, 215 insertions(+), 85 deletions(-) diff --git a/Sources/AllInApp/AllIn/Components/BetCard.swift b/Sources/AllInApp/AllIn/Components/BetCard.swift index 68058b2..4e4073d 100644 --- a/Sources/AllInApp/AllIn/Components/BetCard.swift +++ b/Sources/AllInApp/AllIn/Components/BetCard.swift @@ -51,7 +51,8 @@ struct BetCard: View { Spacer() }.padding(0) - ParticipateButton(isOpen: $showDetails, isParticapatedOpen: $showParticipate, bet: bet).padding(.top, 5) + ParticipateButton(isOpen: $showDetails, isParticapatedOpen: $showParticipate, bet: bet) + .padding(.top, 5) } .frame(width: .infinity) .padding(.all,8) diff --git a/Sources/AllInApp/AllIn/Components/BetLineLoading.swift b/Sources/AllInApp/AllIn/Components/BetLineLoading.swift index 8bf0d58..5d841af 100644 --- a/Sources/AllInApp/AllIn/Components/BetLineLoading.swift +++ b/Sources/AllInApp/AllIn/Components/BetLineLoading.swift @@ -10,28 +10,28 @@ import Model struct BetLineLoading: View { - @State var participations: [Participation] + var participations: [Participation] var value: CGFloat { let totalParticipations = participations.count - let numberOfYes = participations.filter { $0.response == "OUI" }.count - let numberOfNo = participations.filter { $0.response == "NON" }.count + let numberOfYes = participations.filter { $0.response.uppercased() == "YES" }.count + let numberOfNo = participations.filter { $0.response.uppercased() == "NO" }.count if(numberOfNo == 0 && numberOfYes == 0){ return 0.5 } - - return totalParticipations > 0 ? CGFloat(numberOfYes) / CGFloat(totalParticipations) : 0.0 - } + + return totalParticipations > 0 ? CGFloat(numberOfYes) / CGFloat(totalParticipations) : 0.0 + } + - var yesParticipations: [Participation] { - return participations.filter { $0.response == "OUI" } - } - - var noParticipations: [Participation] { - return participations.filter { $0.response == "NON" } - } + return participations.filter { $0.response.uppercased() == "YES" } + } + + var noParticipations: [Participation] { + return participations.filter { $0.response.uppercased() == "NO" } + } var body: some View { @@ -41,7 +41,7 @@ struct BetLineLoading: View { Text("OUI").font(.system(size: 25)).fontWeight(.bold).foregroundColor(AllInColors.bleue200) Spacer() Text("NON").font(.system(size: 25)).fontWeight(.bold).foregroundColor(AllInColors.pink100) - + } ZStack(alignment: .leading) { HStack{ @@ -65,7 +65,7 @@ struct BetLineLoading: View { Spacer() Text(noParticipations.reduce(0, {x,y in x + y.stake}).description).font(.system(size: 15)).fontWeight(.bold).foregroundColor(AllInColors.pink100) Image("PinkBadge").resizable().frame(width:10, height: 14) - + } HStack(spacing: 5){ Image("BleuePersonIcon").resizable().frame(width:14, height: 12) @@ -73,7 +73,7 @@ struct BetLineLoading: View { Spacer() Text(noParticipations.count.description).font(.system(size: 15)).fontWeight(.bold).foregroundColor(AllInColors.pink100) Image("PinkBadge").resizable().frame(width:10, height: 14) - + } HStack(spacing: 5){ Image("BleueBadge").resizable().frame(width:10, height: 14) @@ -81,7 +81,7 @@ struct BetLineLoading: View { Spacer() Text(noParticipations.max(by: { $0.stake < $1.stake })?.stake.description ?? "0").font(.system(size: 15)).fontWeight(.bold).foregroundColor(AllInColors.pink100) Image("PinkBadge").resizable().frame(width:10, height: 14) - + } HStack(spacing: 5){ Image("BleueTrophyIcon").resizable().frame(width:14, height: 13) @@ -89,11 +89,11 @@ struct BetLineLoading: View { Spacer() Text("1.2").font(.system(size: 15)).fontWeight(.bold).foregroundColor(AllInColors.pink100) Image("PinkBadge").resizable().frame(width:10, height: 14) - + } } } - + }.frame(height: 140) } } diff --git a/Sources/AllInApp/AllIn/Components/Menu.swift b/Sources/AllInApp/AllIn/Components/Menu.swift index 29804cf..3bfd971 100644 --- a/Sources/AllInApp/AllIn/Components/Menu.swift +++ b/Sources/AllInApp/AllIn/Components/Menu.swift @@ -6,8 +6,12 @@ // import SwiftUI +import DependencyInjection struct Menu: View { + + @Inject var authService: IAuthService + var body: some View { VStack(alignment: .leading, spacing: 10) { @@ -93,6 +97,17 @@ struct Menu: View { .padding([.leading,.trailing], 13) } + HStack { + Spacer() + Button { + authService.logout() + } label: { + Text("Deconnexion") + .foregroundColor(.white) + } + Spacer() + } + Spacer() Image("gearIcon") .resizable() diff --git a/Sources/AllInApp/AllIn/Components/ParticipateButton.swift b/Sources/AllInApp/AllIn/Components/ParticipateButton.swift index cdb3ea8..44c1511 100644 --- a/Sources/AllInApp/AllIn/Components/ParticipateButton.swift +++ b/Sources/AllInApp/AllIn/Components/ParticipateButton.swift @@ -9,25 +9,22 @@ import SwiftUI import Model struct ParticipateButton: View { + @Binding var isOpen : Bool @Binding var isParticapatedOpen: Bool - @State var bet: Bet? + var bet: Bet? var isDisabled: Bool { - let endRegisterDate: Date? = bet?.endRegisterDate - if endRegisterDate != nil{ - let currentDate = Date() + guard let endRegisterDate = bet?.endRegisterDate else { + return true + } + + let currentDate = Date() - switch currentDate.compare(endRegisterDate!) { - case .orderedAscending: - return false - case .orderedDescending: - return true - case .orderedSame: - return true - } - - } else { + switch currentDate.compare(endRegisterDate) { + case .orderedAscending: + return false + case .orderedDescending, .orderedSame: return true } } @@ -43,7 +40,7 @@ struct ParticipateButton: View { .frame(maxWidth: .infinity).padding(10) .multilineTextAlignment(.center) .overlay { - switch isDisabled{ + switch isDisabled { case true: AllInColors.grey700Color.frame(width: 170) .mask( @@ -69,8 +66,6 @@ struct ParticipateButton: View { .overlay( RoundedRectangle(cornerRadius: 12).stroke(AllInColors.delimiterGrey, lineWidth: 1) ) - - }.disabled(isDisabled) } } diff --git a/Sources/AllInApp/AllIn/Components/ParticipationModal.swift b/Sources/AllInApp/AllIn/Components/ParticipationModal.swift index da30d02..04f33a3 100644 --- a/Sources/AllInApp/AllIn/Components/ParticipationModal.swift +++ b/Sources/AllInApp/AllIn/Components/ParticipationModal.swift @@ -8,29 +8,52 @@ import SwiftUI struct ParticipationModal: View { - @State private var selectedOption = 0 - @State private var mise: String = "" + + @Binding private var selectedOption: Int + @Binding private var mise: String + private var description: String + var participationAddedCallback: (() -> Void)? + + 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 { VStack(alignment: .leading){ HStack{ Spacer() - Rectangle().foregroundStyle(AllInColors.grey800Color).frame(maxWidth: 80, maxHeight: 5).cornerRadius(999) + Rectangle() + .foregroundStyle(AllInColors.grey800Color) + .frame(maxWidth: 80, maxHeight: 5) + .cornerRadius(999) Spacer() - + }.padding(10) HStack{ - Text("Faites vos paris").font(.system(size: 18)).foregroundColor(AllInColors.blackTitleColor).fontWeight(.semibold) + Text("Faites vos paris") + .font(.system(size: 18)) + .foregroundColor(AllInColors.blackTitleColor) + .fontWeight(.semibold) Spacer() AllcoinsCounter(backgroundColor: AllInColors.purpleAccentColor, foregroundColor: AllInColors.whiteColor) } .padding(.leading, 15) VStack(alignment: .leading){ - Text("Emre va réussir son TP de CI/CD mercredi?").font(.system(size: 13)).foregroundColor(AllInColors.grey100Color).fontWeight(.light) + Text(description) + .font(.system(size: 13)) + .foregroundColor(AllInColors.grey100Color) + .fontWeight(.light) + DropDownAnswerMenu(selectedOption: $selectedOption, options: options) + TextField("",text: $mise, prompt: Text("Mise") .foregroundColor(AllInColors.lightGrey300Color) .font(.system(size: 14)) @@ -49,25 +72,41 @@ struct ParticipationModal: View { .stroke(AllInColors.delimiterGrey, lineWidth: 1) ) .padding(.bottom, 5) - + } .padding(15) Spacer() VStack{ HStack{ - Text("Gains possibles").font(.system(size: 13)).foregroundColor(AllInColors.blackTitleColor).fontWeight(.regular) + Text("Gains possibles") + .font(.system(size: 13)) + .foregroundColor(AllInColors.blackTitleColor) + .fontWeight(.regular) Spacer() - Text("231").font(.system(size: 13)).foregroundColor(AllInColors.blackTitleColor).fontWeight(.light) - }.padding(.top, 10).padding(.bottom, 0) + Text("231") + .font(.system(size: 13)) + .foregroundColor(AllInColors.blackTitleColor) + .fontWeight(.light) + } + .padding(.top, 10).padding(.bottom, 0) Button { - + participationAddedCallback?() } label: { - Text("Miser").font(.system(size: 23)).foregroundColor(AllInColors.whiteColor).fontWeight(.bold) - .frame(maxWidth: .infinity).padding(.vertical, 3) + Text("Miser") + .font(.system(size: 23)) + .foregroundColor(AllInColors.whiteColor) + .fontWeight(.bold) + .frame(maxWidth: .infinity) + .padding(.vertical, 3) } - .buttonStyle(.borderedProminent).tint(AllInColors.purpleAccentColor) - - }.padding(.horizontal, 10).background(AllInColors.whiteColor).border(width: 1, edges: [.top], color: AllInColors.delimiterGrey) - }.background(AllInColors.underComponentBackgroundColor) + .buttonStyle(.borderedProminent) + .tint(AllInColors.purpleAccentColor) + + } + .padding(.horizontal, 10) + .background(AllInColors.whiteColor) + .border(width: 1, edges: [.top], color: AllInColors.delimiterGrey) + } + .background(AllInColors.underComponentBackgroundColor) } } diff --git a/Sources/AllInApp/AllIn/Services/AuthService.swift b/Sources/AllInApp/AllIn/Services/AuthService.swift index 3d2ab62..700fd21 100644 --- a/Sources/AllInApp/AllIn/Services/AuthService.swift +++ b/Sources/AllInApp/AllIn/Services/AuthService.swift @@ -153,4 +153,9 @@ class AuthService: IAuthService { DependencyInjection.shared.addSingleton(Manager.self, Manager(withBetDataManager: BetApiManager(withUserToken: token), withUserDataManager: UserApiManager(withUserToken: token))) } + public func logout() { + AppStateContainer.shared.authenticationRefresh = nil + AppStateContainer.shared.loggedState.connectedUser = false + } + } diff --git a/Sources/AllInApp/AllIn/Services/IAuthService.swift b/Sources/AllInApp/AllIn/Services/IAuthService.swift index cafd070..57c7050 100644 --- a/Sources/AllInApp/AllIn/Services/IAuthService.swift +++ b/Sources/AllInApp/AllIn/Services/IAuthService.swift @@ -11,4 +11,5 @@ protocol IAuthService { func login(login: String, password: String, completion : @escaping (Int)-> ()) func register(username: String, email: String, password: String, completion : @escaping (Int)-> ()) func refreshAuthentication() + func logout() } diff --git a/Sources/AllInApp/AllIn/ViewModels/DetailsViewModel.swift b/Sources/AllInApp/AllIn/ViewModels/DetailsViewModel.swift index e6980e6..3615836 100644 --- a/Sources/AllInApp/AllIn/ViewModels/DetailsViewModel.swift +++ b/Sources/AllInApp/AllIn/ViewModels/DetailsViewModel.swift @@ -14,7 +14,9 @@ class DetailsViewModel: ObservableObject { @Inject var manager: Manager var id: String - + @Published var answer = 0 + @Published var mise: String = "" + @Published var betDetail: BetDetail? init(id: String) { @@ -27,4 +29,16 @@ class DetailsViewModel: ObservableObject { self.betDetail = bet } } + + 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) + } + } } diff --git a/Sources/AllInApp/AllIn/Views/DetailsView.swift b/Sources/AllInApp/AllIn/Views/DetailsView.swift index 2afda06..2bd3322 100644 --- a/Sources/AllInApp/AllIn/Views/DetailsView.swift +++ b/Sources/AllInApp/AllIn/Views/DetailsView.swift @@ -6,6 +6,9 @@ struct DetailsView: View { @Binding var isModalPresented: Bool @Binding var isModalParticipated: Bool @State var progressValue: Float = 0.2 + var id: String + @StateObject private var viewModel: DetailsViewModel + var isFinished: Bool { viewModel.betDetail?.finalAnswer == nil ? false : true } @@ -13,13 +16,13 @@ struct DetailsView: View { var isDisabled: Bool { if let endRegisterDate = viewModel.betDetail?.bet.endRegisterDate { let currentDate = Date() - + switch currentDate.compare(endRegisterDate) { - case .orderedAscending: + case .orderedAscending: return false - case .orderedDescending: + case .orderedDescending: return true - case .orderedSame: + case .orderedSame: return true } @@ -31,13 +34,13 @@ struct DetailsView: View { var StatusValues: (String, Color) { if let endRegisterDate = viewModel.betDetail?.bet.endRegisterDate { let currentDate = Date() - + switch currentDate.compare(endRegisterDate) { - case .orderedAscending: + case .orderedAscending: return ("En cours...", AllInColors.purpleAccentColor) - case .orderedDescending: + case .orderedDescending: return ("En attente...",AllInColors.pink100) - case .orderedSame: + case .orderedSame: return ("Fin des inscriptions...",AllInColors.grey50Color) } @@ -45,11 +48,8 @@ struct DetailsView: View { return ("Statut indisponible", AllInColors.whiteColor) } } - - var id: String - @StateObject private var viewModel: DetailsViewModel - init(isModalPresented: Binding, isModalParticipated: Binding,id: String) { + init(isModalPresented: Binding, isModalParticipated: Binding, id: String) { self._isModalPresented = isModalPresented self._isModalParticipated = isModalParticipated self.id = id @@ -61,7 +61,11 @@ struct DetailsView: View { ZStack(alignment: .bottom) { VStack(alignment: .center) { HStack{ - Text(StatusValues.0).font(.system(size: 25)).fontWeight(.bold).padding(.bottom, 10).foregroundStyle(Color.black).opacity(0.4) + Text(StatusValues.0) + .font(.system(size: 25)) + .fontWeight(.bold).padding(.bottom, 10) + .foregroundStyle(Color.black) + .opacity(0.4) Spacer() Image("closeIcon") .resizable() @@ -75,6 +79,7 @@ struct DetailsView: View { .padding(.horizontal, 15) .background(StatusValues.1) .transition(.slideInFromBottom(yOffset:0)) + VStack(spacing: 0) { VStack(alignment: .leading,spacing: 5){ HStack{ @@ -107,18 +112,20 @@ struct DetailsView: View { .foregroundColor(AllInColors.grey800Color) TextCapsule(date: viewModel.betDetail?.bet.endBetDate ?? Date()) Spacer() - + } } .frame(width: .infinity) .padding(.all,15).padding(.vertical, 10) .background(AllInColors.componentBackgroundColor) .cornerRadius(20, corners: [.topLeft,.topRight]).padding(.bottom,0) + if isFinished { ResultBanner() } VStack(alignment: .leading, spacing: 5) { - BetLineLoading(participations: viewModel.betDetail?.participations ?? []).padding(.vertical,15) + BetLineLoading(participations: viewModel.betDetail?.participations ?? []) + .padding(.vertical,15) Text("Liste des participants") .font(.system(size: 18)) .foregroundStyle(AllInColors.grey100Color) @@ -135,7 +142,7 @@ struct DetailsView: View { .background(AllInColors.underComponentBackgroundColor) .border(width: 1, edges: [.top], color: AllInColors.delimiterGrey) Spacer() - + } @@ -143,12 +150,15 @@ struct DetailsView: View { .background(AllInColors.componentBackgroundColor) .cornerRadius(15) - ParticipateButton(isOpen: $isModalPresented, isParticapatedOpen: $isModalParticipated,bet: viewModel.betDetail?.bet ).padding(10) - - + ParticipateButton(isOpen: $isModalPresented, isParticapatedOpen: $isModalParticipated, bet: viewModel.betDetail?.bet) + .padding(10) } .sheet(isPresented: $isModalParticipated) { - ParticipationModal().presentationDetents([.fraction(0.55)]) + ParticipationModal(answer: $viewModel.answer, mise: $viewModel.mise, description: viewModel.betDetail?.bet.phrase ?? "Not loaded", participationAddedCallback: { + viewModel.addParticipate() + isModalParticipated.toggle() + }) + .presentationDetents([.fraction(0.55)]) } .edgesIgnoringSafeArea(.bottom) } diff --git a/Sources/Api/Sources/Api/Factory/FactoryApiBet.swift b/Sources/Api/Sources/Api/Factory/FactoryApiBet.swift index 0a4931c..555d852 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.isPublic), - "response": [], + "response": ["Yes","No"], ] return json @@ -37,7 +37,7 @@ public class FactoryApiBet: FactoryBet { let date = formatter.date(from: dateString) return date } - + public func toBet(from json: [String: Any]) -> Bet? { guard let id = json["id"] as? String, let theme = json["theme"] as? String, @@ -76,6 +76,28 @@ public class FactoryApiBet: FactoryBet { return nil } - return BetDetail(bet: bet, answers: [], participations: []) + var participations: [Participation] = [] + + if let participationsJson = json["participations"] as? [[String: Any]], !participationsJson.isEmpty { + for participationJson in participationsJson { + if let participation = self.toParticipate(from: participationJson) { + participations.append(participation) + } + } + } + + return BetDetail(bet: bet, answers: [], participations: participations) + } + + public func toParticipate(from json: [String: Any]) -> Participation? { + guard let id = json["id"] as? String, + let betId = json["betId"] as? String, + let username = json["username"] as? String, + let answer = json["answer"] as? String, + let stake = json["stake"] as? Int else { + return nil + } + + return Participation(id: id, stake: stake, date: Date(), response: answer, user: User(username: username, email: "Email", nbCoins: 0, friends: []), betId: betId) } } diff --git a/Sources/Api/Sources/Api/UserApiManager.swift b/Sources/Api/Sources/Api/UserApiManager.swift index eecc76b..cea22c2 100644 --- a/Sources/Api/Sources/Api/UserApiManager.swift +++ b/Sources/Api/Sources/Api/UserApiManager.swift @@ -49,4 +49,27 @@ public struct UserApiManager: UserDataManager { public func getOldBets(withIndex index: Int, withCount count: Int, completion: @escaping ([Bet]) -> Void) { fatalError("Not implemented yet") } + + public func addParticipation(withId id: String, withAnswer answer: String, andStake stake: Int) { + let url = URL(string: allInApi + "participations/add")! + var request = URLRequest(url: url) + request.httpMethod = "POST" + request.setValue("application/json", forHTTPHeaderField: "Content-Type") + request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization") + + let json: [String: Any] = [ + "betId": id, + "answer": answer, + "stake": stake + ] + + if let jsonData = try? JSONSerialization.data(withJSONObject: json, options: []){ + URLSession.shared.uploadTask(with: request, from: jsonData) { data, response, error in + print ("ALLIN : Add Participation") + if let httpResponse = response as? HTTPURLResponse { + print(httpResponse.statusCode) + } + }.resume() + } + } } diff --git a/Sources/Model/Sources/Model/Manager.swift b/Sources/Model/Sources/Model/Manager.swift index 06392df..cc4e25e 100644 --- a/Sources/Model/Sources/Model/Manager.swift +++ b/Sources/Model/Sources/Model/Manager.swift @@ -37,4 +37,8 @@ public struct Manager { completion(bets) } } + + public func addParticipation(withId id: String, withAnswer answer: String, andStake stake: Int) { + userDataManager.addParticipation(withId: id, withAnswer: answer, andStake: stake) + } } diff --git a/Sources/Model/Sources/Model/Participation.swift b/Sources/Model/Sources/Model/Participation.swift index f8e7058..c435ff9 100644 --- a/Sources/Model/Sources/Model/Participation.swift +++ b/Sources/Model/Sources/Model/Participation.swift @@ -10,7 +10,7 @@ import Foundation /// A class representing a user's participation in a bet. public class Participation: ObservableObject, Identifiable { - public let id: UUID + public let id: String /// The amount of stake in the bet. public private(set) var stake: Int @@ -34,12 +34,12 @@ public class Participation: ObservableObject, Identifiable { /// - response: The response or outcome of the participation. /// - user: The user who participated in the bet. /// - betId: The unique identifier of the bet. - public init(stake: Int, date: Date, response: String, user: User, betId: String) { + public init(id: String, stake: Int, date: Date, response: String, user: User, betId: String) { + self.id = id self.stake = stake self.date = date self.response = response self.user = user self.betId = betId - self.id = UUID() } } diff --git a/Sources/Model/Sources/Model/UserDataManager.swift b/Sources/Model/Sources/Model/UserDataManager.swift index 487cad9..87eb2a6 100644 --- a/Sources/Model/Sources/Model/UserDataManager.swift +++ b/Sources/Model/Sources/Model/UserDataManager.swift @@ -12,4 +12,5 @@ public protocol UserDataManager { func addBet(bet: Bet) func getFriends() -> [User] func getOldBets(withIndex index: Int, withCount count: Int, completion: @escaping ([Bet]) -> Void) + func addParticipation(withId id: String, withAnswer answer: String, andStake stake: Int) } diff --git a/Sources/StubLib/Sources/StubLib/Stub.swift b/Sources/StubLib/Sources/StubLib/Stub.swift index 09618bf..5371b6c 100644 --- a/Sources/StubLib/Sources/StubLib/Stub.swift +++ b/Sources/StubLib/Sources/StubLib/Stub.swift @@ -89,10 +89,10 @@ struct Stub { self.betsDetail.append(betDetail) } - self.betsDetail[0].addParticipation(newParticipation: Participation(stake: 120, date: Date(), response: "OUI", user: user1, betId: "1")) - self.betsDetail[0].addParticipation(newParticipation: Participation(stake: 20, date: Date(), response: "NON", user: user2, betId: "2")) - self.betsDetail[0].addParticipation(newParticipation: Participation(stake: 320, date: Date(), response: "OUI", user: user3, betId: "3")) - self.betsDetail[0].addParticipation(newParticipation: Participation(stake: 320, date: Date(), response: "OUI", user: user3, betId: "3")) + self.betsDetail[0].addParticipation(newParticipation: Participation(id: UUID().uuidString, stake: 120, date: Date(), response: "OUI", user: user1, betId: "1")) + self.betsDetail[0].addParticipation(newParticipation: Participation(id: UUID().uuidString, stake: 20, date: Date(), response: "NON", user: user2, betId: "2")) + self.betsDetail[0].addParticipation(newParticipation: Participation(id: UUID().uuidString, stake: 320, date: Date(), response: "OUI", user: user3, betId: "3")) + self.betsDetail[0].addParticipation(newParticipation: Participation(id: UUID().uuidString, stake: 320, date: Date(), response: "OUI", user: user3, betId: "3")) }