Compare commits

...

36 Commits

Author SHA1 Message Date
Emre KARTAL c640fc0acb fix icon
10 months ago
Emre KARTAL ee3f0f0c9b fix icon
10 months ago
Emre KARTAL 1b8db51116 Merge pull request 'fix/multiples-fix' (#40) from fix/multiples-fix into master
10 months ago
Emre KARTAL a4f149b81c fix little errors 🐛
10 months ago
Emre KARTAL 2bb46d3b6f Add a semaphore to avoid the empty page in the DetailsView and get the user infos when the menu is open 🐛
10 months ago
Lucas DELANIER d5e50e4168 fix multiple issues with floats, responsive etc
10 months ago
Emre KARTAL b97254d000 Merge pull request 'feature/bet-custom' (#39) from feature/bet-custom into master
10 months ago
Lucas DELANIER f668388088 fix error when loading
10 months ago
Lucas DELANIER 77e6790f59 fix order
10 months ago
Lucas DELANIER bb2a2baac3 make the review for the custom bets
10 months ago
Emre KARTAL b7efad2eed get custom answers and add the creation of bet custom
10 months ago
Lucas DELANIER f4e3edbf4a fix winning and loosing status (#38)
10 months ago
Emre KARTAL b878759b3d Merge pull request 'feature/winnings' (#37) from feature/winnings into master
10 months ago
Lucas DELANIER 65940cb3bc delete submodule
10 months ago
Lucas DELANIER e116fc9dca fix some bugs with current and finished list
10 months ago
Emre KARTAL b542ff1bf2 Bind winModal with betResult
11 months ago
Lucas DELANIER ef1f130ec5 bind images and fix ranking row (#36)
11 months ago
Emre KARTAL 68345572ef Get bets won
11 months ago
Lucas DELANIER 5d1ea8492a feature/in-progress-card (#35)
11 months ago
Emre KARTAL d82c8e7d79 Merge pull request 'Add private friends invitation' (#34) from feature/user-invitation into master
11 months ago
Emre KARTAL ab4aa3313a Add private friends invitation
11 months ago
Lucas DELANIER 32480e0cb1 fix mistake ispublic (#33)
11 months ago
Lucas DELANIER 5ce52cff75 limit the minimal date (#32)
11 months ago
Emre KARTAL 0c30fa1571 Mise à jour de 'README.md'
11 months ago
Emre KARTAL 0a9d0c0508 Transférer les fichiers vers 'Documentation/Images'
11 months ago
Lucas DELANIER b27cd37a39 feature/user-picture (#31)
11 months ago
Emre KARTAL 71e5c12992 Merge pull request 'Fix errors' (#30) from fix/participation into master
11 months ago
Emre KARTAL eed6fcca99 Fix errors
11 months ago
Arthur VALIN 7cbd2c1336 Mise à jour de 'README.md'
11 months ago
Lucas DELANIER e1c8d85dd1 i18n and empty view (#29)
11 months ago
avalin 28b0f19619 Fix bet confirmation
11 months ago
Emre KARTAL 030e3fb2f0 Merge pull request 'fix/api-calls' (#28) from fix/api-calls into master
11 months ago
Emre KARTAL b700938539 Add popular bet
11 months ago
Emre KARTAL 68e8c4d41c Get old bets
11 months ago
Lucas DELANIER 1146eeeaa8 add friend request (#27)
11 months ago
Emre KARTAL c9fb2ca2ed Fix filtering
11 months ago

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 354 KiB

@ -21,7 +21,7 @@
**Description** : Ce dépôt contient l'ensemble du code pour la partie client iOS de l'application *ALL IN*. **Description** : Ce dépôt contient l'ensemble du code pour la partie client iOS de l'application *ALL IN*.
</br> </br>
# Répartition du GitLab # Répartition du dépot
[**Sources**](Sources) : **Code de l'application** [**Sources**](Sources) : **Code de l'application**
@ -32,7 +32,43 @@
- MVVM - 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 # Technologies

@ -3,8 +3,6 @@
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>com.apple.security.application-groups</key> <key>com.apple.security.application-groups</key>
<array> <array/>
<string>group.alldev.AllIn</string>
</array>
</dict> </dict>
</plist> </plist>

@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "blueFlame.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "globe.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "pinkFlame.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

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

@ -15,7 +15,7 @@ struct AllcoinsCounter: View {
var body: some View { var body: some View {
HStack(alignment: .center) { HStack(alignment: .center) {
Text(String(appStateContainer.user?.nbCoins ?? 0)) Text(String(appStateContainer.user!.nbCoins))
.contentTransition(.numericText()) .contentTransition(.numericText())
.fontWeight(.black) .fontWeight(.black)
.foregroundColor(foregroundColor) .foregroundColor(foregroundColor)

@ -22,7 +22,6 @@ struct BetCard: View {
Text("bet_proposed_by_format \(bet.author.capitalized)") Text("bet_proposed_by_format \(bet.author.capitalized)")
.font(.system(size: 10)) .font(.system(size: 10))
.foregroundColor(AllInColors.grey800Color) .foregroundColor(AllInColors.grey800Color)
} }
Text(bet.theme) Text(bet.theme)
.font(.system(size: 15)) .font(.system(size: 15))
@ -45,8 +44,8 @@ struct BetCard: View {
VStack(alignment: .leading,spacing: 2){ VStack(alignment: .leading,spacing: 2){
HStack{ HStack{
Spacer() Spacer()
UsersPreview() UsersPreview(users: [])
Text("bet_players_waiting_format \(bet.registered.count.description)") Text("bet_players_waiting_format \(bet.invited.count.description)")
.font(.system(size: 15)) .font(.system(size: 15))
.foregroundColor(AllInColors.grey800Color) .foregroundColor(AllInColors.grey800Color)
.fontWeight(.medium) .fontWeight(.medium)
@ -63,12 +62,11 @@ struct BetCard: View {
.cornerRadius(20, corners: [.bottomLeft,.bottomRight]) .cornerRadius(20, corners: [.bottomLeft,.bottomRight])
.border(width: 1, edges: [.top], color: AllInColors.delimiterGrey) .border(width: 1, edges: [.top], color: AllInColors.delimiterGrey)
} }
.onTapGesture { .onTapGesture {
showDetails.toggle() showDetails.toggle()
} }
.fullScreenCover(isPresented: $showDetails) { .fullScreenCover(isPresented: $showDetails) {
DetailsView(isModalPresented: $showDetails, isModalParticipated: $showParticipate,id: bet.id) DetailsView(isModalPresented: $showDetails, isModalParticipated: $showParticipate, id: bet.id)
} }
} }
} }
@ -79,11 +77,10 @@ struct BetCard_Previews: PreviewProvider {
phrase: "Le gagnant de la finale sera l'équipe avec le plus de tirs au but.", phrase: "Le gagnant de la finale sera l'équipe avec le plus de tirs au but.",
endRegisterDate: Date().addingTimeInterval(86400), endRegisterDate: Date().addingTimeInterval(86400),
endBetDate: Date().addingTimeInterval(172800), endBetDate: Date().addingTimeInterval(172800),
isPublic: true, isPrivate: false,
status: .inProgress, status: .inProgress,
invited: [], invited: [],
author: "Imri", author: "Imri"))
registered: []))
.preferredColorScheme(.dark) .preferredColorScheme(.dark)
} }
} }

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

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

@ -51,12 +51,12 @@ struct ChoiceCapsule: View {
} }
} }
.onTapGesture() { .onTapGesture() {
if(!pressed) { pressed.toggle()
if(pressed) {
viewModel.filters.insert(filter) viewModel.filters.insert(filter)
} else { } else {
viewModel.filters.remove(filter) viewModel.filters.remove(filter)
} }
pressed.toggle()
} }
} }

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

@ -0,0 +1,141 @@
//
// CustomBetLine.swift
// AllIn
//
// Created by Lucas Delanier on 10/06/2024.
//
import SwiftUI
import Model
struct CustomBetLine: View {
@State var showInfos: [String: Bool] = [:]
var bet: BetDetail
var participationsForAnswer: [String: [Participation]] {
Dictionary(grouping: bet.participations, by: { $0.answer.uppercased() })
}
var sortedAnswers: [AnswerDetail] {
bet.answers.sorted { $0.totalParticipants > $1.totalParticipants }
}
func getTextStyle(for answer: AnswerDetail) -> Font.Weight {
return answer == sortedAnswers.first ? .bold : .light
}
func getColor(for answer: AnswerDetail) -> Color {
return answer == sortedAnswers.first ? AllInColors.pinkAccentColor : AllInColors.blueAccentColor
}
func getGradiant(for answer: AnswerDetail) -> LinearGradient {
return answer == sortedAnswers.first ? AllInColors.PinkBetGradiant : AllInColors.BlueBetLineGradiant
}
func getFlameImage(for answer: AnswerDetail) -> String {
return answer == sortedAnswers.first ? "pinkFlameIcon" : "blueFlameIcon"
}
var body: some View {
VStack(alignment: .leading, spacing: 0) {
ForEach(sortedAnswers, id: \.response) { answer in
let participations = participationsForAnswer[answer.response.uppercased()] ?? []
let totalParticipations = bet.participations.count
let percentage = totalParticipations > 0 ? CGFloat(participations.count) / CGFloat(totalParticipations) : 0.0
VStack {
HStack {
Text(answer.response)
.font(.system(size: 15))
.fontWeight(getTextStyle(for: answer))
.foregroundColor(getColor(for: answer))
Spacer()
}
GeometryReader { geometry in
ZStack(alignment: .leading) {
HStack(spacing: 0) {
Rectangle()
.frame(width: min(percentage * geometry.size.width, geometry.size.width-20), height: 17)
.foregroundStyle(getGradiant(for: answer))
.cornerRadius(999, corners: [.topLeft, .bottomLeft])
Image(getFlameImage(for: answer))
.resizable()
.frame(width: 29, height: 32)
.padding(.leading, -6)
Text("\(Int(percentage * 100))%")
.font(.system(size: 15))
.fontWeight(.bold)
.foregroundStyle(getColor(for: answer))
.padding(.leading, 2)
}
}
.padding(.bottom, 5)
}
.frame(height: 40)
HStack {
Spacer()
Text("bet_status_details_drawer")
.textStyle(weight: .medium, color: AllInColors.primaryTextColor, size: 10)
Image(showInfos[answer.response] ?? false ? "chevronUpIcon" : "chevronDownIcon")
.resizable()
.frame(width: 10, height: 7)
.scaledToFill()
}
.onTapGesture {
withAnimation {
showInfos[answer.response, default: false].toggle()
}
}
.padding(.trailing, 5)
if showInfos[answer.response] ?? false {
HStack{
VStack(alignment: .leading,spacing: 1) {
HStack(spacing: 5) {
Image(answer == sortedAnswers.first ? "pinkAllCoinIcon" : "blueAllCoinIcon")
.resizable()
.frame(width: 12, height: 12)
Text(participations.reduce(0, { x, y in x + y.stake }).description)
.font(.system(size: 15))
.fontWeight(.bold)
.foregroundColor(getColor(for: answer))
}
HStack(spacing: 5) {
Image(answer == sortedAnswers.first ? "pinkPersonIcon" : "bluePersonIcon")
.resizable()
.frame(width: 12, height: 12)
Text(participations.count.description)
.font(.system(size: 15))
.fontWeight(.bold)
.foregroundColor(getColor(for: answer))
}
HStack(spacing: 5) {
Image(answer == sortedAnswers.first ? "pinkBadgeIcon" : "blueBadgeIcon")
.resizable()
.frame(width: 12, height: 12)
Text(participations.max(by: { $0.stake < $1.stake })?.stake.description ?? "0")
.font(.system(size: 15))
.fontWeight(.bold)
.foregroundColor(getColor(for: answer))
}
HStack(spacing: 5) {
Image(answer == sortedAnswers.first ? "pinkTrophyIcon" : "blueTrophyIcon")
.resizable()
.frame(width: 12, height: 12)
Text(String(format: "%.2f", answer.odds))
.font(.system(size: 15))
.fontWeight(.bold)
.foregroundColor(getColor(for: answer))
}
}
Spacer()
}
}
}
}
}
}
}

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

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

@ -14,12 +14,14 @@ struct EmptyInfo: View {
var body: some View { var body: some View {
VStack{ VStack{
Text(emoji).font(.system(size: 120)) Text(emoji).font(.system(size: 120))
Text(title).textStyle(weight: .bold, color: .black, size: 15) Text(title).textStyle(weight: .bold, color: AllInColors.primaryTextColor , size: 15)
explain.isEmpty ? nil : Text(explain).textStyle(weight: .light, color: .gray, size: 12) explain.isEmpty ? nil : Text(explain).textStyle(weight: .light, color: .gray, size: 12)
}.opacity(0.55).padding(.horizontal, 20).multilineTextAlignment(.center) }.opacity(0.55).padding(.horizontal, 20).multilineTextAlignment(.center)
} }
} }
#Preview { struct EmptyInfo_Previews: PreviewProvider {
static var previews: some View {
EmptyInfo(emoji:"👥", title: "Vous navez pas encore damis", explain: "Ajoutez les depuis cet écran") EmptyInfo(emoji:"👥", title: "Vous navez pas encore damis", explain: "Ajoutez les depuis cet écran")
}
} }

@ -11,14 +11,15 @@ import Model
struct Friend: View { struct Friend: View {
var user: User var user: User
let isRequest: Bool
@ObservedObject var viewModel: FriendsViewModel @ObservedObject var viewModel: FriendsViewModel
var StatusValues: (String, Color, Color) { var StatusValues: (String, Color, Color) {
switch user.friendStatus { switch user.friendStatus {
case .friend: case .friend:
return (String(localized: "generic_delete"), AllInColors.grey400Color, AllInColors.componentBackgroundColor) return isRequest ? (String(localized: "generic_decline"), AllInColors.grey400Color, AllInColors.componentBackgroundColor) : (String(localized: "generic_delete"), AllInColors.grey400Color, AllInColors.componentBackgroundColor)
case .notFriend: case .notFriend:
return (String(localized: "generic_add"), .white, AllInColors.lightPurpleColor) return isRequest ? (String(localized: "generic_acccept"), .white, AllInColors.lightPurpleColor) : (String(localized: "generic_add"), .white, AllInColors.lightPurpleColor)
case .requested: case .requested:
return (String(localized: "friends_request_sent"), AllInColors.grey400Color, AllInColors.componentBackgroundColor) return (String(localized: "friends_request_sent"), AllInColors.grey400Color, AllInColors.componentBackgroundColor)
default: default:
@ -28,10 +29,7 @@ struct Friend: View {
var body: some View { var body: some View {
HStack{ HStack{
AsyncImage(url: URL(string: "https://picsum.photos/536/354")) UserPicture(picture: user.image,username: user.username, size: 45)
.frame(width: 50, height: 50)
.cornerRadius(180)
.scaledToFit()
Text(user.username) Text(user.username)
.fontWeight(.medium) .fontWeight(.medium)
.padding(.leading, 5) .padding(.leading, 5)
@ -39,7 +37,7 @@ struct Friend: View {
.lineLimit(1) .lineLimit(1)
Spacer() Spacer()
Button(StatusValues.0) { Button(StatusValues.0) {
viewModel.toggleFriendStatus(for: user) viewModel.toggleFriendStatus(for: user, isRequest: isRequest)
} }
.minimumScaleFactor(0.3) .minimumScaleFactor(0.3)
.lineLimit(2) .lineLimit(2)
@ -48,6 +46,13 @@ struct Friend: View {
.font(.system(size: 14)) .font(.system(size: 14))
.background(StatusValues.2) .background(StatusValues.2)
.cornerRadius(5) .cornerRadius(5)
if(isRequest){
Button{
viewModel.declineRequest(username: user.username)
}label: {
Image(systemName: "xmark").foregroundColor(.gray)
}.padding([.leading], 25)
}
} }
.padding([.trailing,.leading], 25) .padding([.trailing,.leading], 25)
} }

@ -14,24 +14,19 @@ struct Menu: View {
let parameters: [(String, String, String, String)] = [ let parameters: [(String, String, String, String)] = [
("videoGameImage", String(localized: "drawer_create_a_bet"), String(localized: "drawer_create_a_bet_subtitle"), "CreationBet"), ("videoGameImage", String(localized: "drawer_create_a_bet"), String(localized: "drawer_create_a_bet_subtitle"), "CreationBet"),
("globeImage", String(localized: "drawer_public_bets"), String(localized: "drawer_public_bets_subtitle"), "Bet"),
("moneyImage", String(localized: "drawer_current_bets"), String(localized: "drawer_current_bets_subtitle"), "Current"),
("eyesImage", String(localized: "drawer_bet_history"), String(localized: "drawer_bet_history_subtitle"),"Historic"), ("eyesImage", String(localized: "drawer_bet_history"), String(localized: "drawer_bet_history_subtitle"),"Historic"),
("friendsImage", String(localized: "drawer_friends"), String(localized: "drawer_friends_subtitle"), "Friends"), ("friendsImage", String(localized: "drawer_friends"), String(localized: "drawer_friends_subtitle"), "Friends"),
("moneyImage", String(localized: "drawer_public_bets"), String(localized: "drawer_public_bets_subtitle"), "Bet"),
("rankingImage", String(localized: "drawer_ranking"), String(localized: "drawer_ranking_subtitle"), "Ranking"), ("rankingImage", String(localized: "drawer_ranking"), String(localized: "drawer_ranking_subtitle"), "Ranking"),
("eyesImage", String(localized: "drawer_current_bets"), String(localized: "drawer_current_bets_subtitle"), "Current")
] ]
var body: some View { var body: some View {
VStack(alignment: .leading, spacing: 10) { VStack(alignment: .leading, spacing: 10) {
HStack() { HStack() {
Spacer() Spacer()
VStack(){ VStack(){
Image("defaultUserImage") UserPicture(picture: AppStateContainer.shared.user?.image, username: (AppStateContainer.shared.user?.username)!,size: 100)
.resizable()
.scaledToFit()
.frame(width: 100, height: 100)
.cornerRadius(180)
Text(AppStateContainer.shared.user?.username.capitalized ?? "") Text(AppStateContainer.shared.user?.username.capitalized ?? "")
.fontWeight(.medium) .fontWeight(.medium)
.font(.system(size: 17)) .font(.system(size: 17))
@ -44,7 +39,7 @@ struct Menu: View {
HStack(spacing: 30) { HStack(spacing: 30) {
Spacer() Spacer()
VStack(){ VStack(){
Text("114") Text(AppStateContainer.shared.user?.bestWin ?? 0, format: .number)
.fontWeight(.heavy) .fontWeight(.heavy)
.font(.system(size: 15)) .font(.system(size: 15))
.foregroundColor(.white) .foregroundColor(.white)
@ -54,7 +49,7 @@ struct Menu: View {
.foregroundColor(AllInColors.grey600Color) .foregroundColor(AllInColors.grey600Color)
} }
VStack(){ VStack(){
Text("343") Text(AppStateContainer.shared.user!.bestWin, format: .number)
.fontWeight(.heavy) .fontWeight(.heavy)
.font(.system(size: 15)) .font(.system(size: 15))
.foregroundColor(.white) .foregroundColor(.white)
@ -64,7 +59,7 @@ struct Menu: View {
.foregroundColor(AllInColors.grey600Color) .foregroundColor(AllInColors.grey600Color)
} }
VStack(){ VStack(){
Text("5") Text(AppStateContainer.shared.user!.nbFriends, format: .number)
.fontWeight(.heavy) .fontWeight(.heavy)
.font(.system(size: 15)) .font(.system(size: 15))
.foregroundColor(.white) .foregroundColor(.white)
@ -108,6 +103,9 @@ struct Menu: View {
.frame(maxWidth: .infinity,alignment: .leading) .frame(maxWidth: .infinity,alignment: .leading)
.background(AllInColors.primaryColor) .background(AllInColors.primaryColor)
.edgesIgnoringSafeArea(.bottom) .edgesIgnoringSafeArea(.bottom)
.onAppear {
authService.refreshAuthentication()
}
} }
} }

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

@ -15,6 +15,8 @@ struct ParticipateButton: View {
var bet: Bet? var bet: Bet?
var isDisabled: Bool { var isDisabled: Bool {
guard bet?.author != AppStateContainer.shared.user?.username else { return true }
if let betType = bet?.status { if let betType = bet?.status {
switch betType { switch betType {
case .inProgress: case .inProgress:

@ -1,35 +0,0 @@
//
// ParticipationCell.swift
// AllIn
//
// Created by Lucas Delanier on 21/01/2024.
//
import SwiftUI
import Model
struct ParticipationCell: View {
@State var participation: Participation
var body: some View {
HStack(alignment: .center, spacing: 0){
Circle()
.frame(width: 30, height: 30)
.foregroundColor(AllInColors.grey700Color)
.padding(.trailing, 7)
Text(participation.username)
.font(.system(size: 15))
.foregroundStyle(AllInColors.primaryTextColor)
.fontWeight(.semibold)
Spacer()
Text(participation.stake.description)
.font(.system(size: 18))
.foregroundStyle(AllInColors.lightPurpleColor)
.fontWeight(.bold)
.padding(.trailing, 8)
Image("PurpleAllCoin")
.resizable()
.frame(width: 11, height: 12)
}
}
}

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

@ -10,7 +10,7 @@ import SwiftUI
struct RankingRow: View { struct RankingRow: View {
var number: Int var number: Int
var image: String var image: String?
var pseudo: String var pseudo: String
var allCoins: Int var allCoins: Int
@ -18,29 +18,12 @@ struct RankingRow: View {
HStack(){ HStack(){
Text(number.description) Text(number.description)
.textStyle(weight: .bold, color: AllInColors.lightPurpleColor, size: 18) .textStyle(weight: .bold, color: AllInColors.lightPurpleColor, size: 18)
.padding(.leading, 15) UserInfo(username: pseudo, picture: image, value: allCoins)
Image(image)
.resizable()
.scaledToFit()
.frame(width: 40, height: 40)
.cornerRadius(180)
.padding([.bottom,.top], 10)
Text(pseudo)
.fontWeight(.medium)
.font(.system(size: 16))
.lineLimit(1)
Spacer()
Image("allcoinIcon")
.resizable()
.frame(width: 17, height: 17, alignment: .leading)
Text(allCoins.description)
.textStyle(weight: .black, color: AllInColors.lightPurpleColor, size: 16)
.padding(.trailing, 15)
} }
.padding(10)
.padding(.horizontal, 5)
.background(AllInColors.componentBackgroundColor) .background(AllInColors.componentBackgroundColor)
.cornerRadius(8) .cornerRadius(8)
.padding([.leading,.trailing],20)
.frame(maxWidth: 750) .frame(maxWidth: 750)
} }
} }

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

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

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

@ -6,8 +6,14 @@
// //
import SwiftUI import SwiftUI
import Model
struct TrendingBetCard: View { struct TrendingBetCard: View {
var bet: Bet
@State var showDetails: Bool = false
@State var showParticipate: Bool = false
var body: some View { var body: some View {
VStack(alignment: .leading) { VStack(alignment: .leading) {
HStack { HStack {
@ -20,7 +26,7 @@ struct TrendingBetCard: View {
} }
.padding([.leading, .top], 10) .padding([.leading, .top], 10)
Text("Emre va réussir son TP de CI/CD mercredi?") Text(bet.theme)
.textStyle(weight: .heavy, color: .white, size: 17) .textStyle(weight: .heavy, color: .white, size: 17)
.frame(height: 47) .frame(height: 47)
.multilineTextAlignment(.leading) .multilineTextAlignment(.leading)
@ -56,11 +62,24 @@ struct TrendingBetCard: View {
.stroke(AllInColors.primaryGradient, lineWidth: 5) .stroke(AllInColors.primaryGradient, lineWidth: 5)
) )
.clipShape(RoundedRectangle(cornerRadius: 20, style: .continuous)) .clipShape(RoundedRectangle(cornerRadius: 20, style: .continuous))
.onTapGesture {
showDetails.toggle()
}
.fullScreenCover(isPresented: $showDetails) {
DetailsView(isModalPresented: $showDetails, isModalParticipated: $showParticipate, id: bet.id)
}
} }
} }
struct TrendingBetCard_Previews: PreviewProvider { struct TrendingBetCard_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
TrendingBetCard() TrendingBetCard(bet: BinaryBet(theme: "Football - Finale de la Ligue des Champions",
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,
status: .inProgress,
invited: [],
author: "Imri"))
} }
} }

@ -6,27 +6,29 @@
// //
import SwiftUI import SwiftUI
import Model
struct UserInfo: View { struct UserInfo: View {
var username: String
var picture: String?
var value: Int
var body: some View { var body: some View {
HStack { HStack {
Image("defaultUserImage") UserPicture(picture: picture, username: username, size: 35)
.resizable() .padding(.trailing, 7)
.frame(width: 35, height: 35) Text(username)
Text("David") .font(.system(size: 15))
.textStyle(weight: .bold, color: AllInColors.primaryTextColor, size: 13) .foregroundStyle(AllInColors.primaryTextColor)
.fontWeight(.semibold)
Spacer() Spacer()
Image("allcoinIcon") Text(value.description)
.font(.system(size: 18))
.foregroundStyle(AllInColors.lightPurpleColor)
.fontWeight(.bold)
.padding(.trailing, 4)
Image("PurpleAllCoin")
.resizable() .resizable()
.frame(width: 17, height: 17, alignment: .leading) .frame(width: 15, height: 16)
Text("541")
.textStyle(weight: .black, color: AllInColors.lightPurpleColor, size: 16)
}
} }
}
struct UserInfo_Previews: PreviewProvider {
static var previews: some View {
UserInfo()
} }
} }

@ -6,22 +6,19 @@
// //
import SwiftUI import SwiftUI
import Model
struct UsersPreview: View { struct UsersPreview: View {
var users: [User]?
var body: some View { var body: some View {
if users != nil {
HStack(spacing: -20){ HStack(spacing: -20){
ForEach(users!.prefix(4)) { user in
Image("defaultUserImage") Image("defaultUserImage")
.resizable() .resizable()
.frame(width: 35, height: 35) .frame(width: 35, height: 35)
Image("defaultUserImage") }
.resizable() }
.frame(width: 35, height: 35)
Image("defaultUserImage")
.resizable()
.frame(width: 35, height: 35)
Image("defaultUserImage")
.resizable()
.frame(width: 35, height: 35)
} }
} }
} }

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

@ -0,0 +1,58 @@
//
// userPicture.swift
// AllIn
//
// Created by Lucas Delanier on 04/06/2024.
//
import SwiftUI
struct UserPicture: View {
var picture: String?
var username: String
var size: CGFloat
var body: some View {
ZStack {
if let pictureURL = picture {
userImage(url: pictureURL)
} else {
placeholderImage
}
}
}
@MainActor private func userImage(url: String) -> some View {
AsyncCachedImage(url: URL(string: url)) { image in
image
.resizable()
.frame(width: size, height: size)
.clipShape(Circle())
} placeholder: {
placeholderImage
}
}
private var placeholderImage: some View {
Circle()
.foregroundColor(.gray)
.frame(width: size, height: size)
.overlay(
Text(username.prefix(2).uppercased())
.fontWeight(.medium)
.foregroundStyle(.white)
.font(.system(size: fontSize(for: size)))
)
}
private func fontSize(for diameter: CGFloat) -> CGFloat {
let fontScaleFactor: CGFloat = 0.45
return diameter * fontScaleFactor
}
}
struct UserPicture_Previews: PreviewProvider {
static var previews: some View {
UserPicture(picture: nil, username: "Lucas", size: 100)
}
}

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

@ -21,6 +21,8 @@
"generic_in_waiting" = "In waiting"; "generic_in_waiting" = "In waiting";
"generic_search" = "Search"; "generic_search" = "Search";
"generic_add" = "Add"; "generic_add" = "Add";
"generic_acccept"="Accept";
"generic_decline"="Decline";
"generic_delete" = "Delete"; "generic_delete" = "Delete";
"generic_stake" = "Stake"; "generic_stake" = "Stake";
"network_error_text" = "Make sure to be properly connected to the network then try again."; "network_error_text" = "Make sure to be properly connected to the network then try again.";
@ -44,15 +46,13 @@
"drawer_nb_friends" = "Friends"; "drawer_nb_friends" = "Friends";
"drawer_friends" = "FRIENDS"; "drawer_friends" = "FRIENDS";
"drawer_friends_subtitle" = "Challenge your folks by adding them as friends."; "drawer_friends_subtitle" = "Challenge your folks by adding them as friends.";
"drawer_public_bets" = "PUBLIC BETS"; "drawer_public_bets" = "PARTICIPATE";
"drawer_public_bets_subtitle" = "Browse the most popular bets of the moment."; "drawer_public_bets_subtitle" = "Browse the most popular bets of the moment.";
"drawer_current_bets" = "CURRENT BETS"; "drawer_create_a_bet" = "NEW BET";
"drawer_current_bets_subtitle" = "View your current bets.";
"drawer_create_a_bet" = "CREATE A BET";
"drawer_create_a_bet_subtitle" = "Create a net bet and get your friends participating."; "drawer_create_a_bet_subtitle" = "Create a net bet and get your friends participating.";
"drawer_bet_history" = "BET HISTORY"; "drawer_bet_history" = "HISTORY";
"drawer_bet_history_subtitle" = "View your current and finished bets."; "drawer_bet_history_subtitle" = "View your current and finished bets.";
"drawer_current_bets" = "CURRENT BETS"; "drawer_current_bets" = "MY PARTICIPATIONS";
"drawer_current_bets_subtitle" = "Manage your bets and reward the winners."; "drawer_current_bets_subtitle" = "Manage your bets and reward the winners.";
"drawer_ranking" = "RANKING"; "drawer_ranking" = "RANKING";
"drawer_ranking_subtitle" = "Check your ranking among your friends."; "drawer_ranking_subtitle" = "Check your ranking among your friends.";
@ -133,7 +133,7 @@
"bet_participate" = "Participate"; "bet_participate" = "Participate";
"bet_proposed_by_format %@" = "Proposed by %@"; "bet_proposed_by_format %@" = "Proposed by %@";
"bet_proposed_by_format" = "Proposed by"; "bet_proposed_by_format" = "Proposed by";
"bet_players_waiting_format %@" = "%@ joueurs en attente"; "bet_players_waiting_format %@" = "%@ players waiting";
"bet_players_format" = "players"; "bet_players_format" = "players";
"bet_points_at_stake_format" = "points at stake"; "bet_points_at_stake_format" = "points at stake";
@ -148,6 +148,7 @@
"bet_status_participants_list" = "Participants"; "bet_status_participants_list" = "Participants";
"bet_status_details_drawer" = "Details"; "bet_status_details_drawer" = "Details";
"participation_possible_winnings" = "Possible winnings"; "participation_possible_winnings" = "Possible winnings";
"bet_status_stake" = "Gambled";
/// Bet history /// Bet history
@ -167,6 +168,7 @@
"friends_title" = "Friends"; "friends_title" = "Friends";
"friends_request_sent" = "Request sent"; "friends_request_sent" = "Request sent";
"friends_request"= "Friends Request";
/// Daily reward /// Daily reward
@ -177,3 +179,16 @@
"notification_title_end_bet_date" = "Who will be the winners?"; "notification_title_end_bet_date" = "Who will be the winners?";
"notification_subtitle_end_bet_date %@" = "The %@ bet has reached its deadline. Go to the app to enter the winning answer."; "notification_subtitle_end_bet_date %@" = "The %@ bet has reached its deadline. Go to the app to enter the winning answer.";
/// Empty Views
"empty_ranking_title" = "It's a bit empty around here";
"empty_ranking_explain" = "Add some friends to display them in the leaderboard";
"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";

@ -21,6 +21,8 @@
"generic_in_waiting" = "En attente"; "generic_in_waiting" = "En attente";
"generic_search" = "Rechercher"; "generic_search" = "Rechercher";
"generic_add" = "Ajouter"; "generic_add" = "Ajouter";
"generic_acccept"="Accepter";
"generic_decline"="Decliner";
"generic_delete" = "Supprimer"; "generic_delete" = "Supprimer";
"generic_stake" = "Mise"; "generic_stake" = "Mise";
"network_error_text" = "Assurez-vous d\'être bien connecté au réseau puis réessayez."; "network_error_text" = "Assurez-vous d\'être bien connecté au réseau puis réessayez.";
@ -44,15 +46,13 @@
"drawer_nb_friends" = "Amis"; "drawer_nb_friends" = "Amis";
"drawer_friends" = "AMIS"; "drawer_friends" = "AMIS";
"drawer_friends_subtitle" = "Défiez vos proches en les ajoutant en amis."; "drawer_friends_subtitle" = "Défiez vos proches en les ajoutant en amis.";
"drawer_public_bets" = "BETS PUBLIQUES"; "drawer_public_bets" = "PARTICIPER";
"drawer_public_bets_subtitle" = "Parcourez les bets les plus populaires du moment."; "drawer_public_bets_subtitle" = "Parcourez les bets les plus populaires du moment.";
"drawer_current_bets" = "BETS EN COURS"; "drawer_create_a_bet" = "NOUVEAU BET";
"drawer_current_bets_subtitle" = "Consultez vos paris en cours.";
"drawer_create_a_bet" = "CREER UN BET";
"drawer_create_a_bet_subtitle" = "Créez un nouveau bet et faites participer vos amis."; "drawer_create_a_bet_subtitle" = "Créez un nouveau bet et faites participer vos amis.";
"drawer_bet_history" = "HISTORIQUE DES BETS"; "drawer_bet_history" = "HISTORIQUE";
"drawer_bet_history_subtitle" = "Consultez vos paris en cours et terminés."; "drawer_bet_history_subtitle" = "Consultez vos paris en cours et terminés.";
"drawer_current_bets" = "BETS EN COURS"; "drawer_current_bets" = "MES PARTICIPATIONS";
"drawer_current_bets_subtitle" = "Gérez vos bets et récompensez les gagnants."; "drawer_current_bets_subtitle" = "Gérez vos bets et récompensez les gagnants.";
"drawer_ranking" = "CLASSEMENT"; "drawer_ranking" = "CLASSEMENT";
"drawer_ranking_subtitle" = "Consultez votre classement parmis vos amis."; "drawer_ranking_subtitle" = "Consultez votre classement parmis vos amis.";
@ -133,7 +133,7 @@
"bet_participate" = "Participer"; "bet_participate" = "Participer";
"bet_proposed_by_format %@" = "Proposé par %@"; "bet_proposed_by_format %@" = "Proposé par %@";
"bet_proposed_by_format" = "Proposé par"; "bet_proposed_by_format" = "Proposé par";
"bet_players_waiting_format %@" = "%@ players waiting"; "bet_players_waiting_format %@" = "%@ joueurs en attente";
"bet_players_format" = "joueurs"; "bet_players_format" = "joueurs";
"bet_points_at_stake_format" = "points en jeu"; "bet_points_at_stake_format" = "points en jeu";
@ -148,6 +148,7 @@
"bet_status_participants_list" = "Liste des participants"; "bet_status_participants_list" = "Liste des participants";
"bet_status_details_drawer" = "Détails"; "bet_status_details_drawer" = "Détails";
"participation_possible_winnings" = "Gains possibles"; "participation_possible_winnings" = "Gains possibles";
"bet_status_stake" = "Pariés";
/// Bet history /// Bet history
@ -167,6 +168,7 @@
"friends_title" = "Amis"; "friends_title" = "Amis";
"friends_request_sent" = "Requête envoyée"; "friends_request_sent" = "Requête envoyée";
"friends_request"= "Requêtes d'amis";
/// Daily reward /// Daily reward
@ -177,3 +179,16 @@
"notification_title_end_bet_date" = "Qui seront les vainqueurs ?"; "notification_title_end_bet_date" = "Qui seront les vainqueurs ?";
"notification_subtitle_end_bet_date %@" = "Le pari %@ a atteint sa date limite. Rendez-vous dans l'application pour renseigner la réponse gagnante."; "notification_subtitle_end_bet_date %@" = "Le pari %@ a atteint sa date limite. Rendez-vous dans l'application pour renseigner la réponse gagnante.";
/// Empty Views
"empty_ranking_title" = "Cest un peu vide par ici";
"empty_ranking_explain" = "Ajoutez des amis pour les afficher dans le classement";
"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.";

@ -148,7 +148,7 @@ class AuthService: IAuthService {
} }
private func initManagerVM(token: String) { private func initManagerVM(token: String) {
DependencyInjection.shared.addSingleton(Manager.self, Manager(withBetDataManager: BetApiManager(withUserToken: token), withUserDataManager: UserApiManager(withUserToken: token))) DependencyInjection.shared.addSingleton(Manager.self, Manager(withBetDataManager: BetApiManager(withUserToken: token, andApiUrl: Config.allInApi), withUserDataManager: UserApiManager(withUserToken: token, andApiUrl: Config.allInApi)))
} }
public func logout() { public func logout() {

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

@ -13,27 +13,24 @@ import Combine
class BetViewModel: ObservableObject { class BetViewModel: ObservableObject {
@Inject var manager: Manager @Inject var manager: Manager
@Inject var authService: IAuthService
@Published var popularBet: Bet?
@Published private(set) var bets: [Bet] = [] @Published private(set) var bets: [Bet] = []
@Published var betsOver: [BetDetail] = [] @Published var betsOver: [BetDetail] = []
@Published var showingSheet: Bool = false @Published var betsWon: [BetResultDetail] = []
@Published var showingSheetOver: Bool = false
@Published var showingSheetWon: Bool = false
@Published var filters: Set<BetFilter> = [] { @Published var filters: Set<BetFilter> = [] {
didSet { didSet {
getItems() getItems()
} }
} }
private var cancellables = Set<AnyCancellable>()
init() { init() {
getItems() getItems()
getPopularBet()
// Observer for changes in filters getBetsOver()
$filters getBetsWon()
.sink { [weak self] _ in
self?.getItems()
}
.store(in: &cancellables)
} }
func getItems() { func getItems() {
@ -42,12 +39,35 @@ class BetViewModel: ObservableObject {
self.bets = bets self.bets = bets
} }
} }
}
func getBetsOver() {
manager.getBetsOver() { bets in manager.getBetsOver() { bets in
DispatchQueue.main.async { DispatchQueue.main.async {
self.betsOver = bets self.betsOver = bets
if !self.betsOver.isEmpty { if !self.betsOver.isEmpty {
self.showingSheet = true self.showingSheetOver = true
}
}
}
} }
func getBetsWon() {
manager.getBetsWon() { bets in
DispatchQueue.main.async {
self.betsWon = bets
if !self.betsWon.isEmpty {
self.showingSheetWon = true
self.authService.refreshAuthentication()
}
}
}
}
func getPopularBet() {
manager.getPopularBet() { bet in
DispatchQueue.main.async {
self.popularBet = bet
} }
} }
} }

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

@ -21,8 +21,10 @@ class CurrentBetViewModel: ObservableObject {
func getItems() { func getItems() {
manager.getCurrentBets(withIndex: 0, withCount: 20) { bets in manager.getCurrentBets(withIndex: 0, withCount: 20) { bets in
DispatchQueue.main.async {
self.bets = bets self.bets = bets
} }
} }
}
} }

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

@ -14,6 +14,7 @@ class FriendsViewModel: ObservableObject {
@Inject var manager: Manager @Inject var manager: Manager
@Published private(set) var users: [User] = [] @Published private(set) var users: [User] = []
@Published private(set) var requests: [User] = []
@Published var text: String = "" { @Published var text: String = "" {
didSet { didSet {
if text.isEmpty { if text.isEmpty {
@ -26,6 +27,7 @@ class FriendsViewModel: ObservableObject {
init() { init() {
getItems() getItems()
getRequests()
} }
func getItems() { func getItems() {
@ -36,6 +38,14 @@ class FriendsViewModel: ObservableObject {
} }
} }
func getRequests() {
manager.getRequests() { friends in
DispatchQueue.main.async {
self.requests = friends
}
}
}
func search() { func search() {
guard text.allSatisfy({ $0.isLetter || $0.isNumber }) else { guard text.allSatisfy({ $0.isLetter || $0.isNumber }) else {
return return
@ -46,10 +56,22 @@ class FriendsViewModel: ObservableObject {
} }
} }
func toggleFriendStatus(for user: User) { func toggleFriendStatus(for user: User, isRequest: Bool) {
guard let index = users.firstIndex(where: { $0.username == user.username }) else { return } let targetList = isRequest ? requests : users
var updatedUser = users[index]
guard let index = targetList.firstIndex(where: { $0.username == user.username }) else { return }
var updatedUser = targetList[index]
if isRequest {
if updatedUser.friendStatus == .requested {
updatedUser.friendStatus = .notFriend
deleteItem(username: user.username)
} else {
updatedUser.friendStatus = .friend
addItem(username: user.username)
}
requests.remove(at: index)
} else {
switch updatedUser.friendStatus { switch updatedUser.friendStatus {
case .friend: case .friend:
updatedUser.friendStatus = .notFriend updatedUser.friendStatus = .notFriend
@ -63,9 +85,19 @@ class FriendsViewModel: ObservableObject {
default: default:
break break
} }
users[index] = updatedUser users[index] = updatedUser
} }
}
func declineRequest(username: String){
guard let index = requests.firstIndex(where: { $0.username == username }) else { return }
manager.removeFriend(withUsername: username) { statusCode in
}
requests.remove(at: index)
}
func deleteItem(username: String) { func deleteItem(username: String) {
manager.removeFriend(withUsername: username) { statusCode in manager.removeFriend(withUsername: username) { statusCode in

@ -6,7 +6,6 @@
// //
import Foundation import Foundation
import SwiftUI
import DependencyInjection import DependencyInjection
import Model import Model
@ -14,11 +13,15 @@ class HistoricBetViewModel: ObservableObject {
@Inject var manager: Manager @Inject var manager: Manager
@Published private(set) var bets: [BetResultDetail] = []
init() { init() {
getItems() getItems()
} }
func getItems() { func getItems() {
manager.getHistoricBets(withIndex: 0, withCount: 20) { bets in
self.bets = bets
}
} }
} }

@ -23,7 +23,9 @@ class RankingViewModel: ObservableObject {
manager.getFriends() { users in manager.getFriends() { users in
var friends = users var friends = users
friends.append(AppStateContainer.shared.user!) friends.append(AppStateContainer.shared.user!)
DispatchQueue.main.async {
self.friends = friends.sorted(by: { $0.nbCoins > $1.nbCoins }) self.friends = friends.sorted(by: { $0.nbCoins > $1.nbCoins })
} }
} }
}
} }

@ -102,6 +102,7 @@ class RegisterViewModel: ObservableObject {
registerPasswordFieldError = newRegisterPasswordFieldError registerPasswordFieldError = newRegisterPasswordFieldError
registerConfirmPasswordFieldError = newRegisterConfirmPasswordFieldError registerConfirmPasswordFieldError = newRegisterConfirmPasswordFieldError
} }
return false return false
} }

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

@ -20,12 +20,16 @@ struct BetView: View {
ScrollView(showsIndicators: false) { ScrollView(showsIndicators: false) {
LazyVStack(alignment: .center, spacing: 0, pinnedViews: [.sectionHeaders]) { LazyVStack(alignment: .center, spacing: 0, pinnedViews: [.sectionHeaders]) {
TrendingBetCard().padding(.top,25).padding([.leading,.trailing],25) if let bet = viewModel.popularBet {
TrendingBetCard(bet: bet)
.padding(.top,25)
.padding([.leading,.trailing],25)
}
Section { Section {
VStack(spacing: 20){ VStack(spacing: 20){
if(viewModel.bets.isEmpty){ if(viewModel.bets.isEmpty){
EmptyInfo(emoji:"🎮", title: "Aucun Bet ne correspond à votre recherche", explain: "") EmptyInfo(emoji:"🎮", title: String(localized: "empty_bets_title"), explain: "")
} }
else{ else{
ForEach(viewModel.bets, id: \.id) { (bet: Bet) in ForEach(viewModel.bets, id: \.id) { (bet: Bet) in
@ -55,26 +59,25 @@ struct BetView: View {
.refreshable { .refreshable {
viewModel.getItems() viewModel.getItems()
} }
.sheet(isPresented: $viewModel.showingSheet, onDismiss: { .sheet(isPresented: $viewModel.showingSheetOver, onDismiss: {
viewModel.betsOver.removeFirst() viewModel.betsOver.removeFirst()
viewModel.showingSheet = !viewModel.betsOver.isEmpty viewModel.showingSheetOver = !viewModel.betsOver.isEmpty
}) { }) {
if let firstBetDetail = viewModel.betsOver.first { if let firstBetDetail = viewModel.betsOver.first {
BetEndingValidationView(bet: firstBetDetail) BetEndingValidationView(bet: firstBetDetail)
} }
} }
.sheet(isPresented: $viewModel.showingSheetWon, onDismiss: {
viewModel.betsWon.removeFirst()
viewModel.showingSheetWon = !viewModel.betsWon.isEmpty
}) {
if let firstBetResultDetail = viewModel.betsWon.first {
WinModal(betResult: firstBetResultDetail)
}
}
Spacer() Spacer()
} }
.edgesIgnoringSafeArea(.bottom) .edgesIgnoringSafeArea(.bottom)
.background(AllInColors.backgroundColor) .background(AllInColors.backgroundColor)
}
}
struct BetView_Previews: PreviewProvider {
static var previews: some View {
BetView(showMenu: .constant(false))
.preferredColorScheme(.light)
} }
} }

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

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

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

@ -11,17 +11,18 @@ struct FriendsView: View {
@StateObject private var viewModel = FriendsViewModel() @StateObject private var viewModel = FriendsViewModel()
@Binding var showMenu: Bool @Binding var showMenu: Bool
@State private var selectedTab = 0
var body: some View { var body: some View {
VStack(alignment: .center, spacing: 0) { VStack(alignment: .center, spacing: 0) {
TopBar(showMenu: self.$showMenu) TopBar(showMenu: self.$showMenu)
Text("friends_title") TabView(selection: $selectedTab) {
.textStyle(weight: .bold, color: AllInColors.grey500Color, size: 25) ScrollView(showsIndicators: false){
.padding([.top,.bottom],15) VStack{
HStack { HStack {
TextField("Search...", text: $viewModel.text) TextField("Search...", text: $viewModel.text)
.padding(7) .padding(7)
.zIndex(200)
.padding(.horizontal, 25) .padding(.horizontal, 25)
.background(Color(.systemGray6)) .background(Color(.systemGray6))
.cornerRadius(8) .cornerRadius(8)
@ -46,18 +47,73 @@ struct FriendsView: View {
.padding(.horizontal, 10) .padding(.horizontal, 10)
} }
if(viewModel.users.isEmpty){ if(viewModel.users.isEmpty){
EmptyInfo(emoji:"👥", title: "Vous navez pas encore damis", explain: "Ajoutez les depuis cet écran").padding(.top, 40) EmptyInfo(emoji:"👥", title: String(localized: "empty_friends_title"), explain: String(localized: "empty_friends_explain")).padding(.top, 40)
} }
else{ else{
ScrollView(showsIndicators: false){
ForEach(viewModel.users, id: \.self) { friend in ForEach(viewModel.users, id: \.self) { friend in
Friend(user: friend, viewModel: viewModel) Friend(user: friend, isRequest: false, viewModel: viewModel)
}
}
}
Spacer()
}
.refreshable {
viewModel.getItems()
}
.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()
} }
.padding(.top, 25) .padding(.top, 25)
} }
Spacer() Spacer()
} }
}.refreshable {
viewModel.getRequests()
}
.padding(.top, 50)
.tag(1)
}
.overlay(
HStack {
Button(action: {
selectedTab = 0
}) {
Text("friends_title")
.font(.system(size: 16))
.padding()
.fontWeight(selectedTab == 0 ? .bold : .semibold)
.foregroundColor(selectedTab == 0 ? AllInColors.primaryTextColor : .gray)
.offset(y: 0)
}
Button(action: {
selectedTab = 1
}) {
Text(String(localized: "friends_request") + (viewModel.requests.isEmpty ? "" : "(\(viewModel.requests.count.description))"))
.font(.system(size: 16))
.padding()
.fontWeight(selectedTab == 1 ? .bold : .semibold)
.foregroundColor(selectedTab == 1 ? AllInColors.primaryTextColor : .gray)
.offset(y: 0)
}
}
, alignment: .top)
.tabViewStyle(PageTabViewStyle())
}
.edgesIgnoringSafeArea(.bottom) .edgesIgnoringSafeArea(.bottom)
.background(AllInColors.backgroundColor) .background(AllInColors.backgroundColor)
} }

@ -6,9 +6,11 @@
// //
import SwiftUI import SwiftUI
import Model
struct HistoricBetView: View { struct HistoricBetView: View {
@StateObject private var viewModel = HistoricBetViewModel()
@Binding var showMenu: Bool @Binding var showMenu: Bool
@State private var showingSheet = false @State private var showingSheet = false
@ -22,8 +24,9 @@ struct HistoricBetView: View {
.textStyle(weight: .bold, color: AllInColors.grey500Color, size: 25) .textStyle(weight: .bold, color: AllInColors.grey500Color, size: 25)
.padding([.top],15) .padding([.top],15)
VStack(spacing: 20){ VStack(spacing: 20){
// ReviewCard(amountBetted: 110, isAWin: true) ForEach(viewModel.bets, id: \.bet.id) { (betDetail: BetResultDetail) in
// ReviewCard(amountBetted: 3, isAWin: false) ReviewCard(bet: betDetail.bet, amount: betDetail.participation.stake, isWin: betDetail.won)
}
} }
.padding([.trailing, .leading, .bottom],25) .padding([.trailing, .leading, .bottom],25)
} }

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

@ -20,7 +20,6 @@ struct RankingView: View {
.textStyle(weight: .bold, color: AllInColors.grey500Color, size: 25) .textStyle(weight: .bold, color: AllInColors.grey500Color, size: 25)
.padding([.top,.bottom], 15) .padding([.top,.bottom], 15)
if !viewModel.friends.isEmpty {
HStack { HStack {
if viewModel.friends.indices.contains(0) { if viewModel.friends.indices.contains(0) {
ZStack { ZStack {
@ -52,12 +51,7 @@ struct RankingView: View {
.cornerRadius(41.5, corners: .topLeft) .cornerRadius(41.5, corners: .topLeft)
.cornerRadius(8, corners: .topRight) .cornerRadius(8, corners: .topRight)
.cornerRadius(15, corners: [.bottomLeft, .bottomRight]) .cornerRadius(15, corners: [.bottomLeft, .bottomRight])
UserPicture(picture: viewModel.friends[0].image, username: viewModel.friends[0].username, size: 70)
Image("defaultUserImage")
.resizable()
.frame(width: 70, height: 70)
.scaledToFit()
.cornerRadius(180)
.offset(x: 0, y: -55) .offset(x: 0, y: -55)
Text("1") Text("1")
@ -101,11 +95,7 @@ struct RankingView: View {
.cornerRadius(8, corners: .topLeft) .cornerRadius(8, corners: .topLeft)
.cornerRadius(15, corners: [.bottomLeft, .bottomRight]) .cornerRadius(15, corners: [.bottomLeft, .bottomRight])
Image("defaultUserImage") UserPicture(picture: viewModel.friends[1].image, username: viewModel.friends[1].username, size: 50)
.resizable()
.frame(width: 50, height: 50)
.scaledToFit()
.cornerRadius(180)
.offset(x: 0, y: -50) .offset(x: 0, y: -50)
Text("2") Text("2")
@ -121,20 +111,26 @@ struct RankingView: View {
} }
} }
.padding([.leading, .trailing, .top], 20) .padding([.leading, .trailing, .top], 20)
}
if viewModel.friends.count == 1 {
EmptyInfo(emoji:"👀", title: String(localized: "empty_ranking_title"), explain: String(localized: "empty_ranking_explain")).padding(.top, 40)
}
else{
ScrollView(showsIndicators: false) { ScrollView(showsIndicators: false) {
ForEach(viewModel.friends.indices.dropFirst(2), id: \.self) { index in ForEach(viewModel.friends.indices.dropFirst(2), id: \.self) { index in
let friend = viewModel.friends[index] let friend = viewModel.friends[index]
RankingRow( RankingRow(
number: index + 1, number: index + 1,
image: "defaultUserImage", image: friend.image,
pseudo: friend.username, pseudo: friend.username,
allCoins: friend.nbCoins allCoins: friend.nbCoins
) )
} }
} }
.padding(.top, 10) .padding(.top, 10)
.padding(.horizontal, 20)
}
Spacer() Spacer()
} }
.edgesIgnoringSafeArea(.bottom).background(AllInColors.backgroundColor) .edgesIgnoringSafeArea(.bottom).background(AllInColors.backgroundColor)

@ -9,17 +9,20 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
120919182B56D0AE00D0FA29 /* ParticipationModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 120919172B56D0AE00D0FA29 /* ParticipationModal.swift */; }; 120919182B56D0AE00D0FA29 /* ParticipationModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 120919172B56D0AE00D0FA29 /* ParticipationModal.swift */; };
1209191A2B56DC6C00D0FA29 /* DropDownAnswerMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 120919192B56DC6C00D0FA29 /* DropDownAnswerMenu.swift */; }; 1209191A2B56DC6C00D0FA29 /* DropDownAnswerMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 120919192B56DC6C00D0FA29 /* DropDownAnswerMenu.swift */; };
12228A212C1760D2008524F4 /* BinaryBetLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12228A202C1760D2008524F4 /* BinaryBetLine.swift */; };
12228A232C176117008524F4 /* CustomBetLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12228A222C176117008524F4 /* CustomBetLine.swift */; };
123225D92B67B46100D30BB3 /* BetEndingValidationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123225D82B67B46100D30BB3 /* BetEndingValidationView.swift */; }; 123225D92B67B46100D30BB3 /* BetEndingValidationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123225D82B67B46100D30BB3 /* BetEndingValidationView.swift */; };
123225DB2B67E41400D30BB3 /* ChoiceFinalAnswerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123225DA2B67E41400D30BB3 /* ChoiceFinalAnswerCell.swift */; }; 123225DB2B67E41400D30BB3 /* ChoiceFinalAnswerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123225DA2B67E41400D30BB3 /* ChoiceFinalAnswerCell.swift */; };
123590B42B51792000F7AEBD /* DetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123590B32B51792000F7AEBD /* DetailsView.swift */; }; 123590B42B51792000F7AEBD /* DetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123590B32B51792000F7AEBD /* DetailsView.swift */; };
123590B62B5537E200F7AEBD /* ResultBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123590B52B5537E200F7AEBD /* ResultBanner.swift */; }; 123590B62B5537E200F7AEBD /* ResultBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123590B52B5537E200F7AEBD /* ResultBanner.swift */; };
123590B82B5541BA00F7AEBD /* ParticipateButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123590B72B5541BA00F7AEBD /* ParticipateButton.swift */; }; 123590B82B5541BA00F7AEBD /* ParticipateButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123590B72B5541BA00F7AEBD /* ParticipateButton.swift */; };
123F31DB2C0F26E8009B6D65 /* userPicture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123F31DA2C0F26E8009B6D65 /* userPicture.swift */; };
123F31E02C11E99E009B6D65 /* asyncCachedImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123F31DF2C11E99E009B6D65 /* asyncCachedImage.swift */; };
1244EF602B4EC31E00374ABF /* HistoricBetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1244EF5F2B4EC31E00374ABF /* HistoricBetView.swift */; }; 1244EF602B4EC31E00374ABF /* HistoricBetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1244EF5F2B4EC31E00374ABF /* HistoricBetView.swift */; };
1244EF622B4EC67000374ABF /* ReviewCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1244EF612B4EC67000374ABF /* ReviewCard.swift */; }; 1244EF622B4EC67000374ABF /* ReviewCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1244EF612B4EC67000374ABF /* ReviewCard.swift */; };
129D051D2B6E7FF0003D3E08 /* OddCapsule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 129D051C2B6E7FF0003D3E08 /* OddCapsule.swift */; }; 129D051D2B6E7FF0003D3E08 /* OddCapsule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 129D051C2B6E7FF0003D3E08 /* OddCapsule.swift */; };
12A9E4942C07132600AB8677 /* EmptyInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12A9E4932C07132600AB8677 /* EmptyInfo.swift */; }; 12A9E4942C07132600AB8677 /* EmptyInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12A9E4932C07132600AB8677 /* EmptyInfo.swift */; };
12C370482B5A5EE500CD9F0F /* BetLineLoading.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12C370472B5A5EE500CD9F0F /* BetLineLoading.swift */; }; 12C370482B5A5EE500CD9F0F /* BetLineLoading.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12C370472B5A5EE500CD9F0F /* BetLineLoading.swift */; };
12C3704A2B5D5BD000CD9F0F /* ParticipationCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12C370492B5D5BD000CD9F0F /* ParticipationCell.swift */; };
EC0193782B25BF16005D81E6 /* AllcoinsCapsule.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0193772B25BF16005D81E6 /* AllcoinsCapsule.swift */; }; EC0193782B25BF16005D81E6 /* AllcoinsCapsule.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0193772B25BF16005D81E6 /* AllcoinsCapsule.swift */; };
EC01937A2B25C12B005D81E6 /* BetCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0193792B25C12B005D81E6 /* BetCard.swift */; }; EC01937A2B25C12B005D81E6 /* BetCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0193792B25C12B005D81E6 /* BetCard.swift */; };
EC01937C2B25C2A8005D81E6 /* AllcoinsCounter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC01937B2B25C2A8005D81E6 /* AllcoinsCounter.swift */; }; EC01937C2B25C2A8005D81E6 /* AllcoinsCounter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC01937B2B25C2A8005D81E6 /* AllcoinsCounter.swift */; };
@ -33,6 +36,7 @@
EC30770B2B24D9160060E34D /* WelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC30770A2B24D9160060E34D /* WelcomeView.swift */; }; EC30770B2B24D9160060E34D /* WelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC30770A2B24D9160060E34D /* WelcomeView.swift */; };
EC30770D2B24DB7A0060E34D /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC30770C2B24DB7A0060E34D /* Extensions.swift */; }; EC30770D2B24DB7A0060E34D /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC30770C2B24DB7A0060E34D /* Extensions.swift */; };
EC30770F2B24FCB00060E34D /* RegisterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC30770E2B24FCB00060E34D /* RegisterView.swift */; }; EC30770F2B24FCB00060E34D /* RegisterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC30770E2B24FCB00060E34D /* RegisterView.swift */; };
EC34FDCE2C0DB46C0020A371 /* Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC34FDCD2C0DB46C0020A371 /* Config.swift */; };
EC4F0D2C2B4EBF5600853949 /* Model in Frameworks */ = {isa = PBXBuildFile; productRef = ECB357342B3E13A400045D41 /* Model */; }; EC4F0D2C2B4EBF5600853949 /* Model in Frameworks */ = {isa = PBXBuildFile; productRef = ECB357342B3E13A400045D41 /* Model */; };
EC60C5682B5A83FB00FFD6EF /* StubLib in Frameworks */ = {isa = PBXBuildFile; productRef = EC60C5672B5A83FB00FFD6EF /* StubLib */; }; EC60C5682B5A83FB00FFD6EF /* StubLib in Frameworks */ = {isa = PBXBuildFile; productRef = EC60C5672B5A83FB00FFD6EF /* StubLib */; };
EC650A422B25C817003AFCAD /* Friend.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC650A412B25C817003AFCAD /* Friend.swift */; }; EC650A422B25C817003AFCAD /* Friend.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC650A412B25C817003AFCAD /* Friend.swift */; };
@ -56,7 +60,6 @@
EC6B96B72B24B4CC00FC1C58 /* AllInUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC6B96B62B24B4CC00FC1C58 /* AllInUITests.swift */; }; EC6B96B72B24B4CC00FC1C58 /* AllInUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC6B96B62B24B4CC00FC1C58 /* AllInUITests.swift */; };
EC6B96B92B24B4CC00FC1C58 /* AllInUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC6B96B82B24B4CC00FC1C58 /* AllInUITestsLaunchTests.swift */; }; EC6B96B92B24B4CC00FC1C58 /* AllInUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC6B96B82B24B4CC00FC1C58 /* AllInUITestsLaunchTests.swift */; };
EC6B96CC2B24B7E500FC1C58 /* IAuthService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC6B96CB2B24B7E500FC1C58 /* IAuthService.swift */; }; EC6B96CC2B24B7E500FC1C58 /* IAuthService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC6B96CB2B24B7E500FC1C58 /* IAuthService.swift */; };
EC6B96CF2B24B8D900FC1C58 /* Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC6B96CE2B24B8D900FC1C58 /* Config.swift */; };
EC6B96D12B24BAE800FC1C58 /* AuthService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC6B96D02B24BAE800FC1C58 /* AuthService.swift */; }; EC6B96D12B24BAE800FC1C58 /* AuthService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC6B96D02B24BAE800FC1C58 /* AuthService.swift */; };
EC6B96D52B24BE0E00FC1C58 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC6B96D42B24BE0E00FC1C58 /* MainView.swift */; }; EC6B96D52B24BE0E00FC1C58 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC6B96D42B24BE0E00FC1C58 /* MainView.swift */; };
EC6B96D82B24BF2100FC1C58 /* Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC6B96D72B24BF2100FC1C58 /* Menu.swift */; }; EC6B96D82B24BF2100FC1C58 /* Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC6B96D72B24BF2100FC1C58 /* Menu.swift */; };
@ -148,17 +151,20 @@
120919192B56DC6C00D0FA29 /* DropDownAnswerMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropDownAnswerMenu.swift; sourceTree = "<group>"; }; 120919192B56DC6C00D0FA29 /* DropDownAnswerMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropDownAnswerMenu.swift; sourceTree = "<group>"; };
122278B72B4BDE1100E632AA /* DependencyInjection.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DependencyInjection.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 122278B72B4BDE1100E632AA /* DependencyInjection.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DependencyInjection.framework; sourceTree = BUILT_PRODUCTS_DIR; };
122278B92B4BDE9500E632AA /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Package.swift; path = ../Model/Package.swift; sourceTree = "<group>"; }; 122278B92B4BDE9500E632AA /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Package.swift; path = ../Model/Package.swift; sourceTree = "<group>"; };
12228A202C1760D2008524F4 /* BinaryBetLine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BinaryBetLine.swift; sourceTree = "<group>"; };
12228A222C176117008524F4 /* CustomBetLine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomBetLine.swift; sourceTree = "<group>"; };
123225D82B67B46100D30BB3 /* BetEndingValidationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BetEndingValidationView.swift; sourceTree = "<group>"; }; 123225D82B67B46100D30BB3 /* BetEndingValidationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BetEndingValidationView.swift; sourceTree = "<group>"; };
123225DA2B67E41400D30BB3 /* ChoiceFinalAnswerCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChoiceFinalAnswerCell.swift; sourceTree = "<group>"; }; 123225DA2B67E41400D30BB3 /* ChoiceFinalAnswerCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChoiceFinalAnswerCell.swift; sourceTree = "<group>"; };
123590B32B51792000F7AEBD /* DetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailsView.swift; sourceTree = "<group>"; }; 123590B32B51792000F7AEBD /* DetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailsView.swift; sourceTree = "<group>"; };
123590B52B5537E200F7AEBD /* ResultBanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResultBanner.swift; sourceTree = "<group>"; }; 123590B52B5537E200F7AEBD /* ResultBanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResultBanner.swift; sourceTree = "<group>"; };
123590B72B5541BA00F7AEBD /* ParticipateButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParticipateButton.swift; sourceTree = "<group>"; }; 123590B72B5541BA00F7AEBD /* ParticipateButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParticipateButton.swift; sourceTree = "<group>"; };
123F31DA2C0F26E8009B6D65 /* userPicture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = userPicture.swift; sourceTree = "<group>"; };
123F31DF2C11E99E009B6D65 /* asyncCachedImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = asyncCachedImage.swift; sourceTree = "<group>"; };
1244EF5F2B4EC31E00374ABF /* HistoricBetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoricBetView.swift; sourceTree = "<group>"; }; 1244EF5F2B4EC31E00374ABF /* HistoricBetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoricBetView.swift; sourceTree = "<group>"; };
1244EF612B4EC67000374ABF /* ReviewCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReviewCard.swift; sourceTree = "<group>"; }; 1244EF612B4EC67000374ABF /* ReviewCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReviewCard.swift; sourceTree = "<group>"; };
129D051C2B6E7FF0003D3E08 /* OddCapsule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OddCapsule.swift; sourceTree = "<group>"; }; 129D051C2B6E7FF0003D3E08 /* OddCapsule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OddCapsule.swift; sourceTree = "<group>"; };
12A9E4932C07132600AB8677 /* EmptyInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyInfo.swift; sourceTree = "<group>"; }; 12A9E4932C07132600AB8677 /* EmptyInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyInfo.swift; sourceTree = "<group>"; };
12C370472B5A5EE500CD9F0F /* BetLineLoading.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BetLineLoading.swift; sourceTree = "<group>"; }; 12C370472B5A5EE500CD9F0F /* BetLineLoading.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BetLineLoading.swift; sourceTree = "<group>"; };
12C370492B5D5BD000CD9F0F /* ParticipationCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParticipationCell.swift; sourceTree = "<group>"; };
EC0193772B25BF16005D81E6 /* AllcoinsCapsule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AllcoinsCapsule.swift; sourceTree = "<group>"; }; EC0193772B25BF16005D81E6 /* AllcoinsCapsule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AllcoinsCapsule.swift; sourceTree = "<group>"; };
EC0193792B25C12B005D81E6 /* BetCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BetCard.swift; sourceTree = "<group>"; }; EC0193792B25C12B005D81E6 /* BetCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BetCard.swift; sourceTree = "<group>"; };
EC01937B2B25C2A8005D81E6 /* AllcoinsCounter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AllcoinsCounter.swift; sourceTree = "<group>"; }; EC01937B2B25C2A8005D81E6 /* AllcoinsCounter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AllcoinsCounter.swift; sourceTree = "<group>"; };
@ -172,6 +178,7 @@
EC30770A2B24D9160060E34D /* WelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeView.swift; sourceTree = "<group>"; }; EC30770A2B24D9160060E34D /* WelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeView.swift; sourceTree = "<group>"; };
EC30770C2B24DB7A0060E34D /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = "<group>"; }; EC30770C2B24DB7A0060E34D /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = "<group>"; };
EC30770E2B24FCB00060E34D /* RegisterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegisterView.swift; sourceTree = "<group>"; }; EC30770E2B24FCB00060E34D /* RegisterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegisterView.swift; sourceTree = "<group>"; };
EC34FDCD2C0DB46C0020A371 /* Config.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Config.swift; sourceTree = "<group>"; };
EC650A412B25C817003AFCAD /* Friend.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Friend.swift; sourceTree = "<group>"; }; EC650A412B25C817003AFCAD /* Friend.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Friend.swift; sourceTree = "<group>"; };
EC650A432B25CDF3003AFCAD /* ParameterMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParameterMenu.swift; sourceTree = "<group>"; }; EC650A432B25CDF3003AFCAD /* ParameterMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParameterMenu.swift; sourceTree = "<group>"; };
EC650A452B25D686003AFCAD /* RankingRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RankingRow.swift; sourceTree = "<group>"; }; EC650A452B25D686003AFCAD /* RankingRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RankingRow.swift; sourceTree = "<group>"; };
@ -196,7 +203,6 @@
EC6B96B62B24B4CC00FC1C58 /* AllInUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AllInUITests.swift; sourceTree = "<group>"; }; EC6B96B62B24B4CC00FC1C58 /* AllInUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AllInUITests.swift; sourceTree = "<group>"; };
EC6B96B82B24B4CC00FC1C58 /* AllInUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AllInUITestsLaunchTests.swift; sourceTree = "<group>"; }; EC6B96B82B24B4CC00FC1C58 /* AllInUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AllInUITestsLaunchTests.swift; sourceTree = "<group>"; };
EC6B96CB2B24B7E500FC1C58 /* IAuthService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IAuthService.swift; sourceTree = "<group>"; }; EC6B96CB2B24B7E500FC1C58 /* IAuthService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IAuthService.swift; sourceTree = "<group>"; };
EC6B96CE2B24B8D900FC1C58 /* Config.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Config.swift; sourceTree = "<group>"; };
EC6B96D02B24BAE800FC1C58 /* AuthService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthService.swift; sourceTree = "<group>"; }; EC6B96D02B24BAE800FC1C58 /* AuthService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthService.swift; sourceTree = "<group>"; };
EC6B96D42B24BE0E00FC1C58 /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = "<group>"; }; EC6B96D42B24BE0E00FC1C58 /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = "<group>"; };
EC6B96D72B24BF2100FC1C58 /* Menu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Menu.swift; sourceTree = "<group>"; }; EC6B96D72B24BF2100FC1C58 /* Menu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Menu.swift; sourceTree = "<group>"; };
@ -274,6 +280,14 @@
/* End PBXFrameworksBuildPhase section */ /* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */ /* Begin PBXGroup section */
123F31DE2C11E979009B6D65 /* Utils */ = {
isa = PBXGroup;
children = (
123F31DF2C11E99E009B6D65 /* asyncCachedImage.swift */,
);
path = Utils;
sourceTree = "<group>";
};
EC1D15402B715A7A0094833E /* Protocols */ = { EC1D15402B715A7A0094833E /* Protocols */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -309,6 +323,7 @@
EC6B969A2B24B4CC00FC1C58 /* AllIn */ = { EC6B969A2B24B4CC00FC1C58 /* AllIn */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
123F31DE2C11E979009B6D65 /* Utils */,
ECF872852B93B9D900F9D240 /* AllIn.entitlements */, ECF872852B93B9D900F9D240 /* AllIn.entitlements */,
EC7EF7462B87E3470022B5D9 /* QuickActions */, EC7EF7462B87E3470022B5D9 /* QuickActions */,
ECB7BC662B2F1AAD002A6654 /* ViewModels */, ECB7BC662B2F1AAD002A6654 /* ViewModels */,
@ -365,9 +380,9 @@
EC6B96CD2B24B8A300FC1C58 /* Ressources */ = { EC6B96CD2B24B8A300FC1C58 /* Ressources */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
EC6B96CE2B24B8D900FC1C58 /* Config.swift */,
EC3077082B24CF7F0060E34D /* Colors.swift */, EC3077082B24CF7F0060E34D /* Colors.swift */,
ECDDD48C2BF4A7920009223A /* Localizable.strings */, ECDDD48C2BF4A7920009223A /* Localizable.strings */,
EC34FDCD2C0DB46C0020A371 /* Config.swift */,
); );
path = Ressources; path = Ressources;
sourceTree = "<group>"; sourceTree = "<group>";
@ -429,10 +444,12 @@
120919172B56D0AE00D0FA29 /* ParticipationModal.swift */, 120919172B56D0AE00D0FA29 /* ParticipationModal.swift */,
120919192B56DC6C00D0FA29 /* DropDownAnswerMenu.swift */, 120919192B56DC6C00D0FA29 /* DropDownAnswerMenu.swift */,
12C370472B5A5EE500CD9F0F /* BetLineLoading.swift */, 12C370472B5A5EE500CD9F0F /* BetLineLoading.swift */,
12C370492B5D5BD000CD9F0F /* ParticipationCell.swift */,
123225DA2B67E41400D30BB3 /* ChoiceFinalAnswerCell.swift */, 123225DA2B67E41400D30BB3 /* ChoiceFinalAnswerCell.swift */,
129D051C2B6E7FF0003D3E08 /* OddCapsule.swift */, 129D051C2B6E7FF0003D3E08 /* OddCapsule.swift */,
12A9E4932C07132600AB8677 /* EmptyInfo.swift */, 12A9E4932C07132600AB8677 /* EmptyInfo.swift */,
123F31DA2C0F26E8009B6D65 /* userPicture.swift */,
12228A202C1760D2008524F4 /* BinaryBetLine.swift */,
12228A222C176117008524F4 /* CustomBetLine.swift */,
); );
path = Components; path = Components;
sourceTree = "<group>"; sourceTree = "<group>";
@ -664,17 +681,20 @@
files = ( files = (
EC6B96CC2B24B7E500FC1C58 /* IAuthService.swift in Sources */, EC6B96CC2B24B7E500FC1C58 /* IAuthService.swift in Sources */,
ECB26A172B4073F100FE06B3 /* CreationBetViewModel.swift in Sources */, ECB26A172B4073F100FE06B3 /* CreationBetViewModel.swift in Sources */,
12228A232C176117008524F4 /* CustomBetLine.swift in Sources */,
EC3077092B24CF7F0060E34D /* Colors.swift in Sources */, EC3077092B24CF7F0060E34D /* Colors.swift in Sources */,
EC6B969E2B24B4CC00FC1C58 /* ContentView.swift in Sources */, EC6B969E2B24B4CC00FC1C58 /* ContentView.swift in Sources */,
EC89F7BD2B250D66003821CE /* LoginView.swift in Sources */, EC89F7BD2B250D66003821CE /* LoginView.swift in Sources */,
EC650A442B25CDF3003AFCAD /* ParameterMenu.swift in Sources */, EC650A442B25CDF3003AFCAD /* ParameterMenu.swift in Sources */,
120919182B56D0AE00D0FA29 /* ParticipationModal.swift in Sources */, 120919182B56D0AE00D0FA29 /* ParticipationModal.swift in Sources */,
123F31DB2C0F26E8009B6D65 /* userPicture.swift in Sources */,
ECB26A132B406A9400FE06B3 /* BetViewModel.swift in Sources */, ECB26A132B406A9400FE06B3 /* BetViewModel.swift in Sources */,
EC30770F2B24FCB00060E34D /* RegisterView.swift in Sources */, EC30770F2B24FCB00060E34D /* RegisterView.swift in Sources */,
EC650A522B2794DD003AFCAD /* BetView.swift in Sources */, EC650A522B2794DD003AFCAD /* BetView.swift in Sources */,
EC6B969C2B24B4CC00FC1C58 /* AllInApp.swift in Sources */, EC6B969C2B24B4CC00FC1C58 /* AllInApp.swift in Sources */,
123590B42B51792000F7AEBD /* DetailsView.swift in Sources */, 123590B42B51792000F7AEBD /* DetailsView.swift in Sources */,
ECB26A192B40744F00FE06B3 /* RankingViewModel.swift in Sources */, ECB26A192B40744F00FE06B3 /* RankingViewModel.swift in Sources */,
123F31E02C11E99E009B6D65 /* asyncCachedImage.swift in Sources */,
EC650A502B2793D5003AFCAD /* TextCapsule.swift in Sources */, EC650A502B2793D5003AFCAD /* TextCapsule.swift in Sources */,
EC650A482B25DCFF003AFCAD /* UsersPreview.swift in Sources */, EC650A482B25DCFF003AFCAD /* UsersPreview.swift in Sources */,
EC17A15E2B6A955E008A8679 /* CurrentBetView.swift in Sources */, EC17A15E2B6A955E008A8679 /* CurrentBetView.swift in Sources */,
@ -692,9 +712,9 @@
EC7A882B2B28D1E0004F226A /* DropDownMenu.swift in Sources */, EC7A882B2B28D1E0004F226A /* DropDownMenu.swift in Sources */,
EC7A882D2B28D8A1004F226A /* CreationBetView.swift in Sources */, EC7A882D2B28D8A1004F226A /* CreationBetView.swift in Sources */,
ECED90B52B6D9CEC00F50937 /* DailyGiftPage.swift in Sources */, ECED90B52B6D9CEC00F50937 /* DailyGiftPage.swift in Sources */,
EC6B96CF2B24B8D900FC1C58 /* Config.swift in Sources */,
1244EF602B4EC31E00374ABF /* HistoricBetView.swift in Sources */, 1244EF602B4EC31E00374ABF /* HistoricBetView.swift in Sources */,
EC30770B2B24D9160060E34D /* WelcomeView.swift in Sources */, EC30770B2B24D9160060E34D /* WelcomeView.swift in Sources */,
EC34FDCE2C0DB46C0020A371 /* Config.swift in Sources */,
EC30770D2B24DB7A0060E34D /* Extensions.swift in Sources */, EC30770D2B24DB7A0060E34D /* Extensions.swift in Sources */,
EC6B96D82B24BF2100FC1C58 /* Menu.swift in Sources */, EC6B96D82B24BF2100FC1C58 /* Menu.swift in Sources */,
EC650A4E2B278EDB003AFCAD /* TrendingBetCard.swift in Sources */, EC650A4E2B278EDB003AFCAD /* TrendingBetCard.swift in Sources */,
@ -707,8 +727,8 @@
EC3077072B24CB840060E34D /* SplashView.swift in Sources */, EC3077072B24CB840060E34D /* SplashView.swift in Sources */,
EC01937E2B25C52E005D81E6 /* TopBar.swift in Sources */, EC01937E2B25C52E005D81E6 /* TopBar.swift in Sources */,
ECA9D1CB2B2DA2320076E0EC /* DropDownFriends.swift in Sources */, ECA9D1CB2B2DA2320076E0EC /* DropDownFriends.swift in Sources */,
12C3704A2B5D5BD000CD9F0F /* ParticipationCell.swift in Sources */,
123590B82B5541BA00F7AEBD /* ParticipateButton.swift in Sources */, 123590B82B5541BA00F7AEBD /* ParticipateButton.swift in Sources */,
12228A212C1760D2008524F4 /* BinaryBetLine.swift in Sources */,
EC01FCC32B56650400BB2390 /* DetailsViewModel.swift in Sources */, EC01FCC32B56650400BB2390 /* DetailsViewModel.swift in Sources */,
ECB26A1B2B40746C00FE06B3 /* FriendsViewModel.swift in Sources */, ECB26A1B2B40746C00FE06B3 /* FriendsViewModel.swift in Sources */,
ECB7BC682B2F1ADF002A6654 /* LoginViewModel.swift in Sources */, ECB7BC682B2F1ADF002A6654 /* LoginViewModel.swift in Sources */,

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

@ -3,8 +3,6 @@
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>com.apple.security.application-groups</key> <key>com.apple.security.application-groups</key>
<array> <array/>
<string>group.alldev.AllIn</string>
</array>
</dict> </dict>
</plist> </plist>

@ -11,13 +11,15 @@ import Model
public struct BetApiManager: BetDataManager { public struct BetApiManager: BetDataManager {
public let token: String public let token: String
public let url: String
public init(withUserToken token: String) { public init(withUserToken token: String, andApiUrl url: String) {
self.token = token self.token = token
self.url = url
} }
public func getBets(withIndex index: Int, withCount count: Int, filters: [BetFilter] = [], completion: @escaping ([Bet]) -> Void) { public func getBets(withIndex index: Int, withCount count: Int, filters: [BetFilter] = [], completion: @escaping ([Bet]) -> Void) {
let url = URL(string: allInApi + "bets/gets")! let url = URL(string: url + "bets/gets")!
var request = URLRequest(url: url) var request = URLRequest(url: url)
request.httpMethod = "POST" request.httpMethod = "POST"
@ -26,6 +28,7 @@ public struct BetApiManager: BetDataManager {
let filterStrings = filters.map { $0.rawValue } let filterStrings = filters.map { $0.rawValue }
let jsonDictionary: [String: Any] = ["filters": filterStrings] let jsonDictionary: [String: Any] = ["filters": filterStrings]
do { do {
let jsonData = try JSONSerialization.data(withJSONObject: jsonDictionary, options: []) let jsonData = try JSONSerialization.data(withJSONObject: jsonDictionary, options: [])
request.httpBody = jsonData request.httpBody = jsonData
@ -61,7 +64,7 @@ public struct BetApiManager: BetDataManager {
} }
public func getBet(withId id: String, completion: @escaping (BetDetail) -> Void) { public func getBet(withId id: String, completion: @escaping (BetDetail) -> Void) {
let url = URL(string: allInApi + "betdetail/get/" + id)! let url = URL(string: url + "betdetail/get/" + id)!
var request = URLRequest(url: url) var request = URLRequest(url: url)
request.httpMethod = "GET" request.httpMethod = "GET"
@ -73,10 +76,36 @@ public struct BetApiManager: BetDataManager {
print ("ALLIN : get bet with id :" + id) print ("ALLIN : get bet with id :" + id)
do { do {
if let httpResponse = response as? HTTPURLResponse, let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] { if let httpResponse = response as? HTTPURLResponse, let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
print(httpResponse.statusCode)
print(json)
if let betDetail = FactoryApiBet().toBetDetail(from: json) { if let betDetail = FactoryApiBet().toBetDetail(from: json) {
completion(betDetail) completion(betDetail)
} }
}
} catch {
print("Error parsing JSON: \(error)")
}
}
}.resume()
}
public func getPopularBet(completion: @escaping (Bet) -> Void) {
let url = URL(string: url + "bets/popular")!
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
URLSession.shared.dataTask(with: request) { data, response, error in
if let data = data {
print ("ALLIN : get popular bet")
do {
if let httpResponse = response as? HTTPURLResponse, let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
if let bet = FactoryApiBet().toBet(from: json) {
completion(bet)
}
print(httpResponse.statusCode) print(httpResponse.statusCode)
} }
} catch { } catch {

@ -23,11 +23,14 @@ public class FactoryApiBet: FactoryBet {
"sentenceBet": bet.phrase, "sentenceBet": bet.phrase,
"endRegistration": formatZonedDateTime(dateTime: bet.endRegisterDate), "endRegistration": formatZonedDateTime(dateTime: bet.endRegisterDate),
"endBet": formatZonedDateTime(dateTime: bet.endBetDate), "endBet": formatZonedDateTime(dateTime: bet.endBetDate),
"isPrivate": String(bet.isPublic), "isPrivate": String(bet.isPrivate),
"response": ["Yes","No"], "response": bet.getResponses(),
"userInvited": bet.invited,
"type": betTypeString(fromType: String(describing: type(of: bet))) "type": betTypeString(fromType: String(describing: type(of: bet)))
] ]
print(json)
return json return json
} }
@ -60,6 +63,8 @@ public class FactoryApiBet: FactoryBet {
} }
var participations: [Participation] = [] var participations: [Participation] = []
var wonParticipation: Participation?
var userParticipation: Participation?
if let participationsJson = json["participations"] as? [[String: Any]] { if let participationsJson = json["participations"] as? [[String: Any]] {
do { do {
@ -79,7 +84,53 @@ public class FactoryApiBet: FactoryBet {
} }
} }
return BetDetail(bet: bet, answers: answers, participations: participations) if let participationJson = json["wonParticipation"] as? [String: Any] {
do {
wonParticipation = try JSONDecoder().decode(Participation.self, from: JSONSerialization.data(withJSONObject: participationJson))
} catch {
print("Error decoding participation: \(error)")
}
}
if let participationUserJson = json["userParticipation"] as? [String: Any] {
do {
userParticipation = try JSONDecoder().decode(Participation.self, from: JSONSerialization.data(withJSONObject: participationUserJson))
} catch {
print("Error decoding participation: \(error)")
}
}
return BetDetail(bet: bet, answers: answers, participations: participations, wonParticipation: wonParticipation, userParticipation: userParticipation)
}
public func toBetResultDetail(from json: [String: Any]) -> BetResultDetail? {
guard let amount = json["amount"] as? Int,
let won = json["won"] as? Bool else {
return nil
}
guard let betJson = json["bet"] as? [String: Any],
let bet = self.toBet(from: betJson) else {
return nil
}
let decoder = JSONDecoder()
guard let betResultJson = json["betResult"] as? [String: Any],
let betResultData = try? JSONSerialization.data(withJSONObject: betResultJson),
let betResult = try? decoder.decode(BetResult.self, from: betResultData) else {
print("Error decoding bet result")
return nil
}
guard let participationJson = json["participation"] as? [String: Any],
let participationData = try? JSONSerialization.data(withJSONObject: participationJson),
let participation = try? decoder.decode(Participation.self, from: participationData) else {
print("Error decoding participation")
return nil
}
return BetResultDetail(betResult: betResult, bet: bet, participation: participation, amount: amount, won: won)
} }
public func betTypeString(fromType type: String) -> String { public func betTypeString(fromType type: String) -> String {

@ -8,14 +8,14 @@
import Foundation import Foundation
import Model import Model
let allInApi = "https://codefirst.iut.uca.fr/containers/AllDev-api/"
public struct UserApiManager: UserDataManager { public struct UserApiManager: UserDataManager {
public let token: String public let token: String
public let url: String
public init(withUserToken token: String) { public init(withUserToken token: String, andApiUrl url: String) {
self.token = token self.token = token
self.url = url
} }
public func getBets(withIndex index: Int, withCount count: Int) -> [Bet] { public func getBets(withIndex index: Int, withCount count: Int) -> [Bet] {
@ -23,7 +23,7 @@ public struct UserApiManager: UserDataManager {
} }
public func getBetsOver(completion : @escaping ([BetDetail])-> ()) { public func getBetsOver(completion : @escaping ([BetDetail])-> ()) {
let url = URL(string: allInApi + "bets/toConfirm")! let url = URL(string: url + "bets/toConfirm")!
var request = URLRequest(url: url) var request = URLRequest(url: url)
request.httpMethod = "GET" request.httpMethod = "GET"
@ -52,9 +52,41 @@ public struct UserApiManager: UserDataManager {
}.resume() }.resume()
} }
public func getBetsWon(completion : @escaping ([BetResultDetail])-> ()) {
let url = URL(string: url + "bets/getWon")!
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
var bets: [BetResultDetail] = []
URLSession.shared.dataTask(with: request) { data, response, error in
if let data = data {
print ("ALLIN : get bets won")
do {
if let httpResponse = response as? HTTPURLResponse, let jsonArray = try JSONSerialization.jsonObject(with: data, options: []) as? [[String: Any]] {
for json in jsonArray {
if let bet = FactoryApiBet().toBetResultDetail(from: json) {
print(bet)
bets.append(bet)
}
}
print(httpResponse.statusCode)
completion(bets)
}
} catch {
print("Error parsing JSON: \(error)")
}
}
}.resume()
}
public func addBet(bet: Bet, completion : @escaping (Int)-> ()) { public func addBet(bet: Bet, completion : @escaping (Int)-> ()) {
let url = URL(string: allInApi + "bets/add")! let url = URL(string: url + "bets/add")!
var request = URLRequest(url: url) var request = URLRequest(url: url)
request.httpMethod = "POST" request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type") request.setValue("application/json", forHTTPHeaderField: "Content-Type")
@ -75,7 +107,7 @@ public struct UserApiManager: UserDataManager {
public func addFriend(username: String, completion : @escaping (Int)-> ()) { public func addFriend(username: String, completion : @escaping (Int)-> ()) {
let url = URL(string: allInApi + "friends/add")! let url = URL(string: url + "friends/add")!
var request = URLRequest(url: url) var request = URLRequest(url: url)
request.httpMethod = "POST" request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type") request.setValue("application/json", forHTTPHeaderField: "Content-Type")
@ -96,7 +128,7 @@ public struct UserApiManager: UserDataManager {
public func removeFriend(username: String, completion : @escaping (Int)-> ()) { public func removeFriend(username: String, completion : @escaping (Int)-> ()) {
let url = URL(string: allInApi + "friends/delete")! let url = URL(string: url + "friends/delete")!
var request = URLRequest(url: url) var request = URLRequest(url: url)
request.httpMethod = "POST" request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type") request.setValue("application/json", forHTTPHeaderField: "Content-Type")
@ -116,7 +148,33 @@ public struct UserApiManager: UserDataManager {
} }
public func getFriends(completion: @escaping ([User]) -> Void) { public func getFriends(completion: @escaping ([User]) -> Void) {
let url = URL(string: allInApi + "friends/gets")! let url = URL(string: url + "friends/gets")!
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
var users: [User] = []
URLSession.shared.dataTask(with: request) { data, response, error in
if let data = data {
print ("ALLIN : get friends")
do {
if let httpResponse = response as? HTTPURLResponse, let jsonArray = try JSONSerialization.jsonObject(with: data, options: []) as? [[String: Any]] {
users = try JSONDecoder().decode([User].self, from: JSONSerialization.data(withJSONObject: jsonArray))
print(httpResponse.statusCode)
completion(users)
}
} catch {
print("Error parsing JSON: \(error)")
}
}
}.resume()
}
public func getRequests(completion: @escaping ([User]) -> Void) {
let url = URL(string: url + "friends/requests")!
var request = URLRequest(url: url) var request = URLRequest(url: url)
request.httpMethod = "GET" request.httpMethod = "GET"
@ -130,7 +188,6 @@ public struct UserApiManager: UserDataManager {
print ("ALLIN : get friends") print ("ALLIN : get friends")
do { do {
if let httpResponse = response as? HTTPURLResponse, let jsonArray = try JSONSerialization.jsonObject(with: data, options: []) as? [[String: Any]] { if let httpResponse = response as? HTTPURLResponse, let jsonArray = try JSONSerialization.jsonObject(with: data, options: []) as? [[String: Any]] {
print(jsonArray)
users = try JSONDecoder().decode([User].self, from: JSONSerialization.data(withJSONObject: jsonArray)) users = try JSONDecoder().decode([User].self, from: JSONSerialization.data(withJSONObject: jsonArray))
print(httpResponse.statusCode) print(httpResponse.statusCode)
completion(users) completion(users)
@ -143,7 +200,7 @@ public struct UserApiManager: UserDataManager {
} }
public func getUsers(withName name: String, completion: @escaping ([User]) -> Void) { public func getUsers(withName name: String, completion: @escaping ([User]) -> Void) {
let url = URL(string: allInApi + "friends/search/" + name)! let url = URL(string: url + "friends/search/" + name)!
var request = URLRequest(url: url) var request = URLRequest(url: url)
request.httpMethod = "GET" request.httpMethod = "GET"
@ -157,7 +214,6 @@ public struct UserApiManager: UserDataManager {
print ("ALLIN : get friends by search") print ("ALLIN : get friends by search")
do { do {
if let httpResponse = response as? HTTPURLResponse, let jsonArray = try JSONSerialization.jsonObject(with: data, options: []) as? [[String: Any]] { if let httpResponse = response as? HTTPURLResponse, let jsonArray = try JSONSerialization.jsonObject(with: data, options: []) as? [[String: Any]] {
print(jsonArray)
users = try JSONDecoder().decode([User].self, from: JSONSerialization.data(withJSONObject: jsonArray)) users = try JSONDecoder().decode([User].self, from: JSONSerialization.data(withJSONObject: jsonArray))
print(httpResponse.statusCode) print(httpResponse.statusCode)
completion(users) completion(users)
@ -170,7 +226,7 @@ public struct UserApiManager: UserDataManager {
} }
public func getGifts(completion : @escaping (Int, Int)-> ()) { public func getGifts(completion : @escaping (Int, Int)-> ()) {
let url = URL(string: allInApi + "users/gift")! let url = URL(string: url + "users/gift")!
var request = URLRequest(url: url) var request = URLRequest(url: url)
request.httpMethod = "GET" request.httpMethod = "GET"
@ -194,12 +250,39 @@ public struct UserApiManager: UserDataManager {
}.resume() }.resume()
} }
public func getOldBets(withIndex index: Int, withCount count: Int, completion: @escaping ([Bet]) -> Void) { public func getOldBets(withIndex index: Int, withCount count: Int, completion: @escaping ([BetResultDetail]) -> Void) {
fatalError("Not implemented yet") let url = URL(string: url + "bets/history")!
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 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) {
bets.append(bet)
}
}
print(httpResponse.statusCode)
completion(bets)
}
} catch {
print("Error parsing JSON: \(error)")
}
}
}.resume()
} }
public func getCurrentBets(withIndex index: Int, withCount count: Int, completion: @escaping ([BetDetail]) -> Void) { public func getCurrentBets(withIndex index: Int, withCount count: Int, completion: @escaping ([BetDetail]) -> Void) {
let url = URL(string: allInApi + "bets/current")! let url = URL(string: url + "bets/current")!
var request = URLRequest(url: url) var request = URLRequest(url: url)
request.httpMethod = "GET" request.httpMethod = "GET"
@ -214,7 +297,6 @@ public struct UserApiManager: UserDataManager {
do { do {
if let httpResponse = response as? HTTPURLResponse, let jsonArray = try JSONSerialization.jsonObject(with: data, options: []) as? [[String: Any]] { if let httpResponse = response as? HTTPURLResponse, let jsonArray = try JSONSerialization.jsonObject(with: data, options: []) as? [[String: Any]] {
for json in jsonArray { for json in jsonArray {
print(json)
if let bet = FactoryApiBet().toBetDetail(from: json) { if let bet = FactoryApiBet().toBetDetail(from: json) {
bets.append(bet) bets.append(bet)
} }
@ -230,7 +312,7 @@ public struct UserApiManager: UserDataManager {
} }
public func addParticipation(withId id: String, withAnswer answer: String, andStake stake: Int, completion : @escaping (Int)-> ()) { public func addParticipation(withId id: String, withAnswer answer: String, andStake stake: Int, completion : @escaping (Int)-> ()) {
let url = URL(string: allInApi + "participations/add")! let url = URL(string: url + "participations/add")!
var request = URLRequest(url: url) var request = URLRequest(url: url)
request.httpMethod = "POST" request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type") request.setValue("application/json", forHTTPHeaderField: "Content-Type")
@ -254,24 +336,18 @@ public struct UserApiManager: UserDataManager {
} }
public func addResponse(withIdBet id: String, andResponse responseBet: String) { public func addResponse(withIdBet id: String, andResponse responseBet: String) {
let url = URL(string: allInApi + "bets/confirm/" + id)! let url = URL(string: url + "bets/confirm/" + id)!
var request = URLRequest(url: url) var request = URLRequest(url: url)
request.httpMethod = "POST" request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type") request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization") request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
let json: [String: String] = [ URLSession.shared.uploadTask(with: request, from: responseBet.data(using: .utf8)) { data, response, error in
"result": responseBet,
]
if let jsonData = try? JSONSerialization.data(withJSONObject: json, options: []){
URLSession.shared.uploadTask(with: request, from: jsonData) { data, response, error in
print ("ALLIN : add response " + responseBet + " for the bet Id " + id) print ("ALLIN : add response " + responseBet + " for the bet Id " + id)
if let httpResponse = response as? HTTPURLResponse { if let httpResponse = response as? HTTPURLResponse {
print(httpResponse.statusCode) print(httpResponse.statusCode)
} }
}.resume() }.resume()
} }
}
} }

@ -339,9 +339,11 @@
ECEE18C82B3C9CF400C95E8A /* Debug */ = { ECEE18C82B3C9CF400C95E8A /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = P39ZK4GA2T;
DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1; DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath"; DYLIB_INSTALL_NAME_BASE = "@rpath";
@ -376,9 +378,11 @@
ECEE18C92B3C9CF400C95E8A /* Release */ = { ECEE18C92B3C9CF400C95E8A /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = P39ZK4GA2T;
DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1; DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath"; DYLIB_INSTALL_NAME_BASE = "@rpath";
@ -416,6 +420,7 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 35KQ5BDC64;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 16.4; IPHONEOS_DEPLOYMENT_TARGET = 16.4;
MACOSX_DEPLOYMENT_TARGET = 13.3; MACOSX_DEPLOYMENT_TARGET = 13.3;
@ -436,6 +441,7 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 35KQ5BDC64;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 16.4; IPHONEOS_DEPLOYMENT_TARGET = 16.4;
MACOSX_DEPLOYMENT_TARGET = 13.3; MACOSX_DEPLOYMENT_TARGET = 13.3;

@ -8,7 +8,7 @@
import Foundation import Foundation
/// A class representing detailed information about a specific answer option for a bet. /// A class representing detailed information about a specific answer option for a bet.
public class AnswerDetail: ObservableObject, Identifiable, Codable { public class AnswerDetail: ObservableObject, Codable, Equatable, Identifiable, Hashable {
/// The response or outcome associated with this answer. /// The response or outcome associated with this answer.
public private(set) var response: String public private(set) var response: String
@ -41,4 +41,19 @@ public class AnswerDetail: ObservableObject, Identifiable, Codable {
self.odds = odds self.odds = odds
} }
public static func == (lhs: AnswerDetail, rhs: AnswerDetail) -> Bool {
return lhs.response == rhs.response &&
lhs.totalStakes == rhs.totalStakes &&
lhs.totalParticipants == rhs.totalParticipants &&
lhs.highestStake == rhs.highestStake &&
lhs.odds == rhs.odds
}
public func hash(into hasher: inout Hasher) {
hasher.combine(response)
hasher.combine(totalStakes)
hasher.combine(totalParticipants)
hasher.combine(highestStake)
hasher.combine(odds)
}
} }

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

@ -11,4 +11,6 @@ public protocol BetDataManager {
func getBets(withIndex index: Int, withCount count: Int, filters: [BetFilter], completion: @escaping ([Bet]) -> Void) func getBets(withIndex index: Int, withCount count: Int, filters: [BetFilter], completion: @escaping ([Bet]) -> Void)
func getUsers(username: String) -> [User] func getUsers(username: String) -> [User]
func getBet(withId id: String, completion: @escaping (BetDetail) -> Void) func getBet(withId id: String, completion: @escaping (BetDetail) -> Void)
func getPopularBet(completion: @escaping (Bet) -> Void)
} }

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

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

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

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

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

@ -46,6 +46,12 @@ public struct Manager {
} }
} }
public func getBetsWon(completion: @escaping ([BetResultDetail]) -> Void) {
userDataManager.getBetsWon() { bets in
completion(bets)
}
}
public func getBet(withId id: String, completion: @escaping (BetDetail) -> Void) { public func getBet(withId id: String, completion: @escaping (BetDetail) -> Void) {
betDataManager.getBet(withId: id) { bet in betDataManager.getBet(withId: id) { bet in
completion(bet) completion(bet)
@ -58,18 +64,30 @@ public struct Manager {
} }
} }
public func getRequests(completion: @escaping ([User]) -> Void) {
userDataManager.getRequests() { users in
completion(users)
}
}
public func getUsers(withName name: String, completion: @escaping ([User]) -> Void) { public func getUsers(withName name: String, completion: @escaping ([User]) -> Void) {
userDataManager.getUsers(withName: name) { users in userDataManager.getUsers(withName: name) { users in
completion(users) completion(users)
} }
} }
public func getHistoricBets(withIndex index: Int, withCount count: Int, completion: @escaping ([Bet]) -> Void) { public func getHistoricBets(withIndex index: Int, withCount count: Int, completion: @escaping ([BetResultDetail]) -> Void) {
userDataManager.getOldBets(withIndex: index, withCount: count) { bets in userDataManager.getOldBets(withIndex: index, withCount: count) { bets in
completion(bets) completion(bets)
} }
} }
public func getPopularBet(completion: @escaping (Bet) -> Void) {
betDataManager.getPopularBet() { bet in
completion(bet)
}
}
public func getCurrentBets(withIndex index: Int, withCount count: Int, completion: @escaping ([BetDetail]) -> Void) { public func getCurrentBets(withIndex index: Int, withCount count: Int, completion: @escaping ([BetDetail]) -> Void) {
userDataManager.getCurrentBets(withIndex: index, withCount: count) { bets in userDataManager.getCurrentBets(withIndex: index, withCount: count) { bets in
completion(bets) completion(bets)

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

@ -8,9 +8,7 @@
import Foundation import Foundation
/// A class representing a user's participation in a bet. /// A class representing a user's participation in a bet.
public class Participation: ObservableObject, Codable, Identifiable { public class Participation: ObservableObject, Codable, Identifiable, Hashable {
public let id: String
/// The amount of stake in the bet. /// The amount of stake in the bet.
public private(set) var stake: Int public private(set) var stake: Int
@ -28,15 +26,29 @@ public class Participation: ObservableObject, Codable, Identifiable {
/// ///
/// - Parameters: /// - Parameters:
/// - stake: The amount of stake in the bet. /// - stake: The amount of stake in the bet.
/// - date: The date and time when the participation occurred. /// - answer: The response or outcome of the participation.
/// - response: The response or outcome of the participation. /// - username: The user who participated in the bet.
/// - user: The user who participated in the bet.
/// - betId: The unique identifier of the bet. /// - betId: The unique identifier of the bet.
public init(id: String, stake: Int, answer: String, username: String, betId: String) { public init(stake: Int, answer: String, username: String, betId: String) {
self.id = id
self.stake = stake self.stake = stake
self.answer = answer self.answer = answer
self.username = username self.username = username
self.betId = betId self.betId = betId
} }
// Conformance to Equatable
public static func == (lhs: Participation, rhs: Participation) -> Bool {
return lhs.stake == rhs.stake &&
lhs.answer == rhs.answer &&
lhs.username == rhs.username &&
lhs.betId == rhs.betId
}
// Conformance to Hashable
public func hash(into hasher: inout Hasher) {
hasher.combine(stake)
hasher.combine(answer)
hasher.combine(username)
hasher.combine(betId)
}
} }

@ -8,7 +8,9 @@
import Foundation import Foundation
/// A struct representing a user with details such as username, email, number of coins, and friends. /// A struct representing a user with details such as username, email, number of coins, and friends.
public struct User: Codable, Hashable { public struct User: Codable, Hashable, Identifiable {
public var id: String
/// The username of the user. /// The username of the user.
public private(set) var username: String public private(set) var username: String
@ -22,17 +24,17 @@ public struct User: Codable, Hashable {
/// The friendship status with the main user. /// The friendship status with the main user.
public var friendStatus: FriendStatus? public var friendStatus: FriendStatus?
/// Custom constructor to initialize a User instance. /// The user's profile image.
/// public var image: String?
/// - Parameters:
/// - username: The username of the user. /// The number of bets made by the user.
/// - email: The email address of the user. public var nbBets: Int
/// - nbCoins: The number of coins associated with the user.
public init(username: String, email: String, nbCoins: Int) { /// The number of friends the user has.
self.username = username public var nbFriends: Int
self.email = email
self.nbCoins = nbCoins /// The user's best win.
} public var bestWin: Int
/// Custom constructor to initialize a User instance. /// Custom constructor to initialize a User instance.
/// ///
@ -40,11 +42,20 @@ public struct User: Codable, Hashable {
/// - username: The username of the user. /// - username: The username of the user.
/// - email: The email address of the user. /// - email: The email address of the user.
/// - nbCoins: The number of coins associated with the user. /// - nbCoins: The number of coins associated with the user.
/// - status: The friendship status with the main user. /// - friendStatus: The friendship status with the main user.
public init(username: String, email: String, nbCoins: Int, status: FriendStatus) { /// - image: The user's profile image.
/// - nbBets: The number of bets made by the user.
/// - 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.username = username
self.email = email self.email = email
self.nbCoins = nbCoins self.nbCoins = nbCoins
self.friendStatus = status self.friendStatus = friendStatus
self.image = image
self.nbBets = nbBets
self.nbFriends = nbFriends
self.bestWin = bestWin
} }
} }

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

@ -29,6 +29,10 @@ public struct BetStubManager: BetDataManager {
} }
} }
public func getPopularBet(completion: @escaping (Bet) -> Void) {
}
public func getABetDetail() -> BetDetail{ public func getABetDetail() -> BetDetail{
Stub.shared.betsDetail.first! Stub.shared.betsDetail.first!
} }

@ -21,13 +21,13 @@ struct Stub {
public mutating func loadBets() { public mutating func loadBets() {
let user1 = User(username: "Lucas", email: "lucas.delanier@etu.uca.fr", nbCoins: 100) let user1 = User(username: "Lucas", email: "lucas.delanier@etu.uca.fr", nbCoins: 100, nbBets: 0, nbFriends: 0, bestWin: 0)
users.append(user1) users.append(user1)
let user2 = User(username: "Imri", email: "emre.kartal@etu.uca.fr", nbCoins: 75) let user2 = User(username: "Imri", email: "emre.kartal@etu.uca.fr", nbCoins: 75, nbBets: 0, nbFriends: 0, bestWin: 0)
users.append(user2) users.append(user2)
let user3 = User(username: "Arthur", email: "arthur.valin@etu.uca.fr", nbCoins: 30) let user3 = User(username: "Arthur", email: "arthur.valin@etu.uca.fr", nbCoins: 30, nbBets: 0, nbFriends: 0, bestWin: 0)
users.append(user3) users.append(user3)
let bet1 = BinaryBet( let bet1 = BinaryBet(
@ -35,11 +35,10 @@ struct Stub {
phrase: "Le gagnant de la finale sera l'équipe avec le plus de tirs au but.", phrase: "Le gagnant de la finale sera l'équipe avec le plus de tirs au but.",
endRegisterDate: Date().addingTimeInterval(-86400), endRegisterDate: Date().addingTimeInterval(-86400),
endBetDate: Date().addingTimeInterval(172800), endBetDate: Date().addingTimeInterval(172800),
isPublic: true, isPrivate: false,
status: .inProgress, status: .inProgress,
invited: [], invited: [],
author: "Lucas", author: "Lucas"
registered: [user2]
) )
self.bets.append(bet1) self.bets.append(bet1)
@ -48,11 +47,10 @@ struct Stub {
phrase: "Le plat préféré du jury sera une recette végétarienne.", phrase: "Le plat préféré du jury sera une recette végétarienne.",
endRegisterDate: Date().addingTimeInterval(172800), endRegisterDate: Date().addingTimeInterval(172800),
endBetDate: Date().addingTimeInterval(259200), endBetDate: Date().addingTimeInterval(259200),
isPublic: false, isPrivate: false,
status: .inProgress, status: .inProgress,
invited: [user3], invited: [],
author: "Lucas", author: "Lucas"
registered: [user2]
) )
self.bets.append(bet2) self.bets.append(bet2)
@ -61,11 +59,10 @@ struct Stub {
phrase: "Le nombre total de précommandes dépassera-t-il 1 million dans la première semaine ?", phrase: "Le nombre total de précommandes dépassera-t-il 1 million dans la première semaine ?",
endRegisterDate: Date().addingTimeInterval(259200), endRegisterDate: Date().addingTimeInterval(259200),
endBetDate: Date().addingTimeInterval(345600), endBetDate: Date().addingTimeInterval(345600),
isPublic: true, isPrivate: true,
status: .finished, status: .finished,
invited: [], invited: [],
author: "Lucas", author: "Lucas"
registered: [user2, user1, user3]
) )
self.bets.append(bet3) self.bets.append(bet3)
@ -74,11 +71,10 @@ struct Stub {
phrase: "Le film favori des critiques remportera-t-il le prix du meilleur film ?", phrase: "Le film favori des critiques remportera-t-il le prix du meilleur film ?",
endRegisterDate: Date().addingTimeInterval(345600), endRegisterDate: Date().addingTimeInterval(345600),
endBetDate: Date().addingTimeInterval(432000), endBetDate: Date().addingTimeInterval(432000),
isPublic: false, isPrivate: false,
status: .finished, status: .finished,
invited: [user1], invited: [],
author: "Lucase", author: "Lucase"
registered: [user3]
) )
self.bets.append(bet4) self.bets.append(bet4)
@ -87,15 +83,15 @@ struct Stub {
self.betsDetail.append(betDetail) self.betsDetail.append(betDetail)
} }
self.betsDetail[0].addParticipation(newParticipation: Participation(id: UUID().uuidString, stake: 120, answer: "OUI", username: user1.username, betId: "1")) self.betsDetail[0].addParticipation(newParticipation: Participation(stake: 120, answer: "OUI", username: user1.username, betId: "1"))
self.betsDetail[0].addParticipation(newParticipation: Participation(id: UUID().uuidString, stake: 20, answer: "NON", username: user2.username, betId: "2")) self.betsDetail[0].addParticipation(newParticipation: Participation(stake: 20, answer: "NON", username: user2.username, betId: "2"))
self.betsDetail[0].addParticipation(newParticipation: Participation(id: UUID().uuidString, stake: 320, answer: "OUI", username: user3.username, betId: "3")) self.betsDetail[0].addParticipation(newParticipation: Participation(stake: 320, answer: "OUI", username: user3.username, betId: "3"))
self.betsDetail[0].addParticipation(newParticipation: Participation(id: UUID().uuidString, stake: 320, answer: "OUI", username: user3.username, betId: "3")) self.betsDetail[0].addParticipation(newParticipation: Participation(stake: 320, answer: "OUI", username: user3.username, betId: "3"))
} }
public mutating func add(bet: Bet) { public mutating func add(bet: Bet) {
let newBetDetail = BetDetail(bet: bet, answers: [], participations: [], finalAnswer: "test") let newBetDetail = BetDetail(bet: bet, answers: [], participations: [])
self.betsDetail.append(newBetDetail) self.betsDetail.append(newBetDetail)
} }
} }

Loading…
Cancel
Save