From 895fba9ded39d86d61ded59d8935da406b63222c Mon Sep 17 00:00:00 2001 From: Sudo-Ivan Date: Sat, 27 Dec 2025 21:26:28 -0600 Subject: [PATCH] Update fetching and filtering capabilities by adding category support in GetArticles methods across multiple files. Update NewsStore to manage selected category state and integrate category filtering in article loading. Improve feed fetching with abort signal handling in fetchFeed function. Update UI components to reflect category selection and enhance user experience with new feed health management features. --- README.md | 7 +-- desktop/app.go | 6 +-- eslint.config.js | 1 + internal/storage/sqlite.go | 18 +++++-- src/components/Sidebar.svelte | 12 ++++- src/lib/db.ts | 90 ++++++++++++++++++++++++++++------- src/lib/rss.ts | 25 +++++++--- src/lib/store.svelte.ts | 83 +++++++++++++++++++++++++++++--- src/routes/+page.svelte | 77 +++++++++++++++++++++++------- 9 files changed, 258 insertions(+), 61 deletions(-) diff --git a/README.md b/README.md index f4e6a98..03d5ee8 100644 --- a/README.md +++ b/README.md @@ -29,14 +29,9 @@ Web News follows a "zero-knowledge" philosophy: - [ ] Reading time - [ ] UI/UX Cleanup -- [ ] Add feed fetching timeout and button to remove if failed 3 times - [ ] Use Go Mobile, remove Java RSS plugin. -- [ ] Dont show loading screen if not initial load (eg. reloading tab) -- [ ] Fix feeds double image (Feed image and article image at top) - [ ] Export article(s) -- [ ] Export/Import OPML on add feed modal -- [ ] Favicons for feeds and caching -- [ ] +- [ ] Favicon fetcher and caching ## Getting Started diff --git a/desktop/app.go b/desktop/app.go index 3c4c146..c3aa8a3 100644 --- a/desktop/app.go +++ b/desktop/app.go @@ -151,12 +151,12 @@ func (a *App) SaveArticles(articles string) error { return a.db.SaveArticles(articles) } -func (a *App) GetArticles(feedId string, offset, limit int) (string, error) { - a.logDebug("GetArticles feedId=%s offset=%d limit=%d", feedId, offset, limit) +func (a *App) GetArticles(feedId string, offset, limit int, categoryId string) (string, error) { + a.logDebug("GetArticles feedId=%s categoryId=%s offset=%d limit=%d", feedId, categoryId, offset, limit) if a.db == nil { return "[]", nil } - return a.db.GetArticles(feedId, offset, limit) + return a.db.GetArticles(feedId, offset, limit, categoryId) } func (a *App) SearchArticles(query string, limit int) (string, error) { diff --git a/eslint.config.js b/eslint.config.js index 76b102a..65f6f25 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -52,6 +52,7 @@ export default [ FileReader: 'readonly', performance: 'readonly', AbortController: 'readonly', + AbortSignal: 'readonly', DOMParser: 'readonly', Element: 'readonly', Node: 'readonly', diff --git a/internal/storage/sqlite.go b/internal/storage/sqlite.go index e1b190e..098609a 100644 --- a/internal/storage/sqlite.go +++ b/internal/storage/sqlite.go @@ -322,11 +322,18 @@ func (s *SQLiteDB) SaveArticles(articlesJSON string) error { return tx.Commit() } -func (s *SQLiteDB) GetArticles(feedId string, offset, limit int) (string, error) { +func (s *SQLiteDB) GetArticles(feedId string, offset, limit int, categoryId string) (string, error) { var rows *sql.Rows var err error if feedId != "" { rows, err = s.db.Query("SELECT id, feedId, title, link, description, content, author, pubDate, read, saved, imageUrl, readAt FROM articles WHERE feedId = ? ORDER BY pubDate DESC LIMIT ? OFFSET ?", feedId, limit, offset) + } else if categoryId != "" { + rows, err = s.db.Query(` + SELECT a.id, a.feedId, a.title, a.link, a.description, a.content, a.author, a.pubDate, a.read, a.saved, a.imageUrl, a.readAt + FROM articles a + JOIN feeds f ON a.feedId = f.id + WHERE f.categoryId = ? + ORDER BY a.pubDate DESC LIMIT ? OFFSET ?`, categoryId, limit, offset) } else { rows, err = s.db.Query("SELECT id, feedId, title, link, description, content, author, pubDate, read, saved, imageUrl, readAt FROM articles ORDER BY pubDate DESC LIMIT ? OFFSET ?", limit, offset) } @@ -488,7 +495,7 @@ func (s *SQLiteDB) ClearAll() error { func (s *SQLiteDB) GetReadingHistory(days int) (string, error) { cutoff := time.Now().AddDate(0, 0, -days).UnixMilli() rows, err := s.db.Query(` - SELECT strftime('%Y-%m-%d', datetime(readAt/1000, 'unixepoch')) as date, COUNT(*) as count + SELECT strftime('%Y-%m-%d', datetime(readAt/1000, 'unixepoch', 'localtime')) as date, COUNT(*) as count FROM articles WHERE read = 1 AND readAt > ? GROUP BY date @@ -505,8 +512,11 @@ func (s *SQLiteDB) GetReadingHistory(days int) (string, error) { if err := rows.Scan(&date, &count); err != nil { continue } - // Convert date string back to timestamp for frontend - t, _ := time.Parse("2006-01-02", date) + // Convert local date string back to local midnight timestamp for frontend + t, err := time.ParseInLocation("2006-01-02", date, time.Local) + if err != nil { + continue + } history = append(history, map[string]any{ "date": t.UnixMilli(), "count": count, diff --git a/src/components/Sidebar.svelte b/src/components/Sidebar.svelte index a33d692..a2fa6db 100644 --- a/src/components/Sidebar.svelte +++ b/src/components/Sidebar.svelte @@ -353,8 +353,16 @@ {:else} + + +

{ if (e.key === 'Enter' && newFilter) { - newsStore.settings.muteFilters = [ - ...newsStore.settings.muteFilters, - newFilter, - ]; + muteFilters = [...muteFilters, newFilter]; newFilter = ''; } }} @@ -743,10 +781,7 @@ class="btn-primary px-4 py-2 rounded-xl whitespace-nowrap" onclick={() => { if (newFilter) { - newsStore.settings.muteFilters = [ - ...newsStore.settings.muteFilters, - newFilter, - ]; + muteFilters = [...muteFilters, newFilter]; newFilter = ''; } }} @@ -757,7 +792,7 @@
- {#each newsStore.settings.muteFilters as filter} + {#each muteFilters as filter} @@ -765,8 +800,7 @@ @@ -835,12 +869,14 @@ {#each Array(30) .fill(0) .map((_, i) => { - const date = new Date(); - date.setDate(date.getDate() - (29 - i)); - const dStr = date.toISOString().split('T')[0]; - const entry = readingHistory.find((h) => new Date(h.date) - .toISOString() - .split('T')[0] === dStr); + const d = new Date(); + d.setDate(d.getDate() - (29 - i)); + const dStr = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`; + const entry = readingHistory.find((h) => { + const hd = new Date(h.date); + const hStr = `${hd.getFullYear()}-${String(hd.getMonth() + 1).padStart(2, '0')}-${String(hd.getDate()).padStart(2, '0')}`; + return hStr === dStr; + }); return { date: dStr, count: entry?.count || 0 }; }) as day}
{feed?.id}
+ {:else if newsStore.selectedCategoryId} + {@const cat = newsStore.categories.find( + (c) => c.id === newsStore.selectedCategoryId + )} + {cat?.name} {:else if newsStore.currentView === 'saved'} Saved stories {:else if newsStore.currentView === 'following'}