diff --git a/Sources/AllInApp/AllIn/Components/EmptyInfo.swift b/Sources/AllInApp/AllIn/Components/EmptyInfo.swift index 365b61f..c4ae925 100644 --- a/Sources/AllInApp/AllIn/Components/EmptyInfo.swift +++ b/Sources/AllInApp/AllIn/Components/EmptyInfo.swift @@ -14,7 +14,7 @@ struct EmptyInfo: View { var body: some View { VStack{ Text(emoji).font(.system(size: 120)) - Text(title).textStyle(weight: .bold, color: .black, size: 15) + Text(title).textStyle(weight: .bold, color: AllInColors.primaryTextColor , size: 15) explain.isEmpty ? nil : Text(explain).textStyle(weight: .light, color: .gray, size: 12) }.opacity(0.55).padding(.horizontal, 20).multilineTextAlignment(.center) } diff --git a/Sources/AllInApp/AllIn/Components/Friend.swift b/Sources/AllInApp/AllIn/Components/Friend.swift index 3744a79..f99d545 100644 --- a/Sources/AllInApp/AllIn/Components/Friend.swift +++ b/Sources/AllInApp/AllIn/Components/Friend.swift @@ -11,14 +11,15 @@ import Model struct Friend: View { var user: User + let isRequest: Bool @ObservedObject var viewModel: FriendsViewModel var StatusValues: (String, Color, Color) { switch user.friendStatus { case .friend: - return (String(localized: "generic_delete"), AllInColors.grey400Color, AllInColors.componentBackgroundColor) + return isRequest ? (String(localized: "generic_decline"), AllInColors.grey400Color, AllInColors.componentBackgroundColor) : (String(localized: "generic_delete"), AllInColors.grey400Color, AllInColors.componentBackgroundColor) case .notFriend: - return (String(localized: "generic_add"), .white, AllInColors.lightPurpleColor) + return isRequest ? (String(localized: "generic_acccept"), .white, AllInColors.lightPurpleColor) : (String(localized: "generic_add"), .white, AllInColors.lightPurpleColor) case .requested: return (String(localized: "friends_request_sent"), AllInColors.grey400Color, AllInColors.componentBackgroundColor) default: @@ -39,7 +40,7 @@ struct Friend: View { .lineLimit(1) Spacer() Button(StatusValues.0) { - viewModel.toggleFriendStatus(for: user) + viewModel.toggleFriendStatus(for: user, isRequest: isRequest) } .minimumScaleFactor(0.3) .lineLimit(2) @@ -48,6 +49,13 @@ struct Friend: View { .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) } diff --git a/Sources/AllInApp/AllIn/Ressources/en.lproj/Localizable.strings b/Sources/AllInApp/AllIn/Ressources/en.lproj/Localizable.strings index 1a3e605..eadccb7 100644 --- a/Sources/AllInApp/AllIn/Ressources/en.lproj/Localizable.strings +++ b/Sources/AllInApp/AllIn/Ressources/en.lproj/Localizable.strings @@ -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."; @@ -167,6 +169,7 @@ "friends_title" = "Friends"; "friends_request_sent" = "Request sent"; +"friends_request"= "Friends Request"; /// Daily reward diff --git a/Sources/AllInApp/AllIn/Ressources/fr.lproj/Localizable.strings b/Sources/AllInApp/AllIn/Ressources/fr.lproj/Localizable.strings index c8c7cb8..1ccb9ba 100644 --- a/Sources/AllInApp/AllIn/Ressources/fr.lproj/Localizable.strings +++ b/Sources/AllInApp/AllIn/Ressources/fr.lproj/Localizable.strings @@ -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."; @@ -167,6 +169,7 @@ "friends_title" = "Amis"; "friends_request_sent" = "Requête envoyée"; +"friends_request"= "Requêtes d'amis"; /// Daily reward diff --git a/Sources/AllInApp/AllIn/ViewModels/FriendsViewModel.swift b/Sources/AllInApp/AllIn/ViewModels/FriendsViewModel.swift index 7e52413..dcdebf8 100644 --- a/Sources/AllInApp/AllIn/ViewModels/FriendsViewModel.swift +++ b/Sources/AllInApp/AllIn/ViewModels/FriendsViewModel.swift @@ -14,6 +14,7 @@ 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 { @@ -26,6 +27,7 @@ class FriendsViewModel: ObservableObject { init() { getItems() + getRequests() } func getItems() { @@ -36,6 +38,14 @@ class FriendsViewModel: ObservableObject { } } + func getRequests() { + manager.getRequests() { friends in + DispatchQueue.main.async { + self.requests = friends + } + } + } + func search() { guard text.allSatisfy({ $0.isLetter || $0.isNumber }) else { return @@ -46,26 +56,48 @@ class FriendsViewModel: ObservableObject { } } - func toggleFriendStatus(for user: User) { - guard let index = users.firstIndex(where: { $0.username == user.username }) else { return } - var updatedUser = users[index] + func toggleFriendStatus(for user: User, isRequest: Bool) { + let targetList = isRequest ? requests : users - 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 - } + guard let index = targetList.firstIndex(where: { $0.username == user.username }) else { return } + var updatedUser = targetList[index] - users[index] = updatedUser + 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 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 diff --git a/Sources/AllInApp/AllIn/Views/FriendsView.swift b/Sources/AllInApp/AllIn/Views/FriendsView.swift index 324ddca..405db82 100644 --- a/Sources/AllInApp/AllIn/Views/FriendsView.swift +++ b/Sources/AllInApp/AllIn/Views/FriendsView.swift @@ -11,52 +11,105 @@ 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) - - HStack { - TextField("Search...", text: $viewModel.text) - .padding(7) - .padding(.horizontal, 25) - .background(Color(.systemGray6)) - .cornerRadius(8) - .overlay( + TabView(selection: $selectedTab) { + ScrollView(showsIndicators: false){ + VStack{ HStack { - Image(systemName: "magnifyingglass") - .foregroundColor(.gray) - .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading) - .padding(.leading, 8) + 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: "Vous n’avez pas encore d’amis", explain: "Ajoutez les depuis cet écran").padding(.top, 40) + } + else{ - if !viewModel.text.isEmpty { - Button(action: { - self.viewModel.text = "" - }) { - Image(systemName: "multiply.circle.fill") - .foregroundColor(.gray) - .padding(.trailing, 8) - } + ForEach(viewModel.users, id: \.self) { friend in + Friend(user: friend, isRequest: false, viewModel: viewModel) } } - ) - .padding(.horizontal, 10) - } - if(viewModel.users.isEmpty){ - EmptyInfo(emoji:"👥", title: "Vous n’avez pas encore d’amis", explain: "Ajoutez les depuis cet écran").padding(.top, 40) - } - else{ - ScrollView(showsIndicators: false){ - ForEach(viewModel.users, id: \.self) { friend in - Friend(user: friend, viewModel: viewModel) + + } + Spacer() + + } + .refreshable { + viewModel.getItems() + } + .padding(.top, 50) + .tag(0) + 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() } - .padding(.top, 25) + .padding(.top, 50) + .tag(1) } - 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) diff --git a/Sources/Api/Sources/Api/UserApiManager.swift b/Sources/Api/Sources/Api/UserApiManager.swift index a956ead..1b9ea35 100644 --- a/Sources/Api/Sources/Api/UserApiManager.swift +++ b/Sources/Api/Sources/Api/UserApiManager.swift @@ -142,6 +142,33 @@ public struct UserApiManager: UserDataManager { }.resume() } + public func getRequests(completion: @escaping ([User]) -> Void) { + let url = URL(string: allInApi + "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]] { + print(jsonArray) + 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: allInApi + "friends/search/" + name)! diff --git a/Sources/Model/Sources/Model/Manager.swift b/Sources/Model/Sources/Model/Manager.swift index 33dc10b..0620247 100644 --- a/Sources/Model/Sources/Model/Manager.swift +++ b/Sources/Model/Sources/Model/Manager.swift @@ -58,6 +58,12 @@ public struct Manager { } } + public func getRequests(completion: @escaping ([User]) -> Void) { + userDataManager.getRequests() { users in + completion(users) + } + } + public func getUsers(withName name: String, completion: @escaping ([User]) -> Void) { userDataManager.getUsers(withName: name) { users in completion(users) diff --git a/Sources/Model/Sources/Model/UserDataManager.swift b/Sources/Model/Sources/Model/UserDataManager.swift index 4b76f5f..6c805a1 100644 --- a/Sources/Model/Sources/Model/UserDataManager.swift +++ b/Sources/Model/Sources/Model/UserDataManager.swift @@ -14,6 +14,7 @@ public protocol UserDataManager { 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)