forked from tangled.org/core
Monorepo for Tangled — https://tangled.org

fix first commit duplication in log view

Changed files
+177 -159
appview
+107
appview/pages/funcMap.go
···
+
package pages
+
+
import (
+
"fmt"
+
"html"
+
"html/template"
+
"reflect"
+
"strings"
+
+
"github.com/dustin/go-humanize"
+
)
+
+
func funcMap() template.FuncMap {
+
return template.FuncMap{
+
"split": func(s string) []string {
+
return strings.Split(s, "\n")
+
},
+
"splitOn": func(s, sep string) []string {
+
return strings.Split(s, sep)
+
},
+
"add": func(a, b int) int {
+
return a + b
+
},
+
"sub": func(a, b int) int {
+
return a - b
+
},
+
"cond": func(cond interface{}, a, b string) string {
+
if cond == nil {
+
return b
+
}
+
+
if boolean, ok := cond.(bool); boolean && ok {
+
return a
+
}
+
+
return b
+
},
+
"didOrHandle": func(did, handle string) string {
+
if handle != "" {
+
return fmt.Sprintf("@%s", handle)
+
} else {
+
return did
+
}
+
},
+
"assoc": func(values ...string) ([][]string, error) {
+
if len(values)%2 != 0 {
+
return nil, fmt.Errorf("invalid assoc call, must have an even number of arguments")
+
}
+
pairs := make([][]string, 0)
+
for i := 0; i < len(values); i += 2 {
+
pairs = append(pairs, []string{values[i], values[i+1]})
+
}
+
return pairs, nil
+
},
+
"append": func(s []string, values ...string) []string {
+
s = append(s, values...)
+
return s
+
},
+
"timeFmt": humanize.Time,
+
"byteFmt": humanize.Bytes,
+
"length": func(slice interface{}) int {
+
v := reflect.ValueOf(slice)
+
if v.Kind() == reflect.Slice || v.Kind() == reflect.Array {
+
return v.Len()
+
}
+
return 0
+
},
+
"splitN": func(s, sep string, n int) []string {
+
return strings.SplitN(s, sep, n)
+
},
+
"escapeHtml": func(s string) template.HTML {
+
if s == "" {
+
return template.HTML("<br>")
+
}
+
return template.HTML(s)
+
},
+
"unescapeHtml": func(s string) string {
+
return html.UnescapeString(s)
+
},
+
"nl2br": func(text string) template.HTML {
+
return template.HTML(strings.Replace(template.HTMLEscapeString(text), "\n", "<br>", -1))
+
},
+
"unwrapText": func(text string) string {
+
paragraphs := strings.Split(text, "\n\n")
+
+
for i, p := range paragraphs {
+
lines := strings.Split(p, "\n")
+
paragraphs[i] = strings.Join(lines, " ")
+
}
+
+
return strings.Join(paragraphs, "\n\n")
+
},
+
"sequence": func(n int) []struct{} {
+
return make([]struct{}, n)
+
},
+
"subslice": func(slice interface{}, start, end int) interface{} {
+
v := reflect.ValueOf(slice)
+
if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
+
return nil
+
}
+
if start < 0 || start > v.Len() || end > v.Len() || start > end {
+
return nil
+
}
+
return v.Slice(start, end).Interface()
+
},
+
}
+
}
-84
appview/pages/pages.go
···
"bytes"
"embed"
"fmt"
-
"html"
"html/template"
"io"
"io/fs"
···
chromahtml "github.com/alecthomas/chroma/v2/formatters/html"
"github.com/alecthomas/chroma/v2/lexers"
"github.com/alecthomas/chroma/v2/styles"
-
"github.com/dustin/go-humanize"
"github.com/sotangled/tangled/appview/auth"
"github.com/sotangled/tangled/appview/db"
"github.com/sotangled/tangled/types"
···
type Pages struct {
t map[string]*template.Template
-
}
-
-
func funcMap() template.FuncMap {
-
return template.FuncMap{
-
"split": func(s string) []string {
-
return strings.Split(s, "\n")
-
},
-
"splitOn": func(s, sep string) []string {
-
return strings.Split(s, sep)
-
},
-
"add": func(a, b int) int {
-
return a + b
-
},
-
"sub": func(a, b int) int {
-
return a - b
-
},
-
"cond": func(cond interface{}, a, b string) string {
-
if cond == nil {
-
return b
-
}
-
-
if boolean, ok := cond.(bool); boolean && ok {
-
return a
-
}
-
-
return b
-
},
-
"didOrHandle": func(did, handle string) string {
-
if handle != "" {
-
return fmt.Sprintf("@%s", handle)
-
} else {
-
return did
-
}
-
},
-
"assoc": func(values ...string) ([][]string, error) {
-
if len(values)%2 != 0 {
-
return nil, fmt.Errorf("invalid assoc call, must have an even number of arguments")
-
}
-
pairs := make([][]string, 0)
-
for i := 0; i < len(values); i += 2 {
-
pairs = append(pairs, []string{values[i], values[i+1]})
-
}
-
return pairs, nil
-
},
-
"append": func(s []string, values ...string) []string {
-
s = append(s, values...)
-
return s
-
},
-
"timeFmt": humanize.Time,
-
"byteFmt": humanize.Bytes,
-
"length": func(v []string) int {
-
return len(v)
-
},
-
"splitN": func(s, sep string, n int) []string {
-
return strings.SplitN(s, sep, n)
-
},
-
"escapeHtml": func(s string) template.HTML {
-
if s == "" {
-
return template.HTML("<br>")
-
}
-
return template.HTML(s)
-
},
-
"unescapeHtml": func(s string) string {
-
return html.UnescapeString(s)
-
},
-
"nl2br": func(text string) template.HTML {
-
return template.HTML(strings.Replace(template.HTMLEscapeString(text), "\n", "<br>", -1))
-
},
-
"unwrapText": func(text string) string {
-
paragraphs := strings.Split(text, "\n\n")
-
-
for i, p := range paragraphs {
-
lines := strings.Split(p, "\n")
-
paragraphs[i] = strings.Join(lines, " ")
-
}
-
-
return strings.Join(paragraphs, "\n\n")
-
},
-
"sequence": func(n int) []struct{} {
-
return make([]struct{}, n)
-
},
-
}
}
func NewPages() *Pages {
+2 -2
appview/pages/templates/layouts/repobase.html
···
{{ define "content" }}
<section id="repo-header" class="mb-4 p-2">
-
<p class="text-lg font-bold">
+
<p class="text-lg">
<a href="/{{ .RepoInfo.OwnerWithAt }}">{{ .RepoInfo.OwnerWithAt }}</a>
<span class="select-none">/</span>
-
<a href="/{{ .RepoInfo.FullName }}">{{ .RepoInfo.Name }}</a>
+
<a href="/{{ .RepoInfo.FullName }}" class="font-bold">{{ .RepoInfo.Name }}</a>
</p>
<span>
{{ if .RepoInfo.Description }}
+58 -64
appview/pages/templates/repo/index.html
···
</a>
</div>
-
<div class="flex gap-4">
+
<div class="flex gap-2">
<div id="file-tree" class="w-3/5 pr-2 border-r border-gray-200">
{{ $containerstyle := "py-1" }}
{{ $linkstyle := "no-underline hover:underline" }}
···
<div id="commit-log" class="flex-1">
{{ range .Commits }}
-
<div class="flex flex-row items-center">
-
<i
-
class="w-5 h-5 text-gray-400 align-top"
-
data-lucide="git-commit-horizontal"
-
></i>
-
<div class="relative px-4 py-4">
-
<div id="commit-message">
-
{{ $messageParts := splitN .Message "\n\n" 2 }}
-
<div class="text-base cursor-pointer">
-
<div>
-
<div>
-
<a
-
href="/{{ $.RepoInfo.FullName }}/commit/{{ .Hash.String }}"
-
class="inline no-underline hover:underline"
-
>{{ index $messageParts 0 }}</a
-
>
-
{{ if gt (len $messageParts) 1 }}
+
<div class="relative px-2 pb-8">
+
<div id="commit-message">
+
{{ $messageParts := splitN .Message "\n\n" 2 }}
+
<div class="text-base cursor-pointer">
+
<div>
+
<div>
+
<a
+
href="/{{ $.RepoInfo.FullName }}/commit/{{ .Hash.String }}"
+
class="inline no-underline hover:underline"
+
>{{ index $messageParts 0 }}</a
+
>
+
{{ if gt (len $messageParts) 1 }}
-
<button
-
class="py-1/2 px-1 bg-gray-200 hover:bg-gray-400 rounded"
-
hx-on:click="this.parentElement.nextElementSibling.classList.toggle('hidden')"
-
>
-
<i
-
class="w-3 h-3"
-
data-lucide="ellipsis"
-
></i>
-
</button>
-
{{ end }}
-
</div>
-
{{ if gt (len $messageParts) 1 }}
-
<p
-
class="hidden mt-1 text-sm cursor-text pb-2"
-
>
-
{{ nl2br (unwrapText (index $messageParts 1)) }}
-
</p>
-
{{ end }}
-
</div>
-
</div>
-
</div>
+
<button
+
class="py-1/2 px-1 bg-gray-200 hover:bg-gray-400 rounded"
+
hx-on:click="this.parentElement.nextElementSibling.classList.toggle('hidden')"
+
>
+
<i
+
class="w-3 h-3"
+
data-lucide="ellipsis"
+
></i>
+
</button>
+
{{ end }}
+
</div>
+
{{ if gt (len $messageParts) 1 }}
+
<p
+
class="hidden mt-1 text-sm cursor-text pb-2"
+
>
+
{{ nl2br (unwrapText (index $messageParts 1)) }}
+
</p>
+
{{ end }}
+
</div>
+
</div>
+
</div>
-
<div class="text-xs text-gray-500">
-
<span class="font-mono">
-
<a
-
href="/{{ $.RepoInfo.FullName }}/commit/{{ .Hash.String }}"
-
class="text-gray-500 no-underline hover:underline"
-
>{{ slice .Hash.String 0 8 }}</a
-
>
-
</span>
-
<span
-
class="mx-2 before:content-['·'] before:select-none"
-
></span>
-
<span>
-
<a
-
href="mailto:{{ .Author.Email }}"
-
class="text-gray-500 no-underline hover:underline"
-
>{{ .Author.Name }}</a
-
>
-
</span>
-
<div
-
class="inline-block px-1 select-none after:content-['·']"
-
></div>
-
<span>{{ timeFmt .Author.When }}</span>
-
</div>
-
</div>
-
</div>
+
<div class="text-xs text-gray-500">
+
<span class="font-mono">
+
<a
+
href="/{{ $.RepoInfo.FullName }}/commit/{{ .Hash.String }}"
+
class="text-gray-500 no-underline hover:underline"
+
>{{ slice .Hash.String 0 8 }}</a
+
>
+
</span>
+
<span
+
class="mx-2 before:content-['·'] before:select-none"
+
></span>
+
<span>
+
<a
+
href="mailto:{{ .Author.Email }}"
+
class="text-gray-500 no-underline hover:underline"
+
>{{ .Author.Name }}</a
+
>
+
</span>
+
<div
+
class="inline-block px-1 select-none after:content-['·']"
+
></div>
+
<span>{{ timeFmt .Author.When }}</span>
+
</div>
+
</div>
{{ end }}
</div>
</div>
+5 -3
appview/pages/templates/repo/log.html
···
<main>
<div id="commit-log" class="flex-1 relative">
<div class="absolute left-8 top-0 bottom-0 w-px bg-gray-300"></div>
-
{{ range .Commits }}
+
{{ $end := length .Commits }}
+
{{ $commits := subslice .Commits 1 $end }}
+
{{ range $commits }}
<div class="flex flex-row justify-between items-center">
<div
class="relative w-full px-4 py-4 mt-5 hover:bg-gray-50 rounded-sm bg-white"
···
<div class="flex justify-end mt-4 gap-2">
{{ if gt .Page 1 }}
<a
-
class="btn flex items-center gap-2 no-underline"
+
class="btn flex items-center gap-2 no-underline hover:no-underline"
hx-boost="true"
onclick="window.location.href = window.location.pathname + '?page={{ sub .Page 1 }}'"
>
···
{{ if eq $commits_len 30 }}
<a
-
class="btn flex items-center gap-2 no-underline"
+
class="btn flex items-center gap-2 no-underline hover:no-underline"
hx-boost="true"
onclick="window.location.href = window.location.pathname + '?page={{ add .Page 1 }}'"
>
+3 -3
appview/pages/templates/user/profile.html
···
{{ define "title" }}{{ or .UserHandle .UserDid }}{{ end }}
{{ define "content" }}
-
<div class="flex ">
+
<div class="flex">
<h1 class="pb-1">
{{ didOrHandle .UserDid .UserHandle }}
</h1>
···
<div class="inline-block px-1 select-none after:content-['·']"></div>
<span>{{ .ProfileStats.Following }} following</span>
</div>
-
<p class="text-xs font-bold py-2">REPOS</p>
+
<p class="text-sm font-bold py-2">REPOS</p>
<div id="repos" class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
{{ range .Repos }}
<div
···
<p>This user does not have any repos yet.</p>
{{ end }}
</div>
-
<p class="text-xs font-bold py-2">COLLABORATING ON</p>
+
<p class="text-sm font-bold py-2">COLLABORATING ON</p>
<div id="collaborating" class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
{{ range .CollaboratingRepos }}
<div
-3
appview/state/middleware.go
···
func ResolveIdent(s *State) Middleware {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
-
start := time.Now()
didOrHandle := chi.URLParam(req, "user")
id, err := s.resolver.ResolveIdent(req.Context(), didOrHandle)
···
ctx := context.WithValue(req.Context(), "resolvedId", *id)
-
elapsed := time.Since(start)
-
log.Println("Execution time:", elapsed)
next.ServeHTTP(w, req.WithContext(ctx))
})
}
+2
appview/state/repo.go
···
baseTreeLink := path.Join(f.OwnerDid(), f.RepoName, "tree", ref, treePath)
baseBlobLink := path.Join(f.OwnerDid(), f.RepoName, "blob", ref, treePath)
+
log.Println(result)
+
s.pages.RepoTree(w, pages.RepoTreeParams{
LoggedInUser: user,
BreadCrumbs: breadcrumbs,