Compare commits

..

5 Commits

Author SHA1 Message Date
Lucas DELANIER 51a94cc80d merge conflits
11 months ago
Lucas DELANIER 6d03ace732 Merge branch 'master'
11 months ago
Lucas DELANIER 560ee2f4c5 merge
11 months ago
Lucas DELANIER fbdf863df7 change defaultimage by userpicture component
11 months ago
Lucas DELANIER 25cb5e9de4 username display finished
11 months ago

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 354 KiB

@ -32,43 +32,7 @@
- MVVM
<div align = center>
<img src="https://codefirst.iut.uca.fr/git/AllDev/Gestion_de_projet/raw/branch/master/Documentation/Diagrammes/AllInMVVM.png" width="600" />
</div>
# Fonctionnement
- ### Comment lancer le projet ?
:information_source: *Assurez-vous d'avoir un Mac à disposition*
Tout d'abord si ce n'est pas fait cloner le dépôt de la branche **master/main**, pour cela copier le lien URL du dépôt git :
<div align = center>
![Comment cloner](Documentation/Images/HowToClone.png)
</div>
Sur votre Mac, ouvrez l'IDE **Xcode** (disponible via l'App Store), puis cloner le dépôt en utilisant l'URL copiée précédemment :
<div align = center>
<img src="Documentation/Images/WelcomeToXcode.png" width="500" >
</div>
Vous serez alors redirigé par l'IDE et pourrez lancer l'application sur l'appareil de votre choix :
<div align = center>
<img src="Documentation/Images/LaunchApp.png" width="900" >
</div>
*Si vous souhaitez lancer l'application sur votre appareil personnel, il sera nécessaire de renseigner votre compte iCloud dans l'IDE !*
# Technologies

@ -1,21 +0,0 @@
{
"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.

Before

Width:  |  Height:  |  Size: 3.4 KiB

@ -1,21 +0,0 @@
{
"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.

Before

Width:  |  Height:  |  Size: 39 KiB

@ -1,21 +0,0 @@
{
"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.

Before

Width:  |  Height:  |  Size: 3.5 KiB

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

@ -44,8 +44,8 @@ struct BetCard: View {
VStack(alignment: .leading,spacing: 2){
HStack{
Spacer()
UsersPreview(users: [])
Text("bet_players_waiting_format \(bet.invited.count.description)")
UsersPreview(users: bet.registered)
Text("bet_players_waiting_format \(bet.registered.count.description)")
.font(.system(size: 15))
.foregroundColor(AllInColors.grey800Color)
.fontWeight(.medium)
@ -77,10 +77,11 @@ struct BetCard_Previews: PreviewProvider {
phrase: "Le gagnant de la finale sera l'équipe avec le plus de tirs au but.",
endRegisterDate: Date().addingTimeInterval(86400),
endBetDate: Date().addingTimeInterval(172800),
isPrivate: false,
isPublic: true,
status: .inProgress,
invited: [],
author: "Imri"))
author: "Imri",
registered: []))
.preferredColorScheme(.dark)
}
}

@ -11,12 +11,12 @@ import Model
struct BetLineLoading: View {
@State var showInfos: Bool = false
var bet: BetDetail
var participations: [Participation]
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
let totalParticipations = participations.count
let numberOfYes = participations.filter { $0.answer.uppercased() == "YES" }.count
let numberOfNo = participations.filter { $0.answer.uppercased() == "NO" }.count
if(numberOfNo == 0 && numberOfYes == 0){
return 0.5
}
@ -25,22 +25,144 @@ struct BetLineLoading: View {
}
var yesParticipations: [Participation] {
bet.participations.filter { $0.answer.uppercased() == "YES" }
participations.filter { $0.answer.uppercased() == "YES" }
}
var noParticipations: [Participation] {
bet.participations.filter { $0.answer.uppercased() == "NO" }
participations.filter { $0.answer.uppercased() == "NO" }
}
var body: some View {
switch bet.bet {
case is BinaryBet:
BinaryBetLine(bet: bet)
case is CustomBet:
CustomBetLine(bet: bet)
default:
BinaryBetLine(bet: bet)
VStack(alignment: .leading, spacing: 0) {
HStack(spacing: 5) {
Text("OUI")
.font(.system(size: 25))
.fontWeight(.bold)
.foregroundColor(AllInColors.blue200)
Spacer()
Text("NON")
.font(.system(size: 25))
.fontWeight(.bold)
.foregroundColor(AllInColors.pink100)
}
GeometryReader { geometry in
ZStack(alignment: .leading) {
HStack{
Spacer()
Rectangle()
.frame(width: min(CGFloat(1-self.value) * geometry.size.width, geometry.size.width), height: 17)
.foregroundStyle(AllInColors.PinkBetGradiant).cornerRadius(999)
}
HStack(spacing: 0) {
Rectangle()
.frame(width: min(CGFloat(self.value) * geometry.size.width, geometry.size.width), height: 17)
.foregroundStyle(AllInColors.BlueBetGradiant)
.cornerRadius(999)
Image("loadingHeartIcon")
.resizable()
.frame(width: 29, height: 32)
.padding(.leading, -10)
}
}
.padding(.bottom, 5)
}
.frame(height: 40)
HStack {
Spacer()
Text("bet_status_details_drawer")
.textStyle(weight: .medium, color: AllInColors.primaryTextColor, size: 10)
Image(showInfos ? "chevronUpIcon" : "chevronDownIcon")
.resizable()
.frame(width: 10, height: 7)
.scaledToFill()
}
.onTapGesture {
withAnimation {
showInfos.toggle()
}
}
.padding(.bottom, 5)
.padding(.trailing, 5)
if showInfos {
VStack(spacing: 1) {
HStack(spacing: 5) {
Image("blueAllCoinIcon")
.resizable()
.frame(width:12, height: 12)
Text(yesParticipations.reduce(0, {x,y in x + y.stake}).description)
.font(.system(size: 15))
.fontWeight(.bold)
.foregroundColor(AllInColors.blue200)
Spacer()
Text(noParticipations.reduce(0, {x,y in x + y.stake}).description)
.font(.system(size: 15))
.fontWeight(.bold)
.foregroundColor(AllInColors.pink100)
Image("pinkAllCoinIcon")
.resizable()
.frame(width: 12, height: 12)
}
HStack(spacing: 5){
Image("bluePersonIcon")
.resizable()
.frame(width: 12, height: 12)
Text(yesParticipations.count.description)
.font(.system(size: 15))
.fontWeight(.bold)
.foregroundColor(AllInColors.blue200)
Spacer()
Text(noParticipations.count.description)
.font(.system(size: 15))
.fontWeight(.bold)
.foregroundColor(AllInColors.pink100)
Image("pinkPersonIcon")
.resizable()
.frame(width: 12, height: 12)
}
HStack(spacing: 5){
Image("blueBadgeIcon")
.resizable()
.frame(width: 12, height: 12)
Text(yesParticipations.max(by: { $0.stake < $1.stake })?.stake.description ?? "0")
.font(.system(size: 15))
.fontWeight(.bold)
.foregroundColor(AllInColors.blue200)
Spacer()
Text(noParticipations.max(by: { $0.stake < $1.stake })?.stake.description ?? "0")
.font(.system(size: 15))
.fontWeight(.bold)
.foregroundColor(AllInColors.pink100)
Image("pinkBadgeIcon")
.resizable()
.frame(width: 12, height: 12)
}
HStack(spacing: 5){
Image("blueTrophyIcon")
.resizable()
.frame(width: 12, height: 12)
Text("1.2")
.font(.system(size: 15))
.fontWeight(.bold)
.foregroundColor(AllInColors.blue200)
Spacer()
Text("1.2")
.font(.system(size: 15))
.fontWeight(.bold)
.foregroundColor(AllInColors.pink100)
Image("pinkTrophyIcon")
.resizable()
.frame(width:12, height: 12)
}
}
}
}
}
}

@ -1,168 +0,0 @@
//
// BinaryBetLine.swift
// AllIn
//
// Created by Lucas Delanier on 10/06/2024.
//
import SwiftUI
import Model
struct BinaryBetLine: View {
@State var showInfos: Bool = false
var bet: BetDetail
var value: CGFloat {
let totalParticipations = bet.participations.count
let numberOfYes = bet.participations.filter { $0.answer.uppercased() == "YES" }.count
let numberOfNo = bet.participations.filter { $0.answer.uppercased() == "NO" }.count
if(numberOfNo == 0 && numberOfYes == 0){
return 0.5
}
return totalParticipations > 0 ? CGFloat(numberOfYes) / CGFloat(totalParticipations) : 0.0
}
var yesParticipations: [Participation] {
bet.participations.filter { $0.answer.uppercased() == "YES" }
}
var noParticipations: [Participation] {
bet.participations.filter { $0.answer.uppercased() == "NO" }
}
var body: some View {
VStack(alignment: .leading, spacing: 0) {
HStack(spacing: 5) {
Text("OUI")
.font(.system(size: 25))
.fontWeight(.bold)
.foregroundColor(AllInColors.blue200)
Spacer()
Text("NON")
.font(.system(size: 25))
.fontWeight(.bold)
.foregroundColor(AllInColors.pink100)
}
GeometryReader { geometry in
ZStack(alignment: .leading) {
HStack{
Spacer()
Rectangle()
.frame(width: min(CGFloat(1-self.value) * geometry.size.width, geometry.size.width-20), height: 17)
.foregroundStyle(AllInColors.PinkBetGradiant).cornerRadius(999)
}
HStack(spacing: 0) {
Rectangle()
.frame(width: min(CGFloat(self.value) * geometry.size.width, geometry.size.width-20), height: 17)
.foregroundStyle(AllInColors.BlueBetGradiant)
.cornerRadius(999)
Image("loadingHeartIcon")
.resizable()
.frame(width: 29, height: 32)
.padding(.leading, -10)
}
}
.padding(.bottom, 5)
}
.frame(height: 40)
HStack {
Spacer()
Text("bet_status_details_drawer")
.textStyle(weight: .medium, color: AllInColors.primaryTextColor, size: 10)
Image(showInfos ? "chevronUpIcon" : "chevronDownIcon")
.resizable()
.frame(width: 10, height: 7)
.scaledToFill()
}
.onTapGesture {
withAnimation {
showInfos.toggle()
}
}
.padding(.bottom, 5)
.padding(.trailing, 5)
if showInfos {
VStack(spacing: 1) {
HStack(spacing: 5) {
Image("blueAllCoinIcon")
.resizable()
.frame(width:12, height: 12)
Text(yesParticipations.reduce(0, {x,y in x + y.stake}).description)
.font(.system(size: 15))
.fontWeight(.bold)
.foregroundColor(AllInColors.blue200)
Spacer()
Text(noParticipations.reduce(0, {x,y in x + y.stake}).description)
.font(.system(size: 15))
.fontWeight(.bold)
.foregroundColor(AllInColors.pink100)
Image("pinkAllCoinIcon")
.resizable()
.frame(width: 12, height: 12)
}
HStack(spacing: 5){
Image("bluePersonIcon")
.resizable()
.frame(width: 12, height: 12)
Text(yesParticipations.count.description)
.font(.system(size: 15))
.fontWeight(.bold)
.foregroundColor(AllInColors.blue200)
Spacer()
Text(noParticipations.count.description)
.font(.system(size: 15))
.fontWeight(.bold)
.foregroundColor(AllInColors.pink100)
Image("pinkPersonIcon")
.resizable()
.frame(width: 12, height: 12)
}
HStack(spacing: 5){
Image("blueBadgeIcon")
.resizable()
.frame(width: 12, height: 12)
Text(yesParticipations.max(by: { $0.stake < $1.stake })?.stake.description ?? "0")
.font(.system(size: 15))
.fontWeight(.bold)
.foregroundColor(AllInColors.blue200)
Spacer()
Text(noParticipations.max(by: { $0.stake < $1.stake })?.stake.description ?? "0")
.font(.system(size: 15))
.fontWeight(.bold)
.foregroundColor(AllInColors.pink100)
Image("pinkBadgeIcon")
.resizable()
.frame(width: 12, height: 12)
}
HStack(spacing: 5){
Image("blueTrophyIcon")
.resizable()
.frame(width: 12, height: 12)
Text("1.2")
.font(.system(size: 15))
.fontWeight(.bold)
.foregroundColor(AllInColors.blue200)
Spacer()
Text("1.2")
.font(.system(size: 15))
.fontWeight(.bold)
.foregroundColor(AllInColors.pink100)
Image("pinkTrophyIcon")
.resizable()
.frame(width:12, height: 12)
}
}
}
}
}
}

@ -15,29 +15,22 @@ struct ChoiceFinalAnswerCell: View {
var answer: AnswerDetail
var rawColor = AllInColors.blueAccentColor
var body: some View {
ZStack {
HStack {
ZStack{
HStack{
Spacer()
Text(answer.response)
.textStyle(weight: .bold, color: selected ? .white : rawColor, size: 40)
.padding(.vertical, 10)
.textStyle(weight: .bold, color: selected ? .white :rawColor, size: 40).padding(.vertical, 10)
Spacer()
}
HStack {
HStack{
Spacer()
OddCapsule(
backgroundColor: selected ? .white : AllInColors.purpleAccentColor,
foregroundColor: selected ? AllInColors.purpleAccentColor : .white,
odd: answer.odds
)
.padding(.trailing, 20)
.scaleEffect(0.9)
OddCapsule(backgroundColor: selected ? .white : AllInColors.purpleAccentColor, foregroundColor: selected ? AllInColors.purpleAccentColor : .white ,odd: answer.odds ).padding(.trailing,20).scaleEffect(0.9)
}
}
.background(selected ? AllInColors.purpleAccentColor : .white)
.cornerRadius(17)
.scaleEffect(selected ? 1.02 : 1)
.animation(.easeInOut(duration: 0.3), value: selected)
}.background(selected ? AllInColors.purpleAccentColor : .white).cornerRadius(17).scaleEffect(selected ? 1.02 : 1).animation(
.easeInOut(duration: 0.3),
value: selected
)
}
}

@ -1,141 +0,0 @@
//
// 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,3 +1,12 @@
//
// DropDownAnswerMenu.swift
// AllIn
//
// Created by Lucas Delanier on 16/01/2024.
//
import SwiftUI
//
// DropDownMenu.swift
// AllIn
@ -6,21 +15,20 @@
//
import SwiftUI
import Model
struct DropDownAnswerMenu: View {
@State var expand = false
@Binding var selectedAnswer: AnswerDetail
var answers: [AnswerDetail]
@Binding var selectedOption: Int
var options: [(Int, String, Float)]
var body: some View {
VStack(spacing: 0, content: {
Button(action: { withTransaction(Transaction(animation: nil)) { self.expand.toggle() } }) {
Button(action: { self.expand.toggle() }) {
HStack{
Text(selectedAnswer.response)
Text(options[selectedOption].1.description)
.textStyle(weight: .bold, color: AllInColors.blueAccentColor, size: 20)
Text("\(selectedAnswer.odds, specifier: "%.2f")")
Text(options[selectedOption].2.description)
.textStyle(weight: .bold, color: AllInColors.lightPurpleColor, size: 10)
Spacer()
@ -35,21 +43,19 @@ struct DropDownAnswerMenu: View {
.foregroundColor(AllInColors.delimiterGrey)
.padding(.bottom, 18)
VStack(spacing: 0) {
ForEach(answers) { (answer: AnswerDetail) in
if answer != selectedAnswer {
Button(action: {
self.selectedAnswer = answer
self.expand.toggle()}
) {
HStack{
Text(answer.response)
.textStyle(weight: .bold, color: AllInColors.blueAccentColor, size: 20)
Text("\(answer.odds, specifier: "%.2f")")
.textStyle(weight: .bold, color: AllInColors.lightPurpleColor, size: 10)
Spacer()
ForEach(0..<options.count, id: \.self) { index in
if options[index].0 != selectedOption {
Button(action: {self.selectedOption = options[index].0
self.expand.toggle()}) {
HStack{
Text(options[index].1.description)
.textStyle(weight: .bold, color: AllInColors.blueAccentColor, size: 20)
Text(options[index].2.description)
.textStyle(weight: .bold, color: AllInColors.lightPurpleColor, size: 10)
Spacer()
}
}
}
.padding(.bottom, 15)
.padding(.bottom, 15)
}
}
}
@ -64,3 +70,15 @@ 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,19 +6,25 @@
//
import SwiftUI
import Model
struct DropDownFriends: View {
@Binding var selectedItems: Set<String>
@State private var selectedItems: Set<Int> = []
@State var expand = false
var friends: [User]
let friends: [(Int, Int, String, String)] = [
(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 {
VStack(spacing: 0, content: {
Button(action: { self.expand.toggle() }) {
HStack(spacing: 3){
Text(friends.count.description)
Text("41")
.textStyle(weight: .bold, color: AllInColors.primaryTextColor, size: 15)
Text("bet_creation_friends_available_format")
.textStyle(weight: .regular, color: AllInColors.grey800Color, size: 15)
@ -36,33 +42,33 @@ struct DropDownFriends: View {
.foregroundColor(AllInColors.delimiterGrey)
ScrollView(.vertical) {
VStack(spacing: 0) {
ForEach(friends, id: \.self) { (friend: User) in
ForEach(0..<friends.count, id: \.self) { item in
HStack {
Circle()
.fill(selectedItems.contains(friend.id) ? AllInColors.lightPurpleColor : Color.clear)
.fill(selectedItems.contains(friends[item].0) ? AllInColors.lightPurpleColor : Color.clear)
.overlay(
Circle()
.stroke(selectedItems.contains(friend.id) ? Color.clear : AllInColors.skyBlueColor, lineWidth: 1)
.stroke(selectedItems.contains(friends[item].0) ? Color.clear : AllInColors.skyBlueColor, lineWidth: 1)
)
.frame(width: 15, height: 15)
.padding(.trailing, 5)
UserInfo(username: friend.username, value: friend.nbCoins)
UserInfo(username: "", value: 0)
.contentShape(Rectangle())
}
.padding([.leading, .trailing], 15)
.padding([.top, .bottom], 5)
.overlay(
selectedItems.contains(friend.id) ?
selectedItems.contains(friends[item].0) ?
Rectangle()
.fill(AllInColors.lightPurpleColor.opacity(0.13))
: nil
)
.opacity(1.0)
.onTapGesture {
if selectedItems.contains(friend.id) {
selectedItems.remove(friend.id)
if selectedItems.contains(friends[item].0) {
selectedItems.remove(friends[item].0)
} else {
selectedItems.insert(friend.id)
selectedItems.insert(friends[item].0)
}
}
Rectangle()
@ -84,3 +90,9 @@ struct DropDownFriends: View {
)
}
}
struct DropDownFriends_Previews: PreviewProvider {
static var previews: some View {
DropDownFriends()
}
}

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

@ -12,21 +12,23 @@ struct Menu: View {
@Inject var authService: IAuthService
let parameters: [(String, String, String, String)] = [
("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"),
("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"),
("eyesImage", String(localized: "drawer_current_bets"), String(localized: "drawer_current_bets_subtitle"), "Current")
]
var body: some View {
VStack(alignment: .leading, spacing: 10) {
HStack() {
Spacer()
VStack(){
UserPicture(picture: AppStateContainer.shared.user?.image, username: (AppStateContainer.shared.user?.username)!,size: 100)
UserPicture(username: (AppStateContainer.shared.user?.username)!, size: 100)
Text(AppStateContainer.shared.user?.username.capitalized ?? "")
.fontWeight(.medium)
.font(.system(size: 17))
@ -39,7 +41,7 @@ struct Menu: View {
HStack(spacing: 30) {
Spacer()
VStack(){
Text(AppStateContainer.shared.user?.bestWin ?? 0, format: .number)
Text(AppStateContainer.shared.user?.bestWin.description ?? "0")
.fontWeight(.heavy)
.font(.system(size: 15))
.foregroundColor(.white)
@ -49,7 +51,7 @@ struct Menu: View {
.foregroundColor(AllInColors.grey600Color)
}
VStack(){
Text(AppStateContainer.shared.user!.bestWin, format: .number)
Text(AppStateContainer.shared.user?.bestWin.description ?? "0")
.fontWeight(.heavy)
.font(.system(size: 15))
.foregroundColor(.white)
@ -59,7 +61,7 @@ struct Menu: View {
.foregroundColor(AllInColors.grey600Color)
}
VStack(){
Text(AppStateContainer.shared.user!.nbFriends, format: .number)
Text(AppStateContainer.shared.user?.nbFriends.description ?? "0")
.fontWeight(.heavy)
.font(.system(size: 15))
.foregroundColor(.white)
@ -103,9 +105,6 @@ struct Menu: View {
.frame(maxWidth: .infinity,alignment: .leading)
.background(AllInColors.primaryColor)
.edgesIgnoringSafeArea(.bottom)
.onAppear {
authService.refreshAuthentication()
}
}
}

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

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

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

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

@ -6,29 +6,26 @@
//
import SwiftUI
import Model
struct ResultBanner: View {
var finalAnswer: Participation
var odds: Float
var body: some View {
VStack{
HStack{
Image(systemName: "trophy.fill").resizable().frame(maxWidth: 70, maxHeight: 60).foregroundColor(AllInColors.blueGrey800Color)
Text(finalAnswer.answer).font(.system(size: 70)).fontWeight(.bold).foregroundStyle(AllInColors.blueGrey800Color)
Image("BleueTrophyIcon").resizable().frame(maxWidth: 70, maxHeight: 60)
Text("OUI").font(.system(size: 70)).fontWeight(.bold).foregroundStyle(AllInColors.blueGrey800Color)
}.frame(height: 80)
HStack(spacing: 20){
HStack{
Image("blueAllCoinIcon").resizable().frame(maxWidth: 12, maxHeight: 12)
Text(finalAnswer.stake.description).font(.system(size: 16)).fontWeight(.semibold).foregroundStyle(AllInColors.blueGrey800Color)
Image("BlueAllCoinIcon").resizable().frame(maxWidth: 12, maxHeight: 12)
Text("460").font(.system(size: 16)).fontWeight(.semibold).foregroundStyle(AllInColors.blueGrey800Color)
}
HStack{
Image("bluePersonIcon").resizable().frame(maxWidth: 15, maxHeight: 12)
Text(finalAnswer.username).font(.system(size: 16)).fontWeight(.semibold).foregroundStyle(AllInColors.blueGrey800Color)
Image("BleuePersonIcon").resizable().frame(maxWidth: 15, maxHeight: 12)
Text("ImriDu43").font(.system(size: 16)).fontWeight(.semibold).foregroundStyle(AllInColors.blueGrey800Color)
}
HStack{
Image("blueTrophyIcon").resizable().frame(maxWidth: 15, maxHeight: 12)
Text("\(odds, specifier: "%.2f")").font(.system(size: 16)).fontWeight(.semibold).foregroundStyle(AllInColors.blueGrey800Color)
Image("BleueTrophyIcon").resizable().frame(maxWidth: 15, maxHeight: 12)
Text("x1.2").font(.system(size: 16)).fontWeight(.semibold).foregroundStyle(AllInColors.blueGrey800Color)
}
}
}

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

@ -77,9 +77,10 @@ struct TrendingBetCard_Previews: PreviewProvider {
phrase: "Le gagnant de la finale sera l'équipe avec le plus de tirs au but.",
endRegisterDate: Date().addingTimeInterval(86400),
endBetDate: Date().addingTimeInterval(172800),
isPrivate: true,
isPublic: true,
status: .inProgress,
invited: [],
author: "Imri"))
author: "Imri",
registered: []))
}
}

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

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

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

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

@ -46,13 +46,15 @@
"drawer_nb_friends" = "Friends";
"drawer_friends" = "FRIENDS";
"drawer_friends_subtitle" = "Challenge your folks by adding them as friends.";
"drawer_public_bets" = "PARTICIPATE";
"drawer_public_bets" = "PUBLIC BETS";
"drawer_public_bets_subtitle" = "Browse the most popular bets of the moment.";
"drawer_create_a_bet" = "NEW BET";
"drawer_current_bets" = "CURRENT BETS";
"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_bet_history" = "HISTORY";
"drawer_bet_history" = "BET HISTORY";
"drawer_bet_history_subtitle" = "View your current and finished bets.";
"drawer_current_bets" = "MY PARTICIPATIONS";
"drawer_current_bets" = "CURRENT BETS";
"drawer_current_bets_subtitle" = "Manage your bets and reward the winners.";
"drawer_ranking" = "RANKING";
"drawer_ranking_subtitle" = "Check your ranking among your friends.";
@ -133,7 +135,7 @@
"bet_participate" = "Participate";
"bet_proposed_by_format %@" = "Proposed by %@";
"bet_proposed_by_format" = "Proposed by";
"bet_players_waiting_format %@" = "%@ players waiting";
"bet_players_waiting_format %@" = "%@ joueurs en attente";
"bet_players_format" = "players";
"bet_points_at_stake_format" = "points at stake";
@ -148,7 +150,6 @@
"bet_status_participants_list" = "Participants";
"bet_status_details_drawer" = "Details";
"participation_possible_winnings" = "Possible winnings";
"bet_status_stake" = "Gambled";
/// Bet history
@ -178,7 +179,7 @@
/// Notification
"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
@ -187,8 +188,3 @@
"empty_bets_title"= "No bet matches your search";
"empty_friends_title" = "You don't have any friends yet";
"empty_friends_explain" = "Add them from this screen";
/// Error Messages
"error_title" = "Error: Failed to Load Content.";
"error_message" = "The content you are trying to load could not be retrieved. Please check your internet connection and try again";

@ -46,13 +46,15 @@
"drawer_nb_friends" = "Amis";
"drawer_friends" = "AMIS";
"drawer_friends_subtitle" = "Défiez vos proches en les ajoutant en amis.";
"drawer_public_bets" = "PARTICIPER";
"drawer_public_bets" = "BETS PUBLIQUES";
"drawer_public_bets_subtitle" = "Parcourez les bets les plus populaires du moment.";
"drawer_create_a_bet" = "NOUVEAU BET";
"drawer_current_bets" = "BETS EN COURS";
"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_bet_history" = "HISTORIQUE";
"drawer_bet_history" = "HISTORIQUE DES BETS";
"drawer_bet_history_subtitle" = "Consultez vos paris en cours et terminés.";
"drawer_current_bets" = "MES PARTICIPATIONS";
"drawer_current_bets" = "BETS EN COURS";
"drawer_current_bets_subtitle" = "Gérez vos bets et récompensez les gagnants.";
"drawer_ranking" = "CLASSEMENT";
"drawer_ranking_subtitle" = "Consultez votre classement parmis vos amis.";
@ -133,7 +135,7 @@
"bet_participate" = "Participer";
"bet_proposed_by_format %@" = "Proposé par %@";
"bet_proposed_by_format" = "Proposé par";
"bet_players_waiting_format %@" = "%@ joueurs en attente";
"bet_players_waiting_format %@" = "%@ players waiting";
"bet_players_format" = "joueurs";
"bet_points_at_stake_format" = "points en jeu";
@ -148,7 +150,6 @@
"bet_status_participants_list" = "Liste des participants";
"bet_status_details_drawer" = "Détails";
"participation_possible_winnings" = "Gains possibles";
"bet_status_stake" = "Pariés";
/// Bet history
@ -187,8 +188,3 @@
"empty_bets_title"= "Aucun Bet ne correspond à votre recherche";
"empty_friends_title" = "Vous navez pas encore damis";
"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.";

@ -1,71 +0,0 @@
//
// 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,13 +13,11 @@ import Combine
class BetViewModel: ObservableObject {
@Inject var manager: Manager
@Inject var authService: IAuthService
@Published var popularBet: Bet?
@Published private(set) var bets: [Bet] = []
@Published var betsOver: [BetDetail] = []
@Published var betsWon: [BetResultDetail] = []
@Published var showingSheetOver: Bool = false
@Published var showingSheetWon: Bool = false
@Published var showingSheet: Bool = false
@Published var filters: Set<BetFilter> = [] {
didSet {
getItems()
@ -29,8 +27,6 @@ class BetViewModel: ObservableObject {
init() {
getItems()
getPopularBet()
getBetsOver()
getBetsWon()
}
func getItems() {
@ -39,26 +35,11 @@ class BetViewModel: ObservableObject {
self.bets = bets
}
}
}
func getBetsOver() {
manager.getBetsOver() { bets in
DispatchQueue.main.async {
self.betsOver = bets
if !self.betsOver.isEmpty {
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()
self.showingSheet = true
}
}
}

@ -15,67 +15,30 @@ class CreationBetViewModel: ObservableObject {
@Inject var manager: Manager
@Published var theme: String = ""
@Published var description: String = ""
@Published var isPrivate = false {
didSet {
invited.removeAll()
}
}
@Published var isPublic = true
@Published var endRegisterDate = Date()
@Published var endBetDate = Date()
@Published var betAdded = false
@Published var selectedTypeBet = 0 {
didSet {
values.removeAll()
groupedItems.removeAll()
response = ""
}
}
@Published var values: [String] = []
@Published var invited: Set<String> = []
@Published var selectedOption = 0
@Published var themeFieldError: String?
@Published var descriptionFieldError: String?
@Published var endRegisterDateFieldError: String?
@Published var endBetDateFieldError: String?
@Published var responsesFieldError: String?
@Published var errorMessage: String?
@Published var showErrorMessage = false
@Published var friends: [User] = []
let options: [(Int, String, String)] = [
(0, "questionMarkIcon", String(localized: "bet_type_binary")),
(1, "footballIcon", String(localized: "bet_type_match")),
(2, "paintbrushIcon", String(localized: "bet_type_custom"))
]
@Published var response = ""
@Published var groupedItems: [[String]] = [[String]] ()
init() {
getFriends()
}
func getFriends() {
manager.getFriends() { friends in
DispatchQueue.main.async {
self.friends = friends
}
}
}
func create() {
guard checkAndSetError(forTheme: true, forDescription: true, forEndRegisterDate: true, forEndBetDate: true, forResponse: true) else {
guard checkAndSetError(forTheme: true, forDescription: true, forEndRegisterDate: true, forEndBetDate: true) else {
return
}
resetAllFieldErrors()
if let user = AppStateContainer.shared.user {
manager.addBet(bet: toBet(theme: theme, description: description, endRegister: endRegisterDate, endBet: endBetDate, isPrivate: isPrivate, status: .inProgress, creator: user.username, invited: Array(invited), type: selectedTypeBet)) { statusCode in
print(statusCode)
manager.addBet(bet: toBet(theme: theme, description: description, endRegister: endRegisterDate, endBet: endBetDate, isPublic: isPublic, status: .inProgress, creator: user.username, type: selectedOption)) { statusCode in
switch statusCode {
case 201:
self.betAdded = true
@ -89,13 +52,12 @@ class CreationBetViewModel: ObservableObject {
}
}
func checkAndSetError(forTheme checkTheme: Bool, forDescription checkDescription: Bool, forEndRegisterDate checkEndRegisterDate: Bool, forEndBetDate checkEndBetDate: Bool, forResponse checkResponse: Bool) -> Bool {
func checkAndSetError(forTheme checkTheme: Bool, forDescription checkDescription: Bool, forEndRegisterDate checkEndRegisterDate: Bool, forEndBetDate checkEndBetDate: Bool) -> Bool {
var newThemeFieldError: String?
var newDescriptionFieldError: String?
var newEndRegisterDateFieldError: String?
var newEndBetDateFieldError: String?
var newResponsesFieldError: String?
var hasError = false
@ -123,19 +85,6 @@ class CreationBetViewModel: ObservableObject {
hasError = true
}
if checkResponse, selectedTypeBet == 2 {
if values.count < 2 {
newResponsesFieldError = "Il doit y'avoir 2 réponses minimum"
hasError = true
} else {
let uniqueValues = Set(values)
if uniqueValues.count != values.count {
newResponsesFieldError = "Les réponses doivent être uniques"
hasError = true
}
}
}
if !hasError {
// No error
return true
@ -146,7 +95,6 @@ class CreationBetViewModel: ObservableObject {
descriptionFieldError = newDescriptionFieldError
endRegisterDateFieldError = newEndRegisterDateFieldError
endBetDateFieldError = newEndBetDateFieldError
responsesFieldError = newResponsesFieldError
}
return false
}
@ -157,7 +105,6 @@ class CreationBetViewModel: ObservableObject {
descriptionFieldError = nil
endRegisterDateFieldError = nil
endBetDateFieldError = nil
responsesFieldError = nil
}
}
@ -166,20 +113,16 @@ class CreationBetViewModel: ObservableObject {
self.errorMessage = errorMessage
}
func toBet(theme: String, description: String, endRegister: Date, endBet: Date, isPrivate: Bool, status: BetStatus, creator: String, invited: [String], type: Int) -> Bet {
func toBet(theme: String, description: String, endRegister: Date, endBet: Date, isPublic: Bool, status: BetStatus, creator: String, type: Int) -> Bet {
switch type {
case 0:
return BinaryBet(theme: theme, phrase: description, endRegisterDate: endRegister, endBetDate: endBet, isPrivate: isPrivate, status: status, invited: invited, author: creator)
return BinaryBet(theme: theme, phrase: description, endRegisterDate: endRegister, endBetDate: endBet, isPublic: isPublic, status: status, invited: [], author: creator, registered: [])
case 1:
return MatchBet(theme: theme, phrase: description, endRegisterDate: endRegister, endBetDate: endBet, isPrivate: isPrivate, status: status, invited: invited, author: creator, nameTeam1: "", nameTeam2: "")
return MatchBet(theme: theme, phrase: description, endRegisterDate: endRegister, endBetDate: endBet, isPublic: isPublic, status: status, invited: [], author: creator, registered: [], nameTeam1: "", nameTeam2: "")
case 2:
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
return CustomBet(theme: theme, phrase: description, endRegisterDate: endRegister, endBetDate: endBet, isPublic: isPublic, status: status, invited: [], author: creator, registered: [])
default:
return BinaryBet(theme: theme, phrase: description, endRegisterDate: endRegister, endBetDate: endBet, isPrivate: isPrivate, status: status, invited: invited, author: creator)
return BinaryBet(theme: theme, phrase: description, endRegisterDate: endRegister, endBetDate: endBet, isPublic: isPublic, status: status, invited: [], author: creator, registered: [])
}
}
}

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

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

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

@ -59,22 +59,14 @@ struct BetView: View {
.refreshable {
viewModel.getItems()
}
.sheet(isPresented: $viewModel.showingSheetOver, onDismiss: {
.sheet(isPresented: $viewModel.showingSheet, onDismiss: {
viewModel.betsOver.removeFirst()
viewModel.showingSheetOver = !viewModel.betsOver.isEmpty
viewModel.showingSheet = !viewModel.betsOver.isEmpty
}) {
if let firstBetDetail = viewModel.betsOver.first {
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()
}
.edgesIgnoringSafeArea(.bottom)

@ -28,6 +28,17 @@ struct CreationBetView: View {
}()
let screenWidth = UIScreen.main.bounds.width
@State private var response = ""
@State private var values: [String] = []
let options: [(Int, String, String)] = [
(0, "questionMarkIcon", String(localized: "bet_type_binary")),
(1, "footballIcon", String(localized: "bet_type_match")),
(2, "paintbrushIcon", String(localized: "bet_type_custom"))
]
@State var groupedItems: [[String]] = [[String]] ()
private func updateGroupedItems() {
var updatedGroupedItems: [[String]] = [[String]] ()
@ -35,7 +46,7 @@ struct CreationBetView: View {
var width: CGFloat = 0
var dynamicWidthLimit: CGFloat
for value in viewModel.values {
for value in values {
let label = UILabel()
label.text = value
label.sizeToFit()
@ -54,7 +65,7 @@ struct CreationBetView: View {
}
updatedGroupedItems.append(tempItems)
viewModel.groupedItems = updatedGroupedItems
groupedItems = updatedGroupedItems
}
var body: some View {
@ -97,7 +108,6 @@ struct CreationBetView: View {
.foregroundColor(AllInColors.lightGrey300Color)
.font(.system(size: 14))
.fontWeight(.light))
.autocorrectionDisabled(true)
.padding()
.background(
RoundedRectangle(cornerRadius: 9)
@ -141,7 +151,6 @@ struct CreationBetView: View {
.foregroundColor(AllInColors.lightGrey300Color)
.font(.system(size: 14))
.fontWeight(.light), axis: .vertical)
.autocorrectionDisabled(true)
.lineLimit(4, reservesSpace: true)
.padding()
.background(
@ -182,7 +191,7 @@ struct CreationBetView: View {
DatePicker(
"",
selection: $viewModel.endRegisterDate,
in: Date()...,
in: dateRange,
displayedComponents: [.date, .hourAndMinute]
)
.accentColor(AllInColors.lightPurpleColor)
@ -220,7 +229,7 @@ struct CreationBetView: View {
DatePicker(
"",
selection: $viewModel.endBetDate,
in: viewModel.endRegisterDate...,
in: dateRange,
displayedComponents: [.date, .hourAndMinute]
)
.accentColor(AllInColors.lightPurpleColor)
@ -248,15 +257,15 @@ struct CreationBetView: View {
.padding(.leading, 10)
HStack(spacing: 5) {
ConfidentialityButton(image: "globe", text: String(localized: "bet_public"), selected: !viewModel.isPrivate)
ConfidentialityButton(image: "globe", text: String(localized: "bet_public"), selected: viewModel.isPublic)
.onTapGesture {
viewModel.isPrivate = false
viewModel.isPublic = true
}
.padding(.trailing, 5)
ConfidentialityButton(image: "lock", text: String(localized: "bet_private"), selected: viewModel.isPrivate)
ConfidentialityButton(image: "lock", text: String(localized: "bet_private"), selected: !viewModel.isPublic)
.onTapGesture {
viewModel.isPrivate = true
viewModel.isPublic = false
}
Spacer()
}
@ -267,8 +276,8 @@ struct CreationBetView: View {
VStack(spacing: 10) {
if self.viewModel.isPrivate {
DropDownFriends(selectedItems: $viewModel.invited, friends: viewModel.friends)
if !self.viewModel.isPublic {
DropDownFriends()
.padding(.bottom, 30)
HStack() {
@ -351,14 +360,14 @@ struct CreationBetView: View {
VStack(spacing: 5) {
VStack() {
DropDownMenu(selectedOption: $viewModel.selectedTypeBet, options: viewModel.options)
DropDownMenu(selectedOption: $viewModel.selectedOption, options: options)
}
.padding([.bottom], 15)
.frame(width: 340)
Group {
switch viewModel.selectedTypeBet {
switch viewModel.selectedOption {
case 0:
Text("bet_creation_yes_no_bottom_text_1")
.textStyle(weight: .bold, color: AllInColors.veryLightPurpleColor, size: 13)
@ -376,18 +385,12 @@ struct CreationBetView: View {
.textStyle(weight: .bold, color: AllInColors.veryLightPurpleColor, size: 13)
.padding(.bottom, 15)
if let responseError = $viewModel.responsesFieldError.wrappedValue {
Text(responseError)
.textStyle(weight: .bold, color: .red, size: 10)
}
VStack(spacing: 5) {
HStack(spacing: 0) {
TextField("", text: $viewModel.response, prompt: Text("bet_creation_response_title")
TextField("", text: $response, prompt: Text("bet_creation_response_title")
.foregroundColor(AllInColors.lightGrey200Color)
.font(.system(size: 16))
.fontWeight(.medium))
.autocorrectionDisabled(true)
.padding()
.background(
Rectangle()
@ -397,17 +400,17 @@ struct CreationBetView: View {
)
.frame(width: 250, height: 38)
.foregroundColor(.black)
.onChange(of: viewModel.response) { newValue in
.onChange(of: response) { newValue in
if newValue.count > 20 {
viewModel.response = String(newValue.prefix(20))
response = String(newValue.prefix(20))
}
}
Button(action: {
if !viewModel.response.isEmpty && viewModel.values.count < 5 {
viewModel.values.append(viewModel.response)
if !response.isEmpty && values.count < 5 {
values.append(response)
updateGroupedItems()
viewModel.response = ""
response = ""
}
}) {
Text("generic_add")
@ -420,12 +423,12 @@ struct CreationBetView: View {
}
HStack {
Spacer()
Text(String(localized: "bet_creation_max_answers \(5 - viewModel.values.count)"))
Text(String(localized: "bet_creation_max_answers \(5 - values.count)"))
.textStyle(weight: .regular, color: AllInColors.primaryTextColor, size: 12)
}
VStack(spacing: 10) {
ForEach(viewModel.groupedItems, id: \.self) { items in
ForEach(groupedItems, id: \.self) { items in
HStack {
ForEach(items, id: \.self) { text in
HStack {
@ -433,8 +436,8 @@ struct CreationBetView: View {
.foregroundColor(.white)
.lineLimit(1)
Button(action: {
if let index = viewModel.values.firstIndex(of: text) {
viewModel.values.remove(at: index)
if let index = values.firstIndex(of: text) {
values.remove(at: index)
updateGroupedItems()
}
}) {

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

@ -8,7 +8,7 @@ struct DetailsView: View {
@StateObject private var viewModel: DetailsViewModel
var isFinished: Bool {
viewModel.betDetail?.wonParticipation == nil ? false : true
viewModel.betDetail?.finalAnswer == nil ? false : true
}
var StatusValues: (String, Color) {
@ -57,117 +57,97 @@ struct DetailsView: View {
}
.padding(.horizontal, 15)
.background(StatusValues.1)
if viewModel.betDetail != nil{
ScrollView {
VStack(alignment: .leading, spacing: 0) {
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))
VStack(spacing: 0) {
VStack(alignment: .leading, spacing: 5) {
HStack(spacing: 3) {
Spacer()
Text("bet_proposed_by_format")
.font(.system(size: 10))
.foregroundColor(AllInColors.grey800Color)
Text(viewModel.betDetail?.bet.phrase ?? "Not loaded")
.font(.system(size: 20))
.fontWeight(.bold)
.padding(.bottom, 10)
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 {
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()
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)
}.padding(.bottom, 10)
HStack {
HStack {
HStack {
Spacer()
Text("bet_ends")
.font(.system(size: 15))
.foregroundColor(AllInColors.grey800Color)
}
.frame(width: 105)
.padding(.trailing, 10)
TextCapsule(date: viewModel.betDetail?.bet.endBetDate ?? Date())
Spacer()
Text("bet_ends")
.font(.system(size: 15))
.foregroundColor(AllInColors.grey800Color)
}
.frame(width: 105)
.padding(.trailing, 10)
TextCapsule(date: viewModel.betDetail?.bet.endBetDate ?? Date())
Spacer()
}
.padding(.all, 15)
.padding(.top, 6)
.border(width: 1, edges: [.bottom], color: AllInColors.delimiterGrey)
.background(AllInColors.componentBackgroundColor)
.cornerRadius(20, corners: [.topLeft,.topRight])
if isFinished {
ResultBanner(finalAnswer: (viewModel.betDetail?.wonParticipation)!, odds: (viewModel.betDetail?.odds)!)
}
VStack(alignment: .leading, spacing: 0) {
if let bet = viewModel.betDetail{
BetLineLoading(bet: bet).padding(.vertical, 20)
}
Text("bet_status_participants_list")
.font(.system(size: 18))
.foregroundStyle(AllInColors.grey100Color)
.fontWeight(.bold)
.padding(.bottom, 10)
}
.padding(.all, 15)
.padding(.vertical, 10)
.background(AllInColors.componentBackgroundColor)
.cornerRadius(20, corners: [.topLeft,.topRight]).padding(.bottom,0)
if isFinished {
ResultBanner()
}
VStack(alignment: .leading, spacing: 5) {
BetLineLoading(participations: viewModel.betDetail?.participations ?? [])
.padding(.vertical,15)
Text("bet_status_participants_list")
.font(.system(size: 18))
.foregroundStyle(AllInColors.grey100Color)
.fontWeight(.bold)
.padding(.bottom, 10)
ScrollView(showsIndicators: false) {
ForEach(viewModel.betDetail?.participations ?? []) { participation in
UserInfo(username: participation.username, picture: nil , value: participation.stake).padding(.horizontal, 10)
UserInfo(username: participation.username, value: participation.stake).padding(.horizontal, 10)
}
.padding(.bottom)
Spacer()
}
.padding([.trailing,.leading], 15)
.padding(.bottom, 100)
.padding(.bottom, geometry.safeAreaInsets.bottom + 28)
Spacer()
}
.frame(maxWidth: .infinity, maxHeight: (geometry.size.height + geometry.safeAreaInsets.bottom) - 50)
.padding([.bottom,.trailing,.leading], 15)
.background(AllInColors.underComponentBackgroundColor)
.cornerRadius(15, corners: [.topLeft, .topRight])
}
else{
ScrollView {
HStack(alignment: .center){
Spacer()
Image(systemName: "exclamationmark.triangle.fill").font(.system(size: 45))
VStack(alignment:.leading){
Text("error_title").font(.system(size: 10))
Text("error_message").font(.system(size: 10))
}
Spacer()
}.opacity(0.6)
.padding(.vertical, 20)
.border(width: 1, edges: [.top], color: AllInColors.delimiterGrey)
Spacer()
}
.frame(maxWidth: .infinity, maxHeight: (geometry.size.height + geometry.safeAreaInsets.bottom) - 50)
.background(AllInColors.underComponentBackgroundColor)
.cornerRadius(15, corners: [.topLeft, .topRight])
}
.frame(maxWidth: .infinity, maxHeight: (geometry.size.height + geometry.safeAreaInsets.bottom) - 50)
.background(AllInColors.componentBackgroundColor)
.cornerRadius(15, corners: [.topLeft, .topRight])
ParticipateButton(isOpen: $isModalPresented, isParticapatedOpen: $isModalParticipated, bet: viewModel.betDetail?.bet)
.padding(.bottom, geometry.safeAreaInsets.bottom + 5)
.padding(.horizontal, 10)
}
.sheet(isPresented: $isModalParticipated) {
ParticipationModal(selectedAnswer: $viewModel.selectedAnswer, mise: $viewModel.mise, phrase: viewModel.betDetail?.bet.phrase ?? "", answers: viewModel.betDetail?.answers ?? [], participationAddedCallback: {
ParticipationModal(answer: $viewModel.answer, mise: $viewModel.mise, description: viewModel.betDetail?.bet.phrase ?? "Not loaded", participationAddedCallback: {
viewModel.addParticipate()
isModalParticipated.toggle()
},
checkAndSetError: viewModel.checkAndSetError)
})
.presentationDetents([.fraction(0.55)])
}
.edgesIgnoringSafeArea(.bottom)

@ -64,26 +64,22 @@ struct FriendsView: View {
}
.padding(.top, 50)
.tag(0)
ScrollView{
VStack(alignment: .center, spacing: 0) {
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 {
viewModel.getRequests()
VStack(alignment: .center, spacing: 0) {
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)
}
.padding(.top, 25)
}
Spacer()
.refreshable {
viewModel.getRequests()
}
.padding(.top, 25)
}
}.refreshable {
viewModel.getRequests()
Spacer()
}
.padding(.top, 50)
.tag(1)

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

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

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

@ -9,15 +9,12 @@
/* Begin PBXBuildFile section */
120919182B56D0AE00D0FA29 /* ParticipationModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 120919172B56D0AE00D0FA29 /* ParticipationModal.swift */; };
1209191A2B56DC6C00D0FA29 /* DropDownAnswerMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 120919192B56DC6C00D0FA29 /* DropDownAnswerMenu.swift */; };
12228A212C1760D2008524F4 /* BinaryBetLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12228A202C1760D2008524F4 /* BinaryBetLine.swift */; };
12228A232C176117008524F4 /* CustomBetLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12228A222C176117008524F4 /* CustomBetLine.swift */; };
123225D92B67B46100D30BB3 /* BetEndingValidationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123225D82B67B46100D30BB3 /* BetEndingValidationView.swift */; };
123225DB2B67E41400D30BB3 /* ChoiceFinalAnswerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123225DA2B67E41400D30BB3 /* ChoiceFinalAnswerCell.swift */; };
123590B42B51792000F7AEBD /* DetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123590B32B51792000F7AEBD /* DetailsView.swift */; };
123590B62B5537E200F7AEBD /* ResultBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123590B52B5537E200F7AEBD /* ResultBanner.swift */; };
123590B82B5541BA00F7AEBD /* ParticipateButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123590B72B5541BA00F7AEBD /* ParticipateButton.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 */; };
1244EF622B4EC67000374ABF /* ReviewCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1244EF612B4EC67000374ABF /* ReviewCard.swift */; };
129D051D2B6E7FF0003D3E08 /* OddCapsule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 129D051C2B6E7FF0003D3E08 /* OddCapsule.swift */; };
@ -151,15 +148,12 @@
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; };
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>"; };
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>"; };
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>"; };
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>"; };
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>"; };
@ -280,14 +274,6 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
123F31DE2C11E979009B6D65 /* Utils */ = {
isa = PBXGroup;
children = (
123F31DF2C11E99E009B6D65 /* asyncCachedImage.swift */,
);
path = Utils;
sourceTree = "<group>";
};
EC1D15402B715A7A0094833E /* Protocols */ = {
isa = PBXGroup;
children = (
@ -323,7 +309,6 @@
EC6B969A2B24B4CC00FC1C58 /* AllIn */ = {
isa = PBXGroup;
children = (
123F31DE2C11E979009B6D65 /* Utils */,
ECF872852B93B9D900F9D240 /* AllIn.entitlements */,
EC7EF7462B87E3470022B5D9 /* QuickActions */,
ECB7BC662B2F1AAD002A6654 /* ViewModels */,
@ -448,8 +433,6 @@
129D051C2B6E7FF0003D3E08 /* OddCapsule.swift */,
12A9E4932C07132600AB8677 /* EmptyInfo.swift */,
123F31DA2C0F26E8009B6D65 /* userPicture.swift */,
12228A202C1760D2008524F4 /* BinaryBetLine.swift */,
12228A222C176117008524F4 /* CustomBetLine.swift */,
);
path = Components;
sourceTree = "<group>";
@ -681,7 +664,6 @@
files = (
EC6B96CC2B24B7E500FC1C58 /* IAuthService.swift in Sources */,
ECB26A172B4073F100FE06B3 /* CreationBetViewModel.swift in Sources */,
12228A232C176117008524F4 /* CustomBetLine.swift in Sources */,
EC3077092B24CF7F0060E34D /* Colors.swift in Sources */,
EC6B969E2B24B4CC00FC1C58 /* ContentView.swift in Sources */,
EC89F7BD2B250D66003821CE /* LoginView.swift in Sources */,
@ -694,7 +676,6 @@
EC6B969C2B24B4CC00FC1C58 /* AllInApp.swift in Sources */,
123590B42B51792000F7AEBD /* DetailsView.swift in Sources */,
ECB26A192B40744F00FE06B3 /* RankingViewModel.swift in Sources */,
123F31E02C11E99E009B6D65 /* asyncCachedImage.swift in Sources */,
EC650A502B2793D5003AFCAD /* TextCapsule.swift in Sources */,
EC650A482B25DCFF003AFCAD /* UsersPreview.swift in Sources */,
EC17A15E2B6A955E008A8679 /* CurrentBetView.swift in Sources */,
@ -728,7 +709,6 @@
EC01937E2B25C52E005D81E6 /* TopBar.swift in Sources */,
ECA9D1CB2B2DA2320076E0EC /* DropDownFriends.swift in Sources */,
123590B82B5541BA00F7AEBD /* ParticipateButton.swift in Sources */,
12228A212C1760D2008524F4 /* BinaryBetLine.swift in Sources */,
EC01FCC32B56650400BB2390 /* DetailsViewModel.swift in Sources */,
ECB26A1B2B40746C00FE06B3 /* FriendsViewModel.swift in Sources */,
ECB7BC682B2F1ADF002A6654 /* LoginViewModel.swift in Sources */,
@ -936,7 +916,7 @@
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"AllIn/Preview Content\"";
DEVELOPMENT_TEAM = P39ZK4GA2T;
DEVELOPMENT_TEAM = 35KQ5BDC64;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = "All In";
@ -970,7 +950,7 @@
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"AllIn/Preview Content\"";
DEVELOPMENT_TEAM = P39ZK4GA2T;
DEVELOPMENT_TEAM = 35KQ5BDC64;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = "All In";
@ -1074,7 +1054,7 @@
CODE_SIGN_ENTITLEMENTS = AllInWidgetsExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = P39ZK4GA2T;
DEVELOPMENT_TEAM = 35KQ5BDC64;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = AllInWidgets/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = AllInWidgets;
@ -1102,7 +1082,7 @@
CODE_SIGN_ENTITLEMENTS = AllInWidgetsExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = P39ZK4GA2T;
DEVELOPMENT_TEAM = 35KQ5BDC64;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = AllInWidgets/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = AllInWidgets;

@ -7,21 +7,5 @@
<key>NSExtensionPointIdentifier</key>
<string>com.apple.widgetkit-extension</string>
</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>
</plist>

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

@ -23,14 +23,11 @@ public class FactoryApiBet: FactoryBet {
"sentenceBet": bet.phrase,
"endRegistration": formatZonedDateTime(dateTime: bet.endRegisterDate),
"endBet": formatZonedDateTime(dateTime: bet.endBetDate),
"isPrivate": String(bet.isPrivate),
"response": bet.getResponses(),
"userInvited": bet.invited,
"isPrivate": String(bet.isPublic),
"response": ["Yes","No"],
"type": betTypeString(fromType: String(describing: type(of: bet)))
]
print(json)
return json
}
@ -63,8 +60,6 @@ public class FactoryApiBet: FactoryBet {
}
var participations: [Participation] = []
var wonParticipation: Participation?
var userParticipation: Participation?
if let participationsJson = json["participations"] as? [[String: Any]] {
do {
@ -84,53 +79,7 @@ public class FactoryApiBet: FactoryBet {
}
}
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)
return BetDetail(bet: bet, answers: answers, participations: participations)
}
public func betTypeString(fromType type: String) -> String {

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

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

@ -8,7 +8,7 @@
import Foundation
/// A class representing detailed information about a specific answer option for a bet.
public class AnswerDetail: ObservableObject, Codable, Equatable, Identifiable, Hashable {
public class AnswerDetail: ObservableObject, Identifiable, Codable {
/// The response or outcome associated with this answer.
public private(set) var response: String
@ -41,19 +41,4 @@ public class AnswerDetail: ObservableObject, Codable, Equatable, Identifiable, H
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
/// A class representing a betting entity, including details about the bet theme, participants, and deadlines.
public class Bet: ObservableObject, Codable {
public class Bet: ObservableObject, Identifiable, Codable {
/// The id for the bet.
public private(set) var id: String
@ -26,24 +26,27 @@ public class Bet: ObservableObject, Codable {
public private(set) var endBetDate: Date
/// Indicates whether the bet is public or private.
public private(set) var isPrivate: Bool
public private(set) var isPublic: Bool
/// The current status of the bet.
public private(set) var status: BetStatus
/// List of users who are invited to participate in the bet.
public private(set) var invited: [String] = []
public private(set) var invited: [User] = []
/// The user who created the bet.
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 {
case id
case theme
case phrase = "sentenceBet"
case endRegisterDate = "endRegistration"
case endBetDate = "endBet"
case isPrivate = "isPrivate"
case isPublic = "isPrivate"
case status
case author = "createdBy"
}
@ -60,7 +63,7 @@ public class Bet: ObservableObject, Codable {
self.endRegisterDate = formatter.date(from: endRegisterDateString)!
let endBetDateString = try container.decode(String.self, forKey: .endBetDate)
self.endBetDate = formatter.date(from: endBetDateString)!
self.isPrivate = try container.decode(Bool.self, forKey: .isPrivate)
self.isPublic = try container.decode(Bool.self, forKey: .isPublic)
self.status = try container.decode(BetStatus.self, forKey: .status)
self.author = try container.decode(String.self, forKey: .author)
}
@ -77,16 +80,18 @@ public class Bet: ObservableObject, Codable {
/// - status: The current status of the bet.
/// - invited: List of users who are invited to participate in the bet.
/// - author: The user who created the bet.
public init(id: String, theme: String, phrase: String, endRegisterDate: Date, endBetDate: Date, isPrivate: Bool, status: BetStatus, invited: [String], author: String) {
/// - registered: List of users who have registered for the bet.
public init(id: String, theme: String, phrase: String, endRegisterDate: Date, endBetDate: Date, isPublic: Bool, status: BetStatus, invited: [User], author: String, registered: [User]) {
self.id = id
self.theme = theme
self.phrase = phrase
self.endRegisterDate = endRegisterDate
self.endBetDate = endBetDate
self.isPrivate = isPrivate
self.isPublic = isPublic
self.status = status
self.invited = invited
self.author = author
self.registered = registered
}
/// Custom Constructor without Id
@ -100,23 +105,18 @@ public class Bet: ObservableObject, Codable {
/// - status: The current status of the bet.
/// - invited: List of users who are invited to participate in the bet.
/// - author: The user who created the bet.
public init(theme: String, phrase: String, endRegisterDate: Date, endBetDate: Date, isPrivate: Bool, status: BetStatus, invited: [String], author: String) {
/// - registered: List of users who have registered for the bet.
public init(theme: String, phrase: String, endRegisterDate: Date, endBetDate: Date, isPublic: Bool, status: BetStatus, invited: [User], author: String, registered: [User]) {
self.id = UUID().uuidString
self.theme = theme
self.phrase = phrase
self.endRegisterDate = endRegisterDate
self.endBetDate = endBetDate
self.isPrivate = isPrivate
self.isPublic = isPublic
self.status = status
self.invited = invited
self.author = author
}
/// Function that returns an empty list of responses
///
/// - Returns: An empty list of responses
public func getResponses() -> [String] {
return []
self.registered = registered
}
}

@ -20,19 +20,7 @@ public class BetDetail: ObservableObject {
public private(set) var participations: [Participation]
/// The final answer selected for the bet, if available.
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
}
}
public private(set) var finalAnswer: String?
/// Custom Constructor
///
@ -41,19 +29,18 @@ public class BetDetail: ObservableObject {
/// - answers: Details about the answers available for the bet.
/// - participations: List of user participations in the bet.
/// - finalAnswer: The final answer selected for the bet, if available.
public init(bet: Bet, answers: [AnswerDetail], participations: [Participation], wonParticipation: Participation? = nil, userParticipation: Participation? = nil) {
public init(bet: Bet, answers: [AnswerDetail], participations: [Participation], finalAnswer: String? = nil) {
self.bet = bet
self.answers = answers
self.participations = participations
self.wonParticipation = wonParticipation
self.userParticipation = userParticipation
self.finalAnswer = finalAnswer
}
/// Updates the final answer selected for the bet.
///
/// - Parameter newFinalAnswer: The new final answer for the bet.
public func updateFinalAnswer(wonParticipation: Participation) {
self.wonParticipation = wonParticipation
public func updateFinalAnswer(newFinalAnswer: String) {
self.finalAnswer = newFinalAnswer
}
/// Adds a new user participation to the list of participations for the bet.

@ -1,21 +0,0 @@
//
// 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
}
}

@ -1,30 +0,0 @@
//
// 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,21 +10,4 @@ import Foundation
/// A subclass of Bet that represents a custom bet, allowing users to define their own parameters and rules.
public class CustomBet: Bet {
/// List of custom responses
private var customResponses: [String] = []
/// Override the getResponses function to return custom responses
///
/// - Returns: A list of custom responses
public override func getResponses() -> [String] {
return customResponses
}
/// Add a custom response
///
/// - Parameter response: The custom response to add
public func addCustomResponse(_ response: String) {
customResponses.append(response)
}
}

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

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

@ -33,12 +33,13 @@ public class MatchBet: Bet {
/// - status: The current status of the match bet.
/// - invited: List of users who are invited to participate in 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.
/// - 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: [String], author: String, nameTeam1: String, nameTeam2: String) {
public init(id: String, theme: String, phrase: String, endRegisterDate: Date, endBetDate: Date, isPublic: Bool, status: BetStatus, invited: [User], author: String, registered: [User], nameTeam1: String, nameTeam2: String) {
self.nameTeam1 = nameTeam1
self.nameTeam2 = nameTeam2
super.init(id: id, theme: theme, phrase: phrase, endRegisterDate: endRegisterDate, endBetDate: endBetDate, isPrivate: isPrivate, status: status, invited: invited, author: author)
super.init(id: id, theme: theme, phrase: phrase, endRegisterDate: endRegisterDate, endBetDate: endBetDate, isPublic: isPublic, status: status, invited: invited, author: author, registered: registered)
}
/// Custom Constructor without Id
@ -52,12 +53,13 @@ public class MatchBet: Bet {
/// - status: The current status of the match bet.
/// - invited: List of users who are invited to participate in 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.
/// - 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: [String], author: String, nameTeam1: String, nameTeam2: String) {
public init(theme: String, phrase: String, endRegisterDate: Date, endBetDate: Date, isPublic: Bool, status: BetStatus, invited: [User], author: String, registered: [User], nameTeam1: String, nameTeam2: String) {
self.nameTeam1 = nameTeam1
self.nameTeam2 = nameTeam2
super.init(theme: theme, phrase: phrase, endRegisterDate: endRegisterDate, endBetDate: endBetDate, isPrivate: isPrivate, status: status, invited: invited, author: author)
super.init(theme: theme, phrase: phrase, endRegisterDate: endRegisterDate, endBetDate: endBetDate, isPublic: isPublic, status: status, invited: invited, author: author, registered: registered)
}
public required init(from decoder: Decoder) throws {

@ -10,8 +10,9 @@ import Foundation
/// A struct representing a user with details such as username, email, number of coins, and friends.
public struct User: Codable, Hashable, Identifiable {
public var id: String
public var id: String {
return email
}
/// The username of the user.
public private(set) var username: String
@ -48,7 +49,6 @@ public struct User: Codable, Hashable, Identifiable {
/// - nbFriends: The number of friends the user has.
/// - 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) {
self.id = UUID().description
self.username = username
self.email = email
self.nbCoins = nbCoins

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

@ -35,10 +35,11 @@ struct Stub {
phrase: "Le gagnant de la finale sera l'équipe avec le plus de tirs au but.",
endRegisterDate: Date().addingTimeInterval(-86400),
endBetDate: Date().addingTimeInterval(172800),
isPrivate: false,
isPublic: true,
status: .inProgress,
invited: [],
author: "Lucas"
author: "Lucas",
registered: [user2]
)
self.bets.append(bet1)
@ -47,10 +48,11 @@ struct Stub {
phrase: "Le plat préféré du jury sera une recette végétarienne.",
endRegisterDate: Date().addingTimeInterval(172800),
endBetDate: Date().addingTimeInterval(259200),
isPrivate: false,
isPublic: false,
status: .inProgress,
invited: [],
author: "Lucas"
invited: [user3],
author: "Lucas",
registered: [user2]
)
self.bets.append(bet2)
@ -59,10 +61,11 @@ struct Stub {
phrase: "Le nombre total de précommandes dépassera-t-il 1 million dans la première semaine ?",
endRegisterDate: Date().addingTimeInterval(259200),
endBetDate: Date().addingTimeInterval(345600),
isPrivate: true,
isPublic: true,
status: .finished,
invited: [],
author: "Lucas"
author: "Lucas",
registered: [user2, user1, user3]
)
self.bets.append(bet3)
@ -71,10 +74,11 @@ struct Stub {
phrase: "Le film favori des critiques remportera-t-il le prix du meilleur film ?",
endRegisterDate: Date().addingTimeInterval(345600),
endBetDate: Date().addingTimeInterval(432000),
isPrivate: false,
isPublic: false,
status: .finished,
invited: [],
author: "Lucase"
invited: [user1],
author: "Lucase",
registered: [user3]
)
self.bets.append(bet4)
@ -91,7 +95,7 @@ struct Stub {
}
public mutating func add(bet: Bet) {
let newBetDetail = BetDetail(bet: bet, answers: [], participations: [])
let newBetDetail = BetDetail(bet: bet, answers: [], participations: [], finalAnswer: "test")
self.betsDetail.append(newBetDetail)
}
}

Loading…
Cancel
Save