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 E to edit config in nom #86

Merged
merged 3 commits into from
Jun 11, 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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ You can also add feeds with the `add` command:
```sh
$ nom add <url>
```
Feeds are editable within `nom` by pressing `E` to open the config in your `$EDITOR` or `$NOMEDITOR`. After editing feeds, you will need to then refresh with `r`.

#### Youtube feeds
To add youtube feeds you can go to a channel and run the following in the browser console to get the rss feed link:
```js
Expand Down
59 changes: 8 additions & 51 deletions internal/commands/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ import (
)

type Commands struct {
config config.Config
config *config.Config
store store.Store
}

func New(config config.Config, store store.Store) Commands {
func New(config *config.Config, store store.Store) Commands {
return Commands{config, store}
}

Expand Down Expand Up @@ -152,11 +152,6 @@ func (c Commands) TUI() error {
defer f.Close()
}

err := c.CleanFeeds()
if err != nil {
return fmt.Errorf("commands List: %w", err)
}

its, err := c.GetAllFeeds()
if err != nil {
return fmt.Errorf("commands List: %w", err)
Expand Down Expand Up @@ -197,10 +192,6 @@ func (c Commands) TUI() error {
}

func (c Commands) List(numResults int) error {
err := c.CleanFeeds()
if err != nil {
return fmt.Errorf("commands List: %w", err)
}
its, err := c.GetAllFeeds()
if err != nil {
return fmt.Errorf("commands List: %w", err)
Expand Down Expand Up @@ -309,6 +300,11 @@ func includes[T comparable](arr []T, item T) bool {
}

func (c Commands) GetAllFeeds() ([]store.Item, error) {
err := c.CleanFeeds()
if err != nil {
return []store.Item{}, fmt.Errorf("[commands.go] GetAllFeeds: %w", err)
}

is, err := c.store.GetAllItems()
if err != nil {
return []store.Item{}, fmt.Errorf("commands.go: GetAllFeeds %w", err)
Expand Down Expand Up @@ -372,47 +368,8 @@ func fetchFeed(ch chan FetchResultError, wg *sync.WaitGroup, feed config.Feed, v
ch <- FetchResultError{res: r, err: nil, url: feed.URL}
}

func (c Commands) GetArticleByID(ID int) (store.Item, error) {
items, err := c.GetAllFeeds()
if err != nil {
return store.Item{}, fmt.Errorf("commands.FindArticle: %w", err)
}

var item store.Item
for _, it := range items {
if it.ID == ID {
item = it
break
}
}

return item, nil
}

func (c Commands) FindArticle(substr string) (item store.Item, err error) {
items, err := c.GetAllFeeds()
if err != nil {
return store.Item{}, fmt.Errorf("commands.FindArticle: %w", err)
}

regex, err := regexp.Compile(strings.ToLower(substr))
if err != nil {
return store.Item{}, fmt.Errorf("commands.FindArticle: regexp: %w", err)
}

for _, it := range items {
// very basic string matching on title to read an article
if regex.MatchString(strings.ToLower(it.Title)) {
item = it
break
}
}

return item, nil
}

func (c Commands) GetGlamourisedArticle(ID int) (string, error) {
article, err := c.GetArticleByID(ID)
article, err := c.store.GetItemByID(ID)
if err != nil {
return "", fmt.Errorf("commands.FindGlamourisedArticle: %w", err)
}
Expand Down
7 changes: 6 additions & 1 deletion internal/commands/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type ListKeyMapT struct {
oCancelWhileFiltering key.Binding
oNextPage key.Binding
oPrevPage key.Binding
EditConfig key.Binding
}

// ViewportKeyMapT shows *all* keybinds, pulling from viewport.DefaultKeyMap()
Expand Down Expand Up @@ -71,6 +72,10 @@ var ListKeyMap = ListKeyMapT{
key.WithKeys("o"),
key.WithHelp("o", "open in browser"),
),
EditConfig: key.NewBinding(
key.WithKeys("E"),
key.WithHelp("E", "edit config in $EDITOR"),
),
// o for override
oQuit: key.NewBinding(
key.WithKeys("q", "esc"),
Expand Down Expand Up @@ -162,7 +167,7 @@ func (k ListKeyMapT) FullHelp() []key.Binding {
return []key.Binding{
k.Open, k.Read, k.Favourite, k.Refresh,
k.OpenInBrowser, k.ToggleFavourites, k.ToggleReads,
k.MarkAllRead,
k.MarkAllRead, k.EditConfig,
}
}

Expand Down
39 changes: 37 additions & 2 deletions internal/commands/tui.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"io"
"os"
"os/exec"
"regexp"
"sort"
"strings"
Expand Down Expand Up @@ -106,7 +107,7 @@ type model struct {
selectedArticle *int
viewport viewport.Model
errors []string
cfg config.Config
cfg *config.Config
}

func (m model) Init() tea.Cmd {
Expand Down Expand Up @@ -294,6 +295,29 @@ func updateList(msg tea.Msg, m model) (tea.Model, tea.Cmd) {
cmd = m.UpdateList()
cmds = append(cmds, cmd)
}
case key.Matches(msg, ListKeyMap.EditConfig):
// open editor with config file
// take output of editor and put into file
//
filePath := m.cfg.ConfigPath

cmd := strings.Split(getEditor("NOMEDITOR", "EDITOR"), " ")
cmd = append(cmd, filePath)

execCmd := exec.Command(cmd[0], cmd[1:]...)
return m, tea.ExecProcess(execCmd, func(err error) tea.Msg {
if err != nil {
m.list.NewStatusMessage(err.Error())
return nil
}

err = m.cfg.Load()
if err != nil {
m.list.NewStatusMessage(err.Error())
return nil
}
return nil
})
}
}

Expand Down Expand Up @@ -567,7 +591,7 @@ func CustomFilter(term string, targets []string) []list.Rank {

const defaultTitle = "nom"

func Render(items []list.Item, cmds Commands, errors []string, cfg config.Config) error {
func Render(items []list.Item, cmds Commands, errors []string, cfg *config.Config) error {
const defaultWidth = 20
_, ts, _ := term.GetSize(int(os.Stdout.Fd()))
_, y := appStyle.GetFrameSize()
Expand Down Expand Up @@ -604,3 +628,14 @@ func Render(items []list.Item, cmds Commands, errors []string, cfg config.Config

return nil
}

func getEditor(vars ...string) string {
for _, e := range vars {
val := os.Getenv(e)
if val != "" {
return val
}
}

return "nano"
}
14 changes: 7 additions & 7 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ type Theme struct {

// need to add to Load() below if loading from config file
type Config struct {
configPath string
ConfigPath string
ShowFavourites bool
Version string
ConfigDir string `yaml:"-"`
Expand All @@ -72,13 +72,13 @@ func (c *Config) ToggleShowFavourites() {
c.ShowFavourites = !c.ShowFavourites
}

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

if configPath == "" {
userConfigDir, err := os.UserConfigDir()
if err != nil {
return Config{}, fmt.Errorf("config.New: %w", err)
return nil, fmt.Errorf("config.New: %w", err)
}

configDir = filepath.Join(userConfigDir, "nom")
Expand All @@ -95,8 +95,8 @@ func New(configPath string, pager string, previewFeeds []string, version string)
f = append(f, Feed{URL: feedURL})
}

return Config{
configPath: configPath,
return &Config{
ConfigPath: configPath,
ConfigDir: configDir,
Pager: pager,
Feeds: []Feed{},
Expand All @@ -120,7 +120,7 @@ func (c *Config) Load() error {
return fmt.Errorf("config Load: %w", err)
}

rawData, err := os.ReadFile(c.configPath)
rawData, err := os.ReadFile(c.ConfigPath)
if err != nil {
return fmt.Errorf("config.Load: %w", err)
}
Expand Down Expand Up @@ -189,7 +189,7 @@ func (c *Config) Write() error {
return fmt.Errorf("config.Write: %w", err)
}

err = os.WriteFile(c.configPath, []byte(str), 0655)
err = os.WriteFile(c.ConfigPath, []byte(str), 0655)
if err != nil {
return fmt.Errorf("config.Write: %w", err)
}
Expand Down
8 changes: 4 additions & 4 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ func TestNewDefault(t *testing.T) {
c, _ := New("", "", []string{}, "")
ucd, _ := os.UserConfigDir()

test.Equal(t, fmt.Sprintf("%s/nom/config.yml", ucd), c.configPath, "Wrong defaults set")
test.Equal(t, fmt.Sprintf("%s/nom/config.yml", ucd), c.ConfigPath, "Wrong defaults set")
test.Equal(t, fmt.Sprintf("%s/nom", ucd), c.ConfigDir, "Wrong default ConfigDir set")
}

func TestConfigCustomPath(t *testing.T) {
c, _ := New("foo/bar.yml", "", []string{}, "")

test.Equal(t, "foo/bar.yml", c.configPath, "Config path override not set")
test.Equal(t, "foo/bar.yml", c.ConfigPath, "Config path override not set")
}

func TestConfigDir(t *testing.T) {
Expand All @@ -43,7 +43,7 @@ func TestConfigDir(t *testing.T) {
func TestNewOverride(t *testing.T) {
c, _ := New("foobar", "", []string{}, "")

test.Equal(t, "foobar", c.configPath, "Override not respected")
test.Equal(t, "foobar", c.ConfigPath, "Override not respected")
}

func TestPreviewFeedsOverrideFeedsFromConfigFile(t *testing.T) {
Expand Down Expand Up @@ -99,7 +99,7 @@ func TestConfigAddFeed(t *testing.T) {
c.AddFeed(Feed{URL: "foo"})

var actual Config
rawData, _ := os.ReadFile(c.configPath)
rawData, _ := os.ReadFile(c.ConfigPath)
_ = yaml.Unmarshal(rawData, &actual)

hasAdded := false
Expand Down
24 changes: 24 additions & 0 deletions internal/store/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ func (i Item) Read() bool {
type Store interface {
UpsertItem(item Item) error
GetAllItems() ([]Item, error)
GetItemByID(ID int) (Item, error)
GetAllFeedURLs() ([]string, error)
ToggleRead(ID int) error
MarkAllRead() error
Expand Down Expand Up @@ -289,3 +290,26 @@ func (sls SQLiteStore) DeleteByFeedURL(feedurl string, incFavourites bool) error

return nil
}

func (sls SQLiteStore) GetItemByID(ID int) (Item, error) {
var stmt *sql.Stmt
stmt, _ = sls.db.Prepare(`select id, feedurl, link, title, content, author, readat, favourite, publishedat, createdat, updatedat from items where id = ?;`)

var i Item
var readAtNull sql.NullTime
var publishedAtNull sql.NullTime
var linkNull sql.NullString

r := stmt.QueryRow(ID)

err := r.Scan(&i.ID, &i.FeedURL, &linkNull, &i.Title, &i.Content, &i.Author, &readAtNull, &i.Favourite, &publishedAtNull, &i.CreatedAt, &i.UpdatedAt)
if err != nil {
return Item{}, fmt.Errorf("[store.go] GetItemByID: %w", err)
}

i.Link = linkNull.String
i.ReadAt = readAtNull.Time
i.PublishedAt = publishedAtNull.Time

return i, nil
}
Loading