···2424 MaxBurrTypeLength = 50
2525 MaxBrewerTypeLength = 100
2626 MaxCommentLength = 1000
2727- MaxCommentGraphemes = 300
2827)
29283029// Visibility controls who can see a piece of profile data.
-27
internal/web/bff/helpers.go
···5151 return fmt.Sprintf("%dm %ds", minutes, remaining)
5252}
53535454-// FormatRating formats a rating as "X/10".
5555-// Returns "N/A" if rating is 0.
5656-func FormatRating(rating int) string {
5757- if rating == 0 {
5858- return "N/A"
5959- }
6060- return fmt.Sprintf("%d/10", rating)
6161-}
6262-6354// FormatBeanRating formats a bean's optional rating as "X/10".
6455// Returns empty string if rating is nil (unrated).
6556func FormatBeanRating(rating *int) string {
···10899// HasTemp returns true if temperature is greater than zero
109100func HasTemp(temp float64) bool {
110101 return temp > 0
111111-}
112112-113113-// HasValue returns true if the int value is greater than zero
114114-func HasValue(val int) bool {
115115- return val > 0
116102}
117103118104// SafeAvatarURL validates and sanitizes avatar URLs to prevent XSS and other attacks.
···187173 }
188174189175 return websiteURL
190190-}
191191-192192-// EscapeJS escapes a string for safe use in JavaScript string literals.
193193-// Handles newlines, quotes, backslashes, and other special characters.
194194-func EscapeJS(s string) string {
195195- // Replace special characters that would break JavaScript strings
196196- s = strings.ReplaceAll(s, "\\", "\\\\") // Must be first
197197- s = strings.ReplaceAll(s, "'", "\\'")
198198- s = strings.ReplaceAll(s, "\"", "\\\"")
199199- s = strings.ReplaceAll(s, "\n", "\\n")
200200- s = strings.ReplaceAll(s, "\r", "\\r")
201201- s = strings.ReplaceAll(s, "\t", "\\t")
202202- return s
203176}
204177205178// FormatISO returns the time formatted as RFC3339 UTC, suitable for HTML datetime attributes.
···11-package components
22-33-// SocialButtonsProps defines properties for the social buttons cluster
44-type SocialButtonsProps struct {
55- // Like button props
66- SubjectURI string // AT-URI of the record being liked
77- SubjectCID string // CID of the record being liked
88- IsLiked bool // Whether the current user has liked this record
99- LikeCount int // Number of likes on this record
1010-1111- // Comment button props
1212- CommentCount int // Number of comments on this record
1313- ShowComment bool // Whether to show the comment button
1414-1515- // Share button props
1616- ShareURL string // URL to share
1717- ShareTitle string // Title for native share dialog
1818- ShareText string // Text for native share dialog
1919-2020- // Display options
2121- ShowLike bool // Whether to show the like button (requires authentication)
2222- IsAuthenticated bool // Whether the user is authenticated (controls click behavior)
2323-}
2424-2525-// SocialButtons renders a cluster of social interaction buttons (like, comment, share)
2626-templ SocialButtons(props SocialButtonsProps) {
2727- <div class="flex items-center gap-2">
2828- if props.ShowLike && props.SubjectURI != "" && props.SubjectCID != "" {
2929- @LikeButton(LikeButtonProps{
3030- SubjectURI: props.SubjectURI,
3131- SubjectCID: props.SubjectCID,
3232- IsLiked: props.IsLiked,
3333- LikeCount: props.LikeCount,
3434- IsAuthenticated: props.IsAuthenticated,
3535- })
3636- }
3737- if props.ShowComment && props.SubjectURI != "" && props.SubjectCID != "" {
3838- @CommentButton(CommentButtonProps{
3939- SubjectURI: props.SubjectURI,
4040- SubjectCID: props.SubjectCID,
4141- CommentCount: props.CommentCount,
4242- IsAuthenticated: props.IsAuthenticated,
4343- })
4444- }
4545- if props.ShareURL != "" {
4646- @ShareButton(ShareButtonProps{
4747- URL: props.ShareURL,
4848- Title: props.ShareTitle,
4949- Text: props.ShareText,
5050- })
5151- }
5252- </div>
5353-}