+35
-20
appview/db/issues.go
+35
-20
appview/db/issues.go
···
5
"time"
6
7
"github.com/bluesky-social/indigo/atproto/syntax"
8
)
9
10
type Issue struct {
···
102
return ownerDid, err
103
}
104
105
-
func GetIssues(e Execer, repoAt syntax.ATURI, isOpen bool) ([]Issue, error) {
106
var issues []Issue
107
openValue := 0
108
if isOpen {
···
110
}
111
112
rows, err := e.Query(
113
-
`select
114
-
i.owner_did,
115
-
i.issue_id,
116
-
i.created,
117
-
i.title,
118
-
i.body,
119
-
i.open,
120
-
count(c.id)
121
-
from
122
-
issues i
123
-
left join
124
-
comments c on i.repo_at = c.repo_at and i.issue_id = c.issue_id
125
-
where
126
-
i.repo_at = ? and i.open = ?
127
-
group by
128
-
i.id, i.owner_did, i.issue_id, i.created, i.title, i.body, i.open
129
-
order by
130
-
i.created desc`,
131
-
repoAt, openValue)
132
if err != nil {
133
return nil, err
134
}
···
5
"time"
6
7
"github.com/bluesky-social/indigo/atproto/syntax"
8
+
"tangled.sh/tangled.sh/core/appview/pagination"
9
)
10
11
type Issue struct {
···
103
return ownerDid, err
104
}
105
106
+
func GetIssues(e Execer, repoAt syntax.ATURI, isOpen bool, page pagination.Page) ([]Issue, error) {
107
var issues []Issue
108
openValue := 0
109
if isOpen {
···
111
}
112
113
rows, err := e.Query(
114
+
`
115
+
with numbered_issue as (
116
+
select
117
+
i.owner_did,
118
+
i.issue_id,
119
+
i.created,
120
+
i.title,
121
+
i.body,
122
+
i.open,
123
+
count(c.id) as comment_count,
124
+
row_number() over (order by i.created desc) as row_num
125
+
from
126
+
issues i
127
+
left join
128
+
comments c on i.repo_at = c.repo_at and i.issue_id = c.issue_id
129
+
where
130
+
i.repo_at = ? and i.open = ?
131
+
group by
132
+
i.id, i.owner_did, i.issue_id, i.created, i.title, i.body, i.open
133
+
)
134
+
select
135
+
owner_did,
136
+
issue_id,
137
+
created,
138
+
title,
139
+
body,
140
+
open,
141
+
comment_count
142
+
from
143
+
numbered_issue
144
+
where
145
+
row_num between ? and ?`,
146
+
repoAt, openValue, page.Offset+1, page.Offset+page.Limit)
147
if err != nil {
148
return nil, err
149
}
+32
appview/middleware/middleware.go
+32
appview/middleware/middleware.go
···
1
package middleware
2
3
import (
4
"log"
5
"net/http"
6
"time"
7
8
comatproto "github.com/bluesky-social/indigo/api/atproto"
9
"github.com/bluesky-social/indigo/xrpc"
10
"tangled.sh/tangled.sh/core/appview"
11
"tangled.sh/tangled.sh/core/appview/auth"
12
)
13
14
type Middleware func(http.Handler) http.Handler
···
92
})
93
}
94
}
···
1
package middleware
2
3
import (
4
+
"context"
5
"log"
6
"net/http"
7
+
"strconv"
8
"time"
9
10
comatproto "github.com/bluesky-social/indigo/api/atproto"
11
"github.com/bluesky-social/indigo/xrpc"
12
"tangled.sh/tangled.sh/core/appview"
13
"tangled.sh/tangled.sh/core/appview/auth"
14
+
"tangled.sh/tangled.sh/core/appview/pagination"
15
)
16
17
type Middleware func(http.Handler) http.Handler
···
95
})
96
}
97
}
98
+
99
+
func Paginate(next http.Handler) http.Handler {
100
+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
101
+
page := pagination.FirstPage()
102
+
103
+
offsetVal := r.URL.Query().Get("offset")
104
+
if offsetVal != "" {
105
+
offset, err := strconv.Atoi(offsetVal)
106
+
if err != nil {
107
+
log.Println("invalid offset")
108
+
} else {
109
+
page.Offset = offset
110
+
}
111
+
}
112
+
113
+
limitVal := r.URL.Query().Get("limit")
114
+
if limitVal != "" {
115
+
limit, err := strconv.Atoi(limitVal)
116
+
if err != nil {
117
+
log.Println("invalid limit")
118
+
} else {
119
+
page.Limit = limit
120
+
}
121
+
}
122
+
123
+
ctx := context.WithValue(r.Context(), "page", page)
124
+
next.ServeHTTP(w, r.WithContext(ctx))
125
+
})
126
+
}
+7
-6
appview/pages/pages.go
+7
-6
appview/pages/pages.go
···
19
"tangled.sh/tangled.sh/core/appview/auth"
20
"tangled.sh/tangled.sh/core/appview/db"
21
"tangled.sh/tangled.sh/core/appview/pages/markup"
22
"tangled.sh/tangled.sh/core/appview/state/userutil"
23
"tangled.sh/tangled.sh/core/patchutil"
24
"tangled.sh/tangled.sh/core/types"
···
564
}
565
566
type RepoIssuesParams struct {
567
-
LoggedInUser *auth.User
568
-
RepoInfo RepoInfo
569
-
Active string
570
-
Issues []db.Issue
571
-
DidHandleMap map[string]string
572
-
573
FilteringByOpen bool
574
}
575
···
19
"tangled.sh/tangled.sh/core/appview/auth"
20
"tangled.sh/tangled.sh/core/appview/db"
21
"tangled.sh/tangled.sh/core/appview/pages/markup"
22
+
"tangled.sh/tangled.sh/core/appview/pagination"
23
"tangled.sh/tangled.sh/core/appview/state/userutil"
24
"tangled.sh/tangled.sh/core/patchutil"
25
"tangled.sh/tangled.sh/core/types"
···
565
}
566
567
type RepoIssuesParams struct {
568
+
LoggedInUser *auth.User
569
+
RepoInfo RepoInfo
570
+
Active string
571
+
Issues []db.Issue
572
+
DidHandleMap map[string]string
573
+
Page pagination.Page
574
FilteringByOpen bool
575
}
576
+38
appview/pages/templates/repo/issues/issues.html
+38
appview/pages/templates/repo/issues/issues.html
···
70
</div>
71
{{ end }}
72
</div>
73
+
74
+
{{ block "pagination" . }} {{ end }}
75
+
76
+
{{ end }}
77
+
78
+
{{ define "pagination" }}
79
+
<div class="flex justify-end mt-4 gap-2">
80
+
{{ $currentState := "closed" }}
81
+
{{ if .FilteringByOpen }}
82
+
{{ $currentState = "open" }}
83
+
{{ end }}
84
+
85
+
{{ if gt .Page.Offset 0 }}
86
+
{{ $prev := .Page.Previous }}
87
+
<a
88
+
class="btn flex items-center gap-2 no-underline hover:no-underline dark:text-white dark:hover:bg-gray-700"
89
+
hx-boost="true"
90
+
href = "/{{ $.RepoInfo.FullName }}/issues?state={{ $currentState }}&offset={{ $prev.Offset }}&limit={{ $prev.Limit }}"
91
+
>
92
+
{{ i "chevron-left" "w-4 h-4" }}
93
+
previous
94
+
</a>
95
+
{{ else }}
96
+
<div></div>
97
+
{{ end }}
98
+
99
+
{{ if eq (len .Issues) .Page.Limit }}
100
+
{{ $next := .Page.Next }}
101
+
<a
102
+
class="btn flex items-center gap-2 no-underline hover:no-underline dark:text-white dark:hover:bg-gray-700"
103
+
hx-boost="true"
104
+
href = "/{{ $.RepoInfo.FullName }}/issues?state={{ $currentState }}&offset={{ $next.Offset }}&limit={{ $next.Limit }}"
105
+
>
106
+
next
107
+
{{ i "chevron-right" "w-4 h-4" }}
108
+
</a>
109
+
{{ end }}
110
+
</div>
111
{{ end }}
+31
appview/pagination/page.go
+31
appview/pagination/page.go
···
···
1
+
package pagination
2
+
3
+
type Page struct {
4
+
Offset int // where to start from
5
+
Limit int // number of items in a page
6
+
}
7
+
8
+
func FirstPage() Page {
9
+
return Page{
10
+
Offset: 0,
11
+
Limit: 10,
12
+
}
13
+
}
14
+
15
+
func (p Page) Previous() Page {
16
+
if p.Offset-p.Limit < 0 {
17
+
return FirstPage()
18
+
} else {
19
+
return Page{
20
+
Offset: p.Offset - p.Limit,
21
+
Limit: p.Limit,
22
+
}
23
+
}
24
+
}
25
+
26
+
func (p Page) Next() Page {
27
+
return Page{
28
+
Offset: p.Offset + p.Limit,
29
+
Limit: p.Limit,
30
+
}
31
+
}
+9
-1
appview/state/repo.go
+9
-1
appview/state/repo.go
···
28
"tangled.sh/tangled.sh/core/appview/db"
29
"tangled.sh/tangled.sh/core/appview/pages"
30
"tangled.sh/tangled.sh/core/appview/pages/markup"
31
"tangled.sh/tangled.sh/core/types"
32
33
comatproto "github.com/bluesky-social/indigo/api/atproto"
···
1559
isOpen = true
1560
}
1561
1562
user := s.auth.GetUser(r)
1563
f, err := fullyResolvedRepo(r)
1564
if err != nil {
···
1566
return
1567
}
1568
1569
-
issues, err := db.GetIssues(s.db, f.RepoAt, isOpen)
1570
if err != nil {
1571
log.Println("failed to get issues", err)
1572
s.pages.Notice(w, "issues", "Failed to load issues. Try again later.")
···
1593
Issues: issues,
1594
DidHandleMap: didHandleMap,
1595
FilteringByOpen: isOpen,
1596
})
1597
return
1598
}
···
28
"tangled.sh/tangled.sh/core/appview/db"
29
"tangled.sh/tangled.sh/core/appview/pages"
30
"tangled.sh/tangled.sh/core/appview/pages/markup"
31
+
"tangled.sh/tangled.sh/core/appview/pagination"
32
"tangled.sh/tangled.sh/core/types"
33
34
comatproto "github.com/bluesky-social/indigo/api/atproto"
···
1560
isOpen = true
1561
}
1562
1563
+
page, ok := r.Context().Value("page").(pagination.Page)
1564
+
if !ok {
1565
+
log.Println("failed to get page")
1566
+
page = pagination.FirstPage()
1567
+
}
1568
+
1569
user := s.auth.GetUser(r)
1570
f, err := fullyResolvedRepo(r)
1571
if err != nil {
···
1573
return
1574
}
1575
1576
+
issues, err := db.GetIssues(s.db, f.RepoAt, isOpen, page)
1577
if err != nil {
1578
log.Println("failed to get issues", err)
1579
s.pages.Notice(w, "issues", "Failed to load issues. Try again later.")
···
1600
Issues: issues,
1601
DidHandleMap: didHandleMap,
1602
FilteringByOpen: isOpen,
1603
+
Page: page,
1604
})
1605
return
1606
}
+1
-1
appview/state/router.go
+1
-1
appview/state/router.go