Monorepo for Tangled tangled.org
6

Configure Feed

Select the types of activity you want to include in your feed.

appview/pages: use comment at-uri for internal identifier

still use `#comment-{rkey}` for UI, but internal identifier should
always use full at-uri.

Signed-off-by: Seongmin Lee <git@boltless.me>

author
Seongmin Lee
date (Jun 21, 2026, 8:28 PM +0900) commit ea346668 parent 549f5fdd change-id qvpxptxz
+35 -27
+9
appview/pages/funcmap.go
··· 458 458 "safeUrl": func(s string) template.URL { 459 459 return template.URL(s) 460 460 }, 461 + "sanitizeAtUri": func(u syntax.ATURI) string { 462 + s := strings.ToLower(u.String()) 463 + s = strings.TrimPrefix(s, "at://") 464 + s = strings.NewReplacer(":", "-", "/", "-", ".", "-").Replace(s) 465 + return s 466 + }, 467 + "safeCSS": func(s string) template.CSS { 468 + return template.CSS(s) 469 + }, 461 470 "tinyAvatar": func(handle string) string { 462 471 return p.AvatarUrl(handle, "tiny") 463 472 },
+5 -6
appview/pages/pages.go
··· 1313 1313 } 1314 1314 1315 1315 type ThreadReactionFragmentParams struct { 1316 - Kind models.ReactionKind 1317 - Count int 1318 - Users []string 1319 - IsReacted bool 1320 - CommentRkey string 1321 - SubjectUri string 1316 + Kind models.ReactionKind 1317 + Count int 1318 + Users []string 1319 + IsReacted bool 1320 + SubjectUri string 1322 1321 } 1323 1322 1324 1323 func (p *Pages) ThreadReactionFragment(w io.Writer, params ThreadReactionFragmentParams) error {
+1 -1
appview/pages/templates/fragments/comment/commentHeader.html
··· 13 13 {{ if not .Comment.Deleted }} 14 14 <div class="ml-auto flex items-center gap-4 opacity-0 group-hover/comment:opacity-100"> 15 15 {{ if .LoggedInUser }} 16 - {{ template "repo/fragments/reactionsPopup" (dict "CommentRkey" (string .Comment.Rkey) "Scope" "header") }} 16 + {{ template "repo/fragments/reactionsPopup" (dict "SubjectUri" .Comment.AtUri "Scope" "header") }} 17 17 {{ end }} 18 18 {{ if $isCommentOwner }} 19 19 {{ if not .Comment.IsLegacy }}
+3 -1
appview/pages/templates/repo/fragments/reaction.html
··· 1 1 {{ define "repo/fragments/reaction" }} 2 2 <button 3 + data-reaction-kind="{{ .Kind }}" 4 + data-subject-uri="{{ .SubjectUri | string | safeUrl }}" 3 5 class="flex justify-center items-center min-h-8 rounded border 4 6 leading-4 px-2 gap-1.5 text-sm relative group 5 7 {{ if eq .Count 0 }} ··· 28 30 hx-post="/react?kind={{ .Kind }}&subject-uri={{ .SubjectUri | urlquery }}" 29 31 {{ end }} 30 32 hx-swap="outerHTML" 31 - hx-trigger="click from:(.reactBtn-{{ .CommentRkey }}-{{ .Kind }}, closest button)" 33 + hx-trigger="click from:button[data-subject-uri='{{ .SubjectUri }}'][data-reaction-kind='{{ .Kind }}']" 32 34 hx-disabled-elt="this" 33 35 > 34 36 <span class="inline group-[.htmx-request]:hidden">{{ .Kind }}</span>
+1 -3
appview/pages/templates/repo/fragments/reactions.html
··· 4 4 {{- $userReacted := .UserReacted -}} 5 5 {{- $threadAt := .ThreadAt -}} 6 6 {{- $parts := splitOn (string $threadAt) "/" -}} 7 - {{- $commentRkey := index $parts (sub (len $parts) 1) -}} 8 7 9 8 <input name="subject-uri" type="hidden" value="{{ $threadAt }}"> 10 9 ··· 16 15 "Count" $reactionData.Count 17 16 "IsReacted" (index $userReacted $kind) 18 17 "Users" $reactionData.Users 19 - "CommentRkey" $commentRkey 20 18 "SubjectUri" $threadAt) }} 21 19 {{ end }} 22 20 23 21 {{ if or (not .HideIfEmpty) (gt (len $reactions) 0) }} 24 - {{ template "repo/fragments/reactionsPopup" (dict "CommentRkey" $commentRkey "Scope" "body") }} 22 + {{ template "repo/fragments/reactionsPopup" (dict "SubjectUri" $threadAt "Scope" "body") }} 25 23 {{ end }} 26 24 27 25 </div>
+6 -4
appview/pages/templates/repo/fragments/reactionsPopup.html
··· 1 1 {{ define "repo/fragments/reactionsPopup" }} 2 - {{ $id := printf "%s-%s" (string .CommentRkey) .Scope }} 2 + {{ $id := printf "%s-%s" (sanitizeAtUri .SubjectUri) .Scope }} 3 3 <button 4 4 type="button" 5 5 popovertarget="reactions-popover-{{ $id }}" 6 - style="anchor-name: --reactions-anchor-{{ $id }}" 6 + style="anchor-name: --reactions-anchor-{{ $id | safeCSS }}" 7 7 class=" 8 8 flex justify-center items-center rounded 9 9 text-gray-400 dark:text-gray-400 ··· 15 15 <div 16 16 id="reactions-popover-{{ $id }}" 17 17 popover 18 - style="position-anchor: --reactions-anchor-{{ $id }};" 18 + style="position-anchor: --reactions-anchor-{{ $id | safeCSS }};" 19 19 class="[&:popover-open]:grid grid-cols-4 [position-area:bottom_center] justify-self-center w-max inset-auto mt-1 rounded bg-white dark:bg-gray-800 dark:text-white border border-gray-200 dark:border-gray-700 shadow-lg" 20 20 > 21 21 {{ range $kind := const.OrderedReactionKinds }} ··· 23 23 type="button" 24 24 popovertarget="reactions-popover-{{ $id }}" 25 25 popovertargetaction="hide" 26 - class="reactBtn-{{ $.CommentRkey }}-{{ $kind }} btn-flat px-3 text-base hover:bg-gray-100 dark:hover:bg-gray-700" 26 + data-reaction-kind="{{ $kind }}" 27 + data-subject-uri="{{ $.SubjectUri | string | safeUrl }}" 28 + class="btn-flat px-3 text-base hover:bg-gray-100 dark:hover:bg-gray-700" 27 29 > 28 30 {{ $kind }} 29 31 </button>
+10 -12
appview/state/reaction.go
··· 81 81 l.Info("created atproto record", "uri", resp.Uri) 82 82 83 83 s.pages.ThreadReactionFragment(w, pages.ThreadReactionFragmentParams{ 84 - Kind: reactionKind, 85 - Count: reactionMap[reactionKind].Count, 86 - Users: reactionMap[reactionKind].Users, 87 - IsReacted: true, 88 - CommentRkey: subjectUri.RecordKey().String(), 89 - SubjectUri: subject, 84 + Kind: reactionKind, 85 + Count: reactionMap[reactionKind].Count, 86 + Users: reactionMap[reactionKind].Users, 87 + IsReacted: true, 88 + SubjectUri: subject, 90 89 }) 91 90 92 91 return ··· 121 120 } 122 121 123 122 s.pages.ThreadReactionFragment(w, pages.ThreadReactionFragmentParams{ 124 - Kind: reactionKind, 125 - Count: reactionMap[reactionKind].Count, 126 - Users: reactionMap[reactionKind].Users, 127 - IsReacted: false, 128 - CommentRkey: subjectUri.RecordKey().String(), 129 - SubjectUri: subject, 123 + Kind: reactionKind, 124 + Count: reactionMap[reactionKind].Count, 125 + Users: reactionMap[reactionKind].Users, 126 + IsReacted: false, 127 + SubjectUri: subject, 130 128 }) 131 129 132 130 return