Skip to content

Commit

Permalink
feat(greader): support groups and feeds
Browse files Browse the repository at this point in the history
  • Loading branch information
Ashinch committed Jan 18, 2024
1 parent 337da06 commit c579c01
Show file tree
Hide file tree
Showing 11 changed files with 207 additions and 62 deletions.
9 changes: 9 additions & 0 deletions app/src/main/java/me/ash/reader/domain/repository/FeedDao.kt
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ interface FeedDao {
isNotification: Boolean,
)

@Query(
"""
SELECT * FROM feed
WHERE groupId = :groupId
AND accountId = :accountId
"""
)
suspend fun queryByGroupId(accountId: Int, groupId: String): List<Feed>

@Query(
"""
DELETE FROM feed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ abstract class AbstractRssRepository(
private val dispatcherDefault: CoroutineDispatcher,
) {

open val subscribe: Boolean = true
open val move: Boolean = true
open val delete: Boolean = true
open val update: Boolean = true
open val addSubscription: Boolean = true
open val moveSubscription: Boolean = true
open val deleteSubscription: Boolean = true
open val updateSubscription: Boolean = true

open suspend fun validCredentials(): Boolean = true

Expand All @@ -73,13 +73,16 @@ abstract class AbstractRssRepository(
})
}

open suspend fun addGroup(name: String): String {
open suspend fun addGroup(
destFeed: Feed?,
newGroupName: String
): String {
context.currentAccountId.let { accountId ->
return accountId.spacerDollar(UUID.randomUUID().toString()).also {
groupDao.insert(
Group(
id = it,
name = name,
name = newGroupName,
accountId = accountId
)
)
Expand Down Expand Up @@ -290,21 +293,33 @@ abstract class AbstractRssRepository(

suspend fun isFeedExist(url: String): Boolean = feedDao.queryByLink(context.currentAccountId, url).isNotEmpty()

suspend fun updateGroup(group: Group) {
open suspend fun renameGroup(group: Group) {
groupDao.update(group)
}

suspend fun updateFeed(feed: Feed) {
open suspend fun renameFeed(feed: Feed) {
updateFeed(feed)
}

open suspend fun moveFeed(originGroupId: String, feed: Feed) {
updateFeed(feed)
}

open suspend fun changeFeedUrl(feed: Feed) {
updateFeed(feed)
}

internal suspend fun updateFeed(feed: Feed) {
feedDao.update(feed)
}

suspend fun deleteGroup(group: Group) {
open suspend fun deleteGroup(group: Group) {
deleteArticles(group = group)
feedDao.deleteByGroupId(context.currentAccountId, group.id)
groupDao.delete(group)
}

suspend fun deleteFeed(feed: Feed) {
open suspend fun deleteFeed(feed: Feed) {
deleteArticles(feed = feed)
feedDao.delete(feed)
}
Expand Down
34 changes: 29 additions & 5 deletions app/src/main/java/me/ash/reader/domain/service/FeverRssService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,10 @@ class FeverRssService @Inject constructor(
feedDao, workManager, rssHelper, notificationHelper, ioDispatcher, defaultDispatcher
) {

override val subscribe: Boolean = false
override val move: Boolean = false
override val delete: Boolean = false
override val update: Boolean = false
override val addSubscription: Boolean = false
override val moveSubscription: Boolean = false
override val deleteSubscription: Boolean = false
override val updateSubscription: Boolean = false

private suspend fun getFeverAPI() =
FeverSecurityKey(accountDao.queryById(context.currentAccountId)!!.securityKey).run {
Expand All @@ -86,7 +86,31 @@ class FeverRssService @Inject constructor(
throw Exception("Unsupported")
}

override suspend fun addGroup(name: String): String {
override suspend fun addGroup(destFeed: Feed?, newGroupName: String): String {
throw Exception("Unsupported")
}

override suspend fun renameGroup(group: Group) {
throw Exception("Unsupported")
}

override suspend fun renameFeed(feed: Feed) {
throw Exception("Unsupported")
}

override suspend fun deleteGroup(group: Group) {
throw Exception("Unsupported")
}

override suspend fun deleteFeed(feed: Feed) {
throw Exception("Unsupported")
}

override suspend fun moveFeed(originGroupId: String, feed: Feed) {
throw Exception("Unsupported")
}

override suspend fun changeFeedUrl(feed: Feed) {
throw Exception("Unsupported")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ import me.ash.reader.infrastructure.di.IODispatcher
import me.ash.reader.infrastructure.di.MainDispatcher
import me.ash.reader.infrastructure.rss.RssHelper
import me.ash.reader.infrastructure.rss.provider.greader.GoogleReaderAPI
import me.ash.reader.infrastructure.rss.provider.greader.GoogleReaderAPI.Companion.ofCategoryPathToId
import me.ash.reader.infrastructure.rss.provider.greader.GoogleReaderAPI.Companion.ofFeedPathToId
import me.ash.reader.infrastructure.rss.provider.greader.GoogleReaderAPI.Companion.ofItemPathToId
import me.ash.reader.infrastructure.rss.provider.greader.GoogleReaderAPI.Companion.ofCategoryStreamIdToId
import me.ash.reader.infrastructure.rss.provider.greader.GoogleReaderAPI.Companion.ofFeedStreamIdToId
import me.ash.reader.infrastructure.rss.provider.greader.GoogleReaderAPI.Companion.ofItemStreamIdToId
import me.ash.reader.ui.ext.currentAccountId
import me.ash.reader.ui.ext.dollarLast
import me.ash.reader.ui.ext.showToast
Expand Down Expand Up @@ -59,10 +59,10 @@ class GoogleReaderRssService @Inject constructor(
feedDao, workManager, rssHelper, notificationHelper, ioDispatcher, defaultDispatcher
) {

// override val subscribe: Boolean = true
// override val move: Boolean = true
// override val delete: Boolean = true
// override val update: Boolean = true
override val addSubscription: Boolean = true
override val moveSubscription: Boolean = true
override val deleteSubscription: Boolean = true
override val updateSubscription: Boolean = true

private suspend fun getGoogleReaderAPI() =
GoogleReaderSecurityKey(accountDao.queryById(context.currentAccountId)!!.securityKey).run {
Expand All @@ -87,9 +87,12 @@ class GoogleReaderRssService @Inject constructor(
) {
val accountId = context.currentAccountId
val quickAdd = getGoogleReaderAPI().subscriptionQuickAdd(feedLink)
val feedId = quickAdd.streamId?.ofFeedPathToId()!!
getGoogleReaderAPI().subscriptionEdit(feedId, groupId.dollarLast())
// TODO: Support rename while adding a subscription
val feedId = quickAdd.streamId?.ofFeedStreamIdToId()!!
getGoogleReaderAPI().subscriptionEdit(
destFeedId = feedId,
destCategoryId = groupId.dollarLast(),
destFeedName = searchedFeed.title!!
)
feedDao.insert(Feed(
id = accountId.spacerDollar(feedId),
name = searchedFeed.title!!,
Expand All @@ -102,10 +105,72 @@ class GoogleReaderRssService @Inject constructor(
SyncWorker.enqueueOneTimeWork(workManager)
}

override suspend fun addGroup(name: String): String {
override suspend fun addGroup(
destFeed: Feed?,
newGroupName: String,
): String {
val accountId = context.currentAccountId
getGoogleReaderAPI().subscriptionEdit(
destFeedId = destFeed?.id?.dollarLast(),
destCategoryId = newGroupName
)
val id = accountId.spacerDollar(newGroupName)
groupDao.insert(
Group(
id = id,
name = newGroupName,
accountId = accountId
)
)
return id
}

override suspend fun renameGroup(group: Group) {
getGoogleReaderAPI().renameTag(
categoryId = group.id.dollarLast(),
renameToName = group.name
)
// TODO: Whether to switch the old ID to the new ID?
super.renameGroup(group)
}

override suspend fun moveFeed(originGroupId: String, feed: Feed) {
getGoogleReaderAPI().subscriptionEdit(
destFeedId = feed.id.dollarLast(),
destCategoryId = feed.groupId.dollarLast(),
originCategoryId = originGroupId.dollarLast(),
)
super.moveFeed(originGroupId, feed)
}

override suspend fun changeFeedUrl(feed: Feed) {
throw Exception("Unsupported")
}

override suspend fun renameFeed(feed: Feed) {
getGoogleReaderAPI().subscriptionEdit(
destFeedId = feed.id.dollarLast(),
destFeedName = feed.name
)
// TODO: Whether to switch the old ID to the new ID?
super.renameFeed(feed)
}

override suspend fun deleteGroup(group: Group) {
feedDao.queryByGroupId(context.currentAccountId, group.id)
.forEach { deleteFeed(it) }
getGoogleReaderAPI().disableTag(group.id.dollarLast())
super.deleteGroup(group)
}

override suspend fun deleteFeed(feed: Feed) {
getGoogleReaderAPI().subscriptionEdit(
action = "unsubscribe",
destFeedId = feed.id.dollarLast()
)
super.deleteFeed(feed)
}

/**
* Google Reader API synchronous processing with object's ID to ensure idempotence
* and handle foreign key relationships such as read status, starred status, etc.
Expand Down Expand Up @@ -141,7 +206,7 @@ class GoogleReaderRssService @Inject constructor(
googleReaderAPI.getSubscriptionList()
.subscriptions.groupBy { it.categories?.first() }
.forEach { (category, feeds) ->
val groupId = accountId.spacerDollar(category?.id?.ofCategoryPathToId()!!)
val groupId = accountId.spacerDollar(category?.id?.ofCategoryStreamIdToId()!!)

// Handle folders
groupDao.insert(
Expand All @@ -156,7 +221,7 @@ class GoogleReaderRssService @Inject constructor(
// Handle feeds
feedDao.insert(
*feeds.map {
val feedId = accountId.spacerDollar(it.id?.ofFeedPathToId()!!)
val feedId = accountId.spacerDollar(it.id?.ofFeedStreamIdToId()!!)
Feed(
id = feedId,
name = it.title ?: context.getString(R.string.empty),
Expand Down Expand Up @@ -197,7 +262,7 @@ class GoogleReaderRssService @Inject constructor(
readIds?.map { it.id!! }?.chunked(100)?.forEach { chunkedIds ->
articleDao.insert(
*googleReaderAPI.getItemsContents(chunkedIds).items?.map {
val articleId = it.id!!.ofItemPathToId()
val articleId = it.id!!.ofItemStreamIdToId()
Article(
id = accountId.spacerDollar(articleId),
date = it.published?.run { Date(this * 1000) } ?: Date(),
Expand All @@ -213,7 +278,7 @@ class GoogleReaderRssService @Inject constructor(
link = it.canonical?.first()?.href
?: it.alternate?.first()?.href
?: it.origin?.htmlUrl ?: "",
feedId = accountId.spacerDollar(it.origin?.streamId?.ofFeedPathToId() ?: feedIds.first()),
feedId = accountId.spacerDollar(it.origin?.streamId?.ofFeedStreamIdToId() ?: feedIds.first()),
accountId = accountId,
isUnread = unreadIds?.contains(articleId) ?: true,
isStarred = starredIds?.contains(articleId) ?: false,
Expand Down
Loading

0 comments on commit c579c01

Please sign in to comment.