mirror of
https://github.com/nkanaev/yarr.git
synced 2025-05-24 21:19:19 +00:00
paginate item list
This commit is contained in:
parent
bc627f5d2c
commit
a203792b1d
@ -12,6 +12,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"math"
|
||||||
)
|
)
|
||||||
|
|
||||||
func IndexHandler(rw http.ResponseWriter, req *http.Request) {
|
func IndexHandler(rw http.ResponseWriter, req *http.Request) {
|
||||||
@ -287,7 +288,12 @@ func ItemHandler(rw http.ResponseWriter, req *http.Request) {
|
|||||||
|
|
||||||
func ItemListHandler(rw http.ResponseWriter, req *http.Request) {
|
func ItemListHandler(rw http.ResponseWriter, req *http.Request) {
|
||||||
if req.Method == "GET" {
|
if req.Method == "GET" {
|
||||||
|
perPage := 20
|
||||||
|
curPage := 1
|
||||||
query := req.URL.Query()
|
query := req.URL.Query()
|
||||||
|
if page, err := strconv.ParseInt(query.Get("page"), 10, 64); err == nil {
|
||||||
|
curPage = int(page)
|
||||||
|
}
|
||||||
filter := storage.ItemFilter{}
|
filter := storage.ItemFilter{}
|
||||||
if folderID, err := strconv.ParseInt(query.Get("folder_id"), 10, 64); err == nil {
|
if folderID, err := strconv.ParseInt(query.Get("folder_id"), 10, 64); err == nil {
|
||||||
filter.FolderID = &folderID
|
filter.FolderID = &folderID
|
||||||
@ -299,9 +305,16 @@ func ItemListHandler(rw http.ResponseWriter, req *http.Request) {
|
|||||||
statusValue := storage.StatusValues[status]
|
statusValue := storage.StatusValues[status]
|
||||||
filter.Status = &statusValue
|
filter.Status = &statusValue
|
||||||
}
|
}
|
||||||
items := db(req).ListItems(filter)
|
items := db(req).ListItems(filter, (curPage-1)*perPage, perPage)
|
||||||
|
count := db(req).CountItems(filter)
|
||||||
rw.WriteHeader(http.StatusOK)
|
rw.WriteHeader(http.StatusOK)
|
||||||
writeJSON(rw, items)
|
writeJSON(rw, map[string]interface{}{
|
||||||
|
"page": map[string]int{
|
||||||
|
"cur": curPage,
|
||||||
|
"num": int(math.Ceil(float64(count) / float64(perPage))),
|
||||||
|
},
|
||||||
|
"list": items,
|
||||||
|
})
|
||||||
} else if req.Method == "PUT" {
|
} else if req.Method == "PUT" {
|
||||||
query := req.URL.Query()
|
query := req.URL.Query()
|
||||||
filter := storage.ItemFilter{}
|
filter := storage.ItemFilter{}
|
||||||
|
@ -96,7 +96,7 @@ func (s *Storage) CreateItems(items []Item) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Storage) ListItems(filter ItemFilter) []Item {
|
func listQueryPredicate(filter ItemFilter) (string, []interface{}) {
|
||||||
cond := make([]string, 0)
|
cond := make([]string, 0)
|
||||||
args := make([]interface{}, 0)
|
args := make([]interface{}, 0)
|
||||||
if filter.FolderID != nil {
|
if filter.FolderID != nil {
|
||||||
@ -116,7 +116,11 @@ func (s *Storage) ListItems(filter ItemFilter) []Item {
|
|||||||
if len(cond) > 0 {
|
if len(cond) > 0 {
|
||||||
predicate = strings.Join(cond, " and ")
|
predicate = strings.Join(cond, " and ")
|
||||||
}
|
}
|
||||||
|
return predicate, args
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Storage) ListItems(filter ItemFilter, offset, limit int) []Item {
|
||||||
|
predicate, args := listQueryPredicate(filter)
|
||||||
result := make([]Item, 0, 0)
|
result := make([]Item, 0, 0)
|
||||||
query := fmt.Sprintf(`
|
query := fmt.Sprintf(`
|
||||||
select
|
select
|
||||||
@ -126,7 +130,8 @@ func (s *Storage) ListItems(filter ItemFilter) []Item {
|
|||||||
join feeds f on f.id = i.feed_id
|
join feeds f on f.id = i.feed_id
|
||||||
where %s
|
where %s
|
||||||
order by i.date desc
|
order by i.date desc
|
||||||
`, predicate)
|
limit %d offset %d
|
||||||
|
`, predicate, limit, offset)
|
||||||
rows, err := s.db.Query(query, args...)
|
rows, err := s.db.Query(query, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Print(err)
|
s.log.Print(err)
|
||||||
@ -157,6 +162,22 @@ func (s *Storage) ListItems(filter ItemFilter) []Item {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Storage) CountItems(filter ItemFilter) int64 {
|
||||||
|
predicate, args := listQueryPredicate(filter)
|
||||||
|
query := fmt.Sprintf(`
|
||||||
|
select count(i.id)
|
||||||
|
from items i
|
||||||
|
join feeds f on f.id = i.feed_id
|
||||||
|
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 {
|
func (s *Storage) UpdateItemStatus(item_id int64, status ItemStatus) bool {
|
||||||
_, err := s.db.Exec(`update items set status = ? where id = ?`, status, item_id)
|
_, err := s.db.Exec(`update items set status = ? where id = ?`, status, item_id)
|
||||||
return err == nil
|
return err == nil
|
||||||
|
@ -77,9 +77,9 @@
|
|||||||
<img src="./static/images/check.svg" alt="" style="width: 20px; height: 20px;">
|
<img src="./static/images/check.svg" alt="" style="width: 20px; height: 20px;">
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="p-2 overflow-auto">
|
<div class="p-2 overflow-auto" v-scroll="loadMoreItems">
|
||||||
<label v-for="item in items" :key="item.id"
|
<label v-for="item in items" :key="item.id"
|
||||||
class="nav-select mb-1"
|
class="nav-select"
|
||||||
:class="{'text-muted' : filterSelected=='all' && item.status=='read',
|
:class="{'text-muted' : filterSelected=='all' && item.status=='read',
|
||||||
'text-primary': filterSelected=='all' && item.status=='starred'}">
|
'text-primary': filterSelected=='all' && item.status=='starred'}">
|
||||||
<input type="radio" name="item" :value="item.id" v-model="itemSelected">
|
<input type="radio" name="item" :value="item.id" v-model="itemSelected">
|
||||||
@ -93,6 +93,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
|
<button class="btn btn-link btn-block loading my-3" v-if="itemsPage.cur < itemsPage.num"></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="vh-100 d-flex flex-column w-100">
|
<div class="vh-100 d-flex flex-column w-100">
|
||||||
|
@ -1,5 +1,24 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
var debounce = function(callback, wait) {
|
||||||
|
var timeout
|
||||||
|
return function() {
|
||||||
|
var context = this, args = arguments
|
||||||
|
clearTimeout(timeout)
|
||||||
|
timeout = setTimeout(function() {
|
||||||
|
callback.apply(this, args)
|
||||||
|
}, wait)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Vue.directive('scroll', {
|
||||||
|
inserted: function(el, binding) {
|
||||||
|
el.addEventListener('scroll', debounce(function(event) {
|
||||||
|
binding.value(event, el)
|
||||||
|
}, 200))
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
var vm = new Vue({
|
var vm = new Vue({
|
||||||
el: '#app',
|
el: '#app',
|
||||||
created: function() {
|
created: function() {
|
||||||
@ -17,10 +36,17 @@ var vm = new Vue({
|
|||||||
'feeds': [],
|
'feeds': [],
|
||||||
'feedSelected': null,
|
'feedSelected': null,
|
||||||
'items': [],
|
'items': [],
|
||||||
|
'itemsPage': {
|
||||||
|
'cur': 1,
|
||||||
|
'num': 1,
|
||||||
|
},
|
||||||
'itemSelected': null,
|
'itemSelected': null,
|
||||||
'itemSelectedDetails': {},
|
'itemSelectedDetails': {},
|
||||||
'settings': 'create',
|
'settings': 'create',
|
||||||
'loading': {newfeed: 0},
|
'loading': {
|
||||||
|
'newfeed': false,
|
||||||
|
'items': false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -94,10 +120,29 @@ var vm = new Vue({
|
|||||||
},
|
},
|
||||||
refreshItems: function() {
|
refreshItems: function() {
|
||||||
var query = this.getItemsQuery()
|
var query = this.getItemsQuery()
|
||||||
api.items.list(query).then(function(items) {
|
this.loading.items = true
|
||||||
vm.items = items
|
var vm = this
|
||||||
|
api.items.list(query).then(function(data) {
|
||||||
|
vm.items = data.list
|
||||||
|
vm.itemsPage = data.page
|
||||||
|
vm.loading.items = false
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
loadMoreItems: function(event, el) {
|
||||||
|
if (this.itemsPage.cur >= this.itemsPage.num) 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
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
markItemsRead: function() {
|
markItemsRead: function() {
|
||||||
var vm = this
|
var vm = this
|
||||||
var query = this.getItemsQuery()
|
var query = this.getItemsQuery()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user