From 4550508dff3499fd65ec406f8200ccb0070cb596 Mon Sep 17 00:00:00 2001 From: Sasikanth Miriyampalli Date: Thu, 18 Apr 2024 10:24:06 +0530 Subject: [PATCH 1/3] Fix scroll to top button placement in home screen --- .../rss/reader/home/ui/HomeScreen.kt | 159 +++++++++--------- 1 file changed, 77 insertions(+), 82 deletions(-) diff --git a/shared/src/commonMain/kotlin/dev/sasikanth/rss/reader/home/ui/HomeScreen.kt b/shared/src/commonMain/kotlin/dev/sasikanth/rss/reader/home/ui/HomeScreen.kt index e4e0a5bb3..9437cd6eb 100644 --- a/shared/src/commonMain/kotlin/dev/sasikanth/rss/reader/home/ui/HomeScreen.kt +++ b/shared/src/commonMain/kotlin/dev/sasikanth/rss/reader/home/ui/HomeScreen.kt @@ -135,41 +135,43 @@ internal fun HomeScreen(homePresenter: HomePresenter, modifier: Modifier = Modif } } - Box(modifier = modifier) { - val bottomSheetSwipeTransition = - updateTransition( - targetState = bottomSheetScaffoldState.currentFraction, - label = "Bottom Sheet Swipe Progress" - ) - val bottomSheetCornerSize by - bottomSheetSwipeTransition.animateDp { BOTTOM_SHEET_CORNER_SIZE * it.inverse() } + val bottomSheetSwipeTransition = + updateTransition( + targetState = bottomSheetScaffoldState.currentFraction, + label = "Bottom Sheet Swipe Progress" + ) + val bottomSheetCornerSize by + bottomSheetSwipeTransition.animateDp { BOTTOM_SHEET_CORNER_SIZE * it.inverse() } + val sheetPeekHeight = + BOTTOM_SHEET_PEEK_HEIGHT + + WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding() + val showScrollToTop by remember { derivedStateOf { listState.firstVisibleItemIndex > 0 } } - BottomSheetScaffold( - modifier = - Modifier.windowInsetsPadding(WindowInsets.systemBars.only(WindowInsetsSides.Horizontal)), - scaffoldState = bottomSheetScaffoldState, - backgroundColor = AppTheme.colorScheme.surfaceContainerLowest, - sheetBackgroundColor = AppTheme.colorScheme.tintedBackground, - sheetContentColor = AppTheme.colorScheme.tintedForeground, - sheetElevation = 0.dp, - sheetPeekHeight = - BOTTOM_SHEET_PEEK_HEIGHT + - WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding(), - sheetShape = - RoundedCornerShape(topStart = bottomSheetCornerSize, topEnd = bottomSheetCornerSize), - sheetGesturesEnabled = !feedsState.isInMultiSelectMode, - topBar = { - HomeTopAppBar( - source = state.activeSource, - postsType = state.postsType, - listState = listState, - onSearchClicked = { homePresenter.dispatch(HomeEvent.SearchClicked) }, - onBookmarksClicked = { homePresenter.dispatch(HomeEvent.BookmarksClicked) }, - onSettingsClicked = { homePresenter.dispatch(HomeEvent.SettingsClicked) }, - onPostTypeChanged = { homePresenter.dispatch(HomeEvent.OnPostsTypeChanged(it)) } - ) - }, - content = { paddingValues -> + BottomSheetScaffold( + modifier = + modifier.windowInsetsPadding(WindowInsets.systemBars.only(WindowInsetsSides.Horizontal)), + scaffoldState = bottomSheetScaffoldState, + backgroundColor = AppTheme.colorScheme.surfaceContainerLowest, + sheetBackgroundColor = AppTheme.colorScheme.tintedBackground, + sheetContentColor = AppTheme.colorScheme.tintedForeground, + sheetElevation = 0.dp, + sheetPeekHeight = sheetPeekHeight, + sheetShape = + RoundedCornerShape(topStart = bottomSheetCornerSize, topEnd = bottomSheetCornerSize), + sheetGesturesEnabled = !feedsState.isInMultiSelectMode, + topBar = { + HomeTopAppBar( + source = state.activeSource, + postsType = state.postsType, + listState = listState, + onSearchClicked = { homePresenter.dispatch(HomeEvent.SearchClicked) }, + onBookmarksClicked = { homePresenter.dispatch(HomeEvent.BookmarksClicked) }, + onSettingsClicked = { homePresenter.dispatch(HomeEvent.SettingsClicked) }, + onPostTypeChanged = { homePresenter.dispatch(HomeEvent.OnPostsTypeChanged(it)) } + ) + }, + content = { paddingValues -> + Box(modifier = Modifier.fillMaxSize()) { HomeScreenContent( paddingValues = paddingValues, state = state, @@ -189,63 +191,56 @@ internal fun HomeScreen(homePresenter: HomePresenter, modifier: Modifier = Modif homePresenter.dispatch(HomeEvent.TogglePostReadStatus(postId, postRead)) } ) - }, - sheetContent = { - FeedsBottomSheet( - feedsPresenter = homePresenter.feedsPresenter, - bottomSheetSwipeTransition = bottomSheetSwipeTransition, - closeSheet = { coroutineScope.launch { bottomSheetState.collapse() } }, - selectedFeedChanged = { - coroutineScope.launch { - listState.scrollToItem(0) - featuredPostsPagerState.scrollToPage(0) - } - } - ) - }, - snackbarHost = { - val snackbarModifier = - if (bottomSheetState.isExpanded) { - Modifier.padding(bottom = BOTTOM_SHEET_PEEK_HEIGHT) - .windowInsetsPadding( - WindowInsets.systemBars.only( - WindowInsetsSides.Horizontal + WindowInsetsSides.Bottom - ) - ) - } else { - Modifier - } - - SnackbarHost(hostState = it, modifier = snackbarModifier) { snackbarData -> - Snackbar( - modifier = Modifier.padding(12.dp), - content = { - Text(text = snackbarData.message, maxLines = 4, overflow = TextOverflow.Ellipsis) - }, - action = null, - actionOnNewLine = false, - shape = SnackbarDefaults.shape, - backgroundColor = SnackbarDefaults.color, - contentColor = SnackbarDefaults.contentColor, - elevation = 0.dp - ) - } - }, - floatingActionButton = { - val showScrollToTop by remember { derivedStateOf { listState.firstVisibleItemIndex > 0 } } CompactFloatingActionButton( label = LocalStrings.current.scrollToTop, visible = showScrollToTop, modifier = Modifier.windowInsetsPadding(WindowInsets.systemBars.only(WindowInsetsSides.Horizontal)) - .padding(end = 16.dp, bottom = 16.dp), + .padding(end = 16.dp, bottom = sheetPeekHeight + 16.dp), ) { listState.animateScrollToItem(0) } - }, - ) - } + } + }, + sheetContent = { + FeedsBottomSheet( + feedsPresenter = homePresenter.feedsPresenter, + bottomSheetSwipeTransition = bottomSheetSwipeTransition, + closeSheet = { coroutineScope.launch { bottomSheetState.collapse() } }, + selectedFeedChanged = { + coroutineScope.launch { + listState.scrollToItem(0) + featuredPostsPagerState.scrollToPage(0) + } + } + ) + }, + snackbarHost = { + val snackbarModifier = + if (bottomSheetState.isExpanded) { + Modifier.padding(bottom = sheetPeekHeight) + .windowInsetsPadding(WindowInsets.systemBars.only(WindowInsetsSides.Horizontal)) + } else { + Modifier + } + + SnackbarHost(hostState = it, modifier = snackbarModifier) { snackbarData -> + Snackbar( + modifier = Modifier.padding(12.dp), + content = { + Text(text = snackbarData.message, maxLines = 4, overflow = TextOverflow.Ellipsis) + }, + action = null, + actionOnNewLine = false, + shape = SnackbarDefaults.shape, + backgroundColor = SnackbarDefaults.color, + contentColor = SnackbarDefaults.contentColor, + elevation = 0.dp + ) + } + }, + ) } @Composable From 0123ce36fd75e1bbe948436d7847b3e93f56a982 Mon Sep 17 00:00:00 2001 From: Sasikanth Miriyampalli Date: Thu, 18 Apr 2024 11:16:01 +0530 Subject: [PATCH 2/3] Remove sub-query for skipping featured posts when loading posts Instead we will pass a collection of IDs to skip, which will be much more efficient --- .../rss/reader/home/HomePresenter.kt | 69 ++++++++++++++----- .../rss/reader/repository/RssRepository.kt | 7 +- .../dev/sasikanth/rss/reader/database/Post.sq | 32 +-------- 3 files changed, 56 insertions(+), 52 deletions(-) diff --git a/shared/src/commonMain/kotlin/dev/sasikanth/rss/reader/home/HomePresenter.kt b/shared/src/commonMain/kotlin/dev/sasikanth/rss/reader/home/HomePresenter.kt index 62257f837..9343b9667 100644 --- a/shared/src/commonMain/kotlin/dev/sasikanth/rss/reader/home/HomePresenter.kt +++ b/shared/src/commonMain/kotlin/dev/sasikanth/rss/reader/home/HomePresenter.kt @@ -224,17 +224,56 @@ class HomePresenter( } private fun init() { - observableActiveSource.activeSource - .onEach { selectedSource -> + combine(observableActiveSource.activeSource, settingsRepository.postsType) { + activeSource, + postsType -> + Pair(activeSource, postsType) + } + .onEach { (activeSource, postsType) -> _state.update { - it.copy(activeSource = selectedSource, posts = null, featuredPosts = null) + it.copy( + activeSource = activeSource, + postsType = postsType, + featuredPosts = null, + posts = null + ) } } - .combine(settingsRepository.postsType) { selectedSource, postsType -> - _state.update { it.copy(postsType = postsType) } - selectedSource to postsType + .flatMapLatest { + val postsType = _state.value.postsType + val activeSource = _state.value.activeSource + + val unreadOnly = + when (postsType) { + PostsType.ALL, + PostsType.TODAY, + PostsType.LAST_24_HOURS -> null + PostsType.UNREAD -> true + } + + val postsAfter = + when (postsType) { + PostsType.ALL, + PostsType.UNREAD -> Instant.DISTANT_PAST + PostsType.TODAY -> { + getTodayStartInstant() + } + PostsType.LAST_24_HOURS -> { + getLast24HourStart() + } + } + + rssRepository.featuredPosts( + selectedFeedId = activeSource?.id, + unreadOnly = unreadOnly, + after = postsAfter + ) } - .flatMapLatest { (selectedSource, postsType) -> + .onEach { featuredPosts -> + val featuredPostIds = featuredPosts.map { it.id } + val postsType = _state.value.postsType + val activeSource = _state.value.activeSource + val unreadOnly = when (postsType) { PostsType.ALL, @@ -258,24 +297,16 @@ class HomePresenter( val posts = createPager(config = createPagingConfig(pageSize = 20, enablePlaceholders = true)) { rssRepository.posts( - selectedFeedId = selectedSource?.id, + selectedFeedId = activeSource?.id, + featuredPostsIds = featuredPostIds, unreadOnly = unreadOnly, - after = postsAfter + after = postsAfter, ) } .flow .cachedIn(coroutineScope) - rssRepository - .featuredPosts( - selectedFeedId = selectedSource?.id, - unreadOnly = unreadOnly, - after = postsAfter - ) - .map { featuredPosts -> Pair(featuredPosts.toImmutableList(), posts) } - } - .onEach { (featuredPosts, posts) -> - _state.update { it.copy(featuredPosts = featuredPosts, posts = posts) } + _state.update { it.copy(featuredPosts = featuredPosts.toImmutableList(), posts = posts) } } .launchIn(coroutineScope) diff --git a/shared/src/commonMain/kotlin/dev/sasikanth/rss/reader/repository/RssRepository.kt b/shared/src/commonMain/kotlin/dev/sasikanth/rss/reader/repository/RssRepository.kt index b2736fdf1..4ac0b1730 100644 --- a/shared/src/commonMain/kotlin/dev/sasikanth/rss/reader/repository/RssRepository.kt +++ b/shared/src/commonMain/kotlin/dev/sasikanth/rss/reader/repository/RssRepository.kt @@ -197,14 +197,15 @@ class RssRepository( fun posts( selectedFeedId: String?, + featuredPostsIds: List, unreadOnly: Boolean? = null, - after: Instant = Instant.DISTANT_PAST + after: Instant = Instant.DISTANT_PAST, ): PagingSource { return QueryPagingSource( countQuery = postQueries.count( sourceId = selectedFeedId, - featuredPostsLimit = NUMBER_OF_FEATURED_POSTS, + featuredPosts = featuredPostsIds, unreadOnly = unreadOnly, postsAfter = after, ), @@ -213,7 +214,7 @@ class RssRepository( queryProvider = { limit, offset -> postQueries.posts( sourceId = selectedFeedId, - featuredPostsLimit = NUMBER_OF_FEATURED_POSTS, + featuredPosts = featuredPostsIds, unreadOnly = unreadOnly, postsAfter = after, limit = limit, diff --git a/shared/src/commonMain/sqldelight/dev/sasikanth/rss/reader/database/Post.sq b/shared/src/commonMain/sqldelight/dev/sasikanth/rss/reader/database/Post.sq index fb58ca6fe..92edc4c84 100644 --- a/shared/src/commonMain/sqldelight/dev/sasikanth/rss/reader/database/Post.sq +++ b/shared/src/commonMain/sqldelight/dev/sasikanth/rss/reader/database/Post.sq @@ -34,21 +34,7 @@ WHERE post.sourceId = :sourceId OR feedGroup.id = :sourceId ) AND - -- Skip featured posts -- - post.id NOT IN ( - SELECT post.id FROM post - WHERE - (:unreadOnly IS NULL OR post.read != :unreadOnly) AND - ( - :sourceId IS NULL OR - post.sourceId = :sourceId OR - feedGroup.id = :sourceId - ) AND - post.imageUrl IS NOT NULL AND - post.date > :postsAfter - ORDER BY post.date DESC LIMIT :featuredPostsLimit - ) AND - -- Skip featured posts -- + post.id NOT IN :featuredPosts AND post.date > :postsAfter ORDER BY post.date DESC; @@ -104,21 +90,7 @@ WHERE post.sourceId = :sourceId OR feedGroup.id = :sourceId ) AND - -- Skip featured posts -- - post.id NOT IN ( - SELECT post.id FROM post - WHERE - (:unreadOnly IS NULL OR post.read != :unreadOnly) AND - ( - :sourceId IS NULL OR - post.sourceId = :sourceId OR - feedGroup.id = :sourceId - ) AND - post.imageUrl IS NOT NULL AND - post.date > :postsAfter - ORDER BY post.date DESC LIMIT :featuredPostsLimit - ) AND - -- Skip featured posts -- + post.id NOT IN :featuredPosts AND post.date > :postsAfter ORDER BY post.date DESC LIMIT :limit OFFSET :offset; From 6a6dd77e57055aa71234849a7920aa09f12abaa8 Mon Sep 17 00:00:00 2001 From: Sasikanth Miriyampalli Date: Thu, 18 Apr 2024 11:16:48 +0530 Subject: [PATCH 3/3] Add index for post date in DESC order --- .../src/commonMain/sqldelight/databases/18.db | Bin 0 -> 106496 bytes .../dev/sasikanth/rss/reader/database/Post.sq | 1 + .../commonMain/sqldelight/migrations/17.sqm | 1 + 3 files changed, 2 insertions(+) create mode 100644 shared/src/commonMain/sqldelight/databases/18.db create mode 100644 shared/src/commonMain/sqldelight/migrations/17.sqm diff --git a/shared/src/commonMain/sqldelight/databases/18.db b/shared/src/commonMain/sqldelight/databases/18.db new file mode 100644 index 0000000000000000000000000000000000000000..043a20b431c1affde0e640e267868cf130e444a2 GIT binary patch literal 106496 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 M;JF~6|Ng)K0WgN}*Z=?k literal 0 HcmV?d00001 diff --git a/shared/src/commonMain/sqldelight/dev/sasikanth/rss/reader/database/Post.sq b/shared/src/commonMain/sqldelight/dev/sasikanth/rss/reader/database/Post.sq index 92edc4c84..ef32f58ad 100644 --- a/shared/src/commonMain/sqldelight/dev/sasikanth/rss/reader/database/Post.sq +++ b/shared/src/commonMain/sqldelight/dev/sasikanth/rss/reader/database/Post.sq @@ -17,6 +17,7 @@ CREATE TABLE post( ); CREATE INDEX post_source_id_index ON post(sourceId); +CREATE INDEX post_date_desc_index ON post (date DESC); upsert: INSERT INTO post(id, sourceId, title, description, rawContent, imageUrl, date, link, commentsLink) diff --git a/shared/src/commonMain/sqldelight/migrations/17.sqm b/shared/src/commonMain/sqldelight/migrations/17.sqm new file mode 100644 index 000000000..902910430 --- /dev/null +++ b/shared/src/commonMain/sqldelight/migrations/17.sqm @@ -0,0 +1 @@ +CREATE INDEX post_date_desc_index ON post (date DESC);