mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-04 02:32:57 +00:00
dev day #80
This commit is contained in:
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user