diff --git a/desktop/go.mod b/desktop/go.mod index c9af7cd..5a702a2 100644 --- a/desktop/go.mod +++ b/desktop/go.mod @@ -43,6 +43,7 @@ require ( golang.org/x/crypto v0.46.0 // indirect golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect golang.org/x/net v0.48.0 // indirect + golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.39.0 // indirect golang.org/x/text v0.32.0 // indirect golang.org/x/time v0.14.0 // indirect diff --git a/internal/api/handlers.go b/internal/api/handlers.go index dc5a533..bd16918 100644 --- a/internal/api/handlers.go +++ b/internal/api/handlers.go @@ -17,8 +17,8 @@ import ( "time" "git.quad4.io/Go-Libs/RSS" - readability "github.com/go-shiori/go-readability" "git.quad4.io/Quad4-Software/webnews/internal/storage" + readability "github.com/go-shiori/go-readability" "golang.org/x/sync/singleflight" "golang.org/x/time/rate" ) diff --git a/internal/storage/sqlite.go b/internal/storage/sqlite.go index decc5c4..e1b190e 100644 --- a/internal/storage/sqlite.go +++ b/internal/storage/sqlite.go @@ -330,7 +330,7 @@ func (s *SQLiteDB) GetArticles(feedId string, offset, limit int) (string, error) } 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) } - + if err != nil { return "[]", err } @@ -377,7 +377,7 @@ func (s *SQLiteDB) SearchArticles(query string, limit int) (string, error) { FROM articles WHERE title LIKE ? OR description LIKE ? OR content LIKE ? ORDER BY pubDate DESC LIMIT ?`, q, q, q, limit) - + if err != nil { return "[]", err } diff --git a/src/components/AddFeedModal.svelte b/src/components/AddFeedModal.svelte index 86a08f7..159cb77 100644 --- a/src/components/AddFeedModal.svelte +++ b/src/components/AddFeedModal.svelte @@ -1,8 +1,10 @@
@@ -107,6 +139,30 @@ Add Feed {/if} + +
+
+
+
+
+ Or +
+
+ +
diff --git a/src/components/ArticleCard.svelte b/src/components/ArticleCard.svelte index 6da4e16..8e4ac2d 100644 --- a/src/components/ArticleCard.svelte +++ b/src/components/ArticleCard.svelte @@ -5,6 +5,7 @@ import { Bookmark, Share2, MoreVertical, Check, FileText, Loader2 } from 'lucide-svelte'; let { article }: { article: Article } = $props(); + const feed = $derived(newsStore.feeds.find((f) => f.id === article.feedId)); let copied = $state(false); let loadingFullText = $state(false); @@ -18,9 +19,11 @@ return date.toLocaleDateString(undefined, { month: 'short', day: 'numeric' }); } - function getSource(feedId: string) { - const feed = newsStore.feeds.find((f) => f.id === feedId); - return feed?.title || new URL(feedId).hostname; + function getSourceTitle() { + return ( + feed?.title || + (article.feedId.startsWith('http') ? new URL(article.feedId).hostname : article.feedId) + ); } async function shareArticle(e: MouseEvent) { @@ -78,6 +81,30 @@ ? 'ring-2 ring-accent-blue shadow-lg bg-accent-blue/5' : ''}" > + + {#if article.imageUrl || feed?.icon} +
+ {#if article.imageUrl && article.imageUrl !== feed?.icon} +
+ +
+ {:else if feed?.icon} +
+ +
+ {/if} +
+ {/if} + {#if newsStore.isSelectMode}
@@ -112,8 +139,11 @@
+ {#if feed?.icon} + + {/if} {getSource(article.feedId)}{getSourceTitle()} {formatDate(article.pubDate)} @@ -174,16 +204,4 @@
- - {#if article.imageUrl} -
- -
- {/if} diff --git a/src/lib/store.svelte.ts b/src/lib/store.svelte.ts index 3e6f5c3..0780aad 100644 --- a/src/lib/store.svelte.ts +++ b/src/lib/store.svelte.ts @@ -51,6 +51,7 @@ class NewsStore { isOnline = $state(true); ping = $state(null); lastStatusCheck = $state(Date.now()); + lastArticlesUpdate = $state(Date.now()); authInfo = $state<{ required: boolean; mode: string; canReg: boolean } | null>(null); isAuthenticated = $state(false); newlyRegisteredToken = $state(null); @@ -277,11 +278,20 @@ class NewsStore { this.statusInterval = setInterval(() => this.checkStatus(), 30000); } - window.addEventListener('online', () => this.checkStatus()); - window.addEventListener('offline', () => { - this.isOnline = false; - this.ping = null; - }); + if (typeof window !== 'undefined') { + window.addEventListener('online', () => this.checkStatus()); + window.addEventListener('offline', () => { + this.isOnline = false; + this.ping = null; + }); + + // Auto-refresh when tab becomes visible + document.addEventListener('visibilitychange', () => { + if (document.visibilityState === 'visible' && this.isAuthenticated) { + this.refresh(); + } + }); + } } async loadArticles() { @@ -324,6 +334,7 @@ class NewsStore { } else { this.articles = articles; } + this.lastArticlesUpdate = Date.now(); } private rankArticles(articles: Article[]): Article[] { @@ -633,6 +644,7 @@ class NewsStore { art.read = true; art.readAt = Date.now(); if (data.content) art.content = data.content; + data.feedId = art.feedId; } if (data.content) { @@ -760,7 +772,9 @@ class NewsStore { startAutoFetch() { if (this.fetchInterval) clearInterval(this.fetchInterval); this.fetchInterval = setInterval(() => { - this.refresh(); + if (this.isAuthenticated) { + this.refresh(); + } }, this.settings.globalFetchInterval * 60000); } } diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 6ec808b..ec8afd1 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1064,7 +1064,7 @@
- {#if newsStore.readingArticle.image} + {#if newsStore.readingArticle.image && newsStore.readingArticle.image !== newsStore.feeds.find((f) => f.id === newsStore.readingArticle.feedId)?.icon}
+ {#if newsStore.feeds.find((f) => f.id === newsStore.readingArticle.feedId)?.icon} + f.id === newsStore.readingArticle.feedId) + ?.icon} + alt="" + class="w-4 h-4 rounded-sm" + /> + {/if} {newsStore.readingArticle.siteName || ''} @@ -1118,7 +1126,7 @@ {/if} - Updated {new Date(newsStore.lastStatusCheck).toLocaleTimeString([], { + Updated {new Date(newsStore.lastArticlesUpdate).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', })} @@ -1269,7 +1277,7 @@
- {#if newsStore.readingArticle.image} + {#if newsStore.readingArticle.image && newsStore.readingArticle.image !== newsStore.feeds.find((f) => f.id === newsStore.readingArticle.feedId)?.icon} {newsStore.readingArticle.title} -

- {newsStore.readingArticle.byline || newsStore.readingArticle.siteName || ''} -

+
+ {#if newsStore.feeds.find((f) => f.id === newsStore.readingArticle.feedId)?.icon} + f.id === newsStore.readingArticle.feedId) + ?.icon} + alt="" + class="w-4 h-4 rounded-sm" + /> + {/if} + {newsStore.readingArticle.byline || + newsStore.readingArticle.siteName || + ''} +