forked from
leaflet.pub/leaflet
a tool for shared writing and social publishing
1"use server";
2
3import { getIdentityData } from "actions/getIdentityData";
4import { getPublicationURL } from "app/lish/createPub/getPublicationURL";
5import { supabaseServerClient } from "supabase/serverClient";
6import { IdResolver } from "@atproto/identity";
7import type { DidCache, CacheResult, DidDocument } from "@atproto/identity";
8import Client from "ioredis";
9import { AtUri } from "@atproto/api";
10import { Json } from "supabase/database.types";
11import { idResolver } from "./idResolver";
12
13export type Cursor = {
14 timestamp: string;
15 uri: string;
16};
17
18export async function getReaderFeed(
19 cursor?: Cursor | null,
20): Promise<{ posts: Post[]; nextCursor: Cursor | null }> {
21 let auth_res = await getIdentityData();
22 if (!auth_res?.atp_did) return { posts: [], nextCursor: null };
23 let query = supabaseServerClient
24 .from("documents")
25 .select(
26 `*,
27 comments_on_documents(count),
28 document_mentions_in_bsky(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("indexed_at", { ascending: false })
36 .order("uri", { ascending: false })
37 .limit(25);
38 if (cursor) {
39 query = query.or(
40 `indexed_at.lt.${cursor.timestamp},and(indexed_at.eq.${cursor.timestamp},uri.lt.${cursor.uri})`,
41 );
42 }
43 let { data: feed, error } = await query;
44
45 let posts = await Promise.all(
46 feed?.map(async (post) => {
47 let pub = post.documents_in_publications[0].publications!;
48 let uri = new AtUri(post.uri);
49 let handle = await idResolver.did.resolve(uri.host);
50 let p: Post = {
51 publication: {
52 href: getPublicationURL(pub),
53 pubRecord: pub?.record || null,
54 uri: pub?.uri || "",
55 },
56 author: handle?.alsoKnownAs?.[0]
57 ? `@${handle.alsoKnownAs[0].slice(5)}`
58 : null,
59 documents: {
60 comments_on_documents: post.comments_on_documents,
61 document_mentions_in_bsky: post.document_mentions_in_bsky,
62 data: post.data,
63 uri: post.uri,
64 indexed_at: post.indexed_at,
65 },
66 };
67 return p;
68 }) || [],
69 );
70 const nextCursor =
71 posts.length > 0
72 ? {
73 timestamp: posts[posts.length - 1].documents.indexed_at,
74 uri: posts[posts.length - 1].documents.uri,
75 }
76 : null;
77
78 return {
79 posts,
80 nextCursor,
81 };
82}
83
84export type Post = {
85 author: string | null;
86 publication: {
87 href: string;
88 pubRecord: Json;
89 uri: string;
90 };
91 documents: {
92 data: Json;
93 uri: string;
94 indexed_at: string;
95 comments_on_documents:
96 | {
97 count: number;
98 }[]
99 | undefined;
100 document_mentions_in_bsky:
101 | {
102 count: number;
103 }[]
104 | undefined;
105 };
106};