mirror of
				https://github.com/nkanaev/yarr.git
				synced 2025-10-29 22:29:59 +00:00 
			
		
		
		
	show unread/starred count
This commit is contained in:
		| @@ -45,7 +45,7 @@ func StaticHandler(rw http.ResponseWriter, req *http.Request) { | |||||||
| func StatusHandler(rw http.ResponseWriter, req *http.Request) { | func StatusHandler(rw http.ResponseWriter, req *http.Request) { | ||||||
| 	writeJSON(rw, map[string]interface{}{ | 	writeJSON(rw, map[string]interface{}{ | ||||||
| 		"running": handler(req).fetchRunning, | 		"running": handler(req).fetchRunning, | ||||||
| 		"stats": map[string]int64{}, | 		"stats": db(req).FeedStats(), | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -213,3 +213,31 @@ func (s *Storage) MarkItemsRead(filter ItemFilter) bool { | |||||||
| 	} | 	} | ||||||
| 	return err == nil | 	return err == nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | type FeedStat struct { | ||||||
|  | 	FeedId int64 `json:"feed_id"` | ||||||
|  | 	UnreadCount int64 `json:"unread"` | ||||||
|  | 	StarredCount int64 `json:"starred"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *Storage) FeedStats() []FeedStat { | ||||||
|  | 	result := make([]FeedStat, 0) | ||||||
|  | 	rows, err := s.db.Query(fmt.Sprintf(` | ||||||
|  | 		select | ||||||
|  | 			feed_id, | ||||||
|  | 			sum(case status when %d then 1 else 0 end), | ||||||
|  | 			sum(case status when %d then 1 else 0 end) | ||||||
|  | 		from items | ||||||
|  | 		group by feed_id | ||||||
|  | 	`, UNREAD, STARRED)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		s.log.Print(err) | ||||||
|  | 		return result | ||||||
|  | 	} | ||||||
|  | 	for rows.Next() { | ||||||
|  | 		stat := FeedStat{} | ||||||
|  | 		rows.Scan(&stat.FeedId, &stat.UnreadCount, &stat.StarredCount) | ||||||
|  | 		result = append(result, stat) | ||||||
|  | 	} | ||||||
|  | 	return result | ||||||
|  | } | ||||||
|   | |||||||
| @@ -25,7 +25,7 @@ | |||||||
|                         <div class="menu-item d-flex align-items-center w-100"> |                         <div class="menu-item d-flex align-items-center w-100"> | ||||||
|                             <img src="./static/images/circle.svg" alt="" class="nav-icon"> |                             <img src="./static/images/circle.svg" alt="" class="nav-icon"> | ||||||
|                             <span class="flex-fill text-left text-truncate">Unread</span> |                             <span class="flex-fill text-left text-truncate">Unread</span> | ||||||
|                             <span class="counter text-right"></span> |                             <span class="counter text-right">{{totalStats.unread || ''}}</span> | ||||||
|                         </div> |                         </div> | ||||||
|                     </label> |                     </label> | ||||||
|                     <label class="nav-select"> |                     <label class="nav-select"> | ||||||
| @@ -33,7 +33,7 @@ | |||||||
|                         <div class="menu-item d-flex align-items-center w-100"> |                         <div class="menu-item d-flex align-items-center w-100"> | ||||||
|                             <img src="./static/images/star.svg" alt="" class="nav-icon"> |                             <img src="./static/images/star.svg" alt="" class="nav-icon"> | ||||||
|                             <span class="flex-fill text-left text-truncate">Starred</span> |                             <span class="flex-fill text-left text-truncate">Starred</span> | ||||||
|                             <span class="counter text-right"></span> |                             <span class="counter text-right">{{totalStats.starred || ''}}</span> | ||||||
|                         </div> |                         </div> | ||||||
|                     </label> |                     </label> | ||||||
|                 </div> |                 </div> | ||||||
| @@ -54,7 +54,7 @@ | |||||||
|                                  :class="{expanded: folder.is_expanded}" |                                  :class="{expanded: folder.is_expanded}" | ||||||
|                                  @click.prevent="toggleFolderExpanded(folder)"> |                                  @click.prevent="toggleFolderExpanded(folder)"> | ||||||
|                             <span class="flex-fill text-left text-truncate">{{ folder.title }}</span> |                             <span class="flex-fill text-left text-truncate">{{ folder.title }}</span> | ||||||
|                             <span class="counter text-right"></span> |                             <span class="counter text-right">{{filteredFolderStats[folder.id] || ''}}</span> | ||||||
|                         </div> |                         </div> | ||||||
|                     </label> |                     </label> | ||||||
|                     <div v-show="!folder.id || folder.is_expanded" class="mt-1" :class="{'pl-3': folder.id}"> |                     <div v-show="!folder.id || folder.is_expanded" class="mt-1" :class="{'pl-3': folder.id}"> | ||||||
| @@ -64,7 +64,7 @@ | |||||||
|                             <div class="menu-item d-flex align-items-center w-100"> |                             <div class="menu-item d-flex align-items-center w-100"> | ||||||
|                                 <img src="./static/images/rss.svg" alt="" class="nav-icon"> |                                 <img src="./static/images/rss.svg" alt="" class="nav-icon"> | ||||||
|                                 <span class="flex-fill text-left text-truncate">{{ feed.title }}</span> |                                 <span class="flex-fill text-left text-truncate">{{ feed.title }}</span> | ||||||
|                                 <span class="counter text-right"></span> |                                 <span class="counter text-right">{{filteredFeedStats[feed.id] || ''}}</span> | ||||||
|                             </div> |                             </div> | ||||||
|                         </label> |                         </label> | ||||||
|                     </div> |                     </div> | ||||||
|   | |||||||
| @@ -74,6 +74,9 @@ | |||||||
|         return api('put', '/api/settings', data) |         return api('put', '/api/settings', data) | ||||||
|       }, |       }, | ||||||
|     }, |     }, | ||||||
|  |     status: function() { | ||||||
|  |       return api('get', '/api/status').then(json) | ||||||
|  |     }, | ||||||
|     upload_opml: function(form) { |     upload_opml: function(form) { | ||||||
|       return fetch('/opml/import', { |       return fetch('/opml/import', { | ||||||
|         method: 'post', |         method: 'post', | ||||||
|   | |||||||
| @@ -28,6 +28,7 @@ var vm = new Vue({ | |||||||
|       vm.refreshItems() |       vm.refreshItems() | ||||||
|     }) |     }) | ||||||
|     this.refreshFeeds() |     this.refreshFeeds() | ||||||
|  |     this.refreshStats() | ||||||
|   }, |   }, | ||||||
|   data: function() { |   data: function() { | ||||||
|     return { |     return { | ||||||
| @@ -47,6 +48,7 @@ var vm = new Vue({ | |||||||
|         'newfeed': false, |         'newfeed': false, | ||||||
|         'items': false, |         'items': false, | ||||||
|       }, |       }, | ||||||
|  |       'feedStats': {}, | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   computed: { |   computed: { | ||||||
| @@ -71,6 +73,34 @@ var vm = new Vue({ | |||||||
|     itemsById: function() { |     itemsById: function() { | ||||||
|       return this.items.reduce(function(acc, item) { acc[item.id] = item; return acc }, {}) |       return this.items.reduce(function(acc, item) { acc[item.id] = item; return acc }, {}) | ||||||
|     }, |     }, | ||||||
|  |     filteredFeedStats: function() { | ||||||
|  |       var filter = this.filterSelected | ||||||
|  |       if (filter != 'unread' && filter != 'starred') return {} | ||||||
|  |  | ||||||
|  |       var feedStats = this.feedStats | ||||||
|  |       return this.feeds.reduce(function(acc, feed) { | ||||||
|  |         if (feedStats[feed.id]) acc[feed.id] = vm.feedStats[feed.id][filter] | ||||||
|  |         return acc | ||||||
|  |       }, {}) | ||||||
|  |     }, | ||||||
|  |     filteredFolderStats: function() { | ||||||
|  |       var filter = this.filterSelected | ||||||
|  |       if (filter != 'unread' && filter != 'starred') return {} | ||||||
|  |  | ||||||
|  |       var feedStats = this.filteredFeedStats | ||||||
|  |       return this.feeds.reduce(function(acc, feed) { | ||||||
|  |         if (!acc[feed.folder_id]) acc[feed.folder_id] = 0 | ||||||
|  |         if (feedStats[feed.id]) acc[feed.folder_id] += feedStats[feed.id] | ||||||
|  |         return acc | ||||||
|  |       }, {}) | ||||||
|  |     }, | ||||||
|  |     totalStats: function() { | ||||||
|  |       return Object.values(this.feedStats).reduce(function(acc, stat) { | ||||||
|  |         acc.unread += stat.unread | ||||||
|  |         acc.starred += stat.starred | ||||||
|  |         return acc | ||||||
|  |       }, {unread: 0, starred: 0}) | ||||||
|  |     }, | ||||||
|   }, |   }, | ||||||
|   watch: { |   watch: { | ||||||
|     'filterSelected': function(newVal, oldVal) { |     'filterSelected': function(newVal, oldVal) { | ||||||
| @@ -87,11 +117,21 @@ var vm = new Vue({ | |||||||
|       this.itemSelectedDetails = this.itemsById[newVal] |       this.itemSelectedDetails = this.itemsById[newVal] | ||||||
|       if (this.itemSelectedDetails.status == 'unread') { |       if (this.itemSelectedDetails.status == 'unread') { | ||||||
|         this.itemSelectedDetails.status = 'read' |         this.itemSelectedDetails.status = 'read' | ||||||
|  |         this.feedStats[this.itemSelectedDetails.feed_id].unread -= 1 | ||||||
|         api.items.update(this.itemSelectedDetails.id, {status: this.itemSelectedDetails.status}) |         api.items.update(this.itemSelectedDetails.id, {status: this.itemSelectedDetails.status}) | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|   }, |   }, | ||||||
|   methods: { |   methods: { | ||||||
|  |     refreshStats: function() { | ||||||
|  |       var vm = this | ||||||
|  |       api.status().then(function(data) { | ||||||
|  |         vm.feedStats = data.stats.reduce(function(acc, stat) { | ||||||
|  |           acc[stat.feed_id] = stat | ||||||
|  |           return acc | ||||||
|  |         }, {}) | ||||||
|  |       }) | ||||||
|  |     }, | ||||||
|     getItemsQuery: function() { |     getItemsQuery: function() { | ||||||
|       var query = {} |       var query = {} | ||||||
|       if (this.feedSelected) { |       if (this.feedSelected) { | ||||||
| @@ -148,6 +188,7 @@ var vm = new Vue({ | |||||||
|       var query = this.getItemsQuery() |       var query = this.getItemsQuery() | ||||||
|       api.items.mark_read(query).then(function() { |       api.items.mark_read(query).then(function() { | ||||||
|         vm.items = [] |         vm.items = [] | ||||||
|  |         vm.refreshStats() | ||||||
|       }) |       }) | ||||||
|     }, |     }, | ||||||
|     toggleFolderExpanded: function(folder) { |     toggleFolderExpanded: function(folder) { | ||||||
| @@ -227,16 +268,20 @@ var vm = new Vue({ | |||||||
|     toggleItemStarred: function(item) { |     toggleItemStarred: function(item) { | ||||||
|       if (item.status == 'starred') { |       if (item.status == 'starred') { | ||||||
|         item.status = 'read' |         item.status = 'read' | ||||||
|  |         this.feedStats[item.feed_id].starred -= 1 | ||||||
|       } else if (item.status != 'starred') { |       } else if (item.status != 'starred') { | ||||||
|         item.status = 'starred' |         item.status = 'starred' | ||||||
|  |         this.feedStats[item.feed_id].starred += 1 | ||||||
|       } |       } | ||||||
|       api.items.update(item.id, {status: item.status}) |       api.items.update(item.id, {status: item.status}) | ||||||
|     }, |     }, | ||||||
|     toggleItemRead: function(item) { |     toggleItemRead: function(item) { | ||||||
|       if (item.status == 'unread') { |       if (item.status == 'unread') { | ||||||
|         item.status = 'read' |         item.status = 'read' | ||||||
|  |         this.feedStats[item.feed_id].unread -= 1 | ||||||
|       } else if (item.status == 'read') { |       } else if (item.status == 'read') { | ||||||
|         item.status = 'unread' |         item.status = 'unread' | ||||||
|  |         this.feedStats[item.feed_id].unread += 1 | ||||||
|       } |       } | ||||||
|       api.items.update(item.id, {status: item.status}) |       api.items.update(item.id, {status: item.status}) | ||||||
|     }, |     }, | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user