forked from tangled.org/core
Monorepo for Tangled

appview: add pages for tags and branches

Changed files
+456 -273
appview
knotserver
types
+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
··· 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
··· 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 - &hellip; 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
+43 -13
appview/pages/templates/repo/tags.html
··· 1 + {{ define "title" }} 2 + tags · {{ .RepoInfo.FullName }} 3 + {{ end }} 4 + 1 5 {{ define "repoContent" }} 2 - {{ $name := .RepoInfo.Name }} 3 - <h3>tags</h3> 4 - <div class="refs"> 5 - {{ range .Tags }} 6 - <div> 7 - <strong>{{ .Ref.Name }}</strong> 8 - <a href="/{{ $name }}/tree/{{ .Ref.Name }}/">browse</a> 9 - <a href="/{{ $name }}/log/{{ .Ref.Name }}">log</a> 10 - <a href="/{{ $name }}/archive/{{ .Ref.Name }}.tar.gz">tar.gz</a> 11 - {{ if .Message }} 12 - <pre>{{ .Message }}</pre> 6 + <section> 7 + <div class="flex flex-col py-2"> 8 + {{ range .Tags }} 9 + <div class="grid grid-cols-12 items-start"> 10 + <!-- Header column (left) --> 11 + <div class="col-span-3 border-r border-gray-200 dark:border-gray-700 h-full text-right px-2 pb-6"> 12 + <a href="/{{ $.RepoInfo.FullName }}/tree/{{ .Name | urlquery }}" class="no-underline hover:underline flex items-center justify-end gap-2 font-bold"> 13 + {{ i "tag" "w-4 h-4" }} 14 + {{ .Name }} 15 + </a> 16 + <div class="flex flex-grow flex-col text-gray-500 dark:text-gray-400 text-sm"> 17 + {{ if .Tag }} 18 + <a href="/{{ $.RepoInfo.FullName }}/commit/{{ .Tag.Target.String }}" 19 + class="no-underline hover:underline text-gray-500 dark:text-gray-400 flex items-center justify-end gap-2"> 20 + {{ i "git-commit-horizontal" "w-4 h-4" }} 21 + {{ slice .Tag.Target.String 0 8 }} 22 + </a> 23 + <span>{{ .Tag.Tagger.Name }}</span> 24 + <time>{{ timeFmt .Tag.Tagger.When }}</time> 25 + {{ end }} 26 + </div> 27 + </div> 28 + 29 + <!-- Paragraph column (right) --> 30 + <div class="col-span-9 px-2 pb-6"> 31 + <div> 32 + {{ if .Tag }} 33 + {{ $messageParts := splitN .Tag.Message "\n\n" 2 }} 34 + <p class="font-bold">{{ index $messageParts 0 }}</p> 35 + {{ if gt (len $messageParts) 1 }} 36 + <p class="cursor-text pb-2 text-sm">{{ nl2br (index $messageParts 1) }}</p> 13 37 {{ end }} 14 - </div> 15 - {{ end }} 38 + {{ else }} 39 + <p class="italic text-gray-500 dark:text-gray-400">no message</p> 40 + {{ end }} 41 + </div> 42 + </div> 43 + </div> 44 + {{ end }} 16 45 </div> 46 + </section> 17 47 {{ end }}
+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
··· 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
··· 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
··· 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
··· 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
+2
types/repo.go
··· 61 61 62 62 type Branch struct { 63 63 Reference `json:"reference"` 64 + Commit *object.Commit `json:"commit,omitempty"` 65 + IsDefault bool `json:"is_deafult,omitempty"` 64 66 } 65 67 66 68 type RepoTagsResponse struct {