Skip to content

Commit

Permalink
Merge pull request #32 from SwEnt-Group13/chore/firestore
Browse files Browse the repository at this point in the history
Chore/firestore
  • Loading branch information
oskar-codes authored Oct 9, 2024
2 parents a127743 + 20c578d commit 27352e7
Show file tree
Hide file tree
Showing 15 changed files with 644 additions and 72 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package com.android.unio.model.association

import com.android.unio.model.firestore.FirestoreReferenceList
import com.android.unio.model.user.User

data class Association(
val uid: String,
val url: String = "",
val acronym: String = "",
val fullName: String = "",
val description: String = "",
val members: List<String> = emptyList()
val members: FirestoreReferenceList<User>
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,10 @@ interface AssociationRepository {
fun init(onSuccess: () -> Unit)

fun getAssociations(onSuccess: (List<Association>) -> Unit, onFailure: (Exception) -> Unit)

fun getAssociationWithId(
id: String,
onSuccess: (Association) -> Unit,
onFailure: (Exception) -> Unit
)
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package com.android.unio.model.association

import com.android.unio.model.firestore.FirestorePaths.ASSOCIATION_PATH
import com.android.unio.model.firestore.FirestorePaths.USER_PATH
import com.android.unio.model.firestore.FirestoreReferenceList
import com.android.unio.model.user.UserRepositoryFirestore
import com.google.firebase.Firebase
import com.google.firebase.auth.auth
import com.google.firebase.firestore.DocumentSnapshot
Expand Down Expand Up @@ -33,18 +37,37 @@ class AssociationRepositoryFirestore(private val db: FirebaseFirestore) : Associ
.addOnFailureListener { exception -> onFailure(exception) }
}

fun hydrate(doc: DocumentSnapshot): Association {
return Association(
uid = doc.id,
url = doc.getString("url") ?: "",
acronym = doc.getString("acronym") ?: "",
fullName = doc.getString("fullName") ?: "",
description = doc.getString("description") ?: "",
members = doc.get("members") as? List<String> ?: emptyList())
override fun getAssociationWithId(
id: String,
onSuccess: (Association) -> Unit,
onFailure: (Exception) -> Unit
) {
db.collection(ASSOCIATION_PATH)
.document(id)
.get()
.addOnSuccessListener { document ->
val association = hydrate(document)
onSuccess(association)
}
.addOnFailureListener { exception -> onFailure(exception) }
}

companion object {
private const val ASSOCIATION_PATH = "associations"
private const val USER_PATH = "users"
fun hydrate(doc: DocumentSnapshot): Association {
val memberUids = doc.get("members") as? List<String> ?: emptyList()
val members =
FirestoreReferenceList.fromList(
list = memberUids,
collectionPath = USER_PATH,
hydrate = UserRepositoryFirestore::hydrate)

return Association(
uid = doc.id,
url = doc.getString("url") ?: "",
acronym = doc.getString("acronym") ?: "",
fullName = doc.getString("fullName") ?: "",
description = doc.getString("description") ?: "",
members = members)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package com.android.unio.model.association

import com.android.unio.model.firestore.FirestorePaths.USER_PATH
import com.android.unio.model.firestore.FirestoreReferenceList
import com.android.unio.model.user.UserRepositoryFirestore

enum class AssociationType {
MUSIC,
FESTIVALS,
Expand All @@ -12,6 +16,11 @@ enum class AssociationType {

data class MockAssociation(val association: Association, val type: AssociationType)

val emptyMembers = {
FirestoreReferenceList.empty(
collectionPath = USER_PATH, hydrate = UserRepositoryFirestore::hydrate)
}

val mockAssociations =
listOf(
MockAssociation(
Expand All @@ -21,7 +30,7 @@ val mockAssociations =
fullName = "Musical Association",
description =
"AGEPoly Commission – stimulation of the practice of music on the campus",
members = emptyList()),
members = emptyMembers()),
AssociationType.MUSIC),
MockAssociation(
Association(
Expand All @@ -30,46 +39,46 @@ val mockAssociations =
fullName = "Nuit De la Magistrale Association",
description =
"AGEPoly Commission – party following the formal Magistrale Graduation Ceremony",
members = emptyList()),
members = emptyMembers()),
AssociationType.FESTIVALS),
MockAssociation(
Association(
uid = "3",
acronym = "Balélec",
fullName = "Festival Balélec",
description = "Open-air unique en Suisse, organisée par des bénévoles étudiants.",
members = emptyList()),
members = emptyMembers()),
AssociationType.FESTIVALS),
MockAssociation(
Association(
uid = "4",
acronym = "Artiphys",
fullName = "Festival Artiphys",
description = "Festival à l'EPFL",
members = emptyList()),
members = emptyMembers()),
AssociationType.FESTIVALS),
MockAssociation(
Association(
uid = "5",
acronym = "Sysmic",
fullName = "Festival Sysmic",
description = "Festival à l'EPFL",
members = emptyList()),
members = emptyMembers()),
AssociationType.FESTIVALS),
MockAssociation(
Association(
uid = "6",
acronym = "IFL",
fullName = "Innovation Forum Lausanne",
description = "Innovation Forum Lausanne",
members = emptyList()),
members = emptyMembers()),
AssociationType.INNOVATION),
MockAssociation(
Association(
uid = "7",
acronym = "Clic",
fullName = "Clic Association",
description = "Association of EPFL Students of IC Faculty",
members = emptyList()),
members = emptyMembers()),
AssociationType.FACULTIES),
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.android.unio.model.firestore

object FirestorePaths {
const val ASSOCIATION_PATH = "associations"
const val USER_PATH = "users"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package com.android.unio.model.firestore

import com.google.firebase.firestore.DocumentSnapshot
import com.google.firebase.firestore.FirebaseFirestore
import com.google.firebase.firestore.ktx.firestore
import com.google.firebase.ktx.Firebase
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow

/**
* A class that represents a list of Firestore objects that are identified by their UIDs. The list
* is stored as a [StateFlow] and can be updated by calling [requestAll].
*
* In a @Composable function, the list should be accessed using:
* ```kotlin
* val list by FirestoreReferenceList.list.collectAsState()
* ```
*
* @param T The type of the objects in the list.
* @property db The [FirebaseFirestore] instance to use.
* @property collectionPath The path to the Firestore collection that contains the objects.
* @property hydrate A function that converts a [DocumentSnapshot] to a [T].
*/
class FirestoreReferenceList<T>(
private val db: FirebaseFirestore,
private val collectionPath: String,
private val hydrate: (DocumentSnapshot) -> T
) : ReferenceList {
// The internal list of UIDs.
private var _uids = mutableListOf<String>()

// The internal list of objects.
private val _list = MutableStateFlow<List<T>>(emptyList())

// The public list of objects.
val list: StateFlow<List<T>> = _list

/**
* Adds a UID to the list.
*
* @param uid The UID to add.
*/
override fun add(uid: String) {
_uids.add(uid)
}

/**
* Adds a list of UIDs to the list.
*
* @param uids The UIDs to add.
*/
override fun addAll(uids: List<String>) {
_uids.addAll(uids)
}

/** Requests all documents from Firestore and updates the list. */
override fun requestAll() {
println("Requesting all")
_list.value = emptyList()
_uids.forEach { uid ->
db.collection(collectionPath).document(uid).get().addOnSuccessListener { result ->
val item = hydrate(result)
_list.value += item
println("Added $item")
}
}
}

companion object {
/** Creates a [FirestoreReferenceList] from a list of UIDs. */
fun <T> fromList(
list: List<String>,
db: FirebaseFirestore = Firebase.firestore,
collectionPath: String,
hydrate: (DocumentSnapshot) -> T
): FirestoreReferenceList<T> {
val result = FirestoreReferenceList(db, collectionPath, hydrate)
result.addAll(list)
return result
}

/** Creates an empty [FirestoreReferenceList]. */
fun <T> empty(
db: FirebaseFirestore = Firebase.firestore,
collectionPath: String,
hydrate: (DocumentSnapshot) -> T
): FirestoreReferenceList<T> {
return FirestoreReferenceList(db, collectionPath, hydrate)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.android.unio.model.firestore

interface ReferenceList {
fun add(uid: String)

fun addAll(uids: List<String>)

fun requestAll()
}
5 changes: 3 additions & 2 deletions app/src/main/java/com/android/unio/model/user/User.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package com.android.unio.model.user

import com.android.unio.model.association.Association
import com.android.unio.model.firestore.FirestoreReferenceList

data class User(
val id: String,
val uid: String,
val name: String,
val email: String,
val followingAssociations: List<Association>
val followingAssociations: FirestoreReferenceList<Association>
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.android.unio.model.user

interface UserRepository {
fun init(onSuccess: () -> Unit)

fun getUsers(onSuccess: (List<User>) -> Unit, onFailure: (Exception) -> Unit)

fun getUserWithId(id: String, onSuccess: (User) -> Unit, onFailure: (Exception) -> Unit)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.android.unio.model.user

import com.android.unio.model.association.AssociationRepositoryFirestore
import com.android.unio.model.firestore.FirestorePaths.ASSOCIATION_PATH
import com.android.unio.model.firestore.FirestorePaths.USER_PATH
import com.android.unio.model.firestore.FirestoreReferenceList
import com.google.firebase.Firebase
import com.google.firebase.auth.auth
import com.google.firebase.firestore.DocumentSnapshot
import com.google.firebase.firestore.FirebaseFirestore

class UserRepositoryFirestore(private val db: FirebaseFirestore) : UserRepository {

override fun init(onSuccess: () -> Unit) {
Firebase.auth.addAuthStateListener {
if (it.currentUser != null) {
onSuccess()
}
}
}

override fun getUsers(onSuccess: (List<User>) -> Unit, onFailure: (Exception) -> Unit) {
db.collection(USER_PATH)
.get()
.addOnSuccessListener { result ->
val associations = result.map { hydrate(it) }
onSuccess(associations)
}
.addOnFailureListener { exception -> onFailure(exception) }
}

override fun getUserWithId(
id: String,
onSuccess: (User) -> Unit,
onFailure: (Exception) -> Unit
) {
db.collection(USER_PATH)
.document(id)
.get()
.addOnSuccessListener { document ->
val association = hydrate(document)
onSuccess(association)
}
.addOnFailureListener { exception -> onFailure(exception) }
}

companion object {
fun hydrate(doc: DocumentSnapshot): User {
val db = FirebaseFirestore.getInstance()

val followingAssociationsUids =
doc.get("followingAssociations") as? List<String> ?: emptyList()
val followingAssociations =
FirestoreReferenceList.fromList(
followingAssociationsUids,
db,
ASSOCIATION_PATH,
AssociationRepositoryFirestore::hydrate)

return User(
uid = doc.id,
name = doc.getString("name") ?: "",
email = doc.getString("email") ?: "",
followingAssociations = followingAssociations)
}
}
}
Loading

0 comments on commit 27352e7

Please sign in to comment.