111 lines
3.6 KiB
TypeScript
111 lines
3.6 KiB
TypeScript
import React, { useRef, useEffect } from 'react';
|
|
import { Feed, FeedEntry } from '../../types';
|
|
import { Star, ChevronRight } from 'lucide-react';
|
|
|
|
interface StarredTabViewProps {
|
|
entries: FeedEntry[];
|
|
feeds: Feed[];
|
|
onSelectFeedFilter: (feedId?: string) => void;
|
|
onOpenAllStarred: () => void;
|
|
savedScrollTop?: number;
|
|
onScrollPosChange?: (pos: number) => void;
|
|
}
|
|
|
|
export const StarredTabView: React.FC<StarredTabViewProps> = ({
|
|
entries,
|
|
feeds,
|
|
onSelectFeedFilter,
|
|
onOpenAllStarred,
|
|
savedScrollTop = 0,
|
|
onScrollPosChange,
|
|
}) => {
|
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
|
|
// Restore scroll position on mount
|
|
useEffect(() => {
|
|
if (containerRef.current && savedScrollTop > 0) {
|
|
containerRef.current.scrollTop = savedScrollTop;
|
|
}
|
|
}, []);
|
|
|
|
const handleAllStarredClick = () => {
|
|
if (containerRef.current && onScrollPosChange) {
|
|
onScrollPosChange(containerRef.current.scrollTop);
|
|
}
|
|
onOpenAllStarred();
|
|
};
|
|
|
|
const handleFeedClick = (feedId: string) => {
|
|
if (containerRef.current && onScrollPosChange) {
|
|
onScrollPosChange(containerRef.current.scrollTop);
|
|
}
|
|
onSelectFeedFilter(feedId);
|
|
};
|
|
|
|
const starredEntries = entries.filter((e) => e.is_starred);
|
|
|
|
// Group starred count by feed
|
|
const starredCountsByFeed: Record<string, number> = {};
|
|
starredEntries.forEach((e) => {
|
|
starredCountsByFeed[e.feed_id] = (starredCountsByFeed[e.feed_id] || 0) + 1;
|
|
});
|
|
|
|
const feedsWithStarred = feeds.filter((f) => starredCountsByFeed[f.id] > 0);
|
|
|
|
return (
|
|
<div
|
|
ref={containerRef}
|
|
style={{
|
|
flex: 1,
|
|
overflowY: 'auto',
|
|
WebkitOverflowScrolling: 'touch',
|
|
padding: '1rem',
|
|
paddingBottom: 'calc(var(--bottom-nav-height) + 1.5rem)',
|
|
}}
|
|
>
|
|
<div style={{ marginBottom: '1.25rem' }}>
|
|
<h2 style={{ fontSize: '1.2rem', fontWeight: 800, color: 'var(--text-primary)' }}>Starred Items</h2>
|
|
<div style={{ fontSize: '0.82rem', color: 'var(--text-muted)' }}>
|
|
{starredEntries.length} saved articles across {feedsWithStarred.length} sources
|
|
</div>
|
|
</div>
|
|
|
|
<div className="settings-section-card" style={{ padding: '0.5rem' }}>
|
|
<div
|
|
className="sidebar-item"
|
|
style={{ padding: '0.75rem 0.6rem', borderBottom: '1px solid var(--border-light)' }}
|
|
onClick={handleAllStarredClick}
|
|
>
|
|
<div className="sidebar-item-left">
|
|
<Star size={18} color="var(--star-gold)" fill="var(--star-gold)" />
|
|
<span style={{ fontWeight: 600, fontSize: '0.9rem', color: 'var(--text-primary)' }}>
|
|
All Starred Articles
|
|
</span>
|
|
</div>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: '0.4rem' }}>
|
|
<span className="badge badge-starred">{starredEntries.length}</span>
|
|
<ChevronRight size={16} color="var(--text-dim)" />
|
|
</div>
|
|
</div>
|
|
|
|
{feedsWithStarred.map((feed) => (
|
|
<div
|
|
key={feed.id}
|
|
className="sidebar-item"
|
|
style={{ padding: '0.75rem 0.6rem', borderBottom: '1px solid var(--border-light)' }}
|
|
onClick={() => handleFeedClick(feed.id)}
|
|
>
|
|
<div className="sidebar-item-left">
|
|
<span style={{ fontSize: '0.88rem', color: 'var(--text-primary)' }}>{feed.title}</span>
|
|
</div>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: '0.4rem' }}>
|
|
<span className="badge badge-starred">{starredCountsByFeed[feed.id]}</span>
|
|
<ChevronRight size={16} color="var(--text-dim)" />
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|