● Zion Boggan
repos/Drophawk/frontend/components/dashboard/LiveDropFeed.tsx
zionboggan.com ↗
62 lines · tsx
History for this file →
1
"use client";
2
 
3
import { useEffect } from "react";
4
import { AnimatePresence } from "framer-motion";
5
import { Inbox, Radio } from "lucide-react";
6
import { DropCard } from "./DropCard";
7
import { Card, CardBody, CardHeader, CardTitle } from "@/components/ui/card";
8
import { Skeleton } from "@/components/ui/skeleton";
9
import { EmptyState } from "@/components/shared/EmptyState";
10
import { ConnectionStatus } from "@/components/shared/ConnectionStatus";
11
import { useRecentDrops } from "@/hooks/useDrops";
12
import { useUiStore } from "@/stores/ui";
13
 
14
export function LiveDropFeed() {
15
  const { data, isLoading } = useRecentDrops(30);
16
  const liveDrops = useUiStore((s) => s.liveDrops);
17
  const seedDrops = useUiStore((s) => s.seedDrops);
18
 
19
  useEffect(() => {
20
    if (data?.data?.length && liveDrops.length === 0) seedDrops(data.data);
21
  }, [data]);
22
 
23
  return (
24
    <Card className="flex h-full flex-col">
25
      <CardHeader>
26
        <CardTitle className="flex items-center gap-2">
27
          <Radio className="h-4 w-4 text-[var(--color-accent-teal)]" aria-hidden />
28
          Live feed
29
        </CardTitle>
30
        <ConnectionStatus compact />
31
      </CardHeader>
32
      <CardBody className="flex-1 overflow-hidden pt-3">
33
        {isLoading && liveDrops.length === 0 ? (
34
          <div className="space-y-3">
35
            {Array.from({ length: 4 }).map((_, i) => (
36
              <Skeleton key={i} className="h-[148px] rounded-[var(--radius-lg)]" />
37
            ))}
38
          </div>
39
        ) : liveDrops.length === 0 ? (
40
          <EmptyState
41
            icon={Inbox}
42
            title="Listening for drops"
43
            description="Nothing yet. The moment a tracked event goes on sale, it lands here in real time and pings your phone."
44
          />
45
        ) : (
46
          <div
47
            className="max-h-[calc(100dvh-19rem)] space-y-3 overflow-y-auto pr-1"
48
            role="feed"
49
            aria-busy={isLoading}
50
            aria-label="Live ticket drops"
51
          >
52
            <AnimatePresence initial={false} mode="popLayout">
53
              {liveDrops.map((drop) => (
54
                <DropCard key={drop.id} drop={drop} />
55
              ))}
56
            </AnimatePresence>
57
          </div>
58
        )}
59
      </CardBody>
60
    </Card>
61
  );
62
}