Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ui): large screen suppport for article list-detail view #846

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ plugins {
alias(libs.plugins.aboutlibraries)
alias(libs.plugins.room)
alias(libs.plugins.hilt)
alias(libs.plugins.kotlin.parcelize)
}

fun fetchGitCommitHash(): String {
Expand Down Expand Up @@ -131,6 +132,9 @@ dependencies {
implementation(libs.compose.ui.tooling.preview)
androidTestImplementation(libs.compose.ui.test.junit4)
implementation(libs.compose.material3)
implementation(libs.compose.material3.adaptive)
implementation(libs.compose.material3.adaptive.layout)
implementation(libs.compose.material3.adaptive.navigation)

// Accompanist
implementation(libs.accompanist.swiperefresh)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package me.ash.reader.ui.component.webview

import android.util.Log
import android.view.View
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
Expand Down
29 changes: 26 additions & 3 deletions app/src/main/java/me/ash/reader/ui/page/common/HomeEntry.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import me.ash.reader.ui.ext.initialFilter
import me.ash.reader.ui.ext.initialPage
import me.ash.reader.ui.ext.isFirstLaunch
import me.ash.reader.ui.page.home.HomeViewModel
import me.ash.reader.ui.page.home.adaptive.ArticleListReaderPage
import me.ash.reader.ui.page.home.feeds.FeedsPage
import me.ash.reader.ui.page.home.feeds.subscribe.SubscribeViewModel
import me.ash.reader.ui.page.home.flow.FlowPage
Expand Down Expand Up @@ -164,13 +165,35 @@ fun HomeEntry(
)
}
animatedComposable(route = RouteName.FLOW) {
FlowPage(

ArticleListReaderPage(
modifier = Modifier,
navController = navController,
homeViewModel = homeViewModel,
homeViewModel = homeViewModel
)
/* FlowPage(
homeViewModel = homeViewModel,
onNavigateToFeeds = {
if (navController.previousBackStackEntry == null) {
navController.navigate(RouteName.FEEDS) {
launchSingleTop = true
}
} else {
navController.popBackStack()
}
}, onOpenArticle = {
navController.navigate("${RouteName.READING}/${it}") {
launchSingleTop = true
}
}
)*/

}
animatedComposable(route = "${RouteName.READING}/{articleId}") {
ReadingPage(navController = navController, homeViewModel = homeViewModel)
ReadingPage(
navController = navController,
homeViewModel = homeViewModel
)
}

// Settings
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package me.ash.reader.ui.page.home.adaptive

import android.os.Parcelable
import androidx.activity.compose.BackHandler
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
import androidx.compose.material3.adaptive.layout.AnimatedPane
import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffold
import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffoldRole
import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavHostController
import kotlinx.parcelize.Parcelize
import me.ash.reader.ui.ext.collectAsStateValue
import me.ash.reader.ui.page.common.RouteName
import me.ash.reader.ui.page.home.HomeViewModel
import me.ash.reader.ui.page.home.flow.FlowPage
import me.ash.reader.ui.page.home.flow.FlowViewModel
import me.ash.reader.ui.page.home.reading.ReadingPage
import me.ash.reader.ui.page.home.reading.ReadingViewModel

@Parcelize
data class ArticleData(val id: String) : Parcelable

@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
fun ArticleListReaderPage(
modifier: Modifier = Modifier,
navController: NavHostController,
flowViewModel: FlowViewModel = hiltViewModel(),
readingViewModel: ReadingViewModel = hiltViewModel(),
homeViewModel: HomeViewModel,
) {
val navigator = rememberListDetailPaneScaffoldNavigator<ArticleData>()
BackHandler(navigator.canNavigateBack()) {
navigator.navigateBack()
}
val readerState = readingViewModel.readerStateStateFlow.collectAsStateValue()

LaunchedEffect(navigator.currentDestination?.content) {
navigator.currentDestination?.content?.id?.let { readingViewModel.initData(it) }
}

ListDetailPaneScaffold(
modifier = modifier,
directive = navigator.scaffoldDirective,
value = navigator.scaffoldValue,
listPane = {
AnimatedPane {
FlowPage(
homeViewModel = homeViewModel,
flowViewModel = flowViewModel,
readingArticleId = readerState.articleId,
onNavigateToFeeds = {
if (navController.previousBackStackEntry == null) {
navController.navigate(RouteName.FEEDS) {
launchSingleTop = true
}
} else {
navController.popBackStack()
}
}, onOpenArticle = {
navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, ArticleData(it))
}
)
}
},
detailPane = {
AnimatedPane {
ReadingPage(
navController = navController,
homeViewModel = homeViewModel,
readingViewModel = readingViewModel
)
}
}
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import androidx.compose.material.icons.rounded.Add
import androidx.compose.material.icons.rounded.UnfoldLess
import androidx.compose.material.icons.rounded.UnfoldMore
import androidx.compose.material.rememberModalBottomSheetState

import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
Expand Down Expand Up @@ -131,7 +132,7 @@ fun FeedsPage(
var isSyncing by remember { mutableStateOf(false) }
val syncingState = rememberPullToRefreshState()
val syncingScope = rememberCoroutineScope()
val doSync:() -> Unit = {
val doSync: () -> Unit = {
isSyncing = true
syncingScope.launch {

Expand Down Expand Up @@ -230,13 +231,14 @@ fun FeedsPage(
}
},
content = {

PullToRefreshBox(
state=syncingState,
state = syncingState,
isRefreshing = isSyncing,
onRefresh = doSync
) {
LazyColumn(
modifier=Modifier.fillMaxSize(),
modifier = Modifier.fillMaxSize(),
state = listState
) {
item {
Expand Down Expand Up @@ -351,6 +353,7 @@ fun FeedsPage(
}
}


is GroupFeedsView.Feed -> {
FeedItem(
feed = groupWithFeed.feed,
Expand Down
31 changes: 20 additions & 11 deletions app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleItem.kt
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.ContentScale
Expand Down Expand Up @@ -90,6 +91,7 @@ private const val TAG = "ArticleItem"
fun ArticleItem(
modifier: Modifier = Modifier,
articleWithFeed: ArticleWithFeed,
isHighlighted: Boolean = false,
isUnread: Boolean = articleWithFeed.article.isUnread,
onClick: (ArticleWithFeed) -> Unit = {},
onLongClick: (() -> Unit)? = null
Expand All @@ -106,6 +108,7 @@ fun ArticleItem(
dateString = article.dateString,
imgData = article.img,
isStarred = article.isStarred,
isHighlighted = isHighlighted,
isUnread = isUnread,
onClick = { onClick(articleWithFeed) },
onLongClick = onLongClick
Expand All @@ -125,6 +128,7 @@ fun ArticleItem(
imgData: Any? = null,
isStarred: Boolean = false,
isUnread: Boolean = false,
isHighlighted: Boolean = false,
onClick: () -> Unit = {},
onLongClick: (() -> Unit)? = null
) {
Expand All @@ -139,21 +143,25 @@ fun ArticleItem(
modifier = modifier
.padding(horizontal = 12.dp)
.clip(Shape20)
.background(if (isHighlighted) MaterialTheme.colorScheme.primaryContainer else Color.Transparent)
.combinedClickable(
onClick = onClick,
onLongClick = onLongClick,
)
.padding(horizontal = 12.dp, vertical = 12.dp)
.alpha(
when (articleListReadIndicator) {
FlowArticleReadIndicatorPreference.None -> 1f
if (isHighlighted) 1f else {
when (articleListReadIndicator) {

FlowArticleReadIndicatorPreference.AllRead -> {
if (isUnread) 1f else 0.5f
}
FlowArticleReadIndicatorPreference.None -> 1f

FlowArticleReadIndicatorPreference.AllRead -> {
if (isUnread) 1f else 0.5f
}

FlowArticleReadIndicatorPreference.ExcludingStarred -> {
if (isUnread || isStarred) 1f else 0.5f
FlowArticleReadIndicatorPreference.ExcludingStarred -> {
if (isUnread || isStarred) 1f else 0.5f
}
}
}
),
Expand Down Expand Up @@ -281,13 +289,13 @@ private const val SwipeActionDelay = 300L
@Composable
fun SwipeableArticleItem(
articleWithFeed: ArticleWithFeed,
isHighlighted: Boolean = false,
isUnread: Boolean = articleWithFeed.article.isUnread,
articleListTonalElevation: Int = 0,
onClick: (ArticleWithFeed) -> Unit = {},
isSwipeEnabled: () -> Boolean = { false },
isMenuEnabled: Boolean = true,
onToggleStarred: (ArticleWithFeed) -> Unit = { },
onToggleRead: (ArticleWithFeed) -> Unit = { },
onToggleStarred: (ArticleWithFeed) -> Unit = {},
onToggleRead: (ArticleWithFeed) -> Unit = {},
onMarkAboveAsRead: ((ArticleWithFeed) -> Unit)? = null,
onMarkBelowAsRead: ((ArticleWithFeed) -> Unit)? = null,
onShare: ((ArticleWithFeed) -> Unit)? = null,
Expand Down Expand Up @@ -338,9 +346,10 @@ fun SwipeableArticleItem(
) {
ArticleItem(
articleWithFeed = articleWithFeed,
isHighlighted = isHighlighted,
isUnread = isUnread,
onClick = onClick,
onLongClick = onLongClick
onLongClick = onLongClick,
)
with(articleWithFeed.article) {
if (isMenuEnabled) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ fun LazyListScope.ArticleList(
isShowFeedIcon: Boolean,
isShowStickyHeader: Boolean,
articleListTonalElevation: Int,
isSwipeEnabled: () -> Boolean = { false },
readingArticleId: String?,
isMenuEnabled: Boolean = true,
onClick: (ArticleWithFeed) -> Unit = {},
onToggleStarred: (ArticleWithFeed) -> Unit = { },
Expand All @@ -43,10 +43,10 @@ fun LazyListScope.ArticleList(
val article = item.articleWithFeed.article
SwipeableArticleItem(
articleWithFeed = item.articleWithFeed,
isHighlighted = readingArticleId == item.articleWithFeed.article.id,
isUnread = diffMap[article.id]?.isUnread ?: article.isUnread,
articleListTonalElevation = articleListTonalElevation,
onClick = onClick,
isSwipeEnabled = isSwipeEnabled,
isMenuEnabled = isMenuEnabled,
onToggleStarred = onToggleStarred,
onToggleRead = onToggleRead,
Expand Down Expand Up @@ -74,10 +74,10 @@ fun LazyListScope.ArticleList(
val article = item.articleWithFeed.article
SwipeableArticleItem(
articleWithFeed = item.articleWithFeed,
isHighlighted = readingArticleId == item.articleWithFeed.article.id,
isUnread = diffMap[article.id]?.isUnread ?: article.isUnread,
articleListTonalElevation = articleListTonalElevation,
onClick = onClick,
isSwipeEnabled = isSwipeEnabled,
isMenuEnabled = isMenuEnabled,
onToggleStarred = onToggleStarred,
onToggleRead = onToggleRead,
Expand Down
33 changes: 21 additions & 12 deletions app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import androidx.work.WorkInfo
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import me.ash.reader.R
import me.ash.reader.domain.model.article.ArticleFlowItem
import me.ash.reader.domain.model.article.ArticleWithFeed
import me.ash.reader.domain.model.general.Filter
import me.ash.reader.infrastructure.preference.LocalFlowArticleListDateStickyHeader
Expand All @@ -67,7 +68,9 @@ import me.ash.reader.ui.page.home.HomeViewModel
)
@Composable
fun FlowPage(
navController: NavHostController,
onNavigateToFeeds: () -> Unit,
readingArticleId: String?,
onOpenArticle: (articleId: String) -> Unit,
flowViewModel: FlowViewModel = hiltViewModel(),
homeViewModel: HomeViewModel,
) {
Expand Down Expand Up @@ -193,6 +196,20 @@ fun FlowPage(
}
}

LaunchedEffect(readingArticleId) {
if (readingArticleId != null) {
val item =
listState.layoutInfo.visibleItemsInfo.firstOrNull { it.key == readingArticleId }

val index = item?.index
?: pagingItems.itemSnapshotList.indexOfFirst { it is ArticleFlowItem.Article && it.articleWithFeed.article.id == readingArticleId }

if (index != -1) {
listState.animateScrollToItem(index, scrollOffset = -100)
}
}
}

BackHandler(onSearch) {
onSearch = false
}
Expand All @@ -207,13 +224,7 @@ fun FlowPage(
tint = MaterialTheme.colorScheme.onSurface
) {
onSearch = false
if (navController.previousBackStackEntry == null) {
navController.navigate(RouteName.FEEDS) {
launchSingleTop = true
}
} else {
navController.popBackStack()
}
onNavigateToFeeds()
}
},
actions = {
Expand Down Expand Up @@ -333,16 +344,14 @@ fun FlowPage(
}
ArticleList(
pagingItems = pagingItems,
readingArticleId = readingArticleId,
diffMap = flowViewModel.diffMap,
isShowFeedIcon = articleListFeedIcon.value,
isShowStickyHeader = articleListDateStickyHeader.value,
articleListTonalElevation = articleListTonalElevation.value,
isSwipeEnabled = { listState.isScrollInProgress },
onClick = {
onSearch = false
navController.navigate("${RouteName.READING}/${it.article.id}") {
launchSingleTop = true
}
onOpenArticle(it.article.id)
},
onToggleStarred = onToggleStarred,
onToggleRead = onToggleRead,
Expand Down
Loading
Loading