[mirror] yet another tui rss reader github.com/olexsmir/smutok

feat(tui): show list of articles

olexsmir.xyz e1ab4d0a 47b0d336

verified
Changed files
+66 -26
internal
+18 -4
internal/store/sqlite_articles.go
··· 56 56 return err 57 57 } 58 58 59 + type ArticleKind int 60 + 61 + const ( 62 + ArticleStarred ArticleKind = iota 63 + ArticleUnread 64 + ArticleAll 65 + ) 66 + 59 67 type Article struct { 60 68 ID string 61 69 Title string ··· 69 77 PublishedAt int64 70 78 } 71 79 72 - func (s *Sqlite) GetArticles(ctx context.Context) ([]Article, error) { 73 - query := `--sql 80 + var getArticlesWhereClause = map[ArticleKind]string{ 81 + ArticleAll: "", 82 + ArticleStarred: "where s.is_starred = true", 83 + ArticleUnread: "where s.is_read = false", 84 + } 85 + 86 + func (s *Sqlite) GetArticles(ctx context.Context, kind ArticleKind) ([]Article, error) { 87 + query := fmt.Sprintf(`--sql 74 88 select a.id, a.title, a.href, a.content, a.author, s.is_read, s.is_starred, a.feed_id, f.title feed_name, a.published_at 75 89 from articles a 76 90 join article_statuses s on a.id = s.article_id 77 91 join feeds f on f.id = a.feed_id 78 - where s.is_read = false 79 - order by a.published_at desc` 92 + %s 93 + order by a.published_at desc`, getArticlesWhereClause[kind]) 80 94 81 95 rows, err := s.db.QueryContext(ctx, query) 82 96 if err != nil {
+45 -2
internal/tui/fetcher.go
··· 1 1 package tui 2 2 3 3 import ( 4 + "time" 5 + 6 + "github.com/charmbracelet/bubbles/table" 4 7 tea "github.com/charmbracelet/bubbletea" 5 8 "olexsmir.xyz/smutok/internal/store" 6 9 ) 7 10 8 11 type fetchedArticles []store.Article 9 12 10 - func (m *Model) fetchArticles() tea.Cmd { 13 + func (m *Model) fetchArticles(kind store.ArticleKind) tea.Cmd { 11 14 return func() tea.Msg { 12 - articles, err := m.store.GetArticles(m.ctx) 15 + articles, err := m.store.GetArticles(m.ctx, kind) 13 16 if err != nil { 14 17 return sendErr(err) 15 18 } 16 19 return fetchedArticles(articles) 17 20 } 18 21 } 22 + 23 + func (m *Model) setupTableWithArticles() { 24 + // clean up previous state 25 + m.table.SetRows([]table.Row{}) 26 + 27 + columns := []table.Column{ 28 + {Title: "date", Width: 10}, 29 + {Title: "status", Width: 6}, 30 + {Title: "author", Width: 14}, 31 + {Title: "title", Width: m.table.Width() - 30}, 32 + } 33 + 34 + rows := make([]table.Row, len(m.articles)) 35 + for i, a := range m.articles { 36 + rows[i] = table.Row{ 37 + m.toArticleDate(a.PublishedAt), 38 + m.toArticleStatus(a.IsRead, a.IsStarred), 39 + a.Author, 40 + a.Title, 41 + } 42 + } 43 + 44 + m.table.SetColumns(columns) 45 + m.table.SetRows(rows) 46 + } 47 + 48 + func (m *Model) toArticleStatus(isRead, isStarred bool) string { 49 + var out string 50 + if isRead { 51 + out += "✓ " 52 + } 53 + if isStarred { 54 + out += "★ " 55 + } 56 + return out 57 + } 58 + 59 + func (m *Model) toArticleDate(publishedAt int64) string { 60 + return time.Unix(publishedAt, 0).Format("2006/01/02") 61 + }
+3 -20
internal/tui/tui.go
··· 2 2 3 3 import ( 4 4 "context" 5 - "log/slog" 6 5 7 6 "github.com/charmbracelet/bubbles/table" 8 7 "github.com/charmbracelet/bubbles/viewport" ··· 67 66 func (m *Model) Init() tea.Cmd { 68 67 return tea.Batch( 69 68 tea.SetWindowTitle("smutok"), 70 - m.fetchArticles(), 69 + m.fetchArticles(store.ArticleAll), 71 70 ) 72 71 } 73 72 ··· 81 80 82 81 case tea.WindowSizeMsg: 83 82 m.table.SetHeight(msg.Height) 84 - m.table.SetWidth(msg.Width) 83 + m.table.SetWidth(msg.Width - 2) 85 84 m.viewport.Height = msg.Height 86 85 m.viewport.Width = msg.Width 87 86 return m, nil 88 87 89 88 case fetchedArticles: 90 89 m.articles = msg 91 - 92 - columns := []table.Column{ 93 - // {Title: "read", Width: 4}, 94 - // {Title: "stared", Width: 6}, 95 - {Title: "author", Width: 14}, 96 - {Title: "title", Width: m.table.Width() - 14}, 97 - } 98 - 99 - rows := make([]table.Row, len(msg)) 100 - for i, article := range msg { 101 - rows[i] = table.Row{article.Author, article.Title} 102 - } 103 - 104 - m.table.SetColumns(columns) 105 - m.table.SetRows(rows) 106 - 107 - slog.Debug("got articles") 90 + m.setupTableWithArticles() 108 91 return m, nil 109 92 110 93 case tea.KeyMsg: