Signed-off-by: Seongmin Lee boltlessengineer@proton.me
+87
-37
appview/db/issues.go
+87
-37
appview/db/issues.go
···
2
2
3
3
import (
4
4
"database/sql"
5
+
"fmt"
6
+
"strconv"
7
+
"strings"
5
8
"time"
6
9
7
10
"github.com/bluesky-social/indigo/atproto/syntax"
11
+
"tangled.sh/tangled.sh/core/appview/models"
8
12
"tangled.sh/tangled.sh/core/appview/pagination"
9
13
)
10
14
···
160
164
return issues, nil
161
165
}
162
166
163
-
func GetIssues(e Execer, repoAt syntax.ATURI, isOpen bool, page pagination.Page) ([]Issue, error) {
164
-
var issues []Issue
167
+
// GetIssueIDs gets list of all existing issue's IDs
168
+
func GetIssueIDs(e Execer, opts models.IssueSearchOptions) ([]int64, error) {
169
+
var ids []int64
170
+
171
+
var filters []filter
165
172
openValue := 0
166
-
if isOpen {
173
+
if opts.IsOpen {
167
174
openValue = 1
168
175
}
176
+
filters = append(filters, FilterEq("open", openValue))
177
+
if opts.RepoAt != "" {
178
+
filters = append(filters, FilterEq("repo_at", opts.RepoAt))
179
+
}
169
180
170
-
rows, err := e.Query(
181
+
var conditions []string
182
+
var args []any
183
+
184
+
for _, filter := range filters {
185
+
conditions = append(conditions, filter.Condition())
186
+
args = append(args, filter.Arg()...)
187
+
}
188
+
189
+
whereClause := ""
190
+
if conditions != nil {
191
+
whereClause = " where " + strings.Join(conditions, " and ")
192
+
}
193
+
query := fmt.Sprintf(
171
194
`
172
-
with numbered_issue as (
173
-
select
174
-
i.id,
175
-
i.owner_did,
176
-
i.issue_id,
177
-
i.created,
178
-
i.title,
179
-
i.body,
180
-
i.open,
181
-
count(c.id) as comment_count,
182
-
row_number() over (order by i.created desc) as row_num
183
-
from
184
-
issues i
185
-
left join
186
-
comments c on i.repo_at = c.repo_at and i.issue_id = c.issue_id
187
-
where
188
-
i.repo_at = ? and i.open = ?
189
-
group by
190
-
i.id, i.owner_did, i.issue_id, i.created, i.title, i.body, i.open
191
-
)
192
195
select
193
-
id,
194
-
owner_did,
195
-
issue_id,
196
-
created,
197
-
title,
198
-
body,
199
-
open,
200
-
comment_count
201
-
from
202
-
numbered_issue
203
-
where
204
-
row_num between ? and ?`,
205
-
repoAt, openValue, page.Offset+1, page.Offset+page.Limit)
196
+
id
197
+
from
198
+
issues
199
+
%s
200
+
limit ? offset ?`,
201
+
whereClause,
202
+
)
203
+
args = append(args, opts.Page.Limit, opts.Page.Offset)
204
+
rows, err := e.Query(query, args...)
205
+
if err != nil {
206
+
return nil, err
207
+
}
208
+
defer rows.Close()
209
+
210
+
for rows.Next() {
211
+
var id int64
212
+
err := rows.Scan(&id)
213
+
if err != nil {
214
+
return nil, err
215
+
}
216
+
217
+
ids = append(ids, id)
218
+
}
219
+
220
+
return ids, nil
221
+
}
222
+
223
+
// GetIssuesByIDs gets list of issues from given IDs
224
+
func GetIssuesByIDs(e Execer, ids []int64) ([]Issue, error) {
225
+
var issues []Issue
226
+
227
+
// HACK: would be better to create "?,?,?,..." or use something like sqlx
228
+
idStrings := make([]string, len(ids))
229
+
for i, id := range ids {
230
+
idStrings[i] = strconv.FormatInt(id, 10)
231
+
}
232
+
idList := strings.Join(idStrings, ",")
233
+
query := fmt.Sprintf(
234
+
`
235
+
select
236
+
i.id,
237
+
i.owner_did,
238
+
i.issue_id,
239
+
i.created,
240
+
i.title,
241
+
i.body,
242
+
i.open,
243
+
count(c.id) as comment_count
244
+
from
245
+
issues i
246
+
left join
247
+
comments c on i.repo_at = c.repo_at and i.issue_id = c.issue_id
248
+
where
249
+
i.id in (%s)
250
+
group by
251
+
i.id
252
+
order by i.created desc`,
253
+
idList,
254
+
)
255
+
rows, err := e.Query(query)
206
256
if err != nil {
207
257
return nil, err
208
258
}
+28
-1
appview/issues/issues.go
+28
-1
appview/issues/issues.go
···
19
19
"tangled.sh/tangled.sh/core/appview/config"
20
20
"tangled.sh/tangled.sh/core/appview/db"
21
21
issues_indexer "tangled.sh/tangled.sh/core/appview/indexer/issues"
22
+
"tangled.sh/tangled.sh/core/appview/models"
22
23
"tangled.sh/tangled.sh/core/appview/notify"
23
24
"tangled.sh/tangled.sh/core/appview/oauth"
24
25
"tangled.sh/tangled.sh/core/appview/pages"
···
607
608
return
608
609
}
609
610
610
-
issues, err := db.GetIssues(rp.db, f.RepoAt, isOpen, page)
611
+
keyword := params.Get("q")
612
+
613
+
var ids []int64
614
+
searchOpts := models.IssueSearchOptions{
615
+
Keyword: keyword,
616
+
RepoAt: f.RepoAt.String(),
617
+
IsOpen: isOpen,
618
+
Page: page,
619
+
}
620
+
if keyword != "" {
621
+
res, err := rp.indexer.Search(r.Context(), searchOpts)
622
+
if err != nil {
623
+
log.Println("failed to search for issues", err)
624
+
return
625
+
}
626
+
log.Println("searched issues:", res.Hits)
627
+
ids = res.Hits
628
+
} else {
629
+
ids, err = db.GetIssueIDs(rp.db, searchOpts)
630
+
if err != nil {
631
+
log.Println("failed to search for issues", err)
632
+
return
633
+
}
634
+
}
635
+
636
+
issues, err := db.GetIssuesByIDs(rp.db, ids)
611
637
if err != nil {
612
638
log.Println("failed to get issues", err)
613
639
rp.pages.Notice(w, "issues", "Failed to load issues. Try again later.")
···
619
645
RepoInfo: f.RepoInfo(user),
620
646
Issues: issues,
621
647
FilteringByOpen: isOpen,
648
+
FilterQuery: keyword,
622
649
Page: page,
623
650
})
624
651
}
+9
-2
appview/pages/templates/repo/issues/issues.html
+9
-2
appview/pages/templates/repo/issues/issues.html
···
24
24
{{ i "ban" "w-4 h-4" }}
25
25
<span>{{ .RepoInfo.Stats.IssueCount.Closed }} closed</span>
26
26
</a>
27
+
<form class="flex gap-4" method="GET">
28
+
<input type="hidden" name="state" value="{{ if .FilteringByOpen }}open{{ else }}closed{{ end }}">
29
+
<input class="" type="text" name="q" value="{{ .FilterQuery }}">
30
+
<button class="btn" type="submit">
31
+
search
32
+
</button>
33
+
</form>
27
34
</div>
28
35
<a
29
36
href="/{{ .RepoInfo.FullName }}/issues/new"
···
100
107
<a
101
108
class="btn flex items-center gap-2 no-underline hover:no-underline dark:text-white dark:hover:bg-gray-700"
102
109
hx-boost="true"
103
-
href = "/{{ $.RepoInfo.FullName }}/issues?state={{ $currentState }}&offset={{ $prev.Offset }}&limit={{ $prev.Limit }}"
110
+
href = "/{{ $.RepoInfo.FullName }}/issues?state={{ $currentState }}&q={{ .FilterQuery }}&offset={{ $prev.Offset }}&limit={{ $prev.Limit }}"
104
111
>
105
112
{{ i "chevron-left" "w-4 h-4" }}
106
113
previous
···
114
121
<a
115
122
class="btn flex items-center gap-2 no-underline hover:no-underline dark:text-white dark:hover:bg-gray-700"
116
123
hx-boost="true"
117
-
href = "/{{ $.RepoInfo.FullName }}/issues?state={{ $currentState }}&offset={{ $next.Offset }}&limit={{ $next.Limit }}"
124
+
href = "/{{ $.RepoInfo.FullName }}/issues?state={{ $currentState }}&q={{ .FilterQuery }}&offset={{ $next.Offset }}&limit={{ $next.Limit }}"
118
125
>
119
126
next
120
127
{{ i "chevron-right" "w-4 h-4" }}