From 7aeb458ee5dc833dbab28a9787b5352e96078bfb Mon Sep 17 00:00:00 2001 From: Nazar Kanaev Date: Mon, 31 May 2021 14:37:45 +0100 Subject: [PATCH] fix pagination --- src/assets/index.html | 2 +- src/assets/javascripts/app.js | 43 ++++++++++++++++------------------- src/server/routes.go | 23 ++++++++++--------- src/storage/item.go | 40 ++++++++++++++------------------ 4 files changed, 50 insertions(+), 58 deletions(-) diff --git a/src/assets/index.html b/src/assets/index.html index 969917f..25a5f23 100644 --- a/src/assets/index.html +++ b/src/assets/index.html @@ -260,7 +260,7 @@
{{ item.title || 'untitled' }}
- +
{{ feed_errors[current.feed.id] }} diff --git a/src/assets/javascripts/app.js b/src/assets/javascripts/app.js index cb8f67f..5a629c6 100644 --- a/src/assets/javascripts/app.js +++ b/src/assets/javascripts/app.js @@ -180,7 +180,7 @@ var vm = new Vue({ created: function() { this.refreshStats() .then(this.refreshFeeds.bind(this)) - .then(this.refreshItems.bind(this)) + .then(this.refreshItems.bind(this, false)) api.feeds.list_errors().then(function(errors) { vm.feed_errors = errors @@ -197,10 +197,7 @@ var vm = new Vue({ 'feedNewChoice': [], 'feedNewChoiceSelected': '', 'items': [], - 'itemsPage': { - 'cur': 1, - 'num': 1, - }, + 'itemsHasMore': true, 'itemSelected': null, 'itemSelectedDetails': null, 'itemSelectedReadability': '', @@ -304,13 +301,13 @@ var vm = new Vue({ }, 'filterSelected': function(newVal, oldVal) { if (oldVal === undefined) return // do nothing, initial setup - api.settings.update({filter: newVal}).then(this.refreshItems.bind(this)) + api.settings.update({filter: newVal}).then(this.refreshItems.bind(this, false)) this.itemSelected = null this.computeStats() }, 'feedSelected': function(newVal, oldVal) { if (oldVal === undefined) return // do nothing, initial setup - api.settings.update({feed: newVal}).then(this.refreshItems.bind(this)) + api.settings.update({feed: newVal}).then(this.refreshItems.bind(this, false)) this.itemSelected = null if (this.$refs.itemlist) this.$refs.itemlist.scrollTop = 0 }, @@ -339,7 +336,7 @@ var vm = new Vue({ }, 500), 'itemSortNewestFirst': function(newVal, oldVal) { if (oldVal === undefined) return // do nothing, initial setup - api.settings.update({sort_newest_first: newVal}).then(this.refreshItems.bind(this)) + api.settings.update({sort_newest_first: newVal}).then(vm.refreshItems.bind(this, false)) }, 'feedListWidth': debounce(function(newVal, oldVal) { if (oldVal === undefined) return // do nothing, initial setup @@ -404,34 +401,34 @@ var vm = new Vue({ vm.feeds = values[1] }) }, - refreshItems: function() { + refreshItems: function(loadMore) { if (this.feedSelected === null) { vm.items = [] - vm.itemsPage = {'cur': 1, 'num': 1} return } + var query = this.getItemsQuery() + if (loadMore) { + query.after = vm.items[vm.items.length-1].id + } + this.loading.items = true return api.items.list(query).then(function(data) { - vm.items = data.list - vm.itemsPage = data.page + if (loadMore) { + vm.items = vm.items.concat(data.list) + } else { + vm.items = data.list + } + vm.itemsHasMore = data.has_more vm.loading.items = false }) }, loadMoreItems: function(event, el) { - if (this.itemsPage.cur >= this.itemsPage.num) return + if (!this.itemsHasMore) return + if (this.loading.items) return var closeToBottom = (el.scrollHeight - el.scrollTop - el.offsetHeight) < 50 - if (closeToBottom) { - this.loading.moreitems = true - var query = this.getItemsQuery() - query.page = this.itemsPage.cur + 1 - api.items.list(query).then(function(data) { - vm.items = vm.items.concat(data.list) - vm.itemsPage = data.page - vm.loading.items = false - }) - } + if (closeToBottom) this.refreshItems(true) }, markItemsRead: function() { var query = this.getItemsQuery() diff --git a/src/server/routes.go b/src/server/routes.go index 4529dc6..97d14ea 100644 --- a/src/server/routes.go +++ b/src/server/routes.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "log" - "math" "net/http" "path/filepath" "reflect" @@ -307,11 +306,8 @@ func (s *Server) handleItem(c *router.Context) { func (s *Server) handleItemList(c *router.Context) { if c.Req.Method == "GET" { perPage := 20 - curPage := 1 query := c.Req.URL.Query() - if page, err := c.QueryInt64("page"); err == nil { - curPage = int(page) - } + filter := storage.ItemFilter{} if folderID, err := c.QueryInt64("folder_id"); err == nil { filter.FolderID = &folderID @@ -319,6 +315,9 @@ func (s *Server) handleItemList(c *router.Context) { if feedID, err := c.QueryInt64("feed_id"); err == nil { filter.FeedID = &feedID } + if after, err := c.QueryInt64("after"); err == nil { + filter.After = &after + } if status := query.Get("status"); len(status) != 0 { statusValue := storage.StatusValues[status] filter.Status = &statusValue @@ -327,14 +326,16 @@ func (s *Server) handleItemList(c *router.Context) { filter.Search = &search } newestFirst := query.Get("oldest_first") != "true" - items := s.db.ListItems(filter, (curPage-1)*perPage, perPage, newestFirst) - count := s.db.CountItems(filter) + + items := s.db.ListItems(filter, perPage+1, newestFirst) + hasMore := false + if len(items) == perPage+1 { + hasMore = true + items = items[:perPage] + } c.JSON(http.StatusOK, map[string]interface{}{ - "page": map[string]int{ - "cur": curPage, - "num": int(math.Ceil(float64(count) / float64(perPage))), - }, "list": items, + "has_more": hasMore, }) } else if c.Req.Method == "PUT" { filter := storage.MarkFilter{} diff --git a/src/storage/item.go b/src/storage/item.go index bda988f..59227a2 100644 --- a/src/storage/item.go +++ b/src/storage/item.go @@ -61,6 +61,7 @@ type ItemFilter struct { FeedID *int64 Status *ItemStatus Search *string + After *int64 } type MarkFilter struct { @@ -106,7 +107,7 @@ func (s *Storage) CreateItems(items []Item) bool { return true } -func listQueryPredicate(filter ItemFilter) (string, []interface{}) { +func listQueryPredicate(filter ItemFilter, newestFirst bool) (string, []interface{}) { cond := make([]string, 0) args := make([]interface{}, 0) if filter.FolderID != nil { @@ -131,6 +132,14 @@ func listQueryPredicate(filter ItemFilter) (string, []interface{}) { cond = append(cond, "i.search_rowid in (select rowid from search where search match ?)") args = append(args, strings.Join(terms, " ")) } + if filter.After != nil { + compare := ">" + if newestFirst { + compare = "<" + } + cond = append(cond, fmt.Sprintf("(i.date, i.id) %s (select date, id from items where id = ?)", compare)) + args = append(args, *filter.After) + } predicate := "1" if len(cond) > 0 { @@ -140,13 +149,13 @@ func listQueryPredicate(filter ItemFilter) (string, []interface{}) { return predicate, args } -func (s *Storage) ListItems(filter ItemFilter, offset, limit int, newestFirst bool) []Item { - predicate, args := listQueryPredicate(filter) +func (s *Storage) ListItems(filter ItemFilter, limit int, newestFirst bool) []Item { + predicate, args := listQueryPredicate(filter, newestFirst) result := make([]Item, 0, 0) - order := "date desc" + order := "date desc, id desc" if !newestFirst { - order = "date asc" + order = "date asc, id asc" } query := fmt.Sprintf(` @@ -157,8 +166,8 @@ func (s *Storage) ListItems(filter ItemFilter, offset, limit int, newestFirst bo from items i where %s order by %s - limit %d offset %d - `, predicate, order, limit, offset) + limit %d + `, predicate, order, limit) rows, err := s.db.Query(query, args...) if err != nil { log.Print(err) @@ -199,28 +208,13 @@ func (s *Storage) GetItem(id int64) *Item { return i } -func (s *Storage) CountItems(filter ItemFilter) int64 { - predicate, args := listQueryPredicate(filter) - query := fmt.Sprintf(` - select count(i.id) - from items i - where %s`, predicate) - row := s.db.QueryRow(query, args...) - if row != nil { - var result int64 - row.Scan(&result) - return result - } - return 0 -} - func (s *Storage) UpdateItemStatus(item_id int64, status ItemStatus) bool { _, err := s.db.Exec(`update items set status = ? where id = ?`, status, item_id) return err == nil } func (s *Storage) MarkItemsRead(filter MarkFilter) bool { - predicate, args := listQueryPredicate(ItemFilter{FolderID: filter.FolderID, FeedID: filter.FeedID}) + predicate, args := listQueryPredicate(ItemFilter{FolderID: filter.FolderID, FeedID: filter.FeedID}, false) query := fmt.Sprintf(` update items as i set status = %d where %s and i.status != %d