Skip to content

Commit

Permalink
Open drawer at end of feed
Browse files Browse the repository at this point in the history
  • Loading branch information
jocmp committed Dec 11, 2024
1 parent 2d16682 commit 8efadd0
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 62 deletions.
32 changes: 20 additions & 12 deletions app/src/main/java/com/capyreader/app/ui/articles/ArticleLayout.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.DrawerValue
import androidx.compose.material3.DrawerState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarDuration
Expand All @@ -20,7 +20,6 @@ import androidx.compose.material3.TopAppBarState
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffoldRole
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
import androidx.compose.material3.rememberDrawerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
Expand Down Expand Up @@ -91,7 +90,7 @@ fun ArticleLayout(
onMarkAllRead: (range: MarkRead) -> Unit,
onRequestNextFeed: () -> Unit,
onRemoveFeed: (feedID: String, onSuccess: () -> Unit, onFailure: () -> Unit) -> Unit,
drawerValue: DrawerValue = DrawerValue.Closed,
drawerState: DrawerState,
showUnauthorizedMessage: Boolean,
onUnauthorizedDismissRequest: () -> Unit,
canSwipeToNextFeed: Boolean,
Expand All @@ -104,7 +103,6 @@ fun ArticleLayout(
val (isUpdatePasswordDialogOpen, setUpdatePasswordDialogOpen) = rememberSaveable {
mutableStateOf(false)
}
val drawerState = rememberDrawerState(drawerValue)
val coroutineScope = rememberCoroutineScope()
val scaffoldNavigator = rememberArticleScaffoldNavigator()
val hasMultipleColumns = scaffoldNavigator.scaffoldDirective.maxHorizontalPartitions > 1
Expand Down Expand Up @@ -145,17 +143,22 @@ fun ArticleLayout(
scrollBehavior = scrollBehavior
)

suspend fun resetListVisibility() {
listState.scrollToItem(0)
resetScrollBehaviorOffset()
listVisible = true
}

suspend fun openNextStatus(action: suspend () -> Unit) {
listVisible = false
delay(200)
action()
scaffoldNavigator.navigateTo(ListDetailPaneScaffoldRole.List)
resetScrollBehaviorOffset()

coroutineScope.launch {
delay(500)
if (!listVisible) {
listVisible = true
resetListVisibility()
}
}
}
Expand All @@ -170,10 +173,14 @@ fun ArticleLayout(

fun markAllRead(range: MarkRead) {
if (range is MarkRead.All && filter !is ArticleFilter.Articles && canSwipeToNextFeed) {
requestNextFeed()
coroutineScope.launchUI {
openNextStatus {
onMarkAllRead(range)
}
}
} else {
onMarkAllRead(range)
}

onMarkAllRead(range)
}

val scrollToTop = {
Expand Down Expand Up @@ -376,7 +383,9 @@ fun ArticleLayout(
articles = pagingArticles,
selectedArticleKey = article?.id,
listState = listState,
onMarkAllRead = onMarkAllRead,
onMarkAllRead = { range ->
onMarkAllRead(range)
},
onSelect = { selectArticle(it) },
)
}
Expand Down Expand Up @@ -481,8 +490,7 @@ fun ArticleLayout(

LaunchedEffect(pagingArticles.itemCount) {
if (!listVisible) {
listState.scrollToItem(0)
listVisible = true
resetListVisibility()
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
package com.capyreader.app.ui.articles

import androidx.compose.material3.DrawerValue
import androidx.compose.material3.rememberDrawerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.paging.compose.collectAsLazyPagingItems
import com.capyreader.app.common.AppPreferences
import com.capyreader.app.ui.LocalConnectivity
import com.capyreader.app.ui.components.ArticleSearch
import com.capyreader.app.ui.rememberLocalConnectivity
import com.jocmp.capy.common.launchUI
import org.koin.androidx.compose.koinViewModel
import org.koin.compose.koinInject

Expand All @@ -30,10 +34,12 @@ fun ArticleScreen(
val articles = viewModel.articles.collectAsLazyPagingItems()
val nextFilter by viewModel.nextFilter.collectAsStateWithLifecycle(initialValue = null)
val canSwipeToNextFeed = nextFilter != null
val scope = rememberCoroutineScope()

val fullContent = rememberFullContent(viewModel)
val articleActions = rememberArticleActions(viewModel)
val connectivity = rememberLocalConnectivity()
val drawerState = rememberDrawerState(DrawerValue.Closed)

CompositionLocalProvider(
LocalFullContent provides fullContent,
Expand All @@ -53,6 +59,7 @@ fun ArticleScreen(
onFeedRefresh = { completion ->
viewModel.refreshFeed(completion)
},
drawerState = drawerState,
onSelectFolder = viewModel::selectFolder,
onSelectFeed = viewModel::selectFeed,
onSelectArticleFilter = viewModel::selectArticleFilter,
Expand All @@ -62,7 +69,19 @@ fun ArticleScreen(
onRequestClearArticle = viewModel::clearArticle,
onToggleArticleRead = viewModel::toggleArticleRead,
onToggleArticleStar = viewModel::toggleArticleStar,
onMarkAllRead = viewModel::markAllRead,
onMarkAllRead = { range ->
viewModel.markAllRead(
onEndOfList = {
scope.launchUI {
drawerState.open()
}
},
range = range,
filter = filter,
feeds = feeds,
folders = folders,
)
},
onRemoveFeed = viewModel::removeFeed,
showUnauthorizedMessage = viewModel.showUnauthorizedMessage,
onUnauthorizedDismissRequest = viewModel::dismissUnauthorizedMessage,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ class ArticleScreenViewModel(

private val nextFilterListener: Flow<NextFilter?> =
combine(feeds, folders, filter) { feeds, folders, filter ->
NextFilter.find(filter, feeds, folders)
NextFilter.findSwipeDestination(filter, feeds, folders)
}

private val _nextFilter = MutableStateFlow<NextFilter?>(null)
Expand Down Expand Up @@ -163,7 +163,13 @@ class ArticleScreenViewModel(
}
}

fun markAllRead(range: MarkRead) {
fun markAllRead(
onEndOfList: () -> Unit,
range: MarkRead,
filter: ArticleFilter,
feeds: List<Feed>,
folders: List<Folder>,
) {
viewModelScope.launchIO {
val articleIDs = account.unreadArticleIDs(
filter = latestFilter,
Expand All @@ -174,6 +180,20 @@ class ArticleScreenViewModel(
account.markAllRead(articleIDs).onFailure {
Sync.markReadAsync(articleIDs, context)
}

if (range is MarkRead.All) {
val nextFilter = NextFilter.findMarkReadDestination(filter, folders, feeds)

if (nextFilter != null) {
selectNextFilter(nextFilter)
} else {
if (filter.status == ArticleStatus.UNREAD) {
selectArticleFilter()
updateArticlesSince(OffsetDateTime.now().plusSeconds(1))
}
onEndOfList()
}
}
}
}

Expand Down Expand Up @@ -296,15 +316,17 @@ class ArticleScreenViewModel(
}

fun requestNextFeed() {
_nextFilter.value?.let {
when (it) {
is NextFilter.FeedFilter -> selectFeed(
feedID = it.feedID,
folderTitle = it.folderTitle
)
_nextFilter.value?.let(::selectNextFilter)
}

is NextFilter.FolderFilter -> selectFolder(title = it.folderTitle)
}
private fun selectNextFilter(filter: NextFilter) {
when (filter) {
is NextFilter.FeedFilter -> selectFeed(
feedID = filter.feedID,
folderTitle = filter.folderTitle
)

is NextFilter.FolderFilter -> selectFolder(title = filter.folderTitle)
}
}

Expand Down Expand Up @@ -366,8 +388,8 @@ class ArticleScreenViewModel(
clearArticle()
}

private fun updateArticlesSince() {
articlesSince.value = OffsetDateTime.now()
private fun updateArticlesSince(since: OffsetDateTime = OffsetDateTime.now()) {
articlesSince.value = since
}

private fun copyFolderCounts(
Expand Down
86 changes: 59 additions & 27 deletions capy/src/main/java/com/jocmp/capy/articles/NextFilter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,33 @@ sealed class NextFilter {
data class FeedFilter(val feedID: String, val folderTitle: String? = null) : NextFilter()

companion object {
fun find(
fun findMarkReadDestination(
filter: ArticleFilter,
folders: List<Folder>,
feeds: List<Feed>,
): NextFilter? {
return when (filter) {
is ArticleFilter.Feeds -> findNextFeed(filter, folders, feeds)
is ArticleFilter.Folders -> {
val folderIndex = folders.indexOfFirst { it.title == filter.folderTitle }

val nextFolder = folders.getOrNull(folderIndex + 1)
val nextFeed = feeds.firstOrNull()

if (nextFolder != null) {
FolderFilter(nextFolder.title)
} else if (nextFeed != null) {
FeedFilter(feedID = nextFeed.id, folderTitle = filter.folderTitle)
} else {
null
}
}

else -> null
}
}

fun findSwipeDestination(
filter: ArticleFilter,
feeds: List<Feed>,
folders: List<Folder>,
Expand Down Expand Up @@ -38,34 +64,40 @@ sealed class NextFilter {
FeedFilter(feedID = firstFeed.id, folderTitle = filter.folderTitle)
}

is ArticleFilter.Feeds -> {
return if (filter.folderTitle == null) {
val index = feeds.indexOfFirst { it.id == filter.feedID }
is ArticleFilter.Feeds -> findNextFeed(filter, folders, feeds)
}
}

val nextFeed = feeds.getOrNull(index + 1) ?: return null
private fun findNextFeed(
filter: ArticleFilter.Feeds,
folders: List<Folder>,
feeds: List<Feed>
): NextFilter? {
return if (filter.folderTitle == null) {
val index = feeds.indexOfFirst { it.id == filter.feedID }

FeedFilter(feedID = nextFeed.id, folderTitle = null)
} else {
val folderIndex = folders
.indexOfFirst { it.title == filter.folderTitle }

val folderFeeds = folders.getOrNull(folderIndex)?.feeds.orEmpty()

val index = folderFeeds.indexOfFirst { it.id == filter.feedID }
val nextFolderFeed = folderFeeds.getOrNull(index + 1)
val nextFolder = folders.getOrNull(folderIndex + 1)
val nextFeed = feeds.firstOrNull()

if (nextFolderFeed != null) {
FeedFilter(feedID = nextFolderFeed.id, folderTitle = filter.folderTitle)
} else if (nextFolder != null) {
FolderFilter(nextFolder.title)
} else if (nextFeed != null) {
FeedFilter(feedID = nextFeed.id, folderTitle = null)
} else {
null
}
}
val nextFeed = feeds.getOrNull(index + 1) ?: return null

FeedFilter(feedID = nextFeed.id, folderTitle = null)
} else {
val folderIndex = folders
.indexOfFirst { it.title == filter.folderTitle }

val folderFeeds = folders.getOrNull(folderIndex)?.feeds.orEmpty()

val index = folderFeeds.indexOfFirst { it.id == filter.feedID }
val nextFolderFeed = folderFeeds.getOrNull(index + 1)
val nextFolder = folders.getOrNull(folderIndex + 1)
val nextFeed = feeds.firstOrNull { it.id != filter.feedID }

if (nextFolderFeed != null) {
FeedFilter(feedID = nextFolderFeed.id, folderTitle = filter.folderTitle)
} else if (nextFolder != null) {
FolderFilter(nextFolder.title)
} else if (nextFeed != null) {
FeedFilter(feedID = nextFeed.id, folderTitle = null)
} else {
null
}
}
}
Expand Down
Loading

0 comments on commit 8efadd0

Please sign in to comment.