#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
|
import be.ugent.sel.studeez.R.string as AppText
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BasicTextButton(@StringRes text: Int, modifier: Modifier, action: () -> Unit) {
|
fun BasicTextButton(
|
||||||
|
@StringRes text: Int,
|
||||||
|
modifier: Modifier,
|
||||||
|
action: () -> Unit
|
||||||
|
) {
|
||||||
TextButton(
|
TextButton(
|
||||||
onClick = action,
|
onClick = action,
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package be.ugent.sel.studeez.domain.implementation
|
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.data.local.models.Friendship
|
||||||
import be.ugent.sel.studeez.domain.AccountDAO
|
import be.ugent.sel.studeez.domain.AccountDAO
|
||||||
import be.ugent.sel.studeez.domain.FriendshipDAO
|
import be.ugent.sel.studeez.domain.FriendshipDAO
|
||||||
|
@ -7,8 +8,14 @@ import com.google.firebase.firestore.DocumentReference
|
||||||
import com.google.firebase.firestore.FirebaseFirestore
|
import com.google.firebase.firestore.FirebaseFirestore
|
||||||
import com.google.firebase.firestore.ktx.snapshots
|
import com.google.firebase.firestore.ktx.snapshots
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.catch
|
||||||
|
import kotlinx.coroutines.flow.flow
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import javax.inject.Inject
|
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(
|
class FirebaseFriendshipDAO @Inject constructor(
|
||||||
private val firestore: FirebaseFirestore,
|
private val firestore: FirebaseFirestore,
|
||||||
|
@ -27,7 +34,22 @@ class FirebaseFriendshipDAO @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getFriendshipCount(): Flow<Int> {
|
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 {
|
override fun getFriendshipDetails(id: String): Friendship {
|
||||||
|
|
|
@ -30,6 +30,7 @@ object StudeezDestinations {
|
||||||
const val EDIT_TASK_FORM = "edit_task"
|
const val EDIT_TASK_FORM = "edit_task"
|
||||||
|
|
||||||
// Friends flow
|
// Friends flow
|
||||||
|
const val FRIENDS_OVERVIEW_SCREEN = "friends_overview"
|
||||||
const val SEARCH_FRIENDS_SCREEN = "search_friends"
|
const val SEARCH_FRIENDS_SCREEN = "search_friends"
|
||||||
|
|
||||||
// Create & edit screens
|
// Create & edit screens
|
||||||
|
|
|
@ -220,6 +220,10 @@ fun StudeezNavGraph(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Friends flow
|
// Friends flow
|
||||||
|
composable(StudeezDestinations.FRIENDS_OVERVIEW_SCREEN) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
composable(StudeezDestinations.SEARCH_FRIENDS_SCREEN) {
|
composable(StudeezDestinations.SEARCH_FRIENDS_SCREEN) {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +1,38 @@
|
||||||
package be.ugent.sel.studeez.screens.profile
|
package be.ugent.sel.studeez.screens.profile
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.material.Button
|
||||||
import androidx.compose.material.Icon
|
import androidx.compose.material.Icon
|
||||||
import androidx.compose.material.IconButton
|
import androidx.compose.material.IconButton
|
||||||
import androidx.compose.material.Text
|
import androidx.compose.material.Text
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Edit
|
import androidx.compose.material.icons.filled.Edit
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
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.Headline
|
||||||
import be.ugent.sel.studeez.common.composable.PrimaryScreenTemplate
|
import be.ugent.sel.studeez.common.composable.PrimaryScreenTemplate
|
||||||
import be.ugent.sel.studeez.common.composable.drawer.DrawerActions
|
import be.ugent.sel.studeez.common.composable.drawer.DrawerActions
|
||||||
import be.ugent.sel.studeez.common.composable.navbar.NavigationBarActions
|
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.resources
|
||||||
|
import be.ugent.sel.studeez.ui.theme.StudeezTheme
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.emptyFlow
|
||||||
import be.ugent.sel.studeez.R.string as AppText
|
import be.ugent.sel.studeez.R.string as AppText
|
||||||
|
|
||||||
data class ProfileActions(
|
data class ProfileActions(
|
||||||
val getUsername: suspend CoroutineScope.() -> String?,
|
val getUsername: suspend CoroutineScope.() -> String?,
|
||||||
val getBiography: suspend CoroutineScope.() -> String?,
|
val getBiography: suspend CoroutineScope.() -> String?,
|
||||||
val onEditProfileClick: () -> Unit
|
val getAmountOfFriends: () -> Flow<Int>,
|
||||||
|
val onEditProfileClick: () -> Unit,
|
||||||
|
val onViewFriendsClick: () -> Unit
|
||||||
)
|
)
|
||||||
|
|
||||||
fun getProfileActions(
|
fun getProfileActions(
|
||||||
|
@ -34,7 +42,9 @@ fun getProfileActions(
|
||||||
return ProfileActions(
|
return ProfileActions(
|
||||||
getUsername = { viewModel.getUsername() },
|
getUsername = { viewModel.getUsername() },
|
||||||
getBiography = { viewModel.getBiography() },
|
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 username: String? by remember { mutableStateOf("") }
|
||||||
var biography: String? by remember { mutableStateOf("") }
|
var biography: String? by remember { mutableStateOf("") }
|
||||||
|
val amountOfFriends = profileActions.getAmountOfFriends().collectAsState(initial = 0)
|
||||||
|
|
||||||
LaunchedEffect(key1 = Unit) {
|
LaunchedEffect(key1 = Unit) {
|
||||||
username = profileActions.getUsername(this)
|
username = profileActions.getUsername(this)
|
||||||
biography = profileActions.getBiography(this)
|
biography = profileActions.getBiography(this)
|
||||||
|
@ -76,6 +88,21 @@ fun ProfileScreen(
|
||||||
item {
|
item {
|
||||||
Headline(text = username ?: resources().getString(AppText.no_username))
|
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 {
|
item {
|
||||||
Text(
|
Text(
|
||||||
text = biography ?: "",
|
text = biography ?: "",
|
||||||
|
@ -96,7 +123,6 @@ fun EditAction(
|
||||||
imageVector = Icons.Default.Edit,
|
imageVector = Icons.Default.Edit,
|
||||||
contentDescription = resources().getString(AppText.edit_profile)
|
contentDescription = resources().getString(AppText.edit_profile)
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,8 +130,38 @@ fun EditAction(
|
||||||
@Composable
|
@Composable
|
||||||
fun ProfileScreenPreview() {
|
fun ProfileScreenPreview() {
|
||||||
ProfileScreen(
|
ProfileScreen(
|
||||||
profileActions = ProfileActions({ null }, { null }, {}),
|
profileActions = ProfileActions({ null }, { null }, { emptyFlow() }, {}, {}),
|
||||||
drawerActions = DrawerActions({}, {}, {}, {}, {}),
|
drawerActions = DrawerActions({}, {}, {}, {}, {}),
|
||||||
navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {})
|
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
|
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.LogService
|
||||||
import be.ugent.sel.studeez.domain.UserDAO
|
import be.ugent.sel.studeez.domain.UserDAO
|
||||||
import be.ugent.sel.studeez.navigation.StudeezDestinations
|
import be.ugent.sel.studeez.navigation.StudeezDestinations
|
||||||
import be.ugent.sel.studeez.screens.StudeezViewModel
|
import be.ugent.sel.studeez.screens.StudeezViewModel
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class ProfileViewModel @Inject constructor(
|
class ProfileViewModel @Inject constructor(
|
||||||
private val userDAO: UserDAO,
|
private val userDAO: UserDAO,
|
||||||
|
private val friendshipDAO: FriendshipDAO,
|
||||||
logService: LogService
|
logService: LogService
|
||||||
) : StudeezViewModel(logService) {
|
) : StudeezViewModel(logService) {
|
||||||
|
|
||||||
|
@ -21,8 +24,16 @@ class ProfileViewModel @Inject constructor(
|
||||||
return userDAO.getLoggedInUser().biography
|
return userDAO.getLoggedInUser().biography
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getAmountOfFriends(): Flow<Int> {
|
||||||
|
return friendshipDAO.getFriendshipCount()
|
||||||
|
}
|
||||||
|
|
||||||
fun onEditProfileClick(open: (String) -> Unit) {
|
fun onEditProfileClick(open: (String) -> Unit) {
|
||||||
open(StudeezDestinations.EDIT_PROFILE_SCREEN)
|
open(StudeezDestinations.EDIT_PROFILE_SCREEN)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onViewFriendsClick(open: (String) -> Unit) {
|
||||||
|
open(StudeezDestinations.FRIENDS_OVERVIEW_SCREEN)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -20,7 +20,7 @@
|
||||||
<!-- Messages -->
|
<!-- Messages -->
|
||||||
<string name="success">Success!</string>
|
<string name="success">Success!</string>
|
||||||
<string name="try_again">Try again</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>
|
<string name="email_error">Please insert a valid email.</string>
|
||||||
|
|
||||||
<!-- ========== NavBar ========== -->
|
<!-- ========== NavBar ========== -->
|
||||||
|
@ -107,6 +107,10 @@
|
||||||
|
|
||||||
<string name="friends">Friends</string>
|
<string name="friends">Friends</string>
|
||||||
<string name="friend">Friend</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. -->
|
<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 ========== -->
|
<!-- ========== Create & edit screens ========== -->
|
||||||
|
|
Reference in a new issue