>(
+ ({ children, className, ...props }, ref) => (
+
+ {children}
+
+ )
+)
+SettingRow.displayName = 'SettingRow'
diff --git a/src/pages/secondary/RssArticlePage/index.tsx b/src/pages/secondary/RssArticlePage/index.tsx
index c93e6749..77143e96 100644
--- a/src/pages/secondary/RssArticlePage/index.tsx
+++ b/src/pages/secondary/RssArticlePage/index.tsx
@@ -1,5 +1,6 @@
import NoteInteractions from '@/components/NoteInteractions'
import NoteStats from '@/components/NoteStats'
+import RssArticleWebBookmarks from '@/components/RssArticleWebBookmarks'
import RssFeedItem from '@/components/RssFeedItem'
import { RefreshButton } from '@/components/RefreshButton'
import { Button } from '@/components/ui/button'
@@ -291,6 +292,11 @@ const RssArticlePage = forwardRef(
) : null}
+ {isHttpArticleUrl(articleUrl) ? (
+
+
+
+ ) : null}
{showNostrThread && syntheticRoot ? (
@@ -374,6 +380,11 @@ const RssArticlePage = forwardRef(
/>
))}
+ {isHttpArticleUrl(articleUrl) ? (
+
+
+
+ ) : null}
{showNostrThread && syntheticRoot ? (
diff --git a/src/routes.tsx b/src/routes.tsx
index f429a42c..11e557f1 100644
--- a/src/routes.tsx
+++ b/src/routes.tsx
@@ -25,6 +25,7 @@ const RelaySettingsPageLazy = lazy(() => import('./pages/secondary/RelaySettings
const CacheSettingsPageLazy = lazy(() => import('./pages/secondary/CacheSettingsPage'))
const RssFeedSettingsPageLazy = lazy(() => import('./pages/secondary/RssFeedSettingsPage'))
const FollowSetsSettingsPageLazy = lazy(() => import('./pages/secondary/FollowSetsSettingsPage'))
+const PersonalListsSettingsPageLazy = lazy(() => import('./pages/secondary/PersonalListsSettingsPage'))
const SearchPageLazy = lazy(() => import('./pages/secondary/SearchPage'))
const SettingsPageLazy = lazy(() => import('./pages/secondary/SettingsPage'))
const TranslationPageLazy = lazy(() => import('./pages/secondary/TranslationPage'))
@@ -81,6 +82,7 @@ const ROUTES = [
{ path: '/settings/translation', element: SR(TranslationPageLazy) },
{ path: '/settings/rss-feeds', element: SR(RssFeedSettingsPageLazy) },
{ path: '/settings/follow-sets', element: SR(FollowSetsSettingsPageLazy) },
+ { path: '/settings/personal-lists', element: SR(PersonalListsSettingsPageLazy) },
{ path: '/profile-editor', element: SR(ProfileEditorPageLazy) },
{ path: '/mutes', element: SR(MuteListPageLazy) },
{ path: '/follow-packs', element: SR(FollowPacksRedirectLazy) }
diff --git a/src/services/navigation.service.ts b/src/services/navigation.service.ts
index 11055c62..085a827b 100644
--- a/src/services/navigation.service.ts
+++ b/src/services/navigation.service.ts
@@ -16,6 +16,8 @@ import GeneralSettingsPage from '@/pages/secondary/GeneralSettingsPage'
import TranslationPage from '@/pages/secondary/TranslationPage'
import RssFeedSettingsPage from '@/pages/secondary/RssFeedSettingsPage'
import FollowSetsSettingsPage from '@/pages/secondary/FollowSetsSettingsPage'
+import CacheSettingsPage from '@/pages/secondary/CacheSettingsPage'
+import PersonalListsSettingsPage from '@/pages/secondary/PersonalListsSettingsPage'
import NotePage from '@/pages/secondary/NotePage'
import SecondaryProfilePage from '@/pages/secondary/ProfilePage'
import FollowingListPage from '@/pages/secondary/FollowingListPage'
@@ -68,13 +70,26 @@ export class URLParser {
}
static getSettingsSubPageType(url: string): string {
- if (url.includes('/general')) return 'general'
- if (url.includes('/relays')) return 'relays'
- if (url.includes('/wallet')) return 'wallet'
- if (url.includes('/posts')) return 'posts'
- if (url.includes('/translation')) return 'translation'
- if (url.includes('/rss-feeds')) return 'rss-feeds'
- return 'general'
+ try {
+ const pathOnly = url.split('?')[0].split('#')[0]
+ const parts = pathOnly.split('/').filter(Boolean)
+ if (parts[0] !== 'settings') return 'general'
+ const sub = parts[1] ?? ''
+ const known = new Set([
+ 'general',
+ 'relays',
+ 'wallet',
+ 'posts',
+ 'translation',
+ 'rss-feeds',
+ 'follow-sets',
+ 'cache',
+ 'personal-lists'
+ ])
+ return known.has(sub) ? sub : 'general'
+ } catch {
+ return 'general'
+ }
}
}
@@ -134,6 +149,10 @@ export class ComponentFactory {
return React.createElement(RssFeedSettingsPage, { index: 0, hideTitlebar: true })
case 'follow-sets':
return React.createElement(FollowSetsSettingsPage, { index: 0, hideTitlebar: true })
+ case 'cache':
+ return React.createElement(CacheSettingsPage, { index: 0, hideTitlebar: true })
+ case 'personal-lists':
+ return React.createElement(PersonalListsSettingsPage, { index: 0, hideTitlebar: true })
default:
return React.createElement(GeneralSettingsPage, { index: 0, hideTitlebar: true })
}
diff --git a/src/services/rss-feed.service.ts b/src/services/rss-feed.service.ts
index 1651c92c..713aa770 100644
--- a/src/services/rss-feed.service.ts
+++ b/src/services/rss-feed.service.ts
@@ -1702,6 +1702,61 @@ class RssFeedService {
}
}
+ /**
+ * All RSS rows for these feed URLs from IndexedDB only (no network).
+ * Lets the RSS+Web UI search and filter against the full persisted cache.
+ */
+ async getCachedItemsForFeedUrls(feedUrls: string[]): Promise {
+ if (feedUrls.length === 0) return []
+ await this.ensureRssFeedAttemptedKeysLoaded()
+ try {
+ const allCachedItems = await indexedDb.getRssFeedItems()
+ const normalizedRequestedUrls = new Set(feedUrls.map((u) => this.normalizeRssFeedKeyUrl(u)))
+ let cachedItems = allCachedItems.filter((item) =>
+ normalizedRequestedUrls.has(this.normalizeRssFeedKeyUrl(item.feedUrl))
+ )
+ cachedItems = cachedItems.map((item) => ({
+ ...item,
+ pubDate: this.parseItemPubDate(item)
+ }))
+ cachedItems.sort((a, b) => {
+ const dateA = a.pubDate?.getTime() || 0
+ const dateB = b.pubDate?.getTime() || 0
+ return dateB - dateA
+ })
+ return cachedItems
+ } catch (error) {
+ logger.warn('[RssFeedService] getCachedItemsForFeedUrls failed', { error })
+ return []
+ }
+ }
+
+ /**
+ * Dedupe by normalized feed URL + guid; when both have pubDate, keep the newer row.
+ */
+ mergeRssFeedItemLists(lists: RssFeedItem[][]): RssFeedItem[] {
+ const map = new Map()
+ for (const list of lists) {
+ for (const item of list) {
+ const key = `${this.normalizeRssFeedKeyUrl(item.feedUrl)}:${item.guid}`
+ const existing = map.get(key)
+ const nextDate = this.parseItemPubDate(item)?.getTime() ?? 0
+ const prevDate = existing ? this.parseItemPubDate(existing)?.getTime() ?? 0 : -1
+ if (!existing || nextDate >= prevDate) {
+ map.set(key, {
+ ...item,
+ pubDate: this.parseItemPubDate(item)
+ })
+ }
+ }
+ }
+ return Array.from(map.values()).sort((a, b) => {
+ const dateA = a.pubDate?.getTime() || 0
+ const dateB = b.pubDate?.getTime() || 0
+ return dateB - dateA
+ })
+ }
+
/**
* Clear cache for a specific feed or all feeds
*/