diff --git a/android/src/main/kotlin/dev/msfjarvis/claw/android/SearchActivity.kt b/android/src/main/kotlin/dev/msfjarvis/claw/android/SearchActivity.kt index bd06899d..1f36eea3 100644 --- a/android/src/main/kotlin/dev/msfjarvis/claw/android/SearchActivity.kt +++ b/android/src/main/kotlin/dev/msfjarvis/claw/android/SearchActivity.kt @@ -15,6 +15,7 @@ import androidx.activity.SystemBarStyle import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.unit.dp @@ -56,8 +57,10 @@ class SearchActivity : ComponentActivity() { ) { val navController = rememberNavController() val postActions = rememberPostActions(urlLauncher, navController, viewModel) + val listState = rememberLazyListState() SearchList( items = viewModel.searchResults, + listState = listState, isPostSaved = viewModel::isPostSaved, postActions = postActions, searchQuery = viewModel.searchQuery, diff --git a/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/lists/SearchList.kt b/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/lists/SearchList.kt index 00d4fdf8..f99516bb 100644 --- a/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/lists/SearchList.kt +++ b/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/lists/SearchList.kt @@ -6,18 +6,25 @@ */ package dev.msfjarvis.claw.android.ui.lists +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.HorizontalDivider -import androidx.compose.material3.SearchBar +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.SearchOff +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.testTag import androidx.compose.ui.semantics.isTraversalGroup @@ -27,15 +34,15 @@ import androidx.compose.ui.zIndex import androidx.paging.PagingData import androidx.paging.compose.collectAsLazyPagingItems import dev.msfjarvis.claw.common.posts.PostActions +import dev.msfjarvis.claw.common.ui.SearchBar import dev.msfjarvis.claw.database.local.SavedPost import dev.msfjarvis.claw.model.LobstersPost -import dev.msfjarvis.claw.model.toSavedPost import kotlinx.coroutines.flow.Flow -@OptIn(ExperimentalMaterial3Api::class) @Composable fun SearchList( items: Flow>, + listState: LazyListState, isPostSaved: (SavedPost) -> Boolean, postActions: PostActions, searchQuery: String, @@ -51,30 +58,36 @@ fun SearchList( // Clear search field when navigating away onDispose { setSearchQuery("") } } + var searchActive by remember { mutableStateOf(false) } - Column( - modifier = modifier.semantics { isTraversalGroup = true }.zIndex(1f).fillMaxWidth(), - ) { + Column(modifier = modifier.semantics { isTraversalGroup = true }.zIndex(1f).fillMaxWidth()) { SearchBar( - query = searchQuery, - onQueryChange = setSearchQuery, + value = searchQuery, + onValueChange = setSearchQuery, onSearch = { triggerSearch(it) searchActive = true }, - active = searchActive, - onActiveChange = { searchActive = it }, modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp).testTag("search_bar"), - ) { - lazyPagingItems.itemSnapshotList.items.forEach { item -> - val dbModel = item.toSavedPost() - LobstersListItem( - item = dbModel, - isSaved = isPostSaved, - isRead = { false }, - postActions = postActions, - ) - HorizontalDivider() + ) + if (searchActive) { + NetworkPosts( + lazyPagingItems = lazyPagingItems, + listState = listState, + isPostSaved = isPostSaved, + isPostRead = { false }, + postActions = postActions, + ) + } else { + Box(modifier = Modifier.fillMaxSize()) { + Column(modifier = Modifier.align(Alignment.Center)) { + Icon( + imageVector = Icons.Filled.SearchOff, + contentDescription = "No search results", + modifier = Modifier.align(Alignment.CenterHorizontally).size(36.dp), + ) + Text(text = "Nothing to see here", style = MaterialTheme.typography.headlineSmall) + } } } } diff --git a/common/src/main/kotlin/dev/msfjarvis/claw/common/ui/SearchBar.kt b/common/src/main/kotlin/dev/msfjarvis/claw/common/ui/SearchBar.kt new file mode 100644 index 00000000..ab072a6e --- /dev/null +++ b/common/src/main/kotlin/dev/msfjarvis/claw/common/ui/SearchBar.kt @@ -0,0 +1,80 @@ +/* + * Copyright © 2023 Harsh Shandilya. + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. + */ +package dev.msfjarvis.claw.common.ui + +import androidx.compose.foundation.background +import androidx.compose.foundation.focusable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Search +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import dev.msfjarvis.claw.common.theme.LobstersTheme + +@Composable +fun SearchBar( + value: String, + onValueChange: (String) -> Unit, + onSearch: (String) -> Unit, + modifier: Modifier = Modifier, +) { + TextField( + value = value, + onValueChange = onValueChange, + shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp), + textStyle = MaterialTheme.typography.bodyLarge, + placeholder = { Text(text = "Search") }, + trailingIcon = { + IconButton(onClick = { onSearch(value) }) { + Icon(imageVector = Icons.Outlined.Search, contentDescription = "Search") + } + }, + keyboardActions = KeyboardActions(onSearch = { onSearch(value) }), + keyboardOptions = + KeyboardOptions( + keyboardType = KeyboardType.Text, + imeAction = ImeAction.Search, + ), + singleLine = true, + modifier = modifier.focusable(), + ) +} + +@Preview +@Composable +fun SearchBarPreview() { + LobstersTheme { + Box(Modifier.fillMaxWidth().background(MaterialTheme.colorScheme.background).padding(8.dp)) { + var value by remember { mutableStateOf("") } + SearchBar( + value = value, + onValueChange = { value = it }, + onSearch = {}, + modifier = Modifier.align(Alignment.TopCenter) + ) + } + } +}