From cdee8ec0d64c61c449a620eb9c6978b848789a2e Mon Sep 17 00:00:00 2001 From: celine Date: Thu, 16 Apr 2026 23:22:31 -0400 Subject: [PATCH] refactored publish pages --- .../publish/BskyPostEditorProsemirror.tsx | 2 +- app/[leaflet_id]/publish/PublishPost.tsx | 357 ++++++++++-------- app/[leaflet_id]/publish/ShareOptions.tsx | 152 ++++++++ components/Tags.tsx | 21 +- 4 files changed, 366 insertions(+), 166 deletions(-) create mode 100644 app/[leaflet_id]/publish/ShareOptions.tsx diff --git a/app/[leaflet_id]/publish/BskyPostEditorProsemirror.tsx b/app/[leaflet_id]/publish/BskyPostEditorProsemirror.tsx index 26a74252..793aafb0 100644 --- a/app/[leaflet_id]/publish/BskyPostEditorProsemirror.tsx +++ b/app/[leaflet_id]/publish/BskyPostEditorProsemirror.tsx @@ -304,7 +304,7 @@ export function BlueskyPostEditorProsemirror(props: { /> {editorState?.doc.textContent.length === 0 && (
- Write a post to share your writing! + post something...
)}
{ let editorStateRef = useRef(null); + let [state, setState] = useState<"post-details" | "share-options">( + "post-details", + ); let [charCount, setCharCount] = useState(0); - let [shareOption, setShareOption] = useState<"bluesky" | "quiet">("bluesky"); + let [shareState, setShareState] = useState({ + bluesky: true, + postToReaders: true, + email: true, + quiet: false, + }); let [isLoading, setIsLoading] = useState(false); let [oauthError, setOauthError] = useState< import("src/atproto-oauth").OAuthSessionError | null @@ -82,6 +93,7 @@ const PublishPostForm = ( tx.get("publication_tags"), ); let [localTags, setLocalTags] = useState([]); + let [showTagSelector, setShowTagSelector] = useState(false); let [localPublishedAt, setLocalPublishedAt] = useState( undefined, @@ -152,7 +164,7 @@ const PublishPostForm = ( let [text, facets] = editorStateRef.current ? editorStateToFacetedText(editorStateRef.current) : []; - if (shareOption === "bluesky") { + if (shareState.bluesky) { let bskyResult = await publishPostToBsky({ facets: facets || [], text: text || "", @@ -181,69 +193,181 @@ const PublishPostForm = ( }} >
- -
+ {state === "post-details" ? ( + <> +

Publish: Post Details

+ +
- -
- -
-

Tags

- -
-
- -
- -
-
- - Back - - 300} - > - {isLoading ? ( - - ) : ( - "Publish this Post!" - )} - -
- {oauthError && ( - - )} -
+
+ +
+
Tags
+
+ {currentTags.length !== 0 || showTagSelector === true ? ( +
+ +
+ ) : ( + + )} +
+
+
+
+
Social Preview
+
+ +
+
+
+ {props.title || "Untitled"} +
+ {props.description && ( +
+ {props.description} +
+ )} +
+
+ {(props.record?.url || "leaflet.pub").replace( + /^https?:\/\//, + "", + )} +
+
+
+
+
+ +
+ + Back + + setState("share-options")} + > + Next: Share + +
+ + ) : ( + <> +

Publish: Share Options

+ +
+ +
+
+ + 300} + > + {isLoading ? ( + + ) : ( + "Publish this Post!" + )} + +
+ {oauthError && ( + + )} +
+ + )}
); }; +const SocialPreviewImage = (props: { + rootEntity: string; + did: string; + coverImageCid: string | null; +}) => { + const firstPage = useEntity(props.rootEntity, "root/page")[0]; + const page = firstPage?.data.value || props.rootEntity; + + if (props.coverImageCid) { + return ( + + ); + } + + return ( + + +
+
+ +
+
+
+
+ ); +}; + const BackdateOptions = (props: { publishedAt: Date | undefined; setPublishedAt: (date: Date | undefined) => void; @@ -305,14 +429,16 @@ const BackdateOptions = (props: { return (
-

Publish Date

+
Publish Date
{formattedDate}
+
+ {formattedDate} +
) : ( -
now
+
Now
) } > @@ -332,108 +458,17 @@ const BackdateOptions = (props: { ); }; -const ShareOptions = (props: { - shareOption: "quiet" | "bluesky"; - setShareOption: (option: typeof props.shareOption) => void; - charCount: number; - setCharCount: (c: number) => void; - editorStateRef: React.MutableRefObject; - title: string; - profile: ProfileViewDetailed; - description: string; - record?: NormalizedPublication | null; -}) => { - return ( -
-

Share and Notify

- { - if (e.target === e.currentTarget) { - props.setShareOption("quiet"); - } - }} - name="share-options" - id="share-quietly" - value="Share Quietly" - > -
-
Share Quietly
-
- No one will be notified about this post -
-
-
- { - if (e.target === e.currentTarget) { - props.setShareOption("bluesky"); - } - }} - name="share-options" - id="share-bsky" - value="Share on Bluesky" - > -
-
Share on Bluesky
-
- Pub subscribers will be updated via a custom Bluesky feed -
-
-
-
-
-
- -
-
-

{props.profile.displayName}

-

@{props.profile.handle}

-
-
- -
-
-
-
{props.title}
-
{props.description}
-
-

- {props.record?.url?.replace(/^https?:\/\//, "")} -

-
-
-
- {props.charCount}/300 -
-
-
-
-
-
- ); -}; - const PublishingTo = (props: { publication_uri?: string; record?: NormalizedPublication | null; }) => { if (props.publication_uri && props.record) { return ( -
-

Publishing to

-
- +
+
Publishing to
+
{props.record.name}
+
); diff --git a/app/[leaflet_id]/publish/ShareOptions.tsx b/app/[leaflet_id]/publish/ShareOptions.tsx new file mode 100644 index 00000000..269a0078 --- /dev/null +++ b/app/[leaflet_id]/publish/ShareOptions.tsx @@ -0,0 +1,152 @@ +"use client"; +import { Checkbox } from "components/Checkbox"; +import { BlueskyPostEditorProsemirror } from "./BskyPostEditorProsemirror"; +import { EditorState } from "prosemirror-state"; +import { ProfileViewDetailed } from "@atproto/api/dist/client/types/app/bsky/actor/defs"; +import type { NormalizedPublication } from "src/utils/normalizeRecords"; + +export type ShareState = { + bluesky: boolean; + postToReaders: boolean; + email: boolean; + quiet: boolean; +}; + +type Props = { + shareState: ShareState; + setShareState: (state: ShareState) => void; + charCount: number; + setCharCount: (c: number) => void; + editorStateRef: React.MutableRefObject; + title: string; + profile: ProfileViewDetailed; + description: string; + record?: NormalizedPublication | null; + subscriberCount?: number; +}; + +export function ShareOptions(props: Props) { + const { shareState, setShareState } = props; + const handleChange = ( + key: keyof Omit, + checked: boolean, + ) => { + setShareState({ ...shareState, [key]: checked, quiet: false }); + }; + + const handleQuietChange = (checked: boolean) => { + if (checked) { + setShareState({ + bluesky: false, + postToReaders: false, + email: false, + quiet: true, + }); + } else { + setShareState({ ...shareState, quiet: false }); + } + }; + + return ( +
+ handleChange("email", e.target.checked)} + > +
+
+ Email{" "} + {props.subscriberCount !== undefined + ? `${props.subscriberCount} ` + : ""} + Subscribers +
+
+
+ + handleChange("postToReaders", e.target.checked)} + > +
+
Post to Readers
+
+ This post will show up in Leaflet feeds and other Standard Site + enabled reader feeds +
+
+
+ + handleChange("bluesky", e.target.checked)} + > +
+
Share on Bluesky
+
+ Pub subscribers will be updated via a custom Bluesky feed +
+
+
+
+
+
+ +
+
+

{props.profile.displayName}

+

@{props.profile.handle}

+
+
+ +
+
+
+
+ {props.title} +
+
+ {props.description} +
+
+

+ {props.record?.url?.replace(/^https?:\/\//, "")} +

+
+
+
+ {props.charCount}/300 +
+
+
+
+
+ +
+ + handleQuietChange(e.target.checked)} + > +
+
Post Quietly
+
+ No one will be notified about this post +
+
+
+
+ ); +} diff --git a/components/Tags.tsx b/components/Tags.tsx index bd6ec822..8ad403b9 100644 --- a/components/Tags.tsx +++ b/components/Tags.tsx @@ -39,15 +39,19 @@ export const Tag = (props: { export const TagSelector = (props: { selectedTags: string[]; setSelectedTags: (tags: string[]) => void; + rightAlign?: boolean; }) => { return (
{props.selectedTags.length > 0 ? ( -
+
{props.selectedTags.map((tag) => ( ) : ( -
no tags selected
+
+ no tags selected +
)}
); }; export const TagSearchInput = (props: { + rightAlign?: boolean; selectedTags: string[]; setSelectedTags: (tags: string[]) => void; }) => { @@ -203,7 +212,9 @@ export const TagSearchInput = (props: { }} /> {props.selectedTags.length > 0 ? ( -
+
{props.selectedTags.map((tag) => ( ) : ( -
+
no tags selected
)} -- 2.51.2