+75
-4
appview/db/repos.go
+75
-4
appview/db/repos.go
···
13
13
Created time.Time
14
14
AtUri string
15
15
Description string
16
+
17
+
// optionally, populate this when querying for reverse mappings
18
+
RepoStats *RepoStats
16
19
}
17
20
18
21
func GetAllRepos(e Execer, limit int) ([]Repo, error) {
···
52
55
func GetAllReposByDid(e Execer, did string) ([]Repo, error) {
53
56
var repos []Repo
54
57
55
-
rows, err := e.Query(`select did, name, knot, rkey, description, created from repos where did = ?`, did)
58
+
rows, err := e.Query(
59
+
`select
60
+
r.did,
61
+
r.name,
62
+
r.knot,
63
+
r.rkey,
64
+
r.description,
65
+
r.created,
66
+
count(s.id) as star_count
67
+
from
68
+
repos r
69
+
left join
70
+
stars s on r.at_uri = s.repo_at
71
+
where
72
+
r.did = ?
73
+
group by
74
+
r.at_uri`, did)
56
75
if err != nil {
57
76
return nil, err
58
77
}
···
60
79
61
80
for rows.Next() {
62
81
var repo Repo
63
-
err := scanRepo(rows, &repo.Did, &repo.Name, &repo.Knot, &repo.Rkey, &repo.Description, &repo.Created)
82
+
var repoStats RepoStats
83
+
var createdAt string
84
+
var nullableDescription sql.NullString
85
+
86
+
err := rows.Scan(&repo.Did, &repo.Name, &repo.Knot, &repo.Rkey, &nullableDescription, &createdAt, &repoStats.StarCount)
64
87
if err != nil {
65
88
return nil, err
66
89
}
90
+
91
+
if nullableDescription.Valid {
92
+
repo.Description = nullableDescription.String
93
+
} else {
94
+
repo.Description = ""
95
+
}
96
+
97
+
createdAtTime, err := time.Parse(time.RFC3339, createdAt)
98
+
if err != nil {
99
+
repo.Created = time.Now()
100
+
} else {
101
+
repo.Created = createdAtTime
102
+
}
103
+
104
+
repo.RepoStats = &repoStats
105
+
67
106
repos = append(repos, repo)
68
107
}
69
108
···
150
189
func CollaboratingIn(e Execer, collaborator string) ([]Repo, error) {
151
190
var repos []Repo
152
191
153
-
rows, err := e.Query(`select r.did, r.name, r.knot, r.rkey, r.description, r.created from repos r join collaborators c on r.id = c.repo where c.did = ?;`, collaborator)
192
+
rows, err := e.Query(
193
+
`select
194
+
r.did, r.name, r.knot, r.rkey, r.description, r.created, count(s.id) as star_count
195
+
from
196
+
repos r
197
+
join
198
+
collaborators c on r.id = c.repo
199
+
left join
200
+
stars s on r.at_uri = s.repo_at
201
+
where
202
+
c.did = ?
203
+
group by
204
+
r.id;`, collaborator)
154
205
if err != nil {
155
206
return nil, err
156
207
}
···
158
209
159
210
for rows.Next() {
160
211
var repo Repo
161
-
err := scanRepo(rows, &repo.Did, &repo.Name, &repo.Knot, &repo.Rkey, &repo.Description, &repo.Created)
212
+
var repoStats RepoStats
213
+
var createdAt string
214
+
var nullableDescription sql.NullString
215
+
216
+
err := rows.Scan(&repo.Did, &repo.Name, &repo.Knot, &repo.Rkey, &nullableDescription, &createdAt, &repoStats.StarCount)
162
217
if err != nil {
163
218
return nil, err
164
219
}
220
+
221
+
if nullableDescription.Valid {
222
+
repo.Description = nullableDescription.String
223
+
} else {
224
+
repo.Description = ""
225
+
}
226
+
227
+
createdAtTime, err := time.Parse(time.RFC3339, createdAt)
228
+
if err != nil {
229
+
repo.Created = time.Now()
230
+
} else {
231
+
repo.Created = createdAtTime
232
+
}
233
+
234
+
repo.RepoStats = &repoStats
235
+
165
236
repos = append(repos, repo)
166
237
}
167
238
+3
-1
appview/db/star.go
+3
-1
appview/db/star.go
+6
-1
appview/pages/pages.go
+6
-1
appview/pages/pages.go
···
523
523
524
524
func Cache(h http.Handler) http.Handler {
525
525
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
526
-
w.Header().Set("Cache-Control", "public, max-age=31536000, immutable")
526
+
if strings.HasSuffix(r.URL.Path, ".css") {
527
+
// on day for css files
528
+
w.Header().Set("Cache-Control", "public, max-age=86400")
529
+
} else {
530
+
w.Header().Set("Cache-Control", "public, max-age=31536000, immutable")
531
+
}
527
532
h.ServeHTTP(w, r)
528
533
})
529
534
}
-3
appview/pages/templates/fragments/star.html
-3
appview/pages/templates/fragments/star.html
+11
-8
appview/pages/templates/repo/issues/issue.html
+11
-8
appview/pages/templates/repo/issues/issue.html
···
4
4
{{ end }}
5
5
6
6
{{ define "repoContent" }}
7
-
<h1>
7
+
<header>
8
+
<p class="text-2xl font-bold">
8
9
{{ .Issue.Title }}
9
-
<span class="text-gray-400">#{{ .Issue.IssueId }}</span>
10
-
</h1>
10
+
<span class="text-gray-500">#{{ .Issue.IssueId }}</span>
11
+
</p>
12
+
</header>
11
13
12
14
{{ $bgColor := "bg-gray-800" }}
13
15
{{ $icon := "ban" }}
···
23
25
<i data-lucide="{{ $icon }}" class="w-4 h-4 mr-1.5 text-white" ></i>
24
26
<span class="text-white">{{ .State }}</span>
25
27
</div>
26
-
<span class="text-gray-400 text-sm">
28
+
<span class="text-gray-500 text-sm">
27
29
opened by
28
30
{{ $owner := didOrHandle .Issue.OwnerDid .IssueOwnerHandle }}
29
31
<a href="/{{ $owner }}" class="no-underline hover:underline"
···
35
37
</div>
36
38
37
39
{{ if .Issue.Body }}
38
-
<article id="body" class="mt-8 prose">
40
+
<article id="body" class="mt-4 prose">
39
41
{{ .Issue.Body | markdown }}
40
42
</article>
41
43
{{ end }}
···
47
49
{{ range $index, $comment := .Comments }}
48
50
<div
49
51
id="comment-{{ .CommentId }}"
50
-
class="rounded bg-white p-4 relative"
52
+
class="rounded bg-white px-6 py-4 relative"
51
53
>
52
54
{{ if eq $index 0 }}
53
55
<div
···
58
60
class="absolute left-8 -top-4 w-px h-4 bg-gray-300"
59
61
></div>
60
62
{{ end }}
61
-
<div class="flex items-center gap-2 mb-2 text-gray-400">
63
+
<div class="flex items-center gap-2 mb-2 text-gray-500">
62
64
{{ $owner := index $.DidHandleMap .OwnerDid }}
63
65
<span class="text-sm">
64
66
<a
···
67
69
>{{ $owner }}</a
68
70
>
69
71
</span>
70
-
<span class="px-1 select-none before:content-['\00B7']"></span>
72
+
73
+
<span class="before:content-['·']"></span>
71
74
<a
72
75
href="#{{ .CommentId }}"
73
76
class="text-gray-500 text-sm hover:text-gray-500 hover:underline no-underline"
+4
-4
appview/pages/templates/repo/issues/issues.html
+4
-4
appview/pages/templates/repo/issues/issues.html
···
4
4
<div class="flex justify-between items-center">
5
5
<p>
6
6
filtering
7
-
<select class="font-bold border border-gray-200 rounded" onchange="window.location.href = '/{{ .RepoInfo.FullName }}/issues?state=' + this.value">
7
+
<select class="border px-1 bg-white border-gray-200" onchange="window.location.href = '/{{ .RepoInfo.FullName }}/issues?state=' + this.value">
8
8
<option value="open" {{ if .FilteringByOpen }}selected{{ end }}>open</option>
9
9
<option value="closed" {{ if not .FilteringByOpen }}selected{{ end }}>closed</option>
10
10
</select>
···
30
30
class="no-underline hover:underline"
31
31
>
32
32
{{ .Title }}
33
-
<span class="text-gray-400">#{{ .IssueId }}</span>
33
+
<span class="text-gray-500">#{{ .IssueId }}</span>
34
34
</a>
35
35
</div>
36
-
<p class="text-sm text-gray-400">
36
+
<p class="text-sm text-gray-500">
37
37
{{ $bgColor := "bg-gray-800" }}
38
38
{{ $icon := "ban" }}
39
39
{{ $state := "closed" }}
···
64
64
{{ if eq .Metadata.CommentCount 1 }}
65
65
{{ $s = "" }}
66
66
{{ end }}
67
-
<a href="/{{ $.RepoInfo.FullName }}/issues/{{ .IssueId }}" class="text-gray-400">{{ .Metadata.CommentCount }} comment{{$s}}</a>
67
+
<a href="/{{ $.RepoInfo.FullName }}/issues/{{ .IssueId }}" class="text-gray-500">{{ .Metadata.CommentCount }} comment{{$s}}</a>
68
68
</span>
69
69
</p>
70
70
</div>
+17
-5
appview/pages/templates/user/profile.html
+17
-5
appview/pages/templates/user/profile.html
···
44
44
class="py-4 px-6 drop-shadow-sm rounded bg-white"
45
45
>
46
46
<div id="repo-card-name" class="font-medium">
47
-
<a href="/@{{ or $.UserHandle $.UserDid }}/{{ .Name }}"
48
-
>{{ .Name }}</a
49
-
>
47
+
<a href="/@{{ or $.UserHandle $.UserDid }}/{{ .Name }}">{{ .Name }}</a>
50
48
</div>
51
49
{{ if .Description }}
52
50
<div class="text-gray-600 text-sm">
53
51
{{ .Description }}
54
52
</div>
55
53
{{ end }}
56
-
<div class="text-gray-600 text-sm font-mono">
54
+
<div class="text-gray-600 text-sm font-mono inline-flex gap-4">
57
55
{{ .Knot }}
56
+
57
+
{{ if .RepoStats.StarCount }}
58
+
<div class="flex gap-1 items-center text-sm">
59
+
<span class="w-2 h-2 fill-current" data-lucide="star"></span>
60
+
<span>{{ .RepoStats.StarCount }}</span>
61
+
</div>
62
+
{{ end }}
58
63
</div>
59
64
</div>
60
65
{{ else }}
···
81
86
{{ .Description }}
82
87
</div>
83
88
{{ end }}
84
-
<div class="text-gray-600 text-sm font-mono">
89
+
<div class="text-gray-600 text-sm font-mono inline-flex gap-4">
85
90
{{ .Knot }}
91
+
92
+
{{ if .RepoStats.StarCount }}
93
+
<div class="flex gap-1 items-center text-sm">
94
+
<span class="w-2 h-2 fill-current" data-lucide="star"></span>
95
+
<span>{{ .RepoStats.StarCount }}</span>
96
+
</div>
97
+
{{ end }}
86
98
</div>
87
99
</div>
88
100
{{ else }}