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"]["data"];
26 children: React.ReactNode;
27}) {
28 return (
29 <SWRConfig
30 value={{
31 fallback: {
32 [`${props.leaflet_id}-leaflet_data`]: props.leaflet_data,
33 },
34 }}
35 >
36 {props.children}
37 </SWRConfig>
38 );
39}
40
41export function useRSVPData() {
42 let { permission_token } = useReplicache();
43 return useSWR(`rsvp_data`, () =>
44 getRSVPData(
45 permission_token.permission_token_rights.map((pr) => pr.entity_set),
46 ),
47 );
48}
49export function usePollData() {
50 let { permission_token } = useReplicache();
51 return useSWR(`poll_data`, () =>
52 getPollData(
53 permission_token.permission_token_rights.map((pr) => pr.entity_set),
54 ),
55 );
56}
57
58let useLeafletData = () => {
59 let { permission_token } = useReplicache();
60 let staticLeafletData = useContext(StaticLeafletDataContext);
61 let res = useSWR(
62 staticLeafletData ? null : `${permission_token.id}-leaflet_data`,
63 async () =>
64 permission_token.id
65 ? (await callRPC("get_leaflet_data", { token_id: permission_token.id }))
66 ?.result.data
67 : undefined,
68 );
69 if (staticLeafletData) return { data: staticLeafletData, mutate: res.mutate };
70 return res;
71};
72export function useLeafletPublicationData() {
73 let { data, mutate } = useLeafletData();
74
75 // First check for leaflets in publications
76 let pubData = getPublicationMetadataFromLeafletData(data);
77
78 // Normalize records so consumers don't have to
79 const normalizedPublication = useMemo(
80 () => normalizePublicationRecord(pubData?.publications?.record),
81 [pubData?.publications?.record]
82 );
83 const normalizedDocument = useMemo(
84 () => normalizeDocumentRecord(pubData?.documents?.data),
85 [pubData?.documents?.data]
86 );
87
88 return {
89 data: pubData || null,
90 // Pre-normalized data - consumers should use these instead of normalizing themselves
91 normalizedPublication,
92 normalizedDocument,
93 mutate,
94 };
95}
96export function useLeafletDomains() {
97 let { data, mutate } = useLeafletData();
98 return { data: data?.custom_domain_routes, mutate: mutate };
99}
100
101export function useLeafletPublicationStatus() {
102 const data = useContext(StaticLeafletDataContext);
103 if (!data) return null;
104
105 const publishedInPublication = data.leaflets_in_publications?.find(
106 (l) => l.doc,
107 );
108 const publishedStandalone = data.leaflets_to_documents?.find(
109 (l) => !!l.documents,
110 );
111
112 const documentUri =
113 publishedInPublication?.documents?.uri ?? publishedStandalone?.document;
114
115 // Compute the full post URL for sharing
116 let postShareLink: string | undefined;
117 if (publishedInPublication?.publications && publishedInPublication.documents) {
118 const normalizedDoc = normalizeDocumentRecord(
119 publishedInPublication.documents.data,
120 publishedInPublication.documents.uri,
121 );
122 if (normalizedDoc) {
123 postShareLink = getDocumentURL(
124 normalizedDoc,
125 publishedInPublication.documents.uri,
126 publishedInPublication.publications,
127 );
128 }
129 } else if (publishedStandalone?.document) {
130 const normalizedDoc = publishedStandalone.documents
131 ? normalizeDocumentRecord(publishedStandalone.documents.data, publishedStandalone.document)
132 : null;
133 if (normalizedDoc) {
134 postShareLink = getDocumentURL(normalizedDoc, publishedStandalone.document);
135 } else {
136 const docUri = new AtUri(publishedStandalone.document);
137 postShareLink = `/p/${docUri.host}/${docUri.rkey}`;
138 }
139 }
140
141 return {
142 token: data,
143 leafletId: data.root_entity,
144 shareLink: data.id,
145 // Draft state - in a publication but not yet published
146 draftInPublication:
147 data.leaflets_in_publications?.[0]?.publication ?? undefined,
148 // Published state
149 isPublished: !!(publishedInPublication || publishedStandalone),
150 publishedAt:
151 publishedInPublication?.documents?.indexed_at ??
152 publishedStandalone?.documents?.indexed_at,
153 documentUri,
154 // Full URL for sharing published posts
155 postShareLink,
156 };
157}