Add friends search screen

This commit is contained in:
Tibo De Peuter 2023-05-15 14:14:49 +02:00
parent 7c95e78b2a
commit 74a6f77261
3 changed files with 336 additions and 0 deletions

View file

@ -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<List<User>> = emptyFlow()
)

View file

@ -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<List<User>>,
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 = { }
)
}
}

View file

@ -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<List<User>> {
return userDAO.getUsersWithQuery(
fieldName = FirebaseUser.USERNAME,
value = value
)
}
fun getAllUsers(): Flow<List<User>> {
return userDAO.getAllUsers()
}
fun goToProfile(
userId: String,
open: (String) -> Unit
) {
selectedProfileState.selectedUserId = userId
open(StudeezDestinations.PUBLIC_PROFILE_SCREEN)
}
}