From bb500f4e486bb9c5cebf6d8119cc7214d9e12144 Mon Sep 17 00:00:00 2001 From: Pavel Denisjuk Date: Thu, 10 Oct 2024 12:17:01 +0200 Subject: [PATCH] fix(app-record-locking): decorate singleton entry form (#4325) --- .../src/ContentEntryEditorConfig.ts | 5 +++ .../SingletonHeader/SaveAction.tsx | 2 +- .../ContentEntry/SingletonContentEntry.tsx | 2 +- .../SingletonContentEntryContext.tsx | 4 +- .../ContentEntryGuard.tsx | 8 ++-- .../ContentEntryLocker.tsx | 20 ++++++--- .../HeadlessCmsContentEntry.tsx | 43 ++++++++++++++++--- .../decorators/UseSaveEntryDecorator.tsx | 8 ++-- 8 files changed, 70 insertions(+), 22 deletions(-) diff --git a/packages/app-headless-cms/src/ContentEntryEditorConfig.ts b/packages/app-headless-cms/src/ContentEntryEditorConfig.ts index 1ec5a07814d..ec6603cd896 100644 --- a/packages/app-headless-cms/src/ContentEntryEditorConfig.ts +++ b/packages/app-headless-cms/src/ContentEntryEditorConfig.ts @@ -12,6 +12,8 @@ import { useContentEntryForm } from "./admin/components/ContentEntryForm/useCont import { useContentEntry } from "~/admin/views/contentEntries/hooks"; import { ContentEntry } from "~/admin/views/contentEntries/ContentEntry"; import { ContentEntryEditorConfig as BaseContentEntryEditorConfig } from "./admin/config/contentEntries"; +import { SingletonContentEntry } from "~/admin/views/contentEntries/ContentEntry/SingletonContentEntry"; +import { useSingletonContentEntry } from "~/admin/views/contentEntries/hooks/useSingletonContentEntry"; export const ContentEntryEditorConfig = Object.assign(BaseContentEntryEditorConfig, { ContentEntry: Object.assign(ContentEntry, { @@ -21,6 +23,9 @@ export const ContentEntryEditorConfig = Object.assign(BaseContentEntryEditorConf }), ContentEntryFormPreview }), + SingletonContentEntry: Object.assign(SingletonContentEntry, { + useSingletonContentEntry + }), FieldRenderers: { DynamicZone: { Template: { diff --git a/packages/app-headless-cms/src/admin/components/ContentEntryForm/SingletonHeader/SaveAction.tsx b/packages/app-headless-cms/src/admin/components/ContentEntryForm/SingletonHeader/SaveAction.tsx index d8d230d5edc..493d56aad45 100644 --- a/packages/app-headless-cms/src/admin/components/ContentEntryForm/SingletonHeader/SaveAction.tsx +++ b/packages/app-headless-cms/src/admin/components/ContentEntryForm/SingletonHeader/SaveAction.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { ContentEntryEditorConfig } from "~/ContentEntryEditorConfig"; +import { ContentEntryEditorConfig } from "~/admin/config/contentEntries"; import { useContentEntryForm } from "~/admin/components/ContentEntryForm/useContentEntryForm"; const { Actions } = ContentEntryEditorConfig; diff --git a/packages/app-headless-cms/src/admin/views/contentEntries/ContentEntry/SingletonContentEntry.tsx b/packages/app-headless-cms/src/admin/views/contentEntries/ContentEntry/SingletonContentEntry.tsx index 354cdfbe380..fc5183e7d84 100644 --- a/packages/app-headless-cms/src/admin/views/contentEntries/ContentEntry/SingletonContentEntry.tsx +++ b/packages/app-headless-cms/src/admin/views/contentEntries/ContentEntry/SingletonContentEntry.tsx @@ -4,7 +4,7 @@ import { Elevation as BaseElevation } from "@webiny/ui/Elevation"; import { CircularProgress } from "@webiny/ui/Progress"; import { ContentEntryForm } from "~/admin/components/ContentEntryForm/ContentEntryForm"; import { makeDecoratable } from "@webiny/app"; -import { useSingletonContentEntry } from "~/admin/views/contentEntries/hooks/useSingletonContentEntry"; +import { useSingletonContentEntry } from "../hooks/useSingletonContentEntry"; import { PartialCmsContentEntryWithId } from "~/admin/contexts/Cms"; import { SingletonHeader } from "~/admin/components/ContentEntryForm/SingletonHeader"; diff --git a/packages/app-headless-cms/src/admin/views/contentEntries/ContentEntry/SingletonContentEntryContext.tsx b/packages/app-headless-cms/src/admin/views/contentEntries/ContentEntry/SingletonContentEntryContext.tsx index 89cbb0d6c5f..9f47f3ff82e 100644 --- a/packages/app-headless-cms/src/admin/views/contentEntries/ContentEntry/SingletonContentEntryContext.tsx +++ b/packages/app-headless-cms/src/admin/views/contentEntries/ContentEntry/SingletonContentEntryContext.tsx @@ -2,8 +2,8 @@ import React, { useEffect, useState } from "react"; import { useSnackbar, useIsMounted } from "@webiny/app-admin"; import { useCms } from "~/admin/hooks"; import { useContentEntries } from "~/admin/views/contentEntries/hooks/useContentEntries"; -import { CmsContentEntry, CmsModel } from "~/types"; -import * as Cms from "~/admin/contexts/Cms"; +import type { CmsContentEntry, CmsModel } from "~/types"; +import type * as Cms from "~/admin/contexts/Cms"; export type UpdateEntryParams = Omit; diff --git a/packages/app-record-locking/src/components/HeadlessCmsContentEntry/ContentEntryGuard.tsx b/packages/app-record-locking/src/components/HeadlessCmsContentEntry/ContentEntryGuard.tsx index 62bbb7f0f86..de853504a71 100644 --- a/packages/app-record-locking/src/components/HeadlessCmsContentEntry/ContentEntryGuard.tsx +++ b/packages/app-record-locking/src/components/HeadlessCmsContentEntry/ContentEntryGuard.tsx @@ -1,4 +1,3 @@ -import { useContentEntry } from "@webiny/app-headless-cms"; import { useRecordLocking } from "~/hooks"; import { Elevation } from "@webiny/ui/Elevation"; import { CircularProgress } from "@webiny/ui/Progress"; @@ -7,6 +6,7 @@ import styled from "@emotion/styled"; import React, { useEffect, useState } from "react"; import { LockedRecord } from "../LockedRecord"; import { IRecordLockingLockRecord } from "~/types"; +import { CmsContentEntry, CmsModel } from "@webiny/app-headless-cms/types"; const DetailsContainer = styled("div")({ height: "calc(100% - 10px)", @@ -31,12 +31,14 @@ const elevationStyles = css({ }); export interface IContentEntryGuardProps { + loading: boolean; + entry: CmsContentEntry; + model: CmsModel; children: React.ReactElement; } export const ContentEntryGuard = (props: IContentEntryGuardProps) => { - const { loading, entry, contentModel: model } = useContentEntry(); - const { children } = props; + const { loading, entry, model, children } = props; const { fetchLockedEntryLockRecord } = useRecordLocking(); const [locked, setLocked] = useState(undefined); diff --git a/packages/app-record-locking/src/components/HeadlessCmsContentEntry/ContentEntryLocker.tsx b/packages/app-record-locking/src/components/HeadlessCmsContentEntry/ContentEntryLocker.tsx index 427dffe85c3..d279bcffaf8 100644 --- a/packages/app-record-locking/src/components/HeadlessCmsContentEntry/ContentEntryLocker.tsx +++ b/packages/app-record-locking/src/components/HeadlessCmsContentEntry/ContentEntryLocker.tsx @@ -1,4 +1,3 @@ -import { useContentEntriesList, useContentEntry } from "@webiny/app-headless-cms"; import React, { useEffect, useRef } from "react"; import { useRecordLocking } from "~/hooks"; import { IIsRecordLockedParams, IRecordLockingIdentity, IRecordLockingLockRecord } from "~/types"; @@ -10,12 +9,16 @@ import { import { parseIdentifier } from "@webiny/utils"; import { useDialogs } from "@webiny/app-admin"; import styled from "@emotion/styled"; +import { CmsContentEntry, CmsModel } from "@webiny/app-headless-cms/types"; const Bold = styled.span` font-weight: 600; `; export interface IContentEntryLockerProps { + entry: CmsContentEntry; + model: CmsModel; + onEntryUnlocked: () => void; onDisablePrompt: (flag: boolean) => void; children: React.ReactElement; } @@ -39,13 +42,16 @@ const ForceUnlocked = ({ user }: IForceUnlockedProps) => { ); }; -export const ContentEntryLocker = ({ onDisablePrompt, children }: IContentEntryLockerProps) => { - const { entry, contentModel: model } = useContentEntry(); +export const ContentEntryLocker = ({ + onEntryUnlocked, + onDisablePrompt, + entry, + model, + children +}: IContentEntryLockerProps) => { const { updateEntryLock, unlockEntry, fetchLockedEntryLockRecord, removeEntryLock } = useRecordLocking(); - const { navigateTo } = useContentEntriesList(); - const subscription = useRef>(); const websockets = useWebsockets(); @@ -77,7 +83,7 @@ export const ContentEntryLocker = ({ onDisablePrompt, children }: IContentEntryL onClose: undefined, cancelLabel: undefined }); - navigateTo(); + onEntryUnlocked(); } ); @@ -87,7 +93,7 @@ export const ContentEntryLocker = ({ onDisablePrompt, children }: IContentEntryL } subscription.current.off(); }; - }, [entry.id, navigateTo, model.modelId]); + }, [entry.id, onEntryUnlocked, model.modelId]); useEffect(() => { if (!entry.id) { diff --git a/packages/app-record-locking/src/components/HeadlessCmsContentEntry/HeadlessCmsContentEntry.tsx b/packages/app-record-locking/src/components/HeadlessCmsContentEntry/HeadlessCmsContentEntry.tsx index 898233bc7a2..f7f536cfe72 100644 --- a/packages/app-record-locking/src/components/HeadlessCmsContentEntry/HeadlessCmsContentEntry.tsx +++ b/packages/app-record-locking/src/components/HeadlessCmsContentEntry/HeadlessCmsContentEntry.tsx @@ -1,11 +1,11 @@ import React, { useState } from "react"; import { CompositionScope, createGenericContext } from "@webiny/app-admin"; -import { ContentEntryEditorConfig } from "@webiny/app-headless-cms"; +import { ContentEntryEditorConfig, ContentEntryListConfig } from "@webiny/app-headless-cms"; import { Prompt } from "@webiny/react-router"; import { ContentEntryGuard } from "./ContentEntryGuard"; import { ContentEntryLocker } from "./ContentEntryLocker"; -const { ContentEntry } = ContentEntryEditorConfig; +const { ContentEntry, SingletonContentEntry } = ContentEntryEditorConfig; const DisablePrompt = createGenericContext<{ disablePrompt: boolean }>("DisablePrompt"); @@ -20,7 +20,8 @@ const PromptDecorator = Prompt.createDecorator(Original => { const ContentEntryDecorator = ContentEntry.createDecorator(Original => { return function RecordLockingContentEntry() { const [disablePrompt, setDisablePrompt] = useState(false); - const { entry } = ContentEntry.useContentEntry(); + const { entry, contentModel, loading } = ContentEntry.useContentEntry(); + const { navigateTo } = ContentEntryListConfig.ContentEntries.useContentEntriesList(); /** * New entry does not have ID yet. */ @@ -34,9 +35,40 @@ const ContentEntryDecorator = ContentEntry.createDecorator(Original => { /** * Continue with existing entry. */ + const props = { entry, model: contentModel }; + + return ( + + setDisablePrompt(flag)} + onEntryUnlocked={navigateTo} + > + + + + + + ); + }; +}); + +const SingletonContentEntryDecorator = SingletonContentEntry.createDecorator(Original => { + return function RecordLockingSingletonContentEntry() { + const [disablePrompt, setDisablePrompt] = useState(false); + const { entry, contentModel, loading } = SingletonContentEntry.useSingletonContentEntry(); + + const props = { entry, model: contentModel }; + return ( - - setDisablePrompt(flag)}> + + setDisablePrompt(flag)} + onEntryUnlocked={() => { + // There's nowhere to go, since singleton entry doesn't have a list view. + }} + > @@ -50,6 +82,7 @@ export const HeadlessCmsContentEntry = () => { return ( <> + diff --git a/packages/app-record-locking/src/components/decorators/UseSaveEntryDecorator.tsx b/packages/app-record-locking/src/components/decorators/UseSaveEntryDecorator.tsx index 0d269e17f84..8419eb75199 100644 --- a/packages/app-record-locking/src/components/decorators/UseSaveEntryDecorator.tsx +++ b/packages/app-record-locking/src/components/decorators/UseSaveEntryDecorator.tsx @@ -1,10 +1,10 @@ import { useCallback } from "react"; import { useRecordLocking } from "~/hooks"; -import { ContentEntryEditorConfig } from "@webiny/app-headless-cms"; +import { ContentEntryEditorConfig, useModel } from "@webiny/app-headless-cms"; import { useSnackbar } from "@webiny/app-admin"; const { - ContentEntry: { ContentEntryForm, useContentEntry } + ContentEntry: { ContentEntryForm } } = ContentEntryEditorConfig; type SaveEntry = ReturnType["saveEntry"]; @@ -13,9 +13,11 @@ export const UseSaveEntryDecorator = ContentEntryForm.useContentEntryForm.create originalHook => { return function useRecordLockingUseSave() { const hook = originalHook(); - const { entry, contentModel: model } = useContentEntry(); const { fetchLockedEntryLockRecord, updateEntryLock } = useRecordLocking(); const { showSnackbar } = useSnackbar(); + const { model } = useModel(); + + const { entry } = hook; const saveEntry: SaveEntry = useCallback( async (...params) => {