Add FlowPage style settings

This commit is contained in:
Ash 2022-05-01 06:28:52 +08:00
parent 0cfd63c571
commit 9bea2e8e8b
26 changed files with 1098 additions and 111 deletions

View File

@ -2,13 +2,16 @@ package me.ash.reader.data.entity
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.FiberManualRecord import androidx.compose.material.icons.outlined.FiberManualRecord
import androidx.compose.material.icons.rounded.FiberManualRecord
import androidx.compose.material.icons.rounded.Star
import androidx.compose.material.icons.rounded.StarOutline import androidx.compose.material.icons.rounded.StarOutline
import androidx.compose.material.icons.rounded.Subject import androidx.compose.material.icons.rounded.Subject
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
class Filter( class Filter(
var index: Int, val index: Int,
var icon: ImageVector, val iconOutline: ImageVector,
val iconFilled: ImageVector,
) { ) {
fun isStarred(): Boolean = this == Starred fun isStarred(): Boolean = this == Starred
fun isUnread(): Boolean = this == Unread fun isUnread(): Boolean = this == Unread
@ -17,15 +20,18 @@ class Filter(
companion object { companion object {
val Starred = Filter( val Starred = Filter(
index = 0, index = 0,
icon = Icons.Rounded.StarOutline, iconOutline = Icons.Rounded.StarOutline,
iconFilled = Icons.Rounded.Star,
) )
val Unread = Filter( val Unread = Filter(
index = 1, index = 1,
icon = Icons.Outlined.FiberManualRecord, iconOutline = Icons.Outlined.FiberManualRecord,
iconFilled = Icons.Rounded.FiberManualRecord,
) )
val All = Filter( val All = Filter(
index = 2, index = 2,
icon = Icons.Rounded.Subject, iconOutline = Icons.Rounded.Subject,
iconFilled = Icons.Rounded.Subject,
) )
} }
} }

View File

@ -0,0 +1,45 @@
package me.ash.reader.data.preference
import android.content.Context
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import me.ash.reader.ui.ext.DataStoreKeys
import me.ash.reader.ui.ext.dataStore
import me.ash.reader.ui.ext.put
sealed class ArticleListDatePreference(val value: Boolean) : Preference() {
object ON : ArticleListDatePreference(true)
object OFF : ArticleListDatePreference(false)
override fun put(context: Context, scope: CoroutineScope) {
scope.launch(Dispatchers.IO) {
context.dataStore.put(
DataStoreKeys.ArticleListDate,
value
)
}
}
companion object {
val default = ON
val values = listOf(ON, OFF)
val Context.articleListDate: Flow<ArticleListDatePreference>
get() = this.dataStore.data.map {
when (it[DataStoreKeys.ArticleListDate.key]) {
true -> ON
false -> OFF
else -> default
}
}
}
}
operator fun ArticleListDatePreference.not(): ArticleListDatePreference =
when (value) {
true -> ArticleListDatePreference.OFF
false -> ArticleListDatePreference.ON
}

View File

@ -0,0 +1,45 @@
package me.ash.reader.data.preference
import android.content.Context
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import me.ash.reader.ui.ext.DataStoreKeys
import me.ash.reader.ui.ext.dataStore
import me.ash.reader.ui.ext.put
sealed class ArticleListDescPreference(val value: Boolean) : Preference() {
object ON : ArticleListDescPreference(true)
object OFF : ArticleListDescPreference(false)
override fun put(context: Context, scope: CoroutineScope) {
scope.launch(Dispatchers.IO) {
context.dataStore.put(
DataStoreKeys.ArticleListDesc,
value
)
}
}
companion object {
val default = ON
val values = listOf(ON, OFF)
val Context.articleListDesc: Flow<ArticleListDescPreference>
get() = this.dataStore.data.map {
when (it[DataStoreKeys.ArticleListDesc.key]) {
true -> ON
false -> OFF
else -> default
}
}
}
}
operator fun ArticleListDescPreference.not(): ArticleListDescPreference =
when (value) {
true -> ArticleListDescPreference.OFF
false -> ArticleListDescPreference.ON
}

View File

@ -0,0 +1,45 @@
package me.ash.reader.data.preference
import android.content.Context
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import me.ash.reader.ui.ext.DataStoreKeys
import me.ash.reader.ui.ext.dataStore
import me.ash.reader.ui.ext.put
sealed class ArticleListFeedIconPreference(val value: Boolean) : Preference() {
object ON : ArticleListFeedIconPreference(true)
object OFF : ArticleListFeedIconPreference(false)
override fun put(context: Context, scope: CoroutineScope) {
scope.launch(Dispatchers.IO) {
context.dataStore.put(
DataStoreKeys.ArticleListFeedIcon,
value
)
}
}
companion object {
val default = ON
val values = listOf(ON, OFF)
val Context.articleListFeedIcon: Flow<ArticleListFeedIconPreference>
get() = this.dataStore.data.map {
when (it[DataStoreKeys.ArticleListFeedIcon.key]) {
true -> ON
false -> OFF
else -> default
}
}
}
}
operator fun ArticleListFeedIconPreference.not(): ArticleListFeedIconPreference =
when (value) {
true -> ArticleListFeedIconPreference.OFF
false -> ArticleListFeedIconPreference.ON
}

View File

@ -0,0 +1,45 @@
package me.ash.reader.data.preference
import android.content.Context
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import me.ash.reader.ui.ext.DataStoreKeys
import me.ash.reader.ui.ext.dataStore
import me.ash.reader.ui.ext.put
sealed class ArticleListFeedNamePreference(val value: Boolean) : Preference() {
object ON : ArticleListFeedNamePreference(true)
object OFF : ArticleListFeedNamePreference(false)
override fun put(context: Context, scope: CoroutineScope) {
scope.launch(Dispatchers.IO) {
context.dataStore.put(
DataStoreKeys.ArticleListFeedName,
value
)
}
}
companion object {
val default = ON
val values = listOf(ON, OFF)
val Context.articleListFeedName: Flow<ArticleListFeedNamePreference>
get() = this.dataStore.data.map {
when (it[DataStoreKeys.ArticleListFeedName.key]) {
true -> ON
false -> OFF
else -> default
}
}
}
}
operator fun ArticleListFeedNamePreference.not(): ArticleListFeedNamePreference =
when (value) {
true -> ArticleListFeedNamePreference.OFF
false -> ArticleListFeedNamePreference.ON
}

View File

@ -0,0 +1,45 @@
package me.ash.reader.data.preference
import android.content.Context
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import me.ash.reader.ui.ext.DataStoreKeys
import me.ash.reader.ui.ext.dataStore
import me.ash.reader.ui.ext.put
sealed class ArticleListImagePreference(val value: Boolean) : Preference() {
object ON : ArticleListImagePreference(true)
object OFF : ArticleListImagePreference(false)
override fun put(context: Context, scope: CoroutineScope) {
scope.launch(Dispatchers.IO) {
context.dataStore.put(
DataStoreKeys.ArticleListImage,
value
)
}
}
companion object {
val default = ON
val values = listOf(ON, OFF)
val Context.articleListImage: Flow<ArticleListImagePreference>
get() = this.dataStore.data.map {
when (it[DataStoreKeys.ArticleListImage.key]) {
true -> ON
false -> OFF
else -> default
}
}
}
}
operator fun ArticleListImagePreference.not(): ArticleListImagePreference =
when (value) {
true -> ArticleListImagePreference.OFF
false -> ArticleListImagePreference.ON
}

View File

@ -0,0 +1,57 @@
package me.ash.reader.data.preference
import android.content.Context
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import me.ash.reader.ui.ext.DataStoreKeys
import me.ash.reader.ui.ext.dataStore
import me.ash.reader.ui.ext.put
sealed class ArticleListTonalElevationPreference(val value: Int) : Preference() {
object Level0 : ArticleListTonalElevationPreference(0)
object Level1 : ArticleListTonalElevationPreference(1)
object Level2 : ArticleListTonalElevationPreference(3)
object Level3 : ArticleListTonalElevationPreference(6)
object Level4 : ArticleListTonalElevationPreference(8)
object Level5 : ArticleListTonalElevationPreference(12)
override fun put(context: Context, scope: CoroutineScope) {
scope.launch(Dispatchers.IO) {
context.dataStore.put(
DataStoreKeys.ArticleListTonalElevation,
value
)
}
}
fun getDesc(context: Context): String =
when (this) {
Level0 -> "Level 0 (0dp)"
Level1 -> "Level 1 (1dp)"
Level2 -> "Level 2 (3dp)"
Level3 -> "Level 3 (6dp)"
Level4 -> "Level 4 (8dp)"
Level5 -> "Level 5 (12dp)"
}
companion object {
val default = Level0
val values = listOf(Level0, Level1, Level2, Level3, Level4, Level5)
val Context.articleListTonalElevation: Flow<ArticleListTonalElevationPreference>
get() = this.dataStore.data.map {
when (it[DataStoreKeys.ArticleListTonalElevation.key]) {
0 -> Level0
1 -> Level1
3 -> Level2
6 -> Level3
8 -> Level4
12 -> Level5
else -> default
}
}
}
}

View File

@ -0,0 +1,45 @@
package me.ash.reader.data.preference
import android.content.Context
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import me.ash.reader.ui.ext.DataStoreKeys
import me.ash.reader.ui.ext.dataStore
import me.ash.reader.ui.ext.put
sealed class FilterBarFilledPreference(val value: Boolean) : Preference() {
object ON : FilterBarFilledPreference(true)
object OFF : FilterBarFilledPreference(false)
override fun put(context: Context, scope: CoroutineScope) {
scope.launch(Dispatchers.IO) {
context.dataStore.put(
DataStoreKeys.FilterBarFilled,
value
)
}
}
companion object {
val default = OFF
val values = listOf(ON, OFF)
val Context.filterBarFilled: Flow<FilterBarFilledPreference>
get() = this.dataStore.data.map {
when (it[DataStoreKeys.FilterBarFilled.key]) {
true -> ON
false -> OFF
else -> default
}
}
}
}
operator fun FilterBarFilledPreference.not(): FilterBarFilledPreference =
when (value) {
true -> FilterBarFilledPreference.OFF
false -> FilterBarFilledPreference.ON
}

View File

@ -0,0 +1,29 @@
package me.ash.reader.data.preference
import android.content.Context
import android.util.Log
import androidx.compose.runtime.Immutable
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import me.ash.reader.ui.ext.DataStoreKeys
import me.ash.reader.ui.ext.dataStore
import me.ash.reader.ui.ext.put
@Immutable
object FilterBarPaddingPreference {
const val default = 0
val Context.filterBarPadding: Flow<Int>
get() = this.dataStore.data.map {
it[DataStoreKeys.FilterBarPadding.key] ?: 0
}
fun put(context: Context, scope: CoroutineScope, value: Int) {
scope.launch(Dispatchers.IO) {
context.dataStore.put(DataStoreKeys.FilterBarPadding, value)
}
}
}

View File

@ -0,0 +1,48 @@
package me.ash.reader.data.preference
import android.content.Context
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import me.ash.reader.ui.ext.DataStoreKeys
import me.ash.reader.ui.ext.dataStore
import me.ash.reader.ui.ext.put
sealed class FilterBarStylePreference(val value: Int) : Preference() {
object Icon : FilterBarStylePreference(0)
object IconLabel : FilterBarStylePreference(1)
object IconLabelOnlySelected : FilterBarStylePreference(2)
override fun put(context: Context, scope: CoroutineScope) {
scope.launch(Dispatchers.IO) {
context.dataStore.put(
DataStoreKeys.FilterBarStyle,
value
)
}
}
fun getDesc(context: Context): String =
when (this) {
Icon -> "图标"
IconLabel -> "图标 + 标签"
IconLabelOnlySelected -> "图标 + 标签(仅选中时)"
}
companion object {
val default = Icon
val values = listOf(Icon, IconLabel, IconLabelOnlySelected)
val Context.filterBarStyle: Flow<FilterBarStylePreference>
get() = this.dataStore.data.map {
when (it[DataStoreKeys.FilterBarStyle.key]) {
0 -> Icon
1 -> IconLabel
2 -> IconLabelOnlySelected
else -> default
}
}
}
}

View File

@ -0,0 +1,57 @@
package me.ash.reader.data.preference
import android.content.Context
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import me.ash.reader.ui.ext.DataStoreKeys
import me.ash.reader.ui.ext.dataStore
import me.ash.reader.ui.ext.put
sealed class FilterBarTonalElevationPreference(val value: Int) : Preference() {
object Level0 : FilterBarTonalElevationPreference(0)
object Level1 : FilterBarTonalElevationPreference(1)
object Level2 : FilterBarTonalElevationPreference(3)
object Level3 : FilterBarTonalElevationPreference(6)
object Level4 : FilterBarTonalElevationPreference(8)
object Level5 : FilterBarTonalElevationPreference(12)
override fun put(context: Context, scope: CoroutineScope) {
scope.launch(Dispatchers.IO) {
context.dataStore.put(
DataStoreKeys.FilterBarTonalElevation,
value
)
}
}
fun getDesc(context: Context): String =
when (this) {
Level0 -> "Level 0 (0dp)"
Level1 -> "Level 1 (1dp)"
Level2 -> "Level 2 (3dp)"
Level3 -> "Level 3 (6dp)"
Level4 -> "Level 4 (8dp)"
Level5 -> "Level 5 (12dp)"
}
companion object {
val default = Level0
val values = listOf(Level0, Level1, Level2, Level3, Level4, Level5)
val Context.filterBarTonalElevation: Flow<FilterBarTonalElevationPreference>
get() = this.dataStore.data.map {
when (it[DataStoreKeys.FilterBarTonalElevation.key]) {
0 -> Level0
1 -> Level1
3 -> Level2
6 -> Level3
8 -> Level4
12 -> Level5
else -> default
}
}
}
}

View File

@ -0,0 +1,8 @@
package me.ash.reader.data.preference
import android.content.Context
import kotlinx.coroutines.CoroutineScope
sealed class Preference {
abstract fun put(context: Context, scope: CoroutineScope)
}

View File

@ -33,6 +33,7 @@ val Context.currentAccountType: Int
get() = this.dataStore.get(DataStoreKeys.CurrentAccountType)!! get() = this.dataStore.get(DataStoreKeys.CurrentAccountType)!!
val Context.themeIndex: Int val Context.themeIndex: Int
get() = this.dataStore.get(DataStoreKeys.ThemeIndex) ?: 5 get() = this.dataStore.get(DataStoreKeys.ThemeIndex) ?: 5
val Context.customPrimaryColor: String val Context.customPrimaryColor: String
get() = this.dataStore.get(DataStoreKeys.CustomPrimaryColor) ?: "" get() = this.dataStore.get(DataStoreKeys.CustomPrimaryColor) ?: ""
val Context.initialPage: Int val Context.initialPage: Int
@ -129,6 +130,56 @@ sealed class DataStoreKeys<T> {
get() = stringPreferencesKey("customPrimaryColor") get() = stringPreferencesKey("customPrimaryColor")
} }
object FilterBarStyle : DataStoreKeys<Int>() {
override val key: Preferences.Key<Int>
get() = intPreferencesKey("filterBarStyle")
}
object FilterBarFilled : DataStoreKeys<Boolean>() {
override val key: Preferences.Key<Boolean>
get() = booleanPreferencesKey("filterBarFilled")
}
object FilterBarPadding : DataStoreKeys<Int>() {
override val key: Preferences.Key<Int>
get() = intPreferencesKey("filterBarPadding")
}
object FilterBarTonalElevation : DataStoreKeys<Int>() {
override val key: Preferences.Key<Int>
get() = intPreferencesKey("filterBarTonalElevation")
}
object ArticleListFeedIcon : DataStoreKeys<Boolean>() {
override val key: Preferences.Key<Boolean>
get() = booleanPreferencesKey("articleListFeedIcon")
}
object ArticleListFeedName : DataStoreKeys<Boolean>() {
override val key: Preferences.Key<Boolean>
get() = booleanPreferencesKey("articleListFeedName")
}
object ArticleListImage : DataStoreKeys<Boolean>() {
override val key: Preferences.Key<Boolean>
get() = booleanPreferencesKey("articleListImage")
}
object ArticleListDesc : DataStoreKeys<Boolean>() {
override val key: Preferences.Key<Boolean>
get() = booleanPreferencesKey("articleListDesc")
}
object ArticleListDate : DataStoreKeys<Boolean>() {
override val key: Preferences.Key<Boolean>
get() = booleanPreferencesKey("articleListDate")
}
object ArticleListTonalElevation : DataStoreKeys<Int>() {
override val key: Preferences.Key<Int>
get() = intPreferencesKey("articleListTonalElevation")
}
object InitialPage : DataStoreKeys<Int>() { object InitialPage : DataStoreKeys<Int>() {
override val key: Preferences.Key<Int> override val key: Preferences.Key<Int>
get() = intPreferencesKey("initialPage") get() = intPreferencesKey("initialPage")

View File

@ -3,10 +3,17 @@ package me.ash.reader.ui.ext
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
@Composable @Composable
fun <T> StateFlow<T>.collectAsStateValue( fun <T> StateFlow<T>.collectAsStateValue(
context: CoroutineContext = Dispatchers.Default context: CoroutineContext = Dispatchers.Default
): T = collectAsState(context).value ): T = collectAsState(context).value
@Composable
fun <T : R, R> Flow<T>.collectAsStateValue(
initial: R,
context: CoroutineContext = Dispatchers.Default
): R = collectAsState(initial, context).value

View File

@ -22,6 +22,7 @@ import me.ash.reader.ui.page.home.flow.FlowPage
import me.ash.reader.ui.page.home.read.ReadPage import me.ash.reader.ui.page.home.read.ReadPage
import me.ash.reader.ui.page.settings.SettingsPage import me.ash.reader.ui.page.settings.SettingsPage
import me.ash.reader.ui.page.settings.color.ColorAndStyle import me.ash.reader.ui.page.settings.color.ColorAndStyle
import me.ash.reader.ui.page.settings.color.flow.FlowPageStyle
import me.ash.reader.ui.page.settings.interaction.Interaction import me.ash.reader.ui.page.settings.interaction.Interaction
import me.ash.reader.ui.page.settings.tips.TipsAndSupport import me.ash.reader.ui.page.settings.tips.TipsAndSupport
import me.ash.reader.ui.page.startup.StartupPage import me.ash.reader.ui.page.startup.StartupPage
@ -97,9 +98,12 @@ fun HomeEntry(
navController = navController, navController = navController,
startDestination = if (context.isFirstLaunch) RouteName.STARTUP else RouteName.FEEDS, startDestination = if (context.isFirstLaunch) RouteName.STARTUP else RouteName.FEEDS,
) { ) {
// Startup
animatedComposable(route = RouteName.STARTUP) { animatedComposable(route = RouteName.STARTUP) {
StartupPage(navController) StartupPage(navController)
} }
// Home
animatedComposable(route = RouteName.FEEDS) { animatedComposable(route = RouteName.FEEDS) {
FeedsPage(navController = navController, homeViewModel = homeViewModel) FeedsPage(navController = navController, homeViewModel = homeViewModel)
} }
@ -113,15 +117,26 @@ fun HomeEntry(
animatedComposable(route = "${RouteName.READING}/{articleId}") { animatedComposable(route = "${RouteName.READING}/{articleId}") {
ReadPage(navController = navController) ReadPage(navController = navController)
} }
// Settings
animatedComposable(route = RouteName.SETTINGS) { animatedComposable(route = RouteName.SETTINGS) {
SettingsPage(navController) SettingsPage(navController)
} }
// Color & Style
animatedComposable(route = RouteName.COLOR_AND_STYLE) { animatedComposable(route = RouteName.COLOR_AND_STYLE) {
ColorAndStyle(navController) ColorAndStyle(navController)
} }
animatedComposable(route = RouteName.FLOW_PAGE_STYLE) {
FlowPageStyle(navController)
}
// Interaction
animatedComposable(route = RouteName.INTERACTION) { animatedComposable(route = RouteName.INTERACTION) {
Interaction(navController) Interaction(navController)
} }
// Tips & Support
animatedComposable(route = RouteName.TIPS_AND_SUPPORT) { animatedComposable(route = RouteName.TIPS_AND_SUPPORT) {
TipsAndSupport(navController) TipsAndSupport(navController)
} }

View File

@ -1,12 +1,24 @@
package me.ash.reader.ui.page.common package me.ash.reader.ui.page.common
object RouteName { object RouteName {
// Startup
const val STARTUP = "startup" const val STARTUP = "startup"
// Home
const val FEEDS = "feeds" const val FEEDS = "feeds"
const val FLOW = "flow" const val FLOW = "flow"
const val READING = "reading" const val READING = "reading"
// Settings
const val SETTINGS = "settings" const val SETTINGS = "settings"
// Color & Style
const val COLOR_AND_STYLE = "color_and_style" const val COLOR_AND_STYLE = "color_and_style"
const val FLOW_PAGE_STYLE = "flow_page_style"
// Interaction
const val INTERACTION = "interaction" const val INTERACTION = "interaction"
// Tips & Support
const val TIPS_AND_SUPPORT = "tips_and_support" const val TIPS_AND_SUPPORT = "tips_and_support"
} }

View File

@ -1,16 +1,27 @@
package me.ash.reader.ui.page.home package me.ash.reader.ui.page.home
import android.view.SoundEffectConstants import android.view.SoundEffectConstants
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.width
import androidx.compose.material3.* import androidx.compose.material3.*
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import com.google.accompanist.pager.ExperimentalPagerApi import com.google.accompanist.pager.ExperimentalPagerApi
import me.ash.reader.data.entity.Filter import me.ash.reader.data.entity.Filter
import me.ash.reader.data.preference.FilterBarFilledPreference
import me.ash.reader.data.preference.FilterBarFilledPreference.Companion.filterBarFilled
import me.ash.reader.data.preference.FilterBarPaddingPreference
import me.ash.reader.data.preference.FilterBarPaddingPreference.filterBarPadding
import me.ash.reader.data.preference.FilterBarStylePreference
import me.ash.reader.data.preference.FilterBarStylePreference.Companion.filterBarStyle
import me.ash.reader.data.preference.FilterBarTonalElevationPreference
import me.ash.reader.data.preference.FilterBarTonalElevationPreference.Companion.filterBarTonalElevation
import me.ash.reader.ui.ext.collectAsStateValue
import me.ash.reader.ui.ext.getName import me.ash.reader.ui.ext.getName
import me.ash.reader.ui.theme.palette.onDark
@OptIn(ExperimentalPagerApi::class) @OptIn(ExperimentalPagerApi::class)
@Composable @Composable
@ -20,51 +31,67 @@ fun FilterBar(
filterOnClick: (Filter) -> Unit = {}, filterOnClick: (Filter) -> Unit = {},
) { ) {
val view = LocalView.current val view = LocalView.current
val context = LocalContext.current
val filterBarStyle =
context.filterBarStyle.collectAsStateValue(initial = FilterBarStylePreference.default)
val filterBarFilled =
context.filterBarFilled.collectAsStateValue(initial = FilterBarFilledPreference.default)
val filterBarPadding =
context.filterBarPadding.collectAsStateValue(initial = FilterBarPaddingPreference.default)
val filterBarTonalElevation =
context.filterBarTonalElevation.collectAsStateValue(initial = FilterBarTonalElevationPreference.default)
Box( NavigationBar(
// modifier = Modifier.height(60.dp) tonalElevation = filterBarTonalElevation.value.dp,
) { ) {
Divider( Spacer(modifier = Modifier.width(filterBarPadding.dp))
modifier = Modifier listOf(
.fillMaxWidth() Filter.Starred,
.height(1.dp) Filter.Unread,
.zIndex(1f), Filter.All,
color = MaterialTheme.colorScheme.secondaryContainer.copy(alpha = 0.24f) ).forEach { item ->
) NavigationBarItem(
NavigationBar( // modifier = Modifier.height(60.dp),
// modifier = Modifier.fillMaxSize(), alwaysShowLabel = when (filterBarStyle) {
// tonalElevation = 0.dp, is FilterBarStylePreference.Icon -> false
) { is FilterBarStylePreference.IconLabel -> true
Spacer(modifier = Modifier.width(60.dp)) is FilterBarStylePreference.IconLabelOnlySelected -> false
listOf( },
Filter.Starred, icon = {
Filter.Unread, Icon(
Filter.All, imageVector = if (filter == item && filterBarFilled.value) {
).forEach { item -> item.iconFilled
NavigationBarItem( } else {
icon = { item.iconOutline
Icon( },
imageVector = item.icon, contentDescription = item.getName()
contentDescription = item.getName() )
},
label = if (filterBarStyle is FilterBarStylePreference.Icon) {
null
} else {
{
Text(
text = item.getName(),
style = MaterialTheme.typography.labelLarge
) )
}, }
label = { Text(text = item.getName(), style = MaterialTheme.typography.labelMedium) }, },
selected = filter == item, selected = filter == item,
onClick = { onClick = {
// view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP) // view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
view.playSoundEffect(SoundEffectConstants.CLICK) view.playSoundEffect(SoundEffectConstants.CLICK)
filterOnClick(item) filterOnClick(item)
}, },
colors = NavigationBarItemDefaults.colors( colors = NavigationBarItemDefaults.colors(
// selectedIconColor = MaterialTheme.colorScheme.onSecondaryContainer alwaysLight true, // selectedIconColor = MaterialTheme.colorScheme.onSecondaryContainer alwaysLight true,
// unselectedIconColor = MaterialTheme.colorScheme.outline, // unselectedIconColor = MaterialTheme.colorScheme.outline,
// selectedTextColor = MaterialTheme.colorScheme.onSurface alwaysLight true, // selectedTextColor = MaterialTheme.colorScheme.onSurface alwaysLight true,
// unselectedTextColor = MaterialTheme.colorScheme.onSurfaceVariant, // unselectedTextColor = MaterialTheme.colorScheme.onSurfaceVariant,
// indicatorColor = MaterialTheme.colorScheme.primaryContainer alwaysLight true, indicatorColor = MaterialTheme.colorScheme.primaryContainer onDark MaterialTheme.colorScheme.secondaryContainer,
)
) )
} )
Spacer(modifier = Modifier.width(60.dp))
} }
Spacer(modifier = Modifier.width(filterBarPadding.dp))
} }
} }

View File

@ -174,7 +174,7 @@ fun FeedsPage(
Banner( Banner(
title = filterState.filter.getName(), title = filterState.filter.getName(),
desc = feedsViewState.importantCount, desc = feedsViewState.importantCount,
icon = filterState.filter.icon, icon = filterState.filter.iconOutline,
action = { action = {
Icon( Icon(
imageVector = Icons.Outlined.KeyboardArrowRight, imageVector = Icons.Outlined.KeyboardArrowRight,
@ -241,9 +241,6 @@ fun FeedsPage(
}, },
bottomBar = { bottomBar = {
FilterBar( FilterBar(
modifier = Modifier
.height(60.dp)
.fillMaxWidth(),
filter = filterState.filter, filter = filterState.filter,
filterOnClick = { filterOnClick = {
filterChange( filterChange(

View File

@ -21,6 +21,13 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import me.ash.reader.R import me.ash.reader.R
import me.ash.reader.data.entity.ArticleWithFeed import me.ash.reader.data.entity.ArticleWithFeed
import me.ash.reader.data.preference.*
import me.ash.reader.data.preference.ArticleListDatePreference.Companion.articleListDate
import me.ash.reader.data.preference.ArticleListDescPreference.Companion.articleListDesc
import me.ash.reader.data.preference.ArticleListFeedIconPreference.Companion.articleListFeedIcon
import me.ash.reader.data.preference.ArticleListFeedNamePreference.Companion.articleListFeedName
import me.ash.reader.data.preference.ArticleListImagePreference.Companion.articleListImage
import me.ash.reader.ui.ext.collectAsStateValue
import me.ash.reader.ui.ext.formatAsString import me.ash.reader.ui.ext.formatAsString
@Composable @Composable
@ -30,6 +37,16 @@ fun ArticleItem(
onClick: (ArticleWithFeed) -> Unit = {}, onClick: (ArticleWithFeed) -> Unit = {},
) { ) {
val context = LocalContext.current val context = LocalContext.current
val articleListFeedIcon =
context.articleListFeedIcon.collectAsStateValue(initial = ArticleListFeedIconPreference.default)
val articleListFeedName =
context.articleListFeedName.collectAsStateValue(initial = ArticleListFeedNamePreference.default)
val articleListImage =
context.articleListImage.collectAsStateValue(initial = ArticleListImagePreference.default)
val articleListDesc =
context.articleListDesc.collectAsStateValue(initial = ArticleListDescPreference.default)
val articleListDate =
context.articleListDate.collectAsStateValue(initial = ArticleListDatePreference.default)
Column( Column(
modifier = Modifier modifier = Modifier
@ -44,67 +61,84 @@ fun ArticleItem(
horizontalArrangement = Arrangement.SpaceBetween, horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
) { ) {
Text( // Feed name
modifier = Modifier if (articleListFeedName.value) {
.weight(1f) Text(
.padding(start = 30.dp), modifier = Modifier
text = articleWithFeed.feed.name, .weight(1f)
color = MaterialTheme.colorScheme.tertiary, .padding(start = if (articleListFeedIcon.value) 30.dp else 0.dp),
style = MaterialTheme.typography.labelMedium, text = articleWithFeed.feed.name,
maxLines = 1, color = MaterialTheme.colorScheme.tertiary,
overflow = TextOverflow.Ellipsis, style = MaterialTheme.typography.labelMedium,
) maxLines = 1,
Row( overflow = TextOverflow.Ellipsis,
modifier = Modifier.padding(start = 6.dp), )
verticalAlignment = Alignment.CenterVertically, }
) {
if (articleWithFeed.article.isStarred) { if (articleListDate.value) {
Icon( Row(
modifier = Modifier modifier = Modifier.padding(start = 6.dp),
.size(14.dp) verticalAlignment = Alignment.CenterVertically,
.padding(end = 2.dp), ) {
imageVector = Icons.Rounded.Star, // Starred
contentDescription = stringResource(R.string.starred), if (articleWithFeed.article.isStarred) {
tint = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f), Icon(
modifier = Modifier
.size(14.dp)
.padding(end = 2.dp),
imageVector = Icons.Rounded.Star,
contentDescription = stringResource(R.string.starred),
tint = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f),
)
}
// Date
Text(
text = articleWithFeed.article.date.formatAsString(
context,
onlyHourMinute = true
),
color = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f),
style = MaterialTheme.typography.labelMedium,
) )
} }
Text(
text = articleWithFeed.article.date.formatAsString(
context,
onlyHourMinute = true
),
color = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f),
style = MaterialTheme.typography.labelMedium,
)
} }
} }
Row( Row(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
) { ) {
Row( // Feed icon
modifier = Modifier if (articleListFeedIcon.value) {
.size(20.dp) Row(
.clip(CircleShape) modifier = Modifier
.background(MaterialTheme.colorScheme.outline.copy(alpha = 0.2f)) .size(20.dp)
) {} .clip(CircleShape)
Spacer(modifier = Modifier.width(10.dp)) .background(MaterialTheme.colorScheme.outline.copy(alpha = 0.2f))
) {}
Spacer(modifier = Modifier.width(10.dp))
}
// Article
Column( Column(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
) { ) {
// Title
Text( Text(
text = articleWithFeed.article.title, text = articleWithFeed.article.title,
color = MaterialTheme.colorScheme.onSurface, color = MaterialTheme.colorScheme.onSurface,
style = MaterialTheme.typography.titleMedium, style = MaterialTheme.typography.titleMedium,
maxLines = 2, maxLines = if (articleListDesc.value) 2 else 4,
overflow = TextOverflow.Ellipsis,
)
Text(
text = articleWithFeed.article.shortDescription,
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.7f),
style = MaterialTheme.typography.bodySmall,
maxLines = 2,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
) )
// Description
if (articleListDesc.value) {
Text(
text = articleWithFeed.article.shortDescription,
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.7f),
style = MaterialTheme.typography.bodySmall,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
)
}
} }
} }
} }

View File

@ -13,6 +13,8 @@ import me.ash.reader.data.entity.ArticleWithFeed
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
fun LazyListScope.ArticleList( fun LazyListScope.ArticleList(
pagingItems: LazyPagingItems<FlowItemView>, pagingItems: LazyPagingItems<FlowItemView>,
articleListFeedIcon: Boolean,
articleListTonalElevation: Int,
onClick: (ArticleWithFeed) -> Unit = {}, onClick: (ArticleWithFeed) -> Unit = {},
) { ) {
for (index in 0 until pagingItems.itemCount) { for (index in 0 until pagingItems.itemCount) {
@ -30,7 +32,7 @@ fun LazyListScope.ArticleList(
val separator = pagingItems[index] as FlowItemView.Date val separator = pagingItems[index] as FlowItemView.Date
if (separator.showSpacer) item { Spacer(modifier = Modifier.height(40.dp)) } if (separator.showSpacer) item { Spacer(modifier = Modifier.height(40.dp)) }
stickyHeader { stickyHeader {
StickyHeader(separator.date) StickyHeader(separator.date, articleListFeedIcon, articleListTonalElevation)
} }
} }
else -> {} else -> {}

View File

@ -15,6 +15,7 @@ import androidx.compose.material3.*
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
@ -26,6 +27,10 @@ import androidx.paging.compose.LazyPagingItems
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import me.ash.reader.R import me.ash.reader.R
import me.ash.reader.data.preference.ArticleListFeedIconPreference
import me.ash.reader.data.preference.ArticleListFeedIconPreference.Companion.articleListFeedIcon
import me.ash.reader.data.preference.ArticleListTonalElevationPreference
import me.ash.reader.data.preference.ArticleListTonalElevationPreference.Companion.articleListTonalElevation
import me.ash.reader.data.repository.SyncWorker.Companion.getIsSyncing import me.ash.reader.data.repository.SyncWorker.Companion.getIsSyncing
import me.ash.reader.ui.component.DisplayText import me.ash.reader.ui.component.DisplayText
import me.ash.reader.ui.component.FeedbackIconButton import me.ash.reader.ui.component.FeedbackIconButton
@ -52,6 +57,7 @@ fun FlowPage(
homeViewModel: HomeViewModel, homeViewModel: HomeViewModel,
pagingItems: LazyPagingItems<FlowItemView>, pagingItems: LazyPagingItems<FlowItemView>,
) { ) {
val context = LocalContext.current
val keyboardController = LocalSoftwareKeyboardController.current val keyboardController = LocalSoftwareKeyboardController.current
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
@ -64,6 +70,11 @@ fun FlowPage(
val homeViewState = homeViewModel.viewState.collectAsStateValue() val homeViewState = homeViewModel.viewState.collectAsStateValue()
val listState = if (pagingItems.itemCount > 0) viewState.listState else rememberLazyListState() val listState = if (pagingItems.itemCount > 0) viewState.listState else rememberLazyListState()
val articleListTonalElevation =
context.articleListTonalElevation.collectAsStateValue(initial = ArticleListTonalElevationPreference.default)
val articleListFeedIcon =
context.articleListFeedIcon.collectAsStateValue(initial = ArticleListFeedIconPreference.default)
val owner = LocalLifecycleOwner.current val owner = LocalLifecycleOwner.current
var isSyncing by remember { mutableStateOf(false) } var isSyncing by remember { mutableStateOf(false) }
homeViewModel.syncWorkLiveData.observe(owner) { homeViewModel.syncWorkLiveData.observe(owner) {
@ -98,15 +109,17 @@ fun FlowPage(
Scaffold( Scaffold(
modifier = Modifier modifier = Modifier
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp)) .background(MaterialTheme.colorScheme.surfaceColorAtElevation(articleListTonalElevation.value.dp))
.statusBarsPadding() .statusBarsPadding()
.navigationBarsPadding(), .navigationBarsPadding(),
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp), containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(articleListTonalElevation.value.dp),
topBar = { topBar = {
SmallTopAppBar( SmallTopAppBar(
title = {}, title = {},
colors = TopAppBarDefaults.smallTopAppBarColors( colors = TopAppBarDefaults.smallTopAppBarColors(
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp), containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(
articleListTonalElevation.value.dp
),
), ),
navigationIcon = { navigationIcon = {
FeedbackIconButton( FeedbackIconButton(
@ -115,7 +128,7 @@ fun FlowPage(
tint = MaterialTheme.colorScheme.onSurface tint = MaterialTheme.colorScheme.onSurface
) { ) {
onSearch = false onSearch = false
if(navController.previousBackStackEntry == null) { if (navController.previousBackStackEntry == null) {
navController.navigate(RouteName.FEEDS) { navController.navigate(RouteName.FEEDS) {
launchSingleTop = true launchSingleTop = true
} }
@ -184,7 +197,7 @@ fun FlowPage(
state = listState, state = listState,
) { ) {
item { item {
DisplayTextHeader(filterState, isSyncing) DisplayTextHeader(filterState, isSyncing, articleListFeedIcon.value)
AnimatedVisibility( AnimatedVisibility(
visible = markAsRead, visible = markAsRead,
enter = fadeIn() + expandVertically(), enter = fadeIn() + expandVertically(),
@ -246,6 +259,8 @@ fun FlowPage(
} }
ArticleList( ArticleList(
pagingItems = pagingItems, pagingItems = pagingItems,
articleListFeedIcon = articleListFeedIcon.value,
articleListTonalElevation = articleListTonalElevation.value,
) { ) {
onSearch = false onSearch = false
navController.navigate("${RouteName.READING}/${it.article.id}") { navController.navigate("${RouteName.READING}/${it.article.id}") {
@ -263,9 +278,6 @@ fun FlowPage(
}, },
bottomBar = { bottomBar = {
FilterBar( FilterBar(
modifier = Modifier
.height(60.dp)
.fillMaxWidth(),
filter = filterState.filter, filter = filterState.filter,
filterOnClick = { filterOnClick = {
flowViewModel.dispatch(FlowViewAction.ScrollToItem(0)) flowViewModel.dispatch(FlowViewAction.ScrollToItem(0))
@ -280,10 +292,11 @@ fun FlowPage(
@Composable @Composable
private fun DisplayTextHeader( private fun DisplayTextHeader(
filterState: FilterState, filterState: FilterState,
isSyncing: Boolean isSyncing: Boolean,
articleListFeedIcon: Boolean,
) { ) {
DisplayText( DisplayText(
modifier = Modifier.padding(start = 30.dp), modifier = Modifier.padding(start = if (articleListFeedIcon) 30.dp else 0.dp),
text = when { text = when {
filterState.group != null -> filterState.group.name filterState.group != null -> filterState.group.name
filterState.feed != null -> filterState.feed.name filterState.feed != null -> filterState.feed.name

View File

@ -13,16 +13,20 @@ import androidx.compose.ui.unit.dp
import me.ash.reader.ui.ext.surfaceColorAtElevation import me.ash.reader.ui.ext.surfaceColorAtElevation
@Composable @Composable
fun StickyHeader(currentItemDay: String) { fun StickyHeader(
currentItemDay: String,
articleListFeedIcon: Boolean,
articleListTonalElevation: Int,
) {
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp)), .background(MaterialTheme.colorScheme.surfaceColorAtElevation(articleListTonalElevation.dp)),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Text( Text(
modifier = Modifier modifier = Modifier
.padding(start = if (true) 54.dp else 24.dp, bottom = 4.dp), .padding(start = if (articleListFeedIcon) 54.dp else 24.dp, bottom = 4.dp),
text = currentItemDay, text = currentItemDay,
color = MaterialTheme.colorScheme.primary, color = MaterialTheme.colorScheme.primary,
style = MaterialTheme.typography.labelLarge, style = MaterialTheme.typography.labelLarge,

View File

@ -30,6 +30,7 @@ import kotlinx.coroutines.launch
import me.ash.reader.R import me.ash.reader.R
import me.ash.reader.ui.component.* import me.ash.reader.ui.component.*
import me.ash.reader.ui.ext.* import me.ash.reader.ui.ext.*
import me.ash.reader.ui.page.common.RouteName
import me.ash.reader.ui.page.settings.SettingItem import me.ash.reader.ui.page.settings.SettingItem
import me.ash.reader.ui.svg.PALETTE import me.ash.reader.ui.svg.PALETTE
import me.ash.reader.ui.svg.SVGString import me.ash.reader.ui.svg.SVGString
@ -167,8 +168,11 @@ fun ColorAndStyle(
) {} ) {}
SettingItem( SettingItem(
title = stringResource(R.string.flow_page), title = stringResource(R.string.flow_page),
enable = false, onClick = {
onClick = {}, navController.navigate(RouteName.FLOW_PAGE_STYLE) {
launchSingleTop = true
}
},
) {} ) {}
SettingItem( SettingItem(
title = stringResource(R.string.reading_page), title = stringResource(R.string.reading_page),

View File

@ -0,0 +1,346 @@
package me.ash.reader.ui.page.settings.color.flow
import android.annotation.SuppressLint
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.ArrowBack
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
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.entity.Filter
import me.ash.reader.data.preference.*
import me.ash.reader.data.preference.ArticleListDatePreference.Companion.articleListDate
import me.ash.reader.data.preference.ArticleListDescPreference.Companion.articleListDesc
import me.ash.reader.data.preference.ArticleListFeedIconPreference.Companion.articleListFeedIcon
import me.ash.reader.data.preference.ArticleListFeedNamePreference.Companion.articleListFeedName
import me.ash.reader.data.preference.ArticleListImagePreference.Companion.articleListImage
import me.ash.reader.data.preference.ArticleListTonalElevationPreference.Companion.articleListTonalElevation
import me.ash.reader.data.preference.FilterBarFilledPreference.Companion.filterBarFilled
import me.ash.reader.data.preference.FilterBarPaddingPreference.filterBarPadding
import me.ash.reader.data.preference.FilterBarStylePreference.Companion.filterBarStyle
import me.ash.reader.data.preference.FilterBarTonalElevationPreference.Companion.filterBarTonalElevation
import me.ash.reader.ui.component.*
import me.ash.reader.ui.ext.collectAsStateValue
import me.ash.reader.ui.ext.surfaceColorAtElevation
import me.ash.reader.ui.page.home.FilterBar
import me.ash.reader.ui.page.home.flow.ArticleItem
import me.ash.reader.ui.page.settings.SettingItem
import me.ash.reader.ui.theme.palette.onLight
import java.util.*
@SuppressLint("FlowOperatorInvokedInComposition")
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun FlowPageStyle(
navController: NavHostController,
) {
val context = LocalContext.current
val scope = rememberCoroutineScope()
val filterBarStyle =
context.filterBarStyle.collectAsStateValue(initial = FilterBarStylePreference.default)
val filterBarFilled =
context.filterBarFilled.collectAsStateValue(initial = FilterBarFilledPreference.default)
val filterBarPadding =
context.filterBarPadding.collectAsStateValue(initial = FilterBarPaddingPreference.default)
val filterBarTonalElevation =
context.filterBarTonalElevation.collectAsStateValue(initial = FilterBarTonalElevationPreference.default)
val articleListFeedIcon =
context.articleListFeedIcon.collectAsStateValue(initial = ArticleListFeedIconPreference.default)
val articleListFeedName =
context.articleListFeedName.collectAsStateValue(initial = ArticleListFeedNamePreference.default)
val articleListImage =
context.articleListImage.collectAsStateValue(initial = ArticleListImagePreference.default)
val articleListDesc =
context.articleListDesc.collectAsStateValue(initial = ArticleListDescPreference.default)
val articleListDate =
context.articleListDate.collectAsStateValue(initial = ArticleListDatePreference.default)
val articleListTonalElevation =
context.articleListTonalElevation.collectAsStateValue(initial = ArticleListTonalElevationPreference.default)
var filterBarStyleDialogVisible by remember { mutableStateOf(false) }
var filterBarPaddingDialogVisible by remember { mutableStateOf(false) }
var filterBarTonalElevationDialogVisible by remember { mutableStateOf(false) }
var articleListTonalElevationDialogVisible by remember { mutableStateOf(false) }
var filterBarPaddingValue: Int? by remember { mutableStateOf(filterBarPadding) }
Scaffold(
modifier = Modifier
.background(MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface)
.statusBarsPadding()
.navigationBarsPadding(),
containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface,
topBar = {
SmallTopAppBar(
colors = TopAppBarDefaults.smallTopAppBarColors(
containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface
),
title = {},
navigationIcon = {
FeedbackIconButton(
imageVector = Icons.Rounded.ArrowBack,
contentDescription = stringResource(R.string.back),
tint = MaterialTheme.colorScheme.onSurface
) {
navController.popBackStack()
}
},
actions = {}
)
},
content = {
LazyColumn {
item {
DisplayText(text = stringResource(R.string.flow_page), desc = "")
}
item {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 24.dp)
.clip(RoundedCornerShape(24.dp))
.background(
MaterialTheme.colorScheme.inverseOnSurface
onLight MaterialTheme.colorScheme.surface.copy(0.7f)
)
.clickable { },
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
FeedsPagePreview(articleListTonalElevation)
}
Spacer(modifier = Modifier.height(24.dp))
}
item {
Subtitle(
modifier = Modifier.padding(horizontal = 24.dp),
text = "过滤栏",
)
SettingItem(
title = "样式",
desc = filterBarStyle.getDesc(context),
onClick = {
filterBarStyleDialogVisible = true
},
) {}
SettingItem(
title = "填充已选中的图标",
onClick = {
(!filterBarFilled).put(context, scope)
},
) {
Switch(activated = filterBarFilled.value) {
(!filterBarFilled).put(context, scope)
}
}
SettingItem(
title = "两端边距",
desc = "${filterBarPadding}dp",
onClick = {
filterBarPaddingDialogVisible = true
},
) {}
SettingItem(
title = "色调海拔",
desc = "${filterBarTonalElevation.value}dp",
onClick = {
filterBarTonalElevationDialogVisible = true
},
) {}
Spacer(modifier = Modifier.height(24.dp))
}
item {
Subtitle(
modifier = Modifier.padding(horizontal = 24.dp),
text = "标题栏"
)
SettingItem(
title = "“标记为已读”按钮的位置",
desc = "顶部",
onClick = {},
) {}
Spacer(modifier = Modifier.height(24.dp))
}
item {
Subtitle(
modifier = Modifier.padding(horizontal = 24.dp),
text = "文章列表"
)
SettingItem(
title = "显示订阅源图标",
onClick = {
(!articleListFeedIcon).put(context, scope)
},
) {
Switch(activated = articleListFeedIcon.value) {
(!articleListFeedIcon).put(context, scope)
}
}
SettingItem(
title = "显示订阅源名称",
onClick = {
(!articleListFeedName).put(context, scope)
},
) {
Switch(activated = articleListFeedName.value) {
(!articleListFeedName).put(context, scope)
}
}
SettingItem(
title = "显示文章插图",
onClick = {},
) {
Switch(activated = false, enable = false)
}
SettingItem(
title = "显示文章描述",
onClick = {
(!articleListDesc).put(context, scope)
},
) {
Switch(activated = articleListDesc.value) {
(!articleListDesc).put(context, scope)
}
}
SettingItem(
title = "显示文章发布时间",
onClick = {
(!articleListDate).put(context, scope)
},
) {
Switch(activated = articleListDate.value) {
(!articleListDate).put(context, scope)
}
}
SettingItem(
title = "色调海拔",
desc = "${articleListTonalElevation.value}dp",
onClick = {
articleListTonalElevationDialogVisible = true
},
) {}
Spacer(modifier = Modifier.height(24.dp))
}
}
}
)
RadioDialog(
visible = filterBarStyleDialogVisible,
title = stringResource(R.string.initial_filter),
options = FilterBarStylePreference.values.map {
RadioDialogOption(
text = it.getDesc(context),
selected = filterBarStyle == it,
) {
it.put(context, scope)
}
}
) {
filterBarStyleDialogVisible = false
}
TextFieldDialog(
visible = filterBarPaddingDialogVisible,
title = "两端边距",
value = (filterBarPaddingValue ?: "").toString(),
placeholder = stringResource(R.string.name),
onValueChange = {
filterBarPaddingValue = it.toIntOrNull()
},
onDismissRequest = {
filterBarPaddingDialogVisible = false
},
onConfirm = {
FilterBarPaddingPreference.put(context, scope, filterBarPaddingValue ?: 0)
filterBarPaddingDialogVisible = false
}
)
RadioDialog(
visible = filterBarTonalElevationDialogVisible,
title = stringResource(R.string.tonal_elevation),
options = FilterBarTonalElevationPreference.values.map {
RadioDialogOption(
text = it.getDesc(context),
selected = filterBarTonalElevation == it,
) {
it.put(context, scope)
}
}
) {
filterBarTonalElevationDialogVisible = false
}
RadioDialog(
visible = articleListTonalElevationDialogVisible,
title = stringResource(R.string.tonal_elevation),
options = ArticleListTonalElevationPreference.values.map {
RadioDialogOption(
text = it.getDesc(context),
selected = articleListTonalElevation == it,
) {
it.put(context, scope)
}
}
) {
articleListTonalElevationDialogVisible = false
}
}
@Composable
fun FeedsPagePreview(
articleListTonalElevation: ArticleListTonalElevationPreference,
) {
var filter by remember { mutableStateOf(Filter.Unread) }
Column(
modifier = Modifier
.background(
color = MaterialTheme.colorScheme.surfaceColorAtElevation(articleListTonalElevation.value.dp),
shape = RoundedCornerShape(24.dp)
)
) {
Spacer(modifier = Modifier.height(12.dp))
ArticleItem(
articleWithFeed = ArticleWithFeed(
Article(
id = "",
title = "《黎明之剑》撒花完结,白金远瞳的“希灵三部曲”值得你通宵阅读",
shortDescription = "昨日在找书的时候无意间发现,“远瞳”的《黎明之剑》突然冲上了起点热搜榜首位,去小说中查找原因,原来是这部六百多万字的作品已经完结了。四年的时间,这部小说始终占据科幻分类前三甲的位置,不得不说“远瞳”的实力的确不容小觑。",
rawDescription = "昨日在找书的时候无意间发现,“远瞳”的《黎明之剑》突然冲上了起点热搜榜首位,去小说中查找原因,原来是这部六百多万字的作品已经完结了。四年的时间,这部小说始终占据科幻分类前三甲的位置,不得不说“远瞳”的实力的确不容小觑。",
link = "",
feedId = "",
accountId = 0,
date = Date(),
),
feed = Feed(
id = "",
name = "佛门射手",
icon = "",
accountId = 0,
groupId = "",
url = "",
)
)
)
Spacer(modifier = Modifier.height(12.dp))
FilterBar(modifier = Modifier.padding(horizontal = 12.dp), filter = filter) {
filter = it
}
}
}

View File

@ -79,7 +79,7 @@
<string name="accounts">账户</string> <string name="accounts">账户</string>
<string name="accounts_desc">本地、FreshRSS</string> <string name="accounts_desc">本地、FreshRSS</string>
<string name="color_and_style">颜色和样式</string> <string name="color_and_style">颜色和样式</string>
<string name="color_and_style_desc">主题、色彩系统、字体大小</string> <string name="color_and_style_desc">主题、色调样式、字体大小</string>
<string name="interaction">交互</string> <string name="interaction">交互</string>
<string name="interaction_desc">启动时、触感反馈</string> <string name="interaction_desc">启动时、触感反馈</string>
<string name="languages">语言</string> <string name="languages">语言</string>

View File

@ -79,7 +79,7 @@
<string name="accounts">Accounts</string> <string name="accounts">Accounts</string>
<string name="accounts_desc">Local, FreshRSS</string> <string name="accounts_desc">Local, FreshRSS</string>
<string name="color_and_style">Color &amp; style</string> <string name="color_and_style">Color &amp; style</string>
<string name="color_and_style_desc">Theme, color system, font size</string> <string name="color_and_style_desc">Theme, color style, font size</string>
<string name="interaction">Interaction</string> <string name="interaction">Interaction</string>
<string name="interaction_desc">On start, haptic feedback</string> <string name="interaction_desc">On start, haptic feedback</string>
<string name="languages">Languages</string> <string name="languages">Languages</string>