a tool for shared writing and social publishing
1"use server";
2
3import { getIdentityData } from "actions/getIdentityData";
4import { supabaseServerClient } from "supabase/serverClient";
5import type {
6 NormalizedDocument,
7 NormalizedPublication,
8} from "src/utils/normalizeRecords";
9import { deduplicateByUriOrdered } from "src/utils/deduplicateRecords";
10import { enrichDocumentToPost } from "./enrichPost";
11
12export type Cursor = {
13 timestamp: string;
14 uri: string;
15};
16
17export async function getReaderFeed(
18 cursor?: Cursor | null,
19): Promise<{ posts: Post[]; nextCursor: Cursor | null }> {
20 let auth_res = await getIdentityData();
21 if (!auth_res?.atp_did) return { posts: [], nextCursor: null };
22 let query = supabaseServerClient
23 .from("documents")
24 .select(
25 `*,
26 comments_on_documents(count),
27 document_mentions_in_bsky(count),
28 recommends_on_documents(count),
29 documents_in_publications!inner(publications!inner(*, publication_subscriptions!inner(*)))`,
30 )
31 .eq(
32 "documents_in_publications.publications.publication_subscriptions.identity",
33 auth_res.atp_did,
34 )
35 .order("sort_date", { ascending: false })
36 .order("uri", { ascending: false })
37 .limit(25);
38 if (cursor) {
39 query = query.or(
40 `sort_date.lt.${cursor.timestamp},and(sort_date.eq.${cursor.timestamp},uri.lt.${cursor.uri})`,
41 );
42 }
43 let { data: rawFeed, error } = await query;
44
45 // Deduplicate records that may exist under both pub.leaflet and site.standard namespaces
46 const feed = deduplicateByUriOrdered(rawFeed || []);
47
48 let posts = (
49 await Promise.all(feed.map((post) => enrichDocumentToPost(post as any)))
50 ).filter((post): post is Post => post !== null);
51
52 const nextCursor =
53 posts.length > 0
54 ? {
55 timestamp: posts[posts.length - 1].documents.sort_date,
56 uri: posts[posts.length - 1].documents.uri,
57 }
58 : null;
59
60 return {
61 posts,
62 nextCursor,
63 };
64}
65
66export type Post = {
67 author: string | null;
68 publication?: {
69 href: string;
70 pubRecord: NormalizedPublication | null;
71 uri: string;
72 };
73 documents: {
74 data: NormalizedDocument | null;
75 uri: string;
76 sort_date: string;
77 comments_on_documents: { count: number }[] | undefined;
78 document_mentions_in_bsky: { count: number }[] | undefined;
79 recommends_on_documents: { count: number }[] | undefined;
80 mentionsCount?: number;
81 };
82};