#63 Add getFriendshipCount and add to profile screen
This commit is contained in:
		
							parent
							
								
									bb64875bad
								
							
						
					
					
						commit
						3b50054ff5
					
				
					 7 changed files with 111 additions and 9 deletions
				
			
		|  | @ -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 | ||||
|  |  | |||
|  | @ -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<Int> { | ||||
|         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 { | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -220,6 +220,10 @@ fun StudeezNavGraph( | |||
|         } | ||||
| 
 | ||||
|         // Friends flow | ||||
|         composable(StudeezDestinations.FRIENDS_OVERVIEW_SCREEN) { | ||||
|             // TODO | ||||
|         } | ||||
| 
 | ||||
|         composable(StudeezDestinations.SEARCH_FRIENDS_SCREEN) { | ||||
|             // TODO | ||||
|         } | ||||
|  |  | |||
|  | @ -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<Int>, | ||||
|     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) { } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -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<Int> { | ||||
|         return friendshipDAO.getFriendshipCount() | ||||
|     } | ||||
| 
 | ||||
|     fun onEditProfileClick(open: (String) -> Unit) { | ||||
|         open(StudeezDestinations.EDIT_PROFILE_SCREEN) | ||||
|     } | ||||
| 
 | ||||
|     fun onViewFriendsClick(open: (String) -> Unit) { | ||||
|         open(StudeezDestinations.FRIENDS_OVERVIEW_SCREEN) | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -20,7 +20,7 @@ | |||
|     <!-- Messages --> | ||||
|     <string name="success">Success!</string> | ||||
|     <string name="try_again">Try again</string> | ||||
|     <string name="generic_error">Something wrong happened. Please try again.</string> | ||||
|     <string name="generic_error">Something went wrong. Please try again.</string> | ||||
|     <string name="email_error">Please insert a valid email.</string> | ||||
| 
 | ||||
|     <!-- ========== NavBar ========== --> | ||||
|  | @ -107,6 +107,10 @@ | |||
| 
 | ||||
|     <string name="friends">Friends</string> | ||||
|     <string name="friend">Friend</string> | ||||
|     <plurals name="friends_amount"> | ||||
|         <item quantity="one">%d Friend</item> | ||||
|         <item quantity="other">%d Friends</item> | ||||
|     </plurals> | ||||
|     <string name="add_friend_not_possible_yet">Adding friends still needs to be implemented. Hang on tight!</string> <!-- TODO Remove this description line once implemented. --> | ||||
| 
 | ||||
|     <!-- ========== Create & edit screens ========== --> | ||||
|  |  | |||
		Reference in a new issue