diff --git a/app/build.gradle b/app/build.gradle index a6ba043..d5ab52a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -108,6 +108,7 @@ dependencies { implementation "com.rometools:rome:$rome" // https://coil-kt.github.io/coil/changelog/ + implementation("io.coil-kt:coil-base:$coil") implementation("io.coil-kt:coil-compose:$coil") implementation("io.coil-kt:coil-svg:$coil") implementation("io.coil-kt:coil-gif:$coil") diff --git a/app/src/main/java/me/ash/reader/App.kt b/app/src/main/java/me/ash/reader/App.kt index 7d75823..0e463be 100644 --- a/app/src/main/java/me/ash/reader/App.kt +++ b/app/src/main/java/me/ash/reader/App.kt @@ -1,24 +1,11 @@ package me.ash.reader import android.app.Application -import android.graphics.Color -import android.graphics.drawable.ColorDrawable -import android.graphics.drawable.Drawable -import android.os.Build import androidx.hilt.work.HiltWorkerFactory import androidx.work.Configuration import androidx.work.WorkManager -import coil.ComponentRegistry import coil.ImageLoader -import coil.decode.DataSource -import coil.decode.GifDecoder -import coil.decode.ImageDecoderDecoder -import coil.decode.SvgDecoder -import coil.disk.DiskCache -import coil.memory.MemoryCache -import coil.request.* import dagger.hilt.android.HiltAndroidApp -import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -29,12 +16,16 @@ import me.ash.reader.data.source.AppNetworkDataSource import me.ash.reader.data.source.OpmlLocalDataSource import me.ash.reader.data.source.ReaderDatabase import me.ash.reader.ui.ext.* +import okhttp3.Cache +import okhttp3.OkHttpClient import org.conscrypt.Conscrypt +import java.io.File import java.security.Security +import java.util.concurrent.TimeUnit import javax.inject.Inject @HiltAndroidApp -class App : Application(), Configuration.Provider, ImageLoader { +class App : Application(), Configuration.Provider { init { // From: https://gitlab.com/spacecowboy/Feeder // Install Conscrypt to handle TLSv1.3 pre Android10 @@ -88,6 +79,9 @@ class App : Application(), Configuration.Provider, ImageLoader { @DispatcherDefault lateinit var dispatcherDefault: CoroutineDispatcher + @Inject + lateinit var imageLoader: ImageLoader + override fun onCreate() { super.onCreate() CrashHandler(this) @@ -130,58 +124,29 @@ class App : Application(), Configuration.Provider, ImageLoader { .setWorkerFactory(workerFactory) .setMinimumLoggingLevel(android.util.Log.DEBUG) .build() +} - override val components: ComponentRegistry - get() = ComponentRegistry.Builder() - .add(SvgDecoder.Factory()) - .add( - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - ImageDecoderDecoder.Factory() - } else { - GifDecoder.Factory() - } - ) - .build() - override val defaults: DefaultRequestOptions - get() = DefaultRequestOptions() - override val diskCache: DiskCache - get() = DiskCache.Builder() - .directory(cacheDir.resolve("images")) - .maxSizePercent(0.02) - .build() - override val memoryCache: MemoryCache - get() = MemoryCache.Builder(this) - .maxSizePercent(0.25) - .build() +fun cachingHttpClient( + cacheDirectory: File? = null, + cacheSize: Long = 10L * 1024L * 1024L, + trustAllCerts: Boolean = true, + connectTimeoutSecs: Long = 30L, + readTimeoutSecs: Long = 30L +): OkHttpClient { + val builder: OkHttpClient.Builder = OkHttpClient.Builder() - override fun enqueue(request: ImageRequest): Disposable { - // Always call onStart before onSuccess. - request.target?.onStart(request.placeholder) - val result = ColorDrawable(Color.BLACK) - request.target?.onSuccess(result) - return object : Disposable { - override val job = CompletableDeferred(newResult(request, result)) - override val isDisposed get() = true - override fun dispose() {} - } + if (cacheDirectory != null) { + builder.cache(Cache(cacheDirectory, cacheSize)) } - override suspend fun execute(request: ImageRequest): ImageResult { - return newResult(request, ColorDrawable(Color.BLACK)) - } + builder + .connectTimeout(connectTimeoutSecs, TimeUnit.SECONDS) + .readTimeout(readTimeoutSecs, TimeUnit.SECONDS) + .followRedirects(true) - override fun newBuilder(): ImageLoader.Builder { - throw UnsupportedOperationException() - } +// if (trustAllCerts) { +// builder.trustAllCerts() +// } - override fun shutdown() { - } - - private fun newResult(request: ImageRequest, drawable: Drawable): SuccessResult { - return SuccessResult( - drawable = drawable, - request = request, - dataSource = DataSource.MEMORY_CACHE - ) - } + return builder.build() } \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/MainActivity.kt b/app/src/main/java/me/ash/reader/MainActivity.kt index cc9c75f..d85979e 100644 --- a/app/src/main/java/me/ash/reader/MainActivity.kt +++ b/app/src/main/java/me/ash/reader/MainActivity.kt @@ -4,16 +4,22 @@ import android.os.Bundle import android.util.Log import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.compose.runtime.CompositionLocalProvider import androidx.core.view.WindowCompat import androidx.profileinstaller.ProfileInstallerInitializer +import coil.ImageLoader +import coil.compose.LocalImageLoader import dagger.hilt.android.AndroidEntryPoint import me.ash.reader.data.preference.LanguagesPreference import me.ash.reader.data.preference.SettingsProvider import me.ash.reader.ui.ext.languages import me.ash.reader.ui.page.common.HomeEntry +import javax.inject.Inject @AndroidEntryPoint class MainActivity : ComponentActivity() { + @Inject + lateinit var imageLoader: ImageLoader override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -27,8 +33,12 @@ class MainActivity : ComponentActivity() { } setContent { - SettingsProvider { - HomeEntry() + CompositionLocalProvider( + LocalImageLoader provides imageLoader, + ) { + SettingsProvider { + HomeEntry() + } } } } diff --git a/app/src/main/java/me/ash/reader/data/entity/Article.kt b/app/src/main/java/me/ash/reader/data/entity/Article.kt index 42845e0..c358d2b 100644 --- a/app/src/main/java/me/ash/reader/data/entity/Article.kt +++ b/app/src/main/java/me/ash/reader/data/entity/Article.kt @@ -32,7 +32,7 @@ data class Article( @ColumnInfo var fullContent: String? = null, @ColumnInfo - var img: String? = null, + val img: String? = null, @ColumnInfo val link: String, @ColumnInfo(index = true) diff --git a/app/src/main/java/me/ash/reader/data/module/ImageLoaderModule.kt b/app/src/main/java/me/ash/reader/data/module/ImageLoaderModule.kt new file mode 100644 index 0000000..bd79879 --- /dev/null +++ b/app/src/main/java/me/ash/reader/data/module/ImageLoaderModule.kt @@ -0,0 +1,62 @@ +package me.ash.reader.data.module + +import android.content.Context +import android.os.Build +import android.os.Build.VERSION.SDK_INT +import coil.ImageLoader +import coil.decode.GifDecoder +import coil.decode.ImageDecoderDecoder +import coil.decode.SvgDecoder +import coil.disk.DiskCache +import coil.memory.MemoryCache +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import kotlinx.coroutines.Dispatchers +import me.ash.reader.cachingHttpClient +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object ImageLoaderModule { + + @Provides + @Singleton + fun provideImageLoader( + @ApplicationContext context: Context + ): ImageLoader { + return ImageLoader.Builder(context) + .okHttpClient( + okHttpClient = cachingHttpClient( + cacheDirectory = context.cacheDir.resolve("http") + ).newBuilder() + //.addNetworkInterceptor(UserAgentInterceptor) + .build() + ) + .dispatcher(Dispatchers.Default) // This slightly improves scrolling performance + .components{ + add(SvgDecoder.Factory()) + add( + if (SDK_INT >= Build.VERSION_CODES.P) { + ImageDecoderDecoder.Factory() + } else { + GifDecoder.Factory() + } + ) + } + .diskCache( + DiskCache.Builder() + .directory(context.cacheDir.resolve("images")) + .maxSizePercent(0.02) + .build() + ) + .memoryCache( + MemoryCache.Builder(context) + .maxSizePercent(0.25) + .build() + ) + .build() + } +} \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/data/repository/RssHelper.kt b/app/src/main/java/me/ash/reader/data/repository/RssHelper.kt index 149ec8c..8cbeaa1 100644 --- a/app/src/main/java/me/ash/reader/data/repository/RssHelper.kt +++ b/app/src/main/java/me/ash/reader/data/repository/RssHelper.kt @@ -115,10 +115,9 @@ class RssHelper @Inject constructor( .take(100) .trim(), fullContent = content, + img = findImg((desc ?: content) ?: ""), link = it.link ?: "", - ).apply { - img = findImg(rawDescription) - } + ) ) } a @@ -130,7 +129,8 @@ class RssHelper @Inject constructor( // Using negative lookahead to skip data: urls, being inline base64 // And capturing original quote to use as ending quote val regex = """img.*?src=(["'])((?!data).*?)\1""".toRegex(RegexOption.DOT_MATCHES_ALL) - return regex.find(rawDescription)?.groupValues?.get(2) + // Base64 encoded images can be quite large - and crash database cursors + return regex.find(rawDescription)?.groupValues?.get(2)?.takeIf { !it.startsWith("data:") } } @Throws(Exception::class) diff --git a/app/src/main/java/me/ash/reader/ui/component/AsyncImage.kt b/app/src/main/java/me/ash/reader/ui/component/AsyncImage.kt index fd9a2fc..8689c2f 100644 --- a/app/src/main/java/me/ash/reader/ui/component/AsyncImage.kt +++ b/app/src/main/java/me/ash/reader/ui/component/AsyncImage.kt @@ -3,9 +3,7 @@ package me.ash.reader.ui.component import androidx.annotation.DrawableRes import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember +import androidx.compose.runtime.Immutable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.DefaultAlpha @@ -14,7 +12,7 @@ import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource -import coil.imageLoader +import coil.compose.LocalImageLoader import coil.request.ImageRequest import coil.size.Precision import coil.size.Scale @@ -33,37 +31,10 @@ fun AsyncImage( @DrawableRes placeholder: Int? = R.drawable.ic_hourglass_empty_black_24dp, @DrawableRes error: Int? = R.drawable.ic_broken_image_black_24dp, ) { - val context = LocalContext.current - val color = MaterialTheme.colorScheme.onSurfaceVariant - val placeholderPainterResource = placeholder?.run { painterResource(this) } - val errorPainterResource = error?.run { painterResource(this) } - val placeholderPainter by remember { - mutableStateOf( - placeholderPainterResource?.run { - forwardingPainter( - painter = this, - colorFilter = ColorFilter.tint(color), - alpha = 0.1f, - ) - } - ) - } - val errorPainter by remember { - mutableStateOf( - errorPainterResource?.run { - forwardingPainter( - painter = this, - colorFilter = ColorFilter.tint(color), - alpha = 0.1f, - ) - } - ) - } - coil.compose.AsyncImage( modifier = modifier, model = ImageRequest - .Builder(context) + .Builder(LocalContext.current) .data(data) .crossfade(true) .scale(scale) @@ -72,9 +43,21 @@ fun AsyncImage( .build(), contentDescription = contentDescription, contentScale = contentScale, - imageLoader = context.imageLoader, - placeholder = placeholderPainter, - error = errorPainter, + imageLoader = LocalImageLoader.current, + placeholder = placeholder?.run { + forwardingPainter( + painter = painterResource(this), + colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onSurfaceVariant), + alpha = 0.1f, + ) + }, + error = error?.run { + forwardingPainter( + painter = painterResource(this), + colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onSurfaceVariant), + alpha = 0.1f, + ) + }, ) } @@ -90,12 +73,14 @@ fun forwardingPainter( onDraw: DrawScope.(ForwardingDrawInfo) -> Unit = DefaultOnDraw, ): Painter = ForwardingPainter(painter, alpha, colorFilter, onDraw) +@Immutable data class ForwardingDrawInfo( val painter: Painter, val alpha: Float, val colorFilter: ColorFilter?, ) +@Immutable private class ForwardingPainter( private val painter: Painter, private var alpha: Float, diff --git a/app/src/main/java/me/ash/reader/ui/page/home/FeedIcon.kt b/app/src/main/java/me/ash/reader/ui/page/home/FeedIcon.kt new file mode 100644 index 0000000..e52288c --- /dev/null +++ b/app/src/main/java/me/ash/reader/ui/page/home/FeedIcon.kt @@ -0,0 +1,45 @@ +package me.ash.reader.ui.page.home + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp + +@Composable +fun FeedIcon( + feedName: String, + size: Dp = 20.dp +) { + Row( + modifier = Modifier + .size(20.dp) + .clip(CircleShape) + .background(MaterialTheme.colorScheme.outline.copy(alpha = 0.2f)) + ) {} + +// val url by remember { +// mutableStateOf( +// "https://ui-avatars.com/api/?length=1&background=random&name=${ +// URLEncoder.encode( +// feedName, +// Charsets.UTF_8.toString() +// ) +// }" +// ) +// } +// +// AsyncImage( +// modifier = Modifier +// .size(size) +// .clip(CircleShape), +// contentDescription = feedName, +// data = url, +// placeholder = null, +// ) +} diff --git a/app/src/main/java/me/ash/reader/ui/page/home/HomeViewModel.kt b/app/src/main/java/me/ash/reader/ui/page/home/HomeViewModel.kt index 219ab62..9149e23 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/HomeViewModel.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/HomeViewModel.kt @@ -60,7 +60,7 @@ class HomeViewModel @Inject constructor( private fun fetchArticles() { _viewState.update { it.copy( - pagingData = Pager(PagingConfig(pageSize = 15)) { + pagingData = Pager(PagingConfig(pageSize = 50)) { if (_viewState.value.searchContent.isNotBlank()) { rssRepository.get().searchArticles( content = _viewState.value.searchContent.trim(), diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedItem.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedItem.kt index 87284a2..43e1814 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedItem.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedItem.kt @@ -1,16 +1,16 @@ package me.ash.reader.ui.page.home.feeds import android.view.HapticFeedbackConstants -import androidx.compose.foundation.background import androidx.compose.foundation.combinedClickable -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Badge import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -20,6 +20,7 @@ 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.FeedIcon 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 @@ -30,7 +31,6 @@ import kotlin.math.ln ) @Composable fun FeedItem( - modifier: Modifier = Modifier, feed: Feed, feedOptionViewModel: FeedOptionViewModel = hiltViewModel(), tonalElevation: Dp, @@ -38,6 +38,11 @@ fun FeedItem( ) { val view = LocalView.current val scope = rememberCoroutineScope() + val tonalElevationAlpha by remember { + derivedStateOf { + (ln(tonalElevation.value + 1.4f) + 2f) / 100f + } + } Row( modifier = Modifier @@ -63,12 +68,7 @@ fun FeedItem( verticalAlignment = Alignment.CenterVertically, ) { Row(modifier = Modifier.weight(1f)) { - Row( - modifier = Modifier - .size(20.dp) - .clip(CircleShape) - .background(MaterialTheme.colorScheme.outline.copy(alpha = 0.2f)) - ) {} + FeedIcon(feed.name) Text( modifier = Modifier.padding(start = 12.dp, end = 6.dp), text = feed.name, @@ -78,10 +78,10 @@ fun FeedItem( overflow = TextOverflow.Ellipsis, ) } - if (feed.important ?: 0 != 0) { + if ((feed.important ?: 0) != 0) { Badge( containerColor = MaterialTheme.colorScheme.surfaceTint.copy( - alpha = (ln(tonalElevation.value + 1.4f) + 2f) / 100f + alpha = tonalElevationAlpha ), contentColor = MaterialTheme.colorScheme.outline, content = { diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/GroupItem.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/GroupItem.kt index 3563a00..9d6559f 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/GroupItem.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/GroupItem.kt @@ -8,7 +8,6 @@ import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.ExpandLess import androidx.compose.material.icons.rounded.ExpandMore @@ -32,7 +31,7 @@ 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 -@OptIn(ExperimentalMaterialApi::class, androidx.compose.foundation.ExperimentalFoundationApi::class) +@OptIn(androidx.compose.foundation.ExperimentalFoundationApi::class) @Composable fun GroupItem( modifier: Modifier = Modifier, @@ -114,7 +113,6 @@ fun GroupItem( Column { feeds.forEach { feed -> FeedItem( - modifier = Modifier.padding(horizontal = 20.dp), feed = feed, tonalElevation = tonalElevation, ) { diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/feed/FeedOptionDrawer.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/feed/FeedOptionDrawer.kt index 4fd85e0..843b495 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/feed/FeedOptionDrawer.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/feed/FeedOptionDrawer.kt @@ -6,8 +6,6 @@ import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.CreateNewFolder import androidx.compose.material.icons.outlined.Edit -import androidx.compose.material.icons.rounded.RssFeed -import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -26,6 +24,7 @@ import me.ash.reader.ui.component.TextFieldDialog import me.ash.reader.ui.ext.collectAsStateValue import me.ash.reader.ui.ext.roundClick import me.ash.reader.ui.ext.showToast +import me.ash.reader.ui.page.home.FeedIcon import me.ash.reader.ui.page.home.feeds.subscribe.ResultView @OptIn(ExperimentalMaterialApi::class) @@ -56,12 +55,13 @@ fun FeedOptionDrawer( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { - Icon( - modifier = Modifier.roundClick { }, - imageVector = Icons.Rounded.RssFeed, - contentDescription = feed?.name ?: stringResource(R.string.unknown), - tint = MaterialTheme.colorScheme.secondary, - ) + FeedIcon(feedName = feed?.name ?: "", size = 24.dp) +// Icon( +// modifier = Modifier.roundClick { }, +// imageVector = Icons.Rounded.RssFeed, +// contentDescription = feed?.name ?: stringResource(R.string.unknown), +// tint = MaterialTheme.colorScheme.secondary, +// ) Spacer(modifier = Modifier.height(16.dp)) Text( modifier = Modifier.roundClick { diff --git a/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleItem.kt b/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleItem.kt index 752b64f..8b8a61e 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleItem.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleItem.kt @@ -1,9 +1,7 @@ package me.ash.reader.ui.page.home.flow -import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.Star @@ -26,10 +24,10 @@ import me.ash.reader.data.entity.ArticleWithFeed import me.ash.reader.data.preference.* import me.ash.reader.ui.component.AsyncImage import me.ash.reader.ui.ext.formatAsString +import me.ash.reader.ui.page.home.FeedIcon @Composable fun ArticleItem( - modifier: Modifier = Modifier, articleWithFeed: ArticleWithFeed, onClick: (ArticleWithFeed) -> Unit = {}, ) { @@ -107,12 +105,7 @@ fun ArticleItem( ) { // Feed icon if (articleListFeedIcon.value) { - Row( - modifier = Modifier - .size(20.dp) - .clip(CircleShape) - .background(MaterialTheme.colorScheme.outline.copy(alpha = 0.2f)) - ) {} + FeedIcon(articleWithFeed.feed.name) Spacer(modifier = Modifier.width(10.dp)) } diff --git a/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleList.kt b/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleList.kt index f05ad97..c0aaf27 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleList.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleList.kt @@ -30,15 +30,14 @@ fun LazyListScope.ArticleList( } } is FlowItemView.Date -> { - val separator = pagingItems[index] as FlowItemView.Date - if (separator.showSpacer) item { Spacer(modifier = Modifier.height(40.dp)) } + if (item.showSpacer) item { Spacer(modifier = Modifier.height(40.dp)) } if (articleListDateStickyHeader) { - stickyHeader(key = separator.date) { - StickyHeader(separator.date, articleListFeedIcon, articleListTonalElevation) + stickyHeader(key = item.date) { + StickyHeader(item.date, articleListFeedIcon, articleListTonalElevation) } } else { - item(key = separator.date) { - StickyHeader(separator.date, articleListFeedIcon, articleListTonalElevation) + item(key = item.date) { + StickyHeader(item.date, articleListFeedIcon, articleListTonalElevation) } } } diff --git a/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowViewModel.kt b/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowViewModel.kt index c115dac..257609b 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowViewModel.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowViewModel.kt @@ -12,6 +12,7 @@ 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 @@ -116,7 +117,10 @@ 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() } \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/ui/page/home/read/ReadPage.kt b/app/src/main/java/me/ash/reader/ui/page/home/read/ReadPage.kt index 05ac6e2..940819a 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/read/ReadPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/read/ReadPage.kt @@ -1,6 +1,7 @@ package me.ash.reader.ui.page.home.read import android.content.Intent +import android.util.Log import androidx.compose.animation.* import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn @@ -35,7 +36,6 @@ fun ReadPage( ) { val viewState = readViewModel.viewState.collectAsStateValue() val isScrollDown = viewState.listState.isScrollDown() -// val isScrollDown by remember { mutableStateOf(false) } LaunchedEffect(Unit) { navController.currentBackStackEntryFlow.collect { @@ -46,6 +46,7 @@ fun ReadPage( } LaunchedEffect(viewState.articleWithFeed?.article?.id) { + Log.i("RLog", "ReadPage: ${viewState.articleWithFeed}") viewState.articleWithFeed?.let { if (it.article.isUnread) { readViewModel.dispatch(ReadViewAction.MarkUnread(false))