import { atprotoClient } from '../api/client'; import type { GetPostThreadResponse, GetTimelineResponse, GetFeedResponse, AtUri } from '../api/types'; import { cacheSet, cacheGet, threadCacheKey, timelineCacheKey, feedCacheKey } from './cache'; const inflightThreads = new Map>(); export function prefetchThread(uri: string): void { const key = threadCacheKey(uri); if (cacheGet(key)) return; if (inflightThreads.has(key)) return; const promise = atprotoClient.getPostThread(uri) .then((resp) => { cacheSet(key, resp); inflightThreads.delete(key); return resp; }) .catch(() => { inflightThreads.delete(key); return null as never; }); inflightThreads.set(key, promise); } export function getCachedThread(uri: string): GetPostThreadResponse | undefined { return cacheGet(threadCacheKey(uri)); } let inflightTimeline: Promise | null = null; export function prefetchTimeline(): void { const key = timelineCacheKey(); if (cacheGet(key)) return; if (inflightTimeline) return; inflightTimeline = atprotoClient.getTimeline(50) .then((resp) => { cacheSet(key, resp); inflightTimeline = null; return resp; }) .catch(() => { inflightTimeline = null; return null as never; }); } export function getCachedTimeline(): GetTimelineResponse | undefined { return cacheGet(timelineCacheKey()); } const inflightFeeds = new Map>(); export function prefetchFeed(feedUri: AtUri): void { const key = feedCacheKey(feedUri); if (cacheGet(key)) return; if (inflightFeeds.has(key)) return; const promise = atprotoClient.getFeed(feedUri, 50) .then((resp) => { cacheSet(key, resp); inflightFeeds.delete(key); return resp; }) .catch(() => { inflightFeeds.delete(key); return null as never; }); inflightFeeds.set(key, promise); } export function getCachedFeed(feedUri: AtUri): GetFeedResponse | undefined { return cacheGet(feedCacheKey(feedUri)); } const inflightProfileTabs = new Map>(); export function prefetchProfileTab(actor: string, filter?: string): void { const key = filter ? `profile-tab:${actor}:${filter}` : `profile-tab:${actor}:posts_with_replies`; if (cacheGet(key)) return; if (inflightProfileTabs.has(key)) return; const promise = atprotoClient.getAuthorFeed( actor, 30, undefined, filter as 'posts_with_replies' | 'posts_no_replies' | 'posts_with_media' | 'posts_with_video' | undefined, ) .then((resp) => { cacheSet(key, resp); inflightProfileTabs.delete(key); }) .catch(() => { inflightProfileTabs.delete(key); }); inflightProfileTabs.set(key, promise); } export function prefetchProfileLikes(actor: string, isOwnProfile = true): void { const key = `profile-tab:${actor}:likes`; if (cacheGet(key)) return; if (inflightProfileTabs.has(key)) return; const promise = (isOwnProfile ? atprotoClient.getActorLikes(actor, 30) : atprotoClient.getActorLikesViaRecords(actor, 25) ) .then((resp) => { cacheSet(key, resp); inflightProfileTabs.delete(key); }) .catch(() => { inflightProfileTabs.delete(key); }); inflightProfileTabs.set(key, promise); } export function getCachedProfileTab(actor: string, filter: string): T | undefined { return cacheGet(`profile-tab:${actor}:${filter}`); } export function prefetchFollowers(actor: string): void { const key = `followers:${actor}`; if (cacheGet(key)) return; if (inflightProfileTabs.has(key)) return; const promise = atprotoClient.getFollowers(actor, 50) .then((resp) => { cacheSet(key, resp); inflightProfileTabs.delete(key); }) .catch(() => { inflightProfileTabs.delete(key); }); inflightProfileTabs.set(key, promise); } export function prefetchFollows(actor: string): void { const key = `follows:${actor}`; if (cacheGet(key)) return; if (inflightProfileTabs.has(key)) return; const promise = atprotoClient.getFollows(actor, 50) .then((resp) => { cacheSet(key, resp); inflightProfileTabs.delete(key); }) .catch(() => { inflightProfileTabs.delete(key); }); inflightProfileTabs.set(key, promise); } const prefetchedModules = new Set(); export function prefetchModule(importFn: () => Promise, key: string): void { if (prefetchedModules.has(key)) return; prefetchedModules.add(key); importFn().catch(() => { prefetchedModules.delete(key); }); }