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

fix first commit duplication in log view

Changed files
+177 -159
appview
+107
appview/pages/funcMap.go
··· 1 + package pages 2 + 3 + import ( 4 + "fmt" 5 + "html" 6 + "html/template" 7 + "reflect" 8 + "strings" 9 + 10 + "github.com/dustin/go-humanize" 11 + ) 12 + 13 + func funcMap() template.FuncMap { 14 + return template.FuncMap{ 15 + "split": func(s string) []string { 16 + return strings.Split(s, "\n") 17 + }, 18 + "splitOn": func(s, sep string) []string { 19 + return strings.Split(s, sep) 20 + }, 21 + "add": func(a, b int) int { 22 + return a + b 23 + }, 24 + "sub": func(a, b int) int { 25 + return a - b 26 + }, 27 + "cond": func(cond interface{}, a, b string) string { 28 + if cond == nil { 29 + return b 30 + } 31 + 32 + if boolean, ok := cond.(bool); boolean && ok { 33 + return a 34 + } 35 + 36 + return b 37 + }, 38 + "didOrHandle": func(did, handle string) string { 39 + if handle != "" { 40 + return fmt.Sprintf("@%s", handle) 41 + } else { 42 + return did 43 + } 44 + }, 45 + "assoc": func(values ...string) ([][]string, error) { 46 + if len(values)%2 != 0 { 47 + return nil, fmt.Errorf("invalid assoc call, must have an even number of arguments") 48 + } 49 + pairs := make([][]string, 0) 50 + for i := 0; i < len(values); i += 2 { 51 + pairs = append(pairs, []string{values[i], values[i+1]}) 52 + } 53 + return pairs, nil 54 + }, 55 + "append": func(s []string, values ...string) []string { 56 + s = append(s, values...) 57 + return s 58 + }, 59 + "timeFmt": humanize.Time, 60 + "byteFmt": humanize.Bytes, 61 + "length": func(slice interface{}) int { 62 + v := reflect.ValueOf(slice) 63 + if v.Kind() == reflect.Slice || v.Kind() == reflect.Array { 64 + return v.Len() 65 + } 66 + return 0 67 + }, 68 + "splitN": func(s, sep string, n int) []string { 69 + return strings.SplitN(s, sep, n) 70 + }, 71 + "escapeHtml": func(s string) template.HTML { 72 + if s == "" { 73 + return template.HTML("<br>") 74 + } 75 + return template.HTML(s) 76 + }, 77 + "unescapeHtml": func(s string) string { 78 + return html.UnescapeString(s) 79 + }, 80 + "nl2br": func(text string) template.HTML { 81 + return template.HTML(strings.Replace(template.HTMLEscapeString(text), "\n", "<br>", -1)) 82 + }, 83 + "unwrapText": func(text string) string { 84 + paragraphs := strings.Split(text, "\n\n") 85 + 86 + for i, p := range paragraphs { 87 + lines := strings.Split(p, "\n") 88 + paragraphs[i] = strings.Join(lines, " ") 89 + } 90 + 91 + return strings.Join(paragraphs, "\n\n") 92 + }, 93 + "sequence": func(n int) []struct{} { 94 + return make([]struct{}, n) 95 + }, 96 + "subslice": func(slice interface{}, start, end int) interface{} { 97 + v := reflect.ValueOf(slice) 98 + if v.Kind() != reflect.Slice && v.Kind() != reflect.Array { 99 + return nil 100 + } 101 + if start < 0 || start > v.Len() || end > v.Len() || start > end { 102 + return nil 103 + } 104 + return v.Slice(start, end).Interface() 105 + }, 106 + } 107 + }
-84
appview/pages/pages.go
··· 4 4 "bytes" 5 5 "embed" 6 6 "fmt" 7 - "html" 8 7 "html/template" 9 8 "io" 10 9 "io/fs" ··· 18 17 chromahtml "github.com/alecthomas/chroma/v2/formatters/html" 19 18 "github.com/alecthomas/chroma/v2/lexers" 20 19 "github.com/alecthomas/chroma/v2/styles" 21 - "github.com/dustin/go-humanize" 22 20 "github.com/sotangled/tangled/appview/auth" 23 21 "github.com/sotangled/tangled/appview/db" 24 22 "github.com/sotangled/tangled/types" ··· 29 27 30 28 type Pages struct { 31 29 t map[string]*template.Template 32 - } 33 - 34 - func funcMap() template.FuncMap { 35 - return template.FuncMap{ 36 - "split": func(s string) []string { 37 - return strings.Split(s, "\n") 38 - }, 39 - "splitOn": func(s, sep string) []string { 40 - return strings.Split(s, sep) 41 - }, 42 - "add": func(a, b int) int { 43 - return a + b 44 - }, 45 - "sub": func(a, b int) int { 46 - return a - b 47 - }, 48 - "cond": func(cond interface{}, a, b string) string { 49 - if cond == nil { 50 - return b 51 - } 52 - 53 - if boolean, ok := cond.(bool); boolean && ok { 54 - return a 55 - } 56 - 57 - return b 58 - }, 59 - "didOrHandle": func(did, handle string) string { 60 - if handle != "" { 61 - return fmt.Sprintf("@%s", handle) 62 - } else { 63 - return did 64 - } 65 - }, 66 - "assoc": func(values ...string) ([][]string, error) { 67 - if len(values)%2 != 0 { 68 - return nil, fmt.Errorf("invalid assoc call, must have an even number of arguments") 69 - } 70 - pairs := make([][]string, 0) 71 - for i := 0; i < len(values); i += 2 { 72 - pairs = append(pairs, []string{values[i], values[i+1]}) 73 - } 74 - return pairs, nil 75 - }, 76 - "append": func(s []string, values ...string) []string { 77 - s = append(s, values...) 78 - return s 79 - }, 80 - "timeFmt": humanize.Time, 81 - "byteFmt": humanize.Bytes, 82 - "length": func(v []string) int { 83 - return len(v) 84 - }, 85 - "splitN": func(s, sep string, n int) []string { 86 - return strings.SplitN(s, sep, n) 87 - }, 88 - "escapeHtml": func(s string) template.HTML { 89 - if s == "" { 90 - return template.HTML("<br>") 91 - } 92 - return template.HTML(s) 93 - }, 94 - "unescapeHtml": func(s string) string { 95 - return html.UnescapeString(s) 96 - }, 97 - "nl2br": func(text string) template.HTML { 98 - return template.HTML(strings.Replace(template.HTMLEscapeString(text), "\n", "<br>", -1)) 99 - }, 100 - "unwrapText": func(text string) string { 101 - paragraphs := strings.Split(text, "\n\n") 102 - 103 - for i, p := range paragraphs { 104 - lines := strings.Split(p, "\n") 105 - paragraphs[i] = strings.Join(lines, " ") 106 - } 107 - 108 - return strings.Join(paragraphs, "\n\n") 109 - }, 110 - "sequence": func(n int) []struct{} { 111 - return make([]struct{}, n) 112 - }, 113 - } 114 30 } 115 31 116 32 func NewPages() *Pages {
+2 -2
appview/pages/templates/layouts/repobase.html
··· 2 2 3 3 {{ define "content" }} 4 4 <section id="repo-header" class="mb-4 p-2"> 5 - <p class="text-lg font-bold"> 5 + <p class="text-lg"> 6 6 <a href="/{{ .RepoInfo.OwnerWithAt }}">{{ .RepoInfo.OwnerWithAt }}</a> 7 7 <span class="select-none">/</span> 8 - <a href="/{{ .RepoInfo.FullName }}">{{ .RepoInfo.Name }}</a> 8 + <a href="/{{ .RepoInfo.FullName }}" class="font-bold">{{ .RepoInfo.Name }}</a> 9 9 </p> 10 10 <span> 11 11 {{ if .RepoInfo.Description }}
+58 -64
appview/pages/templates/repo/index.html
··· 46 46 </a> 47 47 </div> 48 48 49 - <div class="flex gap-4"> 49 + <div class="flex gap-2"> 50 50 <div id="file-tree" class="w-3/5 pr-2 border-r border-gray-200"> 51 51 {{ $containerstyle := "py-1" }} 52 52 {{ $linkstyle := "no-underline hover:underline" }} ··· 104 104 105 105 <div id="commit-log" class="flex-1"> 106 106 {{ range .Commits }} 107 - <div class="flex flex-row items-center"> 108 - <i 109 - class="w-5 h-5 text-gray-400 align-top" 110 - data-lucide="git-commit-horizontal" 111 - ></i> 112 - <div class="relative px-4 py-4"> 113 - <div id="commit-message"> 114 - {{ $messageParts := splitN .Message "\n\n" 2 }} 115 - <div class="text-base cursor-pointer"> 116 - <div> 117 - <div> 118 - <a 119 - href="/{{ $.RepoInfo.FullName }}/commit/{{ .Hash.String }}" 120 - class="inline no-underline hover:underline" 121 - >{{ index $messageParts 0 }}</a 122 - > 123 - {{ if gt (len $messageParts) 1 }} 107 + <div class="relative px-2 pb-8"> 108 + <div id="commit-message"> 109 + {{ $messageParts := splitN .Message "\n\n" 2 }} 110 + <div class="text-base cursor-pointer"> 111 + <div> 112 + <div> 113 + <a 114 + href="/{{ $.RepoInfo.FullName }}/commit/{{ .Hash.String }}" 115 + class="inline no-underline hover:underline" 116 + >{{ index $messageParts 0 }}</a 117 + > 118 + {{ if gt (len $messageParts) 1 }} 124 119 125 - <button 126 - class="py-1/2 px-1 bg-gray-200 hover:bg-gray-400 rounded" 127 - hx-on:click="this.parentElement.nextElementSibling.classList.toggle('hidden')" 128 - > 129 - <i 130 - class="w-3 h-3" 131 - data-lucide="ellipsis" 132 - ></i> 133 - </button> 134 - {{ end }} 135 - </div> 136 - {{ if gt (len $messageParts) 1 }} 137 - <p 138 - class="hidden mt-1 text-sm cursor-text pb-2" 139 - > 140 - {{ nl2br (unwrapText (index $messageParts 1)) }} 141 - </p> 142 - {{ end }} 143 - </div> 144 - </div> 145 - </div> 120 + <button 121 + class="py-1/2 px-1 bg-gray-200 hover:bg-gray-400 rounded" 122 + hx-on:click="this.parentElement.nextElementSibling.classList.toggle('hidden')" 123 + > 124 + <i 125 + class="w-3 h-3" 126 + data-lucide="ellipsis" 127 + ></i> 128 + </button> 129 + {{ end }} 130 + </div> 131 + {{ if gt (len $messageParts) 1 }} 132 + <p 133 + class="hidden mt-1 text-sm cursor-text pb-2" 134 + > 135 + {{ nl2br (unwrapText (index $messageParts 1)) }} 136 + </p> 137 + {{ end }} 138 + </div> 139 + </div> 140 + </div> 146 141 147 - <div class="text-xs text-gray-500"> 148 - <span class="font-mono"> 149 - <a 150 - href="/{{ $.RepoInfo.FullName }}/commit/{{ .Hash.String }}" 151 - class="text-gray-500 no-underline hover:underline" 152 - >{{ slice .Hash.String 0 8 }}</a 153 - > 154 - </span> 155 - <span 156 - class="mx-2 before:content-['·'] before:select-none" 157 - ></span> 158 - <span> 159 - <a 160 - href="mailto:{{ .Author.Email }}" 161 - class="text-gray-500 no-underline hover:underline" 162 - >{{ .Author.Name }}</a 163 - > 164 - </span> 165 - <div 166 - class="inline-block px-1 select-none after:content-['·']" 167 - ></div> 168 - <span>{{ timeFmt .Author.When }}</span> 169 - </div> 170 - </div> 171 - </div> 142 + <div class="text-xs text-gray-500"> 143 + <span class="font-mono"> 144 + <a 145 + href="/{{ $.RepoInfo.FullName }}/commit/{{ .Hash.String }}" 146 + class="text-gray-500 no-underline hover:underline" 147 + >{{ slice .Hash.String 0 8 }}</a 148 + > 149 + </span> 150 + <span 151 + class="mx-2 before:content-['·'] before:select-none" 152 + ></span> 153 + <span> 154 + <a 155 + href="mailto:{{ .Author.Email }}" 156 + class="text-gray-500 no-underline hover:underline" 157 + >{{ .Author.Name }}</a 158 + > 159 + </span> 160 + <div 161 + class="inline-block px-1 select-none after:content-['·']" 162 + ></div> 163 + <span>{{ timeFmt .Author.When }}</span> 164 + </div> 165 + </div> 172 166 {{ end }} 173 167 </div> 174 168 </div>
+5 -3
appview/pages/templates/repo/log.html
··· 41 41 <main> 42 42 <div id="commit-log" class="flex-1 relative"> 43 43 <div class="absolute left-8 top-0 bottom-0 w-px bg-gray-300"></div> 44 - {{ range .Commits }} 44 + {{ $end := length .Commits }} 45 + {{ $commits := subslice .Commits 1 $end }} 46 + {{ range $commits }} 45 47 <div class="flex flex-row justify-between items-center"> 46 48 <div 47 49 class="relative w-full px-4 py-4 mt-5 hover:bg-gray-50 rounded-sm bg-white" ··· 112 114 <div class="flex justify-end mt-4 gap-2"> 113 115 {{ if gt .Page 1 }} 114 116 <a 115 - class="btn flex items-center gap-2 no-underline" 117 + class="btn flex items-center gap-2 no-underline hover:no-underline" 116 118 hx-boost="true" 117 119 onclick="window.location.href = window.location.pathname + '?page={{ sub .Page 1 }}'" 118 120 > ··· 125 127 126 128 {{ if eq $commits_len 30 }} 127 129 <a 128 - class="btn flex items-center gap-2 no-underline" 130 + class="btn flex items-center gap-2 no-underline hover:no-underline" 129 131 hx-boost="true" 130 132 onclick="window.location.href = window.location.pathname + '?page={{ add .Page 1 }}'" 131 133 >
+3 -3
appview/pages/templates/user/profile.html
··· 1 1 {{ define "title" }}{{ or .UserHandle .UserDid }}{{ end }} 2 2 3 3 {{ define "content" }} 4 - <div class="flex "> 4 + <div class="flex"> 5 5 <h1 class="pb-1"> 6 6 {{ didOrHandle .UserDid .UserHandle }} 7 7 </h1> ··· 26 26 <div class="inline-block px-1 select-none after:content-['·']"></div> 27 27 <span>{{ .ProfileStats.Following }} following</span> 28 28 </div> 29 - <p class="text-xs font-bold py-2">REPOS</p> 29 + <p class="text-sm font-bold py-2">REPOS</p> 30 30 <div id="repos" class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6"> 31 31 {{ range .Repos }} 32 32 <div ··· 49 49 <p>This user does not have any repos yet.</p> 50 50 {{ end }} 51 51 </div> 52 - <p class="text-xs font-bold py-2">COLLABORATING ON</p> 52 + <p class="text-sm font-bold py-2">COLLABORATING ON</p> 53 53 <div id="collaborating" class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6"> 54 54 {{ range .CollaboratingRepos }} 55 55 <div
-3
appview/state/middleware.go
··· 147 147 func ResolveIdent(s *State) Middleware { 148 148 return func(next http.Handler) http.Handler { 149 149 return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 150 - start := time.Now() 151 150 didOrHandle := chi.URLParam(req, "user") 152 151 153 152 id, err := s.resolver.ResolveIdent(req.Context(), didOrHandle) ··· 160 159 161 160 ctx := context.WithValue(req.Context(), "resolvedId", *id) 162 161 163 - elapsed := time.Since(start) 164 - log.Println("Execution time:", elapsed) 165 162 next.ServeHTTP(w, req.WithContext(ctx)) 166 163 }) 167 164 }
+2
appview/state/repo.go
··· 207 207 baseTreeLink := path.Join(f.OwnerDid(), f.RepoName, "tree", ref, treePath) 208 208 baseBlobLink := path.Join(f.OwnerDid(), f.RepoName, "blob", ref, treePath) 209 209 210 + log.Println(result) 211 + 210 212 s.pages.RepoTree(w, pages.RepoTreeParams{ 211 213 LoggedInUser: user, 212 214 BreadCrumbs: breadcrumbs,