@@ -7,18 +7,17 @@ import com.jocmp.capy.accounts.AddFeedResult
77import com.jocmp.capy.accounts.withErrorHandling
88import com.jocmp.capy.common.TimeHelpers
99import com.jocmp.capy.common.UnauthorizedError
10- import com.jocmp.capy.common.host
1110import com.jocmp.capy.common.toDateTime
1211import com.jocmp.capy.common.transactionWithErrorHandling
1312import com.jocmp.capy.common.withResult
1413import com.jocmp.capy.db.Database
1514import com.jocmp.capy.persistence.ArticleRecords
1615import com.jocmp.capy.persistence.EnclosureRecords
1716import com.jocmp.capy.persistence.FeedRecords
18- import com.jocmp.minifluxclient.Category
1917import com.jocmp.minifluxclient.CreateCategoryRequest
2018import com.jocmp.minifluxclient.CreateFeedRequest
2119import com.jocmp.minifluxclient.Entry
20+ import com.jocmp.minifluxclient.EntryStatus
2221import com.jocmp.minifluxclient.Miniflux
2322import com.jocmp.minifluxclient.UpdateCategoryRequest
2423import com.jocmp.minifluxclient.UpdateEntriesRequest
@@ -40,7 +39,6 @@ internal class MinifluxAccountDelegate(
4039 override suspend fun refresh (filter : ArticleFilter , cutoffDate : ZonedDateTime ? ): Result <Unit > {
4140 return try {
4241 refreshFeeds()
43- refreshCategories()
4442 refreshArticles()
4543
4644 Result .success(Unit )
@@ -55,7 +53,7 @@ internal class MinifluxAccountDelegate(
5553 val entryIDs = articleIDs.map { it.toLong() }
5654
5755 return withErrorHandling {
58- miniflux.updateEntries(UpdateEntriesRequest (entry_ids = entryIDs, status = " read " ))
56+ miniflux.updateEntries(UpdateEntriesRequest (entry_ids = entryIDs, status = EntryStatus . READ ))
5957 Unit
6058 }
6159 }
@@ -64,7 +62,7 @@ internal class MinifluxAccountDelegate(
6462 val entryIDs = articleIDs.map { it.toLong() }
6563
6664 return withErrorHandling {
67- miniflux.updateEntries(UpdateEntriesRequest (entry_ids = entryIDs, status = " unread " ))
65+ miniflux.updateEntries(UpdateEntriesRequest (entry_ids = entryIDs, status = EntryStatus . UNREAD ))
6866 Unit
6967 }
7068 }
@@ -114,7 +112,8 @@ internal class MinifluxAccountDelegate(
114112 val feed = feedResponse.body()
115113
116114 return if (feed != null ) {
117- upsertFeed(feed)
115+ val icons = fetchIcons(listOf (feed))
116+ upsertFeed(feed, icons)
118117
119118 val localFeed = feedRecords.find(feed.id.toString())
120119
@@ -193,30 +192,23 @@ internal class MinifluxAccountDelegate(
193192 }
194193
195194 private suspend fun refreshFeeds () {
196- withResult(miniflux.feeds()) { feeds ->
197- database.transactionWithErrorHandling {
198- feeds.forEach { feed ->
199- upsertFeed(feed)
200- }
201- }
195+ val feedsResponse = miniflux.feeds()
196+ val feeds = feedsResponse.body()
202197
203- val feedsToKeep = feeds.map { it.id.toString() }
204- database.feedsQueries.deleteAllExcept(feedsToKeep)
198+ if ( ! feedsResponse.isSuccessful || feeds == null ) {
199+ return
205200 }
206- }
207201
208- private suspend fun refreshCategories () {
209- withResult(miniflux.categories()) { categories ->
210- database.transactionWithErrorHandling {
211- categories.forEach { category ->
212- database.taggingsQueries.upsert(
213- id = category.id.toString(),
214- feed_id = " " , // Miniflux categories are not directly tied to a single feed
215- name = category.title,
216- )
217- }
202+ val icons = fetchIcons(feeds)
203+
204+ database.transactionWithErrorHandling {
205+ feeds.forEach { feed ->
206+ upsertFeed(feed, icons)
218207 }
219208 }
209+
210+ val feedsToKeep = feeds.map { it.id.toString() }
211+ database.feedsQueries.deleteAllExcept(feedsToKeep)
220212 }
221213
222214 private suspend fun refreshArticles () {
@@ -233,7 +225,7 @@ internal class MinifluxAccountDelegate(
233225 }
234226
235227 private suspend fun refreshUnreadEntries () {
236- withResult(miniflux.entries(status = " unread " )) { result ->
228+ withResult(miniflux.entries(status = EntryStatus . UNREAD )) { result ->
237229 val ids = result.entries.map { it.id.toString() }
238230 articleRecords.markAllUnread(articleIDs = ids)
239231 }
@@ -288,7 +280,7 @@ internal class MinifluxAccountDelegate(
288280 articleRecords.createStatus(
289281 articleID = articleID,
290282 updatedAt = updated,
291- read = entry.status == " read "
283+ read = entry.status == EntryStatus . READ
292284 )
293285
294286 entry.enclosures?.forEach { enclosure ->
@@ -304,15 +296,27 @@ internal class MinifluxAccountDelegate(
304296 }
305297 }
306298
307- private fun upsertFeed (feed : com.jocmp.minifluxclient.Feed ) {
299+ private fun upsertFeed (feed : com.jocmp.minifluxclient.Feed , icons : Map <Long , String >) {
300+ val icon = feed.icon?.icon_id?.let { icons[it] }
301+
308302 database.feedsQueries.upsert(
309303 id = feed.id.toString(),
310304 subscription_id = feed.id.toString(),
311305 title = feed.title,
312306 feed_url = feed.feed_url,
313307 site_url = feed.site_url,
314- favicon_url = feed.icon?.data
308+ favicon_url = icon,
309+ priority = null
315310 )
311+
312+ // Create tagging for the feed's category
313+ feed.category?.let { category ->
314+ database.taggingsQueries.upsert(
315+ id = " ${feed.id} -${category.id} " ,
316+ feed_id = feed.id.toString(),
317+ name = category.title
318+ )
319+ }
316320 }
317321
318322 private suspend fun getOrCreateCategory (title : String ): Long {
@@ -327,6 +331,27 @@ internal class MinifluxAccountDelegate(
327331 }
328332 }
329333
334+ private suspend fun fetchIcons (feeds : List <com.jocmp.minifluxclient.Feed >): Map <Long , String > {
335+ val iconMap = mutableMapOf<Long , String >()
336+
337+ feeds.forEach { feed ->
338+ feed.icon?.icon_id?.let { iconId ->
339+ try {
340+ val response = miniflux.icon(iconId)
341+ val iconData = response.body()
342+
343+ if (response.isSuccessful && iconData != null ) {
344+ iconMap[iconId] = iconData.data
345+ }
346+ } catch (_: Exception ) {
347+ // Ignore icon fetch failures
348+ }
349+ }
350+ }
351+
352+ return iconMap
353+ }
354+
330355 companion object {
331356 const val MAX_ENTRY_LIMIT = 100
332357 }
0 commit comments