Compare commits

..

19 Commits

Author SHA1 Message Date
Emre KARTAL c640fc0acb fix icon
10 months ago
Emre KARTAL ee3f0f0c9b fix icon
10 months ago
Emre KARTAL 1b8db51116 Merge pull request 'fix/multiples-fix' (#40) from fix/multiples-fix into master
10 months ago
Emre KARTAL a4f149b81c fix little errors 🐛
10 months ago
Emre KARTAL 2bb46d3b6f Add a semaphore to avoid the empty page in the DetailsView and get the user infos when the menu is open 🐛
10 months ago
Lucas DELANIER d5e50e4168 fix multiple issues with floats, responsive etc
10 months ago
Emre KARTAL b97254d000 Merge pull request 'feature/bet-custom' (#39) from feature/bet-custom into master
10 months ago
Lucas DELANIER f668388088 fix error when loading
10 months ago
Lucas DELANIER 77e6790f59 fix order
10 months ago
Lucas DELANIER bb2a2baac3 make the review for the custom bets
10 months ago
Emre KARTAL b7efad2eed get custom answers and add the creation of bet custom
10 months ago
Lucas DELANIER f4e3edbf4a fix winning and loosing status (#38)
10 months ago
Emre KARTAL b878759b3d Merge pull request 'feature/winnings' (#37) from feature/winnings into master
10 months ago
Lucas DELANIER 65940cb3bc delete submodule
10 months ago
Lucas DELANIER e116fc9dca fix some bugs with current and finished list
10 months ago
Emre KARTAL b542ff1bf2 Bind winModal with betResult
11 months ago
Lucas DELANIER ef1f130ec5 bind images and fix ranking row (#36)
11 months ago
Emre KARTAL 68345572ef Get bets won
11 months ago
Lucas DELANIER 5d1ea8492a feature/in-progress-card (#35)
11 months ago

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

@ -8,11 +8,12 @@
import SwiftUI import SwiftUI
struct AllcoinsCapsule: View { struct AllcoinsCapsule: View {
var gains: Int
var body: some View { var body: some View {
Text("Vous remportez") Text("Vous remportez")
.foregroundColor(.white) .foregroundColor(.white)
HStack{ HStack{
Text("2340") Text(gains.description)
.textStyle(weight: .bold, color: .white, size: 60) .textStyle(weight: .bold, color: .white, size: 60)
Image("allcoinWhiteIcon") Image("allcoinWhiteIcon")
.resizable() .resizable()
@ -30,6 +31,6 @@ struct AllcoinsCapsule: View {
struct AllcoinsCapsule_Previews: PreviewProvider { struct AllcoinsCapsule_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
AllcoinsCapsule() AllcoinsCapsule(gains: 100)
} }
} }

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

@ -29,7 +29,7 @@ struct Friend: View {
var body: some View { var body: some View {
HStack{ HStack{
UserPicture(picture: nil,username: user.username, size: 45) UserPicture(picture: user.image,username: user.username, size: 45)
Text(user.username) Text(user.username)
.fontWeight(.medium) .fontWeight(.medium)
.padding(.leading, 5) .padding(.leading, 5)

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

@ -10,7 +10,7 @@ import SwiftUI
struct RankingRow: View { struct RankingRow: View {
var number: Int var number: Int
var image: String var image: String?
var pseudo: String var pseudo: String
var allCoins: Int var allCoins: Int
@ -18,12 +18,12 @@ struct RankingRow: View {
HStack(){ HStack(){
Text(number.description) Text(number.description)
.textStyle(weight: .bold, color: AllInColors.lightPurpleColor, size: 18) .textStyle(weight: .bold, color: AllInColors.lightPurpleColor, size: 18)
.padding(.leading, 15) UserInfo(username: pseudo, picture: image, value: allCoins)
UserInfo(username: pseudo, value: allCoins)
} }
.padding(10)
.padding(.horizontal, 5)
.background(AllInColors.componentBackgroundColor) .background(AllInColors.componentBackgroundColor)
.cornerRadius(8) .cornerRadius(8)
.padding([.leading,.trailing],20)
.frame(maxWidth: 750) .frame(maxWidth: 750)
} }
} }

@ -6,9 +6,11 @@
// //
import SwiftUI import SwiftUI
import Model
struct RecapBetCard: View { struct RecapBetCard: View {
var betResult: BetResultDetail
@GestureState private var longPressTap = false @GestureState private var longPressTap = false
@State private var isPressed = false @State private var isPressed = false
@State var showDetails: Bool = false @State var showDetails: Bool = false
@ -19,22 +21,22 @@ struct RecapBetCard: View {
VStack(alignment: .leading,spacing: 2){ VStack(alignment: .leading,spacing: 2){
HStack{ HStack{
Spacer() Spacer()
Text("bet_proposed_by_format \("Lucas")") Text("bet_proposed_by_format \(betResult.bet.author)")
.font(.system(size: 10)) .font(.system(size: 10))
.foregroundColor(AllInColors.grey800Color) .foregroundColor(AllInColors.grey800Color)
} }
Text("Etudes") Text(betResult.bet.theme)
.font(.system(size: 15)) .font(.system(size: 15))
.foregroundColor(AllInColors.grey800Color) .foregroundColor(AllInColors.grey800Color)
Text("Emre va réussir son TP de CI/CD mercredi?") Text(betResult.bet.phrase)
.font(.system(size: 20)) .font(.system(size: 20))
.fontWeight(.bold) .fontWeight(.bold)
HStack{ HStack{
Text("bet_ends") Text("bet_ends")
.font(.system(size: 15)) .font(.system(size: 15))
.foregroundColor(AllInColors.grey800Color) .foregroundColor(AllInColors.grey800Color)
TextCapsule(date: Date()) TextCapsule(date: betResult.bet.endBetDate)
Spacer() Spacer()
} }
} }
@ -48,7 +50,7 @@ struct RecapBetCard: View {
Text("Mise") Text("Mise")
.textStyle(weight: .regular, color: AllInColors.grey800Color, size: 15) .textStyle(weight: .regular, color: AllInColors.grey800Color, size: 15)
Spacer() Spacer()
Text("1630") Text(betResult.participation.stake.description)
.textStyle(weight: .regular, color: AllInColors.grey800Color, size: 15) .textStyle(weight: .regular, color: AllInColors.grey800Color, size: 15)
Image("Allcoins") Image("Allcoins")
.resizable() .resizable()
@ -65,13 +67,13 @@ struct RecapBetCard: View {
Text("Gains") Text("Gains")
.textStyle(weight: .medium, color: AllInColors.lightPurpleColor, size: 15) .textStyle(weight: .medium, color: AllInColors.lightPurpleColor, size: 15)
Spacer() Spacer()
Text("1630") Text(betResult.amount.description)
.font(.system(size: 15)) .font(.system(size: 15))
.fontWeight(.medium) .fontWeight(.medium)
.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)
@ -87,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)
@ -108,7 +111,7 @@ struct RecapBetCard: View {
.onTapGesture { .onTapGesture {
showDetails.toggle() showDetails.toggle()
}.fullScreenCover(isPresented: $showDetails) { }.fullScreenCover(isPresented: $showDetails) {
DetailsView(isModalPresented: $showDetails, isModalParticipated: $showPartipated,id: "1") DetailsView(isModalPresented: $showDetails, isModalParticipated: $showPartipated, id: betResult.bet.id)
} }
.gesture( .gesture(
LongPressGesture(minimumDuration: 0.5) LongPressGesture(minimumDuration: 0.5)
@ -118,10 +121,3 @@ struct RecapBetCard: View {
) )
} }
} }
struct RecapBetCard_Previews: PreviewProvider {
static var previews: some View {
RecapBetCard()
.preferredColorScheme(.dark)
}
}

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

@ -12,26 +12,25 @@ struct ReviewCard: View {
@State var showDetails: Bool = false @State var showDetails: Bool = false
@State var showPartipated: Bool = false @State var showPartipated: Bool = false
@State var betDetail: BetDetail var bet: Bet
var amount: Int
var amountBetted: Int var isWin: Bool
var isAWin: Bool
var body: some View { var body: some View {
VStack(spacing: 0){ VStack(spacing: 0){
VStack(alignment: .leading,spacing: 2){ VStack(alignment: .leading,spacing: 2){
HStack{ HStack{
Spacer() Spacer()
Text("bet_proposed_by_format \(betDetail.bet.author)") Text("bet_proposed_by_format \(bet.author)")
.font(.system(size: 10)) .font(.system(size: 10))
.foregroundColor(AllInColors.grey800Color) .foregroundColor(AllInColors.grey800Color)
} }
Text(betDetail.bet.theme).font(.system(size: 15)).foregroundColor(AllInColors.grey800Color) Text(bet.theme).font(.system(size: 15)).foregroundColor(AllInColors.grey800Color)
Text(betDetail.bet.phrase).font(.system(size: 20)).fontWeight(.bold) Text(bet.phrase).font(.system(size: 20)).fontWeight(.bold)
HStack{ HStack{
Text("bet_ends").font(.system(size: 15)).foregroundColor(AllInColors.grey800Color) Text("bet_ends").font(.system(size: 15)).foregroundColor(AllInColors.grey800Color)
TextCapsule(date: betDetail.bet.endBetDate) TextCapsule(date: bet.endBetDate)
Spacer() Spacer()
} }
@ -40,12 +39,11 @@ struct ReviewCard: View {
.padding(.all,15) .padding(.all,15)
.background(AllInColors.componentBackgroundColor) .background(AllInColors.componentBackgroundColor)
.cornerRadius(20, corners: [.topLeft,.topRight]).padding(.bottom,0) .cornerRadius(20, corners: [.topLeft,.topRight]).padding(.bottom,0)
VStack(alignment: .center,spacing:0){ VStack(alignment: .center,spacing:0){
HStack(){ HStack(){
Spacer() Spacer()
if(betDetail.userParticipation != nil){ if bet.status == .finished {
Text((betDetail.userParticipation?.stake.description ?? "")) Text(amount.description)
.foregroundColor(.white) .foregroundColor(.white)
.font(.system(size: 25)) .font(.system(size: 25))
.fontWeight(.bold) .fontWeight(.bold)
@ -53,7 +51,8 @@ struct ReviewCard: View {
.resizable() .resizable()
.frame(width: 18, height: 20) .frame(width: 18, height: 20)
} }
switch betDetail.bet.status {
switch bet.status {
case .waiting, .inProgress: case .waiting, .inProgress:
Text("bet_status_stake") Text("bet_status_stake")
.foregroundColor(.white) .foregroundColor(.white)
@ -65,14 +64,7 @@ struct ReviewCard: View {
.font(.system(size: 25)) .font(.system(size: 25))
.fontWeight(.bold) .fontWeight(.bold)
case .finished: case .finished:
Text(amountBetted.description) Text(isWin ? "Gagnés!" : "Perdus!")
.foregroundColor(.white)
.font(.system(size: 25))
.fontWeight(.bold)
Image("allcoinWhiteIcon")
.resizable()
.frame(width: 20, height: 20, alignment: .bottom)
Text(isAWin ? "Gagnés!" : "Perdus!")
.foregroundColor(.white) .foregroundColor(.white)
.font(.system(size: 25)) .font(.system(size: 25))
.fontWeight(.bold) .fontWeight(.bold)
@ -91,24 +83,31 @@ struct ReviewCard: View {
} }
.frame(width: .infinity) .frame(width: .infinity)
.padding(.all,2) .padding(.all,2)
.background({ .background(backgroundColor())
switch betDetail.bet.status {
case .inProgress, .waiting, .closing:
return AllInColors.grey50Color
case .finished:
return Color.black
case .cancelled:
return Color.red
}
}())
.cornerRadius(20, corners: [.bottomLeft,.bottomRight]) .cornerRadius(20, corners: [.bottomLeft,.bottomRight])
.border(width: 1, edges: [.top], color: AllInColors.delimiterGrey)
} }
.onTapGesture { .onTapGesture {
showDetails.toggle() showDetails.toggle()
}.fullScreenCover(isPresented: $showDetails) { }.fullScreenCover(isPresented: $showDetails) {
DetailsView(isModalPresented: $showDetails, isModalParticipated: $showPartipated, id: betDetail.bet.id) DetailsView(isModalPresented: $showDetails, isModalParticipated: $showPartipated, id: bet.id)
}
}
private func backgroundColor() -> some View {
Group {
if bet.status == .finished && isWin {
AllInColors.primaryGradient
} else {
switch bet.status {
case .inProgress, .waiting, .closing:
AllInColors.grey50Color
case .finished:
Color.black
case .cancelled:
Color.red
}
}
} }
} }
} }

@ -10,10 +10,11 @@ import Model
struct UserInfo: View { struct UserInfo: View {
var username: String var username: String
var picture: String?
var value: Int var value: Int
var body: some View { var body: some View {
HStack { HStack {
UserPicture(username: username, size: 35) UserPicture(picture: picture, username: username, size: 35)
.padding(.trailing, 7) .padding(.trailing, 7)
Text(username) Text(username)
.font(.system(size: 15)) .font(.system(size: 15))
@ -24,10 +25,10 @@ struct UserInfo: View {
.font(.system(size: 18)) .font(.system(size: 18))
.foregroundStyle(AllInColors.lightPurpleColor) .foregroundStyle(AllInColors.lightPurpleColor)
.fontWeight(.bold) .fontWeight(.bold)
.padding(.trailing, 8) .padding(.trailing, 4)
Image("PurpleAllCoin") Image("PurpleAllCoin")
.resizable() .resizable()
.frame(width: 11, height: 12) .frame(width: 15, height: 16)
} }
} }
} }

@ -6,9 +6,11 @@
// //
import SwiftUI import SwiftUI
import Model
struct WinModal: View { struct WinModal: View {
@Environment(\.dismiss) var dismiss @Environment(\.dismiss) var dismiss
var betResult: BetResultDetail
@State var xOffset: CGFloat = 0 @State var xOffset: CGFloat = 0
var body: some View { var body: some View {
@ -35,15 +37,15 @@ struct WinModal: View {
} }
HStack{ HStack{
Text("FÉLICITATIONS").font(.system(size: 20)).foregroundColor(.white).fontWeight(.semibold).italic() Text("FÉLICITATIONS").font(.system(size: 20)).foregroundColor(.white).fontWeight(.semibold).italic()
Text("PSEUDO!").padding(.top,9).font(.system(size: 33)).fontWeight(.heavy).foregroundColor(.white) Text(AppStateContainer.shared.user?.username ?? "").padding(.top,9).font(.system(size: 33)).fontWeight(.heavy).foregroundColor(.white)
} }
.rotationEffect(.degrees(-4)) .rotationEffect(.degrees(-4))
.padding(.top,40) .padding(.top,40)
Spacer() Spacer()
AllcoinsCapsule() AllcoinsCapsule(gains: betResult.amount)
Spacer() Spacer()
RecapBetCard() RecapBetCard(betResult: betResult)
Spacer() Spacer()
} }
.padding([.all],20) .padding([.all],20)
@ -58,12 +60,6 @@ struct WinModal: View {
} }
} }
struct WinModal_Previews: PreviewProvider {
static var previews: some View {
WinModal()
}
}
struct InfiniteScroller<Content: View>: View { struct InfiniteScroller<Content: View>: View {
var contentWidth: CGFloat var contentWidth: CGFloat
var content: (() -> Content) var content: (() -> Content)

@ -6,27 +6,34 @@
// //
import SwiftUI import SwiftUI
struct UserPicture: View { struct UserPicture: View {
var picture: String? var picture: String?
var username: String var username: String
var size: CGFloat var size: CGFloat
var body: some View { var body: some View {
ZStack { ZStack {
if picture != nil { if let pictureURL = picture {
AsyncImage( userImage(url: pictureURL)
url: URL(string:picture!), } else {
content: { image in placeholderImage
}
}
}
@MainActor private func userImage(url: String) -> some View {
AsyncCachedImage(url: URL(string: url)) { image in
image image
.resizable() .resizable()
.aspectRatio(contentMode: .fit) .frame(width: size, height: size)
.clipShape(Circle()) .clipShape(Circle())
}, } placeholder: {
placeholder: { placeholderImage
ProgressView()
} }
) }
} else {
private var placeholderImage: some View {
Circle() Circle()
.foregroundColor(.gray) .foregroundColor(.gray)
.frame(width: size, height: size) .frame(width: size, height: size)
@ -37,10 +44,8 @@ struct UserPicture: View {
.font(.system(size: fontSize(for: size))) .font(.system(size: fontSize(for: size)))
) )
} }
}
}
func fontSize(for diameter: CGFloat) -> CGFloat { private func fontSize(for diameter: CGFloat) -> CGFloat {
let fontScaleFactor: CGFloat = 0.45 let fontScaleFactor: CGFloat = 0.45
return diameter * fontScaleFactor return diameter * fontScaleFactor
} }

@ -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.";

@ -0,0 +1,71 @@
//
// asyncCachedImage.swift
// AllIn
//
// Created by Lucas Delanier on 06/06/2024.
//
import SwiftUI
@MainActor
struct AsyncCachedImage<ImageView: View, PlaceholderView: View>: View {
// Input dependencies
var url: URL?
@ViewBuilder var content: (Image) -> ImageView
@ViewBuilder var placeholder: () -> PlaceholderView
// Downloaded image
@State var image: UIImage? = nil
init(
url: URL?,
@ViewBuilder content: @escaping (Image) -> ImageView,
@ViewBuilder placeholder: @escaping () -> PlaceholderView
) {
self.url = url
self.content = content
self.placeholder = placeholder
}
var body: some View {
VStack {
if let uiImage = image {
content(Image(uiImage: uiImage))
} else {
placeholder()
.onAppear {
Task {
image = await downloadPhoto()
}
}
}
}
}
// Downloads if the image is not cached already
// Otherwise returns from the cache
private func downloadPhoto() async -> UIImage? {
do {
guard let url else { return nil }
// Check if the image is cached already
if let cachedResponse = URLCache.shared.cachedResponse(for: .init(url: url)) {
return UIImage(data: cachedResponse.data)
} else {
let (data, response) = try await URLSession.shared.data(from: url)
// Save returned image data into the cache
URLCache.shared.storeCachedResponse(.init(response: response, data: data), for: .init(url: url))
guard let image = UIImage(data: data) else {
return nil
}
return image
}
} catch {
print("Error downloading: \(error)")
return nil
}
}
}

@ -13,11 +13,13 @@ 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] = []
@Published var showingSheet: Bool = false @Published var betsWon: [BetResultDetail] = []
@Published var showingSheetOver: Bool = false
@Published var showingSheetWon: Bool = false
@Published var filters: Set<BetFilter> = [] { @Published var filters: Set<BetFilter> = [] {
didSet { didSet {
getItems() getItems()
@ -28,6 +30,7 @@ class BetViewModel: ObservableObject {
getItems() getItems()
getPopularBet() getPopularBet()
getBetsOver() getBetsOver()
getBetsWon()
} }
func getItems() { func getItems() {
@ -43,7 +46,19 @@ class BetViewModel: ObservableObject {
DispatchQueue.main.async { DispatchQueue.main.async {
self.betsOver = bets self.betsOver = bets
if !self.betsOver.isEmpty { if !self.betsOver.isEmpty {
self.showingSheet = true self.showingSheetOver = true
}
}
}
}
func getBetsWon() {
manager.getBetsWon() { bets in
DispatchQueue.main.async {
self.betsWon = bets
if !self.betsWon.isEmpty {
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
}
} }
} }

@ -13,7 +13,7 @@ class HistoricBetViewModel: ObservableObject {
@Inject var manager: Manager @Inject var manager: Manager
@Published private(set) var bets: [BetDetail] = [] @Published private(set) var bets: [BetResultDetail] = []
init() { init() {
getItems() getItems()

@ -15,10 +15,10 @@ struct BetEndingValidationView: View {
@Environment(\.dismiss) var dismiss @Environment(\.dismiss) var dismiss
@StateObject private var viewModel: BetEndingValidationViewModel @StateObject private var viewModel: BetEndingValidationViewModel
var bet: BetDetail var betDetail: BetDetail
init(bet: BetDetail) { init(bet: BetDetail) {
self.bet = bet self.betDetail = bet
self._viewModel = StateObject(wrappedValue: BetEndingValidationViewModel(id: bet.bet.id)) self._viewModel = StateObject(wrappedValue: BetEndingValidationViewModel(id: bet.bet.id))
} }
@ -48,7 +48,7 @@ struct BetEndingValidationView: View {
dismiss() dismiss()
} }
} }
ReviewCard(betDetail: bet, amountBetted: 0, isAWin: false) ReviewCard(bet: betDetail.bet, amount: 0, isWin: false)
.padding(.top, 20) .padding(.top, 20)
.padding(.bottom, 10) .padding(.bottom, 10)
Text("bet_confirmation_text") Text("bet_confirmation_text")
@ -64,7 +64,7 @@ struct BetEndingValidationView: View {
.frame(maxWidth: .infinity, alignment: .leading) .frame(maxWidth: .infinity, alignment: .leading)
VStack(spacing: 14){ VStack(spacing: 14){
ForEach(bet.answers) { answer in ForEach(betDetail.answers) { answer in
ChoiceFinalAnswerCell(selected : answer.response == viewModel.selectedAnswer, answer: answer).onTapGesture { ChoiceFinalAnswerCell(selected : answer.response == viewModel.selectedAnswer, answer: answer).onTapGesture {
if(viewModel.selectedAnswer == answer.response){ if(viewModel.selectedAnswer == answer.response){
viewModel.selectedAnswer = nil viewModel.selectedAnswer = nil

@ -59,14 +59,22 @@ struct BetView: View {
.refreshable { .refreshable {
viewModel.getItems() viewModel.getItems()
} }
.sheet(isPresented: $viewModel.showingSheet, onDismiss: { .sheet(isPresented: $viewModel.showingSheetOver, onDismiss: {
viewModel.betsOver.removeFirst() viewModel.betsOver.removeFirst()
viewModel.showingSheet = !viewModel.betsOver.isEmpty viewModel.showingSheetOver = !viewModel.betsOver.isEmpty
}) { }) {
if let firstBetDetail = viewModel.betsOver.first { if let firstBetDetail = viewModel.betsOver.first {
BetEndingValidationView(bet: firstBetDetail) BetEndingValidationView(bet: firstBetDetail)
} }
} }
.sheet(isPresented: $viewModel.showingSheetWon, onDismiss: {
viewModel.betsWon.removeFirst()
viewModel.showingSheetWon = !viewModel.betsWon.isEmpty
}) {
if let firstBetResultDetail = viewModel.betsWon.first {
WinModal(betResult: firstBetResultDetail)
}
}
Spacer() Spacer()
} }
.edgesIgnoringSafeArea(.bottom) .edgesIgnoringSafeArea(.bottom)

@ -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()
} }
}) { }) {

@ -23,8 +23,8 @@ struct CurrentBetView: View {
.textStyle(weight: .bold, color: AllInColors.grey500Color, size: 25) .textStyle(weight: .bold, color: AllInColors.grey500Color, size: 25)
.padding([.top],15) .padding([.top],15)
VStack(spacing: 20){ VStack(spacing: 20){
ForEach(viewModel.bets, id: \.bet.id) { (bet: BetDetail) in ForEach(viewModel.bets, id: \.bet.id) { (betDetail: BetDetail) in
ReviewCard(betDetail: bet, amountBetted: 110, isAWin: false) ReviewCard(bet: betDetail.bet, amount: betDetail.userParticipation?.stake ?? 0, isWin: false)
} }
} }
.padding([.trailing, .leading, .bottom],25) .padding([.trailing, .leading, .bottom],25)

@ -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,49 +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
UserInfo(username: participation.username, 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)
} }

@ -24,8 +24,8 @@ struct HistoricBetView: View {
.textStyle(weight: .bold, color: AllInColors.grey500Color, size: 25) .textStyle(weight: .bold, color: AllInColors.grey500Color, size: 25)
.padding([.top],15) .padding([.top],15)
VStack(spacing: 20){ VStack(spacing: 20){
ForEach(viewModel.bets, id: \.bet.id) { (bet: BetDetail) in ForEach(viewModel.bets, id: \.bet.id) { (betDetail: BetResultDetail) in
ReviewCard(betDetail: bet, amountBetted: 110, isAWin: false) ReviewCard(bet: betDetail.bet, amount: betDetail.participation.stake, isWin: betDetail.won)
} }
} }
.padding([.trailing, .leading, .bottom],25) .padding([.trailing, .leading, .bottom],25)

@ -60,8 +60,8 @@ struct MainView: View {
if self.showMenu { if self.showMenu {
Menu() Menu()
.frame(width: geometry.size.width*0.83)
.transition(.move(edge: .leading)) .transition(.move(edge: .leading))
.frame(width: geometry.size.width*0.83)
} }
} }

@ -51,7 +51,7 @@ struct RankingView: View {
.cornerRadius(41.5, corners: .topLeft) .cornerRadius(41.5, corners: .topLeft)
.cornerRadius(8, corners: .topRight) .cornerRadius(8, corners: .topRight)
.cornerRadius(15, corners: [.bottomLeft, .bottomRight]) .cornerRadius(15, corners: [.bottomLeft, .bottomRight])
UserPicture(picture: nil, username: viewModel.friends[0].username, size: 70) UserPicture(picture: viewModel.friends[0].image, username: viewModel.friends[0].username, size: 70)
.offset(x: 0, y: -55) .offset(x: 0, y: -55)
Text("1") Text("1")
@ -95,7 +95,7 @@ struct RankingView: View {
.cornerRadius(8, corners: .topLeft) .cornerRadius(8, corners: .topLeft)
.cornerRadius(15, corners: [.bottomLeft, .bottomRight]) .cornerRadius(15, corners: [.bottomLeft, .bottomRight])
UserPicture(picture: nil, username: viewModel.friends[1].username, size: 50) UserPicture(picture: viewModel.friends[1].image, username: viewModel.friends[1].username, size: 50)
.offset(x: 0, y: -50) .offset(x: 0, y: -50)
Text("2") Text("2")
@ -122,13 +122,14 @@ struct RankingView: View {
let friend = viewModel.friends[index] let friend = viewModel.friends[index]
RankingRow( RankingRow(
number: index + 1, number: index + 1,
image: "defaultUserImage", image: friend.image,
pseudo: friend.username, pseudo: friend.username,
allCoins: friend.nbCoins allCoins: friend.nbCoins
) )
} }
} }
.padding(.top, 10) .padding(.top, 10)
.padding(.horizontal, 20)
} }
Spacer() Spacer()
} }

@ -9,12 +9,15 @@
/* 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 */; };
123590B62B5537E200F7AEBD /* ResultBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123590B52B5537E200F7AEBD /* ResultBanner.swift */; }; 123590B62B5537E200F7AEBD /* ResultBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123590B52B5537E200F7AEBD /* ResultBanner.swift */; };
123590B82B5541BA00F7AEBD /* ParticipateButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123590B72B5541BA00F7AEBD /* ParticipateButton.swift */; }; 123590B82B5541BA00F7AEBD /* ParticipateButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123590B72B5541BA00F7AEBD /* ParticipateButton.swift */; };
123F31DB2C0F26E8009B6D65 /* userPicture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123F31DA2C0F26E8009B6D65 /* userPicture.swift */; }; 123F31DB2C0F26E8009B6D65 /* userPicture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123F31DA2C0F26E8009B6D65 /* userPicture.swift */; };
123F31E02C11E99E009B6D65 /* asyncCachedImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123F31DF2C11E99E009B6D65 /* asyncCachedImage.swift */; };
1244EF602B4EC31E00374ABF /* HistoricBetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1244EF5F2B4EC31E00374ABF /* HistoricBetView.swift */; }; 1244EF602B4EC31E00374ABF /* HistoricBetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1244EF5F2B4EC31E00374ABF /* HistoricBetView.swift */; };
1244EF622B4EC67000374ABF /* ReviewCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1244EF612B4EC67000374ABF /* ReviewCard.swift */; }; 1244EF622B4EC67000374ABF /* ReviewCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1244EF612B4EC67000374ABF /* ReviewCard.swift */; };
129D051D2B6E7FF0003D3E08 /* OddCapsule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 129D051C2B6E7FF0003D3E08 /* OddCapsule.swift */; }; 129D051D2B6E7FF0003D3E08 /* OddCapsule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 129D051C2B6E7FF0003D3E08 /* OddCapsule.swift */; };
@ -148,12 +151,15 @@
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>"; };
123590B52B5537E200F7AEBD /* ResultBanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResultBanner.swift; sourceTree = "<group>"; }; 123590B52B5537E200F7AEBD /* ResultBanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResultBanner.swift; sourceTree = "<group>"; };
123590B72B5541BA00F7AEBD /* ParticipateButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParticipateButton.swift; sourceTree = "<group>"; }; 123590B72B5541BA00F7AEBD /* ParticipateButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParticipateButton.swift; sourceTree = "<group>"; };
123F31DA2C0F26E8009B6D65 /* userPicture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = userPicture.swift; sourceTree = "<group>"; }; 123F31DA2C0F26E8009B6D65 /* userPicture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = userPicture.swift; sourceTree = "<group>"; };
123F31DF2C11E99E009B6D65 /* asyncCachedImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = asyncCachedImage.swift; sourceTree = "<group>"; };
1244EF5F2B4EC31E00374ABF /* HistoricBetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoricBetView.swift; sourceTree = "<group>"; }; 1244EF5F2B4EC31E00374ABF /* HistoricBetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoricBetView.swift; sourceTree = "<group>"; };
1244EF612B4EC67000374ABF /* ReviewCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReviewCard.swift; sourceTree = "<group>"; }; 1244EF612B4EC67000374ABF /* ReviewCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReviewCard.swift; sourceTree = "<group>"; };
129D051C2B6E7FF0003D3E08 /* OddCapsule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OddCapsule.swift; sourceTree = "<group>"; }; 129D051C2B6E7FF0003D3E08 /* OddCapsule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OddCapsule.swift; sourceTree = "<group>"; };
@ -274,6 +280,14 @@
/* End PBXFrameworksBuildPhase section */ /* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */ /* Begin PBXGroup section */
123F31DE2C11E979009B6D65 /* Utils */ = {
isa = PBXGroup;
children = (
123F31DF2C11E99E009B6D65 /* asyncCachedImage.swift */,
);
path = Utils;
sourceTree = "<group>";
};
EC1D15402B715A7A0094833E /* Protocols */ = { EC1D15402B715A7A0094833E /* Protocols */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -309,6 +323,7 @@
EC6B969A2B24B4CC00FC1C58 /* AllIn */ = { EC6B969A2B24B4CC00FC1C58 /* AllIn */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
123F31DE2C11E979009B6D65 /* Utils */,
ECF872852B93B9D900F9D240 /* AllIn.entitlements */, ECF872852B93B9D900F9D240 /* AllIn.entitlements */,
EC7EF7462B87E3470022B5D9 /* QuickActions */, EC7EF7462B87E3470022B5D9 /* QuickActions */,
ECB7BC662B2F1AAD002A6654 /* ViewModels */, ECB7BC662B2F1AAD002A6654 /* ViewModels */,
@ -433,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>";
@ -664,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 */,
@ -676,6 +694,7 @@
EC6B969C2B24B4CC00FC1C58 /* AllInApp.swift in Sources */, EC6B969C2B24B4CC00FC1C58 /* AllInApp.swift in Sources */,
123590B42B51792000F7AEBD /* DetailsView.swift in Sources */, 123590B42B51792000F7AEBD /* DetailsView.swift in Sources */,
ECB26A192B40744F00FE06B3 /* RankingViewModel.swift in Sources */, ECB26A192B40744F00FE06B3 /* RankingViewModel.swift in Sources */,
123F31E02C11E99E009B6D65 /* asyncCachedImage.swift in Sources */,
EC650A502B2793D5003AFCAD /* TextCapsule.swift in Sources */, EC650A502B2793D5003AFCAD /* TextCapsule.swift in Sources */,
EC650A482B25DCFF003AFCAD /* UsersPreview.swift in Sources */, EC650A482B25DCFF003AFCAD /* UsersPreview.swift in Sources */,
EC17A15E2B6A955E008A8679 /* CurrentBetView.swift in Sources */, EC17A15E2B6A955E008A8679 /* CurrentBetView.swift in Sources */,
@ -709,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 */,
@ -916,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";
@ -950,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";
@ -1054,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;
@ -1082,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;

@ -77,6 +77,7 @@ public struct BetApiManager: BetDataManager {
do { do {
if let httpResponse = response as? HTTPURLResponse, let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] { if let httpResponse = response as? HTTPURLResponse, let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
print(httpResponse.statusCode) print(httpResponse.statusCode)
print(json)
if let betDetail = FactoryApiBet().toBetDetail(from: json) { if let betDetail = FactoryApiBet().toBetDetail(from: json) {
completion(betDetail) completion(betDetail)
} }

@ -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)))
] ]
@ -88,20 +88,51 @@ public class FactoryApiBet: FactoryBet {
do { do {
wonParticipation = try JSONDecoder().decode(Participation.self, from: JSONSerialization.data(withJSONObject: participationJson)) wonParticipation = try JSONDecoder().decode(Participation.self, from: JSONSerialization.data(withJSONObject: participationJson))
} catch { } catch {
print("Error decoding participations: \(error)") print("Error decoding participation: \(error)")
} }
} }
if let participationUserJson = json["userParticipation"] as? [String: Any] { if let participationUserJson = json["userParticipation"] as? [String: Any] {
do { do {
userParticipation = try JSONDecoder().decode(Participation.self, from: JSONSerialization.data(withJSONObject: participationUserJson)) userParticipation = try JSONDecoder().decode(Participation.self, from: JSONSerialization.data(withJSONObject: participationUserJson))
} catch { } catch {
print("Error decoding participations: \(error)") print("Error decoding participation: \(error)")
} }
} }
return BetDetail(bet: bet, answers: answers, participations: participations, wonParticipation: wonParticipation, userParticipation: userParticipation) return BetDetail(bet: bet, answers: answers, participations: participations, wonParticipation: wonParticipation, userParticipation: userParticipation)
} }
public func toBetResultDetail(from json: [String: Any]) -> BetResultDetail? {
guard let amount = json["amount"] as? Int,
let won = json["won"] as? Bool else {
return nil
}
guard let betJson = json["bet"] as? [String: Any],
let bet = self.toBet(from: betJson) else {
return nil
}
let decoder = JSONDecoder()
guard let betResultJson = json["betResult"] as? [String: Any],
let betResultData = try? JSONSerialization.data(withJSONObject: betResultJson),
let betResult = try? decoder.decode(BetResult.self, from: betResultData) else {
print("Error decoding bet result")
return nil
}
guard let participationJson = json["participation"] as? [String: Any],
let participationData = try? JSONSerialization.data(withJSONObject: participationJson),
let participation = try? decoder.decode(Participation.self, from: participationData) else {
print("Error decoding participation")
return nil
}
return BetResultDetail(betResult: betResult, bet: bet, participation: participation, amount: amount, won: won)
}
public func betTypeString(fromType type: String) -> String { public func betTypeString(fromType type: String) -> String {
switch type { switch type {
case "BinaryBet": case "BinaryBet":

@ -52,6 +52,38 @@ public struct UserApiManager: UserDataManager {
}.resume() }.resume()
} }
public func getBetsWon(completion : @escaping ([BetResultDetail])-> ()) {
let url = URL(string: url + "bets/getWon")!
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
var bets: [BetResultDetail] = []
URLSession.shared.dataTask(with: request) { data, response, error in
if let data = data {
print ("ALLIN : get bets won")
do {
if let httpResponse = response as? HTTPURLResponse, let jsonArray = try JSONSerialization.jsonObject(with: data, options: []) as? [[String: Any]] {
for json in jsonArray {
if let bet = FactoryApiBet().toBetResultDetail(from: json) {
print(bet)
bets.append(bet)
}
}
print(httpResponse.statusCode)
completion(bets)
}
} catch {
print("Error parsing JSON: \(error)")
}
}
}.resume()
}
public func addBet(bet: Bet, completion : @escaping (Int)-> ()) { public func addBet(bet: Bet, completion : @escaping (Int)-> ()) {
let url = URL(string: url + "bets/add")! let url = URL(string: url + "bets/add")!
@ -218,7 +250,7 @@ public struct UserApiManager: UserDataManager {
}.resume() }.resume()
} }
public func getOldBets(withIndex index: Int, withCount count: Int, completion: @escaping ([BetDetail]) -> Void) { public func getOldBets(withIndex index: Int, withCount count: Int, completion: @escaping ([BetResultDetail]) -> Void) {
let url = URL(string: url + "bets/history")! let url = URL(string: url + "bets/history")!
var request = URLRequest(url: url) var request = URLRequest(url: url)
@ -226,15 +258,16 @@ public struct UserApiManager: UserDataManager {
request.setValue("application/json", forHTTPHeaderField: "Content-Type") request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization") request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
var bets: [BetDetail] = [] var bets: [BetResultDetail] = []
URLSession.shared.dataTask(with: request) { data, response, error in URLSession.shared.dataTask(with: request) { data, response, error in
if let data = data { if let data = data {
print ("ALLIN : get old bets") print ("ALLIN : get old bets")
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().toBetDetail(from: json) { if let bet = FactoryApiBet().toBetResultDetail(from: json) {
bets.append(bet) bets.append(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 []
}
} }

@ -0,0 +1,21 @@
//
// BetResult.swift
//
//
// Created by Emre on 06/06/2024.
//
import Foundation
public class BetResult: Codable {
public private(set) var betId: String
public private(set) var result: String
public init(betId: String, result: String) {
self.betId = betId
self.result = result
}
}

@ -0,0 +1,30 @@
//
// BetResultDetail.swift
//
//
// Created by Emre on 06/06/2024.
//
import Foundation
public class BetResultDetail: Codable {
public private(set) var betResult: BetResult
public private(set) var bet: Bet
public private(set) var participation: Participation
public private(set) var amount: Int
public private(set) var won: Bool
public init(betResult: BetResult, bet: Bet, participation: Participation, amount: Int, won: Bool) {
self.betResult = betResult
self.bet = bet
self.participation = participation
self.amount = amount
self.won = won
}
}

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

@ -11,4 +11,5 @@ public protocol FactoryBet {
func toResponse(bet: Bet) -> [String: Any] func toResponse(bet: Bet) -> [String: Any]
func toBet(from json: [String: Any]) -> Bet? func toBet(from json: [String: Any]) -> Bet?
func toBetDetail(from json: [String: Any]) -> BetDetail? func toBetDetail(from json: [String: Any]) -> BetDetail?
func toBetResultDetail(from json: [String: Any]) -> BetResultDetail?
} }

@ -46,6 +46,12 @@ public struct Manager {
} }
} }
public func getBetsWon(completion: @escaping ([BetResultDetail]) -> Void) {
userDataManager.getBetsWon() { bets in
completion(bets)
}
}
public func getBet(withId id: String, completion: @escaping (BetDetail) -> Void) { public func getBet(withId id: String, completion: @escaping (BetDetail) -> Void) {
betDataManager.getBet(withId: id) { bet in betDataManager.getBet(withId: id) { bet in
completion(bet) completion(bet)
@ -70,7 +76,7 @@ public struct Manager {
} }
} }
public func getHistoricBets(withIndex index: Int, withCount count: Int, completion: @escaping ([BetDetail]) -> Void) { public func getHistoricBets(withIndex index: Int, withCount count: Int, completion: @escaping ([BetResultDetail]) -> Void) {
userDataManager.getOldBets(withIndex: index, withCount: count) { bets in userDataManager.getOldBets(withIndex: index, withCount: count) { bets in
completion(bets) completion(bets)
} }

@ -10,6 +10,7 @@ import Foundation
public protocol UserDataManager { public protocol UserDataManager {
func getBets(withIndex index: Int, withCount count: Int) -> [Bet] func getBets(withIndex index: Int, withCount count: Int) -> [Bet]
func getBetsOver(completion: @escaping ([BetDetail]) -> Void) func getBetsOver(completion: @escaping ([BetDetail]) -> Void)
func getBetsWon(completion : @escaping ([BetResultDetail])-> ())
func addBet(bet: Bet, completion : @escaping (Int)-> ()) func addBet(bet: Bet, completion : @escaping (Int)-> ())
func addFriend(username: String, completion : @escaping (Int)-> ()) func addFriend(username: String, completion : @escaping (Int)-> ())
func removeFriend(username: String, completion : @escaping (Int)-> ()) func removeFriend(username: String, completion : @escaping (Int)-> ())
@ -17,7 +18,7 @@ public protocol UserDataManager {
func getRequests(completion: @escaping ([User]) -> Void) func getRequests(completion: @escaping ([User]) -> Void)
func getUsers(withName name: String, completion: @escaping ([User]) -> Void) func getUsers(withName name: String, completion: @escaping ([User]) -> Void)
func getGifts(completion : @escaping (Int, Int)-> ()) func getGifts(completion : @escaping (Int, Int)-> ())
func getOldBets(withIndex index: Int, withCount count: Int, completion: @escaping ([BetDetail]) -> Void) func getOldBets(withIndex index: Int, withCount count: Int, completion: @escaping ([BetResultDetail]) -> Void)
func getCurrentBets(withIndex index: Int, withCount count: Int, completion: @escaping ([BetDetail]) -> Void) func getCurrentBets(withIndex index: Int, withCount count: Int, completion: @escaping ([BetDetail]) -> Void)
func addParticipation(withId id: String, withAnswer answer: String, andStake stake: Int, completion : @escaping (Int)-> ()) func addParticipation(withId id: String, withAnswer answer: String, andStake stake: Int, completion : @escaping (Int)-> ())
func addResponse(withIdBet id: String, andResponse responseBet: String) func addResponse(withIdBet id: String, andResponse responseBet: String)

Loading…
Cancel
Save