From 8a57174397cb5864d098b52f9e9fc6170fc3733e Mon Sep 17 00:00:00 2001 From: Akshay Date: Fri, 25 Apr 2025 10:41:21 +0000 Subject: [PATCH] appview: repo/issues: paginate issues loading up issues on @tangled.sh/core should be much faster because we only have to resolve ~10 DIDs at a time. with a saturated did-handle cache, this should be near-instant page load times. --- appview/db/issues.go | 55 +++++++++++++++++++++++++++++++++++-------------------- appview/middleware/middleware.go | 32 ++++++++++++++++++++++++++++++++ appview/pages/pages.go | 13 +++++++------ appview/pagination/page.go | 31 +++++++++++++++++++++++++++++++ appview/state/repo.go | 10 +++++++++- appview/state/router.go | 2 +- appview/pages/templates/repo/issues/issues.html | 38 ++++++++++++++++++++++++++++++++++++++ 7 file(s) changed, 153 insertion(s)(+), 28 deletion(s)(-) diff --git a/appview/db/issues.go b/appview/db/issues.go --- a/appview/db/issues.go +++ b/appview/db/issues.go @@ -5,6 +5,7 @@ "time" "github.com/bluesky-social/indigo/atproto/syntax" + "tangled.sh/tangled.sh/core/appview/pagination" ) type Issue struct { @@ -102,7 +103,7 @@ return ownerDid, err } -func GetIssues(e Execer, repoAt syntax.ATURI, isOpen bool) ([]Issue, error) { +func GetIssues(e Execer, repoAt syntax.ATURI, isOpen bool, page pagination.Page) ([]Issue, error) { var issues []Issue openValue := 0 if isOpen { @@ -110,25 +111,39 @@ } rows, err := e.Query( - `select - i.owner_did, - i.issue_id, - i.created, - i.title, - i.body, - i.open, - count(c.id) - from - issues i - left join - comments c on i.repo_at = c.repo_at and i.issue_id = c.issue_id - where - i.repo_at = ? and i.open = ? - group by - i.id, i.owner_did, i.issue_id, i.created, i.title, i.body, i.open - order by - i.created desc`, - repoAt, openValue) + ` + with numbered_issue as ( + select + i.owner_did, + i.issue_id, + i.created, + i.title, + i.body, + i.open, + count(c.id) as comment_count, + row_number() over (order by i.created desc) as row_num + from + issues i + left join + comments c on i.repo_at = c.repo_at and i.issue_id = c.issue_id + where + i.repo_at = ? and i.open = ? + group by + i.id, i.owner_did, i.issue_id, i.created, i.title, i.body, i.open + ) + select + owner_did, + issue_id, + created, + title, + body, + open, + comment_count + from + numbered_issue + where + row_num between ? and ?`, + repoAt, openValue, page.Offset+1, page.Offset+page.Limit) if err != nil { return nil, err } diff --git a/appview/middleware/middleware.go b/appview/middleware/middleware.go --- a/appview/middleware/middleware.go +++ b/appview/middleware/middleware.go @@ -1,14 +1,17 @@ package middleware import ( + "context" "log" "net/http" + "strconv" "time" comatproto "github.com/bluesky-social/indigo/api/atproto" "github.com/bluesky-social/indigo/xrpc" "tangled.sh/tangled.sh/core/appview" "tangled.sh/tangled.sh/core/appview/auth" + "tangled.sh/tangled.sh/core/appview/pagination" ) type Middleware func(http.Handler) http.Handler @@ -91,4 +94,33 @@ next.ServeHTTP(w, r) }) } +} + +func Paginate(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + page := pagination.FirstPage() + + offsetVal := r.URL.Query().Get("offset") + if offsetVal != "" { + offset, err := strconv.Atoi(offsetVal) + if err != nil { + log.Println("invalid offset") + } else { + page.Offset = offset + } + } + + limitVal := r.URL.Query().Get("limit") + if limitVal != "" { + limit, err := strconv.Atoi(limitVal) + if err != nil { + log.Println("invalid limit") + } else { + page.Limit = limit + } + } + + ctx := context.WithValue(r.Context(), "page", page) + next.ServeHTTP(w, r.WithContext(ctx)) + }) } diff --git a/appview/pages/pages.go b/appview/pages/pages.go --- a/appview/pages/pages.go +++ b/appview/pages/pages.go @@ -20,6 +20,7 @@ "tangled.sh/tangled.sh/core/appview/auth" "tangled.sh/tangled.sh/core/appview/db" "tangled.sh/tangled.sh/core/appview/pages/markup" + "tangled.sh/tangled.sh/core/appview/pagination" "tangled.sh/tangled.sh/core/appview/state/userutil" "tangled.sh/tangled.sh/core/patchutil" "tangled.sh/tangled.sh/core/types" @@ -651,12 +652,12 @@ } type RepoIssuesParams struct { - LoggedInUser *auth.User - RepoInfo RepoInfo - Active string - Issues []db.Issue - DidHandleMap map[string]string - + LoggedInUser *auth.User + RepoInfo RepoInfo + Active string + Issues []db.Issue + DidHandleMap map[string]string + Page pagination.Page FilteringByOpen bool } diff --git a/appview/pagination/page.go b/appview/pagination/page.go new file mode 100644 --- /dev/null +++ b/appview/pagination/page.go @@ -0,0 +1,31 @@ +package pagination + +type Page struct { + Offset int // where to start from + Limit int // number of items in a page +} + +func FirstPage() Page { + return Page{ + Offset: 0, + Limit: 10, + } +} + +func (p Page) Previous() Page { + if p.Offset-p.Limit < 0 { + return FirstPage() + } else { + return Page{ + Offset: p.Offset - p.Limit, + Limit: p.Limit, + } + } +} + +func (p Page) Next() Page { + return Page{ + Offset: p.Offset + p.Limit, + Limit: p.Limit, + } +} diff --git a/appview/state/repo.go b/appview/state/repo.go --- a/appview/state/repo.go +++ b/appview/state/repo.go @@ -28,6 +28,7 @@ "tangled.sh/tangled.sh/core/appview/db" "tangled.sh/tangled.sh/core/appview/pages" "tangled.sh/tangled.sh/core/appview/pages/markup" + "tangled.sh/tangled.sh/core/appview/pagination" "tangled.sh/tangled.sh/core/types" comatproto "github.com/bluesky-social/indigo/api/atproto" @@ -1559,6 +1560,12 @@ isOpen = true } + page, ok := r.Context().Value("page").(pagination.Page) + if !ok { + log.Println("failed to get page") + page = pagination.FirstPage() + } + user := s.auth.GetUser(r) f, err := fullyResolvedRepo(r) if err != nil { @@ -1566,7 +1573,7 @@ return } - issues, err := db.GetIssues(s.db, f.RepoAt, isOpen) + issues, err := db.GetIssues(s.db, f.RepoAt, isOpen, page) if err != nil { log.Println("failed to get issues", err) s.pages.Notice(w, "issues", "Failed to load issues. Try again later.") @@ -1593,6 +1600,7 @@ Issues: issues, DidHandleMap: didHandleMap, FilteringByOpen: isOpen, + Page: page, }) return } diff --git a/appview/state/router.go b/appview/state/router.go --- a/appview/state/router.go +++ b/appview/state/router.go @@ -68,7 +68,7 @@ r.Get("/blob/{ref}/raw/*", s.RepoBlobRaw) r.Route("/issues", func(r chi.Router) { - r.Get("/", s.RepoIssues) + r.With(middleware.Paginate).Get("/", s.RepoIssues) r.Get("/{issue}", s.RepoSingleIssue) r.Group(func(r chi.Router) { diff --git a/appview/pages/templates/repo/issues/issues.html b/appview/pages/templates/repo/issues/issues.html --- a/appview/pages/templates/repo/issues/issues.html +++ b/appview/pages/templates/repo/issues/issues.html @@ -70,4 +70,42 @@ {{ end }} + +{{ block "pagination" . }} {{ end }} + +{{ end }} + +{{ define "pagination" }} +
+ {{ $currentState := "closed" }} + {{ if .FilteringByOpen }} + {{ $currentState = "open" }} + {{ end }} + + {{ if gt .Page.Offset 0 }} + {{ $prev := .Page.Previous }} + + {{ i "chevron-left" "w-4 h-4" }} + previous + + {{ else }} +
+ {{ end }} + + {{ if eq (len .Issues) .Page.Limit }} + {{ $next := .Page.Next }} + + next + {{ i "chevron-right" "w-4 h-4" }} + + {{ end }} +
{{ end }} -- tangled.sh