Signed-off-by: Seongmin Lee boltlessengineer@proton.me
+56
appview/db/issues.go
+56
appview/db/issues.go
···
247
return GetIssuesPaginated(e, pagination.FirstPage(), filters...)
248
}
249
250
+
// GetIssueIDs gets list of all existing issue's IDs
251
+
func GetIssueIDs(e Execer, opts models.IssueSearchOptions) ([]int64, error) {
252
+
var ids []int64
253
+
254
+
var filters []filter
255
+
openValue := 0
256
+
if opts.IsOpen {
257
+
openValue = 1
258
+
}
259
+
filters = append(filters, FilterEq("open", openValue))
260
+
if opts.RepoAt != "" {
261
+
filters = append(filters, FilterEq("repo_at", opts.RepoAt))
262
+
}
263
+
264
+
var conditions []string
265
+
var args []any
266
+
267
+
for _, filter := range filters {
268
+
conditions = append(conditions, filter.Condition())
269
+
args = append(args, filter.Arg()...)
270
+
}
271
+
272
+
whereClause := ""
273
+
if conditions != nil {
274
+
whereClause = " where " + strings.Join(conditions, " and ")
275
+
}
276
+
query := fmt.Sprintf(
277
+
`
278
+
select
279
+
id
280
+
from
281
+
issues
282
+
%s
283
+
limit ? offset ?`,
284
+
whereClause,
285
+
)
286
+
args = append(args, opts.Page.Limit, opts.Page.Offset)
287
+
rows, err := e.Query(query, args...)
288
+
if err != nil {
289
+
return nil, err
290
+
}
291
+
defer rows.Close()
292
+
293
+
for rows.Next() {
294
+
var id int64
295
+
err := rows.Scan(&id)
296
+
if err != nil {
297
+
return nil, err
298
+
}
299
+
300
+
ids = append(ids, id)
301
+
}
302
+
303
+
return ids, nil
304
+
}
305
+
306
func AddIssueComment(e Execer, c models.IssueComment) (int64, error) {
307
result, err := e.Exec(
308
`insert into issue_comments (
+28
-7
appview/issues/issues.go
+28
-7
appview/issues/issues.go
···
787
return
788
}
789
790
-
openVal := 0
791
-
if isOpen {
792
-
openVal = 1
793
}
794
-
issues, err := db.GetIssuesPaginated(
795
rp.db,
796
-
page,
797
-
db.FilterEq("repo_at", f.RepoAt()),
798
-
db.FilterEq("open", openVal),
799
)
800
if err != nil {
801
l.Error("failed to get issues", "err", err)
···
825
Issues: issues,
826
LabelDefs: defs,
827
FilteringByOpen: isOpen,
828
Page: page,
829
})
830
}
···
787
return
788
}
789
790
+
keyword := params.Get("q")
791
+
792
+
var ids []int64
793
+
searchOpts := models.IssueSearchOptions{
794
+
Keyword: keyword,
795
+
RepoAt: f.RepoAt().String(),
796
+
IsOpen: isOpen,
797
+
Page: page,
798
+
}
799
+
if keyword != "" {
800
+
res, err := rp.indexer.Search(r.Context(), searchOpts)
801
+
if err != nil {
802
+
l.Error("failed to search for issues", "err", err)
803
+
return
804
+
}
805
+
l.Debug("searched issues with indexer", "res.Hits", res.Hits)
806
+
ids = res.Hits
807
+
} else {
808
+
ids, err = db.GetIssueIDs(rp.db, searchOpts)
809
+
if err != nil {
810
+
l.Error("failed to search for issues", "err", err)
811
+
return
812
+
}
813
+
l.Debug("indexed all issues from the db", "ids", ids)
814
}
815
+
816
+
issues, err := db.GetIssues(
817
rp.db,
818
+
db.FilterIn("id", ids),
819
)
820
if err != nil {
821
l.Error("failed to get issues", "err", err)
···
845
Issues: issues,
846
LabelDefs: defs,
847
FilteringByOpen: isOpen,
848
+
FilterQuery: keyword,
849
Page: page,
850
})
851
}
+9
-2
appview/pages/templates/repo/issues/issues.html
+9
-2
appview/pages/templates/repo/issues/issues.html
···
24
{{ i "ban" "w-4 h-4" }}
25
<span>{{ .RepoInfo.Stats.IssueCount.Closed }} closed</span>
26
</a>
27
</div>
28
<a
29
href="/{{ .RepoInfo.FullName }}/issues/new"
···
55
<a
56
class="btn flex items-center gap-2 no-underline hover:no-underline dark:text-white dark:hover:bg-gray-700"
57
hx-boost="true"
58
-
href = "/{{ $.RepoInfo.FullName }}/issues?state={{ $currentState }}&offset={{ $prev.Offset }}&limit={{ $prev.Limit }}"
59
>
60
{{ i "chevron-left" "w-4 h-4" }}
61
previous
···
69
<a
70
class="btn flex items-center gap-2 no-underline hover:no-underline dark:text-white dark:hover:bg-gray-700"
71
hx-boost="true"
72
-
href = "/{{ $.RepoInfo.FullName }}/issues?state={{ $currentState }}&offset={{ $next.Offset }}&limit={{ $next.Limit }}"
73
>
74
next
75
{{ i "chevron-right" "w-4 h-4" }}
···
24
{{ i "ban" "w-4 h-4" }}
25
<span>{{ .RepoInfo.Stats.IssueCount.Closed }} closed</span>
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>
34
</div>
35
<a
36
href="/{{ .RepoInfo.FullName }}/issues/new"
···
62
<a
63
class="btn flex items-center gap-2 no-underline hover:no-underline dark:text-white dark:hover:bg-gray-700"
64
hx-boost="true"
65
+
href = "/{{ $.RepoInfo.FullName }}/issues?state={{ $currentState }}&q={{ .FilterQuery }}&offset={{ $prev.Offset }}&limit={{ $prev.Limit }}"
66
>
67
{{ i "chevron-left" "w-4 h-4" }}
68
previous
···
76
<a
77
class="btn flex items-center gap-2 no-underline hover:no-underline dark:text-white dark:hover:bg-gray-700"
78
hx-boost="true"
79
+
href = "/{{ $.RepoInfo.FullName }}/issues?state={{ $currentState }}&q={{ .FilterQuery }}&offset={{ $next.Offset }}&limit={{ $next.Limit }}"
80
>
81
next
82
{{ i "chevron-right" "w-4 h-4" }}