From 81e777f21ddc78b54c50e98b083920d7583de2b8 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Wed, 10 May 2023 12:13:48 +0200 Subject: [PATCH 01/21] #63 Add bio to profile --- .../sel/studeez/data/local/models/User.kt | 8 ++- .../be/ugent/sel/studeez/domain/UserDAO.kt | 10 ++- .../domain/implementation/FirebaseUserDAO.kt | 21 +++++-- .../screens/profile/ProfileEditScreen.kt | 61 ++++++++++++------- .../screens/profile/ProfileEditUiState.kt | 3 +- .../screens/profile/ProfileEditViewModel.kt | 16 ++++- .../studeez/screens/profile/ProfileScreen.kt | 42 +++++++++---- .../screens/profile/ProfileViewModel.kt | 8 ++- .../screens/sign_up/SignUpViewModel.kt | 2 +- app/src/main/res/values/strings.xml | 1 + 10 files changed, 124 insertions(+), 48 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/data/local/models/User.kt b/app/src/main/java/be/ugent/sel/studeez/data/local/models/User.kt index 2fba2ce..a92bebb 100644 --- a/app/src/main/java/be/ugent/sel/studeez/data/local/models/User.kt +++ b/app/src/main/java/be/ugent/sel/studeez/data/local/models/User.kt @@ -1,3 +1,9 @@ package be.ugent.sel.studeez.data.local.models -data class User(val id: String = "") +import com.google.firebase.firestore.DocumentId + +data class User( + @DocumentId val id: String = "", + val username: String = "", + val biography: String = "" +) diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt index b96cf17..619c77b 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt @@ -1,9 +1,15 @@ package be.ugent.sel.studeez.domain +import be.ugent.sel.studeez.data.local.models.User + interface UserDAO { - suspend fun getUsername(): String? - suspend fun save(newUsername: String) + suspend fun getUser(): User + + suspend fun saveUser( + newUsername: String, + newBiography: String = "" + ) /** * Delete all references to this user in the database. Similar to the deleteCascade in diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt index 3158b88..b9421de 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt @@ -2,6 +2,7 @@ package be.ugent.sel.studeez.domain.implementation import be.ugent.sel.studeez.R import be.ugent.sel.studeez.common.snackbar.SnackbarManager +import be.ugent.sel.studeez.data.local.models.User import be.ugent.sel.studeez.domain.AccountDAO import be.ugent.sel.studeez.domain.UserDAO import com.google.firebase.firestore.DocumentReference @@ -14,12 +15,22 @@ class FirebaseUserDAO @Inject constructor( private val auth: AccountDAO ) : UserDAO { - override suspend fun getUsername(): String? { - return currentUserDocument().get().await().getString("username") + override suspend fun getUser(): User { + val userDocument = currentUserDocument().get().await() + return User( + username = userDocument.getString(USERNAME_FIELD) ?: "", + biography = userDocument.getString(BIOGRAPHY_FIELD) ?: "" + ) } - override suspend fun save(newUsername: String) { - currentUserDocument().set(mapOf("username" to newUsername)) + override suspend fun saveUser( + newUsername: String, + newBiography: String + ) { + currentUserDocument().set(mapOf( + USERNAME_FIELD to newUsername, + BIOGRAPHY_FIELD to newBiography + )) } private fun currentUserDocument(): DocumentReference = @@ -27,6 +38,8 @@ class FirebaseUserDAO @Inject constructor( companion object { private const val USER_COLLECTION = "users" + private const val USERNAME_FIELD = "username" + private const val BIOGRAPHY_FIELD = "biography" } override suspend fun deleteUserReferences() { diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditScreen.kt index c6fcbaf..950595f 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditScreen.kt @@ -1,20 +1,21 @@ package be.ugent.sel.studeez.screens.profile -import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview -import be.ugent.sel.studeez.R import be.ugent.sel.studeez.common.composable.BasicTextButton import be.ugent.sel.studeez.common.composable.LabelledInputField import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate import be.ugent.sel.studeez.common.ext.textButton import be.ugent.sel.studeez.resources import be.ugent.sel.studeez.ui.theme.StudeezTheme +import be.ugent.sel.studeez.R.string as AppText data class EditProfileActions( val onUserNameChange: (String) -> Unit, + val onBiographyChange: (String) -> Unit, val onSaveClick: () -> Unit, val onDeleteClick: () -> Unit ) @@ -25,6 +26,7 @@ fun getEditProfileActions( ): EditProfileActions { return EditProfileActions( onUserNameChange = { viewModel.onUsernameChange(it) }, + onBiographyChange = { viewModel.onBiographyChange(it) }, onSaveClick = { viewModel.onSaveClick() }, onDeleteClick = { viewModel.onDeleteClick(openAndPopUp) }, ) @@ -51,28 +53,41 @@ fun EditProfileScreen( editProfileActions: EditProfileActions, ) { SecondaryScreenTemplate( - title = resources().getString(R.string.editing_profile), + title = resources().getString(AppText.editing_profile), popUp = goBack ) { - Column { - LabelledInputField( - value = uiState.username, - onNewValue = editProfileActions.onUserNameChange, - label = R.string.username - ) - BasicTextButton( - text = R.string.save, - Modifier.textButton(), - action = { - editProfileActions.onSaveClick() - goBack() - } - ) - BasicTextButton( - text = R.string.delete_profile, - Modifier.textButton(), - action = editProfileActions.onDeleteClick - ) + LazyColumn { + item { + LabelledInputField( + value = uiState.username, + onNewValue = editProfileActions.onUserNameChange, + label = AppText.username + ) + } + item { + LabelledInputField( + value = uiState.biography, + onNewValue = editProfileActions.onBiographyChange, + label = AppText.biography + ) + } + item { + BasicTextButton( + text = AppText.save, + Modifier.textButton(), + action = { + editProfileActions.onSaveClick() + goBack() + } + ) + } + item { + BasicTextButton( + text = AppText.delete_profile, + Modifier.textButton(), + action = editProfileActions.onDeleteClick + ) + } } } } @@ -81,6 +96,6 @@ fun EditProfileScreen( @Composable fun EditProfileScreenComposable() { StudeezTheme { - EditProfileScreen({}, ProfileEditUiState(), EditProfileActions({}, {}, {})) + EditProfileScreen({}, ProfileEditUiState(), EditProfileActions({}, {}, {}, {})) } } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditUiState.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditUiState.kt index 9ecaba3..c686c92 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditUiState.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditUiState.kt @@ -1,5 +1,6 @@ package be.ugent.sel.studeez.screens.profile data class ProfileEditUiState ( - val username: String = "" + val username: String = "", + val biography: String = "" ) \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditViewModel.kt index cb270be..d715e10 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditViewModel.kt @@ -3,6 +3,7 @@ package be.ugent.sel.studeez.screens.profile import androidx.compose.runtime.mutableStateOf import be.ugent.sel.studeez.R import be.ugent.sel.studeez.common.snackbar.SnackbarManager +import be.ugent.sel.studeez.data.local.models.User import be.ugent.sel.studeez.domain.AccountDAO import be.ugent.sel.studeez.domain.LogService import be.ugent.sel.studeez.domain.UserDAO @@ -23,7 +24,11 @@ class ProfileEditViewModel @Inject constructor( init { launchCatching { - uiState.value = uiState.value.copy(username = userDAO.getUsername()!!) + val user: User = userDAO.getUser() + uiState.value = uiState.value.copy( + username = user.username, + biography = user.biography + ) } } @@ -31,9 +36,16 @@ class ProfileEditViewModel @Inject constructor( uiState.value = uiState.value.copy(username = newValue) } + fun onBiographyChange(newValue: String) { + uiState.value = uiState.value.copy(biography = newValue) + } + fun onSaveClick() { launchCatching { - userDAO.save(uiState.value.username) + userDAO.saveUser( + newUsername = uiState.value.username, + newBiography = uiState.value.biography + ) SnackbarManager.showMessage(R.string.success) } } diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileScreen.kt index 9c76337..3652c70 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileScreen.kt @@ -1,17 +1,18 @@ package be.ugent.sel.studeez.screens.profile +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material.Icon import androidx.compose.material.IconButton +import androidx.compose.material.Text import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Edit -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview -import be.ugent.sel.studeez.R +import androidx.compose.ui.unit.dp import be.ugent.sel.studeez.common.composable.Headline import be.ugent.sel.studeez.common.composable.PrimaryScreenTemplate import be.ugent.sel.studeez.common.composable.drawer.DrawerActions @@ -22,16 +23,18 @@ import be.ugent.sel.studeez.R.string as AppText data class ProfileActions( val getUsername: suspend CoroutineScope.() -> String?, - val onEditProfileClick: () -> Unit, + val getBiography: suspend CoroutineScope.() -> String?, + val onEditProfileClick: () -> Unit ) fun getProfileActions( viewModel: ProfileViewModel, - open: (String) -> Unit, + open: (String) -> Unit ): ProfileActions { return ProfileActions( getUsername = { viewModel.getUsername() }, - onEditProfileClick = { viewModel.onEditProfileClick(open) }, + getBiography = { viewModel.getBiography() }, + onEditProfileClick = { viewModel.onEditProfileClick(open) } ) } @@ -56,8 +59,10 @@ fun ProfileScreen( navigationBarActions: NavigationBarActions, ) { var username: String? by remember { mutableStateOf("") } + var biography: String? by remember { mutableStateOf("") } LaunchedEffect(key1 = Unit) { username = profileActions.getUsername(this) + biography = profileActions.getBiography(this) } PrimaryScreenTemplate( title = resources().getString(AppText.profile), @@ -65,7 +70,20 @@ fun ProfileScreen( navigationBarActions = navigationBarActions, barAction = { EditAction(onClick = profileActions.onEditProfileClick) } ) { - Headline(text = (username ?: resources().getString(R.string.no_username))) + LazyColumn( + verticalArrangement = Arrangement.spacedBy(15.dp) + ) { + item { + Headline(text = username ?: resources().getString(AppText.no_username)) + } + item { + Text( + text = biography ?: "", + textAlign = TextAlign.Center, + modifier = Modifier.padding(48.dp, 0.dp) + ) + } + } } } @@ -86,7 +104,7 @@ fun EditAction( @Composable fun ProfileScreenPreview() { ProfileScreen( - profileActions = ProfileActions({ null }, {}), + profileActions = ProfileActions({ null }, { null }, {}), drawerActions = DrawerActions({}, {}, {}, {}, {}), navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {}) ) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt index e24defd..760b962 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt @@ -13,8 +13,12 @@ class ProfileViewModel @Inject constructor( logService: LogService ) : StudeezViewModel(logService) { - suspend fun getUsername(): String? { - return userDAO.getUsername() + suspend fun getUsername(): String { + return userDAO.getUser().username + } + + suspend fun getBiography(): String { + return userDAO.getUser().biography } fun onEditProfileClick(open: (String) -> Unit) { diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/sign_up/SignUpViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/sign_up/SignUpViewModel.kt index a08d063..2a448c8 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/sign_up/SignUpViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/sign_up/SignUpViewModel.kt @@ -66,7 +66,7 @@ class SignUpViewModel @Inject constructor( launchCatching { accountDAO.signUpWithEmailAndPassword(email, password) accountDAO.signInWithEmailAndPassword(email, password) - userDAO.save(username) + userDAO.saveUser(username) openAndPopUp(HOME_SCREEN, SIGN_UP_SCREEN) } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d51259c..0ed2693 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -54,6 +54,7 @@ Edit profile Editing profile Delete profile + Bio From 4419c86b050b5e403e984939edb69c4600e9e20d Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Wed, 10 May 2023 12:38:58 +0200 Subject: [PATCH 02/21] Unify DAO names --- .../main/java/be/ugent/sel/studeez/di/DatabaseModule.kt | 6 +++--- .../{FireBaseCollections.kt => FirebaseCollections.kt} | 2 +- .../{FireBaseSessionDAO.kt => FirebaseSessionDAO.kt} | 6 +++--- .../{FireBaseSubjectDAO.kt => FirebaseSubjectDAO.kt} | 6 +++--- .../{FireBaseTaskDAO.kt => FirebaseTaskDAO.kt} | 8 ++++---- .../sel/studeez/domain/implementation/FirebaseTimerDAO.kt | 4 ++-- 6 files changed, 16 insertions(+), 16 deletions(-) rename app/src/main/java/be/ugent/sel/studeez/domain/implementation/{FireBaseCollections.kt => FirebaseCollections.kt} (90%) rename app/src/main/java/be/ugent/sel/studeez/domain/implementation/{FireBaseSessionDAO.kt => FirebaseSessionDAO.kt} (87%) rename app/src/main/java/be/ugent/sel/studeez/domain/implementation/{FireBaseSubjectDAO.kt => FirebaseSubjectDAO.kt} (87%) rename app/src/main/java/be/ugent/sel/studeez/domain/implementation/{FireBaseTaskDAO.kt => FirebaseTaskDAO.kt} (87%) diff --git a/app/src/main/java/be/ugent/sel/studeez/di/DatabaseModule.kt b/app/src/main/java/be/ugent/sel/studeez/di/DatabaseModule.kt index 7ee4992..93dea2d 100644 --- a/app/src/main/java/be/ugent/sel/studeez/di/DatabaseModule.kt +++ b/app/src/main/java/be/ugent/sel/studeez/di/DatabaseModule.kt @@ -26,11 +26,11 @@ abstract class DatabaseModule { abstract fun provideConfigurationService(impl: FirebaseConfigurationService): ConfigurationService @Binds - abstract fun provideSessionDAO(impl: FireBaseSessionDAO): SessionDAO + abstract fun provideSessionDAO(impl: FirebaseSessionDAO): SessionDAO @Binds - abstract fun provideSubjectDAO(impl: FireBaseSubjectDAO): SubjectDAO + abstract fun provideSubjectDAO(impl: FirebaseSubjectDAO): SubjectDAO @Binds - abstract fun provideTaskDAO(impl: FireBaseTaskDAO): TaskDAO + abstract fun provideTaskDAO(impl: FirebaseTaskDAO): TaskDAO } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseCollections.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseCollections.kt similarity index 90% rename from app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseCollections.kt rename to app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseCollections.kt index 78867c9..fcdc0a4 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseCollections.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseCollections.kt @@ -1,6 +1,6 @@ package be.ugent.sel.studeez.domain.implementation -object FireBaseCollections { +object FirebaseCollections { const val SESSION_COLLECTION = "sessions" const val USER_COLLECTION = "users" const val TIMER_COLLECTION = "timers" diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSessionDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseSessionDAO.kt similarity index 87% rename from app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSessionDAO.kt rename to app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseSessionDAO.kt index a818236..ca7c6aa 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSessionDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseSessionDAO.kt @@ -11,7 +11,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import javax.inject.Inject -class FireBaseSessionDAO @Inject constructor( +class FirebaseSessionDAO @Inject constructor( private val firestore: FirebaseFirestore, private val auth: AccountDAO ) : SessionDAO { @@ -31,7 +31,7 @@ class FireBaseSessionDAO @Inject constructor( } private fun currentUserSessionsCollection(): CollectionReference = - firestore.collection(FireBaseCollections.USER_COLLECTION) + firestore.collection(FirebaseCollections.USER_COLLECTION) .document(auth.currentUserId) - .collection(FireBaseCollections.SESSION_COLLECTION) + .collection(FirebaseCollections.SESSION_COLLECTION) } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSubjectDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseSubjectDAO.kt similarity index 87% rename from app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSubjectDAO.kt rename to app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseSubjectDAO.kt index 7d90fbf..fc93da1 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSubjectDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseSubjectDAO.kt @@ -10,7 +10,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import javax.inject.Inject -class FireBaseSubjectDAO @Inject constructor( +class FirebaseSubjectDAO @Inject constructor( private val firestore: FirebaseFirestore, private val auth: AccountDAO, ) : SubjectDAO { @@ -33,7 +33,7 @@ class FireBaseSubjectDAO @Inject constructor( } private fun currentUserSubjectsCollection(): CollectionReference = - firestore.collection(FireBaseCollections.USER_COLLECTION) + firestore.collection(FirebaseCollections.USER_COLLECTION) .document(auth.currentUserId) - .collection(FireBaseCollections.SUBJECT_COLLECTION) + .collection(FirebaseCollections.SUBJECT_COLLECTION) } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseTaskDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseTaskDAO.kt similarity index 87% rename from app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseTaskDAO.kt rename to app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseTaskDAO.kt index b8855e6..d0185ff 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseTaskDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseTaskDAO.kt @@ -12,7 +12,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import javax.inject.Inject -class FireBaseTaskDAO @Inject constructor( +class FirebaseTaskDAO @Inject constructor( private val firestore: FirebaseFirestore, private val auth: AccountDAO, ) : TaskDAO { @@ -41,9 +41,9 @@ class FireBaseTaskDAO @Inject constructor( } private fun selectedSubjectTasksCollection(subjectId: String): CollectionReference = - firestore.collection(FireBaseCollections.USER_COLLECTION) + firestore.collection(FirebaseCollections.USER_COLLECTION) .document(auth.currentUserId) - .collection(FireBaseCollections.SUBJECT_COLLECTION) + .collection(FirebaseCollections.SUBJECT_COLLECTION) .document(subjectId) - .collection(FireBaseCollections.TASK_COLLECTION) + .collection(FirebaseCollections.TASK_COLLECTION) } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseTimerDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseTimerDAO.kt index 1f37a18..dad7047 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseTimerDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseTimerDAO.kt @@ -48,8 +48,8 @@ class FirebaseTimerDAO @Inject constructor( } private fun currentUserTimersCollection(): CollectionReference = - firestore.collection(FireBaseCollections.USER_COLLECTION) + firestore.collection(FirebaseCollections.USER_COLLECTION) .document(auth.currentUserId) - .collection(FireBaseCollections.TIMER_COLLECTION) + .collection(FirebaseCollections.TIMER_COLLECTION) } \ No newline at end of file From 75411627ac2b62699653b9597b2ca8079f191327 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Sun, 14 May 2023 09:06:37 +0200 Subject: [PATCH 03/21] #71 Define Friendship DAO's --- .../studeez/data/local/models/Friendship.kt | 11 +++++ .../ugent/sel/studeez/domain/FriendshipDAO.kt | 48 +++++++++++++++++++ .../be/ugent/sel/studeez/domain/UserDAO.kt | 33 +++++++++++-- 3 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/be/ugent/sel/studeez/data/local/models/Friendship.kt create mode 100644 app/src/main/java/be/ugent/sel/studeez/domain/FriendshipDAO.kt diff --git a/app/src/main/java/be/ugent/sel/studeez/data/local/models/Friendship.kt b/app/src/main/java/be/ugent/sel/studeez/data/local/models/Friendship.kt new file mode 100644 index 0000000..98aa9a5 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/data/local/models/Friendship.kt @@ -0,0 +1,11 @@ +package be.ugent.sel.studeez.data.local.models + +import com.google.firebase.Timestamp +import com.google.firebase.firestore.DocumentId + +data class Friendship( + @DocumentId val id: String = "", + val friendId: String = "", + val friendsSince: Timestamp = Timestamp.now(), + val accepted: Boolean = false +) \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/FriendshipDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/FriendshipDAO.kt new file mode 100644 index 0000000..eda933d --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/domain/FriendshipDAO.kt @@ -0,0 +1,48 @@ +package be.ugent.sel.studeez.domain + +import be.ugent.sel.studeez.data.local.models.Friendship +import kotlinx.coroutines.flow.Flow + +/** + * Should be used for interactions between friends. + */ +interface FriendshipDAO { + + /** + * @return all friendships of the user that is currently logged in. + */ + fun getAllFriendships(): Flow> + + /** + * @return the amount of friends of the currently logged in user. + * This method should be faster than just counting the length of getAllFriends() + */ + fun getFriendshipCount(): Flow + + /** + * @param id the id of the friendship that you want details of + * @return the details of a Friendship + */ + fun getFriendshipDetails(id: String): Friendship + + /** + * Send a friend request to a user. + * @param id of the user that you want to add as a friend + * @return Success/faillure of transaction + */ + fun sendFriendshipRequest(id: String): Boolean + + /** + * Accept a friend request that has already been sent. + * @param id of the friendship that you want to update + * @return: Success/faillure of transaction + */ + fun acceptFriendship(id: String): Boolean + + /** + * Remove a friend or decline a friendrequest. + * @param id of the friendship that you want to update + * @return: Success/faillure of transaction + */ + fun removeFriendship(id: String): Boolean +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt index 619c77b..6017342 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt @@ -1,19 +1,44 @@ package be.ugent.sel.studeez.domain import be.ugent.sel.studeez.data.local.models.User +import kotlinx.coroutines.flow.Flow interface UserDAO { - suspend fun getUser(): User + /** + * @return all users + */ + fun getAllUsers(): Flow> - suspend fun saveUser( + /** + * @return all users based on a query, a trimmed down version of getAllUsers() + */ + fun getUsersWithQuery(): Flow> + // TODO Add query parameter + + /** + * Request information about a user + */ + fun getUserDetails( + userId: String + ): Flow + + /** + * @return information on the currently logged in user. + */ + suspend fun getLoggedInUser(): User + // TODO Should be refactored to fun getLoggedInUser(): Flow, without suspend. + + suspend fun saveLoggedInUser( newUsername: String, newBiography: String = "" ) + // TODO Should be refactored to fun saveLoggedInUser(...): Boolean, without suspend. /** - * Delete all references to this user in the database. Similar to the deleteCascade in + * Delete all references to the logged in user in the database. Similar to the deleteCascade in * relational databases. */ - suspend fun deleteUserReferences() + suspend fun deleteLoggedInUserReferences() + // TODO Should be refactored to fun deleteLoggedInUserReferences(): Boolean, without suspend. } \ No newline at end of file From 56427a69af2cdd07de10c1435b71c58bb87e7103 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Sun, 14 May 2023 09:30:36 +0200 Subject: [PATCH 04/21] Allow build --- .../be/ugent/sel/studeez/di/DatabaseModule.kt | 3 ++ .../implementation/FirebaseCollections.kt | 1 + .../domain/implementation/FirebaseUserDAO.kt | 39 +++++++++++++------ .../screens/profile/ProfileEditViewModel.kt | 6 +-- .../screens/profile/ProfileViewModel.kt | 4 +- .../screens/sign_up/SignUpViewModel.kt | 2 +- 6 files changed, 37 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/di/DatabaseModule.kt b/app/src/main/java/be/ugent/sel/studeez/di/DatabaseModule.kt index 93dea2d..a24adc2 100644 --- a/app/src/main/java/be/ugent/sel/studeez/di/DatabaseModule.kt +++ b/app/src/main/java/be/ugent/sel/studeez/di/DatabaseModule.kt @@ -16,6 +16,9 @@ abstract class DatabaseModule { @Binds abstract fun provideUserDAO(impl: FirebaseUserDAO): UserDAO + @Binds + abstract fun provideFriendshipDAO(impl: FirebaseFriendshipDAO): FriendshipDAO + @Binds abstract fun provideTimerDAO(impl: FirebaseTimerDAO): TimerDAO diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseCollections.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseCollections.kt index fcdc0a4..042c0f0 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseCollections.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseCollections.kt @@ -3,6 +3,7 @@ package be.ugent.sel.studeez.domain.implementation object FirebaseCollections { const val SESSION_COLLECTION = "sessions" const val USER_COLLECTION = "users" + const val FRIENDS_COLLECTION = "friends" const val TIMER_COLLECTION = "timers" const val SUBJECT_COLLECTION = "subjects" const val TASK_COLLECTION = "tasks" diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt index b9421de..293dd1f 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt @@ -7,6 +7,7 @@ import be.ugent.sel.studeez.domain.AccountDAO import be.ugent.sel.studeez.domain.UserDAO import com.google.firebase.firestore.DocumentReference import com.google.firebase.firestore.FirebaseFirestore +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.tasks.await import javax.inject.Inject @@ -15,7 +16,30 @@ class FirebaseUserDAO @Inject constructor( private val auth: AccountDAO ) : UserDAO { - override suspend fun getUser(): User { + companion object { + private const val USER_COLLECTION = FirebaseCollections.USER_COLLECTION + private const val USERNAME_FIELD = "username" + private const val BIOGRAPHY_FIELD = "biography" + } + + private fun currentUserDocument(): DocumentReference = + firestore + .collection(USER_COLLECTION) + .document(auth.currentUserId) + + override fun getAllUsers(): Flow> { + TODO("Not yet implemented") + } + + override fun getUsersWithQuery(): Flow> { + TODO("Not yet implemented") + } + + override fun getUserDetails(userId: String): Flow { + TODO("Not yet implemented") + } + + override suspend fun getLoggedInUser(): User { val userDocument = currentUserDocument().get().await() return User( username = userDocument.getString(USERNAME_FIELD) ?: "", @@ -23,7 +47,7 @@ class FirebaseUserDAO @Inject constructor( ) } - override suspend fun saveUser( + override suspend fun saveLoggedInUser( newUsername: String, newBiography: String ) { @@ -33,16 +57,7 @@ class FirebaseUserDAO @Inject constructor( )) } - private fun currentUserDocument(): DocumentReference = - firestore.collection(USER_COLLECTION).document(auth.currentUserId) - - companion object { - private const val USER_COLLECTION = "users" - private const val USERNAME_FIELD = "username" - private const val BIOGRAPHY_FIELD = "biography" - } - - override suspend fun deleteUserReferences() { + override suspend fun deleteLoggedInUserReferences() { currentUserDocument().delete() .addOnSuccessListener { SnackbarManager.showMessage(R.string.success) } .addOnFailureListener { SnackbarManager.showMessage(R.string.generic_error) } diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditViewModel.kt index d715e10..9370881 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditViewModel.kt @@ -24,7 +24,7 @@ class ProfileEditViewModel @Inject constructor( init { launchCatching { - val user: User = userDAO.getUser() + val user: User = userDAO.getLoggedInUser() uiState.value = uiState.value.copy( username = user.username, biography = user.biography @@ -42,7 +42,7 @@ class ProfileEditViewModel @Inject constructor( fun onSaveClick() { launchCatching { - userDAO.saveUser( + userDAO.saveLoggedInUser( newUsername = uiState.value.username, newBiography = uiState.value.biography ) @@ -52,7 +52,7 @@ class ProfileEditViewModel @Inject constructor( fun onDeleteClick(openAndPopUp: (String, String) -> Unit) { launchCatching { - userDAO.deleteUserReferences() // Delete references + userDAO.deleteLoggedInUserReferences() // Delete references accountDAO.deleteAccount() // Delete authentication } openAndPopUp(StudeezDestinations.SIGN_UP_SCREEN, StudeezDestinations.EDIT_PROFILE_SCREEN) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt index 760b962..d1fba29 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt @@ -14,11 +14,11 @@ class ProfileViewModel @Inject constructor( ) : StudeezViewModel(logService) { suspend fun getUsername(): String { - return userDAO.getUser().username + return userDAO.getLoggedInUser().username } suspend fun getBiography(): String { - return userDAO.getUser().biography + return userDAO.getLoggedInUser().biography } fun onEditProfileClick(open: (String) -> Unit) { diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/sign_up/SignUpViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/sign_up/SignUpViewModel.kt index 2a448c8..4cfa6a9 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/sign_up/SignUpViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/sign_up/SignUpViewModel.kt @@ -66,7 +66,7 @@ class SignUpViewModel @Inject constructor( launchCatching { accountDAO.signUpWithEmailAndPassword(email, password) accountDAO.signInWithEmailAndPassword(email, password) - userDAO.saveUser(username) + userDAO.saveLoggedInUser(username) openAndPopUp(HOME_SCREEN, SIGN_UP_SCREEN) } } From 4f08453a2dbe057f6672489e036770d84b9b5eb4 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Sun, 14 May 2023 09:36:18 +0200 Subject: [PATCH 05/21] Add getAllFriendships() --- .../implementation/FirebaseFriendshipDAO.kt | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFriendshipDAO.kt diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFriendshipDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFriendshipDAO.kt new file mode 100644 index 0000000..4f2203c --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFriendshipDAO.kt @@ -0,0 +1,49 @@ +package be.ugent.sel.studeez.domain.implementation + +import be.ugent.sel.studeez.data.local.models.Friendship +import be.ugent.sel.studeez.domain.AccountDAO +import be.ugent.sel.studeez.domain.FriendshipDAO +import com.google.firebase.firestore.DocumentReference +import com.google.firebase.firestore.FirebaseFirestore +import com.google.firebase.firestore.ktx.snapshots +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import javax.inject.Inject + +class FirebaseFriendshipDAO @Inject constructor( + private val firestore: FirebaseFirestore, + private val auth: AccountDAO +): FriendshipDAO { + + private fun currentUserDocument(): DocumentReference = firestore + .collection(FirebaseCollections.USER_COLLECTION) + .document(auth.currentUserId) + + override fun getAllFriendships(): Flow> { + return currentUserDocument() + .collection(FirebaseCollections.FRIENDS_COLLECTION) + .snapshots() + .map { it.toObjects(Friendship::class.java) } + } + + override fun getFriendshipCount(): Flow { + TODO("Not yet implemented") + } + + override fun getFriendshipDetails(id: String): Friendship { + TODO("Not yet implemented") + } + + override fun sendFriendshipRequest(id: String): Boolean { + TODO("Not yet implemented") + } + + override fun acceptFriendship(id: String): Boolean { + TODO("Not yet implemented") + } + + override fun removeFriendship(id: String): Boolean { + TODO("Not yet implemented") + } + +} \ No newline at end of file From a6d4cf1369a4878516d80128bd276c1ce8d9a09f Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Sun, 14 May 2023 09:36:56 +0200 Subject: [PATCH 06/21] add getAllUsers() --- .../sel/studeez/domain/implementation/FirebaseUserDAO.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt index 293dd1f..df592fe 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt @@ -7,7 +7,9 @@ import be.ugent.sel.studeez.domain.AccountDAO import be.ugent.sel.studeez.domain.UserDAO import com.google.firebase.firestore.DocumentReference import com.google.firebase.firestore.FirebaseFirestore +import com.google.firebase.firestore.ktx.snapshots import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map import kotlinx.coroutines.tasks.await import javax.inject.Inject @@ -28,7 +30,10 @@ class FirebaseUserDAO @Inject constructor( .document(auth.currentUserId) override fun getAllUsers(): Flow> { - TODO("Not yet implemented") + return firestore + .collection(FirebaseCollections.USER_COLLECTION) + .snapshots() + .map { it.toObjects(User::class.java) } } override fun getUsersWithQuery(): Flow> { From bb64875bad5512b1f3f9861885e6c07ad9c84bb8 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Sun, 14 May 2023 09:39:34 +0200 Subject: [PATCH 07/21] Add getUserDetails() --- .../studeez/domain/implementation/FirebaseUserDAO.kt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt index df592fe..8c84914 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt @@ -9,6 +9,7 @@ import com.google.firebase.firestore.DocumentReference import com.google.firebase.firestore.FirebaseFirestore import com.google.firebase.firestore.ktx.snapshots import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map import kotlinx.coroutines.tasks.await import javax.inject.Inject @@ -41,7 +42,15 @@ class FirebaseUserDAO @Inject constructor( } override fun getUserDetails(userId: String): Flow { - TODO("Not yet implemented") + return flow { + val snapshot = firestore + .collection(FirebaseCollections.USER_COLLECTION) + .document(userId) + .get() + .await() + val user = snapshot.toObject(User::class.java)!! + emit(user) + } } override suspend fun getLoggedInUser(): User { From 3b50054ff5ef182a6b1e54574e34a781a0c27782 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Sun, 14 May 2023 12:07:56 +0200 Subject: [PATCH 08/21] #63 Add getFriendshipCount and add to profile screen --- .../common/composable/ButtonComposable.kt | 6 +- .../implementation/FirebaseFriendshipDAO.kt | 24 ++++++- .../studeez/navigation/StudeezDestinations.kt | 1 + .../sel/studeez/navigation/StudeezNavGraph.kt | 4 ++ .../studeez/screens/profile/ProfileScreen.kt | 68 +++++++++++++++++-- .../screens/profile/ProfileViewModel.kt | 11 +++ app/src/main/res/values/strings.xml | 6 +- 7 files changed, 111 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/ButtonComposable.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/ButtonComposable.kt index 73ae1b5..2670095 100644 --- a/app/src/main/java/be/ugent/sel/studeez/common/composable/ButtonComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/ButtonComposable.kt @@ -31,7 +31,11 @@ import be.ugent.sel.studeez.common.ext.defaultButtonShape import be.ugent.sel.studeez.R.string as AppText @Composable -fun BasicTextButton(@StringRes text: Int, modifier: Modifier, action: () -> Unit) { +fun BasicTextButton( + @StringRes text: Int, + modifier: Modifier, + action: () -> Unit +) { TextButton( onClick = action, modifier = modifier diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFriendshipDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFriendshipDAO.kt index 4f2203c..4665e0f 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFriendshipDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFriendshipDAO.kt @@ -1,5 +1,6 @@ package be.ugent.sel.studeez.domain.implementation +import be.ugent.sel.studeez.common.snackbar.SnackbarManager import be.ugent.sel.studeez.data.local.models.Friendship import be.ugent.sel.studeez.domain.AccountDAO import be.ugent.sel.studeez.domain.FriendshipDAO @@ -7,8 +8,14 @@ import com.google.firebase.firestore.DocumentReference import com.google.firebase.firestore.FirebaseFirestore import com.google.firebase.firestore.ktx.snapshots import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map import javax.inject.Inject +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine +import be.ugent.sel.studeez.R.string as AppText class FirebaseFriendshipDAO @Inject constructor( private val firestore: FirebaseFirestore, @@ -27,7 +34,22 @@ class FirebaseFriendshipDAO @Inject constructor( } override fun getFriendshipCount(): Flow { - TODO("Not yet implemented") + return flow { + val friendshipCount = suspendCoroutine { continuation -> + currentUserDocument() + .collection(FirebaseCollections.FRIENDS_COLLECTION) + .get() + .addOnSuccessListener { querySnapshot -> + continuation.resume(querySnapshot.size()) + } + .addOnFailureListener { exception -> + continuation.resumeWithException(exception) + } + } + emit(friendshipCount) + }.catch { + SnackbarManager.showMessage(AppText.generic_error) + } } override fun getFriendshipDetails(id: String): Friendship { diff --git a/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezDestinations.kt b/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezDestinations.kt index 49856c9..2df3899 100644 --- a/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezDestinations.kt +++ b/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezDestinations.kt @@ -30,6 +30,7 @@ object StudeezDestinations { const val EDIT_TASK_FORM = "edit_task" // Friends flow + const val FRIENDS_OVERVIEW_SCREEN = "friends_overview" const val SEARCH_FRIENDS_SCREEN = "search_friends" // Create & edit screens diff --git a/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt b/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt index a09846a..0c4b803 100644 --- a/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt +++ b/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt @@ -220,6 +220,10 @@ fun StudeezNavGraph( } // Friends flow + composable(StudeezDestinations.FRIENDS_OVERVIEW_SCREEN) { + // TODO + } + composable(StudeezDestinations.SEARCH_FRIENDS_SCREEN) { // TODO } diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileScreen.kt index 3652c70..ca59fba 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileScreen.kt @@ -1,30 +1,38 @@ package be.ugent.sel.studeez.screens.profile -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material.Button import androidx.compose.material.Icon import androidx.compose.material.IconButton import androidx.compose.material.Text import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Edit import androidx.compose.runtime.* +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import be.ugent.sel.studeez.R import be.ugent.sel.studeez.common.composable.Headline import be.ugent.sel.studeez.common.composable.PrimaryScreenTemplate import be.ugent.sel.studeez.common.composable.drawer.DrawerActions import be.ugent.sel.studeez.common.composable.navbar.NavigationBarActions +import be.ugent.sel.studeez.common.ext.defaultButtonShape import be.ugent.sel.studeez.resources +import be.ugent.sel.studeez.ui.theme.StudeezTheme import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emptyFlow import be.ugent.sel.studeez.R.string as AppText data class ProfileActions( val getUsername: suspend CoroutineScope.() -> String?, val getBiography: suspend CoroutineScope.() -> String?, - val onEditProfileClick: () -> Unit + val getAmountOfFriends: () -> Flow, + val onEditProfileClick: () -> Unit, + val onViewFriendsClick: () -> Unit ) fun getProfileActions( @@ -34,7 +42,9 @@ fun getProfileActions( return ProfileActions( getUsername = { viewModel.getUsername() }, getBiography = { viewModel.getBiography() }, - onEditProfileClick = { viewModel.onEditProfileClick(open) } + getAmountOfFriends = { viewModel.getAmountOfFriends() }, + onEditProfileClick = { viewModel.onEditProfileClick(open) }, + onViewFriendsClick = { viewModel.onViewFriendsClick(open) } ) } @@ -60,6 +70,8 @@ fun ProfileScreen( ) { var username: String? by remember { mutableStateOf("") } var biography: String? by remember { mutableStateOf("") } + val amountOfFriends = profileActions.getAmountOfFriends().collectAsState(initial = 0) + LaunchedEffect(key1 = Unit) { username = profileActions.getUsername(this) biography = profileActions.getBiography(this) @@ -76,6 +88,21 @@ fun ProfileScreen( item { Headline(text = username ?: resources().getString(AppText.no_username)) } + + item { + Row( + horizontalArrangement = Arrangement.spacedBy(5.dp), + modifier = Modifier.fillMaxWidth() + .wrapContentWidth(align = Alignment.CenterHorizontally) + ) { + AmountOfFriendsButton( + amountOfFriends = amountOfFriends.value + ) { + profileActions.onViewFriendsClick() + } + } + } + item { Text( text = biography ?: "", @@ -96,7 +123,6 @@ fun EditAction( imageVector = Icons.Default.Edit, contentDescription = resources().getString(AppText.edit_profile) ) - } } @@ -104,8 +130,38 @@ fun EditAction( @Composable fun ProfileScreenPreview() { ProfileScreen( - profileActions = ProfileActions({ null }, { null }, {}), + profileActions = ProfileActions({ null }, { null }, { emptyFlow() }, {}, {}), drawerActions = DrawerActions({}, {}, {}, {}, {}), navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {}) ) +} + +@Composable +fun AmountOfFriendsButton( + amountOfFriends: Int, + onClick: () -> Unit +){ + Button( + onClick = onClick, + shape = defaultButtonShape() + ) { + Text( + text = resources().getQuantityString( + /* id = */ R.plurals.friends_amount, + /* quantity = */ amountOfFriends, + /* ...formatArgs = */ amountOfFriends + ) + ) + } +} + +@Preview +@Composable +fun AmountOfFriendsButtonPreview() { + StudeezTheme { + Column { + AmountOfFriendsButton(amountOfFriends = 1) { } + AmountOfFriendsButton(amountOfFriends = 100) { } + } + } } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt index d1fba29..e485de6 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt @@ -1,15 +1,18 @@ package be.ugent.sel.studeez.screens.profile +import be.ugent.sel.studeez.domain.FriendshipDAO import be.ugent.sel.studeez.domain.LogService import be.ugent.sel.studeez.domain.UserDAO import be.ugent.sel.studeez.navigation.StudeezDestinations import be.ugent.sel.studeez.screens.StudeezViewModel import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.Flow import javax.inject.Inject @HiltViewModel class ProfileViewModel @Inject constructor( private val userDAO: UserDAO, + private val friendshipDAO: FriendshipDAO, logService: LogService ) : StudeezViewModel(logService) { @@ -21,8 +24,16 @@ class ProfileViewModel @Inject constructor( return userDAO.getLoggedInUser().biography } + fun getAmountOfFriends(): Flow { + return friendshipDAO.getFriendshipCount() + } + fun onEditProfileClick(open: (String) -> Unit) { open(StudeezDestinations.EDIT_PROFILE_SCREEN) } + fun onViewFriendsClick(open: (String) -> Unit) { + open(StudeezDestinations.FRIENDS_OVERVIEW_SCREEN) + } + } \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0ed2693..f370b35 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -20,7 +20,7 @@ Success! Try again - Something wrong happened. Please try again. + Something went wrong. Please try again. Please insert a valid email. @@ -107,6 +107,10 @@ Friends Friend + + %d Friend + %d Friends + Adding friends still needs to be implemented. Hang on tight! From 87fe4767245571119e97c98ec2020a23c481b831 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Sun, 14 May 2023 13:10:32 +0200 Subject: [PATCH 09/21] Basic friends overview screen --- .../screens/friend/FriendsOverviewScreen.kt | 218 ++++++++++++++++++ .../friend/FriendsOverviewViewModel.kt | 41 ++++ .../main/res/drawable/ic_more_horizontal.xml | 5 + app/src/main/res/values/strings.xml | 2 + 4 files changed, 266 insertions(+) create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewScreen.kt create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewViewModel.kt create mode 100644 app/src/main/res/drawable/ic_more_horizontal.xml diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewScreen.kt new file mode 100644 index 0000000..c9e80f5 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewScreen.kt @@ -0,0 +1,218 @@ +package be.ugent.sel.studeez.screens.friend + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.Icon +import androidx.compose.material.IconButton +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import be.ugent.sel.studeez.R +import be.ugent.sel.studeez.common.composable.BasicButton +import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate +import be.ugent.sel.studeez.common.ext.basicButton +import be.ugent.sel.studeez.data.local.models.Friendship +import be.ugent.sel.studeez.data.local.models.User +import be.ugent.sel.studeez.resources +import be.ugent.sel.studeez.ui.theme.StudeezTheme +import com.google.firebase.Timestamp +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emptyFlow +import be.ugent.sel.studeez.R.string as AppText + +data class FriendsOverviewActions( + val getFriendsFlow: () -> Flow>>, + val searchFriends: () -> Unit +) + +fun getFriendsOverviewActions( + viewModel: FriendsOverviewViewModel, + open: (String) -> Unit +): FriendsOverviewActions { + return FriendsOverviewActions( + getFriendsFlow = viewModel::getAllFriends, + searchFriends = { viewModel.searchFriends(open) } + ) +} + +@Composable +fun FriendsOveriewRoute( + open: (String) -> Unit, + popUp: () -> Unit, + viewModel: FriendsOverviewViewModel +) { + FriendsOverviewScreen( + friendsOverviewActions = getFriendsOverviewActions( + viewModel = viewModel, + open = open + ), + popUp = popUp + ) +} + +@Composable +fun FriendsOverviewScreen( + friendsOverviewActions: FriendsOverviewActions, + popUp: () -> Unit +) { + val friends = friendsOverviewActions.getFriendsFlow().collectAsState(initial = emptyList()) + + SecondaryScreenTemplate( + title = "TODO there needs to be a search field here", // TODO + popUp = popUp + ) { + LazyColumn { + if (friends.value.isEmpty()) { + // Show a quick button to search friends when the user does not have any friends yet. + item { + BasicButton( + text = AppText.no_friends, + modifier = Modifier.basicButton() + ) { + friendsOverviewActions.searchFriends() + } + } + } + + items(friends.value) { friend -> + FriendsEntry( + user = friend.first, + friendship = friend.second + ) + } + } + } +} + +@Preview +@Composable +fun FriendsOverviewPreview() { + StudeezTheme { + FriendsOverviewScreen( + friendsOverviewActions = FriendsOverviewActions( + getFriendsFlow = { emptyFlow() }, + searchFriends = {} + ), + popUp = {} + ) + } +} + +@Composable +fun FriendsEntry( + user: User, + friendship: Friendship +) { + // TODO Styling + Row ( + modifier = Modifier + .fillMaxWidth() + ) { + Box( + modifier = Modifier + .fillMaxWidth(0.15f) + .background(MaterialTheme.colors.primary, CircleShape) + ) { + Icon( + painter = painterResource(id = R.drawable.ic_visibility_on), + contentDescription = null, + modifier = Modifier + .fillMaxHeight() + .align(Alignment.Center), + tint = MaterialTheme.colors.onPrimary + ) + } + + Box ( + modifier = Modifier + .fillMaxWidth(0.65f) + ) { + Column ( + modifier = Modifier + .padding(vertical = 4.dp) + ) { + Text( + text = user.username, + fontSize = 16.sp, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + Text( + text = "${resources().getString(AppText.app_name)} ${resources().getString(AppText.friend)}", + fontSize = 14.sp, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } + } + + Box( + modifier = Modifier.fillMaxWidth(0.15f) + ) { + ThreeDots(friendship = friendship) + } + } +} + +@Preview +@Composable +fun FriendsEntryPreview() { + StudeezTheme { + FriendsEntry( + user = User( + id = "", + username = "Tibo De Peuter", + biography = "short bio" + ), + friendship = Friendship( + id = "", + friendId = "someId", + friendsSince = Timestamp.now(), + accepted = true + ) + ) + } +} + +@Composable +fun ThreeDots( + friendship: Friendship +) { + IconButton( + onClick = { /* TODO Open dropdown */ } + ) { + Icon( + imageVector = ImageVector.vectorResource(id = R.drawable.ic_more_horizontal), + contentDescription = resources().getString(AppText.view_more), + modifier = Modifier.fillMaxSize() + ) + } +} + +@Preview +@Composable +fun ThreeDotsPreview() { + StudeezTheme { + ThreeDots( + friendship = Friendship( + id = "", + friendId = "someId", + friendsSince = Timestamp.now(), + accepted = true + ) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewViewModel.kt new file mode 100644 index 0000000..0dec506 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewViewModel.kt @@ -0,0 +1,41 @@ +package be.ugent.sel.studeez.screens.friend + +import be.ugent.sel.studeez.data.local.models.Friendship +import be.ugent.sel.studeez.data.local.models.User +import be.ugent.sel.studeez.domain.FriendshipDAO +import be.ugent.sel.studeez.domain.LogService +import be.ugent.sel.studeez.domain.UserDAO +import be.ugent.sel.studeez.navigation.StudeezDestinations +import be.ugent.sel.studeez.screens.StudeezViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.flatMapConcat +import javax.inject.Inject + +@HiltViewModel +class FriendsOverviewViewModel @Inject constructor( + private val userDAO: UserDAO, + private val friendshipDAO: FriendshipDAO, + logService: LogService +) : StudeezViewModel(logService) { + + fun getAllFriends(): Flow>> { + return friendshipDAO.getAllFriendships() + .flatMapConcat { friendships -> + val userFlows = friendships.map { friendship -> + userDAO.getUserDetails(friendship.friendId) + } + combine(userFlows) { users -> + friendships.zip(users) { friendship, user -> + Pair(user, friendship) + } + } + } + } + + fun searchFriends(open: (String) -> Unit) { + open(StudeezDestinations.SEARCH_FRIENDS_SCREEN) + } + +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_more_horizontal.xml b/app/src/main/res/drawable/ic_more_horizontal.xml new file mode 100644 index 0000000..afbe22d --- /dev/null +++ b/app/src/main/res/drawable/ic_more_horizontal.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f370b35..1ae1bc8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -16,6 +16,7 @@ Go back Next Start + View more Success! @@ -112,6 +113,7 @@ %d Friends Adding friends still needs to be implemented. Hang on tight! + You don\'t have any friends yet. Add one! From 71b9550bd04546aa87726b89faaebae9da66a238 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Mon, 15 May 2023 14:08:54 +0200 Subject: [PATCH 10/21] Move profile edit screen to new package --- .../screens/profile/{ => edit_profile}/ProfileEditScreen.kt | 2 +- .../screens/profile/{ => edit_profile}/ProfileEditUiState.kt | 2 +- .../screens/profile/{ => edit_profile}/ProfileEditViewModel.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename app/src/main/java/be/ugent/sel/studeez/screens/profile/{ => edit_profile}/ProfileEditScreen.kt (98%) rename app/src/main/java/be/ugent/sel/studeez/screens/profile/{ => edit_profile}/ProfileEditUiState.kt (62%) rename app/src/main/java/be/ugent/sel/studeez/screens/profile/{ => edit_profile}/ProfileEditViewModel.kt (97%) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/edit_profile/ProfileEditScreen.kt similarity index 98% rename from app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditScreen.kt rename to app/src/main/java/be/ugent/sel/studeez/screens/profile/edit_profile/ProfileEditScreen.kt index 950595f..31dcb9d 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/edit_profile/ProfileEditScreen.kt @@ -1,4 +1,4 @@ -package be.ugent.sel.studeez.screens.profile +package be.ugent.sel.studeez.screens.profile.edit_profile import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.runtime.Composable diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditUiState.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/edit_profile/ProfileEditUiState.kt similarity index 62% rename from app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditUiState.kt rename to app/src/main/java/be/ugent/sel/studeez/screens/profile/edit_profile/ProfileEditUiState.kt index c686c92..911df68 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditUiState.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/edit_profile/ProfileEditUiState.kt @@ -1,4 +1,4 @@ -package be.ugent.sel.studeez.screens.profile +package be.ugent.sel.studeez.screens.profile.edit_profile data class ProfileEditUiState ( val username: String = "", diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/edit_profile/ProfileEditViewModel.kt similarity index 97% rename from app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditViewModel.kt rename to app/src/main/java/be/ugent/sel/studeez/screens/profile/edit_profile/ProfileEditViewModel.kt index 9370881..57bbbc0 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/edit_profile/ProfileEditViewModel.kt @@ -1,4 +1,4 @@ -package be.ugent.sel.studeez.screens.profile +package be.ugent.sel.studeez.screens.profile.edit_profile import androidx.compose.runtime.mutableStateOf import be.ugent.sel.studeez.R From bc7753fce53475fbe2e214e4f68e4fc1d2fdf7a9 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Mon, 15 May 2023 14:11:28 +0200 Subject: [PATCH 11/21] Move friends overview to new package and new stuff --- .../FriendsOverviewScreen.kt | 143 ++++++++++++++---- .../FriendsOverviewUiState.kt | 6 + .../FriendsOverviewViewModel.kt | 41 ++++- 3 files changed, 155 insertions(+), 35 deletions(-) rename app/src/main/java/be/ugent/sel/studeez/screens/{friend => friends/friends_overview}/FriendsOverviewScreen.kt (57%) create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewUiState.kt rename app/src/main/java/be/ugent/sel/studeez/screens/{friend => friends/friends_overview}/FriendsOverviewViewModel.kt (53%) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewScreen.kt similarity index 57% rename from app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewScreen.kt rename to app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewScreen.kt index c9e80f5..7e2462d 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewScreen.kt @@ -1,20 +1,21 @@ -package be.ugent.sel.studeez.screens.friend +package be.ugent.sel.studeez.screens.friends.friends_overview import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.Icon -import androidx.compose.material.IconButton -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState +import androidx.compose.material.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.filled.Delete +import androidx.compose.material.icons.filled.Person +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview @@ -22,7 +23,8 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import be.ugent.sel.studeez.R import be.ugent.sel.studeez.common.composable.BasicButton -import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate +import be.ugent.sel.studeez.common.composable.SearchField +import be.ugent.sel.studeez.common.composable.drawer.DrawerEntry import be.ugent.sel.studeez.common.ext.basicButton import be.ugent.sel.studeez.data.local.models.Friendship import be.ugent.sel.studeez.data.local.models.User @@ -35,7 +37,11 @@ import be.ugent.sel.studeez.R.string as AppText data class FriendsOverviewActions( val getFriendsFlow: () -> Flow>>, - val searchFriends: () -> Unit + val searchFriends: () -> Unit, + val onQueryStringChange: (String) -> Unit, + val onSubmit: () -> Unit, + val viewProfile: (String) -> Unit, + val removeFriend: (Friendship) -> Unit ) fun getFriendsOverviewActions( @@ -44,7 +50,13 @@ fun getFriendsOverviewActions( ): FriendsOverviewActions { return FriendsOverviewActions( getFriendsFlow = viewModel::getAllFriends, - searchFriends = { viewModel.searchFriends(open) } + searchFriends = { viewModel.searchFriends(open) }, + onQueryStringChange = viewModel::onQueryStringChange, + onSubmit = { viewModel.onSubmit(open) }, + viewProfile = { userId -> + viewModel.viewProfile(userId, open) + }, + removeFriend = viewModel::removeFriend ) } @@ -54,27 +66,52 @@ fun FriendsOveriewRoute( popUp: () -> Unit, viewModel: FriendsOverviewViewModel ) { + val uiState by viewModel.uiState FriendsOverviewScreen( + popUp = popUp, + uiState = uiState, friendsOverviewActions = getFriendsOverviewActions( viewModel = viewModel, open = open - ), - popUp = popUp + ) ) } @Composable fun FriendsOverviewScreen( - friendsOverviewActions: FriendsOverviewActions, - popUp: () -> Unit + popUp: () -> Unit, + uiState: FriendsOverviewUiState, + friendsOverviewActions: FriendsOverviewActions ) { val friends = friendsOverviewActions.getFriendsFlow().collectAsState(initial = emptyList()) - - SecondaryScreenTemplate( - title = "TODO there needs to be a search field here", // TODO - popUp = popUp - ) { - LazyColumn { + + Scaffold( + topBar = { + TopAppBar( + title = { + // TODO Link to each other + SearchField( + value = uiState.queryString, + onValueChange = friendsOverviewActions.onQueryStringChange, + onSubmit = friendsOverviewActions.onSubmit, + label = AppText.search_friends + ) + }, + navigationIcon = { + IconButton(onClick = popUp) { + Icon( + imageVector = Icons.Default.ArrowBack, + contentDescription = resources().getString(R.string.go_back) + ) + } + } + // TODO Add inbox action + ) + } + ) { paddingValues -> + LazyColumn ( + modifier = Modifier.padding(paddingValues) + ) { if (friends.value.isEmpty()) { // Show a quick button to search friends when the user does not have any friends yet. item { @@ -90,7 +127,9 @@ fun FriendsOverviewScreen( items(friends.value) { friend -> FriendsEntry( user = friend.first, - friendship = friend.second + friendship = friend.second, + viewProfile = { userId -> friendsOverviewActions.viewProfile(userId) }, + removeFriend = friendsOverviewActions.removeFriend ) } } @@ -102,11 +141,16 @@ fun FriendsOverviewScreen( fun FriendsOverviewPreview() { StudeezTheme { FriendsOverviewScreen( + popUp = {}, + uiState = FriendsOverviewUiState(""), friendsOverviewActions = FriendsOverviewActions( getFriendsFlow = { emptyFlow() }, - searchFriends = {} - ), - popUp = {} + searchFriends = {}, + onQueryStringChange = {}, + onSubmit = {}, + viewProfile = {}, + removeFriend = {} + ) ) } } @@ -114,7 +158,9 @@ fun FriendsOverviewPreview() { @Composable fun FriendsEntry( user: User, - friendship: Friendship + friendship: Friendship, + viewProfile: (String) -> Unit, + removeFriend: (Friendship) -> Unit ) { // TODO Styling Row ( @@ -162,7 +208,11 @@ fun FriendsEntry( Box( modifier = Modifier.fillMaxWidth(0.15f) ) { - ThreeDots(friendship = friendship) + FriendsOverviewDropDown( + friendship = friendship, + viewProfile = viewProfile, + removeFriend = removeFriend + ) } } } @@ -182,17 +232,23 @@ fun FriendsEntryPreview() { friendId = "someId", friendsSince = Timestamp.now(), accepted = true - ) + ), + viewProfile = {}, + removeFriend = {} ) } } @Composable -fun ThreeDots( - friendship: Friendship +fun FriendsOverviewDropDown( + friendship: Friendship, + viewProfile: (String) -> Unit, + removeFriend: (Friendship) -> Unit ) { + var expanded by remember { mutableStateOf(false) } + IconButton( - onClick = { /* TODO Open dropdown */ } + onClick = { expanded = true } ) { Icon( imageVector = ImageVector.vectorResource(id = R.drawable.ic_more_horizontal), @@ -200,19 +256,40 @@ fun ThreeDots( modifier = Modifier.fillMaxSize() ) } + + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false } + ) { + DrawerEntry( + icon = Icons.Default.Person, + text = stringResource(id = AppText.show_profile) + ) { + viewProfile(friendship.friendId) + } + DrawerEntry( + icon = Icons.Default.Delete, + text = stringResource(id = AppText.remove_friend) + ) { + removeFriend(friendship) + expanded = false + } + } } @Preview @Composable -fun ThreeDotsPreview() { +fun FriendsOverviewDropDownPreview() { StudeezTheme { - ThreeDots( + FriendsOverviewDropDown( friendship = Friendship( id = "", friendId = "someId", friendsSince = Timestamp.now(), accepted = true - ) + ), + viewProfile = {}, + removeFriend = { } ) } } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewUiState.kt b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewUiState.kt new file mode 100644 index 0000000..8672814 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewUiState.kt @@ -0,0 +1,6 @@ +package be.ugent.sel.studeez.screens.friends.friends_overview + +data class FriendsOverviewUiState( + val userId: String, + val queryString: String = "" +) \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewViewModel.kt similarity index 53% rename from app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewViewModel.kt rename to app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewViewModel.kt index 0dec506..ce0c5af 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewViewModel.kt @@ -1,5 +1,6 @@ -package be.ugent.sel.studeez.screens.friend +package be.ugent.sel.studeez.screens.friends.friends_overview +import androidx.compose.runtime.mutableStateOf import be.ugent.sel.studeez.data.local.models.Friendship import be.ugent.sel.studeez.data.local.models.User import be.ugent.sel.studeez.domain.FriendshipDAO @@ -7,6 +8,7 @@ import be.ugent.sel.studeez.domain.LogService import be.ugent.sel.studeez.domain.UserDAO import be.ugent.sel.studeez.navigation.StudeezDestinations import be.ugent.sel.studeez.screens.StudeezViewModel +import be.ugent.sel.studeez.screens.profile.public_profile.SelectedProfileState import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine @@ -17,11 +19,19 @@ import javax.inject.Inject class FriendsOverviewViewModel @Inject constructor( private val userDAO: UserDAO, private val friendshipDAO: FriendshipDAO, + private val selectedProfileState: SelectedProfileState, logService: LogService ) : StudeezViewModel(logService) { + var uiState = mutableStateOf(FriendsOverviewUiState( + userId = selectedProfileState.selectedUserId + )) + private set + fun getAllFriends(): Flow>> { - return friendshipDAO.getAllFriendships() + return friendshipDAO.getAllFriendships( + userId = uiState.value.userId + ) .flatMapConcat { friendships -> val userFlows = friendships.map { friendship -> userDAO.getUserDetails(friendship.friendId) @@ -38,4 +48,31 @@ class FriendsOverviewViewModel @Inject constructor( open(StudeezDestinations.SEARCH_FRIENDS_SCREEN) } + fun onQueryStringChange(newValue: String) { + uiState.value = uiState.value.copy( + queryString = newValue + ) + } + + fun onSubmit(open: (String) -> Unit) { + val query = uiState.value.queryString // TODO Pass as argument + open(StudeezDestinations.SEARCH_FRIENDS_SCREEN) + } + + fun viewProfile( + userId: String, + open: (String) -> Unit + ) { + selectedProfileState.selectedUserId = userId + open(StudeezDestinations.PUBLIC_PROFILE_SCREEN) + } + + fun removeFriend( + friendship: Friendship + ) { + friendshipDAO.removeFriendship( + friendship = friendship + ) + } + } \ No newline at end of file From a6a5fb5e958548d318054063dc3d37405cdd7337 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Mon, 15 May 2023 14:11:55 +0200 Subject: [PATCH 12/21] Add public profile --- .../public_profile/PublicProfileScreen.kt | 178 ++++++++++++++++++ .../public_profile/PublicProfileUiState.kt | 5 + .../public_profile/PublicProfileViewModel.kt | 60 ++++++ .../public_profile/SelectedProfileState.kt | 12 ++ 4 files changed, 255 insertions(+) create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileScreen.kt create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileUiState.kt create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileViewModel.kt create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/SelectedProfileState.kt diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileScreen.kt new file mode 100644 index 0000000..41e33c5 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileScreen.kt @@ -0,0 +1,178 @@ +package be.ugent.sel.studeez.screens.profile.public_profile + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.MailOutline +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import be.ugent.sel.studeez.R +import be.ugent.sel.studeez.common.composable.Headline +import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate +import be.ugent.sel.studeez.common.composable.drawer.DrawerEntry +import be.ugent.sel.studeez.data.local.models.User +import be.ugent.sel.studeez.resources +import be.ugent.sel.studeez.screens.profile.AmountOfFriendsButton +import be.ugent.sel.studeez.ui.theme.StudeezTheme +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf +import be.ugent.sel.studeez.R.string as AppText + +data class PublicProfileActions( + val getUserDetails: () -> Flow, + val getAmountOfFriends: () -> Flow, + val onViewFriendsClick: () -> Unit, + val sendFriendRequest: () -> Boolean +) + +fun getPublicProfileActions( + viewModel: PublicProfileViewModel, + open: (String) -> Unit +): PublicProfileActions { + return PublicProfileActions( + getUserDetails = { viewModel.getUserDetails(viewModel.uiState.value.userId) }, + getAmountOfFriends = { viewModel.getAmountOfFriends( + userId = viewModel.uiState.value.userId + ) }, + onViewFriendsClick = { viewModel.onViewFriendsClick(open) }, + sendFriendRequest = { viewModel.sendFriendRequest( + userId = viewModel.uiState.value.userId + ) } + ) +} + +@Composable +fun PublicProfileRoute( + popUp: () -> Unit, + open: (String) -> Unit, + viewModel: PublicProfileViewModel +) { + PublicProfileScreen( + publicProfileActions = getPublicProfileActions( + viewModel = viewModel, + open = open + ), + popUp = popUp + ) +} + +@Composable +fun PublicProfileScreen( + publicProfileActions: PublicProfileActions, + popUp: () -> Unit +) { + val user = publicProfileActions.getUserDetails().collectAsState(initial = User()) + val amountOfFriends = publicProfileActions.getAmountOfFriends().collectAsState(initial = 0) + + SecondaryScreenTemplate( + title = stringResource(id = AppText.profile), + popUp = popUp, + barAction = { + PublicProfileEllipsis( + sendFriendRequest = publicProfileActions.sendFriendRequest + ) + } + ) { + LazyColumn( + verticalArrangement = Arrangement.spacedBy(15.dp) + ) { + item { + Headline(text = user.value.username) + } + + item { + Row( + horizontalArrangement = Arrangement.spacedBy(5.dp), + modifier = Modifier + .fillMaxWidth() + .wrapContentWidth(align = Alignment.CenterHorizontally) + ) { + AmountOfFriendsButton( + amountOfFriends = amountOfFriends.value + ) { + publicProfileActions.onViewFriendsClick() + } + } + } + + item { + Text( + text = user.value.biography, + textAlign = TextAlign.Center, + modifier = Modifier.padding(48.dp, 0.dp) + ) + } + } + } +} + +@Preview +@Composable +fun PublicProfilePreview() { + StudeezTheme { + PublicProfileScreen( + publicProfileActions = PublicProfileActions( + getUserDetails = { + flowOf(User( + id = "someid", + username = "Maxime De Poorter", + biography = "I am a different student and this is my public profile" + )) + }, + getAmountOfFriends = { flowOf(113) }, + onViewFriendsClick = {}, + sendFriendRequest = { true } + ), + popUp = {} + ) + } +} + +@Composable +fun PublicProfileEllipsis( + sendFriendRequest: () -> Boolean +) { + var expanded by remember { mutableStateOf(false) } + + IconButton( + onClick = { expanded = true } + ) { + Icon( + imageVector = ImageVector.vectorResource(id = R.drawable.ic_more_horizontal), + contentDescription = resources().getString(AppText.view_more), + modifier = Modifier.fillMaxSize() + ) + } + + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false } + ) { + DropdownMenuItem(onClick = { expanded = false }) { + DrawerEntry( + icon = Icons.Default.MailOutline, + text = stringResource(id = AppText.send_friend_request) + ) { + sendFriendRequest() + } + } + } +} + +@Preview +@Composable +fun PublicProfileEllipsisPreview() { + StudeezTheme { + PublicProfileEllipsis( + sendFriendRequest = { true } + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileUiState.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileUiState.kt new file mode 100644 index 0000000..537fed9 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileUiState.kt @@ -0,0 +1,5 @@ +package be.ugent.sel.studeez.screens.profile.public_profile + +data class PublicProfileUiState( + var userId: String = "" +) \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileViewModel.kt new file mode 100644 index 0000000..6cf22d9 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileViewModel.kt @@ -0,0 +1,60 @@ +package be.ugent.sel.studeez.screens.profile.public_profile + +import androidx.compose.runtime.mutableStateOf +import be.ugent.sel.studeez.data.local.models.User +import be.ugent.sel.studeez.domain.FriendshipDAO +import be.ugent.sel.studeez.domain.LogService +import be.ugent.sel.studeez.domain.UserDAO +import be.ugent.sel.studeez.navigation.StudeezDestinations +import be.ugent.sel.studeez.screens.StudeezViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.selects.select +import javax.inject.Inject + +@HiltViewModel +class PublicProfileViewModel @Inject constructor( + private val userDAO: UserDAO, + private val friendshipDAO: FriendshipDAO, + selectedProfileState: SelectedProfileState, + logService: LogService +): StudeezViewModel(logService) { + + val uiState = mutableStateOf( + PublicProfileUiState( + userId = selectedProfileState.selectedUserId + ) + ) + + fun getUserDetails( + userId: String + ): Flow { + uiState.value = uiState.value.copy( + userId = userId + ) + return userDAO.getUserDetails( + userId = uiState.value.userId + ) + } + + fun getAmountOfFriends( + userId: String + ): Flow { + return friendshipDAO.getFriendshipCount( + userId = userId + ) + } + + fun onViewFriendsClick( + open: (String) -> Unit + ) { + open(StudeezDestinations.FRIENDS_OVERVIEW_SCREEN) + } + + fun sendFriendRequest( + userId: String + ): Boolean { + return friendshipDAO.sendFriendshipRequest(userId) + } + +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/SelectedProfileState.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/SelectedProfileState.kt new file mode 100644 index 0000000..4226d65 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/SelectedProfileState.kt @@ -0,0 +1,12 @@ +package be.ugent.sel.studeez.screens.profile.public_profile + +import be.ugent.sel.studeez.domain.UserDAO +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class SelectedProfileState @Inject constructor( + userDAO: UserDAO +) { + var selectedUserId: String = userDAO.getCurrentUserId() +} \ No newline at end of file From 566102d5d4868e2a4331666661e0ea6e74bba43f Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Mon, 15 May 2023 14:13:13 +0200 Subject: [PATCH 13/21] More with friendship --- .../studeez/data/remote/FirebaseFriendship.kt | 7 ++ .../ugent/sel/studeez/domain/FriendshipDAO.kt | 18 ++-- .../implementation/FirebaseFriendshipDAO.kt | 83 ++++++++++++++++--- .../screens/profile/ProfileViewModel.kt | 3 +- 4 files changed, 94 insertions(+), 17 deletions(-) create mode 100644 app/src/main/java/be/ugent/sel/studeez/data/remote/FirebaseFriendship.kt diff --git a/app/src/main/java/be/ugent/sel/studeez/data/remote/FirebaseFriendship.kt b/app/src/main/java/be/ugent/sel/studeez/data/remote/FirebaseFriendship.kt new file mode 100644 index 0000000..fb2af4b --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/data/remote/FirebaseFriendship.kt @@ -0,0 +1,7 @@ +package be.ugent.sel.studeez.data.remote + +object FirebaseFriendship { + const val FRIENDID: String = "friendId" + const val ACCEPTED: String = "accepted" + const val FRIENDSSINCE: String = "friendsSince" +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/FriendshipDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/FriendshipDAO.kt index eda933d..0beb01a 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/FriendshipDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/FriendshipDAO.kt @@ -9,15 +9,19 @@ import kotlinx.coroutines.flow.Flow interface FriendshipDAO { /** - * @return all friendships of the user that is currently logged in. + * @return all friendships of a chosen user. */ - fun getAllFriendships(): Flow> + fun getAllFriendships( + userId: String + ): Flow> /** - * @return the amount of friends of the currently logged in user. + * @return the amount of friends of a chosen user. * This method should be faster than just counting the length of getAllFriends() */ - fun getFriendshipCount(): Flow + fun getFriendshipCount( + userId: String + ): Flow /** * @param id the id of the friendship that you want details of @@ -41,8 +45,10 @@ interface FriendshipDAO { /** * Remove a friend or decline a friendrequest. - * @param id of the friendship that you want to update + * @param friendship the one you want to remove * @return: Success/faillure of transaction */ - fun removeFriendship(id: String): Boolean + fun removeFriendship( + friendship: Friendship + ): Boolean } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFriendshipDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFriendshipDAO.kt index 4665e0f..bd429e1 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFriendshipDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFriendshipDAO.kt @@ -1,9 +1,16 @@ package be.ugent.sel.studeez.domain.implementation +import androidx.compose.runtime.collectAsState import be.ugent.sel.studeez.common.snackbar.SnackbarManager import be.ugent.sel.studeez.data.local.models.Friendship +import be.ugent.sel.studeez.data.remote.FirebaseFriendship.ACCEPTED +import be.ugent.sel.studeez.data.remote.FirebaseFriendship.FRIENDSSINCE +import be.ugent.sel.studeez.data.remote.FirebaseFriendship.FRIENDID import be.ugent.sel.studeez.domain.AccountDAO import be.ugent.sel.studeez.domain.FriendshipDAO +import be.ugent.sel.studeez.domain.implementation.FirebaseCollections.FRIENDS_COLLECTION +import be.ugent.sel.studeez.domain.implementation.FirebaseCollections.USER_COLLECTION +import com.google.firebase.Timestamp import com.google.firebase.firestore.DocumentReference import com.google.firebase.firestore.FirebaseFirestore import com.google.firebase.firestore.ktx.snapshots @@ -23,21 +30,29 @@ class FirebaseFriendshipDAO @Inject constructor( ): FriendshipDAO { private fun currentUserDocument(): DocumentReference = firestore - .collection(FirebaseCollections.USER_COLLECTION) + .collection(USER_COLLECTION) .document(auth.currentUserId) - override fun getAllFriendships(): Flow> { - return currentUserDocument() - .collection(FirebaseCollections.FRIENDS_COLLECTION) + override fun getAllFriendships( + userId: String + ): Flow> { + return firestore + .collection(USER_COLLECTION) + .document(userId) + .collection(FRIENDS_COLLECTION) .snapshots() .map { it.toObjects(Friendship::class.java) } } - override fun getFriendshipCount(): Flow { + override fun getFriendshipCount( + userId: String + ): Flow { return flow { val friendshipCount = suspendCoroutine { continuation -> - currentUserDocument() - .collection(FirebaseCollections.FRIENDS_COLLECTION) + firestore + .collection(USER_COLLECTION) + .document(userId) + .collection(FRIENDS_COLLECTION) .get() .addOnSuccessListener { querySnapshot -> continuation.resume(querySnapshot.size()) @@ -57,15 +72,63 @@ class FirebaseFriendshipDAO @Inject constructor( } override fun sendFriendshipRequest(id: String): Boolean { - TODO("Not yet implemented") + val currentUserId: String = auth.currentUserId + val otherUserId: String = id + + // Add entry to current user + currentUserDocument() + .collection(FRIENDS_COLLECTION) + .add(mapOf( + FRIENDID to otherUserId, + ACCEPTED to true, // TODO Make it not automatically accepted. + FRIENDSSINCE to Timestamp.now() + )) + + // Add entry to other user + firestore.collection(USER_COLLECTION) + .document(otherUserId) + .collection(FRIENDS_COLLECTION) + .add(mapOf( + FRIENDID to currentUserId, + ACCEPTED to true, // TODO Make it not automatically accepted. + FRIENDSSINCE to Timestamp.now() + )) + + return true } override fun acceptFriendship(id: String): Boolean { TODO("Not yet implemented") } - override fun removeFriendship(id: String): Boolean { - TODO("Not yet implemented") + override fun removeFriendship( + friendship: Friendship + ): Boolean { + val currentUserId: String = auth.currentUserId + val otherUserId: String = friendship.friendId + + // Remove at logged in user + firestore.collection(USER_COLLECTION) + .document(currentUserId) + .collection(FRIENDS_COLLECTION) + .document(friendship.id) + .delete() + + // Remove at other user + firestore.collection(USER_COLLECTION) + .document(otherUserId) + .collection(FRIENDS_COLLECTION) + .whereEqualTo(FRIENDID, currentUserId) + .get() + .addOnSuccessListener { + for (document in it) { + document.reference.delete() + } + }.addOnFailureListener { + SnackbarManager.showMessage(AppText.generic_error) + } + + return true } } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt index e485de6..93fa086 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt @@ -5,6 +5,7 @@ import be.ugent.sel.studeez.domain.LogService import be.ugent.sel.studeez.domain.UserDAO import be.ugent.sel.studeez.navigation.StudeezDestinations import be.ugent.sel.studeez.screens.StudeezViewModel +import com.google.firebase.auth.FirebaseAuth import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.Flow import javax.inject.Inject @@ -25,7 +26,7 @@ class ProfileViewModel @Inject constructor( } fun getAmountOfFriends(): Flow { - return friendshipDAO.getFriendshipCount() + return friendshipDAO.getFriendshipCount(userDAO.getCurrentUserId()) } fun onEditProfileClick(open: (String) -> Unit) { From 7c95e78b2a8eb168ce25cbf9049ad1a243a201e9 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Mon, 15 May 2023 14:14:26 +0200 Subject: [PATCH 14/21] More with UserDAO --- .../sel/studeez/data/remote/FirebaseUser.kt | 6 ++++ .../be/ugent/sel/studeez/domain/UserDAO.kt | 8 +++-- .../domain/implementation/FirebaseUserDAO.kt | 34 ++++++++++++------- 3 files changed, 33 insertions(+), 15 deletions(-) create mode 100644 app/src/main/java/be/ugent/sel/studeez/data/remote/FirebaseUser.kt diff --git a/app/src/main/java/be/ugent/sel/studeez/data/remote/FirebaseUser.kt b/app/src/main/java/be/ugent/sel/studeez/data/remote/FirebaseUser.kt new file mode 100644 index 0000000..9ee5aa2 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/data/remote/FirebaseUser.kt @@ -0,0 +1,6 @@ +package be.ugent.sel.studeez.data.remote + +object FirebaseUser { + const val USERNAME: String = "username" + const val BIOGRAPHY: String = "biography" +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt index 6017342..09179f1 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt @@ -5,6 +5,8 @@ import kotlinx.coroutines.flow.Flow interface UserDAO { + fun getCurrentUserId(): String + /** * @return all users */ @@ -13,8 +15,10 @@ interface UserDAO { /** * @return all users based on a query, a trimmed down version of getAllUsers() */ - fun getUsersWithQuery(): Flow> - // TODO Add query parameter + fun getUsersWithQuery( + fieldName: String, + value: String + ): Flow> /** * Request information about a user diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt index 8c84914..f41fcd9 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt @@ -3,8 +3,11 @@ package be.ugent.sel.studeez.domain.implementation import be.ugent.sel.studeez.R import be.ugent.sel.studeez.common.snackbar.SnackbarManager import be.ugent.sel.studeez.data.local.models.User +import be.ugent.sel.studeez.data.remote.FirebaseUser.BIOGRAPHY +import be.ugent.sel.studeez.data.remote.FirebaseUser.USERNAME import be.ugent.sel.studeez.domain.AccountDAO import be.ugent.sel.studeez.domain.UserDAO +import be.ugent.sel.studeez.domain.implementation.FirebaseCollections.USER_COLLECTION import com.google.firebase.firestore.DocumentReference import com.google.firebase.firestore.FirebaseFirestore import com.google.firebase.firestore.ktx.snapshots @@ -17,12 +20,10 @@ import javax.inject.Inject class FirebaseUserDAO @Inject constructor( private val firestore: FirebaseFirestore, private val auth: AccountDAO - ) : UserDAO { +) : UserDAO { - companion object { - private const val USER_COLLECTION = FirebaseCollections.USER_COLLECTION - private const val USERNAME_FIELD = "username" - private const val BIOGRAPHY_FIELD = "biography" + override fun getCurrentUserId(): String { + return auth.currentUserId } private fun currentUserDocument(): DocumentReference = @@ -32,19 +33,26 @@ class FirebaseUserDAO @Inject constructor( override fun getAllUsers(): Flow> { return firestore - .collection(FirebaseCollections.USER_COLLECTION) + .collection(USER_COLLECTION) .snapshots() .map { it.toObjects(User::class.java) } } - override fun getUsersWithQuery(): Flow> { - TODO("Not yet implemented") + override fun getUsersWithQuery( + fieldName: String, + value: String + ): Flow> { + return firestore + .collection(USER_COLLECTION) + .whereEqualTo(fieldName, value) + .snapshots() + .map { it.toObjects(User::class.java) } } override fun getUserDetails(userId: String): Flow { return flow { val snapshot = firestore - .collection(FirebaseCollections.USER_COLLECTION) + .collection(USER_COLLECTION) .document(userId) .get() .await() @@ -56,8 +64,8 @@ class FirebaseUserDAO @Inject constructor( override suspend fun getLoggedInUser(): User { val userDocument = currentUserDocument().get().await() return User( - username = userDocument.getString(USERNAME_FIELD) ?: "", - biography = userDocument.getString(BIOGRAPHY_FIELD) ?: "" + username = userDocument.getString(USERNAME) ?: "", + biography = userDocument.getString(BIOGRAPHY) ?: "" ) } @@ -66,8 +74,8 @@ class FirebaseUserDAO @Inject constructor( newBiography: String ) { currentUserDocument().set(mapOf( - USERNAME_FIELD to newUsername, - BIOGRAPHY_FIELD to newBiography + USERNAME to newUsername, + BIOGRAPHY to newBiography )) } From 74a6f77261661728761380ff21c54fae25fed211 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Mon, 15 May 2023 14:14:49 +0200 Subject: [PATCH 15/21] Add friends search screen --- .../friends_search/SearchFriendUiState.kt | 10 + .../friends_search/SearchFriendsScreen.kt | 269 ++++++++++++++++++ .../friends_search/SearchFriendsViewModel.kt | 57 ++++ 3 files changed, 336 insertions(+) create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendUiState.kt create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendsScreen.kt create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendsViewModel.kt diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendUiState.kt b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendUiState.kt new file mode 100644 index 0000000..0a5a10f --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendUiState.kt @@ -0,0 +1,10 @@ +package be.ugent.sel.studeez.screens.friends.friends_search + +import be.ugent.sel.studeez.data.local.models.User +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emptyFlow + +data class SearchFriendUiState( + val queryString: String = "", + val searchResults: Flow> = emptyFlow() +) \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendsScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendsScreen.kt new file mode 100644 index 0000000..a91d0f4 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendsScreen.kt @@ -0,0 +1,269 @@ +package be.ugent.sel.studeez.screens.friends.friends_search + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.filled.Person +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import be.ugent.sel.studeez.R +import be.ugent.sel.studeez.common.composable.SearchField +import be.ugent.sel.studeez.common.composable.drawer.DrawerEntry +import be.ugent.sel.studeez.data.local.models.User +import be.ugent.sel.studeez.resources +import be.ugent.sel.studeez.ui.theme.StudeezTheme +import kotlinx.coroutines.flow.* +import be.ugent.sel.studeez.R.string as AppText + +data class SearchFriendsActions( + val onQueryStringChange: (String) -> Unit, + val getUsersWithUsername: (String) -> Unit, + val getAllUsers: () -> Flow>, + val goToProfile: (String) -> Unit +) + +fun getSearchFriendsActions( + viewModel: SearchFriendsViewModel, + open: (String) -> Unit +): SearchFriendsActions { + return SearchFriendsActions( + onQueryStringChange = viewModel::onQueryStringChange, + getUsersWithUsername = viewModel::getUsersWithUsername, + getAllUsers = { viewModel.getAllUsers() }, + goToProfile = { userId -> viewModel.goToProfile(userId, open) } + ) +} + +@Composable +fun SearchFriendsRoute( + popUp: () -> Unit, + open: (String) -> Unit, + viewModel: SearchFriendsViewModel +) { + val uiState by viewModel.uiState + + SearchFriendsScreen( + popUp = popUp, + uiState = uiState, + searchFriendsActions = getSearchFriendsActions( + viewModel = viewModel, + open = open + ) + ) +} + +@Composable +fun SearchFriendsScreen( + popUp: () -> Unit, + uiState: SearchFriendUiState, + searchFriendsActions: SearchFriendsActions +) { + var query by remember { mutableStateOf(uiState.queryString) } + val searchResults = searchFriendsActions.getAllUsers().collectAsState( + initial = emptyList() + ) + + Scaffold( + topBar = { + TopAppBar( + title = { + SearchField( + value = query, + onValueChange = { newValue -> + searchFriendsActions.onQueryStringChange(newValue) + query = newValue + }, + onSubmit = { }, + label = AppText.search_friends + ) + }, + navigationIcon = { + IconButton(onClick = popUp) { + Icon( + imageVector = Icons.Default.ArrowBack, + contentDescription = resources().getString(R.string.go_back) + ) + } + } + ) + } + ) { paddingValues -> + LazyColumn( + modifier = Modifier.padding(paddingValues) + ) { + items (searchResults.value) { user -> + UserEntry( + user = user, + goToProfile = searchFriendsActions.goToProfile + ) + } + } + } +} + +@Preview +@Composable +fun SearchFriendsPreview() { + StudeezTheme { + SearchFriendsScreen( + popUp = {}, + uiState = SearchFriendUiState( + queryString = "dit is een test", + searchResults = flowOf(listOf(User( + id = "someid", + username = "Eerste user", + biography = "blah blah blah" + ))) + ), + searchFriendsActions = SearchFriendsActions( + onQueryStringChange = {}, + getUsersWithUsername = {}, + getAllUsers = { + flowOf(listOf(User( + id = "someid", + username = "Eerste user", + biography = "blah blah blah" + ))) + }, + goToProfile = { } + ) + ) + } +} + +@Composable +fun UserEntry( + user: User, + goToProfile: (String) -> Unit +) { + Row( + modifier = Modifier + .fillMaxWidth() + ) { + Box( + modifier = Modifier + .fillMaxWidth(0.15f) + .background(MaterialTheme.colors.primary, CircleShape) + ) { + Icon( + painter = painterResource(id = R.drawable.ic_visibility_on), + contentDescription = null, + modifier = Modifier + .fillMaxHeight() + .align(Alignment.Center), + tint = MaterialTheme.colors.onPrimary + ) + } + + Box ( + modifier = Modifier + .fillMaxWidth(0.65f) + ) { + Column ( + modifier = Modifier + .padding(vertical = 4.dp) + ) { + Text( + text = user.username, + fontSize = 16.sp, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + Text( + text = "${resources().getString(AppText.app_name)} ${resources().getString(AppText.friend)}", + fontSize = 14.sp, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } + } + + Box( + modifier = Modifier.fillMaxWidth(0.15f) + ) { + SearchFriendsDropDown( + user = user, + goToProfile = goToProfile + ) + } + } +} + +@Preview +@Composable +fun UserEntryPreview() { + StudeezTheme { + UserEntry( + user = User( + id = "someid", + username = "Eerste user", + biography = "blah blah blah" + ), + goToProfile = { } + ) + } +} + +/** + * Three dots that open a dropdown menu that allow to go the users profile. + */ +@Composable +fun SearchFriendsDropDown( + user: User, + goToProfile: (String) -> Unit +) { + var expanded by remember { mutableStateOf(false) } + + IconButton( + onClick = { expanded = true } + ) { + Icon( + imageVector = ImageVector.vectorResource(id = R.drawable.ic_more_horizontal), + contentDescription = stringResource(AppText.view_more), + modifier = Modifier.fillMaxSize() + ) + } + + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false } + ) { + DropdownMenuItem(onClick = { expanded = false }) { + DrawerEntry( + icon = Icons.Default.Person, + text = stringResource(id = AppText.show_profile) + ) { + goToProfile(user.id) + } + } + } +} + +@Preview +@Composable +fun SearchFriendsDropDownPreview() { + StudeezTheme { + SearchFriendsDropDown( + user = User( + id = "someid", + username = "Eerste user", + biography = "blah blah blah" + ), + goToProfile = { } + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendsViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendsViewModel.kt new file mode 100644 index 0000000..a25e1a7 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendsViewModel.kt @@ -0,0 +1,57 @@ +package be.ugent.sel.studeez.screens.friends.friends_search + +import androidx.compose.runtime.mutableStateOf +import be.ugent.sel.studeez.data.local.models.User +import be.ugent.sel.studeez.data.remote.FirebaseUser +import be.ugent.sel.studeez.domain.LogService +import be.ugent.sel.studeez.domain.UserDAO +import be.ugent.sel.studeez.navigation.StudeezDestinations +import be.ugent.sel.studeez.screens.StudeezViewModel +import be.ugent.sel.studeez.screens.profile.public_profile.SelectedProfileState +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.Flow +import javax.inject.Inject + +@HiltViewModel +class SearchFriendsViewModel @Inject constructor( + private val userDAO: UserDAO, + private val selectedProfileState: SelectedProfileState, + logService: LogService +): StudeezViewModel(logService) { + + var uiState = mutableStateOf(SearchFriendUiState()) + private set + + fun onQueryStringChange(newValue: String) { + uiState.value = uiState.value.copy( + queryString = newValue + ) + uiState.value = uiState.value.copy( + searchResults = userDAO.getUsersWithQuery( + fieldName = FirebaseUser.USERNAME, + value = uiState.value.queryString + ) + ) + } + + fun getUsersWithUsername( + value: String + ): Flow> { + return userDAO.getUsersWithQuery( + fieldName = FirebaseUser.USERNAME, + value = value + ) + } + + fun getAllUsers(): Flow> { + return userDAO.getAllUsers() + } + + fun goToProfile( + userId: String, + open: (String) -> Unit + ) { + selectedProfileState.selectedUserId = userId + open(StudeezDestinations.PUBLIC_PROFILE_SCREEN) + } +} \ No newline at end of file From f03a06b24594a32b13811018128a1abfc11d401a Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Mon, 15 May 2023 14:15:10 +0200 Subject: [PATCH 16/21] Update navigation --- .../studeez/navigation/StudeezDestinations.kt | 1 + .../sel/studeez/navigation/StudeezNavGraph.kt | 25 ++++++++++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezDestinations.kt b/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezDestinations.kt index 2df3899..578c74a 100644 --- a/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezDestinations.kt +++ b/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezDestinations.kt @@ -32,6 +32,7 @@ object StudeezDestinations { // Friends flow const val FRIENDS_OVERVIEW_SCREEN = "friends_overview" const val SEARCH_FRIENDS_SCREEN = "search_friends" + const val PUBLIC_PROFILE_SCREEN = "public_profile" // Create & edit screens const val CREATE_TASK_SCREEN = "create_task" diff --git a/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt b/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt index 0c4b803..d6264fc 100644 --- a/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt +++ b/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt @@ -14,10 +14,13 @@ import be.ugent.sel.studeez.common.composable.drawer.getDrawerActions import be.ugent.sel.studeez.common.composable.navbar.NavigationBarActions import be.ugent.sel.studeez.common.composable.navbar.NavigationBarViewModel import be.ugent.sel.studeez.common.composable.navbar.getNavigationBarActions +import be.ugent.sel.studeez.screens.friends.friends_overview.FriendsOveriewRoute +import be.ugent.sel.studeez.screens.friends.friends_search.SearchFriendsRoute import be.ugent.sel.studeez.screens.home.HomeRoute import be.ugent.sel.studeez.screens.log_in.LoginRoute -import be.ugent.sel.studeez.screens.profile.EditProfileRoute +import be.ugent.sel.studeez.screens.profile.edit_profile.EditProfileRoute import be.ugent.sel.studeez.screens.profile.ProfileRoute +import be.ugent.sel.studeez.screens.profile.public_profile.PublicProfileRoute import be.ugent.sel.studeez.screens.session.SessionRoute import be.ugent.sel.studeez.screens.session_recap.SessionRecapRoute import be.ugent.sel.studeez.screens.sessions.SessionsRoute @@ -221,11 +224,27 @@ fun StudeezNavGraph( // Friends flow composable(StudeezDestinations.FRIENDS_OVERVIEW_SCREEN) { - // TODO + FriendsOveriewRoute( + open = open, + popUp = goBack, + viewModel = hiltViewModel() + ) } composable(StudeezDestinations.SEARCH_FRIENDS_SCREEN) { - // TODO + SearchFriendsRoute( + popUp = goBack, + open = open, + viewModel = hiltViewModel() + ) + } + + composable(StudeezDestinations.PUBLIC_PROFILE_SCREEN) { + PublicProfileRoute( + popUp = goBack, + open = open, + viewModel = hiltViewModel() + ) } // Create & edit screens From b7ffc7d802a4f3b08c0f415793d658982ac6c2db Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Mon, 15 May 2023 14:15:36 +0200 Subject: [PATCH 17/21] Add searchfield composable --- .../common/composable/TextFieldComposable.kt | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/TextFieldComposable.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/TextFieldComposable.kt index aadcee3..dfd989c 100644 --- a/app/src/main/java/be/ugent/sel/studeez/common/composable/TextFieldComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/TextFieldComposable.kt @@ -1,6 +1,7 @@ package be.ugent.sel.studeez.common.composable import androidx.annotation.StringRes +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.text.KeyboardActions @@ -10,6 +11,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Email import androidx.compose.material.icons.filled.Lock import androidx.compose.material.icons.filled.Person +import androidx.compose.material.icons.filled.Search import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource @@ -22,6 +24,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import be.ugent.sel.studeez.common.ext.fieldModifier import be.ugent.sel.studeez.resources +import com.google.android.material.color.MaterialColors import kotlin.math.sin import be.ugent.sel.studeez.R.drawable as AppIcon import be.ugent.sel.studeez.R.string as AppText @@ -218,4 +221,34 @@ private fun PasswordField( keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password), visualTransformation = visualTransformation ) +} + +@Composable +fun SearchField( + value: String, + onValueChange: (String) -> Unit, + onSubmit: () -> Unit, + @StringRes label: Int, + modifier: Modifier = Modifier +) { + OutlinedTextField( + value = value, + onValueChange = onValueChange, + modifier = modifier, + label = { Text(text = stringResource(id = label)) }, + trailingIcon = { + IconButton(onClick = onSubmit) { + Icon( + imageVector = Icons.Default.Search, + contentDescription = stringResource(label), + tint = MaterialTheme.colors.primary + ) + } + }, + singleLine = true, + colors = TextFieldDefaults.outlinedTextFieldColors( + textColor = MaterialTheme.colors.onBackground, + backgroundColor = MaterialTheme.colors.background + ) + ) } \ No newline at end of file From 70f1be9b2223641a37a12504303237870b3bba01 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Mon, 15 May 2023 14:16:11 +0200 Subject: [PATCH 18/21] Add friends button to home screen --- .../be/ugent/sel/studeez/screens/home/HomeScreen.kt | 11 ++++++++--- .../ugent/sel/studeez/screens/home/HomeViewModel.kt | 4 ++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt index f02852e..689e512 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt @@ -24,6 +24,7 @@ fun HomeRoute( ) { HomeScreen( onStartSessionClick = { viewModel.onStartSessionClick(open) }, + onViewFriendsClick = { viewModel.onViewFriendsClick(open) }, drawerActions = drawerActions, navigationBarActions = navigationBarActions, ) @@ -32,6 +33,7 @@ fun HomeRoute( @Composable fun HomeScreen( onStartSessionClick: () -> Unit, + onViewFriendsClick: () -> Unit, drawerActions: DrawerActions, navigationBarActions: NavigationBarActions ) { @@ -39,7 +41,7 @@ fun HomeScreen( title = resources().getString(R.string.home), drawerActions = drawerActions, navigationBarActions = navigationBarActions, - // TODO barAction = { FriendsAction() } + barAction = { FriendsAction(onViewFriendsClick) } ) { BasicButton(R.string.start_session, Modifier.basicButton()) { onStartSessionClick() @@ -48,8 +50,10 @@ fun HomeScreen( } @Composable -fun FriendsAction() { - IconButton(onClick = { /*TODO*/ }) { +fun FriendsAction( + onClick: () -> Unit +) { + IconButton(onClick = onClick) { Icon( imageVector = Icons.Default.Person, contentDescription = resources().getString(R.string.friends) @@ -62,6 +66,7 @@ fun FriendsAction() { fun HomeScreenPreview() { HomeScreen( onStartSessionClick = {}, + onViewFriendsClick = {}, drawerActions = DrawerActions({}, {}, {}, {}, {}), navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {}) ) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeViewModel.kt index b27f995..e757b15 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeViewModel.kt @@ -16,4 +16,8 @@ class HomeViewModel @Inject constructor( fun onStartSessionClick(open: (String) -> Unit) { open(StudeezDestinations.TIMER_SELECTION_SCREEN) } + + fun onViewFriendsClick(open: (String) -> Unit) { + open(StudeezDestinations.FRIENDS_OVERVIEW_SCREEN) + } } \ No newline at end of file From 10c86c9bf088216866ba3d7e2e00130342ed96f6 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Mon, 15 May 2023 14:16:30 +0200 Subject: [PATCH 19/21] Add resources --- app/src/main/res/values/strings.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1ae1bc8..4a72fdc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -114,6 +114,10 @@ Adding friends still needs to be implemented. Hang on tight! You don\'t have any friends yet. Add one! + Search friends + Send friend request + Remove as friend + Show profile From a814bd74d7473a0b0fb29e5d14149095b1ca4d38 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Mon, 15 May 2023 22:40:36 +0200 Subject: [PATCH 20/21] QOL improvements & friendsSessionsDAOstuff --- .../composable/ProfilePictureComposable.kt | 44 +++++++++++++++++ .../data/remote/FirebaseSessionReport.kt | 6 +++ .../be/ugent/sel/studeez/domain/SessionDAO.kt | 7 +++ .../be/ugent/sel/studeez/domain/UserDAO.kt | 4 ++ .../implementation/FirebaseSessionDAO.kt | 48 +++++++++++++++++-- .../domain/implementation/FirebaseUserDAO.kt | 7 +++ .../friends_overview/FriendsOverviewScreen.kt | 37 ++++++-------- .../friends_search/SearchFriendsScreen.kt | 38 +++++++-------- .../friends_search/SearchFriendsViewModel.kt | 9 ++++ 9 files changed, 154 insertions(+), 46 deletions(-) create mode 100644 app/src/main/java/be/ugent/sel/studeez/common/composable/ProfilePictureComposable.kt create mode 100644 app/src/main/java/be/ugent/sel/studeez/data/remote/FirebaseSessionReport.kt diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/ProfilePictureComposable.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/ProfilePictureComposable.kt new file mode 100644 index 0000000..c214088 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/ProfilePictureComposable.kt @@ -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() + } +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/data/remote/FirebaseSessionReport.kt b/app/src/main/java/be/ugent/sel/studeez/data/remote/FirebaseSessionReport.kt new file mode 100644 index 0000000..f33718f --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/data/remote/FirebaseSessionReport.kt @@ -0,0 +1,6 @@ +package be.ugent.sel.studeez.data.remote + +object FirebaseSessionReport { + const val STUDYTIME: String = "studyTime" + const val ENDTIME: String = "endTime" +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/SessionDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/SessionDAO.kt index 77087d2..bb233e9 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/SessionDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/SessionDAO.kt @@ -1,12 +1,19 @@ package be.ugent.sel.studeez.domain 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 kotlinx.coroutines.flow.Flow interface SessionDAO { fun getSessions(): Flow> + suspend fun getSessionsOfUser(userId: String): List + + /** + * Return a list of pairs, containing the username and all the studysessions of that user. + */ + fun getFriendsSessions(): Flow>>> fun saveSession(newSessionReport: SessionReport) diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt index 09179f1..80a7689 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt @@ -27,6 +27,10 @@ interface UserDAO { userId: String ): Flow + suspend fun getUsername( + userId: String + ): String + /** * @return information on the currently logged in user. */ diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseSessionDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseSessionDAO.kt index ca7c6aa..e7cb763 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseSessionDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseSessionDAO.kt @@ -1,19 +1,33 @@ package be.ugent.sel.studeez.domain.implementation 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.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.FriendshipDAO 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.FirebaseFirestore +import com.google.firebase.firestore.ktx.getField import com.google.firebase.firestore.ktx.snapshots import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.map +import kotlinx.coroutines.tasks.await import javax.inject.Inject class FirebaseSessionDAO @Inject constructor( private val firestore: FirebaseFirestore, - private val auth: AccountDAO + private val auth: AccountDAO, + private val userDAO: UserDAO, + private val friendshipDAO: FriendshipDAO ) : SessionDAO { override fun getSessions(): Flow> { @@ -22,6 +36,34 @@ class FirebaseSessionDAO @Inject constructor( .map { it.toObjects(SessionReport::class.java) } } + override suspend fun getSessionsOfUser(userId: String): List { + val collection = firestore.collection(USER_COLLECTION) + .document(userId) + .collection(SESSION_COLLECTION) + .get().await() + val list: MutableList = mutableListOf() + for (document in collection) { + val id = document.id + val studyTime: Int = document.getField(STUDYTIME)!! + val endTime: Timestamp = document.getField(ENDTIME)!! + list.add(SessionReport(id, studyTime, endTime)) + } + return list + } + + override fun getFriendsSessions(): Flow>>> { + 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) { currentUserSessionsCollection().add(newSessionReport) } @@ -31,7 +73,7 @@ class FirebaseSessionDAO @Inject constructor( } private fun currentUserSessionsCollection(): CollectionReference = - firestore.collection(FirebaseCollections.USER_COLLECTION) + firestore.collection(USER_COLLECTION) .document(auth.currentUserId) - .collection(FirebaseCollections.SESSION_COLLECTION) + .collection(SESSION_COLLECTION) } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt index f41fcd9..04239c0 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt @@ -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 { val userDocument = currentUserDocument().get().await() return User( diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewScreen.kt index 7e2462d..8ea6d20 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewScreen.kt @@ -14,7 +14,6 @@ import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.style.TextOverflow @@ -23,6 +22,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import be.ugent.sel.studeez.R 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.drawer.DrawerEntry import be.ugent.sel.studeez.common.ext.basicButton @@ -162,29 +162,21 @@ fun FriendsEntry( viewProfile: (String) -> Unit, removeFriend: (Friendship) -> Unit ) { - // TODO Styling Row ( modifier = Modifier .fillMaxWidth() + .padding(horizontal = 15.dp, vertical = 7.dp), ) { Box( modifier = Modifier - .fillMaxWidth(0.15f) - .background(MaterialTheme.colors.primary, CircleShape) + .padding(vertical = 4.dp) ) { - Icon( - painter = painterResource(id = R.drawable.ic_visibility_on), - contentDescription = null, - modifier = Modifier - .fillMaxHeight() - .align(Alignment.Center), - tint = MaterialTheme.colors.onPrimary - ) + ProfilePicture() } Box ( modifier = Modifier - .fillMaxWidth(0.65f) + .fillMaxWidth() ) { Column ( modifier = Modifier @@ -203,16 +195,17 @@ fun FriendsEntry( overflow = TextOverflow.Ellipsis ) } - } - Box( - modifier = Modifier.fillMaxWidth(0.15f) - ) { - FriendsOverviewDropDown( - friendship = friendship, - viewProfile = viewProfile, - removeFriend = removeFriend - ) + Box( + modifier = Modifier.fillMaxWidth(), + contentAlignment = Alignment.CenterEnd + ) { + FriendsOverviewDropDown( + friendship = friendship, + viewProfile = viewProfile, + removeFriend = removeFriend + ) + } } } } diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendsScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendsScreen.kt index a91d0f4..e84bb9f 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendsScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendsScreen.kt @@ -13,7 +13,6 @@ import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource 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.sp 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.drawer.DrawerEntry import be.ugent.sel.studeez.data.local.models.User import be.ugent.sel.studeez.resources 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 data class SearchFriendsActions( @@ -153,25 +154,19 @@ fun UserEntry( Row( modifier = Modifier .fillMaxWidth() + .padding(horizontal = 15.dp, vertical = 7.dp), + horizontalArrangement = Arrangement.spacedBy(15.dp) ) { Box( modifier = Modifier - .fillMaxWidth(0.15f) - .background(MaterialTheme.colors.primary, CircleShape) + .padding(vertical = 4.dp) ) { - Icon( - painter = painterResource(id = R.drawable.ic_visibility_on), - contentDescription = null, - modifier = Modifier - .fillMaxHeight() - .align(Alignment.Center), - tint = MaterialTheme.colors.onPrimary - ) + ProfilePicture() } Box ( modifier = Modifier - .fillMaxWidth(0.65f) + .fillMaxWidth() ) { Column ( modifier = Modifier @@ -190,15 +185,16 @@ fun UserEntry( overflow = TextOverflow.Ellipsis ) } - } - Box( - modifier = Modifier.fillMaxWidth(0.15f) - ) { - SearchFriendsDropDown( - user = user, - goToProfile = goToProfile - ) + Box( + modifier = Modifier.fillMaxWidth(), + contentAlignment = Alignment.CenterEnd + ) { + SearchFriendsDropDown( + user = user, + goToProfile = goToProfile + ) + } } } } diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendsViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendsViewModel.kt index a25e1a7..11aecd7 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendsViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendsViewModel.kt @@ -10,6 +10,7 @@ import be.ugent.sel.studeez.screens.StudeezViewModel import be.ugent.sel.studeez.screens.profile.public_profile.SelectedProfileState import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filter import javax.inject.Inject @HiltViewModel @@ -43,8 +44,16 @@ class SearchFriendsViewModel @Inject constructor( ) } + /** + * Get all users, except for the current user. + */ fun getAllUsers(): Flow> { return userDAO.getAllUsers() + .filter { users -> + users.any { user -> + user.id != userDAO.getCurrentUserId() + } + } } fun goToProfile( From 0bf15a1647f8a7d157661e150f43b08529d58e4b Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Mon, 15 May 2023 23:29:01 +0200 Subject: [PATCH 21/21] Fixes SelectedProfileState --- .../java/be/ugent/sel/studeez/data/SelectedState.kt | 8 ++++++++ .../friends_overview/FriendsOverviewViewModel.kt | 8 ++++---- .../friends/friends_search/SearchFriendsViewModel.kt | 6 +++--- .../profile/public_profile/PublicProfileViewModel.kt | 6 +++--- .../profile/public_profile/SelectedProfileState.kt | 12 ------------ 5 files changed, 18 insertions(+), 22 deletions(-) delete mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/SelectedProfileState.kt diff --git a/app/src/main/java/be/ugent/sel/studeez/data/SelectedState.kt b/app/src/main/java/be/ugent/sel/studeez/data/SelectedState.kt index c52939f..ebe8589 100644 --- a/app/src/main/java/be/ugent/sel/studeez/data/SelectedState.kt +++ b/app/src/main/java/be/ugent/sel/studeez/data/SelectedState.kt @@ -5,6 +5,7 @@ import be.ugent.sel.studeez.data.local.models.task.Subject import be.ugent.sel.studeez.data.local.models.task.Task import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo +import be.ugent.sel.studeez.domain.UserDAO import javax.inject.Inject import javax.inject.Singleton @@ -42,4 +43,11 @@ class SelectedSubject @Inject constructor() : SelectedState() { @Singleton class SelectedTimerInfo @Inject constructor() : SelectedState() { override lateinit var value: TimerInfo +} + +@Singleton +class SelectedUserId @Inject constructor( + userDAO: UserDAO +): SelectedState() { + override var value: String = userDAO.getCurrentUserId() } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewViewModel.kt index ce0c5af..556e435 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewViewModel.kt @@ -1,6 +1,7 @@ package be.ugent.sel.studeez.screens.friends.friends_overview import androidx.compose.runtime.mutableStateOf +import be.ugent.sel.studeez.data.SelectedUserId import be.ugent.sel.studeez.data.local.models.Friendship import be.ugent.sel.studeez.data.local.models.User import be.ugent.sel.studeez.domain.FriendshipDAO @@ -8,7 +9,6 @@ import be.ugent.sel.studeez.domain.LogService import be.ugent.sel.studeez.domain.UserDAO import be.ugent.sel.studeez.navigation.StudeezDestinations import be.ugent.sel.studeez.screens.StudeezViewModel -import be.ugent.sel.studeez.screens.profile.public_profile.SelectedProfileState import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine @@ -19,12 +19,12 @@ import javax.inject.Inject class FriendsOverviewViewModel @Inject constructor( private val userDAO: UserDAO, private val friendshipDAO: FriendshipDAO, - private val selectedProfileState: SelectedProfileState, + private val selectedUserIdState: SelectedUserId, logService: LogService ) : StudeezViewModel(logService) { var uiState = mutableStateOf(FriendsOverviewUiState( - userId = selectedProfileState.selectedUserId + userId = selectedUserIdState.value )) private set @@ -63,7 +63,7 @@ class FriendsOverviewViewModel @Inject constructor( userId: String, open: (String) -> Unit ) { - selectedProfileState.selectedUserId = userId + selectedUserIdState.value = userId open(StudeezDestinations.PUBLIC_PROFILE_SCREEN) } diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendsViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendsViewModel.kt index 11aecd7..39aabf6 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendsViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendsViewModel.kt @@ -1,13 +1,13 @@ package be.ugent.sel.studeez.screens.friends.friends_search import androidx.compose.runtime.mutableStateOf +import be.ugent.sel.studeez.data.SelectedUserId import be.ugent.sel.studeez.data.local.models.User import be.ugent.sel.studeez.data.remote.FirebaseUser import be.ugent.sel.studeez.domain.LogService import be.ugent.sel.studeez.domain.UserDAO import be.ugent.sel.studeez.navigation.StudeezDestinations import be.ugent.sel.studeez.screens.StudeezViewModel -import be.ugent.sel.studeez.screens.profile.public_profile.SelectedProfileState import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.filter @@ -16,7 +16,7 @@ import javax.inject.Inject @HiltViewModel class SearchFriendsViewModel @Inject constructor( private val userDAO: UserDAO, - private val selectedProfileState: SelectedProfileState, + private val selectedProfileState: SelectedUserId, logService: LogService ): StudeezViewModel(logService) { @@ -60,7 +60,7 @@ class SearchFriendsViewModel @Inject constructor( userId: String, open: (String) -> Unit ) { - selectedProfileState.selectedUserId = userId + selectedProfileState.value = userId open(StudeezDestinations.PUBLIC_PROFILE_SCREEN) } } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileViewModel.kt index 6cf22d9..031950c 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileViewModel.kt @@ -1,6 +1,7 @@ package be.ugent.sel.studeez.screens.profile.public_profile import androidx.compose.runtime.mutableStateOf +import be.ugent.sel.studeez.data.SelectedUserId import be.ugent.sel.studeez.data.local.models.User import be.ugent.sel.studeez.domain.FriendshipDAO import be.ugent.sel.studeez.domain.LogService @@ -9,20 +10,19 @@ import be.ugent.sel.studeez.navigation.StudeezDestinations import be.ugent.sel.studeez.screens.StudeezViewModel import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.selects.select import javax.inject.Inject @HiltViewModel class PublicProfileViewModel @Inject constructor( private val userDAO: UserDAO, private val friendshipDAO: FriendshipDAO, - selectedProfileState: SelectedProfileState, + selectedUserIdState: SelectedUserId, logService: LogService ): StudeezViewModel(logService) { val uiState = mutableStateOf( PublicProfileUiState( - userId = selectedProfileState.selectedUserId + userId = selectedUserIdState.value ) ) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/SelectedProfileState.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/SelectedProfileState.kt deleted file mode 100644 index 4226d65..0000000 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/SelectedProfileState.kt +++ /dev/null @@ -1,12 +0,0 @@ -package be.ugent.sel.studeez.screens.profile.public_profile - -import be.ugent.sel.studeez.domain.UserDAO -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class SelectedProfileState @Inject constructor( - userDAO: UserDAO -) { - var selectedUserId: String = userDAO.getCurrentUserId() -} \ No newline at end of file