mirror of
https://github.com/msfjarvis/compose-lobsters
synced 2025-08-14 17:37:05 +05:30
feat(android): wire up search UI into navigation graph
This commit is contained in:
parent
8d6aba9865
commit
d3c3e3ca08
3 changed files with 79 additions and 29 deletions
|
@ -18,9 +18,11 @@ import androidx.compose.material.icons.filled.ArrowBack
|
|||
import androidx.compose.material.icons.filled.Favorite
|
||||
import androidx.compose.material.icons.filled.ImportExport
|
||||
import androidx.compose.material.icons.filled.NewReleases
|
||||
import androidx.compose.material.icons.filled.Search
|
||||
import androidx.compose.material.icons.filled.Whatshot
|
||||
import androidx.compose.material.icons.outlined.FavoriteBorder
|
||||
import androidx.compose.material.icons.outlined.NewReleases
|
||||
import androidx.compose.material.icons.outlined.Search
|
||||
import androidx.compose.material.icons.outlined.Whatshot
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
|
@ -59,6 +61,7 @@ import dev.msfjarvis.claw.android.ui.decorations.NavigationItem
|
|||
import dev.msfjarvis.claw.android.ui.decorations.TransparentSystemBars
|
||||
import dev.msfjarvis.claw.android.ui.lists.DatabasePosts
|
||||
import dev.msfjarvis.claw.android.ui.lists.NetworkPosts
|
||||
import dev.msfjarvis.claw.android.ui.lists.SearchList
|
||||
import dev.msfjarvis.claw.android.ui.navigation.ClawNavigationType
|
||||
import dev.msfjarvis.claw.android.ui.navigation.Destinations
|
||||
import dev.msfjarvis.claw.android.viewmodel.ClawViewModel
|
||||
|
@ -86,6 +89,7 @@ fun LobstersApp(
|
|||
val hottestListState = rememberLazyListState()
|
||||
val newestListState = rememberLazyListState()
|
||||
val savedListState = rememberLazyListState()
|
||||
val searchListState = rememberLazyListState()
|
||||
val navController = rememberNavController()
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
|
@ -97,6 +101,7 @@ fun LobstersApp(
|
|||
val hottestPosts = viewModel.hottestPosts.collectAsLazyPagingItems()
|
||||
val newestPosts = viewModel.newestPosts.collectAsLazyPagingItems()
|
||||
val savedPosts by viewModel.savedPosts.collectAsState(persistentMapOf())
|
||||
val searchResults = viewModel.searchResults.collectAsLazyPagingItems()
|
||||
|
||||
val navigationType = ClawNavigationType.fromSize(windowSizeClass.widthSizeClass)
|
||||
|
||||
|
@ -130,44 +135,54 @@ fun LobstersApp(
|
|||
) {
|
||||
coroutineScope.launch { savedListState.animateScrollToItem(index = 0) }
|
||||
},
|
||||
NavigationItem(
|
||||
label = "Search",
|
||||
route = Destinations.Search.route,
|
||||
icon = Icons.Outlined.Search,
|
||||
selectedIcon = Icons.Filled.Search,
|
||||
) {
|
||||
coroutineScope.launch { searchListState.animateScrollToItem(index = 0) }
|
||||
},
|
||||
)
|
||||
|
||||
TransparentSystemBars()
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
ClawAppBar(
|
||||
navigationIcon = {
|
||||
if (
|
||||
navController.previousBackStackEntry != null &&
|
||||
navItems.none { it.route == currentDestination }
|
||||
) {
|
||||
IconButton(
|
||||
onClick = { if (!navController.popBackStack()) context.getActivity()?.finish() }
|
||||
if (currentDestination != Destinations.Search.route) {
|
||||
ClawAppBar(
|
||||
navigationIcon = {
|
||||
if (
|
||||
navController.previousBackStackEntry != null &&
|
||||
navItems.none { it.route == currentDestination }
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.ArrowBack,
|
||||
contentDescription = "Go back to previous screen",
|
||||
)
|
||||
IconButton(
|
||||
onClick = { if (!navController.popBackStack()) context.getActivity()?.finish() },
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.ArrowBack,
|
||||
contentDescription = "Go back to previous screen",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
title = {
|
||||
if (navItems.any { it.route == currentDestination }) {
|
||||
Text(text = stringResource(R.string.app_name), fontWeight = FontWeight.Bold)
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
if (navItems.any { it.route == currentDestination }) {
|
||||
IconButton(onClick = { navController.navigate(Destinations.DataTransfer.route) }) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.ImportExport,
|
||||
contentDescription = "Data transfer options"
|
||||
)
|
||||
},
|
||||
title = {
|
||||
if (navItems.any { it.route == currentDestination }) {
|
||||
Text(text = stringResource(R.string.app_name), fontWeight = FontWeight.Bold)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
actions = {
|
||||
if (navItems.any { it.route == currentDestination }) {
|
||||
IconButton(onClick = { navController.navigate(Destinations.DataTransfer.route) }) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.ImportExport,
|
||||
contentDescription = "Data transfer options",
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
},
|
||||
bottomBar = {
|
||||
AnimatedVisibility(visible = navigationType == ClawNavigationType.BOTTOM_NAVIGATION) {
|
||||
|
@ -224,6 +239,21 @@ fun LobstersApp(
|
|||
postActions = postActions,
|
||||
)
|
||||
}
|
||||
composable(Destinations.Search.route) {
|
||||
setWebUri("https://lobste.rs/search")
|
||||
SearchList(
|
||||
items = searchResults,
|
||||
listState = searchListState,
|
||||
isPostSaved = viewModel::isPostSaved,
|
||||
postActions = postActions,
|
||||
searchQuery = viewModel.searchQuery,
|
||||
setSearchQuery = { query -> viewModel.searchQuery = query },
|
||||
triggerSearch = { query ->
|
||||
viewModel.searchQuery = query
|
||||
searchResults.refresh()
|
||||
}
|
||||
)
|
||||
}
|
||||
composable(
|
||||
route = Destinations.Comments.route,
|
||||
arguments = listOf(navArgument("postId") { type = NavType.StringType }),
|
||||
|
|
|
@ -21,6 +21,10 @@ sealed class Destinations {
|
|||
override val route = "saved"
|
||||
}
|
||||
|
||||
object Search : Destinations() {
|
||||
override val route = "search"
|
||||
}
|
||||
|
||||
object Comments : Destinations() {
|
||||
const val placeholder = "{postId}"
|
||||
override val route = "comments/$placeholder"
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
*/
|
||||
package dev.msfjarvis.claw.android.viewmodel
|
||||
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.paging.Pager
|
||||
|
@ -16,7 +19,9 @@ import com.slack.eithernet.ApiResult.Success
|
|||
import dev.msfjarvis.claw.android.paging.LobstersPagingSource
|
||||
import dev.msfjarvis.claw.android.paging.LobstersPagingSource.Companion.PAGE_SIZE
|
||||
import dev.msfjarvis.claw.android.paging.LobstersPagingSource.Companion.STARTING_PAGE_INDEX
|
||||
import dev.msfjarvis.claw.android.paging.SearchPagingSource
|
||||
import dev.msfjarvis.claw.api.LobstersApi
|
||||
import dev.msfjarvis.claw.api.LobstersSearchApi
|
||||
import dev.msfjarvis.claw.core.injection.IODispatcher
|
||||
import dev.msfjarvis.claw.database.local.SavedPost
|
||||
import dev.msfjarvis.claw.model.Comment
|
||||
|
@ -41,11 +46,13 @@ class ClawViewModel
|
|||
@Inject
|
||||
constructor(
|
||||
private val api: LobstersApi,
|
||||
private val searchApi: LobstersSearchApi,
|
||||
private val savedPostsRepository: SavedPostsRepository,
|
||||
private val commentsRepository: CommentsRepository,
|
||||
private val linkMetadataRepository: LinkMetadataRepository,
|
||||
private val dataTransferRepository: DataTransferRepository,
|
||||
private val pagingSourceFactory: LobstersPagingSource.Factory,
|
||||
private val searchPagingSourceFactory: SearchPagingSource.Factory,
|
||||
@IODispatcher private val ioDispatcher: CoroutineDispatcher,
|
||||
) : ViewModel() {
|
||||
private val hottestPostsPager =
|
||||
|
@ -56,6 +63,10 @@ constructor(
|
|||
Pager(PagingConfig(pageSize = PAGE_SIZE), initialKey = STARTING_PAGE_INDEX) {
|
||||
pagingSourceFactory.create(api::getNewestPosts)
|
||||
}
|
||||
private val searchResultsPager =
|
||||
Pager(PagingConfig(pageSize = PAGE_SIZE), initialKey = STARTING_PAGE_INDEX) {
|
||||
searchPagingSourceFactory.create { searchApi.searchPosts(searchQuery, it) }
|
||||
}
|
||||
|
||||
val hottestPosts
|
||||
get() = hottestPostsPager.flow
|
||||
|
@ -69,6 +80,11 @@ constructor(
|
|||
val savedPosts
|
||||
get() = savedPostsFlow.map(::mapSavedPosts)
|
||||
|
||||
val searchResults
|
||||
get() = searchResultsPager.flow
|
||||
|
||||
var searchQuery by mutableStateOf("")
|
||||
|
||||
private fun mapSavedPosts(items: List<SavedPost>): ImmutableMap<Month, List<SavedPost>> {
|
||||
val sorted = items.sortedByDescending { post -> post.createdAt.toLocalDateTime() }
|
||||
return sorted.groupBy { post -> post.createdAt.toLocalDateTime().month }.toImmutableMap()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue