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