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

View File

@ -527,7 +527,7 @@ func compileJSTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName stri
itemsPerPage := 25 itemsPerPage := 25
_, page, lastPage := PageOffset(20, 1, itemsPerPage) _, 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("paginator", "common.Paginator", Paginator{pageList, page, lastPage})
tmpls.AddStd("topic_c_edit_post", "common.TopicCEditPost", TopicCEditPost{ID: 0, Source: "", Ref: ""}) 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] topic.LastUser = userList[topic.LastReplyBy]
} }
pageList := Paginate(topicCount, Config.ItemsPerPage, 5) pageList := Paginate(page, lastPage, 5)
return topicList, Paginator{pageList, page, lastPage}, nil return topicList, Paginator{pageList, page, lastPage}, nil
} }

View File

@ -66,44 +66,40 @@ func NewDefaultTopicStore(cache TopicCache) (*DefaultTopicStore, error) {
}, acc.FirstError() }, acc.FirstError()
} }
func (mts *DefaultTopicStore) DirtyGet(id int) *Topic { func (s *DefaultTopicStore) DirtyGet(id int) *Topic {
topic, err := mts.cache.Get(id) topic, err := s.cache.Get(id)
if err == nil { if err == nil {
return topic return topic
} }
topic, err = s.BypassGet(id)
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)
if err == nil { if err == nil {
topic.Link = BuildTopicURL(NameToSlug(topic.Title), id) _ = s.cache.Set(topic)
_ = mts.cache.Set(topic)
return topic return topic
} }
return BlankTopic() return BlankTopic()
} }
// TODO: Log weird cache errors? // TODO: Log weird cache errors?
func (mts *DefaultTopicStore) Get(id int) (topic *Topic, err error) { func (s *DefaultTopicStore) Get(id int) (topic *Topic, err error) {
topic, err = mts.cache.Get(id) topic, err = s.cache.Get(id)
if err == nil { if err == nil {
return topic, nil return topic, nil
} }
topic, err = s.BypassGet(id)
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)
if err == nil { if err == nil {
topic.Link = BuildTopicURL(NameToSlug(topic.Title), id) _ = s.cache.Set(topic)
_ = mts.cache.Set(topic)
} }
return topic, err return topic, err
} }
// BypassGet will always bypass the cache and pull the topic directly from the database // BypassGet will always bypass the cache and pull the topic directly from the database
func (mts *DefaultTopicStore) BypassGet(id int) (*Topic, error) { func (s *DefaultTopicStore) BypassGet(id int) (*Topic, error) {
topic := &Topic{ID: id} t := &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) 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)
topic.Link = BuildTopicURL(NameToSlug(topic.Title), id) if err == nil {
return topic, err t.Link = BuildTopicURL(NameToSlug(t.Title), id)
}
return t, err
} }
// TODO: Avoid duplicating much of this logic from user_store.go // 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() defer rows.Close()
for rows.Next() { for rows.Next() {
topic := &Topic{} t := &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) 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 { if err != nil {
return list, err return list, err
} }
topic.Link = BuildTopicURL(NameToSlug(topic.Title), topic.ID) t.Link = BuildTopicURL(NameToSlug(t.Title), t.ID)
s.cache.Set(topic) s.cache.Set(t)
list[topic.ID] = topic list[t.ID] = t
} }
err = rows.Err() err = rows.Err()
if err != nil { if err != nil {
@ -187,21 +183,19 @@ func (s *DefaultTopicStore) BulkGetMap(ids []int) (list map[int]*Topic, err erro
return list, err return list, err
} }
func (mts *DefaultTopicStore) Reload(id int) error { func (s *DefaultTopicStore) Reload(id int) error {
topic := &Topic{ID: id} topic, err := s.BypassGet(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)
if err == nil { if err == nil {
topic.Link = BuildTopicURL(NameToSlug(topic.Title), id) _ = s.cache.Set(topic)
_ = mts.cache.Set(topic)
} else { } else {
_ = mts.cache.Remove(id) _ = s.cache.Remove(id)
} }
TopicListThaw.Thaw() TopicListThaw.Thaw()
return err return err
} }
func (mts *DefaultTopicStore) Exists(id int) bool { func (s *DefaultTopicStore) Exists(id int) bool {
return mts.exists.QueryRow(id).Scan(&id) == nil 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) { 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? // ? - 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... // Coming Soon...
return nil return nil
} }
@ -248,15 +242,15 @@ func (s *DefaultTopicStore) Count() (count int) {
return count return count
} }
func (mts *DefaultTopicStore) SetCache(cache TopicCache) { func (s *DefaultTopicStore) SetCache(cache TopicCache) {
mts.cache = cache s.cache = cache
} }
// TODO: We're temporarily doing this so that you can do tcache != nil in getTopicUser. Refactor it. // TODO: We're temporarily doing this so that you can do tcache != nil in getTopicUser. Refactor it.
func (mts *DefaultTopicStore) GetCache() TopicCache { func (s *DefaultTopicStore) GetCache() TopicCache {
_, ok := mts.cache.(*NullTopicCache) _, ok := s.cache.(*NullTopicCache)
if ok { if ok {
return nil return nil
} }
return mts.cache return s.cache
} }

View File

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

View File

@ -57,7 +57,7 @@ func Groups(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError {
count++ 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}} pi := c.PanelGroupPage{basePage, groupList, c.Paginator{pageList, page, lastPage}}
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage,"","","panel_groups",&pi}) 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)} 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}} pi := c.PanelRegLogsPage{basePage, llist, c.Paginator{pageList, page, lastPage}}
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage,"","","panel_reglogs", pi}) 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} 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}} pi := c.PanelLogsPage{basePage, llist, c.Paginator{pageList, page, lastPage}}
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage,"","","panel_modlogs", pi}) 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} 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}} pi := c.PanelLogsPage{basePage, llist, c.Paginator{pageList, page, lastPage}}
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage,"","","panel_adminlogs", pi}) 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) 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}} 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 { 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} 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 { 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) 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}} pi := c.PanelUserPage{basePage, users, c.Paginator{pageList, page, lastPage}}
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage,"","","panel_users",&pi}) 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 // Calculate the offset
offset, page, lastPage := c.PageOffset(topic.PostCount, page, c.Config.ItemsPerPage) 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}} tpage := c.TopicPage{header, nil, topic, forum, poll, c.Paginator{pageList, page, lastPage}}
// Get the replies if we have any... // Get the replies if we have any...