diff --git a/app/components/Sharing/Collection/PublicAccess.tsx b/app/components/Sharing/Collection/PublicAccess.tsx index 1c68e05500..98a7173866 100644 --- a/app/components/Sharing/Collection/PublicAccess.tsx +++ b/app/components/Sharing/Collection/PublicAccess.tsx @@ -20,6 +20,7 @@ import Text from "~/components/Text"; import Tooltip from "~/components/Tooltip"; import env from "~/env"; import usePolicy from "~/hooks/usePolicy"; +import useStores from "~/hooks/useStores"; import { ListItem } from "../components/ListItem"; import { DomainPrefix, ShareLinkInput, StyledInfoIcon } from "../components"; @@ -35,13 +36,15 @@ function InnerPublicAccess( ref: React.RefObject ) { const { t } = useTranslation(); + const { shares } = useStores(); const theme = useTheme(); const [validationError, setValidationError] = React.useState(""); const [urlId, setUrlId] = React.useState(share?.urlId); const inputRef = React.useRef(null); const can = usePolicy(share); const collectionAbilities = usePolicy(collection); - const canPublish = can.update && collectionAbilities.share; + const canPublish = share ? can.update : collectionAbilities.share; + const [creating, setCreating] = React.useState(false); React.useEffect(() => { setUrlId(share?.urlId); @@ -102,14 +105,23 @@ function InnerPublicAccess( const handlePublishedChange = React.useCallback( async (checked: boolean) => { try { - await share?.save({ - published: checked, - }); + if (checked && !share) { + setCreating(true); + await shares.create({ + type: "collection", + collectionId: collection.id, + published: true, + }); + } else if (share) { + await share.save({ published: checked }); + } } catch (err) { toast.error(err.message); + } finally { + setCreating(false); } }, - [share] + [share, shares, collection] ); const handleUrlChange = React.useMemo( @@ -172,7 +184,7 @@ function InnerPublicAccess( aria-label={t("Publish to internet")} checked={share?.published ?? false} onChange={handlePublishedChange} - disabled={!canPublish} + disabled={!canPublish || creating} width={26} height={14} /> diff --git a/app/components/Sharing/Document/PublicAccess.tsx b/app/components/Sharing/Document/PublicAccess.tsx index a43763b294..59dcdd29d6 100644 --- a/app/components/Sharing/Document/PublicAccess.tsx +++ b/app/components/Sharing/Document/PublicAccess.tsx @@ -14,6 +14,7 @@ import type Share from "~/models/Share"; import Switch from "~/components/Switch"; import env from "~/env"; import usePolicy from "~/hooks/usePolicy"; +import useStores from "~/hooks/useStores"; import { AvatarSize } from "../../Avatar"; import CopyToClipboard from "../../CopyToClipboard"; import NudeButton from "../../NudeButton"; @@ -45,13 +46,15 @@ function PublicAccess( ref: React.RefObject ) { const { t } = useTranslation(); + const { shares } = useStores(); const theme = useTheme(); const [validationError, setValidationError] = React.useState(""); const [urlId, setUrlId] = React.useState(share?.urlId); const inputRef = React.useRef(null); const can = usePolicy(share); const documentAbilities = usePolicy(document); - const canPublish = can.update && documentAbilities.share; + const canPublish = share ? can.update : documentAbilities.share; + const [creating, setCreating] = React.useState(false); React.useEffect(() => { setUrlId(share?.urlId); @@ -112,14 +115,23 @@ function PublicAccess( const handlePublishedChange = React.useCallback( async (checked: boolean) => { try { - await share?.save({ - published: checked, - }); + if (checked && !share) { + setCreating(true); + await shares.create({ + type: "document", + documentId: document.id, + published: true, + }); + } else if (share) { + await share.save({ published: checked }); + } } catch (err) { toast.error(err.message); + } finally { + setCreating(false); } }, - [share] + [share, shares, document] ); const handleUrlChange = React.useMemo( @@ -215,7 +227,7 @@ function PublicAccess( aria-label={t("Publish to internet")} checked={share?.published ?? false} onChange={handlePublishedChange} - disabled={!canPublish} + disabled={!canPublish || creating} width={26} height={14} /> diff --git a/app/hooks/useShareDataLoader.ts b/app/hooks/useShareDataLoader.ts index 690ea2db09..6f5246819e 100644 --- a/app/hooks/useShareDataLoader.ts +++ b/app/hooks/useShareDataLoader.ts @@ -16,7 +16,8 @@ type Params = * @returns preload function, loading state, and reset function. */ export default function useShareDataLoader(params: Params) { - const { userMemberships, groupMemberships, memberships } = useStores(); + const { shares, userMemberships, groupMemberships, memberships } = + useStores(); const [loading, setLoading] = useState(false); const requestedRef = useRef(false); const requestCountRef = useRef(0); @@ -42,7 +43,7 @@ export default function useShareDataLoader(params: Params) { if (params.document) { const doc = params.document; promises.push( - doc.share(), + shares.fetchOne({ documentId: doc.id }), userMemberships.fetchDocumentMemberships({ id: doc.id, limit: Pagination.defaultLimit, @@ -52,7 +53,7 @@ export default function useShareDataLoader(params: Params) { } else { const col = params.collection; promises.push( - col.share(), + shares.fetchOne({ collectionId: col.id }), memberships.fetchAll({ id: col.id }), groupMemberships.fetchAll({ collectionId: col.id }) ); @@ -66,6 +67,7 @@ export default function useShareDataLoader(params: Params) { }, [ params.document, params.collection, + shares, userMemberships, groupMemberships, memberships, diff --git a/app/models/Collection.ts b/app/models/Collection.ts index b34eb2bd91..1f8ab54d91 100644 --- a/app/models/Collection.ts +++ b/app/models/Collection.ts @@ -318,13 +318,6 @@ export default class Collection extends ParanoidModel { this.index = index; } - @action - share = async () => - this.store.rootStore.shares.create({ - type: "collection", - collectionId: this.id, - }); - getChildrenForDocument(documentId: string) { let result: NavigationNode[] = []; diff --git a/app/models/Document.ts b/app/models/Document.ts index 3d35d33949..1a0a35f732 100644 --- a/app/models/Document.ts +++ b/app/models/Document.ts @@ -460,13 +460,6 @@ export default class Document extends ArchivableModel implements Searchable { } } - @action - share = async () => - this.store.rootStore.shares.create({ - type: "document", - documentId: this.id, - }); - archive = () => this.store.archive(this); restore = (options?: { revisionId?: string; collectionId?: string }) =>