Rewrite the pagination algorithm so it works properly.

Shorten a few bits of JS.
Get the topic store methods to call BypassGet instead of duplicating logic.
This commit is contained in:
Azareal 2019-06-04 15:48:12 +10:00
parent 5b5b8339d6
commit fdd223d9cf
12 changed files with 72 additions and 78 deletions

View File

@ -1002,18 +1002,20 @@ func CoerceIntString(data string) (res int, length int) {
// TODO: Write tests for this
// Make sure we reflect changes to this in the JS port in /public/global.js
func Paginate(count int, perPage int, maxPages int) []int {
if count < perPage {
return []int{1}
func Paginate(currentPage int, lastPage int, maxPages int) (out []int) {
diff := lastPage - currentPage
pre := 3
if diff < 3 {
pre = maxPages - diff
}
var page int
var out []int
for current := 0; current < count; current += perPage {
page := currentPage - pre
if page < 0 {
page = 0
}
for len(out) < maxPages && page < lastPage {
page++
out = append(out, page)
if len(out) >= maxPages {
break
}
}
return out
}

View File

@ -527,7 +527,7 @@ func compileJSTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName stri
itemsPerPage := 25
_, page, lastPage := PageOffset(20, 1, itemsPerPage)
pageList := Paginate(20, itemsPerPage, 5)
pageList := Paginate(page, lastPage, 5)
tmpls.AddStd("paginator", "common.Paginator", Paginator{pageList, page, lastPage})
tmpls.AddStd("topic_c_edit_post", "common.TopicCEditPost", TopicCEditPost{ID: 0, Source: "", Ref: ""})

View File

@ -365,7 +365,7 @@ func (tList *DefaultTopicList) getList(page int, orderby string, argList []inter
topic.LastUser = userList[topic.LastReplyBy]
}
pageList := Paginate(topicCount, Config.ItemsPerPage, 5)
pageList := Paginate(page, lastPage, 5)
return topicList, Paginator{pageList, page, lastPage}, nil
}

View File

@ -66,44 +66,40 @@ func NewDefaultTopicStore(cache TopicCache) (*DefaultTopicStore, error) {
}, acc.FirstError()
}
func (mts *DefaultTopicStore) DirtyGet(id int) *Topic {
topic, err := mts.cache.Get(id)
func (s *DefaultTopicStore) DirtyGet(id int) *Topic {
topic, err := s.cache.Get(id)
if err == nil {
return topic
}
topic = &Topic{ID: id}
err = mts.get.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.LastReplyBy, &topic.LastReplyAt, &topic.LastReplyID, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.ViewCount, &topic.PostCount, &topic.LikeCount, &topic.AttachCount, &topic.Poll, &topic.Data)
topic, err = s.BypassGet(id)
if err == nil {
topic.Link = BuildTopicURL(NameToSlug(topic.Title), id)
_ = mts.cache.Set(topic)
_ = s.cache.Set(topic)
return topic
}
return BlankTopic()
}
// TODO: Log weird cache errors?
func (mts *DefaultTopicStore) Get(id int) (topic *Topic, err error) {
topic, err = mts.cache.Get(id)
func (s *DefaultTopicStore) Get(id int) (topic *Topic, err error) {
topic, err = s.cache.Get(id)
if err == nil {
return topic, nil
}
topic = &Topic{ID: id}
err = mts.get.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.LastReplyBy, &topic.LastReplyAt, &topic.LastReplyID, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.ViewCount, &topic.PostCount, &topic.LikeCount, &topic.AttachCount, &topic.Poll, &topic.Data)
topic, err = s.BypassGet(id)
if err == nil {
topic.Link = BuildTopicURL(NameToSlug(topic.Title), id)
_ = mts.cache.Set(topic)
_ = s.cache.Set(topic)
}
return topic, err
}
// BypassGet will always bypass the cache and pull the topic directly from the database
func (mts *DefaultTopicStore) BypassGet(id int) (*Topic, error) {
topic := &Topic{ID: id}
err := mts.get.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.LastReplyBy, &topic.LastReplyAt, &topic.LastReplyID, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.ViewCount, &topic.PostCount, &topic.LikeCount, &topic.AttachCount, &topic.Poll, &topic.Data)
topic.Link = BuildTopicURL(NameToSlug(topic.Title), id)
return topic, err
func (s *DefaultTopicStore) BypassGet(id int) (*Topic, error) {
t := &Topic{ID: id}
err := s.get.QueryRow(id).Scan(&t.Title, &t.Content, &t.CreatedBy, &t.CreatedAt, &t.LastReplyBy, &t.LastReplyAt, &t.LastReplyID, &t.IsClosed, &t.Sticky, &t.ParentID, &t.IPAddress, &t.ViewCount, &t.PostCount, &t.LikeCount, &t.AttachCount, &t.Poll, &t.Data)
if err == nil {
t.Link = BuildTopicURL(NameToSlug(t.Title), id)
}
return t, err
}
// TODO: Avoid duplicating much of this logic from user_store.go
@ -155,14 +151,14 @@ func (s *DefaultTopicStore) BulkGetMap(ids []int) (list map[int]*Topic, err erro
defer rows.Close()
for rows.Next() {
topic := &Topic{}
err := rows.Scan(&topic.ID, &topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.LastReplyBy, &topic.LastReplyAt, &topic.LastReplyID, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.ViewCount, &topic.PostCount, &topic.LikeCount, &topic.AttachCount, &topic.Poll, &topic.Data)
t := &Topic{}
err := rows.Scan(&t.ID, &t.Title, &t.Content, &t.CreatedBy, &t.CreatedAt, &t.LastReplyBy, &t.LastReplyAt, &t.LastReplyID, &t.IsClosed, &t.Sticky, &t.ParentID, &t.IPAddress, &t.ViewCount, &t.PostCount, &t.LikeCount, &t.AttachCount, &t.Poll, &t.Data)
if err != nil {
return list, err
}
topic.Link = BuildTopicURL(NameToSlug(topic.Title), topic.ID)
s.cache.Set(topic)
list[topic.ID] = topic
t.Link = BuildTopicURL(NameToSlug(t.Title), t.ID)
s.cache.Set(t)
list[t.ID] = t
}
err = rows.Err()
if err != nil {
@ -187,21 +183,19 @@ func (s *DefaultTopicStore) BulkGetMap(ids []int) (list map[int]*Topic, err erro
return list, err
}
func (mts *DefaultTopicStore) Reload(id int) error {
topic := &Topic{ID: id}
err := mts.get.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.LastReplyBy, &topic.LastReplyAt, &topic.LastReplyID, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.ViewCount, &topic.PostCount, &topic.LikeCount, &topic.AttachCount, &topic.Poll, &topic.Data)
func (s *DefaultTopicStore) Reload(id int) error {
topic, err := s.BypassGet(id)
if err == nil {
topic.Link = BuildTopicURL(NameToSlug(topic.Title), id)
_ = mts.cache.Set(topic)
_ = s.cache.Set(topic)
} else {
_ = mts.cache.Remove(id)
_ = s.cache.Remove(id)
}
TopicListThaw.Thaw()
return err
}
func (mts *DefaultTopicStore) Exists(id int) bool {
return mts.exists.QueryRow(id).Scan(&id) == nil
func (s *DefaultTopicStore) Exists(id int) bool {
return s.exists.QueryRow(id).Scan(&id) == nil
}
func (mts *DefaultTopicStore) Create(fid int, topicName string, content string, uid int, ipaddress string) (tid int, err error) {
@ -234,7 +228,7 @@ func (mts *DefaultTopicStore) Create(fid int, topicName string, content string,
}
// ? - What is this? Do we need it? Should it be in the main store interface?
func (mts *DefaultTopicStore) AddLastTopic(item *Topic, fid int) error {
func (s *DefaultTopicStore) AddLastTopic(item *Topic, fid int) error {
// Coming Soon...
return nil
}
@ -248,15 +242,15 @@ func (s *DefaultTopicStore) Count() (count int) {
return count
}
func (mts *DefaultTopicStore) SetCache(cache TopicCache) {
mts.cache = cache
func (s *DefaultTopicStore) SetCache(cache TopicCache) {
s.cache = cache
}
// TODO: We're temporarily doing this so that you can do tcache != nil in getTopicUser. Refactor it.
func (mts *DefaultTopicStore) GetCache() TopicCache {
_, ok := mts.cache.(*NullTopicCache)
func (s *DefaultTopicStore) GetCache() TopicCache {
_, ok := s.cache.(*NullTopicCache)
if ok {
return nil
}
return mts.cache
}
return s.cache
}

View File

@ -386,14 +386,17 @@ function PageOffset(count, page, perPage) {
function LastPage(count, perPage) {
return (count / perPage) + 1
}
function Paginate(count, perPage, maxPages) {
if(count < perPage) return [1];
let page = 0;
function Paginate(currentPage, lastPage, maxPages) {
let diff = lastPage - currentPage;
let pre = 3;
if(diff < 3) pre = maxPages - diff;
let page = currentPage - pre;
if(page < 0) page = 0;
let out = [];
for(let current = 0; current < count; current += perPage){
while(out.length < maxPages && page < lastPage){
page++;
out.push(page);
if(out.length >= maxPages) break;
}
return out;
}
@ -405,9 +408,9 @@ function mainInit(){
event.preventDefault();
let moreTopicBlocks = document.getElementsByClassName("more_topic_block_active");
for(let i = 0; i < moreTopicBlocks.length; i++) {
let moreTopicBlock = moreTopicBlocks[i];
moreTopicBlock.classList.remove("more_topic_block_active");
moreTopicBlock.classList.add("more_topic_block_initial");
let block = moreTopicBlocks[i];
block.classList.remove("more_topic_block_active");
block.classList.add("more_topic_block_initial");
}
$(".ajax_topic_dupe").fadeOut("slow", function(){
$(this).remove();
@ -437,9 +440,7 @@ function mainInit(){
data: { isJs: 1 },
error: ajaxError,
success: function (data, status, xhr) {
if("success" in data) {
if(data["success"] == "1") return;
}
if("success" in data && data["success"] == "1") return;
// addNotice("Failed to add a like: {err}")
likeButton.classList.add("add_like");
likeButton.classList.remove("remove_like");
@ -465,11 +466,8 @@ function mainInit(){
let urlParams = new URLSearchParams(window.location.search);
let page = urlParams.get('page');
if(page=="") page = 1;
let stopAtPage = lastPage;
if(stopAtPage>5) stopAtPage = 5;
let pageList = [];
for(let i = 0; i < stopAtPage;i++) pageList.push(i+1);
let pageList = Paginate(page,lastPage,5)
//$(".pageset").html(Template_paginator({PageList: pageList, Page: page, LastPage: lastPage}));
let ok = false;
$(".pageset").each(function(){
@ -804,7 +802,7 @@ function mainInit(){
// This one's for Tempra Conflux
// TODO: We might want to use pure JS here
$(".ip_item").each(function(){
/*$(".ip_item").each(function(){
var ip = this.textContent;
if(ip.length > 10){
this.innerHTML = "Show IP";
@ -813,7 +811,7 @@ function mainInit(){
this.textContent = ip;
};
}
});
});*/
$(".quote_item").click(function(){
event.preventDefault();

View File

@ -724,7 +724,7 @@ func AccountLogins(w http.ResponseWriter, r *http.Request, user c.User, header *
return c.InternalError(err, w, r)
}
pageList := c.Paginate(logCount, perPage, 5)
pageList := c.Paginate(page, lastPage, 5)
pi := c.Account{header, "logins", "account_logins", c.AccountLoginsPage{header, logs, c.Paginator{pageList, page, lastPage}}}
return renderTemplate("account", w, r, header, pi)
}

View File

@ -122,7 +122,7 @@ func ViewForum(w http.ResponseWriter, r *http.Request, user c.User, header *c.He
return nil
}
pageList := c.Paginate(forum.TopicCount, c.Config.ItemsPerPage, 5)
pageList := c.Paginate(page, lastPage, 5)
pi := c.ForumPage{header, topicList, forum, c.Paginator{pageList, page, lastPage}}
tmpl := forum.Tmpl
if tmpl == "" {

View File

@ -57,7 +57,7 @@ func Groups(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError {
count++
}
pageList := c.Paginate(basePage.Stats.Groups, perPage, 5)
pageList := c.Paginate(page, lastPage, 5)
pi := c.PanelGroupPage{basePage, groupList, c.Paginator{pageList, page, lastPage}}
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage,"","","panel_groups",&pi})
}

View File

@ -32,7 +32,7 @@ func LogsRegs(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError
llist[index] = c.PageRegLogItem{log, strings.Replace(strings.TrimSuffix(log.FailureReason, "|"), "|", " | ", -1)}
}
pageList := c.Paginate(logCount, perPage, 5)
pageList := c.Paginate(page, lastPage, 5)
pi := c.PanelRegLogsPage{basePage, llist, c.Paginator{pageList, page, lastPage}}
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage,"","","panel_reglogs", pi})
}
@ -123,7 +123,7 @@ func LogsMod(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError {
llist[index] = c.PageLogItem{Action: template.HTML(action), IPAddress: log.IPAddress, DoneAt: log.DoneAt}
}
pageList := c.Paginate(logCount, perPage, 5)
pageList := c.Paginate(page, lastPage, 5)
pi := c.PanelLogsPage{basePage, llist, c.Paginator{pageList, page, lastPage}}
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage,"","","panel_modlogs", pi})
}
@ -150,7 +150,7 @@ func LogsAdmin(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError
llist[index] = c.PageLogItem{Action: template.HTML(action), IPAddress: log.IPAddress, DoneAt: log.DoneAt}
}
pageList := c.Paginate(logCount, perPage, 5)
pageList := c.Paginate(page, lastPage, 5)
pi := c.PanelLogsPage{basePage, llist, c.Paginator{pageList, page, lastPage}}
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage,"","","panel_adminlogs", pi})
}

View File

@ -31,9 +31,9 @@ func Pages(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError {
return c.InternalError(err, w, r)
}
pageList := c.Paginate(pageCount, perPage, 5)
pageList := c.Paginate(page, lastPage, 5)
pi := c.PanelCustomPagesPage{basePage, cPages, c.Paginator{pageList, page, lastPage}}
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage,"panel_page_list","","panel_pages",&pi})
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage, "panel_page_list", "", "panel_pages", &pi})
}
func PagesCreateSubmit(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError {
@ -90,7 +90,7 @@ func PagesEdit(w http.ResponseWriter, r *http.Request, user c.User, spid string)
}
pi := c.PanelCustomPageEditPage{basePage, page}
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage,"panel_page_edit","","panel_pages_edit",&pi})
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage, "panel_page_edit", "", "panel_pages_edit", &pi})
}
func PagesEditSubmit(w http.ResponseWriter, r *http.Request, user c.User, spid string) c.RouteError {

View File

@ -23,7 +23,7 @@ func Users(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError {
return c.InternalError(err, w, r)
}
pageList := c.Paginate(basePage.Stats.Users, perPage, 5)
pageList := c.Paginate(page, lastPage, 5)
pi := c.PanelUserPage{basePage, users, c.Paginator{pageList, page, lastPage}}
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage,"","","panel_users",&pi})
}

View File

@ -126,7 +126,7 @@ func ViewTopic(w http.ResponseWriter, r *http.Request, user c.User, header *c.He
// Calculate the offset
offset, page, lastPage := c.PageOffset(topic.PostCount, page, c.Config.ItemsPerPage)
pageList := c.Paginate(topic.PostCount, c.Config.ItemsPerPage, 5)
pageList := c.Paginate(page, lastPage, 5)
tpage := c.TopicPage{header, nil, topic, forum, poll, c.Paginator{pageList, page, lastPage}}
// Get the replies if we have any...