Compare commits

...

12 Commits

@ -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
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

@ -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
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

@ -11,12 +11,12 @@ import Model
struct BetLineLoading: View { struct BetLineLoading: View {
@State var showInfos: Bool = false @State var showInfos: Bool = false
var participations: [Participation] var bet: BetDetail
var value: CGFloat { var value: CGFloat {
let totalParticipations = participations.count let totalParticipations = bet.participations.count
let numberOfYes = participations.filter { $0.answer.uppercased() == "YES" }.count let numberOfYes = bet.participations.filter { $0.answer.uppercased() == "YES" }.count
let numberOfNo = participations.filter { $0.answer.uppercased() == "NO" }.count let numberOfNo = bet.participations.filter { $0.answer.uppercased() == "NO" }.count
if(numberOfNo == 0 && numberOfYes == 0){ if(numberOfNo == 0 && numberOfYes == 0){
return 0.5 return 0.5
} }
@ -25,143 +25,21 @@ struct BetLineLoading: View {
} }
var yesParticipations: [Participation] { var yesParticipations: [Participation] {
participations.filter { $0.answer.uppercased() == "YES" } bet.participations.filter { $0.answer.uppercased() == "YES" }
} }
var noParticipations: [Participation] { var noParticipations: [Participation] {
participations.filter { $0.answer.uppercased() == "NO" } bet.participations.filter { $0.answer.uppercased() == "NO" }
} }
var body: some View { var body: some View {
VStack(alignment: .leading, spacing: 0) { switch bet.bet {
HStack(spacing: 5) { case is BinaryBet:
Text("OUI") BinaryBetLine(bet: bet)
.font(.system(size: 25)) case is CustomBet:
.fontWeight(.bold) CustomBetLine(bet: bet)
.foregroundColor(AllInColors.blue200) default:
Spacer() BinaryBetLine(bet: bet)
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)
}
}
}
} }
} }
} }

@ -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-20), height: 17)
.foregroundStyle(AllInColors.PinkBetGradiant).cornerRadius(999)
}
HStack(spacing: 0) {
Rectangle()
.frame(width: min(CGFloat(self.value) * geometry.size.width, geometry.size.width-20), 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)
}
}
}
}
}
}

@ -15,22 +15,29 @@ struct ChoiceFinalAnswerCell: View {
var answer: AnswerDetail var answer: AnswerDetail
var rawColor = AllInColors.blueAccentColor var rawColor = AllInColors.blueAccentColor
var body: some View { var body: some View {
ZStack{ ZStack {
HStack{ HStack {
Spacer() Spacer()
Text(answer.response) 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() Spacer()
} }
HStack{ HStack {
Spacer() 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,
}.background(selected ? AllInColors.purpleAccentColor : .white).cornerRadius(17).scaleEffect(selected ? 1.02 : 1).animation( foregroundColor: selected ? AllInColors.purpleAccentColor : .white,
.easeInOut(duration: 0.3), odd: answer.odds
value: selected
) )
.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)
} }
} }

@ -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 ? "pinkFlameIcon" : "blueFlameIcon"
}
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-20), 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()
}
}
}
}
}
}
}

@ -1,12 +1,3 @@
//
// DropDownAnswerMenu.swift
// AllIn
//
// Created by Lucas Delanier on 16/01/2024.
//
import SwiftUI
// //
// DropDownMenu.swift // DropDownMenu.swift
// AllIn // AllIn
@ -15,20 +6,21 @@ import SwiftUI
// //
import SwiftUI import SwiftUI
import Model
struct DropDownAnswerMenu: View { struct DropDownAnswerMenu: View {
@State var expand = false @State var expand = false
@Binding var selectedOption: Int @Binding var selectedAnswer: AnswerDetail
var options: [(Int, String, Float)] var answers: [AnswerDetail]
var body: some View { var body: some View {
VStack(spacing: 0, content: { VStack(spacing: 0, content: {
Button(action: { self.expand.toggle() }) { Button(action: { withTransaction(Transaction(animation: nil)) { self.expand.toggle() } }) {
HStack{ HStack{
Text(options[selectedOption].1.description) Text(selectedAnswer.response)
.textStyle(weight: .bold, color: AllInColors.blueAccentColor, size: 20) .textStyle(weight: .bold, color: AllInColors.blueAccentColor, size: 20)
Text(options[selectedOption].2.description) Text("\(selectedAnswer.odds, specifier: "%.2f")")
.textStyle(weight: .bold, color: AllInColors.lightPurpleColor, size: 10) .textStyle(weight: .bold, color: AllInColors.lightPurpleColor, size: 10)
Spacer() Spacer()
@ -43,14 +35,16 @@ struct DropDownAnswerMenu: View {
.foregroundColor(AllInColors.delimiterGrey) .foregroundColor(AllInColors.delimiterGrey)
.padding(.bottom, 18) .padding(.bottom, 18)
VStack(spacing: 0) { VStack(spacing: 0) {
ForEach(0..<options.count, id: \.self) { index in ForEach(answers) { (answer: AnswerDetail) in
if options[index].0 != selectedOption { if answer != selectedAnswer {
Button(action: {self.selectedOption = options[index].0 Button(action: {
self.expand.toggle()}) { self.selectedAnswer = answer
self.expand.toggle()}
) {
HStack{ HStack{
Text(options[index].1.description) Text(answer.response)
.textStyle(weight: .bold, color: AllInColors.blueAccentColor, size: 20) .textStyle(weight: .bold, color: AllInColors.blueAccentColor, size: 20)
Text(options[index].2.description) Text("\(answer.odds, specifier: "%.2f")")
.textStyle(weight: .bold, color: AllInColors.lightPurpleColor, size: 10) .textStyle(weight: .bold, color: AllInColors.lightPurpleColor, size: 10)
Spacer() Spacer()
} }
@ -70,15 +64,3 @@ struct DropDownAnswerMenu: View {
) )
} }
} }
struct DropDownAnswerMenu_Previews: PreviewProvider {
static var previews: some View {
DropDownAnswerMenu(selectedOption: .constant(0), options: [
(0, "questionMarkIcon", 1.2),
(1, "footballIcon", 2.2),
(2, "paintbrushIcon", 3.3)
])
.preferredColorScheme(.dark)
}
}

@ -12,7 +12,6 @@ struct Menu: View {
@Inject var authService: IAuthService @Inject var authService: IAuthService
let parameters: [(String, String, String, String)] = [ let parameters: [(String, String, String, String)] = [
("videoGameImage", String(localized: "drawer_create_a_bet"), String(localized: "drawer_create_a_bet_subtitle"), "CreationBet"), ("videoGameImage", String(localized: "drawer_create_a_bet"), String(localized: "drawer_create_a_bet_subtitle"), "CreationBet"),
("globeImage", String(localized: "drawer_public_bets"), String(localized: "drawer_public_bets_subtitle"), "Bet"), ("globeImage", String(localized: "drawer_public_bets"), String(localized: "drawer_public_bets_subtitle"), "Bet"),
@ -40,7 +39,7 @@ struct Menu: View {
HStack(spacing: 30) { HStack(spacing: 30) {
Spacer() Spacer()
VStack(){ VStack(){
Text(AppStateContainer.shared.user?.bestWin.description ?? "0") Text(AppStateContainer.shared.user?.bestWin ?? 0, format: .number)
.fontWeight(.heavy) .fontWeight(.heavy)
.font(.system(size: 15)) .font(.system(size: 15))
.foregroundColor(.white) .foregroundColor(.white)
@ -50,7 +49,7 @@ struct Menu: View {
.foregroundColor(AllInColors.grey600Color) .foregroundColor(AllInColors.grey600Color)
} }
VStack(){ VStack(){
Text(AppStateContainer.shared.user?.bestWin.description ?? "0") Text(AppStateContainer.shared.user!.bestWin, format: .number)
.fontWeight(.heavy) .fontWeight(.heavy)
.font(.system(size: 15)) .font(.system(size: 15))
.foregroundColor(.white) .foregroundColor(.white)
@ -60,7 +59,7 @@ struct Menu: View {
.foregroundColor(AllInColors.grey600Color) .foregroundColor(AllInColors.grey600Color)
} }
VStack(){ VStack(){
Text(AppStateContainer.shared.user?.nbFriends.description ?? "0") Text(AppStateContainer.shared.user!.nbFriends, format: .number)
.fontWeight(.heavy) .fontWeight(.heavy)
.font(.system(size: 15)) .font(.system(size: 15))
.foregroundColor(.white) .foregroundColor(.white)
@ -104,6 +103,9 @@ struct Menu: View {
.frame(maxWidth: .infinity,alignment: .leading) .frame(maxWidth: .infinity,alignment: .leading)
.background(AllInColors.primaryColor) .background(AllInColors.primaryColor)
.edgesIgnoringSafeArea(.bottom) .edgesIgnoringSafeArea(.bottom)
.onAppear {
authService.refreshAuthentication()
}
} }
} }

@ -14,7 +14,7 @@ struct OddCapsule: View {
var odd: Float = 0.0 var odd: Float = 0.0
var body: some View { var body: some View {
HStack(alignment: .center) { HStack(alignment: .center) {
Text("x\(odd.description)") Text("x\(odd, specifier: "%.2f")")
.fontWeight(.bold) .fontWeight(.bold)
.foregroundColor(foregroundColor) .foregroundColor(foregroundColor)
} }

@ -6,33 +6,25 @@
// //
import SwiftUI import SwiftUI
import Model
struct ParticipationModal: View { struct ParticipationModal: View {
@Binding private var selectedOption: Int @Binding var selectedAnswer: AnswerDetail
@Binding private var mise: String @Binding var mise: String
private var description: String var phrase: String
var participationAddedCallback: (() -> Void)? var answers: [AnswerDetail]
var participationAddedCallback: () -> Void
var checkAndSetError: () -> Bool
var possibleGain: Int { var possibleGain: Int {
if let stake = Float(mise), let selectedOption = options.first(where: { $0.0 == self.selectedOption }) { if let stake = Float(mise) {
return Int(round(stake * selectedOption.2)) return Int(round(stake * selectedAnswer.odds))
} else { } else {
return 0 return 0
} }
} }
init(answer: Binding<Int>, mise: Binding<String>, 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 { var body: some View {
GeometryReader { geometry in GeometryReader { geometry in
VStack(alignment: .leading){ VStack(alignment: .leading){
@ -56,12 +48,12 @@ struct ParticipationModal: View {
} }
.padding(.leading, 15) .padding(.leading, 15)
VStack(alignment: .leading){ VStack(alignment: .leading){
Text(description) Text(phrase)
.font(.system(size: 13)) .font(.system(size: 13))
.foregroundColor(AllInColors.primaryTextColor) .foregroundColor(AllInColors.primaryTextColor)
.fontWeight(.light) .fontWeight(.light)
DropDownAnswerMenu(selectedOption: $selectedOption, options: options) DropDownAnswerMenu(selectedAnswer: $selectedAnswer, answers: answers)
TextField("", text: $mise, prompt: Text("generic_stake") TextField("", text: $mise, prompt: Text("generic_stake")
.foregroundColor(AllInColors.lightGrey300Color) .foregroundColor(AllInColors.lightGrey300Color)
@ -104,7 +96,7 @@ struct ParticipationModal: View {
.padding(.top, 10) .padding(.top, 10)
.padding(.bottom, 0) .padding(.bottom, 0)
Button { Button {
participationAddedCallback?() participationAddedCallback()
} label: { } label: {
Text("Miser") Text("Miser")
.font(.system(size: 23)) .font(.system(size: 23))
@ -125,16 +117,4 @@ struct ParticipationModal: View {
.background(AllInColors.underComponentBackgroundColor) .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
}
}
} }

@ -73,7 +73,7 @@ struct RecapBetCard: View {
.overlay { .overlay {
AllInColors.primaryGradient.frame(width: 50) AllInColors.primaryGradient.frame(width: 50)
.mask( .mask(
Text("1630").font(.system(size: 15)).fontWeight(.medium) Text(betResult.amount.description).font(.system(size: 15)).fontWeight(.medium)
) )
} }
.padding(0) .padding(0)
@ -89,7 +89,8 @@ struct RecapBetCard: View {
HStack{ HStack{
Text("Côte totale").font(.system(size: 15)).fontWeight(.medium) Text("Côte totale").font(.system(size: 15)).fontWeight(.medium)
Spacer() Spacer()
Text("3,46") // TODO bind le odd
Text("1,0")
.textStyle(weight: .bold, color: .white, size: 18) .textStyle(weight: .bold, color: .white, size: 18)
.padding([.leading,.trailing],10) .padding([.leading,.trailing],10)
.padding([.top,.bottom],5) .padding([.top,.bottom],5)

@ -19,16 +19,16 @@ struct ResultBanner: View {
}.frame(height: 80) }.frame(height: 80)
HStack(spacing: 20){ HStack(spacing: 20){
HStack{ HStack{
Image("BlueAllCoinIcon").resizable().frame(maxWidth: 12, maxHeight: 12) Image("blueAllCoinIcon").resizable().frame(maxWidth: 12, maxHeight: 12)
Text(finalAnswer.stake.description).font(.system(size: 16)).fontWeight(.semibold).foregroundStyle(AllInColors.blueGrey800Color) Text(finalAnswer.stake.description).font(.system(size: 16)).fontWeight(.semibold).foregroundStyle(AllInColors.blueGrey800Color)
} }
HStack{ HStack{
Image("BleuePersonIcon").resizable().frame(maxWidth: 15, maxHeight: 12) Image("bluePersonIcon").resizable().frame(maxWidth: 15, maxHeight: 12)
Text(finalAnswer.username).font(.system(size: 16)).fontWeight(.semibold).foregroundStyle(AllInColors.blueGrey800Color) Text(finalAnswer.username).font(.system(size: 16)).fontWeight(.semibold).foregroundStyle(AllInColors.blueGrey800Color)
} }
HStack{ HStack{
Image("BleueTrophyIcon").resizable().frame(maxWidth: 15, maxHeight: 12) Image("blueTrophyIcon").resizable().frame(maxWidth: 15, maxHeight: 12)
Text(odds.description).font(.system(size: 16)).fontWeight(.semibold).foregroundStyle(AllInColors.blueGrey800Color) Text("\(odds, specifier: "%.2f")").font(.system(size: 16)).fontWeight(.semibold).foregroundStyle(AllInColors.blueGrey800Color)
} }
} }
} }

@ -42,6 +42,7 @@ struct ReviewCard: View {
VStack(alignment: .center,spacing:0){ VStack(alignment: .center,spacing:0){
HStack(){ HStack(){
Spacer() Spacer()
if bet.status == .finished {
Text(amount.description) Text(amount.description)
.foregroundColor(.white) .foregroundColor(.white)
.font(.system(size: 25)) .font(.system(size: 25))
@ -49,6 +50,7 @@ struct ReviewCard: View {
Image("allcoinWhiteIcon") Image("allcoinWhiteIcon")
.resizable() .resizable()
.frame(width: 18, height: 20) .frame(width: 18, height: 20)
}
switch bet.status { switch bet.status {
case .waiting, .inProgress: case .waiting, .inProgress:
@ -82,9 +84,8 @@ struct ReviewCard: View {
.frame(width: .infinity) .frame(width: .infinity)
.padding(.all,2) .padding(.all,2)
.background(backgroundColor()) .background(backgroundColor())
.cornerRadius(20, corners: [.bottomLeft,.bottomRight]) .cornerRadius(20, corners: [.bottomLeft,.bottomRight])
.border(width: 1, edges: [.top], color: AllInColors.delimiterGrey)
} }
.onTapGesture { .onTapGesture {
showDetails.toggle() showDetails.toggle()
@ -93,14 +94,20 @@ struct ReviewCard: View {
} }
} }
private func backgroundColor() -> Color { private func backgroundColor() -> some View {
Group {
if bet.status == .finished && isWin {
AllInColors.primaryGradient
} else {
switch bet.status { switch bet.status {
case .inProgress, .waiting, .closing: case .inProgress, .waiting, .closing:
return AllInColors.grey50Color AllInColors.grey50Color
case .finished: case .finished:
return Color.black Color.black
case .cancelled: case .cancelled:
return Color.red Color.red
}
}
} }
} }
} }

@ -77,6 +77,12 @@ struct AllInColors {
endPoint: .trailing endPoint: .trailing
) )
static let BlueBetLineGradiant = LinearGradient(
gradient: Gradient(colors: [AllInColors.blue200, AllInColors.blueAccentColor]),
startPoint: .leading,
endPoint: .trailing
)
static let PinkBetGradiant = LinearGradient( static let PinkBetGradiant = LinearGradient(
gradient: Gradient(colors: [AllInColors.pink100, AllInColors.pink200]), gradient: Gradient(colors: [AllInColors.pink100, AllInColors.pink200]),
startPoint: .leading, startPoint: .leading,

@ -178,7 +178,7 @@
/// Notification /// Notification
"notification_title_end_bet_date" = "Who will be the winners?"; "notification_title_end_bet_date" = "Who will be the winners?";
"notification_subtitle_end_bet_date %@" = "The %@ bet has reached its deadline. Go to the app to enter the winning answer.";` "notification_subtitle_end_bet_date %@" = "The %@ bet has reached its deadline. Go to the app to enter the winning answer.";
/// Empty Views /// Empty Views
@ -187,3 +187,8 @@
"empty_bets_title"= "No bet matches your search"; "empty_bets_title"= "No bet matches your search";
"empty_friends_title" = "You don't have any friends yet"; "empty_friends_title" = "You don't have any friends yet";
"empty_friends_explain" = "Add them from this screen"; "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";

@ -28,6 +28,7 @@
"network_error_text" = "Assurez-vous d\'être bien connecté au réseau puis réessayez."; "network_error_text" = "Assurez-vous d\'être bien connecté au réseau puis réessayez.";
/// Bet type /// Bet type
"bet_type_binary" = "Oui / Non"; "bet_type_binary" = "Oui / Non";
"bet_type_match" = "Match sportif"; "bet_type_match" = "Match sportif";
"bet_type_custom" = "Réponses personnalisées"; "bet_type_custom" = "Réponses personnalisées";
@ -186,3 +187,8 @@
"empty_bets_title"= "Aucun Bet ne correspond à votre recherche"; "empty_bets_title"= "Aucun Bet ne correspond à votre recherche";
"empty_friends_title" = "Vous navez pas encore damis"; "empty_friends_title" = "Vous navez pas encore damis";
"empty_friends_explain" = "Ajoutez les depuis cet écran"; "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.";

@ -13,7 +13,7 @@ import Combine
class BetViewModel: ObservableObject { class BetViewModel: ObservableObject {
@Inject var manager: Manager @Inject var manager: Manager
@Inject var authService: IAuthService
@Published var popularBet: Bet? @Published var popularBet: Bet?
@Published private(set) var bets: [Bet] = [] @Published private(set) var bets: [Bet] = []
@Published var betsOver: [BetDetail] = [] @Published var betsOver: [BetDetail] = []
@ -58,6 +58,7 @@ class BetViewModel: ObservableObject {
self.betsWon = bets self.betsWon = bets
if !self.betsWon.isEmpty { if !self.betsWon.isEmpty {
self.showingSheetWon = true self.showingSheetWon = true
self.authService.refreshAuthentication()
} }
} }
} }

@ -23,19 +23,36 @@ class CreationBetViewModel: ObservableObject {
@Published var endRegisterDate = Date() @Published var endRegisterDate = Date()
@Published var endBetDate = Date() @Published var endBetDate = Date()
@Published var betAdded = false @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<String> = [] @Published var invited: Set<String> = []
@Published var themeFieldError: String? @Published var themeFieldError: String?
@Published var descriptionFieldError: String? @Published var descriptionFieldError: String?
@Published var endRegisterDateFieldError: String? @Published var endRegisterDateFieldError: String?
@Published var endBetDateFieldError: String? @Published var endBetDateFieldError: String?
@Published var responsesFieldError: String?
@Published var errorMessage: String? @Published var errorMessage: String?
@Published var showErrorMessage = false @Published var showErrorMessage = false
@Published var friends: [User] = [] @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() { init() {
getFriends() getFriends()
} }
@ -50,14 +67,14 @@ class CreationBetViewModel: ObservableObject {
func create() { 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 return
} }
resetAllFieldErrors() resetAllFieldErrors()
if let user = AppStateContainer.shared.user { 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) print(statusCode)
switch statusCode { switch statusCode {
case 201: 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 newThemeFieldError: String?
var newDescriptionFieldError: String? var newDescriptionFieldError: String?
var newEndRegisterDateFieldError: String? var newEndRegisterDateFieldError: String?
var newEndBetDateFieldError: String? var newEndBetDateFieldError: String?
var newResponsesFieldError: String?
var hasError = false var hasError = false
@ -105,6 +123,19 @@ class CreationBetViewModel: ObservableObject {
hasError = true 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 { if !hasError {
// No error // No error
return true return true
@ -115,6 +146,7 @@ class CreationBetViewModel: ObservableObject {
descriptionFieldError = newDescriptionFieldError descriptionFieldError = newDescriptionFieldError
endRegisterDateFieldError = newEndRegisterDateFieldError endRegisterDateFieldError = newEndRegisterDateFieldError
endBetDateFieldError = newEndBetDateFieldError endBetDateFieldError = newEndBetDateFieldError
responsesFieldError = newResponsesFieldError
} }
return false return false
} }
@ -125,6 +157,7 @@ class CreationBetViewModel: ObservableObject {
descriptionFieldError = nil descriptionFieldError = nil
endRegisterDateFieldError = nil endRegisterDateFieldError = nil
endBetDateFieldError = nil endBetDateFieldError = nil
responsesFieldError = nil
} }
} }
@ -140,7 +173,11 @@ class CreationBetViewModel: ObservableObject {
case 1: case 1:
return MatchBet(theme: theme, phrase: description, endRegisterDate: endRegister, endBetDate: endBet, isPrivate: isPrivate, status: status, invited: invited, author: creator, nameTeam1: "", nameTeam2: "") return MatchBet(theme: theme, phrase: description, endRegisterDate: endRegister, endBetDate: endBet, isPrivate: isPrivate, status: status, invited: invited, author: creator, nameTeam1: "", nameTeam2: "")
case 2: 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: default:
return BinaryBet(theme: theme, phrase: description, endRegisterDate: endRegister, endBetDate: endBet, isPrivate: isPrivate, status: status, invited: invited, author: creator) return BinaryBet(theme: theme, phrase: description, endRegisterDate: endRegister, endBetDate: endBet, isPrivate: isPrivate, status: status, invited: invited, author: creator)
} }

@ -15,7 +15,7 @@ class DetailsViewModel: ObservableObject {
@Inject var manager: Manager @Inject var manager: Manager
var id: String 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 mise: String = ""
@Published var betDetail: BetDetail? @Published var betDetail: BetDetail?
@ -26,25 +26,26 @@ class DetailsViewModel: ObservableObject {
} }
func getItem(withId id: String) { func getItem(withId id: String) {
let semaphore = DispatchSemaphore(value: 0)
manager.getBet(withId: id) { bet in manager.getBet(withId: id) { bet in
DispatchQueue.main.async {
for par in bet.participations {
print(par.id)
}
self.betDetail = bet self.betDetail = bet
if let firstAnswer = bet.answers.first {
self.selectedAnswer = firstAnswer
}
semaphore.signal()
} }
let result = semaphore.wait(timeout: DispatchTime.now() + .seconds(2))
if result == .timedOut {
print("The request has exceeded the deadline")
return
} }
} }
func addParticipate() { func addParticipate() {
if let stake = Int(mise) { if let stake = Int(mise) {
var rep: String = "" manager.addParticipation(withId: id, withAnswer: selectedAnswer.response, andStake: stake) { statusCode in
if answer == 0 {
rep = "Yes"
} else {
rep = "No"
}
manager.addParticipation(withId: id, withAnswer: rep, andStake: stake) { statusCode in
switch statusCode { switch statusCode {
case 201: case 201:
AppStateContainer.shared.user?.nbCoins -= stake AppStateContainer.shared.user?.nbCoins -= stake
@ -57,10 +58,20 @@ class DetailsViewModel: ObservableObject {
} }
} }
mise = "" 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
}
} }
} }

@ -28,17 +28,6 @@ struct CreationBetView: View {
}() }()
let screenWidth = UIScreen.main.bounds.width 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() { private func updateGroupedItems() {
var updatedGroupedItems: [[String]] = [[String]] () var updatedGroupedItems: [[String]] = [[String]] ()
@ -46,7 +35,7 @@ struct CreationBetView: View {
var width: CGFloat = 0 var width: CGFloat = 0
var dynamicWidthLimit: CGFloat var dynamicWidthLimit: CGFloat
for value in values { for value in viewModel.values {
let label = UILabel() let label = UILabel()
label.text = value label.text = value
label.sizeToFit() label.sizeToFit()
@ -65,7 +54,7 @@ struct CreationBetView: View {
} }
updatedGroupedItems.append(tempItems) updatedGroupedItems.append(tempItems)
groupedItems = updatedGroupedItems viewModel.groupedItems = updatedGroupedItems
} }
var body: some View { var body: some View {
@ -108,6 +97,7 @@ struct CreationBetView: View {
.foregroundColor(AllInColors.lightGrey300Color) .foregroundColor(AllInColors.lightGrey300Color)
.font(.system(size: 14)) .font(.system(size: 14))
.fontWeight(.light)) .fontWeight(.light))
.autocorrectionDisabled(true)
.padding() .padding()
.background( .background(
RoundedRectangle(cornerRadius: 9) RoundedRectangle(cornerRadius: 9)
@ -151,6 +141,7 @@ struct CreationBetView: View {
.foregroundColor(AllInColors.lightGrey300Color) .foregroundColor(AllInColors.lightGrey300Color)
.font(.system(size: 14)) .font(.system(size: 14))
.fontWeight(.light), axis: .vertical) .fontWeight(.light), axis: .vertical)
.autocorrectionDisabled(true)
.lineLimit(4, reservesSpace: true) .lineLimit(4, reservesSpace: true)
.padding() .padding()
.background( .background(
@ -360,14 +351,14 @@ struct CreationBetView: View {
VStack(spacing: 5) { VStack(spacing: 5) {
VStack() { VStack() {
DropDownMenu(selectedOption: $viewModel.selectedOption, options: options) DropDownMenu(selectedOption: $viewModel.selectedTypeBet, options: viewModel.options)
} }
.padding([.bottom], 15) .padding([.bottom], 15)
.frame(width: 340) .frame(width: 340)
Group { Group {
switch viewModel.selectedOption { switch viewModel.selectedTypeBet {
case 0: case 0:
Text("bet_creation_yes_no_bottom_text_1") Text("bet_creation_yes_no_bottom_text_1")
.textStyle(weight: .bold, color: AllInColors.veryLightPurpleColor, size: 13) .textStyle(weight: .bold, color: AllInColors.veryLightPurpleColor, size: 13)
@ -385,12 +376,18 @@ struct CreationBetView: View {
.textStyle(weight: .bold, color: AllInColors.veryLightPurpleColor, size: 13) .textStyle(weight: .bold, color: AllInColors.veryLightPurpleColor, size: 13)
.padding(.bottom, 15) .padding(.bottom, 15)
if let responseError = $viewModel.responsesFieldError.wrappedValue {
Text(responseError)
.textStyle(weight: .bold, color: .red, size: 10)
}
VStack(spacing: 5) { VStack(spacing: 5) {
HStack(spacing: 0) { 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) .foregroundColor(AllInColors.lightGrey200Color)
.font(.system(size: 16)) .font(.system(size: 16))
.fontWeight(.medium)) .fontWeight(.medium))
.autocorrectionDisabled(true)
.padding() .padding()
.background( .background(
Rectangle() Rectangle()
@ -400,17 +397,17 @@ struct CreationBetView: View {
) )
.frame(width: 250, height: 38) .frame(width: 250, height: 38)
.foregroundColor(.black) .foregroundColor(.black)
.onChange(of: response) { newValue in .onChange(of: viewModel.response) { newValue in
if newValue.count > 20 { if newValue.count > 20 {
response = String(newValue.prefix(20)) viewModel.response = String(newValue.prefix(20))
} }
} }
Button(action: { Button(action: {
if !response.isEmpty && values.count < 5 { if !viewModel.response.isEmpty && viewModel.values.count < 5 {
values.append(response) viewModel.values.append(viewModel.response)
updateGroupedItems() updateGroupedItems()
response = "" viewModel.response = ""
} }
}) { }) {
Text("generic_add") Text("generic_add")
@ -423,12 +420,12 @@ struct CreationBetView: View {
} }
HStack { HStack {
Spacer() 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) .textStyle(weight: .regular, color: AllInColors.primaryTextColor, size: 12)
} }
VStack(spacing: 10) { VStack(spacing: 10) {
ForEach(groupedItems, id: \.self) { items in ForEach(viewModel.groupedItems, id: \.self) { items in
HStack { HStack {
ForEach(items, id: \.self) { text in ForEach(items, id: \.self) { text in
HStack { HStack {
@ -436,8 +433,8 @@ struct CreationBetView: View {
.foregroundColor(.white) .foregroundColor(.white)
.lineLimit(1) .lineLimit(1)
Button(action: { Button(action: {
if let index = values.firstIndex(of: text) { if let index = viewModel.values.firstIndex(of: text) {
values.remove(at: index) viewModel.values.remove(at: index)
updateGroupedItems() updateGroupedItems()
} }
}) { }) {

@ -57,9 +57,9 @@ struct DetailsView: View {
} }
.padding(.horizontal, 15) .padding(.horizontal, 15)
.background(StatusValues.1) .background(StatusValues.1)
if viewModel.betDetail != nil{
VStack(spacing: 0) { ScrollView {
VStack(alignment: .leading, spacing: 5) { VStack(alignment: .leading, spacing: 0) {
HStack(spacing: 3) { HStack(spacing: 3) {
Spacer() Spacer()
Text("bet_proposed_by_format") Text("bet_proposed_by_format")
@ -105,50 +105,69 @@ struct DetailsView: View {
} }
} }
.padding(.all, 15) .padding(.all, 15)
.padding(.vertical, 10) .padding(.top, 6)
.border(width: 1, edges: [.bottom], color: AllInColors.delimiterGrey)
.background(AllInColors.componentBackgroundColor) .background(AllInColors.componentBackgroundColor)
.cornerRadius(20, corners: [.topLeft,.topRight]).padding(.bottom,0) .cornerRadius(20, corners: [.topLeft,.topRight])
if isFinished { if isFinished {
ResultBanner(finalAnswer: (viewModel.betDetail?.wonParticipation)!, odds: (viewModel.betDetail?.odds)!) ResultBanner(finalAnswer: (viewModel.betDetail?.wonParticipation)!, odds: (viewModel.betDetail?.odds)!)
} }
VStack(alignment: .leading, spacing: 5) { VStack(alignment: .leading, spacing: 0) {
BetLineLoading(participations: viewModel.betDetail?.participations ?? []) if let bet = viewModel.betDetail{
.padding(.vertical,15) BetLineLoading(bet: bet).padding(.vertical, 20)
}
Text("bet_status_participants_list") Text("bet_status_participants_list")
.font(.system(size: 18)) .font(.system(size: 18))
.foregroundStyle(AllInColors.grey100Color) .foregroundStyle(AllInColors.grey100Color)
.fontWeight(.bold) .fontWeight(.bold)
.padding(.bottom, 10) .padding(.bottom, 10)
ScrollView(showsIndicators: false) {
ForEach(viewModel.betDetail?.participations ?? []) { participation in ForEach(viewModel.betDetail?.participations ?? []) { participation in
// TODO
UserInfo(username: participation.username, picture: nil , value: participation.stake).padding(.horizontal, 10) UserInfo(username: participation.username, picture: nil , value: participation.stake).padding(.horizontal, 10)
} }
} .padding(.bottom)
.padding(.bottom, geometry.safeAreaInsets.bottom + 28)
Spacer() Spacer()
} }
.padding([.bottom,.trailing,.leading], 15) .padding([.trailing,.leading], 15)
.padding(.bottom, 100)
}
.frame(maxWidth: .infinity, maxHeight: (geometry.size.height + geometry.safeAreaInsets.bottom) - 50)
.background(AllInColors.underComponentBackgroundColor) .background(AllInColors.underComponentBackgroundColor)
.border(width: 1, edges: [.top], color: AllInColors.delimiterGrey) .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() Spacer()
}.opacity(0.6)
.padding(.vertical, 20)
} }
.frame(maxWidth: .infinity, maxHeight: (geometry.size.height + geometry.safeAreaInsets.bottom) - 50) .frame(maxWidth: .infinity, maxHeight: (geometry.size.height + geometry.safeAreaInsets.bottom) - 50)
.background(AllInColors.componentBackgroundColor) .background(AllInColors.underComponentBackgroundColor)
.cornerRadius(15, corners: [.topLeft, .topRight]) .cornerRadius(15, corners: [.topLeft, .topRight])
}
ParticipateButton(isOpen: $isModalPresented, isParticapatedOpen: $isModalParticipated, bet: viewModel.betDetail?.bet) ParticipateButton(isOpen: $isModalPresented, isParticapatedOpen: $isModalParticipated, bet: viewModel.betDetail?.bet)
.padding(.bottom, geometry.safeAreaInsets.bottom + 5) .padding(.bottom, geometry.safeAreaInsets.bottom + 5)
.padding(.horizontal, 10) .padding(.horizontal, 10)
} }
.sheet(isPresented: $isModalParticipated) { .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() viewModel.addParticipate()
isModalParticipated.toggle() isModalParticipated.toggle()
}) },
checkAndSetError: viewModel.checkAndSetError)
.presentationDetents([.fraction(0.55)]) .presentationDetents([.fraction(0.55)])
} }
.edgesIgnoringSafeArea(.bottom) .edgesIgnoringSafeArea(.bottom)

@ -64,6 +64,7 @@ struct FriendsView: View {
} }
.padding(.top, 50) .padding(.top, 50)
.tag(0) .tag(0)
ScrollView{
VStack(alignment: .center, spacing: 0) { VStack(alignment: .center, spacing: 0) {
if(viewModel.requests.isEmpty){ if(viewModel.requests.isEmpty){
EmptyInfo(emoji:"📬", title: "Aucune demande d'amis en attente", explain: "").padding(.top, 40) EmptyInfo(emoji:"📬", title: "Aucune demande d'amis en attente", explain: "").padding(.top, 40)
@ -81,6 +82,9 @@ struct FriendsView: View {
} }
Spacer() Spacer()
} }
}.refreshable {
viewModel.getRequests()
}
.padding(.top, 50) .padding(.top, 50)
.tag(1) .tag(1)
} }

@ -9,6 +9,8 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
120919182B56D0AE00D0FA29 /* ParticipationModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 120919172B56D0AE00D0FA29 /* ParticipationModal.swift */; }; 120919182B56D0AE00D0FA29 /* ParticipationModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 120919172B56D0AE00D0FA29 /* ParticipationModal.swift */; };
1209191A2B56DC6C00D0FA29 /* DropDownAnswerMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 120919192B56DC6C00D0FA29 /* DropDownAnswerMenu.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 */; }; 123225D92B67B46100D30BB3 /* BetEndingValidationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123225D82B67B46100D30BB3 /* BetEndingValidationView.swift */; };
123225DB2B67E41400D30BB3 /* ChoiceFinalAnswerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123225DA2B67E41400D30BB3 /* ChoiceFinalAnswerCell.swift */; }; 123225DB2B67E41400D30BB3 /* ChoiceFinalAnswerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123225DA2B67E41400D30BB3 /* ChoiceFinalAnswerCell.swift */; };
123590B42B51792000F7AEBD /* DetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123590B32B51792000F7AEBD /* DetailsView.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 = "<group>"; }; 120919192B56DC6C00D0FA29 /* DropDownAnswerMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropDownAnswerMenu.swift; sourceTree = "<group>"; };
122278B72B4BDE1100E632AA /* DependencyInjection.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DependencyInjection.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 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 = "<group>"; }; 122278B92B4BDE9500E632AA /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Package.swift; path = ../Model/Package.swift; sourceTree = "<group>"; };
12228A202C1760D2008524F4 /* BinaryBetLine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BinaryBetLine.swift; sourceTree = "<group>"; };
12228A222C176117008524F4 /* CustomBetLine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomBetLine.swift; sourceTree = "<group>"; };
123225D82B67B46100D30BB3 /* BetEndingValidationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BetEndingValidationView.swift; sourceTree = "<group>"; }; 123225D82B67B46100D30BB3 /* BetEndingValidationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BetEndingValidationView.swift; sourceTree = "<group>"; };
123225DA2B67E41400D30BB3 /* ChoiceFinalAnswerCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChoiceFinalAnswerCell.swift; sourceTree = "<group>"; }; 123225DA2B67E41400D30BB3 /* ChoiceFinalAnswerCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChoiceFinalAnswerCell.swift; sourceTree = "<group>"; };
123590B32B51792000F7AEBD /* DetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailsView.swift; sourceTree = "<group>"; }; 123590B32B51792000F7AEBD /* DetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailsView.swift; sourceTree = "<group>"; };
@ -444,6 +448,8 @@
129D051C2B6E7FF0003D3E08 /* OddCapsule.swift */, 129D051C2B6E7FF0003D3E08 /* OddCapsule.swift */,
12A9E4932C07132600AB8677 /* EmptyInfo.swift */, 12A9E4932C07132600AB8677 /* EmptyInfo.swift */,
123F31DA2C0F26E8009B6D65 /* userPicture.swift */, 123F31DA2C0F26E8009B6D65 /* userPicture.swift */,
12228A202C1760D2008524F4 /* BinaryBetLine.swift */,
12228A222C176117008524F4 /* CustomBetLine.swift */,
); );
path = Components; path = Components;
sourceTree = "<group>"; sourceTree = "<group>";
@ -675,6 +681,7 @@
files = ( files = (
EC6B96CC2B24B7E500FC1C58 /* IAuthService.swift in Sources */, EC6B96CC2B24B7E500FC1C58 /* IAuthService.swift in Sources */,
ECB26A172B4073F100FE06B3 /* CreationBetViewModel.swift in Sources */, ECB26A172B4073F100FE06B3 /* CreationBetViewModel.swift in Sources */,
12228A232C176117008524F4 /* CustomBetLine.swift in Sources */,
EC3077092B24CF7F0060E34D /* Colors.swift in Sources */, EC3077092B24CF7F0060E34D /* Colors.swift in Sources */,
EC6B969E2B24B4CC00FC1C58 /* ContentView.swift in Sources */, EC6B969E2B24B4CC00FC1C58 /* ContentView.swift in Sources */,
EC89F7BD2B250D66003821CE /* LoginView.swift in Sources */, EC89F7BD2B250D66003821CE /* LoginView.swift in Sources */,
@ -721,6 +728,7 @@
EC01937E2B25C52E005D81E6 /* TopBar.swift in Sources */, EC01937E2B25C52E005D81E6 /* TopBar.swift in Sources */,
ECA9D1CB2B2DA2320076E0EC /* DropDownFriends.swift in Sources */, ECA9D1CB2B2DA2320076E0EC /* DropDownFriends.swift in Sources */,
123590B82B5541BA00F7AEBD /* ParticipateButton.swift in Sources */, 123590B82B5541BA00F7AEBD /* ParticipateButton.swift in Sources */,
12228A212C1760D2008524F4 /* BinaryBetLine.swift in Sources */,
EC01FCC32B56650400BB2390 /* DetailsViewModel.swift in Sources */, EC01FCC32B56650400BB2390 /* DetailsViewModel.swift in Sources */,
ECB26A1B2B40746C00FE06B3 /* FriendsViewModel.swift in Sources */, ECB26A1B2B40746C00FE06B3 /* FriendsViewModel.swift in Sources */,
ECB7BC682B2F1ADF002A6654 /* LoginViewModel.swift in Sources */, ECB7BC682B2F1ADF002A6654 /* LoginViewModel.swift in Sources */,
@ -928,7 +936,7 @@
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"AllIn/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"AllIn/Preview Content\"";
DEVELOPMENT_TEAM = 35KQ5BDC64; DEVELOPMENT_TEAM = P39ZK4GA2T;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = "All In"; INFOPLIST_KEY_CFBundleDisplayName = "All In";
@ -962,7 +970,7 @@
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"AllIn/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"AllIn/Preview Content\"";
DEVELOPMENT_TEAM = 35KQ5BDC64; DEVELOPMENT_TEAM = P39ZK4GA2T;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = "All In"; INFOPLIST_KEY_CFBundleDisplayName = "All In";
@ -1066,7 +1074,7 @@
CODE_SIGN_ENTITLEMENTS = AllInWidgetsExtension.entitlements; CODE_SIGN_ENTITLEMENTS = AllInWidgetsExtension.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 35KQ5BDC64; DEVELOPMENT_TEAM = P39ZK4GA2T;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = AllInWidgets/Info.plist; INFOPLIST_FILE = AllInWidgets/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = AllInWidgets; INFOPLIST_KEY_CFBundleDisplayName = AllInWidgets;
@ -1094,7 +1102,7 @@
CODE_SIGN_ENTITLEMENTS = AllInWidgetsExtension.entitlements; CODE_SIGN_ENTITLEMENTS = AllInWidgetsExtension.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 35KQ5BDC64; DEVELOPMENT_TEAM = P39ZK4GA2T;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = AllInWidgets/Info.plist; INFOPLIST_FILE = AllInWidgets/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = AllInWidgets; INFOPLIST_KEY_CFBundleDisplayName = AllInWidgets;

@ -24,7 +24,7 @@ public class FactoryApiBet: FactoryBet {
"endRegistration": formatZonedDateTime(dateTime: bet.endRegisterDate), "endRegistration": formatZonedDateTime(dateTime: bet.endRegisterDate),
"endBet": formatZonedDateTime(dateTime: bet.endBetDate), "endBet": formatZonedDateTime(dateTime: bet.endBetDate),
"isPrivate": String(bet.isPrivate), "isPrivate": String(bet.isPrivate),
"response": ["Yes","No"], "response": bet.getResponses(),
"userInvited": bet.invited, "userInvited": bet.invited,
"type": betTypeString(fromType: String(describing: type(of: bet))) "type": betTypeString(fromType: String(describing: type(of: bet)))
] ]

@ -67,7 +67,6 @@ public struct UserApiManager: UserDataManager {
print ("ALLIN : get bets won") print ("ALLIN : get bets won")
do { do {
if let httpResponse = response as? HTTPURLResponse, let jsonArray = try JSONSerialization.jsonObject(with: data, options: []) as? [[String: Any]] { if let httpResponse = response as? HTTPURLResponse, let jsonArray = try JSONSerialization.jsonObject(with: data, options: []) as? [[String: Any]] {
print(jsonArray)
for json in jsonArray { for json in jsonArray {
if let bet = FactoryApiBet().toBetResultDetail(from: json) { if let bet = FactoryApiBet().toBetResultDetail(from: json) {
print(bet) print(bet)

@ -343,7 +343,7 @@
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 35KQ5BDC64; DEVELOPMENT_TEAM = P39ZK4GA2T;
DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1; DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath"; DYLIB_INSTALL_NAME_BASE = "@rpath";
@ -382,7 +382,7 @@
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 35KQ5BDC64; DEVELOPMENT_TEAM = P39ZK4GA2T;
DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1; DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath"; DYLIB_INSTALL_NAME_BASE = "@rpath";

@ -8,7 +8,7 @@
import Foundation import Foundation
/// A class representing detailed information about a specific answer option for a bet. /// 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. /// The response or outcome associated with this answer.
public private(set) var response: String public private(set) var response: String
@ -41,4 +41,19 @@ public class AnswerDetail: ObservableObject, Identifiable, Codable {
self.odds = odds 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)
}
} }

@ -8,7 +8,7 @@
import Foundation import Foundation
/// A class representing a betting entity, including details about the bet theme, participants, and deadlines. /// 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. /// The id for the bet.
public private(set) var id: String public private(set) var id: String
@ -112,4 +112,11 @@ public class Bet: ObservableObject, Identifiable, Codable {
self.author = author self.author = author
} }
/// Function that returns an empty list of responses
///
/// - Returns: An empty list of responses
public func getResponses() -> [String] {
return []
}
} }

@ -10,4 +10,21 @@ import Foundation
/// A subclass of Bet that represents a custom bet, allowing users to define their own parameters and rules. /// A subclass of Bet that represents a custom bet, allowing users to define their own parameters and rules.
public class CustomBet: Bet { 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)
}
} }

Loading…
Cancel
Save