Coffee journaling on ATProto (alpha) alpha.arabica.social
coffee
17
fork

Configure Feed

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

fix: click to visit comment in notifications

+57 -19
+55 -5
internal/handlers/notifications.go
··· 6 6 "strings" 7 7 8 8 "arabica/internal/atproto" 9 + "arabica/internal/models" 9 10 "arabica/internal/web/pages" 10 11 11 12 "github.com/rs/zerolog/log" ··· 38 39 item := pages.NotificationItem{ 39 40 Notification: notif, 40 41 Link: resolveNotificationLink(notif.SubjectURI), 42 + ActionText: notifActionText(notif), 41 43 } 42 44 43 45 profile, err := h.feedIndex.GetProfile(r.Context(), notif.ActorDID) ··· 88 90 http.Redirect(w, r, "/notifications", http.StatusSeeOther) 89 91 } 90 92 93 + // collectionURLPath maps AT Protocol collection NSIDs to their URL path prefixes. 94 + var collectionURLPath = map[string]string{ 95 + atproto.NSIDBrew: "/brews/", 96 + atproto.NSIDBean: "/beans/", 97 + atproto.NSIDRoaster: "/roasters/", 98 + atproto.NSIDGrinder: "/grinders/", 99 + atproto.NSIDBrewer: "/brewers/", 100 + atproto.NSIDRecipe: "/recipes/", 101 + } 102 + 103 + // collectionDisplayName maps AT Protocol collection NSIDs to human-readable names. 104 + var collectionDisplayName = map[string]string{ 105 + atproto.NSIDBrew: "brew", 106 + atproto.NSIDBean: "bean", 107 + atproto.NSIDRoaster: "roaster", 108 + atproto.NSIDGrinder: "grinder", 109 + atproto.NSIDBrewer: "brewer", 110 + atproto.NSIDRecipe: "recipe", 111 + } 112 + 91 113 // resolveNotificationLink converts a SubjectURI (AT-URI) to a local page URL. 92 - // All notification types store a brew AT-URI as SubjectURI. 93 114 // Format: at://did:plc:xxx/social.arabica.alpha.brew/rkey -> /brews/rkey?owner=did:plc:xxx 94 115 func resolveNotificationLink(subjectURI string) string { 95 116 if !strings.HasPrefix(subjectURI, "at://") { ··· 106 127 collection := parts[1] 107 128 rkey := parts[2] 108 129 109 - switch collection { 110 - case atproto.NSIDBrew: 111 - return fmt.Sprintf("/brews/%s?owner=%s", rkey, did) 130 + if prefix, ok := collectionURLPath[collection]; ok { 131 + return fmt.Sprintf("%s%s?owner=%s", prefix, rkey, did) 132 + } 133 + return "" 134 + } 135 + 136 + // resolveNotificationEntityName returns the display name for the entity in a SubjectURI. 137 + func resolveNotificationEntityName(subjectURI string) string { 138 + if !strings.HasPrefix(subjectURI, "at://") { 139 + return "content" 140 + } 141 + rest := subjectURI[5:] 142 + parts := strings.SplitN(rest, "/", 3) 143 + if len(parts) < 3 { 144 + return "content" 145 + } 146 + if name, ok := collectionDisplayName[parts[1]]; ok { 147 + return name 148 + } 149 + return "content" 150 + } 151 + 152 + // notifActionText returns human-readable action text for a notification. 153 + func notifActionText(notif models.Notification) string { 154 + entity := resolveNotificationEntityName(notif.SubjectURI) 155 + switch notif.Type { 156 + case models.NotificationLike: 157 + return "liked your " + entity 158 + case models.NotificationComment: 159 + return "commented on your " + entity 160 + case models.NotificationCommentReply: 161 + return "replied to your comment" 112 162 default: 113 - return "" 163 + return "interacted with your " + entity 114 164 } 115 165 }
+2 -14
internal/web/pages/notifications.templ
··· 19 19 ActorDisplayName string 20 20 ActorAvatar string 21 21 Link string // Resolved local URL (e.g. /brews/rkey?owner=did) 22 + ActionText string // e.g. "commented on your brew" 22 23 } 23 24 24 25 templ Notifications(layout *components.LayoutData, props NotificationsProps) { ··· 85 86 { notifActorName(notif) } 86 87 </span> 87 88 { " " } 88 - { notifActionText(notif.Type) } 89 + { notif.ActionText } 89 90 </p> 90 91 <p class="text-xs text-brown-400 mt-1"> 91 92 { bff.FormatTimeAgo(notif.CreatedAt) } ··· 108 109 return notif.ActorHandle 109 110 } 110 111 return notif.ActorDID 111 - } 112 - 113 - func notifActionText(t models.NotificationType) string { 114 - switch t { 115 - case models.NotificationLike: 116 - return "liked your brew" 117 - case models.NotificationComment: 118 - return "commented on your brew" 119 - case models.NotificationCommentReply: 120 - return "replied to your comment" 121 - default: 122 - return "interacted with your content" 123 - } 124 112 } 125 113 126 114 // notifClickURL returns the URL to navigate to when clicking a notification.