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, getDocumentURL } 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 const normalizedDoc = normalizeDocumentRecord(
123 publishedInPublication.documents.data,
124 publishedInPublication.documents.uri,
125 );
126 if (normalizedDoc) {
127 postShareLink = getDocumentURL(
128 normalizedDoc,
129 publishedInPublication.documents.uri,
130 publishedInPublication.publications,
131 );
132 }
133 } else if (publishedStandalone?.document) {
134 const normalizedDoc = publishedStandalone.documents
135 ? normalizeDocumentRecord(publishedStandalone.documents.data, publishedStandalone.document)
136 : null;
137 if (normalizedDoc) {
138 postShareLink = getDocumentURL(normalizedDoc, publishedStandalone.document);
139 } else {
140 const docUri = new AtUri(publishedStandalone.document);
141 postShareLink = `/p/${docUri.host}/${docUri.rkey}`;
142 }
143 }
144
145 return {
146 token: data,
147 leafletId: data.root_entity,
148 shareLink: data.id,
149 // Draft state - in a publication but not yet published
150 draftInPublication:
151 data.leaflets_in_publications?.[0]?.publication ?? undefined,
152 // Published state
153 isPublished: !!(publishedInPublication || publishedStandalone),
154 publishedAt:
155 publishedInPublication?.documents?.indexed_at ??
156 publishedStandalone?.documents?.indexed_at,
157 documentUri,
158 // Full URL for sharing published posts
159 postShareLink,
160 };
161}