Skip to content

Commit

Permalink
Sync when article is marked as read
Browse files Browse the repository at this point in the history
  • Loading branch information
jocmp committed Feb 26, 2024
1 parent d32d239 commit fbc6635
Show file tree
Hide file tree
Showing 17 changed files with 147 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.transform
Expand Down Expand Up @@ -85,17 +88,21 @@ class AccountViewModel(
}

fun selectFeed(feedID: String) {
val feed = account.findFeed(feedID) ?: return
val feedFilter = ArticleFilter.Feeds(feed = feed, feedStatus = _filter.value.status)
viewModelScope.launch {
val feed = account.findFeed(feedID) ?: return@launch
val feedFilter = ArticleFilter.Feeds(feed = feed, feedStatus = _filter.value.status)

selectArticleFilter(feedFilter)
selectArticleFilter(feedFilter)
}
}

fun selectFolder(title: String) {
val folder = account.findFolder(title) ?: return
val feedFilter = ArticleFilter.Folders(folder = folder, folderStatus = _filter.value.status)
viewModelScope.launch {
val folder = account.findFolder(title) ?: return@launch
val feedFilter = ArticleFilter.Folders(folder = folder, folderStatus = _filter.value.status)

selectArticleFilter(feedFilter)
selectArticleFilter(feedFilter)
}
}

fun removeFeed(feedID: String) {
Expand Down Expand Up @@ -127,7 +134,7 @@ class AccountViewModel(

fun selectArticle(articleID: String, completion: (article: Article) -> Unit) {
viewModelScope.launch {
articleState.value = account.findArticle(articleID = articleID)
articleState.value = account.findArticle(articleID = articleID)?.copy(read = true)
articleState.value?.let(completion)
appPreferences.articleID.set(articleID)
account.markRead(articleID)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package com.jocmp.basilreader.ui.articles

import androidx.compose.animation.Crossfade
import androidx.compose.animation.core.tween
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Menu
import androidx.compose.material3.DrawerValue
Expand All @@ -21,31 +21,30 @@ import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
import androidx.compose.material3.rememberDrawerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.paging.PagingData
import androidx.paging.compose.collectAsLazyPagingItems
import com.jocmp.basil.Article
import com.jocmp.basil.ArticleFilter
import com.jocmp.basil.ArticleStatus
import com.jocmp.basil.Feed
import com.jocmp.basil.Folder
import com.jocmp.basil.persistence.AllFeeds
import com.jocmp.basilreader.ui.components.rememberSaveableWebViewState
import com.jocmp.basilreader.ui.components.rememberWebViewNavigator
import com.jocmp.basilreader.ui.fixtures.FeedPreviewFixture
import com.jocmp.basilreader.ui.fixtures.FolderPreviewFixture
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.launch

private const val TAG = "ArticleLayout"

@OptIn(
ExperimentalMaterial3AdaptiveApi::class,
ExperimentalMaterial3Api::class
Expand Down Expand Up @@ -85,6 +84,8 @@ fun ArticleLayout(
val context = LocalContext.current
val webViewNavigator = rememberWebViewNavigator()
val webViewState = rememberSaveableWebViewState()
val listState = rememberLazyListState()
val pagingArticles = articles.collectAsLazyPagingItems()

val navigateToDetail = {
navigator.navigateTo(ListDetailPaneScaffoldRole.Detail)
Expand All @@ -104,6 +105,11 @@ fun ArticleLayout(
LaunchedEffect(true) {
onFeedRefresh {
state.endRefresh()
pagingArticles.refresh()
coroutineScope.launch {
delay(200)
listState.scrollToItem(0)
}
}
}
}
Expand Down Expand Up @@ -170,12 +176,13 @@ fun ArticleLayout(
.nestedScroll(state.nestedScrollConnection)
) {
Crossfade(
articles,
pagingArticles,
label = ""
) {
ArticleList(
articles = it,
selectedArticleKey = article?.id,
listState = listState,
onSelect = { articleID ->
onSelectArticle(articleID) {
coroutineScope.launch {
Expand Down Expand Up @@ -204,7 +211,7 @@ fun ArticleLayout(
webViewNavigator = webViewNavigator,
onBackPressed = {
onClearArticle()
navigator.navigateTo(ListDetailPaneScaffoldRole.List)
navigator.navigateBack()
}
)
}
Expand Down
23 changes: 11 additions & 12 deletions app/src/main/java/com/jocmp/basilreader/ui/articles/ArticleList.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ package com.jocmp.basilreader.ui.articles

import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier
import androidx.paging.PagingData
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.compose.itemKey
import com.jocmp.basil.Article
Expand All @@ -14,12 +18,12 @@ import kotlinx.coroutines.launch

@Composable
fun ArticleList(
articles: Flow<PagingData<Article>>,
articles: LazyPagingItems<Article>,
onSelect: suspend (articleID: String) -> Unit,
selectedArticleKey: String?,
listState: LazyListState
) {
val composableScope = rememberCoroutineScope()
val lazyPagingItems = articles.collectAsLazyPagingItems()

val selectArticle = { articleID: String ->
composableScope.launch {
Expand All @@ -28,13 +32,14 @@ fun ArticleList(
}

LazyColumn(
modifier = Modifier.fillMaxSize()
modifier = Modifier.fillMaxSize(),
state = listState,
) {
items(
count = lazyPagingItems.itemCount,
key = lazyPagingItems.itemKey { it.id },
count = articles.itemCount,
key = articles.itemKey { it.id },
) { index ->
val item = lazyPagingItems[index] ?: return@items
val item = articles[index] ?: return@items

ArticleRow(
article = item,
Expand All @@ -44,9 +49,3 @@ fun ArticleList(
}
}
}

private fun String.orNullIfBlank(): String? {
return ifBlank {
null
}
}
23 changes: 17 additions & 6 deletions app/src/main/java/com/jocmp/basilreader/ui/articles/ArticleRow.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import androidx.compose.material3.ListItemColors
import androidx.compose.material3.ListItemDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.MaterialTheme.colorScheme
import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
Expand All @@ -24,6 +25,7 @@ import androidx.compose.ui.unit.dp
import com.bumptech.glide.integration.compose.ExperimentalGlideComposeApi
import com.bumptech.glide.integration.compose.GlideImage
import com.bumptech.glide.integration.compose.placeholder
import com.bumptech.glide.load.resource.bitmap.DownsampleStrategy
import com.jocmp.basil.Article
import com.jocmp.basilreader.ui.theme.BasilReaderTheme
import java.net.URL
Expand All @@ -39,7 +41,7 @@ fun ArticleRow(
selected: Boolean,
onSelect: (articleID: String) -> Unit,
) {
val thumbnailSize = with(LocalDensity.current) { THUMBNAIL_SIZE.roundToPx()}
val thumbnailSize = with(LocalDensity.current) { THUMBNAIL_SIZE.roundToPx() }
val imageURL = article.imageURL?.toString()
val colors = listItemColors(
selected = selected,
Expand Down Expand Up @@ -67,6 +69,7 @@ fun ArticleRow(
failure = placeholder(background),
) {
it.override(thumbnailSize)
.downsample(DownsampleStrategy.CENTER_INSIDE)
}
}
} else {
Expand All @@ -78,11 +81,16 @@ fun ArticleRow(
)
},
supportingContent = {
Text(
text = article.summary,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
)
Column {
Text(
article.feedName,
)
Text(
text = article.summary,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
)
}
},
colors = colors
)
Expand Down Expand Up @@ -121,6 +129,7 @@ fun ArticleRowPreview_Selected_DarkMode() {
arrivedAt = ZonedDateTime.of(2024, 2, 11, 8, 33, 0, 0, ZoneId.systemDefault()),
read = true,
starred = false,
feedName = "9to5Google"
)

BasilReaderTheme(dynamicColor = false) {
Expand Down Expand Up @@ -153,6 +162,7 @@ fun ArticleRowPreview_Selected() {
arrivedAt = ZonedDateTime.of(2024, 2, 11, 8, 33, 0, 0, ZoneId.systemDefault()),
read = true,
starred = false,
feedName = "9to5Google"
)

BasilReaderTheme(dynamicColor = false) {
Expand All @@ -178,6 +188,7 @@ fun ArticleRowPreview_Unread() {
arrivedAt = ZonedDateTime.of(2024, 2, 11, 8, 33, 0, 0, ZoneId.systemDefault()),
read = false,
starred = false,
feedName = "9to5Google"
)

ArticleRow(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.jocmp.basil.Folder
import com.jocmp.basil.preferences.getAndSet
import com.jocmp.basilreader.common.AppPreferences
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking

class EditFeedViewModel(
savedStateHandle: SavedStateHandle,
Expand All @@ -21,7 +22,7 @@ class EditFeedViewModel(
private val args = EditFeedArgs(savedStateHandle)

val feed: Feed
get() = account.findFeed(args.feedID)!!
get() = runBlocking { account.findFeed(args.feedID)!! }

val feedFolderTitles: List<String>
get() = emptyList() // account.folders.filter { it.feeds.contains(feed) }.map { it.title }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import com.jocmp.basil.EditFolderForm
import com.jocmp.basil.Folder
import com.jocmp.basilreader.common.AppPreferences
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking

class EditFolderViewModel(
savedStateHandle: SavedStateHandle,
Expand All @@ -19,7 +20,7 @@ class EditFolderViewModel(
private val args = EditFolderArgs(savedStateHandle)

val folder: Folder
get() = account.findFolder(args.folderTitle)!!
get() = runBlocking { account.findFolder(args.folderTitle)!! }

fun submit(form: EditFolderForm, onSubmit: () -> Unit) {
viewModelScope.launch {
Expand Down
16 changes: 7 additions & 9 deletions basil/src/main/java/com/jocmp/basil/Account.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package com.jocmp.basil

import android.util.Log
import com.jocmp.basil.accounts.FeedbinAccountDelegate
import com.jocmp.basil.accounts.asOPML
import com.jocmp.basil.common.upsert
import com.jocmp.basil.db.Database
import com.jocmp.basil.opml.OPMLImporter
import com.jocmp.basil.persistence.ArticleRecords
Expand Down Expand Up @@ -152,14 +150,12 @@ data class Account(
refreshCompactedFeeds(flattenedFeeds.filter { ids.contains(it.id) })
}

fun findFeed(feedID: String): Feed? {
// return flattenedFeeds.find { it.id == feedID }
return null
suspend fun findFeed(feedID: String): Feed? {
return feedRecords.findBy(feedID)
}

fun findFolder(title: String): Folder? {
// return folders.find { it.title == title }
return null
suspend fun findFolder(title: String): Folder? {
return feedRecords.findFolder(title = title)
}

fun findArticle(articleID: String): Article? {
Expand All @@ -184,8 +180,10 @@ data class Account(
delegate.markRead(listOf(articleID))
}

fun markUnread(articleID: String) {
suspend fun markUnread(articleID: String) {
articleRecords.markUnread(articleID = articleID)

delegate.markUnread(listOf(articleID))
}

suspend fun import(inputStream: InputStream) {
Expand Down
3 changes: 2 additions & 1 deletion basil/src/main/java/com/jocmp/basil/Article.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ data class Article(
val imageURL: URL?,
val arrivedAt: ZonedDateTime,
val read: Boolean,
val starred: Boolean
val starred: Boolean,
val feedName: String = "",
)
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ internal class FeedbinAccountDelegate(
feedbin.deleteUnreadEntries(UnreadEntriesRequest(unread_entries = entryIDs))
}

suspend fun markUnread(articleIDs: List<String>) {
val entryIDs = articleIDs.map { it.toLong() }

feedbin.postUnreadEntries(UnreadEntriesRequest(unread_entries = entryIDs))
}

suspend fun refreshAll() {
refreshFeeds()
refreshTaggings()
Expand Down Expand Up @@ -85,9 +91,10 @@ internal class FeedbinAccountDelegate(
}

private fun maxArrivedAt(): String? {
val result = database.articlesQueries.lastArrivalTime().executeAsOne()
val max = database.articlesQueries.lastArrivalTime().executeAsOne().MAX
max ?: return null

return result.MAX?.toDateTime?.toString()
return (max * 1000).toDateTime.toString()
}
}

Expand Down
Loading

0 comments on commit fbc6635

Please sign in to comment.