From 7f32cd6ad4c413e1acf2ca48c42d63ff4ecc96c5 Mon Sep 17 00:00:00 2001 From: anthonyle51 Date: Sat, 22 Feb 2025 14:16:28 -0500 Subject: [PATCH 1/6] acceptFriendRequest func --- .../ViewModels/FriendsViewModel.swift | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/Gauge/Profiles+Search/ViewModels/FriendsViewModel.swift b/Gauge/Profiles+Search/ViewModels/FriendsViewModel.swift index 0e7c5a8..8bf7779 100644 --- a/Gauge/Profiles+Search/ViewModels/FriendsViewModel.swift +++ b/Gauge/Profiles+Search/ViewModels/FriendsViewModel.swift @@ -133,4 +133,67 @@ class FriendsViewModel: ObservableObject { return nil } } + +// Create a addfriend method. addFriends(friendId, hostID) +// +// Adds the friendsID to the hostIDs friends. +// Removes it from the hostIDs incoming requests +// REmoves it from the friendIDs outgoing requests + + enum FriendRequestError: Error { + case invalidData(reason: String) + case userError(reason:String) + } + func acceptFriendRequest(friendId: String, hostId: String) async throws { + do { + let friendDocumentRef = Firebase.db.collection("USERS").document(friendId) + let friendDocumentSnapshot = try await friendDocumentRef.getDocument() + guard let friendDocument = friendDocumentSnapshot.data() else { + throw FriendRequestError.invalidData(reason: "Friend document not found") + } + guard var friendsOut = friendDocument["friendOut"] as? [String: [String]] else { throw FriendRequestError.invalidData(reason: "No outgoing request data for friend")} + if friendsOut.removeValue(forKey: hostId) != nil { + try await friendDocumentRef.updateData(["friendOut": friendsOut]) + } else { + throw FriendRequestError.invalidData(reason: "Host is not in friend's outgoing requests") + } + + let hostDocumentRef = Firebase.db.collection("USERS").document(hostId) + let hostDocumentSnapshot = try await hostDocumentRef.getDocument() + guard let hostDocument = hostDocumentSnapshot.data() else { + throw FriendRequestError.invalidData(reason: "Host document not found") + } + guard var hostIn = hostDocument["friendIn"] as? [String: [String]] else { throw FriendRequestError.invalidData(reason: "No incoming request data for host")} + if hostIn.removeValue(forKey: friendId) != nil{ + try await hostDocumentRef.updateData(["friendIn": hostIn]) + } else { + throw FriendRequestError.invalidData(reason: "Friend is not in host's incoming requests") + } + + var hostFriends = hostDocument["friends"] as? [String: [String]] ?? [:] + guard let friendUserName = friendDocument["userName"] as? String else { throw FriendRequestError.userError(reason: "Friend document does not contain userName")} + let friendProfilePhoto = friendDocument["profilePhoto"] as? String ?? "" + hostFriends[friendId] = [friendUserName, friendProfilePhoto] + try await hostDocumentRef.updateData(["friends": hostFriends]) + } catch FriendRequestError.invalidData(let reason) { + print("Data Error - \(reason)") + throw FriendRequestError.invalidData(reason: reason) + } catch FriendRequestError.userError(let reason) { + print("User Error - \(reason)") + throw FriendRequestError.userError(reason: reason) + } + catch { + print("Unexpected Error") + throw error + } + } + +//Create a deleteFriend method. deleteFriend(friendID, hostID) +// +//Removes it from the hostIDs incoming requests +//Removes it from the friendIDs outgoing requests + + func rejectFriendRequest(friendId: String, hostId: String) async throws { + + } } From 6fa984de0e73b69a5591101edccc9d021ffac111 Mon Sep 17 00:00:00 2001 From: anthonyle51 Date: Sat, 22 Feb 2025 14:22:42 -0500 Subject: [PATCH 2/6] fetch both document concurrently in accept friend --- .../Profiles+Search/ViewModels/FriendsViewModel.swift | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Gauge/Profiles+Search/ViewModels/FriendsViewModel.swift b/Gauge/Profiles+Search/ViewModels/FriendsViewModel.swift index 8bf7779..9da9e60 100644 --- a/Gauge/Profiles+Search/ViewModels/FriendsViewModel.swift +++ b/Gauge/Profiles+Search/ViewModels/FriendsViewModel.swift @@ -146,9 +146,13 @@ class FriendsViewModel: ObservableObject { } func acceptFriendRequest(friendId: String, hostId: String) async throws { do { + async let friendDocumentSnapshot = Firebase.db.collection("USERS").document(friendId).getDocument() + async let hostDocumentSnapshot = Firebase.db.collection("USERS").document(hostId).getDocument() + + let (friendSnapshot, hostSnapshot) = try await (friendDocumentSnapshot, hostDocumentSnapshot) + let friendDocumentRef = Firebase.db.collection("USERS").document(friendId) - let friendDocumentSnapshot = try await friendDocumentRef.getDocument() - guard let friendDocument = friendDocumentSnapshot.data() else { + guard let friendDocument = friendSnapshot.data() else { throw FriendRequestError.invalidData(reason: "Friend document not found") } guard var friendsOut = friendDocument["friendOut"] as? [String: [String]] else { throw FriendRequestError.invalidData(reason: "No outgoing request data for friend")} @@ -159,8 +163,7 @@ class FriendsViewModel: ObservableObject { } let hostDocumentRef = Firebase.db.collection("USERS").document(hostId) - let hostDocumentSnapshot = try await hostDocumentRef.getDocument() - guard let hostDocument = hostDocumentSnapshot.data() else { + guard let hostDocument = hostSnapshot.data() else { throw FriendRequestError.invalidData(reason: "Host document not found") } guard var hostIn = hostDocument["friendIn"] as? [String: [String]] else { throw FriendRequestError.invalidData(reason: "No incoming request data for host")} From e74920cca806e6b94fe691f67acd6c71699ad1aa Mon Sep 17 00:00:00 2001 From: anthonyle51 Date: Sat, 22 Feb 2025 14:37:27 -0500 Subject: [PATCH 3/6] use batch to update data --- .../ViewModels/FriendsViewModel.swift | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/Gauge/Profiles+Search/ViewModels/FriendsViewModel.swift b/Gauge/Profiles+Search/ViewModels/FriendsViewModel.swift index 9da9e60..de38355 100644 --- a/Gauge/Profiles+Search/ViewModels/FriendsViewModel.swift +++ b/Gauge/Profiles+Search/ViewModels/FriendsViewModel.swift @@ -146,38 +146,42 @@ class FriendsViewModel: ObservableObject { } func acceptFriendRequest(friendId: String, hostId: String) async throws { do { - async let friendDocumentSnapshot = Firebase.db.collection("USERS").document(friendId).getDocument() - async let hostDocumentSnapshot = Firebase.db.collection("USERS").document(hostId).getDocument() + let batch = Firebase.db.batch() + let friendDocRef = Firebase.db.collection("USERS").document(friendId) + let hostDocRef = Firebase.db.collection("USERS").document(hostId) + async let friendDocumentSnapshot = friendDocRef.getDocument() + async let hostDocumentSnapshot = hostDocRef.getDocument() let (friendSnapshot, hostSnapshot) = try await (friendDocumentSnapshot, hostDocumentSnapshot) - let friendDocumentRef = Firebase.db.collection("USERS").document(friendId) + // remove hostId from friend's outgoing requests guard let friendDocument = friendSnapshot.data() else { throw FriendRequestError.invalidData(reason: "Friend document not found") } guard var friendsOut = friendDocument["friendOut"] as? [String: [String]] else { throw FriendRequestError.invalidData(reason: "No outgoing request data for friend")} - if friendsOut.removeValue(forKey: hostId) != nil { - try await friendDocumentRef.updateData(["friendOut": friendsOut]) - } else { + guard friendsOut.removeValue(forKey: hostId) != nil else { throw FriendRequestError.invalidData(reason: "Host is not in friend's outgoing requests") } + batch.updateData(["friendOut": friendsOut], forDocument: friendDocRef) - let hostDocumentRef = Firebase.db.collection("USERS").document(hostId) + // remove friendId from host's incoming requests guard let hostDocument = hostSnapshot.data() else { throw FriendRequestError.invalidData(reason: "Host document not found") } guard var hostIn = hostDocument["friendIn"] as? [String: [String]] else { throw FriendRequestError.invalidData(reason: "No incoming request data for host")} - if hostIn.removeValue(forKey: friendId) != nil{ - try await hostDocumentRef.updateData(["friendIn": hostIn]) - } else { + guard hostIn.removeValue(forKey: friendId) != nil else { throw FriendRequestError.invalidData(reason: "Friend is not in host's incoming requests") } + // add friend as host's friends var hostFriends = hostDocument["friends"] as? [String: [String]] ?? [:] guard let friendUserName = friendDocument["userName"] as? String else { throw FriendRequestError.userError(reason: "Friend document does not contain userName")} let friendProfilePhoto = friendDocument["profilePhoto"] as? String ?? "" hostFriends[friendId] = [friendUserName, friendProfilePhoto] - try await hostDocumentRef.updateData(["friends": hostFriends]) + batch.updateData(["friendIn": hostIn, "friends": hostFriends], forDocument: hostDocRef) + + try await batch.commit() + } catch FriendRequestError.invalidData(let reason) { print("Data Error - \(reason)") throw FriendRequestError.invalidData(reason: reason) From 90af454f6d8c1fb34b21cd7224093e16328c38c7 Mon Sep 17 00:00:00 2001 From: anthonyle51 Date: Sat, 22 Feb 2025 14:52:01 -0500 Subject: [PATCH 4/6] reject friend --- .../ViewModels/FriendsViewModel.swift | 57 ++++++++++++++----- 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/Gauge/Profiles+Search/ViewModels/FriendsViewModel.swift b/Gauge/Profiles+Search/ViewModels/FriendsViewModel.swift index de38355..df9ed6a 100644 --- a/Gauge/Profiles+Search/ViewModels/FriendsViewModel.swift +++ b/Gauge/Profiles+Search/ViewModels/FriendsViewModel.swift @@ -133,17 +133,12 @@ class FriendsViewModel: ObservableObject { return nil } } - -// Create a addfriend method. addFriends(friendId, hostID) -// -// Adds the friendsID to the hostIDs friends. -// Removes it from the hostIDs incoming requests -// REmoves it from the friendIDs outgoing requests - + enum FriendRequestError: Error { case invalidData(reason: String) case userError(reason:String) } + func acceptFriendRequest(friendId: String, hostId: String) async throws { do { let batch = Firebase.db.batch() @@ -194,13 +189,49 @@ class FriendsViewModel: ObservableObject { throw error } } - -//Create a deleteFriend method. deleteFriend(friendID, hostID) -// -//Removes it from the hostIDs incoming requests -//Removes it from the friendIDs outgoing requests func rejectFriendRequest(friendId: String, hostId: String) async throws { - + do { + let batch = Firebase.db.batch() + let friendDocRef = Firebase.db.collection("USERS").document(friendId) + let hostDocRef = Firebase.db.collection("USERS").document(hostId) + async let friendDocumentSnapshot = friendDocRef.getDocument() + async let hostDocumentSnapshot = hostDocRef.getDocument() + + let (friendSnapshot, hostSnapshot) = try await (friendDocumentSnapshot, hostDocumentSnapshot) + + // remove hostId from friend's outgoing requests + guard let friendDocument = friendSnapshot.data() else { + throw FriendRequestError.invalidData(reason: "Friend document not found") + } + guard var friendsOut = friendDocument["friendOut"] as? [String: [String]] else { throw FriendRequestError.invalidData(reason: "No outgoing request data for friend")} + guard friendsOut.removeValue(forKey: hostId) != nil else { + throw FriendRequestError.invalidData(reason: "Host is not in friend's outgoing requests") + } + batch.updateData(["friendOut": friendsOut], forDocument: friendDocRef) + + // remove friendId from host's incoming requests + guard let hostDocument = hostSnapshot.data() else { + throw FriendRequestError.invalidData(reason: "Host document not found") + } + guard var hostIn = hostDocument["friendIn"] as? [String: [String]] else { throw FriendRequestError.invalidData(reason: "No incoming request data for host")} + guard hostIn.removeValue(forKey: friendId) != nil else { + throw FriendRequestError.invalidData(reason: "Friend is not in host's incoming requests") + } + batch.updateData(["friendIn": hostIn], forDocument: hostDocRef) + + try await batch.commit() + + } catch FriendRequestError.invalidData(let reason) { + print("Data Error - \(reason)") + throw FriendRequestError.invalidData(reason: reason) + } catch FriendRequestError.userError(let reason) { + print("User Error - \(reason)") + throw FriendRequestError.userError(reason: reason) + } + catch { + print("Unexpected Error") + throw error + } } } From 0bfe1943ec050d2e678dff7ebde8b5f8537461e2 Mon Sep 17 00:00:00 2001 From: anthonyle51 Date: Sat, 22 Feb 2025 15:09:21 -0500 Subject: [PATCH 5/6] variable name typo --- .../ViewModels/FriendsViewModel.swift | 4 ++-- GaugeTests/GaugeTests.swift | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/Gauge/Profiles+Search/ViewModels/FriendsViewModel.swift b/Gauge/Profiles+Search/ViewModels/FriendsViewModel.swift index df9ed6a..5dc9e7c 100644 --- a/Gauge/Profiles+Search/ViewModels/FriendsViewModel.swift +++ b/Gauge/Profiles+Search/ViewModels/FriendsViewModel.swift @@ -170,9 +170,9 @@ class FriendsViewModel: ObservableObject { // add friend as host's friends var hostFriends = hostDocument["friends"] as? [String: [String]] ?? [:] - guard let friendUserName = friendDocument["userName"] as? String else { throw FriendRequestError.userError(reason: "Friend document does not contain userName")} + guard let friendUsername = friendDocument["username"] as? String else { throw FriendRequestError.userError(reason: "Friend document does not contain username")} let friendProfilePhoto = friendDocument["profilePhoto"] as? String ?? "" - hostFriends[friendId] = [friendUserName, friendProfilePhoto] + hostFriends[friendId] = [friendUsername, friendProfilePhoto] batch.updateData(["friendIn": hostIn, "friends": hostFriends], forDocument: hostDocRef) try await batch.commit() diff --git a/GaugeTests/GaugeTests.swift b/GaugeTests/GaugeTests.swift index b355439..328586a 100644 --- a/GaugeTests/GaugeTests.swift +++ b/GaugeTests/GaugeTests.swift @@ -7,6 +7,7 @@ import XCTest @testable import Gauge +import FirebaseFirestore final class GaugeTests: XCTestCase { @@ -50,4 +51,21 @@ final class GaugeTests: XCTestCase { let user = await viewModel.getUserFromId(userId: userId) print(user) } + + + func testFriendRequest() async { + let friendUser = User(userId: "thing2", username: "dummy", email: "dummy") + let hostUser = User(userId: "thing1", username: "dummy", email: "dummy") + let viewModel = FriendsViewModel(user: hostUser) + + let friendId = friendUser.id + let hostId = hostUser.id + do { + try await viewModel.acceptFriendRequest(friendId: friendId, hostId: hostId) + print("Friend request accepted successfully.") + } catch { + print("error") + } + + } } From 05ff77a450260e99e4e796583752e4eda4e37f8b Mon Sep 17 00:00:00 2001 From: anthonyle51 Date: Sat, 22 Feb 2025 15:27:34 -0500 Subject: [PATCH 6/6] host added to friend's friends --- .../ViewModels/FriendsViewModel.swift | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/Gauge/Profiles+Search/ViewModels/FriendsViewModel.swift b/Gauge/Profiles+Search/ViewModels/FriendsViewModel.swift index 5dc9e7c..3a2a780 100644 --- a/Gauge/Profiles+Search/ViewModels/FriendsViewModel.swift +++ b/Gauge/Profiles+Search/ViewModels/FriendsViewModel.swift @@ -141,7 +141,6 @@ class FriendsViewModel: ObservableObject { func acceptFriendRequest(friendId: String, hostId: String) async throws { do { - let batch = Firebase.db.batch() let friendDocRef = Firebase.db.collection("USERS").document(friendId) let hostDocRef = Firebase.db.collection("USERS").document(hostId) async let friendDocumentSnapshot = friendDocRef.getDocument() @@ -157,8 +156,6 @@ class FriendsViewModel: ObservableObject { guard friendsOut.removeValue(forKey: hostId) != nil else { throw FriendRequestError.invalidData(reason: "Host is not in friend's outgoing requests") } - batch.updateData(["friendOut": friendsOut], forDocument: friendDocRef) - // remove friendId from host's incoming requests guard let hostDocument = hostSnapshot.data() else { throw FriendRequestError.invalidData(reason: "Host document not found") @@ -168,15 +165,23 @@ class FriendsViewModel: ObservableObject { throw FriendRequestError.invalidData(reason: "Friend is not in host's incoming requests") } - // add friend as host's friends + // add host to freind's friends + var friendFriends = friendDocument["friends"] as? [String: [String]] ?? [:] + guard let hostUsername = hostDocument["username"] as? String else { throw FriendRequestError.userError(reason: "Host document does not contain username")} + let hostProfilePhoto = hostDocument["profilePhoto"] as? String ?? "" + friendFriends[hostId] = [hostUsername, hostProfilePhoto] + + // add friend to host's friends var hostFriends = hostDocument["friends"] as? [String: [String]] ?? [:] guard let friendUsername = friendDocument["username"] as? String else { throw FriendRequestError.userError(reason: "Friend document does not contain username")} let friendProfilePhoto = friendDocument["profilePhoto"] as? String ?? "" hostFriends[friendId] = [friendUsername, friendProfilePhoto] + + let batch = Firebase.db.batch() + batch.updateData(["friendOut": friendsOut,"friends": friendFriends], forDocument: friendDocRef) batch.updateData(["friendIn": hostIn, "friends": hostFriends], forDocument: hostDocRef) try await batch.commit() - } catch FriendRequestError.invalidData(let reason) { print("Data Error - \(reason)") throw FriendRequestError.invalidData(reason: reason)