Add friends search screen
This commit is contained in:
parent
7c95e78b2a
commit
74a6f77261
3 changed files with 336 additions and 0 deletions
|
@ -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()
|
||||||
|
)
|
|
@ -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 = { }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
Reference in a new issue