Scrapboard.org client
1import { useEffect, useState } from "react";
2import { useAuth } from "@/lib/hooks/useAuth";
3import { useFeedDefsStore } from "../stores/feedDefs";
4import { AtUri } from "@atproto/api";
5
6// How long the cache stays fresh (30 minutes by default)
7const CACHE_DURATION = 30 * 60 * 1000;
8
9export function useFeeds() {
10 const { agent } = useAuth();
11 const store = useFeedDefsStore();
12 const [isLoading, setLoading] = useState(store.feeds == null);
13
14 useEffect(() => {
15 if (agent == null) return;
16
17 // Use cached data immediately if available
18 if (store.feeds != null) {
19 setLoading(false);
20 }
21
22 // Check if we need to refresh the data
23 const lastFetchedAt = localStorage.getItem("feedsLastFetchedAt");
24 const now = Date.now();
25 const isStale =
26 !lastFetchedAt || now - parseInt(lastFetchedAt) > CACHE_DURATION;
27
28 // Skip fetching if data is fresh
29 if (!isStale && store.feeds != null) return;
30
31 const loadFeeds = async () => {
32 // Only show loading state if we have no cached data
33 const isBackgroundRefresh = store.feeds != null;
34 if (!isBackgroundRefresh) {
35 setLoading(true);
36 }
37
38 try {
39 const prefs = await agent.getPreferences();
40 if (prefs?.savedFeeds == null) return;
41
42 for (const feed of prefs.savedFeeds) {
43 if (!feed.value.startsWith("at")) continue;
44 const urip = AtUri.make(feed.value);
45
46 if (!urip.host.startsWith("did:")) {
47 const res = await agent.resolveHandle({ handle: urip.host });
48 urip.host = res.data.did;
49 }
50
51 if (feed.type == "feed") {
52 const feedDef = await agent.app.bsky.feed.getFeedGenerators({
53 feeds: [urip.toString()],
54 });
55
56 store.setFeedDef(feed.value, feedDef.data.feeds[0]);
57 } else if (feed.type == "list") {
58 const listDef = await agent.app.bsky.graph.getList({
59 list: urip.toString(),
60 });
61
62 store.setFeedDef(feed.value, {
63 displayName: listDef.data.list.name,
64 });
65 }
66 }
67
68 // Update the last fetched timestamp
69 localStorage.setItem("feedsLastFetchedAt", now.toString());
70 } finally {
71 setLoading(false);
72 }
73 };
74
75 loadFeeds();
76 }, [agent]);
77
78 return { isLoading };
79}