Compare commits

...

44 Commits

Author SHA1 Message Date
Emre KARTAL c640fc0acb fix icon
5 months ago
Emre KARTAL ee3f0f0c9b fix icon
5 months ago
Emre KARTAL 1b8db51116 Merge pull request 'fix/multiples-fix' (#40) from fix/multiples-fix into master
5 months ago
Emre KARTAL a4f149b81c fix little errors 🐛
5 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 🐛
5 months ago
Lucas DELANIER d5e50e4168 fix multiple issues with floats, responsive etc
5 months ago
Emre KARTAL b97254d000 Merge pull request 'feature/bet-custom' (#39) from feature/bet-custom into master
5 months ago
Lucas DELANIER f668388088 fix error when loading
5 months ago
Lucas DELANIER 77e6790f59 fix order
5 months ago
Lucas DELANIER bb2a2baac3 make the review for the custom bets
5 months ago
Emre KARTAL b7efad2eed get custom answers and add the creation of bet custom
5 months ago
Lucas DELANIER f4e3edbf4a fix winning and loosing status (#38)
5 months ago
Emre KARTAL b878759b3d Merge pull request 'feature/winnings' (#37) from feature/winnings into master
6 months ago
Lucas DELANIER 65940cb3bc delete submodule
6 months ago
Lucas DELANIER e116fc9dca fix some bugs with current and finished list
6 months ago
Emre KARTAL b542ff1bf2 Bind winModal with betResult
6 months ago
Lucas DELANIER ef1f130ec5 bind images and fix ranking row (#36)
6 months ago
Emre KARTAL 68345572ef Get bets won
6 months ago
Lucas DELANIER 5d1ea8492a feature/in-progress-card (#35)
6 months ago
Emre KARTAL d82c8e7d79 Merge pull request 'Add private friends invitation' (#34) from feature/user-invitation into master
6 months ago
Emre KARTAL ab4aa3313a Add private friends invitation
6 months ago
Lucas DELANIER 32480e0cb1 fix mistake ispublic (#33)
6 months ago
Lucas DELANIER 5ce52cff75 limit the minimal date (#32)
6 months ago
Emre KARTAL 0c30fa1571 Mise à jour de 'README.md'
6 months ago
Emre KARTAL 0a9d0c0508 Transférer les fichiers vers 'Documentation/Images'
6 months ago
Lucas DELANIER b27cd37a39 feature/user-picture (#31)
6 months ago
Emre KARTAL 71e5c12992 Merge pull request 'Fix errors' (#30) from fix/participation into master
6 months ago
Emre KARTAL eed6fcca99 Fix errors
6 months ago
Arthur VALIN 7cbd2c1336 Mise à jour de 'README.md'
6 months ago
Lucas DELANIER e1c8d85dd1 i18n and empty view (#29)
6 months ago
avalin 28b0f19619 Fix bet confirmation
6 months ago
Emre KARTAL 030e3fb2f0 Merge pull request 'fix/api-calls' (#28) from fix/api-calls into master
6 months ago
Emre KARTAL b700938539 Add popular bet
6 months ago
Emre KARTAL 68e8c4d41c Get old bets
6 months ago
Lucas DELANIER 1146eeeaa8 add friend request (#27)
6 months ago
Lucas DELANIER e32d032d3c empty informations (#26)
6 months ago
Emre KARTAL c9fb2ca2ed Fix filtering
6 months ago
Emre KARTAL d3dc2536eb Merge pull request 'fix/api-calls' (#25) from fix/api-calls into master
6 months ago
Emre KARTAL e7d8908970 Adding friends and displaying the ranking
6 months ago
Lucas DELANIER 431c95e84c nice
6 months ago
Emre KARTAL 80b5f9ba51 Fix get bets
6 months ago
Emre KARTAL 155e893561 Merge pull request 'Add profile page' (#24) from feature/profile_page into master
6 months ago
Emre KARTAL 7d73e8e23a Add profile page
6 months ago
Emre KARTAL ed084dbc1b Merge pull request 'feature/localize' (#23) from feature/localize into master
6 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*.
</br>
# Répartition du GitLab
# Répartition du dépot
[**Sources**](Sources) : **Code de l'application**
@ -32,7 +32,43 @@
- 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

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

@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x3C",
"green" : "0x3C",
"red" : "0x3C"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xFF",
"green" : "0xFF",
"red" : "0xFF"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

@ -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
struct AllcoinsCapsule: View {
var gains: Int
var body: some View {
Text("Vous remportez")
.foregroundColor(.white)
HStack{
Text("2340")
Text(gains.description)
.textStyle(weight: .bold, color: .white, size: 60)
Image("allcoinWhiteIcon")
.resizable()
@ -30,6 +31,6 @@ struct AllcoinsCapsule: View {
struct AllcoinsCapsule_Previews: PreviewProvider {
static var previews: some View {
AllcoinsCapsule()
AllcoinsCapsule(gains: 100)
}
}

@ -15,13 +15,13 @@ struct AllcoinsCounter: View {
var body: some View {
HStack(alignment: .center) {
Image("allcoinIcon")
.resizable()
.frame(width: 17, height: 17, alignment: .leading)
Text(String(appStateContainer.user?.nbCoins ?? 0))
Text(String(appStateContainer.user!.nbCoins))
.contentTransition(.numericText())
.fontWeight(.black)
.foregroundColor(foregroundColor)
Image("allcoinIcon")
.resizable()
.frame(width: 17, height: 17, alignment: .leading)
}
.frame(width: 90, height: 40)
.background(backgroundColor)

@ -13,16 +13,15 @@ struct BetCard: View {
var bet: Bet
@State var showDetails: Bool = false
@State var showParticipate: Bool = false
var body: some View {
VStack(spacing: 0) {
VStack(alignment: .leading,spacing: 2){
HStack{
Spacer()
Text("bet_proposed_by_format \(bet.author.capitalized)")
Text("bet_proposed_by_format \(bet.author.capitalized)")
.font(.system(size: 10))
.foregroundColor(AllInColors.grey800Color)
}
Text(bet.theme)
.font(.system(size: 15))
@ -45,8 +44,8 @@ struct BetCard: View {
VStack(alignment: .leading,spacing: 2){
HStack{
Spacer()
UsersPreview()
Text("bet_players_waiting_format \(bet.registered.count.description)")
UsersPreview(users: [])
Text("bet_players_waiting_format \(bet.invited.count.description)")
.font(.system(size: 15))
.foregroundColor(AllInColors.grey800Color)
.fontWeight(.medium)
@ -63,12 +62,11 @@ struct BetCard: View {
.cornerRadius(20, corners: [.bottomLeft,.bottomRight])
.border(width: 1, edges: [.top], color: AllInColors.delimiterGrey)
}
.onTapGesture {
showDetails.toggle()
}
.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.",
endRegisterDate: Date().addingTimeInterval(86400),
endBetDate: Date().addingTimeInterval(172800),
isPublic: true,
isPrivate: false,
status: .inProgress,
invited: [],
author: "Imri",
registered: []))
author: "Imri"))
.preferredColorScheme(.dark)
}
}

@ -11,12 +11,12 @@ import Model
struct BetLineLoading: View {
@State var showInfos: Bool = false
var participations: [Participation]
var bet: BetDetail
var value: CGFloat {
let totalParticipations = participations.count
let numberOfYes = participations.filter { $0.answer.uppercased() == "YES" }.count
let numberOfNo = participations.filter { $0.answer.uppercased() == "NO" }.count
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
}
@ -25,144 +25,22 @@ struct BetLineLoading: View {
}
var yesParticipations: [Participation] {
participations.filter { $0.answer.uppercased() == "YES" }
bet.participations.filter { $0.answer.uppercased() == "YES" }
}
var noParticipations: [Participation] {
participations.filter { $0.answer.uppercased() == "NO" }
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)
switch bet.bet {
case is BinaryBet:
BinaryBetLine(bet: bet)
case is CustomBet:
CustomBetLine(bet: bet)
default:
BinaryBetLine(bet: bet)
}
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)
}
}
}
}
}
}

@ -6,22 +6,39 @@
//
import SwiftUI
import Model
struct ChoiceCapsule: View {
let filter: BetFilter
@State var pressed = false
@ObservedObject var viewModel: BetViewModel
var label: String {
switch filter {
case .isPublic:
return String(localized: "bet_public")
case .isInvitation:
return String(localized: "bet_invitation")
case .inProgress:
return String(localized: "bet_current")
case .isFinished:
return String(localized: "bet_finished")
default:
return "NaN"
}
}
var body: some View {
Group {
if(pressed) {
Text("bet_current")
Text(label)
.textStyle(weight: .semibold, color: .white, size: 15)
.padding([.leading,.trailing],13.8)
.padding([.top,.bottom], 7)
.background(AllInColors.lightPurpleColor)
.clipShape(Capsule())
} else {
Text("bet_current")
Text(label)
.textStyle(weight: .regular, color: AllInColors.grey800Color, size: 15)
.padding([.leading,.trailing], 15)
.padding([.top,.bottom], 7)
@ -35,13 +52,12 @@ struct ChoiceCapsule: View {
}
.onTapGesture() {
pressed.toggle()
if(pressed) {
viewModel.filters.insert(filter)
} else {
viewModel.filters.remove(filter)
}
}
}
}
struct ChoiceCapsule_Previews: PreviewProvider {
static var previews: some View {
ChoiceCapsule()
}
}

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

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

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

@ -0,0 +1,27 @@
//
// EmptyInfo.swift
// AllIn
//
// Created by Lucas Delanier on 29/05/2024.
//
import SwiftUI
struct EmptyInfo: View {
let emoji: String
let title: String
let explain: String
var body: some View {
VStack{
Text(emoji).font(.system(size: 120))
Text(title).textStyle(weight: .bold, color: AllInColors.primaryTextColor , size: 15)
explain.isEmpty ? nil : Text(explain).textStyle(weight: .light, color: .gray, size: 12)
}.opacity(0.55).padding(.horizontal, 20).multilineTextAlignment(.center)
}
}
struct EmptyInfo_Previews: PreviewProvider {
static var previews: some View {
EmptyInfo(emoji:"👥", title: "Vous navez pas encore damis", explain: "Ajoutez les depuis cet écran")
}
}

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

@ -12,17 +12,21 @@ struct Menu: View {
@Inject var authService: IAuthService
let parameters: [(String, String, String, String)] = [
("videoGameImage", String(localized: "drawer_create_a_bet"), String(localized: "drawer_create_a_bet_subtitle"), "CreationBet"),
("globeImage", String(localized: "drawer_public_bets"), String(localized: "drawer_public_bets_subtitle"), "Bet"),
("moneyImage", String(localized: "drawer_current_bets"), String(localized: "drawer_current_bets_subtitle"), "Current"),
("eyesImage", String(localized: "drawer_bet_history"), String(localized: "drawer_bet_history_subtitle"),"Historic"),
("friendsImage", String(localized: "drawer_friends"), String(localized: "drawer_friends_subtitle"), "Friends"),
("rankingImage", String(localized: "drawer_ranking"), String(localized: "drawer_ranking_subtitle"), "Ranking"),
]
var body: some View {
VStack(alignment: .leading, spacing: 10) {
HStack() {
Spacer()
VStack(){
Image("defaultUserImage")
.resizable()
.scaledToFit()
.frame(width: 100, height: 100)
.cornerRadius(180)
UserPicture(picture: AppStateContainer.shared.user?.image, username: (AppStateContainer.shared.user?.username)!,size: 100)
Text(AppStateContainer.shared.user?.username.capitalized ?? "")
.fontWeight(.medium)
.font(.system(size: 17))
@ -35,7 +39,7 @@ struct Menu: View {
HStack(spacing: 30) {
Spacer()
VStack(){
Text("114")
Text(AppStateContainer.shared.user?.bestWin ?? 0, format: .number)
.fontWeight(.heavy)
.font(.system(size: 15))
.foregroundColor(.white)
@ -45,7 +49,7 @@ struct Menu: View {
.foregroundColor(AllInColors.grey600Color)
}
VStack(){
Text("343")
Text(AppStateContainer.shared.user!.bestWin, format: .number)
.fontWeight(.heavy)
.font(.system(size: 15))
.foregroundColor(.white)
@ -55,7 +59,7 @@ struct Menu: View {
.foregroundColor(AllInColors.grey600Color)
}
VStack(){
Text("5")
Text(AppStateContainer.shared.user!.nbFriends, format: .number)
.fontWeight(.heavy)
.font(.system(size: 15))
.foregroundColor(.white)
@ -68,46 +72,13 @@ struct Menu: View {
}
.padding(.bottom, 15)
NavigationLink(destination: MainView(page: "CreationBet").navigationBarBackButtonHidden(true))
{
ParameterMenu(image: "videoGameImage", title: String(localized: "drawer_create_a_bet"), description: String(localized: "drawer_create_a_bet_subtitle"))
.padding([.leading,.trailing], 13)
}
NavigationLink(destination: MainView(page: "Historic")
.navigationBarBackButtonHidden(true))
{
ParameterMenu(image: "eyesImage", title: String(localized: "drawer_bet_history"), description: String(localized: "drawer_bet_history_subtitle"))
.padding([.leading,.trailing], 13)
}
NavigationLink(destination: MainView(page: "Friends")
.navigationBarBackButtonHidden(true))
{
ParameterMenu(image: "friendsImage", title: String(localized: "drawer_friends"), description: String(localized: "drawer_friends_subtitle"))
.padding([.leading,.trailing], 13)
}
VStack {
NavigationLink(destination: MainView(page: "Bet")
.navigationBarBackButtonHidden(true))
{
ParameterMenu(image: "moneyImage", title: String(localized: "drawer_public_bets"), description: String(localized: "drawer_public_bets_subtitle"))
.padding([.leading,.trailing], 13)
}
NavigationLink(destination: MainView(page: "Ranking").navigationBarBackButtonHidden(true))
{
ParameterMenu(image: "rankingImage", title: String(localized: "drawer_ranking"), description: String(localized: "drawer_ranking_subtitle"))
.padding([.leading,.trailing], 13)
ForEach(0..<parameters.count, id: \.self) { index in
NavigationLink(destination: MainView(page: parameters[index].3).navigationBarBackButtonHidden(true)) {
ParameterMenu(image: parameters[index].0, title: parameters[index].1, description: parameters[index].2)
.padding([.leading, .trailing], 13)
}
}
NavigationLink(destination: MainView(page: "Current").navigationBarBackButtonHidden(true))
{
ParameterMenu(image: "eyesImage", title: String(localized: "drawer_current_bets"), description: String(localized: "drawer_current_bets_subtitle"))
.padding([.leading,.trailing], 13)
}
HStack {
Spacer()
Button {
@ -120,14 +91,21 @@ struct Menu: View {
}
Spacer()
Image("gearIcon")
.resizable()
.frame(width: 30, height: 30)
.padding([.leading,.bottom], 20)
NavigationLink(destination: MainView(page: "Profile")
.navigationBarBackButtonHidden(true))
{
Image("gearIcon")
.resizable()
.frame(width: 30, height: 30)
.padding([.leading,.bottom], 20)
}
}
.frame(maxWidth: .infinity,alignment: .leading)
.background(AllInColors.primaryColor)
.edgesIgnoringSafeArea(.bottom)
.onAppear {
authService.refreshAuthentication()
}
}
}

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

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

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

@ -6,9 +6,11 @@
//
import SwiftUI
import Model
struct RecapBetCard: View {
var betResult: BetResultDetail
@GestureState private var longPressTap = false
@State private var isPressed = false
@State var showDetails: Bool = false
@ -19,22 +21,22 @@ struct RecapBetCard: View {
VStack(alignment: .leading,spacing: 2){
HStack{
Spacer()
Text("bet_proposed_by_format \("Lucas")")
Text("bet_proposed_by_format \(betResult.bet.author)")
.font(.system(size: 10))
.foregroundColor(AllInColors.grey800Color)
}
Text("Etudes")
Text(betResult.bet.theme)
.font(.system(size: 15))
.foregroundColor(AllInColors.grey800Color)
Text("Emre va réussir son TP de CI/CD mercredi?")
Text(betResult.bet.phrase)
.font(.system(size: 20))
.fontWeight(.bold)
HStack{
Text("bet_ends")
.font(.system(size: 15))
.foregroundColor(AllInColors.grey800Color)
TextCapsule(date: Date())
TextCapsule(date: betResult.bet.endBetDate)
Spacer()
}
}
@ -48,7 +50,7 @@ struct RecapBetCard: View {
Text("Mise")
.textStyle(weight: .regular, color: AllInColors.grey800Color, size: 15)
Spacer()
Text("1630")
Text(betResult.participation.stake.description)
.textStyle(weight: .regular, color: AllInColors.grey800Color, size: 15)
Image("Allcoins")
.resizable()
@ -65,13 +67,13 @@ struct RecapBetCard: View {
Text("Gains")
.textStyle(weight: .medium, color: AllInColors.lightPurpleColor, size: 15)
Spacer()
Text("1630")
Text(betResult.amount.description)
.font(.system(size: 15))
.fontWeight(.medium)
.overlay {
AllInColors.primaryGradient.frame(width: 50)
.mask(
Text("1630").font(.system(size: 15)).fontWeight(.medium)
Text(betResult.amount.description).font(.system(size: 15)).fontWeight(.medium)
)
}
.padding(0)
@ -87,7 +89,8 @@ struct RecapBetCard: View {
HStack{
Text("Côte totale").font(.system(size: 15)).fontWeight(.medium)
Spacer()
Text("3,46")
// TODO bind le odd
Text("1,0")
.textStyle(weight: .bold, color: .white, size: 18)
.padding([.leading,.trailing],10)
.padding([.top,.bottom],5)
@ -108,7 +111,7 @@ struct RecapBetCard: View {
.onTapGesture {
showDetails.toggle()
}.fullScreenCover(isPresented: $showDetails) {
DetailsView(isModalPresented: $showDetails, isModalParticipated: $showPartipated,id: "1")
DetailsView(isModalPresented: $showDetails, isModalParticipated: $showPartipated, id: betResult.bet.id)
}
.gesture(
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 Model
struct ResultBanner: View {
var finalAnswer: Participation
var odds: Float
var body: some View {
VStack{
HStack{
Image("BleueTrophyIcon").resizable().frame(maxWidth: 70, maxHeight: 60)
Text("OUI").font(.system(size: 70)).fontWeight(.bold).foregroundStyle(AllInColors.blueGrey800Color)
Image(systemName: "trophy.fill").resizable().frame(maxWidth: 70, maxHeight: 60).foregroundColor(AllInColors.blueGrey800Color)
Text(finalAnswer.answer).font(.system(size: 70)).fontWeight(.bold).foregroundStyle(AllInColors.blueGrey800Color)
}.frame(height: 80)
HStack(spacing: 20){
HStack{
Image("BlueAllCoinIcon").resizable().frame(maxWidth: 12, maxHeight: 12)
Text("460").font(.system(size: 16)).fontWeight(.semibold).foregroundStyle(AllInColors.blueGrey800Color)
Image("blueAllCoinIcon").resizable().frame(maxWidth: 12, maxHeight: 12)
Text(finalAnswer.stake.description).font(.system(size: 16)).fontWeight(.semibold).foregroundStyle(AllInColors.blueGrey800Color)
}
HStack{
Image("BleuePersonIcon").resizable().frame(maxWidth: 15, maxHeight: 12)
Text("ImriDu43").font(.system(size: 16)).fontWeight(.semibold).foregroundStyle(AllInColors.blueGrey800Color)
Image("bluePersonIcon").resizable().frame(maxWidth: 15, maxHeight: 12)
Text(finalAnswer.username).font(.system(size: 16)).fontWeight(.semibold).foregroundStyle(AllInColors.blueGrey800Color)
}
HStack{
Image("BleueTrophyIcon").resizable().frame(maxWidth: 15, maxHeight: 12)
Text("x1.2").font(.system(size: 16)).fontWeight(.semibold).foregroundStyle(AllInColors.blueGrey800Color)
Image("blueTrophyIcon").resizable().frame(maxWidth: 15, maxHeight: 12)
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 showPartipated: Bool = false
@State var betDetail: BetDetail
var amountBetted: Int
var isAWin: Bool
var bet: Bet
var amount: Int
var isWin: Bool
var body: some View {
VStack(spacing: 0){
VStack(alignment: .leading,spacing: 2){
HStack{
Spacer()
Text("bet_proposed_by_format \(betDetail.bet.author)")
.font(.system(size: 10))
.foregroundColor(AllInColors.grey800Color)
Text("bet_proposed_by_format \(bet.author)")
.font(.system(size: 10))
.foregroundColor(AllInColors.grey800Color)
}
Text(betDetail.bet.theme).font(.system(size: 15)).foregroundColor(AllInColors.grey800Color)
Text(betDetail.bet.phrase).font(.system(size: 20)).fontWeight(.bold)
Text(bet.theme).font(.system(size: 15)).foregroundColor(AllInColors.grey800Color)
Text(bet.phrase).font(.system(size: 20)).fontWeight(.bold)
HStack{
Text("bet_ends").font(.system(size: 15)).foregroundColor(AllInColors.grey800Color)
TextCapsule(date: betDetail.bet.endBetDate)
TextCapsule(date: bet.endBetDate)
Spacer()
}
@ -40,30 +39,43 @@ struct ReviewCard: View {
.padding(.all,15)
.background(AllInColors.componentBackgroundColor)
.cornerRadius(20, corners: [.topLeft,.topRight]).padding(.bottom,0)
VStack(alignment: .center,spacing:0){
HStack(){
Spacer()
if(betDetail.bet.endBetDate < Date()){
Text("bet_finished")
if bet.status == .finished {
Text(amount.description)
.foregroundColor(.white)
.font(.system(size: 25))
.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)
.font(.system(size: 25))
.fontWeight(.bold)
Image("allcoinWhiteIcon")
.resizable()
.frame(width: 20, height: 20, alignment: .bottom)
Text(isAWin ? "Gagnés!" : "Perdus!")
case .closing:
Text("bet_status_finished")
.foregroundColor(.white)
.font(.system(size: 25))
.fontWeight(.bold)
case .finished:
Text(isWin ? "Gagnés!" : "Perdus!")
.foregroundColor(.white)
.font(.system(size: 25))
.fontWeight(.bold)
case .cancelled:
Text("cancelled")
.foregroundColor(.white)
.font(.system(size: 25))
.fontWeight(.bold)
}
Spacer()
}
.frame(width: .infinity)
.padding(.all,10)
@ -71,17 +83,31 @@ struct ReviewCard: View {
}
.frame(width: .infinity)
.padding(.all,2)
.background(
isAWin || betDetail.bet.endBetDate < Date() ?
AnyView(AllInColors.primaryGradient) :
AnyView(Color.black)
) .cornerRadius(20, corners: [.bottomLeft,.bottomRight])
.border(width: 1, edges: [.top], color: AllInColors.delimiterGrey)
.background(backgroundColor())
.cornerRadius(20, corners: [.bottomLeft,.bottomRight])
}
.onTapGesture {
showDetails.toggle()
}.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 Model
struct TrendingBetCard: View {
var bet: Bet
@State var showDetails: Bool = false
@State var showParticipate: Bool = false
var body: some View {
VStack(alignment: .leading) {
HStack {
@ -20,7 +26,7 @@ struct TrendingBetCard: View {
}
.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)
.frame(height: 47)
.multilineTextAlignment(.leading)
@ -56,11 +62,24 @@ struct TrendingBetCard: View {
.stroke(AllInColors.primaryGradient, lineWidth: 5)
)
.clipShape(RoundedRectangle(cornerRadius: 20, style: .continuous))
.onTapGesture {
showDetails.toggle()
}
.fullScreenCover(isPresented: $showDetails) {
DetailsView(isModalPresented: $showDetails, isModalParticipated: $showParticipate, id: bet.id)
}
}
}
struct TrendingBetCard_Previews: PreviewProvider {
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 Model
struct UserInfo: View {
var username: String
var picture: String?
var value: Int
var body: some View {
HStack {
Image("defaultUserImage")
.resizable()
.frame(width: 35, height: 35)
Text("David")
.textStyle(weight: .bold, color: AllInColors.primaryTextColor, size: 13)
UserPicture(picture: picture, username: username, size: 35)
.padding(.trailing, 7)
Text(username)
.font(.system(size: 15))
.foregroundStyle(AllInColors.primaryTextColor)
.fontWeight(.semibold)
Spacer()
Image("allcoinIcon")
Text(value.description)
.font(.system(size: 18))
.foregroundStyle(AllInColors.lightPurpleColor)
.fontWeight(.bold)
.padding(.trailing, 4)
Image("PurpleAllCoin")
.resizable()
.frame(width: 17, height: 17, alignment: .leading)
Text("541")
.textStyle(weight: .black, color: AllInColors.lightPurpleColor, size: 16)
.frame(width: 15, height: 16)
}
}
}
struct UserInfo_Previews: PreviewProvider {
static var previews: some View {
UserInfo()
}
}

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

@ -6,9 +6,11 @@
//
import SwiftUI
import Model
struct WinModal: View {
@Environment(\.dismiss) var dismiss
var betResult: BetResultDetail
@State var xOffset: CGFloat = 0
var body: some View {
@ -35,15 +37,15 @@ struct WinModal: View {
}
HStack{
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))
.padding(.top,40)
Spacer()
AllcoinsCapsule()
AllcoinsCapsule(gains: betResult.amount)
Spacer()
RecapBetCard()
RecapBetCard(betResult: betResult)
Spacer()
}
.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 {
var contentWidth: CGFloat
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)
}
}

@ -35,6 +35,9 @@ struct AllInColors {
static let pink200 = Color("Pink200")
static let pink100 = Color("Pink100")
// Profile
static let secondaryTextColor = Color("SecondaryTextColor")
// Others
static let backgroundColor = Color("BackgroundColor")
static let darkBlueColor = Color("DarkBlueColor")
@ -74,6 +77,12 @@ struct AllInColors {
endPoint: .trailing
)
static let BlueBetLineGradiant = LinearGradient(
gradient: Gradient(colors: [AllInColors.blue200, AllInColors.blueAccentColor]),
startPoint: .leading,
endPoint: .trailing
)
static let PinkBetGradiant = LinearGradient(
gradient: Gradient(colors: [AllInColors.pink100, AllInColors.pink200]),
startPoint: .leading,

@ -21,6 +21,8 @@
"generic_in_waiting" = "In waiting";
"generic_search" = "Search";
"generic_add" = "Add";
"generic_acccept"="Accept";
"generic_decline"="Decline";
"generic_delete" = "Delete";
"generic_stake" = "Stake";
"network_error_text" = "Make sure to be properly connected to the network then try again.";
@ -44,15 +46,13 @@
"drawer_nb_friends" = "Friends";
"drawer_friends" = "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_current_bets" = "CURRENT BETS";
"drawer_current_bets_subtitle" = "View your current bets.";
"drawer_create_a_bet" = "CREATE A BET";
"drawer_create_a_bet" = "NEW BET";
"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_current_bets" = "CURRENT BETS";
"drawer_current_bets" = "MY PARTICIPATIONS";
"drawer_current_bets_subtitle" = "Manage your bets and reward the winners.";
"drawer_ranking" = "RANKING";
"drawer_ranking_subtitle" = "Check your ranking among your friends.";
@ -131,8 +131,9 @@
"bet_ends" = "Ends on";
"bet_ended" = "Ended on";
"bet_participate" = "Participate";
"bet_proposed_by_format %@" = "Proposed by %@";
"bet_players_waiting_format %@" = "%@ joueurs en attente";
"bet_proposed_by_format %@" = "Proposed by %@";
"bet_proposed_by_format" = "Proposed by";
"bet_players_waiting_format %@" = "%@ players waiting";
"bet_players_format" = "players";
"bet_points_at_stake_format" = "points at stake";
@ -147,6 +148,7 @@
"bet_status_participants_list" = "Participants";
"bet_status_details_drawer" = "Details";
"participation_possible_winnings" = "Possible winnings";
"bet_status_stake" = "Gambled";
/// Bet history
@ -165,6 +167,8 @@
/// Friends
"friends_title" = "Friends";
"friends_request_sent" = "Request sent";
"friends_request"= "Friends Request";
/// Daily reward
@ -175,3 +179,16 @@
"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.";
/// 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_search" = "Rechercher";
"generic_add" = "Ajouter";
"generic_acccept"="Accepter";
"generic_decline"="Decliner";
"generic_delete" = "Supprimer";
"generic_stake" = "Mise";
"network_error_text" = "Assurez-vous d\'être bien connecté au réseau puis réessayez.";
@ -44,15 +46,13 @@
"drawer_nb_friends" = "Amis";
"drawer_friends" = "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_current_bets" = "BETS EN COURS";
"drawer_current_bets_subtitle" = "Consultez vos paris en cours.";
"drawer_create_a_bet" = "CREER UN BET";
"drawer_create_a_bet" = "NOUVEAU BET";
"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_current_bets" = "BETS EN COURS";
"drawer_current_bets" = "MES PARTICIPATIONS";
"drawer_current_bets_subtitle" = "Gérez vos bets et récompensez les gagnants.";
"drawer_ranking" = "CLASSEMENT";
"drawer_ranking_subtitle" = "Consultez votre classement parmis vos amis.";
@ -132,7 +132,8 @@
"bet_ended" = "A pris fin le";
"bet_participate" = "Participer";
"bet_proposed_by_format %@" = "Proposé par %@";
"bet_players_waiting_format %@" = "%@ players waiting";
"bet_proposed_by_format" = "Proposé par";
"bet_players_waiting_format %@" = "%@ joueurs en attente";
"bet_players_format" = "joueurs";
"bet_points_at_stake_format" = "points en jeu";
@ -147,6 +148,7 @@
"bet_status_participants_list" = "Liste des participants";
"bet_status_details_drawer" = "Détails";
"participation_possible_winnings" = "Gains possibles";
"bet_status_stake" = "Pariés";
/// Bet history
@ -165,6 +167,8 @@
/// Friends
"friends_title" = "Amis";
"friends_request_sent" = "Requête envoyée";
"friends_request"= "Requêtes d'amis";
/// Daily reward
@ -175,3 +179,16 @@
"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.";
/// 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.";

@ -56,7 +56,7 @@ class AuthService: IAuthService {
func register(username: String, email: String, password: String, completion : @escaping (Int)-> ()) {
let url = URL(string: Config.allInApi + "/users/register")!
let url = URL(string: Config.allInApi + "users/register")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
@ -148,7 +148,7 @@ class AuthService: IAuthService {
}
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() {

@ -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,25 +13,62 @@ import Combine
class BetViewModel: ObservableObject {
@Inject var manager: Manager
@Inject var authService: IAuthService
@Published var popularBet: Bet?
@Published private(set) var bets: [Bet] = []
@Published var betsOver: [BetDetail] = []
@Published var showingSheet: Bool = false
@Published var betsWon: [BetResultDetail] = []
@Published var showingSheetOver: Bool = false
@Published var showingSheetWon: Bool = false
@Published var filters: Set<BetFilter> = [] {
didSet {
getItems()
}
}
init() {
getItems()
getPopularBet()
getBetsOver()
getBetsWon()
}
func getItems() {
manager.getBets(withIndex: 0, withCount: 20) { bets in
self.bets = bets
manager.getBets(withIndex: 0, withCount: 20, filters: Array(filters)) { bets in
DispatchQueue.main.async {
self.bets = bets
}
}
}
func getBetsOver() {
manager.getBetsOver() { bets in
self.betsOver = bets
if !self.betsOver.isEmpty {
self.showingSheet = true
DispatchQueue.main.async {
self.betsOver = bets
if !self.betsOver.isEmpty {
self.showingSheetOver = true
}
}
}
}
func getBetsWon() {
manager.getBetsWon() { bets in
DispatchQueue.main.async {
self.betsWon = bets
if !self.betsWon.isEmpty {
self.showingSheetWon = true
self.authService.refreshAuthentication()
}
}
}
}
func getPopularBet() {
manager.getPopularBet() { bet in
DispatchQueue.main.async {
self.popularBet = bet
}
print(bets)
}
}
}

@ -15,30 +15,67 @@ class CreationBetViewModel: ObservableObject {
@Inject var manager: Manager
@Published var theme: String = ""
@Published var description: String = ""
@Published var isPublic = true
@Published var isPrivate = false {
didSet {
invited.removeAll()
}
}
@Published var endRegisterDate = Date()
@Published var endBetDate = Date()
@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 descriptionFieldError: String?
@Published var endRegisterDateFieldError: String?
@Published var endBetDateFieldError: String?
@Published var responsesFieldError: String?
@Published var errorMessage: String?
@Published var showErrorMessage = false
@Published var friends: [User] = []
let options: [(Int, String, String)] = [
(0, "questionMarkIcon", String(localized: "bet_type_binary")),
(1, "footballIcon", String(localized: "bet_type_match")),
(2, "paintbrushIcon", String(localized: "bet_type_custom"))
]
@Published var response = ""
@Published var groupedItems: [[String]] = [[String]] ()
init() {
getFriends()
}
func getFriends() {
manager.getFriends() { friends in
DispatchQueue.main.async {
self.friends = friends
}
}
}
func create() {
guard checkAndSetError(forTheme: true, forDescription: true, forEndRegisterDate: true, forEndBetDate: true) else {
guard checkAndSetError(forTheme: true, forDescription: true, forEndRegisterDate: true, forEndBetDate: true, forResponse: true) else {
return
}
resetAllFieldErrors()
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 {
case 201:
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 newDescriptionFieldError: String?
var newEndRegisterDateFieldError: String?
var newEndBetDateFieldError: String?
var newResponsesFieldError: String?
var hasError = false
@ -85,6 +123,19 @@ class CreationBetViewModel: ObservableObject {
hasError = true
}
if checkResponse, selectedTypeBet == 2 {
if values.count < 2 {
newResponsesFieldError = "Il doit y'avoir 2 réponses minimum"
hasError = true
} else {
let uniqueValues = Set(values)
if uniqueValues.count != values.count {
newResponsesFieldError = "Les réponses doivent être uniques"
hasError = true
}
}
}
if !hasError {
// No error
return true
@ -95,6 +146,7 @@ class CreationBetViewModel: ObservableObject {
descriptionFieldError = newDescriptionFieldError
endRegisterDateFieldError = newEndRegisterDateFieldError
endBetDateFieldError = newEndBetDateFieldError
responsesFieldError = newResponsesFieldError
}
return false
}
@ -105,6 +157,7 @@ class CreationBetViewModel: ObservableObject {
descriptionFieldError = nil
endRegisterDateFieldError = nil
endBetDateFieldError = nil
responsesFieldError = nil
}
}
@ -113,16 +166,20 @@ class CreationBetViewModel: ObservableObject {
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 {
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:
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:
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:
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)
}
}
}

@ -12,7 +12,7 @@ import Model
class CurrentBetViewModel: ObservableObject {
@Inject var manager: Manager
@Published private(set) var bets: [BetDetail] = []
init() {
@ -21,9 +21,8 @@ class CurrentBetViewModel: ObservableObject {
func getItems() {
manager.getCurrentBets(withIndex: 0, withCount: 20) { bets in
for bet in bets {
let betDetail = BetDetail(bet: bet, answers: [AnswerDetail(response: "OUI", totalStakes: 120, totalParticipants: 2, highestStake: 200, odds: 1.2), AnswerDetail(response: "NON", totalStakes: 120, totalParticipants: 2, highestStake: 200, odds: 1.2)], participations: [])
self.bets.append(betDetail)
DispatchQueue.main.async {
self.bets = bets
}
}
}

@ -15,9 +15,9 @@ class DetailsViewModel: ObservableObject {
@Inject var manager: Manager
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 betDetail: BetDetail?
init(id: String) {
@ -26,25 +26,31 @@ class DetailsViewModel: ObservableObject {
}
func getItem(withId id: String) {
let semaphore = DispatchSemaphore(value: 0)
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() {
if let stake = Int(mise) {
var rep: String = ""
if answer == 0 {
rep = "Yes"
} else {
rep = "No"
}
manager.addParticipation(withId: id, withAnswer: rep, andStake: stake) { statusCode in
manager.addParticipation(withId: id, withAnswer: selectedAnswer.response, andStake: stake) { statusCode in
switch statusCode {
case 201:
AppStateContainer.shared.user?.nbCoins -= stake
WidgetCenter.shared.reloadAllTimelines()
self.getItem(withId: self.id)
default:
break
@ -52,10 +58,20 @@ class DetailsViewModel: ObservableObject {
}
}
mise = ""
answer = 0
if let firstAnswer = betDetail!.answers.first {
self.selectedAnswer = firstAnswer
}
}
func checkAndSetError() {
func checkAndSetError() -> Bool {
if let stake = Int(mise) {
if stake <= AppStateContainer.shared.user?.nbCoins ?? 0 && stake > 0 {
return false
} else {
return true
}
} else {
return true
}
}
}

@ -13,23 +13,101 @@ class FriendsViewModel: ObservableObject {
@Inject var manager: Manager
@Published private(set) var users: [User] = []
@Published private(set) var requests: [User] = []
@Published var text: String = "" {
didSet {
if text.isEmpty {
getItems()
} else {
search()
}
}
}
init() {
getItems()
getRequests()
}
func getItems ( ) {
func getItems() {
manager.getFriends() { friends in
DispatchQueue.main.async {
self.users = friends
}
}
}
func deleteItem(indexSet: IndexSet) {
func getRequests() {
manager.getRequests() { friends in
DispatchQueue.main.async {
self.requests = friends
}
}
}
func search() {
guard text.allSatisfy({ $0.isLetter || $0.isNumber }) else {
return
}
manager.getUsers(withName: text) { users in
self.users = users
}
}
func moveltem(from: IndexSet, to: Int) {
func toggleFriendStatus(for user: User, isRequest: Bool) {
let targetList = isRequest ? requests : users
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 {
case .friend:
updatedUser.friendStatus = .notFriend
deleteItem(username: user.username)
case .notFriend:
updatedUser.friendStatus = .requested
addItem(username: user.username)
case .requested:
updatedUser.friendStatus = .notFriend
deleteItem(username: user.username)
default:
break
}
users[index] = updatedUser
}
}
func addItem(title: String) {
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) {
manager.removeFriend(withUsername: username) { statusCode in
}
}
func addItem(username: String) {
manager.addFriend(withUsername: username) { statusCode in
}
}
}

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

@ -13,23 +13,19 @@ class RankingViewModel: ObservableObject {
@Inject var manager: Manager
@Published private(set) var friends: [User] = []
init() {
getItems()
}
func getItems ( ) {
}
func deleteItem(indexSet: IndexSet) {
}
func moveltem(from: IndexSet, to: Int) {
}
func addItem(title: String) {
func getItems() {
manager.getFriends() { users in
var friends = users
friends.append(AppStateContainer.shared.user!)
DispatchQueue.main.async {
self.friends = friends.sorted(by: { $0.nbCoins > $1.nbCoins })
}
}
}
}

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

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

@ -18,14 +18,23 @@ struct BetView: View {
VStack(alignment: .center, spacing: 0) {
TopBar(showMenu: self.$showMenu)
ScrollView(showsIndicators: false) {
LazyVStack(alignment: .leading, 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 {
VStack(spacing: 20){
ForEach(viewModel.bets, id: \.id) { (bet: Bet) in
BetCard(bet: bet)
if(viewModel.bets.isEmpty){
EmptyInfo(emoji:"🎮", title: String(localized: "empty_bets_title"), explain: "")
}
else{
ForEach(viewModel.bets, id: \.id) { (bet: Bet) in
BetCard(bet: bet)
}
}
}
.padding([.leading,.trailing],25)
@ -35,13 +44,10 @@ struct BetView: View {
AllInColors.fadeInGradiantCard
ScrollView(.horizontal,showsIndicators: false){
HStack{
ChoiceCapsule()
ChoiceCapsule()
ChoiceCapsule()
ChoiceCapsule()
ChoiceCapsule()
ChoiceCapsule()
ChoiceCapsule()
ChoiceCapsule(filter: .isPublic, viewModel: viewModel)
ChoiceCapsule(filter: .isInvitation, viewModel: viewModel)
ChoiceCapsule(filter: .inProgress, viewModel: viewModel)
ChoiceCapsule(filter: .isFinished, viewModel: viewModel)
}
.padding(.leading,25)
.padding([.top,.bottom],15)
@ -53,26 +59,25 @@ struct BetView: View {
.refreshable {
viewModel.getItems()
}
.sheet(isPresented: $viewModel.showingSheet, onDismiss: {
.sheet(isPresented: $viewModel.showingSheetOver, onDismiss: {
viewModel.betsOver.removeFirst()
viewModel.showingSheet = !viewModel.betsOver.isEmpty
viewModel.showingSheetOver = !viewModel.betsOver.isEmpty
}) {
if let firstBetDetail = viewModel.betsOver.first {
BetEndingValidationView(bet: firstBetDetail)
}
}
.sheet(isPresented: $viewModel.showingSheetWon, onDismiss: {
viewModel.betsWon.removeFirst()
viewModel.showingSheetWon = !viewModel.betsWon.isEmpty
}) {
if let firstBetResultDetail = viewModel.betsWon.first {
WinModal(betResult: firstBetResultDetail)
}
}
Spacer()
}
.edgesIgnoringSafeArea(.bottom)
.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
@State private var response = ""
@State private var values: [String] = []
let options: [(Int, String, String)] = [
(0, "questionMarkIcon", String(localized: "bet_type_binary")),
(1, "footballIcon", String(localized: "bet_type_match")),
(2, "paintbrushIcon", String(localized: "bet_type_custom"))
]
@State var groupedItems: [[String]] = [[String]] ()
private func updateGroupedItems() {
var updatedGroupedItems: [[String]] = [[String]] ()
@ -46,7 +35,7 @@ struct CreationBetView: View {
var width: CGFloat = 0
var dynamicWidthLimit: CGFloat
for value in values {
for value in viewModel.values {
let label = UILabel()
label.text = value
label.sizeToFit()
@ -65,7 +54,7 @@ struct CreationBetView: View {
}
updatedGroupedItems.append(tempItems)
groupedItems = updatedGroupedItems
viewModel.groupedItems = updatedGroupedItems
}
var body: some View {
@ -108,6 +97,7 @@ struct CreationBetView: View {
.foregroundColor(AllInColors.lightGrey300Color)
.font(.system(size: 14))
.fontWeight(.light))
.autocorrectionDisabled(true)
.padding()
.background(
RoundedRectangle(cornerRadius: 9)
@ -151,6 +141,7 @@ struct CreationBetView: View {
.foregroundColor(AllInColors.lightGrey300Color)
.font(.system(size: 14))
.fontWeight(.light), axis: .vertical)
.autocorrectionDisabled(true)
.lineLimit(4, reservesSpace: true)
.padding()
.background(
@ -191,7 +182,7 @@ struct CreationBetView: View {
DatePicker(
"",
selection: $viewModel.endRegisterDate,
in: dateRange,
in: Date()...,
displayedComponents: [.date, .hourAndMinute]
)
.accentColor(AllInColors.lightPurpleColor)
@ -229,7 +220,7 @@ struct CreationBetView: View {
DatePicker(
"",
selection: $viewModel.endBetDate,
in: dateRange,
in: viewModel.endRegisterDate...,
displayedComponents: [.date, .hourAndMinute]
)
.accentColor(AllInColors.lightPurpleColor)
@ -257,15 +248,15 @@ struct CreationBetView: View {
.padding(.leading, 10)
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 {
viewModel.isPublic = true
viewModel.isPrivate = false
}
.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 {
viewModel.isPublic = false
viewModel.isPrivate = true
}
Spacer()
}
@ -276,8 +267,8 @@ struct CreationBetView: View {
VStack(spacing: 10) {
if !self.viewModel.isPublic {
DropDownFriends()
if self.viewModel.isPrivate {
DropDownFriends(selectedItems: $viewModel.invited, friends: viewModel.friends)
.padding(.bottom, 30)
HStack() {
@ -360,14 +351,14 @@ struct CreationBetView: View {
VStack(spacing: 5) {
VStack() {
DropDownMenu(selectedOption: $viewModel.selectedOption, options: options)
DropDownMenu(selectedOption: $viewModel.selectedTypeBet, options: viewModel.options)
}
.padding([.bottom], 15)
.frame(width: 340)
Group {
switch viewModel.selectedOption {
switch viewModel.selectedTypeBet {
case 0:
Text("bet_creation_yes_no_bottom_text_1")
.textStyle(weight: .bold, color: AllInColors.veryLightPurpleColor, size: 13)
@ -385,12 +376,18 @@ struct CreationBetView: View {
.textStyle(weight: .bold, color: AllInColors.veryLightPurpleColor, size: 13)
.padding(.bottom, 15)
if let responseError = $viewModel.responsesFieldError.wrappedValue {
Text(responseError)
.textStyle(weight: .bold, color: .red, size: 10)
}
VStack(spacing: 5) {
HStack(spacing: 0) {
TextField("", text: $response, prompt: Text("bet_creation_response_title")
TextField("", text: $viewModel.response, prompt: Text("bet_creation_response_title")
.foregroundColor(AllInColors.lightGrey200Color)
.font(.system(size: 16))
.fontWeight(.medium))
.autocorrectionDisabled(true)
.padding()
.background(
Rectangle()
@ -400,17 +397,17 @@ struct CreationBetView: View {
)
.frame(width: 250, height: 38)
.foregroundColor(.black)
.onChange(of: response) { newValue in
.onChange(of: viewModel.response) { newValue in
if newValue.count > 20 {
response = String(newValue.prefix(20))
viewModel.response = String(newValue.prefix(20))
}
}
Button(action: {
if !response.isEmpty && values.count < 5 {
values.append(response)
if !viewModel.response.isEmpty && viewModel.values.count < 5 {
viewModel.values.append(viewModel.response)
updateGroupedItems()
response = ""
viewModel.response = ""
}
}) {
Text("generic_add")
@ -423,12 +420,12 @@ struct CreationBetView: View {
}
HStack {
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)
}
VStack(spacing: 10) {
ForEach(groupedItems, id: \.self) { items in
ForEach(viewModel.groupedItems, id: \.self) { items in
HStack {
ForEach(items, id: \.self) { text in
HStack {
@ -436,8 +433,8 @@ struct CreationBetView: View {
.foregroundColor(.white)
.lineLimit(1)
Button(action: {
if let index = values.firstIndex(of: text) {
values.remove(at: index)
if let index = viewModel.values.firstIndex(of: text) {
viewModel.values.remove(at: index)
updateGroupedItems()
}
}) {

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

@ -5,27 +5,26 @@ struct DetailsView: View {
@Binding var isModalPresented: Bool
@Binding var isModalParticipated: Bool
@State var progressValue: Float = 0.2
@StateObject private var viewModel: DetailsViewModel
var isFinished: Bool {
viewModel.betDetail?.finalAnswer == nil ? false : true
viewModel.betDetail?.wonParticipation == nil ? false : true
}
var StatusValues: (String, Color) {
if let betType = viewModel.betDetail?.bet.status {
switch betType {
case .inProgress:
return ("bet_status_in_progress", AllInColors.darkPurpleColor)
return (String(localized: "bet_status_in_progress"), AllInColors.darkPurpleColor)
case .waiting, .closing:
return ("bet_status_waiting", AllInColors.pink100)
return (String(localized: "bet_status_waiting"), AllInColors.pink100)
case .finished:
return ("bet_status_finished", AllInColors.grey100Color)
return (String(localized: "bet_status_finished"), AllInColors.grey100Color)
case .cancelled:
return ("bet_status_cancelled", AllInColors.grey100Color)
return (String(localized: "bet_status_cancelled"), AllInColors.grey100Color)
}
} else {
return ("bet_status_unavailable", AllInColors.pink100)
return (String(localized: "bet_status_unavailable"), AllInColors.pink100)
}
}
@ -58,97 +57,117 @@ struct DetailsView: View {
}
.padding(.horizontal, 15)
.background(StatusValues.1)
VStack(spacing: 0) {
VStack(alignment: .leading, spacing: 5) {
HStack(spacing: 3) {
Spacer()
Text("bet_proposed_by_format")
.font(.system(size: 10))
.foregroundColor(AllInColors.grey800Color)
Text((viewModel.betDetail?.bet.author ?? "Unknown").capitalized)
.font(.system(size: 10))
.fontWeight(.semibold)
.foregroundColor(AllInColors.primaryTextColor)
}
Text(viewModel.betDetail?.bet.theme ?? "Not loaded")
.font(.system(size: 15))
.foregroundColor(AllInColors.grey800Color)
Text(viewModel.betDetail?.bet.phrase ?? "Not loaded")
.font(.system(size: 20))
.fontWeight(.bold)
.padding(.bottom, 10)
HStack {
HStack {
if viewModel.betDetail != nil{
ScrollView {
VStack(alignment: .leading, spacing: 0) {
HStack(spacing: 3) {
Spacer()
Text("bet_starting")
.font(.system(size: 15))
Text("bet_proposed_by_format")
.font(.system(size: 10))
.foregroundColor(AllInColors.grey800Color)
Text((viewModel.betDetail?.bet.author ?? "Unknown").capitalized)
.font(.system(size: 10))
.fontWeight(.semibold)
.foregroundColor(AllInColors.primaryTextColor)
}
.frame(width: 105)
.padding(.trailing, 10)
TextCapsule(date: viewModel.betDetail?.bet.endRegisterDate ?? Date())
Spacer()
}.padding(.bottom, 10)
HStack {
Text(viewModel.betDetail?.bet.theme ?? "Not loaded")
.font(.system(size: 15))
.foregroundColor(AllInColors.grey800Color)
Text(viewModel.betDetail?.bet.phrase ?? "Not loaded")
.font(.system(size: 20))
.fontWeight(.bold)
.padding(.bottom, 10)
HStack {
HStack {
Spacer()
Text("bet_starting")
.font(.system(size: 15))
.foregroundColor(AllInColors.grey800Color)
}
.frame(width: 105)
.padding(.trailing, 10)
TextCapsule(date: viewModel.betDetail?.bet.endRegisterDate ?? Date())
Spacer()
}.padding(.bottom, 10)
HStack {
HStack {
Spacer()
Text("bet_ends")
.font(.system(size: 15))
.foregroundColor(AllInColors.grey800Color)
}
.frame(width: 105)
.padding(.trailing, 10)
TextCapsule(date: viewModel.betDetail?.bet.endBetDate ?? Date())
Spacer()
Text("bet_ends")
.font(.system(size: 15))
.foregroundColor(AllInColors.grey800Color)
}
.frame(width: 105)
.padding(.trailing, 10)
TextCapsule(date: viewModel.betDetail?.bet.endBetDate ?? Date())
Spacer()
}
}
.padding(.all, 15)
.padding(.vertical, 10)
.background(AllInColors.componentBackgroundColor)
.cornerRadius(20, corners: [.topLeft,.topRight]).padding(.bottom,0)
if isFinished {
ResultBanner()
}
VStack(alignment: .leading, spacing: 5) {
BetLineLoading(participations: viewModel.betDetail?.participations ?? [])
.padding(.vertical,15)
Text("bet_status_participants_list")
.font(.system(size: 18))
.foregroundStyle(AllInColors.grey100Color)
.fontWeight(.bold)
.padding(.bottom, 10)
ScrollView(showsIndicators: false) {
.padding(.all, 15)
.padding(.top, 6)
.border(width: 1, edges: [.bottom], color: AllInColors.delimiterGrey)
.background(AllInColors.componentBackgroundColor)
.cornerRadius(20, corners: [.topLeft,.topRight])
if isFinished {
ResultBanner(finalAnswer: (viewModel.betDetail?.wonParticipation)!, odds: (viewModel.betDetail?.odds)!)
}
VStack(alignment: .leading, spacing: 0) {
if let bet = viewModel.betDetail{
BetLineLoading(bet: bet).padding(.vertical, 20)
}
Text("bet_status_participants_list")
.font(.system(size: 18))
.foregroundStyle(AllInColors.grey100Color)
.fontWeight(.bold)
.padding(.bottom, 10)
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)
Spacer()
}
.padding(.bottom, geometry.safeAreaInsets.bottom + 28)
.padding([.trailing,.leading], 15)
.padding(.bottom, 100)
Spacer()
}
.padding([.bottom,.trailing,.leading], 15)
.frame(maxWidth: .infinity, maxHeight: (geometry.size.height + geometry.safeAreaInsets.bottom) - 50)
.background(AllInColors.underComponentBackgroundColor)
.border(width: 1, edges: [.top], color: AllInColors.delimiterGrey)
Spacer()
.cornerRadius(15, corners: [.topLeft, .topRight])
}
else{
ScrollView {
HStack(alignment: .center){
Spacer()
Image(systemName: "exclamationmark.triangle.fill").font(.system(size: 45))
VStack(alignment:.leading){
Text("error_title").font(.system(size: 10))
Text("error_message").font(.system(size: 10))
}
Spacer()
}.opacity(0.6)
.padding(.vertical, 20)
}
.frame(maxWidth: .infinity, maxHeight: (geometry.size.height + geometry.safeAreaInsets.bottom) - 50)
.background(AllInColors.underComponentBackgroundColor)
.cornerRadius(15, corners: [.topLeft, .topRight])
}
.frame(maxWidth: .infinity, maxHeight: (geometry.size.height + geometry.safeAreaInsets.bottom) - 50)
.background(AllInColors.componentBackgroundColor)
.cornerRadius(15, corners: [.topLeft, .topRight])
ParticipateButton(isOpen: $isModalPresented, isParticapatedOpen: $isModalParticipated, bet: viewModel.betDetail?.bet)
.padding(.bottom, geometry.safeAreaInsets.bottom + 5)
.padding(.horizontal, 10)
}
.sheet(isPresented: $isModalParticipated) {
ParticipationModal(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()
isModalParticipated.toggle()
})
},
checkAndSetError: viewModel.checkAndSetError)
.presentationDetents([.fraction(0.55)])
}
.edgesIgnoringSafeArea(.bottom)

@ -9,32 +9,112 @@ import SwiftUI
struct FriendsView: View {
@StateObject private var viewModel = FriendsViewModel()
@Binding var showMenu: Bool
@State private var selectedTab = 0
var body: some View {
VStack(alignment: .center, spacing: 0) {
TopBar(showMenu: self.$showMenu)
Text("friends_title")
.textStyle(weight: .bold, color: AllInColors.grey500Color, size: 25)
.padding([.top,.bottom],15)
ScrollView(showsIndicators: false){
Friend(image: "https://picsum.photos/536/354", pseudo: "Lucas")
Friend(image: "https://picsum.photos/536/354", pseudo: "Arthur")
Friend(image: "https://picsum.photos/536/354", pseudo: "Lucase")
Friend(image: "https://picsum.photos/536/354", pseudo: "Rayhan")
TabView(selection: $selectedTab) {
ScrollView(showsIndicators: false){
VStack{
HStack {
TextField("Search...", text: $viewModel.text)
.padding(7)
.zIndex(200)
.padding(.horizontal, 25)
.background(Color(.systemGray6))
.cornerRadius(8)
.overlay(
HStack {
Image(systemName: "magnifyingglass")
.foregroundColor(.gray)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
.padding(.leading, 8)
if !viewModel.text.isEmpty {
Button(action: {
self.viewModel.text = ""
}) {
Image(systemName: "multiply.circle.fill")
.foregroundColor(.gray)
.padding(.trailing, 8)
}
}
}
)
.padding(.horizontal, 10)
}
if(viewModel.users.isEmpty){
EmptyInfo(emoji:"👥", title: String(localized: "empty_friends_title"), explain: String(localized: "empty_friends_explain")).padding(.top, 40)
}
else{
ForEach(viewModel.users, id: \.self) { friend in
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)
}
Spacer()
}
}.refreshable {
viewModel.getRequests()
}
.padding(.top, 50)
.tag(1)
}
.padding(.top, 25)
Spacer()
.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)
.background(AllInColors.backgroundColor)
}
}
struct FriendsView_Previews: PreviewProvider {
static var previews: some View {
FriendsView(showMenu: .constant(false))
}
}

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

@ -48,6 +48,8 @@ struct MainView: View {
CreationBetView(showMenu: self.$showMenu)
case "Current":
CurrentBetView(showMenu: self.$showMenu)
case "Profile":
ProfileView(showMenu: self.$showMenu)
default:
BetView(showMenu: self.$showMenu)
}
@ -58,8 +60,8 @@ struct MainView: View {
if self.showMenu {
Menu()
.frame(width: geometry.size.width*0.83)
.transition(.move(edge: .leading))
.frame(width: geometry.size.width*0.83)
}
}

@ -0,0 +1,116 @@
//
// ProfileView.swift
// AllIn
//
// Created by étudiant on 26/05/2024.
//
import SwiftUI
struct ProfileView: View {
@Binding var showMenu: Bool
let parameters: [(title: String, icon: String, backgroundColor: Color, itemColor: Color)] = [
(title: "Changer de pseudo", icon: "person", backgroundColor: AllInColors.componentBackgroundColor, itemColor: AllInColors.secondaryTextColor),
(title: "Changer le mot de passe", icon: "lock", backgroundColor: AllInColors.componentBackgroundColor, itemColor: AllInColors.secondaryTextColor),
(title: "Supprimer le compte", icon: "trash", backgroundColor: AllInColors.componentBackgroundColor, itemColor: AllInColors.secondaryTextColor),
(title: "Déconnexion", icon: "xmark", backgroundColor: AllInColors.lightPurpleColor, itemColor: .white)
]
var body: some View {
GeometryReader { geometry in
VStack(alignment: .leading, spacing: 0) {
TopBar(showMenu: self.$showMenu)
VStack(alignment: .leading) {
HStack {
Image("defaultUserImage")
.resizable()
.scaledToFit()
.frame(width: 80, height: 80)
.cornerRadius(180)
VStack(alignment: .leading, spacing: 5) {
Text("Pseudo")
.fontWeight(.bold)
.font(.system(size: 20))
HStack(spacing: 15) {
VStack(){
Text("114")
.fontWeight(.heavy)
.font(.system(size: 15))
Text(String(localized: "drawer_bets"))
.fontWeight(.regular)
.font(.system(size: 12))
.foregroundColor(AllInColors.grey600Color)
}
VStack(){
Text("343")
.fontWeight(.heavy)
.font(.system(size: 15))
Text(String(localized: "drawer_best_win"))
.fontWeight(.regular)
.font(.system(size: 12))
.foregroundColor(AllInColors.grey600Color)
}
VStack(){
Text("5")
.fontWeight(.heavy)
.font(.system(size: 15))
Text(String(localized: "drawer_nb_friends"))
.fontWeight(.regular)
.font(.system(size: 12))
.foregroundColor(AllInColors.grey600Color)
}
}
}
.padding(.leading, 10)
}
Text("Compte")
.fontWeight(.bold)
.font(.system(size: 16))
.padding(.leading, 13)
.padding(.top, 35)
VStack(spacing: 0) {
ForEach(0..<parameters.count, id: \.self) { index in
HStack {
Image(systemName: parameters[index].icon)
.foregroundColor(parameters[index].itemColor)
.padding(.leading, 13)
Text(parameters[index].title)
.font(.system(size: 14))
.fontWeight(.bold)
.foregroundColor(parameters[index].itemColor)
Spacer()
Image(systemName: "chevron.right")
.resizable()
.frame(width: 8, height: 12)
.foregroundColor(parameters[index].itemColor)
.padding(.trailing, 14)
}
.padding(.vertical, 15)
.background(parameters[index].backgroundColor)
}
}
.frame(maxWidth: .infinity)
.cornerRadius(7)
Spacer()
}
.padding(.top, geometry.size.width*0.15)
.padding(.horizontal, 20)
}
.edgesIgnoringSafeArea(.bottom).background(AllInColors.backgroundColor)
}
}
}
struct ProfileView_Previews: PreviewProvider {
static var previews: some View {
ProfileView(showMenu: .constant(false))
}
}

@ -10,6 +10,7 @@ import SwiftUI
struct RankingView: View {
@Binding var showMenu: Bool
@StateObject var viewModel = RankingViewModel()
var body: some View {
GeometryReader { geometry in
@ -17,121 +18,122 @@ struct RankingView: View {
TopBar(showMenu: self.$showMenu)
Text("ranking_title")
.textStyle(weight: .bold, color: AllInColors.grey500Color, size: 25)
.padding([.top,.bottom],15)
.padding([.top,.bottom], 15)
HStack {
ZStack {
VStack(spacing: 0){
Spacer()
Text("Pseudo")
.fontWeight(.bold)
.padding(.bottom, 4)
.font(.system(size: 16))
.lineLimit(1)
Divider()
.background(AllInColors.lightGrey100Color)
HStack{
Spacer()
Image("allcoinIcon")
.resizable()
.frame(width: 18, height: 18, alignment: .leading)
.padding([.top,.bottom],10)
Text(String("570"))
.textStyle(weight: .black, color: AllInColors.lightPurpleColor, size: 16)
.padding(.trailing, 18)
if viewModel.friends.indices.contains(0) {
ZStack {
VStack(spacing: 0) {
Spacer()
Text(viewModel.friends[0].username)
.fontWeight(.bold)
.padding(.bottom, 4)
.font(.system(size: 16))
.lineLimit(1)
Divider()
.background(AllInColors.lightGrey100Color)
HStack {
Spacer()
Image("allcoinIcon")
.resizable()
.frame(width: 18, height: 18, alignment: .leading)
.padding([.top, .bottom], 10)
Text(String(viewModel.friends[0].nbCoins))
.textStyle(weight: .black, color: AllInColors.lightPurpleColor, size: 16)
.padding(.trailing, 18)
Spacer()
}
.frame(width: geometry.size.width * 0.43)
.background(AllInColors.underComponentBackgroundColor)
}
.frame(width: geometry.size.width * 0.43)
.background(AllInColors.underComponentBackgroundColor)
.frame(width: geometry.size.width * 0.43, height: 120)
.background(AllInColors.componentBackgroundColor)
.cornerRadius(41.5, corners: .topLeft)
.cornerRadius(8, corners: .topRight)
.cornerRadius(15, corners: [.bottomLeft, .bottomRight])
UserPicture(picture: viewModel.friends[0].image, username: viewModel.friends[0].username, size: 70)
.offset(x: 0, y: -55)
Text("1")
.frame(width: 28, height: 28)
.foregroundColor(.white)
.background(AllInColors.lightPurpleColor)
.cornerRadius(30)
.font(.system(size: 18))
.fontWeight(.bold)
.offset(x: 0, y: -23)
}
.frame(width: geometry.size.width * 0.43, height: 120)
.background(AllInColors.componentBackgroundColor)
.cornerRadius(41.5, corners: .topLeft)
.cornerRadius(8, corners: .topRight)
.cornerRadius(15, corners: [.bottomLeft, .bottomRight])
Image("defaultUserImage")
.resizable()
.frame(width: 70, height: 70)
.scaledToFit()
.cornerRadius(180)
.offset(x: 0, y: -55)
Text("1")
.frame(width: 28, height: 28)
.foregroundColor(.white)
.background(AllInColors.lightPurpleColor)
.cornerRadius(30)
.font(.system(size: 18))
.fontWeight(.bold)
.offset(x: 0, y: -23)
}
ZStack {
VStack(spacing: 0){
Spacer()
Text("Pseudo")
.fontWeight(.bold)
.padding(.bottom, 4)
.font(.system(size: 15))
.lineLimit(1)
Divider()
.background(AllInColors.lightGrey100Color)
HStack{
Spacer()
Image("allcoinIcon")
.resizable()
.frame(width: 18, height: 18, alignment: .leading)
.padding([.top,.bottom],10)
Text(String("570"))
.textStyle(weight: .black, color: AllInColors.lightPurpleColor, size: 16)
.padding(.trailing, 18)
if viewModel.friends.indices.contains(1) {
ZStack {
VStack(spacing: 0) {
Spacer()
Text(viewModel.friends[1].username)
.fontWeight(.bold)
.padding(.bottom, 4)
.font(.system(size: 15))
.lineLimit(1)
Divider()
.background(AllInColors.lightGrey100Color)
HStack {
Spacer()
Image("allcoinIcon")
.resizable()
.frame(width: 18, height: 18, alignment: .leading)
.padding([.top, .bottom], 10)
Text(String(viewModel.friends[1].nbCoins))
.textStyle(weight: .black, color: AllInColors.lightPurpleColor, size: 16)
.padding(.trailing, 18)
Spacer()
}
.frame(width: geometry.size.width * 0.43)
.background(AllInColors.underComponentBackgroundColor)
}
.frame(width: geometry.size.width * 0.43)
.background(AllInColors.underComponentBackgroundColor)
.frame(width: geometry.size.width * 0.43, height: 90)
.background(AllInColors.componentBackgroundColor)
.cornerRadius(27.5, corners: .topRight)
.cornerRadius(8, corners: .topLeft)
.cornerRadius(15, corners: [.bottomLeft, .bottomRight])
UserPicture(picture: viewModel.friends[1].image, username: viewModel.friends[1].username, size: 50)
.offset(x: 0, y: -50)
Text("2")
.frame(width: 20, height: 20)
.foregroundColor(.white)
.background(AllInColors.lightPurpleColor)
.cornerRadius(30)
.font(.system(size: 14))
.fontWeight(.bold)
.offset(x: 0, y: -28)
}
.frame(width: geometry.size.width * 0.43, height: 90)
.background(AllInColors.componentBackgroundColor)
.cornerRadius(27.5, corners: .topRight)
.cornerRadius(8, corners: .topLeft)
.cornerRadius(15, corners: [.bottomLeft, .bottomRight])
Image("defaultUserImage")
.resizable()
.frame(width: 60, height: 60)
.scaledToFit()
.cornerRadius(180)
.offset(x: 0, y: -50)
Text("2")
.frame(width: 23, height: 23)
.foregroundColor(.white)
.background(AllInColors.lightPurpleColor)
.cornerRadius(30)
.font(.system(size: 15))
.fontWeight(.bold)
.offset(x: 0, y: -22)
.padding(.top, 28)
}
.padding(.top, 28)
}
.padding([.leading,.trailing,.top],20)
.padding([.leading, .trailing, .top], 20)
ScrollView(showsIndicators: false){
VStack(spacing: 10) {
RankingRow(number: 3, image: "defaultUserImage", pseudo: "Lucas", allCoins: 541)
RankingRow(number: 4, image: "defaultUserImage", pseudo: "Arthur", allCoins: 542)
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) {
ForEach(viewModel.friends.indices.dropFirst(2), id: \.self) { index in
let friend = viewModel.friends[index]
RankingRow(
number: index + 1,
image: friend.image,
pseudo: friend.username,
allCoins: friend.nbCoins
)
}
}
}.padding(.top, 10)
.padding(.top, 10)
.padding(.horizontal, 20)
}
Spacer()
}
.edgesIgnoringSafeArea(.bottom).background(AllInColors.backgroundColor)
}
}
}
struct RankingView_Previews: PreviewProvider {
static var previews: some View {
RankingView(showMenu: .constant(false))
}
}

@ -9,16 +9,20 @@
/* Begin PBXBuildFile section */
120919182B56D0AE00D0FA29 /* ParticipationModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 120919172B56D0AE00D0FA29 /* ParticipationModal.swift */; };
1209191A2B56DC6C00D0FA29 /* DropDownAnswerMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 120919192B56DC6C00D0FA29 /* DropDownAnswerMenu.swift */; };
12228A212C1760D2008524F4 /* BinaryBetLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12228A202C1760D2008524F4 /* BinaryBetLine.swift */; };
12228A232C176117008524F4 /* CustomBetLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12228A222C176117008524F4 /* CustomBetLine.swift */; };
123225D92B67B46100D30BB3 /* BetEndingValidationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123225D82B67B46100D30BB3 /* BetEndingValidationView.swift */; };
123225DB2B67E41400D30BB3 /* ChoiceFinalAnswerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123225DA2B67E41400D30BB3 /* ChoiceFinalAnswerCell.swift */; };
123590B42B51792000F7AEBD /* DetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123590B32B51792000F7AEBD /* DetailsView.swift */; };
123590B62B5537E200F7AEBD /* ResultBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123590B52B5537E200F7AEBD /* ResultBanner.swift */; };
123590B82B5541BA00F7AEBD /* ParticipateButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123590B72B5541BA00F7AEBD /* ParticipateButton.swift */; };
123F31DB2C0F26E8009B6D65 /* userPicture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123F31DA2C0F26E8009B6D65 /* userPicture.swift */; };
123F31E02C11E99E009B6D65 /* asyncCachedImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123F31DF2C11E99E009B6D65 /* asyncCachedImage.swift */; };
1244EF602B4EC31E00374ABF /* HistoricBetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1244EF5F2B4EC31E00374ABF /* HistoricBetView.swift */; };
1244EF622B4EC67000374ABF /* ReviewCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1244EF612B4EC67000374ABF /* ReviewCard.swift */; };
129D051D2B6E7FF0003D3E08 /* OddCapsule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 129D051C2B6E7FF0003D3E08 /* OddCapsule.swift */; };
12A9E4942C07132600AB8677 /* EmptyInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12A9E4932C07132600AB8677 /* EmptyInfo.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 */; };
EC01937A2B25C12B005D81E6 /* BetCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0193792B25C12B005D81E6 /* BetCard.swift */; };
EC01937C2B25C2A8005D81E6 /* AllcoinsCounter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC01937B2B25C2A8005D81E6 /* AllcoinsCounter.swift */; };
@ -32,6 +36,7 @@
EC30770B2B24D9160060E34D /* WelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC30770A2B24D9160060E34D /* WelcomeView.swift */; };
EC30770D2B24DB7A0060E34D /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC30770C2B24DB7A0060E34D /* Extensions.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 */; };
EC60C5682B5A83FB00FFD6EF /* StubLib in Frameworks */ = {isa = PBXBuildFile; productRef = EC60C5672B5A83FB00FFD6EF /* StubLib */; };
EC650A422B25C817003AFCAD /* Friend.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC650A412B25C817003AFCAD /* Friend.swift */; };
@ -55,7 +60,6 @@
EC6B96B72B24B4CC00FC1C58 /* AllInUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC6B96B62B24B4CC00FC1C58 /* AllInUITests.swift */; };
EC6B96B92B24B4CC00FC1C58 /* AllInUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC6B96B82B24B4CC00FC1C58 /* AllInUITestsLaunchTests.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 */; };
EC6B96D52B24BE0E00FC1C58 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC6B96D42B24BE0E00FC1C58 /* MainView.swift */; };
EC6B96D82B24BF2100FC1C58 /* Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC6B96D72B24BF2100FC1C58 /* Menu.swift */; };
@ -79,6 +83,7 @@
ECB7BC702B336E28002A6654 /* RegisterViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECB7BC6F2B336E28002A6654 /* RegisterViewModel.swift */; };
ECCD244A2B4DE8010071FA9E /* Api in Frameworks */ = {isa = PBXBuildFile; productRef = ECCD24492B4DE8010071FA9E /* Api */; };
ECDDD48A2BF4A7920009223A /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = ECDDD48C2BF4A7920009223A /* Localizable.strings */; };
ECDDD48F2C034A420009223A /* ProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECDDD48E2C034A420009223A /* ProfileView.swift */; };
ECED90B52B6D9CEC00F50937 /* DailyGiftPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECED90B42B6D9CEC00F50937 /* DailyGiftPage.swift */; };
ECF872662B9266C000F9D240 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ECF872652B9266C000F9D240 /* WidgetKit.framework */; };
ECF872682B9266C000F9D240 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ECF872672B9266C000F9D240 /* SwiftUI.framework */; };
@ -146,16 +151,20 @@
120919192B56DC6C00D0FA29 /* DropDownAnswerMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropDownAnswerMenu.swift; sourceTree = "<group>"; };
122278B72B4BDE1100E632AA /* DependencyInjection.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DependencyInjection.framework; sourceTree = BUILT_PRODUCTS_DIR; };
122278B92B4BDE9500E632AA /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Package.swift; path = ../Model/Package.swift; sourceTree = "<group>"; };
12228A202C1760D2008524F4 /* BinaryBetLine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BinaryBetLine.swift; sourceTree = "<group>"; };
12228A222C176117008524F4 /* CustomBetLine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomBetLine.swift; sourceTree = "<group>"; };
123225D82B67B46100D30BB3 /* BetEndingValidationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BetEndingValidationView.swift; sourceTree = "<group>"; };
123225DA2B67E41400D30BB3 /* ChoiceFinalAnswerCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChoiceFinalAnswerCell.swift; sourceTree = "<group>"; };
123590B32B51792000F7AEBD /* DetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailsView.swift; sourceTree = "<group>"; };
123590B52B5537E200F7AEBD /* ResultBanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResultBanner.swift; sourceTree = "<group>"; };
123590B72B5541BA00F7AEBD /* ParticipateButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParticipateButton.swift; sourceTree = "<group>"; };
123F31DA2C0F26E8009B6D65 /* userPicture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = userPicture.swift; sourceTree = "<group>"; };
123F31DF2C11E99E009B6D65 /* asyncCachedImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = asyncCachedImage.swift; sourceTree = "<group>"; };
1244EF5F2B4EC31E00374ABF /* HistoricBetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoricBetView.swift; sourceTree = "<group>"; };
1244EF612B4EC67000374ABF /* ReviewCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReviewCard.swift; sourceTree = "<group>"; };
129D051C2B6E7FF0003D3E08 /* OddCapsule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OddCapsule.swift; sourceTree = "<group>"; };
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>"; };
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>"; };
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>"; };
@ -169,6 +178,7 @@
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>"; };
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>"; };
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>"; };
@ -193,7 +203,6 @@
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>"; };
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>"; };
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>"; };
@ -218,6 +227,7 @@
ECB7BC6F2B336E28002A6654 /* RegisterViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegisterViewModel.swift; sourceTree = "<group>"; };
ECDDD48B2BF4A7920009223A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
ECDDD48D2BF4A7970009223A /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = "<group>"; };
ECDDD48E2C034A420009223A /* ProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileView.swift; sourceTree = "<group>"; };
ECED90B42B6D9CEC00F50937 /* DailyGiftPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DailyGiftPage.swift; sourceTree = "<group>"; };
ECF872642B9266C000F9D240 /* AllInWidgetsExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = AllInWidgetsExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
ECF872652B9266C000F9D240 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; };
@ -270,6 +280,14 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
123F31DE2C11E979009B6D65 /* Utils */ = {
isa = PBXGroup;
children = (
123F31DF2C11E99E009B6D65 /* asyncCachedImage.swift */,
);
path = Utils;
sourceTree = "<group>";
};
EC1D15402B715A7A0094833E /* Protocols */ = {
isa = PBXGroup;
children = (
@ -305,6 +323,7 @@
EC6B969A2B24B4CC00FC1C58 /* AllIn */ = {
isa = PBXGroup;
children = (
123F31DE2C11E979009B6D65 /* Utils */,
ECF872852B93B9D900F9D240 /* AllIn.entitlements */,
EC7EF7462B87E3470022B5D9 /* QuickActions */,
ECB7BC662B2F1AAD002A6654 /* ViewModels */,
@ -361,9 +380,9 @@
EC6B96CD2B24B8A300FC1C58 /* Ressources */ = {
isa = PBXGroup;
children = (
EC6B96CE2B24B8D900FC1C58 /* Config.swift */,
EC3077082B24CF7F0060E34D /* Colors.swift */,
ECDDD48C2BF4A7920009223A /* Localizable.strings */,
EC34FDCD2C0DB46C0020A371 /* Config.swift */,
);
path = Ressources;
sourceTree = "<group>";
@ -393,6 +412,7 @@
123225D82B67B46100D30BB3 /* BetEndingValidationView.swift */,
EC17A15D2B6A955E008A8679 /* CurrentBetView.swift */,
ECED90B42B6D9CEC00F50937 /* DailyGiftPage.swift */,
ECDDD48E2C034A420009223A /* ProfileView.swift */,
);
path = Views;
sourceTree = "<group>";
@ -424,9 +444,12 @@
120919172B56D0AE00D0FA29 /* ParticipationModal.swift */,
120919192B56DC6C00D0FA29 /* DropDownAnswerMenu.swift */,
12C370472B5A5EE500CD9F0F /* BetLineLoading.swift */,
12C370492B5D5BD000CD9F0F /* ParticipationCell.swift */,
123225DA2B67E41400D30BB3 /* ChoiceFinalAnswerCell.swift */,
129D051C2B6E7FF0003D3E08 /* OddCapsule.swift */,
12A9E4932C07132600AB8677 /* EmptyInfo.swift */,
123F31DA2C0F26E8009B6D65 /* userPicture.swift */,
12228A202C1760D2008524F4 /* BinaryBetLine.swift */,
12228A222C176117008524F4 /* CustomBetLine.swift */,
);
path = Components;
sourceTree = "<group>";
@ -658,21 +681,25 @@
files = (
EC6B96CC2B24B7E500FC1C58 /* IAuthService.swift in Sources */,
ECB26A172B4073F100FE06B3 /* CreationBetViewModel.swift in Sources */,
12228A232C176117008524F4 /* CustomBetLine.swift in Sources */,
EC3077092B24CF7F0060E34D /* Colors.swift in Sources */,
EC6B969E2B24B4CC00FC1C58 /* ContentView.swift in Sources */,
EC89F7BD2B250D66003821CE /* LoginView.swift in Sources */,
EC650A442B25CDF3003AFCAD /* ParameterMenu.swift in Sources */,
120919182B56D0AE00D0FA29 /* ParticipationModal.swift in Sources */,
123F31DB2C0F26E8009B6D65 /* userPicture.swift in Sources */,
ECB26A132B406A9400FE06B3 /* BetViewModel.swift in Sources */,
EC30770F2B24FCB00060E34D /* RegisterView.swift in Sources */,
EC650A522B2794DD003AFCAD /* BetView.swift in Sources */,
EC6B969C2B24B4CC00FC1C58 /* AllInApp.swift in Sources */,
123590B42B51792000F7AEBD /* DetailsView.swift in Sources */,
ECB26A192B40744F00FE06B3 /* RankingViewModel.swift in Sources */,
123F31E02C11E99E009B6D65 /* asyncCachedImage.swift in Sources */,
EC650A502B2793D5003AFCAD /* TextCapsule.swift in Sources */,
EC650A482B25DCFF003AFCAD /* UsersPreview.swift in Sources */,
EC17A15E2B6A955E008A8679 /* CurrentBetView.swift in Sources */,
EC7EF7482B87E3E00022B5D9 /* Delegates.swift in Sources */,
12A9E4942C07132600AB8677 /* EmptyInfo.swift in Sources */,
EC650A462B25D686003AFCAD /* RankingRow.swift in Sources */,
EC01937A2B25C12B005D81E6 /* BetCard.swift in Sources */,
EC650A422B25C817003AFCAD /* Friend.swift in Sources */,
@ -680,13 +707,14 @@
ECB7BC702B336E28002A6654 /* RegisterViewModel.swift in Sources */,
EC17A1602B6A9781008A8679 /* CurrentBetViewModel.swift in Sources */,
EC650A4C2B25E9C7003AFCAD /* RankingView.swift in Sources */,
ECDDD48F2C034A420009223A /* ProfileView.swift in Sources */,
EC9464E92B7413E1004EEBD8 /* BetEndingValidationViewModel.swift in Sources */,
EC7A882B2B28D1E0004F226A /* DropDownMenu.swift in Sources */,
EC7A882D2B28D8A1004F226A /* CreationBetView.swift in Sources */,
ECED90B52B6D9CEC00F50937 /* DailyGiftPage.swift in Sources */,
EC6B96CF2B24B8D900FC1C58 /* Config.swift in Sources */,
1244EF602B4EC31E00374ABF /* HistoricBetView.swift in Sources */,
EC30770B2B24D9160060E34D /* WelcomeView.swift in Sources */,
EC34FDCE2C0DB46C0020A371 /* Config.swift in Sources */,
EC30770D2B24DB7A0060E34D /* Extensions.swift in Sources */,
EC6B96D82B24BF2100FC1C58 /* Menu.swift in Sources */,
EC650A4E2B278EDB003AFCAD /* TrendingBetCard.swift in Sources */,
@ -699,8 +727,8 @@
EC3077072B24CB840060E34D /* SplashView.swift in Sources */,
EC01937E2B25C52E005D81E6 /* TopBar.swift in Sources */,
ECA9D1CB2B2DA2320076E0EC /* DropDownFriends.swift in Sources */,
12C3704A2B5D5BD000CD9F0F /* ParticipationCell.swift in Sources */,
123590B82B5541BA00F7AEBD /* ParticipateButton.swift in Sources */,
12228A212C1760D2008524F4 /* BinaryBetLine.swift in Sources */,
EC01FCC32B56650400BB2390 /* DetailsViewModel.swift in Sources */,
ECB26A1B2B40746C00FE06B3 /* FriendsViewModel.swift in Sources */,
ECB7BC682B2F1ADF002A6654 /* LoginViewModel.swift in Sources */,

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

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

@ -11,26 +11,38 @@ import Model
public struct BetApiManager: BetDataManager {
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.url = url
}
public func getBets(withIndex index: Int, withCount count: Int, completion: @escaping ([Bet]) -> Void) {
let url = URL(string: allInApi + "bets/gets")!
public func getBets(withIndex index: Int, withCount count: Int, filters: [BetFilter] = [], completion: @escaping ([Bet]) -> Void) {
let url = URL(string: url + "bets/gets")!
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
var bets: [Bet] = []
let filterStrings = filters.map { $0.rawValue }
let jsonDictionary: [String: Any] = ["filters": filterStrings]
do {
let jsonData = try JSONSerialization.data(withJSONObject: jsonDictionary, options: [])
request.httpBody = jsonData
} catch {
print("Error creating JSON data: \(error)")
return
}
URLSession.shared.dataTask(with: request) { data, response, error in
if let data = data {
print ("ALLIN : get bets")
do {
if let httpResponse = response as? HTTPURLResponse, let jsonArray = try JSONSerialization.jsonObject(with: data, options: []) as? [[String: Any]] {
var bets: [Bet] = []
for json in jsonArray {
if let bet = FactoryApiBet().toBet(from: json) {
bets.append(bet)
@ -45,13 +57,14 @@ public struct BetApiManager: BetDataManager {
}
}.resume()
}
public func getUsers(username: String) -> [User] {
return []
}
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)
request.httpMethod = "GET"
@ -63,10 +76,36 @@ public struct BetApiManager: BetDataManager {
print ("ALLIN : get bet with id :" + id)
do {
if let httpResponse = response as? HTTPURLResponse, let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
print(httpResponse.statusCode)
print(json)
if let betDetail = FactoryApiBet().toBetDetail(from: json) {
completion(betDetail)
}
}
} 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)
}
} catch {

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

@ -8,14 +8,14 @@
import Foundation
import Model
let allInApi = "https://codefirst.iut.uca.fr/containers/AllDev-api/"
public struct UserApiManager: UserDataManager {
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.url = url
}
public func getBets(withIndex index: Int, withCount count: Int) -> [Bet] {
@ -23,7 +23,7 @@ public struct UserApiManager: UserDataManager {
}
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)
request.httpMethod = "GET"
@ -52,9 +52,41 @@ public struct UserApiManager: UserDataManager {
}.resume()
}
public func getBetsWon(completion : @escaping ([BetResultDetail])-> ()) {
let url = URL(string: url + "bets/getWon")!
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
var bets: [BetResultDetail] = []
URLSession.shared.dataTask(with: request) { data, response, error in
if let data = data {
print ("ALLIN : get bets won")
do {
if let httpResponse = response as? HTTPURLResponse, let jsonArray = try JSONSerialization.jsonObject(with: data, options: []) as? [[String: Any]] {
for json in jsonArray {
if let bet = FactoryApiBet().toBetResultDetail(from: json) {
print(bet)
bets.append(bet)
}
}
print(httpResponse.statusCode)
completion(bets)
}
} catch {
print("Error parsing JSON: \(error)")
}
}
}.resume()
}
public func addBet(bet: Bet, completion : @escaping (Int)-> ()) {
let url = URL(string: allInApi + "bets/add")!
let url = URL(string: url + "bets/add")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
@ -73,12 +105,128 @@ public struct UserApiManager: UserDataManager {
}
}
public func getFriends() -> [User] {
fatalError("Not implemented yet")
public func addFriend(username: String, completion : @escaping (Int)-> ()) {
let url = URL(string: url + "friends/add")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
let json: [String: Any] = ["username": username]
if let jsonData = try? JSONSerialization.data(withJSONObject: json, options: []){
URLSession.shared.uploadTask(with: request, from: jsonData) { data, response, error in
print ("ALLIN : Add friend")
if let httpResponse = response as? HTTPURLResponse {
print(httpResponse.statusCode)
completion(httpResponse.statusCode)
}
}.resume()
}
}
public func removeFriend(username: String, completion : @escaping (Int)-> ()) {
let url = URL(string: url + "friends/delete")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
let json: [String: Any] = ["username": username]
if let jsonData = try? JSONSerialization.data(withJSONObject: json, options: []){
URLSession.shared.uploadTask(with: request, from: jsonData) { data, response, error in
print ("ALLIN : Remove friend")
if let httpResponse = response as? HTTPURLResponse {
print(httpResponse.statusCode)
completion(httpResponse.statusCode)
}
}.resume()
}
}
public func getFriends(completion: @escaping ([User]) -> Void) {
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)
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 getUsers(withName name: String, completion: @escaping ([User]) -> Void) {
let url = URL(string: url + "friends/search/" + name)!
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 by search")
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 getGifts(completion : @escaping (Int, Int)-> ()) {
let url = URL(string: allInApi + "users/gift")!
let url = URL(string: url + "users/gift")!
var request = URLRequest(url: url)
request.httpMethod = "GET"
@ -102,19 +250,46 @@ public struct UserApiManager: UserDataManager {
}.resume()
}
public func getOldBets(withIndex index: Int, withCount count: Int, completion: @escaping ([Bet]) -> Void) {
fatalError("Not implemented yet")
public func getOldBets(withIndex index: Int, withCount count: Int, completion: @escaping ([BetResultDetail]) -> Void) {
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 ([Bet]) -> Void) {
let url = URL(string: allInApi + "bets/current")!
public func getCurrentBets(withIndex index: Int, withCount count: Int, completion: @escaping ([BetDetail]) -> Void) {
let url = URL(string: url + "bets/current")!
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
var bets: [Bet] = []
var bets: [BetDetail] = []
URLSession.shared.dataTask(with: request) { data, response, error in
if let data = data {
@ -122,8 +297,7 @@ public struct UserApiManager: UserDataManager {
do {
if let httpResponse = response as? HTTPURLResponse, let jsonArray = try JSONSerialization.jsonObject(with: data, options: []) as? [[String: Any]] {
for json in jsonArray {
print(json)
if let bet = FactoryApiBet().toBet(from: json) {
if let bet = FactoryApiBet().toBetDetail(from: json) {
bets.append(bet)
}
}
@ -138,7 +312,7 @@ public struct UserApiManager: UserDataManager {
}
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)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
@ -162,24 +336,18 @@ public struct UserApiManager: UserDataManager {
}
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)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
let json: [String: String] = [
"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)
if let httpResponse = response as? HTTPURLResponse {
print(httpResponse.statusCode)
}
}.resume()
}
URLSession.shared.uploadTask(with: request, from: responseBet.data(using: .utf8)) { data, response, error in
print ("ALLIN : add response " + responseBet + " for the bet Id " + id)
if let httpResponse = response as? HTTPURLResponse {
print(httpResponse.statusCode)
}
}.resume()
}
}

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

@ -8,7 +8,7 @@
import Foundation
/// 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.
public private(set) var response: String
@ -41,4 +41,19 @@ public class AnswerDetail: ObservableObject, Identifiable, Codable {
self.odds = odds
}
public static func == (lhs: AnswerDetail, rhs: AnswerDetail) -> Bool {
return lhs.response == rhs.response &&
lhs.totalStakes == rhs.totalStakes &&
lhs.totalParticipants == rhs.totalParticipants &&
lhs.highestStake == rhs.highestStake &&
lhs.odds == rhs.odds
}
public func hash(into hasher: inout Hasher) {
hasher.combine(response)
hasher.combine(totalStakes)
hasher.combine(totalParticipants)
hasher.combine(highestStake)
hasher.combine(odds)
}
}

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

@ -8,7 +8,9 @@
import Foundation
public protocol BetDataManager {
func getBets(withIndex index: Int, withCount count: Int, completion: @escaping ([Bet]) -> Void)
func getBets(withIndex index: Int, withCount count: Int, filters: [BetFilter], completion: @escaping ([Bet]) -> Void)
func getUsers(username: String) -> [User]
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]
/// 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
///
@ -29,18 +41,19 @@ public class BetDetail: ObservableObject {
/// - answers: Details about the answers available for the bet.
/// - participations: List of user participations in the bet.
/// - finalAnswer: The final answer selected for the bet, if available.
public init(bet: Bet, answers: [AnswerDetail], participations: [Participation], finalAnswer: String? = nil) {
public init(bet: Bet, answers: [AnswerDetail], participations: [Participation], wonParticipation: Participation? = nil, userParticipation: Participation? = nil) {
self.bet = bet
self.answers = answers
self.participations = participations
self.finalAnswer = finalAnswer
self.wonParticipation = wonParticipation
self.userParticipation = userParticipation
}
/// Updates the final answer selected for the bet.
///
/// - Parameter newFinalAnswer: The new final answer for the bet.
public func updateFinalAnswer(newFinalAnswer: String) {
self.finalAnswer = newFinalAnswer
public func updateFinalAnswer(wonParticipation: Participation) {
self.wonParticipation = wonParticipation
}
/// 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.
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)
}
}

@ -0,0 +1,16 @@
//
// BetFilter.swift
//
//
// Created by Lucas Delanier on 27/05/2024.
//
import Foundation
public enum BetFilter: String, Codable {
case inProgress = "IN_PROGRESS"
case isPublic = "PUBLIC"
case isInvitation = "INVITATION"
case isFinished = "FINISHED"
}

@ -0,0 +1,14 @@
//
// FriendStatus.swift
//
//
// Created by Emre on 28/05/2024.
//
import Foundation
public enum FriendStatus: String, Codable {
case friend = "FRIEND"
case requested = "REQUESTED"
case notFriend = "NOT_FRIEND"
}

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

@ -21,9 +21,21 @@ public struct Manager {
completion(status)
}
}
public func addFriend(withUsername username: String, completion : @escaping (Int)-> ()) {
userDataManager.addFriend(username: username) { status in
completion(status)
}
}
public func getBets(withIndex index: Int, withCount count: Int, completion: @escaping ([Bet]) -> Void) {
betDataManager.getBets(withIndex: index, withCount: count) { bets in
public func removeFriend(withUsername username: String, completion : @escaping (Int)-> ()) {
userDataManager.removeFriend(username: username) { status in
completion(status)
}
}
public func getBets(withIndex index: Int, withCount count: Int, filters: [BetFilter], completion: @escaping ([Bet]) -> Void) {
betDataManager.getBets(withIndex: index, withCount: count, filters: filters) { bets in
completion(bets)
}
}
@ -34,19 +46,49 @@ public struct Manager {
}
}
public func getBetsWon(completion: @escaping ([BetResultDetail]) -> Void) {
userDataManager.getBetsWon() { bets in
completion(bets)
}
}
public func getBet(withId id: String, completion: @escaping (BetDetail) -> Void) {
betDataManager.getBet(withId: id) { bet in
completion(bet)
}
}
public func getHistoricBets(withIndex index: Int, withCount count: Int, completion: @escaping ([Bet]) -> Void) {
public func getFriends(completion: @escaping ([User]) -> Void) {
userDataManager.getFriends() { users in
completion(users)
}
}
public func getRequests(completion: @escaping ([User]) -> Void) {
userDataManager.getRequests() { users in
completion(users)
}
}
public func getUsers(withName name: String, completion: @escaping ([User]) -> Void) {
userDataManager.getUsers(withName: name) { users in
completion(users)
}
}
public func getHistoricBets(withIndex index: Int, withCount count: Int, completion: @escaping ([BetResultDetail]) -> Void) {
userDataManager.getOldBets(withIndex: index, withCount: count) { bets in
completion(bets)
}
}
public func getCurrentBets(withIndex index: Int, withCount count: Int, completion: @escaping ([Bet]) -> Void) {
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) {
userDataManager.getCurrentBets(withIndex: index, withCount: count) { bets in
completion(bets)
}

@ -9,6 +9,7 @@ import Foundation
/// A subclass of Bet that represents a bet on a match between two teams.
public class MatchBet: Bet {
/// The name of the first team involved in the match.
public var nameTeam1: String
@ -32,13 +33,12 @@ public class MatchBet: Bet {
/// - status: The current status of the match bet.
/// - invited: List of users who are invited to participate in the match bet.
/// - author: The user who created the match bet.
/// - registered: List of users who have registered for the match bet.
/// - nameTeam1: The name of the first team involved in the match.
/// - nameTeam2: The name of the second team involved in the match.
public init(id: String, theme: String, phrase: String, endRegisterDate: Date, endBetDate: Date, 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.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
@ -52,13 +52,12 @@ public class MatchBet: Bet {
/// - status: The current status of the match bet.
/// - invited: List of users who are invited to participate in the match bet.
/// - author: The user who created the match bet.
/// - registered: List of users who have registered for the match bet.
/// - nameTeam1: The name of the first team involved in the match.
/// - nameTeam2: The name of the second team involved in the match.
public init(theme: String, phrase: String, endRegisterDate: Date, endBetDate: Date, 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.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 {

@ -8,9 +8,7 @@
import Foundation
/// A class representing a user's participation in a bet.
public class Participation: ObservableObject, Codable, Identifiable {
public let id: String
public class Participation: ObservableObject, Codable, Identifiable, Hashable {
/// The amount of stake in the bet.
public private(set) var stake: Int
@ -28,15 +26,29 @@ public class Participation: ObservableObject, Codable, Identifiable {
///
/// - Parameters:
/// - stake: The amount of stake in the bet.
/// - date: The date and time when the participation occurred.
/// - response: The response or outcome of the participation.
/// - user: The user who participated in the bet.
/// - answer: The response or outcome of the participation.
/// - username: The user who participated in the bet.
/// - betId: The unique identifier of the bet.
public init(id: String, stake: Int, answer: String, username: String, betId: String) {
self.id = id
public init(stake: Int, answer: String, username: String, betId: String) {
self.stake = stake
self.answer = answer
self.username = username
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
/// A struct representing a user with details such as username, email, number of coins, and friends.
public struct User: Codable {
public struct User: Codable, Hashable, Identifiable {
public var id: String
/// The username of the user.
public private(set) var username: String
@ -19,15 +21,41 @@ public struct User: Codable {
/// The number of coins associated with the user.
public var nbCoins: Int
/// The friendship status with the main user.
public var friendStatus: FriendStatus?
/// The user's profile image.
public var image: String?
/// The number of bets made by the user.
public var nbBets: Int
/// The number of friends the user has.
public var nbFriends: Int
/// The user's best win.
public var bestWin: Int
/// Custom constructor to initialize a User instance.
///
/// - Parameters:
/// - username: The username of the user.
/// - email: The email address of the user.
/// - nbCoins: The number of coins associated with the user.
public init(username: String, email: String, nbCoins: Int) {
/// - friendStatus: The friendship status with the main user.
/// - 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.email = email
self.nbCoins = nbCoins
self.friendStatus = friendStatus
self.image = image
self.nbBets = nbBets
self.nbFriends = nbFriends
self.bestWin = bestWin
}
}

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

@ -12,7 +12,7 @@ public struct BetStubManager: BetDataManager {
public init() {}
public func getBets(withIndex index: Int, withCount count: Int, completion: @escaping ([Bet]) -> Void) {
public func getBets(withIndex index: Int, withCount count: Int, filters: [BetFilter] = [], completion: @escaping ([Bet]) -> Void) {
completion(Stub.shared.bets)
}
@ -29,6 +29,10 @@ public struct BetStubManager: BetDataManager {
}
}
public func getPopularBet(completion: @escaping (Bet) -> Void) {
}
public func getABetDetail() -> BetDetail{
Stub.shared.betsDetail.first!
}

@ -21,13 +21,13 @@ struct Stub {
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)
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)
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)
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.",
endRegisterDate: Date().addingTimeInterval(-86400),
endBetDate: Date().addingTimeInterval(172800),
isPublic: true,
isPrivate: false,
status: .inProgress,
invited: [],
author: "Lucas",
registered: [user2]
author: "Lucas"
)
self.bets.append(bet1)
@ -48,11 +47,10 @@ struct Stub {
phrase: "Le plat préféré du jury sera une recette végétarienne.",
endRegisterDate: Date().addingTimeInterval(172800),
endBetDate: Date().addingTimeInterval(259200),
isPublic: false,
isPrivate: false,
status: .inProgress,
invited: [user3],
author: "Lucas",
registered: [user2]
invited: [],
author: "Lucas"
)
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 ?",
endRegisterDate: Date().addingTimeInterval(259200),
endBetDate: Date().addingTimeInterval(345600),
isPublic: true,
isPrivate: true,
status: .finished,
invited: [],
author: "Lucas",
registered: [user2, user1, user3]
author: "Lucas"
)
self.bets.append(bet3)
@ -74,11 +71,10 @@ struct Stub {
phrase: "Le film favori des critiques remportera-t-il le prix du meilleur film ?",
endRegisterDate: Date().addingTimeInterval(345600),
endBetDate: Date().addingTimeInterval(432000),
isPublic: false,
isPrivate: false,
status: .finished,
invited: [user1],
author: "Lucase",
registered: [user3]
invited: [],
author: "Lucase"
)
self.bets.append(bet4)
@ -87,15 +83,15 @@ struct Stub {
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(id: UUID().uuidString, 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(id: UUID().uuidString, stake: 320, answer: "OUI", username: user3.username, betId: "3"))
self.betsDetail[0].addParticipation(newParticipation: Participation(stake: 120, answer: "OUI", username: user1.username, betId: "1"))
self.betsDetail[0].addParticipation(newParticipation: Participation(stake: 20, answer: "NON", username: user2.username, betId: "2"))
self.betsDetail[0].addParticipation(newParticipation: Participation(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) {
let newBetDetail = BetDetail(bet: bet, answers: [], participations: [], finalAnswer: "test")
let newBetDetail = BetDetail(bet: bet, answers: [], participations: [])
self.betsDetail.append(newBetDetail)
}
}

Loading…
Cancel
Save