Something went wrong. Try again.
a tool for shared writing and social publishing
Something went wrong. Try again.
8.1 kB · 284 lines
TSX
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285import { useReplicache } from "src/replicache";import React, { useEffect, useState } from "react";import { getShareLink } from "./getShareLink";import { useEntitySetContext } from "components/EntitySetProvider";import { useSmoker } from "components/Toast";import { Menu, MenuItem } from "components/Layout";import { ActionButton } from "components/ActionBar/ActionButton";import useSWR from "swr";import { useTemplateState } from "app/(home-pages)/home/Actions/CreateNewButton";import LoginForm from "app/login/LoginForm";import { CustomDomainMenu } from "./DomainOptions";import { useIdentityData } from "components/IdentityProvider";import { useLeafletDomains, useLeafletPublicationData,} from "components/PageSWRDataProvider";import { ShareSmall } from "components/Icons/ShareSmall";import { PubLeafletDocument } from "lexicons/api";import { getPublicationURL } from "app/lish/createPub/getPublicationURL";import { AtUri } from "@atproto/syntax";import { useIsMobile } from "src/hooks/isMobile";
export type ShareMenuStates = "default" | "login" | "domain";
export let usePublishLink = () => { let { permission_token, rootEntity } = useReplicache(); let entity_set = useEntitySetContext(); let { data: publishLink } = useSWR( "publishLink-" + permission_token.id, async () => { if ( !permission_token.permission_token_rights.find( (s) => s.entity_set === entity_set.set && s.create_token, ) ) return; let shareLink = await getShareLink( { id: permission_token.id, entity_set: entity_set.set }, rootEntity, ); return shareLink?.id; }, ); return publishLink;};
export function ShareOptions() { let [menuState, setMenuState] = useState<ShareMenuStates>("default"); let { data: pub } = useLeafletPublicationData(); let isMobile = useIsMobile();
return ( <Menu asChild side={isMobile ? "top" : "right"} align={isMobile ? "center" : "start"} className="max-w-xs" onOpenChange={() => { setMenuState("default"); }} trigger={ <ActionButton icon=<ShareSmall /> primary={!!!pub} secondary={!!pub} label={`Share ${pub ? "Draft" : ""}`} /> } > {menuState === "login" ? ( <div className="px-3 py-1"> <LoginForm text="Save your Leaflets and access them on multiple devices!" /> </div> ) : menuState === "domain" ? ( <CustomDomainMenu setShareMenuState={setMenuState} /> ) : ( <ShareMenu setMenuState={setMenuState} domainConnected={false} isPub={!!pub} /> )} </Menu> );}
const ShareMenu = (props: { setMenuState: (state: ShareMenuStates) => void; domainConnected: boolean; isPub?: boolean;}) => { let { permission_token } = useReplicache(); let { data: pub } = useLeafletPublicationData();
let record = pub?.documents?.data as PubLeafletDocument.Record | null;
let postLink = pub?.publications && pub.documents ? `${getPublicationURL(pub.publications)}/${new AtUri(pub?.documents.uri).rkey}` : null; let publishLink = usePublishLink(); let [collabLink, setCollabLink] = useState<null | string>(null); useEffect(() => { // strip leading '/' character from pathname setCollabLink(window.location.pathname.slice(1)); }, []); let { data: domains } = useLeafletDomains();
let isTemplate = useTemplateState( (s) => !!s.templates.find((t) => t.id === permission_token.id), );
return ( <> {isTemplate && ( <> <ShareButton text="Share Template" subtext="Let others make new Leaflets as copies of this template" smokerText="Template link copied!" id="get-template-link" link={`template/${publishLink}` || ""} /> <hr className="border-border my-1" /> </> )}
<ShareButton text={`Share ${postLink ? "Draft" : ""} Edit Link`} subtext="" smokerText="Edit link copied!" id="get-edit-link" link={collabLink} /> <ShareButton text={`Share ${postLink ? "Draft" : ""} View Link`} subtext=<> {domains?.[0] ? ( <> This Leaflet is published on{" "} <span className="italic underline"> {domains[0].domain} {domains[0].route} </span> </> ) : ( "" )} </> smokerText="View link copied!" id="get-view-link" fullLink={ domains?.[0] ? `https://${domains[0].domain}${domains[0].route}` : undefined } link={publishLink || ""} /> {postLink && ( <> <hr className="border-border-light" />
<ShareButton text="Share Published Link" subtext="" smokerText="Post link copied!" id="get-post-link" fullLink={postLink.includes("http") ? postLink : undefined} link={postLink} /> </> )} {!props.isPub && ( <> <hr className="border-border mt-1" /> <DomainMenuItem setMenuState={props.setMenuState} /> </> )} </> );};
export const ShareButton = (props: { text: React.ReactNode; subtext: React.ReactNode; helptext?: string; smokerText: string; id: string; link: null | string; fullLink?: string; className?: string;}) => { let smoker = useSmoker();
return ( <MenuItem id={props.id} onSelect={(e) => { e.preventDefault(); let rect = document.getElementById(props.id)?.getBoundingClientRect(); if (props.link || props.fullLink) { navigator.clipboard.writeText( props.fullLink ? props.fullLink : `${location.protocol}//${location.host}/${props.link}`, ); smoker({ position: { x: rect ? rect.left + (rect.right - rect.left) / 2 : 0, y: rect ? rect.top + 26 : 0, }, text: props.smokerText, }); } }} > <div className={`group/${props.id} ${props.className}`}> <div className={`group-hover/${props.id}:text-accent-contrast`}> {props.text} </div> <div className={`text-sm font-normal text-tertiary group-hover/${props.id}:text-accent-contrast`} > {props.subtext} </div> {/* optional help text */} {props.helptext && ( <div className={`text-sm italic font-normal text-tertiary group-hover/${props.id}:text-accent-contrast`} > {props.helptext} </div> )} </div> </MenuItem> );};
const DomainMenuItem = (props: { setMenuState: (state: ShareMenuStates) => void;}) => { let { identity } = useIdentityData(); let { data: domains } = useLeafletDomains();
if (identity === null) return ( <div className="text-tertiary font-normal text-sm px-3 py-1"> <button className="text-accent-contrast hover:font-bold" onClick={() => { props.setMenuState("login"); }} > Log In </button>{" "} to publish on a custom domain! </div> ); else return ( <> {domains?.[0] ? ( <button className="px-3 py-1 text-accent-contrast text-sm hover:font-bold w-fit text-left" onMouseDown={() => { props.setMenuState("domain"); }} > Edit custom domain </button> ) : ( <MenuItem className="font-normal text-tertiary text-sm" onSelect={(e) => { e.preventDefault(); props.setMenuState("domain"); }} > Publish on a custom domain </MenuItem> )} </> );};