forked from tangled.org/core
this repo has no description

appview/pages: improve grid layout in directory listings

unifies layouts in repo-index and repo-tree.

Signed-off-by: oppiliappan <me@oppi.li>

oppi.li 87d9107d 23e971e6

verified
Changed files
+101 -115
appview
+1 -1
appview/pages/funcmap.go
··· 191 if v.Len() == 0 { 192 return nil 193 } 194 - return v.Slice(0, min(n, v.Len()-1)).Interface() 195 }, 196 197 "markdown": func(text string) template.HTML {
··· 191 if v.Len() == 0 { 192 return nil 193 } 194 + return v.Slice(0, min(n, v.Len())).Interface() 195 }, 196 197 "markdown": func(text string) template.HTML {
+1 -2
appview/pages/pages.go
··· 555 RepoInfo repoinfo.RepoInfo 556 Active string 557 BreadCrumbs [][]string 558 - BaseTreeLink string 559 - BaseBlobLink string 560 types.RepoTreeResponse 561 } 562
··· 555 RepoInfo repoinfo.RepoInfo 556 Active string 557 BreadCrumbs [][]string 558 + TreePath string 559 types.RepoTreeResponse 560 } 561
+27 -47
appview/pages/templates/repo/index.html
··· 127 {{ end }} 128 129 {{ define "fileTree" }} 130 - <div 131 - id="file-tree" 132 - class="col-span-1 pr-2 md:border-r md:border-gray-200 dark:md:border-gray-700" 133 - > 134 - {{ $containerstyle := "py-1" }} 135 - {{ $linkstyle := "no-underline hover:underline dark:text-white" }} 136 137 - {{ range .Files }} 138 - {{ if not .IsFile }} 139 - <div class="{{ $containerstyle }}"> 140 - <div class="flex justify-between items-center"> 141 - <a 142 - href="/{{ $.RepoInfo.FullName }}/tree/{{ $.Ref | urlquery }}/{{ .Name }}" 143 - class="{{ $linkstyle }}" 144 - > 145 - <div class="flex items-center gap-2"> 146 - {{ i "folder" "size-4 fill-current" }} 147 - {{ .Name }} 148 - </div> 149 - </a> 150 151 - {{ if .LastCommit }} 152 - <span class="text-xs text-gray-500 dark:text-gray-400">{{ template "repo/fragments/time" .LastCommit.When }}</span> 153 - {{ end }} 154 - </div> 155 - </div> 156 - {{ end }} 157 - {{ end }} 158 - 159 - {{ range .Files }} 160 - {{ if .IsFile }} 161 - <div class="{{ $containerstyle }}"> 162 - <div class="flex justify-between items-center"> 163 - <a 164 - href="/{{ $.RepoInfo.FullName }}/blob/{{ $.Ref | urlquery }}/{{ .Name }}" 165 - class="{{ $linkstyle }}" 166 - > 167 - <div class="flex items-center gap-2"> 168 - {{ i "file" "size-4" }}{{ .Name }} 169 - </div> 170 - </a> 171 172 - {{ if .LastCommit }} 173 - <span class="text-xs text-gray-500 dark:text-gray-400">{{ template "repo/fragments/time" .LastCommit.When }}</span> 174 - {{ end }} 175 - </div> 176 - </div> 177 - {{ end }} 178 - {{ end }} 179 - </div> 180 {{ end }} 181 182 {{ define "rightInfo" }}
··· 127 {{ end }} 128 129 {{ define "fileTree" }} 130 + <div id="file-tree" class="col-span-1 pr-2 md:border-r md:border-gray-200 dark:md:border-gray-700" > 131 + {{ $linkstyle := "no-underline hover:underline dark:text-white" }} 132 133 + {{ range .Files }} 134 + <div class="grid grid-cols-2 gap-4 items-center py-1"> 135 + <div class="col-span-1"> 136 + {{ $link := printf "/%s/%s/%s/%s" $.RepoInfo.FullName "tree" (urlquery $.Ref) .Name }} 137 + {{ $icon := "folder" }} 138 + {{ $iconStyle := "size-4 fill-current" }} 139 140 + {{ if .IsFile }} 141 + {{ $link = printf "/%s/%s/%s/%s" $.RepoInfo.FullName "blob" (urlquery $.Ref) .Name }} 142 + {{ $icon = "file" }} 143 + {{ $iconStyle = "size-4" }} 144 + {{ end }} 145 + <a href="{{ $link }}" class="{{ $linkstyle }}"> 146 + <div class="flex items-center gap-2"> 147 + {{ i $icon $iconStyle }}{{ .Name }} 148 + </div> 149 + </a> 150 + </div> 151 152 + <div class="text-xs col-span-1 text-right"> 153 + {{ with .LastCommit }} 154 + <a href="/{{ $.RepoInfo.FullName }}/commit/{{ .Hash }}" class="text-gray-500 dark:text-gray-400">{{ template "repo/fragments/time" .When }}</a> 155 + {{ end }} 156 + </div> 157 + </div> 158 + {{ end }} 159 + </div> 160 {{ end }} 161 162 {{ define "rightInfo" }}
+26 -34
appview/pages/templates/repo/tree.html
··· 19 {{define "repoContent"}} 20 <main> 21 <div class="tree"> 22 - {{ $containerstyle := "py-1" }} 23 {{ $linkstyle := "no-underline hover:underline" }} 24 25 <div class="pb-2 mb-3 text-base border-b border-gray-200 dark:border-gray-700"> ··· 54 </div> 55 56 {{ range .Files }} 57 - {{ if not .IsFile }} 58 - <div class="{{ $containerstyle }}"> 59 - <div class="flex justify-between items-center"> 60 - <a href="/{{ $.BaseTreeLink }}/{{ .Name }}" class="{{ $linkstyle }}"> 61 - <div class="flex items-center gap-2"> 62 - {{ i "folder" "size-4 fill-current" }}{{ .Name }} 63 - </div> 64 - </a> 65 - {{ if .LastCommit}} 66 - <div class="flex items-end gap-2"> 67 - <span class="text text-gray-500 dark:text-gray-400 mr-6">{{ .LastCommit.Message }}</span> 68 - <span class="text-xs text-gray-500 dark:text-gray-400">{{ template "repo/fragments/time" .LastCommit.When }}</span> 69 </div> 70 - {{ end }} 71 </div> 72 - </div> 73 - {{ end }} 74 - {{ end }} 75 76 - {{ range .Files }} 77 - {{ if .IsFile }} 78 - <div class="{{ $containerstyle }}"> 79 - <div class="flex justify-between items-center"> 80 - <a href="/{{ $.BaseBlobLink }}/{{ .Name }}" class="{{ $linkstyle }}"> 81 - <div class="flex items-center gap-2"> 82 - {{ i "file" "size-4" }}{{ .Name }} 83 - </div> 84 - </a> 85 - {{ if .LastCommit}} 86 - <div class="flex items-end gap-2"> 87 - <span class="text text-gray-500 dark:text-gray-400 mr-6">{{ .LastCommit.Message }}</span> 88 - <span class="text-xs text-gray-500 dark:text-gray-400">{{ template "repo/fragments/time" .LastCommit.When }}</span> 89 - </div> 90 - {{ end }} 91 </div> 92 - </div> 93 {{ end }} 94 - {{ end }} 95 </div> 96 </main> 97 {{end}}
··· 19 {{define "repoContent"}} 20 <main> 21 <div class="tree"> 22 {{ $linkstyle := "no-underline hover:underline" }} 23 24 <div class="pb-2 mb-3 text-base border-b border-gray-200 dark:border-gray-700"> ··· 53 </div> 54 55 {{ range .Files }} 56 + <div class="grid grid-cols-12 gap-4 items-center py-1"> 57 + <div class="col-span-6 md:col-span-3"> 58 + {{ $link := printf "/%s/%s/%s/%s/%s" $.RepoInfo.FullName "tree" (urlquery $.Ref) $.TreePath .Name }} 59 + {{ $icon := "folder" }} 60 + {{ $iconStyle := "size-4 fill-current" }} 61 + 62 + {{ if .IsFile }} 63 + {{ $icon = "file" }} 64 + {{ $iconStyle = "size-4" }} 65 + {{ end }} 66 + <a href="{{ $link }}" class="{{ $linkstyle }}"> 67 + <div class="flex items-center gap-2"> 68 + {{ i $icon $iconStyle }}{{ .Name }} 69 </div> 70 + </a> 71 </div> 72 73 + <div class="col-span-0 md:col-span-7 hidden md:block overflow-hidden"> 74 + {{ with .LastCommit }} 75 + <a href="/{{ $.RepoInfo.FullName }}/commit/{{ .Hash }}" class="text-gray-500 dark:text-gray-400 block truncate">{{ .Message }}</a> 76 + {{ end }} 77 + </div> 78 + 79 + <div class="col-span-6 md:col-span-2 text-right"> 80 + {{ with .LastCommit }} 81 + <a href="/{{ $.RepoInfo.FullName }}/commit/{{ .Hash }}" class="text-gray-500 dark:text-gray-400">{{ template "repo/fragments/time" .When }}</a> 82 + {{ end }} 83 </div> 84 + </div> 85 {{ end }} 86 + 87 </div> 88 </main> 89 {{end}}
+3 -3
appview/pages/templates/user/repos.html
··· 8 {{ end }} 9 10 {{ define "content" }} 11 - <div class="grid grid-cols-1 md:grid-cols-8 gap-4"> 12 - <div class="md:col-span-2 order-1 md:order-1"> 13 {{ template "user/fragments/profileCard" .Card }} 14 </div> 15 - <div id="all-repos" class="md:col-span-6 order-2 md:order-2"> 16 {{ block "ownRepos" . }}{{ end }} 17 </div> 18 </div>
··· 8 {{ end }} 9 10 {{ define "content" }} 11 + <div class="grid grid-cols-1 md:grid-cols-11 gap-4"> 12 + <div class="md:col-span-3 order-1 md:order-1"> 13 {{ template "user/fragments/profileCard" .Card }} 14 </div> 15 + <div id="all-repos" class="md:col-span-8 order-2 md:order-2"> 16 {{ block "ownRepos" . }}{{ end }} 17 </div> 18 </div>
+2
appview/repo/index.go
··· 58 tagMap[hash] = append(tagMap[hash], branch.Name) 59 } 60 61 slices.SortFunc(result.Branches, func(a, b types.Branch) int { 62 if a.Name == result.Ref { 63 return -1
··· 58 tagMap[hash] = append(tagMap[hash], branch.Name) 59 } 60 61 + sortFiles(result.Files) 62 + 63 slices.SortFunc(result.Branches, func(a, b types.Branch) int { 64 if a.Name == result.Ref { 65 return -1
+7 -28
appview/repo/repo.go
··· 10 "log" 11 "net/http" 12 "net/url" 13 - "path" 14 "slices" 15 - "sort" 16 "strconv" 17 "strings" 18 "time" ··· 374 375 // redirects tree paths trying to access a blob; in this case the result.Files is unpopulated, 376 // so we can safely redirect to the "parent" (which is the same file). 377 - if len(result.Files) == 0 && result.Parent == treePath { 378 http.Redirect(w, r, fmt.Sprintf("/%s/blob/%s/%s", f.OwnerSlashRepo(), ref, result.Parent), http.StatusFound) 379 return 380 } ··· 389 } 390 } 391 392 - baseTreeLink := path.Join(f.OwnerSlashRepo(), "tree", ref, treePath) 393 - baseBlobLink := path.Join(f.OwnerSlashRepo(), "blob", ref, treePath) 394 395 rp.pages.RepoTree(w, pages.RepoTreeParams{ 396 LoggedInUser: user, 397 BreadCrumbs: breadcrumbs, 398 - BaseTreeLink: baseTreeLink, 399 - BaseBlobLink: baseBlobLink, 400 RepoInfo: f.RepoInfo(user), 401 RepoTreeResponse: result, 402 }) 403 - return 404 } 405 406 func (rp *Repo) RepoTags(w http.ResponseWriter, r *http.Request) { ··· 480 return 481 } 482 483 - slices.SortFunc(result.Branches, func(a, b types.Branch) int { 484 - if a.IsDefault { 485 - return -1 486 - } 487 - if b.IsDefault { 488 - return 1 489 - } 490 - if a.Commit != nil && b.Commit != nil { 491 - if a.Commit.Committer.When.Before(b.Commit.Committer.When) { 492 - return 1 493 - } else { 494 - return -1 495 - } 496 - } 497 - return strings.Compare(a.Name, b.Name) * -1 498 - }) 499 500 user := rp.oauth.GetUser(r) 501 rp.pages.RepoBranches(w, pages.RepoBranchesParams{ ··· 503 RepoInfo: f.RepoInfo(user), 504 RepoBranchesResponse: *result, 505 }) 506 - return 507 } 508 509 func (rp *Repo) RepoBlob(w http.ResponseWriter, r *http.Request) { ··· 1233 return 1234 } 1235 branches := result.Branches 1236 - sort.Slice(branches, func(i int, j int) bool { 1237 - return branches[i].Commit.Committer.When.After(branches[j].Commit.Committer.When) 1238 - }) 1239 1240 var defaultBranch string 1241 for _, b := range branches {
··· 10 "log" 11 "net/http" 12 "net/url" 13 "slices" 14 "strconv" 15 "strings" 16 "time" ··· 372 373 // redirects tree paths trying to access a blob; in this case the result.Files is unpopulated, 374 // so we can safely redirect to the "parent" (which is the same file). 375 + unescapedTreePath, _ := url.PathUnescape(treePath) 376 + if len(result.Files) == 0 && result.Parent == unescapedTreePath { 377 http.Redirect(w, r, fmt.Sprintf("/%s/blob/%s/%s", f.OwnerSlashRepo(), ref, result.Parent), http.StatusFound) 378 return 379 } ··· 388 } 389 } 390 391 + sortFiles(result.Files) 392 393 rp.pages.RepoTree(w, pages.RepoTreeParams{ 394 LoggedInUser: user, 395 BreadCrumbs: breadcrumbs, 396 + TreePath: treePath, 397 RepoInfo: f.RepoInfo(user), 398 RepoTreeResponse: result, 399 }) 400 } 401 402 func (rp *Repo) RepoTags(w http.ResponseWriter, r *http.Request) { ··· 476 return 477 } 478 479 + sortBranches(result.Branches) 480 481 user := rp.oauth.GetUser(r) 482 rp.pages.RepoBranches(w, pages.RepoBranchesParams{ ··· 484 RepoInfo: f.RepoInfo(user), 485 RepoBranchesResponse: *result, 486 }) 487 } 488 489 func (rp *Repo) RepoBlob(w http.ResponseWriter, r *http.Request) { ··· 1213 return 1214 } 1215 branches := result.Branches 1216 + 1217 + sortBranches(branches) 1218 1219 var defaultBranch string 1220 for _, b := range branches {
+34
appview/repo/repo_util.go
··· 5 "crypto/rand" 6 "fmt" 7 "math/big" 8 9 "tangled.sh/tangled.sh/core/appview/db" 10 "tangled.sh/tangled.sh/core/appview/pages/repoinfo" 11 12 "github.com/go-git/go-git/v5/plumbing/object" 13 ) 14 15 func uniqueEmails(commits []*object.Commit) []string { 16 emails := make(map[string]struct{})
··· 5 "crypto/rand" 6 "fmt" 7 "math/big" 8 + "slices" 9 + "sort" 10 + "strings" 11 12 "tangled.sh/tangled.sh/core/appview/db" 13 "tangled.sh/tangled.sh/core/appview/pages/repoinfo" 14 + "tangled.sh/tangled.sh/core/types" 15 16 "github.com/go-git/go-git/v5/plumbing/object" 17 ) 18 + 19 + func sortFiles(files []types.NiceTree) { 20 + sort.Slice(files, func(i, j int) bool { 21 + iIsFile := files[i].IsFile 22 + jIsFile := files[j].IsFile 23 + if iIsFile != jIsFile { 24 + return !iIsFile 25 + } 26 + return files[i].Name < files[j].Name 27 + }) 28 + } 29 + 30 + func sortBranches(branches []types.Branch) { 31 + slices.SortFunc(branches, func(a, b types.Branch) int { 32 + if a.IsDefault { 33 + return -1 34 + } 35 + if b.IsDefault { 36 + return 1 37 + } 38 + if a.Commit != nil && b.Commit != nil { 39 + if a.Commit.Committer.When.Before(b.Commit.Committer.When) { 40 + return 1 41 + } else { 42 + return -1 43 + } 44 + } 45 + return strings.Compare(a.Name, b.Name) 46 + }) 47 + } 48 49 func uniqueEmails(commits []*object.Commit) []string { 50 emails := make(map[string]struct{})