Skip to content

Commit

Permalink
[FreshRSS] Add support to fetch user labels (#736)
Browse files Browse the repository at this point in the history
* Add user label database tables

* Save label articles on refetch

* Display user labels and articles

* Add next filter handling
  • Loading branch information
jocmp authored Jan 28, 2025
1 parent b396043 commit adac736
Show file tree
Hide file tree
Showing 43 changed files with 886 additions and 90 deletions.
1 change: 0 additions & 1 deletion .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import com.jocmp.capy.ArticleStatus
import com.jocmp.capy.Feed
import com.jocmp.capy.Folder
import com.jocmp.capy.MarkRead
import com.jocmp.capy.SavedSearch
import com.jocmp.capy.common.launchUI
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
Expand All @@ -70,6 +71,7 @@ fun ArticleLayout(
filter: ArticleFilter,
folders: List<Folder>,
feeds: List<Feed>,
savedSearches: List<SavedSearch>,
allFeeds: List<Feed>,
allFolders: List<Folder>,
articles: LazyPagingItems<Article>,
Expand All @@ -79,6 +81,7 @@ fun ArticleLayout(
refreshInterval: RefreshInterval,
onFeedRefresh: (completion: () -> Unit) -> Unit,
onSelectFolder: (folderTitle: String) -> Unit,
onSelectSavedSearch: (savedSearchID: String) -> Unit,
onSelectFeed: (feedID: String, folderTitle: String?) -> Unit,
onSelectArticleFilter: () -> Unit,
onSelectStatus: (status: ArticleStatus) -> Unit,
Expand Down Expand Up @@ -301,6 +304,14 @@ fun ArticleLayout(
}
}

fun selectSavedSearch(savedSearch: SavedSearch) {
if (!filter.isSavedSearchSelected(savedSearch)) {
openNextList { onSelectSavedSearch(savedSearch.id) }
} else {
closeDrawer()
}
}

ArticleHandler {
selectArticle(it)
}
Expand All @@ -315,6 +326,8 @@ fun ArticleLayout(
onSelectFolder = ::selectFolder,
onSelectFeed = ::selectFeed,
onFeedAdded = { onFeedAdded(it) },
savedSearches = savedSearches,
onSelectSavedSearch = ::selectSavedSearch,
onNavigateToSettings = onNavigateToSettings,
onFilterSelect = ::selectFilter,
filter = filter,
Expand Down Expand Up @@ -362,7 +375,8 @@ fun ArticleLayout(
filter = filter,
currentFeed = currentFeed,
feeds = allFeeds,
allFolders = allFolders
savedSearches = savedSearches,
folders = allFolders
)
},
snackbarHost = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ fun ArticleScreen(
val allFeeds by viewModel.allFeeds.collectAsStateWithLifecycle(initialValue = emptyList())
val allFolders by viewModel.allFolders.collectAsStateWithLifecycle(initialValue = emptyList())
val folders by viewModel.folders.collectAsStateWithLifecycle(initialValue = emptyList())
val savedSearches by viewModel.savedSearches.collectAsStateWithLifecycle(initialValue = emptyList())
val statusCount by viewModel.statusCount.collectAsStateWithLifecycle(initialValue = 0)
val filter by viewModel.filter.collectAsStateWithLifecycle(appPreferences.filter.get())
val searchQuery by viewModel.searchQuery.collectAsState(initial = null)
Expand All @@ -51,6 +52,7 @@ fun ArticleScreen(
ArticleLayout(
filter = filter,
folders = folders,
savedSearches = savedSearches,
feeds = feeds,
allFolders = allFolders,
allFeeds = allFeeds,
Expand All @@ -64,6 +66,7 @@ fun ArticleScreen(
drawerState = drawerState,
onSelectFolder = viewModel::selectFolder,
onSelectFeed = viewModel::selectFeed,
onSelectSavedSearch = viewModel::selectSavedSearch,
onSelectArticleFilter = viewModel::selectArticleFilter,
onSelectStatus = viewModel::selectStatus,
onSelectArticle = viewModel::selectArticle,
Expand All @@ -78,8 +81,9 @@ fun ArticleScreen(
drawerState.open()
}
},
feeds = feeds,
searches = savedSearches,
folders = folders,
feeds = feeds,
range = range,
)
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import com.jocmp.capy.ArticleStatus
import com.jocmp.capy.Feed
import com.jocmp.capy.Folder
import com.jocmp.capy.MarkRead
import com.jocmp.capy.SavedSearch
import com.jocmp.capy.articles.ArticleContent
import com.jocmp.capy.articles.NextFilter
import com.jocmp.capy.buildArticlePager
Expand Down Expand Up @@ -92,6 +93,8 @@ class ArticleScreenViewModel(
.withPositiveCount(filter.status)
}

val savedSearches = account.savedSearches

val allFeeds = account.allFeeds

val allFolders = account.folders
Expand All @@ -106,8 +109,13 @@ class ArticleScreenViewModel(
}

private val nextFilterListener: Flow<NextFilter?> =
combine(feeds, folders, filter) { feeds, folders, filter ->
NextFilter.findSwipeDestination(filter, feeds, folders)
combine(savedSearches, feeds, folders, filter) { savedSearches, feeds, folders, filter ->
NextFilter.findSwipeDestination(
filter,
searches = savedSearches,
folders = folders,
feeds = feeds,
)
}

private val _nextFilter = MutableStateFlow<NextFilter?>(null)
Expand Down Expand Up @@ -161,6 +169,18 @@ class ArticleScreenViewModel(
}
}

fun selectSavedSearch(savedSearchID: String) {
viewModelScope.launch(Dispatchers.IO) {
val savedSearch = account.findSavedSearch(savedSearchID) ?: return@launch
val searchFilter = ArticleFilter.SavedSearches(
savedSearch.id,
savedSearchStatus = latestFilter.status
)

updateFilter(searchFilter)
}
}

fun selectFolder(title: String) {
viewModelScope.launch(Dispatchers.IO) {
val folder = account.findFolder(title) ?: return@launch
Expand All @@ -177,8 +197,9 @@ class ArticleScreenViewModel(
fun markAllRead(
onArticlesCleared: () -> Unit,
range: MarkRead,
feeds: List<Feed>,
searches: List<SavedSearch>,
folders: List<Folder>,
feeds: List<Feed>,
) {
viewModelScope.launchIO {
val articleIDs = account.unreadArticleIDs(
Expand All @@ -202,7 +223,7 @@ class ArticleScreenViewModel(
if (afterReadAll.value == AfterReadAllBehavior.OPEN_DRAWER) {
clearArticlesOnAllRead(onArticlesCleared)
} else if (afterReadAll.value == AfterReadAllBehavior.OPEN_NEXT_FEED) {
openNextFeedOnAllRead(onArticlesCleared, feeds, folders)
openNextFeedOnAllRead(onArticlesCleared, searches, folders, feeds)
}
}
}
Expand Down Expand Up @@ -337,6 +358,7 @@ class ArticleScreenViewModel(
)

is NextFilter.FolderFilter -> selectFolder(title = filter.folderTitle)
is NextFilter.SearchFilter -> selectSavedSearch(filter.savedSearchID)
}
}

Expand Down Expand Up @@ -511,11 +533,13 @@ class ArticleScreenViewModel(

private fun openNextFeedOnAllRead(
onArticlesCleared: () -> Unit,
feeds: List<Feed>,
searches: List<SavedSearch>,
folders: List<Folder>,
feeds: List<Feed>,
) {
val nextFilter = NextFilter.findMarkReadDestination(
latestFilter,
searches,
folders,
feeds,
)
Expand Down
19 changes: 18 additions & 1 deletion app/src/main/java/com/capyreader/app/ui/articles/FeedList.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,17 @@ import com.jocmp.capy.ArticleFilter
import com.jocmp.capy.ArticleStatus
import com.jocmp.capy.Feed
import com.jocmp.capy.Folder
import com.jocmp.capy.SavedSearch

@Composable
fun FeedList(
filter: ArticleFilter,
statusCount: Long,
folders: List<Folder> = emptyList(),
feeds: List<Feed> = emptyList(),
savedSearches: List<SavedSearch> = emptyList(),
onFilterSelect: () -> Unit,
onSelectSavedSearch: (search: SavedSearch) -> Unit,
onSelectFolder: (folder: Folder) -> Unit,
onSelectFeed: (feed: Feed, folderTitle: String?) -> Unit,
onFeedAdded: (feedID: String) -> Unit,
Expand Down Expand Up @@ -105,6 +108,19 @@ fun FeedList(
}
)

if (savedSearches.isNotEmpty()) {
FeedListDivider()
ListHeadline(text = stringResource(R.string.nav_headline_saved_searches))

savedSearches.forEach {
SavedSearchRow(
onSelect = onSelectSavedSearch,
selected = filter.isSavedSearchSelected(it),
savedSearch = it,
)
}
}

if (folders.isNotEmpty()) {
FeedListDivider()
ListHeadline(text = stringResource(R.string.nav_headline_tags))
Expand Down Expand Up @@ -183,6 +199,7 @@ fun FeedListPreview() {
filter = ArticleFilter.default(),
statusCount = 10,
onFeedAdded = {},
onSelectStatus = {}
onSelectStatus = {},
onSelectSavedSearch = {}
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ import com.capyreader.app.ui.navigationTitle
import com.jocmp.capy.ArticleFilter
import com.jocmp.capy.Feed
import com.jocmp.capy.Folder
import com.jocmp.capy.SavedSearch

@Composable
fun FilterAppBarTitle(
filter: ArticleFilter,
allFeeds: List<Feed>,
allFolders: List<Folder>,
allSavedSearches: List<SavedSearch>,
onRequestJumpToTop: () -> Unit
) {
val text = when (filter) {
Expand All @@ -36,6 +38,9 @@ fun FilterAppBarTitle(
is ArticleFilter.Folders -> {
allFolders.find { it.title == filter.folderTitle }?.title
}

is ArticleFilter.SavedSearches ->
allSavedSearches.find { it.id == filter.savedSearchID }?.name
}.orEmpty()

Box(
Expand Down Expand Up @@ -67,6 +72,7 @@ fun FilterAppBarTitlePreview() {
filter = ArticleFilter.default(),
allFeeds = emptyList(),
allFolders = emptyList(),
allSavedSearches = emptyList(),
onRequestJumpToTop = {}
)
})
Expand Down
40 changes: 40 additions & 0 deletions app/src/main/java/com/capyreader/app/ui/articles/SavedSearchRow.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.capyreader.app.ui.articles

import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationDrawerItem
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import com.jocmp.capy.SavedSearch

@Composable
fun SavedSearchRow(
onSelect: (savedSearch: SavedSearch) -> Unit,
selected: Boolean,
savedSearch: SavedSearch,
) {
NavigationDrawerItem(
label = { ListTitle(savedSearch.name) },
selected = selected,
onClick = {
onSelect(savedSearch)
}
)
}

@Preview
@Composable
fun SavedSearchRowPreview() {
val savedSearch = SavedSearch(
id = "123",
name = "Galaxy S25",
query = null,
)

MaterialTheme {
SavedSearchRow (
savedSearch = savedSearch,
onSelect = {},
selected = false
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import com.jocmp.capy.ArticleFilter
import com.jocmp.capy.Feed
import com.jocmp.capy.Folder
import com.jocmp.capy.MarkRead
import com.jocmp.capy.SavedSearch

@OptIn(ExperimentalMaterial3Api::class)
@Composable
Expand All @@ -52,7 +53,8 @@ fun FeedListTopBar(
filter: ArticleFilter,
currentFeed: Feed?,
feeds: List<Feed>,
allFolders: List<Folder>,
savedSearches: List<SavedSearch>,
folders: List<Folder>,
) {
val editSuccessMessage = stringResource(R.string.feed_action_edit_success)
val unsubscribeMessage = stringResource(R.string.feed_action_unsubscribe_success)
Expand Down Expand Up @@ -116,7 +118,8 @@ fun FeedListTopBar(
FilterAppBarTitle(
filter = filter,
allFeeds = feeds,
allFolders = allFolders,
allFolders = folders,
allSavedSearches = savedSearches,
onRequestJumpToTop = onRequestJumpToTop
)
}
Expand Down Expand Up @@ -189,6 +192,7 @@ private fun FeedListTopBarPreview() {
filter = ArticleFilter.default(),
currentFeed = null,
feeds = listOf(),
allFolders = emptyList()
savedSearches = emptyList(),
folders = emptyList()
)
}
2 changes: 1 addition & 1 deletion app/src/main/res/values-b+sr+Latn/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -211,4 +211,4 @@
<string name="settings_enable_high_contrast_dark_theme">Omogućite tamnu temu visokog kontrasta</string>
<string name="settings_gestures_reader_tap_to_page_title">E Mastilo dodirnite za pomeranje</string>
<string name="settings_gestures_reader_tap_to_page_subtitle">Dodirnite donje uglove da biste se kretali po sadržaju članka</string>
</resources>
</resources>
2 changes: 1 addition & 1 deletion app/src/main/res/values-cy/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -212,4 +212,4 @@
<string name="add_account_freshrss_signup_link_text">Dysgu mwy.</string>
<string name="transfers_export_subscriptions">Allforio\'r tanysgrifiadau</string>
<string name="article_view_open_externally">Agor yn allanol</string>
</resources>
</resources>
2 changes: 1 addition & 1 deletion app/src/main/res/values-pt-rBR/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -220,4 +220,4 @@
<string name="full_content_error_generic">Falha ao analisar artigo</string>
<string name="settings_donate_button">Doar</string>
<string name="after_read_all_behavior_default">Padrão</string>
</resources>
</resources>
2 changes: 1 addition & 1 deletion app/src/main/res/values-ta/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -207,4 +207,4 @@
<string name="settings_confirm_mark_all_read">அனைத்தையும் படித்ததாகக் குறிப்பதை உறுதிப்படுத்தவும்</string>
<string name="settings_mark_read_on_scroll">சுருளில் படிக்க மார்க்</string>
<string name="settings_section_browser">உலாவி</string>
</resources>
</resources>
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
</plurals>
<string name="refresh_feeds_menu_label">Refresh Feeds</string>
<string name="nav_headline_tags">Tags</string>
<string name="nav_headline_saved_searches">Searches</string>
<string name="nav_headline_feeds">Feeds</string>
<string name="nav_add_feed">Add Feed</string>
<string name="add_feed_feed_not_error">Couldn\'t find feed</string>
Expand Down
Loading

0 comments on commit adac736

Please sign in to comment.