Optimize the flow page

This commit is contained in:
Ash 2022-05-24 17:47:24 +08:00
parent ba3620d84f
commit 66094f8075
25 changed files with 230 additions and 199 deletions

View File

@ -0,0 +1,10 @@
package me.ash.reader.data.constant
object ElevationTokens {
const val Level0 = 0
const val Level1 = 1
const val Level2 = 3
const val Level3 = 6
const val Level4 = 8
const val Level5 = 12
}

View File

@ -1,9 +1,6 @@
package me.ash.reader.data.entity
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
import androidx.room.*
import java.util.*
@Entity(
@ -45,4 +42,7 @@ data class Article(
var isStarred: Boolean = false,
@ColumnInfo(defaultValue = "false")
var isReadLater: Boolean = false,
)
) {
@Ignore
var dateString: String? = null
}

View File

@ -4,17 +4,18 @@ import android.content.Context
import androidx.datastore.preferences.core.Preferences
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import me.ash.reader.data.constant.ElevationTokens
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)
object Level0 : FeedsFilterBarTonalElevationPreference(ElevationTokens.Level0)
object Level1 : FeedsFilterBarTonalElevationPreference(ElevationTokens.Level1)
object Level2 : FeedsFilterBarTonalElevationPreference(ElevationTokens.Level2)
object Level3 : FeedsFilterBarTonalElevationPreference(ElevationTokens.Level3)
object Level4 : FeedsFilterBarTonalElevationPreference(ElevationTokens.Level4)
object Level5 : FeedsFilterBarTonalElevationPreference(ElevationTokens.Level5)
override fun put(context: Context, scope: CoroutineScope) {
scope.launch {
@ -27,12 +28,12 @@ sealed class FeedsFilterBarTonalElevationPreference(val value: Int) : Preference
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)"
Level0 -> "Level 0 (${ElevationTokens.Level0}dp)"
Level1 -> "Level 1 (${ElevationTokens.Level1}dp)"
Level2 -> "Level 2 (${ElevationTokens.Level2}dp)"
Level3 -> "Level 3 (${ElevationTokens.Level3}dp)"
Level4 -> "Level 4 (${ElevationTokens.Level4}dp)"
Level5 -> "Level 5 (${ElevationTokens.Level5}dp)"
}
companion object {
@ -41,13 +42,14 @@ sealed class FeedsFilterBarTonalElevationPreference(val value: Int) : Preference
fun fromPreferences(preferences: Preferences) =
when (preferences[DataStoreKeys.FeedsFilterBarTonalElevation.key]) {
0 -> Level0
1 -> Level1
3 -> Level2
6 -> Level3
8 -> Level4
12 -> Level5
ElevationTokens.Level0 -> Level0
ElevationTokens.Level1 -> Level1
ElevationTokens.Level2 -> Level2
ElevationTokens.Level3 -> Level3
ElevationTokens.Level4 -> Level4
ElevationTokens.Level5 -> Level5
else -> default
}
}
}
}

View File

@ -4,17 +4,18 @@ import android.content.Context
import androidx.datastore.preferences.core.Preferences
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import me.ash.reader.data.constant.ElevationTokens
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)
object Level0 : FeedsGroupListTonalElevationPreference(ElevationTokens.Level0)
object Level1 : FeedsGroupListTonalElevationPreference(ElevationTokens.Level1)
object Level2 : FeedsGroupListTonalElevationPreference(ElevationTokens.Level2)
object Level3 : FeedsGroupListTonalElevationPreference(ElevationTokens.Level3)
object Level4 : FeedsGroupListTonalElevationPreference(ElevationTokens.Level4)
object Level5 : FeedsGroupListTonalElevationPreference(ElevationTokens.Level5)
override fun put(context: Context, scope: CoroutineScope) {
scope.launch {
@ -27,12 +28,12 @@ sealed class FeedsGroupListTonalElevationPreference(val value: Int) : Preference
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)"
Level0 -> "Level 0 (${ElevationTokens.Level0}dp)"
Level1 -> "Level 1 (${ElevationTokens.Level1}dp)"
Level2 -> "Level 2 (${ElevationTokens.Level2}dp)"
Level3 -> "Level 3 (${ElevationTokens.Level3}dp)"
Level4 -> "Level 4 (${ElevationTokens.Level4}dp)"
Level5 -> "Level 5 (${ElevationTokens.Level5}dp)"
}
companion object {
@ -41,12 +42,12 @@ sealed class FeedsGroupListTonalElevationPreference(val value: Int) : Preference
fun fromPreferences(preferences: Preferences) =
when (preferences[DataStoreKeys.FeedsGroupListTonalElevation.key]) {
0 -> Level0
1 -> Level1
3 -> Level2
6 -> Level3
8 -> Level4
12 -> Level5
ElevationTokens.Level0 -> Level0
ElevationTokens.Level1 -> Level1
ElevationTokens.Level2 -> Level2
ElevationTokens.Level3 -> Level3
ElevationTokens.Level4 -> Level4
ElevationTokens.Level5 -> Level5
else -> default
}
}

View File

@ -4,17 +4,18 @@ import android.content.Context
import androidx.datastore.preferences.core.Preferences
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import me.ash.reader.data.constant.ElevationTokens
import me.ash.reader.ui.ext.DataStoreKeys
import me.ash.reader.ui.ext.dataStore
import me.ash.reader.ui.ext.put
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)
object Level0 : FeedsTopBarTonalElevationPreference(ElevationTokens.Level0)
object Level1 : FeedsTopBarTonalElevationPreference(ElevationTokens.Level1)
object Level2 : FeedsTopBarTonalElevationPreference(ElevationTokens.Level2)
object Level3 : FeedsTopBarTonalElevationPreference(ElevationTokens.Level3)
object Level4 : FeedsTopBarTonalElevationPreference(ElevationTokens.Level4)
object Level5 : FeedsTopBarTonalElevationPreference(ElevationTokens.Level5)
override fun put(context: Context, scope: CoroutineScope) {
scope.launch {
@ -27,12 +28,12 @@ sealed class FeedsTopBarTonalElevationPreference(val value: Int) : Preference()
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)"
Level0 -> "Level 0 (${ElevationTokens.Level0}dp)"
Level1 -> "Level 1 (${ElevationTokens.Level1}dp)"
Level2 -> "Level 2 (${ElevationTokens.Level2}dp)"
Level3 -> "Level 3 (${ElevationTokens.Level3}dp)"
Level4 -> "Level 4 (${ElevationTokens.Level4}dp)"
Level5 -> "Level 5 (${ElevationTokens.Level5}dp)"
}
companion object {
@ -41,12 +42,12 @@ sealed class FeedsTopBarTonalElevationPreference(val value: Int) : Preference()
fun fromPreferences(preferences: Preferences) =
when (preferences[DataStoreKeys.FeedsTopBarTonalElevation.key]) {
0 -> Level0
1 -> Level1
3 -> Level2
6 -> Level3
8 -> Level4
12 -> Level5
ElevationTokens.Level0 -> Level0
ElevationTokens.Level1 -> Level1
ElevationTokens.Level2 -> Level2
ElevationTokens.Level3 -> Level3
ElevationTokens.Level4 -> Level4
ElevationTokens.Level5 -> Level5
else -> default
}
}

View File

@ -4,17 +4,18 @@ import android.content.Context
import androidx.datastore.preferences.core.Preferences
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import me.ash.reader.data.constant.ElevationTokens
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)
object Level0 : FlowArticleListTonalElevationPreference(ElevationTokens.Level0)
object Level1 : FlowArticleListTonalElevationPreference(ElevationTokens.Level1)
object Level2 : FlowArticleListTonalElevationPreference(ElevationTokens.Level2)
object Level3 : FlowArticleListTonalElevationPreference(ElevationTokens.Level3)
object Level4 : FlowArticleListTonalElevationPreference(ElevationTokens.Level4)
object Level5 : FlowArticleListTonalElevationPreference(ElevationTokens.Level5)
override fun put(context: Context, scope: CoroutineScope) {
scope.launch {
@ -27,12 +28,12 @@ sealed class FlowArticleListTonalElevationPreference(val value: Int) : Preferenc
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)"
Level0 -> "Level 0 (${ElevationTokens.Level0}dp)"
Level1 -> "Level 1 (${ElevationTokens.Level1}dp)"
Level2 -> "Level 2 (${ElevationTokens.Level2}dp)"
Level3 -> "Level 3 (${ElevationTokens.Level3}dp)"
Level4 -> "Level 4 (${ElevationTokens.Level4}dp)"
Level5 -> "Level 5 (${ElevationTokens.Level5}dp)"
}
companion object {
@ -41,12 +42,12 @@ sealed class FlowArticleListTonalElevationPreference(val value: Int) : Preferenc
fun fromPreferences(preferences: Preferences) =
when (preferences[DataStoreKeys.FlowArticleListTonalElevation.key]) {
0 -> Level0
1 -> Level1
3 -> Level2
6 -> Level3
8 -> Level4
12 -> Level5
ElevationTokens.Level0 -> Level0
ElevationTokens.Level1 -> Level1
ElevationTokens.Level2 -> Level2
ElevationTokens.Level3 -> Level3
ElevationTokens.Level4 -> Level4
ElevationTokens.Level5 -> Level5
else -> default
}
}

View File

@ -4,17 +4,18 @@ import android.content.Context
import androidx.datastore.preferences.core.Preferences
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import me.ash.reader.data.constant.ElevationTokens
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)
object Level0 : FlowFilterBarTonalElevationPreference(ElevationTokens.Level0)
object Level1 : FlowFilterBarTonalElevationPreference(ElevationTokens.Level1)
object Level2 : FlowFilterBarTonalElevationPreference(ElevationTokens.Level2)
object Level3 : FlowFilterBarTonalElevationPreference(ElevationTokens.Level3)
object Level4 : FlowFilterBarTonalElevationPreference(ElevationTokens.Level4)
object Level5 : FlowFilterBarTonalElevationPreference(ElevationTokens.Level5)
override fun put(context: Context, scope: CoroutineScope) {
scope.launch {
@ -27,12 +28,12 @@ sealed class FlowFilterBarTonalElevationPreference(val value: Int) : Preference(
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)"
Level0 -> "Level 0 (${ElevationTokens.Level0}dp)"
Level1 -> "Level 1 (${ElevationTokens.Level1}dp)"
Level2 -> "Level 2 (${ElevationTokens.Level2}dp)"
Level3 -> "Level 3 (${ElevationTokens.Level3}dp)"
Level4 -> "Level 4 (${ElevationTokens.Level4}dp)"
Level5 -> "Level 5 (${ElevationTokens.Level5}dp)"
}
companion object {
@ -41,12 +42,12 @@ sealed class FlowFilterBarTonalElevationPreference(val value: Int) : Preference(
fun fromPreferences(preferences: Preferences) =
when (preferences[DataStoreKeys.FlowFilterBarTonalElevation.key]) {
0 -> Level0
1 -> Level1
3 -> Level2
6 -> Level3
8 -> Level4
12 -> Level5
ElevationTokens.Level0 -> Level0
ElevationTokens.Level1 -> Level1
ElevationTokens.Level2 -> Level2
ElevationTokens.Level3 -> Level3
ElevationTokens.Level4 -> Level4
ElevationTokens.Level5 -> Level5
else -> default
}
}

View File

@ -4,17 +4,18 @@ import android.content.Context
import androidx.datastore.preferences.core.Preferences
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import me.ash.reader.data.constant.ElevationTokens
import me.ash.reader.ui.ext.DataStoreKeys
import me.ash.reader.ui.ext.dataStore
import me.ash.reader.ui.ext.put
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)
object Level0 : FlowTopBarTonalElevationPreference(ElevationTokens.Level0)
object Level1 : FlowTopBarTonalElevationPreference(ElevationTokens.Level1)
object Level2 : FlowTopBarTonalElevationPreference(ElevationTokens.Level2)
object Level3 : FlowTopBarTonalElevationPreference(ElevationTokens.Level3)
object Level4 : FlowTopBarTonalElevationPreference(ElevationTokens.Level4)
object Level5 : FlowTopBarTonalElevationPreference(ElevationTokens.Level5)
override fun put(context: Context, scope: CoroutineScope) {
scope.launch {
@ -27,12 +28,12 @@ sealed class FlowTopBarTonalElevationPreference(val value: Int) : Preference() {
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)"
Level0 -> "Level 0 (${ElevationTokens.Level0}dp)"
Level1 -> "Level 1 (${ElevationTokens.Level1}dp)"
Level2 -> "Level 2 (${ElevationTokens.Level2}dp)"
Level3 -> "Level 3 (${ElevationTokens.Level3}dp)"
Level4 -> "Level 4 (${ElevationTokens.Level4}dp)"
Level5 -> "Level 5 (${ElevationTokens.Level5}dp)"
}
companion object {
@ -41,12 +42,12 @@ sealed class FlowTopBarTonalElevationPreference(val value: Int) : Preference() {
fun fromPreferences(preferences: Preferences) =
when (preferences[DataStoreKeys.FlowTopBarTonalElevation.key]) {
0 -> Level0
1 -> Level1
3 -> Level2
6 -> Level3
8 -> Level4
12 -> Level5
ElevationTokens.Level0 -> Level0
ElevationTokens.Level1 -> Level1
ElevationTokens.Level2 -> Level2
ElevationTokens.Level3 -> Level3
ElevationTokens.Level4 -> Level4
ElevationTokens.Level5 -> Level5
else -> default
}
}

View File

@ -54,7 +54,7 @@ class OpmlRepository @Inject constructor(
Opml(
"2.0",
Head(
accountDao.queryById(context.currentAccountId).name,
accountDao.queryById(context.currentAccountId)?.name,
Date().toString(), null, null, null,
null, null, null, null,
null, null, null, null,

View File

@ -21,8 +21,6 @@ import net.dankito.readability4j.extended.Readability4JExtended
import okhttp3.OkHttpClient
import okhttp3.Request
import java.net.URL
import java.text.ParsePosition
import java.text.SimpleDateFormat
import java.util.*
import javax.inject.Inject
@ -86,7 +84,12 @@ class RssHelper @Inject constructor(
return withContext(dispatcherIO) {
val a = mutableListOf<Article>()
val accountId = context.currentAccountId
val parseRss: SyndFeed = SyndFeedInput().build(XmlReader(URL(feed.url)))
val parseRss: SyndFeed = SyndFeedInput().build(
XmlReader(URL(feed.url).openConnection().apply {
connectTimeout = 5000
readTimeout = 5000
})
)
parseRss.entries.forEach {
if (latestLink != null && latestLink == it.link) return@withContext a
val desc = it.description?.value
@ -111,13 +114,13 @@ class RssHelper @Inject constructor(
date = it.publishedDate ?: it.updatedDate ?: Date(),
title = Html.fromHtml(it.title.toString()).toString(),
author = it.author,
rawDescription = (desc ?: content) ?: "",
rawDescription = (content ?: desc) ?: "",
shortDescription = (Readability4JExtended("", desc ?: content ?: "")
.parse().textContent ?: "")
.take(100)
.trim(),
fullContent = content,
img = findImg((desc ?: content) ?: ""),
img = findImg((content ?: desc) ?: ""),
link = it.link ?: "",
)
)

View File

@ -11,6 +11,13 @@ class StringsRepository @Inject constructor(
private val context: Context,
) {
fun getString(resId: Int, vararg formatArgs: Any) = context.getString(resId, *formatArgs)
fun getQuantityString(resId: Int, quantity: Int, vararg formatArgs: Any) = context.resources.getQuantityString(resId, quantity, *formatArgs)
fun formatAsString(date: Date?) = date?.formatAsString(context)
fun getQuantityString(resId: Int, quantity: Int, vararg formatArgs: Any) =
context.resources.getQuantityString(resId, quantity, *formatArgs)
fun formatAsString(
date: Date?,
onlyHourMinute: Boolean? = false,
atHourMinute: Boolean? = false
) = date?.formatAsString(context, onlyHourMinute, atHourMinute)
}

View File

@ -16,7 +16,7 @@ import coil.size.Scale
import coil.size.Size
import me.ash.reader.R
val Size_1000 = Size(1000, 1000)
val SIZE_1000 = Size(1000, 1000)
@Composable
fun RYAsyncImage(

View File

@ -50,7 +50,12 @@ class HomeViewModel @Inject constructor(
fun fetchArticles() {
_homeUiState.update {
it.copy(
pagingData = Pager(PagingConfig(pageSize = 50)) {
pagingData = Pager(
config = PagingConfig(
pageSize = 100,
enablePlaceholders = false,
)
) {
if (_homeUiState.value.searchContent.isNotBlank()) {
rssRepository.get().searchArticles(
content = _homeUiState.value.searchContent.trim(),
@ -68,7 +73,14 @@ class HomeViewModel @Inject constructor(
)
}
}.flow.map {
it.map { FlowItemView.Article(it) }.insertSeparators { before, after ->
it.map {
FlowItemView.Article(it.apply {
article.dateString = stringsRepository.formatAsString(
date = article.date,
onlyHourMinute = true
)
})
}.insertSeparators { before, after ->
val beforeDate =
stringsRepository.formatAsString(before?.articleWithFeed?.article?.date)
val afterDate =

View File

@ -4,16 +4,13 @@ import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import me.ash.reader.R
import me.ash.reader.data.entity.Article
import me.ash.reader.data.entity.Feed
import me.ash.reader.data.entity.Group
import me.ash.reader.data.module.DispatcherIO
import me.ash.reader.data.repository.OpmlRepository
import me.ash.reader.data.repository.RssHelper
import me.ash.reader.data.repository.RssRepository
@ -28,8 +25,6 @@ class SubscribeViewModel @Inject constructor(
private val rssRepository: RssRepository,
private val rssHelper: RssHelper,
private val stringsRepository: StringsRepository,
@DispatcherIO
private val dispatcherIO: CoroutineDispatcher,
) : ViewModel() {
private val _subscribeUiState = MutableStateFlow(SubscribeUiState())
val subscribeUiState: StateFlow<SubscribeUiState> = _subscribeUiState.asStateFlow()
@ -55,7 +50,7 @@ class SubscribeViewModel @Inject constructor(
}
fun importFromInputStream(inputStream: InputStream) {
viewModelScope.launch(dispatcherIO) {
viewModelScope.launch {
try {
opmlRepository.saveToDatabase(inputStream)
rssRepository.get().doSync()
@ -68,13 +63,10 @@ class SubscribeViewModel @Inject constructor(
fun subscribe() {
val feed = _subscribeUiState.value.feed ?: return
val articles = _subscribeUiState.value.articles
viewModelScope.launch(dispatcherIO) {
val groupId = async {
_subscribeUiState.value.selectedGroupId
}
viewModelScope.launch {
rssRepository.get().subscribe(
feed.copy(
groupId = groupId.await(),
groupId = _subscribeUiState.value.selectedGroupId,
isNotification = _subscribeUiState.value.allowNotificationPreset,
isFullContent = _subscribeUiState.value.parseFullContentPreset,
), articles
@ -123,7 +115,7 @@ class SubscribeViewModel @Inject constructor(
fun search() {
searchJob?.cancel()
viewModelScope.launch(dispatcherIO) {
viewModelScope.launch {
try {
_subscribeUiState.update {
it.copy(

View File

@ -2,7 +2,6 @@ package me.ash.reader.ui.page.home.flow
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Star
import androidx.compose.material3.Icon
@ -23,10 +22,10 @@ import coil.size.Scale
import me.ash.reader.R
import me.ash.reader.data.entity.ArticleWithFeed
import me.ash.reader.data.preference.*
import me.ash.reader.ui.component.base.RYAsyncImage
import me.ash.reader.ui.ext.formatAsString
import me.ash.reader.ui.component.FeedIcon
import me.ash.reader.ui.component.base.Size_1000
import me.ash.reader.ui.component.base.RYAsyncImage
import me.ash.reader.ui.component.base.SIZE_1000
import me.ash.reader.ui.theme.SHAPE_20
@Composable
fun ArticleItem(
@ -43,7 +42,7 @@ fun ArticleItem(
Column(
modifier = Modifier
.padding(horizontal = 12.dp)
.clip(RoundedCornerShape(20.dp))
.clip(SHAPE_20)
.clickable { onClick(articleWithFeed) }
.padding(horizontal = 12.dp, vertical = 12.dp)
.alpha(if (articleWithFeed.article.isStarred || articleWithFeed.article.isUnread) 1f else 0.5f),
@ -80,21 +79,20 @@ fun ArticleItem(
if (articleWithFeed.article.isStarred) {
Icon(
modifier = Modifier
.alpha(0.7f)
.size(14.dp)
.padding(end = 2.dp),
imageVector = Icons.Rounded.Star,
contentDescription = stringResource(R.string.starred),
tint = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f),
tint = MaterialTheme.colorScheme.outline,
)
}
// Date
Text(
text = articleWithFeed.article.date.formatAsString(
context,
onlyHourMinute = true
),
color = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f),
modifier = Modifier.alpha(0.7f),
text = articleWithFeed.article.dateString ?: "",
color = MaterialTheme.colorScheme.outline,
style = MaterialTheme.typography.labelMedium,
)
}
@ -128,8 +126,9 @@ fun ArticleItem(
// Description
if (articleListDesc.value && articleWithFeed.article.shortDescription.isNotBlank()) {
Text(
modifier = Modifier.alpha(0.7f),
text = articleWithFeed.article.shortDescription,
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.7f),
color = MaterialTheme.colorScheme.onSurfaceVariant,
style = MaterialTheme.typography.bodySmall,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
@ -143,11 +142,11 @@ fun ArticleItem(
modifier = Modifier
.padding(start = 10.dp)
.size(80.dp)
.clip(RoundedCornerShape(20.dp)),
.clip(SHAPE_20),
data = articleWithFeed.article.img,
scale = Scale.FILL,
precision = Precision.INEXACT,
size = Size_1000,
size = SIZE_1000,
contentScale = ContentScale.Crop,
)
}

View File

@ -13,8 +13,8 @@ import me.ash.reader.data.entity.ArticleWithFeed
@OptIn(ExperimentalFoundationApi::class)
fun LazyListScope.ArticleList(
pagingItems: LazyPagingItems<FlowItemView>,
articleListFeedIcon: Boolean,
articleListDateStickyHeader: Boolean,
isShowFeedIcon: Boolean,
isShowStickyHeader: Boolean,
articleListTonalElevation: Int,
onClick: (ArticleWithFeed) -> Unit = {},
) {
@ -31,13 +31,13 @@ fun LazyListScope.ArticleList(
}
is FlowItemView.Date -> {
if (item.showSpacer) item { Spacer(modifier = Modifier.height(40.dp)) }
if (articleListDateStickyHeader) {
if (isShowStickyHeader) {
stickyHeader(key = item.date) {
StickyHeader(item.date, articleListFeedIcon, articleListTonalElevation)
StickyHeader(item.date, isShowFeedIcon, articleListTonalElevation)
}
} else {
item(key = item.date) {
StickyHeader(item.date, articleListFeedIcon, articleListTonalElevation)
StickyHeader(item.date, isShowFeedIcon, articleListTonalElevation)
}
}
}

View File

@ -33,7 +33,6 @@ import me.ash.reader.ui.component.base.RYScaffold
import me.ash.reader.ui.component.base.SwipeRefresh
import me.ash.reader.ui.ext.collectAsStateValue
import me.ash.reader.ui.page.common.RouteName
import me.ash.reader.ui.page.home.FilterState
import me.ash.reader.ui.page.home.HomeViewModel
@OptIn(
@ -165,7 +164,15 @@ fun FlowPage(
state = listState,
) {
item {
DisplayTextHeader(filterUiState, isSyncing, articleListFeedIcon.value)
DisplayText(
modifier = Modifier.padding(start = if (articleListFeedIcon.value) 30.dp else 0.dp),
text = when {
filterUiState.group != null -> filterUiState.group.name
filterUiState.feed != null -> filterUiState.feed.name
else -> filterUiState.filter.getName()
},
desc = if (isSyncing) stringResource(R.string.syncing) else "",
)
RYExtensibleVisibility(visible = markAsRead) {
Spacer(modifier = Modifier.height((56 + 24 + 10).dp))
}
@ -217,8 +224,8 @@ fun FlowPage(
}
ArticleList(
pagingItems = pagingItems,
articleListFeedIcon = articleListFeedIcon.value,
articleListDateStickyHeader = articleListDateStickyHeader.value,
isShowFeedIcon = articleListFeedIcon.value,
isShowStickyHeader = articleListDateStickyHeader.value,
articleListTonalElevation = articleListTonalElevation.value,
) {
onSearch = false
@ -248,20 +255,3 @@ fun FlowPage(
}
)
}
@Composable
private fun DisplayTextHeader(
filterState: FilterState,
isSyncing: Boolean,
articleListFeedIcon: Boolean,
) {
DisplayText(
modifier = Modifier.padding(start = if (articleListFeedIcon) 30.dp else 0.dp),
text = when {
filterState.group != null -> filterState.group.name
filterState.feed != null -> filterState.feed.name
else -> filterState.filter.getName()
},
desc = if (isSyncing) stringResource(R.string.syncing) else "",
)
}

View File

@ -11,7 +11,6 @@ import kotlinx.coroutines.launch
import me.ash.reader.data.entity.ArticleWithFeed
import me.ash.reader.data.repository.RssRepository
import java.util.*
import javax.annotation.concurrent.Immutable
import javax.inject.Inject
@HiltViewModel
@ -77,10 +76,7 @@ enum class MarkAsReadBefore {
All,
}
@Immutable
sealed class FlowItemView {
@Immutable
class Article(val articleWithFeed: ArticleWithFeed) : FlowItemView()
@Immutable
class Date(val date: String, val showSpacer: Boolean) : FlowItemView()
}

View File

@ -12,19 +12,19 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.style.BaselineShift
import androidx.compose.ui.unit.dp
import me.ash.reader.R
import me.ash.reader.data.constant.ElevationTokens
@Composable
fun SearchBar(
modifier: Modifier = Modifier,
value: String,
placeholder: String = "",
focusRequester: FocusRequester = remember { FocusRequester() },
@ -39,7 +39,7 @@ fun SearchBar(
.padding(horizontal = 24.dp)
.fillMaxWidth(),
shape = CircleShape,
tonalElevation = 3.dp
tonalElevation = ElevationTokens.Level2.dp
) {
Row(
modifier = Modifier.fillMaxSize(),
@ -62,6 +62,7 @@ fun SearchBar(
.fillMaxWidth()
.focusRequester(focusRequester),
colors = TextFieldDefaults.textFieldColors(
textColor = MaterialTheme.colorScheme.onSurfaceVariant,
containerColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
@ -70,17 +71,13 @@ fun SearchBar(
onValueChange = { onValueChange(it) },
placeholder = {
Text(
modifier = Modifier.alpha(0.7f),
text = placeholder,
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(
alpha = 0.7f
),
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
},
textStyle = MaterialTheme.typography.bodyLarge.copy(
color = MaterialTheme.colorScheme.onSurfaceVariant,
baselineShift = BaselineShift(0.1f)
),
textStyle = MaterialTheme.typography.bodyLarge,
singleLine = true,
keyboardOptions = KeyboardOptions(
imeAction = ImeAction.Done

View File

@ -15,8 +15,8 @@ import me.ash.reader.ui.theme.palette.onDark
@Composable
fun StickyHeader(
currentItemDay: String,
articleListFeedIcon: Boolean,
dateString: String,
isShowFeedIcon: Boolean,
articleListTonalElevation: Int,
) {
Row(
@ -30,10 +30,10 @@ fun StickyHeader(
) {
Text(
modifier = Modifier.padding(
start = if (articleListFeedIcon) 54.dp else 24.dp,
start = if (isShowFeedIcon) 54.dp else 24.dp,
bottom = 4.dp
),
text = currentItemDay,
text = dateString,
color = MaterialTheme.colorScheme.primary,
style = MaterialTheme.typography.labelLarge,
)

View File

@ -18,7 +18,7 @@ import javax.inject.Inject
@HiltViewModel
class ReadingViewModel @Inject constructor(
val rssRepository: RssRepository,
private val rssRepository: RssRepository,
private val rssHelper: RssHelper,
) : ViewModel() {
private val _readingUiState = MutableStateFlow(ReadingUiState())

View File

@ -0,0 +1,15 @@
package me.ash.reader.ui.theme
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Shapes
import androidx.compose.ui.unit.dp
val Shapes = Shapes(
extraSmall = RoundedCornerShape(4.0.dp),
small = RoundedCornerShape(8.0.dp),
medium = RoundedCornerShape(12.0.dp),
large = RoundedCornerShape(16.0.dp),
extraLarge = RoundedCornerShape(28.0.dp)
)
val SHAPE_20 = RoundedCornerShape(20.0.dp)

View File

@ -40,7 +40,8 @@ fun AppTheme(
if (useDarkTheme) dynamicDarkColorScheme()
else dynamicLightColorScheme(),
typography = AppTypography,
content = content
shapes = Shapes,
content = content,
)
}
}

View File

@ -5,5 +5,6 @@
android:viewportHeight="24">
<path
android:pathData="M19,3L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM19,19L5,19v-4.58l0.99,0.99 4,-4 4,4 4,-3.99L19,12.43L19,19zM19,9.59l-1.01,-1.01 -4,4.01 -4,-4 -4,4 -0.99,-1L5,5h14v4.59z"
android:fillColor="#000000"/>
android:fillColor="#000000"
android:fillAlpha="0.3"/>
</vector>

View File

@ -5,5 +5,6 @@
android:viewportHeight="24">
<path
android:pathData="M8,2c-1.1,0 -2,0.9 -2,2v3.17c0,0.53 0.21,1.04 0.59,1.42L10,12l-3.42,3.42c-0.37,0.38 -0.58,0.89 -0.58,1.42L6,20c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2v-3.16c0,-0.53 -0.21,-1.04 -0.58,-1.41L14,12l3.41,-3.4c0.38,-0.38 0.59,-0.89 0.59,-1.42L18,4c0,-1.1 -0.9,-2 -2,-2L8,2zM16,16.5L16,19c0,0.55 -0.45,1 -1,1L9,20c-0.55,0 -1,-0.45 -1,-1v-2.5l4,-4 4,4zM12,11.5l-4,-4L8,5c0,-0.55 0.45,-1 1,-1h6c0.55,0 1,0.45 1,1v2.5l-4,4z"
android:fillColor="#000000"/>
android:fillColor="#000000"
android:fillAlpha="0.3"/>
</vector>