Skip to content

Commit

Permalink
Save unique external ID with feeds
Browse files Browse the repository at this point in the history
  • Loading branch information
jocmp committed Dec 19, 2023
1 parent 87b2e15 commit 19e4083
Show file tree
Hide file tree
Showing 20 changed files with 224 additions and 76 deletions.
18 changes: 10 additions & 8 deletions app/src/main/java/com/jocmp/basilreader/ui/articles/FeedList.kt
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,16 @@ fun FeedList(
onFeedSelect = onFeedSelect
)
}
Box(modifier = Modifier.padding(horizontal = 8.dp)) {
Text("Feeds", fontWeight = FontWeight.Bold)
}
feeds.forEach {
FeedRow(
feed = it,
onSelect = onFeedSelect
)
if (feeds.isNotEmpty()) {
Box(modifier = Modifier.padding(horizontal = 8.dp)) {
Text("Feeds", fontWeight = FontWeight.Bold)
}
feeds.forEach {
FeedRow(
feed = it,
onSelect = onFeedSelect
)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,16 @@ class FeedPreviewFixture : PreviewParameterProvider<Feed> {

private fun feeds(): Sequence<Feed> {
return sequenceOf(
Feed(id = UUID.randomUUID().toString(), name = "GamersNexus", feedURL = "https://gamersnexus.net/rss.xml"),
Feed(id = UUID.randomUUID().toString(), name = "9to5Google", feedURL = "https://9to5google.com/feed/")
Feed(
id = UUID.randomUUID().toString(),
name = "GamersNexus",
feedURL = "https://gamersnexus.net/rss.xml"
),
Feed(
id = UUID.randomUUID().toString(),
name = "9to5Google",
feedURL = "https://9to5google.com/feed/"
)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,31 @@ class FolderPreviewFixture : PreviewParameterProvider<Folder> {
Folder(
title = "Tech",
feeds = mutableListOf(
Feed(id = UUID.randomUUID().toString(), name = "The Verge", feedURL = "https://www.theverge.com/rss/index.xml"),
Feed(id = UUID.randomUUID().toString(), name = "Ars Technica", feedURL = "https://arstechnica.com/feed/")
Feed(
id = UUID.randomUUID().toString(),
name = "The Verge",
feedURL = "https://www.theverge.com/rss/index.xml"
),
Feed(
id = UUID.randomUUID().toString(),
name = "Ars Technica",
feedURL = "https://arstechnica.com/feed/"
)
)
),
Folder(
title = "Programming",
feeds = mutableListOf(
Feed(id = UUID.randomUUID().toString(), name = "Android Weekly", feedURL = ""),
Feed(id = UUID.randomUUID().toString(), name = "Ruby Weekly", feedURL = ""),
Feed(
id = UUID.randomUUID().toString(),
name = "Android Weekly",
feedURL = ""
),
Feed(
id = UUID.randomUUID().toString(),
name = "Ruby Weekly",
feedURL = ""
),
)
)
)
Expand Down
42 changes: 37 additions & 5 deletions basil/src/main/java/com/jocmp/basil/Account.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.jocmp.basil

import com.jocmp.basil.accounts.AccountDelegate
import com.jocmp.basil.accounts.ExternalFeed
import com.jocmp.basil.accounts.LocalAccountDelegate
import com.jocmp.basil.db.Database
import com.jocmp.basil.extensions.asFeed
import com.jocmp.basil.extensions.asFolder
Expand All @@ -9,13 +12,18 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.net.URI
import java.net.URL
import java.util.UUID

data class Account(
val id: String,
val path: URI,
val database: Database,
) {
) : AccountDelegate {
private val delegate: AccountDelegate

init {
delegate = LocalAccountDelegate(this)
}

var folders = mutableSetOf<Folder>()

var feeds = mutableSetOf<Feed>()
Expand Down Expand Up @@ -50,8 +58,14 @@ data class Account(

val found = (result as FeedFinder.Result.Success).feeds.first()

val externalFeed = delegate.createFeed(feedURL = found.feedURL)
database.feedsQueries.create(
externalFeed.externalID,
found.feedURL.toString()
).executeAsOne()

val feed = Feed(
id = UUID.randomUUID().toString(),
id = externalFeed.externalID,
name = entryNameOrDefault(entry, found.name),
feedURL = found.feedURL.toString(),
siteURL = entrySiteURL(found.siteURL)
Expand Down Expand Up @@ -96,13 +110,31 @@ data class Account(
}

private fun loadOPML(items: List<Outline>) {
val externalIDs = mutableListOf<String>()

items.forEach {
when (it) {
is Outline.FeedOutline -> it.feed.externalID?.let { id -> externalIDs.add(id) }
is Outline.FolderOutline -> externalIDs.addAll(it.folder.feeds.mapNotNull { feed -> feed.externalID })
}
}

val dbFeeds =
database.feedsQueries.allByExternalID(externalIDs).executeAsList().associateBy {
it.external_id
}

items.forEach { item ->
when (item) {
is Outline.FolderOutline -> folders.add(item.asFolder)
is Outline.FeedOutline -> feeds.add(item.asFeed)
is Outline.FolderOutline -> folders.add(item.asFolder(dbFeeds))
is Outline.FeedOutline -> item.feed.asFeed(dbFeeds)?.let { feeds.add(it) }
}
}
}

override fun createFeed(feedURL: URL): ExternalFeed {
return delegate.createFeed(feedURL)
}
}

fun Account.asOPML(): String {
Expand Down
1 change: 1 addition & 0 deletions basil/src/main/java/com/jocmp/basil/AccountManager.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.jocmp.basil

import com.jocmp.basil.accounts.LocalAccountDelegate
import kotlinx.coroutines.flow.first
import java.io.File
import java.io.FileFilter
Expand Down
2 changes: 1 addition & 1 deletion basil/src/main/java/com/jocmp/basil/Feed.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ data class Feed(
}

fun Feed.asOPML(indentLevel: Int): String {
val opml = "<outline text=\"${name}\" title=\"${name}\" description=\"\" type=\"rss\" version=\"RSS\" htmlUrl=\"${siteURL}\" xmlUrl=\"${feedURL}\"/>\n"
val opml = "<outline text=\"${name}\" title=\"${name}\" description=\"\" type=\"rss\" version=\"RSS\" htmlUrl=\"${siteURL}\" xmlUrl=\"${feedURL}\" basil_id=\"${id}\"/>\n"
return opml.prepending(tabCount = indentLevel)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.jocmp.basil.accounts

import java.net.URL

interface AccountDelegate {
fun createFeed(feedURL: URL): ExternalFeed
}
3 changes: 3 additions & 0 deletions basil/src/main/java/com/jocmp/basil/accounts/ExternalFeed.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.jocmp.basil.accounts

data class ExternalFeed(val externalID: String)
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.jocmp.basil.accounts

import com.jocmp.basil.Account
import com.jocmp.basil.Feed
import java.net.URL
import java.util.UUID

internal class LocalAccountDelegate(private val account: Account): AccountDelegate {
override fun createFeed(feedURL: URL): ExternalFeed {
val allFeeds = mutableSetOf<Feed>().apply {
addAll(account.feeds)
addAll(account.folders.flatMap { it.feeds })
}

val existingFeed = allFeeds.find { it.feedURL == feedURL.toString() }

if (existingFeed != null) {
return ExternalFeed(externalID = existingFeed.id)
}

return ExternalFeed(externalID = UUID.randomUUID().toString())
}
}
25 changes: 15 additions & 10 deletions basil/src/main/java/com/jocmp/basil/extensions/FeedOutlineExt.kt
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
package com.jocmp.basil.extensions

import com.jocmp.basil.Feed
import com.jocmp.basil.opml.Outline

internal val Outline.FeedOutline.asFeed: Feed
get() {
return Feed(
id = feed.title.toString(),
name = feed.title ?: "",
feedURL = feed.xmlUrl ?: ""
)
}
import com.jocmp.basil.db.Feeds as DBFeed
import com.jocmp.basil.opml.Feed as OPMLFeed

internal fun OPMLFeed.asFeed(feeds: Map<String, DBFeed>): Feed? {
externalID ?: return null
val feed = feeds[externalID]

feed ?: return null

return Feed(
id = feed.external_id,
name = title ?: "",
feedURL = xmlUrl ?: ""
)
}
23 changes: 9 additions & 14 deletions basil/src/main/java/com/jocmp/basil/extensions/FolderOutlineExt.kt
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
package com.jocmp.basil.extensions

import com.jocmp.basil.Feed
import com.jocmp.basil.Folder
import com.jocmp.basil.opml.Outline
import com.jocmp.basil.db.Feeds as DBFeed

internal val Outline.FolderOutline.asFolder: Folder
get() {
return Folder(
title = folder.title ?: "",
feeds = folder.feeds.map { feed ->
Feed(
id = "",
name = feed.title ?: "",
feedURL = feed.xmlUrl ?: ""
)
}.toMutableList()
)
}
internal fun Outline.FolderOutline.asFolder(feeds: Map<String, DBFeed>): Folder {
return Folder(
title = folder.title ?: "",
feeds = folder.feeds.mapNotNull {
it.asFeed(feeds = feeds)
}.toMutableList()
)
}
1 change: 1 addition & 0 deletions basil/src/main/java/com/jocmp/basil/opml/Feed.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.jocmp.basil.opml

internal data class Feed(
val externalID: String? = null,
val title: String? = null,
val text: String? = null,
val htmlUrl: String? = null,
Expand Down
2 changes: 2 additions & 0 deletions basil/src/main/java/com/jocmp/basil/opml/OPMLHandler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ internal class OPMLHandler : DefaultHandler() {
currentFolder?.also { folder ->
folder.feeds.add(
Feed(
externalID = attributes.getValue("basil_id"),
title = attributes.getValue("title"),
text = attributes.getValue("text"),
htmlUrl = attributes.getValue("htmlUrl"),
Expand All @@ -49,6 +50,7 @@ internal class OPMLHandler : DefaultHandler() {
opmlDocument.outlines.add(
Outline.FeedOutline(
Feed(
externalID = attributes.getValue("basil_id"),
title = attributes.getValue("title"),
text = attributes.getValue("text"),
htmlUrl = attributes.getValue("htmlUrl"),
Expand Down
19 changes: 19 additions & 0 deletions basil/src/main/sqldelight/com/jocmp/basil/db/feeds.sq
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
CREATE TABLE feeds (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
external_id TEXT NOT NULL,
feed_url TEXT NOT NULL UNIQUE
);

allByExternalID:
SELECT *
FROM feeds
WHERE external_id IN ?;

create {
REPLACE INTO feeds(external_id, feed_url)
VALUES (?, ?);

SELECT *
FROM feeds
WHERE id = last_insert_rowid();
}
9 changes: 6 additions & 3 deletions basil/src/test/java/com/jocmp/basil/AccountTest.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.jocmp.basil

import com.jocmp.basil.db.Database
import com.jocmp.feedfinder.FeedFinder
import io.mockk.coEvery
import io.mockk.mockkConstructor
Expand All @@ -17,18 +18,20 @@ class AccountTest {
@Rule
val folder = TemporaryFolder()

private lateinit var database: Database

@Before
fun setup() {
mockkConstructor(FeedFinder::class)

coEvery {
anyConstructed<FeedFinder>().find()
} returns FeedFinder.Result.Success(listOf(FakeParserFeed()))

database = InMemoryDatabaseProvider().forAccount("777")
}

private fun buildAccount(id: String, path: File): Account {
val database = InMemoryDatabaseProvider().forAccount(id)

return Account(
id = id,
path = path.toURI(),
Expand Down Expand Up @@ -66,7 +69,7 @@ class AccountTest {
val accountTitle = account.folders.first().title

assertEquals(expected = "Test Title", actual = accountTitle)
assertEquals(expected = account.feeds.size, actual = 1)
assertEquals(expected = 1, actual = account.feeds.size)
}

@Test
Expand Down
Loading

0 comments on commit 19e4083

Please sign in to comment.