Optimize the feeds page
This commit is contained in:
parent
5867186751
commit
433ff6e6f2
|
@ -10,12 +10,12 @@ import androidx.work.WorkManager
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.flowOn
|
import kotlinx.coroutines.flow.flowOn
|
||||||
|
import kotlinx.coroutines.flow.mapLatest
|
||||||
import me.ash.reader.data.dao.AccountDao
|
import me.ash.reader.data.dao.AccountDao
|
||||||
import me.ash.reader.data.dao.ArticleDao
|
import me.ash.reader.data.dao.ArticleDao
|
||||||
import me.ash.reader.data.dao.FeedDao
|
import me.ash.reader.data.dao.FeedDao
|
||||||
import me.ash.reader.data.dao.GroupDao
|
import me.ash.reader.data.dao.GroupDao
|
||||||
import me.ash.reader.data.entity.*
|
import me.ash.reader.data.entity.*
|
||||||
import me.ash.reader.data.model.ImportantCount
|
|
||||||
import me.ash.reader.ui.ext.currentAccountId
|
import me.ash.reader.ui.ext.currentAccountId
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ abstract class AbstractRssRepository constructor(
|
||||||
fun pullImportant(
|
fun pullImportant(
|
||||||
isStarred: Boolean = false,
|
isStarred: Boolean = false,
|
||||||
isUnread: Boolean = false,
|
isUnread: Boolean = false,
|
||||||
): Flow<List<ImportantCount>> {
|
): Flow<Map<String, Int>> {
|
||||||
val accountId = context.currentAccountId
|
val accountId = context.currentAccountId
|
||||||
Log.i(
|
Log.i(
|
||||||
"RLog",
|
"RLog",
|
||||||
|
@ -111,6 +111,12 @@ abstract class AbstractRssRepository constructor(
|
||||||
isUnread -> articleDao
|
isUnread -> articleDao
|
||||||
.queryImportantCountWhenIsUnread(accountId, isUnread)
|
.queryImportantCountWhenIsUnread(accountId, isUnread)
|
||||||
else -> articleDao.queryImportantCountWhenIsAll(accountId)
|
else -> articleDao.queryImportantCountWhenIsAll(accountId)
|
||||||
|
}.mapLatest {
|
||||||
|
mapOf(
|
||||||
|
*(it.map {
|
||||||
|
it.feedId to it.important
|
||||||
|
}.toTypedArray())
|
||||||
|
)
|
||||||
}.flowOn(dispatcherIO)
|
}.flowOn(dispatcherIO)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import android.os.Build
|
||||||
import android.view.SoundEffectConstants
|
import android.view.SoundEffectConstants
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
@ -20,7 +21,6 @@ import me.ash.reader.ui.theme.palette.onDark
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun FilterBar(
|
fun FilterBar(
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
filter: Filter,
|
filter: Filter,
|
||||||
filterBarStyle: Int,
|
filterBarStyle: Int,
|
||||||
filterBarFilled: Boolean,
|
filterBarFilled: Boolean,
|
||||||
|
@ -33,7 +33,8 @@ fun FilterBar(
|
||||||
|
|
||||||
NavigationBar(
|
NavigationBar(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(filterBarTonalElevation)),
|
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(filterBarTonalElevation))
|
||||||
|
.navigationBarsPadding(),
|
||||||
tonalElevation = filterBarTonalElevation,
|
tonalElevation = filterBarTonalElevation,
|
||||||
) {
|
) {
|
||||||
Spacer(modifier = Modifier.width(filterBarPadding))
|
Spacer(modifier = Modifier.width(filterBarPadding))
|
||||||
|
|
|
@ -31,14 +31,14 @@ fun RYScaffold(
|
||||||
color = containerColor
|
color = containerColor
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.statusBarsPadding()
|
.statusBarsPadding(),
|
||||||
.run {
|
// .run {
|
||||||
if (bottomBar != null || floatingActionButton != null) {
|
// if (bottomBar != null || floatingActionButton != null) {
|
||||||
navigationBarsPadding()
|
// navigationBarsPadding()
|
||||||
} else {
|
// } else {
|
||||||
this
|
// this
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(
|
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(
|
||||||
containerTonalElevation,
|
containerTonalElevation,
|
||||||
color = containerColor
|
color = containerColor
|
||||||
|
|
|
@ -67,7 +67,7 @@ fun Context.share(content: String) {
|
||||||
}, getString(R.string.share)))
|
}, getString(R.string.share)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Context.openURL(url: String? = null) {
|
fun Context.openURL(url: String?) {
|
||||||
url?.takeIf { it.trim().isNotEmpty() }
|
url?.takeIf { it.trim().isNotEmpty() }
|
||||||
?.let { startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(it))) }
|
?.let { startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(it))) }
|
||||||
}
|
}
|
|
@ -1,53 +1,55 @@
|
||||||
package me.ash.reader.ui.page.home.feeds
|
package me.ash.reader.ui.page.home.feeds
|
||||||
|
|
||||||
|
import RYExtensibleVisibility
|
||||||
import android.view.HapticFeedbackConstants
|
import android.view.HapticFeedbackConstants
|
||||||
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.combinedClickable
|
import androidx.compose.foundation.combinedClickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
||||||
import androidx.compose.material3.Badge
|
import androidx.compose.material3.Badge
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.graphics.RectangleShape
|
||||||
import androidx.compose.ui.platform.LocalView
|
import androidx.compose.ui.platform.LocalView
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.Dp
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import me.ash.reader.data.entity.Feed
|
import me.ash.reader.data.entity.Feed
|
||||||
import me.ash.reader.ui.component.FeedIcon
|
import me.ash.reader.ui.component.FeedIcon
|
||||||
import me.ash.reader.ui.page.home.feeds.drawer.feed.FeedOptionViewModel
|
import me.ash.reader.ui.page.home.feeds.drawer.feed.FeedOptionViewModel
|
||||||
import kotlin.math.ln
|
import me.ash.reader.ui.theme.ShapeBottom32
|
||||||
|
|
||||||
@OptIn(
|
@OptIn(
|
||||||
androidx.compose.foundation.ExperimentalFoundationApi::class,
|
androidx.compose.foundation.ExperimentalFoundationApi::class,
|
||||||
androidx.compose.material.ExperimentalMaterialApi::class,
|
|
||||||
)
|
)
|
||||||
@Composable
|
@Composable
|
||||||
fun FeedItem(
|
fun FeedItem(
|
||||||
feed: Feed,
|
feed: Feed,
|
||||||
|
alpha: Float = 1f,
|
||||||
|
badgeAlpha: Float = 1f,
|
||||||
|
isEnded: Boolean = false,
|
||||||
|
isExpanded: () -> Boolean,
|
||||||
feedOptionViewModel: FeedOptionViewModel = hiltViewModel(),
|
feedOptionViewModel: FeedOptionViewModel = hiltViewModel(),
|
||||||
tonalElevation: Dp,
|
|
||||||
onClick: () -> Unit = {},
|
onClick: () -> Unit = {},
|
||||||
) {
|
) {
|
||||||
val view = LocalView.current
|
val view = LocalView.current
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val tonalElevationAlpha by remember {
|
|
||||||
derivedStateOf {
|
|
||||||
(ln(tonalElevation.value + 1.4f) + 2f) / 100f
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
RYExtensibleVisibility(visible = isExpanded()) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 14.dp)
|
.padding(horizontal = 16.dp)
|
||||||
.clip(RoundedCornerShape(32.dp))
|
.background(
|
||||||
|
color = MaterialTheme.colorScheme.secondary.copy(alpha = alpha),
|
||||||
|
shape = if (isEnded) ShapeBottom32 else RectangleShape,
|
||||||
|
)
|
||||||
.combinedClickable(
|
.combinedClickable(
|
||||||
onClick = {
|
onClick = {
|
||||||
onClick()
|
onClick()
|
||||||
|
@ -57,7 +59,8 @@ fun FeedItem(
|
||||||
feedOptionViewModel.showDrawer(scope, feed.id)
|
feedOptionViewModel.showDrawer(scope, feed.id)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.padding(vertical = 14.dp),
|
.padding(horizontal = 14.dp)
|
||||||
|
.padding(top = 14.dp, bottom = if (isEnded) 22.dp else 14.dp),
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
@ -80,7 +83,7 @@ fun FeedItem(
|
||||||
if ((feed.important ?: 0) != 0) {
|
if ((feed.important ?: 0) != 0) {
|
||||||
Badge(
|
Badge(
|
||||||
containerColor = MaterialTheme.colorScheme.surfaceTint.copy(
|
containerColor = MaterialTheme.colorScheme.surfaceTint.copy(
|
||||||
alpha = tonalElevationAlpha
|
alpha = badgeAlpha
|
||||||
),
|
),
|
||||||
contentColor = MaterialTheme.colorScheme.outline,
|
contentColor = MaterialTheme.colorScheme.outline,
|
||||||
content = {
|
content = {
|
||||||
|
@ -94,3 +97,4 @@ fun FeedItem(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
|
@ -31,6 +31,7 @@ import me.ash.reader.data.preference.*
|
||||||
import me.ash.reader.data.repository.SyncWorker.Companion.getIsSyncing
|
import me.ash.reader.data.repository.SyncWorker.Companion.getIsSyncing
|
||||||
import me.ash.reader.ui.component.FilterBar
|
import me.ash.reader.ui.component.FilterBar
|
||||||
import me.ash.reader.ui.component.base.*
|
import me.ash.reader.ui.component.base.*
|
||||||
|
import me.ash.reader.ui.ext.alphaLN
|
||||||
import me.ash.reader.ui.ext.collectAsStateValue
|
import me.ash.reader.ui.ext.collectAsStateValue
|
||||||
import me.ash.reader.ui.ext.findActivity
|
import me.ash.reader.ui.ext.findActivity
|
||||||
import me.ash.reader.ui.ext.getCurrentVersion
|
import me.ash.reader.ui.ext.getCurrentVersion
|
||||||
|
@ -41,9 +42,9 @@ import me.ash.reader.ui.page.home.feeds.drawer.feed.FeedOptionDrawer
|
||||||
import me.ash.reader.ui.page.home.feeds.drawer.group.GroupOptionDrawer
|
import me.ash.reader.ui.page.home.feeds.drawer.group.GroupOptionDrawer
|
||||||
import me.ash.reader.ui.page.home.feeds.subscribe.SubscribeDialog
|
import me.ash.reader.ui.page.home.feeds.subscribe.SubscribeDialog
|
||||||
import me.ash.reader.ui.page.home.feeds.subscribe.SubscribeViewModel
|
import me.ash.reader.ui.page.home.feeds.subscribe.SubscribeViewModel
|
||||||
|
import kotlin.math.ln
|
||||||
|
|
||||||
@OptIn(
|
@OptIn(
|
||||||
com.google.accompanist.pager.ExperimentalPagerApi::class,
|
|
||||||
androidx.compose.foundation.ExperimentalFoundationApi::class
|
androidx.compose.foundation.ExperimentalFoundationApi::class
|
||||||
)
|
)
|
||||||
@Composable
|
@Composable
|
||||||
|
@ -96,6 +97,24 @@ fun FeedsPage(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val feedBadgeAlpha by remember { derivedStateOf { (ln(groupListTonalElevation.value + 1.4f) + 2f) / 100f } }
|
||||||
|
val groupAlpha by remember { derivedStateOf { groupListTonalElevation.value.dp.alphaLN(weight = 1.2f) } }
|
||||||
|
val groupIndicatorAlpha by remember {
|
||||||
|
derivedStateOf {
|
||||||
|
groupListTonalElevation.value.dp.alphaLN(
|
||||||
|
weight = 1.4f
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val groupsVisible = remember(feedsUiState.groupWithFeedList) {
|
||||||
|
mutableStateMapOf(
|
||||||
|
*(feedsUiState.groupWithFeedList.filterIsInstance<GroupFeedsView.Group>().map {
|
||||||
|
it.group.id to groupListExpand.value
|
||||||
|
}.toTypedArray())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
feedsViewModel.fetchAccount()
|
feedsViewModel.fetchAccount()
|
||||||
}
|
}
|
||||||
|
@ -161,7 +180,7 @@ fun FeedsPage(
|
||||||
item {
|
item {
|
||||||
Banner(
|
Banner(
|
||||||
title = filterUiState.filter.getName(),
|
title = filterUiState.filter.getName(),
|
||||||
desc = feedsUiState.importantCount.ifEmpty { stringResource(R.string.loading) },
|
desc = feedsUiState.importantSum.ifEmpty { stringResource(R.string.loading) },
|
||||||
icon = filterUiState.filter.iconOutline,
|
icon = filterUiState.filter.iconOutline,
|
||||||
action = {
|
action = {
|
||||||
Icon(
|
Icon(
|
||||||
|
@ -189,14 +208,21 @@ fun FeedsPage(
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
}
|
}
|
||||||
itemsIndexed(feedsUiState.groupWithFeedList) { index, groupWithFeed ->
|
itemsIndexed(feedsUiState.groupWithFeedList) { index, groupWithFeed ->
|
||||||
// Crossfade(targetState = groupWithFeed) { groupWithFeed ->
|
when (groupWithFeed) {
|
||||||
Column {
|
is GroupFeedsView.Group -> {
|
||||||
|
if (index != 0) {
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
}
|
||||||
GroupItem(
|
GroupItem(
|
||||||
isExpanded = groupListExpand.value,
|
isExpanded = { groupsVisible[groupWithFeed.group.id] ?: false },
|
||||||
tonalElevation = groupListTonalElevation.value.dp,
|
|
||||||
group = groupWithFeed.group,
|
group = groupWithFeed.group,
|
||||||
feeds = groupWithFeed.feeds,
|
alpha = groupAlpha,
|
||||||
groupOnClick = {
|
indicatorAlpha = groupIndicatorAlpha,
|
||||||
|
onExpanded = {
|
||||||
|
groupsVisible[groupWithFeed.group.id] =
|
||||||
|
!(groupsVisible[groupWithFeed.group.id] ?: false)
|
||||||
|
}
|
||||||
|
) {
|
||||||
filterChange(
|
filterChange(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
homeViewModel = homeViewModel,
|
homeViewModel = homeViewModel,
|
||||||
|
@ -205,23 +231,27 @@ fun FeedsPage(
|
||||||
feed = null,
|
feed = null,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
},
|
}
|
||||||
feedOnClick = { feed ->
|
}
|
||||||
|
is GroupFeedsView.Feed -> {
|
||||||
|
FeedItem(
|
||||||
|
feed = groupWithFeed.feed,
|
||||||
|
alpha = groupAlpha,
|
||||||
|
badgeAlpha = feedBadgeAlpha,
|
||||||
|
isEnded = index != feedsUiState.groupWithFeedList.lastIndex && feedsUiState.groupWithFeedList[index + 1] is GroupFeedsView.Group,
|
||||||
|
isExpanded = { groupsVisible[groupWithFeed.feed.groupId] ?: false },
|
||||||
|
) {
|
||||||
filterChange(
|
filterChange(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
homeViewModel = homeViewModel,
|
homeViewModel = homeViewModel,
|
||||||
filterState = filterUiState.copy(
|
filterState = filterUiState.copy(
|
||||||
group = null,
|
group = null,
|
||||||
feed = feed,
|
feed = groupWithFeed.feed,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
|
||||||
if (index != feedsUiState.groupWithFeedList.lastIndex) {
|
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
item {
|
item {
|
||||||
Spacer(modifier = Modifier.height(128.dp))
|
Spacer(modifier = Modifier.height(128.dp))
|
||||||
|
|
|
@ -2,6 +2,7 @@ package me.ash.reader.ui.page.home.feeds
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.compose.foundation.lazy.LazyListState
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
|
import androidx.compose.ui.util.fastForEach
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
@ -10,7 +11,6 @@ import kotlinx.coroutines.flow.*
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import me.ash.reader.R
|
import me.ash.reader.R
|
||||||
import me.ash.reader.data.entity.Account
|
import me.ash.reader.data.entity.Account
|
||||||
import me.ash.reader.data.entity.GroupWithFeed
|
|
||||||
import me.ash.reader.data.module.DispatcherDefault
|
import me.ash.reader.data.module.DispatcherDefault
|
||||||
import me.ash.reader.data.module.DispatcherIO
|
import me.ash.reader.data.module.DispatcherIO
|
||||||
import me.ash.reader.data.repository.AccountRepository
|
import me.ash.reader.data.repository.AccountRepository
|
||||||
|
@ -67,42 +67,20 @@ class FeedsViewModel @Inject constructor(
|
||||||
combine(
|
combine(
|
||||||
rssRepository.get().pullFeeds(),
|
rssRepository.get().pullFeeds(),
|
||||||
rssRepository.get().pullImportant(isStarred, isUnread),
|
rssRepository.get().pullImportant(isStarred, isUnread),
|
||||||
) { groupWithFeedList, importantList ->
|
) { groupWithFeedList, importantMap ->
|
||||||
val groupImportantMap = mutableMapOf<String, Int>()
|
groupWithFeedList.fastForEach {
|
||||||
val feedImportantMap = mutableMapOf<String, Int>()
|
var groupImportant = 0
|
||||||
importantList.groupBy { it.groupId }.forEach { (i, list) ->
|
it.feeds.fastForEach {
|
||||||
var groupImportantSum = 0
|
it.important = importantMap[it.id]
|
||||||
list.forEach {
|
groupImportant += it.important ?: 0
|
||||||
feedImportantMap[it.feedId] = it.important
|
|
||||||
groupImportantSum += it.important
|
|
||||||
}
|
|
||||||
groupImportantMap[i] = groupImportantSum
|
|
||||||
}
|
|
||||||
val groupsIt = groupWithFeedList.iterator()
|
|
||||||
while (groupsIt.hasNext()) {
|
|
||||||
val groupWithFeed = groupsIt.next()
|
|
||||||
val groupImportant = groupImportantMap[groupWithFeed.group.id]
|
|
||||||
if (groupImportant == null && (isStarred || isUnread)) {
|
|
||||||
groupsIt.remove()
|
|
||||||
} else {
|
|
||||||
groupWithFeed.group.important = groupImportant
|
|
||||||
val feedsIt = groupWithFeed.feeds.iterator()
|
|
||||||
while (feedsIt.hasNext()) {
|
|
||||||
val feed = feedsIt.next()
|
|
||||||
val feedImportant = feedImportantMap[feed.id]
|
|
||||||
if (feedImportant == null && (isStarred || isUnread)) {
|
|
||||||
feedsIt.remove()
|
|
||||||
} else {
|
|
||||||
feed.important = feedImportant
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
it.group.important = groupImportant
|
||||||
}
|
}
|
||||||
groupWithFeedList
|
groupWithFeedList
|
||||||
}.onEach { groupWithFeedList ->
|
}.mapLatest { groupWithFeedList ->
|
||||||
_feedsUiState.update {
|
_feedsUiState.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
importantCount = groupWithFeedList.sumOf { it.group.important ?: 0 }.run {
|
importantSum = groupWithFeedList.sumOf { it.group.important ?: 0 }.run {
|
||||||
when {
|
when {
|
||||||
isStarred -> stringsRepository.getQuantityString(
|
isStarred -> stringsRepository.getQuantityString(
|
||||||
R.plurals.starred_desc,
|
R.plurals.starred_desc,
|
||||||
|
@ -121,8 +99,15 @@ class FeedsViewModel @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
groupWithFeedList = groupWithFeedList,
|
groupWithFeedList = groupWithFeedList.map {
|
||||||
feedsVisible = List(groupWithFeedList.size, init = { true })
|
mutableListOf<GroupFeedsView>(GroupFeedsView.Group(it.group)).apply {
|
||||||
|
addAll(
|
||||||
|
it.feeds.map {
|
||||||
|
GroupFeedsView.Feed(it)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}.flatten(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}.catch {
|
}.catch {
|
||||||
|
@ -133,9 +118,13 @@ class FeedsViewModel @Inject constructor(
|
||||||
|
|
||||||
data class FeedsUiState(
|
data class FeedsUiState(
|
||||||
val account: Account? = null,
|
val account: Account? = null,
|
||||||
val importantCount: String = "",
|
val importantSum: String = "",
|
||||||
val groupWithFeedList: List<GroupWithFeed> = emptyList(),
|
val groupWithFeedList: List<GroupFeedsView> = emptyList(),
|
||||||
val feedsVisible: List<Boolean> = emptyList(),
|
|
||||||
val listState: LazyListState = LazyListState(),
|
val listState: LazyListState = LazyListState(),
|
||||||
val groupsVisible: Boolean = true,
|
val groupsVisible: Boolean = true,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
sealed class GroupFeedsView {
|
||||||
|
class Group(val group: me.ash.reader.data.entity.Group) : GroupFeedsView()
|
||||||
|
class Feed(val feed: me.ash.reader.data.entity.Feed) : GroupFeedsView()
|
||||||
|
}
|
|
@ -1,58 +1,56 @@
|
||||||
package me.ash.reader.ui.page.home.feeds
|
package me.ash.reader.ui.page.home.feeds
|
||||||
|
|
||||||
import RYExtensibleVisibility
|
|
||||||
import android.view.HapticFeedbackConstants
|
import android.view.HapticFeedbackConstants
|
||||||
|
import androidx.compose.animation.animateContentSize
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.combinedClickable
|
import androidx.compose.foundation.combinedClickable
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.rounded.ExpandLess
|
import androidx.compose.material.icons.rounded.ExpandLess
|
||||||
import androidx.compose.material.icons.rounded.ExpandMore
|
import androidx.compose.material.icons.rounded.ExpandMore
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.platform.LocalView
|
import androidx.compose.ui.platform.LocalView
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.Dp
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import me.ash.reader.R
|
import me.ash.reader.R
|
||||||
import me.ash.reader.data.entity.Feed
|
|
||||||
import me.ash.reader.data.entity.Group
|
import me.ash.reader.data.entity.Group
|
||||||
import me.ash.reader.ui.ext.alphaLN
|
|
||||||
import me.ash.reader.ui.page.home.feeds.drawer.group.GroupOptionViewModel
|
import me.ash.reader.ui.page.home.feeds.drawer.group.GroupOptionViewModel
|
||||||
|
import me.ash.reader.ui.theme.Shape32
|
||||||
|
import me.ash.reader.ui.theme.ShapeTop32
|
||||||
|
|
||||||
@OptIn(androidx.compose.foundation.ExperimentalFoundationApi::class)
|
@OptIn(androidx.compose.foundation.ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun GroupItem(
|
fun GroupItem(
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
tonalElevation: Dp,
|
|
||||||
group: Group,
|
group: Group,
|
||||||
feeds: List<Feed>,
|
alpha: Float = 1f,
|
||||||
isExpanded: Boolean = true,
|
indicatorAlpha: Float = 1f,
|
||||||
|
isExpanded: () -> Boolean,
|
||||||
groupOptionViewModel: GroupOptionViewModel = hiltViewModel(),
|
groupOptionViewModel: GroupOptionViewModel = hiltViewModel(),
|
||||||
|
onExpanded: () -> Unit = {},
|
||||||
groupOnClick: () -> Unit = {},
|
groupOnClick: () -> Unit = {},
|
||||||
feedOnClick: (feed: Feed) -> Unit = {},
|
|
||||||
) {
|
) {
|
||||||
val view = LocalView.current
|
val view = LocalView.current
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
var expanded by remember { mutableStateOf(isExpanded) }
|
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
.animateContentSize()
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 16.dp)
|
.padding(horizontal = 16.dp)
|
||||||
.clip(RoundedCornerShape(32.dp))
|
.clip(if (isExpanded()) ShapeTop32 else Shape32)
|
||||||
.background(
|
.background(
|
||||||
MaterialTheme.colorScheme.secondary.copy(alpha = tonalElevation.alphaLN(weight = 1.2f))
|
MaterialTheme.colorScheme.secondary.copy(alpha = alpha)
|
||||||
)
|
)
|
||||||
.combinedClickable(
|
.combinedClickable(
|
||||||
onClick = {
|
onClick = {
|
||||||
|
@ -66,7 +64,7 @@ fun GroupItem(
|
||||||
.padding(top = 22.dp)
|
.padding(top = 22.dp)
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
|
@ -86,38 +84,21 @@ fun GroupItem(
|
||||||
.size(24.dp)
|
.size(24.dp)
|
||||||
.clip(CircleShape)
|
.clip(CircleShape)
|
||||||
.background(
|
.background(
|
||||||
MaterialTheme.colorScheme.surfaceTint.copy(
|
MaterialTheme.colorScheme.surfaceTint.copy(alpha = indicatorAlpha)
|
||||||
alpha = tonalElevation.alphaLN(weight = 1.4f)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
.clickable {
|
.clickable {
|
||||||
expanded = !expanded
|
onExpanded()
|
||||||
},
|
},
|
||||||
horizontalArrangement = Arrangement.Center,
|
horizontalArrangement = Arrangement.Center,
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = if (expanded) Icons.Rounded.ExpandLess else Icons.Rounded.ExpandMore,
|
imageVector = if (isExpanded()) Icons.Rounded.ExpandLess else Icons.Rounded.ExpandMore,
|
||||||
contentDescription = stringResource(if (expanded) R.string.expand_less else R.string.expand_more),
|
contentDescription = stringResource(if (isExpanded()) R.string.expand_less else R.string.expand_more),
|
||||||
tint = MaterialTheme.colorScheme.onSecondaryContainer,
|
tint = MaterialTheme.colorScheme.onSecondaryContainer,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.height(22.dp))
|
Spacer(modifier = Modifier.height(22.dp))
|
||||||
RYExtensibleVisibility(visible = expanded) {
|
|
||||||
Column {
|
|
||||||
feeds.forEach { feed ->
|
|
||||||
FeedItem(
|
|
||||||
feed = feed,
|
|
||||||
tonalElevation = tonalElevation,
|
|
||||||
) {
|
|
||||||
feedOnClick(feed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (feeds.isNotEmpty()) {
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
package me.ash.reader.ui.page.home.feeds.drawer.feed
|
package me.ash.reader.ui.page.home.feeds.drawer.feed
|
||||||
|
|
||||||
|
import android.view.HapticFeedbackConstants
|
||||||
import androidx.activity.compose.BackHandler
|
import androidx.activity.compose.BackHandler
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.material.ExperimentalMaterialApi
|
import androidx.compose.material.ExperimentalMaterialApi
|
||||||
|
@ -13,6 +14,7 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.platform.LocalView
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
@ -23,6 +25,7 @@ import me.ash.reader.ui.component.FeedIcon
|
||||||
import me.ash.reader.ui.component.base.BottomDrawer
|
import me.ash.reader.ui.component.base.BottomDrawer
|
||||||
import me.ash.reader.ui.component.base.TextFieldDialog
|
import me.ash.reader.ui.component.base.TextFieldDialog
|
||||||
import me.ash.reader.ui.ext.collectAsStateValue
|
import me.ash.reader.ui.ext.collectAsStateValue
|
||||||
|
import me.ash.reader.ui.ext.openURL
|
||||||
import me.ash.reader.ui.ext.roundClick
|
import me.ash.reader.ui.ext.roundClick
|
||||||
import me.ash.reader.ui.ext.showToast
|
import me.ash.reader.ui.ext.showToast
|
||||||
import me.ash.reader.ui.page.home.feeds.subscribe.ResultView
|
import me.ash.reader.ui.page.home.feeds.subscribe.ResultView
|
||||||
|
@ -34,6 +37,7 @@ fun FeedOptionDrawer(
|
||||||
content: @Composable () -> Unit = {},
|
content: @Composable () -> Unit = {},
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
val view = LocalView.current
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val feedOptionUiState = feedOptionViewModel.feedOptionUiState.collectAsStateValue()
|
val feedOptionUiState = feedOptionViewModel.feedOptionUiState.collectAsStateValue()
|
||||||
val feed = feedOptionUiState.feed
|
val feed = feedOptionUiState.feed
|
||||||
|
@ -77,7 +81,8 @@ fun FeedOptionDrawer(
|
||||||
ResultView(
|
ResultView(
|
||||||
link = feed?.url ?: stringResource(R.string.unknown),
|
link = feed?.url ?: stringResource(R.string.unknown),
|
||||||
groups = feedOptionUiState.groups,
|
groups = feedOptionUiState.groups,
|
||||||
selectedAllowNotificationPreset = feedOptionUiState.feed?.isNotification ?: false,
|
selectedAllowNotificationPreset = feedOptionUiState.feed?.isNotification
|
||||||
|
?: false,
|
||||||
selectedParseFullContentPreset = feedOptionUiState.feed?.isFullContent ?: false,
|
selectedParseFullContentPreset = feedOptionUiState.feed?.isFullContent ?: false,
|
||||||
isMoveToGroup = true,
|
isMoveToGroup = true,
|
||||||
showUnsubscribe = true,
|
showUnsubscribe = true,
|
||||||
|
@ -101,6 +106,10 @@ fun FeedOptionDrawer(
|
||||||
feedOptionViewModel.showNewGroupDialog()
|
feedOptionViewModel.showNewGroupDialog()
|
||||||
},
|
},
|
||||||
onFeedUrlClick = {
|
onFeedUrlClick = {
|
||||||
|
context.openURL(feed?.url)
|
||||||
|
},
|
||||||
|
onFeedUrlLongClick = {
|
||||||
|
view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
|
||||||
feedOptionViewModel.showFeedUrlDialog()
|
feedOptionViewModel.showFeedUrlDialog()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,15 +1,11 @@
|
||||||
package me.ash.reader.ui.page.home.feeds.subscribe
|
package me.ash.reader.ui.page.home.feeds.subscribe
|
||||||
|
|
||||||
import androidx.compose.animation.animateContentSize
|
import androidx.compose.animation.animateContentSize
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.*
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.LazyRow
|
import androidx.compose.foundation.lazy.LazyRow
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.rememberScrollState
|
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
|
||||||
import androidx.compose.foundation.verticalScroll
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.outlined.Add
|
import androidx.compose.material.icons.outlined.Add
|
||||||
import androidx.compose.material.icons.outlined.Article
|
import androidx.compose.material.icons.outlined.Article
|
||||||
|
@ -32,7 +28,6 @@ import me.ash.reader.R
|
||||||
import me.ash.reader.data.entity.Group
|
import me.ash.reader.data.entity.Group
|
||||||
import me.ash.reader.ui.component.base.RYSelectionChip
|
import me.ash.reader.ui.component.base.RYSelectionChip
|
||||||
import me.ash.reader.ui.component.base.Subtitle
|
import me.ash.reader.ui.component.base.Subtitle
|
||||||
import me.ash.reader.ui.ext.roundClick
|
|
||||||
import me.ash.reader.ui.theme.palette.alwaysLight
|
import me.ash.reader.ui.theme.palette.alwaysLight
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
@ -51,7 +46,8 @@ fun ResultView(
|
||||||
unsubscribeOnClick: () -> Unit = {},
|
unsubscribeOnClick: () -> Unit = {},
|
||||||
onGroupClick: (groupId: String) -> Unit = {},
|
onGroupClick: (groupId: String) -> Unit = {},
|
||||||
onAddNewGroup: () -> Unit = {},
|
onAddNewGroup: () -> Unit = {},
|
||||||
onFeedUrlClick: () -> Unit = {}
|
onFeedUrlClick: () -> Unit = {},
|
||||||
|
onFeedUrlLongClick: () -> Unit = {},
|
||||||
) {
|
) {
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
if (groups.isNotEmpty() && selectedGroupId.isEmpty()) onGroupClick(groups.first().id)
|
if (groups.isNotEmpty() && selectedGroupId.isEmpty()) onGroupClick(groups.first().id)
|
||||||
|
@ -60,7 +56,11 @@ fun ResultView(
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier.verticalScroll(rememberScrollState())
|
modifier = modifier.verticalScroll(rememberScrollState())
|
||||||
) {
|
) {
|
||||||
EditableUrl(text = link, onFeedUrlClick)
|
EditableUrl(
|
||||||
|
text = link,
|
||||||
|
onClick = onFeedUrlClick,
|
||||||
|
onLongClick = onFeedUrlLongClick,
|
||||||
|
)
|
||||||
Spacer(modifier = Modifier.height(26.dp))
|
Spacer(modifier = Modifier.height(26.dp))
|
||||||
|
|
||||||
Preset(
|
Preset(
|
||||||
|
@ -85,20 +85,24 @@ fun ResultView(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun EditableUrl(
|
private fun EditableUrl(
|
||||||
text: String,
|
text: String,
|
||||||
onClick: () -> Unit
|
onClick: () -> Unit,
|
||||||
|
onLongClick: () -> Unit,
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.Center
|
horizontalArrangement = Arrangement.Center
|
||||||
) {
|
) {
|
||||||
SelectionContainer {
|
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.roundClick {
|
modifier = Modifier
|
||||||
onClick()
|
.clip(MaterialTheme.shapes.small)
|
||||||
},
|
.combinedClickable(
|
||||||
|
onClick = onClick,
|
||||||
|
onLongClick = onLongClick,
|
||||||
|
),
|
||||||
text = text,
|
text = text,
|
||||||
color = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f),
|
color = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f),
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
@ -107,7 +111,6 @@ private fun EditableUrl(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun Preset(
|
private fun Preset(
|
||||||
|
|
|
@ -13,7 +13,6 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.alpha
|
import androidx.compose.ui.draw.alpha
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
@ -25,14 +24,13 @@ import me.ash.reader.data.preference.*
|
||||||
import me.ash.reader.ui.component.FeedIcon
|
import me.ash.reader.ui.component.FeedIcon
|
||||||
import me.ash.reader.ui.component.base.RYAsyncImage
|
import me.ash.reader.ui.component.base.RYAsyncImage
|
||||||
import me.ash.reader.ui.component.base.SIZE_1000
|
import me.ash.reader.ui.component.base.SIZE_1000
|
||||||
import me.ash.reader.ui.theme.SHAPE_20
|
import me.ash.reader.ui.theme.Shape20
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ArticleItem(
|
fun ArticleItem(
|
||||||
articleWithFeed: ArticleWithFeed,
|
articleWithFeed: ArticleWithFeed,
|
||||||
onClick: (ArticleWithFeed) -> Unit = {},
|
onClick: (ArticleWithFeed) -> Unit = {},
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
|
||||||
val articleListFeedIcon = LocalFlowArticleListFeedIcon.current
|
val articleListFeedIcon = LocalFlowArticleListFeedIcon.current
|
||||||
val articleListFeedName = LocalFlowArticleListFeedName.current
|
val articleListFeedName = LocalFlowArticleListFeedName.current
|
||||||
val articleListImage = LocalFlowArticleListImage.current
|
val articleListImage = LocalFlowArticleListImage.current
|
||||||
|
@ -42,12 +40,12 @@ fun ArticleItem(
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(horizontal = 12.dp)
|
.padding(horizontal = 12.dp)
|
||||||
.clip(SHAPE_20)
|
.clip(Shape20)
|
||||||
.clickable { onClick(articleWithFeed) }
|
.clickable { onClick(articleWithFeed) }
|
||||||
.padding(horizontal = 12.dp, vertical = 12.dp)
|
.padding(horizontal = 12.dp, vertical = 12.dp)
|
||||||
.alpha(if (articleWithFeed.article.isStarred || articleWithFeed.article.isUnread) 1f else 0.5f),
|
.alpha(if (articleWithFeed.article.isStarred || articleWithFeed.article.isUnread) 1f else 0.5f),
|
||||||
) {
|
) {
|
||||||
// Upper
|
// Top
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
@ -99,7 +97,7 @@ fun ArticleItem(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lower
|
// Bottom
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
) {
|
) {
|
||||||
|
@ -142,7 +140,7 @@ fun ArticleItem(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(start = 10.dp)
|
.padding(start = 10.dp)
|
||||||
.size(80.dp)
|
.size(80.dp)
|
||||||
.clip(SHAPE_20),
|
.clip(Shape20),
|
||||||
data = articleWithFeed.article.img,
|
data = articleWithFeed.article.img,
|
||||||
scale = Scale.FILL,
|
scale = Scale.FILL,
|
||||||
precision = Precision.INEXACT,
|
precision = Precision.INEXACT,
|
||||||
|
|
|
@ -0,0 +1,143 @@
|
||||||
|
package me.ash.reader.ui.page.settings.color.feeds
|
||||||
|
|
||||||
|
import androidx.compose.animation.animateContentSize
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.rounded.Add
|
||||||
|
import androidx.compose.material.icons.rounded.ArrowBack
|
||||||
|
import androidx.compose.material.icons.rounded.Refresh
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.SmallTopAppBar
|
||||||
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import me.ash.reader.R
|
||||||
|
import me.ash.reader.data.entity.Feed
|
||||||
|
import me.ash.reader.data.entity.Group
|
||||||
|
import me.ash.reader.data.model.Filter
|
||||||
|
import me.ash.reader.data.preference.FeedsGroupListExpandPreference
|
||||||
|
import me.ash.reader.data.preference.FeedsGroupListTonalElevationPreference
|
||||||
|
import me.ash.reader.data.preference.FeedsTopBarTonalElevationPreference
|
||||||
|
import me.ash.reader.ui.component.FilterBar
|
||||||
|
import me.ash.reader.ui.component.base.FeedbackIconButton
|
||||||
|
import me.ash.reader.ui.ext.alphaLN
|
||||||
|
import me.ash.reader.ui.ext.surfaceColorAtElevation
|
||||||
|
import me.ash.reader.ui.page.home.feeds.FeedItem
|
||||||
|
import me.ash.reader.ui.page.home.feeds.GroupItem
|
||||||
|
import me.ash.reader.ui.theme.palette.onDark
|
||||||
|
import kotlin.math.ln
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun FeedsPagePreview(
|
||||||
|
topBarTonalElevation: FeedsTopBarTonalElevationPreference,
|
||||||
|
groupListExpand: FeedsGroupListExpandPreference,
|
||||||
|
groupListTonalElevation: FeedsGroupListTonalElevationPreference,
|
||||||
|
filterBarStyle: Int,
|
||||||
|
filterBarFilled: Boolean,
|
||||||
|
filterBarPadding: Dp,
|
||||||
|
filterBarTonalElevation: Dp,
|
||||||
|
) {
|
||||||
|
var filter by remember { mutableStateOf(Filter.Unread) }
|
||||||
|
val feedBadgeAlpha by remember { derivedStateOf { (ln(groupListTonalElevation.value + 1.4f) + 2f) / 100f } }
|
||||||
|
val groupAlpha by remember { derivedStateOf { groupListTonalElevation.value.dp.alphaLN(weight = 1.2f) } }
|
||||||
|
val groupIndicatorAlpha by remember {
|
||||||
|
derivedStateOf {
|
||||||
|
groupListTonalElevation.value.dp.alphaLN(
|
||||||
|
weight = 1.4f
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.animateContentSize()
|
||||||
|
.background(
|
||||||
|
color = MaterialTheme.colorScheme.surfaceColorAtElevation(
|
||||||
|
groupListTonalElevation.value.dp
|
||||||
|
) onDark MaterialTheme.colorScheme.surface,
|
||||||
|
shape = RoundedCornerShape(24.dp)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
SmallTopAppBar(
|
||||||
|
title = {},
|
||||||
|
colors = TopAppBarDefaults.smallTopAppBarColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(
|
||||||
|
topBarTonalElevation.value.dp
|
||||||
|
),
|
||||||
|
),
|
||||||
|
navigationIcon = {
|
||||||
|
FeedbackIconButton(
|
||||||
|
imageVector = Icons.Rounded.ArrowBack,
|
||||||
|
contentDescription = stringResource(R.string.back),
|
||||||
|
tint = MaterialTheme.colorScheme.onSurface
|
||||||
|
)
|
||||||
|
},
|
||||||
|
actions = {
|
||||||
|
FeedbackIconButton(
|
||||||
|
imageVector = Icons.Rounded.Refresh,
|
||||||
|
contentDescription = stringResource(R.string.refresh),
|
||||||
|
tint = MaterialTheme.colorScheme.onSurface,
|
||||||
|
)
|
||||||
|
FeedbackIconButton(
|
||||||
|
imageVector = Icons.Rounded.Add,
|
||||||
|
contentDescription = stringResource(R.string.subscribe),
|
||||||
|
tint = MaterialTheme.colorScheme.onSurface,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
|
GroupItem(
|
||||||
|
isExpanded = { groupListExpand.value },
|
||||||
|
group = generateGroupPreview(),
|
||||||
|
alpha = groupAlpha,
|
||||||
|
indicatorAlpha = groupIndicatorAlpha,
|
||||||
|
)
|
||||||
|
FeedItem(
|
||||||
|
feed = generateFeedPreview(),
|
||||||
|
alpha = groupAlpha,
|
||||||
|
badgeAlpha = feedBadgeAlpha,
|
||||||
|
isEnded = true,
|
||||||
|
isExpanded = { true },
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
|
FilterBar(
|
||||||
|
filter = filter,
|
||||||
|
filterBarStyle = filterBarStyle,
|
||||||
|
filterBarFilled = filterBarFilled,
|
||||||
|
filterBarPadding = filterBarPadding,
|
||||||
|
filterBarTonalElevation = filterBarTonalElevation,
|
||||||
|
) {
|
||||||
|
filter = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
@Composable
|
||||||
|
fun generateFeedPreview(): Feed =
|
||||||
|
Feed(
|
||||||
|
id = "",
|
||||||
|
name = stringResource(R.string.preview_feed_name),
|
||||||
|
icon = "",
|
||||||
|
accountId = 0,
|
||||||
|
groupId = "",
|
||||||
|
url = "",
|
||||||
|
).apply {
|
||||||
|
important = 100
|
||||||
|
}
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
@Composable
|
||||||
|
fun generateGroupPreview(): Group =
|
||||||
|
Group(
|
||||||
|
id = "",
|
||||||
|
name = stringResource(R.string.defaults),
|
||||||
|
accountId = 0,
|
||||||
|
)
|
|
@ -1,38 +1,25 @@
|
||||||
package me.ash.reader.ui.page.settings.color.feeds
|
package me.ash.reader.ui.page.settings.color.feeds
|
||||||
|
|
||||||
import androidx.compose.animation.animateContentSize
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.rounded.Add
|
|
||||||
import androidx.compose.material.icons.rounded.ArrowBack
|
import androidx.compose.material.icons.rounded.ArrowBack
|
||||||
import androidx.compose.material.icons.rounded.Refresh
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.SmallTopAppBar
|
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.Dp
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import me.ash.reader.R
|
import me.ash.reader.R
|
||||||
import me.ash.reader.data.entity.Feed
|
|
||||||
import me.ash.reader.data.entity.Group
|
|
||||||
import me.ash.reader.data.model.Filter
|
|
||||||
import me.ash.reader.data.preference.*
|
import me.ash.reader.data.preference.*
|
||||||
import me.ash.reader.ui.component.FilterBar
|
|
||||||
import me.ash.reader.ui.component.base.*
|
import me.ash.reader.ui.component.base.*
|
||||||
import me.ash.reader.ui.ext.surfaceColorAtElevation
|
|
||||||
import me.ash.reader.ui.page.home.feeds.GroupItem
|
|
||||||
import me.ash.reader.ui.page.settings.SettingItem
|
import me.ash.reader.ui.page.settings.SettingItem
|
||||||
import me.ash.reader.ui.theme.palette.onDark
|
|
||||||
import me.ash.reader.ui.theme.palette.onLight
|
import me.ash.reader.ui.theme.palette.onLight
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
@ -270,88 +257,3 @@ fun FeedsPageStylePage(
|
||||||
groupListTonalElevationDialogVisible = false
|
groupListTonalElevationDialogVisible = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun FeedsPagePreview(
|
|
||||||
topBarTonalElevation: FeedsTopBarTonalElevationPreference,
|
|
||||||
groupListExpand: FeedsGroupListExpandPreference,
|
|
||||||
groupListTonalElevation: FeedsGroupListTonalElevationPreference,
|
|
||||||
filterBarStyle: Int,
|
|
||||||
filterBarFilled: Boolean,
|
|
||||||
filterBarPadding: Dp,
|
|
||||||
filterBarTonalElevation: Dp,
|
|
||||||
) {
|
|
||||||
var filter by remember { mutableStateOf(Filter.Unread) }
|
|
||||||
|
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
|
||||||
.animateContentSize()
|
|
||||||
.background(
|
|
||||||
color = MaterialTheme.colorScheme.surfaceColorAtElevation(
|
|
||||||
groupListTonalElevation.value.dp
|
|
||||||
) onDark MaterialTheme.colorScheme.surface,
|
|
||||||
shape = RoundedCornerShape(24.dp)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
SmallTopAppBar(
|
|
||||||
title = {},
|
|
||||||
colors = TopAppBarDefaults.smallTopAppBarColors(
|
|
||||||
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(
|
|
||||||
topBarTonalElevation.value.dp
|
|
||||||
),
|
|
||||||
),
|
|
||||||
navigationIcon = {
|
|
||||||
FeedbackIconButton(
|
|
||||||
imageVector = Icons.Rounded.ArrowBack,
|
|
||||||
contentDescription = stringResource(R.string.back),
|
|
||||||
tint = MaterialTheme.colorScheme.onSurface
|
|
||||||
) {}
|
|
||||||
},
|
|
||||||
actions = {
|
|
||||||
FeedbackIconButton(
|
|
||||||
imageVector = Icons.Rounded.Refresh,
|
|
||||||
contentDescription = stringResource(R.string.refresh),
|
|
||||||
tint = MaterialTheme.colorScheme.onSurface,
|
|
||||||
) {}
|
|
||||||
FeedbackIconButton(
|
|
||||||
imageVector = Icons.Rounded.Add,
|
|
||||||
contentDescription = stringResource(R.string.subscribe),
|
|
||||||
tint = MaterialTheme.colorScheme.onSurface,
|
|
||||||
) {}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.height(12.dp))
|
|
||||||
GroupItem(
|
|
||||||
isExpanded = groupListExpand.value,
|
|
||||||
tonalElevation = groupListTonalElevation.value.dp,
|
|
||||||
group = Group(
|
|
||||||
id = "",
|
|
||||||
name = stringResource(R.string.defaults),
|
|
||||||
accountId = 0,
|
|
||||||
),
|
|
||||||
feeds = listOf(
|
|
||||||
Feed(
|
|
||||||
id = "",
|
|
||||||
name = stringResource(R.string.preview_feed_name),
|
|
||||||
icon = "",
|
|
||||||
accountId = 0,
|
|
||||||
groupId = "",
|
|
||||||
url = "",
|
|
||||||
).apply {
|
|
||||||
important = 100
|
|
||||||
}
|
|
||||||
),
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.height(12.dp))
|
|
||||||
FilterBar(
|
|
||||||
modifier = Modifier.padding(horizontal = 12.dp),
|
|
||||||
filter = filter,
|
|
||||||
filterBarStyle = filterBarStyle,
|
|
||||||
filterBarFilled = filterBarFilled,
|
|
||||||
filterBarPadding = filterBarPadding,
|
|
||||||
filterBarTonalElevation = filterBarTonalElevation,
|
|
||||||
) {
|
|
||||||
filter = it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,124 @@
|
||||||
|
package me.ash.reader.ui.page.settings.color.flow
|
||||||
|
|
||||||
|
import androidx.compose.animation.animateContentSize
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.rounded.ArrowBack
|
||||||
|
import androidx.compose.material.icons.rounded.DoneAll
|
||||||
|
import androidx.compose.material.icons.rounded.Search
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.SmallTopAppBar
|
||||||
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import me.ash.reader.R
|
||||||
|
import me.ash.reader.data.entity.Article
|
||||||
|
import me.ash.reader.data.entity.ArticleWithFeed
|
||||||
|
import me.ash.reader.data.entity.Feed
|
||||||
|
import me.ash.reader.data.model.Filter
|
||||||
|
import me.ash.reader.data.preference.FlowArticleListTonalElevationPreference
|
||||||
|
import me.ash.reader.data.preference.FlowTopBarTonalElevationPreference
|
||||||
|
import me.ash.reader.ui.component.FilterBar
|
||||||
|
import me.ash.reader.ui.component.base.FeedbackIconButton
|
||||||
|
import me.ash.reader.ui.ext.surfaceColorAtElevation
|
||||||
|
import me.ash.reader.ui.page.home.flow.ArticleItem
|
||||||
|
import me.ash.reader.ui.theme.palette.onDark
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun FlowPagePreview(
|
||||||
|
topBarTonalElevation: FlowTopBarTonalElevationPreference,
|
||||||
|
articleListTonalElevation: FlowArticleListTonalElevationPreference,
|
||||||
|
filterBarStyle: Int,
|
||||||
|
filterBarFilled: Boolean,
|
||||||
|
filterBarPadding: Dp,
|
||||||
|
filterBarTonalElevation: Dp,
|
||||||
|
) {
|
||||||
|
var filter by remember { mutableStateOf(Filter.Unread) }
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.animateContentSize()
|
||||||
|
.background(
|
||||||
|
color = MaterialTheme.colorScheme.surfaceColorAtElevation(
|
||||||
|
articleListTonalElevation.value.dp
|
||||||
|
) onDark MaterialTheme.colorScheme.surface,
|
||||||
|
shape = RoundedCornerShape(24.dp)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
SmallTopAppBar(
|
||||||
|
title = {},
|
||||||
|
colors = TopAppBarDefaults.smallTopAppBarColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(
|
||||||
|
topBarTonalElevation.value.dp
|
||||||
|
),
|
||||||
|
),
|
||||||
|
navigationIcon = {
|
||||||
|
FeedbackIconButton(
|
||||||
|
imageVector = Icons.Rounded.ArrowBack,
|
||||||
|
contentDescription = stringResource(R.string.back),
|
||||||
|
tint = MaterialTheme.colorScheme.onSurface
|
||||||
|
) {}
|
||||||
|
},
|
||||||
|
actions = {
|
||||||
|
FeedbackIconButton(
|
||||||
|
imageVector = Icons.Rounded.DoneAll,
|
||||||
|
contentDescription = stringResource(R.string.mark_all_as_read),
|
||||||
|
tint = MaterialTheme.colorScheme.onSurface,
|
||||||
|
) {}
|
||||||
|
FeedbackIconButton(
|
||||||
|
imageVector = Icons.Rounded.Search,
|
||||||
|
contentDescription = stringResource(R.string.search),
|
||||||
|
tint = MaterialTheme.colorScheme.onSurface,
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
|
ArticleItem(
|
||||||
|
articleWithFeed = generateArticleWithFeedPreview(),
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
|
FilterBar(
|
||||||
|
filter = filter,
|
||||||
|
filterBarStyle = filterBarStyle,
|
||||||
|
filterBarFilled = filterBarFilled,
|
||||||
|
filterBarPadding = filterBarPadding,
|
||||||
|
filterBarTonalElevation = filterBarTonalElevation,
|
||||||
|
) {
|
||||||
|
filter = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
@Composable
|
||||||
|
fun generateArticleWithFeedPreview(): ArticleWithFeed =
|
||||||
|
ArticleWithFeed(
|
||||||
|
Article(
|
||||||
|
id = "",
|
||||||
|
title = stringResource(R.string.preview_article_title),
|
||||||
|
shortDescription = stringResource(R.string.preview_article_desc),
|
||||||
|
rawDescription = stringResource(R.string.preview_article_desc),
|
||||||
|
link = "",
|
||||||
|
feedId = "",
|
||||||
|
accountId = 0,
|
||||||
|
date = Date(1654053729L),
|
||||||
|
isStarred = true,
|
||||||
|
img = "https://images.unsplash.com/photo-1544716278-ca5e3f4abd8c?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1yZWxhdGVkfDJ8fHxlbnwwfHx8fA%3D%3D&auto=format&fit=crop&w=800&q=60"
|
||||||
|
),
|
||||||
|
feed = Feed(
|
||||||
|
id = "",
|
||||||
|
name = stringResource(R.string.preview_feed_name),
|
||||||
|
icon = "",
|
||||||
|
accountId = 0,
|
||||||
|
groupId = "",
|
||||||
|
url = "",
|
||||||
|
),
|
||||||
|
)
|
|
@ -1,6 +1,5 @@
|
||||||
package me.ash.reader.ui.page.settings.color.flow
|
package me.ash.reader.ui.page.settings.color.flow
|
||||||
|
|
||||||
import androidx.compose.animation.animateContentSize
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
|
@ -8,34 +7,20 @@ import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.rounded.ArrowBack
|
import androidx.compose.material.icons.rounded.ArrowBack
|
||||||
import androidx.compose.material.icons.rounded.DoneAll
|
|
||||||
import androidx.compose.material.icons.rounded.Search
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.SmallTopAppBar
|
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.Dp
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import me.ash.reader.R
|
import me.ash.reader.R
|
||||||
import me.ash.reader.data.entity.Article
|
|
||||||
import me.ash.reader.data.entity.ArticleWithFeed
|
|
||||||
import me.ash.reader.data.entity.Feed
|
|
||||||
import me.ash.reader.data.model.Filter
|
|
||||||
import me.ash.reader.data.preference.*
|
import me.ash.reader.data.preference.*
|
||||||
import me.ash.reader.ui.component.FilterBar
|
|
||||||
import me.ash.reader.ui.component.base.*
|
import me.ash.reader.ui.component.base.*
|
||||||
import me.ash.reader.ui.ext.surfaceColorAtElevation
|
|
||||||
import me.ash.reader.ui.page.home.flow.ArticleItem
|
|
||||||
import me.ash.reader.ui.page.settings.SettingItem
|
import me.ash.reader.ui.page.settings.SettingItem
|
||||||
import me.ash.reader.ui.theme.palette.onDark
|
|
||||||
import me.ash.reader.ui.theme.palette.onLight
|
import me.ash.reader.ui.theme.palette.onLight
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun FlowPageStylePage(
|
fun FlowPageStylePage(
|
||||||
|
@ -332,90 +317,3 @@ fun FlowPageStylePage(
|
||||||
articleListTonalElevationDialogVisible = false
|
articleListTonalElevationDialogVisible = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun FlowPagePreview(
|
|
||||||
topBarTonalElevation: FlowTopBarTonalElevationPreference,
|
|
||||||
articleListTonalElevation: FlowArticleListTonalElevationPreference,
|
|
||||||
filterBarStyle: Int,
|
|
||||||
filterBarFilled: Boolean,
|
|
||||||
filterBarPadding: Dp,
|
|
||||||
filterBarTonalElevation: Dp,
|
|
||||||
) {
|
|
||||||
var filter by remember { mutableStateOf(Filter.Unread) }
|
|
||||||
|
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
|
||||||
.animateContentSize()
|
|
||||||
.background(
|
|
||||||
color = MaterialTheme.colorScheme.surfaceColorAtElevation(
|
|
||||||
articleListTonalElevation.value.dp
|
|
||||||
) onDark MaterialTheme.colorScheme.surface,
|
|
||||||
shape = RoundedCornerShape(24.dp)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
SmallTopAppBar(
|
|
||||||
title = {},
|
|
||||||
colors = TopAppBarDefaults.smallTopAppBarColors(
|
|
||||||
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(
|
|
||||||
topBarTonalElevation.value.dp
|
|
||||||
),
|
|
||||||
),
|
|
||||||
navigationIcon = {
|
|
||||||
FeedbackIconButton(
|
|
||||||
imageVector = Icons.Rounded.ArrowBack,
|
|
||||||
contentDescription = stringResource(R.string.back),
|
|
||||||
tint = MaterialTheme.colorScheme.onSurface
|
|
||||||
) {}
|
|
||||||
},
|
|
||||||
actions = {
|
|
||||||
FeedbackIconButton(
|
|
||||||
imageVector = Icons.Rounded.DoneAll,
|
|
||||||
contentDescription = stringResource(R.string.mark_all_as_read),
|
|
||||||
tint = MaterialTheme.colorScheme.onSurface,
|
|
||||||
) {}
|
|
||||||
FeedbackIconButton(
|
|
||||||
imageVector = Icons.Rounded.Search,
|
|
||||||
contentDescription = stringResource(R.string.search),
|
|
||||||
tint = MaterialTheme.colorScheme.onSurface,
|
|
||||||
) {}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.height(12.dp))
|
|
||||||
ArticleItem(
|
|
||||||
articleWithFeed = ArticleWithFeed(
|
|
||||||
Article(
|
|
||||||
id = "",
|
|
||||||
title = stringResource(R.string.preview_article_title),
|
|
||||||
shortDescription = stringResource(R.string.preview_article_desc),
|
|
||||||
rawDescription = stringResource(R.string.preview_article_desc),
|
|
||||||
link = "",
|
|
||||||
feedId = "",
|
|
||||||
accountId = 0,
|
|
||||||
date = Date(),
|
|
||||||
isStarred = true,
|
|
||||||
img = "https://images.unsplash.com/photo-1544716278-ca5e3f4abd8c?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1yZWxhdGVkfDJ8fHxlbnwwfHx8fA%3D%3D&auto=format&fit=crop&w=800&q=60"
|
|
||||||
),
|
|
||||||
feed = Feed(
|
|
||||||
id = "",
|
|
||||||
name = stringResource(R.string.preview_feed_name),
|
|
||||||
icon = "",
|
|
||||||
accountId = 0,
|
|
||||||
groupId = "",
|
|
||||||
url = "",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.height(12.dp))
|
|
||||||
FilterBar(
|
|
||||||
modifier = Modifier.padding(horizontal = 12.dp),
|
|
||||||
filter = filter,
|
|
||||||
filterBarStyle = filterBarStyle,
|
|
||||||
filterBarFilled = filterBarFilled,
|
|
||||||
filterBarPadding = filterBarPadding,
|
|
||||||
filterBarTonalElevation = filterBarTonalElevation,
|
|
||||||
) {
|
|
||||||
filter = it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,6 +2,7 @@ package me.ash.reader.ui.theme
|
||||||
|
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.Shapes
|
import androidx.compose.material3.Shapes
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
val Shapes = Shapes(
|
val Shapes = Shapes(
|
||||||
|
@ -12,4 +13,14 @@ val Shapes = Shapes(
|
||||||
extraLarge = RoundedCornerShape(28.0.dp)
|
extraLarge = RoundedCornerShape(28.0.dp)
|
||||||
)
|
)
|
||||||
|
|
||||||
val SHAPE_20 = RoundedCornerShape(20.0.dp)
|
@Stable
|
||||||
|
val Shape20 = RoundedCornerShape(20.0.dp)
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
val Shape32 = RoundedCornerShape(32.0.dp)
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
val ShapeTop32 = RoundedCornerShape(32.0.dp, 32.0.dp, 0.0.dp, 0.0.dp)
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
val ShapeBottom32 = RoundedCornerShape(0.0.dp, 0.0.dp, 32.0.dp, 32.0.dp)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user