+15
-1
appview/pages/funcmap.go
+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"
···
19
22
"tangled.sh/tangled.sh/core/appview/pages/markup"
20
23
)
21
24
22
-
func funcMap() template.FuncMap {
25
+
func (p *Pages) funcMap() template.FuncMap {
23
26
return template.FuncMap{
24
27
"split": func(s string) []string {
25
28
return strings.Split(s, "\n")
···
246
249
u, _ := url.PathUnescape(s)
247
250
return u
248
251
},
252
+
253
+
"tinyAvatar": p.tinyAvatar,
249
254
}
255
+
}
256
+
257
+
func (p *Pages) tinyAvatar(handle string) string {
258
+
handle = strings.TrimPrefix(handle, "@")
259
+
secret := p.avatar.SharedSecret
260
+
h := hmac.New(sha256.New, []byte(secret))
261
+
h.Write([]byte(handle))
262
+
signature := hex.EncodeToString(h.Sum(nil))
263
+
return fmt.Sprintf("%s/%s/%s?size=tiny", p.avatar.Host, signature, handle)
250
264
}
251
265
252
266
func icon(name string, classes []string) (template.HTML, error) {
+5
-3
appview/pages/pages.go
+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
···
57
58
p := &Pages{
58
59
t: make(map[string]*template.Template),
59
60
dev: config.Core.Dev,
61
+
avatar: config.Avatar,
60
62
embedFS: Files,
61
63
rctx: rctx,
62
64
templateDir: "appview/pages",
···
90
92
name := strings.TrimPrefix(path, "templates/")
91
93
name = strings.TrimSuffix(name, ".html")
92
94
tmpl, err := template.New(name).
93
-
Funcs(funcMap()).
95
+
Funcs(p.funcMap()).
94
96
ParseFS(p.embedFS, path)
95
97
if err != nil {
96
98
log.Fatalf("setting up fragment: %v", err)
···
131
133
allPaths = append(allPaths, fragmentPaths...)
132
134
allPaths = append(allPaths, path)
133
135
tmpl, err := template.New(name).
134
-
Funcs(funcMap()).
136
+
Funcs(p.funcMap()).
135
137
ParseFS(p.embedFS, allPaths...)
136
138
if err != nil {
137
139
return fmt.Errorf("setting up template: %w", err)
···
185
187
}
186
188
187
189
// Create a new template
188
-
tmpl := template.New(name).Funcs(funcMap())
190
+
tmpl := template.New(name).Funcs(p.funcMap())
189
191
190
192
// Parse layouts
191
193
layoutGlob := filepath.Join(p.templateDir, "templates", "layouts", "*.html")
+21
-23
appview/pages/templates/repo/issues/issue.html
+21
-23
appview/pages/templates/repo/issues/issue.html
···
4
4
{{ define "extrameta" }}
5
5
{{ $title := printf "%s · issue #%d · %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>
···
71
69
72
70
{{ define "newComment" }}
73
71
{{ if .LoggedInUser }}
74
-
<form
75
-
id="comment-form"
72
+
<form
73
+
id="comment-form"
76
74
hx-post="/{{ .RepoInfo.FullName }}/issues/{{ .Issue.IssueId }}/comment"
77
75
hx-on::after-request="if(event.detail.successful) this.reset()"
78
76
>
···
90
88
<div id="issue-comment"></div>
91
89
<div id="issue-action" class="error"></div>
92
90
</div>
93
-
91
+
94
92
<div class="flex gap-2 mt-2">
95
-
<button
93
+
<button
96
94
id="comment-button"
97
95
hx-post="/{{ .RepoInfo.FullName }}/issues/{{ .Issue.IssueId }}/comment"
98
96
type="submit"
···
109
107
{{ $isRepoCollaborator := .RepoInfo.Roles.IsCollaborator }}
110
108
{{ $isRepoOwner := .RepoInfo.Roles.IsOwner }}
111
109
{{ if and (or $isIssueAuthor $isRepoCollaborator $isRepoOwner) (eq .State "open") }}
112
-
<button
110
+
<button
113
111
id="close-button"
114
-
type="button"
112
+
type="button"
115
113
class="btn flex items-center gap-2"
116
114
hx-indicator="#close-spinner"
117
115
hx-trigger="click"
···
122
120
{{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }}
123
121
</span>
124
122
</button>
125
-
<div
126
-
id="close-with-comment"
127
-
hx-post="/{{ .RepoInfo.FullName }}/issues/{{ .Issue.IssueId }}/comment"
128
-
hx-trigger="click from:#close-button"
123
+
<div
124
+
id="close-with-comment"
125
+
hx-post="/{{ .RepoInfo.FullName }}/issues/{{ .Issue.IssueId }}/comment"
126
+
hx-trigger="click from:#close-button"
129
127
hx-disabled-elt="#close-with-comment"
130
128
hx-target="#issue-comment"
131
129
hx-indicator="#close-spinner"
···
133
131
hx-swap="none"
134
132
>
135
133
</div>
136
-
<div
137
-
id="close-issue"
134
+
<div
135
+
id="close-issue"
138
136
hx-disabled-elt="#close-issue"
139
-
hx-post="/{{ .RepoInfo.FullName }}/issues/{{ .Issue.IssueId }}/close"
140
-
hx-trigger="click from:#close-button"
137
+
hx-post="/{{ .RepoInfo.FullName }}/issues/{{ .Issue.IssueId }}/close"
138
+
hx-trigger="click from:#close-button"
141
139
hx-target="#issue-action"
142
140
hx-indicator="#close-spinner"
143
141
hx-swap="none"
···
155
153
});
156
154
</script>
157
155
{{ else if and (or $isIssueAuthor $isRepoCollaborator $isRepoOwner) (eq .State "closed") }}
158
-
<button
159
-
type="button"
156
+
<button
157
+
type="button"
160
158
class="btn flex items-center gap-2"
161
159
hx-post="/{{ .RepoInfo.FullName }}/issues/{{ .Issue.IssueId }}/reopen"
162
160
hx-indicator="#reopen-spinner"
···
206
204
});
207
205
</script>
208
206
</div>
209
-
</form>
207
+
</form>
210
208
{{ else }}
211
209
<div class="bg-white dark:bg-gray-800 rounded drop-shadow-sm py-4 px-4 relative w-fit">
212
210
<a href="/login" class="underline">login</a> to join the discussion
+5
-5
appview/pages/templates/repo/issues/issues.html
+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
+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
+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
+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 }}"
···
98
94
{{ $userHandle := index $.DidHandleMap .Follow.UserDid }}
99
95
{{ $subjectHandle := index $.DidHandleMap .Follow.SubjectDid }}
100
96
<div class="flex items-center">
101
-
<p class="text-gray-600 dark:text-gray-300">
102
-
<a
103
-
href="/{{ $userHandle }}"
104
-
class="no-underline hover:underline"
105
-
>{{ $userHandle | truncateAt30 }}</a
106
-
>
97
+
<p class="text-gray-600 dark:text-gray-300 flex flex-wrap items-center gap-2">
98
+
{{ template "user/fragments/picHandle" $userHandle }}
107
99
followed
108
-
<a
109
-
href="/{{ $subjectHandle }}"
110
-
class="no-underline hover:underline"
111
-
>{{ $subjectHandle | truncateAt30 }}</a
112
-
>
100
+
{{ template "user/fragments/picHandle" $subjectHandle }}
113
101
<time
114
102
class="text-gray-700 dark:text-gray-400 text-xs"
115
103
>{{ .Follow.FollowedAt | timeFmt }}</time
···
120
108
{{ $starrerHandle := index $.DidHandleMap .Star.StarredByDid }}
121
109
{{ $repoOwnerHandle := index $.DidHandleMap .Star.Repo.Did }}
122
110
<div class="flex items-center">
123
-
<p class="text-gray-600 dark:text-gray-300">
124
-
<a
125
-
href="/{{ $starrerHandle }}"
126
-
class="no-underline hover:underline"
127
-
>{{ $starrerHandle | truncateAt30 }}</a
128
-
>
111
+
{{ template "user/fragments/picHandle" $starrerHandle }}
112
+
<p class="text-gray-600 dark:text-gray-300 flex items-center gap-2">
129
113
starred
130
114
<a
131
115
href="/{{ $repoOwnerHandle }}/{{ .Star.Repo.Name }}"
+10
appview/pages/templates/user/fragments/picHandle.html
+10
appview/pages/templates/user/fragments/picHandle.html