Search Apps Documentation Source Content File Folder Download Copy Actions Download

utils.gno

3.14 Kb ยท 146 lines
  1package blog
  2
  3import (
  4	"net/url"
  5	"strings"
  6	"time"
  7
  8	"gno.land/p/moul/md"
  9	"gno.land/p/nt/ufmt"
 10)
 11
 12func renderLinks(prefix, kind string, values []string, labelPrefix string) string {
 13	links := make([]string, len(values))
 14	for i, val := range values {
 15		label := labelPrefix + val
 16		href := prefix + ":" + kind + "/" + val
 17		links[i] = md.Link(label, href)
 18	}
 19	return strings.Join(links, ", ") + "\n"
 20}
 21
 22func hasField(fields []string, match string) bool {
 23	for _, field := range fields {
 24		if field == match {
 25			return true
 26		}
 27	}
 28	return false
 29}
 30
 31func extractListingDisplay(key string, value interface{}) (label string, count int) {
 32	if strings.Contains(key, "::") {
 33		parts := strings.Split(key, "::")
 34		if len(parts) > 1 {
 35			return parts[1], value.(int)
 36		}
 37	}
 38	return key, value.(int)
 39}
 40
 41func renderBreadcrumb(prefix, path string) string {
 42	segments := strings.Split(path, "/")
 43	base := prefix
 44	var parts []string
 45
 46	if prefix != "" {
 47		parts = append(parts, md.Link(prefix, prefix))
 48	}
 49
 50	for i, seg := range segments {
 51		if seg == "" {
 52			continue
 53		}
 54		base += ":" + seg
 55		label := seg
 56		if i == len(segments)-1 {
 57			label = seg
 58			parts = append(parts, label)
 59			break
 60		}
 61		parts = append(parts, md.Link(label, base))
 62	}
 63	return strings.Join(parts, "/")
 64}
 65
 66func formatTime(t time.Time, format string) string {
 67	switch format {
 68	case "relative":
 69		return calculateTimeSince(t)
 70	case "short":
 71		return t.Format("02 Jan 2006")
 72	case "full":
 73		return t.Format("January 02, 2006 at 3:04 PM")
 74	default:
 75		return calculateTimeSince(t)
 76	}
 77}
 78
 79func calculateTimeSince(t time.Time) string {
 80	duration := time.Since(t)
 81	switch {
 82	case duration < time.Minute:
 83		return "just now"
 84	case duration < time.Hour:
 85		if duration < 2*time.Minute {
 86			return "a minute ago"
 87		}
 88		return ufmt.Sprintf("%d minutes ago", int(duration.Minutes()))
 89	case duration < 24*time.Hour:
 90		if duration < 2*time.Hour {
 91			return "an hour ago"
 92		}
 93		return ufmt.Sprintf("%d hours ago", int(duration.Hours()))
 94	case duration < 7*24*time.Hour:
 95		if duration < 2*24*time.Hour {
 96			return "a day ago"
 97		}
 98		return ufmt.Sprintf("%d days ago", int(duration.Hours()/24))
 99	case duration < 30*24*time.Hour:
100		if duration < 2*7*24*time.Hour {
101			return "a week ago"
102		}
103		return ufmt.Sprintf("%d weeks ago", int(duration.Hours()/(7*24)))
104	default:
105		if duration < 2*30*24*time.Hour {
106			return "a month ago"
107		}
108		return ufmt.Sprintf("%d months ago", int(duration.Hours()/(30*24)))
109	}
110}
111
112func tryParseFlexibleDate(s string) *time.Time {
113	formats := []string{
114		time.RFC3339, // rfc3339 format
115		"2006-01-02", // date only
116		"2006-01",    // year and month
117		"2006",       // year only
118	}
119	for _, format := range formats {
120		if t, err := time.Parse(format, s); err == nil {
121			return &t
122		}
123	}
124	return nil
125}
126
127func titleToSlug(title string) string {
128	slug := strings.ToLower(title)
129	slug = strings.ReplaceAll(slug, " ", "-")
130	slug = strings.Map(func(r rune) rune {
131		if r >= 'a' && r <= 'z' || r >= '0' && r <= '9' || r == '-' {
132			return r
133		}
134		return -1
135	}, slug)
136	return slug
137}
138
139func handleSlug(slug, title string) string {
140	if slug == "" {
141		slug = titleToSlug(title)
142	}
143	once := url.PathEscape(slug)
144	twice := url.PathEscape(once)
145	return twice
146}