|
|
@ -51,7 +51,9 @@ import androidx.compose.material3.TextField
|
|
|
|
import androidx.compose.material3.TextFieldDefaults
|
|
|
|
import androidx.compose.material3.TextFieldDefaults
|
|
|
|
import androidx.compose.runtime.Composable
|
|
|
|
import androidx.compose.runtime.Composable
|
|
|
|
import androidx.compose.runtime.LaunchedEffect
|
|
|
|
import androidx.compose.runtime.LaunchedEffect
|
|
|
|
|
|
|
|
import androidx.compose.runtime.State
|
|
|
|
import androidx.compose.runtime.getValue
|
|
|
|
import androidx.compose.runtime.getValue
|
|
|
|
|
|
|
|
import androidx.compose.runtime.mutableStateOf
|
|
|
|
import androidx.compose.runtime.remember
|
|
|
|
import androidx.compose.runtime.remember
|
|
|
|
import androidx.compose.ui.Alignment
|
|
|
|
import androidx.compose.ui.Alignment
|
|
|
|
import androidx.compose.ui.Modifier
|
|
|
|
import androidx.compose.ui.Modifier
|
|
|
@ -105,10 +107,10 @@ internal fun SearchRoute(
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
val recentSearchQueriesUiState by searchViewModel.recentSearchQueriesUiState.collectAsStateWithLifecycle()
|
|
|
|
val recentSearchQueriesUiState by searchViewModel.recentSearchQueriesUiState.collectAsStateWithLifecycle()
|
|
|
|
val searchResultUiState by searchViewModel.searchResultUiState.collectAsStateWithLifecycle()
|
|
|
|
val searchResultUiState by searchViewModel.searchResultUiState.collectAsStateWithLifecycle()
|
|
|
|
val searchQuery by searchViewModel.searchQuery.collectAsStateWithLifecycle()
|
|
|
|
val searchQueryState = searchViewModel.searchQuery.collectAsStateWithLifecycle()
|
|
|
|
SearchScreen(
|
|
|
|
SearchScreen(
|
|
|
|
modifier = modifier,
|
|
|
|
modifier = modifier,
|
|
|
|
searchQuery = searchQuery,
|
|
|
|
searchQueryState = searchQueryState,
|
|
|
|
recentSearchesUiState = recentSearchQueriesUiState,
|
|
|
|
recentSearchesUiState = recentSearchQueriesUiState,
|
|
|
|
searchResultUiState = searchResultUiState,
|
|
|
|
searchResultUiState = searchResultUiState,
|
|
|
|
onSearchQueryChanged = searchViewModel::onSearchQueryChanged,
|
|
|
|
onSearchQueryChanged = searchViewModel::onSearchQueryChanged,
|
|
|
@ -126,7 +128,7 @@ internal fun SearchRoute(
|
|
|
|
@Composable
|
|
|
|
@Composable
|
|
|
|
internal fun SearchScreen(
|
|
|
|
internal fun SearchScreen(
|
|
|
|
modifier: Modifier = Modifier,
|
|
|
|
modifier: Modifier = Modifier,
|
|
|
|
searchQuery: String = "",
|
|
|
|
searchQueryState: State<String> = remember { mutableStateOf("") },
|
|
|
|
recentSearchesUiState: RecentSearchQueriesUiState = RecentSearchQueriesUiState.Loading,
|
|
|
|
recentSearchesUiState: RecentSearchQueriesUiState = RecentSearchQueriesUiState.Loading,
|
|
|
|
searchResultUiState: SearchResultUiState = SearchResultUiState.Loading,
|
|
|
|
searchResultUiState: SearchResultUiState = SearchResultUiState.Loading,
|
|
|
|
onSearchQueryChanged: (String) -> Unit = {},
|
|
|
|
onSearchQueryChanged: (String) -> Unit = {},
|
|
|
@ -146,7 +148,7 @@ internal fun SearchScreen(
|
|
|
|
onBackClick = onBackClick,
|
|
|
|
onBackClick = onBackClick,
|
|
|
|
onSearchQueryChanged = onSearchQueryChanged,
|
|
|
|
onSearchQueryChanged = onSearchQueryChanged,
|
|
|
|
onSearchTriggered = onSearchTriggered,
|
|
|
|
onSearchTriggered = onSearchTriggered,
|
|
|
|
searchQuery = searchQuery,
|
|
|
|
searchQueryState = searchQueryState,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
when (searchResultUiState) {
|
|
|
|
when (searchResultUiState) {
|
|
|
|
SearchResultUiState.Loading,
|
|
|
|
SearchResultUiState.Loading,
|
|
|
@ -171,7 +173,7 @@ internal fun SearchScreen(
|
|
|
|
is SearchResultUiState.Success -> {
|
|
|
|
is SearchResultUiState.Success -> {
|
|
|
|
if (searchResultUiState.isEmpty()) {
|
|
|
|
if (searchResultUiState.isEmpty()) {
|
|
|
|
EmptySearchResultBody(
|
|
|
|
EmptySearchResultBody(
|
|
|
|
searchQuery = searchQuery,
|
|
|
|
searchQueryState = searchQueryState,
|
|
|
|
onInterestsClick = onInterestsClick,
|
|
|
|
onInterestsClick = onInterestsClick,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
if (recentSearchesUiState is RecentSearchQueriesUiState.Success) {
|
|
|
|
if (recentSearchesUiState is RecentSearchQueriesUiState.Success) {
|
|
|
@ -186,7 +188,7 @@ internal fun SearchScreen(
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
SearchResultBody(
|
|
|
|
SearchResultBody(
|
|
|
|
searchQuery = searchQuery,
|
|
|
|
searchQueryState = searchQueryState,
|
|
|
|
topics = searchResultUiState.topics,
|
|
|
|
topics = searchResultUiState.topics,
|
|
|
|
newsResources = searchResultUiState.newsResources,
|
|
|
|
newsResources = searchResultUiState.newsResources,
|
|
|
|
onSearchTriggered = onSearchTriggered,
|
|
|
|
onSearchTriggered = onSearchTriggered,
|
|
|
@ -204,13 +206,14 @@ internal fun SearchScreen(
|
|
|
|
|
|
|
|
|
|
|
|
@Composable
|
|
|
|
@Composable
|
|
|
|
fun EmptySearchResultBody(
|
|
|
|
fun EmptySearchResultBody(
|
|
|
|
searchQuery: String,
|
|
|
|
searchQueryState: State<String>,
|
|
|
|
onInterestsClick: () -> Unit,
|
|
|
|
onInterestsClick: () -> Unit,
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
Column(
|
|
|
|
Column(
|
|
|
|
horizontalAlignment = Alignment.CenterHorizontally,
|
|
|
|
horizontalAlignment = Alignment.CenterHorizontally,
|
|
|
|
modifier = Modifier.padding(horizontal = 48.dp),
|
|
|
|
modifier = Modifier.padding(horizontal = 48.dp),
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
|
|
|
|
val searchQuery = searchQueryState.value
|
|
|
|
val message = stringResource(id = searchR.string.feature_search_result_not_found, searchQuery)
|
|
|
|
val message = stringResource(id = searchR.string.feature_search_result_not_found, searchQuery)
|
|
|
|
val start = message.indexOf(searchQuery)
|
|
|
|
val start = message.indexOf(searchQuery)
|
|
|
|
Text(
|
|
|
|
Text(
|
|
|
@ -283,7 +286,7 @@ private fun SearchNotReadyBody() {
|
|
|
|
|
|
|
|
|
|
|
|
@Composable
|
|
|
|
@Composable
|
|
|
|
private fun SearchResultBody(
|
|
|
|
private fun SearchResultBody(
|
|
|
|
searchQuery: String,
|
|
|
|
searchQueryState: State<String>,
|
|
|
|
topics: List<FollowableTopic>,
|
|
|
|
topics: List<FollowableTopic>,
|
|
|
|
newsResources: List<UserNewsResource>,
|
|
|
|
newsResources: List<UserNewsResource>,
|
|
|
|
onSearchTriggered: (String) -> Unit,
|
|
|
|
onSearchTriggered: (String) -> Unit,
|
|
|
@ -334,7 +337,7 @@ private fun SearchResultBody(
|
|
|
|
topicImageUrl = followableTopic.topic.imageUrl,
|
|
|
|
topicImageUrl = followableTopic.topic.imageUrl,
|
|
|
|
onClick = {
|
|
|
|
onClick = {
|
|
|
|
// Pass the current search query to ViewModel to save it as recent searches
|
|
|
|
// Pass the current search query to ViewModel to save it as recent searches
|
|
|
|
onSearchTriggered(searchQuery)
|
|
|
|
onSearchTriggered(searchQueryState.value)
|
|
|
|
onTopicClick(topicId)
|
|
|
|
onTopicClick(topicId)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
onFollowButtonClick = { onFollowButtonClick(topicId, it) },
|
|
|
|
onFollowButtonClick = { onFollowButtonClick(topicId, it) },
|
|
|
@ -363,7 +366,7 @@ private fun SearchResultBody(
|
|
|
|
onNewsResourceViewed = onNewsResourceViewed,
|
|
|
|
onNewsResourceViewed = onNewsResourceViewed,
|
|
|
|
onTopicClick = onTopicClick,
|
|
|
|
onTopicClick = onTopicClick,
|
|
|
|
onExpandedCardClick = {
|
|
|
|
onExpandedCardClick = {
|
|
|
|
onSearchTriggered(searchQuery)
|
|
|
|
onSearchTriggered(searchQueryState.value)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -441,7 +444,7 @@ private fun RecentSearchesBody(
|
|
|
|
|
|
|
|
|
|
|
|
@Composable
|
|
|
|
@Composable
|
|
|
|
private fun SearchToolbar(
|
|
|
|
private fun SearchToolbar(
|
|
|
|
searchQuery: String,
|
|
|
|
searchQueryState: State<String>,
|
|
|
|
onSearchQueryChanged: (String) -> Unit,
|
|
|
|
onSearchQueryChanged: (String) -> Unit,
|
|
|
|
onSearchTriggered: (String) -> Unit,
|
|
|
|
onSearchTriggered: (String) -> Unit,
|
|
|
|
onBackClick: () -> Unit,
|
|
|
|
onBackClick: () -> Unit,
|
|
|
@ -462,23 +465,24 @@ private fun SearchToolbar(
|
|
|
|
SearchTextField(
|
|
|
|
SearchTextField(
|
|
|
|
onSearchQueryChanged = onSearchQueryChanged,
|
|
|
|
onSearchQueryChanged = onSearchQueryChanged,
|
|
|
|
onSearchTriggered = onSearchTriggered,
|
|
|
|
onSearchTriggered = onSearchTriggered,
|
|
|
|
searchQuery = searchQuery,
|
|
|
|
searchQueryState = searchQueryState,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Composable
|
|
|
|
@Composable
|
|
|
|
private fun SearchTextField(
|
|
|
|
private fun SearchTextField(
|
|
|
|
searchQuery: String,
|
|
|
|
searchQueryState: State<String>,
|
|
|
|
onSearchQueryChanged: (String) -> Unit,
|
|
|
|
onSearchQueryChanged: (String) -> Unit,
|
|
|
|
onSearchTriggered: (String) -> Unit,
|
|
|
|
onSearchTriggered: (String) -> Unit,
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
|
|
|
|
val searchQuery = searchQueryState.value
|
|
|
|
val focusRequester = remember { FocusRequester() }
|
|
|
|
val focusRequester = remember { FocusRequester() }
|
|
|
|
val keyboardController = LocalSoftwareKeyboardController.current
|
|
|
|
val keyboardController = LocalSoftwareKeyboardController.current
|
|
|
|
|
|
|
|
|
|
|
|
val onSearchExplicitlyTriggered = {
|
|
|
|
val onSearchExplicitlyTriggered = {
|
|
|
|
keyboardController?.hide()
|
|
|
|
keyboardController?.hide()
|
|
|
|
onSearchTriggered(searchQuery)
|
|
|
|
onSearchTriggered(searchQueryState.value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TextField(
|
|
|
|
TextField(
|
|
|
@ -554,7 +558,7 @@ private fun SearchTextField(
|
|
|
|
private fun SearchToolbarPreview() {
|
|
|
|
private fun SearchToolbarPreview() {
|
|
|
|
NiaTheme {
|
|
|
|
NiaTheme {
|
|
|
|
SearchToolbar(
|
|
|
|
SearchToolbar(
|
|
|
|
searchQuery = "",
|
|
|
|
searchQueryState = remember { mutableStateOf("") },
|
|
|
|
onBackClick = {},
|
|
|
|
onBackClick = {},
|
|
|
|
onSearchQueryChanged = {},
|
|
|
|
onSearchQueryChanged = {},
|
|
|
|
onSearchTriggered = {},
|
|
|
|
onSearchTriggered = {},
|
|
|
@ -568,7 +572,7 @@ private fun EmptySearchResultColumnPreview() {
|
|
|
|
NiaTheme {
|
|
|
|
NiaTheme {
|
|
|
|
EmptySearchResultBody(
|
|
|
|
EmptySearchResultBody(
|
|
|
|
onInterestsClick = {},
|
|
|
|
onInterestsClick = {},
|
|
|
|
searchQuery = "C++",
|
|
|
|
searchQueryState = remember { mutableStateOf("C++") },
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|