فهرست منبع

Show unread counters on feeds page

Maxim Baz 6 سال پیش
والد
کامیت
e38333e272

+ 18 - 9
locale/translations.go

@@ -86,6 +86,7 @@ var translations = map[string]string{
     "page.edit_user.title": "Benutzer bearbeiten: %s",
     "page.feeds.title": "Abonnements",
     "page.feeds.last_check": "Letzte Aktualisierung:",
+    "page.feeds.unread": "Ungelesen:",
     "page.feeds.error_count": [
         "%d Fehler",
         "%d Fehler"
@@ -387,6 +388,7 @@ var translations = map[string]string{
     "page.edit_user.title": "Edit User: %s",
     "page.feeds.title": "Feeds",
     "page.feeds.last_check": "Last check:",
+    "page.feeds.unread": "Unread:",
     "page.feeds.error_count": [
         "%d error",
         "%d errors"
@@ -668,6 +670,7 @@ var translations = map[string]string{
     "page.edit_user.title": "Editar usuario: %s",
     "page.feeds.title": "Fuentes",
     "page.feeds.last_check": "Última verificación:",
+    "page.feeds.unread": "No leídos:",
     "page.feeds.error_count": [
         "%d error",
         "%d errores"
@@ -949,6 +952,7 @@ var translations = map[string]string{
     "page.edit_user.title": "Modification de l'utilisateur : %s",
     "page.feeds.title": "Abonnements",
     "page.feeds.last_check": "Dernière vérification :",
+    "page.feeds.unread": "Non lus:",
     "page.feeds.error_count": [
         "%d erreur",
         "%d erreurs"
@@ -1250,6 +1254,7 @@ var translations = map[string]string{
     "page.edit_user.title": "Modifica utente: %s",
     "page.feeds.title": "Feed",
     "page.feeds.last_check": "Ultimo controllo:",
+    "page.feeds.unread": "Da leggere:",
     "page.feeds.error_count": [
         "%d errore",
         "%d errori"
@@ -1531,6 +1536,7 @@ var translations = map[string]string{
     "page.edit_user.title": "Bewerk gebruiker: %s",
     "page.feeds.title": "Feeds",
     "page.feeds.last_check": "Laatste update:",
+    "page.feeds.unread": "Ongelezen:",
     "page.feeds.error_count": [
         "%d error",
         "%d errors"
@@ -1831,6 +1837,7 @@ var translations = map[string]string{
     "page.edit_user.title": "Edytuj użytkownika: %s",
     "page.feeds.title": "Kanały",
     "page.feeds.last_check": "Ostatnia aktualizacja:",
+    "page.feeds.unread": "Nieprzeczytane:",
     "page.feeds.error_count": [
         "%d błąd",
         "%d błąd",
@@ -2138,6 +2145,7 @@ var translations = map[string]string{
     "page.edit_user.title": "Изменить пользователя: %s",
     "page.feeds.title": "Подписки",
     "page.feeds.last_check": "Последняя проверка:",
+    "page.feeds.unread": "Непрочитано:",
     "page.feeds.error_count": [
         "%d ошибка",
         "%d ошибки",
@@ -2425,6 +2433,7 @@ var translations = map[string]string{
     "page.edit_user.title": "编辑用户 : %s",
     "page.feeds.title": "源",
     "page.feeds.last_check": "最后检查时间:",
+    "page.feeds.unread": "未读:",
     "page.feeds.error_count": [
         "%d 错误"
     ],
@@ -2638,13 +2647,13 @@ var translations = map[string]string{
 }
 
 var translationsChecksums = map[string]string{
-	"de_DE": "52c82c151a27455501e54a4ea20339c965f030d7204ae9f55bf2639ef396b015",
-	"en_US": "ef73c3934c5df6c2346f96a4eb4b308f84404c58029aac2ee6859bc205ecb6b3",
-	"es_ES": "567fad5046d07efd69b9d29a3e8fa05503f935c02624c71851d9b1b3a8fa82d5",
-	"fr_FR": "11a3f01603a73b4a0c2921aea592bdb24086286f4f1343df4adaeb4a1471c1b9",
-	"it_IT": "19b9614980ded9ee4a5eea1f6623a8d2d4b763ffe81eae633d8c768bb58d6aea",
-	"nl_NL": "a91e2195ac0731a3788405a51c4201e1a89dcce35ef792356e8c17adb57aee97",
-	"pl_PL": "097bc9beac12f33d3a5e5ee98ccba0875e4d1c1bf13e38251a66ac450834c5b3",
-	"ru_RU": "b253bf709a2f4bcac2f894bd1797247481fa7c6b70a0a0d8785d8680be83bac8",
-	"zh_CN": "5004e07fa535ea56e7fbe1501bb8ff4191d1d214e51b4590110b660994c39f0d",
+	"de_DE": "c743f9eeca5d486c7b788e6ab20cf9f859e6c2138563d09599220fe40b3e25b5",
+	"en_US": "7992fd4e6fafceccbcf0b0972f0c085ad9a0eb7507de60eac407882c1f6457f5",
+	"es_ES": "e97765769afac01f4fd4cf868fa75657bd4d4e63999d9145a42d4de4248a9b2a",
+	"fr_FR": "50146f82d9b8e9ab69a5b3950827f7db47ba590ef074cf7f78b18f7e26fb0074",
+	"it_IT": "6a9f44c98b0dc8ab34306868bb9f93c4e511579a45c1fa8a48d637e10b2feca6",
+	"nl_NL": "afa731bd75e18b9483e9c6842b647f9d85405994ceca42f24b314bc967680606",
+	"pl_PL": "40aa0998688fb8dc1a308cda35bcea04be16c852944088c844c676c601a90bbd",
+	"ru_RU": "a1c72bb9ab48cf1b4fb47bfed081df75e391bb8d5c8b70036c1bdd38c57b6cb3",
+	"zh_CN": "a349de59436db0dc07172de89a481d95a660af206b13ae0de236a0cb8e31014b",
 }

+ 1 - 0
locale/translations/de_DE.json

@@ -81,6 +81,7 @@
     "page.edit_user.title": "Benutzer bearbeiten: %s",
     "page.feeds.title": "Abonnements",
     "page.feeds.last_check": "Letzte Aktualisierung:",
+    "page.feeds.unread": "Ungelesen:",
     "page.feeds.error_count": [
         "%d Fehler",
         "%d Fehler"

+ 1 - 0
locale/translations/en_US.json

@@ -81,6 +81,7 @@
     "page.edit_user.title": "Edit User: %s",
     "page.feeds.title": "Feeds",
     "page.feeds.last_check": "Last check:",
+    "page.feeds.unread": "Unread:",
     "page.feeds.error_count": [
         "%d error",
         "%d errors"

+ 1 - 0
locale/translations/es_ES.json

@@ -81,6 +81,7 @@
     "page.edit_user.title": "Editar usuario: %s",
     "page.feeds.title": "Fuentes",
     "page.feeds.last_check": "Última verificación:",
+    "page.feeds.unread": "No leídos:",
     "page.feeds.error_count": [
         "%d error",
         "%d errores"

+ 1 - 0
locale/translations/fr_FR.json

@@ -81,6 +81,7 @@
     "page.edit_user.title": "Modification de l'utilisateur : %s",
     "page.feeds.title": "Abonnements",
     "page.feeds.last_check": "Dernière vérification :",
+    "page.feeds.unread": "Non lus:",
     "page.feeds.error_count": [
         "%d erreur",
         "%d erreurs"

+ 1 - 0
locale/translations/it_IT.json

@@ -81,6 +81,7 @@
     "page.edit_user.title": "Modifica utente: %s",
     "page.feeds.title": "Feed",
     "page.feeds.last_check": "Ultimo controllo:",
+    "page.feeds.unread": "Da leggere:",
     "page.feeds.error_count": [
         "%d errore",
         "%d errori"

+ 1 - 0
locale/translations/nl_NL.json

@@ -81,6 +81,7 @@
     "page.edit_user.title": "Bewerk gebruiker: %s",
     "page.feeds.title": "Feeds",
     "page.feeds.last_check": "Laatste update:",
+    "page.feeds.unread": "Ongelezen:",
     "page.feeds.error_count": [
         "%d error",
         "%d errors"

+ 1 - 0
locale/translations/pl_PL.json

@@ -82,6 +82,7 @@
     "page.edit_user.title": "Edytuj użytkownika: %s",
     "page.feeds.title": "Kanały",
     "page.feeds.last_check": "Ostatnia aktualizacja:",
+    "page.feeds.unread": "Nieprzeczytane:",
     "page.feeds.error_count": [
         "%d błąd",
         "%d błąd",

+ 1 - 0
locale/translations/ru_RU.json

@@ -82,6 +82,7 @@
     "page.edit_user.title": "Изменить пользователя: %s",
     "page.feeds.title": "Подписки",
     "page.feeds.last_check": "Последняя проверка:",
+    "page.feeds.unread": "Непрочитано:",
     "page.feeds.error_count": [
         "%d ошибка",
         "%d ошибки",

+ 1 - 0
locale/translations/zh_CN.json

@@ -80,6 +80,7 @@
     "page.edit_user.title": "编辑用户 : %s",
     "page.feeds.title": "源",
     "page.feeds.last_check": "最后检查时间:",
+    "page.feeds.unread": "未读:",
     "page.feeds.error_count": [
         "%d 错误"
     ],

+ 2 - 0
model/feed.go

@@ -33,6 +33,8 @@ type Feed struct {
 	Category           *Category `json:"category,omitempty"`
 	Entries            Entries   `json:"entries,omitempty"`
 	Icon               *FeedIcon `json:"icon"`
+	UnreadCount        int       `json:"unread_count"`
+	ReadCount          int       `json:"read_count"`
 }
 
 func (f *Feed) String() string {

+ 74 - 0
storage/feed.go

@@ -121,6 +121,80 @@ func (s *Storage) Feeds(userID int64) (model.Feeds, error) {
 	return feeds, nil
 }
 
+// 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, 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
+		ORDER BY f.parsing_error_count DESC, unread_count DESC, lower(f.title) ASC`
+
+	rows, err := s.db.Query(query, userID)
+	if err != nil {
+		return nil, fmt.Errorf("unable to fetch feeds: %v", err)
+	}
+	defer rows.Close()
+
+	for rows.Next() {
+		var feed model.Feed
+		var iconID interface{}
+		var tz string
+		feed.Category = &model.Category{UserID: userID}
+
+		err := rows.Scan(
+			&feed.ID,
+			&feed.FeedURL,
+			&feed.SiteURL,
+			&feed.Title,
+			&feed.EtagHeader,
+			&feed.LastModifiedHeader,
+			&feed.UserID,
+			&feed.CheckedAt,
+			&feed.ParsingErrorCount,
+			&feed.ParsingErrorMsg,
+			&feed.ScraperRules,
+			&feed.RewriteRules,
+			&feed.Crawler,
+			&feed.UserAgent,
+			&feed.Username,
+			&feed.Password,
+			&feed.Disabled,
+			&feed.Category.ID,
+			&feed.Category.Title,
+			&iconID,
+			&tz,
+			&feed.UnreadCount,
+			&feed.ReadCount,
+		)
+
+		if err != nil {
+			return nil, fmt.Errorf("unable to fetch feeds row: %v", err)
+		}
+
+		if iconID != nil {
+			feed.Icon = &model.FeedIcon{FeedID: feed.ID, IconID: iconID.(int64)}
+		}
+
+		feed.CheckedAt = timezone.Convert(tz, feed.CheckedAt)
+		feeds = append(feeds, &feed)
+	}
+
+	return feeds, nil
+}
+
 // FeedByID returns a feed by the ID.
 func (s *Storage) FeedByID(userID, feedID int64) (*model.Feed, error) {
 	var feed model.Feed

+ 5 - 0
template/html/feeds.html

@@ -45,6 +45,11 @@
                     <li>
                         {{ t "page.feeds.last_check" }} <time datetime="{{ isodate .CheckedAt }}" title="{{ isodate .CheckedAt }}">{{ elapsed $.user.Timezone .CheckedAt }}</time>
                     </li>
+                    {{ if gt .UnreadCount 0 }}
+                    <li>
+                        {{ t "page.feeds.unread" }} <span class="unread-counter">{{ .UnreadCount }}</span>
+                    </li>
+                    {{ end }}
                 </ul>
                 <ul>
                     <li>

+ 6 - 1
template/views.go

@@ -851,6 +851,11 @@ var templateViewsMap = map[string]string{
                     <li>
                         {{ t "page.feeds.last_check" }} <time datetime="{{ isodate .CheckedAt }}" title="{{ isodate .CheckedAt }}">{{ elapsed $.user.Timezone .CheckedAt }}</time>
                     </li>
+                    {{ if gt .UnreadCount 0 }}
+                    <li>
+                        {{ t "page.feeds.unread" }} <span class="unread-counter">{{ .UnreadCount }}</span>
+                    </li>
+                    {{ end }}
                 </ul>
                 <ul>
                     <li>
@@ -1483,7 +1488,7 @@ var templateViewsMapChecksums = map[string]string{
 	"edit_user":           "f4f99412ba771cfca2a2a42778b023b413c5494e9a287053ba8cf380c2865c5f",
 	"entry":               "24aeba26ef9a51ce585ca5c4af090f1de7d7bfd7f1e3ff1b63af520e2afa76bd",
 	"feed_entries":        "9c70b82f55e4b311eff20be1641733612e3c1b406ce8010861e4c417d97b6dcc",
-	"feeds":               "fa2dad422445eca898c1daa4ab742691207a8c0d3c274eed84462bc610d22219",
+	"feeds":               "55317035a4c008a720294c1858e9dc626f19e222ae41498db67dbb537ba7a456",
 	"history_entries":     "87e17d39de70eb3fdbc4000326283be610928758eae7924e4b08dcb446f3b6a9",
 	"import":              "5eb56cecaa4d369b9acc991a82be7617710c551089a2e99d34ce8b6e5c37df0a",
 	"integrations":        "f85b4a48ab1fc13b8ca94bfbbc44bd5e8784f35b26a63ec32cbe82b96b45e008",

+ 1 - 1
ui/feed_list.go

@@ -20,7 +20,7 @@ func (h *handler) showFeedsPage(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	feeds, err := h.store.Feeds(user.ID)
+	feeds, err := h.store.FeedsWithCounters(user.ID)
 	if err != nil {
 		html.ServerError(w, r, err)
 		return