QOL improvements & friendsSessionsDAOstuff
This commit is contained in:
		
							parent
							
								
									10c86c9bf0
								
							
						
					
					
						commit
						a814bd74d7
					
				
					 9 changed files with 154 additions and 46 deletions
				
			
		|  | @ -0,0 +1,44 @@ | ||||||
|  | package be.ugent.sel.studeez.common.composable | ||||||
|  | 
 | ||||||
|  | import androidx.compose.foundation.background | ||||||
|  | import androidx.compose.foundation.layout.Box | ||||||
|  | import androidx.compose.foundation.layout.size | ||||||
|  | import androidx.compose.foundation.shape.CircleShape | ||||||
|  | import androidx.compose.material.Icon | ||||||
|  | import androidx.compose.material.MaterialTheme | ||||||
|  | import androidx.compose.material.icons.Icons | ||||||
|  | import androidx.compose.material.icons.filled.Person | ||||||
|  | import androidx.compose.runtime.Composable | ||||||
|  | import androidx.compose.ui.Alignment | ||||||
|  | import androidx.compose.ui.Modifier | ||||||
|  | import androidx.compose.ui.res.stringResource | ||||||
|  | import androidx.compose.ui.tooling.preview.Preview | ||||||
|  | import androidx.compose.ui.unit.dp | ||||||
|  | import be.ugent.sel.studeez.R | ||||||
|  | import be.ugent.sel.studeez.ui.theme.StudeezTheme | ||||||
|  | 
 | ||||||
|  | @Composable | ||||||
|  | fun ProfilePicture() { | ||||||
|  |     Box( | ||||||
|  |         modifier = Modifier | ||||||
|  |             .size(40.dp) | ||||||
|  |             .background(MaterialTheme.colors.primary, CircleShape) | ||||||
|  |     ) { | ||||||
|  |         Icon( | ||||||
|  |             imageVector = Icons.Default.Person, | ||||||
|  |             contentDescription = stringResource(id = R.string.username), | ||||||
|  |             modifier = Modifier | ||||||
|  |                 .size(30.dp) | ||||||
|  |                 .align(Alignment.Center), | ||||||
|  |             tint = MaterialTheme.colors.onPrimary | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @Preview | ||||||
|  | @Composable | ||||||
|  | fun ProfilePicturePreview() { | ||||||
|  |     StudeezTheme { | ||||||
|  |         ProfilePicture() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,6 @@ | ||||||
|  | package be.ugent.sel.studeez.data.remote | ||||||
|  | 
 | ||||||
|  | object FirebaseSessionReport { | ||||||
|  |     const val STUDYTIME: String = "studyTime" | ||||||
|  |     const val ENDTIME: String = "endTime" | ||||||
|  | } | ||||||
|  | @ -1,12 +1,19 @@ | ||||||
| package be.ugent.sel.studeez.domain | package be.ugent.sel.studeez.domain | ||||||
| 
 | 
 | ||||||
| import be.ugent.sel.studeez.data.local.models.SessionReport | import be.ugent.sel.studeez.data.local.models.SessionReport | ||||||
|  | import be.ugent.sel.studeez.data.local.models.User | ||||||
| import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo | import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo | ||||||
| import kotlinx.coroutines.flow.Flow | import kotlinx.coroutines.flow.Flow | ||||||
| 
 | 
 | ||||||
| interface SessionDAO { | interface SessionDAO { | ||||||
| 
 | 
 | ||||||
|     fun getSessions(): Flow<List<SessionReport>> |     fun getSessions(): Flow<List<SessionReport>> | ||||||
|  |     suspend fun getSessionsOfUser(userId: String): List<SessionReport> | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Return a list of pairs, containing the username and all the studysessions of that user. | ||||||
|  |      */ | ||||||
|  |     fun getFriendsSessions(): Flow<List<Pair<String,List<SessionReport>>>> | ||||||
| 
 | 
 | ||||||
|     fun saveSession(newSessionReport: SessionReport) |     fun saveSession(newSessionReport: SessionReport) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -27,6 +27,10 @@ interface UserDAO { | ||||||
|         userId: String |         userId: String | ||||||
|     ): Flow<User> |     ): Flow<User> | ||||||
| 
 | 
 | ||||||
|  |     suspend fun getUsername( | ||||||
|  |         userId: String | ||||||
|  |     ): String | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * @return information on the currently logged in user. |      * @return information on the currently logged in user. | ||||||
|      */ |      */ | ||||||
|  |  | ||||||
|  | @ -1,19 +1,33 @@ | ||||||
| package be.ugent.sel.studeez.domain.implementation | package be.ugent.sel.studeez.domain.implementation | ||||||
| 
 | 
 | ||||||
| import be.ugent.sel.studeez.data.local.models.SessionReport | import be.ugent.sel.studeez.data.local.models.SessionReport | ||||||
|  | import be.ugent.sel.studeez.data.local.models.User | ||||||
| import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo | import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo | ||||||
|  | import be.ugent.sel.studeez.data.remote.FirebaseSessionReport | ||||||
|  | import be.ugent.sel.studeez.data.remote.FirebaseSessionReport.ENDTIME | ||||||
|  | import be.ugent.sel.studeez.data.remote.FirebaseSessionReport.STUDYTIME | ||||||
| import be.ugent.sel.studeez.domain.AccountDAO | import be.ugent.sel.studeez.domain.AccountDAO | ||||||
|  | import be.ugent.sel.studeez.domain.FriendshipDAO | ||||||
| import be.ugent.sel.studeez.domain.SessionDAO | import be.ugent.sel.studeez.domain.SessionDAO | ||||||
|  | import be.ugent.sel.studeez.domain.UserDAO | ||||||
|  | import be.ugent.sel.studeez.domain.implementation.FirebaseCollections.SESSION_COLLECTION | ||||||
|  | import be.ugent.sel.studeez.domain.implementation.FirebaseCollections.USER_COLLECTION | ||||||
|  | import com.google.firebase.Timestamp | ||||||
| import com.google.firebase.firestore.CollectionReference | import com.google.firebase.firestore.CollectionReference | ||||||
| import com.google.firebase.firestore.FirebaseFirestore | import com.google.firebase.firestore.FirebaseFirestore | ||||||
|  | import com.google.firebase.firestore.ktx.getField | ||||||
| import com.google.firebase.firestore.ktx.snapshots | import com.google.firebase.firestore.ktx.snapshots | ||||||
| import kotlinx.coroutines.flow.Flow | import kotlinx.coroutines.flow.Flow | ||||||
|  | import kotlinx.coroutines.flow.emptyFlow | ||||||
| import kotlinx.coroutines.flow.map | import kotlinx.coroutines.flow.map | ||||||
|  | import kotlinx.coroutines.tasks.await | ||||||
| import javax.inject.Inject | import javax.inject.Inject | ||||||
| 
 | 
 | ||||||
| class FirebaseSessionDAO @Inject constructor( | class FirebaseSessionDAO @Inject constructor( | ||||||
|     private val firestore: FirebaseFirestore, |     private val firestore: FirebaseFirestore, | ||||||
|     private val auth: AccountDAO |     private val auth: AccountDAO, | ||||||
|  |     private val userDAO: UserDAO, | ||||||
|  |     private val friendshipDAO: FriendshipDAO | ||||||
| ) : SessionDAO { | ) : SessionDAO { | ||||||
| 
 | 
 | ||||||
|     override fun getSessions(): Flow<List<SessionReport>> { |     override fun getSessions(): Flow<List<SessionReport>> { | ||||||
|  | @ -22,6 +36,34 @@ class FirebaseSessionDAO @Inject constructor( | ||||||
|             .map { it.toObjects(SessionReport::class.java) } |             .map { it.toObjects(SessionReport::class.java) } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     override suspend fun getSessionsOfUser(userId: String): List<SessionReport> { | ||||||
|  |         val collection = firestore.collection(USER_COLLECTION) | ||||||
|  |             .document(userId) | ||||||
|  |             .collection(SESSION_COLLECTION) | ||||||
|  |             .get().await() | ||||||
|  |         val list: MutableList<SessionReport> = mutableListOf() | ||||||
|  |         for (document in collection) { | ||||||
|  |             val id = document.id | ||||||
|  |             val studyTime: Int = document.getField<Int>(STUDYTIME)!! | ||||||
|  |             val endTime: Timestamp = document.getField<Timestamp>(ENDTIME)!! | ||||||
|  |             list.add(SessionReport(id, studyTime, endTime)) | ||||||
|  |         } | ||||||
|  |         return list | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override fun getFriendsSessions(): Flow<List<Pair<String, List<SessionReport>>>> { | ||||||
|  |         return friendshipDAO.getAllFriendships(auth.currentUserId) | ||||||
|  |             .map { friendships -> | ||||||
|  |                 friendships.map { friendship -> | ||||||
|  |                     val userId: String = friendship.friendId | ||||||
|  |                     val username = userDAO.getUsername(userId) | ||||||
|  |                     val userSessions = getSessionsOfUser(userId) | ||||||
|  | 
 | ||||||
|  |                     Pair(username, userSessions) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     override fun saveSession(newSessionReport: SessionReport) { |     override fun saveSession(newSessionReport: SessionReport) { | ||||||
|         currentUserSessionsCollection().add(newSessionReport) |         currentUserSessionsCollection().add(newSessionReport) | ||||||
|     } |     } | ||||||
|  | @ -31,7 +73,7 @@ class FirebaseSessionDAO @Inject constructor( | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun currentUserSessionsCollection(): CollectionReference = |     private fun currentUserSessionsCollection(): CollectionReference = | ||||||
|         firestore.collection(FirebaseCollections.USER_COLLECTION) |         firestore.collection(USER_COLLECTION) | ||||||
|             .document(auth.currentUserId) |             .document(auth.currentUserId) | ||||||
|             .collection(FirebaseCollections.SESSION_COLLECTION) |             .collection(SESSION_COLLECTION) | ||||||
| } | } | ||||||
|  | @ -61,6 +61,13 @@ class FirebaseUserDAO @Inject constructor( | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     override suspend fun getUsername(userId: String): String { | ||||||
|  |         val user = firestore.collection(USER_COLLECTION) | ||||||
|  |             .document(userId) | ||||||
|  |             .get().await() | ||||||
|  |         return user.getString(USERNAME)!! | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     override suspend fun getLoggedInUser(): User { |     override suspend fun getLoggedInUser(): User { | ||||||
|         val userDocument = currentUserDocument().get().await() |         val userDocument = currentUserDocument().get().await() | ||||||
|         return User( |         return User( | ||||||
|  |  | ||||||
|  | @ -14,7 +14,6 @@ import androidx.compose.runtime.* | ||||||
| import androidx.compose.ui.Alignment | import androidx.compose.ui.Alignment | ||||||
| import androidx.compose.ui.Modifier | import androidx.compose.ui.Modifier | ||||||
| import androidx.compose.ui.graphics.vector.ImageVector | import androidx.compose.ui.graphics.vector.ImageVector | ||||||
| import androidx.compose.ui.res.painterResource |  | ||||||
| import androidx.compose.ui.res.stringResource | import androidx.compose.ui.res.stringResource | ||||||
| import androidx.compose.ui.res.vectorResource | import androidx.compose.ui.res.vectorResource | ||||||
| import androidx.compose.ui.text.style.TextOverflow | import androidx.compose.ui.text.style.TextOverflow | ||||||
|  | @ -23,6 +22,7 @@ import androidx.compose.ui.unit.dp | ||||||
| import androidx.compose.ui.unit.sp | import androidx.compose.ui.unit.sp | ||||||
| import be.ugent.sel.studeez.R | import be.ugent.sel.studeez.R | ||||||
| import be.ugent.sel.studeez.common.composable.BasicButton | import be.ugent.sel.studeez.common.composable.BasicButton | ||||||
|  | import be.ugent.sel.studeez.common.composable.ProfilePicture | ||||||
| import be.ugent.sel.studeez.common.composable.SearchField | import be.ugent.sel.studeez.common.composable.SearchField | ||||||
| import be.ugent.sel.studeez.common.composable.drawer.DrawerEntry | import be.ugent.sel.studeez.common.composable.drawer.DrawerEntry | ||||||
| import be.ugent.sel.studeez.common.ext.basicButton | import be.ugent.sel.studeez.common.ext.basicButton | ||||||
|  | @ -162,29 +162,21 @@ fun FriendsEntry( | ||||||
|     viewProfile: (String) -> Unit, |     viewProfile: (String) -> Unit, | ||||||
|     removeFriend: (Friendship) -> Unit |     removeFriend: (Friendship) -> Unit | ||||||
| ) { | ) { | ||||||
|     // TODO Styling |  | ||||||
|     Row ( |     Row ( | ||||||
|         modifier = Modifier |         modifier = Modifier | ||||||
|             .fillMaxWidth() |             .fillMaxWidth() | ||||||
|  |             .padding(horizontal = 15.dp, vertical = 7.dp), | ||||||
|     ) { |     ) { | ||||||
|         Box( |         Box( | ||||||
|             modifier = Modifier |             modifier = Modifier | ||||||
|                 .fillMaxWidth(0.15f) |                 .padding(vertical = 4.dp) | ||||||
|                 .background(MaterialTheme.colors.primary, CircleShape) |  | ||||||
|         ) { |         ) { | ||||||
|             Icon( |             ProfilePicture() | ||||||
|                 painter = painterResource(id = R.drawable.ic_visibility_on), |  | ||||||
|                 contentDescription = null, |  | ||||||
|                 modifier = Modifier |  | ||||||
|                     .fillMaxHeight() |  | ||||||
|                     .align(Alignment.Center), |  | ||||||
|                 tint = MaterialTheme.colors.onPrimary |  | ||||||
|             ) |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         Box ( |         Box ( | ||||||
|             modifier = Modifier |             modifier = Modifier | ||||||
|                 .fillMaxWidth(0.65f) |                 .fillMaxWidth() | ||||||
|         ) { |         ) { | ||||||
|             Column ( |             Column ( | ||||||
|                 modifier = Modifier |                 modifier = Modifier | ||||||
|  | @ -203,10 +195,10 @@ fun FriendsEntry( | ||||||
|                     overflow = TextOverflow.Ellipsis |                     overflow = TextOverflow.Ellipsis | ||||||
|                 ) |                 ) | ||||||
|             } |             } | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|             Box( |             Box( | ||||||
|             modifier = Modifier.fillMaxWidth(0.15f) |                 modifier = Modifier.fillMaxWidth(), | ||||||
|  |                 contentAlignment = Alignment.CenterEnd | ||||||
|             ) { |             ) { | ||||||
|                 FriendsOverviewDropDown( |                 FriendsOverviewDropDown( | ||||||
|                     friendship = friendship, |                     friendship = friendship, | ||||||
|  | @ -215,6 +207,7 @@ fun FriendsEntry( | ||||||
|                 ) |                 ) | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @Preview | @Preview | ||||||
|  |  | ||||||
|  | @ -13,7 +13,6 @@ import androidx.compose.runtime.* | ||||||
| import androidx.compose.ui.Alignment | import androidx.compose.ui.Alignment | ||||||
| import androidx.compose.ui.Modifier | import androidx.compose.ui.Modifier | ||||||
| import androidx.compose.ui.graphics.vector.ImageVector | import androidx.compose.ui.graphics.vector.ImageVector | ||||||
| import androidx.compose.ui.res.painterResource |  | ||||||
| import androidx.compose.ui.res.stringResource | import androidx.compose.ui.res.stringResource | ||||||
| import androidx.compose.ui.res.vectorResource | import androidx.compose.ui.res.vectorResource | ||||||
| import androidx.compose.ui.text.style.TextOverflow | import androidx.compose.ui.text.style.TextOverflow | ||||||
|  | @ -21,12 +20,14 @@ import androidx.compose.ui.tooling.preview.Preview | ||||||
| import androidx.compose.ui.unit.dp | import androidx.compose.ui.unit.dp | ||||||
| import androidx.compose.ui.unit.sp | import androidx.compose.ui.unit.sp | ||||||
| import be.ugent.sel.studeez.R | import be.ugent.sel.studeez.R | ||||||
|  | import be.ugent.sel.studeez.common.composable.ProfilePicture | ||||||
| import be.ugent.sel.studeez.common.composable.SearchField | import be.ugent.sel.studeez.common.composable.SearchField | ||||||
| import be.ugent.sel.studeez.common.composable.drawer.DrawerEntry | import be.ugent.sel.studeez.common.composable.drawer.DrawerEntry | ||||||
| import be.ugent.sel.studeez.data.local.models.User | import be.ugent.sel.studeez.data.local.models.User | ||||||
| import be.ugent.sel.studeez.resources | import be.ugent.sel.studeez.resources | ||||||
| import be.ugent.sel.studeez.ui.theme.StudeezTheme | import be.ugent.sel.studeez.ui.theme.StudeezTheme | ||||||
| import kotlinx.coroutines.flow.* | import kotlinx.coroutines.flow.Flow | ||||||
|  | import kotlinx.coroutines.flow.flowOf | ||||||
| import be.ugent.sel.studeez.R.string as AppText | import be.ugent.sel.studeez.R.string as AppText | ||||||
| 
 | 
 | ||||||
| data class SearchFriendsActions( | data class SearchFriendsActions( | ||||||
|  | @ -153,25 +154,19 @@ fun UserEntry( | ||||||
|     Row( |     Row( | ||||||
|         modifier = Modifier |         modifier = Modifier | ||||||
|             .fillMaxWidth() |             .fillMaxWidth() | ||||||
|  |             .padding(horizontal = 15.dp, vertical = 7.dp), | ||||||
|  |         horizontalArrangement = Arrangement.spacedBy(15.dp) | ||||||
|     ) { |     ) { | ||||||
|         Box( |         Box( | ||||||
|             modifier = Modifier |             modifier = Modifier | ||||||
|                 .fillMaxWidth(0.15f) |                 .padding(vertical = 4.dp) | ||||||
|                 .background(MaterialTheme.colors.primary, CircleShape) |  | ||||||
|         ) { |         ) { | ||||||
|             Icon( |             ProfilePicture() | ||||||
|                 painter = painterResource(id = R.drawable.ic_visibility_on), |  | ||||||
|                 contentDescription = null, |  | ||||||
|                 modifier = Modifier |  | ||||||
|                     .fillMaxHeight() |  | ||||||
|                     .align(Alignment.Center), |  | ||||||
|                 tint = MaterialTheme.colors.onPrimary |  | ||||||
|             ) |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         Box ( |         Box ( | ||||||
|             modifier = Modifier |             modifier = Modifier | ||||||
|                 .fillMaxWidth(0.65f) |                 .fillMaxWidth() | ||||||
|         ) { |         ) { | ||||||
|             Column ( |             Column ( | ||||||
|                 modifier = Modifier |                 modifier = Modifier | ||||||
|  | @ -190,10 +185,10 @@ fun UserEntry( | ||||||
|                     overflow = TextOverflow.Ellipsis |                     overflow = TextOverflow.Ellipsis | ||||||
|                 ) |                 ) | ||||||
|             } |             } | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|             Box( |             Box( | ||||||
|             modifier = Modifier.fillMaxWidth(0.15f) |                 modifier = Modifier.fillMaxWidth(), | ||||||
|  |                 contentAlignment = Alignment.CenterEnd | ||||||
|             ) { |             ) { | ||||||
|                 SearchFriendsDropDown( |                 SearchFriendsDropDown( | ||||||
|                     user = user, |                     user = user, | ||||||
|  | @ -201,6 +196,7 @@ fun UserEntry( | ||||||
|                 ) |                 ) | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @Preview | @Preview | ||||||
|  |  | ||||||
|  | @ -10,6 +10,7 @@ import be.ugent.sel.studeez.screens.StudeezViewModel | ||||||
| import be.ugent.sel.studeez.screens.profile.public_profile.SelectedProfileState | import be.ugent.sel.studeez.screens.profile.public_profile.SelectedProfileState | ||||||
| import dagger.hilt.android.lifecycle.HiltViewModel | import dagger.hilt.android.lifecycle.HiltViewModel | ||||||
| import kotlinx.coroutines.flow.Flow | import kotlinx.coroutines.flow.Flow | ||||||
|  | import kotlinx.coroutines.flow.filter | ||||||
| import javax.inject.Inject | import javax.inject.Inject | ||||||
| 
 | 
 | ||||||
| @HiltViewModel | @HiltViewModel | ||||||
|  | @ -43,8 +44,16 @@ class SearchFriendsViewModel @Inject constructor( | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Get all users, except for the current user. | ||||||
|  |      */ | ||||||
|     fun getAllUsers(): Flow<List<User>> { |     fun getAllUsers(): Flow<List<User>> { | ||||||
|         return userDAO.getAllUsers() |         return userDAO.getAllUsers() | ||||||
|  |             .filter { users -> | ||||||
|  |                 users.any { user -> | ||||||
|  |                     user.id != userDAO.getCurrentUserId() | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fun goToProfile( |     fun goToProfile( | ||||||
|  |  | ||||||
		Reference in a new issue