Add FeedsPage style settings

This commit is contained in:
Ash 2022-05-02 13:52:08 +08:00
parent 9bea2e8e8b
commit fa3c5e3601
42 changed files with 1550 additions and 406 deletions

View File

@ -1,45 +0,0 @@
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,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 FeedsFilterBarFilledPreference(val value: Boolean) : Preference() {
object ON : FeedsFilterBarFilledPreference(true)
object OFF : FeedsFilterBarFilledPreference(false)
override fun put(context: Context, scope: CoroutineScope) {
scope.launch(Dispatchers.IO) {
context.dataStore.put(
DataStoreKeys.FeedsFilterBarFilled,
value
)
}
}
companion object {
val default = OFF
val values = listOf(ON, OFF)
val Context.feedsFilterBarFilled: Flow<FeedsFilterBarFilledPreference>
get() = this.dataStore.data.map {
when (it[DataStoreKeys.FeedsFilterBarFilled.key]) {
true -> ON
false -> OFF
else -> default
}
}
}
}
operator fun FeedsFilterBarFilledPreference.not(): FeedsFilterBarFilledPreference =
when (value) {
true -> FeedsFilterBarFilledPreference.OFF
false -> FeedsFilterBarFilledPreference.ON
}

View File

@ -0,0 +1,28 @@
package me.ash.reader.data.preference
import android.content.Context
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 FeedsFilterBarPaddingPreference {
const val default = 0
val Context.feedsFilterBarPadding: Flow<Int>
get() = this.dataStore.data.map {
it[DataStoreKeys.FeedsFilterBarPadding.key] ?: default
}
fun put(context: Context, scope: CoroutineScope, value: Int) {
scope.launch(Dispatchers.IO) {
context.dataStore.put(DataStoreKeys.FeedsFilterBarPadding, value)
}
}
}

View File

@ -0,0 +1,49 @@
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.R
import me.ash.reader.ui.ext.DataStoreKeys
import me.ash.reader.ui.ext.dataStore
import me.ash.reader.ui.ext.put
sealed class FeedsFilterBarStylePreference(val value: Int) : Preference() {
object Icon : FeedsFilterBarStylePreference(0)
object IconLabel : FeedsFilterBarStylePreference(1)
object IconLabelOnlySelected : FeedsFilterBarStylePreference(2)
override fun put(context: Context, scope: CoroutineScope) {
scope.launch(Dispatchers.IO) {
context.dataStore.put(
DataStoreKeys.FeedsFilterBarStyle,
value
)
}
}
fun getDesc(context: Context): String =
when (this) {
Icon -> context.getString(R.string.icons)
IconLabel -> context.getString(R.string.icons_and_labels)
IconLabelOnlySelected -> context.getString(R.string.icons_and_label_only_selected)
}
companion object {
val default = Icon
val values = listOf(Icon, IconLabel, IconLabelOnlySelected)
val Context.feedsFilterBarStyle: Flow<FeedsFilterBarStylePreference>
get() = this.dataStore.data.map {
when (it[DataStoreKeys.FeedsFilterBarStyle.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 FeedsFilterBarTonalElevationPreference(val value: Int) : Preference() {
object Level0 : FeedsFilterBarTonalElevationPreference(0)
object Level1 : FeedsFilterBarTonalElevationPreference(1)
object Level2 : FeedsFilterBarTonalElevationPreference(3)
object Level3 : FeedsFilterBarTonalElevationPreference(6)
object Level4 : FeedsFilterBarTonalElevationPreference(8)
object Level5 : FeedsFilterBarTonalElevationPreference(12)
override fun put(context: Context, scope: CoroutineScope) {
scope.launch(Dispatchers.IO) {
context.dataStore.put(
DataStoreKeys.FeedsFilterBarTonalElevation,
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.feedsFilterBarTonalElevation: Flow<FeedsFilterBarTonalElevationPreference>
get() = this.dataStore.data.map {
when (it[DataStoreKeys.FeedsFilterBarTonalElevation.key]) {
0 -> Level0
1 -> Level1
3 -> Level2
6 -> Level3
8 -> Level4
12 -> Level5
else -> default
}
}
}
}

View File

@ -10,14 +10,14 @@ 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)
sealed class FeedsGroupListExpandPreference(val value: Boolean) : Preference() {
object ON : FeedsGroupListExpandPreference(true)
object OFF : FeedsGroupListExpandPreference(false)
override fun put(context: Context, scope: CoroutineScope) {
scope.launch(Dispatchers.IO) {
context.dataStore.put(
DataStoreKeys.ArticleListDate,
DataStoreKeys.FeedsGroupListExpand,
value
)
}
@ -27,9 +27,9 @@ sealed class ArticleListDatePreference(val value: Boolean) : Preference() {
val default = ON
val values = listOf(ON, OFF)
val Context.articleListDate: Flow<ArticleListDatePreference>
val Context.feedsGroupListExpand: Flow<FeedsGroupListExpandPreference>
get() = this.dataStore.data.map {
when (it[DataStoreKeys.ArticleListDate.key]) {
when (it[DataStoreKeys.FeedsGroupListExpand.key]) {
true -> ON
false -> OFF
else -> default
@ -38,8 +38,8 @@ sealed class ArticleListDatePreference(val value: Boolean) : Preference() {
}
}
operator fun ArticleListDatePreference.not(): ArticleListDatePreference =
operator fun FeedsGroupListExpandPreference.not(): FeedsGroupListExpandPreference =
when (value) {
true -> ArticleListDatePreference.OFF
false -> ArticleListDatePreference.ON
true -> FeedsGroupListExpandPreference.OFF
false -> FeedsGroupListExpandPreference.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 FeedsGroupListTonalElevationPreference(val value: Int) : Preference() {
object Level0 : FeedsGroupListTonalElevationPreference(0)
object Level1 : FeedsGroupListTonalElevationPreference(1)
object Level2 : FeedsGroupListTonalElevationPreference(3)
object Level3 : FeedsGroupListTonalElevationPreference(6)
object Level4 : FeedsGroupListTonalElevationPreference(8)
object Level5 : FeedsGroupListTonalElevationPreference(12)
override fun put(context: Context, scope: CoroutineScope) {
scope.launch(Dispatchers.IO) {
context.dataStore.put(
DataStoreKeys.FeedsGroupListTonalElevation,
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.feedsGroupListTonalElevation: Flow<FeedsGroupListTonalElevationPreference>
get() = this.dataStore.data.map {
when (it[DataStoreKeys.FeedsGroupListTonalElevation.key]) {
0 -> Level0
1 -> Level1
3 -> Level2
6 -> Level3
8 -> Level4
12 -> Level5
else -> default
}
}
}
}

View File

@ -10,18 +10,18 @@ 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)
sealed class FeedsTopBarTonalElevationPreference(val value: Int) : Preference() {
object Level0 : FeedsTopBarTonalElevationPreference(0)
object Level1 : FeedsTopBarTonalElevationPreference(1)
object Level2 : FeedsTopBarTonalElevationPreference(3)
object Level3 : FeedsTopBarTonalElevationPreference(6)
object Level4 : FeedsTopBarTonalElevationPreference(8)
object Level5 : FeedsTopBarTonalElevationPreference(12)
override fun put(context: Context, scope: CoroutineScope) {
scope.launch(Dispatchers.IO) {
context.dataStore.put(
DataStoreKeys.ArticleListTonalElevation,
DataStoreKeys.FeedsTopBarTonalElevation,
value
)
}
@ -41,9 +41,9 @@ sealed class ArticleListTonalElevationPreference(val value: Int) : Preference()
val default = Level0
val values = listOf(Level0, Level1, Level2, Level3, Level4, Level5)
val Context.articleListTonalElevation: Flow<ArticleListTonalElevationPreference>
val Context.feedsTopBarTonalElevation: Flow<FeedsTopBarTonalElevationPreference>
get() = this.dataStore.data.map {
when (it[DataStoreKeys.ArticleListTonalElevation.key]) {
when (it[DataStoreKeys.FeedsTopBarTonalElevation.key]) {
0 -> Level0
1 -> Level1
3 -> Level2

View File

@ -10,14 +10,14 @@ 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)
sealed class FlowArticleListDatePreference(val value: Boolean) : Preference() {
object ON : FlowArticleListDatePreference(true)
object OFF : FlowArticleListDatePreference(false)
override fun put(context: Context, scope: CoroutineScope) {
scope.launch(Dispatchers.IO) {
context.dataStore.put(
DataStoreKeys.ArticleListFeedName,
DataStoreKeys.FlowArticleListDate,
value
)
}
@ -27,9 +27,9 @@ sealed class ArticleListFeedNamePreference(val value: Boolean) : Preference() {
val default = ON
val values = listOf(ON, OFF)
val Context.articleListFeedName: Flow<ArticleListFeedNamePreference>
val Context.flowArticleListDate: Flow<FlowArticleListDatePreference>
get() = this.dataStore.data.map {
when (it[DataStoreKeys.ArticleListFeedName.key]) {
when (it[DataStoreKeys.FlowArticleListDate.key]) {
true -> ON
false -> OFF
else -> default
@ -38,8 +38,8 @@ sealed class ArticleListFeedNamePreference(val value: Boolean) : Preference() {
}
}
operator fun ArticleListFeedNamePreference.not(): ArticleListFeedNamePreference =
operator fun FlowArticleListDatePreference.not(): FlowArticleListDatePreference =
when (value) {
true -> ArticleListFeedNamePreference.OFF
false -> ArticleListFeedNamePreference.ON
true -> FlowArticleListDatePreference.OFF
false -> FlowArticleListDatePreference.ON
}

View File

@ -10,14 +10,14 @@ 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)
sealed class FlowArticleListDescPreference(val value: Boolean) : Preference() {
object ON : FlowArticleListDescPreference(true)
object OFF : FlowArticleListDescPreference(false)
override fun put(context: Context, scope: CoroutineScope) {
scope.launch(Dispatchers.IO) {
context.dataStore.put(
DataStoreKeys.ArticleListFeedIcon,
DataStoreKeys.FlowArticleListDesc,
value
)
}
@ -27,9 +27,9 @@ sealed class ArticleListFeedIconPreference(val value: Boolean) : Preference() {
val default = ON
val values = listOf(ON, OFF)
val Context.articleListFeedIcon: Flow<ArticleListFeedIconPreference>
val Context.flowArticleListDesc: Flow<FlowArticleListDescPreference>
get() = this.dataStore.data.map {
when (it[DataStoreKeys.ArticleListFeedIcon.key]) {
when (it[DataStoreKeys.FlowArticleListDesc.key]) {
true -> ON
false -> OFF
else -> default
@ -38,8 +38,8 @@ sealed class ArticleListFeedIconPreference(val value: Boolean) : Preference() {
}
}
operator fun ArticleListFeedIconPreference.not(): ArticleListFeedIconPreference =
operator fun FlowArticleListDescPreference.not(): FlowArticleListDescPreference =
when (value) {
true -> ArticleListFeedIconPreference.OFF
false -> ArticleListFeedIconPreference.ON
true -> FlowArticleListDescPreference.OFF
false -> FlowArticleListDescPreference.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 FlowArticleListFeedIconPreference(val value: Boolean) : Preference() {
object ON : FlowArticleListFeedIconPreference(true)
object OFF : FlowArticleListFeedIconPreference(false)
override fun put(context: Context, scope: CoroutineScope) {
scope.launch(Dispatchers.IO) {
context.dataStore.put(
DataStoreKeys.FlowArticleListFeedIcon,
value
)
}
}
companion object {
val default = ON
val values = listOf(ON, OFF)
val Context.flowArticleListFeedIcon: Flow<FlowArticleListFeedIconPreference>
get() = this.dataStore.data.map {
when (it[DataStoreKeys.FlowArticleListFeedIcon.key]) {
true -> ON
false -> OFF
else -> default
}
}
}
}
operator fun FlowArticleListFeedIconPreference.not(): FlowArticleListFeedIconPreference =
when (value) {
true -> FlowArticleListFeedIconPreference.OFF
false -> FlowArticleListFeedIconPreference.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 FlowArticleListFeedNamePreference(val value: Boolean) : Preference() {
object ON : FlowArticleListFeedNamePreference(true)
object OFF : FlowArticleListFeedNamePreference(false)
override fun put(context: Context, scope: CoroutineScope) {
scope.launch(Dispatchers.IO) {
context.dataStore.put(
DataStoreKeys.FlowArticleListFeedName,
value
)
}
}
companion object {
val default = ON
val values = listOf(ON, OFF)
val Context.flowArticleListFeedName: Flow<FlowArticleListFeedNamePreference>
get() = this.dataStore.data.map {
when (it[DataStoreKeys.FlowArticleListFeedName.key]) {
true -> ON
false -> OFF
else -> default
}
}
}
}
operator fun FlowArticleListFeedNamePreference.not(): FlowArticleListFeedNamePreference =
when (value) {
true -> FlowArticleListFeedNamePreference.OFF
false -> FlowArticleListFeedNamePreference.ON
}

View File

@ -10,14 +10,14 @@ 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)
sealed class FlowArticleListImagePreference(val value: Boolean) : Preference() {
object ON : FlowArticleListImagePreference(true)
object OFF : FlowArticleListImagePreference(false)
override fun put(context: Context, scope: CoroutineScope) {
scope.launch(Dispatchers.IO) {
context.dataStore.put(
DataStoreKeys.ArticleListDesc,
DataStoreKeys.FlowArticleListImage,
value
)
}
@ -27,9 +27,9 @@ sealed class ArticleListDescPreference(val value: Boolean) : Preference() {
val default = ON
val values = listOf(ON, OFF)
val Context.articleListDesc: Flow<ArticleListDescPreference>
val Context.flowArticleListImage: Flow<FlowArticleListImagePreference>
get() = this.dataStore.data.map {
when (it[DataStoreKeys.ArticleListDesc.key]) {
when (it[DataStoreKeys.FlowArticleListImage.key]) {
true -> ON
false -> OFF
else -> default
@ -38,8 +38,8 @@ sealed class ArticleListDescPreference(val value: Boolean) : Preference() {
}
}
operator fun ArticleListDescPreference.not(): ArticleListDescPreference =
operator fun FlowArticleListImagePreference.not(): FlowArticleListImagePreference =
when (value) {
true -> ArticleListDescPreference.OFF
false -> ArticleListDescPreference.ON
true -> FlowArticleListImagePreference.OFF
false -> FlowArticleListImagePreference.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 FlowArticleListTonalElevationPreference(val value: Int) : Preference() {
object Level0 : FlowArticleListTonalElevationPreference(0)
object Level1 : FlowArticleListTonalElevationPreference(1)
object Level2 : FlowArticleListTonalElevationPreference(3)
object Level3 : FlowArticleListTonalElevationPreference(6)
object Level4 : FlowArticleListTonalElevationPreference(8)
object Level5 : FlowArticleListTonalElevationPreference(12)
override fun put(context: Context, scope: CoroutineScope) {
scope.launch(Dispatchers.IO) {
context.dataStore.put(
DataStoreKeys.FlowArticleListTonalElevation,
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.flowArticleListTonalElevation: Flow<FlowArticleListTonalElevationPreference>
get() = this.dataStore.data.map {
when (it[DataStoreKeys.FlowArticleListTonalElevation.key]) {
0 -> Level0
1 -> Level1
3 -> Level2
6 -> Level3
8 -> Level4
12 -> Level5
else -> default
}
}
}
}

View File

@ -10,14 +10,14 @@ 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)
sealed class FlowFilterBarFilledPreference(val value: Boolean) : Preference() {
object ON : FlowFilterBarFilledPreference(true)
object OFF : FlowFilterBarFilledPreference(false)
override fun put(context: Context, scope: CoroutineScope) {
scope.launch(Dispatchers.IO) {
context.dataStore.put(
DataStoreKeys.FilterBarFilled,
DataStoreKeys.FlowFilterBarFilled,
value
)
}
@ -27,9 +27,9 @@ sealed class FilterBarFilledPreference(val value: Boolean) : Preference() {
val default = OFF
val values = listOf(ON, OFF)
val Context.filterBarFilled: Flow<FilterBarFilledPreference>
val Context.flowFilterBarFilled: Flow<FlowFilterBarFilledPreference>
get() = this.dataStore.data.map {
when (it[DataStoreKeys.FilterBarFilled.key]) {
when (it[DataStoreKeys.FlowFilterBarFilled.key]) {
true -> ON
false -> OFF
else -> default
@ -38,8 +38,8 @@ sealed class FilterBarFilledPreference(val value: Boolean) : Preference() {
}
}
operator fun FilterBarFilledPreference.not(): FilterBarFilledPreference =
operator fun FlowFilterBarFilledPreference.not(): FlowFilterBarFilledPreference =
when (value) {
true -> FilterBarFilledPreference.OFF
false -> FilterBarFilledPreference.ON
true -> FlowFilterBarFilledPreference.OFF
false -> FlowFilterBarFilledPreference.ON
}

View File

@ -1,7 +1,6 @@
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
@ -13,17 +12,17 @@ import me.ash.reader.ui.ext.dataStore
import me.ash.reader.ui.ext.put
@Immutable
object FilterBarPaddingPreference {
object FlowFilterBarPaddingPreference {
const val default = 0
val Context.filterBarPadding: Flow<Int>
val Context.flowFilterBarPadding: Flow<Int>
get() = this.dataStore.data.map {
it[DataStoreKeys.FilterBarPadding.key] ?: 0
it[DataStoreKeys.FlowFilterBarPadding.key] ?: default
}
fun put(context: Context, scope: CoroutineScope, value: Int) {
scope.launch(Dispatchers.IO) {
context.dataStore.put(DataStoreKeys.FilterBarPadding, value)
context.dataStore.put(DataStoreKeys.FlowFilterBarPadding, value)
}
}
}

View File

@ -6,19 +6,20 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import me.ash.reader.R
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)
sealed class FlowFilterBarStylePreference(val value: Int) : Preference() {
object Icon : FlowFilterBarStylePreference(0)
object IconLabel : FlowFilterBarStylePreference(1)
object IconLabelOnlySelected : FlowFilterBarStylePreference(2)
override fun put(context: Context, scope: CoroutineScope) {
scope.launch(Dispatchers.IO) {
context.dataStore.put(
DataStoreKeys.FilterBarStyle,
DataStoreKeys.FlowFilterBarStyle,
value
)
}
@ -26,18 +27,18 @@ sealed class FilterBarStylePreference(val value: Int) : Preference() {
fun getDesc(context: Context): String =
when (this) {
Icon -> "图标"
IconLabel -> "图标 + 标签"
IconLabelOnlySelected -> "图标 + 标签(仅选中时)"
Icon -> context.getString(R.string.icons)
IconLabel -> context.getString(R.string.icons_and_labels)
IconLabelOnlySelected -> context.getString(R.string.icons_and_label_only_selected)
}
companion object {
val default = Icon
val values = listOf(Icon, IconLabel, IconLabelOnlySelected)
val Context.filterBarStyle: Flow<FilterBarStylePreference>
val Context.flowFilterBarStyle: Flow<FlowFilterBarStylePreference>
get() = this.dataStore.data.map {
when (it[DataStoreKeys.FilterBarStyle.key]) {
when (it[DataStoreKeys.FlowFilterBarStyle.key]) {
0 -> Icon
1 -> IconLabel
2 -> IconLabelOnlySelected

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 FlowFilterBarTonalElevationPreference(val value: Int) : Preference() {
object Level0 : FlowFilterBarTonalElevationPreference(0)
object Level1 : FlowFilterBarTonalElevationPreference(1)
object Level2 : FlowFilterBarTonalElevationPreference(3)
object Level3 : FlowFilterBarTonalElevationPreference(6)
object Level4 : FlowFilterBarTonalElevationPreference(8)
object Level5 : FlowFilterBarTonalElevationPreference(12)
override fun put(context: Context, scope: CoroutineScope) {
scope.launch(Dispatchers.IO) {
context.dataStore.put(
DataStoreKeys.FlowFilterBarTonalElevation,
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.flowFilterBarTonalElevation: Flow<FlowFilterBarTonalElevationPreference>
get() = this.dataStore.data.map {
when (it[DataStoreKeys.FlowFilterBarTonalElevation.key]) {
0 -> Level0
1 -> Level1
3 -> Level2
6 -> Level3
8 -> Level4
12 -> Level5
else -> default
}
}
}
}

View File

@ -10,18 +10,18 @@ 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)
sealed class FlowTopBarTonalElevationPreference(val value: Int) : Preference() {
object Level0 : FlowTopBarTonalElevationPreference(0)
object Level1 : FlowTopBarTonalElevationPreference(1)
object Level2 : FlowTopBarTonalElevationPreference(3)
object Level3 : FlowTopBarTonalElevationPreference(6)
object Level4 : FlowTopBarTonalElevationPreference(8)
object Level5 : FlowTopBarTonalElevationPreference(12)
override fun put(context: Context, scope: CoroutineScope) {
scope.launch(Dispatchers.IO) {
context.dataStore.put(
DataStoreKeys.FilterBarTonalElevation,
DataStoreKeys.FlowTopBarTonalElevation,
value
)
}
@ -41,9 +41,9 @@ sealed class FilterBarTonalElevationPreference(val value: Int) : Preference() {
val default = Level0
val values = listOf(Level0, Level1, Level2, Level3, Level4, Level5)
val Context.filterBarTonalElevation: Flow<FilterBarTonalElevationPreference>
val Context.flowTopBarTonalElevation: Flow<FlowTopBarTonalElevationPreference>
get() = this.dataStore.data.map {
when (it[DataStoreKeys.FilterBarTonalElevation.key]) {
when (it[DataStoreKeys.FlowTopBarTonalElevation.key]) {
0 -> Level0
1 -> Level1
3 -> Level2

View File

@ -0,0 +1,28 @@
package me.ash.reader.data.preference
import android.content.Context
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 ThemePreference {
const val default = 5
val Context.Theme: Flow<Int>
get() = this.dataStore.data.map {
it[DataStoreKeys.ThemeIndex.key] ?: default
}
fun put(context: Context, scope: CoroutineScope, value: Int) {
scope.launch(Dispatchers.IO) {
context.dataStore.put(DataStoreKeys.ThemeIndex, value)
}
}
}

View File

@ -0,0 +1,38 @@
package me.ash.reader.ui.component
import androidx.compose.foundation.layout.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Info
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import me.ash.reader.R
@Composable
fun Tips(
modifier: Modifier = Modifier,
text: String,
) {
Column(
modifier = modifier
.fillMaxWidth()
.padding(horizontal = 24.dp, vertical = 16.dp),
) {
Icon(
imageVector = Icons.Outlined.Info,
contentDescription = stringResource(R.string.tips_and_support),
tint = MaterialTheme.colorScheme.onSurfaceVariant,
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = text,
style = MaterialTheme.typography.labelLarge.copy(fontWeight = FontWeight.Light),
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
}
}

View File

@ -9,8 +9,16 @@ import kotlin.math.ln
fun ColorScheme.surfaceColorAtElevation(
elevation: Dp,
color: Color = surface,
): Color = color.atElevation(surfaceTint, elevation)
fun Color.atElevation(
sourceColor: Color,
elevation: Dp,
): Color {
if (elevation == 0.dp) return surface
val alpha = ((4.5f * ln(elevation.value + 1)) + 2f) / 100f
return primary.copy(alpha = alpha).compositeOver(surface)
if (elevation == 0.dp) return this
return sourceColor.copy(alpha = elevation.alphaLN(constant = 4.5f)).compositeOver(this)
}
fun Dp.alphaLN(constant: Float = 1f, weight: Float = 0f): Float =
((constant * ln(value + 1) + weight) + 2f) / 100f

View File

@ -31,8 +31,6 @@ val Context.currentAccountId: Int
get() = this.dataStore.get(DataStoreKeys.CurrentAccountId)!!
val Context.currentAccountType: Int
get() = this.dataStore.get(DataStoreKeys.CurrentAccountType)!!
val Context.themeIndex: Int
get() = this.dataStore.get(DataStoreKeys.ThemeIndex) ?: 5
val Context.customPrimaryColor: String
get() = this.dataStore.get(DataStoreKeys.CustomPrimaryColor) ?: ""
@ -130,54 +128,94 @@ sealed class DataStoreKeys<T> {
get() = stringPreferencesKey("customPrimaryColor")
}
object FilterBarStyle : DataStoreKeys<Int>() {
object FeedsFilterBarStyle : DataStoreKeys<Int>() {
override val key: Preferences.Key<Int>
get() = intPreferencesKey("filterBarStyle")
get() = intPreferencesKey("feedsFilterBarStyle")
}
object FilterBarFilled : DataStoreKeys<Boolean>() {
object FeedsFilterBarFilled : DataStoreKeys<Boolean>() {
override val key: Preferences.Key<Boolean>
get() = booleanPreferencesKey("filterBarFilled")
get() = booleanPreferencesKey("feedsFilterBarFilled")
}
object FilterBarPadding : DataStoreKeys<Int>() {
object FeedsFilterBarPadding : DataStoreKeys<Int>() {
override val key: Preferences.Key<Int>
get() = intPreferencesKey("filterBarPadding")
get() = intPreferencesKey("feedsFilterBarPadding")
}
object FilterBarTonalElevation : DataStoreKeys<Int>() {
object FeedsFilterBarTonalElevation : DataStoreKeys<Int>() {
override val key: Preferences.Key<Int>
get() = intPreferencesKey("filterBarTonalElevation")
get() = intPreferencesKey("feedsFilterBarTonalElevation")
}
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>() {
object FeedsTopBarTonalElevation : DataStoreKeys<Int>() {
override val key: Preferences.Key<Int>
get() = intPreferencesKey("articleListTonalElevation")
get() = intPreferencesKey("feedsTopBarTonalElevation")
}
object FeedsGroupListExpand : DataStoreKeys<Boolean>() {
override val key: Preferences.Key<Boolean>
get() = booleanPreferencesKey("feedsGroupListExpand")
}
object FeedsGroupListTonalElevation : DataStoreKeys<Int>() {
override val key: Preferences.Key<Int>
get() = intPreferencesKey("feedsGroupListTonalElevation")
}
object FlowFilterBarStyle : DataStoreKeys<Int>() {
override val key: Preferences.Key<Int>
get() = intPreferencesKey("flowFilterBarStyle")
}
object FlowFilterBarFilled : DataStoreKeys<Boolean>() {
override val key: Preferences.Key<Boolean>
get() = booleanPreferencesKey("flowFilterBarFilled")
}
object FlowFilterBarPadding : DataStoreKeys<Int>() {
override val key: Preferences.Key<Int>
get() = intPreferencesKey("flowFilterBarPadding")
}
object FlowFilterBarTonalElevation : DataStoreKeys<Int>() {
override val key: Preferences.Key<Int>
get() = intPreferencesKey("flowFilterBarTonalElevation")
}
object FlowTopBarTonalElevation : DataStoreKeys<Int>() {
override val key: Preferences.Key<Int>
get() = intPreferencesKey("flowTopBarTonalElevation")
}
object FlowArticleListFeedIcon : DataStoreKeys<Boolean>() {
override val key: Preferences.Key<Boolean>
get() = booleanPreferencesKey("flowArticleListFeedIcon")
}
object FlowArticleListFeedName : DataStoreKeys<Boolean>() {
override val key: Preferences.Key<Boolean>
get() = booleanPreferencesKey("flowArticleListFeedName")
}
object FlowArticleListImage : DataStoreKeys<Boolean>() {
override val key: Preferences.Key<Boolean>
get() = booleanPreferencesKey("flowArticleListImage")
}
object FlowArticleListDesc : DataStoreKeys<Boolean>() {
override val key: Preferences.Key<Boolean>
get() = booleanPreferencesKey("flowArticleListDesc")
}
object FlowArticleListDate : DataStoreKeys<Boolean>() {
override val key: Preferences.Key<Boolean>
get() = booleanPreferencesKey("flowArticleListDate")
}
object FlowArticleListTonalElevation : DataStoreKeys<Int>() {
override val key: Preferences.Key<Int>
get() = intPreferencesKey("flowArticleListTonalElevation")
}
object InitialPage : DataStoreKeys<Int>() {

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.settings.SettingsPage
import me.ash.reader.ui.page.settings.color.ColorAndStyle
import me.ash.reader.ui.page.settings.color.feeds.FeedsPageStyle
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.tips.TipsAndSupport
@ -127,6 +128,9 @@ fun HomeEntry(
animatedComposable(route = RouteName.COLOR_AND_STYLE) {
ColorAndStyle(navController)
}
animatedComposable(route = RouteName.FEEDS_PAGE_STYLE) {
FeedsPageStyle(navController)
}
animatedComposable(route = RouteName.FLOW_PAGE_STYLE) {
FlowPageStyle(navController)
}

View File

@ -14,6 +14,7 @@ object RouteName {
// Color & Style
const val COLOR_AND_STYLE = "color_and_style"
const val FEEDS_PAGE_STYLE = "feeds_page_style"
const val FLOW_PAGE_STYLE = "flow_page_style"
// Interaction

View File

@ -6,20 +6,11 @@ import androidx.compose.foundation.layout.width
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.Dp
import com.google.accompanist.pager.ExperimentalPagerApi
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.data.preference.FlowFilterBarStylePreference
import me.ash.reader.ui.ext.getName
import me.ash.reader.ui.theme.palette.onDark
@ -28,23 +19,18 @@ import me.ash.reader.ui.theme.palette.onDark
fun FilterBar(
modifier: Modifier = Modifier,
filter: Filter,
filterBarStyle: Int,
filterBarFilled: Boolean,
filterBarPadding: Dp,
filterBarTonalElevation: Dp,
filterOnClick: (Filter) -> Unit = {},
) {
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)
NavigationBar(
tonalElevation = filterBarTonalElevation.value.dp,
tonalElevation = filterBarTonalElevation,
) {
Spacer(modifier = Modifier.width(filterBarPadding.dp))
Spacer(modifier = Modifier.width(filterBarPadding))
listOf(
Filter.Starred,
Filter.Unread,
@ -53,13 +39,14 @@ fun FilterBar(
NavigationBarItem(
// modifier = Modifier.height(60.dp),
alwaysShowLabel = when (filterBarStyle) {
is FilterBarStylePreference.Icon -> false
is FilterBarStylePreference.IconLabel -> true
is FilterBarStylePreference.IconLabelOnlySelected -> false
FlowFilterBarStylePreference.Icon.value -> false
FlowFilterBarStylePreference.IconLabel.value -> true
FlowFilterBarStylePreference.IconLabelOnlySelected.value -> false
else -> false
},
icon = {
Icon(
imageVector = if (filter == item && filterBarFilled.value) {
imageVector = if (filter == item && filterBarFilled) {
item.iconFilled
} else {
item.iconOutline
@ -67,7 +54,7 @@ fun FilterBar(
contentDescription = item.getName()
)
},
label = if (filterBarStyle is FilterBarStylePreference.Icon) {
label = if (filterBarStyle == FlowFilterBarStylePreference.Icon.value) {
null
} else {
{
@ -92,6 +79,6 @@ fun FilterBar(
)
)
}
Spacer(modifier = Modifier.width(filterBarPadding.dp))
Spacer(modifier = Modifier.width(filterBarPadding))
}
}

View File

@ -62,7 +62,7 @@ class HomeViewModel @Inject constructor(
private fun fetchArticles() {
_viewState.update {
it.copy(
pagingData = Pager(PagingConfig(pageSize = 10)) {
pagingData = Pager(PagingConfig(pageSize = 15)) {
if (_viewState.value.searchContent.isNotBlank()) {
rssRepository.get().searchArticles(
content = _viewState.value.searchContent.trim(),

View File

@ -16,11 +16,13 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import me.ash.reader.data.entity.Feed
import me.ash.reader.ui.page.home.feeds.option.feed.FeedOptionViewAction
import me.ash.reader.ui.page.home.feeds.option.feed.FeedOptionViewModel
import kotlin.math.ln
@OptIn(
androidx.compose.foundation.ExperimentalFoundationApi::class,
@ -31,6 +33,7 @@ fun FeedItem(
modifier: Modifier = Modifier,
feed: Feed,
feedOptionViewModel: FeedOptionViewModel = hiltViewModel(),
tonalElevation: Dp,
onClick: () -> Unit = {},
) {
val view = LocalView.current
@ -76,9 +79,10 @@ fun FeedItem(
)
}
if (feed.important ?: 0 != 0) {
Row() {
Badge(
containerColor = MaterialTheme.colorScheme.secondaryContainer.copy(alpha = 0.24f),
containerColor = MaterialTheme.colorScheme.surfaceTint.copy(
alpha = (ln(tonalElevation.value + 1.4f) + 2f) / 100f
),
contentColor = MaterialTheme.colorScheme.outline,
content = {
Text(
@ -91,4 +95,3 @@ fun FeedItem(
}
}
}
}

View File

@ -45,6 +45,8 @@ import me.ash.reader.ui.page.home.feeds.option.group.GroupOptionDrawer
import me.ash.reader.ui.page.home.feeds.subscribe.SubscribeDialog
import me.ash.reader.ui.page.home.feeds.subscribe.SubscribeViewAction
import me.ash.reader.ui.page.home.feeds.subscribe.SubscribeViewModel
import me.ash.reader.ui.theme.*
import me.ash.reader.ui.theme.palette.onDark
@SuppressLint("FlowOperatorInvokedInComposition")
@OptIn(
@ -59,6 +61,14 @@ fun FeedsPage(
homeViewModel: HomeViewModel,
) {
val context = LocalContext.current
val topBarTonalElevation = LocalFeedsTopBarTonalElevation.current
val groupListTonalElevation = LocalFeedsGroupListTonalElevation.current
val groupListExpand = LocalFeedsGroupListExpand.current
val filterBarStyle = LocalFeedsFilterBarStyle.current
val filterBarFilled = LocalFeedsFilterBarFilled.current
val filterBarPadding = LocalFeedsFilterBarPadding.current
val filterBarTonalElevation = LocalFeedsFilterBarTonalElevation.current
val feedsViewState = feedsViewModel.viewState.collectAsStateValue()
val filterState = homeViewModel.filterState.collectAsStateValue()
@ -117,11 +127,19 @@ fun FeedsPage(
Scaffold(
modifier = Modifier
.background(MaterialTheme.colorScheme.surface)
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(topBarTonalElevation.dp))
.statusBarsPadding()
.navigationBarsPadding(),
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(
groupListTonalElevation.dp
) onDark MaterialTheme.colorScheme.surface,
topBar = {
SmallTopAppBar(
colors = TopAppBarDefaults.smallTopAppBarColors(
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(
topBarTonalElevation.dp
),
),
title = {},
navigationIcon = {
FeedbackIconButton(
@ -204,6 +222,8 @@ fun FeedsPage(
// Crossfade(targetState = groupWithFeed) { groupWithFeed ->
Column {
GroupItem(
isExpanded = groupListExpand,
tonalElevation = groupListTonalElevation.dp,
group = groupWithFeed.group,
feeds = groupWithFeed.feeds,
groupOnClick = {
@ -242,15 +262,18 @@ fun FeedsPage(
bottomBar = {
FilterBar(
filter = filterState.filter,
filterOnClick = {
filterBarStyle = filterBarStyle,
filterBarFilled = filterBarFilled,
filterBarPadding = filterBarPadding.dp,
filterBarTonalElevation = filterBarTonalElevation.dp,
) {
filterChange(
navController = navController,
homeViewModel = homeViewModel,
filterState = filterState.copy(filter = it),
isNavigate = false,
)
},
)
}
}
)

View File

@ -22,11 +22,13 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import me.ash.reader.R
import me.ash.reader.data.entity.Feed
import me.ash.reader.data.entity.Group
import me.ash.reader.ui.ext.alphaLN
import me.ash.reader.ui.page.home.feeds.option.group.GroupOptionViewAction
import me.ash.reader.ui.page.home.feeds.option.group.GroupOptionViewModel
@ -34,6 +36,7 @@ import me.ash.reader.ui.page.home.feeds.option.group.GroupOptionViewModel
@Composable
fun GroupItem(
modifier: Modifier = Modifier,
tonalElevation: Dp,
group: Group,
feeds: List<Feed>,
isExpanded: Boolean = true,
@ -50,7 +53,9 @@ fun GroupItem(
.fillMaxWidth()
.padding(horizontal = 16.dp)
.clip(RoundedCornerShape(32.dp))
.background(MaterialTheme.colorScheme.secondaryContainer.copy(alpha = 0.14f))
.background(
MaterialTheme.colorScheme.secondary.copy(alpha = tonalElevation.alphaLN(weight = 1.2f))
)
.combinedClickable(
onClick = {
groupOnClick()
@ -82,7 +87,11 @@ fun GroupItem(
.padding(end = 20.dp)
.size(24.dp)
.clip(CircleShape)
.background(MaterialTheme.colorScheme.secondaryContainer.copy(alpha = 0.24f))
.background(
MaterialTheme.colorScheme.surfaceTint.copy(
alpha = tonalElevation.alphaLN(weight = 1.4f)
)
)
.clickable {
expanded = !expanded
},
@ -107,6 +116,7 @@ fun GroupItem(
FeedItem(
modifier = Modifier.padding(horizontal = 20.dp),
feed = feed,
tonalElevation = tonalElevation,
) {
feedOnClick(feed)
}

View File

@ -21,14 +21,8 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import me.ash.reader.R
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.theme.*
@Composable
fun ArticleItem(
@ -37,16 +31,11 @@ fun ArticleItem(
onClick: (ArticleWithFeed) -> Unit = {},
) {
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)
val articleListFeedIcon = LocalFlowArticleListFeedIcon.current
val articleListFeedName = LocalFlowArticleListFeedName.current
val articleListImage = LocalFlowArticleListImage.current
val articleListDesc = LocalFlowArticleListDesc.current
val articleListDate = LocalFlowArticleListDate.current
Column(
modifier = Modifier
@ -62,11 +51,11 @@ fun ArticleItem(
verticalAlignment = Alignment.CenterVertically,
) {
// Feed name
if (articleListFeedName.value) {
if (articleListFeedName) {
Text(
modifier = Modifier
.weight(1f)
.padding(start = if (articleListFeedIcon.value) 30.dp else 0.dp),
.padding(start = if (articleListFeedIcon) 30.dp else 0.dp),
text = articleWithFeed.feed.name,
color = MaterialTheme.colorScheme.tertiary,
style = MaterialTheme.typography.labelMedium,
@ -75,11 +64,13 @@ fun ArticleItem(
)
}
if (articleListDate.value) {
if (articleListDate) {
Row(
modifier = Modifier.padding(start = 6.dp),
verticalAlignment = Alignment.CenterVertically,
) {
if (!articleListFeedName) {
Spacer(Modifier.width(if (articleListFeedIcon) 30.dp else 0.dp))
}
// Starred
if (articleWithFeed.article.isStarred) {
Icon(
@ -108,7 +99,7 @@ fun ArticleItem(
modifier = Modifier.fillMaxWidth(),
) {
// Feed icon
if (articleListFeedIcon.value) {
if (articleListFeedIcon) {
Row(
modifier = Modifier
.size(20.dp)
@ -126,11 +117,11 @@ fun ArticleItem(
text = articleWithFeed.article.title,
color = MaterialTheme.colorScheme.onSurface,
style = MaterialTheme.typography.titleMedium,
maxLines = if (articleListDesc.value) 2 else 4,
maxLines = if (articleListDesc) 2 else 4,
overflow = TextOverflow.Ellipsis,
)
// Description
if (articleListDesc.value) {
if (articleListDesc) {
Text(
text = articleWithFeed.article.shortDescription,
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.7f),

View File

@ -27,10 +27,6 @@ import androidx.paging.compose.LazyPagingItems
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
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.ui.component.DisplayText
import me.ash.reader.ui.component.FeedbackIconButton
@ -43,6 +39,8 @@ import me.ash.reader.ui.page.home.FilterBar
import me.ash.reader.ui.page.home.FilterState
import me.ash.reader.ui.page.home.HomeViewAction
import me.ash.reader.ui.page.home.HomeViewModel
import me.ash.reader.ui.theme.*
import me.ash.reader.ui.theme.palette.onDark
@OptIn(
ExperimentalMaterial3Api::class,
@ -59,6 +57,13 @@ fun FlowPage(
) {
val context = LocalContext.current
val keyboardController = LocalSoftwareKeyboardController.current
val topBarTonalElevation = LocalFlowTopBarTonalElevation.current
val articleListTonalElevation = LocalFlowArticleListTonalElevation.current
val articleListFeedIcon = LocalFlowArticleListFeedIcon.current
val filterBarStyle = LocalFlowFilterBarStyle.current
val filterBarFilled = LocalFlowFilterBarFilled.current
val filterBarPadding = LocalFlowFilterBarPadding.current
val filterBarTonalElevation = LocalFlowFilterBarTonalElevation.current
val scope = rememberCoroutineScope()
val focusRequester = remember { FocusRequester() }
@ -70,11 +75,6 @@ fun FlowPage(
val homeViewState = homeViewModel.viewState.collectAsStateValue()
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
var isSyncing by remember { mutableStateOf(false) }
homeViewModel.syncWorkLiveData.observe(owner) {
@ -109,16 +109,18 @@ fun FlowPage(
Scaffold(
modifier = Modifier
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(articleListTonalElevation.value.dp))
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(topBarTonalElevation.dp))
.statusBarsPadding()
.navigationBarsPadding(),
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(articleListTonalElevation.value.dp),
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(
articleListTonalElevation.dp
) onDark MaterialTheme.colorScheme.surface,
topBar = {
SmallTopAppBar(
title = {},
colors = TopAppBarDefaults.smallTopAppBarColors(
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(
articleListTonalElevation.value.dp
topBarTonalElevation.dp
),
),
navigationIcon = {
@ -197,7 +199,7 @@ fun FlowPage(
state = listState,
) {
item {
DisplayTextHeader(filterState, isSyncing, articleListFeedIcon.value)
DisplayTextHeader(filterState, isSyncing, articleListFeedIcon)
AnimatedVisibility(
visible = markAsRead,
enter = fadeIn() + expandVertically(),
@ -259,8 +261,8 @@ fun FlowPage(
}
ArticleList(
pagingItems = pagingItems,
articleListFeedIcon = articleListFeedIcon.value,
articleListTonalElevation = articleListTonalElevation.value,
articleListFeedIcon = articleListFeedIcon,
articleListTonalElevation = articleListTonalElevation,
) {
onSearch = false
navController.navigate("${RouteName.READING}/${it.article.id}") {
@ -279,12 +281,15 @@ fun FlowPage(
bottomBar = {
FilterBar(
filter = filterState.filter,
filterOnClick = {
filterBarStyle = filterBarStyle,
filterBarFilled = filterBarFilled,
filterBarPadding = filterBarPadding.dp,
filterBarTonalElevation = filterBarTonalElevation.dp,
) {
flowViewModel.dispatch(FlowViewAction.ScrollToItem(0))
homeViewModel.dispatch(HomeViewAction.ChangeFilter(filterState.copy(filter = it)))
homeViewModel.dispatch(HomeViewAction.FetchArticles)
},
)
}
}
)
}

View File

@ -11,6 +11,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import me.ash.reader.ui.ext.surfaceColorAtElevation
import me.ash.reader.ui.theme.palette.onDark
@Composable
fun StickyHeader(
@ -21,12 +22,17 @@ fun StickyHeader(
Row(
modifier = Modifier
.fillMaxWidth()
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(articleListTonalElevation.dp)),
.background(
MaterialTheme.colorScheme.surfaceColorAtElevation(articleListTonalElevation.dp)
onDark MaterialTheme.colorScheme.surface
),
verticalAlignment = Alignment.CenterVertically
) {
Text(
modifier = Modifier
.padding(start = if (articleListFeedIcon) 54.dp else 24.dp, bottom = 4.dp),
modifier = Modifier.padding(
start = if (articleListFeedIcon) 54.dp else 24.dp,
bottom = 4.dp
),
text = currentItemDay,
color = MaterialTheme.colorScheme.primary,
style = MaterialTheme.typography.labelLarge,

View File

@ -28,12 +28,17 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import me.ash.reader.R
import me.ash.reader.data.preference.ThemePreference
import me.ash.reader.ui.component.*
import me.ash.reader.ui.ext.*
import me.ash.reader.ui.ext.DataStoreKeys
import me.ash.reader.ui.ext.customPrimaryColor
import me.ash.reader.ui.ext.dataStore
import me.ash.reader.ui.ext.put
import me.ash.reader.ui.page.common.RouteName
import me.ash.reader.ui.page.settings.SettingItem
import me.ash.reader.ui.svg.PALETTE
import me.ash.reader.ui.svg.SVGString
import me.ash.reader.ui.theme.LocalTheme
import me.ash.reader.ui.theme.LocalUseDarkTheme
import me.ash.reader.ui.theme.palette.*
import me.ash.reader.ui.theme.palette.TonalPalettes.Companion.toTonalPalettes
@ -47,8 +52,9 @@ fun ColorAndStyle(
) {
val context = LocalContext.current
val useDarkTheme = LocalUseDarkTheme.current
val theme = LocalTheme.current
val wallpaperTonalPalettes = extractTonalPalettesFromUserWallpaper()
var radioButtonSelected by remember { mutableStateOf(if (context.themeIndex > 4) 0 else 1) }
var radioButtonSelected by remember { mutableStateOf(if (theme > 4) 0 else 1) }
Scaffold(
modifier = Modifier
@ -163,8 +169,11 @@ fun ColorAndStyle(
)
SettingItem(
title = stringResource(R.string.feeds_page),
enable = false,
onClick = {},
onClick = {
navController.navigate(RouteName.FEEDS_PAGE_STYLE) {
launchSingleTop = true
}
},
) {}
SettingItem(
title = stringResource(R.string.flow_page),
@ -245,12 +254,7 @@ fun Palettes(
if (isCustom) {
addDialogVisible = true
} else {
scope.launch(Dispatchers.IO) {
context.dataStore.put(
DataStoreKeys.ThemeIndex,
themeIndexPrefix + index
)
}
ThemePreference.put(context, scope, themeIndexPrefix + index)
}
},
palette = if (isCustom) tonalPalettes else palette

View File

@ -0,0 +1,379 @@
package me.ash.reader.ui.page.settings.color.feeds
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.Add
import androidx.compose.material.icons.rounded.ArrowBack
import androidx.compose.material.icons.rounded.Refresh
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.compose.ui.unit.dp
import androidx.navigation.NavHostController
import me.ash.reader.R
import me.ash.reader.data.entity.Feed
import me.ash.reader.data.entity.Filter
import me.ash.reader.data.entity.Group
import me.ash.reader.data.preference.*
import me.ash.reader.data.preference.FeedsFilterBarFilledPreference.Companion.feedsFilterBarFilled
import me.ash.reader.data.preference.FeedsFilterBarPaddingPreference.feedsFilterBarPadding
import me.ash.reader.data.preference.FeedsFilterBarStylePreference.Companion.feedsFilterBarStyle
import me.ash.reader.data.preference.FeedsFilterBarTonalElevationPreference.Companion.feedsFilterBarTonalElevation
import me.ash.reader.data.preference.FeedsGroupListExpandPreference.Companion.feedsGroupListExpand
import me.ash.reader.data.preference.FeedsGroupListTonalElevationPreference.Companion.feedsGroupListTonalElevation
import me.ash.reader.data.preference.FeedsTopBarTonalElevationPreference.Companion.feedsTopBarTonalElevation
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.feeds.GroupItem
import me.ash.reader.ui.page.settings.SettingItem
import me.ash.reader.ui.theme.palette.onDark
import me.ash.reader.ui.theme.palette.onLight
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun FeedsPageStyle(
navController: NavHostController,
) {
val context = LocalContext.current
val scope = rememberCoroutineScope()
val filterBarStyle =
context.feedsFilterBarStyle.collectAsStateValue(initial = FeedsFilterBarStylePreference.default)
val filterBarFilled =
context.feedsFilterBarFilled.collectAsStateValue(initial = FeedsFilterBarFilledPreference.default)
val filterBarPadding =
context.feedsFilterBarPadding.collectAsStateValue(initial = FeedsFilterBarPaddingPreference.default)
val filterBarTonalElevation =
context.feedsFilterBarTonalElevation.collectAsStateValue(initial = FeedsFilterBarTonalElevationPreference.default)
val topBarTonalElevation =
context.feedsTopBarTonalElevation.collectAsStateValue(initial = FeedsTopBarTonalElevationPreference.default)
val groupListExpand =
context.feedsGroupListExpand.collectAsStateValue(initial = FeedsGroupListExpandPreference.default)
val groupListTonalElevation =
context.feedsGroupListTonalElevation.collectAsStateValue(initial = FeedsGroupListTonalElevationPreference.default)
var filterBarStyleDialogVisible by remember { mutableStateOf(false) }
var filterBarPaddingDialogVisible by remember { mutableStateOf(false) }
var filterBarTonalElevationDialogVisible by remember { mutableStateOf(false) }
var topBarTonalElevationDialogVisible by remember { mutableStateOf(false) }
var groupListTonalElevationDialogVisible 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.feeds_page), desc = "")
}
// Preview
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(
topBarTonalElevation = topBarTonalElevation,
groupListExpand = groupListExpand,
groupListTonalElevation = groupListTonalElevation,
filterBarStyle = filterBarStyle.value,
filterBarFilled = filterBarFilled.value,
filterBarPadding = filterBarPadding.dp,
filterBarTonalElevation = filterBarTonalElevation.value.dp,
)
}
Spacer(modifier = Modifier.height(24.dp))
}
// Top Bar
item {
Subtitle(
modifier = Modifier.padding(horizontal = 24.dp),
text = stringResource(R.string.top_bar)
)
SettingItem(
title = stringResource(R.string.tonal_elevation),
desc = "${topBarTonalElevation.value}dp",
onClick = {
topBarTonalElevationDialogVisible = true
},
) {}
Tips(text = stringResource(R.string.tips_top_bar_tonal_elevation))
Spacer(modifier = Modifier.height(24.dp))
}
// Group List
item {
Subtitle(
modifier = Modifier.padding(horizontal = 24.dp),
text = stringResource(R.string.group_list)
)
SettingItem(
title = stringResource(R.string.always_expand),
onClick = {
(!groupListExpand).put(context, scope)
},
) {
Switch(activated = groupListExpand.value) {
(!groupListExpand).put(context, scope)
}
}
SettingItem(
title = stringResource(R.string.tonal_elevation),
desc = "${groupListTonalElevation.value}dp",
onClick = {
groupListTonalElevationDialogVisible = true
},
) {}
Tips(text = stringResource(R.string.tips_group_list_tonal_elevation))
Spacer(modifier = Modifier.height(24.dp))
}
// Filter Bar
item {
Subtitle(
modifier = Modifier.padding(horizontal = 24.dp),
text = stringResource(R.string.filter_bar),
)
SettingItem(
title = stringResource(R.string.style),
desc = filterBarStyle.getDesc(context),
onClick = {
filterBarStyleDialogVisible = true
},
) {}
SettingItem(
title = stringResource(R.string.fill_selected_icon),
onClick = {
(!filterBarFilled).put(context, scope)
},
) {
Switch(activated = filterBarFilled.value) {
(!filterBarFilled).put(context, scope)
}
}
SettingItem(
title = stringResource(R.string.padding_on_both_ends),
desc = "${filterBarPadding}dp",
onClick = {
filterBarPaddingValue = filterBarPadding
filterBarPaddingDialogVisible = true
},
) {}
SettingItem(
title = stringResource(R.string.tonal_elevation),
desc = "${filterBarTonalElevation.value}dp",
onClick = {
filterBarTonalElevationDialogVisible = true
},
) {}
Spacer(modifier = Modifier.height(24.dp))
}
}
}
)
RadioDialog(
visible = filterBarStyleDialogVisible,
title = stringResource(R.string.style),
options = FeedsFilterBarStylePreference.values.map {
RadioDialogOption(
text = it.getDesc(context),
selected = filterBarStyle == it,
) {
it.put(context, scope)
}
}
) {
filterBarStyleDialogVisible = false
}
TextFieldDialog(
visible = filterBarPaddingDialogVisible,
title = stringResource(R.string.padding_on_both_ends),
value = (filterBarPaddingValue ?: "").toString(),
placeholder = stringResource(R.string.value),
onValueChange = {
filterBarPaddingValue = it.filter { it.isDigit() }.toIntOrNull()
},
onDismissRequest = {
filterBarPaddingDialogVisible = false
},
onConfirm = {
FeedsFilterBarPaddingPreference.put(context, scope, filterBarPaddingValue ?: 0)
filterBarPaddingDialogVisible = false
}
)
RadioDialog(
visible = filterBarTonalElevationDialogVisible,
title = stringResource(R.string.tonal_elevation),
options = FeedsFilterBarTonalElevationPreference.values.map {
RadioDialogOption(
text = it.getDesc(context),
selected = filterBarTonalElevation == it,
) {
it.put(context, scope)
}
}
) {
filterBarTonalElevationDialogVisible = false
}
RadioDialog(
visible = topBarTonalElevationDialogVisible,
title = stringResource(R.string.tonal_elevation),
options = FeedsTopBarTonalElevationPreference.values.map {
RadioDialogOption(
text = it.getDesc(context),
selected = topBarTonalElevation == it,
) {
it.put(context, scope)
}
}
) {
topBarTonalElevationDialogVisible = false
}
RadioDialog(
visible = groupListTonalElevationDialogVisible,
title = stringResource(R.string.tonal_elevation),
options = FeedsGroupListTonalElevationPreference.values.map {
RadioDialogOption(
text = it.getDesc(context),
selected = groupListTonalElevation == it,
) {
it.put(context, scope)
}
}
) {
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
.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
}
}
}

View File

@ -8,6 +8,8 @@ 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.material.icons.rounded.DoneAll
import androidx.compose.material.icons.rounded.Search
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
@ -15,6 +17,7 @@ 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.compose.ui.unit.dp
import androidx.navigation.NavHostController
import me.ash.reader.R
@ -23,22 +26,24 @@ 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.data.preference.FlowArticleListDatePreference.Companion.flowArticleListDate
import me.ash.reader.data.preference.FlowArticleListDescPreference.Companion.flowArticleListDesc
import me.ash.reader.data.preference.FlowArticleListFeedIconPreference.Companion.flowArticleListFeedIcon
import me.ash.reader.data.preference.FlowArticleListFeedNamePreference.Companion.flowArticleListFeedName
import me.ash.reader.data.preference.FlowArticleListImagePreference.Companion.flowArticleListImage
import me.ash.reader.data.preference.FlowArticleListTonalElevationPreference.Companion.flowArticleListTonalElevation
import me.ash.reader.data.preference.FlowFilterBarFilledPreference.Companion.flowFilterBarFilled
import me.ash.reader.data.preference.FlowFilterBarPaddingPreference.flowFilterBarPadding
import me.ash.reader.data.preference.FlowFilterBarStylePreference.Companion.flowFilterBarStyle
import me.ash.reader.data.preference.FlowFilterBarTonalElevationPreference.Companion.flowFilterBarTonalElevation
import me.ash.reader.data.preference.FlowTopBarTonalElevationPreference.Companion.flowTopBarTonalElevation
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.onDark
import me.ash.reader.ui.theme.palette.onLight
import java.util.*
@ -51,29 +56,32 @@ fun FlowPageStyle(
val context = LocalContext.current
val scope = rememberCoroutineScope()
val filterBarStyle =
context.filterBarStyle.collectAsStateValue(initial = FilterBarStylePreference.default)
context.flowFilterBarStyle.collectAsStateValue(initial = FlowFilterBarStylePreference.default)
val filterBarFilled =
context.filterBarFilled.collectAsStateValue(initial = FilterBarFilledPreference.default)
context.flowFilterBarFilled.collectAsStateValue(initial = FlowFilterBarFilledPreference.default)
val filterBarPadding =
context.filterBarPadding.collectAsStateValue(initial = FilterBarPaddingPreference.default)
context.flowFilterBarPadding.collectAsStateValue(initial = FlowFilterBarPaddingPreference.default)
val filterBarTonalElevation =
context.filterBarTonalElevation.collectAsStateValue(initial = FilterBarTonalElevationPreference.default)
context.flowFilterBarTonalElevation.collectAsStateValue(initial = FlowFilterBarTonalElevationPreference.default)
val topBarTonalElevation =
context.flowTopBarTonalElevation.collectAsStateValue(initial = FlowTopBarTonalElevationPreference.default)
val articleListFeedIcon =
context.articleListFeedIcon.collectAsStateValue(initial = ArticleListFeedIconPreference.default)
context.flowArticleListFeedIcon.collectAsStateValue(initial = FlowArticleListFeedIconPreference.default)
val articleListFeedName =
context.articleListFeedName.collectAsStateValue(initial = ArticleListFeedNamePreference.default)
context.flowArticleListFeedName.collectAsStateValue(initial = FlowArticleListFeedNamePreference.default)
val articleListImage =
context.articleListImage.collectAsStateValue(initial = ArticleListImagePreference.default)
context.flowArticleListImage.collectAsStateValue(initial = FlowArticleListImagePreference.default)
val articleListDesc =
context.articleListDesc.collectAsStateValue(initial = ArticleListDescPreference.default)
context.flowArticleListDesc.collectAsStateValue(initial = FlowArticleListDescPreference.default)
val articleListDate =
context.articleListDate.collectAsStateValue(initial = ArticleListDatePreference.default)
context.flowArticleListDate.collectAsStateValue(initial = FlowArticleListDatePreference.default)
val articleListTonalElevation =
context.articleListTonalElevation.collectAsStateValue(initial = ArticleListTonalElevationPreference.default)
context.flowArticleListTonalElevation.collectAsStateValue(initial = FlowArticleListTonalElevationPreference.default)
var filterBarStyleDialogVisible by remember { mutableStateOf(false) }
var filterBarPaddingDialogVisible by remember { mutableStateOf(false) }
var filterBarTonalElevationDialogVisible by remember { mutableStateOf(false) }
var topBarTonalElevationDialogVisible by remember { mutableStateOf(false) }
var articleListTonalElevationDialogVisible by remember { mutableStateOf(false) }
var filterBarPaddingValue: Int? by remember { mutableStateOf(filterBarPadding) }
@ -107,6 +115,8 @@ fun FlowPageStyle(
item {
DisplayText(text = stringResource(R.string.flow_page), desc = "")
}
// Preview
item {
Row(
modifier = Modifier
@ -121,67 +131,49 @@ fun FlowPageStyle(
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
FeedsPagePreview(articleListTonalElevation)
FlowPagePreview(
topBarTonalElevation = topBarTonalElevation,
articleListTonalElevation = articleListTonalElevation,
filterBarStyle = filterBarStyle.value,
filterBarFilled = filterBarFilled.value,
filterBarPadding = filterBarPadding.dp,
filterBarTonalElevation = filterBarTonalElevation.value.dp,
)
}
Spacer(modifier = Modifier.height(24.dp))
}
// Top Bar
item {
Subtitle(
modifier = Modifier.padding(horizontal = 24.dp),
text = "过滤栏",
text = stringResource(R.string.top_bar)
)
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 = "顶部",
title = stringResource(R.string.mark_as_read_button_position),
desc = stringResource(R.string.top),
enable = false,
onClick = {},
) {}
SettingItem(
title = stringResource(R.string.tonal_elevation),
desc = "${topBarTonalElevation.value}dp",
onClick = {
topBarTonalElevationDialogVisible = true
},
) {}
Tips(text = stringResource(R.string.tips_top_bar_tonal_elevation))
Spacer(modifier = Modifier.height(24.dp))
}
// Article List
item {
Subtitle(
modifier = Modifier.padding(horizontal = 24.dp),
text = "文章列表"
text = stringResource(R.string.article_list)
)
SettingItem(
title = "显示订阅源图标",
title = stringResource(R.string.display_feed_favicon),
onClick = {
(!articleListFeedIcon).put(context, scope)
},
@ -191,7 +183,7 @@ fun FlowPageStyle(
}
}
SettingItem(
title = "显示订阅源名称",
title = stringResource(R.string.display_feed_name),
onClick = {
(!articleListFeedName).put(context, scope)
},
@ -201,13 +193,14 @@ fun FlowPageStyle(
}
}
SettingItem(
title = "显示文章插图",
title = stringResource(R.string.display_article_image),
enable = false,
onClick = {},
) {
Switch(activated = false, enable = false)
}
SettingItem(
title = "显示文章描述",
title = stringResource(R.string.display_article_desc),
onClick = {
(!articleListDesc).put(context, scope)
},
@ -217,7 +210,7 @@ fun FlowPageStyle(
}
}
SettingItem(
title = "显示文章发布时间",
title = stringResource(R.string.display_article_date),
onClick = {
(!articleListDate).put(context, scope)
},
@ -227,12 +220,54 @@ fun FlowPageStyle(
}
}
SettingItem(
title = "色调海拔",
title = stringResource(R.string.tonal_elevation),
desc = "${articleListTonalElevation.value}dp",
onClick = {
articleListTonalElevationDialogVisible = true
},
) {}
Tips(text = stringResource(R.string.tips_article_list_tonal_elevation))
Spacer(modifier = Modifier.height(24.dp))
}
// Filter Bar
item {
Subtitle(
modifier = Modifier.padding(horizontal = 24.dp),
text = stringResource(R.string.filter_bar),
)
SettingItem(
title = stringResource(R.string.style),
desc = filterBarStyle.getDesc(context),
onClick = {
filterBarStyleDialogVisible = true
},
) {}
SettingItem(
title = stringResource(R.string.fill_selected_icon),
onClick = {
(!filterBarFilled).put(context, scope)
},
) {
Switch(activated = filterBarFilled.value) {
(!filterBarFilled).put(context, scope)
}
}
SettingItem(
title = stringResource(R.string.padding_on_both_ends),
desc = "${filterBarPadding}dp",
onClick = {
filterBarPaddingValue = filterBarPadding
filterBarPaddingDialogVisible = true
},
) {}
SettingItem(
title = stringResource(R.string.tonal_elevation),
desc = "${filterBarTonalElevation.value}dp",
onClick = {
filterBarTonalElevationDialogVisible = true
},
) {}
Spacer(modifier = Modifier.height(24.dp))
}
}
@ -241,8 +276,8 @@ fun FlowPageStyle(
RadioDialog(
visible = filterBarStyleDialogVisible,
title = stringResource(R.string.initial_filter),
options = FilterBarStylePreference.values.map {
title = stringResource(R.string.style),
options = FlowFilterBarStylePreference.values.map {
RadioDialogOption(
text = it.getDesc(context),
selected = filterBarStyle == it,
@ -256,17 +291,17 @@ fun FlowPageStyle(
TextFieldDialog(
visible = filterBarPaddingDialogVisible,
title = "两端边距",
title = stringResource(R.string.padding_on_both_ends),
value = (filterBarPaddingValue ?: "").toString(),
placeholder = stringResource(R.string.name),
placeholder = stringResource(R.string.value),
onValueChange = {
filterBarPaddingValue = it.toIntOrNull()
filterBarPaddingValue = it.filter { it.isDigit() }.toIntOrNull()
},
onDismissRequest = {
filterBarPaddingDialogVisible = false
},
onConfirm = {
FilterBarPaddingPreference.put(context, scope, filterBarPaddingValue ?: 0)
FlowFilterBarPaddingPreference.put(context, scope, filterBarPaddingValue ?: 0)
filterBarPaddingDialogVisible = false
}
)
@ -274,7 +309,7 @@ fun FlowPageStyle(
RadioDialog(
visible = filterBarTonalElevationDialogVisible,
title = stringResource(R.string.tonal_elevation),
options = FilterBarTonalElevationPreference.values.map {
options = FlowFilterBarTonalElevationPreference.values.map {
RadioDialogOption(
text = it.getDesc(context),
selected = filterBarTonalElevation == it,
@ -286,10 +321,25 @@ fun FlowPageStyle(
filterBarTonalElevationDialogVisible = false
}
RadioDialog(
visible = topBarTonalElevationDialogVisible,
title = stringResource(R.string.tonal_elevation),
options = FlowTopBarTonalElevationPreference.values.map {
RadioDialogOption(
text = it.getDesc(context),
selected = topBarTonalElevation == it,
) {
it.put(context, scope)
}
}
) {
topBarTonalElevationDialogVisible = false
}
RadioDialog(
visible = articleListTonalElevationDialogVisible,
title = stringResource(R.string.tonal_elevation),
options = ArticleListTonalElevationPreference.values.map {
options = FlowArticleListTonalElevationPreference.values.map {
RadioDialogOption(
text = it.getDesc(context),
selected = articleListTonalElevation == it,
@ -303,43 +353,85 @@ fun FlowPageStyle(
}
@Composable
fun FeedsPagePreview(
articleListTonalElevation: ArticleListTonalElevationPreference,
fun FlowPagePreview(
topBarTonalElevation: FlowTopBarTonalElevationPreference,
articleListTonalElevation: FlowArticleListTonalElevationPreference,
filterBarStyle: Int,
filterBarFilled: Boolean,
filterBarPadding: Dp,
filterBarTonalElevation: Dp,
) {
var filter by remember { mutableStateOf(Filter.Unread) }
Column(
modifier = Modifier
.background(
color = MaterialTheme.colorScheme.surfaceColorAtElevation(articleListTonalElevation.value.dp),
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 = "《黎明之剑》撒花完结,白金远瞳的“希灵三部曲”值得你通宵阅读",
shortDescription = "昨日在找书的时候无意间发现,“远瞳”的《黎明之剑》突然冲上了起点热搜榜首位,去小说中查找原因,原来是这部六百多万字的作品已经完结了。四年的时间,这部小说始终占据科幻分类前三甲的位置,不得不说“远瞳”的实力的确不容小觑。",
rawDescription = "昨日在找书的时候无意间发现,“远瞳”的《黎明之剑》突然冲上了起点热搜榜首位,去小说中查找原因,原来是这部六百多万字的作品已经完结了。四年的时间,这部小说始终占据科幻分类前三甲的位置,不得不说“远瞳”的实力的确不容小觑。",
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,
),
feed = Feed(
id = "",
name = "佛门射手",
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) {
FilterBar(
modifier = Modifier.padding(horizontal = 12.dp),
filter = filter,
filterBarStyle = filterBarStyle,
filterBarFilled = filterBarFilled,
filterBarPadding = filterBarPadding,
filterBarTonalElevation = filterBarTonalElevation,
) {
filter = it
}
}

View File

@ -6,7 +6,6 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Info
import androidx.compose.material.icons.rounded.CheckCircleOutline
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
@ -21,6 +20,7 @@ import kotlinx.coroutines.launch
import me.ash.reader.R
import me.ash.reader.ui.component.DisplayText
import me.ash.reader.ui.component.DynamicSVGImage
import me.ash.reader.ui.component.Tips
import me.ash.reader.ui.ext.DataStoreKeys
import me.ash.reader.ui.ext.dataStore
import me.ash.reader.ui.ext.put
@ -57,14 +57,12 @@ fun StartupPage(
)
}
item {
TipsItem(
modifier = Modifier
.padding(horizontal = 24.dp)
.padding(top = 40.dp)
Tips(
modifier = Modifier.padding(top = 40.dp),
text = stringResource(R.string.agree_terms),
)
}
item {
Spacer(modifier = Modifier.height(10.dp))
TextButton(
modifier = Modifier.padding(horizontal = 12.dp),
onClick = {
@ -120,25 +118,3 @@ fun StartupPage(
}
)
}
@Composable
private fun TipsItem(
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier
.fillMaxWidth()
) {
Icon(
imageVector = Icons.Outlined.Info,
contentDescription = stringResource(R.string.tips_and_support),
tint = MaterialTheme.colorScheme.onSurfaceVariant,
)
Spacer(modifier = Modifier.height(22.dp))
Text(
text = stringResource(R.string.agree_terms),
style = MaterialTheme.typography.titleSmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
}
}

View File

@ -10,6 +10,7 @@ object SVGString
fun String.parseDynamicColor(tonalPalettes: TonalPalettes, isDarkTheme: Boolean): String =
replace("fill=\"(.+?)\"".toRegex()) {
val value = it.groupValues[1]
Log.i("RLog", "parseDynamicColor: $value")
if (value.startsWith("#")) return@replace it.value
try {
val (scheme, tone) = value.split("(?<=\\d)(?=\\D)|(?=\\d)(?<=\\D)".toRegex())
@ -24,6 +25,7 @@ fun String.parseDynamicColor(tonalPalettes: TonalPalettes, isDarkTheme: Boolean)
}?.toArgb() ?: 0xFFFFFF
"fill=\"${String.format("#%06X", 0xFFFFFF and argb)}\""
} catch (e: Exception) {
e.printStackTrace()
Log.e("RLog", "parseDynamicColor: ${e.message}")
it.value
}

View File

@ -5,12 +5,29 @@ import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.ui.platform.LocalContext
import kotlinx.coroutines.flow.map
import me.ash.reader.ui.ext.DataStoreKeys
import me.ash.reader.ui.ext.dataStore
import me.ash.reader.data.preference.*
import me.ash.reader.data.preference.FeedsFilterBarFilledPreference.Companion.feedsFilterBarFilled
import me.ash.reader.data.preference.FeedsFilterBarPaddingPreference.feedsFilterBarPadding
import me.ash.reader.data.preference.FeedsFilterBarStylePreference.Companion.feedsFilterBarStyle
import me.ash.reader.data.preference.FeedsFilterBarTonalElevationPreference.Companion.feedsFilterBarTonalElevation
import me.ash.reader.data.preference.FeedsGroupListExpandPreference.Companion.feedsGroupListExpand
import me.ash.reader.data.preference.FeedsGroupListTonalElevationPreference.Companion.feedsGroupListTonalElevation
import me.ash.reader.data.preference.FeedsTopBarTonalElevationPreference.Companion.feedsTopBarTonalElevation
import me.ash.reader.data.preference.FlowArticleListDatePreference.Companion.flowArticleListDate
import me.ash.reader.data.preference.FlowArticleListDescPreference.Companion.flowArticleListDesc
import me.ash.reader.data.preference.FlowArticleListFeedIconPreference.Companion.flowArticleListFeedIcon
import me.ash.reader.data.preference.FlowArticleListFeedNamePreference.Companion.flowArticleListFeedName
import me.ash.reader.data.preference.FlowArticleListImagePreference.Companion.flowArticleListImage
import me.ash.reader.data.preference.FlowArticleListTonalElevationPreference.Companion.flowArticleListTonalElevation
import me.ash.reader.data.preference.FlowFilterBarFilledPreference.Companion.flowFilterBarFilled
import me.ash.reader.data.preference.FlowFilterBarPaddingPreference.flowFilterBarPadding
import me.ash.reader.data.preference.FlowFilterBarStylePreference.Companion.flowFilterBarStyle
import me.ash.reader.data.preference.FlowFilterBarTonalElevationPreference.Companion.flowFilterBarTonalElevation
import me.ash.reader.data.preference.FlowTopBarTonalElevationPreference.Companion.flowTopBarTonalElevation
import me.ash.reader.data.preference.ThemePreference.Theme
import me.ash.reader.ui.ext.collectAsStateValue
import me.ash.reader.ui.theme.palette.LocalTonalPalettes
import me.ash.reader.ui.theme.palette.TonalPalettes
import me.ash.reader.ui.theme.palette.core.ProvideZcamViewingConditions
@ -19,6 +36,36 @@ import me.ash.reader.ui.theme.palette.dynamicDarkColorScheme
import me.ash.reader.ui.theme.palette.dynamicLightColorScheme
val LocalUseDarkTheme = compositionLocalOf { false }
val LocalTheme = compositionLocalOf { ThemePreference.default }
val LocalFeedsFilterBarStyle = compositionLocalOf { FeedsFilterBarStylePreference.default.value }
val LocalFeedsFilterBarFilled = compositionLocalOf { FeedsFilterBarFilledPreference.default.value }
val LocalFeedsFilterBarPadding = compositionLocalOf { FeedsFilterBarPaddingPreference.default }
val LocalFeedsFilterBarTonalElevation =
compositionLocalOf { FeedsFilterBarTonalElevationPreference.default.value }
val LocalFeedsTopBarTonalElevation =
compositionLocalOf { FeedsTopBarTonalElevationPreference.default.value }
val LocalFeedsGroupListExpand =
compositionLocalOf { FeedsGroupListExpandPreference.default.value }
val LocalFeedsGroupListTonalElevation =
compositionLocalOf { FeedsGroupListTonalElevationPreference.default.value }
val LocalFlowFilterBarStyle = compositionLocalOf { FlowFilterBarStylePreference.default.value }
val LocalFlowFilterBarFilled = compositionLocalOf { FlowFilterBarFilledPreference.default.value }
val LocalFlowFilterBarPadding = compositionLocalOf { FlowFilterBarPaddingPreference.default }
val LocalFlowFilterBarTonalElevation =
compositionLocalOf { FlowFilterBarTonalElevationPreference.default.value }
val LocalFlowTopBarTonalElevation =
compositionLocalOf { FlowTopBarTonalElevationPreference.default.value }
val LocalFlowArticleListFeedIcon =
compositionLocalOf { FlowArticleListFeedIconPreference.default.value }
val LocalFlowArticleListFeedName =
compositionLocalOf { FlowArticleListFeedNamePreference.default.value }
val LocalFlowArticleListImage = compositionLocalOf { FlowArticleListImagePreference.default.value }
val LocalFlowArticleListDesc = compositionLocalOf { FlowArticleListDescPreference.default.value }
val LocalFlowArticleListDate = compositionLocalOf { FlowArticleListDatePreference.default.value }
val LocalFlowArticleListTonalElevation =
compositionLocalOf { FlowArticleListTonalElevationPreference.default.value }
@SuppressLint("FlowOperatorInvokedInComposition")
@Composable
@ -28,26 +75,83 @@ fun AppTheme(
content: @Composable () -> Unit
) {
val context = LocalContext.current
val themeIndex = context.dataStore.data
.map { it[DataStoreKeys.ThemeIndex.key] ?: 5 }
.collectAsState(initial = 5).value
val theme = context.Theme.collectAsStateValue(initial = ThemePreference.default)
val tonalPalettes = wallpaperPalettes[
if (themeIndex >= wallpaperPalettes.size) {
if (theme >= wallpaperPalettes.size) {
when {
wallpaperPalettes.size == 5 -> 0
wallpaperPalettes.size > 5 -> 5
else -> 0
}
} else {
themeIndex
theme
}
]
val feedsFilterBarStyle =
context.feedsFilterBarStyle.collectAsStateValue(initial = FeedsFilterBarStylePreference.default)
val feedsFilterBarFilled =
context.feedsFilterBarFilled.collectAsStateValue(initial = FeedsFilterBarFilledPreference.default)
val feedsFilterBarPadding =
context.feedsFilterBarPadding.collectAsStateValue(initial = FeedsFilterBarPaddingPreference.default)
val feedsFilterBarTonalElevation =
context.feedsFilterBarTonalElevation.collectAsStateValue(initial = FeedsFilterBarTonalElevationPreference.default)
val feedsTopBarTonalElevation =
context.feedsTopBarTonalElevation.collectAsStateValue(initial = FeedsTopBarTonalElevationPreference.default)
val feedsGroupListExpand =
context.feedsGroupListExpand.collectAsStateValue(initial = FeedsGroupListExpandPreference.default)
val feedsGroupListTonalElevation =
context.feedsGroupListTonalElevation.collectAsStateValue(initial = FeedsGroupListTonalElevationPreference.default)
val flowFilterBarStyle =
context.flowFilterBarStyle.collectAsStateValue(initial = FlowFilterBarStylePreference.default)
val flowFilterBarFilled =
context.flowFilterBarFilled.collectAsStateValue(initial = FlowFilterBarFilledPreference.default)
val flowFilterBarPadding =
context.flowFilterBarPadding.collectAsStateValue(initial = FlowFilterBarPaddingPreference.default)
val flowFilterBarTonalElevation =
context.flowFilterBarTonalElevation.collectAsStateValue(initial = FlowFilterBarTonalElevationPreference.default)
val flowTopBarTonalElevation =
context.flowTopBarTonalElevation.collectAsStateValue(initial = FlowTopBarTonalElevationPreference.default)
val flowArticleListFeedIcon =
context.flowArticleListFeedIcon.collectAsStateValue(initial = FlowArticleListFeedIconPreference.default)
val flowArticleListFeedName =
context.flowArticleListFeedName.collectAsStateValue(initial = FlowArticleListFeedNamePreference.default)
val flowArticleListImage =
context.flowArticleListImage.collectAsStateValue(initial = FlowArticleListImagePreference.default)
val flowArticleListDesc =
context.flowArticleListDesc.collectAsStateValue(initial = FlowArticleListDescPreference.default)
val flowArticleListDate =
context.flowArticleListDate.collectAsStateValue(initial = FlowArticleListDatePreference.default)
val flowArticleListTonalElevation =
context.flowArticleListTonalElevation.collectAsStateValue(initial = FlowArticleListTonalElevationPreference.default)
ProvideZcamViewingConditions {
CompositionLocalProvider(
LocalTonalPalettes provides tonalPalettes.also { it.Preheating() },
LocalUseDarkTheme provides useDarkTheme
LocalUseDarkTheme provides useDarkTheme,
LocalTheme provides theme,
LocalFeedsFilterBarStyle provides feedsFilterBarStyle.value,
LocalFeedsFilterBarFilled provides feedsFilterBarFilled.value,
LocalFeedsFilterBarPadding provides feedsFilterBarPadding,
LocalFeedsFilterBarTonalElevation provides feedsFilterBarTonalElevation.value,
LocalFeedsTopBarTonalElevation provides feedsTopBarTonalElevation.value,
LocalFeedsGroupListExpand provides feedsGroupListExpand.value,
LocalFeedsGroupListTonalElevation provides feedsGroupListTonalElevation.value,
LocalFlowFilterBarStyle provides flowFilterBarStyle.value,
LocalFlowFilterBarFilled provides flowFilterBarFilled.value,
LocalFlowFilterBarPadding provides flowFilterBarPadding,
LocalFlowFilterBarTonalElevation provides flowFilterBarTonalElevation.value,
LocalFlowTopBarTonalElevation provides flowTopBarTonalElevation.value,
LocalFlowArticleListFeedIcon provides flowArticleListFeedIcon.value,
LocalFlowArticleListFeedName provides flowArticleListFeedName.value,
LocalFlowArticleListImage provides flowArticleListImage.value,
LocalFlowArticleListDesc provides flowArticleListDesc.value,
LocalFlowArticleListDate provides flowArticleListDate.value,
LocalFlowArticleListTonalElevation provides flowArticleListTonalElevation.value,
) {
MaterialTheme(
colorScheme =

View File

@ -31,6 +31,7 @@ fun dynamicLightColorScheme(): ColorScheme {
onSurface = palettes neutral 10,
surfaceVariant = palettes neutralVariant 90,
onSurfaceVariant = palettes neutralVariant 30,
surfaceTint = palettes primary 40,
inverseSurface = palettes neutral 20,
inverseOnSurface = palettes neutral 95,
outline = palettes neutralVariant 50,
@ -60,6 +61,7 @@ fun dynamicDarkColorScheme(): ColorScheme {
onSurface = palettes neutral 90,
surfaceVariant = palettes neutralVariant 30,
onSurfaceVariant = palettes neutralVariant 80,
surfaceTint = palettes primary 80,
inverseSurface = palettes neutral 90,
inverseOnSurface = palettes neutral 20,
outline = palettes neutralVariant 60,

View File

@ -99,7 +99,7 @@
<string name="primary_color_hint">例如 #666666 或 666666</string>
<string name="appearance">外观</string>
<string name="style">样式</string>
<string name="dark_theme">深色模式</string>
<string name="dark_theme">深色主题</string>
<string name="use_device_theme">跟随系统设置</string>
<string name="tonal_elevation">色调海拔</string>
<string name="fonts">字体</string>
@ -124,4 +124,28 @@
<string name="on_start">启动时</string>
<string name="initial_page">起始页面</string>
<string name="initial_filter">起始过滤条件</string>
<string name="preview_article_title">呜呜呜,黎明之剑完结了</string>
<string name="preview_article_desc">是宴席,就有结束的时候,但这本书真的陪了我好久啊,好舍不得,而且主线结束了坑却没填完,不知道啥时候才有番外啊</string>
<string name="preview_feed_name">漩涡书院</string>
<string name="value"></string>
<string name="padding_on_both_ends">两端边距</string>
<string name="display_article_date">显示文章发布时间</string>
<string name="display_article_desc">显示文章描述</string>
<string name="display_article_image">显示文章插图</string>
<string name="display_feed_name">显示订阅源名称</string>
<string name="display_feed_favicon">显示订阅源图标</string>
<string name="article_list">文章列表</string>
<string name="group_list">分组列表</string>
<string name="always_expand">始终展开</string>
<string name="top">顶部</string>
<string name="mark_as_read_button_position">“标记为已读”按钮的位置</string>
<string name="top_bar">标题栏</string>
<string name="fill_selected_icon">填充已选中的图标</string>
<string name="filter_bar">过滤栏</string>
<string name="icons">图标</string>
<string name="icons_and_labels">图标和文字</string>
<string name="icons_and_label_only_selected">图标和文字(仅选中时)</string>
<string name="tips_top_bar_tonal_elevation">标题栏的色调海拔仅在滚动时可用。</string>
<string name="tips_article_list_tonal_elevation">文章列表的色调海拔仅在亮色主题时可用。</string>
<string name="tips_group_list_tonal_elevation">分组列表的色调海拔仅在亮色主题时可用。</string>
</resources>

View File

@ -88,9 +88,9 @@
<string name="tips_and_support_desc">About, open source</string>
<string name="welcome">Welcome</string>
<string name="agree_terms">Before you can continue, you need to agree to Read You\'s Terms of Service and Privacy Policy.</string>
<string name="view_terms">View the &lt;i&gt;&lt;u&gt;Terms of Service and Privacy Policy&lt;/u&gt;&lt;/i&gt;</string>
<string name="view_terms">View the &lt;i&gt;&lt;u&gt;Terms of Service &amp; Privacy Policy&lt;/u&gt;&lt;/i&gt;</string>
<string name="terms_link">https://github.com/Ashinch/ReadYou/blob/main/TERMS_OF_SERVICE_AND_PRIVACY_POLICY.md</string>
<string name="agree_and_continue">Agree and Continue</string>
<string name="agree_and_continue">Agree</string>
<string name="wallpaper_colors">Wallpaper Colors</string>
<string name="no_palettes">No Palettes</string>
<string name="only_android_8.1_plus">Only Android 8.1+</string>
@ -116,7 +116,7 @@
<string name="update">Update</string>
<string name="skip_this_version">Skip This Version</string>
<string name="checking_updates">Checking for updates…</string>
<string name="is_latest_version">This is the latest version</string>
<string name="is_latest_version">This is latest version</string>
<string name="check_failure">Check failure</string>
<string name="download_failure">Download failure</string>
<string name="rate_limit">The request rate is limited</string>
@ -124,4 +124,28 @@
<string name="on_start">On Start</string>
<string name="initial_page">Initial Page</string>
<string name="initial_filter">Initial Filter</string>
<string name="preview_article_title">The novel "Lord of the Mysteries" has finally come to an end</string>
<string name="preview_article_desc">The Fool is the eighth and final volume of the Lord of the Mysteries series written by Cuttlefish That Loves Diving.</string>
<string name="preview_feed_name">Reddit</string>
<string name="value">value</string>
<string name="padding_on_both_ends">Padding on Both Ends</string>
<string name="display_article_date">Display Article Publish Time</string>
<string name="display_article_desc">Display Article Descriptions</string>
<string name="display_article_image">Display Article Images</string>
<string name="display_feed_name">Display Feed Names</string>
<string name="display_feed_favicon">Display Feed Favicons</string>
<string name="article_list">Article List</string>
<string name="group_list">Group List</string>
<string name="always_expand">Always Expand</string>
<string name="top">Top</string>
<string name="mark_as_read_button_position">\"Mark as Read\" Button Position</string>
<string name="top_bar">Top Bar</string>
<string name="fill_selected_icon">Fill The Selected Icon</string>
<string name="filter_bar">Filter Bar</string>
<string name="icons">Icons</string>
<string name="icons_and_labels">Icons &amp; Labels</string>
<string name="icons_and_label_only_selected">Icons &amp; Label (Only Selected)</string>
<string name="tips_top_bar_tonal_elevation">Tone elevation of the top bar is only available when scrolling.</string>
<string name="tips_article_list_tonal_elevation">Tone elevation of the article list is only available for light theme.</string>
<string name="tips_group_list_tonal_elevation">Tone elevation of the group list is only available for light theme.</string>
</resources>