diff --git a/src/components/RssFeedItem/index.tsx b/src/components/RssFeedItem/index.tsx index b8723cf1..45196696 100644 --- a/src/components/RssFeedItem/index.tsx +++ b/src/components/RssFeedItem/index.tsx @@ -16,6 +16,7 @@ import MediaPlayer from '@/components/MediaPlayer' import { Drawer, DrawerContent, DrawerHeader, DrawerTitle } from '@/components/ui/drawer' import { useScreenSize } from '@/providers/ScreenSizeProvider' import { useSmartRssArticleNavigation } from '@/PageManager' +import { getStandardRssFeedProfile } from '@/lib/standard-rss-feed-url' /** * Convert HTML to plain text by extracting text content and cleaning up whitespace @@ -401,16 +402,26 @@ export default function RssFeedItem({ setSelectedText('') } - // Format feed source name from URL + const standardFeedProfile = useMemo( + () => (isWebFaux ? null : getStandardRssFeedProfile(item.feedUrl)), + [item.feedUrl, isWebFaux] + ) + + // Format feed source name from URL (known shapes get a translated label) const feedSourceName = useMemo(() => { if (isWebFaux) return '' + if (standardFeedProfile) { + return t(standardFeedProfile.labelKey, { + defaultValue: standardFeedProfile.defaultLabel + }) + } try { const url = new URL(item.feedUrl) return url.hostname.replace(/^www\./, '') } catch { return item.feedTitle || 'RSS Feed' } - }, [item.feedUrl, item.feedTitle, isWebFaux]) + }, [item.feedUrl, item.feedTitle, isWebFaux, standardFeedProfile, t]) // Clean and parse HTML description safely // Decode HTML entities and remove any XML artifacts that might have leaked through @@ -586,6 +597,12 @@ export default function RssFeedItem({

{isWebFaux ? t('Web page') : item.feedTitle || feedSourceName}

+ {!isWebFaux && standardFeedProfile && item.feedTitle ? ( +

+ {feedSourceName} + {standardFeedProfile.detail ? ` · ${standardFeedProfile.detail}` : ''} +

+ ) : null} {item.feedDescription && (

{item.feedDescription} diff --git a/src/components/RssFeedList/index.tsx b/src/components/RssFeedList/index.tsx index 0be4cc4b..8bcfe102 100644 --- a/src/components/RssFeedList/index.tsx +++ b/src/components/RssFeedList/index.tsx @@ -43,6 +43,11 @@ import { import { useScreenSize } from '@/providers/ScreenSizeProvider' import { Check, ChevronDown } from 'lucide-react' import { normalizeHttpArticleUrl } from '@/lib/rss-article' +import { + getRssFeedUrlHostname, + getStandardRssFeedProfile +} from '@/lib/standard-rss-feed-url' +import { StandardRssFeedUrlInline } from '@/components/StandardRssFeedUrlRow' function ManualRssUrlAddRow({ className, @@ -436,15 +441,22 @@ export default function RssFeedList() { // Normalize URLs to prevent duplicates (e.g., with/without trailing slash) const availableFeeds = useMemo(() => { const feedMap = new Map() - - items.forEach(item => { + + items.forEach((item) => { const normalizedUrl = normalizeFeedUrl(item.feedUrl) if (!feedMap.has(normalizedUrl)) { - feedMap.set(normalizedUrl, { url: normalizedUrl, title: item.feedTitle || item.feedUrl }) + const profile = getStandardRssFeedProfile(normalizedUrl) + const fallback = profile + ? t(profile.labelKey, { defaultValue: profile.defaultLabel }) + : getRssFeedUrlHostname(normalizedUrl) + feedMap.set(normalizedUrl, { + url: normalizedUrl, + title: item.feedTitle?.trim() || fallback + }) } }) return Array.from(feedMap.values()) - }, [items]) + }, [items, t]) // Helper function to truncate text const truncateText = (text: string, maxLength: number): string => { @@ -882,8 +894,12 @@ export default function RssFeedList() {

{isChecked && }
-