aboutsummaryrefslogtreecommitdiffhomepage
path: root/frontend/src
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2025-07-13 02:07:34 +0900
committernsfisis <nsfisis@gmail.com>2025-07-13 02:07:34 +0900
commita43e3db41633e149736a2153dbc97979a723771e (patch)
treed14a5138700f2401ad62ce07ffd4c11ee8595cb2 /frontend/src
parent463f0414fcedff732cfdc38d369c394b8a05be7a (diff)
downloadfeedaka-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.tsx81
-rw-r--r--frontend/src/pages/Settings.tsx130
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>
);