-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #29 from SwEnt-Group8/feat/mvvm-for-users
Feat/MVVM for users
- Loading branch information
Showing
7 changed files
with
1,280 additions
and
0 deletions.
There are no files selected for viewing
9 changes: 9 additions & 0 deletions
9
app/src/main/java/com/android/streetworkapp/model/user/User.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.android.streetworkapp.model.user | ||
|
||
data class User( | ||
val uid: String, | ||
val username: String, | ||
val email: String, | ||
val score: Int, | ||
val friends: List<String> | ||
) |
26 changes: 26 additions & 0 deletions
26
app/src/main/java/com/android/streetworkapp/model/user/UserRepository.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package com.android.streetworkapp.model.user | ||
/** A repository interface for user data. */ | ||
interface UserRepository { | ||
|
||
fun getNewUid(): String | ||
|
||
suspend fun getUserByUid(uid: String): User? | ||
|
||
suspend fun getUserByEmail(email: String): User? | ||
|
||
suspend fun getUserByUserName(userName: String): User? | ||
|
||
suspend fun getFriendsByUid(uid: String): List<User>? | ||
|
||
suspend fun addUser(user: User) | ||
|
||
suspend fun updateUserScore(uid: String, newScore: Int) | ||
|
||
suspend fun increaseUserScore(uid: String, points: Int) | ||
|
||
suspend fun addFriend(uid: String, friendUid: String) | ||
|
||
suspend fun removeFriend(uid: String, friendUid: String) | ||
|
||
suspend fun deleteUserByUid(uid: String) | ||
} |
265 changes: 265 additions & 0 deletions
265
app/src/main/java/com/android/streetworkapp/model/user/UserRepositoryFirestore.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,265 @@ | ||
package com.android.streetworkapp.model.user | ||
|
||
import android.util.Log | ||
import com.google.firebase.firestore.DocumentSnapshot | ||
import com.google.firebase.firestore.FieldPath | ||
import com.google.firebase.firestore.FieldValue | ||
import com.google.firebase.firestore.FirebaseFirestore | ||
import kotlinx.coroutines.tasks.await | ||
|
||
private const val UID_EMPTY = "UID must not be empty" | ||
|
||
class UserRepositoryFirestore(private val db: FirebaseFirestore) : UserRepository { | ||
// Setup the collection path | ||
companion object { | ||
private const val COLLECTION_PATH = "users" | ||
} | ||
|
||
/** | ||
* Generates a new unique ID for a user. | ||
* | ||
* @return A new unique ID (document ID) generated by Firestore. | ||
*/ | ||
override fun getNewUid(): String { | ||
return db.collection(COLLECTION_PATH).document().id | ||
} | ||
|
||
/** | ||
* Retrieves a user from Firestore based on the provided ID. | ||
* | ||
* @param uid The unique ID of the user to retrieve. | ||
* @return The User object if found, or null if the user doesn't exist or an error occurs. | ||
*/ | ||
override suspend fun getUserByUid(uid: String): User? { | ||
require(uid.isNotEmpty()) { UID_EMPTY } | ||
return try { | ||
val document = db.collection("users").document(uid).get().await() | ||
documentToUser(document) | ||
} catch (e: Exception) { | ||
Log.e("FirestoreError", "Error getting user with ID: $uid. Reason: ${e.message}") | ||
null | ||
} | ||
} | ||
|
||
/** | ||
* Retrieves a user from Firestore based on the provided email. | ||
* | ||
* @param email The email of the user to retrieve. | ||
* @return The User object if found, or null if the user doesn't exist or an error occurs. | ||
*/ | ||
override suspend fun getUserByEmail(email: String): User? { | ||
require(email.isNotEmpty()) { "Email must not be empty" } | ||
return try { | ||
val querySnapshot = db.collection(COLLECTION_PATH).whereEqualTo("email", email).get().await() | ||
|
||
if (querySnapshot.documents.isNotEmpty()) { | ||
documentToUser(querySnapshot.documents[0]) // Return the first match | ||
} else { | ||
null // No user with that email found | ||
} | ||
} catch (e: Exception) { | ||
Log.e("FirestoreError", "Error getting user by email: $email. Reason: ${e.message}") | ||
null | ||
} | ||
} | ||
|
||
/** | ||
* Retrieves a user from Firestore based on the provided username. | ||
* | ||
* @param userName The username of the user to retrieve. | ||
* @return The User object if found, or null if the user doesn't exist or an error occurs. | ||
*/ | ||
override suspend fun getUserByUserName(userName: String): User? { | ||
require(userName.isNotEmpty()) { "Username must not be empty" } | ||
return try { | ||
val querySnapshot = | ||
db.collection(COLLECTION_PATH).whereEqualTo("username", userName).get().await() | ||
|
||
if (querySnapshot.documents.isNotEmpty()) { | ||
documentToUser(querySnapshot.documents[0]) | ||
} else { | ||
null | ||
} | ||
} catch (e: Exception) { | ||
Log.e("FirestoreError", "Error getting user by username: $userName. Reason: ${e.message}") | ||
null | ||
} | ||
} | ||
|
||
/** | ||
* Retrieves the friends of a user from Firestore based on the provided user ID (uid). | ||
* | ||
* @param uid The unique ID of the user whose friends are being retrieved. | ||
* @return A list of User objects representing the user's friends, or null if an error occurs. | ||
*/ | ||
override suspend fun getFriendsByUid(uid: String): List<User>? { | ||
require(uid.isNotEmpty()) { UID_EMPTY } | ||
return try { | ||
// Get the user's document first to retrieve the list of friend UIDs | ||
val document = db.collection(COLLECTION_PATH).document(uid).get().await() | ||
val friendIds = document["friends"] as? List<*> ?: emptyList<String>() | ||
|
||
if (friendIds.isNotEmpty()) { | ||
// Now fetch all the friends' user documents | ||
val friendsQuery = | ||
db.collection(COLLECTION_PATH).whereIn(FieldPath.documentId(), friendIds).get().await() | ||
|
||
friendsQuery.documents.mapNotNull { | ||
documentToUser(it) | ||
} // Convert documents to User objects | ||
} else { | ||
emptyList() // No friends found | ||
} | ||
} catch (e: Exception) { | ||
Log.e("FirestoreError", "Error getting friends for user ID: $uid. Reason: ${e.message}") | ||
null | ||
} | ||
} | ||
|
||
/** | ||
* Adds a new user to Firestore. | ||
* | ||
* @param user The User object to add to Firestore. | ||
*/ | ||
override suspend fun addUser(user: User) { | ||
require(user.uid.isNotEmpty()) { UID_EMPTY } | ||
try { | ||
db.collection("users").document(user.uid).set(user).await() | ||
} catch (e: Exception) { | ||
Log.e("FirestoreError", "Error adding user: ${e.message}") | ||
} | ||
} | ||
|
||
/** | ||
* Updates the user's score in Firestore. | ||
* | ||
* @param uid The unique ID of the user whose score is being updated. | ||
* @param newScore The new score to set for the user. | ||
*/ | ||
override suspend fun updateUserScore(uid: String, newScore: Int) { | ||
require(uid.isNotEmpty()) { UID_EMPTY } | ||
require(newScore >= 0) { "Score must be a non-negative integer" } | ||
try { | ||
db.collection("users").document(uid).update("score", newScore).await() | ||
} catch (e: Exception) { | ||
Log.e( | ||
"FirestoreError", "Error updating score of the user with ID: $uid. Reason: ${e.message}") | ||
} | ||
} | ||
|
||
/** | ||
* Increases the user's score in Firestore by a specified number of points. | ||
* | ||
* @param uid The unique ID of the user whose score is being increased. | ||
* @param points The number of points to add to the user's score. | ||
*/ | ||
override suspend fun increaseUserScore(uid: String, points: Int) { | ||
require(uid.isNotEmpty()) { UID_EMPTY } | ||
require(points >= 0) { "Points must be a non-negative integer" } | ||
try { | ||
db.collection("users") | ||
.document(uid) | ||
.update("score", FieldValue.increment(points.toLong())) | ||
.await() | ||
} catch (e: Exception) { | ||
Log.e( | ||
"FirestoreError", | ||
"Error increasing score of the user with ID: $uid. Reason: ${e.message}") | ||
} | ||
} | ||
|
||
/** | ||
* Adds a friend to both the user's and friend's friend lists in Firestore. | ||
* | ||
* @param uid The unique ID of the user. | ||
* @param friendUid The ID of the friend to add to the user's friend list. | ||
*/ | ||
override suspend fun addFriend(uid: String, friendUid: String) { | ||
require(uid.isNotEmpty()) { UID_EMPTY } | ||
require(friendUid.isNotEmpty()) { "Friend UID must not be empty" } | ||
try { | ||
// Start a Firestore batch operation for both updates | ||
val batch = db.batch() | ||
|
||
// Add friendId to the user's friends list | ||
val userRef = db.collection("users").document(uid) | ||
batch.update(userRef, "friends", FieldValue.arrayUnion(friendUid)) | ||
|
||
// Add uid to the friend's friends list and commit the batch | ||
val friendRef = db.collection("users").document(friendUid) | ||
batch.update(friendRef, "friends", FieldValue.arrayUnion(uid)) | ||
batch.commit().await() | ||
} catch (e: Exception) { | ||
Log.e("FirestoreError", "Error adding friend: ${e.message}") | ||
} | ||
} | ||
|
||
/** | ||
* Removes a friend from the user's friend list in Firestore. | ||
* | ||
* @param uid The unique ID of the user. | ||
* @param friendUid The ID of the friend to remove from the user's friend list. | ||
*/ | ||
override suspend fun removeFriend(uid: String, friendUid: String) { | ||
require(uid.isNotEmpty()) { UID_EMPTY } | ||
require(friendUid.isNotEmpty()) { "Friend UID must not be empty" } | ||
try { | ||
// Start a Firestore batch operation for both removals | ||
val batch = db.batch() | ||
|
||
// Remove friendId from the user's friends list | ||
val userRef = db.collection("users").document(uid) | ||
batch.update(userRef, "friends", FieldValue.arrayRemove(friendUid)) | ||
|
||
// Remove uid from the friend's friends list and commit the batch | ||
val friendRef = db.collection("users").document(friendUid) | ||
batch.update(friendRef, "friends", FieldValue.arrayRemove(uid)) | ||
batch.commit().await() | ||
} catch (e: Exception) { | ||
Log.e("FirestoreError", "Error removing friend: ${e.message}") | ||
} | ||
} | ||
|
||
/** | ||
* Deletes a user from Firestore based on the provided UID. | ||
* | ||
* @param uid The unique UID of the user to delete. | ||
*/ | ||
override suspend fun deleteUserByUid(uid: String) { | ||
require(uid.isNotEmpty()) { UID_EMPTY } | ||
try { | ||
db.collection("users").document(uid).delete().await() | ||
} catch (e: Exception) { | ||
Log.e("FirestoreError", "Error deleting user: ${e.message}") | ||
} | ||
} | ||
|
||
/** | ||
* Converts a Firestore DocumentSnapshot into a User object. | ||
* | ||
* @param document The Firestore DocumentSnapshot containing user data. | ||
* @return A User object if the document is valid, or null if the document cannot be converted. | ||
*/ | ||
internal fun documentToUser(document: DocumentSnapshot): User? { | ||
return try { | ||
val uid = document.id | ||
val username = document.getString("username") ?: return null | ||
val email = document.getString("email") ?: return null | ||
val score = document.getLong("score")?.toInt() ?: 0 | ||
// Safely handle the 'friends' field | ||
val friends = | ||
try { | ||
document["friends"] as? List<*> ?: emptyList<String>() | ||
} catch (e: Exception) { | ||
Log.e("FirestoreError", "Error retrieving friends list", e) | ||
emptyList<String>() // Return an empty list in case of an exception | ||
} | ||
|
||
val validFriends = friends.filterIsInstance<String>() | ||
User(uid = uid, username = username, email = email, score = score, friends = validFriends) | ||
} catch (e: Exception) { | ||
Log.e("FirestoreError", "Error converting document to User", e) | ||
null | ||
} | ||
} | ||
} |
Oops, something went wrong.