Skip to content
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
1 change: 1 addition & 0 deletions internal/commands/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ func (c Commands) fetchAllFeeds() ([]store.Item, []ErrorItem, error) {
FeedURL: result.url,
FeedName: r.FeedName,
Link: r.Link,
GUID: r.GUID,
PublishedAt: r.PubDate,
Title: r.Title,
}
Expand Down
1 change: 1 addition & 0 deletions internal/commands/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ func updateList(msg tea.Msg, m model) (tea.Model, tea.Cmd) {

content, err := m.commands.GetGlamourisedArticle(*m.selectedArticle)
if err != nil {
// LKS: there should be an error message here
return m, tea.Quit
}

Expand Down
2 changes: 2 additions & 0 deletions internal/rss/rss.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
type Item struct {
Title string `xml:"title"`
Link string `xml:"link"`
GUID string `xml:"guid"`
Description string `xml:"description"`
Author string `xml:"author"`
Categories []string `xml:"categories"`
Expand Down Expand Up @@ -70,6 +71,7 @@ func feedToRSS(f config.Feed, feed *gofeed.Feed) RSS {
ni := Item{
Title: it.Title,
Link: it.Link,
GUID: it.GUID,
}

if it.Description != "" {
Expand Down
26 changes: 16 additions & 10 deletions internal/store/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type Item struct {
FeedURL string
FeedName string // added from config if set
Link string
GUID string
Content string
ReadAt time.Time
PublishedAt time.Time
Expand Down Expand Up @@ -135,6 +136,7 @@ func runMigrations(db *sql.DB) (err error) {
// 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;`,
`alter table items add guid text`,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need an update table set guid = link; here to fix existing entries

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this will fix the issue of duplicate entries as the first time nom loads with these changes it will migrate the existing entries

Copy link
Contributor Author

@larsks larsks Oct 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That will address some cases, but some blogs use e.g. UUIDs for the <guid> value, so we'll still see duplicates. The alternative would be to just use the <link> value instead of <guid>, which maybe is fine?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using <link> works well; I just didn't go that route originally because, you know, there was a GUID. But using <link> seems better for compatability. I'm going to update the PR.

}

tx, _ := db.Begin()
Expand Down Expand Up @@ -211,24 +213,24 @@ type statementPreparer interface {
}

func (sls *SQLiteStore) upsertItem(db statementPreparer, item *Item) error {
stmt, err := db.Prepare(`select count(id), id from items where feedurl = ? and title = ?;`)
stmt, err := db.Prepare(`select count(id), id from items where feedurl = ? and link = ?;`)
if err != nil {
return fmt.Errorf("sqlite.go: could not prepare query: %w", err)
}

var count int
var id sql.NullInt32
err = stmt.QueryRow(item.FeedURL, item.Title).Scan(&count, &id)
err = stmt.QueryRow(item.FeedURL, item.Link).Scan(&count, &id)
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return fmt.Errorf("store.go: write %w", err)
}
if count == 0 {
stmt, err = db.Prepare(`insert into items (feedurl, link, title, content, author, publishedat, createdat, updatedat) values (?, ?, ?, ?, ?, ?, ?, ?)`)
stmt, err = db.Prepare(`insert into items (feedurl, guid, link, title, content, author, publishedat, createdat, updatedat) values (?, ?, ?, ?, ?, ?, ?, ?, ?)`)
if err != nil {
return fmt.Errorf("sqlite.go: could not prepare query: %w", err)
}

result, err := stmt.Exec(item.FeedURL, item.Link, item.Title, item.Content, item.Author, item.PublishedAt, time.Now(), time.Now())
result, err := stmt.Exec(item.FeedURL, item.GUID, item.Link, item.Title, item.Content, item.Author, item.PublishedAt, time.Now(), time.Now())
if err != nil {
return fmt.Errorf("sqlite.go: Upsert failed: %w", err)
}
Expand All @@ -238,12 +240,12 @@ func (sls *SQLiteStore) upsertItem(db statementPreparer, item *Item) error {
}
item.ID = int(lastID)
} else {
stmt, err = db.Prepare(`update items set content = ?, updatedat = ? where id = ?`)
stmt, err = db.Prepare(`update items set title = ?, content = ?, updatedat = ? where id = ?`)
if err != nil {
return fmt.Errorf("sqlite.go: could not prepare query: %w", err)
}

_, err = stmt.Exec(item.Content, time.Now(), id)
_, err = stmt.Exec(item.Title, item.Content, time.Now(), id)
if err != nil {
return fmt.Errorf("sqlite.go: Upsert failed: %w", err)
}
Expand All @@ -254,7 +256,7 @@ func (sls *SQLiteStore) upsertItem(db statementPreparer, item *Item) error {
// TODO: pagination
func (sls SQLiteStore) GetAllItems(ordering string) ([]Item, error) {
itemStmt := `
select id, feedurl, link, title, content, author, readat, favourite, publishedat, createdat, updatedat from items order by coalesce(publishedat, createdat) %s;
select id, feedurl, guid, link, title, content, author, readat, favourite, publishedat, createdat, updatedat from items order by coalesce(publishedat, createdat) %s;
`

var stmt string
Expand All @@ -277,12 +279,14 @@ func (sls SQLiteStore) GetAllItems(ordering string) ([]Item, error) {
var readAtNull sql.NullTime
var publishedAtNull sql.NullTime
var linkNull sql.NullString
var guidNull sql.NullString

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 {
if err := rows.Scan(&item.ID, &item.FeedURL, &guidNull, &linkNull, &item.Title, &item.Content, &item.Author, &readAtNull, &item.Favourite, &publishedAtNull, &item.CreatedAt, &item.UpdatedAt); err != nil {
fmt.Println("errrerre: ", err)
continue
}

item.GUID = guidNull.String
item.Link = linkNull.String
item.ReadAt = readAtNull.Time
item.PublishedAt = publishedAtNull.Time
Expand Down Expand Up @@ -368,21 +372,23 @@ func (sls SQLiteStore) DeleteByFeedURL(feedurl string, incFavourites bool) error

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 = ?;`)
stmt, _ = sls.db.Prepare(`select id, feedurl, guid, 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
var guidNull 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)
err := r.Scan(&i.ID, &i.FeedURL, &guidNull, &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.GUID = guidNull.String
i.ReadAt = readAtNull.Time
i.PublishedAt = publishedAtNull.Time

Expand Down