Add time filter pills to RSS feed widget
Adds since query param (1h/24h/7d/30d) to the RSS API route and a matching TimeFilterPills row in the NewsFeed UI so users can narrow the feed to recent items. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,7 @@ export async function GET(req: NextRequest) {
|
||||
const q = searchParams.get('q');
|
||||
const feedId = searchParams.get('feed_id');
|
||||
const bookmarked = searchParams.get('bookmarked');
|
||||
const since = searchParams.get('since');
|
||||
const limit = parseInt(searchParams.get('limit') || '50', 10);
|
||||
const offset = parseInt(searchParams.get('offset') || '0', 10);
|
||||
|
||||
@@ -27,6 +28,19 @@ export async function GET(req: NextRequest) {
|
||||
if (bookmarked === '1') {
|
||||
conditions.push('i.bookmarked = 1');
|
||||
}
|
||||
if (since) {
|
||||
const sinceMap: Record<string, string> = {
|
||||
'1h': '-1 hours',
|
||||
'24h': '-24 hours',
|
||||
'7d': '-7 days',
|
||||
'30d': '-30 days',
|
||||
};
|
||||
const modifier = sinceMap[since];
|
||||
if (modifier) {
|
||||
conditions.push("i.pub_date >= datetime('now', ?)");
|
||||
params.push(modifier);
|
||||
}
|
||||
}
|
||||
|
||||
const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
|
||||
|
||||
|
||||
@@ -150,6 +150,51 @@ function FeedFilterPills({
|
||||
);
|
||||
}
|
||||
|
||||
type TimeFilter = '1h' | '24h' | '7d' | '30d';
|
||||
|
||||
const TIME_FILTER_OPTIONS: { value: TimeFilter; label: string }[] = [
|
||||
{ value: '1h', label: '1h' },
|
||||
{ value: '24h', label: '24h' },
|
||||
{ value: '7d', label: '7d' },
|
||||
{ value: '30d', label: '30d' },
|
||||
];
|
||||
|
||||
function TimeFilterPills({
|
||||
active,
|
||||
onSelect,
|
||||
}: {
|
||||
active: TimeFilter | null;
|
||||
onSelect: (v: TimeFilter | null) => void;
|
||||
}) {
|
||||
return (
|
||||
<div className="flex gap-1.5 overflow-x-auto pb-1 scrollbar-none">
|
||||
<button
|
||||
onClick={() => onSelect(null)}
|
||||
className={`shrink-0 px-2.5 py-1 rounded-full text-[11px] font-medium transition-colors ${
|
||||
active === null
|
||||
? 'bg-neutral-700 text-white'
|
||||
: 'bg-neutral-800/60 text-neutral-400 hover:text-neutral-200'
|
||||
}`}
|
||||
>
|
||||
Any time
|
||||
</button>
|
||||
{TIME_FILTER_OPTIONS.map((opt) => (
|
||||
<button
|
||||
key={opt.value}
|
||||
onClick={() => onSelect(opt.value)}
|
||||
className={`shrink-0 px-2.5 py-1 rounded-full text-[11px] font-medium transition-colors ${
|
||||
active === opt.value
|
||||
? 'bg-neutral-700 text-white'
|
||||
: 'bg-neutral-800/60 text-neutral-400 hover:text-neutral-200'
|
||||
}`}
|
||||
>
|
||||
{opt.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function AddFeedForm({ onAdded }: { onAdded: () => void }) {
|
||||
const [url, setUrl] = useState('');
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
@@ -332,6 +377,7 @@ export function NewsFeed() {
|
||||
const [feeds, setFeeds] = useState<Feed[]>([]);
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [activeFeedId, setActiveFeedId] = useState<number | null>(null);
|
||||
const [timeFilter, setTimeFilter] = useState<TimeFilter | null>(null);
|
||||
const [showBookmarked, setShowBookmarked] = useState(false);
|
||||
const [showFeedManager, setShowFeedManager] = useState(false);
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -354,6 +400,7 @@ export function NewsFeed() {
|
||||
if (searchQuery) params.set('q', searchQuery);
|
||||
if (!opts?.resetFeedFilter && activeFeedId !== null) params.set('feed_id', String(activeFeedId));
|
||||
if (showBookmarked) params.set('bookmarked', '1');
|
||||
if (timeFilter) params.set('since', timeFilter);
|
||||
params.set('limit', '50');
|
||||
|
||||
const res = await fetch(`/api/rss?${params}`);
|
||||
@@ -373,7 +420,7 @@ export function NewsFeed() {
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [searchQuery, activeFeedId, showBookmarked]);
|
||||
}, [searchQuery, activeFeedId, showBookmarked, timeFilter]);
|
||||
|
||||
// Initial load + refresh
|
||||
useEffect(() => {
|
||||
@@ -469,6 +516,13 @@ export function NewsFeed() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Time filter pills */}
|
||||
{hasFeeds && (
|
||||
<div className="shrink-0 mb-2">
|
||||
<TimeFilterPills active={timeFilter} onSelect={setTimeFilter} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Feed manager (collapsible) */}
|
||||
{showFeedManager && (
|
||||
<div className="shrink-0 mb-3">
|
||||
|
||||
Reference in New Issue
Block a user