add pagination to microblog

This commit is contained in:
Travis Shears 2025-09-29 10:25:16 +02:00
parent ee4dfdad18
commit 444d143a0b

View file

@ -6,6 +6,8 @@ import (
"gemini_site/internal/pocketbase" "gemini_site/internal/pocketbase"
"log/slog" "log/slog"
"os" "os"
"regexp"
"strconv"
"strings" "strings"
"time" "time"
@ -131,8 +133,8 @@ func transformNostrPost(p pbPost) post {
} }
// GetRecentPosts returns the most recent posts // GetRecentPosts returns the most recent posts
func (mb *MicroBlog) GetRecentPosts(limit int) ([]post, error) { func (mb *MicroBlog) GetRecentPosts(limit int, page int) ([]post, error) {
res, err := mb.pbClient.GetList("micro_blog_posts", 1, limit, "-posted") res, err := mb.pbClient.GetList("micro_blog_posts", page, limit, "-posted")
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to fetch posts from pocketbase: %w", err) return nil, fmt.Errorf("failed to fetch posts from pocketbase: %w", err)
} }
@ -165,15 +167,36 @@ func (mb *MicroBlog) GetRecentPosts(limit int) ([]post, error) {
return filteredPosts, nil return filteredPosts, nil
} }
func replaceLinks(content string) string {
// Regex: ^(https?://\S+)
// ^ : start of line
// https? : http or https
// :// : literal
// \S+ : one or more non-whitespace characters
re := regexp.MustCompile(`(?m)^(https?://\S+)`)
// Replace each match with "=> <url>"
return re.ReplaceAllStringFunc(content, func(match string) string {
return "=> " + match
})
}
// HandleBlogRequest handles Gemini requests for the microblog // HandleBlogRequest handles Gemini requests for the microblog
func (mb *MicroBlog) HandleBlogRequest(w gemini.ResponseWriter, req *gemini.Request) { func (mb *MicroBlog) HandleBlogRequest(w gemini.ResponseWriter, req *gemini.Request) {
path := req.URL.Path path := req.URL.Path
switch { switch {
case path == "/microblog" || path == "/microblog/": case path == "/microblog" || path == "/microblog/":
mb.serveIndex(w, req) mb.serveIndex(w, req, 1)
case strings.HasPrefix(path, "/microblog/page/"):
pageNum := strings.TrimPrefix(path, "/microblog/page/")
num, err := strconv.Atoi(pageNum)
if err != nil {
w.WriteStatusMsg(gemini.StatusBadRequest, "Invalid page number")
return
}
mb.serveIndex(w, req, num)
// case strings.HasPrefix(path, "/microblog/post/"): // case strings.HasPrefix(path, "/microblog/post/"):
// postID := strings.TrimPrefix(path, "/microblog/post/") // postID := strings.TrimPrefix(path, "/microblog/post/")
// mb.servePost(w, req, postID) // mb.servePost(w, req, postID)
default: default:
w.WriteStatusMsg(gemini.StatusNotFound, "Page not found") w.WriteStatusMsg(gemini.StatusNotFound, "Page not found")
@ -182,7 +205,8 @@ func (mb *MicroBlog) HandleBlogRequest(w gemini.ResponseWriter, req *gemini.Requ
func drawPost(builder *strings.Builder, p post) { func drawPost(builder *strings.Builder, p post) {
builder.WriteString("+------------------------------------------+\n") builder.WriteString("+------------------------------------------+\n")
builder.WriteString(p.Content) content := replaceLinks(p.Content)
builder.WriteString(content)
builder.WriteString("\n") builder.WriteString("\n")
builder.WriteString(fmt.Sprintf("source: %s, id: %s...\n", p.Source, p.RemoteID[:10])) builder.WriteString(fmt.Sprintf("source: %s, id: %s...\n", p.Source, p.RemoteID[:10]))
builder.WriteString("+------------------------------------------+\n\n\n") builder.WriteString("+------------------------------------------+\n\n\n")
@ -193,22 +217,24 @@ func drawPost(builder *strings.Builder, p post) {
} }
// serveBlogIndex serves the main blog page with recent posts // serveBlogIndex serves the main blog page with recent posts
func (mb *MicroBlog) serveIndex(w gemini.ResponseWriter, req *gemini.Request) { func (mb *MicroBlog) serveIndex(w gemini.ResponseWriter, req *gemini.Request, pageNum int) {
w.WriteStatusMsg(gemini.StatusSuccess, "text/gemini") w.WriteStatusMsg(gemini.StatusSuccess, "text/gemini")
var content strings.Builder var content strings.Builder
// content.WriteString("# Gemini Microblog\n\n") // content.WriteString("# Gemini Microblog\n\n")
// content.WriteString("Here are my microblog posts from various plantforms\n") // content.WriteString("Here are my microblog posts from various plantforms\n")
// Read and include the contents of ../../pages/microblog.gmi // Read and include the contents of ../../pages/microblog.gmi
page, err := os.ReadFile("./pages/microblog.gmi") if pageNum == 1 {
if err != nil { page, err := os.ReadFile("./pages/microblog.gmi")
slog.Error("Problem reading microblog page", "error", err) if err != nil {
return slog.Error("Problem reading microblog page", "error", err)
return
}
content.Write(page)
content.WriteString("\n")
} }
content.Write(page)
content.WriteString("\n")
posts, err := mb.GetRecentPosts(20) posts, err := mb.GetRecentPosts(20, pageNum)
if err != nil { if err != nil {
content.WriteString("Error fetching posts: " + err.Error() + "\n\n") content.WriteString("Error fetching posts: " + err.Error() + "\n\n")
} }
@ -216,7 +242,7 @@ func (mb *MicroBlog) serveIndex(w gemini.ResponseWriter, req *gemini.Request) {
if len(posts) == 0 { if len(posts) == 0 {
content.WriteString("No posts found.\n\n") content.WriteString("No posts found.\n\n")
} else { } else {
content.WriteString("## Recent Posts\n\n") content.WriteString("## Posts\n\n")
for _, post := range posts { for _, post := range posts {
drawPost(&content, post) drawPost(&content, post)
@ -225,6 +251,8 @@ func (mb *MicroBlog) serveIndex(w gemini.ResponseWriter, req *gemini.Request) {
content.WriteString("## Nav\n\n") content.WriteString("## Nav\n\n")
// content.WriteString("=> /blog/new Write a new post\n") // content.WriteString("=> /blog/new Write a new post\n")
content.WriteString(fmt.Sprintf("=> /microblog/page/%d Next page\n", pageNum+1))
content.WriteString("=> / Back to home\n") content.WriteString("=> / Back to home\n")
w.WriteBody([]byte(content.String())) w.WriteBody([]byte(content.String()))