From e3c379d069ffa9661561d25cdbf2f5894a2f8ee8 Mon Sep 17 00:00:00 2001 From: Adam Mathes Date: Sat, 14 Feb 2026 08:58:38 -0800 Subject: Refactor: project structure, implement dependency injection, and align v2 UI with v1 --- frontend/src/components/FeedList.tsx | 246 ++++++++++++++++++++--------------- 1 file changed, 138 insertions(+), 108 deletions(-) (limited to 'frontend/src/components/FeedList.tsx') diff --git a/frontend/src/components/FeedList.tsx b/frontend/src/components/FeedList.tsx index 56c96cd..497baf8 100644 --- a/frontend/src/components/FeedList.tsx +++ b/frontend/src/components/FeedList.tsx @@ -3,121 +3,151 @@ import { Link, useNavigate, useSearchParams, useLocation, useParams } from 'reac import type { Feed, Category } from '../types'; import './FeedList.css'; -export default function FeedList({ theme, setTheme }: { theme: string, setTheme: (t: string) => void }) { - const [feeds, setFeeds] = useState([]); - const [tags, setTags] = useState([]); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(''); - const [feedsExpanded, setFeedsExpanded] = useState(false); - const [searchQuery, setSearchQuery] = useState(''); - const navigate = useNavigate(); - const [searchParams] = useSearchParams(); - const location = useLocation(); - const { feedId, tagName } = useParams(); +export default function FeedList({ + theme, + setTheme, +}: { + theme: string; + setTheme: (t: string) => void; +}) { + const [feeds, setFeeds] = useState([]); + const [tags, setTags] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(''); + const [feedsExpanded, setFeedsExpanded] = useState(false); + const [searchQuery, setSearchQuery] = useState(''); + const navigate = useNavigate(); + const [searchParams] = useSearchParams(); + const location = useLocation(); + const { feedId, tagName } = useParams(); - const currentFilter = searchParams.get('filter') || (location.pathname === '/' && !feedId && !tagName ? 'unread' : ''); + const currentFilter = + searchParams.get('filter') || + (location.pathname === '/' && !feedId && !tagName ? 'unread' : ''); - const handleSearch = (e: React.FormEvent) => { - e.preventDefault(); - if (searchQuery.trim()) { - navigate(`/?q=${encodeURIComponent(searchQuery.trim())}`); - } - }; + const handleSearch = (e: React.FormEvent) => { + e.preventDefault(); + if (searchQuery.trim()) { + navigate(`/?q=${encodeURIComponent(searchQuery.trim())}`); + } + }; - const toggleFeeds = () => { - setFeedsExpanded(!feedsExpanded); - }; + const toggleFeeds = () => { + setFeedsExpanded(!feedsExpanded); + }; - useEffect(() => { - Promise.all([ - fetch('/api/feed/').then(res => { - if (!res.ok) throw new Error('Failed to fetch feeds'); - return res.json(); - }), - fetch('/api/tag').then(res => { - if (!res.ok) throw new Error('Failed to fetch tags'); - return res.json(); - }) - ]) - .then(([feedsData, tagsData]) => { - setFeeds(feedsData); - setTags(tagsData); - setLoading(false); - }) - .catch((err) => { - setError(err.message); - setLoading(false); - }); - }, []); + useEffect(() => { + Promise.all([ + fetch('/api/feed/').then((res) => { + if (!res.ok) throw new Error('Failed to fetch feeds'); + return res.json(); + }), + fetch('/api/tag').then((res) => { + if (!res.ok) throw new Error('Failed to fetch tags'); + return res.json(); + }), + ]) + .then(([feedsData, tagsData]) => { + setFeeds(feedsData); + setTags(tagsData); + setLoading(false); + }) + .catch((err) => { + setError(err.message); + setLoading(false); + }); + }, []); - if (loading) return
Loading feeds...
; - if (error) return
Error: {error}
; + if (loading) return
Loading feeds...
; + if (error) return
Error: {error}
; - return ( -
-
-
- setSearchQuery(e.target.value)} - className="search-input" - /> -
-
-
-
    -
  • Unread
  • -
  • All
  • -
  • Starred
  • -
-
-
-

- {feedsExpanded ? '▼' : '▶'} Feeds -

- {feedsExpanded && ( - feeds.length === 0 ? ( -

No feeds found.

- ) : ( -
    - {feeds.map((feed) => ( -
  • - - {feed.title || feed.url} - - {feed.category && {feed.category}} -
  • - ))} -
- ) - )} -
+ return ( +
+
+
+ setSearchQuery(e.target.value)} + className="search-input" + /> +
+
+
+
    +
  • + + Unread + +
  • +
  • + + All + +
  • +
  • + + Starred + +
  • +
+
+
+

+ {feedsExpanded ? '▼' : '▶'} Feeds +

+ {feedsExpanded && + (feeds.length === 0 ? ( +

No feeds found.

+ ) : ( +
    + {feeds.map((feed) => ( +
  • + + {feed.title || feed.url} + + {feed.category && {feed.category}} +
  • + ))} +
+ ))} +
- {tags && tags.length > 0 && ( -
-

Tags

-
    - {tags.map((tag) => ( -
  • - - {tag.title} - -
  • - ))} -
-
- )} + {tags && tags.length > 0 && ( +
+

Tags

+
    + {tags.map((tag) => ( +
  • + + {tag.title} + +
  • + ))} +
+
+ )} -
-

Themes

-
- - - -
-
+
+
+ + +
- ); +
+
+ ); } -- cgit v1.2.3