+12
-7
appview/pages/pages.go
+12
-7
appview/pages/pages.go
···
459
459
}
460
460
461
461
type RepoIndexParams struct {
462
-
LoggedInUser *auth.User
463
-
RepoInfo RepoInfo
464
-
Active string
465
-
TagMap map[string][]string
466
-
Tags []*types.TagReference
467
-
CommitsTrunc []*object.Commit
462
+
LoggedInUser *auth.User
463
+
RepoInfo RepoInfo
464
+
Active string
465
+
TagMap map[string][]string
466
+
CommitsTrunc []*object.Commit
467
+
TagsTrunc []*types.TagReference
468
+
BranchesTrunc []types.Branch
468
469
types.RepoIndexResponse
469
470
HTMLReadme template.HTML
470
471
Raw bool
···
505
506
506
507
func (p *Pages) RepoLog(w io.Writer, params RepoLogParams) error {
507
508
params.Active = "overview"
508
-
return p.execute("repo/log", w, params)
509
+
return p.executeRepo("repo/log", w, params)
509
510
}
510
511
511
512
type RepoCommitParams struct {
···
561
562
type RepoBranchesParams struct {
562
563
LoggedInUser *auth.User
563
564
RepoInfo RepoInfo
565
+
Active string
564
566
types.RepoBranchesResponse
565
567
}
566
568
567
569
func (p *Pages) RepoBranches(w io.Writer, params RepoBranchesParams) error {
570
+
params.Active = "overview"
568
571
return p.executeRepo("repo/branches", w, params)
569
572
}
570
573
571
574
type RepoTagsParams struct {
572
575
LoggedInUser *auth.User
573
576
RepoInfo RepoInfo
577
+
Active string
574
578
types.RepoTagsResponse
575
579
}
576
580
577
581
func (p *Pages) RepoTags(w io.Writer, params RepoTagsParams) error {
582
+
params.Active = "overview"
578
583
return p.executeRepo("repo/tags", w, params)
579
584
}
580
585
+50
-10
appview/pages/templates/repo/branches.html
+50
-10
appview/pages/templates/repo/branches.html
···
1
1
{{ define "title" }}
2
-
branches | {{ .RepoInfo.FullName }}
2
+
branches · {{ .RepoInfo.FullName }}
3
3
{{ end }}
4
4
5
5
{{ define "repoContent" }}
6
-
<h3>branches</h3>
7
-
<div class="refs">
8
-
{{ range .Branches }}
9
-
<div>
10
-
<strong>{{ .Name }}</strong>
11
-
<a href="/{{ $.RepoInfo.FullName }}/tree/{{ .Name }}/">browse</a>
12
-
<a href="/{{ $.RepoInfo.FullName }}/log/{{ .Name }}">log</a>
13
-
</div>
14
-
{{ end }}
6
+
<section>
7
+
<header class="font-bold text-sm mb-4 uppercase dark:text-white">
8
+
Branches
9
+
</header>
10
+
11
+
<div class="overflow-x-auto">
12
+
<table class="min-w-full table-auto">
13
+
<tbody>
14
+
{{ range .Branches }}
15
+
<tr>
16
+
<td class="whitespace-nowrap">
17
+
<a href="/{{ $.RepoInfo.FullName }}/tree/{{ .Name | urlquery }}" class="no-underline hover:underline flex items-center gap-2">
18
+
<span>
19
+
{{ .Name }}
20
+
</span>
21
+
{{ if .IsDefault }}
22
+
<span class="
23
+
text-sm rounded
24
+
bg-gray-100 dark:bg-gray-700 text-black dark:text-white
25
+
font-mono
26
+
px-2 mx-1/2
27
+
inline-flex items-center
28
+
">
29
+
default
30
+
</span>
31
+
{{ end }}
32
+
</a>
33
+
</td>
34
+
<td class="whitespace-nowrap hidden md:table-cell">
35
+
{{ if .Commit }}
36
+
<a href="/{{ $.RepoInfo.FullName }}/commits/{{ .Name | urlquery }}" class="font-mono">{{ slice .Commit.Hash.String 0 8 }}</a>
37
+
{{ end }}
38
+
</td>
39
+
<td class="whitespace-nowrap hidden md:table-cell">
40
+
{{ if .Commit }}
41
+
{{ $messageParts := splitN .Commit.Message "\n\n" 2 }}
42
+
{{ index $messageParts 0 }}
43
+
{{ end }}
44
+
</td>
45
+
<td class="whitespace-nowrap">
46
+
{{ if .Commit }}
47
+
{{ .Commit.Author.When | timeFmt }}
48
+
{{ end }}
49
+
</td>
50
+
</tr>
51
+
{{ end }}
52
+
</tbody>
53
+
</table>
15
54
</div>
55
+
</section>
16
56
{{ end }}
+178
-179
appview/pages/templates/repo/index.html
+178
-179
appview/pages/templates/repo/index.html
···
41
41
<div class="flex justify-between pb-5">
42
42
<select
43
43
onchange="window.location.href = '/{{ .RepoInfo.FullName }}/tree/' + encodeURIComponent(this.value)"
44
-
class="p-1 border border-gray-200 bg-white dark:bg-gray-800 dark:text-white dark:border-gray-700"
44
+
class="p-1 border max-w-32 border-gray-200 bg-white dark:bg-gray-800 dark:text-white dark:border-gray-700"
45
45
>
46
-
<optgroup label="branches" class="bold text-sm">
46
+
<optgroup label="branches ({{len .Branches}})" class="bold text-sm">
47
47
{{ range .Branches }}
48
48
<option
49
49
value="{{ .Reference.Name }}"
···
56
56
</option>
57
57
{{ end }}
58
58
</optgroup>
59
-
<optgroup label="tags" class="bold text-sm">
59
+
<optgroup label="tags ({{len .Tags}})" class="bold text-sm">
60
60
{{ range .Tags }}
61
61
<option
62
62
value="{{ .Reference.Name }}"
···
130
130
131
131
{{ define "rightInfo" }}
132
132
<div id="right-info" class="col-span-1">
133
-
<div id="commit-log" class="md:col-span-1">
134
-
<h2
135
-
class="flex text-gray-500 dark:text-gray-400 items-center gap-2 pb-4 pl-2"
136
-
>
137
-
{{ i "logs" "w-4 h-4" }}
138
-
commits
139
-
</h2>
140
-
{{ range .CommitsTrunc }}
141
-
<div class="relative px-2 pb-8">
142
-
<div id="commit-message">
143
-
{{ $messageParts := splitN .Message "\n\n" 2 }}
144
-
<div class="text-base cursor-pointer">
145
-
<div>
146
-
<div>
147
-
<a
148
-
href="/{{ $.RepoInfo.FullName }}/commit/{{ .Hash.String }}"
149
-
class="inline no-underline hover:underline dark:text-white"
150
-
>{{ index $messageParts 0 }}</a
151
-
>
152
-
{{ if gt (len $messageParts) 1 }}
133
+
{{ block "commitLog" . }} {{ end }}
134
+
{{ block "branchList" . }} {{ end }}
135
+
{{ block "tagList" . }} {{ end }}
136
+
</div>
137
+
{{ end }}
138
+
139
+
{{ define "commitLog" }}
140
+
<div id="commit-log" class="md:col-span-1 px-2 pb-4">
141
+
<div class="flex justify-between items-center">
142
+
<a href="/{{ .RepoInfo.FullName }}/commits/{{ .Ref | urlquery }}" class="flex text-black dark:text-white items-center gap-4 pb-2 no-underline hover:no-underline group">
143
+
<div class="flex gap-2 items-center">
144
+
{{ i "git-commit-horizontal" "w-4 h-4" }} commits
145
+
</div>
146
+
{{ if lt (len .CommitsTrunc) .TotalCommits }}
147
+
<span class="hidden group-hover:flex gap-2 items-center text-sm text-gray-500 dark:text-gray-400 ">
148
+
view {{ .TotalCommits }} commits {{ i "chevron-right" "w-4 h-4" }}
149
+
</span>
150
+
{{ end }}
151
+
</a>
152
+
</div>
153
+
<div class="flex flex-col gap-6">
154
+
{{ range .CommitsTrunc }}
155
+
<div>
156
+
<div id="commit-message">
157
+
{{ $messageParts := splitN .Message "\n\n" 2 }}
158
+
<div class="text-base cursor-pointer">
159
+
<div>
160
+
<div>
161
+
<a
162
+
href="/{{ $.RepoInfo.FullName }}/commit/{{ .Hash.String }}"
163
+
class="inline no-underline hover:underline dark:text-white"
164
+
>{{ index $messageParts 0 }}</a
165
+
>
166
+
{{ if gt (len $messageParts) 1 }}
167
+
168
+
<button
169
+
class="py-1/2 px-1 bg-gray-200 hover:bg-gray-400 rounded dark:bg-gray-700 dark:hover:bg-gray-600"
170
+
hx-on:click="this.parentElement.nextElementSibling.classList.toggle('hidden')"
171
+
>
172
+
{{ i "ellipsis" "w-3 h-3" }}
173
+
</button>
174
+
{{ end }}
175
+
</div>
176
+
{{ if gt (len $messageParts) 1 }}
177
+
<p
178
+
class="hidden mt-1 text-sm cursor-text pb-2 dark:text-gray-300"
179
+
>
180
+
{{ nl2br (index $messageParts 1) }}
181
+
</p>
182
+
{{ end }}
183
+
</div>
184
+
</div>
185
+
</div>
186
+
187
+
<div class="text-xs text-gray-500 dark:text-gray-400">
188
+
<span class="font-mono">
189
+
<a
190
+
href="/{{ $.RepoInfo.FullName }}/commit/{{ .Hash.String }}"
191
+
class="text-gray-500 dark:text-gray-400 no-underline hover:underline"
192
+
>{{ slice .Hash.String 0 8 }}</a
193
+
></span
194
+
>
195
+
<span
196
+
class="mx-2 before:content-['·'] before:select-none"
197
+
></span>
198
+
<span>
199
+
{{ $didOrHandle := index $.EmailToDidOrHandle .Author.Email }}
200
+
<a
201
+
href="{{ if $didOrHandle }}
202
+
/{{ $didOrHandle }}
203
+
{{ else }}
204
+
mailto:{{ .Author.Email }}
205
+
{{ end }}"
206
+
class="text-gray-500 dark:text-gray-400 no-underline hover:underline"
207
+
>{{ if $didOrHandle }}
208
+
{{ $didOrHandle }}
209
+
{{ else }}
210
+
{{ .Author.Name }}
211
+
{{ end }}</a
212
+
>
213
+
</span>
214
+
<div
215
+
class="inline-block px-1 select-none after:content-['·']"
216
+
></div>
217
+
<span>{{ timeFmt .Author.When }}</span>
218
+
{{ $tagsForCommit := index $.TagMap .Hash.String }}
219
+
{{ if gt (len $tagsForCommit) 0 }}
220
+
<div
221
+
class="inline-block px-1 select-none after:content-['·']"
222
+
></div>
223
+
{{ end }}
224
+
{{ range $tagsForCommit }}
225
+
<span
226
+
class="text-xs rounded bg-gray-100 dark:bg-gray-700 text-black dark:text-white font-mono px-2 mx-1/2 inline-flex items-center"
227
+
>
228
+
{{ . }}
229
+
</span>
230
+
{{ end }}
231
+
</div>
232
+
</div>
233
+
{{ end }}
234
+
</div>
235
+
</div>
236
+
{{ end }}
153
237
154
-
<button
155
-
class="py-1/2 px-1 bg-gray-200 hover:bg-gray-400 rounded dark:bg-gray-700 dark:hover:bg-gray-600"
156
-
hx-on:click="this.parentElement.nextElementSibling.classList.toggle('hidden')"
157
-
>
158
-
{{ i "ellipsis" "w-3 h-3" }}
159
-
</button>
160
-
{{ end }}
161
-
</div>
162
-
{{ if gt (len $messageParts) 1 }}
163
-
<p
164
-
class="hidden mt-1 text-sm cursor-text pb-2 dark:text-gray-300"
165
-
>
166
-
{{ nl2br (index $messageParts 1) }}
167
-
</p>
168
-
{{ end }}
169
-
</div>
170
-
</div>
171
-
</div>
238
+
{{ define "branchList" }}
239
+
{{ if gt (len .BranchesTrunc) 0 }}
240
+
<div id="branches" class="md:col-span-1 px-2 py-4 border-t border-gray-200 dark:border-gray-700">
241
+
<a href="/{{ .RepoInfo.FullName }}/branches" class="flex text-black dark:text-white items-center gap-4 pb-2 no-underline hover:no-underline group">
242
+
<div class="flex gap-2 items-center">
243
+
<!-- git-branch icon is seemingly bigger than others at 4x4 -->
244
+
{{ i "git-branch" "w-3 h-3" }} branches
245
+
</div>
246
+
{{ if lt (len .BranchesTrunc) (len .Branches) }}
247
+
<span class="hidden group-hover:flex gap-2 items-center text-sm text-gray-500 dark:text-gray-400 ">
248
+
view {{ len .Branches }} branches {{ i "chevron-right" "w-4 h-4" }}
249
+
</span>
250
+
{{ end }}
251
+
</a>
252
+
<div class="flex flex-col gap-1">
253
+
{{ range .BranchesTrunc }}
254
+
<div class="text-base flex items-center gap-2">
255
+
<a href="/{{ $.RepoInfo.FullName }}/tree/{{ .Reference.Name | urlquery }}"
256
+
class="inline no-underline hover:underline dark:text-white">
257
+
{{ .Reference.Name }}
258
+
</a>
259
+
{{ if .Commit }}
260
+
<span class="px-1 text-gray-500 dark:text-gray-400 select-none after:content-['·']"></span>
261
+
<time class="text-xs text-gray-500 dark:text-gray-400">{{ timeFmt .Commit.Author.When }}</time>
262
+
{{ end }}
263
+
{{ if .IsDefault }}
264
+
<span class="px-1 text-gray-500 dark:text-gray-400 select-none after:content-['·']"></span>
265
+
<span class="bg-gray-200 dark:bg-gray-700 rounded py-1/2 px-1 text-xs font-mono">default</span>
266
+
{{ end }}
267
+
</div>
268
+
{{ end }}
269
+
</div>
270
+
</div>
271
+
{{ end }}
272
+
{{ end }}
172
273
173
-
<div class="text-xs text-gray-500 dark:text-gray-400">
174
-
<span class="font-mono">
175
-
<a
176
-
href="/{{ $.RepoInfo.FullName }}/commit/{{ .Hash.String }}"
177
-
class="text-gray-500 dark:text-gray-400 no-underline hover:underline"
178
-
>{{ slice .Hash.String 0 8 }}</a
179
-
></span
180
-
>
181
-
<span
182
-
class="mx-2 before:content-['·'] before:select-none"
183
-
></span>
184
-
<span>
185
-
{{ $didOrHandle := index $.EmailToDidOrHandle .Author.Email }}
186
-
<a
187
-
href="{{ if $didOrHandle }}
188
-
/{{ $didOrHandle }}
189
-
{{ else }}
190
-
mailto:{{ .Author.Email }}
191
-
{{ end }}"
192
-
class="text-gray-500 dark:text-gray-400 no-underline hover:underline"
193
-
>{{ if $didOrHandle }}
194
-
{{ $didOrHandle }}
195
-
{{ else }}
196
-
{{ .Author.Name }}
197
-
{{ end }}</a
198
-
>
199
-
</span>
200
-
<div
201
-
class="inline-block px-1 select-none after:content-['·']"
202
-
></div>
203
-
<span>{{ timeFmt .Author.When }}</span>
204
-
{{ $tagsForCommit := index $.TagMap .Hash.String }}
205
-
{{ if gt (len $tagsForCommit) 0 }}
206
-
<div
207
-
class="inline-block px-1 select-none after:content-['·']"
208
-
></div>
209
-
{{ end }}
210
-
{{ range $tagsForCommit }}
211
-
<span
212
-
class="text-xs rounded bg-gray-100 dark:bg-gray-700 text-black dark:text-white font-mono px-2 mx-1/2 inline-flex items-center"
213
-
>
214
-
{{ . }}
215
-
</span>
216
-
{{ end }}
217
-
</div>
218
-
</div>
274
+
{{ define "tagList" }}
275
+
{{ if gt (len .TagsTrunc) 0 }}
276
+
<div id="tags" class="md:col-span-1 px-2 py-4 border-t border-gray-200 dark:border-gray-700">
277
+
<div class="flex justify-between items-center">
278
+
<a href="/{{ .RepoInfo.FullName }}/tags" class="flex text-black dark:text-white items-center gap-4 pb-2 no-underline hover:no-underline group">
279
+
<div class="flex gap-2 items-center">
280
+
{{ i "tags" "w-4 h-4" }} tags
281
+
</div>
282
+
{{ if lt (len .TagsTrunc) (len .Tags) }}
283
+
<span class="hidden group-hover:flex gap-2 items-center text-sm text-gray-500 dark:text-gray-400 ">
284
+
view {{ len .Tags }} tags {{ i "chevron-right" "w-4 h-4" }}
285
+
</span>
286
+
{{ end }}
287
+
</a>
288
+
</div>
289
+
<div class="flex flex-col gap-1">
290
+
{{ range $idx, $tag := .TagsTrunc }}
291
+
{{ with $tag }}
292
+
<div>
293
+
<div class="text-base flex items-center gap-2">
294
+
<a href="/{{ $.RepoInfo.FullName }}/tree/{{ .Reference.Name | urlquery }}"
295
+
class="inline no-underline hover:underline dark:text-white">
296
+
{{ .Reference.Name }}
297
+
</a>
298
+
</div>
299
+
<div>
300
+
{{ with .Tag }}
301
+
<time class="text-xs text-gray-500 dark:text-gray-400">{{ timeFmt .Tagger.When }}</time>
219
302
{{ end }}
220
-
{{ $more := sub .TotalCommits (len .CommitsTrunc) }}
221
-
{{ if gt $more 0 }}
222
-
<div
223
-
class="flex text-gray-500 dark:text-gray-400 mb-2 gap-1 justify-end items-center"
224
-
>
225
-
<a
226
-
href="/{{ .RepoInfo.FullName }}/commits/{{ .Ref | urlquery }}"
227
-
class="text-gray-500 dark:text-gray-400 text-sm"
228
-
>
229
-
… and {{ $more }} more
230
-
</a>
231
-
{{ i "arrow-right" "w-3 h-3" }}
232
-
</div>
303
+
{{ if eq $idx 0 }}
304
+
<span class="px-1 text-gray-500 dark:text-gray-400 select-none after:content-['·']"></span>
305
+
<span class="bg-gray-200 dark:bg-gray-700 rounded py-1/2 px-1 text-xs font-mono">latest</span>
233
306
{{ end }}
307
+
</div>
234
308
</div>
235
-
{{ if gt (len .Tags) 0 }}
236
-
<div
237
-
id="tags"
238
-
class="md:col-span-1 pt-4 border-t border-gray-200 dark:border-gray-700"
239
-
>
240
-
<h2
241
-
class="flex text-gray-500 dark:text-gray-400 items-center gap-2 pb-4 pl-2"
242
-
>
243
-
{{ i "tags" "w-4 h-4" }}
244
-
tags
245
-
</h2>
246
-
{{ range .Tags }}
247
-
<div class="relative px-2 pb-4">
248
-
<div class="text-base">
249
-
<div>
250
-
<a
251
-
href="/{{ $.RepoInfo.FullName }}/tree/{{ .Reference.Name | urlquery }}"
252
-
class="inline no-underline hover:underline dark:text-white"
253
-
>{{ .Reference.Name }}</a
254
-
>
255
-
{{ if gt (len .Message) 0 }}
256
-
{{ $messageParts := splitN .Message "\n\n" 2 }}
257
-
{{ $title := index $messageParts 0 }}
258
-
{{ $description := index $messageParts 1 }}
259
-
<span
260
-
class="text-sm text-gray-500 dark:text-gray-400 ml-2 truncate inline-block align-middle"
261
-
>{{ $title }}</span
262
-
>
263
-
{{ if gt (len $description) 1 }}
264
-
<button
265
-
class="py-1/2 px-1 bg-gray-200 hover:bg-gray-400 rounded dark:bg-gray-700 dark:hover:bg-gray-600"
266
-
hx-on:click="this.parentElement.nextElementSibling.classList.toggle('hidden')"
267
-
>
268
-
{{ i "ellipsis" "w-3 h-3" }}
269
-
</button>
270
-
{{ end }}
271
-
{{ end }}
272
-
</div>
273
-
{{ if gt (len .Message) 0 }}
274
-
{{ $messageParts := splitN .Message "\n\n" 2 }}
275
-
{{ $description := index $messageParts 1 }}
276
-
{{ if gt (len $description) 1 }}
277
-
<div
278
-
class="hidden mt-1 text-sm cursor-text pb-2 dark:text-gray-300"
279
-
>
280
-
{{ $description | markdown }}
281
-
</div>
282
-
{{ end }}
283
-
{{ end }}
284
-
<div
285
-
class="text-xs text-gray-500 dark:text-gray-400"
286
-
>
287
-
<span class="font-mono">
288
-
<a
289
-
href="/{{ $.RepoInfo.FullName }}/commit/{{ .Reference.Hash }}"
290
-
class="text-gray-500 dark:text-gray-400 no-underline hover:underline"
291
-
>{{ slice .Reference.Hash 0 8 }}</a
292
-
>
293
-
</span>
294
-
{{ with .Tag }}
295
-
<div
296
-
class="inline-block px-1 select-none after:content-['·']"
297
-
></div>
298
-
<time>{{ timeFmt .Tagger.When }}</time>
299
-
{{ end }}
300
-
</div>
301
-
</div>
302
-
</div>
303
-
{{ end }}
304
-
</div>
309
+
{{ end }}
305
310
{{ end }}
311
+
</div>
306
312
</div>
313
+
{{ end }}
307
314
{{ end }}
308
315
309
316
{{ define "repoAfter" }}
310
317
{{- if .HTMLReadme }}
311
-
<div
312
-
class="mt-4 bg-white text-gray-600 dark:text-gray-400
313
-
px-6 py-3 prose dark:prose-invert
314
-
border-gray-200 dark:border-gray-700
315
-
text-sm border-b rounded-tl rounded-tr"
316
-
>
317
-
{{ .ReadmeFileName }}
318
-
</div>
319
318
<section
320
-
class="p-6 rounded-br rounded-bl bg-white dark:bg-gray-800 dark:text-white w-full mx-auto overflow-auto {{ if not .Raw }}
319
+
class="p-6 mt-4 rounded-br rounded-bl bg-white dark:bg-gray-800 dark:text-white w-full mx-auto overflow-auto {{ if not .Raw }}
321
320
prose dark:prose-invert dark:[&_pre]:bg-gray-900
322
321
dark:[&_code]:text-gray-300 dark:[&_pre_code]:bg-gray-900
323
322
dark:[&_pre]:border dark:[&_pre]:border-gray-700
+54
-17
appview/state/repo.go
+54
-17
appview/state/repo.go
···
82
82
tagMap[hash] = append(tagMap[hash], branch.Name)
83
83
}
84
84
85
-
c, t := balanceTagsAndCommits(len(result.Commits), len(result.Tags), 10)
86
-
commits := result.Commits[:c]
87
-
tags := result.Tags[:t]
88
-
emails := uniqueEmails(commits)
85
+
slices.SortFunc(result.Branches, func(a, b types.Branch) int {
86
+
if a.Name == result.Ref {
87
+
return -1
88
+
}
89
+
if a.IsDefault {
90
+
return -1
91
+
}
92
+
if a.Commit != nil {
93
+
if a.Commit.Author.When.Before(b.Commit.Author.When) {
94
+
return 1
95
+
} else {
96
+
return -1
97
+
}
98
+
}
99
+
return strings.Compare(a.Name, b.Name) * -1
100
+
})
89
101
90
-
for _, tag := range tags {
91
-
fmt.Printf("%#v\n\n", tag)
92
-
}
102
+
commitCount := len(result.Commits)
103
+
branchCount := len(result.Branches)
104
+
tagCount := len(result.Tags)
105
+
fileCount := len(result.Files)
106
+
107
+
commitCount, branchCount, tagCount = balanceIndexItems(commitCount, branchCount, tagCount, fileCount)
108
+
commitsTrunc := result.Commits[:min(commitCount, len(result.Commits))]
109
+
tagsTrunc := result.Tags[:min(tagCount, len(result.Tags))]
110
+
branchesTrunc := result.Branches[:min(branchCount, len(result.Branches))]
111
+
112
+
emails := uniqueEmails(commitsTrunc)
93
113
94
114
user := s.auth.GetUser(r)
95
115
s.pages.RepoIndexPage(w, pages.RepoIndexParams{
96
116
LoggedInUser: user,
97
117
RepoInfo: f.RepoInfo(s, user),
98
118
TagMap: tagMap,
99
-
Tags: tags,
100
119
RepoIndexResponse: result,
101
-
CommitsTrunc: commits,
120
+
CommitsTrunc: commitsTrunc,
121
+
TagsTrunc: tagsTrunc,
122
+
BranchesTrunc: branchesTrunc,
102
123
EmailToDidOrHandle: EmailToDidOrHandle(s, emails),
103
124
})
104
125
return
···
121
142
122
143
ref := chi.URLParam(r, "ref")
123
144
124
-
protocol := "http"
125
-
if !s.config.Dev {
126
-
protocol = "https"
145
+
us, err := NewUnsignedClient(f.Knot, s.config.Dev)
146
+
if err != nil {
147
+
log.Println("failed to create unsigned client", err)
148
+
return
127
149
}
128
150
129
-
resp, err := http.Get(fmt.Sprintf("%s://%s/%s/%s/log/%s?page=%d&per_page=60", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref, page))
151
+
resp, err := us.Log(f.OwnerDid(), f.RepoName, ref, page)
130
152
if err != nil {
131
153
log.Println("failed to reach knotserver", err)
132
154
return
···
358
380
return
359
381
}
360
382
361
-
protocol := "http"
362
-
if !s.config.Dev {
363
-
protocol = "https"
383
+
us, err := NewUnsignedClient(f.Knot, s.config.Dev)
384
+
if err != nil {
385
+
log.Println("failed to create unsigned client", err)
386
+
return
364
387
}
365
388
366
-
resp, err := http.Get(fmt.Sprintf("%s://%s/%s/%s/tags", protocol, f.Knot, f.OwnerDid(), f.RepoName))
389
+
resp, err := us.Tags(f.OwnerDid(), f.RepoName)
367
390
if err != nil {
368
391
log.Println("failed to reach knotserver", err)
369
392
return
···
422
445
log.Println("failed to parse response:", err)
423
446
return
424
447
}
448
+
449
+
slices.SortFunc(result.Branches, func(a, b types.Branch) int {
450
+
if a.IsDefault {
451
+
return -1
452
+
}
453
+
if a.Commit != nil {
454
+
if a.Commit.Author.When.Before(b.Commit.Author.When) {
455
+
return 1
456
+
} else {
457
+
return -1
458
+
}
459
+
}
460
+
return strings.Compare(a.Name, b.Name) * -1
461
+
})
425
462
426
463
user := s.auth.GetUser(r)
427
464
s.pages.RepoBranches(w, pages.RepoBranchesParams{
+17
-17
appview/state/repo_util.go
+17
-17
appview/state/repo_util.go
···
82
82
return uniqueEmails
83
83
}
84
84
85
-
func balanceTagsAndCommits(commits, tags, totalDesired int) (int, int) {
86
-
if commits == 0 && tags == 0 {
87
-
return 0, 0
85
+
func balanceIndexItems(commitCount, branchCount, tagCount, fileCount int) (commitsTrunc int, branchesTrunc int, tagsTrunc int) {
86
+
if commitCount == 0 && tagCount == 0 && branchCount == 0 {
87
+
return
88
88
}
89
89
90
-
half := totalDesired / 2
90
+
// typically 1 item on right side = 2 files in height
91
+
availableSpace := fileCount / 2
91
92
92
-
if commits+tags <= totalDesired {
93
-
return commits, tags
94
-
}
95
-
96
-
if commits >= half && tags >= half {
97
-
return half, half
93
+
// clamp tagcount
94
+
if tagCount > 0 {
95
+
tagsTrunc = 1
96
+
availableSpace -= 1 // an extra subtracted for headers etc.
98
97
}
99
98
100
-
if commits < half {
101
-
return commits, min(tags, totalDesired-commits)
99
+
// clamp branchcount
100
+
if branchCount > 0 {
101
+
branchesTrunc = min(max(branchCount, 1), 2)
102
+
availableSpace -= branchesTrunc // an extra subtracted for headers etc.
102
103
}
103
104
104
-
if tags < half {
105
-
return min(commits, totalDesired-tags), tags
105
+
// show
106
+
if commitCount > 0 {
107
+
commitsTrunc = max(availableSpace, 3)
106
108
}
107
109
108
-
c := min(commits, half)
109
-
t := min(tags, totalDesired-c)
110
-
return c, t
110
+
return
111
111
}
112
112
113
113
func EmailToDidOrHandle(s *State, emails []string) map[string]string {
+50
-8
appview/state/signer.go
+50
-8
appview/state/signer.go
···
11
11
"log"
12
12
"net/http"
13
13
"net/url"
14
+
"strconv"
14
15
"time"
15
16
16
17
"tangled.sh/tangled.sh/core/types"
···
286
287
return unsignedClient, nil
287
288
}
288
289
289
-
func (us *UnsignedClient) newRequest(method, endpoint string, body []byte) (*http.Request, error) {
290
-
return http.NewRequest(method, us.Url.JoinPath(endpoint).String(), bytes.NewReader(body))
290
+
func (us *UnsignedClient) newRequest(method, endpoint string, query url.Values, body []byte) (*http.Request, error) {
291
+
reqUrl := us.Url.JoinPath(endpoint)
292
+
293
+
// add query parameters
294
+
if query != nil {
295
+
reqUrl.RawQuery = query.Encode()
296
+
}
297
+
298
+
return http.NewRequest(method, reqUrl.String(), bytes.NewReader(body))
291
299
}
292
300
293
301
func (us *UnsignedClient) Index(ownerDid, repoName, ref string) (*http.Response, error) {
···
300
308
endpoint = fmt.Sprintf("/%s/%s", ownerDid, repoName)
301
309
}
302
310
303
-
req, err := us.newRequest(Method, endpoint, nil)
311
+
req, err := us.newRequest(Method, endpoint, nil, nil)
312
+
if err != nil {
313
+
return nil, err
314
+
}
315
+
316
+
return us.client.Do(req)
317
+
}
318
+
319
+
func (us *UnsignedClient) Log(ownerDid, repoName, ref string, page int) (*http.Response, error) {
320
+
const (
321
+
Method = "GET"
322
+
)
323
+
324
+
endpoint := fmt.Sprintf("/%s/%s/log/%s", ownerDid, repoName, url.PathEscape(ref))
325
+
326
+
query := url.Values{}
327
+
query.Add("page", strconv.Itoa(page))
328
+
query.Add("per_page", strconv.Itoa(60))
329
+
330
+
req, err := us.newRequest(Method, endpoint, query, nil)
304
331
if err != nil {
305
332
return nil, err
306
333
}
···
315
342
316
343
endpoint := fmt.Sprintf("/%s/%s/branches", ownerDid, repoName)
317
344
318
-
req, err := us.newRequest(Method, endpoint, nil)
345
+
req, err := us.newRequest(Method, endpoint, nil, nil)
346
+
if err != nil {
347
+
return nil, err
348
+
}
349
+
350
+
return us.client.Do(req)
351
+
}
352
+
353
+
func (us *UnsignedClient) Tags(ownerDid, repoName string) (*http.Response, error) {
354
+
const (
355
+
Method = "GET"
356
+
)
357
+
358
+
endpoint := fmt.Sprintf("/%s/%s/tags", ownerDid, repoName)
359
+
360
+
req, err := us.newRequest(Method, endpoint, nil, nil)
319
361
if err != nil {
320
362
return nil, err
321
363
}
···
330
372
331
373
endpoint := fmt.Sprintf("/%s/%s/branches/%s", ownerDid, repoName, url.PathEscape(branch))
332
374
333
-
req, err := us.newRequest(Method, endpoint, nil)
375
+
req, err := us.newRequest(Method, endpoint, nil, nil)
334
376
if err != nil {
335
377
return nil, err
336
378
}
···
345
387
346
388
endpoint := fmt.Sprintf("/%s/%s/branches/default", ownerDid, repoName)
347
389
348
-
req, err := us.newRequest(Method, endpoint, nil)
390
+
req, err := us.newRequest(Method, endpoint, nil, nil)
349
391
if err != nil {
350
392
return nil, err
351
393
}
···
359
401
Endpoint = "/capabilities"
360
402
)
361
403
362
-
req, err := us.newRequest(Method, Endpoint, nil)
404
+
req, err := us.newRequest(Method, Endpoint, nil, nil)
363
405
if err != nil {
364
406
return nil, err
365
407
}
···
385
427
386
428
endpoint := fmt.Sprintf("/%s/%s/compare/%s/%s", ownerDid, repoName, url.PathEscape(rev1), url.PathEscape(rev2))
387
429
388
-
req, err := us.newRequest(Method, endpoint, nil)
430
+
req, err := us.newRequest(Method, endpoint, nil, nil)
389
431
if err != nil {
390
432
return nil, fmt.Errorf("Failed to create request.")
391
433
}
+27
-3
knotserver/git/git.go
+27
-3
knotserver/git/git.go
···
156
156
return commits, nil
157
157
}
158
158
159
+
func (g *GitRepo) Commit(h plumbing.Hash) (*object.Commit, error) {
160
+
return g.r.CommitObject(h)
161
+
}
162
+
159
163
func (g *GitRepo) LastCommit() (*object.Commit, error) {
160
164
c, err := g.r.CommitObject(g.h)
161
165
if err != nil {
···
222
226
return tags, nil
223
227
}
224
228
225
-
func (g *GitRepo) Branches() ([]*plumbing.Reference, error) {
229
+
func (g *GitRepo) Branches() ([]types.Branch, error) {
226
230
bi, err := g.r.Branches()
227
231
if err != nil {
228
232
return nil, fmt.Errorf("branchs: %w", err)
229
233
}
230
234
231
-
branches := []*plumbing.Reference{}
235
+
branches := []types.Branch{}
236
+
237
+
defaultBranch, err := g.FindMainBranch()
238
+
if err != nil {
239
+
return nil, fmt.Errorf("getting default branch", "error", err.Error())
240
+
}
232
241
233
242
_ = bi.ForEach(func(ref *plumbing.Reference) error {
234
-
branches = append(branches, ref)
243
+
b := types.Branch{}
244
+
b.Hash = ref.Hash().String()
245
+
b.Name = ref.Name().Short()
246
+
247
+
// resolve commit that this branch points to
248
+
commit, _ := g.Commit(ref.Hash())
249
+
if commit != nil {
250
+
b.Commit = commit
251
+
}
252
+
253
+
if defaultBranch != "" && defaultBranch == b.Name {
254
+
b.IsDefault = true
255
+
}
256
+
257
+
branches = append(branches, b)
258
+
235
259
return nil
236
260
})
237
261
+23
-19
knotserver/routes.go
+23
-19
knotserver/routes.go
···
93
93
return
94
94
}
95
95
96
-
bs := []types.Branch{}
97
-
for _, branch := range branches {
98
-
b := types.Branch{}
99
-
b.Hash = branch.Hash().String()
100
-
b.Name = branch.Name().Short()
101
-
bs = append(bs, b)
102
-
}
103
-
104
96
tags, err := gr.Tags()
105
97
if err != nil {
106
98
// Non-fatal, we *should* have at least one branch to show.
···
160
152
Readme: readmeContent,
161
153
ReadmeFileName: readmeFile,
162
154
Files: files,
163
-
Branches: bs,
155
+
Branches: branches,
164
156
Tags: rtags,
165
157
TotalCommits: total,
166
158
}
···
293
285
294
286
func (h *Handle) Log(w http.ResponseWriter, r *http.Request) {
295
287
ref := chi.URLParam(r, "ref")
288
+
ref, _ = url.PathUnescape(ref)
289
+
296
290
path, _ := securejoin.SecureJoin(h.c.Repo.ScanPath, didPath(r))
297
291
298
292
l := h.l.With("handler", "Log", "ref", ref, "path", path)
···
442
436
return
443
437
}
444
438
445
-
bs := []types.Branch{}
446
-
for _, branch := range branches {
447
-
b := types.Branch{}
448
-
b.Hash = branch.Hash().String()
449
-
b.Name = branch.Name().Short()
450
-
bs = append(bs, b)
451
-
}
452
-
453
439
resp := types.RepoBranchesResponse{
454
-
Branches: bs,
440
+
Branches: branches,
455
441
}
456
442
457
443
writeJSON(w, resp)
···
473
459
474
460
ref, err := gr.Branch(branchName)
475
461
if err != nil {
476
-
l.Error("getting branches", "error", err.Error())
462
+
l.Error("getting branch", "error", err.Error())
477
463
writeError(w, err.Error(), http.StatusInternalServerError)
478
464
return
479
465
}
480
466
467
+
commit, err := gr.Commit(ref.Hash())
468
+
if err != nil {
469
+
l.Error("getting commit object", "error", err.Error())
470
+
writeError(w, err.Error(), http.StatusInternalServerError)
471
+
return
472
+
}
473
+
474
+
defaultBranch, err := gr.FindMainBranch()
475
+
isDefault := false
476
+
if err != nil {
477
+
l.Error("getting default branch", "error", err.Error())
478
+
// do not quit though
479
+
} else if defaultBranch == branchName {
480
+
isDefault = true
481
+
}
482
+
481
483
resp := types.RepoBranchResponse{
482
484
Branch: types.Branch{
483
485
Reference: types.Reference{
484
486
Name: ref.Name().Short(),
485
487
Hash: ref.Hash().String(),
486
488
},
489
+
Commit: commit,
490
+
IsDefault: isDefault,
487
491
},
488
492
}
489
493