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