diff --git a/app/src/main/java/com/jocmp/basilreader/ui/articles/AccountViewModel.kt b/app/src/main/java/com/jocmp/basilreader/ui/articles/AccountViewModel.kt index dd63a13a..5449ceab 100644 --- a/app/src/main/java/com/jocmp/basilreader/ui/articles/AccountViewModel.kt +++ b/app/src/main/java/com/jocmp/basilreader/ui/articles/AccountViewModel.kt @@ -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 @@ -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) { @@ -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) diff --git a/app/src/main/java/com/jocmp/basilreader/ui/articles/ArticleLayout.kt b/app/src/main/java/com/jocmp/basilreader/ui/articles/ArticleLayout.kt index 912a896f..4dd275f8 100644 --- a/app/src/main/java/com/jocmp/basilreader/ui/articles/ArticleLayout.kt +++ b/app/src/main/java/com/jocmp/basilreader/ui/articles/ArticleLayout.kt @@ -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 @@ -21,21 +21,19 @@ 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 @@ -43,9 +41,10 @@ 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 @@ -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) @@ -104,6 +105,11 @@ fun ArticleLayout( LaunchedEffect(true) { onFeedRefresh { state.endRefresh() + pagingArticles.refresh() + coroutineScope.launch { + delay(200) + listState.scrollToItem(0) + } } } } @@ -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 { @@ -204,7 +211,7 @@ fun ArticleLayout( webViewNavigator = webViewNavigator, onBackPressed = { onClearArticle() - navigator.navigateTo(ListDetailPaneScaffoldRole.List) + navigator.navigateBack() } ) } diff --git a/app/src/main/java/com/jocmp/basilreader/ui/articles/ArticleList.kt b/app/src/main/java/com/jocmp/basilreader/ui/articles/ArticleList.kt index 9a05a239..f0abb189 100644 --- a/app/src/main/java/com/jocmp/basilreader/ui/articles/ArticleList.kt +++ b/app/src/main/java/com/jocmp/basilreader/ui/articles/ArticleList.kt @@ -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 @@ -14,12 +18,12 @@ import kotlinx.coroutines.launch @Composable fun ArticleList( - articles: Flow>, + articles: LazyPagingItems
, onSelect: suspend (articleID: String) -> Unit, selectedArticleKey: String?, + listState: LazyListState ) { val composableScope = rememberCoroutineScope() - val lazyPagingItems = articles.collectAsLazyPagingItems() val selectArticle = { articleID: String -> composableScope.launch { @@ -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, @@ -44,9 +49,3 @@ fun ArticleList( } } } - -private fun String.orNullIfBlank(): String? { - return ifBlank { - null - } -} diff --git a/app/src/main/java/com/jocmp/basilreader/ui/articles/ArticleRow.kt b/app/src/main/java/com/jocmp/basilreader/ui/articles/ArticleRow.kt index f38aa36d..ec401329 100644 --- a/app/src/main/java/com/jocmp/basilreader/ui/articles/ArticleRow.kt +++ b/app/src/main/java/com/jocmp/basilreader/ui/articles/ArticleRow.kt @@ -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 @@ -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 @@ -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, @@ -67,6 +69,7 @@ fun ArticleRow( failure = placeholder(background), ) { it.override(thumbnailSize) + .downsample(DownsampleStrategy.CENTER_INSIDE) } } } else { @@ -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 ) @@ -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) { @@ -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) { @@ -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( diff --git a/app/src/main/java/com/jocmp/basilreader/ui/articles/EditFeedViewModel.kt b/app/src/main/java/com/jocmp/basilreader/ui/articles/EditFeedViewModel.kt index 9ae81f25..7137f4f0 100644 --- a/app/src/main/java/com/jocmp/basilreader/ui/articles/EditFeedViewModel.kt +++ b/app/src/main/java/com/jocmp/basilreader/ui/articles/EditFeedViewModel.kt @@ -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, @@ -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 get() = emptyList() // account.folders.filter { it.feeds.contains(feed) }.map { it.title } diff --git a/app/src/main/java/com/jocmp/basilreader/ui/articles/EditFolderViewModel.kt b/app/src/main/java/com/jocmp/basilreader/ui/articles/EditFolderViewModel.kt index 16a080da..b3c76891 100644 --- a/app/src/main/java/com/jocmp/basilreader/ui/articles/EditFolderViewModel.kt +++ b/app/src/main/java/com/jocmp/basilreader/ui/articles/EditFolderViewModel.kt @@ -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, @@ -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 { diff --git a/basil/src/main/java/com/jocmp/basil/Account.kt b/basil/src/main/java/com/jocmp/basil/Account.kt index 4a56669c..72008916 100644 --- a/basil/src/main/java/com/jocmp/basil/Account.kt +++ b/basil/src/main/java/com/jocmp/basil/Account.kt @@ -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 @@ -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? { @@ -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) { diff --git a/basil/src/main/java/com/jocmp/basil/Article.kt b/basil/src/main/java/com/jocmp/basil/Article.kt index bd6b2c33..6108f24b 100644 --- a/basil/src/main/java/com/jocmp/basil/Article.kt +++ b/basil/src/main/java/com/jocmp/basil/Article.kt @@ -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 = "", ) diff --git a/basil/src/main/java/com/jocmp/basil/accounts/FeedbinAccountDelegate.kt b/basil/src/main/java/com/jocmp/basil/accounts/FeedbinAccountDelegate.kt index 9257d720..c6c9d05c 100644 --- a/basil/src/main/java/com/jocmp/basil/accounts/FeedbinAccountDelegate.kt +++ b/basil/src/main/java/com/jocmp/basil/accounts/FeedbinAccountDelegate.kt @@ -22,6 +22,12 @@ internal class FeedbinAccountDelegate( feedbin.deleteUnreadEntries(UnreadEntriesRequest(unread_entries = entryIDs)) } + suspend fun markUnread(articleIDs: List) { + val entryIDs = articleIDs.map { it.toLong() } + + feedbin.postUnreadEntries(UnreadEntriesRequest(unread_entries = entryIDs)) + } + suspend fun refreshAll() { refreshFeeds() refreshTaggings() @@ -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() } } diff --git a/basil/src/main/java/com/jocmp/basil/persistence/ArticleMapper.kt b/basil/src/main/java/com/jocmp/basil/persistence/ArticleMapper.kt index 25e41f0f..bb330e10 100644 --- a/basil/src/main/java/com/jocmp/basil/persistence/ArticleMapper.kt +++ b/basil/src/main/java/com/jocmp/basil/persistence/ArticleMapper.kt @@ -15,6 +15,7 @@ internal fun articleMapper( summary: String?, imageURL: String?, publishedAt: Long?, + feedTitle: String?, arrivedAt: Long?, starred: Boolean?, read: Boolean?, @@ -30,7 +31,8 @@ internal fun articleMapper( summary = summary ?: "", arrivedAt = ZonedDateTime.ofInstant(Instant.ofEpochSecond(arrivedAt!!), zoneID), read = read ?: false, - starred = starred ?: false + starred = starred ?: false, + feedName = feedTitle ?: "" ) } @@ -43,6 +45,7 @@ internal fun listMapper( summary: String?, imageURL: String?, publishedAt: Long?, + feedTitle: String?, arrivedAt: Long?, starred: Boolean?, read: Boolean?, @@ -58,6 +61,7 @@ internal fun listMapper( summary = summary ?: "", arrivedAt = ZonedDateTime.ofInstant(Instant.ofEpochSecond(arrivedAt!!), zoneID), read = read ?: false, - starred = starred ?: false + starred = starred ?: false, + feedName = feedTitle ?: "" ) } diff --git a/basil/src/main/java/com/jocmp/basil/persistence/ArticlePagerFactory.kt b/basil/src/main/java/com/jocmp/basil/persistence/ArticlePagerFactory.kt index d4f9298f..0f724832 100644 --- a/basil/src/main/java/com/jocmp/basil/persistence/ArticlePagerFactory.kt +++ b/basil/src/main/java/com/jocmp/basil/persistence/ArticlePagerFactory.kt @@ -58,9 +58,9 @@ class ArticlePagerFactory(private val database: Database) { filter: ArticleFilter.Folders, since: ZonedDateTime ): PagingSource { -// val feedIDs = filter.folder.feeds.mapNotNull { it.id.toLongOrNull() } + val feedIDs = filter.folder.feeds.map { it.id } - return feedsSource(emptyList(), filter, since) + return feedsSource(feedIDs, filter, since) } private fun feedsSource( diff --git a/basil/src/main/java/com/jocmp/basil/persistence/FeedRecords.kt b/basil/src/main/java/com/jocmp/basil/persistence/FeedRecords.kt index 46644883..6ac8eabd 100644 --- a/basil/src/main/java/com/jocmp/basil/persistence/FeedRecords.kt +++ b/basil/src/main/java/com/jocmp/basil/persistence/FeedRecords.kt @@ -2,13 +2,15 @@ package com.jocmp.basil.persistence import app.cash.sqldelight.coroutines.asFlow import app.cash.sqldelight.coroutines.mapToList +import app.cash.sqldelight.coroutines.mapToOneNotNull import com.jocmp.basil.Feed import com.jocmp.basil.Folder import com.jocmp.basil.db.Database -import com.jocmp.basil.db.Tagged import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.transform +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.firstOrNull +import kotlin.coroutines.coroutineContext internal class FeedRecords(val database: Database) { // internal fun findOrCreate(id: String): Feeds { @@ -27,6 +29,26 @@ internal class FeedRecords(val database: Database) { // ).executeAsOne() // } + suspend fun findBy(id: String): Feed? { + return database.feedsQueries.findBy(id, mapper = ::feedMapper) + .asFlow() + .mapToOneNotNull(coroutineContext) + .firstOrNull() + } + + suspend fun findFolder(title: String): Folder? { + val feeds = database.feedsQueries.findByFolder(title, mapper = ::feedMapper) + .asFlow() + .mapToList(coroutineContext) + .first() + + if (feeds.isEmpty()) { + return null + } + + return Folder(title = title, feeds = feeds) + } + internal fun feeds(): Flow> { return database.feedsQueries .tagged(mapper = ::feedMapper) @@ -45,7 +67,7 @@ internal class FeedRecords(val database: Database) { feedURL: String, siteURL: String?, faviconURL: String?, - folderName: String?, + folderName: String? = "", ): Feed { return Feed( id = id, diff --git a/basil/src/main/sqldelight/com/jocmp/basil/db/articles.sq b/basil/src/main/sqldelight/com/jocmp/basil/db/articles.sq index 0a610afc..841a606e 100644 --- a/basil/src/main/sqldelight/com/jocmp/basil/db/articles.sq +++ b/basil/src/main/sqldelight/com/jocmp/basil/db/articles.sq @@ -1,30 +1,34 @@ findByStatus: SELECT articles.*, + feeds.title AS feed_title, article_statuses.arrived_at, article_statuses.starred, article_statuses.read FROM articles +JOIN feeds ON articles.feed_id = feeds.id JOIN article_statuses ON articles.id = article_statuses.article_id WHERE ((article_statuses.read = :read AND article_statuses.last_read_at IS NULL OR article_statuses.last_read_at >= :lastReadAt) OR :read IS NULL) AND (article_statuses.starred = :starred OR :starred IS NULL) GROUP BY articles.id -ORDER BY article_statuses.arrived_at DESC +ORDER BY articles.published_at DESC LIMIT :limit OFFSET :offset; findByFeeds: SELECT articles.*, + feeds.title AS feed_title, article_statuses.arrived_at, article_statuses.starred, article_statuses.read FROM articles JOIN article_statuses ON articles.id = article_statuses.article_id +JOIN feeds ON articles.feed_id = feeds.id WHERE articles.feed_id IN :feedIDs AND ((article_statuses.read = :read AND article_statuses.last_read_at IS NULL OR article_statuses.last_read_at >= :lastReadAt) OR :read IS NULL) AND (article_statuses.starred = :starred OR :starred IS NULL) GROUP BY articles.id -ORDER BY article_statuses.arrived_at DESC +ORDER BY articles.published_at DESC LIMIT :limit OFFSET :offset; countByFeeds: @@ -45,10 +49,12 @@ AND (article_statuses.starred = :starred OR :starred IS NULL); findBy: SELECT articles.*, + feeds.title AS feed_title, article_statuses.arrived_at, article_statuses.starred, article_statuses.read FROM articles +JOIN feeds ON articles.feed_id = feeds.id JOIN article_statuses ON articles.id = article_statuses.article_id WHERE articles.id = :articleID LIMIT 1; diff --git a/basil/src/main/sqldelight/com/jocmp/basil/db/feeds.sq b/basil/src/main/sqldelight/com/jocmp/basil/db/feeds.sq index a8113cc3..c97bb4f2 100644 --- a/basil/src/main/sqldelight/com/jocmp/basil/db/feeds.sq +++ b/basil/src/main/sqldelight/com/jocmp/basil/db/feeds.sq @@ -6,7 +6,8 @@ tagged: SELECT feeds.*, taggings.name FROM feeds LEFT JOIN taggings ON taggings.feed_id = feeds.id -GROUP BY feeds.id, taggings.name; +GROUP BY feeds.id, taggings.name +ORDER BY taggings.name; findBy: SELECT * @@ -14,6 +15,12 @@ FROM feeds WHERE id = :id LIMIT 1; +findByFolder: +SELECT feeds.* +FROM feeds +LEFT JOIN taggings ON taggings.feed_id = feeds.id +WHERE taggings.name = :name; + upsert: INSERT OR REPLACE INTO feeds( id, diff --git a/basil/src/test/java/com/jocmp/basil/accounts/FeedbinAccountDelegateTest.kt b/basil/src/test/java/com/jocmp/basil/accounts/FeedbinAccountDelegateTest.kt index 4e8175e1..7309bd91 100644 --- a/basil/src/test/java/com/jocmp/basil/accounts/FeedbinAccountDelegateTest.kt +++ b/basil/src/test/java/com/jocmp/basil/accounts/FeedbinAccountDelegateTest.kt @@ -113,7 +113,7 @@ class FeedbinAccountDelegateTest { fun markRead() = runTest { val id = 777L - coEvery { feedbin.deleteUnreadEntries(body = any()) } returns Response.success(listOf(id)) + coEvery { feedbin.deleteUnreadEntries(body = any()) } returns Response.success(null) val delegate = FeedbinAccountDelegate(database, feedbin) @@ -121,4 +121,17 @@ class FeedbinAccountDelegateTest { coVerify { feedbin.deleteUnreadEntries(body = UnreadEntriesRequest(listOf(id))) } } + + @Test + fun markUnread() = runTest { + val id = 777L + + coEvery { feedbin.postUnreadEntries(body = any()) } returns Response.success(listOf(id)) + + val delegate = FeedbinAccountDelegate(database, feedbin) + + delegate.markUnread(listOf(id.toString())) + + coVerify { feedbin.postUnreadEntries(body = UnreadEntriesRequest(listOf(id))) } + } } diff --git a/basil/src/test/java/com/jocmp/basil/persistence/ArticleMapperTest.kt b/basil/src/test/java/com/jocmp/basil/persistence/ArticleMapperTest.kt index f75bdddb..ad3d6a12 100644 --- a/basil/src/test/java/com/jocmp/basil/persistence/ArticleMapperTest.kt +++ b/basil/src/test/java/com/jocmp/basil/persistence/ArticleMapperTest.kt @@ -22,7 +22,8 @@ class ArticleMapperTest { arrivedAt = 1703960809, read = false, starred = false, - zoneID = zoneID + zoneID = zoneID, + feedTitle = "" ) val expectedTime = ZonedDateTime.of( diff --git a/feedbinclient/src/main/java/com/jocmp/feedbinclient/Feedbin.kt b/feedbinclient/src/main/java/com/jocmp/feedbinclient/Feedbin.kt index 3f5023bb..16a3fd71 100644 --- a/feedbinclient/src/main/java/com/jocmp/feedbinclient/Feedbin.kt +++ b/feedbinclient/src/main/java/com/jocmp/feedbinclient/Feedbin.kt @@ -31,7 +31,10 @@ interface Feedbin { @GET("v2/taggings.json") suspend fun taggings(): Response> - + + @POST("v2/unread_entries.json") + suspend fun postUnreadEntries(@Body body: UnreadEntriesRequest): Response> + @HTTP(method = "DELETE", path = "v2/unread_entries.json", hasBody = true) suspend fun deleteUnreadEntries(@Body body: UnreadEntriesRequest): Response>