a tool for shared writing and social publishing
1"use client";
2import { getRSVPData } from "actions/getRSVPData";
3import { SWRConfig } from "swr";
4import { useReplicache } from "src/replicache";
5import useSWR from "swr";
6import { callRPC } from "app/api/rpc/client";
7import { getPollData } from "actions/pollActions";
8import type { GetLeafletDataReturnType } from "app/api/rpc/[command]/get_leaflet_data";
9import { createContext, useContext, useMemo } from "react";
10import { getPublicationMetadataFromLeafletData } from "src/utils/getPublicationMetadataFromLeafletData";
11import { getPublicationURL } from "app/lish/createPub/getPublicationURL";
12import { AtUri } from "@atproto/syntax";
13import {
14 normalizeDocumentRecord,
15 normalizePublicationRecord,
16 type NormalizedDocument,
17 type NormalizedPublication,
18} from "src/utils/normalizeRecords";
19
20export const StaticLeafletDataContext = createContext<
21 null | GetLeafletDataReturnType["result"]["data"]
22>(null);
23export function PageSWRDataProvider(props: {
24 leaflet_id: string;
25 leaflet_data: GetLeafletDataReturnType["result"];
26 rsvp_data: Awaited<ReturnType<typeof getRSVPData>>;
27 poll_data: Awaited<ReturnType<typeof getPollData>>;
28 children: React.ReactNode;
29}) {
30 return (
31 <SWRConfig
32 value={{
33 fallback: {
34 rsvp_data: props.rsvp_data,
35 poll_data: props.poll_data,
36 [`${props.leaflet_id}-leaflet_data`]: props.leaflet_data.data,
37 },
38 }}
39 >
40 {props.children}
41 </SWRConfig>
42 );
43}
44
45export function useRSVPData() {
46 let { permission_token } = useReplicache();
47 return useSWR(`rsvp_data`, () =>
48 getRSVPData(
49 permission_token.permission_token_rights.map((pr) => pr.entity_set),
50 ),
51 );
52}
53export function usePollData() {
54 let { permission_token } = useReplicache();
55 return useSWR(`poll_data`, () =>
56 getPollData(
57 permission_token.permission_token_rights.map((pr) => pr.entity_set),
58 ),
59 );
60}
61
62let useLeafletData = () => {
63 let { permission_token } = useReplicache();
64 let staticLeafletData = useContext(StaticLeafletDataContext);
65 let res = useSWR(
66 staticLeafletData ? null : `${permission_token.id}-leaflet_data`,
67 async () =>
68 permission_token.id
69 ? (await callRPC("get_leaflet_data", { token_id: permission_token.id }))
70 ?.result.data
71 : undefined,
72 );
73 if (staticLeafletData) return { data: staticLeafletData, mutate: res.mutate };
74 return res;
75};
76export function useLeafletPublicationData() {
77 let { data, mutate } = useLeafletData();
78
79 // First check for leaflets in publications
80 let pubData = getPublicationMetadataFromLeafletData(data);
81
82 // Normalize records so consumers don't have to
83 const normalizedPublication = useMemo(
84 () => normalizePublicationRecord(pubData?.publications?.record),
85 [pubData?.publications?.record]
86 );
87 const normalizedDocument = useMemo(
88 () => normalizeDocumentRecord(pubData?.documents?.data),
89 [pubData?.documents?.data]
90 );
91
92 return {
93 data: pubData || null,
94 // Pre-normalized data - consumers should use these instead of normalizing themselves
95 normalizedPublication,
96 normalizedDocument,
97 mutate,
98 };
99}
100export function useLeafletDomains() {
101 let { data, mutate } = useLeafletData();
102 return { data: data?.custom_domain_routes, mutate: mutate };
103}
104
105export function useLeafletPublicationStatus() {
106 const data = useContext(StaticLeafletDataContext);
107 if (!data) return null;
108
109 const publishedInPublication = data.leaflets_in_publications?.find(
110 (l) => l.doc,
111 );
112 const publishedStandalone = data.leaflets_to_documents?.find(
113 (l) => !!l.documents,
114 );
115
116 const documentUri =
117 publishedInPublication?.documents?.uri ?? publishedStandalone?.document;
118
119 // Compute the full post URL for sharing
120 let postShareLink: string | undefined;
121 if (publishedInPublication?.publications && publishedInPublication.documents) {
122 // Published in a publication - use publication URL + document rkey
123 const docUri = new AtUri(publishedInPublication.documents.uri);
124 postShareLink = `${getPublicationURL(publishedInPublication.publications)}/${docUri.rkey}`;
125 } else if (publishedStandalone?.document) {
126 // Standalone published post - use /p/{did}/{rkey} format
127 const docUri = new AtUri(publishedStandalone.document);
128 postShareLink = `/p/${docUri.host}/${docUri.rkey}`;
129 }
130
131 return {
132 token: data,
133 leafletId: data.root_entity,
134 shareLink: data.id,
135 // Draft state - in a publication but not yet published
136 draftInPublication:
137 data.leaflets_in_publications?.[0]?.publication ?? undefined,
138 // Published state
139 isPublished: !!(publishedInPublication || publishedStandalone),
140 publishedAt:
141 publishedInPublication?.documents?.indexed_at ??
142 publishedStandalone?.documents?.indexed_at,
143 documentUri,
144 // Full URL for sharing published posts
145 postShareLink,
146 };
147}