Mirror of @tangled.org/core. Running on a Raspberry Pi Zero 2 (Please be gentle).

appview/pages: show mini avatar next to user handle

Signed-off-by: Anirudh Oppiliappan <anirudh@tangled.sh>

authored by anirudh.fi and committed by

Tangled 2762da91 0caa2162

+72 -66
+15 -1
appview/pages/funcmap.go
··· 1 1 package pages 2 2 3 3 import ( 4 + "crypto/hmac" 5 + "crypto/sha256" 6 + "encoding/hex" 4 7 "errors" 5 8 "fmt" 6 9 "html" ··· 22 19 "tangled.sh/tangled.sh/core/appview/pages/markup" 23 20 ) 24 21 25 - func funcMap() template.FuncMap { 22 + func (p *Pages) funcMap() template.FuncMap { 26 23 return template.FuncMap{ 27 24 "split": func(s string) []string { 28 25 return strings.Split(s, "\n") ··· 249 246 u, _ := url.PathUnescape(s) 250 247 return u 251 248 }, 249 + 250 + "tinyAvatar": p.tinyAvatar, 252 251 } 252 + } 253 + 254 + func (p *Pages) tinyAvatar(handle string) string { 255 + handle = strings.TrimPrefix(handle, "@") 256 + secret := p.avatar.SharedSecret 257 + h := hmac.New(sha256.New, []byte(secret)) 258 + h.Write([]byte(handle)) 259 + signature := hex.EncodeToString(h.Sum(nil)) 260 + return fmt.Sprintf("%s/%s/%s?size=tiny", p.avatar.Host, signature, handle) 253 261 } 254 262 255 263 func icon(name string, classes []string) (template.HTML, error) {
+5 -3
appview/pages/pages.go
··· 40 40 41 41 type Pages struct { 42 42 t map[string]*template.Template 43 + avatar config.AvatarConfig 43 44 dev bool 44 45 embedFS embed.FS 45 46 templateDir string // Path to templates on disk for dev mode ··· 58 57 p := &Pages{ 59 58 t: make(map[string]*template.Template), 60 59 dev: config.Core.Dev, 60 + avatar: config.Avatar, 61 61 embedFS: Files, 62 62 rctx: rctx, 63 63 templateDir: "appview/pages", ··· 92 90 name := strings.TrimPrefix(path, "templates/") 93 91 name = strings.TrimSuffix(name, ".html") 94 92 tmpl, err := template.New(name). 95 - Funcs(funcMap()). 93 + Funcs(p.funcMap()). 96 94 ParseFS(p.embedFS, path) 97 95 if err != nil { 98 96 log.Fatalf("setting up fragment: %v", err) ··· 133 131 allPaths = append(allPaths, fragmentPaths...) 134 132 allPaths = append(allPaths, path) 135 133 tmpl, err := template.New(name). 136 - Funcs(funcMap()). 134 + Funcs(p.funcMap()). 137 135 ParseFS(p.embedFS, allPaths...) 138 136 if err != nil { 139 137 return fmt.Errorf("setting up template: %w", err) ··· 187 185 } 188 186 189 187 // Create a new template 190 - tmpl := template.New(name).Funcs(funcMap()) 188 + tmpl := template.New(name).Funcs(p.funcMap()) 191 189 192 190 // Parse layouts 193 191 layoutGlob := filepath.Join(p.templateDir, "templates", "layouts", "*.html")
+21 -23
appview/pages/templates/repo/issues/issue.html
··· 4 4 {{ define "extrameta" }} 5 5 {{ $title := printf "%s &middot; issue #%d &middot; %s" .Issue.Title .Issue.IssueId .RepoInfo.FullName }} 6 6 {{ $url := printf "https://tangled.sh/%s/issues/%d" .RepoInfo.FullName .Issue.IssueId }} 7 - 7 + 8 8 {{ template "repo/fragments/og" (dict "RepoInfo" .RepoInfo "Title" $title "Url" $url) }} 9 9 {{ end }} 10 10 ··· 30 30 {{ i $icon "w-4 h-4 mr-1.5 text-white" }} 31 31 <span class="text-white">{{ .State }}</span> 32 32 </div> 33 - <span class="text-gray-500 dark:text-gray-400 text-sm"> 33 + <span class="text-gray-500 dark:text-gray-400 text-sm flex flex-wrap items-center gap-1"> 34 34 opened by 35 35 {{ $owner := didOrHandle .Issue.OwnerDid .IssueOwnerHandle }} 36 - <a href="/{{ $owner }}" class="no-underline hover:underline" 37 - >{{ $owner }}</a 38 - > 39 - <span class="px-1 select-none before:content-['\00B7']"></span> 36 + {{ template "user/fragments/picHandle" $owner }} 37 + <span class="select-none before:content-['\00B7']"></span> 40 38 <time title="{{ .Issue.Created | longTimeFmt }}"> 41 39 {{ .Issue.Created | timeFmt }} 42 40 </time> ··· 69 71 70 72 {{ define "newComment" }} 71 73 {{ if .LoggedInUser }} 72 - <form 73 - id="comment-form" 74 + <form 75 + id="comment-form" 74 76 hx-post="/{{ .RepoInfo.FullName }}/issues/{{ .Issue.IssueId }}/comment" 75 77 hx-on::after-request="if(event.detail.successful) this.reset()" 76 78 > ··· 88 90 <div id="issue-comment"></div> 89 91 <div id="issue-action" class="error"></div> 90 92 </div> 91 - 93 + 92 94 <div class="flex gap-2 mt-2"> 93 - <button 95 + <button 94 96 id="comment-button" 95 97 hx-post="/{{ .RepoInfo.FullName }}/issues/{{ .Issue.IssueId }}/comment" 96 98 type="submit" ··· 107 109 {{ $isRepoCollaborator := .RepoInfo.Roles.IsCollaborator }} 108 110 {{ $isRepoOwner := .RepoInfo.Roles.IsOwner }} 109 111 {{ if and (or $isIssueAuthor $isRepoCollaborator $isRepoOwner) (eq .State "open") }} 110 - <button 112 + <button 111 113 id="close-button" 112 - type="button" 114 + type="button" 113 115 class="btn flex items-center gap-2" 114 116 hx-indicator="#close-spinner" 115 117 hx-trigger="click" ··· 120 122 {{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }} 121 123 </span> 122 124 </button> 123 - <div 124 - id="close-with-comment" 125 - hx-post="/{{ .RepoInfo.FullName }}/issues/{{ .Issue.IssueId }}/comment" 126 - hx-trigger="click from:#close-button" 125 + <div 126 + id="close-with-comment" 127 + hx-post="/{{ .RepoInfo.FullName }}/issues/{{ .Issue.IssueId }}/comment" 128 + hx-trigger="click from:#close-button" 127 129 hx-disabled-elt="#close-with-comment" 128 130 hx-target="#issue-comment" 129 131 hx-indicator="#close-spinner" ··· 131 133 hx-swap="none" 132 134 > 133 135 </div> 134 - <div 135 - id="close-issue" 136 + <div 137 + id="close-issue" 136 138 hx-disabled-elt="#close-issue" 137 - hx-post="/{{ .RepoInfo.FullName }}/issues/{{ .Issue.IssueId }}/close" 138 - hx-trigger="click from:#close-button" 139 + hx-post="/{{ .RepoInfo.FullName }}/issues/{{ .Issue.IssueId }}/close" 140 + hx-trigger="click from:#close-button" 139 141 hx-target="#issue-action" 140 142 hx-indicator="#close-spinner" 141 143 hx-swap="none" ··· 153 155 }); 154 156 </script> 155 157 {{ else if and (or $isIssueAuthor $isRepoCollaborator $isRepoOwner) (eq .State "closed") }} 156 - <button 157 - type="button" 158 + <button 159 + type="button" 158 160 class="btn flex items-center gap-2" 159 161 hx-post="/{{ .RepoInfo.FullName }}/issues/{{ .Issue.IssueId }}/reopen" 160 162 hx-indicator="#reopen-spinner" ··· 204 206 }); 205 207 </script> 206 208 </div> 207 - </form> 209 + </form> 208 210 {{ else }} 209 211 <div class="bg-white dark:bg-gray-800 rounded drop-shadow-sm py-4 px-4 relative w-fit"> 210 212 <a href="/login" class="underline">login</a> to join the discussion
+5 -5
appview/pages/templates/repo/issues/issues.html
··· 3 3 {{ define "extrameta" }} 4 4 {{ $title := "issues"}} 5 5 {{ $url := printf "https://tangled.sh/%s/issues" .RepoInfo.FullName }} 6 - 6 + 7 7 {{ template "repo/fragments/og" (dict "RepoInfo" .RepoInfo "Title" $title "Url" $url) }} 8 8 {{ end }} 9 9 ··· 49 49 <span class="text-gray-500">#{{ .IssueId }}</span> 50 50 </a> 51 51 </div> 52 - <p class="text-sm text-gray-500 dark:text-gray-400"> 52 + <p class="text-sm text-gray-500 dark:text-gray-400 flex flex-wrap items-center gap-1"> 53 53 {{ $bgColor := "bg-gray-800 dark:bg-gray-700" }} 54 54 {{ $icon := "ban" }} 55 55 {{ $state := "closed" }} ··· 64 64 <span class="text-white dark:text-white">{{ $state }}</span> 65 65 </span> 66 66 67 - <span> 68 - {{ $owner := index $.DidHandleMap .OwnerDid }} 69 - <a href="/{{ $owner }}">{{ $owner }}</a> 67 + <span class="ml-1"> 68 + {{ $owner := index $.DidHandleMap .OwnerDid }} 69 + {{ template "user/fragments/picHandle" $owner }} 70 70 </span> 71 71 72 72 <span class="before:content-['·']">
+2 -4
appview/pages/templates/repo/pulls/fragments/pullHeader.html
··· 26 26 {{ i $icon "w-4 h-4 mr-1.5 text-white" }} 27 27 <span class="text-white">{{ .Pull.State.String }}</span> 28 28 </div> 29 - <span class="text-gray-500 dark:text-gray-400 text-sm"> 29 + <span class="text-gray-500 dark:text-gray-400 text-sm flex flex-wrap items-center gap-1"> 30 30 opened by 31 31 {{ $owner := index $.DidHandleMap .Pull.OwnerDid }} 32 - <a href="/{{ $owner }}" class="no-underline hover:underline" 33 - >{{ $owner }}</a 34 - > 32 + {{ template "user/fragments/picHandle" $owner }} 35 33 <span class="select-none before:content-['\00B7']"></span> 36 34 <time>{{ .Pull.Created | timeFmt }}</time> 37 35
+5 -5
appview/pages/templates/repo/pulls/pulls.html
··· 3 3 {{ define "extrameta" }} 4 4 {{ $title := "pulls"}} 5 5 {{ $url := printf "https://tangled.sh/%s/pulls" .RepoInfo.FullName }} 6 - 6 + 7 7 {{ template "repo/fragments/og" (dict "RepoInfo" .RepoInfo "Title" $title "Url" $url) }} 8 8 {{ end }} 9 9 ··· 54 54 <span class="text-gray-500 dark:text-gray-400">#{{ .PullId }}</span> 55 55 </a> 56 56 </div> 57 - <p class="text-sm text-gray-500 dark:text-gray-400"> 57 + <p class="text-sm text-gray-500 dark:text-gray-400 flex flex-wrap items-center gap-1"> 58 58 {{ $owner := index $.DidHandleMap .OwnerDid }} 59 59 {{ $bgColor := "bg-gray-800 dark:bg-gray-700" }} 60 60 {{ $icon := "ban" }} ··· 75 75 <span class="text-white">{{ .State.String }}</span> 76 76 </span> 77 77 78 - <span> 79 - <a href="/{{ $owner }}" class="dark:text-gray-300">{{ $owner }}</a> 78 + <span class="ml-1"> 79 + {{ template "user/fragments/picHandle" $owner }} 80 80 </span> 81 81 82 - <span class="before:content-['·']"> 82 + <span> 83 83 <time> 84 84 {{ .Created | timeFmt }} 85 85 </time>
+9 -25
appview/pages/templates/timeline.html
··· 60 60 {{ if .Repo }} 61 61 {{ $userHandle := index $.DidHandleMap .Repo.Did }} 62 62 <div class="flex items-center"> 63 - <p class="text-gray-600 dark:text-gray-300"> 64 - <a 65 - href="/{{ $userHandle }}" 66 - class="no-underline hover:underline" 67 - >{{ $userHandle | truncateAt30 }}</a 68 - > 63 + <p class="text-gray-600 dark:text-gray-300 flex flex-wrap items-center gap-2"> 64 + {{ template "user/fragments/picHandle" $userHandle }} 69 65 {{ if .Source }} 70 66 forked 71 67 <a 72 68 href="/{{ index $.DidHandleMap .Source.Did }}/{{ .Source.Name }}" 73 69 class="no-underline hover:underline" 74 70 > 75 - {{ index $.DidHandleMap .Source.Did }}/{{ .Source.Name }} 76 - </a> 71 + {{ index $.DidHandleMap .Source.Did }}/{{ .Source.Name }}</a 72 + > 77 73 to 78 74 <a 79 75 href="/{{ $userHandle }}/{{ .Repo.Name }}" ··· 94 98 {{ $userHandle := index $.DidHandleMap .Follow.UserDid }} 95 99 {{ $subjectHandle := index $.DidHandleMap .Follow.SubjectDid }} 96 100 <div class="flex items-center"> 97 - <p class="text-gray-600 dark:text-gray-300"> 98 - <a 99 - href="/{{ $userHandle }}" 100 - class="no-underline hover:underline" 101 - >{{ $userHandle | truncateAt30 }}</a 102 - > 101 + <p class="text-gray-600 dark:text-gray-300 flex flex-wrap items-center gap-2"> 102 + {{ template "user/fragments/picHandle" $userHandle }} 103 103 followed 104 - <a 105 - href="/{{ $subjectHandle }}" 106 - class="no-underline hover:underline" 107 - >{{ $subjectHandle | truncateAt30 }}</a 108 - > 104 + {{ template "user/fragments/picHandle" $subjectHandle }} 109 105 <time 110 106 class="text-gray-700 dark:text-gray-400 text-xs" 111 107 >{{ .Follow.FollowedAt | timeFmt }}</time ··· 108 120 {{ $starrerHandle := index $.DidHandleMap .Star.StarredByDid }} 109 121 {{ $repoOwnerHandle := index $.DidHandleMap .Star.Repo.Did }} 110 122 <div class="flex items-center"> 111 - <p class="text-gray-600 dark:text-gray-300"> 112 - <a 113 - href="/{{ $starrerHandle }}" 114 - class="no-underline hover:underline" 115 - >{{ $starrerHandle | truncateAt30 }}</a 116 - > 123 + {{ template "user/fragments/picHandle" $starrerHandle }} 124 + <p class="text-gray-600 dark:text-gray-300 flex items-center gap-2"> 117 125 starred 118 126 <a 119 127 href="/{{ $repoOwnerHandle }}/{{ .Star.Repo.Name }}"
+10
appview/pages/templates/user/fragments/picHandle.html
··· 1 + {{ define "user/fragments/picHandle" }} 2 + <a href="/{{ . }}" class="flex items-center"> 3 + <img 4 + src="{{ tinyAvatar . }}" 5 + alt="{{ . }}" 6 + class="rounded-full h-6 w-6 mr-1" 7 + /> 8 + {{ . | truncateAt30 }} 9 + </a> 10 + {{ end }}