This commit is contained in:
Tomas Dvorak
2025-11-02 21:31:00 +01:00
parent b9cea0cd77
commit 087f30e82c
130 changed files with 20104 additions and 34330 deletions
+212
View File
@@ -0,0 +1,212 @@
package helpers
import (
"fmt"
"strings"
"time"
)
// CommentsHelpers provides utility functions for comments system
// FormatCommentAge returns a human-readable age string
func FormatCommentAge(createdAt time.Time) string {
now := time.Now()
diff := now.Sub(createdAt)
seconds := int(diff.Seconds())
minutes := seconds / 60
hours := minutes / 60
days := hours / 24
switch {
case seconds < 60:
return "právě teď"
case minutes < 2:
return "před minutou"
case minutes < 5:
return fmt.Sprintf("před %d minutami", minutes)
case minutes < 60:
return fmt.Sprintf("před %d min", minutes)
case hours < 2:
return "před hodinou"
case hours < 5:
return fmt.Sprintf("před %d hodinami", hours)
case hours < 24:
return fmt.Sprintf("před %d hod", hours)
case days < 2:
return "včera"
case days < 7:
return fmt.Sprintf("před %d dny", days)
case days < 30:
weeks := days / 7
if weeks == 1 {
return "před týdnem"
}
return fmt.Sprintf("před %d týdny", weeks)
case days < 365:
months := days / 30
if months == 1 {
return "před měsícem"
}
return fmt.Sprintf("před %d měsíci", months)
default:
years := days / 365
if years == 1 {
return "před rokem"
}
return fmt.Sprintf("před %d lety", years)
}
}
// GetBanDurationText returns formatted ban duration text
func GetBanDurationText(until *time.Time) string {
if until == nil {
return "Trvale"
}
now := time.Now()
if until.Before(now) {
return "Vypršelo"
}
diff := until.Sub(now)
hours := int(diff.Hours())
days := hours / 24
if days > 0 {
if days == 1 {
return "1 den"
}
return fmt.Sprintf("%d dnů", days)
}
if hours > 0 {
if hours == 1 {
return "1 hodina"
}
return fmt.Sprintf("%d hodin", hours)
}
minutes := int(diff.Minutes())
if minutes > 0 {
if minutes == 1 {
return "1 minuta"
}
return fmt.Sprintf("%d minut", minutes)
}
return "< 1 minuta"
}
// GetReactionEmoji returns emoji for reaction type
func GetReactionEmoji(reactionType string) string {
emojis := map[string]string{
"like": "👍",
"heart": "❤️",
"smile": "😊",
"laugh": "😂",
"thumbs_up": "👍",
"thumbs_down": "👎",
"sad": "😢",
"angry": "😠",
}
if emoji, ok := emojis[reactionType]; ok {
return emoji
}
return "👍"
}
// GetReactionDisplayName returns localized name for reaction type
func GetReactionDisplayName(reactionType string) string {
names := map[string]string{
"like": "Líbí se",
"heart": "Srdíčko",
"smile": "Úsměv",
"laugh": "Smích",
"thumbs_up": "Palec nahoru",
"thumbs_down": "Palec dolů",
"sad": "Smutné",
"angry": "Naštvaný",
}
if name, ok := names[reactionType]; ok {
return name
}
return reactionType
}
// ShortenComment returns shortened version of comment for previews
func ShortenComment(content string, maxLength int) string {
if maxLength <= 0 {
maxLength = 100
}
c := strings.TrimSpace(content)
if len(c) <= maxLength {
return c
}
// Try to cut at word boundary
shortened := c[:maxLength]
lastSpace := strings.LastIndex(shortened, " ")
if lastSpace > maxLength/2 {
shortened = c[:lastSpace]
}
return shortened + "..."
}
// GetTargetTypeDisplayName returns localized target type name
func GetTargetTypeDisplayName(targetType string) string {
names := map[string]string{
"article": "Článek",
"event": "Aktivita",
"gallery_album": "Galerie",
"youtube_video": "Video",
}
if name, ok := names[targetType]; ok {
return name
}
return targetType
}
// GetCommentStatusColor returns color scheme for comment status
func GetCommentStatusColor(status string) string {
if status == "visible" {
return "green"
}
return "red"
}
// CountReactions returns total reaction count
func CountReactions(reactions map[string]int) int {
total := 0
for _, count := range reactions {
total += count
}
return total
}
// GetTopReaction returns the most popular reaction type
func GetTopReaction(reactions map[string]int) string {
if len(reactions) == 0 {
return ""
}
maxType := ""
maxCount := 0
for rType, count := range reactions {
if count > maxCount {
maxCount = count
maxType = rType
}
}
return maxType
}
// IsSpammy returns whether comment is likely spam based on score
func IsSpammy(spamScore float32) bool {
return spamScore > 0.5
}
// GetSpamScoreColor returns color scheme for spam score
func GetSpamScoreColor(spamScore float32) string {
switch {
case spamScore < 0.3:
return "green"
case spamScore < 0.6:
return "yellow"
default:
return "red"
}
}
+180
View File
@@ -0,0 +1,180 @@
package helpers
import (
"fmt"
"math"
)
// EngagementHelpers provides utility functions for engagement system
// ComputeLevelProgress returns the progress percentage within current level
func ComputeLevelProgress(xp int64, level int) (int, int64, int64) {
if level < 1 {
level = 1
}
// Total XP needed to reach level L: 50 * (L-1) * L
totalToLevel := int64(50 * (level - 1) * level)
nextIncrement := int64(100 * level)
inLevel := xp - totalToLevel
if inLevel < 0 {
inLevel = 0
}
percentage := 0
if nextIncrement > 0 {
percentage = int(math.Floor(float64(inLevel) / float64(nextIncrement) * 100))
if percentage > 100 {
percentage = 100
}
if percentage < 0 {
percentage = 0
}
}
return percentage, inLevel, nextIncrement
}
// GetLevelTitle returns a descriptive title for a level
func GetLevelTitle(level int) string {
switch {
case level >= 100:
return "Legenda"
case level >= 80:
return "Mistr"
case level >= 60:
return "Expert"
case level >= 40:
return "Veterán"
case level >= 25:
return "Zkušený fanoušek"
case level >= 15:
return "Oddaný příznivec"
case level >= 10:
return "Aktivní člen"
case level >= 5:
return "Nováček"
default:
return "Začátečník"
}
}
// GetLevelColor returns a color scheme for level badge
func GetLevelColor(level int) string {
switch {
case level >= 80:
return "gold"
case level >= 60:
return "purple"
case level >= 40:
return "blue"
case level >= 20:
return "cyan"
case level >= 10:
return "green"
default:
return "gray"
}
}
// FormatPoints formats points with thousands separator
func FormatPoints(points int64) string {
if points < 1000 {
return fmt.Sprintf("%d", points)
}
if points < 1000000 {
return fmt.Sprintf("%.1fk", float64(points)/1000)
}
return fmt.Sprintf("%.1fM", float64(points)/1000000)
}
// GetNextLevelXP returns XP needed for next level
func GetNextLevelXP(currentXP int64, currentLevel int) int64 {
totalToLevel := int64(50 * (currentLevel - 1) * currentLevel)
nextIncrement := int64(100 * currentLevel)
return (totalToLevel + nextIncrement) - currentXP
}
// GetRewardTypeDisplayName returns localized display name for reward type
func GetRewardTypeDisplayName(rewardType string) string {
names := map[string]string{
"avatar_static": "Avatar (statický)",
"avatar_animated": "Avatar (animovaný)",
"avatar_upload_unlock": "Odemknutí vlastního avataru",
"merch_coupon": "Slevový kupon",
"merch_physical": "Fyzické zboží",
"merch_digital": "Digitální produkt",
"custom": "Vlastní",
}
if name, ok := names[rewardType]; ok {
return name
}
return rewardType
}
// GetPointsReasonDisplayName returns localized display name for transaction reason
func GetPointsReasonDisplayName(reason string) string {
names := map[string]string{
"comment_create": "Nový komentář",
"comment_reacted": "Reakce na komentář",
"poll_vote": "Hlasování v anketě",
"newsletter_subscribe": "Odběr newsletteru",
"redeem": "Uplatnění odměny",
"redeem_refund": "Vrácení bodů",
"admin_adjust": "Manuální úprava",
}
if name, ok := names[reason]; ok {
return name
}
// Achievement reasons start with "achievement:"
if len(reason) > 12 && reason[:12] == "achievement:" {
return "Úspěch odemknut"
}
return reason
}
// GetRedemptionStatusDisplayName returns localized display name for redemption status
func GetRedemptionStatusDisplayName(status string) string {
names := map[string]string{
"pending": "Čeká na vyřízení",
"approved": "Schváleno",
"rejected": "Zamítnuto",
"fulfilled": "Vydáno",
}
if name, ok := names[status]; ok {
return name
}
return status
}
// GetRedemptionStatusColor returns color scheme for redemption status
func GetRedemptionStatusColor(status string) string {
colors := map[string]string{
"pending": "yellow",
"approved": "blue",
"rejected": "red",
"fulfilled": "green",
}
if color, ok := colors[status]; ok {
return color
}
return "gray"
}
// CalculateBadgeLevel returns badge tier based on points/level
func CalculateBadgeLevel(level int) string {
switch {
case level >= 80:
return "diamond"
case level >= 60:
return "platinum"
case level >= 40:
return "gold"
case level >= 20:
return "silver"
case level >= 10:
return "bronze"
default:
return "none"
}
}