Compare commits

..

22 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
Emre KARTAL d82c8e7d79 Merge pull request 'Add private friends invitation' (#34) from feature/user-invitation into master
11 months ago
Emre KARTAL ab4aa3313a Add private friends invitation
11 months ago
Lucas DELANIER 32480e0cb1 fix mistake ispublic (#33)
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" : "globe.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: 39 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)
} }
} }

@ -44,8 +44,8 @@ struct BetCard: View {
VStack(alignment: .leading,spacing: 2){ VStack(alignment: .leading,spacing: 2){
HStack{ HStack{
Spacer() Spacer()
UsersPreview(users: bet.registered) UsersPreview(users: [])
Text("bet_players_waiting_format \(bet.registered.count.description)") Text("bet_players_waiting_format \(bet.invited.count.description)")
.font(.system(size: 15)) .font(.system(size: 15))
.foregroundColor(AllInColors.grey800Color) .foregroundColor(AllInColors.grey800Color)
.fontWeight(.medium) .fontWeight(.medium)
@ -80,8 +80,7 @@ struct BetCard_Previews: PreviewProvider {
isPrivate: false, isPrivate: false,
status: .inProgress, status: .inProgress,
invited: [], invited: [],
author: "Imri", author: "Imri"))
registered: []))
.preferredColorScheme(.dark) .preferredColorScheme(.dark)
} }
} }

@ -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,144 +25,22 @@ 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)
}
}
}
}
}
}

@ -14,23 +14,30 @@ struct ChoiceFinalAnswerCell: View {
var selected = false var selected = false
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,
foregroundColor: selected ? AllInColors.purpleAccentColor : .white,
odd: answer.odds
)
.padding(.trailing, 20)
.scaleEffect(0.9)
} }
}.background(selected ? AllInColors.purpleAccentColor : .white).cornerRadius(17).scaleEffect(selected ? 1.02 : 1).animation( }
.easeInOut(duration: 0.3), .background(selected ? AllInColors.purpleAccentColor : .white)
value: selected .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,19 +35,21 @@ 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
HStack{ self.expand.toggle()}
Text(options[index].1.description) ) {
.textStyle(weight: .bold, color: AllInColors.blueAccentColor, size: 20) HStack{
Text(options[index].2.description) Text(answer.response)
.textStyle(weight: .bold, color: AllInColors.lightPurpleColor, size: 10) .textStyle(weight: .bold, color: AllInColors.blueAccentColor, size: 20)
Spacer() Text("\(answer.odds, specifier: "%.2f")")
} .textStyle(weight: .bold, color: AllInColors.lightPurpleColor, size: 10)
Spacer()
} }
.padding(.bottom, 15) }
.padding(.bottom, 15)
} }
} }
} }
@ -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)
}
}

@ -6,25 +6,19 @@
// //
import SwiftUI import SwiftUI
import Model
struct DropDownFriends: View { struct DropDownFriends: View {
@State private var selectedItems: Set<Int> = [] @Binding var selectedItems: Set<String>
@State var expand = false @State var expand = false
let friends: [(Int, Int, String, String)] = [ var friends: [User]
(0, 541, "David", "defaultUserImage"),
(1, 541, "David", "defaultUserImage"),
(2, 541, "David", "defaultUserImage"),
(3, 541, "David", "defaultUserImage"),
(4, 541, "David", "defaultUserImage"),
(5, 541, "David", "defaultUserImage")
]
var body: some View { var body: some View {
VStack(spacing: 0, content: { VStack(spacing: 0, content: {
Button(action: { self.expand.toggle() }) { Button(action: { self.expand.toggle() }) {
HStack(spacing: 3){ HStack(spacing: 3){
Text("41") Text(friends.count.description)
.textStyle(weight: .bold, color: AllInColors.primaryTextColor, size: 15) .textStyle(weight: .bold, color: AllInColors.primaryTextColor, size: 15)
Text("bet_creation_friends_available_format") Text("bet_creation_friends_available_format")
.textStyle(weight: .regular, color: AllInColors.grey800Color, size: 15) .textStyle(weight: .regular, color: AllInColors.grey800Color, size: 15)
@ -42,33 +36,33 @@ struct DropDownFriends: View {
.foregroundColor(AllInColors.delimiterGrey) .foregroundColor(AllInColors.delimiterGrey)
ScrollView(.vertical) { ScrollView(.vertical) {
VStack(spacing: 0) { VStack(spacing: 0) {
ForEach(0..<friends.count, id: \.self) { item in ForEach(friends, id: \.self) { (friend: User) in
HStack { HStack {
Circle() Circle()
.fill(selectedItems.contains(friends[item].0) ? AllInColors.lightPurpleColor : Color.clear) .fill(selectedItems.contains(friend.id) ? AllInColors.lightPurpleColor : Color.clear)
.overlay( .overlay(
Circle() Circle()
.stroke(selectedItems.contains(friends[item].0) ? Color.clear : AllInColors.skyBlueColor, lineWidth: 1) .stroke(selectedItems.contains(friend.id) ? Color.clear : AllInColors.skyBlueColor, lineWidth: 1)
) )
.frame(width: 15, height: 15) .frame(width: 15, height: 15)
.padding(.trailing, 5) .padding(.trailing, 5)
UserInfo(username: "", value: 0) UserInfo(username: friend.username, value: friend.nbCoins)
.contentShape(Rectangle()) .contentShape(Rectangle())
} }
.padding([.leading, .trailing], 15) .padding([.leading, .trailing], 15)
.padding([.top, .bottom], 5) .padding([.top, .bottom], 5)
.overlay( .overlay(
selectedItems.contains(friends[item].0) ? selectedItems.contains(friend.id) ?
Rectangle() Rectangle()
.fill(AllInColors.lightPurpleColor.opacity(0.13)) .fill(AllInColors.lightPurpleColor.opacity(0.13))
: nil : nil
) )
.opacity(1.0) .opacity(1.0)
.onTapGesture { .onTapGesture {
if selectedItems.contains(friends[item].0) { if selectedItems.contains(friend.id) {
selectedItems.remove(friends[item].0) selectedItems.remove(friend.id)
} else { } else {
selectedItems.insert(friends[item].0) selectedItems.insert(friend.id)
} }
} }
Rectangle() Rectangle()
@ -90,9 +84,3 @@ struct DropDownFriends: View {
) )
} }
} }
struct DropDownFriends_Previews: PreviewProvider {
static var previews: some View {
DropDownFriends()
}
}

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

@ -11,24 +11,22 @@ import DependencyInjection
struct Menu: View { 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"),
("moneyImage", String(localized: "drawer_current_bets"), String(localized: "drawer_current_bets_subtitle"), "Current"),
("eyesImage", String(localized: "drawer_bet_history"), String(localized: "drawer_bet_history_subtitle"),"Historic"), ("eyesImage", String(localized: "drawer_bet_history"), String(localized: "drawer_bet_history_subtitle"),"Historic"),
("friendsImage", String(localized: "drawer_friends"), String(localized: "drawer_friends_subtitle"), "Friends"), ("friendsImage", String(localized: "drawer_friends"), String(localized: "drawer_friends_subtitle"), "Friends"),
("moneyImage", String(localized: "drawer_public_bets"), String(localized: "drawer_public_bets_subtitle"), "Bet"),
("rankingImage", String(localized: "drawer_ranking"), String(localized: "drawer_ranking_subtitle"), "Ranking"), ("rankingImage", String(localized: "drawer_ranking"), String(localized: "drawer_ranking_subtitle"), "Ranking"),
("eyesImage", String(localized: "drawer_current_bets"), String(localized: "drawer_current_bets_subtitle"), "Current")
] ]
var body: some View { var body: some View {
VStack(alignment: .leading, spacing: 10) { VStack(alignment: .leading, spacing: 10) {
HStack() { HStack() {
Spacer() Spacer()
VStack(){ VStack(){
UserPicture(username: (AppStateContainer.shared.user?.username)!, size: 100) UserPicture(picture: AppStateContainer.shared.user?.image, username: (AppStateContainer.shared.user?.username)!,size: 100)
Text(AppStateContainer.shared.user?.username.capitalized ?? "") Text(AppStateContainer.shared.user?.username.capitalized ?? "")
.fontWeight(.medium) .fontWeight(.medium)
.font(.system(size: 17)) .font(.system(size: 17))
@ -41,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)
@ -51,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)
@ -61,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)
@ -105,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)
}
}

@ -6,26 +6,29 @@
// //
import SwiftUI import SwiftUI
import Model
struct ResultBanner: View { struct ResultBanner: View {
var finalAnswer: Participation
var odds: Float
var body: some View { var body: some View {
VStack{ VStack{
HStack{ HStack{
Image("BleueTrophyIcon").resizable().frame(maxWidth: 70, maxHeight: 60) Image(systemName: "trophy.fill").resizable().frame(maxWidth: 70, maxHeight: 60).foregroundColor(AllInColors.blueGrey800Color)
Text("OUI").font(.system(size: 70)).fontWeight(.bold).foregroundStyle(AllInColors.blueGrey800Color) Text(finalAnswer.answer).font(.system(size: 70)).fontWeight(.bold).foregroundStyle(AllInColors.blueGrey800Color)
}.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("460").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("ImriDu43").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("x1.2").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,25 +39,37 @@ 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.bet.endBetDate < Date()){ if bet.status == .finished {
Text("bet_finished") Text(amount.description)
.foregroundColor(.white) .foregroundColor(.white)
.font(.system(size: 25)) .font(.system(size: 25))
.fontWeight(.bold) .fontWeight(.bold)
Image("allcoinWhiteIcon")
.resizable()
.frame(width: 18, height: 20)
} }
else{
Text(amountBetted.description) switch bet.status {
case .waiting, .inProgress:
Text("bet_status_stake")
.foregroundColor(.white) .foregroundColor(.white)
.font(.system(size: 25)) .font(.system(size: 25))
.fontWeight(.bold) .fontWeight(.bold)
Image("allcoinWhiteIcon") case .closing:
.resizable() Text("bet_status_finished")
.frame(width: 20, height: 20, alignment: .bottom) .foregroundColor(.white)
Text(isAWin ? "Gagnés!" : "Perdus!") .font(.system(size: 25))
.fontWeight(.bold)
case .finished:
Text(isWin ? "Gagnés!" : "Perdus!")
.foregroundColor(.white)
.font(.system(size: 25))
.fontWeight(.bold)
case .cancelled:
Text("cancelled")
.foregroundColor(.white) .foregroundColor(.white)
.font(.system(size: 25)) .font(.system(size: 25))
.fontWeight(.bold) .fontWeight(.bold)
@ -72,17 +83,31 @@ struct ReviewCard: View {
} }
.frame(width: .infinity) .frame(width: .infinity)
.padding(.all,2) .padding(.all,2)
.background( .background(backgroundColor())
isAWin || betDetail.bet.endBetDate < Date() ? .cornerRadius(20, corners: [.bottomLeft,.bottomRight])
AnyView(AllInColors.primaryGradient) :
AnyView(Color.black)
) .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
}
}
}
}
} }

@ -80,7 +80,6 @@ struct TrendingBetCard_Previews: PreviewProvider {
isPrivate: true, isPrivate: true,
status: .inProgress, status: .inProgress,
invited: [], invited: [],
author: "Imri", author: "Imri"))
registered: []))
} }
} }

@ -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,41 +6,46 @@
// //
import SwiftUI import SwiftUI
struct UserPicture: View { struct UserPicture: View {
var picture: URL? 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: picture,
content: { image in
image
.resizable()
.aspectRatio(contentMode: .fit)
.clipShape(Circle())
},
placeholder: {
ProgressView()
}
)
} else { } else {
Circle() placeholderImage
.foregroundColor(.gray)
.frame(width: size, height: size)
.overlay(
Text(username.prefix(2).uppercased())
.fontWeight(.medium)
.foregroundStyle(.white)
.font(.system(size: fontSize(for: size)))
)
} }
} }
} }
func fontSize(for diameter: CGFloat) -> CGFloat { @MainActor private func userImage(url: String) -> some View {
AsyncCachedImage(url: URL(string: url)) { image in
image
.resizable()
.frame(width: size, height: size)
.clipShape(Circle())
} placeholder: {
placeholderImage
}
}
private var placeholderImage: some View {
Circle()
.foregroundColor(.gray)
.frame(width: size, height: size)
.overlay(
Text(username.prefix(2).uppercased())
.fontWeight(.medium)
.foregroundStyle(.white)
.font(.system(size: fontSize(for: size)))
)
}
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,

@ -46,15 +46,13 @@
"drawer_nb_friends" = "Friends"; "drawer_nb_friends" = "Friends";
"drawer_friends" = "FRIENDS"; "drawer_friends" = "FRIENDS";
"drawer_friends_subtitle" = "Challenge your folks by adding them as friends."; "drawer_friends_subtitle" = "Challenge your folks by adding them as friends.";
"drawer_public_bets" = "PUBLIC BETS"; "drawer_public_bets" = "PARTICIPATE";
"drawer_public_bets_subtitle" = "Browse the most popular bets of the moment."; "drawer_public_bets_subtitle" = "Browse the most popular bets of the moment.";
"drawer_current_bets" = "CURRENT BETS"; "drawer_create_a_bet" = "NEW BET";
"drawer_current_bets_subtitle" = "View your current bets.";
"drawer_create_a_bet" = "CREATE A BET";
"drawer_create_a_bet_subtitle" = "Create a net bet and get your friends participating."; "drawer_create_a_bet_subtitle" = "Create a net bet and get your friends participating.";
"drawer_bet_history" = "BET HISTORY"; "drawer_bet_history" = "HISTORY";
"drawer_bet_history_subtitle" = "View your current and finished bets."; "drawer_bet_history_subtitle" = "View your current and finished bets.";
"drawer_current_bets" = "CURRENT BETS"; "drawer_current_bets" = "MY PARTICIPATIONS";
"drawer_current_bets_subtitle" = "Manage your bets and reward the winners."; "drawer_current_bets_subtitle" = "Manage your bets and reward the winners.";
"drawer_ranking" = "RANKING"; "drawer_ranking" = "RANKING";
"drawer_ranking_subtitle" = "Check your ranking among your friends."; "drawer_ranking_subtitle" = "Check your ranking among your friends.";
@ -135,7 +133,7 @@
"bet_participate" = "Participate"; "bet_participate" = "Participate";
"bet_proposed_by_format %@" = "Proposed by %@"; "bet_proposed_by_format %@" = "Proposed by %@";
"bet_proposed_by_format" = "Proposed by"; "bet_proposed_by_format" = "Proposed by";
"bet_players_waiting_format %@" = "%@ joueurs en attente"; "bet_players_waiting_format %@" = "%@ players waiting";
"bet_players_format" = "players"; "bet_players_format" = "players";
"bet_points_at_stake_format" = "points at stake"; "bet_points_at_stake_format" = "points at stake";
@ -150,6 +148,7 @@
"bet_status_participants_list" = "Participants"; "bet_status_participants_list" = "Participants";
"bet_status_details_drawer" = "Details"; "bet_status_details_drawer" = "Details";
"participation_possible_winnings" = "Possible winnings"; "participation_possible_winnings" = "Possible winnings";
"bet_status_stake" = "Gambled";
/// Bet history /// Bet history
@ -179,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
@ -188,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";

@ -46,15 +46,13 @@
"drawer_nb_friends" = "Amis"; "drawer_nb_friends" = "Amis";
"drawer_friends" = "AMIS"; "drawer_friends" = "AMIS";
"drawer_friends_subtitle" = "Défiez vos proches en les ajoutant en amis."; "drawer_friends_subtitle" = "Défiez vos proches en les ajoutant en amis.";
"drawer_public_bets" = "BETS PUBLIQUES"; "drawer_public_bets" = "PARTICIPER";
"drawer_public_bets_subtitle" = "Parcourez les bets les plus populaires du moment."; "drawer_public_bets_subtitle" = "Parcourez les bets les plus populaires du moment.";
"drawer_current_bets" = "BETS EN COURS"; "drawer_create_a_bet" = "NOUVEAU BET";
"drawer_current_bets_subtitle" = "Consultez vos paris en cours.";
"drawer_create_a_bet" = "CREER UN BET";
"drawer_create_a_bet_subtitle" = "Créez un nouveau bet et faites participer vos amis."; "drawer_create_a_bet_subtitle" = "Créez un nouveau bet et faites participer vos amis.";
"drawer_bet_history" = "HISTORIQUE DES BETS"; "drawer_bet_history" = "HISTORIQUE";
"drawer_bet_history_subtitle" = "Consultez vos paris en cours et terminés."; "drawer_bet_history_subtitle" = "Consultez vos paris en cours et terminés.";
"drawer_current_bets" = "BETS EN COURS"; "drawer_current_bets" = "MES PARTICIPATIONS";
"drawer_current_bets_subtitle" = "Gérez vos bets et récompensez les gagnants."; "drawer_current_bets_subtitle" = "Gérez vos bets et récompensez les gagnants.";
"drawer_ranking" = "CLASSEMENT"; "drawer_ranking" = "CLASSEMENT";
"drawer_ranking_subtitle" = "Consultez votre classement parmis vos amis."; "drawer_ranking_subtitle" = "Consultez votre classement parmis vos amis.";
@ -135,7 +133,7 @@
"bet_participate" = "Participer"; "bet_participate" = "Participer";
"bet_proposed_by_format %@" = "Proposé par %@"; "bet_proposed_by_format %@" = "Proposé par %@";
"bet_proposed_by_format" = "Proposé par"; "bet_proposed_by_format" = "Proposé par";
"bet_players_waiting_format %@" = "%@ players waiting"; "bet_players_waiting_format %@" = "%@ joueurs en attente";
"bet_players_format" = "joueurs"; "bet_players_format" = "joueurs";
"bet_points_at_stake_format" = "points en jeu"; "bet_points_at_stake_format" = "points en jeu";
@ -150,6 +148,7 @@
"bet_status_participants_list" = "Liste des participants"; "bet_status_participants_list" = "Liste des participants";
"bet_status_details_drawer" = "Détails"; "bet_status_details_drawer" = "Détails";
"participation_possible_winnings" = "Gains possibles"; "participation_possible_winnings" = "Gains possibles";
"bet_status_stake" = "Pariés";
/// Bet history /// Bet history
@ -188,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()
@ -27,6 +29,8 @@ class BetViewModel: ObservableObject {
init() { init() {
getItems() getItems()
getPopularBet() getPopularBet()
getBetsOver()
getBetsWon()
} }
func getItems() { func getItems() {
@ -35,11 +39,26 @@ class BetViewModel: ObservableObject {
self.bets = bets self.bets = bets
} }
} }
}
func getBetsOver() {
manager.getBetsOver() { bets in manager.getBetsOver() { bets in
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()
} }
} }
} }

@ -15,30 +15,66 @@ class CreationBetViewModel: ObservableObject {
@Inject var manager: Manager @Inject var manager: Manager
@Published var theme: String = "" @Published var theme: String = ""
@Published var description: String = "" @Published var description: String = ""
@Published var isPrivate = false @Published var isPrivate = false {
didSet {
invited.removeAll()
}
}
@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 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] = []
let options: [(Int, String, String)] = [
(0, "questionMarkIcon", String(localized: "bet_type_binary")),
(1, "footballIcon", String(localized: "bet_type_match")),
(2, "paintbrushIcon", String(localized: "bet_type_custom"))
]
@Published var response = ""
@Published var groupedItems: [[String]] = [[String]] ()
init() {
getFriends()
}
func getFriends() {
manager.getFriends() { friends in
DispatchQueue.main.async {
self.friends = friends
}
}
}
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, 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:
@ -53,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
@ -86,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
@ -96,6 +146,7 @@ class CreationBetViewModel: ObservableObject {
descriptionFieldError = newDescriptionFieldError descriptionFieldError = newDescriptionFieldError
endRegisterDateFieldError = newEndRegisterDateFieldError endRegisterDateFieldError = newEndRegisterDateFieldError
endBetDateFieldError = newEndBetDateFieldError endBetDateFieldError = newEndBetDateFieldError
responsesFieldError = newResponsesFieldError
} }
return false return false
} }
@ -106,6 +157,7 @@ class CreationBetViewModel: ObservableObject {
descriptionFieldError = nil descriptionFieldError = nil
endRegisterDateFieldError = nil endRegisterDateFieldError = nil
endBetDateFieldError = nil endBetDateFieldError = nil
responsesFieldError = nil
} }
} }
@ -114,16 +166,20 @@ class CreationBetViewModel: ObservableObject {
self.errorMessage = errorMessage self.errorMessage = errorMessage
} }
func toBet(theme: String, description: String, endRegister: Date, endBet: Date, isPrivate: Bool, status: BetStatus, creator: String, type: Int) -> Bet { func toBet(theme: String, description: String, endRegister: Date, endBet: Date, isPrivate: Bool, status: BetStatus, creator: String, invited: [String], type: Int) -> Bet {
switch type { switch type {
case 0: case 0:
return BinaryBet(theme: theme, phrase: description, endRegisterDate: endRegister, endBetDate: endBet, isPrivate: isPrivate, status: status, invited: [], author: creator, registered: []) return BinaryBet(theme: theme, phrase: description, endRegisterDate: endRegister, endBetDate: endBet, isPrivate: isPrivate, status: status, invited: invited, author: creator)
case 1: case 1:
return MatchBet(theme: theme, phrase: description, endRegisterDate: endRegister, endBetDate: endBet, isPrivate: isPrivate, status: status, invited: [], author: creator, registered: [], 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: [], author: creator, registered: []) 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: [], author: creator, registered: []) return BinaryBet(theme: theme, phrase: description, endRegisterDate: endRegister, endBetDate: endBet, isPrivate: isPrivate, status: status, invited: invited, author: creator)
} }
} }
} }

@ -15,9 +15,9 @@ 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?
init(id: String) { init(id: String) {
@ -26,30 +26,31 @@ 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
WidgetCenter.shared.reloadAllTimelines() WidgetCenter.shared.reloadAllTimelines()
self.getItem(withId: self.id) self.getItem(withId: self.id)
default: default:
break break
@ -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(
@ -277,7 +268,7 @@ struct CreationBetView: View {
VStack(spacing: 10) { VStack(spacing: 10) {
if self.viewModel.isPrivate { if self.viewModel.isPrivate {
DropDownFriends() DropDownFriends(selectedItems: $viewModel.invited, friends: viewModel.friends)
.padding(.bottom, 30) .padding(.bottom, 30)
HStack() { HStack() {
@ -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)

@ -8,7 +8,7 @@ struct DetailsView: View {
@StateObject private var viewModel: DetailsViewModel @StateObject private var viewModel: DetailsViewModel
var isFinished: Bool { var isFinished: Bool {
viewModel.betDetail?.finalAnswer == nil ? false : true viewModel.betDetail?.wonParticipation == nil ? false : true
} }
var StatusValues: (String, Color) { var StatusValues: (String, Color) {
@ -57,97 +57,117 @@ 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()
Text("bet_proposed_by_format")
.font(.system(size: 10))
.foregroundColor(AllInColors.grey800Color)
Text((viewModel.betDetail?.bet.author ?? "Unknown").capitalized)
.font(.system(size: 10))
.fontWeight(.semibold)
.foregroundColor(AllInColors.primaryTextColor)
}
Text(viewModel.betDetail?.bet.theme ?? "Not loaded")
.font(.system(size: 15))
.foregroundColor(AllInColors.grey800Color)
Text(viewModel.betDetail?.bet.phrase ?? "Not loaded")
.font(.system(size: 20))
.fontWeight(.bold)
.padding(.bottom, 10)
HStack {
HStack {
Spacer() Spacer()
Text("bet_starting") Text("bet_proposed_by_format")
.font(.system(size: 15)) .font(.system(size: 10))
.foregroundColor(AllInColors.grey800Color) .foregroundColor(AllInColors.grey800Color)
Text((viewModel.betDetail?.bet.author ?? "Unknown").capitalized)
.font(.system(size: 10))
.fontWeight(.semibold)
.foregroundColor(AllInColors.primaryTextColor)
} }
.frame(width: 105) Text(viewModel.betDetail?.bet.theme ?? "Not loaded")
.padding(.trailing, 10) .font(.system(size: 15))
TextCapsule(date: viewModel.betDetail?.bet.endRegisterDate ?? Date()) .foregroundColor(AllInColors.grey800Color)
Spacer() Text(viewModel.betDetail?.bet.phrase ?? "Not loaded")
.font(.system(size: 20))
}.padding(.bottom, 10) .fontWeight(.bold)
HStack { .padding(.bottom, 10)
HStack { HStack {
HStack {
Spacer()
Text("bet_starting")
.font(.system(size: 15))
.foregroundColor(AllInColors.grey800Color)
}
.frame(width: 105)
.padding(.trailing, 10)
TextCapsule(date: viewModel.betDetail?.bet.endRegisterDate ?? Date())
Spacer()
}.padding(.bottom, 10)
HStack {
HStack {
Spacer()
Text("bet_ends")
.font(.system(size: 15))
.foregroundColor(AllInColors.grey800Color)
}
.frame(width: 105)
.padding(.trailing, 10)
TextCapsule(date: viewModel.betDetail?.bet.endBetDate ?? Date())
Spacer() Spacer()
Text("bet_ends")
.font(.system(size: 15))
.foregroundColor(AllInColors.grey800Color)
} }
.frame(width: 105)
.padding(.trailing, 10)
TextCapsule(date: viewModel.betDetail?.bet.endBetDate ?? Date())
Spacer()
} }
} .padding(.all, 15)
.padding(.all, 15) .padding(.top, 6)
.padding(.vertical, 10) .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() 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")
.font(.system(size: 18)) }
.foregroundStyle(AllInColors.grey100Color) Text("bet_status_participants_list")
.fontWeight(.bold) .font(.system(size: 18))
.padding(.bottom, 10) .foregroundStyle(AllInColors.grey100Color)
ScrollView(showsIndicators: false) { .fontWeight(.bold)
.padding(.bottom, 10)
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)
Spacer()
} }
.padding(.bottom, geometry.safeAreaInsets.bottom + 28) .padding([.trailing,.leading], 15)
.padding(.bottom, 100)
Spacer()
} }
.padding([.bottom,.trailing,.leading], 15) .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])
Spacer() }
else{
ScrollView {
HStack(alignment: .center){
Spacer()
Image(systemName: "exclamationmark.triangle.fill").font(.system(size: 45))
VStack(alignment:.leading){
Text("error_title").font(.system(size: 10))
Text("error_message").font(.system(size: 10))
}
Spacer()
}.opacity(0.6)
.padding(.vertical, 20)
}
.frame(maxWidth: .infinity, maxHeight: (geometry.size.height + geometry.safeAreaInsets.bottom) - 50)
.background(AllInColors.underComponentBackgroundColor)
.cornerRadius(15, corners: [.topLeft, .topRight])
} }
.frame(maxWidth: .infinity, maxHeight: (geometry.size.height + geometry.safeAreaInsets.bottom) - 50)
.background(AllInColors.componentBackgroundColor)
.cornerRadius(15, corners: [.topLeft, .topRight])
ParticipateButton(isOpen: $isModalPresented, isParticapatedOpen: $isModalParticipated, bet: viewModel.betDetail?.bet) 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,22 +64,26 @@ struct FriendsView: View {
} }
.padding(.top, 50) .padding(.top, 50)
.tag(0) .tag(0)
VStack(alignment: .center, spacing: 0) { ScrollView{
if(viewModel.requests.isEmpty){ VStack(alignment: .center, spacing: 0) {
EmptyInfo(emoji:"📬", title: "Aucune demande d'amis en attente", explain: "").padding(.top, 40) if(viewModel.requests.isEmpty){
} EmptyInfo(emoji:"📬", title: "Aucune demande d'amis en attente", explain: "").padding(.top, 40)
else{
ScrollView(showsIndicators: false){
ForEach(viewModel.requests, id: \.self) { request in
Friend(user: request, isRequest: true, viewModel: viewModel)
}
} }
.refreshable { else{
viewModel.getRequests() ScrollView(showsIndicators: false){
ForEach(viewModel.requests, id: \.self) { request in
Friend(user: request, isRequest: true, viewModel: viewModel)
}
}
.refreshable {
viewModel.getRequests()
}
.padding(.top, 25)
} }
.padding(.top, 25) 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;

@ -7,5 +7,21 @@
<key>NSExtensionPointIdentifier</key> <key>NSExtensionPointIdentifier</key>
<string>com.apple.widgetkit-extension</string> <string>com.apple.widgetkit-extension</string>
</dict> </dict>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSExceptionDomains</key>
<dict>
<key>codefirst.iut.uca.fr</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSIncludesSubdomains</key>
<true/>
</dict>
</dict>
</dict>
</dict> </dict>
</plist> </plist>

@ -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,10 +24,13 @@ 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,
"type": betTypeString(fromType: String(describing: type(of: bet))) "type": betTypeString(fromType: String(describing: type(of: bet)))
] ]
print(json)
return json return json
} }
@ -60,6 +63,8 @@ public class FactoryApiBet: FactoryBet {
} }
var participations: [Participation] = [] var participations: [Participation] = []
var wonParticipation: Participation?
var userParticipation: Participation?
if let participationsJson = json["participations"] as? [[String: Any]] { if let participationsJson = json["participations"] as? [[String: Any]] {
do { do {
@ -79,7 +84,53 @@ public class FactoryApiBet: FactoryBet {
} }
} }
return BetDetail(bet: bet, answers: answers, participations: participations) if let participationJson = json["wonParticipation"] as? [String: Any] {
do {
wonParticipation = try JSONDecoder().decode(Participation.self, from: JSONSerialization.data(withJSONObject: participationJson))
} catch {
print("Error decoding participation: \(error)")
}
}
if let participationUserJson = json["userParticipation"] as? [String: Any] {
do {
userParticipation = try JSONDecoder().decode(Participation.self, from: JSONSerialization.data(withJSONObject: participationUserJson))
} catch {
print("Error decoding participation: \(error)")
}
}
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 {

@ -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
@ -32,14 +32,11 @@ public class Bet: ObservableObject, Identifiable, Codable {
public private(set) var status: BetStatus public private(set) var status: BetStatus
/// List of users who are invited to participate in the bet. /// List of users who are invited to participate in the bet.
public private(set) var invited: [User] = [] public private(set) var invited: [String] = []
/// The user who created the bet. /// The user who created the bet.
public private(set) var author: String public private(set) var author: String
/// List of users who have registered for the bet.
public private(set) var registered: [User] = []
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case id case id
case theme case theme
@ -80,8 +77,7 @@ public class Bet: ObservableObject, Identifiable, Codable {
/// - status: The current status of the bet. /// - status: The current status of the bet.
/// - invited: List of users who are invited to participate in the bet. /// - invited: List of users who are invited to participate in the bet.
/// - author: The user who created the bet. /// - author: The user who created the bet.
/// - registered: List of users who have registered for the bet. public init(id: String, theme: String, phrase: String, endRegisterDate: Date, endBetDate: Date, isPrivate: Bool, status: BetStatus, invited: [String], author: String) {
public init(id: String, theme: String, phrase: String, endRegisterDate: Date, endBetDate: Date, isPrivate: Bool, status: BetStatus, invited: [User], author: String, registered: [User]) {
self.id = id self.id = id
self.theme = theme self.theme = theme
self.phrase = phrase self.phrase = phrase
@ -91,7 +87,6 @@ public class Bet: ObservableObject, Identifiable, Codable {
self.status = status self.status = status
self.invited = invited self.invited = invited
self.author = author self.author = author
self.registered = registered
} }
/// Custom Constructor without Id /// Custom Constructor without Id
@ -105,8 +100,7 @@ public class Bet: ObservableObject, Identifiable, Codable {
/// - status: The current status of the bet. /// - status: The current status of the bet.
/// - invited: List of users who are invited to participate in the bet. /// - invited: List of users who are invited to participate in the bet.
/// - author: The user who created the bet. /// - author: The user who created the bet.
/// - registered: List of users who have registered for the bet. public init(theme: String, phrase: String, endRegisterDate: Date, endBetDate: Date, isPrivate: Bool, status: BetStatus, invited: [String], author: String) {
public init(theme: String, phrase: String, endRegisterDate: Date, endBetDate: Date, isPrivate: Bool, status: BetStatus, invited: [User], author: String, registered: [User]) {
self.id = UUID().uuidString self.id = UUID().uuidString
self.theme = theme self.theme = theme
self.phrase = phrase self.phrase = phrase
@ -116,7 +110,13 @@ public class Bet: ObservableObject, Identifiable, Codable {
self.status = status self.status = status
self.invited = invited self.invited = invited
self.author = author self.author = author
self.registered = registered }
/// Function that returns an empty list of responses
///
/// - Returns: An empty list of responses
public func getResponses() -> [String] {
return []
} }
} }

@ -20,7 +20,19 @@ public class BetDetail: ObservableObject {
public private(set) var participations: [Participation] public private(set) var participations: [Participation]
/// The final answer selected for the bet, if available. /// The final answer selected for the bet, if available.
public private(set) var finalAnswer: String? public private(set) var wonParticipation: Participation?
public private(set) var userParticipation: Participation?
public var odds: Float? {
guard let wonParticipation = self.wonParticipation else {
return nil
}
if let matchingAnswer = self.answers.first(where: { $0.response == wonParticipation.answer }) {
return matchingAnswer.odds
} else {
return nil
}
}
/// Custom Constructor /// Custom Constructor
/// ///
@ -29,18 +41,19 @@ public class BetDetail: ObservableObject {
/// - answers: Details about the answers available for the bet. /// - answers: Details about the answers available for the bet.
/// - participations: List of user participations in the bet. /// - participations: List of user participations in the bet.
/// - finalAnswer: The final answer selected for the bet, if available. /// - finalAnswer: The final answer selected for the bet, if available.
public init(bet: Bet, answers: [AnswerDetail], participations: [Participation], finalAnswer: String? = nil) { public init(bet: Bet, answers: [AnswerDetail], participations: [Participation], wonParticipation: Participation? = nil, userParticipation: Participation? = nil) {
self.bet = bet self.bet = bet
self.answers = answers self.answers = answers
self.participations = participations self.participations = participations
self.finalAnswer = finalAnswer self.wonParticipation = wonParticipation
self.userParticipation = userParticipation
} }
/// Updates the final answer selected for the bet. /// Updates the final answer selected for the bet.
/// ///
/// - Parameter newFinalAnswer: The new final answer for the bet. /// - Parameter newFinalAnswer: The new final answer for the bet.
public func updateFinalAnswer(newFinalAnswer: String) { public func updateFinalAnswer(wonParticipation: Participation) {
self.finalAnswer = newFinalAnswer self.wonParticipation = wonParticipation
} }
/// Adds a new user participation to the list of participations for the bet. /// Adds a new user participation to the list of participations for the bet.

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

@ -33,13 +33,12 @@ public class MatchBet: Bet {
/// - status: The current status of the match bet. /// - status: The current status of the match bet.
/// - invited: List of users who are invited to participate in the match bet. /// - invited: List of users who are invited to participate in the match bet.
/// - author: The user who created the match bet. /// - author: The user who created the match bet.
/// - registered: List of users who have registered for the match bet.
/// - nameTeam1: The name of the first team involved in the match. /// - nameTeam1: The name of the first team involved in the match.
/// - nameTeam2: The name of the second team involved in the match. /// - nameTeam2: The name of the second team involved in the match.
public init(id: String, theme: String, phrase: String, endRegisterDate: Date, endBetDate: Date, isPrivate: Bool, status: BetStatus, invited: [User], author: String, registered: [User], nameTeam1: String, nameTeam2: String) { public init(id: String, theme: String, phrase: String, endRegisterDate: Date, endBetDate: Date, isPrivate: Bool, status: BetStatus, invited: [String], author: String, nameTeam1: String, nameTeam2: String) {
self.nameTeam1 = nameTeam1 self.nameTeam1 = nameTeam1
self.nameTeam2 = nameTeam2 self.nameTeam2 = nameTeam2
super.init(id: id, theme: theme, phrase: phrase, endRegisterDate: endRegisterDate, endBetDate: endBetDate, isPrivate: isPrivate, status: status, invited: invited, author: author, registered: registered) super.init(id: id, theme: theme, phrase: phrase, endRegisterDate: endRegisterDate, endBetDate: endBetDate, isPrivate: isPrivate, status: status, invited: invited, author: author)
} }
/// Custom Constructor without Id /// Custom Constructor without Id
@ -53,13 +52,12 @@ public class MatchBet: Bet {
/// - status: The current status of the match bet. /// - status: The current status of the match bet.
/// - invited: List of users who are invited to participate in the match bet. /// - invited: List of users who are invited to participate in the match bet.
/// - author: The user who created the match bet. /// - author: The user who created the match bet.
/// - registered: List of users who have registered for the match bet.
/// - nameTeam1: The name of the first team involved in the match. /// - nameTeam1: The name of the first team involved in the match.
/// - nameTeam2: The name of the second team involved in the match. /// - nameTeam2: The name of the second team involved in the match.
public init(theme: String, phrase: String, endRegisterDate: Date, endBetDate: Date, isPrivate: Bool, status: BetStatus, invited: [User], author: String, registered: [User], nameTeam1: String, nameTeam2: String) { public init(theme: String, phrase: String, endRegisterDate: Date, endBetDate: Date, isPrivate: Bool, status: BetStatus, invited: [String], author: String, nameTeam1: String, nameTeam2: String) {
self.nameTeam1 = nameTeam1 self.nameTeam1 = nameTeam1
self.nameTeam2 = nameTeam2 self.nameTeam2 = nameTeam2
super.init(theme: theme, phrase: phrase, endRegisterDate: endRegisterDate, endBetDate: endBetDate, isPrivate: isPrivate, status: status, invited: invited, author: author, registered: registered) super.init(theme: theme, phrase: phrase, endRegisterDate: endRegisterDate, endBetDate: endBetDate, isPrivate: isPrivate, status: status, invited: invited, author: author)
} }
public required init(from decoder: Decoder) throws { public required init(from decoder: Decoder) throws {

@ -10,9 +10,8 @@ import Foundation
/// A struct representing a user with details such as username, email, number of coins, and friends. /// A struct representing a user with details such as username, email, number of coins, and friends.
public struct User: Codable, Hashable, Identifiable { public struct User: Codable, Hashable, Identifiable {
public var id: String { public var id: String
return email
}
/// The username of the user. /// The username of the user.
public private(set) var username: String public private(set) var username: String
@ -49,6 +48,7 @@ public struct User: Codable, Hashable, Identifiable {
/// - nbFriends: The number of friends the user has. /// - nbFriends: The number of friends the user has.
/// - bestWin: The user's best win. /// - bestWin: The user's best win.
public init(username: String, email: String, nbCoins: Int, friendStatus: FriendStatus? = nil, image: String? = nil, nbBets: Int, nbFriends: Int, bestWin: Int) { public init(username: String, email: String, nbCoins: Int, friendStatus: FriendStatus? = nil, image: String? = nil, nbBets: Int, nbFriends: Int, bestWin: Int) {
self.id = UUID().description
self.username = username self.username = username
self.email = email self.email = email
self.nbCoins = nbCoins self.nbCoins = nbCoins

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

@ -38,8 +38,7 @@ struct Stub {
isPrivate: false, isPrivate: false,
status: .inProgress, status: .inProgress,
invited: [], invited: [],
author: "Lucas", author: "Lucas"
registered: [user2]
) )
self.bets.append(bet1) self.bets.append(bet1)
@ -50,9 +49,8 @@ struct Stub {
endBetDate: Date().addingTimeInterval(259200), endBetDate: Date().addingTimeInterval(259200),
isPrivate: false, isPrivate: false,
status: .inProgress, status: .inProgress,
invited: [user3], invited: [],
author: "Lucas", author: "Lucas"
registered: [user2]
) )
self.bets.append(bet2) self.bets.append(bet2)
@ -64,8 +62,7 @@ struct Stub {
isPrivate: true, isPrivate: true,
status: .finished, status: .finished,
invited: [], invited: [],
author: "Lucas", author: "Lucas"
registered: [user2, user1, user3]
) )
self.bets.append(bet3) self.bets.append(bet3)
@ -76,9 +73,8 @@ struct Stub {
endBetDate: Date().addingTimeInterval(432000), endBetDate: Date().addingTimeInterval(432000),
isPrivate: false, isPrivate: false,
status: .finished, status: .finished,
invited: [user1], invited: [],
author: "Lucase", author: "Lucase"
registered: [user3]
) )
self.bets.append(bet4) self.bets.append(bet4)
@ -95,7 +91,7 @@ struct Stub {
} }
public mutating func add(bet: Bet) { public mutating func add(bet: Bet) {
let newBetDetail = BetDetail(bet: bet, answers: [], participations: [], finalAnswer: "test") let newBetDetail = BetDetail(bet: bet, answers: [], participations: [])
self.betsDetail.append(newBetDetail) self.betsDetail.append(newBetDetail)
} }
} }

Loading…
Cancel
Save