aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Frédéric Guillot <fred@miniflux.net>2019-11-17 19:44:12 -0800
committerGravatar Frédéric Guillot <fred@miniflux.net>2019-11-17 20:10:44 -0800
commitfad9ad2be4fc800f8710e2a498cc8f536af8827c (patch)
tree8bc4f134b63f100442850bd9eb443f03fffcf7e6
parent15fe9c20df7eaab4c1e10461f1a9965eeaf85f0f (diff)
Display list of feeds per category
-rw-r--r--locale/translations.go36
-rw-r--r--locale/translations/de_DE.json2
-rw-r--r--locale/translations/en_US.json2
-rw-r--r--locale/translations/es_ES.json2
-rw-r--r--locale/translations/fr_FR.json2
-rw-r--r--locale/translations/it_IT.json2
-rw-r--r--locale/translations/nl_NL.json2
-rw-r--r--locale/translations/pl_PL.json2
-rw-r--r--locale/translations/ru_RU.json2
-rw-r--r--locale/translations/zh_CN.json2
-rw-r--r--storage/feed.go42
-rw-r--r--template/common.go57
-rw-r--r--template/html/categories.html9
-rw-r--r--template/html/category_entries.html3
-rw-r--r--template/html/category_feeds.html34
-rw-r--r--template/html/common/feed_list.html56
-rw-r--r--template/html/edit_category.html5
-rw-r--r--template/html/feeds.html55
-rw-r--r--template/views.go116
-rw-r--r--ui/category_feeds.go52
-rw-r--r--ui/category_update.go2
-rw-r--r--ui/ui.go1
22 files changed, 345 insertions, 141 deletions
diff --git a/locale/translations.go b/locale/translations.go
index b0e09b3..fa04cde 100644
--- a/locale/translations.go
+++ b/locale/translations.go
@@ -76,6 +76,7 @@ var translations = map[string]string{
"page.starred.title": "Lesezeichen",
"page.categories.title": "Kategorien",
"page.categories.no_feed": "Kein Abonnement.",
+ "page.categories.feeds": "Siehe Abonnements",
"page.categories.feed_count": [
"Es gibt %d Abonnement.",
"Es gibt %d Abonnements."
@@ -175,6 +176,7 @@ var translations = map[string]string{
"alert.no_category_entry": "Es befindet sich kein Artikel in dieser Kategorie.",
"alert.no_feed_entry": "Es existiert kein Artikel für dieses Abonnement.",
"alert.no_feed": "Es sind keine Abonnements vorhanden.",
+ "alert.no_feed_in_category": "Für diese Kategorie gibt es kein Abonnement.",
"alert.no_history": "Es existiert zur Zeit kein Verlauf.",
"alert.feed_error": "Es gibt ein Problem mit diesem Abonnement",
"alert.no_search_result": "Es gibt kein Ergebnis für diese Suche.",
@@ -379,6 +381,7 @@ var translations = map[string]string{
"page.starred.title": "Starred",
"page.categories.title": "Categories",
"page.categories.no_feed": "No feed.",
+ "page.categories.feeds": "See subscriptions",
"page.categories.feed_count": [
"There is %d feed.",
"There are %d feeds."
@@ -478,6 +481,7 @@ var translations = map[string]string{
"alert.no_category_entry": "There are no articles in this category.",
"alert.no_feed_entry": "There are no articles for this feed.",
"alert.no_feed": "You don't have any subscriptions.",
+ "alert.no_feed_in_category": "There is no subscription for this category.",
"alert.no_history": "There is no history at the moment.",
"alert.feed_error": "There is a problem with this feed",
"alert.no_search_result": "There are no results for this search.",
@@ -662,6 +666,7 @@ var translations = map[string]string{
"page.starred.title": "Marcadores",
"page.categories.title": "Categorias",
"page.categories.no_feed": "No fuente.",
+ "page.categories.feeds": "Ver suscripciones",
"page.categories.feed_count": [
"Hay %d fuente.",
"Hay %d fuentes."
@@ -761,6 +766,7 @@ var translations = map[string]string{
"alert.no_category_entry": "No hay artículos en esta categoria.",
"alert.no_feed_entry": "No hay artículos para esta fuente.",
"alert.no_feed": "No tienes suscripciones.",
+ "alert.no_feed_in_category": "No hay suscripción para esta categoría.",
"alert.no_history": "No hay historial en este momento.",
"alert.feed_error": "Hay un problema con esta fuente.",
"alert.no_search_result": "No hay resultados para esta búsqueda.",
@@ -945,6 +951,7 @@ var translations = map[string]string{
"page.starred.title": "Favoris",
"page.categories.title": "Catégories",
"page.categories.no_feed": "Aucun abonnement.",
+ "page.categories.feeds": "Voir les abonnements",
"page.categories.feed_count": [
"Il y a %d abonnement.",
"Il y a %d abonnements."
@@ -1044,6 +1051,7 @@ var translations = map[string]string{
"alert.no_category_entry": "Il n'y a aucun article dans cette catégorie.",
"alert.no_feed_entry": "Il n'y a aucun article pour cet abonnement.",
"alert.no_feed": "Vous n'avez aucun abonnement.",
+ "alert.no_feed_in_category": "Il n'y a pas d'abonnement pour cette catégorie.",
"alert.no_history": "Il n'y a aucun historique pour le moment.",
"alert.feed_error": "Il y a un problème avec cet abonnement",
"alert.no_search_result": "Il n'y a aucun résultat pour cette recherche.",
@@ -1248,6 +1256,7 @@ var translations = map[string]string{
"page.starred.title": "Preferiti",
"page.categories.title": "Categorie",
"page.categories.no_feed": "Nessun feed.",
+ "page.categories.feeds": "Vedi abbonamenti",
"page.categories.feed_count": [
"C'è %d feed.",
"Ci sono %d feed."
@@ -1347,6 +1356,7 @@ var translations = map[string]string{
"alert.no_category_entry": "Questa categoria non contiene alcun articolo.",
"alert.no_feed_entry": "Questo feed non contiene alcun articolo.",
"alert.no_feed": "Nessun feed disponibile.",
+ "alert.no_feed_in_category": "Non esiste un abbonamento per questa categoria.",
"alert.no_history": "La tua cronologia al momento è vuota.",
"alert.feed_error": "Sembra ci sia un problema con questo feed",
"alert.no_search_result": "La ricerca non ha prodotto risultati.",
@@ -1531,6 +1541,7 @@ var translations = map[string]string{
"page.starred.title": "Favorieten",
"page.categories.title": "Categorieën",
"page.categories.no_feed": "Geen feeds.",
+ "page.categories.feeds": "Zie abonnementen",
"page.categories.feed_count": [
"Er is %d feed.",
"Er zijn %d feeds."
@@ -1630,6 +1641,7 @@ var translations = map[string]string{
"alert.no_category_entry": "Deze categorie bevat geen feeds.",
"alert.no_feed_entry": "Er zijn geen artikelen in deze feed.",
"alert.no_feed": "Je hebt nog geen feeds geabboneerd staan.",
+ "alert.no_feed_in_category": "Er is geen abonnement voor deze categorie.",
"alert.no_history": "Geschiedenis is op dit moment leeg.",
"alert.feed_error": "Er is een probleem met deze feed",
"alert.no_search_result": "Er is geen resultaat voor deze zoekopdracht.",
@@ -1832,6 +1844,7 @@ var translations = map[string]string{
"page.starred.title": "Oznaczone gwiazdką",
"page.categories.title": "Kategorie",
"page.categories.no_feed": "Brak kanałów.",
+ "page.categories.feeds": "Zobacz subskrypcje",
"page.categories.feed_count": [
"Jest %d kanał.",
"Są %d kanały.",
@@ -1933,6 +1946,7 @@ var translations = map[string]string{
"alert.no_category_entry": "W tej kategorii nie ma żadnych artykułów",
"alert.no_feed_entry": "Nie ma artykułu dla tego kanału.",
"alert.no_feed": "Nie masz żadnej subskrypcji.",
+ "alert.no_feed_in_category": "Nie ma subskrypcji dla tej kategorii.",
"alert.no_history": "Obecnie nie ma żadnej historii.",
"alert.feed_error": "Z tym kanałem jest problem",
"alert.no_search_result": "Brak wyników dla tego wyszukiwania.",
@@ -2141,6 +2155,7 @@ var translations = map[string]string{
"page.starred.title": "Избранное",
"page.categories.title": "Категории",
"page.categories.no_feed": "Нет подписок.",
+ "page.categories.feeds": "Посмотреть подписку",
"page.categories.feed_count": [
"Есть %d подписка.",
"Есть %d подписки.",
@@ -2242,6 +2257,7 @@ var translations = map[string]string{
"alert.no_category_entry": "В этой категории нет статей.",
"alert.no_feed_entry": "В этой подписке отсутствуют статьи.",
"alert.no_feed": "У вас нет ни одной подписки.",
+ "alert.no_feed_in_category": "Для этой категории нет подписки.",
"alert.no_history": "Истории пока нет.",
"alert.feed_error": "С этой подпиской есть проблема",
"alert.no_search_result": "Нет результатов для данного поискового запроса.",
@@ -2432,6 +2448,7 @@ var translations = map[string]string{
"page.starred.title": "星标",
"page.categories.title": "分类",
"page.categories.no_feed": "没有源",
+ "page.categories.feeds": "查看订阅",
"page.categories.feed_count": [
"有 %d 个源"
],
@@ -2532,6 +2549,7 @@ var translations = map[string]string{
"alert.no_history": "目前没有历史",
"alert.feed_error": "该源存在问题",
"alert.no_search_result": "该搜索没有结果",
+ "alert.no_feed_in_category": "没有该类别的订阅。",
"alert.no_unread_entry": "目前没有未读文章",
"alert.no_user": "您是目前仅有的用户",
"alert.account_unlinked": "您的外部帐户现已解除关联!",
@@ -2656,13 +2674,13 @@ var translations = map[string]string{
}
var translationsChecksums = map[string]string{
- "de_DE": "ec2dd4be11e4bb29efaa6cd124a1edd2e5271889d31d7fda92781be014388387",
- "en_US": "8010481cea76d28aad37c00fb0f481514f81c1581a6172b4a4ad17ad61e2eee0",
- "es_ES": "be58c4452068277826022931d86bd561fe150250756a6254985de5aa6d8129b7",
- "fr_FR": "4d3fa6084994a7b3121dd9c1f3baf8c1b0e519f6012aeaa805e5132ec1eaa60e",
- "it_IT": "3246b020b7ea01f762f19c9ee2825605b0e42f6ffdd34fd6306193597650f8d7",
- "nl_NL": "c58d1100bcd345824086d0df255381f7789379d0e2b95e146be009ad82e0aa5f",
- "pl_PL": "b438c4119ed5685950293f5c3c629a940c6475ca243c53f65879dfca6fc8cdd6",
- "ru_RU": "5b42510b54c678563791010f4c1fad1a024fa7029d268667bbfde1e7a1f02d88",
- "zh_CN": "30f72b341911682877bb86b0e82e9127be833625502ae41e9530447bb2f27de3",
+ "de_DE": "08618fb4a57b1d427ec627ac6c46958bbe16b262a588aa640ad5be8b5e15562d",
+ "en_US": "a611f133d106bb896bbe15df036838f33b7c7848abbf15829e918233e91783eb",
+ "es_ES": "f47862bcd9af07d96510c3c24f89f0718f1325cde375954babe73766c62a6eca",
+ "fr_FR": "0aca382d06630935b905452960a8dceac6f062cd5ebe2967b3837006a282f171",
+ "it_IT": "60716683ca6d6e311154900508c0f8c389664096f3ab41eb9dd745914d497034",
+ "nl_NL": "516c91a2be0f0b5c09e0183d61e53bfce2c8f60b8a5cb06593f0170ad6043f99",
+ "pl_PL": "1fcf9422514fc7ebac57355a50c37d146fad6d19b879ba0dad29c49dfb20e00a",
+ "ru_RU": "e701297d7c1b456dda066877bc5c6d18c9523f96eff3b4d53d13c6e6bf8f84a6",
+ "zh_CN": "4da796ef2fdaf1898d2a17be6668b8308daebc97185bf69a698809067856c6ce",
}
diff --git a/locale/translations/de_DE.json b/locale/translations/de_DE.json
index 83325e0..4960836 100644
--- a/locale/translations/de_DE.json
+++ b/locale/translations/de_DE.json
@@ -71,6 +71,7 @@
"page.starred.title": "Lesezeichen",
"page.categories.title": "Kategorien",
"page.categories.no_feed": "Kein Abonnement.",
+ "page.categories.feeds": "Siehe Abonnements",
"page.categories.feed_count": [
"Es gibt %d Abonnement.",
"Es gibt %d Abonnements."
@@ -170,6 +171,7 @@
"alert.no_category_entry": "Es befindet sich kein Artikel in dieser Kategorie.",
"alert.no_feed_entry": "Es existiert kein Artikel für dieses Abonnement.",
"alert.no_feed": "Es sind keine Abonnements vorhanden.",
+ "alert.no_feed_in_category": "Für diese Kategorie gibt es kein Abonnement.",
"alert.no_history": "Es existiert zur Zeit kein Verlauf.",
"alert.feed_error": "Es gibt ein Problem mit diesem Abonnement",
"alert.no_search_result": "Es gibt kein Ergebnis für diese Suche.",
diff --git a/locale/translations/en_US.json b/locale/translations/en_US.json
index c86e3e2..2153243 100644
--- a/locale/translations/en_US.json
+++ b/locale/translations/en_US.json
@@ -71,6 +71,7 @@
"page.starred.title": "Starred",
"page.categories.title": "Categories",
"page.categories.no_feed": "No feed.",
+ "page.categories.feeds": "See subscriptions",
"page.categories.feed_count": [
"There is %d feed.",
"There are %d feeds."
@@ -170,6 +171,7 @@
"alert.no_category_entry": "There are no articles in this category.",
"alert.no_feed_entry": "There are no articles for this feed.",
"alert.no_feed": "You don't have any subscriptions.",
+ "alert.no_feed_in_category": "There is no subscription for this category.",
"alert.no_history": "There is no history at the moment.",
"alert.feed_error": "There is a problem with this feed",
"alert.no_search_result": "There are no results for this search.",
diff --git a/locale/translations/es_ES.json b/locale/translations/es_ES.json
index ba8f076..de2912a 100644
--- a/locale/translations/es_ES.json
+++ b/locale/translations/es_ES.json
@@ -71,6 +71,7 @@
"page.starred.title": "Marcadores",
"page.categories.title": "Categorias",
"page.categories.no_feed": "No fuente.",
+ "page.categories.feeds": "Ver suscripciones",
"page.categories.feed_count": [
"Hay %d fuente.",
"Hay %d fuentes."
@@ -170,6 +171,7 @@
"alert.no_category_entry": "No hay artículos en esta categoria.",
"alert.no_feed_entry": "No hay artículos para esta fuente.",
"alert.no_feed": "No tienes suscripciones.",
+ "alert.no_feed_in_category": "No hay suscripción para esta categoría.",
"alert.no_history": "No hay historial en este momento.",
"alert.feed_error": "Hay un problema con esta fuente.",
"alert.no_search_result": "No hay resultados para esta búsqueda.",
diff --git a/locale/translations/fr_FR.json b/locale/translations/fr_FR.json
index 052913c..9df25d6 100644
--- a/locale/translations/fr_FR.json
+++ b/locale/translations/fr_FR.json
@@ -71,6 +71,7 @@
"page.starred.title": "Favoris",
"page.categories.title": "Catégories",
"page.categories.no_feed": "Aucun abonnement.",
+ "page.categories.feeds": "Voir les abonnements",
"page.categories.feed_count": [
"Il y a %d abonnement.",
"Il y a %d abonnements."
@@ -170,6 +171,7 @@
"alert.no_category_entry": "Il n'y a aucun article dans cette catégorie.",
"alert.no_feed_entry": "Il n'y a aucun article pour cet abonnement.",
"alert.no_feed": "Vous n'avez aucun abonnement.",
+ "alert.no_feed_in_category": "Il n'y a pas d'abonnement pour cette catégorie.",
"alert.no_history": "Il n'y a aucun historique pour le moment.",
"alert.feed_error": "Il y a un problème avec cet abonnement",
"alert.no_search_result": "Il n'y a aucun résultat pour cette recherche.",
diff --git a/locale/translations/it_IT.json b/locale/translations/it_IT.json
index 60daa20..9d84f96 100644
--- a/locale/translations/it_IT.json
+++ b/locale/translations/it_IT.json
@@ -71,6 +71,7 @@
"page.starred.title": "Preferiti",
"page.categories.title": "Categorie",
"page.categories.no_feed": "Nessun feed.",
+ "page.categories.feeds": "Vedi abbonamenti",
"page.categories.feed_count": [
"C'è %d feed.",
"Ci sono %d feed."
@@ -170,6 +171,7 @@
"alert.no_category_entry": "Questa categoria non contiene alcun articolo.",
"alert.no_feed_entry": "Questo feed non contiene alcun articolo.",
"alert.no_feed": "Nessun feed disponibile.",
+ "alert.no_feed_in_category": "Non esiste un abbonamento per questa categoria.",
"alert.no_history": "La tua cronologia al momento è vuota.",
"alert.feed_error": "Sembra ci sia un problema con questo feed",
"alert.no_search_result": "La ricerca non ha prodotto risultati.",
diff --git a/locale/translations/nl_NL.json b/locale/translations/nl_NL.json
index 87e2e08..0765a7d 100644
--- a/locale/translations/nl_NL.json
+++ b/locale/translations/nl_NL.json
@@ -71,6 +71,7 @@
"page.starred.title": "Favorieten",
"page.categories.title": "Categorieën",
"page.categories.no_feed": "Geen feeds.",
+ "page.categories.feeds": "Zie abonnementen",
"page.categories.feed_count": [
"Er is %d feed.",
"Er zijn %d feeds."
@@ -170,6 +171,7 @@
"alert.no_category_entry": "Deze categorie bevat geen feeds.",
"alert.no_feed_entry": "Er zijn geen artikelen in deze feed.",
"alert.no_feed": "Je hebt nog geen feeds geabboneerd staan.",
+ "alert.no_feed_in_category": "Er is geen abonnement voor deze categorie.",
"alert.no_history": "Geschiedenis is op dit moment leeg.",
"alert.feed_error": "Er is een probleem met deze feed",
"alert.no_search_result": "Er is geen resultaat voor deze zoekopdracht.",
diff --git a/locale/translations/pl_PL.json b/locale/translations/pl_PL.json
index 9fe141b..413945b 100644
--- a/locale/translations/pl_PL.json
+++ b/locale/translations/pl_PL.json
@@ -71,6 +71,7 @@
"page.starred.title": "Oznaczone gwiazdką",
"page.categories.title": "Kategorie",
"page.categories.no_feed": "Brak kanałów.",
+ "page.categories.feeds": "Zobacz subskrypcje",
"page.categories.feed_count": [
"Jest %d kanał.",
"Są %d kanały.",
@@ -172,6 +173,7 @@
"alert.no_category_entry": "W tej kategorii nie ma żadnych artykułów",
"alert.no_feed_entry": "Nie ma artykułu dla tego kanału.",
"alert.no_feed": "Nie masz żadnej subskrypcji.",
+ "alert.no_feed_in_category": "Nie ma subskrypcji dla tej kategorii.",
"alert.no_history": "Obecnie nie ma żadnej historii.",
"alert.feed_error": "Z tym kanałem jest problem",
"alert.no_search_result": "Brak wyników dla tego wyszukiwania.",
diff --git a/locale/translations/ru_RU.json b/locale/translations/ru_RU.json
index 27ed092..03ba8cb 100644
--- a/locale/translations/ru_RU.json
+++ b/locale/translations/ru_RU.json
@@ -71,6 +71,7 @@
"page.starred.title": "Избранное",
"page.categories.title": "Категории",
"page.categories.no_feed": "Нет подписок.",
+ "page.categories.feeds": "Посмотреть подписку",
"page.categories.feed_count": [
"Есть %d подписка.",
"Есть %d подписки.",
@@ -172,6 +173,7 @@
"alert.no_category_entry": "В этой категории нет статей.",
"alert.no_feed_entry": "В этой подписке отсутствуют статьи.",
"alert.no_feed": "У вас нет ни одной подписки.",
+ "alert.no_feed_in_category": "Для этой категории нет подписки.",
"alert.no_history": "Истории пока нет.",
"alert.feed_error": "С этой подпиской есть проблема",
"alert.no_search_result": "Нет результатов для данного поискового запроса.",
diff --git a/locale/translations/zh_CN.json b/locale/translations/zh_CN.json
index 8748695..da0febc 100644
--- a/locale/translations/zh_CN.json
+++ b/locale/translations/zh_CN.json
@@ -71,6 +71,7 @@
"page.starred.title": "星标",
"page.categories.title": "分类",
"page.categories.no_feed": "没有源",
+ "page.categories.feeds": "查看订阅",
"page.categories.feed_count": [
"有 %d 个源"
],
@@ -171,6 +172,7 @@
"alert.no_history": "目前没有历史",
"alert.feed_error": "该源存在问题",
"alert.no_search_result": "该搜索没有结果",
+ "alert.no_feed_in_category": "没有该类别的订阅。",
"alert.no_unread_entry": "目前没有未读文章",
"alert.no_user": "您是目前仅有的用户",
"alert.account_unlinked": "您的外部帐户现已解除关联!",
diff --git a/storage/feed.go b/storage/feed.go
index f6ad021..6db3488 100644
--- a/storage/feed.go
+++ b/storage/feed.go
@@ -139,7 +139,6 @@ func (s *Storage) Feeds(userID int64) (model.Feeds, error) {
// FeedsWithCounters returns all feeds of the given user with counters of read and unread entries.
func (s *Storage) FeedsWithCounters(userID int64) (model.Feeds, error) {
- feeds := make(model.Feeds, 0)
query := `
SELECT
f.id,
@@ -166,7 +165,43 @@ func (s *Storage) FeedsWithCounters(userID int64) (model.Feeds, error) {
f.user_id=$1
ORDER BY f.parsing_error_count DESC, unread_count DESC, lower(f.title) ASC
`
- rows, err := s.db.Query(query, userID)
+ return s.fetchFeedsWithCounters(query, userID)
+}
+
+// FeedsByCategoryWithCounters returns all feeds of the given user/category with counters of read and unread entries.
+func (s *Storage) FeedsByCategoryWithCounters(userID, categoryID int64) (model.Feeds, error) {
+ query := `
+ SELECT
+ f.id,
+ f.feed_url,
+ f.site_url,
+ f.title,
+ f.etag_header,
+ f.last_modified_header,
+ f.user_id,
+ f.checked_at at time zone u.timezone,
+ f.parsing_error_count, f.parsing_error_msg,
+ f.scraper_rules, f.rewrite_rules, f.crawler, f.user_agent,
+ f.username, f.password, f.disabled,
+ f.category_id, c.title as category_title,
+ fi.icon_id,
+ u.timezone,
+ (SELECT count(*) FROM entries WHERE entries.feed_id=f.id AND status='unread') as unread_count,
+ (SELECT count(*) FROM entries WHERE entries.feed_id=f.id AND status='read') as read_count
+ FROM feeds f
+ LEFT JOIN categories c ON c.id=f.category_id
+ LEFT JOIN feed_icons fi ON fi.feed_id=f.id
+ LEFT JOIN users u ON u.id=f.user_id
+ WHERE
+ f.user_id=$1 AND f.category_id=$2
+ ORDER BY f.parsing_error_count DESC, unread_count DESC, lower(f.title) ASC
+ `
+ return s.fetchFeedsWithCounters(query, userID, categoryID)
+}
+
+func (s *Storage) fetchFeedsWithCounters(query string, args ...interface{}) (model.Feeds, error) {
+ feeds := make(model.Feeds, 0)
+ rows, err := s.db.Query(query, args...)
if err != nil {
return nil, fmt.Errorf(`store: unable to fetch feeds: %v`, err)
}
@@ -176,7 +211,7 @@ func (s *Storage) FeedsWithCounters(userID int64) (model.Feeds, error) {
var feed model.Feed
var iconID interface{}
var tz string
- feed.Category = &model.Category{UserID: userID}
+ feed.Category = &model.Category{}
err := rows.Scan(
&feed.ID,
@@ -213,6 +248,7 @@ func (s *Storage) FeedsWithCounters(userID int64) (model.Feeds, error) {
}
feed.CheckedAt = timezone.Convert(tz, feed.CheckedAt)
+ feed.Category.UserID = feed.UserID
feeds = append(feeds, &feed)
}
diff --git a/template/common.go b/template/common.go
index 574e4e1..2539cad 100644
--- a/template/common.go
+++ b/template/common.go
@@ -22,6 +22,62 @@ var templateCommonMap = map[string]string{
</div>
</div>
{{ end }}`,
+ "feed_list": `{{ define "feed_list" }}
+ <div class="items">
+ {{ range .feeds }}
+ <article class="item {{ if ne .ParsingErrorCount 0 }}feed-parsing-error{{ end }}">
+ <div class="item-header">
+ <span class="item-title">
+ {{ if .Icon }}
+ <img src="{{ route "icon" "iconID" .Icon.IconID }}" width="16" height="16" loading="lazy" alt="{{ .Title }}">
+ {{ end }}
+ {{ if .Disabled }} 🚫 {{ end }}
+ <a href="{{ route "feedEntries" "feedID" .ID }}">{{ .Title }}</a>
+ </span>
+ <span class="feed-entries-counter">
+ (<span title="{{ t "page.feeds.unread_counter" }}">{{ .UnreadCount }}</span>/<span title="{{ t "page.feeds.read_counter" }}">{{ .ReadCount }}</span>)
+ </span>
+ <span class="category">
+ <a href="{{ route "categoryEntries" "categoryID" .Category.ID }}">{{ .Category.Title }}</a>
+ </span>
+ </div>
+ <div class="item-meta">
+ <ul>
+ <li>
+ <a href="{{ .SiteURL }}" title="{{ .SiteURL }}" target="_blank" rel="noopener noreferrer" referrerpolicy="no-referrer" data-original-link="true">{{ domain .SiteURL }}</a>
+ </li>
+ <li>
+ {{ t "page.feeds.last_check" }} <time datetime="{{ isodate .CheckedAt }}" title="{{ isodate .CheckedAt }}">{{ elapsed $.user.Timezone .CheckedAt }}</time>
+ </li>
+ </ul>
+ <ul>
+ <li>
+ <a href="{{ route "refreshFeed" "feedID" .ID }}">{{ t "menu.refresh_feed" }}</a>
+ </li>
+ <li>
+ <a href="{{ route "editFeed" "feedID" .ID }}">{{ t "menu.edit_feed" }}</a>
+ </li>
+ <li>
+ <a href="#"
+ data-confirm="true"
+ data-label-question="{{ t "confirm.question" }}"
+ data-label-yes="{{ t "confirm.yes" }}"
+ data-label-no="{{ t "confirm.no" }}"
+ data-label-loading="{{ t "confirm.loading" }}"
+ data-url="{{ route "removeFeed" "feedID" .ID }}">{{ t "action.remove" }}</a>
+ </li>
+ </ul>
+ </div>
+ {{ if ne .ParsingErrorCount 0 }}
+ <div class="parsing-error">
+ <strong title="{{ .ParsingErrorMsg }}" class="parsing-error-count">{{ plural "page.feeds.error_count" .ParsingErrorCount .ParsingErrorCount }}</strong>
+ - <small class="parsing-error-message">{{ .ParsingErrorMsg }}</small>
+ </div>
+ {{ end }}
+ </article>
+ {{ end }}
+ </div>
+{{ end }}`,
"item_meta": `{{ define "item_meta" }}
<div class="item-meta">
<ul>
@@ -274,6 +330,7 @@ var templateCommonMap = map[string]string{
var templateCommonMapChecksums = map[string]string{
"entry_pagination": "4faa91e2eae150c5e4eab4d258e039dfdd413bab7602f0009360e6d52898e353",
+ "feed_list": "7b7ea2c7df07d048c83d86237d5b5e41bddce561273c652d9265950093ca261b",
"item_meta": "34deb081a054f2948ad808bdb2c8603d6ab00c58f2f50c4ead0b47ae092888eb",
"layout": "010e31c9dde88cb429b21f4b0c24bb3769043a3ef1ef4a57100314f5910c8725",
"pagination": "3386e90c6e1230311459e9a484629bc5d5bf39514a75ef2e73bbbc61142f7abb",
diff --git a/template/html/categories.html b/template/html/categories.html
index b534ba1..4be3c2f 100644
--- a/template/html/categories.html
+++ b/template/html/categories.html
@@ -20,18 +20,13 @@
<span class="item-title">
<a href="{{ route "categoryEntries" "categoryID" .ID }}">{{ .Title }}</a>
</span>
+ (<span title="{{ if eq .FeedCount 0 }}{{ t "page.categories.no_feed" }}{{ else }}{{ plural "page.categories.feed_count" .FeedCount .FeedCount }}{{ end }}">{{ .FeedCount }}</span>)
</div>
<div class="item-meta">
<ul>
<li>
- {{ if eq .FeedCount 0 }}
- {{ t "page.categories.no_feed" }}
- {{ else }}
- {{ plural "page.categories.feed_count" .FeedCount .FeedCount }}
- {{ end }}
+ <a href="{{ route "categoryFeeds" "categoryID" .ID }}">{{ t "page.categories.feeds" }}</a>
</li>
- </ul>
- <ul>
<li>
<a href="{{ route "editCategory" "categoryID" .ID }}">{{ t "menu.edit_category" }}</a>
</li>
diff --git a/template/html/category_entries.html b/template/html/category_entries.html
index 48bf205..90687fb 100644
--- a/template/html/category_entries.html
+++ b/template/html/category_entries.html
@@ -24,6 +24,9 @@
<a href="{{ route "categoryEntries" "categoryID" .category.ID }}">{{ t "menu.show_only_unread_entries" }}</a>
</li>
{{ end }}
+ <li>
+ <a href="{{ route "categoryFeeds" "categoryID" .category.ID }}">{{ t "menu.feeds" }}</a>
+ </li>
</ul>
</section>
diff --git a/template/html/category_feeds.html b/template/html/category_feeds.html
new file mode 100644
index 0000000..b903256
--- /dev/null
+++ b/template/html/category_feeds.html
@@ -0,0 +1,34 @@
+{{ define "title"}}{{ .category.Title }} &gt; {{ t "page.feeds.title" }} ({{ .total }}){{ end }}
+
+{{ define "content"}}
+<section class="page-header">
+ <h1>{{ .category.Title }} &gt; {{ t "page.feeds.title" }} ({{ .total }})</h1>
+ <ul>
+ <li>
+ <a href="{{ route "categories" }}">{{ t "menu.categories" }}</a>
+ </li>
+ <li>
+ <a href="{{ route "editCategory" "categoryID" .category.ID }}">{{ t "menu.edit_category" }}</a>
+ </li>
+ {{ if eq .total 0 }}
+ <li>
+ <a href="#"
+ data-confirm="true"
+ data-label-question="{{ t "confirm.question" }}"
+ data-label-yes="{{ t "confirm.yes" }}"
+ data-label-no="{{ t "confirm.no" }}"
+ data-label-loading="{{ t "confirm.loading" }}"
+ data-redirect-url="{{ route "categories" }}"
+ data-url="{{ route "removeCategory" "categoryID" .category.ID }}">{{ t "action.remove" }}</a>
+ </li>
+ {{ end }}
+ </ul>
+</section>
+
+{{ if not .feeds }}
+ <p class="alert">{{ t "alert.no_feed_in_category" }}</p>
+{{ else }}
+ {{ template "feed_list" dict "user" .user "feeds" .feeds "ParsingErrorCount" .ParsingErrorCount }}
+{{ end }}
+
+{{ end }}
diff --git a/template/html/common/feed_list.html b/template/html/common/feed_list.html
new file mode 100644
index 0000000..cb80a1f
--- /dev/null
+++ b/template/html/common/feed_list.html
@@ -0,0 +1,56 @@
+{{ define "feed_list" }}
+ <div class="items">
+ {{ range .feeds }}
+ <article class="item {{ if ne .ParsingErrorCount 0 }}feed-parsing-error{{ end }}">
+ <div class="item-header">
+ <span class="item-title">
+ {{ if .Icon }}
+ <img src="{{ route "icon" "iconID" .Icon.IconID }}" width="16" height="16" loading="lazy" alt="{{ .Title }}">
+ {{ end }}
+ {{ if .Disabled }} 🚫 {{ end }}
+ <a href="{{ route "feedEntries" "feedID" .ID }}">{{ .Title }}</a>
+ </span>
+ <span class="feed-entries-counter">
+ (<span title="{{ t "page.feeds.unread_counter" }}">{{ .UnreadCount }}</span>/<span title="{{ t "page.feeds.read_counter" }}">{{ .ReadCount }}</span>)
+ </span>
+ <span class="category">
+ <a href="{{ route "categoryEntries" "categoryID" .Category.ID }}">{{ .Category.Title }}</a>
+ </span>
+ </div>
+ <div class="item-meta">
+ <ul>
+ <li>
+ <a href="{{ .SiteURL }}" title="{{ .SiteURL }}" target="_blank" rel="noopener noreferrer" referrerpolicy="no-referrer" data-original-link="true">{{ domain .SiteURL }}</a>
+ </li>
+ <li>
+ {{ t "page.feeds.last_check" }} <time datetime="{{ isodate .CheckedAt }}" title="{{ isodate .CheckedAt }}">{{ elapsed $.user.Timezone .CheckedAt }}</time>
+ </li>
+ </ul>
+ <ul>
+ <li>
+ <a href="{{ route "refreshFeed" "feedID" .ID }}">{{ t "menu.refresh_feed" }}</a>
+ </li>
+ <li>
+ <a href="{{ route "editFeed" "feedID" .ID }}">{{ t "menu.edit_feed" }}</a>
+ </li>
+ <li>
+ <a href="#"
+ data-confirm="true"
+ data-label-question="{{ t "confirm.question" }}"
+ data-label-yes="{{ t "confirm.yes" }}"
+ data-label-no="{{ t "confirm.no" }}"
+ data-label-loading="{{ t "confirm.loading" }}"
+ data-url="{{ route "removeFeed" "feedID" .ID }}">{{ t "action.remove" }}</a>
+ </li>
+ </ul>
+ </div>
+ {{ if ne .ParsingErrorCount 0 }}
+ <div class="parsing-error">
+ <strong title="{{ .ParsingErrorMsg }}" class="parsing-error-count">{{ plural "page.feeds.error_count" .ParsingErrorCount .ParsingErrorCount }}</strong>
+ - <small class="parsing-error-message">{{ .ParsingErrorMsg }}</small>
+ </div>
+ {{ end }}
+ </article>
+ {{ end }}
+ </div>
+{{ end }} \ No newline at end of file
diff --git a/template/html/edit_category.html b/template/html/edit_category.html
index 6b21e46..3506e45 100644
--- a/template/html/edit_category.html
+++ b/template/html/edit_category.html
@@ -8,6 +8,9 @@
<a href="{{ route "categories" }}">{{ t "menu.categories" }}</a>
</li>
<li>
+ <a href="{{ route "categoryFeeds" "categoryID" .category.ID }}">{{ t "menu.feeds" }}</a>
+ </li>
+ <li>
<a href="{{ route "createCategory" }}">{{ t "menu.create_category" }}</a>
</li>
</ul>
@@ -24,7 +27,7 @@
<input type="text" name="title" id="form-title" value="{{ .form.Title }}" required autofocus>
<div class="buttons">
- <button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.update" }}</button> {{ t "action.or" }} <a href="{{ route "categories" }}">{{ t "action.cancel" }}</a>
+ <button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.update" }}</button>
</div>
</form>
{{ end }}
diff --git a/template/html/feeds.html b/template/html/feeds.html
index 7d4a428..e4d24bf 100644
--- a/template/html/feeds.html
+++ b/template/html/feeds.html
@@ -22,60 +22,7 @@
{{ if not .feeds }}
<p class="alert">{{ t "alert.no_feed" }}</p>
{{ else }}
- <div class="items">
- {{ range .feeds }}
- <article class="item {{ if ne .ParsingErrorCount 0 }}feed-parsing-error{{ end }}">
- <div class="item-header">
- <span class="item-title">
- {{ if .Icon }}
- <img src="{{ route "icon" "iconID" .Icon.IconID }}" width="16" height="16" loading="lazy" alt="{{ .Title }}">
- {{ end }}
- {{ if .Disabled }} 🚫 {{ end }}
- <a href="{{ route "feedEntries" "feedID" .ID }}">{{ .Title }}</a>
- </span>
- <span class="feed-entries-counter">
- (<span title="{{ t "page.feeds.unread_counter" }}">{{ .UnreadCount }}</span>/<span title="{{ t "page.feeds.read_counter" }}">{{ .ReadCount }}</span>)
- </span>
- <span class="category">
- <a href="{{ route "categoryEntries" "categoryID" .Category.ID }}">{{ .Category.Title }}</a>
- </span>
- </div>
- <div class="item-meta">
- <ul>
- <li>
- <a href="{{ .SiteURL }}" title="{{ .SiteURL }}" target="_blank" rel="noopener noreferrer" referrerpolicy="no-referrer" data-original-link="true">{{ domain .SiteURL }}</a>
- </li>
- <li>
- {{ t "page.feeds.last_check" }} <time datetime="{{ isodate .CheckedAt }}" title="{{ isodate .CheckedAt }}">{{ elapsed $.user.Timezone .CheckedAt }}</time>
- </li>
- </ul>
- <ul>
- <li>
- <a href="{{ route "refreshFeed" "feedID" .ID }}">{{ t "menu.refresh_feed" }}</a>
- </li>
- <li>
- <a href="{{ route "editFeed" "feedID" .ID }}">{{ t "menu.edit_feed" }}</a>
- </li>
- <li>
- <a href="#"
- data-confirm="true"
- data-label-question="{{ t "confirm.question" }}"
- data-label-yes="{{ t "confirm.yes" }}"
- data-label-no="{{ t "confirm.no" }}"
- data-label-loading="{{ t "confirm.loading" }}"
- data-url="{{ route "removeFeed" "feedID" .ID }}">{{ t "action.remove" }}</a>
- </li>
- </ul>
- </div>
- {{ if ne .ParsingErrorCount 0 }}
- <div class="parsing-error">
- <strong title="{{ .ParsingErrorMsg }}" class="parsing-error-count">{{ plural "page.feeds.error_count" .ParsingErrorCount .ParsingErrorCount }}</strong>
- - <small class="parsing-error-message">{{ .ParsingErrorMsg }}</small>
- </div>
- {{ end }}
- </article>
- {{ end }}
- </div>
+ {{ template "feed_list" dict "user" .user "feeds" .feeds "ParsingErrorCount" .ParsingErrorCount }}
{{ end }}
{{ end }}
diff --git a/template/views.go b/template/views.go
index 788e2f5..c4fadb4 100644
--- a/template/views.go
+++ b/template/views.go
@@ -151,18 +151,13 @@ var templateViewsMap = map[string]string{
<span class="item-title">
<a href="{{ route "categoryEntries" "categoryID" .ID }}">{{ .Title }}</a>
</span>
+ (<span title="{{ if eq .FeedCount 0 }}{{ t "page.categories.no_feed" }}{{ else }}{{ plural "page.categories.feed_count" .FeedCount .FeedCount }}{{ end }}">{{ .FeedCount }}</span>)
</div>
<div class="item-meta">
<ul>
<li>
- {{ if eq .FeedCount 0 }}
- {{ t "page.categories.no_feed" }}
- {{ else }}
- {{ plural "page.categories.feed_count" .FeedCount .FeedCount }}
- {{ end }}
+ <a href="{{ route "categoryFeeds" "categoryID" .ID }}">{{ t "page.categories.feeds" }}</a>
</li>
- </ul>
- <ul>
<li>
<a href="{{ route "editCategory" "categoryID" .ID }}">{{ t "menu.edit_category" }}</a>
</li>
@@ -212,6 +207,9 @@ var templateViewsMap = map[string]string{
<a href="{{ route "categoryEntries" "categoryID" .category.ID }}">{{ t "menu.show_only_unread_entries" }}</a>
</li>
{{ end }}
+ <li>
+ <a href="{{ route "categoryFeeds" "categoryID" .category.ID }}">{{ t "menu.feeds" }}</a>
+ </li>
</ul>
</section>
@@ -254,6 +252,41 @@ var templateViewsMap = map[string]string{
{{ end }}
`,
+ "category_feeds": `{{ define "title"}}{{ .category.Title }} &gt; {{ t "page.feeds.title" }} ({{ .total }}){{ end }}
+
+{{ define "content"}}
+<section class="page-header">
+ <h1>{{ .category.Title }} &gt; {{ t "page.feeds.title" }} ({{ .total }})</h1>
+ <ul>
+ <li>
+ <a href="{{ route "categories" }}">{{ t "menu.categories" }}</a>
+ </li>
+ <li>
+ <a href="{{ route "editCategory" "categoryID" .category.ID }}">{{ t "menu.edit_category" }}</a>
+ </li>
+ {{ if eq .total 0 }}
+ <li>
+ <a href="#"
+ data-confirm="true"
+ data-label-question="{{ t "confirm.question" }}"
+ data-label-yes="{{ t "confirm.yes" }}"
+ data-label-no="{{ t "confirm.no" }}"
+ data-label-loading="{{ t "confirm.loading" }}"
+ data-redirect-url="{{ route "categories" }}"
+ data-url="{{ route "removeCategory" "categoryID" .category.ID }}">{{ t "action.remove" }}</a>
+ </li>
+ {{ end }}
+ </ul>
+</section>
+
+{{ if not .feeds }}
+ <p class="alert">{{ t "alert.no_feed_in_category" }}</p>
+{{ else }}
+ {{ template "feed_list" dict "user" .user "feeds" .feeds "ParsingErrorCount" .ParsingErrorCount }}
+{{ end }}
+
+{{ end }}
+`,
"choose_subscription": `{{ define "title"}}{{ t "page.add_feed.title" }}{{ end }}
{{ define "content"}}
@@ -367,6 +400,9 @@ var templateViewsMap = map[string]string{
<a href="{{ route "categories" }}">{{ t "menu.categories" }}</a>
</li>
<li>
+ <a href="{{ route "categoryFeeds" "categoryID" .category.ID }}">{{ t "menu.feeds" }}</a>
+ </li>
+ <li>
<a href="{{ route "createCategory" }}">{{ t "menu.create_category" }}</a>
</li>
</ul>
@@ -383,7 +419,7 @@ var templateViewsMap = map[string]string{
<input type="text" name="title" id="form-title" value="{{ .form.Title }}" required autofocus>
<div class="buttons">
- <button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.update" }}</button> {{ t "action.or" }} <a href="{{ route "categories" }}">{{ t "action.cancel" }}</a>
+ <button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.update" }}</button>
</div>
</form>
{{ end }}
@@ -778,60 +814,7 @@ var templateViewsMap = map[string]string{
{{ if not .feeds }}
<p class="alert">{{ t "alert.no_feed" }}</p>
{{ else }}
- <div class="items">
- {{ range .feeds }}
- <article class="item {{ if ne .ParsingErrorCount 0 }}feed-parsing-error{{ end }}">
- <div class="item-header">
- <span class="item-title">
- {{ if .Icon }}
- <img src="{{ route "icon" "iconID" .Icon.IconID }}" width="16" height="16" loading="lazy" alt="{{ .Title }}">
- {{ end }}
- {{ if .Disabled }} 🚫 {{ end }}
- <a href="{{ route "feedEntries" "feedID" .ID }}">{{ .Title }}</a>
- </span>
- <span class="feed-entries-counter">
- (<span title="{{ t "page.feeds.unread_counter" }}">{{ .UnreadCount }}</span>/<span title="{{ t "page.feeds.read_counter" }}">{{ .ReadCount }}</span>)
- </span>
- <span class="category">
- <a href="{{ route "categoryEntries" "categoryID" .Category.ID }}">{{ .Category.Title }}</a>
- </span>
- </div>
- <div class="item-meta">
- <ul>
- <li>
- <a href="{{ .SiteURL }}" title="{{ .SiteURL }}" target="_blank" rel="noopener noreferrer" referrerpolicy="no-referrer" data-original-link="true">{{ domain .SiteURL }}</a>
- </li>
- <li>
- {{ t "page.feeds.last_check" }} <time datetime="{{ isodate .CheckedAt }}" title="{{ isodate .CheckedAt }}">{{ elapsed $.user.Timezone .CheckedAt }}</time>
- </li>
- </ul>
- <ul>
- <li>
- <a href="{{ route "refreshFeed" "feedID" .ID }}">{{ t "menu.refresh_feed" }}</a>
- </li>
- <li>
- <a href="{{ route "editFeed" "feedID" .ID }}">{{ t "menu.edit_feed" }}</a>
- </li>
- <li>
- <a href="#"
- data-confirm="true"
- data-label-question="{{ t "confirm.question" }}"
- data-label-yes="{{ t "confirm.yes" }}"
- data-label-no="{{ t "confirm.no" }}"
- data-label-loading="{{ t "confirm.loading" }}"
- data-url="{{ route "removeFeed" "feedID" .ID }}">{{ t "action.remove" }}</a>
- </li>
- </ul>
- </div>
- {{ if ne .ParsingErrorCount 0 }}
- <div class="parsing-error">
- <strong title="{{ .ParsingErrorMsg }}" class="parsing-error-count">{{ plural "page.feeds.error_count" .ParsingErrorCount .ParsingErrorCount }}</strong>
- - <small class="parsing-error-message">{{ .ParsingErrorMsg }}</small>
- </div>
- {{ end }}
- </article>
- {{ end }}
- </div>
+ {{ template "feed_list" dict "user" .user "feeds" .feeds "ParsingErrorCount" .ParsingErrorCount }}
{{ end }}
{{ end }}
@@ -1362,17 +1345,18 @@ var templateViewsMapChecksums = map[string]string{
"about": "4035658497363d7af7f79be83190404eb21ec633fe8ec636bdfc219d9fc78cfc",
"add_subscription": "a0f1d2bc02b6adc83dbeae593f74d9b936102cd6dd73302cdbec2137cafdcdd9",
"bookmark_entries": "65588da78665699dd3f287f68325e9777d511f1a57fee4131a5bb6d00bb68df8",
- "categories": "642ee3cddbd825ee6ab5a77caa0d371096b55de0f1bd4ae3055b8c8a70507d8d",
- "category_entries": "3ec30d2cb97f29514ff61898a4f23d2aa73a24b3468b6d410b1c2d18c8808927",
+ "categories": "2c5dd0ed6355bd5acc393bbf6117d20458b5581aab82036008324f6bbbe2af75",
+ "category_entries": "dee7b9cd60c6c46f01dd4289940679df31c1fce28ce4aa7249fa459023e1eeb4",
+ "category_feeds": "527c2ffbc4fcec775071424ba1022ae003525dba53a28cc41f48fb7b30aa984b",
"choose_subscription": "33c04843d7c1b608d034e605e52681822fc6d79bc6b900c04915dd9ebae584e2",
"create_category": "6b22b5ce51abf4e225e23a79f81be09a7fb90acb265e93a8faf9446dff74018d",
"create_user": "9b73a55233615e461d1f07d99ad1d4d3b54532588ab960097ba3e090c85aaf3a",
- "edit_category": "daf073d2944a180ce5aaeb80b597eb69597a50dff55a9a1d6cf7938b48d768cb",
+ "edit_category": "b1c0b38f1b714c5d884edcd61e5b5295a5f1c8b71c469b35391e4dcc97cc6d36",
"edit_feed": "34aa0d668b3ea1a1b5fa480c20cebeae729b37010af3bb915d2a9eed73d3b996",
"edit_user": "c692db9de1a084c57b93e95a14b041d39bf489846cbb91fc982a62b72b77062a",
"entry": "24aeba26ef9a51ce585ca5c4af090f1de7d7bfd7f1e3ff1b63af520e2afa76bd",
"feed_entries": "9c70b82f55e4b311eff20be1641733612e3c1b406ce8010861e4c417d97b6dcc",
- "feeds": "f11ba1c45cf3966843ddc406d96e048fc8f2235428e10111a1660a141ea2c42f",
+ "feeds": "fa06cd1e1e3fec79132386972c640a2fe91237f5dba572389d5f45be74545f25",
"history_entries": "87e17d39de70eb3fdbc4000326283be610928758eae7924e4b08dcb446f3b6a9",
"import": "5eb56cecaa4d369b9acc991a82be7617710c551089a2e99d34ce8b6e5c37df0a",
"integrations": "6104ff6ff3ac3c1ae5e850c78250aab6e99e2342a337589f3848459fa333766a",
diff --git a/ui/category_feeds.go b/ui/category_feeds.go
new file mode 100644
index 0000000..202fc3e
--- /dev/null
+++ b/ui/category_feeds.go
@@ -0,0 +1,52 @@
+// Copyright 2019 Frédéric Guillot. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+package ui // import "miniflux.app/ui"
+
+import (
+ "net/http"
+
+ "miniflux.app/http/request"
+ "miniflux.app/http/response/html"
+ "miniflux.app/ui/session"
+ "miniflux.app/ui/view"
+)
+
+func (h *handler) showCategoryFeedsPage(w http.ResponseWriter, r *http.Request) {
+ user, err := h.store.UserByID(request.UserID(r))
+ if err != nil {
+ html.ServerError(w, r, err)
+ return
+ }
+
+ categoryID := request.RouteInt64Param(r, "categoryID")
+ category, err := h.store.Category(request.UserID(r), categoryID)
+ if err != nil {
+ html.ServerError(w, r, err)
+ return
+ }
+
+ if category == nil {
+ html.NotFound(w, r)
+ return
+ }
+
+ feeds, err := h.store.FeedsByCategoryWithCounters(user.ID, categoryID)
+ if err != nil {
+ html.ServerError(w, r, err)
+ return
+ }
+
+ sess := session.New(h.store, request.SessionID(r))
+ view := view.New(h.tpl, r, sess)
+ view.Set("category", category)
+ view.Set("feeds", feeds)
+ view.Set("total", len(feeds))
+ view.Set("menu", "categories")
+ view.Set("user", user)
+ view.Set("countUnread", h.store.CountUnreadEntries(user.ID))
+ view.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID))
+
+ html.OK(w, r, view.Render("category_feeds"))
+}
diff --git a/ui/category_update.go b/ui/category_update.go
index 026f991..591063a 100644
--- a/ui/category_update.go
+++ b/ui/category_update.go
@@ -66,5 +66,5 @@ func (h *handler) updateCategory(w http.ResponseWriter, r *http.Request) {
return
}
- html.Redirect(w, r, route.Path(h.router, "categories"))
+ html.Redirect(w, r, route.Path(h.router, "categoryFeeds", "categoryID", categoryID))
}
diff --git a/ui/ui.go b/ui/ui.go
index dd1a9d8..dabc2fc 100644
--- a/ui/ui.go
+++ b/ui/ui.go
@@ -74,6 +74,7 @@ func Serve(router *mux.Router, store *storage.Storage, pool *worker.Pool, feedHa
uiRouter.HandleFunc("/categories", handler.showCategoryListPage).Name("categories").Methods("GET")
uiRouter.HandleFunc("/category/create", handler.showCreateCategoryPage).Name("createCategory").Methods("GET")
uiRouter.HandleFunc("/category/save", handler.saveCategory).Name("saveCategory").Methods("POST")
+ uiRouter.HandleFunc("/category/{categoryID}/feeds", handler.showCategoryFeedsPage).Name("categoryFeeds").Methods("GET")
uiRouter.HandleFunc("/category/{categoryID}/entries", handler.showCategoryEntriesPage).Name("categoryEntries").Methods("GET")
uiRouter.HandleFunc("/category/{categoryID}/entries/all", handler.showCategoryEntriesAllPage).Name("categoryEntriesAll").Methods("GET")
uiRouter.HandleFunc("/category/{categoryID}/edit", handler.showEditCategoryPage).Name("editCategory").Methods("GET")