+28
-8
appview/pages/funcmap.go
+28
-8
appview/pages/funcmap.go
···
22
22
chromahtml "github.com/alecthomas/chroma/v2/formatters/html"
23
23
"github.com/alecthomas/chroma/v2/lexers"
24
24
"github.com/alecthomas/chroma/v2/styles"
25
-
"github.com/bluesky-social/indigo/atproto/syntax"
26
25
"github.com/dustin/go-humanize"
27
26
"github.com/go-enry/go-enry/v2"
28
27
"github.com/yuin/goldmark"
29
28
"tangled.org/core/appview/filetree"
29
+
"tangled.org/core/appview/models"
30
30
"tangled.org/core/appview/pages/markup"
31
31
"tangled.org/core/crypto"
32
32
)
···
71
71
}
72
72
73
73
return identity.Handle.String()
74
+
},
75
+
"ownerSlashRepo": func(repo *models.Repo) string {
76
+
ownerId, err := p.resolver.ResolveIdent(context.Background(), repo.Did)
77
+
if err != nil {
78
+
return repo.DidSlashRepo()
79
+
}
80
+
handle := ownerId.Handle
81
+
if handle != "" && !handle.IsInvalidHandle() {
82
+
return string(handle) + "/" + repo.Name
83
+
}
84
+
return repo.DidSlashRepo()
74
85
},
75
86
"truncateAt30": func(s string) string {
76
87
if len(s) <= 30 {
···
140
151
}
141
152
142
153
return b
143
-
},
144
-
"didOrHandle": func(did, handle string) string {
145
-
if handle != "" && handle != syntax.HandleInvalid.String() {
146
-
return handle
147
-
} else {
148
-
return did
149
-
}
150
154
},
151
155
"assoc": func(values ...string) ([][]string, error) {
152
156
if len(values)%2 != 0 {
···
379
383
}
380
384
}
381
385
386
+
func (p *Pages) resolveDid(did string) string {
387
+
identity, err := p.resolver.ResolveIdent(context.Background(), did)
388
+
389
+
if err != nil {
390
+
return did
391
+
}
392
+
393
+
if identity.Handle.IsInvalidHandle() {
394
+
return "handle.invalid"
395
+
}
396
+
397
+
return identity.Handle.String()
398
+
}
399
+
382
400
func (p *Pages) AvatarUrl(handle, size string) string {
383
401
handle = strings.TrimPrefix(handle, "@")
402
+
403
+
handle = p.resolveDid(handle)
384
404
385
405
secret := p.avatar.SharedSecret
386
406
h := hmac.New(sha256.New, []byte(secret))
+2
-4
appview/pages/pages.go
+2
-4
appview/pages/pages.go
···
492
492
493
493
type ProfileCard struct {
494
494
UserDid string
495
-
UserHandle string
496
495
FollowStatus models.FollowStatus
497
496
Punchcard *models.Punchcard
498
497
Profile *models.Profile
···
841
840
}
842
841
843
842
type Collaborator struct {
844
-
Did string
845
-
Handle string
846
-
Role string
843
+
Did string
844
+
Role string
847
845
}
848
846
849
847
type RepoSettingsParams struct {
+2
-2
appview/pages/repoinfo/repoinfo.go
+2
-2
appview/pages/repoinfo/repoinfo.go
···
9
9
"tangled.org/core/appview/state/userutil"
10
10
)
11
11
12
-
func (r RepoInfo) Owner() string {
12
+
func (r RepoInfo) owner() string {
13
13
if r.OwnerHandle != "" {
14
14
return r.OwnerHandle
15
15
} else {
···
18
18
}
19
19
20
20
func (r RepoInfo) FullName() string {
21
-
return path.Join(r.Owner(), r.Name)
21
+
return path.Join(r.owner(), r.Name)
22
22
}
23
23
24
24
func (r RepoInfo) OwnerWithoutAt() string {
+8
-7
appview/pages/templates/layouts/profilebase.html
+8
-7
appview/pages/templates/layouts/profilebase.html
···
1
-
{{ define "title" }}{{ or .Card.UserHandle .Card.UserDid }}{{ end }}
1
+
{{ define "title" }}{{ resolve .Card.UserDid }}{{ end }}
2
2
3
3
{{ define "extrameta" }}
4
-
{{ $avatarUrl := fullAvatar .Card.UserHandle }}
5
-
<meta property="og:title" content="{{ or .Card.UserHandle .Card.UserDid }}" />
4
+
{{ $handle := resolve .Card.UserDid }}
5
+
{{ $avatarUrl := fullAvatar $handle }}
6
+
<meta property="og:title" content="{{ $handle }}" />
6
7
<meta property="og:type" content="profile" />
7
-
<meta property="og:url" content="https://tangled.org/{{ or .Card.UserHandle .Card.UserDid }}?tab={{ .Active }}" />
8
-
<meta property="og:description" content="{{ or .Card.Profile.Description .Card.UserHandle .Card.UserDid }}" />
8
+
<meta property="og:url" content="https://tangled.org/{{ $handle }}?tab={{ .Active }}" />
9
+
<meta property="og:description" content="{{ or .Card.Profile.Description $handle }}" />
9
10
<meta property="og:image" content="{{ $avatarUrl }}" />
10
11
<meta property="og:image:width" content="512" />
11
12
<meta property="og:image:height" content="512" />
12
13
13
14
<meta name="twitter:card" content="summary" />
14
-
<meta name="twitter:title" content="{{ or .Card.UserHandle .Card.UserDid }}" />
15
-
<meta name="twitter:description" content="{{ or .Card.Profile.Description .Card.UserHandle .Card.UserDid }}" />
15
+
<meta name="twitter:title" content="{{ $handle }}" />
16
+
<meta name="twitter:description" content="{{ or .Card.Profile.Description $handle }}" />
16
17
<meta name="twitter:image" content="{{ $avatarUrl }}" />
17
18
{{ end }}
18
19
+1
-1
appview/pages/templates/repo/empty.html
+1
-1
appview/pages/templates/repo/empty.html
···
35
35
36
36
<p><span class="{{$bullet}}">1</span>First, generate a new <a href="https://git-scm.com/book/en/v2/Git-on-the-Server-Generating-Your-SSH-Public-Key" class="underline">SSH key pair</a>.</p>
37
37
<p><span class="{{$bullet}}">2</span>Then add the public key to your account from the <a href="/settings" class="underline">settings</a> page.</p>
38
-
<p><span class="{{$bullet}}">3</span>Configure your remote to <code>git@{{ $knot | stripPort }}:{{ .RepoInfo.OwnerHandle }}/{{ .RepoInfo.Name }}</code></p>
38
+
<p><span class="{{$bullet}}">3</span>Configure your remote to <code>git@{{ $knot | stripPort }}:{{ resolve .RepoInfo.OwnerDid }}/{{ .RepoInfo.Name }}</code></p>
39
39
<p><span class="{{$bullet}}">4</span>Push!</p>
40
40
</div>
41
41
</div>
+3
-2
appview/pages/templates/repo/fragments/cloneDropdown.html
+3
-2
appview/pages/templates/repo/fragments/cloneDropdown.html
···
43
43
44
44
<!-- SSH Clone -->
45
45
<div class="mb-3">
46
+
{{ $repoOwnerHandle := resolve .RepoInfo.OwnerDid }}
46
47
<label class="block text-xs font-medium text-gray-700 dark:text-gray-300 mb-1">SSH</label>
47
48
<div class="flex items-center border border-gray-300 dark:border-gray-600 rounded">
48
49
<code
49
50
class="flex-1 px-3 py-2 text-sm bg-gray-50 dark:bg-gray-700 text-gray-900 dark:text-gray-100 rounded-l select-all cursor-pointer whitespace-nowrap overflow-x-auto"
50
51
onclick="window.getSelection().selectAllChildren(this)"
51
-
data-url="git@{{ $knot | stripPort }}:{{ .RepoInfo.OwnerHandle }}/{{ .RepoInfo.Name }}"
52
-
>git@{{ $knot | stripPort }}:{{ .RepoInfo.OwnerHandle }}/{{ .RepoInfo.Name }}</code>
52
+
data-url="git@{{ $knot | stripPort }}:{{ $repoOwnerHandle }}/{{ .RepoInfo.Name }}"
53
+
>git@{{ $knot | stripPort }}:{{ $repoOwnerHandle }}/{{ .RepoInfo.Name }}</code>
53
54
<button
54
55
onclick="copyToClipboard(this, this.previousElementSibling.getAttribute('data-url'))"
55
56
class="px-3 py-2 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 border-l border-gray-300 dark:border-gray-600"
+5
-4
appview/pages/templates/repo/settings/access.html
+5
-4
appview/pages/templates/repo/settings/access.html
···
29
29
{{ template "addCollaboratorButton" . }}
30
30
{{ end }}
31
31
{{ range .Collaborators }}
32
+
{{ $handle := resolve .Did }}
32
33
<div class="border border-gray-200 dark:border-gray-700 rounded p-4">
33
34
<div class="flex items-center gap-3">
34
35
<img
35
-
src="{{ fullAvatar .Handle }}"
36
-
alt="{{ .Handle }}"
36
+
src="{{ fullAvatar $handle }}"
37
+
alt="{{ $handle }}"
37
38
class="rounded-full h-10 w-10 border border-gray-300 dark:border-gray-600 flex-shrink-0"/>
38
39
39
40
<div class="flex-1 min-w-0">
40
-
<a href="/{{ .Handle }}" class="block truncate">
41
-
{{ didOrHandle .Did .Handle }}
41
+
<a href="/{{ $handle }}" class="block truncate">
42
+
{{ $handle }}
42
43
</a>
43
44
<p class="text-sm text-gray-500 dark:text-gray-400">{{ .Role }}</p>
44
45
</div>
+6
-5
appview/pages/templates/strings/dashboard.html
+6
-5
appview/pages/templates/strings/dashboard.html
···
1
-
{{ define "title" }}strings by {{ or .Card.UserHandle .Card.UserDid }}{{ end }}
1
+
{{ define "title" }}strings by {{ resolve .Card.UserDid }}{{ end }}
2
2
3
3
{{ define "extrameta" }}
4
-
<meta property="og:title" content="{{ or .Card.UserHandle .Card.UserDid }}" />
4
+
{{ $handle := resolve .Card.UserDid }}
5
+
<meta property="og:title" content="{{ $handle }}" />
5
6
<meta property="og:type" content="profile" />
6
-
<meta property="og:url" content="https://tangled.org/{{ or .Card.UserHandle .Card.UserDid }}" />
7
-
<meta property="og:description" content="{{ or .Card.Profile.Description .Card.UserHandle .Card.UserDid }}" />
7
+
<meta property="og:url" content="https://tangled.org/{{ $handle }}" />
8
+
<meta property="og:description" content="{{ or .Card.Profile.Description $handle }}" />
8
9
{{ end }}
9
10
10
11
···
35
36
{{ $s := index . 1 }}
36
37
<div class="py-4 px-6 drop-shadow-sm rounded bg-white dark:bg-gray-800">
37
38
<div class="font-medium dark:text-white flex gap-2 items-center">
38
-
<a href="/strings/{{ or $root.Card.UserHandle $root.Card.UserDid }}/{{ $s.Rkey }}">{{ $s.Filename }}</a>
39
+
<a href="/strings/{{ resolve $root.Card.UserDid }}/{{ $s.Rkey }}">{{ $s.Filename }}</a>
39
40
</div>
40
41
{{ with $s.Description }}
41
42
<div class="text-gray-600 dark:text-gray-300 text-sm">
+3
-3
appview/pages/templates/strings/string.html
+3
-3
appview/pages/templates/strings/string.html
···
1
-
{{ define "title" }}{{ .String.Filename }} · by {{ didOrHandle .Owner.DID.String .Owner.Handle.String }}{{ end }}
1
+
{{ define "title" }}{{ .String.Filename }} · by {{ resolve .Owner.DID.String }}{{ end }}
2
2
3
3
{{ define "extrameta" }}
4
-
{{ $ownerId := didOrHandle .Owner.DID.String .Owner.Handle.String }}
4
+
{{ $ownerId := resolve .Owner.DID.String }}
5
5
<meta property="og:title" content="{{ .String.Filename }} · by {{ $ownerId }}" />
6
6
<meta property="og:type" content="object" />
7
7
<meta property="og:url" content="https://tangled.org/strings/{{ $ownerId }}/{{ .String.Rkey }}" />
···
9
9
{{ end }}
10
10
11
11
{{ define "content" }}
12
-
{{ $ownerId := didOrHandle .Owner.DID.String .Owner.Handle.String }}
12
+
{{ $ownerId := resolve .Owner.DID.String }}
13
13
<section id="string-header" class="mb-4 py-2 px-6 dark:text-white">
14
14
<div class="text-lg flex items-center justify-between">
15
15
<div>
+1
-1
appview/pages/templates/user/followers.html
+1
-1
appview/pages/templates/user/followers.html
+1
-1
appview/pages/templates/user/following.html
+1
-1
appview/pages/templates/user/following.html
+1
-1
appview/pages/templates/user/fragments/profileCard.html
+1
-1
appview/pages/templates/user/fragments/profileCard.html
···
1
1
{{ define "user/fragments/profileCard" }}
2
-
{{ $userIdent := didOrHandle .UserDid .UserHandle }}
2
+
{{ $userIdent := resolve .UserDid }}
3
3
<div class="grid grid-cols-3 md:grid-cols-1 gap-1 items-center">
4
4
<div id="avatar" class="col-span-1 flex justify-center items-center">
5
5
<div class="w-3/4 aspect-square relative">
+2
-2
appview/pages/templates/user/overview.html
+2
-2
appview/pages/templates/user/overview.html
···
1
-
{{ define "title" }}{{ or .Card.UserHandle .Card.UserDid }}{{ end }}
1
+
{{ define "title" }}{{ resolve .Card.UserDid }}{{ end }}
2
2
3
3
{{ define "profileContent" }}
4
4
<div id="all-repos" class="md:col-span-4 order-2 md:order-2">
···
224
224
{{ define "ownRepos" }}
225
225
<div>
226
226
<div class="text-sm font-bold px-2 pb-4 dark:text-white flex items-center gap-2">
227
-
<a href="/@{{ or $.Card.UserHandle $.Card.UserDid }}?tab=repos"
227
+
<a href="/{{ resolve $.Card.UserDid }}?tab=repos"
228
228
class="flex text-black dark:text-white items-center gap-2 no-underline hover:no-underline group">
229
229
<span>PINNED REPOS</span>
230
230
</a>
+1
-1
appview/pages/templates/user/repos.html
+1
-1
appview/pages/templates/user/repos.html
+1
-1
appview/pages/templates/user/starred.html
+1
-1
appview/pages/templates/user/starred.html
+2
-2
appview/pages/templates/user/strings.html
+2
-2
appview/pages/templates/user/strings.html
···
1
-
{{ define "title" }}{{ or .Card.UserHandle .Card.UserDid }} · strings {{ end }}
1
+
{{ define "title" }}{{ resolve .Card.UserDid }} · strings {{ end }}
2
2
3
3
{{ define "profileContent" }}
4
4
<div id="all-strings" class="md:col-span-8 order-2 md:order-2">
···
23
23
{{ $s := index . 1 }}
24
24
<div class="py-4 px-6 rounded bg-white dark:bg-gray-800">
25
25
<div class="font-medium dark:text-white flex gap-2 items-center">
26
-
<a href="/strings/{{ or $root.Card.UserHandle $root.Card.UserDid }}/{{ $s.Rkey }}">{{ $s.Filename }}</a>
26
+
<a href="/strings/{{ resolve $root.Card.UserDid }}/{{ $s.Rkey }}">{{ $s.Filename }}</a>
27
27
</div>
28
28
{{ with $s.Description }}
29
29
<div class="text-gray-600 dark:text-gray-300 text-sm">
-14
appview/reporesolver/resolver.go
-14
appview/reporesolver/resolver.go
···
113
113
114
114
c := pages.Collaborator{
115
115
Did: did,
116
-
Handle: "",
117
116
Role: role,
118
117
}
119
118
collaborators = append(collaborators, c)
120
-
}
121
-
122
-
// populate all collborators with handles
123
-
identsToResolve := make([]string, len(collaborators))
124
-
for i, collab := range collaborators {
125
-
identsToResolve[i] = collab.Did
126
-
}
127
-
128
-
resolvedIdents := f.rr.idResolver.ResolveIdents(ctx, identsToResolve)
129
-
for i, resolved := range resolvedIdents {
130
-
if resolved != nil {
131
-
collaborators[i].Handle = resolved.Handle.String()
132
-
}
133
119
}
134
120
135
121
return collaborators, nil
+5
-6
appview/state/profile.go
+5
-6
appview/state/profile.go
···
96
96
97
97
return &pages.ProfileCard{
98
98
UserDid: did,
99
-
UserHandle: ident.Handle.String(),
100
99
Profile: profile,
101
100
FollowStatus: followStatus,
102
101
Stats: pages.ProfileStats{
···
119
118
s.pages.Error500(w)
120
119
return
121
120
}
122
-
l = l.With("profileDid", profile.UserDid, "profileHandle", profile.UserHandle)
121
+
l = l.With("profileDid", profile.UserDid)
123
122
124
123
repos, err := db.GetRepos(
125
124
s.db,
···
180
179
s.pages.Error500(w)
181
180
return
182
181
}
183
-
l = l.With("profileDid", profile.UserDid, "profileHandle", profile.UserHandle)
182
+
l = l.With("profileDid", profile.UserDid)
184
183
185
184
repos, err := db.GetRepos(
186
185
s.db,
···
209
208
s.pages.Error500(w)
210
209
return
211
210
}
212
-
l = l.With("profileDid", profile.UserDid, "profileHandle", profile.UserHandle)
211
+
l = l.With("profileDid", profile.UserDid)
213
212
214
213
stars, err := db.GetRepoStars(s.db, 0, db.FilterEq("did", profile.UserDid))
215
214
if err != nil {
···
238
237
s.pages.Error500(w)
239
238
return
240
239
}
241
-
l = l.With("profileDid", profile.UserDid, "profileHandle", profile.UserHandle)
240
+
l = l.With("profileDid", profile.UserDid)
242
241
243
242
strings, err := db.GetStrings(s.db, 0, db.FilterEq("did", profile.UserDid))
244
243
if err != nil {
···
270
269
if err != nil {
271
270
return nil, err
272
271
}
273
-
l = l.With("profileDid", profile.UserDid, "profileHandle", profile.UserHandle)
272
+
l = l.With("profileDid", profile.UserDid)
274
273
275
274
loggedInUser := s.oauth.GetUser(r)
276
275
params := FollowsPageParams{