forked from tangled.org/core
Monorepo for Tangled

impl breadcrumbs and file nav

Changed files
+114 -62
appview
+40 -10
appview/pages/pages.go
··· 8 8 "io/fs" 9 9 "log" 10 10 "net/http" 11 - "os" 12 11 "path" 13 - "path/filepath" 14 12 "strings" 15 13 16 14 "github.com/dustin/go-humanize" ··· 34 32 "add": func(a, b int) int { 35 33 return a + b 36 34 }, 35 + "sub": func(a, b int) int { 36 + return a - b 37 + }, 38 + "cond": func(cond interface{}, a, b string) string { 39 + if cond == nil { 40 + return b 41 + } 42 + 43 + if boolean, ok := cond.(bool); boolean && ok { 44 + return a 45 + } 46 + 47 + return b 48 + }, 37 49 "didOrHandle": func(did, handle string) string { 38 50 if handle != "" { 39 51 return fmt.Sprintf("@%s", handle) ··· 50 62 pairs = append(pairs, []string{values[i], values[i+1]}) 51 63 } 52 64 return pairs, nil 65 + }, 66 + "append": func(s []string, values ...string) []string { 67 + for _, v := range values { 68 + s = append(s, v) 69 + } 70 + return s 53 71 }, 54 72 "timeFmt": humanize.Time, 55 73 "length": func(v []string) int { ··· 195 213 return path.Join(r.OwnerWithAt(), r.Name) 196 214 } 197 215 216 + func (r RepoInfo) GetTabs() [][]string { 217 + tabs := [][]string{ 218 + {"overview", "/"}, 219 + {"issues", "/issues"}, 220 + {"pulls", "/pulls"}, 221 + } 222 + 223 + if r.SettingsAllowed { 224 + tabs = append(tabs, []string{"settings", "/settings"}) 225 + } 226 + 227 + return tabs 228 + } 229 + 198 230 type RepoIndexParams struct { 199 231 LoggedInUser *auth.User 200 232 RepoInfo RepoInfo ··· 230 262 type RepoTreeParams struct { 231 263 LoggedInUser *auth.User 232 264 RepoInfo RepoInfo 265 + Active string 266 + BreadCrumbs [][]string 267 + BaseTreeLink string 268 + BaseBlobLink string 233 269 types.RepoTreeResponse 234 270 } 235 271 236 272 func (p *Pages) RepoTree(w io.Writer, params RepoTreeParams) error { 273 + params.Active = "overview" 237 274 return p.execute("repo/tree", w, params) 238 275 } 239 276 ··· 261 298 LoggedInUser *auth.User 262 299 RepoInfo RepoInfo 263 300 Active string 264 - File string 265 - PathElems []string 301 + BreadCrumbs [][]string 266 302 types.RepoBlobResponse 267 303 } 268 304 269 305 func (p *Pages) RepoBlob(w io.Writer, params RepoBlobParams) error { 270 - path := filepath.Dir(params.Path) 271 - file := filepath.Base(params.Path) 272 - 273 - params.PathElems = strings.Split(path, string(os.PathSeparator)) 274 - params.Path = path 275 - params.File = file 276 306 params.Active = "overview" 277 307 return p.executeRepo("repo/blob", w, params) 278 308 }
+1 -1
appview/pages/templates/layouts/repobase.html
··· 16 16 <nav class="w-full mx-auto"> 17 17 <div class="flex z-60 border-black border-b"> 18 18 {{ $activeTabStyles := "border-black border-l border-r border-t border-b-0 -mb-px bg-white" }} 19 - {{ $tabs := assoc "overview" "/" "issues" "/issues" "pulls" "/pulls" "settings" "/settings" }} 19 + {{ $tabs := .RepoInfo.GetTabs }} 20 20 {{ range $item := $tabs }} 21 21 {{ $key := index $item 0 }} 22 22 {{ $value := index $item 1 }}
+1 -1
appview/pages/templates/layouts/topbar.html
··· 1 1 {{ define "layouts/topbar" }} 2 2 {{ with .LoggedInUser }} 3 - <nav class="flex items-center justify-center space-x-4 text-sm mb-4"> 3 + <nav class="flex items-center justify-center space-x-4 text-sm mb-4 border-b border-l border-r border-black"> 4 4 <a 5 5 href="/" 6 6 hx-boost="true"
+9 -4
appview/pages/templates/repo/blob.html
··· 3 3 {{ $tot_lines := len $lines }} 4 4 {{ $tot_chars := len (printf "%d" $tot_lines) }} 5 5 {{ $code_number_style := "text-gray-400 left-0 bg-white text-right mr-2 select-none" }} 6 - <div class="pb-2 text-lg"> 7 - {{ range .PathElems }} 8 - <a href="/{{ $.RepoInfo.FullName }}/tree/{{ $.Ref }}/{{ . }}" class="text-bold text-gray-500">{{ . }}</a> / 9 - {{ end }}<span class="">{{ .File }}</span> 6 + {{ $linkstyle := "no-underline hover:underline" }} 7 + <div class="pb-2 text-base"> 8 + {{ range $idx, $value := .BreadCrumbs }} 9 + {{ if ne $idx (sub (len $.BreadCrumbs) 1) }} 10 + <a href="{{ index . 1}}" class="text-bold text-gray-500 {{ $linkstyle }}">{{ index . 0 }}</a> / 11 + {{ else }} 12 + <a href="{{ index . 1}}" class="text-bold text-gray-500 {{ $linkstyle }}">{{ index . 0 }}</a> 13 + {{ end }} 14 + {{ end }} 10 15 </div> 11 16 12 17
+33 -43
appview/pages/templates/repo/tree.html
··· 1 - {{define "title"}} {{ .RepoInfo.FullName }} {{end}} 2 - 3 - {{define "content"}} 1 + {{define "repoContent"}} 2 + <main> 3 + <div class="tree"> 4 + {{ $containerstyle := "py-1" }} 5 + {{ $linkstyle := "no-underline hover:underline" }} 4 6 5 - <h1> 6 - {{ .RepoInfo.FullName }} 7 - </h1> 8 - <main> 9 - <div class="tree"> 10 - 11 - {{ if .Parent }} 12 - <div></div> 13 - <div></div> 14 - <div><a href="/{{ .RepoInfo.FullName }}/tree/{{ .Ref }}/{{ .DotDot }}">..</a></div> 15 - {{ end }} 7 + <div class="pb-2 text-base"> 8 + {{ range .BreadCrumbs }} 9 + <a href="{{ index . 1}}" class="text-bold text-gray-500 {{ $linkstyle }}">{{ index . 0 }}</a> / 10 + {{ end }} 11 + </div> 16 12 17 - {{ range .Files }} 18 - {{ if not .IsFile }} 19 - <div class="mode">{{ .Mode }}</div> 20 - <div class="size">{{ .Size }}</div> 21 - <div> 22 - {{ if $.Parent }} 23 - <a href="/{{ .RepoInfo.FullName }}/tree/{{ $.Ref }}/{{ $.Parent }}/{{ .Name }}">{{ .Name }}/</a> 24 - {{ else }} 25 - <a href="/{{ .RepoInfo.FullName }}/tree/{{ $.Ref }}/{{ .Name }}">{{ .Name }}/</a> 26 - {{ end }} 13 + {{ range .Files }} 14 + {{ if not .IsFile }} 15 + <div class="{{ $containerstyle }}"> 16 + <a href="/{{ $.BaseTreeLink }}/{{ .Name }}" class="{{ $linkstyle }}"> 17 + <div class="flex items-center gap-2"> 18 + <i class="w-3 h-3 fill-current" data-lucide="folder"></i>{{ .Name }}/ 27 19 </div> 28 - <hr /> 29 - {{ end }} 30 - {{ end }} 20 + </a> 21 + </div> 22 + {{ end }} 23 + {{ end }} 31 24 32 - {{ range .Files }} 33 - {{ if .IsFile }} 34 - <div class="mode">{{ .Mode }}</div> 35 - <div class="size">{{ .Size }}</div> 36 - <div> 37 - {{ if $.Parent }} 38 - <a href="/{{ $.RepoInfo.FullName }}/blob/{{ $.Ref }}/{{ $.Parent }}/{{ .Name }}">{{ .Name }}</a> 39 - {{ else }} 40 - <a href="/{{ $.RepoInfo.FullName }}/blob/{{ $.Ref }}/{{ .Name }}">{{ .Name }}</a> 41 - {{ end }} 25 + {{ range .Files }} 26 + {{ if .IsFile }} 27 + <div class="{{ $containerstyle }}"> 28 + <a href="/{{ $.BaseBlobLink }}/{{ .Name }}" class="{{ $linkstyle }}"> 29 + <div class="flex items-center gap-2"> 30 + <i class="w-3 h-3" data-lucide="file"></i>{{ .Name }} 31 + </div> 32 + </a> 42 33 </div> 43 - <hr /> 44 - {{ end }} 45 - {{ end }} 46 - </div> 47 - </main> 48 - {{end}} 34 + {{ end }} 35 + {{ end }} 36 + </div> 37 + </main> 38 + {{end}}
+28 -2
appview/state/repo.go
··· 6 6 "io" 7 7 "log" 8 8 "net/http" 9 + "path" 10 + "strings" 9 11 10 12 "github.com/bluesky-social/indigo/atproto/identity" 11 13 securejoin "github.com/cyphar/filepath-securejoin" ··· 170 172 return 171 173 } 172 174 173 - log.Println(result) 175 + user := s.auth.GetUser(r) 176 + 177 + var breadcrumbs [][]string 178 + breadcrumbs = append(breadcrumbs, []string{f.RepoName, fmt.Sprintf("/%s/%s/tree/%s", f.OwnerDid(), f.RepoName, ref)}) 179 + if treePath != "" { 180 + for idx, elem := range strings.Split(treePath, "/") { 181 + breadcrumbs = append(breadcrumbs, []string{elem, fmt.Sprintf("%s/%s", breadcrumbs[idx][1], elem)}) 182 + } 183 + } 174 184 175 - user := s.auth.GetUser(r) 185 + baseTreeLink := path.Join(f.OwnerDid(), f.RepoName, "tree", ref, treePath) 186 + baseBlobLink := path.Join(f.OwnerDid(), f.RepoName, "blob", ref, treePath) 187 + 176 188 s.pages.RepoTree(w, pages.RepoTreeParams{ 177 189 LoggedInUser: user, 190 + BreadCrumbs: breadcrumbs, 191 + BaseTreeLink: baseTreeLink, 192 + BaseBlobLink: baseBlobLink, 178 193 RepoInfo: pages.RepoInfo{ 179 194 OwnerDid: f.OwnerDid(), 180 195 OwnerHandle: f.OwnerHandle(), ··· 296 311 return 297 312 } 298 313 314 + var breadcrumbs [][]string 315 + breadcrumbs = append(breadcrumbs, []string{f.RepoName, fmt.Sprintf("/%s/%s/tree/%s", f.OwnerDid(), f.RepoName, ref)}) 316 + if filePath != "" { 317 + for idx, elem := range strings.Split(filePath, "/") { 318 + breadcrumbs = append(breadcrumbs, []string{elem, fmt.Sprintf("%s/%s", breadcrumbs[idx][1], elem)}) 319 + } 320 + } 321 + 299 322 user := s.auth.GetUser(r) 300 323 s.pages.RepoBlob(w, pages.RepoBlobParams{ 301 324 LoggedInUser: user, ··· 306 329 SettingsAllowed: settingsAllowed(s, user, f), 307 330 }, 308 331 RepoBlobResponse: result, 332 + BreadCrumbs: breadcrumbs, 309 333 }) 310 334 return 311 335 } ··· 449 473 ok, err := s.enforcer.IsSettingsAllowed(u.Did, f.Knot, f.OwnerSlashRepo()) 450 474 if err == nil && ok { 451 475 settingsAllowed = true 476 + } else { 477 + log.Println(err, ok) 452 478 } 453 479 } 454 480
+2 -1
appview/state/state.go
··· 535 535 } 536 536 537 537 // acls 538 - p, _ := securejoin.SecureJoin(domain, repoName) 538 + p, _ := securejoin.SecureJoin(user.Did, repoName) 539 539 err = s.enforcer.AddRepo(user.Did, domain, p) 540 540 if err != nil { 541 + log.Println(err) 541 542 s.pages.Notice(w, "repo", "Failed to set up repository permissions.") 542 543 return 543 544 }