···3434 props.parentData?.bsky_profiles?.handle ||
3535 "Someone";
36363737- let rkey = new AtUri(props.commentData.documents?.uri!).rkey;
3737+ let docUri = new AtUri(props.commentData.documents?.uri!);
3838+ let rkey = docUri.rkey;
3939+ let did = docUri.host;
3840 const pubRecord = props.commentData.documents?.documents_in_publications[0]
3939- ?.publications?.record as PubLeafletPublication.Record;
4141+ ?.publications?.record as PubLeafletPublication.Record | undefined;
4242+4343+ const href = pubRecord
4444+ ? `https://${pubRecord.base_path}/${rkey}?interactionDrawer=comments`
4545+ : `/p/${did}/${rkey}?interactionDrawer=comments`;
40464147 return (
4248 <Notification
4349 timestamp={props.commentData.indexed_at}
4444- href={`https://${pubRecord.base_path}/${rkey}?interactionDrawer=comments`}
5050+ href={href}
4551 icon={<ReplyTiny />}
4652 actionText={`${displayName} replied to your comment`}
4753 content={
+1-1
app/(home-pages)/reader/ReaderContent.tsx
···102102 let postRecord = props.documents.data as PubLeafletDocument.Record;
103103 let postUri = new AtUri(props.documents.uri);
104104105105- let theme = usePubTheme(pubRecord);
105105+ let theme = usePubTheme(pubRecord?.theme);
106106 let backgroundImage = pubRecord?.theme?.backgroundImage?.image?.ref
107107 ? blobRefToSrc(
108108 pubRecord?.theme?.backgroundImage?.image?.ref,
···11import {
22 PubLeafletDocument,
33 PubLeafletPagesLinearDocument,
44+ PubLeafletPagesCanvas,
45 PubLeafletBlocksCode,
56} from "lexicons/api";
67import { codeToHtml, bundledLanguagesInfo, bundledThemesInfo } from "shiki";
7889export async function extractCodeBlocks(
99- blocks: PubLeafletPagesLinearDocument.Block[],
1010+ blocks: PubLeafletPagesLinearDocument.Block[] | PubLeafletPagesCanvas.Block[],
1011): Promise<Map<string, string>> {
1112 const codeBlocks = new Map<string, string>();
12131313- // Process all pages in the document
1414+ // Process all blocks (works for both linear and canvas)
1415 for (let i = 0; i < blocks.length; i++) {
1516 const block = blocks[i];
1617 const currentIndex = [i];
···127127 onChange={(e) => setShowInDiscover(e.target.checked)}
128128 >
129129 <div className=" pt-0.5 flex flex-col text-sm text-tertiary ">
130130- <p className="font-bold italic">
131131- Show In{" "}
130130+ <p className="font-bold italic">Show In Discover</p>
131131+ <p className="text-sm text-tertiary font-normal">
132132+ Your posts will appear on our{" "}
132133 <a href="/discover" target="_blank">
133134 Discover
134134- </a>
135135- </p>
136136- <p className="text-sm text-tertiary font-normal">
137137- You'll be able to change this later!
135135+ </a>{" "}
136136+ page. You can change this at any time!
138137 </p>
139138 </div>
140139 </Checkbox>
141140 <hr className="border-border-light" />
142141143143- <div className="flex w-full justify-center">
142142+ <div className="flex w-full justify-end">
144143 <ButtonPrimary
145144 type="submit"
146145 disabled={
+5-2
app/lish/createPub/UpdatePubForm.tsx
···6666 if (!pubData) return;
6767 e.preventDefault();
6868 props.setLoadingAction(true);
6969- console.log("step 1:update");
7069 let data = await updatePublication({
7170 uri: pubData.uri,
7271 name: nameValue,
···171170 </a>
172171 </p>
173172 <p className="text-xs text-tertiary font-normal">
174174- This publication will appear on our public Discover page
173173+ Your posts will appear on our{" "}
174174+ <a href="/discover" target="_blank">
175175+ Discover
176176+ </a>{" "}
177177+ page. You can change this at any time!
175178 </p>
176179 </div>
177180 </Checkbox>
···77import { getPollData } from "actions/pollActions";
88import type { GetLeafletDataReturnType } from "app/api/rpc/[command]/get_leaflet_data";
99import { createContext, useContext } from "react";
1010+import { getPublicationMetadataFromLeafletData } from "src/utils/getPublicationMetadataFromLeafletData";
1111+import { getPublicationURL } from "app/lish/createPub/getPublicationURL";
1212+import { AtUri } from "@atproto/syntax";
10131114export const StaticLeafletDataContext = createContext<
1215 null | GetLeafletDataReturnType["result"]["data"]
···6669};
6770export function useLeafletPublicationData() {
6871 let { data, mutate } = useLeafletData();
7272+7373+ // First check for leaflets in publications
7474+ let pubData = getPublicationMetadataFromLeafletData(data);
7575+6976 return {
7070- data:
7171- data?.leaflets_in_publications?.[0] ||
7272- data?.permission_token_rights[0].entity_sets?.permission_tokens?.find(
7373- (p) => p.leaflets_in_publications.length,
7474- )?.leaflets_in_publications?.[0] ||
7575- null,
7777+ data: pubData || null,
7678 mutate,
7779 };
7880}
···8082 let { data, mutate } = useLeafletData();
8183 return { data: data?.custom_domain_routes, mutate: mutate };
8284}
8585+8686+export function useLeafletPublicationStatus() {
8787+ const data = useContext(StaticLeafletDataContext);
8888+ if (!data) return null;
8989+9090+ const publishedInPublication = data.leaflets_in_publications?.find(
9191+ (l) => l.doc,
9292+ );
9393+ const publishedStandalone = data.leaflets_to_documents?.find(
9494+ (l) => !!l.documents,
9595+ );
9696+9797+ const documentUri =
9898+ publishedInPublication?.documents?.uri ?? publishedStandalone?.document;
9999+100100+ // Compute the full post URL for sharing
101101+ let postShareLink: string | undefined;
102102+ if (publishedInPublication?.publications && publishedInPublication.documents) {
103103+ // Published in a publication - use publication URL + document rkey
104104+ const docUri = new AtUri(publishedInPublication.documents.uri);
105105+ postShareLink = `${getPublicationURL(publishedInPublication.publications)}/${docUri.rkey}`;
106106+ } else if (publishedStandalone?.document) {
107107+ // Standalone published post - use /p/{did}/{rkey} format
108108+ const docUri = new AtUri(publishedStandalone.document);
109109+ postShareLink = `/p/${docUri.host}/${docUri.rkey}`;
110110+ }
111111+112112+ return {
113113+ token: data,
114114+ leafletId: data.root_entity,
115115+ shareLink: data.id,
116116+ // Draft state - in a publication but not yet published
117117+ draftInPublication:
118118+ data.leaflets_in_publications?.[0]?.publication ?? undefined,
119119+ // Published state
120120+ isPublished: !!(publishedInPublication || publishedStandalone),
121121+ publishedAt:
122122+ publishedInPublication?.documents?.indexed_at ??
123123+ publishedStandalone?.documents?.indexed_at,
124124+ documentUri,
125125+ // Full URL for sharing published posts
126126+ postShareLink,
127127+ };
128128+}
+4-1
components/Pages/Page.tsx
···1616import { PageOptions } from "./PageOptions";
1717import { CardThemeProvider } from "components/ThemeManager/ThemeProvider";
1818import { useDrawerOpen } from "app/lish/[did]/[publication]/[rkey]/Interactions/InteractionDrawer";
1919+import { usePreserveScroll } from "src/hooks/usePreserveScroll";
19202021export function Page(props: {
2122 entityID: string;
···6061 />
6162 }
6263 >
6363- {props.first && (
6464+ {props.first && pageType === "doc" && (
6465 <>
6566 <PublicationMetadata />
6667 </>
···8384 pageType: "canvas" | "doc";
8485 drawerOpen: boolean | undefined;
8586}) => {
8787+ let { ref } = usePreserveScroll<HTMLDivElement>(props.id);
8688 return (
8789 // this div wraps the contents AND the page options.
8890 // it needs to be its own div because this container does NOT scroll, and therefore doesn't clip the absolutely positioned pageOptions
···9597 it needs to be a separate div so that the user can scroll from anywhere on the page if there isn't a card border
9698 */}
9799 <div
100100+ ref={ref}
98101 onClick={props.onClickAction}
99102 id={props.id}
100103 className={`
+7-6
components/Pages/PageShareMenu.tsx
···11import { useLeafletDomains } from "components/PageSWRDataProvider";
22-import { ShareButton, usePublishLink } from "components/ShareOptions";
22+import {
33+ ShareButton,
44+ useReadOnlyShareLink,
55+} from "app/[leaflet_id]/actions/ShareOptions";
36import { useEffect, useState } from "react";
4758export const PageShareMenu = (props: { entityID: string }) => {
66- let publishLink = usePublishLink();
99+ let publishLink = useReadOnlyShareLink();
710 let { data: domains } = useLeafletDomains();
811 let [collabLink, setCollabLink] = useState<null | string>(null);
912 useEffect(() => {
···1417 <div>
1518 <ShareButton
1619 text="Share Edit Link"
1717- subtext=""
1818- helptext="recipients can edit the full Leaflet"
2020+ subtext="Recipients can edit the full Leaflet"
1921 smokerText="Collab link copied!"
2022 id="get-page-collab-link"
2123 link={`${collabLink}?page=${props.entityID}`}
2224 />
2325 <ShareButton
2426 text="Share View Link"
2525- subtext=""
2626- helptext="recipients can view the full Leaflet"
2727+ subtext="Recipients can view the full Leaflet"
2728 smokerText="Publish link copied!"
2829 id="get-page-publish-link"
2930 fullLink={
···66import { validate as _validate } from '../../../lexicons'
77import { type $Typed, is$typed as _is$typed, type OmitKey } from '../../../util'
88import type * as ComAtprotoRepoStrongRef from '../../com/atproto/repo/strongRef'
99+import type * as PubLeafletPublication from './publication'
910import type * as PubLeafletPagesLinearDocument from './pages/linearDocument'
1011import type * as PubLeafletPagesCanvas from './pages/canvas'
1112···1920 postRef?: ComAtprotoRepoStrongRef.Main
2021 description?: string
2122 publishedAt?: string
2222- publication: string
2323+ publication?: string
2324 author: string
2525+ theme?: PubLeafletPublication.Theme
2426 pages: (
2527 | $Typed<PubLeafletPagesLinearDocument.Main>
2628 | $Typed<PubLeafletPagesCanvas.Main>
···11/// <reference types="next" />
22/// <reference types="next/image-types/global" />
33-/// <reference path="./.next/types/routes.d.ts" />
33+import "./.next/dev/types/routes.d.ts";
4455// NOTE: This file should not be edited
66// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
···11+create table "public"."leaflets_to_documents" (
22+ "leaflet" uuid not null,
33+ "document" text not null,
44+ "created_at" timestamp with time zone not null default now(),
55+ "title" text not null default ''::text,
66+ "description" text not null default ''::text
77+);
88+99+alter table "public"."leaflets_to_documents" enable row level security;
1010+1111+CREATE UNIQUE INDEX leaflets_to_documents_pkey ON public.leaflets_to_documents USING btree (leaflet, document);
1212+1313+alter table "public"."leaflets_to_documents" add constraint "leaflets_to_documents_pkey" PRIMARY KEY using index "leaflets_to_documents_pkey";
1414+1515+alter table "public"."leaflets_to_documents" add constraint "leaflets_to_documents_document_fkey" FOREIGN KEY (document) REFERENCES documents(uri) ON UPDATE CASCADE ON DELETE CASCADE not valid;
1616+1717+alter table "public"."leaflets_to_documents" validate constraint "leaflets_to_documents_document_fkey";
1818+1919+alter table "public"."leaflets_to_documents" add constraint "leaflets_to_documents_leaflet_fkey" FOREIGN KEY (leaflet) REFERENCES permission_tokens(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
2020+2121+alter table "public"."leaflets_to_documents" validate constraint "leaflets_to_documents_leaflet_fkey";
2222+2323+grant delete on table "public"."leaflets_to_documents" to "anon";
2424+2525+grant insert on table "public"."leaflets_to_documents" to "anon";
2626+2727+grant references on table "public"."leaflets_to_documents" to "anon";
2828+2929+grant select on table "public"."leaflets_to_documents" to "anon";
3030+3131+grant trigger on table "public"."leaflets_to_documents" to "anon";
3232+3333+grant truncate on table "public"."leaflets_to_documents" to "anon";
3434+3535+grant update on table "public"."leaflets_to_documents" to "anon";
3636+3737+grant delete on table "public"."leaflets_to_documents" to "authenticated";
3838+3939+grant insert on table "public"."leaflets_to_documents" to "authenticated";
4040+4141+grant references on table "public"."leaflets_to_documents" to "authenticated";
4242+4343+grant select on table "public"."leaflets_to_documents" to "authenticated";
4444+4545+grant trigger on table "public"."leaflets_to_documents" to "authenticated";
4646+4747+grant truncate on table "public"."leaflets_to_documents" to "authenticated";
4848+4949+grant update on table "public"."leaflets_to_documents" to "authenticated";
5050+5151+grant delete on table "public"."leaflets_to_documents" to "service_role";
5252+5353+grant insert on table "public"."leaflets_to_documents" to "service_role";
5454+5555+grant references on table "public"."leaflets_to_documents" to "service_role";
5656+5757+grant select on table "public"."leaflets_to_documents" to "service_role";
5858+5959+grant trigger on table "public"."leaflets_to_documents" to "service_role";
6060+6161+grant truncate on table "public"."leaflets_to_documents" to "service_role";
6262+6363+grant update on table "public"."leaflets_to_documents" to "service_role";