mirror of
				https://github.com/nkanaev/yarr.git
				synced 2025-10-29 22:29:59 +00:00 
			
		
		
		
	basic feed list view
This commit is contained in:
		| @@ -256,3 +256,14 @@ func FeedHandler(rw http.ResponseWriter, req *http.Request) { | |||||||
| 		rw.WriteHeader(http.StatusMethodNotAllowed) | 		rw.WriteHeader(http.StatusMethodNotAllowed) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func FeedItemsHandler(rw http.ResponseWriter, req *http.Request) { | ||||||
|  | 	id, err := strconv.ParseInt(Vars(req)["id"], 10, 64) | ||||||
|  | 	if err != nil { | ||||||
|  | 		rw.WriteHeader(http.StatusBadRequest) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	rw.WriteHeader(http.StatusOK) | ||||||
|  | 	items := db(req).ListFeedItems(id) | ||||||
|  | 	writeJSON(rw, items) | ||||||
|  | } | ||||||
|   | |||||||
| @@ -80,6 +80,7 @@ var routes []Route = []Route{ | |||||||
| 	p("/api/folders/:id", FolderHandler), | 	p("/api/folders/:id", FolderHandler), | ||||||
| 	p("/api/feeds", FeedListHandler), | 	p("/api/feeds", FeedListHandler), | ||||||
| 	p("/api/feeds/:id", FeedHandler), | 	p("/api/feeds/:id", FeedHandler), | ||||||
|  | 	p("/api/feeds/:id/items", FeedItemsHandler), | ||||||
| 	p("/api/feeds/find", FeedHandler), | 	p("/api/feeds/find", FeedHandler), | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -14,17 +14,17 @@ const ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| type Item struct { | type Item struct { | ||||||
| 	Id string | 	Id string `json:"id"` | ||||||
| 	FeedId int64 | 	FeedId int64 `json:"feed_id"` | ||||||
| 	Title string | 	Title string `json:"title"` | ||||||
| 	Link string | 	Link string `json:"link"` | ||||||
| 	Description string | 	Description string `json:"description"` | ||||||
| 	Content string | 	Content string `json:"content"` | ||||||
| 	Author string | 	Author string `json:"author"` | ||||||
| 	Date *time.Time | 	Date *time.Time `json:"date"` | ||||||
| 	DateUpdated *time.Time | 	DateUpdated *time.Time `json:"date_updated"` | ||||||
| 	Status ItemStatus | 	Status ItemStatus `json:"status"` | ||||||
| 	Image string | 	Image string `json:"image"` | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *Storage) CreateItems(items []Item) bool { | func (s *Storage) CreateItems(items []Item) bool { | ||||||
| @@ -70,7 +70,6 @@ func itemQuery(s *Storage, cond string, v ...interface{}) []Item { | |||||||
| 			content, author, date, date_updated, status, image | 			content, author, date, date_updated, status, image | ||||||
| 		from items | 		from items | ||||||
| 		where %s`, cond) | 		where %s`, cond) | ||||||
| 	s.log.Print(query) |  | ||||||
| 	rows, err := s.db.Query(query, v...) | 	rows, err := s.db.Query(query, v...) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		s.log.Print(err) | 		s.log.Print(err) | ||||||
|   | |||||||
| @@ -77,12 +77,12 @@ | |||||||
|                     <input type="radio" name="item" :value="item.id" v-model="itemSelected"> |                     <input type="radio" name="item" :value="item.id" v-model="itemSelected"> | ||||||
|                     <div class="menu-item p-2"> |                     <div class="menu-item p-2"> | ||||||
|                         <div class="d-flex flex-column ml-4"> |                         <div class="d-flex flex-column ml-4"> | ||||||
|                             <div style="line-height: 1" class="d-flex"> |                             <div style="line-height: 1" class="d-flex text-muted"> | ||||||
|                                 <img src="./static/images/circle-full.svg" class="nav-icon ml-n4 mr-2" v-if="item.status === 'unread'"> |                                 <img src="./static/images/circle-full.svg" class="nav-icon ml-n4 mr-2" v-if="item.status === 'unread'"> | ||||||
|                                 <img src="./static/images/circle.svg" class="nav-icon ml-n4 mr-2" v-if="item.status === 'read'"> |                                 <img src="./static/images/circle.svg" class="nav-icon ml-n4 mr-2" v-if="item.status === 'read'"> | ||||||
|                                 <img src="./static/images/star.svg" class="nav-icon ml-n4 mr-2" v-if="item.status === 'starred'"> |                                 <img src="./static/images/star.svg" class="nav-icon ml-n4 mr-2" v-if="item.status === 'starred'"> | ||||||
|                                 <small class="flex-fill text-truncate">{{feedsById[item.feed_id].title}}</small> |                                 <small class="flex-fill text-truncate mr-1">{{feedsById[item.feed_id].title}}</small> | ||||||
|                                 <small class="">{{formatDate(item.date)}}</small> |                                 <small class="flex-shrink-0">{{formatDate(item.date)}}</small> | ||||||
|                             </div> |                             </div> | ||||||
|                             <span>{{item.title}}</span> |                             <span>{{item.title}}</span> | ||||||
|                         </div> |                         </div> | ||||||
| @@ -97,7 +97,7 @@ | |||||||
|                     <img src="./static/images/settings.svg" alt="" style="width: 20px; height: 20px;"> |                     <img src="./static/images/settings.svg" alt="" style="width: 20px; height: 20px;"> | ||||||
|                 </button> |                 </button> | ||||||
|             </div> |             </div> | ||||||
|             <div v-if="itemSelected" class="mx-3 my-2 overflow-auto"> |             <div v-if="itemSelected" class="px-3 mt-2 overflow-auto"> | ||||||
|                 <h3>{{itemSelectedDetails.title}}</h3> |                 <h3>{{itemSelectedDetails.title}}</h3> | ||||||
|                 <div class="text-muted"> |                 <div class="text-muted"> | ||||||
|                     <div>{{ feedsById[itemSelectedDetails.feed_id].title }}</div> |                     <div>{{ feedsById[itemSelectedDetails.feed_id].title }}</div> | ||||||
| @@ -105,7 +105,7 @@ | |||||||
|                 </div> |                 </div> | ||||||
|                 <hr> |                 <hr> | ||||||
|                 <div> |                 <div> | ||||||
|                     content goes here |                     <div v-html="itemSelectedDetails.description" v-if="itemSelectedDetails.description"></div> | ||||||
|                 </div> |                 </div> | ||||||
|             </div> |             </div> | ||||||
|         </div> |         </div> | ||||||
|   | |||||||
| @@ -26,7 +26,10 @@ | |||||||
|       }, |       }, | ||||||
|       delete: function(id) { |       delete: function(id) { | ||||||
|         return api('delete', '/api/feeds/' + id) |         return api('delete', '/api/feeds/' + id) | ||||||
|       } |       }, | ||||||
|  |       list_items: function(id) { | ||||||
|  |         return api('get', '/api/feeds/' + id + '/items').then(json) | ||||||
|  |       }, | ||||||
|     }, |     }, | ||||||
|     folders: { |     folders: { | ||||||
|       list: function() { |       list: function() { | ||||||
| @@ -41,6 +44,6 @@ | |||||||
|       delete: function(id) { |       delete: function(id) { | ||||||
|         return api('delete', '/api/folders/' + id) |         return api('delete', '/api/folders/' + id) | ||||||
|       }, |       }, | ||||||
|     } |     }, | ||||||
|   } |   } | ||||||
| })() | })() | ||||||
|   | |||||||
| @@ -43,9 +43,15 @@ var vm = new Vue({ | |||||||
|   watch: { |   watch: { | ||||||
|     'feedSelected': function(newVal, oldVal) { |     'feedSelected': function(newVal, oldVal) { | ||||||
|       if (newVal === null) return |       if (newVal === null) return | ||||||
|  |       var vm = this | ||||||
|       var parts = newVal.split(':', 2) |       var parts = newVal.split(':', 2) | ||||||
|       var type = parts[0] |       var type = parts[0] | ||||||
|       var guid = parts[1] |       var guid = parts[1] | ||||||
|  |       if (type === 'feed') { | ||||||
|  |         api.feeds.list_items(guid).then(function(items) { | ||||||
|  |           vm.items = items | ||||||
|  |         }) | ||||||
|  |       } | ||||||
|     }, |     }, | ||||||
|     'itemSelected': function(newVal, oldVal) { |     'itemSelected': function(newVal, oldVal) { | ||||||
|       this.itemSelectedDetails = this.itemsById[newVal] |       this.itemSelectedDetails = this.itemsById[newVal] | ||||||
| @@ -64,9 +70,8 @@ var vm = new Vue({ | |||||||
|     toggleFolderExpanded: function(folder) { |     toggleFolderExpanded: function(folder) { | ||||||
|       folder.is_expanded = !folder.is_expanded |       folder.is_expanded = !folder.is_expanded | ||||||
|     }, |     }, | ||||||
|     formatDate: function(timestamp_s) { |     formatDate: function(datestr) { | ||||||
|       var d = new Date(timestamp_s * 1000) |       return new Date(datestr).toLocaleDateString(undefined, {year: "numeric", month: "long", day: "numeric"}) | ||||||
|       return d.getDate() + '/' + d.getMonth() + '/' + d.getFullYear() |  | ||||||
|     }, |     }, | ||||||
|     moveFeed: function(feed, folder) { |     moveFeed: function(feed, folder) { | ||||||
|       var folder_id = folder ? folder.id : null |       var folder_id = folder ? folder.id : null | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user