-
-
-
- QUOTE.VOTE
-
-
-
- Empowering thoughtful discourse through democratic, ad-free conversations.
+ {/* ── Donation ─────────────────────────────────────────── */}
+
+
+ {/* Col 1 */}
+
+
Join us in creating a truly
open and equal community.
+
+ Quote.Vote is a nonprofit, open-source platform.
Every contribution goes directly toward keeping
the service free and supporting communities.
-
-
-
- {/* About */}
-
-
- {/* Quick Links */}
-
-
- Quick Links
-
-
- {quickLinks.map(({ href, label, external }) => (
- -
-
- {label}
-
-
- ))}
-
-
-
- {/* Resources */}
-
-
- Resources
-
-
- {resourceLinks.map(({ href, label }) => (
- -
-
- {label}
-
-
- ))}
-
+
+
+
+ Request Invite
+
+
- {/* Connect */}
-
-
- Connect
-
-
- {socialLinks.map(({ href, Icon, label }) => (
-
-
-
- ))}
-
+ {/* Col 2 */}
+
+
Please be in touch!
+
+
+
+
No spam, ever. Unsubscribe anytime.
- {/* Bottom row */}
-
-
- © {new Date().getFullYear()} Quote.Vote. All rights reserved.
-
-
- Made with ❤️ on Earth
+ {/* Col 3 */}
+
+
💚
+
Help keep Quote.Vote
free for everyone.
+
+ Your donation supports development,
moderation, and hosting.
-
-
-
- >
-
- );
-}
-
-// ── Hero Search Bar ────────────────────────────────────────────────────────
-
-interface HeroSearchProps {
- router: ReturnType
;
-}
-
-function HeroSearch({ router }: HeroSearchProps) {
- const [query, setQuery] = useState('');
- const [isOpen, setIsOpen] = useState(false);
- const containerRef = useRef(null);
-
- const debouncedQuery = useDebounce(query, 300);
-
- const { data, loading, error } = useQuery<{
- posts: { entities: ContentResult[] };
- searchUser: CreatorResult[];
- }>(SEARCH, {
- variables: { text: debouncedQuery },
- skip: !debouncedQuery.trim(),
- });
-
- const contentResults: ContentResult[] = data?.posts?.entities ?? [];
- const creatorResults: CreatorResult[] = data?.searchUser ?? [];
- const hasResults = contentResults.length > 0 || creatorResults.length > 0;
- const showDropdown = isOpen && debouncedQuery.trim().length > 0;
-
- useEffect(() => {
- function handleClickOutside(e: MouseEvent) {
- if (containerRef.current && !containerRef.current.contains(e.target as Node)) {
- setIsOpen(false);
- }
- }
- document.addEventListener('mousedown', handleClickOutside);
- return () => document.removeEventListener('mousedown', handleClickOutside);
- }, []);
-
- const handleSubmit = (e: React.FormEvent) => {
- e.preventDefault();
- if (query.trim()) {
- setIsOpen(true);
- }
- };
-
- const handleKeyDown = (e: React.KeyboardEvent) => {
- if (e.key === 'Escape') setIsOpen(false);
- };
-
- const handlePostClick = (item: ContentResult) => {
- setIsOpen(false);
- if (item.url) {
- router.push(toAppPostUrl(item.url));
- return;
- }
- router.push(`/dashboard/explore?q=${encodeURIComponent(item.title)}`);
- };
-
- const handleCreatorClick = (creator: CreatorResult) => {
- setIsOpen(false);
- const profileSlug = creator.username || creator._id;
- router.push(`/dashboard/profile/${profileSlug}`);
- };
-
- return (
-
-
-
- {showDropdown && (
-
- {loading && (
-
- {[1, 2, 3].map((i) => (
-
- ))}
-
- )}
-
- {error && !loading && (
-
-
- Search unavailable. Please try again.
-
-
- )}
-
- {!loading && !error && !hasResults && (
-
-
- No results for “{debouncedQuery}”
-
-
Try a different search term
-
- )}
-
- {!loading && !error && hasResults && (
-
- {contentResults.length > 0 && (
- <>
-
- Posts
-
- {contentResults.slice(0, 5).map((item) => (
-
- ))}
- >
- )}
-
- {creatorResults.length > 0 && (
- <>
-
- People
-
- {creatorResults.slice(0, 3).map((creator) => (
-
- ))}
- >
- )}
-
-
-
-
-
- )}
-
- )}
-
- );
-}
-
-// ── Featured Posts Section ─────────────────────────────────────────────────
-
-interface FeaturedPostsData {
- featuredPosts: {
- entities: Post[];
- pagination: { total_count: number; limit: number; offset: number };
- };
-}
-
-// Capture once at module load — stable across renders, avoids impure-render lint
-const MODULE_NOW = typeof window !== 'undefined' ? Date.now() : 0;
-
-function FeaturedPostsSection() {
- const [activeIndex, setActiveIndex] = useState(0);
- const scrollRef = useRef(null);
- const now = MODULE_NOW;
-
- const { data, loading } = useQuery(GET_FEATURED_POSTS, {
- variables: { limit: 10, offset: 0 },
- fetchPolicy: 'cache-first',
- errorPolicy: 'all',
- });
-
- const posts = data?.featuredPosts?.entities || [];
-
- const scrollToIndex = (index: number) => {
- const container = scrollRef.current;
- if (!container) return;
- const cards = container.children;
- if (cards[index]) {
- (cards[index] as HTMLElement).scrollIntoView({
- behavior: 'smooth',
- block: 'nearest',
- inline: 'center',
- });
- }
- setActiveIndex(index);
- };
-
- const handlePrev = () => {
- const next = Math.max(0, activeIndex - 1);
- scrollToIndex(next);
- };
-
- const handleNext = () => {
- const next = Math.min(posts.length - 1, activeIndex + 1);
- scrollToIndex(next);
- };
-
- // Update active index on scroll
- useEffect(() => {
- const container = scrollRef.current;
- if (!container) return;
-
- const handleScroll = () => {
- const { scrollLeft, clientWidth } = container;
- const cardWidth = clientWidth * 0.85;
- const index = Math.round(scrollLeft / cardWidth);
- setActiveIndex(Math.min(index, posts.length - 1));
- };
-
- container.addEventListener('scroll', handleScroll, { passive: true });
- return () => container.removeEventListener('scroll', handleScroll);
- }, [posts.length]);
-
- if (loading || posts.length === 0) return null;
-
- return (
-
-
- {/* Header */}
-
-
- Community Highlights
-
-
- Featured{' '}
-
- Posts
-
-
-
- See what the community is talking about right now.
-
-
-
-
- {/* Navigation arrows */}
- {posts.length > 1 && (
- <>
-
-
- >
- )}
-
- {/* Scrollable card row */}
-
- {posts.map((post) => (
-
- ))}
+
+
+ Donate Today
+
+
+ Learn more about donations →
+
-
- {/* Dot indicators */}
- {posts.length > 1 && (
-
- {posts.map((post, i) => (
-
- )}
-
-
-
- Join to see more and participate
-
-
-
-
- );
-}
-
-function formatTimeAgo(created: string | undefined, now: number): string {
- if (!created) return '';
- const diff = now - new Date(created).getTime();
- const days = Math.floor(diff / 86400000);
- if (days < 1) return 'Today';
- if (days === 1) return '1d ago';
- if (days < 30) return `${days}d ago`;
- const months = Math.floor(days / 30);
- return months === 1 ? '1mo ago' : `${months}mo ago`;
-}
-
-function FeaturedPostCard({ post, timeAgo }: { post: Post; timeAgo: string }) {
- const creatorName = post.creator?.name || post.creator?.username || 'Anonymous';
- const username = post.creator?.username || 'anonymous';
- const upvotes = post.upvotes ?? 0;
- const downvotes = post.downvotes ?? 0;
- const commentCount = post.comments?.length || 0;
- const quoteCount = post.quotes?.length || 0;
+
- const displayText = post.text
- ? post.text.length > 200
- ? post.text.slice(0, 200) + '...'
- : post.text
- : '';
+ {/* ── Community Strip Image ─────────────────────────────────── */}
+
- return (
-
-
- {/* Header: author + star badge */}
-
-
- {creatorName[0]?.toUpperCase() || '?'}
-
-
-
{creatorName}
-
- @{username} · {timeAgo}
-
-
-
-
-
- Featured
-
+ {/* ── Footer ──────────────────────────────────────── */}
+