diff --git a/Documentation/Images/Banner-AllIn.png b/Documentation/Images/Banner-AllIn.png new file mode 100644 index 0000000..f64e77f Binary files /dev/null and b/Documentation/Images/Banner-AllIn.png differ diff --git a/README.md b/README.md index 7dd5a84..d36de68 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,55 @@ -# Swift +
-Client IOS \ No newline at end of file + + +--- + +  ![Swift](https://img.shields.io/badge/Swift-F05138.svg?style=for-the-badge&logo=Swift&logoColor=white) +  ![iOS](https://img.shields.io/badge/iOS-000000.svg?style=for-the-badge&logo=Apple&logoColor=white) + +--- + +[Présentation](#apple---all-in) | [Répartition du dépôt](#répartition-du-gitlab) | [Structures](#structures) | [Technologies](#technologies) | [Outils](#outils) | [Wiki](https://codefirst.iut.uca.fr/git/AllDev/Gestion_de_projet/wiki) + +
+ +### Apple - ALL IN! + +**Contexte** : Application Swift et SwiftUI pour le projet universitaire de troisième année (B.U.T Informatique de Clermont-Ferrand) intitulé *All In*. +
+ +**Description** : Ce dépôt contient l'ensemble du code pour la partie client iOS de l'application *ALL IN*. +
+ +# Répartition du GitLab + +[**Sources**](Sources) : **Code de l'application** + +[**Documentation**](Documentation) : **Documentation de l'application** + + +# Structures + +- MVVM + + + +# Technologies + + + +Pour réaliser l'interface visuelle, nous avons opté pour **SwiftUI** du fait qu'elle permet de réaliser des interfaces utilisateurs complexes de manière élégante. Le framework est récent, mis à jour régulièrement, et facile à prendre en main pour le développement. + +# Outils + +Pour la partie API, nous avons utilisé plusieurs outils : + +- UserDefaults + +Pour stocker le token localement, nous utilisons l'outil fourni par SwiftUI qui est UserDefaults, afin de réaliser une authentification automatique lorsque le client ouvre l'application, en récupérant son token lors de la précédente connexion. + +
+ +© AllDev - Apple + +
\ No newline at end of file diff --git a/Sources/AllIn.xcworkspace/contents.xcworkspacedata b/Sources/AllIn.xcworkspace/contents.xcworkspacedata index 50d956e..74c136d 100644 --- a/Sources/AllIn.xcworkspace/contents.xcworkspacedata +++ b/Sources/AllIn.xcworkspace/contents.xcworkspacedata @@ -2,16 +2,13 @@ - - + location = "group:StubLib"> + location = "group:Api"> + location = "group:AllInApp/AllInApp.xcodeproj"> diff --git a/Sources/AllInApp/AllIn/AllInApp.swift b/Sources/AllInApp/AllIn/AllInApp.swift index f79fa97..374a309 100644 --- a/Sources/AllInApp/AllIn/AllInApp.swift +++ b/Sources/AllInApp/AllIn/AllInApp.swift @@ -8,7 +8,6 @@ import SwiftUI import DependencyInjection import Model -import ViewModel @main struct AllInApp: App { diff --git a/Sources/AllInApp/AllIn/Assets.xcassets/CloseiconRounded.imageset/Contents.json b/Sources/AllInApp/AllIn/Assets.xcassets/closeIcon.imageset/Contents.json similarity index 100% rename from Sources/AllInApp/AllIn/Assets.xcassets/CloseiconRounded.imageset/Contents.json rename to Sources/AllInApp/AllIn/Assets.xcassets/closeIcon.imageset/Contents.json diff --git a/Sources/AllInApp/AllIn/Assets.xcassets/CloseiconRounded.imageset/Exclude (1).png b/Sources/AllInApp/AllIn/Assets.xcassets/closeIcon.imageset/Exclude (1).png similarity index 100% rename from Sources/AllInApp/AllIn/Assets.xcassets/CloseiconRounded.imageset/Exclude (1).png rename to Sources/AllInApp/AllIn/Assets.xcassets/closeIcon.imageset/Exclude (1).png diff --git a/Sources/AllInApp/AllIn/Components/AllcoinsCounter.swift b/Sources/AllInApp/AllIn/Components/AllcoinsCounter.swift index e5771c1..8d2a444 100644 --- a/Sources/AllInApp/AllIn/Components/AllcoinsCounter.swift +++ b/Sources/AllInApp/AllIn/Components/AllcoinsCounter.swift @@ -8,8 +8,8 @@ import SwiftUI struct AllcoinsCounter: View { - var backgroundColor: Color = AllInColors.whiteColor - var foreGroundColor: Color = AllInColors.primaryColor + var backgroundColor: Color = .white + var foregroundColor: Color = AllInColors.primaryColor var body: some View { HStack(alignment: .center) { Image("allcoinIcon") @@ -17,7 +17,7 @@ struct AllcoinsCounter: View { .frame(width: 17, height: 17, alignment: .leading) Text(String(AppStateContainer.shared.user?.nbCoins ?? 0)) .fontWeight(.black) - .foregroundColor(foreGroundColor) + .foregroundColor(foregroundColor) } .frame(width: 90, height: 40) .background(backgroundColor) diff --git a/Sources/AllInApp/AllIn/Components/BetCard.swift b/Sources/AllInApp/AllIn/Components/BetCard.swift index 653f9a9..eac1299 100644 --- a/Sources/AllInApp/AllIn/Components/BetCard.swift +++ b/Sources/AllInApp/AllIn/Components/BetCard.swift @@ -6,26 +6,34 @@ // import SwiftUI +import Model 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("proposé par Lucas").font(.system(size: 10)).foregroundColor(AllInColors.grey800Color) + Text("proposé par " + bet.author.username.capitalized) + .font(.system(size: 10)) + .foregroundColor(AllInColors.grey800Color) } - Text("Etudes").font(.system(size: 15)).foregroundColor(AllInColors.grey800Color) - Text("Emre va réussir son TP de CI/CD mercredi?").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("Commence le").font(.system(size: 15)).foregroundColor(AllInColors.grey800Color) - TextCapsule() - TextCapsule() + TextCapsule(date: bet.endRegisterDate) Spacer() - } } .frame(width: .infinity) @@ -36,33 +44,41 @@ struct BetCard: View { HStack{ Spacer() UsersPreview() - Text(" 4 joueurs en attente").font(.system(size: 15)).foregroundColor(AllInColors.grey800Color).fontWeight(.medium) - + Text(String(bet.registered.count) + " joueurs en attente") + .font(.system(size: 15)) + .foregroundColor(AllInColors.grey800Color) + .fontWeight(.medium) Spacer() }.padding(0) ParticipateButton(isOpen: $showParticipate).padding(.top, 5) - } .frame(width: .infinity) .padding(.all,8) .background(AllInColors.underComponentBackgroundColor) .cornerRadius(20, corners: [.bottomLeft,.bottomRight]) .border(width: 1, edges: [.top], color: AllInColors.delimiterGrey) - }.onTapGesture { + } + .onTapGesture { showDetails.toggle() - }.fullScreenCover(isPresented: $showDetails) { - DetailsView(isModalPresented: $showDetails) } - + .fullScreenCover(isPresented: $showDetails) { + DetailsView(isModalPresented: $showDetails, id: bet.id) + } } - - } struct BetCard_Previews: PreviewProvider { static var previews: some View { - BetCard() - .preferredColorScheme(.dark) + BetCard(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), + totalStakes: 100, + isPublic: true, + invited: [], + author: User(username: "Imri", email: "emre.kartal@etu.uca.fr", nbCoins: 75, friends: []), + registered: [])) + .preferredColorScheme(.dark) } } diff --git a/Sources/AllInApp/AllIn/Components/ParticipationModal.swift b/Sources/AllInApp/AllIn/Components/ParticipationModal.swift index 48b4f6d..da30d02 100644 --- a/Sources/AllInApp/AllIn/Components/ParticipationModal.swift +++ b/Sources/AllInApp/AllIn/Components/ParticipationModal.swift @@ -25,7 +25,7 @@ struct ParticipationModal: View { HStack{ Text("Faites vos paris").font(.system(size: 18)).foregroundColor(AllInColors.blackTitleColor).fontWeight(.semibold) Spacer() - AllcoinsCounter(backgroundColor: AllInColors.purpleAccentColor, foreGroundColor: AllInColors.whiteColor) + AllcoinsCounter(backgroundColor: AllInColors.purpleAccentColor, foregroundColor: AllInColors.whiteColor) } .padding(.leading, 15) VStack(alignment: .leading){ diff --git a/Sources/AllInApp/AllIn/Components/RecapBetCard.swift b/Sources/AllInApp/AllIn/Components/RecapBetCard.swift index 06c8203..440275c 100644 --- a/Sources/AllInApp/AllIn/Components/RecapBetCard.swift +++ b/Sources/AllInApp/AllIn/Components/RecapBetCard.swift @@ -32,8 +32,7 @@ struct RecapBetCard: View { Text("Fini le ") .font(.system(size: 15)) .foregroundColor(AllInColors.grey800Color) - TextCapsule() - TextCapsule() + TextCapsule(date: Date()) Spacer() } } @@ -108,7 +107,7 @@ struct RecapBetCard: View { .onTapGesture { showDetails.toggle() }.fullScreenCover(isPresented: $showDetails) { - DetailsView(isModalPresented: $showDetails) + DetailsView(isModalPresented: $showDetails, id: "1") } .gesture( LongPressGesture(minimumDuration: 0.5) diff --git a/Sources/AllInApp/AllIn/Components/ReviewCard.swift b/Sources/AllInApp/AllIn/Components/ReviewCard.swift index 0859507..2798c36 100644 --- a/Sources/AllInApp/AllIn/Components/ReviewCard.swift +++ b/Sources/AllInApp/AllIn/Components/ReviewCard.swift @@ -24,8 +24,7 @@ struct ReviewCard: View { Text("Emre va réussir son TP de CI/CD mercredi?").font(.system(size: 20)).fontWeight(.bold) HStack{ Text("Fini le").font(.system(size: 15)).foregroundColor(AllInColors.grey800Color) - TextCapsule() - TextCapsule() + TextCapsule(date: Date()) Spacer() } @@ -38,14 +37,14 @@ struct ReviewCard: View { HStack(){ Spacer() Text(amountBetted.description) - .foregroundColor(AllInColors.whiteColor) + .foregroundColor(.white) .font(.system(size: 25)) .fontWeight(.bold) Image("allcoinWhiteIcon") .resizable() .frame(width: 20, height: 20, alignment: .bottom) Text(isAWin ? "Gagnés!" : "Perdus!") - .foregroundColor(AllInColors.whiteColor) + .foregroundColor(.white) .font(.system(size: 25)) .fontWeight(.bold) Spacer() @@ -71,7 +70,7 @@ struct ReviewCard: View { .onTapGesture { showDetails.toggle() }.fullScreenCover(isPresented: $showDetails) { - DetailsView(isModalPresented: $showDetails) + DetailsView(isModalPresented: $showDetails, id: "1") } } } diff --git a/Sources/AllInApp/AllIn/Components/TextCapsule.swift b/Sources/AllInApp/AllIn/Components/TextCapsule.swift index ad556f8..11ec010 100644 --- a/Sources/AllInApp/AllIn/Components/TextCapsule.swift +++ b/Sources/AllInApp/AllIn/Components/TextCapsule.swift @@ -8,21 +8,53 @@ import SwiftUI struct TextCapsule: View { + var date: Date + + private var formattedDate: String { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "dd MMM" + return dateFormatter.string(from: date) + } + + private var formattedTime: String { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "HH:mm" + return dateFormatter.string(from: date) + } + var body: some View { - Text("12 sept.").font(.system(size: 15)) - .foregroundColor(AllInColors.lightPurpleColor) - .fontWeight(.bold) - .padding([.leading,.trailing],10) - .padding([.top,.bottom], 5).background(AllInColors.underComponentBackgroundColor) - .clipShape(Capsule()) - .overlay(RoundedRectangle(cornerRadius: 20) - .stroke(AllInColors.delimiterGrey, lineWidth: 1) - ) + HStack { + Text(formattedDate) + .font(.system(size: 15)) + .foregroundColor(AllInColors.lightPurpleColor) + .fontWeight(.bold) + .padding([.leading, .trailing], 10) + .padding([.top, .bottom], 5) + .background(AllInColors.underComponentBackgroundColor) + .clipShape(Capsule()) + .overlay( + RoundedRectangle(cornerRadius: 20) + .stroke(AllInColors.delimiterGrey, lineWidth: 1) + ) + + Text(formattedTime) + .font(.system(size: 15)) + .foregroundColor(AllInColors.lightPurpleColor) + .fontWeight(.bold) + .padding([.leading, .trailing], 10) + .padding([.top, .bottom], 5) + .background(AllInColors.underComponentBackgroundColor) + .clipShape(Capsule()) + .overlay( + RoundedRectangle(cornerRadius: 20) + .stroke(AllInColors.delimiterGrey, lineWidth: 1) + ) + } } } struct TextCapsule_Previews: PreviewProvider { static var previews: some View { - TextCapsule() + TextCapsule(date: Date()) } } diff --git a/Sources/AllInApp/AllIn/Ressources/Config.swift b/Sources/AllInApp/AllIn/Ressources/Config.swift index e235a9a..d734b64 100644 --- a/Sources/AllInApp/AllIn/Ressources/Config.swift +++ b/Sources/AllInApp/AllIn/Ressources/Config.swift @@ -8,5 +8,5 @@ import Foundation struct Config { - static let allInApi = "https://codefirst.iut.uca.fr/containers/AllDev-api" + static let allInApi = "https://codefirst.iut.uca.fr/containers/AllDev-api/" } diff --git a/Sources/AllInApp/AllIn/Services/AuthService.swift b/Sources/AllInApp/AllIn/Services/AuthService.swift index 1a7ee71..df4b7e6 100644 --- a/Sources/AllInApp/AllIn/Services/AuthService.swift +++ b/Sources/AllInApp/AllIn/Services/AuthService.swift @@ -7,49 +7,52 @@ import Foundation import Model -import ViewModel import DependencyInjection import Api import StubLib class AuthService: IAuthService { - public func login(login: String, password: String, completion : @escaping (Int)-> ()) { + public func login(login: String, password: String, completion: @escaping (Int) -> ()) { let url = URL(string: Config.allInApi + "users/login")! var request = URLRequest(url: url) request.httpMethod = "POST" request.setValue("application/json", forHTTPHeaderField: "Content-Type") - + let json = [ "login": login.lowercased(), "password": password, ] - - if let jsonData = try? JSONSerialization.data(withJSONObject: json, options: []){ + + if let jsonData = try? JSONSerialization.data(withJSONObject: json, options: []) { URLSession.shared.uploadTask(with: request, from: jsonData) { data, response, error in - print ("ALLIN : Process LOGIN") + print("ALLIN: Process LOGIN") if let httpResponse = response as? HTTPURLResponse { if httpResponse.statusCode == 200 { if let data = data, let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any], let token = json["token"] as? String { - AppStateContainer.shared.authenticationRefresh = token; + AppStateContainer.shared.authenticationRefresh = token self.initializeUser(withToken: token) { status in + print(status) if status != 200 { completion(status) - AppStateContainer.shared.authenticationRefresh = nil; + AppStateContainer.shared.authenticationRefresh = nil } else { self.initManagerVM(token: token) + completion(httpResponse.statusCode) } } } + } else { + completion(httpResponse.statusCode) } - completion(httpResponse.statusCode) } }.resume() } } + func register(username: String, email: String, password: String, completion : @escaping (Int)-> ()) { let url = URL(string: Config.allInApi + "/users/register")! @@ -60,8 +63,7 @@ class AuthService: IAuthService { let json = [ "email": email.lowercased(), "username": username.lowercased(), - "password": password, - "nbCoins": "0" + "password": password ] if let jsonData = try? JSONSerialization.data(withJSONObject: json, options: []){ @@ -79,11 +81,13 @@ class AuthService: IAuthService { AppStateContainer.shared.authenticationRefresh = nil; } else { self.initManagerVM(token: token) + completion(httpResponse.statusCode) } } } + } else { + completion(httpResponse.statusCode) } - completion(httpResponse.statusCode) } }.resume() } @@ -119,12 +123,14 @@ class AuthService: IAuthService { } private func initializeUser(withToken token: String, completion: @escaping (Int) -> ()) { + let url = URL(string: Config.allInApi + "users/token")! 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, let httpResponse = response as? HTTPURLResponse { @@ -144,7 +150,7 @@ class AuthService: IAuthService { } private func initManagerVM(token: String) { - DependencyInjection.shared.addSingleton(ManagerVM.self, ManagerVM(withModel: Manager(withBetDataManager: BetStubManager(), withUserDataManager: UserApiManager(withUserToken: token)))) + DependencyInjection.shared.addSingleton(Manager.self, Manager(withBetDataManager: BetStubManager(), withUserDataManager: UserApiManager(withUserToken: token))) } } diff --git a/Sources/AllInApp/AllIn/ViewModels/BetViewModel.swift b/Sources/AllInApp/AllIn/ViewModels/BetViewModel.swift index 522af33..e378534 100644 --- a/Sources/AllInApp/AllIn/ViewModels/BetViewModel.swift +++ b/Sources/AllInApp/AllIn/ViewModels/BetViewModel.swift @@ -7,29 +7,24 @@ import Foundation import DependencyInjection -import ViewModel +import Model +import Combine class BetViewModel: ObservableObject { - @Inject var manager: ManagerVM + @Inject var manager: Manager + @Published private(set) var bets: [Bet] = [] + init() { getItems() } func getItems() { - - } - - func deleteItem(indexSet: IndexSet) { - + manager.getBets(withIndex: 0, withCount: 20) { bets in + self.bets = bets + } } - func moveltem(from: IndexSet, to: Int) { - - } - - func addItem(title: String) { - - } + private var cancellables: Set = [] } diff --git a/Sources/AllInApp/AllIn/ViewModels/CreationBetViewModel.swift b/Sources/AllInApp/AllIn/ViewModels/CreationBetViewModel.swift index 4adb521..bc7b7b0 100644 --- a/Sources/AllInApp/AllIn/ViewModels/CreationBetViewModel.swift +++ b/Sources/AllInApp/AllIn/ViewModels/CreationBetViewModel.swift @@ -8,11 +8,11 @@ import Foundation import SwiftUI import DependencyInjection -import ViewModel +import Model class CreationBetViewModel: ObservableObject { - @Inject var manager: ManagerVM + @Inject var manager: Manager @Published var theme: String = "" @Published var description: String = "" @Published var isPublic = true diff --git a/Sources/AllInApp/AllIn/ViewModels/DetailsViewModel.swift b/Sources/AllInApp/AllIn/ViewModels/DetailsViewModel.swift new file mode 100644 index 0000000..e6980e6 --- /dev/null +++ b/Sources/AllInApp/AllIn/ViewModels/DetailsViewModel.swift @@ -0,0 +1,30 @@ +// +// DetailsViewModel.swift +// AllIn +// +// Created by Emre on 16/01/2024. +// + +import Foundation +import SwiftUI +import DependencyInjection +import Model + +class DetailsViewModel: ObservableObject { + + @Inject var manager: Manager + var id: String + + @Published var betDetail: BetDetail? + + init(id: String) { + self.id = id + getItem(withId: id) + } + + func getItem(withId id: String) { + manager.getBet(withId: id) { bet in + self.betDetail = bet + } + } +} diff --git a/Sources/AllInApp/AllIn/ViewModels/FriendsViewModel.swift b/Sources/AllInApp/AllIn/ViewModels/FriendsViewModel.swift index a98dfbf..294ab23 100644 --- a/Sources/AllInApp/AllIn/ViewModels/FriendsViewModel.swift +++ b/Sources/AllInApp/AllIn/ViewModels/FriendsViewModel.swift @@ -7,11 +7,11 @@ import Foundation import DependencyInjection -import ViewModel +import Model class FriendsViewModel: ObservableObject { - @Inject var manager: ManagerVM + @Inject var manager: Manager init() { getItems() diff --git a/Sources/AllInApp/AllIn/ViewModels/HistoricBetViewModel.swift b/Sources/AllInApp/AllIn/ViewModels/HistoricBetViewModel.swift new file mode 100644 index 0000000..480c7a1 --- /dev/null +++ b/Sources/AllInApp/AllIn/ViewModels/HistoricBetViewModel.swift @@ -0,0 +1,24 @@ +// +// HistoricBetViewModel.swift +// AllIn +// +// Created by Emre on 16/01/2024. +// + +import Foundation +import SwiftUI +import DependencyInjection +import Model + +class HistoricBetViewModel: ObservableObject { + + @Inject var manager: Manager + + init() { + getItems() + } + + func getItems() { + + } +} diff --git a/Sources/AllInApp/AllIn/ViewModels/LoginViewModel.swift b/Sources/AllInApp/AllIn/ViewModels/LoginViewModel.swift index f0e465f..2d5e698 100644 --- a/Sources/AllInApp/AllIn/ViewModels/LoginViewModel.swift +++ b/Sources/AllInApp/AllIn/ViewModels/LoginViewModel.swift @@ -23,10 +23,6 @@ class LoginViewModel: ObservableObject { func login() { - #if DEBUG - self.onLoginSuccess() - #endif - guard checkAndSetError(forLogin: true, forPassword: true) else { return } diff --git a/Sources/AllInApp/AllIn/ViewModels/RankingViewModel.swift b/Sources/AllInApp/AllIn/ViewModels/RankingViewModel.swift index d9bfcbd..e321f91 100644 --- a/Sources/AllInApp/AllIn/ViewModels/RankingViewModel.swift +++ b/Sources/AllInApp/AllIn/ViewModels/RankingViewModel.swift @@ -7,11 +7,11 @@ import Foundation import DependencyInjection -import ViewModel +import Model class RankingViewModel: ObservableObject { - @Inject var manager: ManagerVM + @Inject var manager: Manager init() { getItems() diff --git a/Sources/AllInApp/AllIn/Views/BetView.swift b/Sources/AllInApp/AllIn/Views/BetView.swift index 8aac2a4..9f28940 100644 --- a/Sources/AllInApp/AllIn/Views/BetView.swift +++ b/Sources/AllInApp/AllIn/Views/BetView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import Model struct BetView: View { @@ -14,59 +15,60 @@ struct BetView: View { @State var showingSheet: Bool = false var body: some View { - ZStack(){ - - VStack(alignment: .center, spacing: 0) { - - - TopBar(showMenu: self.$showMenu) - ScrollView(showsIndicators: false) { - LazyVStack(alignment: .leading, spacing: 0, pinnedViews: [.sectionHeaders]) { - - TrendingBetCard().padding(.top,25).padding([.leading,.trailing],25) - - Section { - VStack(spacing: 20){ - BetCard() - Button("Show Sheet") { - showingSheet.toggle() - } - + + VStack(alignment: .center, spacing: 0) { + TopBar(showMenu: self.$showMenu) + ScrollView(showsIndicators: false) { + LazyVStack(alignment: .leading, spacing: 0, pinnedViews: [.sectionHeaders]) { + + TrendingBetCard().padding(.top,25).padding([.leading,.trailing],25) + + Section { + VStack(spacing: 20){ + ForEach(viewModel.bets, id: \.id) { (bet: Bet) in + BetCard(bet: bet) } - .padding([.leading,.trailing],25) - - } header: { - ZStack{ - AllInColors.fadeInGradiantCard - ScrollView(.horizontal,showsIndicators: false){ - HStack{ - ChoiceCapsule() - ChoiceCapsule() - ChoiceCapsule() - ChoiceCapsule() - ChoiceCapsule() - ChoiceCapsule() - ChoiceCapsule() - } - .padding(.leading,25) - .padding([.top,.bottom],15) + Button("Show Sheet") { + showingSheet.toggle() + } + } + .padding([.leading,.trailing],25) + + } header: { + ZStack{ + AllInColors.fadeInGradiantCard + ScrollView(.horizontal,showsIndicators: false){ + HStack{ + ChoiceCapsule() + ChoiceCapsule() + ChoiceCapsule() + ChoiceCapsule() + ChoiceCapsule() + ChoiceCapsule() + ChoiceCapsule() } + .padding(.leading,25) + .padding([.top,.bottom],15) } } } } - .sheet(isPresented: $showingSheet) { - WinModal() - } - Spacer() } - .edgesIgnoringSafeArea(.bottom) - .background(AllInColors.backgroundColor) - + .refreshable { + viewModel.getItems() + } + .sheet(isPresented: $showingSheet) { + WinModal() + } + Spacer() } + .edgesIgnoringSafeArea(.bottom) + .background(AllInColors.backgroundColor) + } } + struct BetView_Previews: PreviewProvider { static var previews: some View { BetView(showMenu: .constant(false)) diff --git a/Sources/AllInApp/AllIn/Views/DetailsView.swift b/Sources/AllInApp/AllIn/Views/DetailsView.swift index 021a907..b17fa02 100644 --- a/Sources/AllInApp/AllIn/Views/DetailsView.swift +++ b/Sources/AllInApp/AllIn/Views/DetailsView.swift @@ -1,9 +1,20 @@ import SwiftUI struct DetailsView: View { + @Binding var isModalPresented: Bool @State var isModalParticipated: Bool = false @State var progressValue: Float = 0.2 + + var id: String + @StateObject private var viewModel: DetailsViewModel + + init(isModalPresented: Binding, id: String) { + self._isModalPresented = isModalPresented + self.id = id + self._viewModel = StateObject(wrappedValue: DetailsViewModel(id: id)) + } + var body: some View { GeometryReader { geometry in ZStack(alignment: .bottom) { @@ -11,7 +22,7 @@ struct DetailsView: View { HStack{ Text("Terminé!").font(.system(size: 25)).fontWeight(.bold).padding(.bottom, 10).foregroundStyle(AllInColors.blackTitleColor).opacity(0.7) Spacer() - Image("CloseiconRounded") + Image("closeIcon") .resizable() .frame(maxWidth: 25, maxHeight: 25) .onTapGesture { @@ -27,38 +38,48 @@ struct DetailsView: View { VStack(alignment: .leading,spacing: 5){ HStack{ Spacer() - Text("proposé par Lucas").font(.system(size: 10)).foregroundColor(AllInColors.grey800Color) + Text("proposé par " + (viewModel.betDetail?.bet.author.username ?? "Unknown").capitalized) + .font(.system(size: 10)) + .foregroundColor(AllInColors.grey800Color) } - Text("Etudes").font(.system(size: 15)).foregroundColor(AllInColors.grey800Color) - Text("Emre va réussir son TP de CI/CD mercredi?").font(.system(size: 20)).fontWeight(.bold).padding(.bottom, 10) + 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{ - Text("Commence le").frame(maxWidth: 100).font(.system(size: 15)).foregroundColor(AllInColors.grey800Color) - TextCapsule() - TextCapsule() + Text("Commence le") + .frame(maxWidth: 100) + .font(.system(size: 15)) + .foregroundColor(AllInColors.grey800Color) + TextCapsule(date: viewModel.betDetail?.bet.endRegisterDate ?? Date()) Spacer() }.padding(.bottom, 10) HStack{ - Text("Fini le").frame(maxWidth: 100).font(.system(size: 15)).foregroundColor(AllInColors.grey800Color) - TextCapsule() - TextCapsule() + Text("Fini le") + .frame(maxWidth: 100) + .font(.system(size: 15)) + .foregroundColor(AllInColors.grey800Color) + TextCapsule(date: viewModel.betDetail?.bet.endBetDate ?? Date()) Spacer() } } .frame(width: .infinity) .padding(.all,15).padding(.vertical, 10) - .background(AllInColors.whiteColor).cornerRadius(20, corners: [.topLeft,.topRight]).padding(.bottom,0) + .background(AllInColors.componentBackgroundColor) + .cornerRadius(20, corners: [.topLeft,.topRight]).padding(.bottom,0) ResultBanner() - VStack(alignment: .leading,spacing: 15){ + VStack(alignment: .leading, spacing: 15) { BetLineLoading(value: $progressValue).padding(.vertical, 15) Spacer() - - } - .frame(maxWidth: /*@START_MENU_TOKEN@*/.infinity/*@END_MENU_TOKEN@*/, maxHeight : .infinity) - .padding([.bottom,.trailing,.leading],15) + .frame(maxWidth: /*@START_MENU_TOKEN@*/.infinity/*@END_MENU_TOKEN@*/, maxHeight: .infinity) + .padding([.bottom,.trailing,.leading], 15) .background(AllInColors.underComponentBackgroundColor) .border(width: 1, edges: [.top], color: AllInColors.delimiterGrey) Spacer() @@ -67,7 +88,7 @@ struct DetailsView: View { } .frame(maxWidth: .infinity, maxHeight: geometry.size.height*0.98) - .background(Color.white) + .background(AllInColors.componentBackgroundColor) .cornerRadius(15) ParticipateButton(isOpen: $isModalParticipated).padding(10) @@ -81,9 +102,3 @@ struct DetailsView: View { } } } - -struct DetailsView_Previews: PreviewProvider { - static var previews: some View { - ContentView() - } -} diff --git a/Sources/AllInApp/AllInApp.xcodeproj/project.pbxproj b/Sources/AllInApp/AllInApp.xcodeproj/project.pbxproj index 8a5d873..f394329 100644 --- a/Sources/AllInApp/AllInApp.xcodeproj/project.pbxproj +++ b/Sources/AllInApp/AllInApp.xcodeproj/project.pbxproj @@ -19,14 +19,15 @@ EC01937A2B25C12B005D81E6 /* BetCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0193792B25C12B005D81E6 /* BetCard.swift */; }; EC01937C2B25C2A8005D81E6 /* AllcoinsCounter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC01937B2B25C2A8005D81E6 /* AllcoinsCounter.swift */; }; EC01937E2B25C52E005D81E6 /* TopBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC01937D2B25C52E005D81E6 /* TopBar.swift */; }; + EC01FCC32B56650400BB2390 /* DetailsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC01FCC22B56650400BB2390 /* DetailsViewModel.swift */; }; + EC01FCC52B56791B00BB2390 /* HistoricBetViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC01FCC42B56791B00BB2390 /* HistoricBetViewModel.swift */; }; EC3077072B24CB840060E34D /* SplashView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC3077062B24CB840060E34D /* SplashView.swift */; }; EC3077092B24CF7F0060E34D /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC3077082B24CF7F0060E34D /* Colors.swift */; }; 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 */; }; EC4F0D2C2B4EBF5600853949 /* Model in Frameworks */ = {isa = PBXBuildFile; productRef = ECB357342B3E13A400045D41 /* Model */; }; - EC4F0D2E2B4EC04B00853949 /* ViewModel in Frameworks */ = {isa = PBXBuildFile; productRef = EC4F0D2D2B4EC04B00853949 /* ViewModel */; }; - EC4F0D302B4EC05D00853949 /* StubLib in Frameworks */ = {isa = PBXBuildFile; productRef = EC4F0D2F2B4EC05D00853949 /* StubLib */; }; + EC60C5682B5A83FB00FFD6EF /* StubLib in Frameworks */ = {isa = PBXBuildFile; productRef = EC60C5672B5A83FB00FFD6EF /* StubLib */; }; EC650A422B25C817003AFCAD /* Friend.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC650A412B25C817003AFCAD /* Friend.swift */; }; EC650A442B25CDF3003AFCAD /* ParameterMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC650A432B25CDF3003AFCAD /* ParameterMenu.swift */; }; EC650A462B25D686003AFCAD /* RankingRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC650A452B25D686003AFCAD /* RankingRow.swift */; }; @@ -106,7 +107,6 @@ 120919192B56DC6C00D0FA29 /* DropDownAnswerMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropDownAnswerMenu.swift; sourceTree = ""; }; 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 = ""; }; - 122278BB2B4BDEC300E632AA /* Sources */ = {isa = PBXFileReference; lastKnownFileType = folder; name = Sources; path = ../StubLib/Sources; sourceTree = ""; }; 123590B32B51792000F7AEBD /* DetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailsView.swift; sourceTree = ""; }; 123590B52B5537E200F7AEBD /* ResultBanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResultBanner.swift; sourceTree = ""; }; 123590B72B5541BA00F7AEBD /* ParticipateButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParticipateButton.swift; sourceTree = ""; }; @@ -117,6 +117,8 @@ EC0193792B25C12B005D81E6 /* BetCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BetCard.swift; sourceTree = ""; }; EC01937B2B25C2A8005D81E6 /* AllcoinsCounter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AllcoinsCounter.swift; sourceTree = ""; }; EC01937D2B25C52E005D81E6 /* TopBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopBar.swift; sourceTree = ""; }; + EC01FCC22B56650400BB2390 /* DetailsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailsViewModel.swift; sourceTree = ""; }; + EC01FCC42B56791B00BB2390 /* HistoricBetViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoricBetViewModel.swift; sourceTree = ""; }; EC3077062B24CB840060E34D /* SplashView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplashView.swift; sourceTree = ""; }; EC3077082B24CF7F0060E34D /* Colors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Colors.swift; sourceTree = ""; }; EC30770A2B24D9160060E34D /* WelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeView.swift; sourceTree = ""; }; @@ -173,10 +175,9 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - EC4F0D302B4EC05D00853949 /* StubLib in Frameworks */, + EC60C5682B5A83FB00FFD6EF /* StubLib in Frameworks */, EC4F0D2C2B4EBF5600853949 /* Model in Frameworks */, ECCD244A2B4DE8010071FA9E /* Api in Frameworks */, - EC4F0D2E2B4EC04B00853949 /* ViewModel in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -341,7 +342,6 @@ ECB3572D2B3CA3BD00045D41 /* Frameworks */ = { isa = PBXGroup; children = ( - 122278BB2B4BDEC300E632AA /* Sources */, 122278B92B4BDE9500E632AA /* Package.swift */, 122278B72B4BDE1100E632AA /* DependencyInjection.framework */, ECB357302B3CA69300045D41 /* DependencyInjection.framework */, @@ -359,6 +359,8 @@ ECB26A162B4073F100FE06B3 /* CreationBetViewModel.swift */, ECB26A182B40744F00FE06B3 /* RankingViewModel.swift */, ECB26A1A2B40746C00FE06B3 /* FriendsViewModel.swift */, + EC01FCC22B56650400BB2390 /* DetailsViewModel.swift */, + EC01FCC42B56791B00BB2390 /* HistoricBetViewModel.swift */, ); path = ViewModels; sourceTree = ""; @@ -383,8 +385,7 @@ packageProductDependencies = ( ECB357342B3E13A400045D41 /* Model */, ECCD24492B4DE8010071FA9E /* Api */, - EC4F0D2D2B4EC04B00853949 /* ViewModel */, - EC4F0D2F2B4EC05D00853949 /* StubLib */, + EC60C5672B5A83FB00FFD6EF /* StubLib */, ); productName = AllIn; productReference = EC6B96982B24B4CC00FC1C58 /* AllIn.app */; @@ -539,6 +540,7 @@ EC01937E2B25C52E005D81E6 /* TopBar.swift in Sources */, ECA9D1CB2B2DA2320076E0EC /* DropDownFriends.swift in Sources */, 123590B82B5541BA00F7AEBD /* ParticipateButton.swift in Sources */, + EC01FCC32B56650400BB2390 /* DetailsViewModel.swift in Sources */, ECB26A1B2B40746C00FE06B3 /* FriendsViewModel.swift in Sources */, ECB7BC682B2F1ADF002A6654 /* LoginViewModel.swift in Sources */, 1244EF622B4EC67000374ABF /* ReviewCard.swift in Sources */, @@ -546,6 +548,7 @@ EC650A562B279D68003AFCAD /* WinModal.swift in Sources */, EC6B96D12B24BAE800FC1C58 /* AuthService.swift in Sources */, 123590B62B5537E200F7AEBD /* ResultBanner.swift in Sources */, + EC01FCC52B56791B00BB2390 /* HistoricBetViewModel.swift in Sources */, EC01937C2B25C2A8005D81E6 /* AllcoinsCounter.swift in Sources */, 12C370482B5A5EE500CD9F0F /* BetLineLoading.swift in Sources */, EC650A542B279545003AFCAD /* ChoiceCapsule.swift in Sources */, @@ -881,11 +884,7 @@ /* End XCConfigurationList section */ /* Begin XCSwiftPackageProductDependency section */ - EC4F0D2D2B4EC04B00853949 /* ViewModel */ = { - isa = XCSwiftPackageProductDependency; - productName = ViewModel; - }; - EC4F0D2F2B4EC05D00853949 /* StubLib */ = { + EC60C5672B5A83FB00FFD6EF /* StubLib */ = { isa = XCSwiftPackageProductDependency; productName = StubLib; }; diff --git a/Sources/Api/Sources/Api/BetApiManager.swift b/Sources/Api/Sources/Api/BetApiManager.swift new file mode 100644 index 0000000..4141547 --- /dev/null +++ b/Sources/Api/Sources/Api/BetApiManager.swift @@ -0,0 +1,53 @@ +// +// BetApiManager.swift +// +// +// Created by Emre on 12/01/2024. +// + +import Foundation +import Model + +public struct BetApiManager: BetDataManager { + + public init() { } + + public func getBets(withIndex index: Int, withCount count: Int, completion: @escaping ([Bet]) -> Void) { + let url = URL(string: allInApi + "bets/gets")! + + var request = URLRequest(url: url) + request.httpMethod = "GET" + request.setValue("application/json", forHTTPHeaderField: "Content-Type") + + var bets: [Bet] = [] + + 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]] { + for json in jsonArray { + if let bet = FactoryApiBet().toModel(from: json) { + bets.append(bet) + print(bet.theme) + } + } + print(httpResponse.statusCode) + completion(bets) + } + } catch { + print("Error parsing JSON: \(error)") + } + } + }.resume() + } + + public func getUsers(username: String) -> [User] { + return [] + } + + public func getBet(withId id: String, completion: @escaping (BetDetail) -> Void) { + + } + +} diff --git a/Sources/Api/Sources/Api/Factory/FactoryApiBet.swift b/Sources/Api/Sources/Api/Factory/FactoryApiBet.swift index 223f685..1adb92c 100644 --- a/Sources/Api/Sources/Api/Factory/FactoryApiBet.swift +++ b/Sources/Api/Sources/Api/Factory/FactoryApiBet.swift @@ -11,13 +11,13 @@ import Model public class FactoryApiBet: FactoryBet { public func toResponse(bet: Bet) -> [String: Any] { - let dateFormatter = DateFormatter() - dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss" + let json: [String: Any] = [ + "id": "1", "theme": bet.theme, "sentenceBet": bet.phrase, - "endRegistration": dateFormatter.string(from: bet.endRegisterDate), - "endBet": dateFormatter.string(from: bet.endBetDate), + "endRegistration": bet.endRegisterDate.timeIntervalSince1970, + "endBet": bet.endBetDate.timeIntervalSince1970, "isPrivate": String(bet.isPublic), "response": [], ] @@ -26,13 +26,13 @@ public class FactoryApiBet: FactoryBet { } public func toModel(from json: [String: Any]) -> Bet? { - guard let theme = json["theme"] as? String, + guard let id = json["id"] as? String, + let theme = json["theme"] as? String, let phrase = json["sentenceBet"] as? String, let endRegisterDateString = json["endRegistration"] as? String, let endBetDateString = json["endBet"] as? String, - let isPublicString = json["isPrivate"] as? String, - let createdBy = json["createdBy"] as? User, // Assuming User object can be parsed from JSON - let type = json["type"] as? Int else { + let isPublic = json["isPrivate"] as? Bool, + let createdBy = json["createdBy"] as? String else { return nil } @@ -43,22 +43,20 @@ public class FactoryApiBet: FactoryBet { let endBetDate = dateFormatter.date(from: endBetDateString) else { return nil } - - let isPublic = (isPublicString.lowercased() == "true") - - return toModel(theme: theme, description: phrase, endRegister: endRegisterDate, endBet: endBetDate, isPublic: isPublic, creator: createdBy, type: type) + + return toModel(id: id, theme: theme, description: phrase, endRegister: endRegisterDate, endBet: endBetDate, isPublic: isPublic, creator: User(username: createdBy, email: createdBy, nbCoins: 0, friends: []), type: 0) } - public func toModel(theme: String, description: String, endRegister: Date, endBet: Date, isPublic: Bool, creator: User, type: Int) -> Bet { + public func toModel(id: String, theme: String, description: String, endRegister: Date, endBet: Date, isPublic: Bool, creator: User, type: Int) -> Bet { switch type { case 0: - return BinaryBet(theme: theme, phrase: description, endRegisterDate: endRegister, endBetDate: endBet, totalStakes: 0, isPublic: isPublic, invited: [], author: creator, registered: []) + return BinaryBet(id: id, theme: theme, phrase: description, endRegisterDate: endRegister, endBetDate: endBet, totalStakes: 0, isPublic: isPublic, invited: [], author: creator, registered: []) case 1: - return MatchBet(theme: theme, phrase: description, endRegisterDate: endRegister, endBetDate: endBet, totalStakes: 0, isPublic: isPublic, invited: [], author: creator, registered: [], nameTeam1: "", nameTeam2: "") + return MatchBet(id: id, theme: theme, phrase: description, endRegisterDate: endRegister, endBetDate: endBet, totalStakes: 0, isPublic: isPublic, invited: [], author: creator, registered: [], nameTeam1: "", nameTeam2: "") case 2: - return CustomBet(theme: theme, phrase: description, endRegisterDate: endRegister, endBetDate: endBet, totalStakes: 0, isPublic: isPublic, invited: [], author: creator, registered: []) + return CustomBet(id: id, theme: theme, phrase: description, endRegisterDate: endRegister, endBetDate: endBet, totalStakes: 0, isPublic: isPublic, invited: [], author: creator, registered: []) default: - return BinaryBet(theme: theme, phrase: description, endRegisterDate: endRegister, endBetDate: endBet, totalStakes: 0, isPublic: isPublic, invited: [], author: creator, registered: []) + return BinaryBet(id: id, theme: theme, phrase: description, endRegisterDate: endRegister, endBetDate: endBet, totalStakes: 0, isPublic: isPublic, invited: [], author: creator, registered: []) } } diff --git a/Sources/Api/Sources/Api/UserApiManager.swift b/Sources/Api/Sources/Api/UserApiManager.swift index 6330e6e..89873c8 100644 --- a/Sources/Api/Sources/Api/UserApiManager.swift +++ b/Sources/Api/Sources/Api/UserApiManager.swift @@ -8,7 +8,7 @@ import Foundation import Model -let allInApi = "https://codefirst.iut.uca.fr/containers/AllDev-api" +let allInApi = "https://codefirst.iut.uca.fr/containers/AllDev-api/" public struct UserApiManager: UserDataManager { @@ -37,9 +37,6 @@ public struct UserApiManager: UserDataManager { URLSession.shared.uploadTask(with: request, from: jsonData) { data, response, error in print ("ALLIN : Add BET") if let httpResponse = response as? HTTPURLResponse { - if httpResponse.statusCode == 201 { - - } print(httpResponse.statusCode) } }.resume() @@ -49,4 +46,8 @@ public struct UserApiManager: UserDataManager { public func getFriends() -> [User] { fatalError("Not implemented yet") } + + public func getOldBets(withIndex index: Int, withCount count: Int, completion: @escaping ([Bet]) -> Void) { + fatalError("Not implemented yet") + } } diff --git a/Sources/Model/Sources/Model/Bet.swift b/Sources/Model/Sources/Model/Bet.swift index 919ebbd..b99b7c2 100644 --- a/Sources/Model/Sources/Model/Bet.swift +++ b/Sources/Model/Sources/Model/Bet.swift @@ -8,7 +8,11 @@ import Foundation /// A class representing a betting entity, including details about the bet theme, participants, and deadlines. -public class Bet: ObservableObject { +public class Bet: ObservableObject, Identifiable { + + /// The id for the bet. + public private(set) var id: String + /// The theme or topic of the bet. public private(set) var theme: String @@ -39,6 +43,32 @@ public class Bet: ObservableObject { /// Custom Constructor /// /// - Parameters: + /// - id: The id for the bet. + /// - theme: The theme or topic of the bet. + /// - phrase: The specific phrase or question related to the bet. + /// - endRegisterDate: The deadline for users to register for the bet. + /// - endBetDate: The deadline for the actual betting to take place. + /// - totalStakes: The total stakes or amount involved in the bet. + /// - isPublic: Indicates whether the bet is public or private. + /// - 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, totalStakes: Int, isPublic: Bool, invited: [User], author: User, registered: [User]) { + self.id = id + self.theme = theme + self.phrase = phrase + self.endRegisterDate = endRegisterDate + self.endBetDate = endBetDate + self.totalStakes = totalStakes + self.isPublic = isPublic + self.invited = invited + self.author = author + self.registered = registered + } + + /// Custom Constructor without Id + /// + /// - Parameters: /// - theme: The theme or topic of the bet. /// - phrase: The specific phrase or question related to the bet. /// - endRegisterDate: The deadline for users to register for the bet. @@ -49,6 +79,7 @@ public class Bet: ObservableObject { /// - 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, totalStakes: Int, isPublic: Bool, invited: [User], author: User, registered: [User]) { + self.id = UUID().uuidString self.theme = theme self.phrase = phrase self.endRegisterDate = endRegisterDate diff --git a/Sources/Model/Sources/Model/BetAnswerDetail.swift b/Sources/Model/Sources/Model/BetAnswerDetail.swift new file mode 100644 index 0000000..77ab2d8 --- /dev/null +++ b/Sources/Model/Sources/Model/BetAnswerDetail.swift @@ -0,0 +1,43 @@ +// +// BetAnswerDetail.swift +// +// +// Created by Emre on 19/01/2024. +// + +import Foundation + +/// A class representing detailed information about a specific answer option for a bet. +public class BetAnswerDetail: ObservableObject { + + /// The response or outcome associated with this answer. + public private(set) var response: String + + /// The total amount of stakes placed on this answer. + public private(set) var totalStakes: Int + + /// The total number of participants who selected this answer. + public private(set) var totalParticipants: Int + + /// The highest stake placed on this answer. + public private(set) var highestStake: Int + + /// The odds associated with this answer. + public private(set) var odds: Float + + /// Custom Constructor + /// + /// - Parameters: + /// - response: The response or outcome associated with this answer. + /// - totalStakes: The total amount of stakes placed on this answer. + /// - totalParticipants: The total number of participants who selected this answer. + /// - highestStake: The highest stake placed on this answer. + /// - odds: The odds associated with this answer. + public init(response: String, totalStakes: Int, totalParticipants: Int, highestStake: Int, odds: Float) { + self.response = response + self.totalStakes = totalStakes + self.totalParticipants = totalParticipants + self.highestStake = highestStake + self.odds = odds + } +} diff --git a/Sources/Model/Sources/Model/BetDataManager.swift b/Sources/Model/Sources/Model/BetDataManager.swift index 1a3fef2..6fede4a 100644 --- a/Sources/Model/Sources/Model/BetDataManager.swift +++ b/Sources/Model/Sources/Model/BetDataManager.swift @@ -8,6 +8,7 @@ import Foundation public protocol BetDataManager { - func getBets(withIndex index: Int, withCount count: Int) -> [Bet] + func getBets(withIndex index: Int, withCount count: Int, completion: @escaping ([Bet]) -> Void) func getUsers(username: String) -> [User] + func getBet(withId id: String, completion: @escaping (BetDetail) -> Void) } diff --git a/Sources/Model/Sources/Model/BetDetail.swift b/Sources/Model/Sources/Model/BetDetail.swift new file mode 100644 index 0000000..e9d7804 --- /dev/null +++ b/Sources/Model/Sources/Model/BetDetail.swift @@ -0,0 +1,38 @@ +// +// BetDetail.swift +// +// +// Created by Emre on 19/01/2024. +// + +import Foundation + +/// A class representing detailed information about a specific bet, including answers and user participations. +public class BetDetail: ObservableObject { + + /// The main bet information. + public private(set) var bet: Bet + + /// Details about the answers available for the bet. + public private(set) var answers: [BetAnswerDetail] + + /// List of user participations in the bet. + public private(set) var participations: [Participation] + + /// The user's own participation in the bet. + public private(set) var userParticipation: Participation + + /// Custom Constructor + /// + /// - Parameters: + /// - bet: The main bet information. + /// - answers: Details about the answers available for the bet. + /// - participations: List of user participations in the bet. + /// - userParticipation: The user's own participation in the bet. + public init(bet: Bet, answers: [BetAnswerDetail], participations: [Participation], userParticipation: Participation) { + self.bet = bet + self.answers = answers + self.participations = participations + self.userParticipation = userParticipation + } +} diff --git a/Sources/Model/Sources/Model/BinaryBet.swift b/Sources/Model/Sources/Model/BinaryBet.swift index 8767849..051ccf6 100644 --- a/Sources/Model/Sources/Model/BinaryBet.swift +++ b/Sources/Model/Sources/Model/BinaryBet.swift @@ -13,6 +13,23 @@ public class BinaryBet: Bet { // Custom Constructor /// /// - Parameters: + /// - id: The id for the bet. + /// - theme: The theme or topic of the binary bet. + /// - phrase: The specific phrase or question related to the binary bet. + /// - endRegisterDate: The deadline for users to register for the binary bet. + /// - endBetDate: The deadline for the actual betting to take place for the binary bet. + /// - totalStakes: The total stakes or amount involved in the binary bet. + /// - isPublic: Indicates whether the binary bet is public or private. + /// - invited: List of users who are invited to participate in the binary bet. + /// - author: The user who created the binary bet. + /// - registered: List of users who have registered for the binary bet. + public override init(id: String, theme: String, phrase: String, endRegisterDate: Date, endBetDate: Date, totalStakes: Int, isPublic: Bool, invited: [User], author: User, registered: [User]) { + super.init(id: id, theme: theme, phrase: phrase, endRegisterDate: endRegisterDate, endBetDate: endBetDate, totalStakes: totalStakes, isPublic: isPublic, invited: invited, author: author, registered: registered) + } + + // Custom Constructor without Id + /// + /// - Parameters: /// - theme: The theme or topic of the binary bet. /// - phrase: The specific phrase or question related to the binary bet. /// - endRegisterDate: The deadline for users to register for the binary bet. diff --git a/Sources/Model/Sources/Model/BinaryParticipation.swift b/Sources/Model/Sources/Model/BinaryParticipation.swift deleted file mode 100644 index 5a02622..0000000 --- a/Sources/Model/Sources/Model/BinaryParticipation.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// BinaryParticipation.swift -// -// -// Created by Emre on 28/12/2023. -// - -import Foundation - -/// Enum to represent the possible answers for a binary participation. -public enum YesNo { - case yes - case no -} - -/// A subclass of Participation that represents a binary participation (yes/no) in a bet. -public class BinaryParticipation: Participation { - /// The answer for the binary participation (yes or no). - public var answer: YesNo - - /// Custom Constructor - /// - /// - Parameters: - /// - coinAmount: The amount of coins involved in the binary participation. - /// - date: The date and time when the binary participation occurred. - /// - user: The user who participated in the binary bet. - /// - bet: The binary bet in which the user participated. - /// - answer: The answer for the binary participation (yes or no). - public init(coinAmount: Int, date: Date, user: User, bet: Bet, answer: YesNo) { - self.answer = answer - super.init(coinAmount: coinAmount, date: date, user: user, bet: bet) - } -} diff --git a/Sources/Model/Sources/Model/CustomParticipation.swift b/Sources/Model/Sources/Model/CustomParticipation.swift deleted file mode 100644 index 9956aa9..0000000 --- a/Sources/Model/Sources/Model/CustomParticipation.swift +++ /dev/null @@ -1,27 +0,0 @@ -// -// CustomParticipation.swift -// -// -// Created by Emre on 28/12/2023. -// - -import Foundation - -/// A subclass of Participation that represents a custom participation in a bet. -public class CustomParticipation: Participation { - /// The user's response to the custom bet. - public var answer: CustomBetResponse - - /// Custom Constructor - /// - /// - Parameters: - /// - coinAmount: The amount of coins involved in the custom participation. - /// - date: The date and time when the custom participation occurred. - /// - user: The user who participated in the custom bet. - /// - bet: The custom bet in which the user participated. - /// - answer: The user's response to the custom bet. - public init(coinAmount: Int, date: Date, user: User, bet: Bet, answer: CustomBetResponse) { - self.answer = answer - super.init(coinAmount: coinAmount, date: date, user: user, bet: bet) - } -} diff --git a/Sources/Model/Sources/Model/Factory/FactoryBet.swift b/Sources/Model/Sources/Model/Factory/FactoryBet.swift index c692035..40a7f0a 100644 --- a/Sources/Model/Sources/Model/Factory/FactoryBet.swift +++ b/Sources/Model/Sources/Model/Factory/FactoryBet.swift @@ -10,5 +10,5 @@ import Foundation public protocol FactoryBet { func toResponse(bet: Bet) -> [String: Any] func toModel(from json: [String: Any]) -> Bet? - func toModel(theme: String, description: String, endRegister: Date, endBet: Date, isPublic: Bool, creator: User, type: Int) -> Bet + func toModel(id: String, theme: String, description: String, endRegister: Date, endBet: Date, isPublic: Bool, creator: User, type: Int) -> Bet } diff --git a/Sources/Model/Sources/Model/Manager.swift b/Sources/Model/Sources/Model/Manager.swift index 7eab799..3ff086a 100644 --- a/Sources/Model/Sources/Model/Manager.swift +++ b/Sources/Model/Sources/Model/Manager.swift @@ -16,7 +16,25 @@ public struct Manager { self.userDataManager = userDataManager } - public func addBet(bet: Bet) { - userDataManager.addBet(bet: bet) + public func addBet(theme: String, description: String, endRegister: Date, endBet: Date, isPublic: Bool, creator: User) { + userDataManager.addBet(bet: BinaryBet(theme: theme, phrase: description, endRegisterDate: endRegister, endBetDate: endBet, totalStakes: 0, isPublic: isPublic, invited: [], author: creator, registered: [])) + } + + public func getBets(withIndex index: Int, withCount count: Int, completion: @escaping ([Bet]) -> Void) { + betDataManager.getBets(withIndex: index, withCount: count) { 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) { + userDataManager.getOldBets(withIndex: index, withCount: count) { bets in + completion(bets) + } } } diff --git a/Sources/Model/Sources/Model/MatchBet.swift b/Sources/Model/Sources/Model/MatchBet.swift index cddcc4e..e197042 100644 --- a/Sources/Model/Sources/Model/MatchBet.swift +++ b/Sources/Model/Sources/Model/MatchBet.swift @@ -18,6 +18,27 @@ public class MatchBet: Bet { /// Custom Constructor /// /// - Parameters: + /// - id: The id for the bet. + /// - theme: The theme or topic of the match bet. + /// - phrase: The specific phrase or question related to the match bet. + /// - endRegisterDate: The deadline for users to register for the match bet. + /// - endBetDate: The deadline for the actual betting to take place for the match bet. + /// - totalStakes: The total stakes or amount involved in the match bet. + /// - isPublic: Indicates whether the match bet is public or private. + /// - 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, totalStakes: Int, isPublic: Bool, invited: [User], author: User, registered: [User], nameTeam1: String, nameTeam2: String) { + self.nameTeam1 = nameTeam1 + self.nameTeam2 = nameTeam2 + super.init(id: id, theme: theme, phrase: phrase, endRegisterDate: endRegisterDate, endBetDate: endBetDate, totalStakes: totalStakes, isPublic: isPublic, invited: invited, author: author, registered: registered) + } + + /// Custom Constructor without Id + /// + /// - Parameters: /// - theme: The theme or topic of the match bet. /// - phrase: The specific phrase or question related to the match bet. /// - endRegisterDate: The deadline for users to register for the match bet. diff --git a/Sources/Model/Sources/Model/MatchParticipation.swift b/Sources/Model/Sources/Model/MatchParticipation.swift deleted file mode 100644 index 68b47ba..0000000 --- a/Sources/Model/Sources/Model/MatchParticipation.swift +++ /dev/null @@ -1,32 +0,0 @@ -// -// MatchParticipation.swift -// -// -// Created by Emre on 28/12/2023. -// - -import Foundation - -/// A subclass of Participation that represents a user's participation in a match bet. -public class MatchParticipation: Participation { - /// The points earned by the user for the first team in the match. - public var pointsTeam1: Int - - /// The points earned by the user for the second team in the match. - public var pointsTeam2: Int - - /// Custom Constructor - /// - /// - Parameters: - /// - coinAmount: The amount of coins involved in the match participation. - /// - date: The date and time when the match participation occurred. - /// - user: The user who participated in the match bet. - /// - bet: The match bet in which the user participated. - /// - pointsTeam1: The points earned by the user for the first team in the match. - /// - pointsTeam2: The points earned by the user for the second team in the match. - public init(coinAmount: Int, date: Date, user: User, bet: Bet, pointsTeam1: Int, pointsTeam2: Int) { - self.pointsTeam1 = pointsTeam1 - self.pointsTeam2 = pointsTeam2 - super.init(coinAmount: coinAmount, date: date, user: user, bet: bet) - } -} diff --git a/Sources/Model/Sources/Model/Participation.swift b/Sources/Model/Sources/Model/Participation.swift index 6ccd9be..4040e22 100644 --- a/Sources/Model/Sources/Model/Participation.swift +++ b/Sources/Model/Sources/Model/Participation.swift @@ -1,37 +1,43 @@ // // Participation.swift -// +// // // Created by Emre on 28/12/2023. // import Foundation -/// A class representing a user's participation in a bet, including the amount of coins, date, user, and the associated bet. +/// A class representing a user's participation in a bet. public class Participation: ObservableObject { - /// The amount of coins involved in the participation. - var coinAmount: Int + + /// The amount of stake in the bet. + public private(set) var stake: Int /// The date and time when the participation occurred. - var date: Date + public private(set) var date: Date + + /// The response or outcome of the participation. + public private(set) var response: String /// The user who participated in the bet. - var user: User + public private(set) var user: User - /// The bet in which the user participated. - var bet: Bet + /// The unique identifier of the bet. + let betId: String /// Custom Constructor /// /// - Parameters: - /// - coinAmount: The amount of coins involved in the participation. + /// - 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. - /// - bet: The bet in which the user participated. - init(coinAmount: Int, date: Date, user: User, bet: Bet) { - self.coinAmount = coinAmount + /// - betId: The unique identifier of the bet. + public init(stake: Int, date: Date, response: String, user: User, betId: String) { + self.stake = stake self.date = date + self.response = response self.user = user - self.bet = bet + self.betId = betId } } diff --git a/Sources/Model/Sources/Model/UserDataManager.swift b/Sources/Model/Sources/Model/UserDataManager.swift index 584add7..487cad9 100644 --- a/Sources/Model/Sources/Model/UserDataManager.swift +++ b/Sources/Model/Sources/Model/UserDataManager.swift @@ -11,4 +11,5 @@ public protocol UserDataManager { func getBets(withIndex index: Int, withCount count: Int) -> [Bet] func addBet(bet: Bet) func getFriends() -> [User] + func getOldBets(withIndex index: Int, withCount count: Int, completion: @escaping ([Bet]) -> Void) } diff --git a/Sources/ViewModel/.gitignore b/Sources/StubLib/.gitignore similarity index 100% rename from Sources/ViewModel/.gitignore rename to Sources/StubLib/.gitignore diff --git a/Sources/StubLib/Sources/StubLib/BetStubManager.swift b/Sources/StubLib/Sources/StubLib/BetStubManager.swift index c9c8347..7bb801f 100644 --- a/Sources/StubLib/Sources/StubLib/BetStubManager.swift +++ b/Sources/StubLib/Sources/StubLib/BetStubManager.swift @@ -1,6 +1,6 @@ // // BetStubManager.swift -// +// // // Created by Emre on 31/12/2023. // @@ -12,15 +12,21 @@ public struct BetStubManager: BetDataManager { public init() {} - public func getBets(withIndex index: Int, withCount count: Int) -> [Bet] { - return Stub.shared.bets + public func getBets(withIndex index: Int, withCount count: Int, completion: @escaping ([Bet]) -> Void) { + completion(Stub.shared.bets) } public func getUsers(username: String) -> [User] { - return Stub.shared.users - .filter { user in - user.username.contains(username) - } + return [] + } + + public func getBet(withId id: String, completion: @escaping (BetDetail) -> Void) { + + if let betDetail = Stub.shared.betsDetail.first(where: { $0.bet.id == id }) { + completion(betDetail) + } else { + print("BetDetail with ID \(id) not found.") + } } } diff --git a/Sources/StubLib/Sources/StubLib/Stub.swift b/Sources/StubLib/Sources/StubLib/Stub.swift index fe9e225..5e70aa0 100644 --- a/Sources/StubLib/Sources/StubLib/Stub.swift +++ b/Sources/StubLib/Sources/StubLib/Stub.swift @@ -1,6 +1,6 @@ // // Stub.swift -// +// // // Created by Emre on 01/01/2024. // @@ -12,6 +12,7 @@ struct Stub { static var shared = Stub() public var bets: [Bet] = [] + public var betsDetail: [BetDetail] = [] public var users: [User] = [] public init() { @@ -82,10 +83,15 @@ struct Stub { registered: [user3] ) self.bets.append(bet4) - + + for bet in bets { + let betDetail = BetDetail(bet: bet, answers: [], participations: [], userParticipation: Participation(stake: 0, date: Date(), response: "", user: user1, betId: "")) + self.betsDetail.append(betDetail) + } } public mutating func add(bet: Bet) { - self.bets.append(bet) + let newBetDetail = BetDetail(bet: bet, answers: [], participations: [], userParticipation: Participation(stake: 0, date: Date(), response: "", user: users[1], betId: "")) + self.betsDetail.append(newBetDetail) } } diff --git a/Sources/StubLib/Sources/StubLib/UserStubManager.swift b/Sources/StubLib/Sources/StubLib/UserStubManager.swift deleted file mode 100644 index a2f8b07..0000000 --- a/Sources/StubLib/Sources/StubLib/UserStubManager.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// UserStubManager.swift -// -// -// Created by Emre on 31/12/2023. -// - -import Foundation -import Model - -public struct UserStubManager: UserDataManager { - - private var username: String - - public init(username: String) { - self.username = username - } - - public func getBets(withIndex index: Int, withCount count: Int) -> [Bet] { - return Stub.shared.bets.filter { bet in - bet.registered.contains { user in - user.username == self.username - } - } - } - - public func addBet(bet: Bet) { - Stub.shared.add(bet: bet) - } - - public func getFriends() -> [User] { - return Stub.shared.users.filter { user in - user.friends.contains { friend in - friend.username == self.username - } - } - } -} diff --git a/Sources/ViewModel/Package.swift b/Sources/ViewModel/Package.swift deleted file mode 100644 index 67fd2cf..0000000 --- a/Sources/ViewModel/Package.swift +++ /dev/null @@ -1,29 +0,0 @@ -// swift-tools-version: 5.8 -// The swift-tools-version declares the minimum version of Swift required to build this package. - -import PackageDescription - -let package = Package( - name: "ViewModel", - platforms: [ - .iOS(.v13) - ], - products: [ - // Products define the executables and libraries a package produces, and make them visible to other packages. - .library( - name: "ViewModel", - targets: ["ViewModel"]), - ], - dependencies: [ - // Dependencies declare other packages that this package depends on. - // .package(url: /* package url */, from: "1.0.0"), - .package(name: "Model", path: "../Model") - ], - targets: [ - // Targets are the basic building blocks of a package. A target can define a module or a test suite. - // Targets can depend on other targets in this package, and on products in packages this package depends on. - .target( - name: "ViewModel", - dependencies: ["Model"]), - ] -) diff --git a/Sources/ViewModel/README.md b/Sources/ViewModel/README.md deleted file mode 100644 index 74e3704..0000000 --- a/Sources/ViewModel/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# ViewModel - -A description of this package. diff --git a/Sources/ViewModel/Sources/ViewModel/ManagerVM.swift b/Sources/ViewModel/Sources/ViewModel/ManagerVM.swift deleted file mode 100644 index 2bcb8b0..0000000 --- a/Sources/ViewModel/Sources/ViewModel/ManagerVM.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// ManagerVM.swift -// -// -// Created by Emre on 30/12/2023. -// - -import Foundation -import Model - -public class ManagerVM: ObservableObject { - @Published var model: Manager - - public init(withModel model: Manager) { - self.model = model - } - - public func getPublicBets() { - - } - - public func addBet(theme: String, description: String, endRegister: Date, endBet: Date, isPublic: Bool, creator: User) { - model.addBet(bet: BinaryBet(theme: theme, phrase: description, endRegisterDate: endRegister, endBetDate: endBet, totalStakes: 0, isPublic: isPublic, invited: [], author: creator, registered: [])) - } -}