QOL improvements & friendsSessionsDAOstuff

This commit is contained in:
Tibo De Peuter 2023-05-15 22:40:36 +02:00
parent 10c86c9bf0
commit a814bd74d7
9 changed files with 154 additions and 46 deletions

View file

@ -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()
}
}

View file

@ -0,0 +1,6 @@
package be.ugent.sel.studeez.data.remote
object FirebaseSessionReport {
const val STUDYTIME: String = "studyTime"
const val ENDTIME: String = "endTime"
}

View file

@ -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)

View file

@ -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.
*/ */

View file

@ -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)
} }

View file

@ -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(

View file

@ -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,16 +195,17 @@ fun FriendsEntry(
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
) )
} }
}
Box( Box(
modifier = Modifier.fillMaxWidth(0.15f) modifier = Modifier.fillMaxWidth(),
) { contentAlignment = Alignment.CenterEnd
FriendsOverviewDropDown( ) {
friendship = friendship, FriendsOverviewDropDown(
viewProfile = viewProfile, friendship = friendship,
removeFriend = removeFriend viewProfile = viewProfile,
) removeFriend = removeFriend
)
}
} }
} }
} }

View file

@ -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,15 +185,16 @@ fun UserEntry(
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
) )
} }
}
Box( Box(
modifier = Modifier.fillMaxWidth(0.15f) modifier = Modifier.fillMaxWidth(),
) { contentAlignment = Alignment.CenterEnd
SearchFriendsDropDown( ) {
user = user, SearchFriendsDropDown(
goToProfile = goToProfile user = user,
) goToProfile = goToProfile
)
}
} }
} }
} }

View file

@ -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(