Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add ability to favourite items #60

Merged
merged 1 commit into from
Apr 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion internal/commands/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ func (c Commands) CleanFeeds() error {
}

for _, url := range urlsToRemove {
err := c.store.DeleteByFeedURL(url)
err := c.store.DeleteByFeedURL(url, false)
if err != nil {
return fmt.Errorf("[commands.go]: %w", err)
}
Expand Down Expand Up @@ -279,6 +279,10 @@ func (c Commands) GetAllFeeds() ([]store.Item, error) {
// filter out read and add feedname
var items []store.Item
for i := range is {
if c.config.ShowFavourites && !is[i].Favourite {
continue
}

if !c.config.ShowRead && is[i].Read() {
continue
}
Expand Down
95 changes: 76 additions & 19 deletions internal/commands/tui.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,29 @@ import (
)

var (
appStyle = lipgloss.NewStyle().Padding(0).Margin(0)
titleStyle = list.DefaultStyles().Title.Margin(1, 0, 0, 0).Width(5)
itemStyle = lipgloss.NewStyle().PaddingLeft(4)
selectedItemStyle = lipgloss.NewStyle().PaddingLeft(2).Foreground(lipgloss.Color("170"))
readStyle = lipgloss.NewStyle().PaddingLeft(4).Foreground(lipgloss.Color("240"))
selectedReadStyle = lipgloss.NewStyle().PaddingLeft(2).Foreground(lipgloss.Color("170"))
paginationStyle = list.DefaultStyles().PaginationStyle.PaddingLeft(4)
helpStyle = list.DefaultStyles().
appStyle = lipgloss.NewStyle().Padding(0).Margin(0)
titleStyle = list.DefaultStyles().Title.Margin(1, 0, 0, 0).Width(5)
itemStyle = lipgloss.NewStyle().PaddingLeft(4)
selectedItemStyle = lipgloss.NewStyle().PaddingLeft(2).Foreground(lipgloss.Color("170"))
readStyle = lipgloss.NewStyle().PaddingLeft(4).Foreground(lipgloss.Color("240"))
selectedReadStyle = lipgloss.NewStyle().PaddingLeft(2).Foreground(lipgloss.Color("170"))
favouriteStyle = itemStyle.Copy().PaddingLeft(2).Bold(true)
selectedFavouriteStyle = selectedItemStyle.Copy().Bold(true)
paginationStyle = list.DefaultStyles().PaginationStyle.PaddingLeft(4)
helpStyle = list.DefaultStyles().
HelpStyle.
PaddingLeft(4).
PaddingBottom(1).
Foreground(lipgloss.Color("#4A4A4A"))
)

type TUIItem struct {
ID int
Title string
FeedName string
URL string
Read bool
ID int
Title string
FeedName string
URL string
Read bool
Favourite bool
}

func (i TUIItem) FilterValue() string { return i.Title }
Expand Down Expand Up @@ -64,11 +67,20 @@ func (d itemDelegate) Render(w io.Writer, m list.Model, index int, listItem list
fn = readStyle.Render
}

if i.Favourite {
fn = func(s string) string {
return favouriteStyle.Render("* " + s)
}
}

if index == m.Index() {
fn = func(s string) string {
if i.Read {
return selectedReadStyle.Render("> " + s)
}
if i.Favourite {
return selectedFavouriteStyle.Render("* " + s)
}
return selectedItemStyle.Render("> " + s)
}
}
Expand Down Expand Up @@ -186,6 +198,36 @@ func updateList(msg tea.Msg, m model) (tea.Model, tea.Cmd) {
m.commands.config.ToggleShowRead()
m.UpdateList()

case "f":
if m.list.SettingFilter() {
break
}

if len(m.list.Items()) == 0 {
return m, m.list.NewStatusMessage("No items to favourite.")
}

current := m.list.SelectedItem().(TUIItem)
err := m.commands.store.ToggleFavourite(current.ID)
if err != nil {
return m, tea.Quit
}
m.UpdateList()

case "F":
if m.list.SettingFilter() {
break
}

if m.commands.config.ShowFavourites {
m.list.NewStatusMessage("")
} else {
m.list.NewStatusMessage("favourites")
}

m.commands.config.ToggleShowFavourites()
m.UpdateList()

case "o":
if m.list.SettingFilter() {
break
Expand Down Expand Up @@ -332,14 +374,17 @@ func (m model) viewportHelp() string {

func ItemToTUIItem(i store.Item) TUIItem {
return TUIItem{
ID: i.ID,
FeedName: i.FeedName,
Title: i.Title,
URL: i.Link,
Read: i.Read(),
ID: i.ID,
FeedName: i.FeedName,
Title: i.Title,
URL: i.Link,
Read: i.Read(),
Favourite: i.Favourite,
}
}

const defaultTitle = "nom"

func Render(items []list.Item, cmds Commands, errors []string) error {
const defaultWidth = 20
_, ts, _ := term.GetSize(int(os.Stdout.Fd()))
Expand All @@ -350,10 +395,14 @@ func Render(items []list.Item, cmds Commands, errors []string) error {

l := list.New(items, itemDelegate{}, defaultWidth, height)
l.SetShowStatusBar(false)
l.Title = "nom"
l.Title = defaultTitle
l.Styles.Title = titleStyle
l.Styles.PaginationStyle = paginationStyle
l.Styles.HelpStyle = helpStyle
// remove some extra keys from next/prev as used for other things
l.KeyMap.NextPage.SetKeys("right", "l", "pgdown")
l.KeyMap.PrevPage.SetKeys("left", "h", "pgup")

l.AdditionalFullHelpKeys = func() []key.Binding {
return []key.Binding{
key.NewBinding(
Expand All @@ -364,6 +413,14 @@ func Render(items []list.Item, cmds Commands, errors []string) error {
key.WithKeys("M"),
key.WithHelp("M", "show/hide read"),
),
key.NewBinding(
key.WithKeys("f"),
key.WithHelp("f", "toggle favourite"),
),
key.NewBinding(
key.WithKeys("F"),
key.WithHelp("F", "show/hide all favourites"),
),
key.NewBinding(
key.WithKeys("r"),
key.WithHelp("r", "refresh feed"),
Expand Down
13 changes: 9 additions & 4 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,21 @@ type Config struct {
Pager string `yaml:"pager,omitempty"`
Feeds []Feed `yaml:"feeds"`
// Preview feeds are distinguished from Feeds because we don't want to inadvertenly write those into the config file.
PreviewFeeds []Feed `yaml:"previewfeeds,omitempty"`
Backends *Backends `yaml:"backends,omitempty"`
ShowRead bool `yaml:"showread,omitempty"`
AutoRead bool `yaml:"autoread,omitempty"`
PreviewFeeds []Feed `yaml:"previewfeeds,omitempty"`
Backends *Backends `yaml:"backends,omitempty"`
ShowRead bool `yaml:"showread,omitempty"`
AutoRead bool `yaml:"autoread,omitempty"`
ShowFavourites bool
}

func (c *Config) ToggleShowRead() {
c.ShowRead = !c.ShowRead
}

func (c *Config) ToggleShowFavourites() {
c.ShowFavourites = !c.ShowFavourites
}

func New(configPath string, pager string, previewFeeds []string) (Config, error) {
var configDir string

Expand Down
35 changes: 29 additions & 6 deletions internal/store/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type Item struct {
ID int
Author string
Title string
Favourite bool
FeedURL string
FeedName string // added from config if set
Link string
Expand All @@ -34,7 +35,8 @@ type Store interface {
GetAllItems() ([]Item, error)
GetAllFeedURLs() ([]string, error)
ToggleRead(ID int) error
DeleteByFeedURL(feedurl string) error
ToggleFavourite(ID int) error
DeleteByFeedURL(feedurl string, incFavourites bool) error
}

type SQLiteStore struct {
Expand Down Expand Up @@ -79,6 +81,7 @@ func dbSetup(db *sql.DB) error {
return fmt.Errorf("dbSetup: %w", err)
}

// See migrations below for additions
stm := `
create table items (id integer primary key, feedurl text, link text, title text, content text, author text, readat datetime, publishedat datetime, updatedat datetime, createdat datetime);
create table migrations (id integer not null, runat datetime);
Expand All @@ -100,7 +103,10 @@ func dbSetup(db *sql.DB) error {
func runMigrations(db *sql.DB) (err error) {
getCurrent := `select count(*) from migrations;`

migrations := []string{}
// Index based so all new migrations must go at the end of the array
migrations := []string{
`alter table items add favourite boolean not null default 0;`,
}

tx, _ := db.Begin()
updateMigrations, _ := tx.Prepare(`insert into migrations (id, runat) values (?, ?);`)
Expand Down Expand Up @@ -178,7 +184,7 @@ func (sls SQLiteStore) UpsertItem(item Item) error {
// TODO: pagination
func (sls SQLiteStore) GetAllItems() ([]Item, error) {
stmt := `
select id, feedurl, link, title, content, author, readat, publishedat, createdat, updatedat from items order by coalesce(publishedat, createdat);
select id, feedurl, link, title, content, author, readat, favourite, publishedat, createdat, updatedat from items order by coalesce(publishedat, createdat);
`

rows, err := sls.db.Query(stmt)
Expand All @@ -194,7 +200,7 @@ func (sls SQLiteStore) GetAllItems() ([]Item, error) {
var publishedAtNull sql.NullTime
var linkNull sql.NullString

if err := rows.Scan(&item.ID, &item.FeedURL, &linkNull, &item.Title, &item.Content, &item.Author, &readAtNull, &publishedAtNull, &item.CreatedAt, &item.UpdatedAt); err != nil {
if err := rows.Scan(&item.ID, &item.FeedURL, &linkNull, &item.Title, &item.Content, &item.Author, &readAtNull, &item.Favourite, &publishedAtNull, &item.CreatedAt, &item.UpdatedAt); err != nil {
fmt.Println("errrerre: ", err)
continue
}
Expand All @@ -220,6 +226,17 @@ func (sls SQLiteStore) ToggleRead(ID int) error {
return nil
}

func (sls SQLiteStore) ToggleFavourite(ID int) error {
stmt, _ := sls.db.Prepare(`update items set favourite = case when favourite is true then false else true end where id = ?`)

_, err := stmt.Exec(ID)
if err != nil {
return fmt.Errorf("[store.go] ToggleFavourite: %w", err)
}

return nil
}

func (sls SQLiteStore) GetAllFeedURLs() ([]string, error) {
var urls []string

Expand All @@ -244,8 +261,14 @@ func (sls SQLiteStore) GetAllFeedURLs() ([]string, error) {
return urls, nil
}

func (sls SQLiteStore) DeleteByFeedURL(feedurl string) error {
stmt, _ := sls.db.Prepare(`delete from items where feedurl = ?`)
func (sls SQLiteStore) DeleteByFeedURL(feedurl string, incFavourites bool) error {

var stmt *sql.Stmt
if incFavourites {
stmt, _ = sls.db.Prepare(`delete from items where feedurl = ?;`)
} else {
stmt, _ = sls.db.Prepare(`delete from items where feedurl = ? and favourite = false;`)
}

_, err := stmt.Exec(feedurl)
if err != nil {
Expand Down
Loading