diff options
| author | nsfisis <nsfisis@gmail.com> | 2025-07-13 02:07:34 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2025-07-13 02:07:34 +0900 |
| commit | a43e3db41633e149736a2153dbc97979a723771e (patch) | |
| tree | d14a5138700f2401ad62ce07ffd4c11ee8595cb2 /frontend/src | |
| parent | 463f0414fcedff732cfdc38d369c394b8a05be7a (diff) | |
| download | feedaka-a43e3db41633e149736a2153dbc97979a723771e.tar.gz feedaka-a43e3db41633e149736a2153dbc97979a723771e.tar.zst feedaka-a43e3db41633e149736a2153dbc97979a723771e.zip | |
feat(frontend): remove bulk edit form
Diffstat (limited to 'frontend/src')
| -rw-r--r-- | frontend/src/components/FeedList.tsx | 81 | ||||
| -rw-r--r-- | frontend/src/pages/Settings.tsx | 130 |
2 files changed, 25 insertions, 186 deletions
diff --git a/frontend/src/components/FeedList.tsx b/frontend/src/components/FeedList.tsx index e5b6751..db12b13 100644 --- a/frontend/src/components/FeedList.tsx +++ b/frontend/src/components/FeedList.tsx @@ -1,8 +1,4 @@ -import { - faCheckDouble, - faCircle, - faTrash, -} from "@fortawesome/free-solid-svg-icons"; +import { faCheck, faCircle, faTrash } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { useMutation, useQuery } from "urql"; import { @@ -14,15 +10,9 @@ import { interface Props { onFeedUnsubscribed?: () => void; - selectedFeeds?: Set<string>; - onSelectFeed?: (feedId: string, selected: boolean) => void; } -export function FeedList({ - onFeedUnsubscribed, - selectedFeeds, - onSelectFeed, -}: Props) { +export function FeedList({ onFeedUnsubscribed }: Props) { const [{ data, fetching, error }] = useQuery({ query: GetFeedsDocument, }); @@ -59,62 +49,27 @@ export function FeedList({ return ( <div className="space-y-4 p-4"> {data.feeds.map((feed) => { - const unreadCount = feed.articles.filter((a) => !a.isRead).length; - const totalCount = feed.articles.length; - - const isSelected = selectedFeeds?.has(feed.id) ?? false; - return ( <div key={feed.id} - className={`rounded-lg border p-4 shadow-sm ${ - isSelected - ? "border-blue-300 bg-blue-50" - : "border-gray-200 bg-white" - }`} + className="rounded-lg border border-gray-200 bg-white p-4 shadow-sm" > <div className="flex items-start justify-between"> - {selectedFeeds && onSelectFeed && ( - <div className="flex items-start gap-3"> - <input - type="checkbox" - checked={isSelected} - onChange={(e) => onSelectFeed(feed.id, e.target.checked)} - className="mt-1 rounded border-gray-300 text-blue-600 focus:ring-blue-500" - /> - <div className="flex-1"> - <h3 className="text-lg font-semibold text-gray-900"> - {feed.title} - </h3> - <p className="mt-1 text-sm text-gray-500">{feed.url}</p> - <div className="mt-2 flex items-center gap-4 text-sm"> - <span className="text-gray-600"> - {unreadCount} unread / {totalCount} total - </span> - <span className="text-gray-400"> - Last fetched:{" "} - {new Date(feed.fetchedAt).toLocaleString()} - </span> - </div> - </div> + <div className="flex-1"> + <h3 className="text-lg font-semibold text-gray-900"> + {feed.title} + </h3> + <p className="mt-1 text-sm text-gray-500"> + <a href={feed.url} target="_blank" rel="noreferrer"> + {feed.url} + </a> + </p> + <div className="mt-2 flex items-center gap-4 text-sm"> + <span className="text-gray-400"> + Last fetched: {new Date(feed.fetchedAt).toLocaleString()} + </span> </div> - )} - {(!selectedFeeds || !onSelectFeed) && ( - <div className="flex-1"> - <h3 className="text-lg font-semibold text-gray-900"> - {feed.title} - </h3> - <p className="mt-1 text-sm text-gray-500">{feed.url}</p> - <div className="mt-2 flex items-center gap-4 text-sm"> - <span className="text-gray-600"> - {unreadCount} unread / {totalCount} total - </span> - <span className="text-gray-400"> - Last fetched: {new Date(feed.fetchedAt).toLocaleString()} - </span> - </div> - </div> - )} + </div> <div className="flex items-center gap-2"> <button type="button" @@ -122,7 +77,7 @@ export function FeedList({ className="rounded p-2 text-gray-600 hover:bg-gray-100 hover:text-gray-900" title="Mark all as read" > - <FontAwesomeIcon icon={faCheckDouble} /> + <FontAwesomeIcon icon={faCheck} /> </button> <button type="button" diff --git a/frontend/src/pages/Settings.tsx b/frontend/src/pages/Settings.tsx index 81c90e0..b10cbf1 100644 --- a/frontend/src/pages/Settings.tsx +++ b/frontend/src/pages/Settings.tsx @@ -1,22 +1,11 @@ -import { useState } from "react"; -import { useMutation, useQuery } from "urql"; +import { useQuery } from "urql"; import { AddFeedForm, FeedList } from "../components"; -import { - GetFeedsDocument, - MarkFeedReadDocument, - MarkFeedUnreadDocument, - UnsubscribeFeedDocument, -} from "../graphql/generated/graphql"; +import { GetFeedsDocument } from "../graphql/generated/graphql"; export function Settings() { - const [{ data: feedsData }, refetchFeeds] = useQuery({ + const [, refetchFeeds] = useQuery({ query: GetFeedsDocument, }); - const [, markFeedRead] = useMutation(MarkFeedReadDocument); - const [, markFeedUnread] = useMutation(MarkFeedUnreadDocument); - const [, unsubscribeFeed] = useMutation(UnsubscribeFeedDocument); - - const [selectedFeeds, setSelectedFeeds] = useState<Set<string>>(new Set()); const handleFeedAdded = () => { refetchFeeds(); @@ -24,60 +13,8 @@ export function Settings() { const handleFeedUnsubscribed = () => { refetchFeeds(); - setSelectedFeeds(new Set()); - }; - - const handleSelectFeed = (feedId: string, selected: boolean) => { - const newSelection = new Set(selectedFeeds); - if (selected) { - newSelection.add(feedId); - } else { - newSelection.delete(feedId); - } - setSelectedFeeds(newSelection); - }; - - const handleSelectAll = () => { - if (!feedsData?.feeds) return; - if (selectedFeeds.size === feedsData.feeds.length) { - setSelectedFeeds(new Set()); - } else { - setSelectedFeeds(new Set(feedsData.feeds.map((feed) => feed.id))); - } - }; - - const handleBulkMarkRead = async () => { - const promises = Array.from(selectedFeeds).map((feedId) => - markFeedRead({ id: feedId }), - ); - await Promise.all(promises); - refetchFeeds(); }; - const handleBulkMarkUnread = async () => { - const promises = Array.from(selectedFeeds).map((feedId) => - markFeedUnread({ id: feedId }), - ); - await Promise.all(promises); - refetchFeeds(); - }; - - const handleBulkUnsubscribe = async () => { - const confirmed = window.confirm( - `Are you sure you want to unsubscribe from ${selectedFeeds.size} selected feeds?`, - ); - if (!confirmed) return; - - const promises = Array.from(selectedFeeds).map((feedId) => - unsubscribeFeed({ id: feedId }), - ); - await Promise.all(promises); - handleFeedUnsubscribed(); - }; - - const hasFeeds = feedsData?.feeds && feedsData.feeds.length > 0; - const hasSelectedFeeds = selectedFeeds.size > 0; - return ( <div className="mx-auto max-w-4xl"> <h1 className="mb-6 text-2xl font-bold text-gray-900">Feed Settings</h1> @@ -92,63 +29,10 @@ export function Settings() { {/* Manage Feeds Section */} <div className="mb-8"> - <div className="flex items-center justify-between mb-4"> - <h2 className="text-xl font-semibold text-gray-800">Manage Feeds</h2> - {hasFeeds && ( - <div className="flex items-center gap-4"> - <label className="flex items-center gap-2 text-sm text-gray-600"> - <input - type="checkbox" - checked={selectedFeeds.size === feedsData.feeds.length} - onChange={handleSelectAll} - className="rounded border-gray-300 text-blue-600 focus:ring-blue-500" - /> - Select All ({feedsData.feeds.length} feeds) - </label> - </div> - )} - </div> - - {/* Bulk Operations */} - {hasSelectedFeeds && ( - <div className="mb-4 rounded-lg bg-blue-50 border border-blue-200 p-4"> - <div className="flex items-center justify-between"> - <span className="text-sm font-medium text-blue-900"> - {selectedFeeds.size} feed{selectedFeeds.size > 1 ? "s" : ""}{" "} - selected - </span> - <div className="flex gap-2"> - <button - type="button" - onClick={handleBulkMarkRead} - className="rounded px-3 py-1 text-sm font-medium text-blue-700 hover:bg-blue-100" - > - Mark All Read - </button> - <button - type="button" - onClick={handleBulkMarkUnread} - className="rounded px-3 py-1 text-sm font-medium text-blue-700 hover:bg-blue-100" - > - Mark All Unread - </button> - <button - type="button" - onClick={handleBulkUnsubscribe} - className="rounded px-3 py-1 text-sm font-medium text-red-700 hover:bg-red-100" - > - Unsubscribe Selected - </button> - </div> - </div> - </div> - )} - - <FeedList - onFeedUnsubscribed={handleFeedUnsubscribed} - selectedFeeds={selectedFeeds} - onSelectFeed={handleSelectFeed} - /> + <h2 className="mb-4 text-xl font-semibold text-gray-800"> + Manage Feeds + </h2> + <FeedList onFeedUnsubscribed={handleFeedUnsubscribed} /> </div> </div> ); |
