Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
452 changes: 452 additions & 0 deletions app/schemas/me.ash.reader.infrastructure.db.AndroidDatabase/8.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package me.ash.reader.domain.model.feed

import androidx.room.Embedded
import androidx.room.Relation
import me.ash.reader.domain.model.group.Group

/**
* A [feed] with filtered keywords.
*/
data class FeedWithKeywordFilter(
@Embedded
var feed: Feed,
@Relation(parentColumn = "feedId", entityColumn = "id")
var keywordFilters: List<KeywordFilter>,
)
25 changes: 25 additions & 0 deletions app/src/main/java/me/ash/reader/domain/model/feed/KeywordFilter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package me.ash.reader.domain.model.feed

import androidx.room.Entity
import androidx.room.ForeignKey

/**
* TODO: Add class description
*/
@Entity(
tableName = "keyword_filter",
primaryKeys = ["feedId", "keyword"],
foreignKeys = [
ForeignKey(
entity = Feed::class,
parentColumns = ["id"],
childColumns = ["feedId"],
onDelete = ForeignKey.CASCADE,
onUpdate = ForeignKey.CASCADE,
)
]
)
data class KeywordFilter(
val feedId: String,
val keyword: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package me.ash.reader.domain.model.group

import androidx.room.Embedded
import androidx.room.Relation
import me.ash.reader.domain.model.feed.Feed
import me.ash.reader.domain.model.feed.FeedWithKeywordFilter

/**
* A [group] contains many [feeds], each of which has [keyword_filters].
*/
data class GroupWithFeedWithKeywordFilters(
@Embedded
val group: Group,
@Relation(parentColumn = "id", entityColumn = "groupId")
val feedsWithKeywordFilters: MutableList<FeedWithKeywordFilter>,
)
18 changes: 18 additions & 0 deletions app/src/main/java/me/ash/reader/domain/repository/ArticleDao.kt
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,16 @@ interface ArticleDao {
)
suspend fun deleteByFeedId(accountId: Int, feedId: String, includeStarred: Boolean = false)

@Query(
"""
DELETE FROM article
WHERE accountId = :accountId
AND feedId = :feedId
AND title LIKE '%' || :keyword || '%'
"""
)
suspend fun deleteByKeyword(accountId: Int, feedId: String, keyword: String)

@Query(
"""
DELETE FROM article
Expand Down Expand Up @@ -917,4 +927,12 @@ interface ArticleDao {

return articles.filterNot { existingArticles.containsKey(it.link) }.also { insertList(it) }
}

@Transaction
suspend fun deleteList(articles: List<Article>) {
articles.forEach {
println("delete : " + it.title)
delete(it)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package me.ash.reader.domain.repository

import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import kotlinx.coroutines.flow.Flow
import me.ash.reader.domain.model.article.Article
import me.ash.reader.domain.model.feed.FeedWithKeywordFilter
import me.ash.reader.domain.model.feed.KeywordFilter

@Dao
interface KeywordFilterDao {

@Query(
"""
SELECT * FROM `keyword_filter`
"""
)
fun queryAll(): Flow<MutableList<KeywordFilter>>

@Query(
"""
SELECT * FROM `keyword_filter`
"""
)
suspend fun queryAllBlocking(): List<KeywordFilter>

@Query(
"""
SELECT * FROM `keyword_filter`
WHERE feedId = :feedId
"""
)
suspend fun queryAllWithFeedId(feedId: String): List<KeywordFilter>

@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(vararg filter: KeywordFilter)

@Delete
suspend fun delete(vararg filter: KeywordFilter)

@Insert
suspend fun insertList(filters: List<KeywordFilter>)
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOn
import me.ash.reader.domain.model.feed.KeywordFilter
import me.ash.reader.domain.model.account.Account
import me.ash.reader.domain.model.article.ArchivedArticle
import me.ash.reader.domain.model.article.Article
Expand All @@ -22,6 +23,7 @@ import me.ash.reader.domain.model.group.GroupWithFeed
import me.ash.reader.domain.repository.ArticleDao
import me.ash.reader.domain.repository.FeedDao
import me.ash.reader.domain.repository.GroupDao
import me.ash.reader.domain.repository.KeywordFilterDao
import me.ash.reader.infrastructure.android.NotificationHelper
import me.ash.reader.infrastructure.preference.KeepArchivedPreference
import me.ash.reader.infrastructure.preference.SyncIntervalPreference
Expand All @@ -32,6 +34,7 @@ import me.ash.reader.ui.ext.spacerDollar
abstract class AbstractRssRepository(
private val articleDao: ArticleDao,
private val groupDao: GroupDao,
private val keywordFilterDao: KeywordFilterDao,
private val feedDao: FeedDao,
private val workManager: WorkManager,
private val rssHelper: RssHelper,
Expand All @@ -58,6 +61,7 @@ abstract class AbstractRssRepository(
isNotification: Boolean,
isFullContent: Boolean,
isBrowser: Boolean,
filteredKeywords: List<String>
) {
val accountId = accountService.getCurrentAccountId()
val feed =
Expand All @@ -72,10 +76,14 @@ abstract class AbstractRssRepository(
isNotification = isNotification,
isFullContent = isFullContent,
)
val articles =
searchedFeed.entries.map { rssHelper.buildArticleFromSyndEntry(feed, accountId, it) }
val articles = searchedFeed.entries.filter { article ->
filteredKeywords.none { keyword ->
article.title.lowercase().contains(keyword.lowercase())
}
}.map { rssHelper.buildArticleFromSyndEntry(feed, accountId, it) }
feedDao.insert(feed)
articleDao.insertList(articles.map { it.copy(feedId = feed.id) })
keywordFilterDao.insertList(filteredKeywords.map { KeywordFilter(feed.id, it) })
}

open suspend fun addGroup(destFeed: Feed?, newGroupName: String): String {
Expand All @@ -86,6 +94,10 @@ abstract class AbstractRssRepository(
}
}

open suspend fun addFilteredKeyword(feed: Feed, newFilteredKeyword: String) {
keywordFilterDao.insert(KeywordFilter(feedId = feed.id, keyword = newFilteredKeyword))
}

abstract suspend fun sync(
accountId: Int,
feedId: String?,
Expand Down Expand Up @@ -209,6 +221,9 @@ abstract class AbstractRssRepository(
fun pullGroups(): Flow<MutableList<Group>> =
groupDao.queryAllGroup(accountService.getCurrentAccountId()).flowOn(dispatcherIO)

fun pullFilteredKeywords(): Flow<MutableList<KeywordFilter>> =
keywordFilterDao.queryAll().flowOn(dispatcherIO)

fun pullFeeds(): Flow<MutableList<GroupWithFeed>> =
groupDao
.queryAllGroupWithFeedAsFlow(accountService.getCurrentAccountId())
Expand Down Expand Up @@ -341,6 +356,10 @@ abstract class AbstractRssRepository(
groupDao.delete(group)
}

open suspend fun deleteFilteredKeyword(keyword: KeywordFilter) {
keywordFilterDao.delete(keyword)
}

open suspend fun deleteFeed(feed: Feed, onlyDeleteNoStarred: Boolean? = false) {
if (
onlyDeleteNoStarred == true &&
Expand All @@ -359,9 +378,17 @@ abstract class AbstractRssRepository(
suspend fun deleteArticles(
group: Group? = null,
feed: Feed? = null,
keywordFilter: String? = null,
includeStarred: Boolean = false,
) {
when {
feed != null && keywordFilter != null ->
articleDao.deleteByKeyword(
accountService.getCurrentAccountId(),
feed.id,
keywordFilter,
)

group != null ->
articleDao.deleteByGroupId(
accountService.getCurrentAccountId(),
Expand Down
12 changes: 12 additions & 0 deletions app/src/main/java/me/ash/reader/domain/service/FeverRssService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import me.ash.reader.domain.model.group.Group
import me.ash.reader.domain.repository.ArticleDao
import me.ash.reader.domain.repository.FeedDao
import me.ash.reader.domain.repository.GroupDao
import me.ash.reader.domain.repository.KeywordFilterDao
import me.ash.reader.infrastructure.android.NotificationHelper
import me.ash.reader.infrastructure.di.DefaultDispatcher
import me.ash.reader.infrastructure.di.IODispatcher
Expand All @@ -47,6 +48,7 @@ constructor(
private val rssHelper: RssHelper,
private val notificationHelper: NotificationHelper,
private val groupDao: GroupDao,
private val keywordFilterDao: KeywordFilterDao,
@IODispatcher private val ioDispatcher: CoroutineDispatcher,
@MainDispatcher private val mainDispatcher: CoroutineDispatcher,
@DefaultDispatcher private val defaultDispatcher: CoroutineDispatcher,
Expand All @@ -56,6 +58,7 @@ constructor(
AbstractRssRepository(
articleDao,
groupDao,
keywordFilterDao,
feedDao,
workManager,
rssHelper,
Expand Down Expand Up @@ -98,6 +101,7 @@ constructor(
isNotification: Boolean,
isFullContent: Boolean,
isBrowser: Boolean,
filteredKeywords: List<String>
) {
throw FeverAPIException("Unsupported")
}
Expand Down Expand Up @@ -227,6 +231,7 @@ constructor(
break
}

val allFilteredKeywords = keywordFilterDao.queryAllBlocking()
val articlesFromBatch =
fetchedItems.map { item ->
Article(
Expand All @@ -248,6 +253,13 @@ constructor(
isStarred = (item.is_saved ?: 0) > 0,
updateAt = preDate,
)
}.filter { article ->
val filteredKeywordsForThisArticle = allFilteredKeywords.filter { keyword ->
keyword.feedId == article.feedId
}
filteredKeywordsForThisArticle.none { keyword ->
article.title.lowercase().contains(keyword.keyword.lowercase())
}
}

allArticles.addAll(articlesFromBatch)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,12 @@ import me.ash.reader.domain.model.account.AccountType.Companion.FreshRSS
import me.ash.reader.domain.model.account.security.GoogleReaderSecurityKey
import me.ash.reader.domain.model.article.Article
import me.ash.reader.domain.model.feed.Feed
import me.ash.reader.domain.model.feed.KeywordFilter
import me.ash.reader.domain.model.group.Group
import me.ash.reader.domain.repository.ArticleDao
import me.ash.reader.domain.repository.FeedDao
import me.ash.reader.domain.repository.GroupDao
import me.ash.reader.domain.repository.KeywordFilterDao
import me.ash.reader.infrastructure.android.NotificationHelper
import me.ash.reader.infrastructure.di.DefaultDispatcher
import me.ash.reader.infrastructure.di.IODispatcher
Expand Down Expand Up @@ -71,6 +73,7 @@ constructor(
private val rssHelper: RssHelper,
private val notificationHelper: NotificationHelper,
private val groupDao: GroupDao,
private val keywordFilterDao: KeywordFilterDao,
@IODispatcher private val ioDispatcher: CoroutineDispatcher,
@MainDispatcher private val mainDispatcher: CoroutineDispatcher,
@DefaultDispatcher private val defaultDispatcher: CoroutineDispatcher,
Expand All @@ -81,6 +84,7 @@ constructor(
AbstractRssRepository(
articleDao,
groupDao,
keywordFilterDao,
feedDao,
workManager,
rssHelper,
Expand Down Expand Up @@ -134,6 +138,7 @@ constructor(
isNotification: Boolean,
isFullContent: Boolean,
isBrowser: Boolean,
filteredKeywords: List<String>
) {
val accountId = accountService.getCurrentAccountId()
val quickAdd = getGoogleReaderAPI().subscriptionQuickAdd(feedLink)
Expand Down Expand Up @@ -396,6 +401,7 @@ constructor(
unreadIds = remoteUnreadIds.await(),
starredIds = remoteStarredIds.await(),
scope = this,
allFilteredKeywords = keywordFilterDao.queryAllBlocking()
)
.toMutableList()

Expand Down Expand Up @@ -563,6 +569,7 @@ constructor(
accountId = accountId,
unreadIds = remoteUnreadIds.await(),
starredIds = remoteStarredIds.await(),
allFilteredKeywords = keywordFilterDao.queryAllBlocking()
)

if (feed.isNotification) {
Expand Down Expand Up @@ -626,6 +633,7 @@ constructor(
unreadIds: Set<String>,
starredIds: Set<String>,
scope: CoroutineScope,
allFilteredKeywords: List<KeywordFilter>,
): List<Deferred<List<Article>>> {
if (itemIds.isEmpty()) return emptyList()
val currentDate = Date()
Expand Down Expand Up @@ -671,7 +679,14 @@ constructor(
?: currentDate,
)
},
)
).filter { article ->
val filteredKeywordsForThisArticle = allFilteredKeywords.filter { keyword ->
keyword.feedId == article.feedId
}
filteredKeywordsForThisArticle.none { keyword ->
article.title.lowercase().contains(keyword.keyword.lowercase())
}
}
}
}
}
Expand All @@ -683,6 +698,7 @@ constructor(
accountId: Int,
unreadIds: Set<String>,
starredIds: Set<String>,
allFilteredKeywords: List<KeywordFilter>
): List<Article> = supervisorScope {
fetchItemsContentsDeferred(
itemIds = itemIds,
Expand All @@ -691,6 +707,7 @@ constructor(
unreadIds = unreadIds,
starredIds = starredIds,
scope = this,
allFilteredKeywords = allFilteredKeywords,
)
.awaitAll()
.flatten()
Expand Down
Loading
Loading