сделал пагинацию /posts

design
serr 2025-06-06 18:04:58 +03:00
parent 4327a14052
commit 380cec2c5a
15 changed files with 130 additions and 40 deletions

View File

@ -41,6 +41,14 @@ div {
overflow: hidden;
}
.pagi {
text-align: center;
background-color: transparent;
box-shadow: none;
border: none;
font-size: 1.5em;
}
h1 {
text-align: center;
padding: 30px;

12
main.go
View File

@ -63,12 +63,18 @@ func setupRoutes(app *models.App) *http.ServeMux {
{
// Обработка главной страницы
router.Handle("/", m(controllers_pages.MainPageHandler(app)))
// Обработка страницы со списком постов
router.Handle("/posts/", m(controllers_pages.PostsPageHandler(app)))
}
// Странички со списками постов
postsPagesCount := (len(app.PostsMap) + app.Cfg.PostsMaxCountOnPage - 1) / app.Cfg.PostsMaxCountOnPage
for i := range postsPagesCount {
router.Handle(
fmt.Sprintf("/posts/%d", i),
m(controllers_pages.PostsPageHandler(app)))
}
// Обработка страничек постов
for key := range app.Posts {
for key := range app.PostsMap {
postLink := string(key)
router.Handle(postLink, m(controllers_pages.PostPageHandler(app)))
}

BIN
main~ Normal file

Binary file not shown.

View File

@ -4,7 +4,6 @@ import (
"main/mvc/models"
"main/mvc/models/models_pages"
"net/http"
"strings"
)
// Обработчик главной страницы
@ -12,27 +11,21 @@ func PostPageHandler(app *models.App) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var err error
postLink := r.URL.Path
// Ссылки на посты имеют вид postLink = /link/, а если прилетело что то типо /link/123123,
// то надо оставить только часть /link/
secondSlash := strings.IndexByte(postLink[1:], '/')
if secondSlash != -1 {
postLink = postLink[:secondSlash+2]
}
// Ссылка вида /postname
link := r.URL.Path
// Страничка рендерится только если ее нет в кэше
pageData, ok := app.PagesCache.Get(postLink)
pageData, ok := app.PagesCache.Get(link)
if !ok {
post := app.Posts[models_pages.PostLink(postLink)]
post := app.PostsMap[models_pages.PostLink(link)]
pageData, err = post.RenderPostPage(app.Templates, app.Version)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
app.PagesCache.Set(postLink, pageData)
app.PagesCache.Set(link, pageData)
}
sendPostPage(w, pageData)

View File

@ -4,22 +4,39 @@ import (
"main/mvc/models"
"main/mvc/models/models_pages"
"net/http"
"path"
"strconv"
)
// Обработчик главной страницы
// Обработчик странички со списком постов
func PostsPageHandler(app *models.App) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var err error
// Ссылка вида /posts/0
link := r.URL.Path
// Страничка рендерится только если ее нет в кэше
pageData, ok := app.PagesCache.Get(models_pages.PostsPageTmplName)
pageData, ok := app.PagesCache.Get(link)
if !ok {
pageData, err = app.Posts.RenderPostsPage(app.Templates, app.Version)
// Из мапы формирую отсортированный список всех постов (сначала новые)
postsList := app.PostsMap.PostsList()
// Ошибки тут быть не может, так как этот обработчик настроен
// на только существующие реально pageNumber и в случае
// какого то не существующего номера страницы он просто
// не сработает (в главном main настроены маршруты)
pageNumber, _ := strconv.Atoi(path.Base(link))
page := models_pages.CreatePage(postsList, pageNumber, app.Cfg.PostsMaxCountOnPage)
pageData, err = page.RenderPostsPage(app.Templates, app.Version)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
app.PagesCache.Set(models_pages.PostsPageTmplName, pageData)
app.PagesCache.Set(link, pageData)
}
sendPostsPage(w, pageData)

View File

@ -7,12 +7,12 @@ import (
)
type App struct {
Cfg *Config // Сонфиг
Posts models_pages.Posts // Посты
Templates *template.Template // Шаблоны страниц
PagesCache *Cache // Кэш (отрендеренные странички)
LastfmLastTrack string // Последний трек, полученный с ластфм
Version int64 // Время запуска
Cfg *Config // Сонфиг
PostsMap models_pages.PostsMap // Посты
Templates *template.Template // Шаблоны страниц
PagesCache *Cache // Кэш (отрендеренные странички)
LastfmLastTrack string // Последний трек, полученный с ластфм
Version int64 // Время запуска
}
// Инициализирует приложение
@ -28,7 +28,7 @@ func InitApp() (*App, error) {
return nil, err
}
// Загрузка постов
if app.Posts, err = models_pages.LoadPosts(app.Cfg.PostsDir); err != nil {
if app.PostsMap, err = models_pages.LoadPosts(app.Cfg.PostsDir); err != nil {
return nil, err
}
// Загрузка шаблонов

View File

@ -12,6 +12,7 @@ const (
type Config struct {
PostsDir string
PostsMaxCountOnPage int
AssetsDir string
TemplatesDir string
TemplatesExt string

View File

@ -37,7 +37,7 @@ func (p *Post) RenderPostPage(templates *template.Template, version int64) ([]by
return pageData.Bytes(), nil
}
func newPost(link string, data []byte, timestamp int64) *Post {
func createPost(link string, data []byte, timestamp int64) *Post {
previewBuf := make([]byte, 0, 503)
if len(data) > 500 {

View File

@ -4,10 +4,12 @@ import (
"bytes"
"fmt"
"html/template"
"log"
"main/tools"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
"time"
)
@ -17,11 +19,16 @@ const (
PostsPageTmplName = "posts.gohtml"
)
type Posts map[PostLink]*Post
type PostsMap map[PostLink]*Post
type PostsPage struct {
PostsListOnPage []*Post // список постов на странице
PageNumber int // номер страницы
PagesCount int // общее количество страниц
}
func LoadPosts(dir string) (Posts, error) {
func LoadPosts(dir string) (PostsMap, error) {
posts := Posts{}
posts := PostsMap{}
err := filepath.Walk(dir, func(path string, f os.FileInfo, err error) error {
if err != nil {
@ -35,9 +42,24 @@ func LoadPosts(dir string) (Posts, error) {
}
html := tools.MdToHTML(md)
link := fmt.Sprintf("/%s/", strings.TrimSuffix(filepath.Base(path), ".md"))
timestamp := f.ModTime().Unix()
posts[PostLink(link)] = newPost(link, html, timestamp)
name := filepath.Base(path)
lowLineIndex := strings.Index(name, "_")
if lowLineIndex == -1 {
// Обработка случая, если "_" нет в имени
log.Fatal(`post name parse error`)
}
timestampStr := name[:lowLineIndex]
timestamp, err := strconv.ParseInt(timestampStr, 10, 64)
if err != nil {
// Ошибка парсинга timestamp
log.Fatal(`post name parse error`)
}
link := fmt.Sprintf("/%s", name[lowLineIndex+1:])
posts[PostLink(link)] = createPost(link, html, timestamp)
}
return nil
})
@ -49,23 +71,65 @@ func LoadPosts(dir string) (Posts, error) {
return posts, nil
}
func (p *Posts) RenderPostsPage(templates *template.Template, version int64) ([]byte, error) {
var pageData bytes.Buffer
// Получение из мапы постов списка постов, отсортированного по ModTimestamp (новые сначала)
func (p *PostsMap) PostsList() []*Post {
postsSlice := make([]*Post, 0, len(*p))
for _, post := range *p {
postsSlice = append(postsSlice, post)
}
// Сортирую по ModTimestamp (новые сначала)
sort.Slice(postsSlice, func(i, j int) bool {
return postsSlice[i].Timestamp > postsSlice[j].Timestamp
})
return postsSlice
}
func CreatePage(postsList []*Post, pageNumber, postsMaxCountOnPage int) *PostsPage {
// Общее количество страниц
pagesCount := (len(postsList) + postsMaxCountOnPage - 1) / postsMaxCountOnPage
// Ошибки тут быть не может, так как этот обработчик настроен
// на только существующие реально pageNumber и в случае
// какого то не существующего номера страницы он просто
// не сработает (в главном main настроены маршруты)
startIndex := pageNumber * postsMaxCountOnPage
endIndex := startIndex + postsMaxCountOnPage
endIndex = min(endIndex, len(postsList))
postsSublistForPageNumber := postsList[startIndex:endIndex]
return &PostsPage{
PostsListOnPage: postsSublistForPageNumber,
PageNumber: pageNumber,
PagesCount: pagesCount,
}
}
func (p *PostsPage) RenderPostsPage(templates *template.Template, version int64) ([]byte, error) {
var pageData bytes.Buffer
var prevPageNumber, nextPageNumber int
if p.PageNumber == 0 {
prevPageNumber = p.PagesCount - 1
} else {
prevPageNumber = p.PageNumber - 1
}
nextPageNumber = (p.PageNumber + 1) % p.PagesCount
pagi := fmt.Sprintf(
`<a href="/posts/%d"><-</a>&nbsp;&nbsp;<a href="/posts/%d">-></a>`,
prevPageNumber,
nextPageNumber,
)
context := map[string]any{
"pagi": template.HTML(pagi),
"version": version,
"renderingTimestamp": time.Now().Unix(),
"posts": postsSlice,
"posts": p.PostsListOnPage,
}
if err := templates.ExecuteTemplate(&pageData, PostsPageTmplName, context); err != nil {

View File

@ -19,7 +19,7 @@
</div>
<div>
<p>
see <a href="/">main page</a> or go to <a href="/posts/">posts section</a>
see <a href="/">main page</a> or go to <a href="/posts/0">posts section</a>
</p>
</div>
<div>

View File

@ -22,7 +22,7 @@
</p>
</div>
{{ end }}
<div class="pagi">{{ .pagi }}</div>
</main>
{{ template "footer" . }}
</body>

View File

@ -0,0 +1 @@
# 123