package cmd import ( "fmt" "slices" "time" "github.com/charmbracelet/bubbles/list" tea "github.com/charmbracelet/bubbletea" "github.com/simonhege/timeago" miniflux "miniflux.app/client" ) type feedEntry struct { ID int64 Name string Feed string Link string PublishedAt time.Time } func (fe feedEntry) Title() string { return fe.Name } func (fe feedEntry) Description() string { formattedTimeAgo := timeago.WithMax(timeago.English, time.Hour*7*24, timeago.English.DefaultLayout).Format(fe.PublishedAt) return fmt.Sprintf("%s - Published %s", fe.Feed, formattedTimeAgo) } func (fe feedEntry) FilterValue() string { return fmt.Sprintf("%s - %s", fe.Name, fe.Feed) } type feedEntriesModel struct { minifluxClient *miniflux.Client entries list.Model selectedEntry *feedEntry } type MsgFetchedEntries []feedEntry type MsgFetchedEntriesError error func (m *feedEntriesModel) fetchEntries() tea.Msg { entriesResult, err := m.minifluxClient.CategoryEntries(4, &miniflux.Filter{Statuses: []string{miniflux.EntryStatusUnread}}) if err != nil { return MsgFetchedEntriesError(err) } var feedEntries []feedEntry for _, e := range entriesResult.Entries { feedEntries = append(feedEntries, feedEntry{ ID: e.ID, Name: e.Title, Feed: e.Feed.Title, Link: e.URL, PublishedAt: e.Date, }) } slices.Reverse(feedEntries) return MsgFetchedEntries(feedEntries) } func (m *feedEntriesModel) Init() tea.Cmd { m.entries = list.New([]list.Item{feedEntry{}}, list.NewDefaultDelegate(), 100, 50) return m.fetchEntries } func (m *feedEntriesModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.WindowSizeMsg: // h, v := docStyle.GetFrameSize() // TODO: this is not ideal, hardcoding -2 here m.entries.SetSize(msg.Width, msg.Height-2) case tea.KeyMsg: switch msg.String() { case "enter": fnGetSelected := func() tea.Msg { i, ok := m.entries.SelectedItem().(feedEntry) if ok { m.selectedEntry = &i } return MsgPlayEntry(i) } return m, fnGetSelected } break case MsgWatchedEntry: m.markEntryRead(int64(msg)) case MsgFetchedEntries: var items []list.Item for _, e := range msg { items = append(items, e) } m.entries.SetItems(items) case MsgFetchedEntriesError: break } var cmd tea.Cmd m.entries, cmd = m.entries.Update(msg) return m, cmd } func (m *feedEntriesModel) View() string { m.entries.Title = "Feed" return m.entries.View() } func (m *feedEntriesModel) markEntryRead(entryID int64) { foundIndex := -1 for i, e := range m.entries.Items() { if e.(feedEntry).ID == entryID { foundIndex = i break } } if foundIndex >= 0 { m.entries.RemoveItem(foundIndex) } m.minifluxClient.UpdateEntries([]int64{entryID}, miniflux.EntryStatusRead) }