diff --git a/app/src/main/java/me/ash/reader/data/repository/StringsRepository.kt b/app/src/main/java/me/ash/reader/data/repository/StringsRepository.kt index 0d64845..0019e54 100644 --- a/app/src/main/java/me/ash/reader/data/repository/StringsRepository.kt +++ b/app/src/main/java/me/ash/reader/data/repository/StringsRepository.kt @@ -2,6 +2,8 @@ package me.ash.reader.data.repository import android.content.Context import dagger.hilt.android.qualifiers.ApplicationContext +import me.ash.reader.ui.ext.formatAsString +import java.util.* import javax.inject.Inject class StringsRepository @Inject constructor( @@ -9,4 +11,5 @@ class StringsRepository @Inject constructor( private val context: Context, ) { fun getString(resId: Int) = context.getString(resId) + fun formatAsString(date: Date?) = date?.formatAsString(context) } diff --git a/app/src/main/java/me/ash/reader/ui/component/SwipeRefresh.kt b/app/src/main/java/me/ash/reader/ui/component/SwipeRefresh.kt new file mode 100644 index 0000000..0664f3a --- /dev/null +++ b/app/src/main/java/me/ash/reader/ui/component/SwipeRefresh.kt @@ -0,0 +1,31 @@ +package me.ash.reader.ui.component + +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import com.google.accompanist.swiperefresh.SwipeRefreshIndicator +import com.google.accompanist.swiperefresh.rememberSwipeRefreshState +import me.ash.reader.ui.theme.palette.onDark + +@Composable +fun SwipeRefresh( + isRefresh: Boolean = false, + onRefresh: () -> Unit = {}, + content: @Composable () -> Unit = {}, +) { + com.google.accompanist.swiperefresh.SwipeRefresh( + state = rememberSwipeRefreshState(isRefresh), + onRefresh = onRefresh, + indicator = { state, trigger -> + SwipeRefreshIndicator( + state = state, + refreshTriggerDistance = trigger, + fade = true, + scale = true, + contentColor = MaterialTheme.colorScheme.primary, + backgroundColor = MaterialTheme.colorScheme.surface onDark MaterialTheme.colorScheme.surfaceVariant, + ) + } + ) { + content() + } +} \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/ui/component/WebView.kt b/app/src/main/java/me/ash/reader/ui/component/WebView.kt index 33cebdd..acdb97b 100644 --- a/app/src/main/java/me/ash/reader/ui/component/WebView.kt +++ b/app/src/main/java/me/ash/reader/ui/component/WebView.kt @@ -1,7 +1,6 @@ package me.ash.reader.ui.component import android.content.Intent -import android.graphics.Bitmap import android.net.Uri import android.net.http.SslError import android.util.Log @@ -15,9 +14,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.viewinterop.AndroidView -import androidx.hilt.navigation.compose.hiltViewModel -import me.ash.reader.ui.page.home.read.ReadViewAction -import me.ash.reader.ui.page.home.read.ReadViewModel const val INJECTION_TOKEN = "/android_asset_font/" @@ -25,8 +21,6 @@ const val INJECTION_TOKEN = "/android_asset_font/" fun WebView( modifier: Modifier = Modifier, content: String, - viewModel: ReadViewModel = hiltViewModel(), - onProgressChange: (progress: Int) -> Unit = {}, onReceivedError: (error: WebResourceError?) -> Unit = {} ) { val context = LocalContext.current @@ -57,16 +51,6 @@ fun WebView( return super.shouldInterceptRequest(view, url); } - override fun onPageStarted( - view: WebView?, - url: String?, - favicon: Bitmap? - ) { - super.onPageStarted(view, url, favicon) -// _isLoading = true - onProgressChange(-1) - } - override fun onPageFinished(view: WebView?, url: String?) { super.onPageFinished(view, url) val jsCode = "javascript:(function(){" + @@ -78,8 +62,6 @@ fun WebView( "alert('asf');" + "}}})()" view!!.loadUrl(jsCode) - viewModel.dispatch(ReadViewAction.ChangeLoading(false)) - onProgressChange(100) } override fun shouldOverrideUrlLoading( @@ -173,10 +155,12 @@ fun getStyle(argb: Int): String = """ padding: 0 24px; } -img { +img, video { margin: 0 -24px 20px; width: calc(100% + 48px); height: auto; + border-top: 1px solid ${argbToCssColor(argb)}08; + border-bottom: 1px solid ${argbToCssColor(argb)}08; } p,span,a,ol,ul,blockquote,article,section { diff --git a/app/src/main/java/me/ash/reader/ui/page/home/HomePage.kt b/app/src/main/java/me/ash/reader/ui/page/home/HomePage.kt index 2df204e..26b2de3 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/HomePage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/HomePage.kt @@ -12,7 +12,6 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavHostController import com.google.accompanist.insets.statusBarsPadding import com.google.accompanist.pager.ExperimentalPagerApi -import kotlinx.coroutines.launch import me.ash.reader.ui.component.ViewPager import me.ash.reader.ui.ext.collectAsStateValue import me.ash.reader.ui.ext.findActivity @@ -49,23 +48,10 @@ fun HomePage( LaunchedEffect(openArticleId) { if (openArticleId.isNotEmpty()) { - readViewModel.dispatch(ReadViewAction.ScrollToItem(2)) - launch { - val article = readViewModel - .rssRepository.get() - .findArticleById(openArticleId) ?: return@launch - readViewModel.dispatch(ReadViewAction.InitData(article)) - if (article.feed.isFullContent) readViewModel.dispatch(ReadViewAction.RenderFullContent) - else readViewModel.dispatch(ReadViewAction.RenderDescriptionContent) - readViewModel.dispatch(ReadViewAction.RenderDescriptionContent) - homeViewModel.dispatch( - HomeViewAction.ScrollToPage( - scope = scope, - targetPage = 2, - ) - ) - openArticleId = "" - } + readViewModel.dispatch(ReadViewAction.InitData(openArticleId)) + readViewModel.dispatch(ReadViewAction.ScrollToItem(0)) + homeViewModel.dispatch(HomeViewAction.ScrollToPage(scope, 2)) + openArticleId = "" } } @@ -142,7 +128,7 @@ fun HomePage( }, onItemClick = { readViewModel.dispatch(ReadViewAction.ScrollToItem(0)) - readViewModel.dispatch(ReadViewAction.InitData(it)) + readViewModel.dispatch(ReadViewAction.InitData(it.article.id)) if (it.feed.isFullContent) readViewModel.dispatch(ReadViewAction.RenderFullContent) else readViewModel.dispatch(ReadViewAction.RenderDescriptionContent) readViewModel.dispatch(ReadViewAction.RenderDescriptionContent) 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 1019d76..128dea5 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 @@ -22,7 +22,7 @@ import javax.inject.Inject @HiltViewModel class HomeViewModel @Inject constructor( private val rssRepository: RssRepository, - private val workManager: WorkManager, + workManager: WorkManager, ) : ViewModel() { private val _viewState = MutableStateFlow(HomeViewState()) 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 1b33079..2fa9e97 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 @@ -1,6 +1,5 @@ package me.ash.reader.ui.page.home.flow -import android.content.Context import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height @@ -9,33 +8,31 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.paging.compose.LazyPagingItems import me.ash.reader.data.entity.ArticleWithFeed -import me.ash.reader.ui.ext.formatAsString +@Suppress("FunctionName") @OptIn(ExperimentalFoundationApi::class) -fun LazyListScope.generateArticleList( - context: Context, - pagingItems: LazyPagingItems, +fun LazyListScope.ArticleList( + pagingItems: LazyPagingItems, onClick: (ArticleWithFeed) -> Unit = {}, ) { - var lastItemDay: String? = null for (itemIndex in 0 until pagingItems.itemCount) { - val currentItem = pagingItems.peek(itemIndex) ?: continue - val currentItemDay = currentItem.article.date.formatAsString(context) - if (lastItemDay != currentItemDay) { - if (itemIndex != 0) { - item { Spacer(modifier = Modifier.height(40.dp)) } + when (val item = pagingItems[itemIndex]) { + is FlowItemView.Article -> { + item { + ArticleItem( + articleWithFeed = item.articleWithFeed, + ) { + onClick(it) + } + } } - stickyHeader { - StickyHeader(currentItemDay) + is FlowItemView.Date -> { + if (itemIndex != 0) item { Spacer(modifier = Modifier.height(40.dp)) } + stickyHeader { + StickyHeader(item.date) + } } + else -> {} } - item { - ArticleItem( - articleWithFeed = pagingItems[itemIndex] ?: return@item, - ) { - onClick(it) - } - } - lastItemDay = currentItemDay } } \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt b/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt index f2cc714..7d869c2 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt @@ -17,7 +17,6 @@ import androidx.compose.material3.SmallTopAppBar import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.res.stringResource @@ -35,6 +34,7 @@ import me.ash.reader.data.entity.ArticleWithFeed import me.ash.reader.data.repository.SyncWorker.Companion.getIsSyncing import me.ash.reader.ui.component.DisplayText import me.ash.reader.ui.component.FeedbackIconButton +import me.ash.reader.ui.component.SwipeRefresh import me.ash.reader.ui.ext.collectAsStateValue import me.ash.reader.ui.ext.getName import me.ash.reader.ui.page.home.FilterBar @@ -56,7 +56,6 @@ fun FlowPage( onScrollToPage: (targetPage: Int) -> Unit = {}, onItemClick: (item: ArticleWithFeed) -> Unit = {}, ) { - val context = LocalContext.current val keyboardController = LocalSoftwareKeyboardController.current val focusRequester = remember { FocusRequester() } val scope = rememberCoroutineScope() @@ -168,90 +167,89 @@ fun FlowPage( // url = "https://assets7.lottiefiles.com/packages/lf20_l4ny0jjm.json", // ) // } - LazyColumn( - modifier = Modifier.fillMaxSize(), - state = viewState.listState, + SwipeRefresh( + onRefresh = { + if (!isSyncing) { + flowViewModel.dispatch(FlowViewAction.Sync) + } + } ) { - item { - DisplayText( - modifier = Modifier.padding(start = 30.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 "", - ) - AnimatedVisibility( - visible = markAsRead, - enter = fadeIn() + expandVertically(), - exit = fadeOut() + shrinkVertically(), - ) { - Spacer(modifier = Modifier.height((56 + 24 + 10).dp)) - } - MarkAsReadBar( - visible = markAsRead, - absoluteY = if (isSyncing) (4 + 16 + 180).dp else 180.dp, - onDismissRequest = { - markAsRead = false - }, - ) { - markAsRead = false - flowViewModel.dispatch( - FlowViewAction.MarkAsRead( - groupId = filterState.group?.id, - feedId = filterState.feed?.id, - articleId = null, - markAsReadBefore = it, - ) - ) - } - AnimatedVisibility( - visible = onSearch, - enter = fadeIn() + expandVertically(), - exit = fadeOut() + shrinkVertically(), - ) { - SearchBar( - value = viewState.searchContent, - placeholder = when { - filterState.group != null -> stringResource( - R.string.search_for_in, - filterState.filter.getName(), - filterState.group.name - ) - filterState.feed != null -> stringResource( - R.string.search_for_in, - filterState.filter.getName(), - filterState.feed.name - ) - else -> stringResource( - R.string.search_for, - filterState.filter.getName() - ) - }, - focusRequester = focusRequester, - onValueChange = { - flowViewModel.dispatch(FlowViewAction.InputSearchContent(it)) - }, - onClose = { - onSearch = false - flowViewModel.dispatch(FlowViewAction.InputSearchContent("")) - } - ) - Spacer(modifier = Modifier.height((56 + 24 + 10).dp)) - } - } - generateArticleList( - context = context, - pagingItems = pagingItems, + LazyColumn( + modifier = Modifier.fillMaxSize(), + state = viewState.listState, ) { - onSearch = false - onItemClick(it) - } - item { - Spacer(modifier = Modifier.height(64.dp)) - if (pagingItems.loadState.source.refresh is LoadState.NotLoading && pagingItems.itemCount != 0) { + item { + DisplayTextHeader(filterState, isSyncing) + AnimatedVisibility( + visible = markAsRead, + enter = fadeIn() + expandVertically(), + exit = fadeOut() + shrinkVertically(), + ) { + Spacer(modifier = Modifier.height((56 + 24 + 10).dp)) + } + MarkAsReadBar( + visible = markAsRead, + absoluteY = if (isSyncing) (4 + 16 + 180).dp else 180.dp, + onDismissRequest = { + markAsRead = false + }, + ) { + markAsRead = false + flowViewModel.dispatch( + FlowViewAction.MarkAsRead( + groupId = filterState.group?.id, + feedId = filterState.feed?.id, + articleId = null, + markAsReadBefore = it, + ) + ) + } + AnimatedVisibility( + visible = onSearch, + enter = fadeIn() + expandVertically(), + exit = fadeOut() + shrinkVertically(), + ) { + SearchBar( + value = viewState.searchContent, + placeholder = when { + filterState.group != null -> stringResource( + R.string.search_for_in, + filterState.filter.getName(), + filterState.group.name + ) + filterState.feed != null -> stringResource( + R.string.search_for_in, + filterState.filter.getName(), + filterState.feed.name + ) + else -> stringResource( + R.string.search_for, + filterState.filter.getName() + ) + }, + focusRequester = focusRequester, + onValueChange = { + flowViewModel.dispatch(FlowViewAction.InputSearchContent(it)) + }, + onClose = { + onSearch = false + flowViewModel.dispatch(FlowViewAction.InputSearchContent("")) + } + ) + Spacer(modifier = Modifier.height((56 + 24 + 10).dp)) + } + } + ArticleList( + pagingItems = pagingItems, + ) { + onSearch = false + onItemClick(it) + } + item { Spacer(modifier = Modifier.height(64.dp)) + if (pagingItems.loadState.source.refresh is LoadState.NotLoading && pagingItems.itemCount != 0) { + Spacer(modifier = Modifier.height(64.dp)) + } } } } @@ -268,4 +266,20 @@ fun FlowPage( ) } ) +} + +@Composable +private fun DisplayTextHeader( + filterState: FilterState, + isSyncing: Boolean +) { + DisplayText( + modifier = Modifier.padding(start = 30.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 "", + ) } \ No newline at end of file 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 9e6dfce..5b8d7ac 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 @@ -3,16 +3,13 @@ package me.ash.reader.ui.page.home.flow import androidx.compose.foundation.lazy.LazyListState import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import androidx.paging.Pager -import androidx.paging.PagingConfig -import androidx.paging.PagingData -import androidx.paging.cachedIn +import androidx.paging.* import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import me.ash.reader.data.entity.ArticleWithFeed import me.ash.reader.data.repository.RssRepository +import me.ash.reader.data.repository.StringsRepository import me.ash.reader.ui.page.home.FilterState import java.util.* import javax.inject.Inject @@ -20,14 +17,16 @@ import javax.inject.Inject @HiltViewModel class FlowViewModel @Inject constructor( private val rssRepository: RssRepository, + private val stringsRepository: StringsRepository, ) : ViewModel() { private val _viewState = MutableStateFlow(ArticleViewState()) val viewState: StateFlow = _viewState.asStateFlow() fun dispatch(action: FlowViewAction) { when (action) { + is FlowViewAction.Sync -> sync() is FlowViewAction.FetchData -> fetchData(action.filterState) - is FlowViewAction.ChangeRefreshing -> changeRefreshing(action.isRefreshing) + is FlowViewAction.ChangeIsBack -> changeIsBack(action.isBack) is FlowViewAction.ScrollToItem -> scrollToItem(action.index) is FlowViewAction.MarkAsRead -> markAsRead( action.groupId, @@ -39,6 +38,10 @@ class FlowViewModel @Inject constructor( } } + private fun sync() { + rssRepository.get().doSync() + } + private fun fetchData(filterState: FilterState? = null) { // viewModelScope.launch(Dispatchers.Default) { // rssRepository.get().pullImportant(filterState.filter.isStarred(), true) @@ -62,7 +65,21 @@ class FlowViewModel @Inject constructor( isStarred = _viewState.value.filterState?.filter?.isStarred() ?: false, isUnread = _viewState.value.filterState?.filter?.isUnread() ?: false, ) - }.flow.flowOn(Dispatchers.IO).cachedIn(viewModelScope) + }.flow.map { + it.map { + FlowItemView.Article(it) + }.insertSeparators { before, after -> + val beforeDate = + stringsRepository.formatAsString(before?.articleWithFeed?.article?.date) + val afterDate = + stringsRepository.formatAsString(after?.articleWithFeed?.article?.date) + if (beforeDate != afterDate) { + afterDate?.let { FlowItemView.Date(it) } + } else { + null + } + } + }.cachedIn(viewModelScope) ) } } else if (filterState != null) { @@ -76,7 +93,21 @@ class FlowViewModel @Inject constructor( isStarred = filterState.filter.isStarred(), isUnread = filterState.filter.isUnread(), ) - }.flow.flowOn(Dispatchers.IO).cachedIn(viewModelScope) + }.flow.map { + it.map { + FlowItemView.Article(it) + }.insertSeparators { before, after -> + val beforeDate = + stringsRepository.formatAsString(before?.articleWithFeed?.article?.date) + val afterDate = + stringsRepository.formatAsString(after?.articleWithFeed?.article?.date) + if (beforeDate != afterDate) { + afterDate?.let { FlowItemView.Date(it) } + } else { + null + } + } + }.cachedIn(viewModelScope) ) } } @@ -88,9 +119,9 @@ class FlowViewModel @Inject constructor( } } - private fun changeRefreshing(isRefreshing: Boolean) { + private fun changeIsBack(isBack: Boolean) { _viewState.update { - it.copy(isRefreshing = isRefreshing) + it.copy(isBack = isBack) } } @@ -139,19 +170,21 @@ data class ArticleViewState( val filterState: FilterState? = null, val filterImportant: Int = 0, val listState: LazyListState = LazyListState(), - val isRefreshing: Boolean = false, - val pagingData: Flow> = emptyFlow(), + val isBack: Boolean = false, + val pagingData: Flow> = emptyFlow(), val syncWorkInfo: String = "", val searchContent: String = "", ) sealed class FlowViewAction { + object Sync : FlowViewAction() + data class FetchData( val filterState: FilterState, ) : FlowViewAction() - data class ChangeRefreshing( - val isRefreshing: Boolean + data class ChangeIsBack( + val isBack: Boolean ) : FlowViewAction() data class ScrollToItem( @@ -175,4 +208,9 @@ enum class MarkAsReadBefore { ThreeDays, OneDay, All, +} + +sealed class FlowItemView { + class Article(val articleWithFeed: ArticleWithFeed) : FlowItemView() + class Date(val date: String) : FlowItemView() } \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/ui/page/home/flow/StickyHeader.kt b/app/src/main/java/me/ash/reader/ui/page/home/flow/StickyHeader.kt index 70fde2f..0ef4020 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/flow/StickyHeader.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/flow/StickyHeader.kt @@ -20,7 +20,8 @@ fun StickyHeader(currentItemDay: String) { verticalAlignment = Alignment.CenterVertically ) { Text( - modifier = Modifier.padding(start = if (true) 54.dp else 24.dp), + modifier = Modifier + .padding(start = if (true) 54.dp else 24.dp, bottom = 4.dp), text = currentItemDay, color = MaterialTheme.colorScheme.primary, style = MaterialTheme.typography.labelLarge, 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 4e48d3d..f1962a0 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 @@ -67,9 +67,6 @@ fun ReadPage( if (it.article.isUnread) { readViewModel.dispatch(ReadViewAction.MarkUnread(false)) } - if (it.feed.isFullContent) { - readViewModel.dispatch(ReadViewAction.RenderFullContent) - } } } @@ -96,6 +93,7 @@ fun ReadPage( Content( content = viewState.content ?: "", articleWithFeed = viewState.articleWithFeed, + viewState = viewState, LazyListState = viewState.listState, ) Box( @@ -156,7 +154,9 @@ private fun TopBar( actions = { if (isShowActions) { FeedbackIconButton( - modifier = Modifier.size(22.dp).alpha(0.5f), + modifier = Modifier + .size(22.dp) + .alpha(0.5f), imageVector = Icons.Outlined.Headphones, contentDescription = stringResource(R.string.mark_all_as_read), tint = MaterialTheme.colorScheme.onSurface, @@ -179,6 +179,7 @@ private fun TopBar( private fun Content( content: String, articleWithFeed: ArticleWithFeed?, + viewState: ReadViewState, LazyListState: LazyListState = rememberLazyListState(), ) { Column { @@ -208,7 +209,27 @@ private fun Content( } item { Spacer(modifier = Modifier.height(22.dp)) - Crossfade(targetState = content) { content -> + AnimatedVisibility( + visible = viewState.isLoading, + enter = fadeIn() + expandVertically(), + exit = fadeOut() + shrinkVertically(), + ) { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center, + ) { + Column { + Spacer(modifier = Modifier.height(22.dp)) + CircularProgressIndicator( + modifier = Modifier + .size(30.dp), + color = MaterialTheme.colorScheme.onSurface, + ) + Spacer(modifier = Modifier.height(22.dp)) + } + } + } + if (!viewState.isLoading) { WebView( content = content ) diff --git a/app/src/main/java/me/ash/reader/ui/page/home/read/ReadViewModel.kt b/app/src/main/java/me/ash/reader/ui/page/home/read/ReadViewModel.kt index 9d0aa4b..674fc2b 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/read/ReadViewModel.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/read/ReadViewModel.kt @@ -26,7 +26,7 @@ class ReadViewModel @Inject constructor( fun dispatch(action: ReadViewAction) { when (action) { - is ReadViewAction.InitData -> bindArticleWithFeed(action.articleWithFeed) + is ReadViewAction.InitData -> bindArticleWithFeed(action.articleId) is ReadViewAction.RenderDescriptionContent -> renderDescriptionContent() is ReadViewAction.RenderFullContent -> renderFullContent() is ReadViewAction.MarkUnread -> markUnread(action.isUnread) @@ -37,9 +37,17 @@ class ReadViewModel @Inject constructor( } } - private fun bindArticleWithFeed(articleWithFeed: ArticleWithFeed) { - _viewState.update { - it.copy(articleWithFeed = articleWithFeed) + private fun bindArticleWithFeed(articleId: String) { + changeLoading(true) + viewModelScope.launch { + _viewState.update { + it.copy(articleWithFeed = rssRepository.get().findArticleById(articleId)) + } + _viewState.value.articleWithFeed?.let { + if (it.feed.isFullContent) internalRenderFullContent() + else renderDescriptionContent() + } + changeLoading(false) } } @@ -55,26 +63,31 @@ class ReadViewModel @Inject constructor( } private fun renderFullContent() { - changeLoading(true) viewModelScope.launch { - try { - _viewState.update { - it.copy( - content = rssHelper.parseFullContent( - _viewState.value.articleWithFeed?.article?.link ?: "", - _viewState.value.articleWithFeed?.article?.title ?: "" - ) + internalRenderFullContent() + } + } + + private suspend fun internalRenderFullContent() { + changeLoading(true) + try { + _viewState.update { + it.copy( + content = rssHelper.parseFullContent( + _viewState.value.articleWithFeed?.article?.link ?: "", + _viewState.value.articleWithFeed?.article?.title ?: "" ) - } - } catch (e: Exception) { - Log.i("RLog", "renderFullContent: ${e.message}") - _viewState.update { - it.copy( - content = e.message - ) - } + ) + } + } catch (e: Exception) { + Log.i("RLog", "renderFullContent: ${e.message}") + _viewState.update { + it.copy( + content = e.message + ) } } + changeLoading(false) } private fun markUnread(isUnread: Boolean) { @@ -141,13 +154,13 @@ class ReadViewModel @Inject constructor( data class ReadViewState( val articleWithFeed: ArticleWithFeed? = null, val content: String? = null, - val isLoading: Boolean = false, + val isLoading: Boolean = true, val listState: LazyListState = LazyListState(), ) sealed class ReadViewAction { data class InitData( - val articleWithFeed: ArticleWithFeed, + val articleId: String, ) : ReadViewAction() object RenderDescriptionContent : ReadViewAction() diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/ColorAndStyle.kt b/app/src/main/java/me/ash/reader/ui/page/settings/ColorAndStyle.kt index 2b20a1f..54abfb9 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/ColorAndStyle.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/ColorAndStyle.kt @@ -191,8 +191,8 @@ fun Palettes( val context = LocalContext.current val scope = rememberCoroutineScope() val themeIndex = context.dataStore.data - .map { it[DataStoreKeys.ThemeIndex.key] ?: 0 } - .collectAsState(initial = 0).value + .map { it[DataStoreKeys.ThemeIndex.key] ?: 5 } + .collectAsState(initial = 5).value val customPrimaryColor = context.dataStore.data .map { it[DataStoreKeys.CustomPrimaryColor.key] ?: "" } .collectAsState(initial = "").value