Monorepo for Tangled tangled.org
4

Configure Feed

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

at icy/lqyotq 2.3 kB View raw
1package timeline 2 3import ( 4 "bytes" 5 "io/fs" 6 "log/slog" 7 "net/http" 8 "strings" 9 "time" 10 11 "github.com/adrg/frontmatter" 12 "github.com/go-chi/chi/v5" 13 "tangled.org/core/appview/config" 14 "tangled.org/core/appview/db" 15 "tangled.org/core/appview/oauth" 16 "tangled.org/core/appview/pages" 17) 18 19type postMeta struct { 20 Slug string `yaml:"slug"` 21 Title string `yaml:"title"` 22 Subtitle string `yaml:"subtitle"` 23 Date string `yaml:"date"` 24 Draft bool `yaml:"draft"` 25} 26 27type Timeline struct { 28 oauth *oauth.OAuth 29 db *db.DB 30 config *config.Config 31 pages *pages.Pages 32 logger *slog.Logger 33 recentPosts []pages.BlogPost 34} 35 36func New( 37 oauth *oauth.OAuth, 38 db *db.DB, 39 config *config.Config, 40 pages *pages.Pages, 41 logger *slog.Logger, 42 postsFS fs.FS, 43) *Timeline { 44 t := &Timeline{ 45 oauth: oauth, 46 db: db, 47 config: config, 48 pages: pages, 49 logger: logger, 50 } 51 t.recentPosts = loadRecentPosts(postsFS, logger) 52 return t 53} 54 55func (t *Timeline) Router() http.Handler { 56 r := chi.NewRouter() 57 r.Get("/", t.HomeOrTimeline) 58 r.Get("/home", t.Home) 59 r.Get("/timeline", t.Timeline) 60 return r 61} 62 63func loadRecentPosts(postsFS fs.FS, logger *slog.Logger) []pages.BlogPost { 64 entries, err := fs.ReadDir(postsFS, "posts") 65 if err != nil { 66 logger.Warn("failed to read blog posts", "err", err) 67 return nil 68 } 69 70 var posts []postMeta 71 for _, entry := range entries { 72 if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".md") { 73 continue 74 } 75 data, err := fs.ReadFile(postsFS, "posts/"+entry.Name()) 76 if err != nil { 77 continue 78 } 79 var meta postMeta 80 if _, err := frontmatter.Parse(bytes.NewReader(data), &meta); err != nil { 81 continue 82 } 83 if meta.Draft { 84 continue 85 } 86 posts = append(posts, meta) 87 } 88 89 // sort newest-first by date string (format "2006-01-02" sorts lexicographically) 90 for i := 1; i < len(posts); i++ { 91 for j := i; j > 0 && posts[j].Date > posts[j-1].Date; j-- { 92 posts[j], posts[j-1] = posts[j-1], posts[j] 93 } 94 } 95 96 if len(posts) > 3 { 97 posts = posts[:3] 98 } 99 100 result := make([]pages.BlogPost, len(posts)) 101 for i, p := range posts { 102 t, _ := time.Parse("2006-01-02", p.Date) 103 result[i] = pages.BlogPost{Slug: p.Slug, Title: p.Title, Subtitle: p.Subtitle, Date: t} 104 } 105 return result 106}