Add FeedsPage style settings
This commit is contained in:
parent
9bea2e8e8b
commit
fa3c5e3601
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
38
app/src/main/java/me/ash/reader/ui/component/Tips.kt
Normal file
38
app/src/main/java/me/ash/reader/ui/component/Tips.kt
Normal 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,
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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>() {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
|
@ -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(),
|
||||
|
|
|
@ -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(
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>
|
|
@ -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 <i><u>Terms of Service and Privacy Policy</u></i></string>
|
||||
<string name="view_terms">View the <i><u>Terms of Service & Privacy Policy</u></i></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 & Labels</string>
|
||||
<string name="icons_and_label_only_selected">Icons & 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>
|
Loading…
Reference in New Issue
Block a user